migrate to new git

This commit is contained in:
2025-08-29 01:27:25 +08:00
parent 946eb9961e
commit af2c152ef6
8623 changed files with 1000453 additions and 1 deletions

View File

@@ -0,0 +1,69 @@
import { error } from './utils/utils.js'
import { DismissReason } from './utils/DismissReason.js'
import * as staticMethods from './staticMethods.js'
import * as instanceMethods from './instanceMethods.js'
import privateProps from './privateProps.js'
let currentInstance
// SweetAlert constructor
function SweetAlert (...args) {
// Prevent run in Node env
/* istanbul ignore if */
if (typeof window === 'undefined') {
return
}
// Check for the existence of Promise
/* istanbul ignore if */
if (typeof Promise === 'undefined') {
error('This package requires a Promise library, please include a shim to enable it in this browser (See: https://github.com/sweetalert2/sweetalert2/wiki/Migration-from-SweetAlert-to-SweetAlert2#1-ie-support)')
}
currentInstance = this
const outerParams = Object.freeze(this.constructor.argsToParams(args))
Object.defineProperties(this, {
params: {
value: outerParams,
writable: false,
enumerable: true,
configurable: true
}
})
const promise = this._main(this.params)
privateProps.promise.set(this, promise)
}
// `catch` cannot be the name of a module export, so we define our thenable methods here instead
SweetAlert.prototype.then = function (onFulfilled) {
const promise = privateProps.promise.get(this)
return promise.then(onFulfilled)
}
SweetAlert.prototype.finally = function (onFinally) {
const promise = privateProps.promise.get(this)
return promise.finally(onFinally)
}
// Assign instance methods from src/instanceMethods/*.js to prototype
Object.assign(SweetAlert.prototype, instanceMethods)
// Assign static methods from src/staticMethods/*.js to constructor
Object.assign(SweetAlert, staticMethods)
// Proxy to instance methods to constructor, for now, for backwards compatibility
Object.keys(instanceMethods).forEach(key => {
SweetAlert[key] = function (...args) {
if (currentInstance) {
return currentInstance[key](...args)
}
}
})
SweetAlert.DismissReason = DismissReason
SweetAlert.version = '9.10.9'
export default SweetAlert

View File

@@ -0,0 +1 @@
export const RESTORE_FOCUS_TIMEOUT = 100

View File

@@ -0,0 +1,30 @@
import { RESTORE_FOCUS_TIMEOUT } from './constants.js'
const globalState = {}
export default globalState
const focusPreviousActiveElement = () => {
if (globalState.previousActiveElement && globalState.previousActiveElement.focus) {
globalState.previousActiveElement.focus()
globalState.previousActiveElement = null
} else if (document.body) {
document.body.focus()
}
}
// Restore previous active (focused) element
export const restoreActiveElement = () => {
return new Promise(resolve => {
const x = window.scrollX
const y = window.scrollY
globalState.restoreFocusTimeout = setTimeout(() => {
focusPreviousActiveElement()
resolve()
}, RESTORE_FOCUS_TIMEOUT) // issues/900
/* istanbul ignore if */
if (typeof x !== 'undefined' && typeof y !== 'undefined') { // IE doesn't have scrollX/scrollY support
window.scrollTo(x, y)
}
})
}

View File

@@ -0,0 +1,9 @@
export * from './instanceMethods/hideLoading.js'
export * from './instanceMethods/getInput.js'
export * from './instanceMethods/close.js'
export * from './instanceMethods/enable-disable-elements.js'
export * from './instanceMethods/show-reset-validation-error.js'
export * from './instanceMethods/progress-steps.js'
export * from './instanceMethods/_main.js'
export * from './instanceMethods/update.js'
export * from './instanceMethods/_destroy.js'

View File

@@ -0,0 +1,46 @@
import globalState from '../globalState.js'
import privateProps from '../privateProps.js'
import privateMethods from '../privateMethods.js'
export function _destroy () {
const domCache = privateProps.domCache.get(this)
const innerParams = privateProps.innerParams.get(this)
if (!innerParams) {
return // This instance has already been destroyed
}
// Check if there is another Swal closing
if (domCache.popup && globalState.swalCloseEventFinishedCallback) {
globalState.swalCloseEventFinishedCallback()
delete globalState.swalCloseEventFinishedCallback
}
// Check if there is a swal disposal defer timer
if (globalState.deferDisposalTimer) {
clearTimeout(globalState.deferDisposalTimer)
delete globalState.deferDisposalTimer
}
if (typeof innerParams.onDestroy === 'function') {
innerParams.onDestroy()
}
disposeSwal(this)
}
const disposeSwal = (instance) => {
// Unset this.params so GC will dispose it (#1569)
delete instance.params
// Unset globalState props so GC will dispose globalState (#1569)
delete globalState.keydownHandler
delete globalState.keydownTarget
// Unset WeakMaps so GC will be able to dispose them (#1569)
unsetWeakMaps(privateProps)
unsetWeakMaps(privateMethods)
}
const unsetWeakMaps = (obj) => {
for (const i in obj) {
obj[i] = new WeakMap()
}
}

View File

@@ -0,0 +1,161 @@
import defaultParams, { showWarningsForParams } from '../utils/params.js'
import * as dom from '../utils/dom/index.js'
import { swalClasses } from '../utils/classes.js'
import Timer from '../utils/Timer.js'
import { callIfFunction } from '../utils/utils.js'
import setParameters from '../utils/setParameters.js'
import globalState from '../globalState.js'
import { openPopup } from '../utils/openPopup.js'
import privateProps from '../privateProps.js'
import privateMethods from '../privateMethods.js'
import { handleInputOptionsAndValue } from '../utils/dom/inputUtils.js'
import { handleConfirmButtonClick, handleCancelButtonClick } from './buttons-handlers.js'
import { addKeydownHandler, setFocus } from './keydown-handler.js'
import { handlePopupClick } from './popup-click-handler.js'
import { DismissReason } from '../utils/DismissReason.js'
export function _main (userParams) {
showWarningsForParams(userParams)
if (globalState.currentInstance) {
globalState.currentInstance._destroy()
}
globalState.currentInstance = this
const innerParams = prepareParams(userParams)
setParameters(innerParams)
Object.freeze(innerParams)
// clear the previous timer
if (globalState.timeout) {
globalState.timeout.stop()
delete globalState.timeout
}
// clear the restore focus timeout
clearTimeout(globalState.restoreFocusTimeout)
const domCache = populateDomCache(this)
dom.render(this, innerParams)
privateProps.innerParams.set(this, innerParams)
return swalPromise(this, domCache, innerParams)
}
const prepareParams = (userParams) => {
const showClass = Object.assign({}, defaultParams.showClass, userParams.showClass)
const hideClass = Object.assign({}, defaultParams.hideClass, userParams.hideClass)
const params = Object.assign({}, defaultParams, userParams)
params.showClass = showClass
params.hideClass = hideClass
// @deprecated
if (userParams.animation === false) {
params.showClass = {
popup: 'swal2-noanimation',
backdrop: 'swal2-noanimation'
}
params.hideClass = {}
}
return params
}
const swalPromise = (instance, domCache, innerParams) => {
return new Promise((resolve) => {
// functions to handle all closings/dismissals
const dismissWith = (dismiss) => {
instance.closePopup({ dismiss })
}
privateMethods.swalPromiseResolve.set(instance, resolve)
domCache.confirmButton.onclick = () => handleConfirmButtonClick(instance, innerParams)
domCache.cancelButton.onclick = () => handleCancelButtonClick(instance, dismissWith)
domCache.closeButton.onclick = () => dismissWith(DismissReason.close)
handlePopupClick(instance, domCache, dismissWith)
addKeydownHandler(instance, globalState, innerParams, dismissWith)
if (innerParams.toast && (innerParams.input || innerParams.footer || innerParams.showCloseButton)) {
dom.addClass(document.body, swalClasses['toast-column'])
} else {
dom.removeClass(document.body, swalClasses['toast-column'])
}
handleInputOptionsAndValue(instance, innerParams)
openPopup(innerParams)
setupTimer(globalState, innerParams, dismissWith)
initFocus(domCache, innerParams)
// Scroll container to top on open (#1247)
domCache.container.scrollTop = 0
})
}
const populateDomCache = (instance) => {
const domCache = {
popup: dom.getPopup(),
container: dom.getContainer(),
content: dom.getContent(),
actions: dom.getActions(),
confirmButton: dom.getConfirmButton(),
cancelButton: dom.getCancelButton(),
closeButton: dom.getCloseButton(),
validationMessage: dom.getValidationMessage(),
progressSteps: dom.getProgressSteps()
}
privateProps.domCache.set(instance, domCache)
return domCache
}
const setupTimer = (globalState, innerParams, dismissWith) => {
const timerProgressBar = dom.getTimerProgressBar()
dom.hide(timerProgressBar)
if (innerParams.timer) {
globalState.timeout = new Timer(() => {
dismissWith('timer')
delete globalState.timeout
}, innerParams.timer)
if (innerParams.timerProgressBar) {
dom.show(timerProgressBar)
setTimeout(() => {
if (globalState.timeout.running) { // timer can be already stopped at this point
dom.animateTimerProgressBar(innerParams.timer)
}
})
}
}
}
const initFocus = (domCache, innerParams) => {
if (innerParams.toast) {
return
}
if (!callIfFunction(innerParams.allowEnterKey)) {
return blurActiveElement()
}
if (innerParams.focusCancel && dom.isVisible(domCache.cancelButton)) {
return domCache.cancelButton.focus()
}
if (innerParams.focusConfirm && dom.isVisible(domCache.confirmButton)) {
return domCache.confirmButton.focus()
}
setFocus(innerParams, -1, 1)
}
const blurActiveElement = () => {
if (document.activeElement && typeof document.activeElement.blur === 'function') {
document.activeElement.blur()
}
}

View File

@@ -0,0 +1,70 @@
import { isVisible } from '../utils/dom/domUtils.js'
import { getInputValue } from '../utils/dom/inputUtils.js'
import { getValidationMessage } from '../utils/dom/getters.js'
import { showLoading } from '../staticMethods/showLoading.js'
import { DismissReason } from '../utils/DismissReason.js'
export const handleConfirmButtonClick = (instance, innerParams) => {
instance.disableButtons()
if (innerParams.input) {
handleConfirmWithInput(instance, innerParams)
} else {
confirm(instance, innerParams, true)
}
}
export const handleCancelButtonClick = (instance, dismissWith) => {
instance.disableButtons()
dismissWith(DismissReason.cancel)
}
const handleConfirmWithInput = (instance, innerParams) => {
const inputValue = getInputValue(instance, innerParams)
if (innerParams.inputValidator) {
instance.disableInput()
const validationPromise = Promise.resolve().then(() => innerParams.inputValidator(inputValue, innerParams.validationMessage))
validationPromise.then(
(validationMessage) => {
instance.enableButtons()
instance.enableInput()
if (validationMessage) {
instance.showValidationMessage(validationMessage)
} else {
confirm(instance, innerParams, inputValue)
}
}
)
} else if (!instance.getInput().checkValidity()) {
instance.enableButtons()
instance.showValidationMessage(innerParams.validationMessage)
} else {
confirm(instance, innerParams, inputValue)
}
}
const succeedWith = (instance, value) => {
instance.closePopup({ value })
}
const confirm = (instance, innerParams, value) => {
if (innerParams.showLoaderOnConfirm) {
showLoading() // TODO: make showLoading an *instance* method
}
if (innerParams.preConfirm) {
instance.resetValidationMessage()
const preConfirmPromise = Promise.resolve().then(() => innerParams.preConfirm(value, innerParams.validationMessage))
preConfirmPromise.then(
(preConfirmValue) => {
if (isVisible(getValidationMessage()) || preConfirmValue === false) {
instance.hideLoading()
} else {
succeedWith(instance, typeof (preConfirmValue) === 'undefined' ? value : preConfirmValue)
}
}
)
} else {
succeedWith(instance, value)
}
}

View File

@@ -0,0 +1,119 @@
import { undoScrollbar } from '../utils/scrollbarFix.js'
import { undoIOSfix } from '../utils/iosFix.js'
import { undoIEfix } from '../utils/ieFix.js'
import { unsetAriaHidden } from '../utils/aria.js'
import * as dom from '../utils/dom/index.js'
import { swalClasses } from '../utils/classes.js'
import globalState, { restoreActiveElement } from '../globalState.js'
import privateProps from '../privateProps.js'
import privateMethods from '../privateMethods.js'
/*
* Instance method to close sweetAlert
*/
function removePopupAndResetState (instance, container, isToast, onAfterClose) {
if (isToast) {
triggerOnAfterCloseAndDispose(instance, onAfterClose)
} else {
restoreActiveElement().then(() => triggerOnAfterCloseAndDispose(instance, onAfterClose))
globalState.keydownTarget.removeEventListener('keydown', globalState.keydownHandler, { capture: globalState.keydownListenerCapture })
globalState.keydownHandlerAdded = false
}
if (container.parentNode && !document.body.getAttribute('data-swal2-queue-step')) {
container.parentNode.removeChild(container)
}
if (dom.isModal()) {
undoScrollbar()
undoIOSfix()
undoIEfix()
unsetAriaHidden()
}
removeBodyClasses()
}
function removeBodyClasses () {
dom.removeClass(
[document.documentElement, document.body],
[
swalClasses.shown,
swalClasses['height-auto'],
swalClasses['no-backdrop'],
swalClasses['toast-shown'],
swalClasses['toast-column']
]
)
}
export function close (resolveValue) {
const popup = dom.getPopup()
if (!popup) {
return
}
const innerParams = privateProps.innerParams.get(this)
if (!innerParams || dom.hasClass(popup, innerParams.hideClass.popup)) {
return
}
const swalPromiseResolve = privateMethods.swalPromiseResolve.get(this)
dom.removeClass(popup, innerParams.showClass.popup)
dom.addClass(popup, innerParams.hideClass.popup)
const backdrop = dom.getContainer()
dom.removeClass(backdrop, innerParams.showClass.backdrop)
dom.addClass(backdrop, innerParams.hideClass.backdrop)
handlePopupAnimation(this, popup, innerParams)
// Resolve Swal promise
swalPromiseResolve(resolveValue || {})
}
const handlePopupAnimation = (instance, popup, innerParams) => {
const container = dom.getContainer()
// If animation is supported, animate
const animationIsSupported = dom.animationEndEvent && dom.hasCssAnimation(popup)
const { onClose, onAfterClose } = innerParams
if (onClose !== null && typeof onClose === 'function') {
onClose(popup)
}
if (animationIsSupported) {
animatePopup(instance, popup, container, onAfterClose)
} else {
// Otherwise, remove immediately
removePopupAndResetState(instance, container, dom.isToast(), onAfterClose)
}
}
const animatePopup = (instance, popup, container, onAfterClose) => {
globalState.swalCloseEventFinishedCallback = removePopupAndResetState.bind(null, instance, container, dom.isToast(), onAfterClose)
popup.addEventListener(dom.animationEndEvent, function (e) {
if (e.target === popup) {
globalState.swalCloseEventFinishedCallback()
delete globalState.swalCloseEventFinishedCallback
}
})
}
const triggerOnAfterCloseAndDispose = (instance, onAfterClose) => {
setTimeout(() => {
if (typeof onAfterClose === 'function') {
onAfterClose()
}
instance._destroy()
})
}
export {
close as closePopup,
close as closeModal,
close as closeToast
}

View File

@@ -0,0 +1,39 @@
import privateProps from '../privateProps.js'
function setButtonsDisabled (instance, buttons, disabled) {
const domCache = privateProps.domCache.get(instance)
buttons.forEach(button => {
domCache[button].disabled = disabled
})
}
function setInputDisabled (input, disabled) {
if (!input) {
return false
}
if (input.type === 'radio') {
const radiosContainer = input.parentNode.parentNode
const radios = radiosContainer.querySelectorAll('input')
for (let i = 0; i < radios.length; i++) {
radios[i].disabled = disabled
}
} else {
input.disabled = disabled
}
}
export function enableButtons () {
setButtonsDisabled(this, ['confirmButton', 'cancelButton'], false)
}
export function disableButtons () {
setButtonsDisabled(this, ['confirmButton', 'cancelButton'], true)
}
export function enableInput () {
return setInputDisabled(this.getInput(), false)
}
export function disableInput () {
return setInputDisabled(this.getInput(), true)
}

View File

@@ -0,0 +1,12 @@
import * as dom from '../utils/dom/index.js'
import privateProps from '../privateProps.js'
// Get input element by specified type or, if type isn't specified, by params.input
export function getInput (instance) {
const innerParams = privateProps.innerParams.get(instance || this)
const domCache = privateProps.domCache.get(instance || this)
if (!domCache) {
return null
}
return dom.getInput(domCache.content, innerParams.input)
}

View File

@@ -0,0 +1,31 @@
import * as dom from '../utils/dom/index.js'
import { swalClasses } from '../utils/classes.js'
import privateProps from '../privateProps.js'
/**
* Enables buttons and hide loader.
*/
function hideLoading () {
// do nothing if popup is closed
const innerParams = privateProps.innerParams.get(this)
if (!innerParams) {
return
}
const domCache = privateProps.domCache.get(this)
if (!innerParams.showConfirmButton) {
dom.hide(domCache.confirmButton)
if (!innerParams.showCancelButton) {
dom.hide(domCache.actions)
}
}
dom.removeClass([domCache.popup, domCache.actions], swalClasses.loading)
domCache.popup.removeAttribute('aria-busy')
domCache.popup.removeAttribute('data-loading')
domCache.confirmButton.disabled = false
domCache.cancelButton.disabled = false
}
export {
hideLoading,
hideLoading as disableLoading
}

View File

@@ -0,0 +1,135 @@
import * as dom from '../utils/dom/index.js'
import { DismissReason } from '../utils/DismissReason.js'
import { callIfFunction } from '../utils/utils.js'
import { clickConfirm } from '../staticMethods/dom.js'
import privateProps from '../privateProps.js'
export const addKeydownHandler = (instance, globalState, innerParams, dismissWith) => {
if (globalState.keydownTarget && globalState.keydownHandlerAdded) {
globalState.keydownTarget.removeEventListener('keydown', globalState.keydownHandler, { capture: globalState.keydownListenerCapture })
globalState.keydownHandlerAdded = false
}
if (!innerParams.toast) {
globalState.keydownHandler = (e) => keydownHandler(instance, e, dismissWith)
globalState.keydownTarget = innerParams.keydownListenerCapture ? window : dom.getPopup()
globalState.keydownListenerCapture = innerParams.keydownListenerCapture
globalState.keydownTarget.addEventListener('keydown', globalState.keydownHandler, { capture: globalState.keydownListenerCapture })
globalState.keydownHandlerAdded = true
}
}
// Focus handling
export const setFocus = (innerParams, index, increment) => {
const focusableElements = dom.getFocusableElements()
// search for visible elements and select the next possible match
for (let i = 0; i < focusableElements.length; i++) {
index = index + increment
// rollover to first item
if (index === focusableElements.length) {
index = 0
// go to last item
} else if (index === -1) {
index = focusableElements.length - 1
}
return focusableElements[index].focus()
}
// no visible focusable elements, focus the popup
dom.getPopup().focus()
}
const arrowKeys = [
'ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown',
'Left', 'Right', 'Up', 'Down' // IE11
]
const escKeys = [
'Escape',
'Esc' // IE11
]
const keydownHandler = (instance, e, dismissWith) => {
const innerParams = privateProps.innerParams.get(instance)
if (innerParams.stopKeydownPropagation) {
e.stopPropagation()
}
// ENTER
if (e.key === 'Enter') {
handleEnter(instance, e, innerParams)
// TAB
} else if (e.key === 'Tab') {
handleTab(e, innerParams)
// ARROWS - switch focus between buttons
} else if (arrowKeys.includes(e.key)) {
handleArrows()
// ESC
} else if (escKeys.includes(e.key)) {
handleEsc(e, innerParams, dismissWith)
}
}
const handleEnter = (instance, e, innerParams) => {
// #720 #721
if (e.isComposing) {
return
}
if (e.target && instance.getInput() && e.target.outerHTML === instance.getInput().outerHTML) {
if (['textarea', 'file'].includes(innerParams.input)) {
return // do not submit
}
clickConfirm()
e.preventDefault()
}
}
const handleTab = (e, innerParams) => {
const targetElement = e.target
const focusableElements = dom.getFocusableElements()
let btnIndex = -1
for (let i = 0; i < focusableElements.length; i++) {
if (targetElement === focusableElements[i]) {
btnIndex = i
break
}
}
if (!e.shiftKey) {
// Cycle to the next button
setFocus(innerParams, btnIndex, 1)
} else {
// Cycle to the prev button
setFocus(innerParams, btnIndex, -1)
}
e.stopPropagation()
e.preventDefault()
}
const handleArrows = () => {
const confirmButton = dom.getConfirmButton()
const cancelButton = dom.getCancelButton()
// focus Cancel button if Confirm button is currently focused
if (document.activeElement === confirmButton && dom.isVisible(cancelButton)) {
cancelButton.focus()
// and vice versa
} else if (document.activeElement === cancelButton && dom.isVisible(confirmButton)) {
confirmButton.focus()
}
}
const handleEsc = (e, innerParams, dismissWith) => {
if (callIfFunction(innerParams.allowEscapeKey)) {
e.preventDefault()
dismissWith(DismissReason.esc)
}
}

View File

@@ -0,0 +1,75 @@
import { callIfFunction } from '../utils/utils.js'
import { DismissReason } from '../utils/DismissReason.js'
import privateProps from '../privateProps.js'
export const handlePopupClick = (instance, domCache, dismissWith) => {
const innerParams = privateProps.innerParams.get(instance)
if (innerParams.toast) {
handleToastClick(instance, domCache, dismissWith)
} else {
// Ignore click events that had mousedown on the popup but mouseup on the container
// This can happen when the user drags a slider
handleModalMousedown(domCache)
// Ignore click events that had mousedown on the container but mouseup on the popup
handleContainerMousedown(domCache)
handleModalClick(instance, domCache, dismissWith)
}
}
const handleToastClick = (instance, domCache, dismissWith) => {
// Closing toast by internal click
domCache.popup.onclick = () => {
const innerParams = privateProps.innerParams.get(instance)
if (
innerParams.showConfirmButton ||
innerParams.showCancelButton ||
innerParams.showCloseButton ||
innerParams.input
) {
return
}
dismissWith(DismissReason.close)
}
}
let ignoreOutsideClick = false
const handleModalMousedown = (domCache) => {
domCache.popup.onmousedown = () => {
domCache.container.onmouseup = function (e) {
domCache.container.onmouseup = undefined
// We only check if the mouseup target is the container because usually it doesn't
// have any other direct children aside of the popup
if (e.target === domCache.container) {
ignoreOutsideClick = true
}
}
}
}
const handleContainerMousedown = (domCache) => {
domCache.container.onmousedown = () => {
domCache.popup.onmouseup = function (e) {
domCache.popup.onmouseup = undefined
// We also need to check if the mouseup target is a child of the popup
if (e.target === domCache.popup || domCache.popup.contains(e.target)) {
ignoreOutsideClick = true
}
}
}
}
const handleModalClick = (instance, domCache, dismissWith) => {
domCache.container.onclick = (e) => {
const innerParams = privateProps.innerParams.get(instance)
if (ignoreOutsideClick) {
ignoreOutsideClick = false
return
}
if (e.target === domCache.container && callIfFunction(innerParams.allowOutsideClick)) {
dismissWith(DismissReason.backdrop)
}
}
}

View File

@@ -0,0 +1,6 @@
import privateProps from '../privateProps.js'
export function getProgressSteps () {
const domCache = privateProps.domCache.get(this)
return domCache.progressSteps
}

View File

@@ -0,0 +1,36 @@
import * as dom from '../utils/dom/index.js'
import { swalClasses } from '../utils/classes.js'
import privateProps from '../privateProps.js'
// Show block with validation message
export function showValidationMessage (error) {
const domCache = privateProps.domCache.get(this)
dom.setInnerHtml(domCache.validationMessage, error)
const popupComputedStyle = window.getComputedStyle(domCache.popup)
domCache.validationMessage.style.marginLeft = `-${popupComputedStyle.getPropertyValue('padding-left')}`
domCache.validationMessage.style.marginRight = `-${popupComputedStyle.getPropertyValue('padding-right')}`
dom.show(domCache.validationMessage)
const input = this.getInput()
if (input) {
input.setAttribute('aria-invalid', true)
input.setAttribute('aria-describedBy', swalClasses['validation-message'])
dom.focusInput(input)
dom.addClass(input, swalClasses.inputerror)
}
}
// Hide block with validation message
export function resetValidationMessage () {
const domCache = privateProps.domCache.get(this)
if (domCache.validationMessage) {
dom.hide(domCache.validationMessage)
}
const input = this.getInput()
if (input) {
input.removeAttribute('aria-invalid')
input.removeAttribute('aria-describedBy')
dom.removeClass(input, swalClasses.inputerror)
}
}

View File

@@ -0,0 +1,40 @@
import * as dom from '../../src/utils/dom/index.js'
import { warn } from '../../src/utils/utils.js'
import sweetAlert from '../sweetalert2.js'
import privateProps from '../privateProps.js'
/**
* Updates popup parameters.
*/
export function update (params) {
const popup = dom.getPopup()
const innerParams = privateProps.innerParams.get(this)
if (!popup || dom.hasClass(popup, innerParams.hideClass.popup)) {
return warn(`You're trying to update the closed or closing popup, that won't work. Use the update() method in preConfirm parameter or show a new popup.`)
}
const validUpdatableParams = {}
// assign valid params from `params` to `defaults`
Object.keys(params).forEach(param => {
if (sweetAlert.isUpdatableParameter(param)) {
validUpdatableParams[param] = params[param]
} else {
warn(`Invalid parameter to update: "${param}". Updatable params are listed here: https://github.com/sweetalert2/sweetalert2/blob/master/src/utils/params.js`)
}
})
const updatedParams = Object.assign({}, innerParams, validUpdatableParams)
dom.render(this, updatedParams)
privateProps.innerParams.set(this, updatedParams)
Object.defineProperties(this, {
params: {
value: Object.assign({}, this.params, params),
writable: false,
enumerable: true
}
})
}

View File

@@ -0,0 +1,13 @@
/**
* This module containts `WeakMap`s for each effectively-"private property" that a `Swal` has.
* For example, to set the private property "foo" of `this` to "bar", you can `privateProps.foo.set(this, 'bar')`
* This is the approach that Babel will probably take to implement private methods/fields
* https://github.com/tc39/proposal-private-methods
* https://github.com/babel/babel/pull/7555
* Once we have the changes from that PR in Babel, and our core class fits reasonable in *one module*
* then we can use that language feature.
*/
export default {
swalPromiseResolve: new WeakMap()
}

View File

@@ -0,0 +1,15 @@
/**
* This module containts `WeakMap`s for each effectively-"private property" that a `Swal` has.
* For example, to set the private property "foo" of `this` to "bar", you can `privateProps.foo.set(this, 'bar')`
* This is the approach that Babel will probably take to implement private methods/fields
* https://github.com/tc39/proposal-private-methods
* https://github.com/babel/babel/pull/7555
* Once we have the changes from that PR in Babel, and our core class fits reasonable in *one module*
* then we can use that language feature.
*/
export default {
promise: new WeakMap(),
innerParams: new WeakMap(),
domCache: new WeakMap()
}

View File

@@ -0,0 +1,158 @@
@import 'toasts-animations';
// Appearance animation
@keyframes swal2-show {
0% {
transform: scale(.7);
}
45% {
transform: scale(1.05);
}
80% {
transform: scale(.95);
}
100% {
transform: scale(1);
}
}
// Disppearance animation
@keyframes swal2-hide {
0% {
transform: scale(1);
opacity: 1;
}
100% {
transform: scale(.5);
opacity: 0;
}
}
// Success icon animations
@keyframes swal2-animate-success-line-tip {
0% {
top: 1.1875em;
left: .0625em;
width: 0;
}
54% {
top: 1.0625em;
left: .125em;
width: 0;
}
70% {
top: 2.1875em;
left: -.375em;
width: 3.125em;
}
84% {
top: 3em;
left: 1.3125em;
width: 1.0625em;
}
100% {
top: 2.8125em;
left: .8125em;
width: 1.5625em;
}
}
@keyframes swal2-animate-success-line-long {
0% {
top: 3.375em;
right: 2.875em;
width: 0;
}
65% {
top: 3.375em;
right: 2.875em;
width: 0;
}
84% {
top: 2.1875em;
right: 0;
width: 3.4375em;
}
100% {
top: 2.375em;
right: .5em;
width: 2.9375em;
}
}
@keyframes swal2-rotate-success-circular-line {
0% {
transform: rotate(-45deg);
}
5% {
transform: rotate(-45deg);
}
12% {
transform: rotate(-405deg);
}
100% {
transform: rotate(-405deg);
}
}
// Error icon animations
@keyframes swal2-animate-error-x-mark {
0% {
margin-top: 1.625em;
transform: scale(.4);
opacity: 0;
}
50% {
margin-top: 1.625em;
transform: scale(.4);
opacity: 0;
}
80% {
margin-top: -.375em;
transform: scale(1.15);
}
100% {
margin-top: 0;
transform: scale(1);
opacity: 1;
}
}
@keyframes swal2-animate-error-icon {
0% {
transform: rotateX(100deg);
opacity: 0;
}
100% {
transform: rotateX(0deg);
opacity: 1;
}
}
@keyframes swal2-rotate-loading {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}

View File

@@ -0,0 +1,100 @@
@import 'toasts-body';
@mixin sweetalert2-body() {
&.swal2-shown {
@include not('.swal2-no-backdrop', '.swal2-toast-shown') {
overflow: hidden; // not overflow-y because of Sarari, #1253
}
}
&.swal2-height-auto {
height: auto !important; // #781 #1107
}
&.swal2-no-backdrop {
.swal2-container {
top: auto;
right: auto;
bottom: auto;
left: auto;
max-width: calc(100% - #{$swal2-container-padding} * 2);
background-color: transparent !important;
& > .swal2-modal {
box-shadow: 0 0 10px $swal2-backdrop;
}
&.swal2-top {
top: 0;
left: 50%;
transform: translateX(-50%);
}
&.swal2-top-start,
&.swal2-top-left {
top: 0;
left: 0;
}
&.swal2-top-end,
&.swal2-top-right {
top: 0;
right: 0;
}
&.swal2-center {
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
&.swal2-center-start,
&.swal2-center-left {
top: 50%;
left: 0;
transform: translateY(-50%);
}
&.swal2-center-end,
&.swal2-center-right {
top: 50%;
right: 0;
transform: translateY(-50%);
}
&.swal2-bottom {
bottom: 0;
left: 50%;
transform: translateX(-50%);
}
&.swal2-bottom-start,
&.swal2-bottom-left {
bottom: 0;
left: 0;
}
&.swal2-bottom-end,
&.swal2-bottom-right {
right: 0;
bottom: 0;
}
}
}
@media print {
&.swal2-shown {
@include not('.swal2-no-backdrop', '.swal2-toast-shown') {
overflow-y: scroll !important;
> [aria-hidden='true'] {
display: none;
}
.swal2-container {
position: static !important;
}
}
}
}
}

View File

@@ -0,0 +1,782 @@
.swal2-container {
// centering
display: flex;
position: fixed;
z-index: 1060;
top: 0;
right: 0;
bottom: 0;
left: 0;
flex-direction: row;
align-items: center;
justify-content: center;
padding: $swal2-container-padding;
overflow-x: hidden;
transition: $swal2-backdrop-transition;
// sweetalert2/issues/905
-webkit-overflow-scrolling: touch;
&.swal2-backdrop-show,
&.swal2-noanimation {
background: $swal2-backdrop;
}
&.swal2-backdrop-hide {
background: transparent !important;
}
&.swal2-top {
align-items: flex-start;
}
&.swal2-top-start,
&.swal2-top-left {
align-items: flex-start;
justify-content: flex-start;
}
&.swal2-top-end,
&.swal2-top-right {
align-items: flex-start;
justify-content: flex-end;
}
&.swal2-center {
align-items: center;
}
&.swal2-center-start,
&.swal2-center-left {
align-items: center;
justify-content: flex-start;
}
&.swal2-center-end,
&.swal2-center-right {
align-items: center;
justify-content: flex-end;
}
&.swal2-bottom {
align-items: flex-end;
}
&.swal2-bottom-start,
&.swal2-bottom-left {
align-items: flex-end;
justify-content: flex-start;
}
&.swal2-bottom-end,
&.swal2-bottom-right {
align-items: flex-end;
justify-content: flex-end;
}
&.swal2-bottom > :first-child,
&.swal2-bottom-start > :first-child,
&.swal2-bottom-left > :first-child,
&.swal2-bottom-end > :first-child,
&.swal2-bottom-right > :first-child {
margin-top: auto;
}
&.swal2-grow-fullscreen > .swal2-modal {
display: flex !important;
flex: 1;
align-self: stretch;
justify-content: center;
}
&.swal2-grow-row > .swal2-modal {
display: flex !important;
flex: 1;
align-content: center;
justify-content: center;
}
&.swal2-grow-column {
flex: 1;
flex-direction: column;
&.swal2-top,
&.swal2-center,
&.swal2-bottom {
align-items: center;
}
&.swal2-top-start,
&.swal2-center-start,
&.swal2-bottom-start,
&.swal2-top-left,
&.swal2-center-left,
&.swal2-bottom-left {
align-items: flex-start;
}
&.swal2-top-end,
&.swal2-center-end,
&.swal2-bottom-end,
&.swal2-top-right,
&.swal2-center-right,
&.swal2-bottom-right {
align-items: flex-end;
}
& > .swal2-modal {
display: flex !important;
flex: 1;
align-content: center;
justify-content: center;
}
}
&.swal2-no-transition {
transition: none !important;
}
@include not('.swal2-top',
'.swal2-top-start',
'.swal2-top-end',
'.swal2-top-left',
'.swal2-top-right',
'.swal2-center-start',
'.swal2-center-end',
'.swal2-center-left',
'.swal2-center-right',
'.swal2-bottom',
'.swal2-bottom-start',
'.swal2-bottom-end',
'.swal2-bottom-left',
'.swal2-bottom-right',
'.swal2-grow-fullscreen') {
& > .swal2-modal {
margin: auto;
}
}
@include ie {
.swal2-modal {
margin: 0 !important;
}
}
}
.swal2-popup {
display: none;
position: relative;
box-sizing: border-box;
flex-direction: column;
justify-content: center;
width: $swal2-width;
max-width: 100%;
padding: $swal2-padding;
border: $swal2-border;
border-radius: $swal2-border-radius;
background: $swal2-background;
font-family: $swal2-font;
font-size: $swal2-font-size;
&:focus {
outline: none;
}
&.swal2-loading {
overflow-y: hidden;
}
}
.swal2-header {
display: flex;
flex-direction: column;
align-items: center;
}
.swal2-title {
position: relative;
max-width: 100%;
margin: $swal2-title-margin;
padding: 0;
color: $swal2-title-color;
font-size: $swal2-title-font-size;
font-weight: 600;
text-align: center;
text-transform: none;
word-wrap: break-word;
}
.swal2-actions {
display: flex;
z-index: 1; // prevent sucess icon from overlapping buttons
flex-wrap: $swal2-actions-flex-wrap;
align-items: $swal2-actions-align-items;
justify-content: $swal2-actions-justify-content;
width: $swal2-actions-width;
margin: $swal2-actions-margin;
&:not(.swal2-loading) {
.swal2-styled {
&[disabled] {
opacity: .4;
}
&:hover {
background-image: linear-gradient($swal2-button-darken-hover, $swal2-button-darken-hover);
}
&:active {
background-image: linear-gradient($swal2-button-darken-active, $swal2-button-darken-active);
}
}
}
&.swal2-loading {
.swal2-styled {
&.swal2-confirm {
box-sizing: border-box;
width: 2.5em;
height: 2.5em;
margin: .46875em;
padding: 0;
animation: swal2-rotate-loading 1.5s linear 0s infinite normal;
border: .25em solid transparent;
border-radius: 100%;
border-color: transparent;
background-color: transparent !important;
color: transparent;
cursor: default;
user-select: none;
}
&.swal2-cancel {
margin-right: 30px;
margin-left: 30px;
}
}
:not(.swal2-styled) {
&.swal2-confirm {
&::after {
content: '';
display: inline-block;
width: 15px;
height: 15px;
margin-left: 5px;
animation: swal2-rotate-loading 1.5s linear 0s infinite normal;
border: 3px solid lighten($swal2-black, 60);
border-radius: 50%;
border-right-color: transparent;
box-shadow: 1px 1px 1px $swal2-white;
}
}
}
}
}
.swal2-styled {
margin: .3125em;
padding: .625em 2em;
box-shadow: none;
font-weight: 500;
&:not([disabled]) {
cursor: pointer;
}
&.swal2-confirm {
border: $swal2-confirm-button-border;
border-radius: $swal2-confirm-button-border-radius;
background: initial;
background-color: $swal2-confirm-button-background-color;
color: $swal2-confirm-button-color;
font-size: $swal2-confirm-button-font-size;
}
&.swal2-cancel {
border: $swal2-cancel-button-border;
border-radius: $swal2-cancel-button-border-radius;
background: initial;
background-color: $swal2-cancel-button-background-color;
color: $swal2-cancel-button-color;
font-size: $swal2-cancel-button-font-size;
}
&:focus {
outline: $swal2-button-focus-outline;
background-color: $swal2-button-focus-background-color;
box-shadow: $swal2-button-focus-box-shadow;
}
&::-moz-focus-inner {
border: 0;
}
}
.swal2-footer {
justify-content: center;
margin: $swal2-footer-margin;
padding: $swal2-footer-padding;
border-top: 1px solid $swal2-footer-border-color;
color: $swal2-footer-color;
font-size: $swal2-footer-font-size;
}
.swal2-timer-progress-bar-container {
position: absolute;
right: 0;
bottom: 0;
left: 0;
height: $swal2-timer-progress-bar-height;
overflow: hidden;
border-bottom-right-radius: $swal2-border-radius;
border-bottom-left-radius: $swal2-border-radius;
}
.swal2-timer-progress-bar {
width: 100%;
height: $swal2-timer-progress-bar-height;
background: $swal2-timer-progress-bar-background;
}
.swal2-image {
max-width: 100%;
margin: $swal2-image-margin;
}
.swal2-close {
position: $swal2-close-button-position;
z-index: 2; // sweetalert2/issues/1617
top: $swal2-close-button-gap;
right: $swal2-close-button-gap;
align-items: $swal2-close-button-align-items;
justify-content: $swal2-close-button-justify-content;
width: $swal2-close-button-width;
height: $swal2-close-button-height;
padding: 0;
overflow: hidden;
transition: $swal2-close-button-transition;
border: $swal2-close-button-border;
border-radius: $swal2-close-button-border-radius;
outline: $swal2-close-button-outline;
background: $swal2-close-button-background;
color: $swal2-close-button-color;
font-family: $swal2-close-button-font-family;
font-size: $swal2-close-button-font-size;
line-height: $swal2-close-button-line-height;
cursor: pointer;
&:hover {
transform: $swal2-close-button-hover-transform;
background: $swal2-close-button-hover-background;
color: $swal2-close-button-hover-color;
}
&::-moz-focus-inner {
border: 0;
}
}
.swal2-content {
z-index: 1; // prevent sucess icon overlapping the content
justify-content: $swal2-content-justify-content;
margin: $swal2-content-margin;
padding: $swal2-content-pading;
color: $swal2-content-color;
font-size: $swal2-content-font-size;
font-weight: $swal2-content-font-weight;
line-height: $swal2-content-line-height;
text-align: $swal2-content-text-align;
word-wrap: $swal2-content-word-wrap;
}
.swal2-input,
.swal2-file,
.swal2-textarea,
.swal2-select,
.swal2-radio,
.swal2-checkbox {
margin: $swal2-input-margin;
}
.swal2-input,
.swal2-file,
.swal2-textarea {
box-sizing: border-box;
width: $swal2-input-width;
transition: $swal2-input-transition;
border: $swal2-input-border;
border-radius: $swal2-input-border-radius;
background: $swal2-input-background;
box-shadow: $swal2-input-box-shadow;
color: $swal2-input-color;
font-size: $swal2-input-font-size;
&.swal2-inputerror {
border-color: $swal2-error !important;
box-shadow: 0 0 2px $swal2-error !important;
}
&:focus {
border: $swal2-input-focus-border;
outline: $swal2-input-focus-outline;
box-shadow: $swal2-input-focus-box-shadow;
}
&::placeholder {
color: lighten($swal2-black, 80);
}
}
.swal2-range {
margin: $swal2-input-margin;
background: $swal2-background;
input {
width: 80%;
}
output {
width: 20%;
color: $swal2-input-color;
font-weight: 600;
text-align: center;
}
input,
output {
height: $swal2-input-height;
padding: 0;
font-size: $swal2-input-font-size;
line-height: $swal2-input-height;
}
}
.swal2-input {
height: $swal2-input-height;
padding: $swal2-input-padding;
&[type='number'] {
max-width: 10em;
}
}
.swal2-file {
background: $swal2-input-background;
font-size: $swal2-input-font-size;
}
.swal2-textarea {
height: $swal2-textarea-height;
padding: $swal2-textarea-padding;
}
.swal2-select {
min-width: 50%;
max-width: 100%;
padding: .375em .625em;
background: $swal2-input-background;
color: $swal2-input-color;
font-size: $swal2-input-font-size;
}
.swal2-radio,
.swal2-checkbox {
align-items: center;
justify-content: center;
background: $swal2-background;
color: $swal2-input-color;
label {
margin: 0 .6em;
font-size: $swal2-input-font-size;
}
input {
margin: 0 .4em;
}
}
.swal2-validation-message {
display: none;
align-items: center;
justify-content: $swal2-validation-message-justify-content;
padding: $swal2-validation-message-padding;
overflow: hidden;
background: $swal2-validation-message-background;
color: $swal2-validation-message-color;
font-size: $swal2-validation-message-font-size;
font-weight: $swal2-validation-message-font-weight;
&::before {
content: '!';
display: inline-block;
width: 1.5em;
min-width: 1.5em;
height: 1.5em;
margin: 0 .625em;
zoom: $swal2-validation-message-icon-zoom;
border-radius: 50%;
background-color: $swal2-validation-message-icon-background;
color: $swal2-validation-message-icon-color;
font-weight: 600;
line-height: 1.5em;
text-align: center;
}
}
.swal2-icon {
position: relative;
box-sizing: content-box;
justify-content: center;
width: $swal2-icon-size;
height: $swal2-icon-size;
margin: $swal2-icon-margin;
zoom: $swal2-icon-zoom;
border: .25em solid transparent;
border-radius: 50%;
font-family: $swal2-icon-font-family;
line-height: $swal2-icon-size;
cursor: default;
user-select: none;
.swal2-icon-content {
display: flex;
align-items: center;
font-size: 3.75em;
}
&.swal2-error {
border-color: $swal2-error;
color: $swal2-error;
.swal2-x-mark {
position: relative;
flex-grow: 1;
}
[class^='swal2-x-mark-line'] {
display: block;
position: absolute;
top: 2.3125em;
width: 2.9375em;
height: .3125em;
border-radius: .125em;
background-color: $swal2-error;
&[class$='left'] {
left: 1.0625em;
transform: rotate(45deg);
}
&[class$='right'] {
right: 1em;
transform: rotate(-45deg);
}
}
// Error icon animation
&.swal2-icon-show {
@if $swal2-icon-animations {
animation: swal2-animate-error-icon .5s;
.swal2-x-mark {
animation: swal2-animate-error-x-mark .5s;
}
}
}
}
&.swal2-warning {
border-color: lighten($swal2-warning, 7);
color: $swal2-warning;
}
&.swal2-info {
border-color: lighten($swal2-info, 20);
color: $swal2-info;
}
&.swal2-question {
border-color: lighten($swal2-question, 20);
color: $swal2-question;
}
&.swal2-success {
border-color: $swal2-success;
color: $swal2-success;
[class^='swal2-success-circular-line'] {
// Emulate moving circular line
position: absolute;
width: 3.75em;
height: 7.5em;
transform: rotate(45deg);
border-radius: 50%;
&[class$='left'] {
top: -.4375em;
left: -2.0635em;
transform: rotate(-45deg);
transform-origin: 3.75em 3.75em;
border-radius: 7.5em 0 0 7.5em;
}
&[class$='right'] {
top: -.6875em;
left: 1.875em;
transform: rotate(-45deg);
transform-origin: 0 3.75em;
border-radius: 0 7.5em 7.5em 0;
}
}
.swal2-success-ring {
// Ring
position: absolute;
z-index: 2;
top: -.25em;
left: -.25em;
box-sizing: content-box;
width: 100%;
height: 100%;
border: .25em solid $swal2-success-border;
border-radius: 50%;
}
.swal2-success-fix {
// Hide corners left from animation
position: absolute;
z-index: 1;
top: .5em;
left: 1.625em;
width: .4375em;
height: 5.625em;
transform: rotate(-45deg);
}
[class^='swal2-success-line'] {
display: block;
position: absolute;
z-index: 2;
height: .3125em;
border-radius: .125em;
background-color: $swal2-success;
&[class$='tip'] {
top: 2.875em;
left: .8125em;
width: 1.5625em;
transform: rotate(45deg);
}
&[class$='long'] {
top: 2.375em;
right: .5em;
width: 2.9375em;
transform: rotate(-45deg);
}
}
// Success icon animation
&.swal2-icon-show {
@if $swal2-icon-animations {
.swal2-success-line-tip {
animation: swal2-animate-success-line-tip .75s;
}
.swal2-success-line-long {
animation: swal2-animate-success-line-long .75s;
}
.swal2-success-circular-line-right {
animation: swal2-rotate-success-circular-line 4.25s ease-in;
}
}
}
}
}
.swal2-progress-steps {
align-items: center;
margin: $swal2-progress-steps-margin;
padding: $swal2-progress-steps-padding;
background: $swal2-progress-steps-background;
font-weight: $swal2-progress-steps-font-weight;
li {
display: inline-block;
position: relative;
}
.swal2-progress-step {
z-index: 20;
width: $swal2-progress-step-width;
height: $swal2-progress-step-height;
border-radius: $swal2-progress-step-border-radius;
background: $swal2-active-step-background;
color: $swal2-active-step-color;
line-height: $swal2-progress-step-height;
text-align: center;
&.swal2-active-progress-step {
background: $swal2-active-step-background;
~ .swal2-progress-step {
background: $swal2-progress-step-background;
color: $swal2-progress-step-color;
}
~ .swal2-progress-step-line {
background: $swal2-progress-step-background;
}
}
}
.swal2-progress-step-line {
z-index: 10;
width: $swal2-progress-steps-distance;
height: .4em;
margin: 0 -1px;
background: $swal2-active-step-background;
}
}
// github.com/sweetalert2/sweetalert2/issues/268
[class^='swal2'] {
-webkit-tap-highlight-color: transparent;
}
.swal2-show {
animation: $swal2-show-animation;
}
.swal2-hide {
animation: $swal2-hide-animation;
}
.swal2-noanimation {
transition: none;
}
// Measure scrollbar width for padding body during modal show/hide
.swal2-scrollbar-measure {
position: absolute;
top: -9999px;
width: 50px;
height: 50px;
overflow: scroll;
}
// Right-to-left support
.swal2-rtl {
.swal2-close {
right: auto;
left: $swal2-close-button-gap;
}
.swal2-timer-progress-bar {
right: 0;
left: auto;
}
}

View File

@@ -0,0 +1,22 @@
@mixin ie {
@media all and (-ms-high-contrast: none), (-ms-high-contrast: active) {
@content;
}
}
// https://stackoverflow.com/a/30250161
@mixin not($ignor-list...) {
@if (length($ignor-list) == 1) {
$ignor-list: nth($ignor-list, 1);
}
$not-output: '';
@each $not in $ignor-list {
$not-output: $not-output + ':not(#{$not})'; // stylelint-disable-line scss/no-duplicate-dollar-variables
}
&#{$not-output} {
@content;
}
}

View File

@@ -0,0 +1,37 @@
@import '../variables';
// Microsoft Edge
@supports (-ms-accelerator: true) {
.swal2-range {
input {
width: 100% !important;
}
output {
display: none;
}
}
}
// IE11
@media all and (-ms-high-contrast: none),
(-ms-high-contrast: active) {
.swal2-range {
input {
width: 100% !important;
}
output {
display: none;
}
}
}
// Firefox
@-moz-document url-prefix() {
.swal2-close {
&:focus {
outline: 2px solid $swal2-outline-color;
}
}
}

View File

@@ -0,0 +1,8 @@
// base file for including when performing theming
// doesn't include at-rules or root selectors (like body) which allows for more comprehensive extending
@import '../variables';
@import 'mixins';
@import 'toasts';
@import 'body';
@import 'core';

View File

@@ -0,0 +1,83 @@
// Animations
@keyframes swal2-toast-show {
0% {
transform: translateY(-.625em) rotateZ(2deg);
}
33% {
transform: translateY(0) rotateZ(-2deg);
}
66% {
transform: translateY(.3125em) rotateZ(2deg);
}
100% {
transform: translateY(0) rotateZ(0deg);
}
}
@keyframes swal2-toast-hide {
100% {
transform: rotateZ(1deg);
opacity: 0;
}
}
@keyframes swal2-toast-animate-success-line-tip {
0% {
top: .5625em;
left: .0625em;
width: 0;
}
54% {
top: .125em;
left: .125em;
width: 0;
}
70% {
top: .625em;
left: -.25em;
width: 1.625em;
}
84% {
top: 1.0625em;
left: .75em;
width: .5em;
}
100% {
top: 1.125em;
left: .1875em;
width: .75em;
}
}
@keyframes swal2-toast-animate-success-line-long {
0% {
top: 1.625em;
right: 1.375em;
width: 0;
}
65% {
top: 1.25em;
right: .9375em;
width: 0;
}
84% {
top: .9375em;
right: 0;
width: 1.125em;
}
100% {
top: .9375em;
right: .1875em;
width: 1.375em;
}
}

View File

@@ -0,0 +1,109 @@
@mixin sweetalert2-toasts-body() {
&.swal2-toast-shown {
.swal2-container {
background-color: transparent;
&.swal2-top {
top: 0;
right: auto;
bottom: auto;
left: 50%;
transform: translateX(-50%);
}
&.swal2-top-end,
&.swal2-top-right {
top: 0;
right: 0;
bottom: auto;
left: auto;
}
&.swal2-top-start,
&.swal2-top-left {
top: 0;
right: auto;
bottom: auto;
left: 0;
}
&.swal2-center-start,
&.swal2-center-left {
top: 50%;
right: auto;
bottom: auto;
left: 0;
transform: translateY(-50%);
}
&.swal2-center {
top: 50%;
right: auto;
bottom: auto;
left: 50%;
transform: translate(-50%, -50%);
}
&.swal2-center-end,
&.swal2-center-right {
top: 50%;
right: 0;
bottom: auto;
left: auto;
transform: translateY(-50%);
}
&.swal2-bottom-start,
&.swal2-bottom-left {
top: auto;
right: auto;
bottom: 0;
left: 0;
}
&.swal2-bottom {
top: auto;
right: auto;
bottom: 0;
left: 50%;
transform: translateX(-50%);
}
&.swal2-bottom-end,
&.swal2-bottom-right {
top: auto;
right: 0;
bottom: 0;
left: auto;
}
}
}
&.swal2-toast-column {
.swal2-toast {
flex-direction: column;
align-items: stretch;
.swal2-actions {
flex: 1;
align-self: stretch;
height: 2.2em;
margin-top: .3125em;
}
.swal2-loading {
justify-content: center;
}
.swal2-input {
height: 2em;
margin: .3125em auto;
font-size: $swal2-toast-input-font-size;
}
.swal2-validation-message {
font-size: $swal2-toast-validation-font-size;
}
}
}
}

View File

@@ -0,0 +1,172 @@
.swal2-popup {
&.swal2-toast {
flex-direction: row;
align-items: center;
width: $swal2-toast-width;
padding: $swal2-toast-padding;
overflow-y: hidden;
background: $swal2-toast-background;
box-shadow: $swal2-toast-box-shadow;
.swal2-header {
flex-direction: row;
}
.swal2-title {
flex-grow: 1;
justify-content: flex-start;
margin: $swal2-toast-title-margin;
font-size: $swal2-toast-title-font-size;
}
.swal2-footer {
margin: $swal2-toast-footer-margin;
padding: $swal2-toast-footer-margin;
font-size: $swal2-toast-footer-font-size;
}
.swal2-close {
position: static;
width: $swal2-toast-close-button-width;
height: $swal2-toast-close-button-height;
line-height: $swal2-toast-close-button-line-height;
}
.swal2-content {
justify-content: flex-start;
font-size: $swal2-toast-content-font-size;
}
.swal2-icon {
width: 2em;
min-width: 2em;
height: 2em;
margin: 0;
.swal2-icon-content {
display: flex;
align-items: center;
font-size: 1.8em;
font-weight: bold;
@include ie {
font-size: .25em;
}
}
&.swal2-success {
.swal2-success-ring {
width: 2em;
height: 2em;
}
}
&.swal2-error {
[class^='swal2-x-mark-line'] {
top: .875em;
width: 1.375em;
&[class$='left'] {
left: .3125em;
}
&[class$='right'] {
right: .3125em;
}
}
}
}
.swal2-actions {
flex-basis: auto !important;
width: auto;
height: auto;
margin: 0 .3125em;
}
.swal2-styled {
margin: 0 .3125em;
padding: .3125em .625em;
font-size: $swal2-toast-buttons-font-size;
&:focus {
box-shadow: $swal2-toast-button-focus-box-shadow;
}
}
.swal2-success {
border-color: $swal2-success;
[class^='swal2-success-circular-line'] { // Emulate moving circular line
position: absolute;
width: 1.6em;
height: 3em;
transform: rotate(45deg);
border-radius: 50%;
&[class$='left'] {
top: -.8em;
left: -.5em;
transform: rotate(-45deg);
transform-origin: 2em 2em;
border-radius: 4em 0 0 4em;
}
&[class$='right'] {
top: -.25em;
left: .9375em;
transform-origin: 0 1.5em;
border-radius: 0 4em 4em 0;
}
}
.swal2-success-ring {
width: 2em;
height: 2em;
}
.swal2-success-fix {
top: 0;
left: .4375em;
width: .4375em;
height: 2.6875em;
}
[class^='swal2-success-line'] {
height: .3125em;
&[class$='tip'] {
top: 1.125em;
left: .1875em;
width: .75em;
}
&[class$='long'] {
top: .9375em;
right: .1875em;
width: 1.375em;
}
}
&.swal2-icon-show {
@if $swal2-icon-animations {
.swal2-success-line-tip {
animation: swal2-toast-animate-success-line-tip .75s;
}
.swal2-success-line-long {
animation: swal2-toast-animate-success-line-long .75s;
}
}
}
}
&.swal2-show {
animation: $swal2-toast-show-animation;
}
&.swal2-hide {
animation: $swal2-toast-hide-animation;
}
}
}

View File

@@ -0,0 +1,12 @@
export * from './staticMethods/argsToParams.js'
export * from './staticMethods/dom.js'
export * from './staticMethods/fire.js'
export * from './staticMethods/mixin.js'
export * from './staticMethods/queue.js'
export * from './staticMethods/showLoading.js'
export * from './staticMethods/timer.js'
export {
isValidParameter,
isUpdatableParameter,
isDeprecatedParameter
} from './utils/params.js'

View File

@@ -0,0 +1,21 @@
import { error } from '../utils/utils.js'
const isJqueryElement = (elem) => typeof elem === 'object' && elem.jquery
const isElement = (elem) => elem instanceof Element || isJqueryElement(elem)
export const argsToParams = (args) => {
const params = {}
if (typeof args[0] === 'object' && !isElement(args[0])) {
Object.assign(params, args[0])
} else {
['title', 'html', 'icon'].forEach((name, index) => {
const arg = args[index]
if (typeof arg === 'string' || isElement(arg)) {
params[name] = arg
} else if (arg !== undefined) {
error(`Unexpected type of ${name}! Expected "string" or "Element", got ${typeof arg}`)
}
})
}
return params
}

View File

@@ -0,0 +1,40 @@
import * as dom from '../utils/dom/index.js'
import * as domUtils from '../utils/dom/domUtils.js'
export {
getContainer,
getPopup,
getTitle,
getContent,
getHtmlContainer,
getImage,
getIcon,
getIcons,
getCloseButton,
getActions,
getConfirmButton,
getCancelButton,
getHeader,
getFooter,
getTimerProgressBar,
getFocusableElements,
getValidationMessage,
isLoading
} from '../utils/dom/index.js'
/*
* Global function to determine if SweetAlert2 popup is shown
*/
export const isVisible = () => {
return domUtils.isVisible(dom.getPopup())
}
/*
* Global function to click 'Confirm' button
*/
export const clickConfirm = () => dom.getConfirmButton() && dom.getConfirmButton().click()
/*
* Global function to click 'Cancel' button
*/
export const clickCancel = () => dom.getCancelButton() && dom.getCancelButton().click()

View File

@@ -0,0 +1,4 @@
export function fire (...args) {
const Swal = this
return new Swal(...args)
}

View File

@@ -0,0 +1,27 @@
/**
* Returns an extended version of `Swal` containing `params` as defaults.
* Useful for reusing Swal configuration.
*
* For example:
*
* Before:
* const textPromptOptions = { input: 'text', showCancelButton: true }
* const {value: firstName} = await Swal.fire({ ...textPromptOptions, title: 'What is your first name?' })
* const {value: lastName} = await Swal.fire({ ...textPromptOptions, title: 'What is your last name?' })
*
* After:
* const TextPrompt = Swal.mixin({ input: 'text', showCancelButton: true })
* const {value: firstName} = await TextPrompt('What is your first name?')
* const {value: lastName} = await TextPrompt('What is your last name?')
*
* @param mixinParams
*/
export function mixin (mixinParams) {
class MixinSwal extends this {
_main (params) {
return super._main(Object.assign({}, mixinParams, params))
}
}
return MixinSwal
}

View File

@@ -0,0 +1,60 @@
import * as dom from '../utils/dom/index.js'
// private global state for the queue feature
let currentSteps = []
/*
* Global function for chaining sweetAlert popups
*/
export const queue = function (steps) {
const Swal = this
currentSteps = steps
const resetAndResolve = (resolve, value) => {
currentSteps = []
resolve(value)
}
const queueResult = []
return new Promise((resolve) => {
(function step (i, callback) {
if (i < currentSteps.length) {
document.body.setAttribute('data-swal2-queue-step', i)
Swal.fire(currentSteps[i]).then((result) => {
if (typeof result.value !== 'undefined') {
queueResult.push(result.value)
step(i + 1, callback)
} else {
resetAndResolve(resolve, { dismiss: result.dismiss })
}
})
} else {
resetAndResolve(resolve, { value: queueResult })
}
})(0)
})
}
/*
* Global function for getting the index of current popup in queue
*/
export const getQueueStep = () => dom.getContainer() && dom.getContainer().getAttribute('data-queue-step')
/*
* Global function for inserting a popup to the queue
*/
export const insertQueueStep = (step, index) => {
if (index && index < currentSteps.length) {
return currentSteps.splice(index, 0, step)
}
return currentSteps.push(step)
}
/*
* Global function for deleting a popup from the queue
*/
export const deleteQueueStep = (index) => {
if (typeof currentSteps[index] !== 'undefined') {
currentSteps.splice(index, 1)
}
}

View File

@@ -0,0 +1,30 @@
import * as dom from '../utils/dom/index.js'
import Swal from '../sweetalert2.js'
import { swalClasses } from '../utils/classes.js'
/**
* Show spinner instead of Confirm button
*/
const showLoading = () => {
let popup = dom.getPopup()
if (!popup) {
Swal.fire()
}
popup = dom.getPopup()
const actions = dom.getActions()
const confirmButton = dom.getConfirmButton()
dom.show(actions)
dom.show(confirmButton, 'inline-block')
dom.addClass([popup, actions], swalClasses.loading)
confirmButton.disabled = true
popup.setAttribute('data-loading', true)
popup.setAttribute('aria-busy', true)
popup.focus()
}
export {
showLoading,
showLoading as enableLoading
}

View File

@@ -0,0 +1,63 @@
import { animateTimerProgressBar, stopTimerProgressBar } from '../utils/dom/domUtils.js'
import globalState from '../globalState.js'
/**
* If `timer` parameter is set, returns number of milliseconds of timer remained.
* Otherwise, returns undefined.
*/
export const getTimerLeft = () => {
return globalState.timeout && globalState.timeout.getTimerLeft()
}
/**
* Stop timer. Returns number of milliseconds of timer remained.
* If `timer` parameter isn't set, returns undefined.
*/
export const stopTimer = () => {
if (globalState.timeout) {
stopTimerProgressBar()
return globalState.timeout.stop()
}
}
/**
* Resume timer. Returns number of milliseconds of timer remained.
* If `timer` parameter isn't set, returns undefined.
*/
export const resumeTimer = () => {
if (globalState.timeout) {
const remaining = globalState.timeout.start()
animateTimerProgressBar(remaining)
return remaining
}
}
/**
* Resume timer. Returns number of milliseconds of timer remained.
* If `timer` parameter isn't set, returns undefined.
*/
export const toggleTimer = () => {
const timer = globalState.timeout
return timer && (timer.running ? stopTimer() : resumeTimer())
}
/**
* Increase timer. Returns number of milliseconds of an updated timer.
* If `timer` parameter isn't set, returns undefined.
*/
export const increaseTimer = (n) => {
if (globalState.timeout) {
const remaining = globalState.timeout.increase(n)
animateTimerProgressBar(remaining, true)
return remaining
}
}
/**
* Check if timer is running. Returns true if timer is running
* or false if timer is paused or stopped.
* If `timer` parameter isn't set, returns undefined
*/
export const isTimerRunning = () => {
return globalState.timeout && globalState.timeout.isRunning()
}

View File

@@ -0,0 +1,6 @@
import SweetAlert from './SweetAlert.js'
const Swal = SweetAlert
Swal.default = Swal
export default Swal

View File

@@ -0,0 +1,11 @@
// SweetAlert2
// github.com/sweetalert2/sweetalert2
@import 'scss/theming';
@import 'scss/polyfills';
@import 'scss/animations';
body {
@include sweetalert2-body();
@include sweetalert2-toasts-body();
}

View File

@@ -0,0 +1,7 @@
export const DismissReason = Object.freeze({
cancel: 'cancel',
backdrop: 'backdrop',
close: 'close',
esc: 'esc',
timer: 'timer'
})

View File

@@ -0,0 +1,51 @@
export default class Timer {
constructor (callback, delay) {
this.callback = callback
this.remaining = delay
this.running = false
this.start()
}
start () {
if (!this.running) {
this.running = true
this.started = new Date()
this.id = setTimeout(this.callback, this.remaining)
}
return this.remaining
}
stop () {
if (this.running) {
this.running = false
clearTimeout(this.id)
this.remaining -= new Date() - this.started
}
return this.remaining
}
increase (n) {
const running = this.running
if (running) {
this.stop()
}
this.remaining += n
if (running) {
this.start()
}
return this.remaining
}
getTimerLeft () {
if (this.running) {
this.stop()
this.start()
}
return this.remaining
}
isRunning () {
return this.running
}
}

View File

@@ -0,0 +1,34 @@
import { getContainer } from './dom/getters.js'
import { contains } from './dom/domUtils.js'
import { toArray } from './utils.js'
// From https://developer.paciellogroup.com/blog/2018/06/the-current-state-of-modal-dialog-accessibility/
// Adding aria-hidden="true" to elements outside of the active modal dialog ensures that
// elements not within the active modal dialog will not be surfaced if a user opens a screen
// readers list of elements (headings, form controls, landmarks, etc.) in the document.
export const setAriaHidden = () => {
const bodyChildren = toArray(document.body.children)
bodyChildren.forEach(el => {
if (el === getContainer() || contains(el, getContainer())) {
return
}
if (el.hasAttribute('aria-hidden')) {
el.setAttribute('data-previous-aria-hidden', el.getAttribute('aria-hidden'))
}
el.setAttribute('aria-hidden', 'true')
})
}
export const unsetAriaHidden = () => {
const bodyChildren = toArray(document.body.children)
bodyChildren.forEach(el => {
if (el.hasAttribute('data-previous-aria-hidden')) {
el.setAttribute('aria-hidden', el.getAttribute('data-previous-aria-hidden'))
el.removeAttribute('data-previous-aria-hidden')
} else {
el.removeAttribute('aria-hidden')
}
})
}

View File

@@ -0,0 +1,88 @@
export const swalPrefix = 'swal2-'
export const prefix = (items) => {
const result = {}
for (const i in items) {
result[items[i]] = swalPrefix + items[i]
}
return result
}
export const swalClasses = prefix([
'container',
'shown',
'height-auto',
'iosfix',
'popup',
'modal',
'no-backdrop',
'no-transition',
'toast',
'toast-shown',
'toast-column',
'show',
'hide',
'close',
'title',
'header',
'content',
'html-container',
'actions',
'confirm',
'cancel',
'footer',
'icon',
'icon-content',
'image',
'input',
'file',
'range',
'select',
'radio',
'checkbox',
'label',
'textarea',
'inputerror',
'validation-message',
'progress-steps',
'active-progress-step',
'progress-step',
'progress-step-line',
'loading',
'styled',
'top',
'top-start',
'top-end',
'top-left',
'top-right',
'center',
'center-start',
'center-end',
'center-left',
'center-right',
'bottom',
'bottom-start',
'bottom-end',
'bottom-left',
'bottom-right',
'grow-row',
'grow-column',
'grow-fullscreen',
'rtl',
'timer-progress-bar',
'timer-progress-bar-container',
'scrollbar-measure',
'icon-success',
'icon-warning',
'icon-info',
'icon-question',
'icon-error',
])
export const iconTypes = prefix([
'success',
'warning',
'info',
'question',
'error'
])

View File

@@ -0,0 +1,13 @@
export default {
email: (string, validationMessage) => {
return /^[a-zA-Z0-9.+_-]+@[a-zA-Z0-9.-]+\.[a-zA-Z0-9-]{2,24}$/.test(string)
? Promise.resolve()
: Promise.resolve(validationMessage || 'Invalid email address')
},
url: (string, validationMessage) => {
// taken from https://stackoverflow.com/a/3809435 with a small change from #1306
return /^https?:\/\/(www\.)?[-a-zA-Z0-9@:%._+~#=]{2,256}\.[a-z]{2,63}\b([-a-zA-Z0-9@:%_+.~#?&/=]*)$/.test(string)
? Promise.resolve()
: Promise.resolve(validationMessage || 'Invalid URL')
}
}

View File

@@ -0,0 +1,23 @@
import { isNodeEnv } from '../isNodeEnv.js'
export const animationEndEvent = (() => {
// Prevent run in Node env
/* istanbul ignore if */
if (isNodeEnv()) {
return false
}
const testEl = document.createElement('div')
const transEndEventNames = {
WebkitAnimation: 'webkitAnimationEnd',
OAnimation: 'oAnimationEnd oanimationend',
animation: 'animationend'
}
for (const i in transEndEventNames) {
if (Object.prototype.hasOwnProperty.call(transEndEventNames, i) && typeof testEl.style[i] !== 'undefined') {
return transEndEventNames[i]
}
}
return false
})()

View File

@@ -0,0 +1,195 @@
import { getTimerProgressBar } from './getters.js'
import { swalClasses, iconTypes } from '../classes.js'
import { toArray, objectValues, warn } from '../utils.js'
// Remember state in cases where opening and handling a modal will fiddle with it.
export const states = {
previousBodyPadding: null
}
export const setInnerHtml = (elem, html) => { // #1926
elem.textContent = ''
if (html) {
const parser = new DOMParser()
const parsed = parser.parseFromString(html, `text/html`)
toArray(parsed.querySelector('head').childNodes).forEach((child) => {
elem.appendChild(child)
})
toArray(parsed.querySelector('body').childNodes).forEach((child) => {
elem.appendChild(child)
})
}
}
export const hasClass = (elem, className) => {
if (!className) {
return false
}
const classList = className.split(/\s+/)
for (let i = 0; i < classList.length; i++) {
if (!elem.classList.contains(classList[i])) {
return false
}
}
return true
}
const removeCustomClasses = (elem, params) => {
toArray(elem.classList).forEach(className => {
if (
!objectValues(swalClasses).includes(className) &&
!objectValues(iconTypes).includes(className) &&
!objectValues(params.showClass).includes(className)
) {
elem.classList.remove(className)
}
})
}
export const applyCustomClass = (elem, params, className) => {
removeCustomClasses(elem, params)
if (params.customClass && params.customClass[className]) {
if (typeof params.customClass[className] !== 'string' && !params.customClass[className].forEach) {
return warn(`Invalid type of customClass.${className}! Expected string or iterable object, got "${typeof params.customClass[className]}"`)
}
addClass(elem, params.customClass[className])
}
}
export function getInput (content, inputType) {
if (!inputType) {
return null
}
switch (inputType) {
case 'select':
case 'textarea':
case 'file':
return getChildByClass(content, swalClasses[inputType])
case 'checkbox':
return content.querySelector(`.${swalClasses.checkbox} input`)
case 'radio':
return content.querySelector(`.${swalClasses.radio} input:checked`) ||
content.querySelector(`.${swalClasses.radio} input:first-child`)
case 'range':
return content.querySelector(`.${swalClasses.range} input`)
default:
return getChildByClass(content, swalClasses.input)
}
}
export const focusInput = (input) => {
input.focus()
// place cursor at end of text in text input
if (input.type !== 'file') {
// http://stackoverflow.com/a/2345915
const val = input.value
input.value = ''
input.value = val
}
}
export const toggleClass = (target, classList, condition) => {
if (!target || !classList) {
return
}
if (typeof classList === 'string') {
classList = classList.split(/\s+/).filter(Boolean)
}
classList.forEach((className) => {
if (target.forEach) {
target.forEach((elem) => {
condition ? elem.classList.add(className) : elem.classList.remove(className)
})
} else {
condition ? target.classList.add(className) : target.classList.remove(className)
}
})
}
export const addClass = (target, classList) => {
toggleClass(target, classList, true)
}
export const removeClass = (target, classList) => {
toggleClass(target, classList, false)
}
export const getChildByClass = (elem, className) => {
for (let i = 0; i < elem.childNodes.length; i++) {
if (hasClass(elem.childNodes[i], className)) {
return elem.childNodes[i]
}
}
}
export const applyNumericalStyle = (elem, property, value) => {
if (value || parseInt(value) === 0) {
elem.style[property] = (typeof value === 'number') ? `${value}px` : value
} else {
elem.style.removeProperty(property)
}
}
export const show = (elem, display = 'flex') => {
elem.style.opacity = ''
elem.style.display = display
}
export const hide = (elem) => {
elem.style.opacity = ''
elem.style.display = 'none'
}
export const toggle = (elem, condition, display) => {
condition ? show(elem, display) : hide(elem)
}
// borrowed from jquery $(elem).is(':visible') implementation
export const isVisible = (elem) => !!(elem && (elem.offsetWidth || elem.offsetHeight || elem.getClientRects().length))
/* istanbul ignore next */
export const isScrollable = (elem) => !!(elem.scrollHeight > elem.clientHeight)
// borrowed from https://stackoverflow.com/a/46352119
export const hasCssAnimation = (elem) => {
const style = window.getComputedStyle(elem)
const animDuration = parseFloat(style.getPropertyValue('animation-duration') || '0')
const transDuration = parseFloat(style.getPropertyValue('transition-duration') || '0')
return animDuration > 0 || transDuration > 0
}
export const contains = (haystack, needle) => {
if (typeof haystack.contains === 'function') {
return haystack.contains(needle)
}
}
export const animateTimerProgressBar = (timer, reset = false) => {
const timerProgressBar = getTimerProgressBar()
if (isVisible(timerProgressBar)) {
if (reset) {
timerProgressBar.style.transition = 'none'
timerProgressBar.style.width = '100%'
}
setTimeout(() => {
timerProgressBar.style.transition = `width ${timer / 1000}s linear`
timerProgressBar.style.width = '0%'
}, 10)
}
}
export const stopTimerProgressBar = () => {
const timerProgressBar = getTimerProgressBar()
const timerProgressBarWidth = parseInt(window.getComputedStyle(timerProgressBar).width)
timerProgressBar.style.removeProperty('transition')
timerProgressBar.style.width = '100%'
const timerProgressBarFullWidth = parseInt(window.getComputedStyle(timerProgressBar).width)
const timerProgressBarPercent = parseInt(timerProgressBarWidth / timerProgressBarFullWidth * 100)
timerProgressBar.style.removeProperty('transition')
timerProgressBar.style.width = `${timerProgressBarPercent}%`
}

View File

@@ -0,0 +1,105 @@
import { swalClasses } from '../classes.js'
import { uniqueArray, toArray } from '../utils.js'
import { isVisible } from './domUtils.js'
export const getContainer = () => document.body.querySelector(`.${swalClasses.container}`)
export const elementBySelector = (selectorString) => {
const container = getContainer()
return container ? container.querySelector(selectorString) : null
}
const elementByClass = (className) => {
return elementBySelector(`.${className}`)
}
export const getPopup = () => elementByClass(swalClasses.popup)
export const getIcons = () => {
const popup = getPopup()
return toArray(popup.querySelectorAll(`.${swalClasses.icon}`))
}
export const getIcon = () => {
const visibleIcon = getIcons().filter(icon => isVisible(icon))
return visibleIcon.length ? visibleIcon[0] : null
}
export const getTitle = () => elementByClass(swalClasses.title)
export const getContent = () => elementByClass(swalClasses.content)
export const getHtmlContainer = () => elementByClass(swalClasses['html-container'])
export const getImage = () => elementByClass(swalClasses.image)
export const getProgressSteps = () => elementByClass(swalClasses['progress-steps'])
export const getValidationMessage = () => elementByClass(swalClasses['validation-message'])
export const getConfirmButton = () => elementBySelector(`.${swalClasses.actions} .${swalClasses.confirm}`)
export const getCancelButton = () => elementBySelector(`.${swalClasses.actions} .${swalClasses.cancel}`)
export const getActions = () => elementByClass(swalClasses.actions)
export const getHeader = () => elementByClass(swalClasses.header)
export const getFooter = () => elementByClass(swalClasses.footer)
export const getTimerProgressBar = () => elementByClass(swalClasses['timer-progress-bar'])
export const getCloseButton = () => elementByClass(swalClasses.close)
// https://github.com/jkup/focusable/blob/master/index.js
const focusable = `
a[href],
area[href],
input:not([disabled]),
select:not([disabled]),
textarea:not([disabled]),
button:not([disabled]),
iframe,
object,
embed,
[tabindex="0"],
[contenteditable],
audio[controls],
video[controls],
summary
`
export const getFocusableElements = () => {
const focusableElementsWithTabindex = toArray(
getPopup().querySelectorAll('[tabindex]:not([tabindex="-1"]):not([tabindex="0"])')
)
// sort according to tabindex
.sort((a, b) => {
a = parseInt(a.getAttribute('tabindex'))
b = parseInt(b.getAttribute('tabindex'))
if (a > b) {
return 1
} else if (a < b) {
return -1
}
return 0
})
const otherFocusableElements = toArray(
getPopup().querySelectorAll(focusable)
).filter(el => el.getAttribute('tabindex') !== '-1')
return uniqueArray(focusableElementsWithTabindex.concat(otherFocusableElements)).filter(el => isVisible(el))
}
export const isModal = () => {
return !isToast() && !document.body.classList.contains(swalClasses['no-backdrop'])
}
export const isToast = () => {
return document.body.classList.contains(swalClasses['toast-shown'])
}
export const isLoading = () => {
return getPopup().hasAttribute('data-loading')
}

View File

@@ -0,0 +1,7 @@
export * from './domUtils.js'
export * from './init.js'
export * from './getters.js'
export * from './parseHtmlToContainer.js'
export * from './animationEndEvent.js'
export * from './measureScrollbar.js'
export * from './renderers/render.js'

View File

@@ -0,0 +1,148 @@
import { swalClasses, iconTypes } from '../classes.js'
import { getContainer, getPopup, getContent } from './getters.js'
import { addClass, removeClass, getChildByClass, setInnerHtml } from './domUtils.js'
import { isNodeEnv } from '../isNodeEnv.js'
import { error } from '../utils.js'
import sweetAlert from '../../sweetalert2.js'
const sweetHTML = `
<div aria-labelledby="${swalClasses.title}" aria-describedby="${swalClasses.content}" class="${swalClasses.popup}" tabindex="-1">
<div class="${swalClasses.header}">
<ul class="${swalClasses['progress-steps']}"></ul>
<div class="${swalClasses.icon} ${iconTypes.error}"></div>
<div class="${swalClasses.icon} ${iconTypes.question}"></div>
<div class="${swalClasses.icon} ${iconTypes.warning}"></div>
<div class="${swalClasses.icon} ${iconTypes.info}"></div>
<div class="${swalClasses.icon} ${iconTypes.success}"></div>
<img class="${swalClasses.image}" />
<h2 class="${swalClasses.title}" id="${swalClasses.title}"></h2>
<button type="button" class="${swalClasses.close}"></button>
</div>
<div class="${swalClasses.content}">
<div id="${swalClasses.content}" class="${swalClasses['html-container']}"></div>
<input class="${swalClasses.input}" />
<input type="file" class="${swalClasses.file}" />
<div class="${swalClasses.range}">
<input type="range" />
<output></output>
</div>
<select class="${swalClasses.select}"></select>
<div class="${swalClasses.radio}"></div>
<label for="${swalClasses.checkbox}" class="${swalClasses.checkbox}">
<input type="checkbox" />
<span class="${swalClasses.label}"></span>
</label>
<textarea class="${swalClasses.textarea}"></textarea>
<div class="${swalClasses['validation-message']}" id="${swalClasses['validation-message']}"></div>
</div>
<div class="${swalClasses.actions}">
<button type="button" class="${swalClasses.confirm}">OK</button>
<button type="button" class="${swalClasses.cancel}">Cancel</button>
</div>
<div class="${swalClasses.footer}"></div>
<div class="${swalClasses['timer-progress-bar-container']}">
<div class="${swalClasses['timer-progress-bar']}"></div>
</div>
</div>
`.replace(/(^|\n)\s*/g, '')
const resetOldContainer = () => {
const oldContainer = getContainer()
if (!oldContainer) {
return false
}
oldContainer.parentNode.removeChild(oldContainer)
removeClass(
[document.documentElement, document.body],
[
swalClasses['no-backdrop'],
swalClasses['toast-shown'],
swalClasses['has-column']
]
)
return true
}
let oldInputVal // IE11 workaround, see #1109 for details
const resetValidationMessage = (e) => {
if (sweetAlert.isVisible() && oldInputVal !== e.target.value) {
sweetAlert.resetValidationMessage()
}
oldInputVal = e.target.value
}
const addInputChangeListeners = () => {
const content = getContent()
const input = getChildByClass(content, swalClasses.input)
const file = getChildByClass(content, swalClasses.file)
const range = content.querySelector(`.${swalClasses.range} input`)
const rangeOutput = content.querySelector(`.${swalClasses.range} output`)
const select = getChildByClass(content, swalClasses.select)
const checkbox = content.querySelector(`.${swalClasses.checkbox} input`)
const textarea = getChildByClass(content, swalClasses.textarea)
input.oninput = resetValidationMessage
file.onchange = resetValidationMessage
select.onchange = resetValidationMessage
checkbox.onchange = resetValidationMessage
textarea.oninput = resetValidationMessage
range.oninput = (e) => {
resetValidationMessage(e)
rangeOutput.value = range.value
}
range.onchange = (e) => {
resetValidationMessage(e)
range.nextSibling.value = range.value
}
}
const getTarget = (target) => typeof target === 'string' ? document.querySelector(target) : target
const setupAccessibility = (params) => {
const popup = getPopup()
popup.setAttribute('role', params.toast ? 'alert' : 'dialog')
popup.setAttribute('aria-live', params.toast ? 'polite' : 'assertive')
if (!params.toast) {
popup.setAttribute('aria-modal', 'true')
}
}
const setupRTL = (targetElement) => {
if (window.getComputedStyle(targetElement).direction === 'rtl') {
addClass(getContainer(), swalClasses.rtl)
}
}
/*
* Add modal + backdrop to DOM
*/
export const init = (params) => {
// Clean up the old popup container if it exists
const oldContainerExisted = resetOldContainer()
/* istanbul ignore if */
if (isNodeEnv()) {
error('SweetAlert2 requires document to initialize')
return
}
const container = document.createElement('div')
container.className = swalClasses.container
if (oldContainerExisted) {
addClass(container, swalClasses['no-transition'])
}
setInnerHtml(container, sweetHTML)
const targetElement = getTarget(params.target)
targetElement.appendChild(container)
setupAccessibility(params)
setupRTL(targetElement)
addInputChangeListeners()
}

View File

@@ -0,0 +1,132 @@
import * as dom from './index.js'
import { swalClasses } from '../classes.js'
import { getChildByClass } from './domUtils.js'
import { error, isPromise } from '../utils.js'
import { showLoading } from '../../staticMethods/showLoading.js'
export const handleInputOptionsAndValue = (instance, params) => {
if (params.input === 'select' || params.input === 'radio') {
handleInputOptions(instance, params)
} else if (['text', 'email', 'number', 'tel', 'textarea'].includes(params.input) && isPromise(params.inputValue)) {
handleInputValue(instance, params)
}
}
export const getInputValue = (instance, innerParams) => {
const input = instance.getInput()
if (!input) {
return null
}
switch (innerParams.input) {
case 'checkbox':
return getCheckboxValue(input)
case 'radio':
return getRadioValue(input)
case 'file':
return getFileValue(input)
default:
return innerParams.inputAutoTrim ? input.value.trim() : input.value
}
}
const getCheckboxValue = (input) => input.checked ? 1 : 0
const getRadioValue = (input) => input.checked ? input.value : null
const getFileValue = (input) => input.files.length ? (input.getAttribute('multiple') !== null ? input.files : input.files[0]) : null
const handleInputOptions = (instance, params) => {
const content = dom.getContent()
const processInputOptions = (inputOptions) => populateInputOptions[params.input](content, formatInputOptions(inputOptions), params)
if (isPromise(params.inputOptions)) {
showLoading()
params.inputOptions.then((inputOptions) => {
instance.hideLoading()
processInputOptions(inputOptions)
})
} else if (typeof params.inputOptions === 'object') {
processInputOptions(params.inputOptions)
} else {
error(`Unexpected type of inputOptions! Expected object, Map or Promise, got ${typeof params.inputOptions}`)
}
}
const handleInputValue = (instance, params) => {
const input = instance.getInput()
dom.hide(input)
params.inputValue.then((inputValue) => {
input.value = params.input === 'number' ? parseFloat(inputValue) || 0 : `${inputValue}`
dom.show(input)
input.focus()
instance.hideLoading()
})
.catch((err) => {
error(`Error in inputValue promise: ${err}`)
input.value = ''
dom.show(input)
input.focus()
instance.hideLoading()
})
}
const populateInputOptions = {
select: (content, inputOptions, params) => {
const select = getChildByClass(content, swalClasses.select)
inputOptions.forEach(inputOption => {
const optionValue = inputOption[0]
const optionLabel = inputOption[1]
const option = document.createElement('option')
option.value = optionValue
dom.setInnerHtml(option, optionLabel)
if (params.inputValue.toString() === optionValue.toString()) {
option.selected = true
}
select.appendChild(option)
})
select.focus()
},
radio: (content, inputOptions, params) => {
const radio = getChildByClass(content, swalClasses.radio)
inputOptions.forEach(inputOption => {
const radioValue = inputOption[0]
const radioLabel = inputOption[1]
const radioInput = document.createElement('input')
const radioLabelElement = document.createElement('label')
radioInput.type = 'radio'
radioInput.name = swalClasses.radio
radioInput.value = radioValue
if (params.inputValue.toString() === radioValue.toString()) {
radioInput.checked = true
}
const label = document.createElement('span')
dom.setInnerHtml(label, radioLabel)
label.className = swalClasses.label
radioLabelElement.appendChild(radioInput)
radioLabelElement.appendChild(label)
radio.appendChild(radioLabelElement)
})
const radios = radio.querySelectorAll('input')
if (radios.length) {
radios[0].focus()
}
}
}
/**
* Converts `inputOptions` into an array of `[value, label]`s
* @param inputOptions
*/
const formatInputOptions = (inputOptions) => {
const result = []
if (typeof Map !== 'undefined' && inputOptions instanceof Map) {
inputOptions.forEach((value, key) => {
result.push([key, value])
})
} else {
Object.keys(inputOptions).forEach(key => {
result.push([key, inputOptions[key]])
})
}
return result
}

View File

@@ -0,0 +1,12 @@
import { swalClasses } from '../classes.js'
// Measure scrollbar width for padding body during modal show/hide
// https://github.com/twbs/bootstrap/blob/master/js/src/modal.js
export const measureScrollbar = () => {
const scrollDiv = document.createElement('div')
scrollDiv.className = swalClasses['scrollbar-measure']
document.body.appendChild(scrollDiv)
const scrollbarWidth = scrollDiv.getBoundingClientRect().width - scrollDiv.clientWidth
document.body.removeChild(scrollDiv)
return scrollbarWidth
}

View File

@@ -0,0 +1,38 @@
import { setInnerHtml } from './domUtils.js'
export const parseHtmlToContainer = (param, target) => {
// DOM element
if (param instanceof HTMLElement) {
target.appendChild(param)
// Object
} else if (typeof param === 'object') {
handleObject(param, target)
// Plain string
} else if (param) {
setInnerHtml(target, param)
}
}
const handleObject = (param, target) => {
// JQuery element(s)
if (param.jquery) {
handleJqueryElem(target, param)
// For other objects use their string representation
} else {
setInnerHtml(target, param.toString())
}
}
const handleJqueryElem = (target, elem) => {
target.textContent = ''
if (0 in elem) {
for (let i = 0; i in elem; i++) {
target.appendChild(elem[i].cloneNode(true))
}
} else {
target.appendChild(elem.cloneNode(true))
}
}

View File

@@ -0,0 +1,21 @@
import { getPopup } from '../getters.js'
import { renderActions } from './renderActions.js'
import { renderContainer } from './renderContainer.js'
import { renderContent } from './renderContent.js'
import { renderFooter } from './renderFooter.js'
import { renderHeader } from './renderHeader.js'
import { renderPopup } from './renderPopup.js'
export const render = (instance, params) => {
renderPopup(instance, params)
renderContainer(instance, params)
renderHeader(instance, params)
renderContent(instance, params)
renderActions(instance, params)
renderFooter(instance, params)
if (typeof params.onRender === 'function') {
params.onRender(getPopup())
}
}

View File

@@ -0,0 +1,62 @@
import { swalClasses } from '../../classes.js'
import * as dom from '../../dom/index.js'
import { capitalizeFirstLetter } from '../../utils.js'
export const renderActions = (instance, params) => {
const actions = dom.getActions()
const confirmButton = dom.getConfirmButton()
const cancelButton = dom.getCancelButton()
// Actions (buttons) wrapper
if (!params.showConfirmButton && !params.showCancelButton) {
dom.hide(actions)
}
// Custom class
dom.applyCustomClass(actions, params, 'actions')
// Render confirm button
renderButton(confirmButton, 'confirm', params)
// render Cancel Button
renderButton(cancelButton, 'cancel', params)
if (params.buttonsStyling) {
handleButtonsStyling(confirmButton, cancelButton, params)
} else {
dom.removeClass([confirmButton, cancelButton], swalClasses.styled)
confirmButton.style.backgroundColor = confirmButton.style.borderLeftColor = confirmButton.style.borderRightColor = ''
cancelButton.style.backgroundColor = cancelButton.style.borderLeftColor = cancelButton.style.borderRightColor = ''
}
if (params.reverseButtons) {
confirmButton.parentNode.insertBefore(cancelButton, confirmButton)
}
}
function handleButtonsStyling (confirmButton, cancelButton, params) {
dom.addClass([confirmButton, cancelButton], swalClasses.styled)
// Buttons background colors
if (params.confirmButtonColor) {
confirmButton.style.backgroundColor = params.confirmButtonColor
}
if (params.cancelButtonColor) {
cancelButton.style.backgroundColor = params.cancelButtonColor
}
// Loading state
const confirmButtonBackgroundColor = window.getComputedStyle(confirmButton).getPropertyValue('background-color')
confirmButton.style.borderLeftColor = confirmButtonBackgroundColor
confirmButton.style.borderRightColor = confirmButtonBackgroundColor
}
function renderButton (button, buttonType, params) {
dom.toggle(button, params[`show${capitalizeFirstLetter(buttonType)}Button`], 'inline-block')
dom.setInnerHtml(button, params[`${buttonType}ButtonText`]) // Set caption text
button.setAttribute('aria-label', params[`${buttonType}ButtonAriaLabel`]) // ARIA label
// Add buttons custom classes
button.className = swalClasses[buttonType]
dom.applyCustomClass(button, params, `${buttonType}Button`)
dom.addClass(button, params[`${buttonType}ButtonClass`])
}

View File

@@ -0,0 +1,13 @@
import * as dom from '../../dom/index.js'
export const renderCloseButton = (instance, params) => {
const closeButton = dom.getCloseButton()
dom.setInnerHtml(closeButton, params.closeButtonHtml)
// Custom class
dom.applyCustomClass(closeButton, params, 'closeButton')
dom.toggle(closeButton, params.showCloseButton)
closeButton.setAttribute('aria-label', params.closeButtonAriaLabel)
}

View File

@@ -0,0 +1,56 @@
import { swalClasses } from '../../classes.js'
import { warn } from '../../utils.js'
import * as dom from '../../dom/index.js'
function handleBackdropParam (container, backdrop) {
if (typeof backdrop === 'string') {
container.style.background = backdrop
} else if (!backdrop) {
dom.addClass([document.documentElement, document.body], swalClasses['no-backdrop'])
}
}
function handlePositionParam (container, position) {
if (position in swalClasses) {
dom.addClass(container, swalClasses[position])
} else {
warn('The "position" parameter is not valid, defaulting to "center"')
dom.addClass(container, swalClasses.center)
}
}
function handleGrowParam (container, grow) {
if (grow && typeof grow === 'string') {
const growClass = `grow-${grow}`
if (growClass in swalClasses) {
dom.addClass(container, swalClasses[growClass])
}
}
}
export const renderContainer = (instance, params) => {
const container = dom.getContainer()
if (!container) {
return
}
handleBackdropParam(container, params.backdrop)
if (!params.backdrop && params.allowOutsideClick) {
warn('"allowOutsideClick" parameter requires `backdrop` parameter to be set to `true`')
}
handlePositionParam(container, params.position)
handleGrowParam(container, params.grow)
// Custom class
dom.applyCustomClass(container, params, 'container')
// Set queue step attribute for getQueueStep() method
const queueStep = document.body.getAttribute('data-swal2-queue-step')
if (queueStep) {
container.setAttribute('data-queue-step', queueStep)
document.body.removeAttribute('data-swal2-queue-step')
}
}

View File

@@ -0,0 +1,27 @@
import { swalClasses } from '../../classes.js'
import * as dom from '../../dom/index.js'
import { renderInput } from './renderInput.js'
export const renderContent = (instance, params) => {
const content = dom.getContent().querySelector(`#${swalClasses.content}`)
// Content as HTML
if (params.html) {
dom.parseHtmlToContainer(params.html, content)
dom.show(content, 'block')
// Content as plain text
} else if (params.text) {
content.textContent = params.text
dom.show(content, 'block')
// No content
} else {
dom.hide(content)
}
renderInput(instance, params)
// Custom class
dom.applyCustomClass(dom.getContent(), params, 'content')
}

View File

@@ -0,0 +1,14 @@
import * as dom from '../../dom/index.js'
export const renderFooter = (instance, params) => {
const footer = dom.getFooter()
dom.toggle(footer, params.footer)
if (params.footer) {
dom.parseHtmlToContainer(params.footer, footer)
}
// Custom class
dom.applyCustomClass(footer, params, 'footer')
}

View File

@@ -0,0 +1,28 @@
import * as dom from '../../dom/index.js'
import { renderCloseButton } from './renderCloseButton.js'
import { renderIcon } from './renderIcon.js'
import { renderImage } from './renderImage.js'
import { renderProgressSteps } from './renderProgressSteps.js'
import { renderTitle } from './renderTitle.js'
export const renderHeader = (instance, params) => {
const header = dom.getHeader()
// Custom class
dom.applyCustomClass(header, params, 'header')
// Progress steps
renderProgressSteps(instance, params)
// Icon
renderIcon(instance, params)
// Image
renderImage(instance, params)
// Title
renderTitle(instance, params)
// Close button
renderCloseButton(instance, params)
}

View File

@@ -0,0 +1,85 @@
import { swalClasses, iconTypes } from '../../classes.js'
import { error } from '../../utils.js'
import * as dom from '../../dom/index.js'
import privateProps from '../../../privateProps.js'
export const renderIcon = (instance, params) => {
const innerParams = privateProps.innerParams.get(instance)
// if the give icon already rendered, apply the custom class without re-rendering the icon
if (innerParams && params.icon === innerParams.icon && dom.getIcon()) {
dom.applyCustomClass(dom.getIcon(), params, 'icon')
return
}
hideAllIcons()
if (!params.icon) {
return
}
if (Object.keys(iconTypes).indexOf(params.icon) !== -1) {
const icon = dom.elementBySelector(`.${swalClasses.icon}.${iconTypes[params.icon]}`)
dom.show(icon)
// Custom or default content
setContent(icon, params)
adjustSuccessIconBackgoundColor()
// Custom class
dom.applyCustomClass(icon, params, 'icon')
// Animate icon
dom.addClass(icon, params.showClass.icon)
} else {
error(`Unknown icon! Expected "success", "error", "warning", "info" or "question", got "${params.icon}"`)
}
}
const hideAllIcons = () => {
const icons = dom.getIcons()
for (let i = 0; i < icons.length; i++) {
dom.hide(icons[i])
}
}
// Adjust success icon background color to match the popup background color
const adjustSuccessIconBackgoundColor = () => {
const popup = dom.getPopup()
const popupBackgroundColor = window.getComputedStyle(popup).getPropertyValue('background-color')
const successIconParts = popup.querySelectorAll('[class^=swal2-success-circular-line], .swal2-success-fix')
for (let i = 0; i < successIconParts.length; i++) {
successIconParts[i].style.backgroundColor = popupBackgroundColor
}
}
const setContent = (icon, params) => {
icon.textContent = ''
if (params.iconHtml) {
dom.setInnerHtml(icon, iconContent(params.iconHtml))
} else if (params.icon === 'success') {
dom.setInnerHtml(icon, `
<div class="swal2-success-circular-line-left"></div>
<span class="swal2-success-line-tip"></span> <span class="swal2-success-line-long"></span>
<div class="swal2-success-ring"></div> <div class="swal2-success-fix"></div>
<div class="swal2-success-circular-line-right"></div>
`)
} else if (params.icon === 'error') {
dom.setInnerHtml(icon, `
<span class="swal2-x-mark">
<span class="swal2-x-mark-line-left"></span>
<span class="swal2-x-mark-line-right"></span>
</span>
`)
} else {
const defaultIconHtml = {
question: '?',
warning: '!',
info: 'i'
}
dom.setInnerHtml(icon, iconContent(defaultIconHtml[params.icon]))
}
}
const iconContent = (content) => `<div class="${swalClasses['icon-content']}">${content}</div>`

View File

@@ -0,0 +1,24 @@
import { swalClasses } from '../../classes.js'
import * as dom from '../../dom/index.js'
export const renderImage = (instance, params) => {
const image = dom.getImage()
if (!params.imageUrl) {
return dom.hide(image)
}
dom.show(image)
// Src, alt
image.setAttribute('src', params.imageUrl)
image.setAttribute('alt', params.imageAlt)
// Width, height
dom.applyNumericalStyle(image, 'width', params.imageWidth)
dom.applyNumericalStyle(image, 'height', params.imageHeight)
// Class
image.className = swalClasses.image
dom.applyCustomClass(image, params, 'image')
}

View File

@@ -0,0 +1,179 @@
import { swalClasses } from '../../classes.js'
import { warn, error, isPromise } from '../../utils.js'
import * as dom from '../../dom/index.js'
import privateProps from '../../../privateProps.js'
const inputTypes = ['input', 'file', 'range', 'select', 'radio', 'checkbox', 'textarea']
export const renderInput = (instance, params) => {
const content = dom.getContent()
const innerParams = privateProps.innerParams.get(instance)
const rerender = !innerParams || params.input !== innerParams.input
inputTypes.forEach((inputType) => {
const inputClass = swalClasses[inputType]
const inputContainer = dom.getChildByClass(content, inputClass)
// set attributes
setAttributes(inputType, params.inputAttributes)
// set class
inputContainer.className = inputClass
if (rerender) {
dom.hide(inputContainer)
}
})
if (params.input) {
if (rerender) {
showInput(params)
}
// set custom class
setCustomClass(params)
}
}
const showInput = (params) => {
if (!renderInputType[params.input]) {
return error(`Unexpected type of input! Expected "text", "email", "password", "number", "tel", "select", "radio", "checkbox", "textarea", "file" or "url", got "${params.input}"`)
}
const inputContainer = getInputContainer(params.input)
const input = renderInputType[params.input](inputContainer, params)
dom.show(input)
// input autofocus
setTimeout(() => {
dom.focusInput(input)
})
}
const removeAttributes = (input) => {
for (let i = 0; i < input.attributes.length; i++) {
const attrName = input.attributes[i].name
if (!['type', 'value', 'style'].includes(attrName)) {
input.removeAttribute(attrName)
}
}
}
const setAttributes = (inputType, inputAttributes) => {
const input = dom.getInput(dom.getContent(), inputType)
if (!input) {
return
}
removeAttributes(input)
for (const attr in inputAttributes) {
// Do not set a placeholder for <input type="range">
// it'll crash Edge, #1298
if (inputType === 'range' && attr === 'placeholder') {
continue
}
input.setAttribute(attr, inputAttributes[attr])
}
}
const setCustomClass = (params) => {
const inputContainer = getInputContainer(params.input)
if (params.customClass) {
dom.addClass(inputContainer, params.customClass.input)
}
}
const setInputPlaceholder = (input, params) => {
if (!input.placeholder || params.inputPlaceholder) {
input.placeholder = params.inputPlaceholder
}
}
const getInputContainer = (inputType) => {
const inputClass = swalClasses[inputType] ? swalClasses[inputType] : swalClasses.input
return dom.getChildByClass(dom.getContent(), inputClass)
}
const renderInputType = {}
renderInputType.text =
renderInputType.email =
renderInputType.password =
renderInputType.number =
renderInputType.tel =
renderInputType.url = (input, params) => {
if (typeof params.inputValue === 'string' || typeof params.inputValue === 'number') {
input.value = params.inputValue
} else if (!isPromise(params.inputValue)) {
warn(`Unexpected type of inputValue! Expected "string", "number" or "Promise", got "${typeof params.inputValue}"`)
}
setInputPlaceholder(input, params)
input.type = params.input
return input
}
renderInputType.file = (input, params) => {
setInputPlaceholder(input, params)
return input
}
renderInputType.range = (range, params) => {
const rangeInput = range.querySelector('input')
const rangeOutput = range.querySelector('output')
rangeInput.value = params.inputValue
rangeInput.type = params.input
rangeOutput.value = params.inputValue
return range
}
renderInputType.select = (select, params) => {
select.textContent = ''
if (params.inputPlaceholder) {
const placeholder = document.createElement('option')
dom.setInnerHtml(placeholder, params.inputPlaceholder)
placeholder.value = ''
placeholder.disabled = true
placeholder.selected = true
select.appendChild(placeholder)
}
return select
}
renderInputType.radio = (radio) => {
radio.textContent = ''
return radio
}
renderInputType.checkbox = (checkboxContainer, params) => {
const checkbox = dom.getInput(dom.getContent(), 'checkbox')
checkbox.value = 1
checkbox.id = swalClasses.checkbox
checkbox.checked = Boolean(params.inputValue)
const label = checkboxContainer.querySelector('span')
dom.setInnerHtml(label, params.inputPlaceholder)
return checkboxContainer
}
renderInputType.textarea = (textarea, params) => {
textarea.value = params.inputValue
setInputPlaceholder(textarea, params)
if ('MutationObserver' in window) { // #1699
const initialPopupWidth = parseInt(window.getComputedStyle(dom.getPopup()).width)
const popupPadding = parseInt(window.getComputedStyle(dom.getPopup()).paddingLeft) + parseInt(window.getComputedStyle(dom.getPopup()).paddingRight)
const outputsize = () => {
const contentWidth = textarea.offsetWidth + popupPadding
if (contentWidth > initialPopupWidth) {
dom.getPopup().style.width = `${contentWidth}px`
} else {
dom.getPopup().style.width = null
}
}
new MutationObserver(outputsize).observe(textarea, {
attributes: true, attributeFilter: ['style']
})
}
return textarea
}

View File

@@ -0,0 +1,43 @@
import { swalClasses } from '../../classes.js'
import * as dom from '../../dom/index.js'
export const renderPopup = (instance, params) => {
const popup = dom.getPopup()
// Width
dom.applyNumericalStyle(popup, 'width', params.width)
// Padding
dom.applyNumericalStyle(popup, 'padding', params.padding)
// Background
if (params.background) {
popup.style.background = params.background
}
// Classes
addClasses(popup, params)
}
const addClasses = (popup, params) => {
// Default Class + showClass when updating Swal.update({})
popup.className = `${swalClasses.popup} ${dom.isVisible(popup) ? params.showClass.popup : ''}`
if (params.toast) {
dom.addClass([document.documentElement, document.body], swalClasses['toast-shown'])
dom.addClass(popup, swalClasses.toast)
} else {
dom.addClass(popup, swalClasses.modal)
}
// Custom class
dom.applyCustomClass(popup, params, 'popup')
if (typeof params.customClass === 'string') {
dom.addClass(popup, params.customClass)
}
// Icon class (#1842)
if (params.icon) {
dom.addClass(popup, swalClasses[`icon-${params.icon}`])
}
}

View File

@@ -0,0 +1,50 @@
import { swalClasses } from '../../classes.js'
import { warn } from '../../utils.js'
import * as dom from '../../dom/index.js'
import { getQueueStep } from '../../../staticMethods/queue.js'
const createStepElement = (step) => {
const stepEl = document.createElement('li')
dom.addClass(stepEl, swalClasses['progress-step'])
dom.setInnerHtml(stepEl, step)
return stepEl
}
const createLineElement = (params) => {
const lineEl = document.createElement('li')
dom.addClass(lineEl, swalClasses['progress-step-line'])
if (params.progressStepsDistance) {
lineEl.style.width = params.progressStepsDistance
}
return lineEl
}
export const renderProgressSteps = (instance, params) => {
const progressStepsContainer = dom.getProgressSteps()
if (!params.progressSteps || params.progressSteps.length === 0) {
return dom.hide(progressStepsContainer)
}
dom.show(progressStepsContainer)
progressStepsContainer.textContent = ''
const currentProgressStep = parseInt(params.currentProgressStep === undefined ? getQueueStep() : params.currentProgressStep)
if (currentProgressStep >= params.progressSteps.length) {
warn(
'Invalid currentProgressStep parameter, it should be less than progressSteps.length ' +
'(currentProgressStep like JS arrays starts from 0)'
)
}
params.progressSteps.forEach((step, index) => {
const stepEl = createStepElement(step)
progressStepsContainer.appendChild(stepEl)
if (index === currentProgressStep) {
dom.addClass(stepEl, swalClasses['active-progress-step'])
}
if (index !== params.progressSteps.length - 1) {
const lineEl = createLineElement(step)
progressStepsContainer.appendChild(lineEl)
}
})
}

View File

@@ -0,0 +1,18 @@
import * as dom from '../../dom/index.js'
export const renderTitle = (instance, params) => {
const title = dom.getTitle()
dom.toggle(title, params.title || params.titleText)
if (params.title) {
dom.parseHtmlToContainer(params.title, title)
}
if (params.titleText) {
title.innerText = params.titleText
}
// Custom class
dom.applyCustomClass(title, params, 'title')
}

View File

@@ -0,0 +1,29 @@
/* istanbul ignore file */
import * as dom from './dom/index.js'
// https://stackoverflow.com/a/21825207
const isIE11 = () => !!window.MSInputMethodContext && !!document.documentMode
// Fix IE11 centering sweetalert2/issues/933
const fixVerticalPositionIE = () => {
const container = dom.getContainer()
const popup = dom.getPopup()
container.style.removeProperty('align-items')
if (popup.offsetTop < 0) {
container.style.alignItems = 'flex-start'
}
}
export const IEfix = () => {
if (typeof window !== 'undefined' && isIE11()) {
fixVerticalPositionIE()
window.addEventListener('resize', fixVerticalPositionIE)
}
}
export const undoIEfix = () => {
if (typeof window !== 'undefined' && isIE11()) {
window.removeEventListener('resize', fixVerticalPositionIE)
}
}

View File

@@ -0,0 +1,43 @@
/* istanbul ignore file */
import * as dom from './dom/index.js'
import { swalClasses } from '../utils/classes.js'
// Fix iOS scrolling http://stackoverflow.com/q/39626302
export const iOSfix = () => {
const iOS = (/iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream) || (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1)
if (iOS && !dom.hasClass(document.body, swalClasses.iosfix)) {
const offset = document.body.scrollTop
document.body.style.top = `${offset * -1}px`
dom.addClass(document.body, swalClasses.iosfix)
lockBodyScroll()
}
}
const lockBodyScroll = () => { // #1246
const container = dom.getContainer()
let preventTouchMove
container.ontouchstart = (e) => {
preventTouchMove =
e.target === container ||
(
!dom.isScrollable(container) &&
e.target.tagName !== 'INPUT' // #1603
)
}
container.ontouchmove = (e) => {
if (preventTouchMove) {
e.preventDefault()
e.stopPropagation()
}
}
}
export const undoIOSfix = () => {
if (dom.hasClass(document.body, swalClasses.iosfix)) {
const offset = parseInt(document.body.style.top, 10)
dom.removeClass(document.body, swalClasses.iosfix)
document.body.style.top = ''
document.body.scrollTop = (offset * -1)
}
}

View File

@@ -0,0 +1,2 @@
// Detect Node env
export const isNodeEnv = () => typeof window === 'undefined' || typeof document === 'undefined'

View File

@@ -0,0 +1,84 @@
import * as dom from './dom/index.js'
import { swalClasses } from './classes.js'
import { fixScrollbar } from './scrollbarFix.js'
import { iOSfix } from './iosFix.js'
import { IEfix } from './ieFix.js'
import { setAriaHidden } from './aria.js'
import globalState from '../globalState.js'
/**
* Open popup, add necessary classes and styles, fix scrollbar
*
* @param {Array} params
*/
export const openPopup = (params) => {
const container = dom.getContainer()
const popup = dom.getPopup()
if (typeof params.onBeforeOpen === 'function') {
params.onBeforeOpen(popup)
}
addClasses(container, popup, params)
// scrolling is 'hidden' until animation is done, after that 'auto'
setScrollingVisibility(container, popup)
if (dom.isModal()) {
fixScrollContainer(container, params.scrollbarPadding)
}
if (!dom.isToast() && !globalState.previousActiveElement) {
globalState.previousActiveElement = document.activeElement
}
if (typeof params.onOpen === 'function') {
setTimeout(() => params.onOpen(popup))
}
dom.removeClass(container, swalClasses['no-transition'])
}
function swalOpenAnimationFinished (event) {
const popup = dom.getPopup()
if (event.target !== popup) {
return
}
const container = dom.getContainer()
popup.removeEventListener(dom.animationEndEvent, swalOpenAnimationFinished)
container.style.overflowY = 'auto'
}
const setScrollingVisibility = (container, popup) => {
if (dom.animationEndEvent && dom.hasCssAnimation(popup)) {
container.style.overflowY = 'hidden'
popup.addEventListener(dom.animationEndEvent, swalOpenAnimationFinished)
} else {
container.style.overflowY = 'auto'
}
}
const fixScrollContainer = (container, scrollbarPadding) => {
iOSfix()
IEfix()
setAriaHidden()
if (scrollbarPadding) {
fixScrollbar()
}
// sweetalert2/issues/1247
setTimeout(() => {
container.scrollTop = 0
})
}
const addClasses = (container, popup, params) => {
dom.addClass(container, params.showClass.backdrop)
dom.show(popup)
// Animate popup right after showing it
dom.addClass(popup, params.showClass.popup)
dom.addClass([document.documentElement, document.body], swalClasses.shown)
if (params.heightAuto && params.backdrop && !params.toast) {
dom.addClass([document.documentElement, document.body], swalClasses['height-auto'])
}
}

View File

@@ -0,0 +1,181 @@
import { warn, warnAboutDepreation } from '../utils/utils.js'
export const defaultParams = {
title: '',
titleText: '',
text: '',
html: '',
footer: '',
icon: undefined,
iconHtml: undefined,
toast: false,
animation: true,
showClass: {
popup: 'swal2-show',
backdrop: 'swal2-backdrop-show',
icon: 'swal2-icon-show',
},
hideClass: {
popup: 'swal2-hide',
backdrop: 'swal2-backdrop-hide',
icon: 'swal2-icon-hide',
},
customClass: undefined,
target: 'body',
backdrop: true,
heightAuto: true,
allowOutsideClick: true,
allowEscapeKey: true,
allowEnterKey: true,
stopKeydownPropagation: true,
keydownListenerCapture: false,
showConfirmButton: true,
showCancelButton: false,
preConfirm: undefined,
confirmButtonText: 'OK',
confirmButtonAriaLabel: '',
confirmButtonColor: undefined,
cancelButtonText: 'Cancel',
cancelButtonAriaLabel: '',
cancelButtonColor: undefined,
buttonsStyling: true,
reverseButtons: false,
focusConfirm: true,
focusCancel: false,
showCloseButton: false,
closeButtonHtml: '&times;',
closeButtonAriaLabel: 'Close this dialog',
showLoaderOnConfirm: false,
imageUrl: undefined,
imageWidth: undefined,
imageHeight: undefined,
imageAlt: '',
timer: undefined,
timerProgressBar: false,
width: undefined,
padding: undefined,
background: undefined,
input: undefined,
inputPlaceholder: '',
inputValue: '',
inputOptions: {},
inputAutoTrim: true,
inputAttributes: {},
inputValidator: undefined,
validationMessage: undefined,
grow: false,
position: 'center',
progressSteps: [],
currentProgressStep: undefined,
progressStepsDistance: undefined,
onBeforeOpen: undefined,
onOpen: undefined,
onRender: undefined,
onClose: undefined,
onAfterClose: undefined,
onDestroy: undefined,
scrollbarPadding: true
}
export const updatableParams = [
'title',
'titleText',
'text',
'html',
'icon',
'hideClass',
'customClass',
'allowOutsideClick',
'allowEscapeKey',
'showConfirmButton',
'showCancelButton',
'confirmButtonText',
'confirmButtonAriaLabel',
'confirmButtonColor',
'cancelButtonText',
'cancelButtonAriaLabel',
'cancelButtonColor',
'buttonsStyling',
'reverseButtons',
'imageUrl',
'imageWidth',
'imageHeight',
'imageAlt',
'progressSteps',
'currentProgressStep'
]
export const deprecatedParams = {
animation: 'showClass" and "hideClass',
}
const toastIncompatibleParams = [
'allowOutsideClick',
'allowEnterKey',
'backdrop',
'focusConfirm',
'focusCancel',
'heightAuto',
'keydownListenerCapture'
]
/**
* Is valid parameter
* @param {String} paramName
*/
export const isValidParameter = (paramName) => {
return Object.prototype.hasOwnProperty.call(defaultParams, paramName)
}
/**
* Is valid parameter for Swal.update() method
* @param {String} paramName
*/
export const isUpdatableParameter = (paramName) => {
return updatableParams.indexOf(paramName) !== -1
}
/**
* Is deprecated parameter
* @param {String} paramName
*/
export const isDeprecatedParameter = (paramName) => {
return deprecatedParams[paramName]
}
const checkIfParamIsValid = (param) => {
if (!isValidParameter(param)) {
warn(`Unknown parameter "${param}"`)
}
}
const checkIfToastParamIsValid = (param) => {
if (toastIncompatibleParams.includes(param)) {
warn(`The parameter "${param}" is incompatible with toasts`)
}
}
const checkIfParamIsDeprecated = (param) => {
if (isDeprecatedParameter(param)) {
warnAboutDepreation(param, isDeprecatedParameter(param))
}
}
/**
* Show relevant warnings for given params
*
* @param params
*/
export const showWarningsForParams = (params) => {
for (const param in params) {
checkIfParamIsValid(param)
if (params.toast) {
checkIfToastParamIsValid(param)
}
checkIfParamIsDeprecated(param)
}
}
export default defaultParams

View File

@@ -0,0 +1,21 @@
import * as dom from './dom/index.js'
export const fixScrollbar = () => {
// for queues, do not do this more than once
if (dom.states.previousBodyPadding !== null) {
return
}
// if the body has overflow
if (document.body.scrollHeight > window.innerHeight) {
// add padding so the content doesn't shift after removal of scrollbar
dom.states.previousBodyPadding = parseInt(window.getComputedStyle(document.body).getPropertyValue('padding-right'))
document.body.style.paddingRight = `${dom.states.previousBodyPadding + dom.measureScrollbar()}px`
}
}
export const undoScrollbar = () => {
if (dom.states.previousBodyPadding !== null) {
document.body.style.paddingRight = `${dom.states.previousBodyPadding}px`
dom.states.previousBodyPadding = null
}
}

View File

@@ -0,0 +1,60 @@
import { warn, callIfFunction } from './utils.js'
import * as dom from './dom/index.js'
import defaultInputValidators from './defaultInputValidators.js'
function setDefaultInputValidators (params) {
// Use default `inputValidator` for supported input types if not provided
if (!params.inputValidator) {
Object.keys(defaultInputValidators).forEach((key) => {
if (params.input === key) {
params.inputValidator = defaultInputValidators[key]
}
})
}
}
function validateCustomTargetElement (params) {
// Determine if the custom target element is valid
if (
!params.target ||
(typeof params.target === 'string' && !document.querySelector(params.target)) ||
(typeof params.target !== 'string' && !params.target.appendChild)
) {
warn('Target parameter is not valid, defaulting to "body"')
params.target = 'body'
}
}
/**
* Set type, text and actions on popup
*
* @param params
* @returns {boolean}
*/
export default function setParameters (params) {
setDefaultInputValidators(params)
// showLoaderOnConfirm && preConfirm
if (params.showLoaderOnConfirm && !params.preConfirm) {
warn(
'showLoaderOnConfirm is set to true, but preConfirm is not defined.\n' +
'showLoaderOnConfirm should be used together with preConfirm, see usage example:\n' +
'https://sweetalert2.github.io/#ajax-request'
)
}
// params.animation will be actually used in renderPopup.js
// but in case when params.animation is a function, we need to call that function
// before popup (re)initialization, so it'll be possible to check Swal.isVisible()
// inside the params.animation function
params.animation = callIfFunction(params.animation)
validateCustomTargetElement(params)
// Replace newlines with <br> in title
if (typeof params.title === 'string') {
params.title = params.title.split('\n').join('<br />')
}
dom.init(params)
}

View File

@@ -0,0 +1,83 @@
export const consolePrefix = 'SweetAlert2:'
/**
* Filter the unique values into a new array
* @param arr
*/
export const uniqueArray = (arr) => {
const result = []
for (let i = 0; i < arr.length; i++) {
if (result.indexOf(arr[i]) === -1) {
result.push(arr[i])
}
}
return result
}
/**
* Capitalize the first letter of a string
* @param str
*/
export const capitalizeFirstLetter = (str) => str.charAt(0).toUpperCase() + str.slice(1)
/**
* Returns the array ob object values (Object.values isn't supported in IE11)
* @param obj
*/
export const objectValues = (obj) => Object.keys(obj).map(key => obj[key])
/**
* Convert NodeList to Array
* @param nodeList
*/
export const toArray = (nodeList) => Array.prototype.slice.call(nodeList)
/**
* Standardise console warnings
* @param message
*/
export const warn = (message) => {
console.warn(`${consolePrefix} ${message}`)
}
/**
* Standardise console errors
* @param message
*/
export const error = (message) => {
console.error(`${consolePrefix} ${message}`)
}
/**
* Private global state for `warnOnce`
* @type {Array}
* @private
*/
const previousWarnOnceMessages = []
/**
* Show a console warning, but only if it hasn't already been shown
* @param message
*/
export const warnOnce = (message) => {
if (!previousWarnOnceMessages.includes(message)) {
previousWarnOnceMessages.push(message)
warn(message)
}
}
/**
* Show a one-time console warning about deprecated params/methods
*/
export const warnAboutDepreation = (deprecatedParam, useInstead) => {
warnOnce(`"${deprecatedParam}" is deprecated and will be removed in the next major release. Please use "${useInstead}" instead.`)
}
/**
* If `arg` is a function, call it (with no arguments or context) and return the result.
* Otherwise, just pass the value through
* @param arg
*/
export const callIfFunction = (arg) => typeof arg === 'function' ? arg() : arg
export const isPromise = (arg) => arg && Promise.resolve(arg) === arg

View File

@@ -0,0 +1,188 @@
$swal2-white: #fff !default;
$swal2-black: #000 !default;
$swal2-outline-color: rgba(50, 100, 150, .4) !default;
// CONTAINER
$swal2-container-padding: .625em !default;
// BOX MODEL
$swal2-width: 32em !default;
$swal2-padding: 1.25em !default;
$swal2-border: none !default;
$swal2-border-radius: .3125em !default;
$swal2-box-shadow: #d9d9d9 !default;
// ANIMATIONS
$swal2-show-animation: swal2-show .3s !default;
$swal2-hide-animation: swal2-hide .15s forwards !default;
// BACKGROUND
$swal2-background: $swal2-white !default;
// TYPOGRAPHY
$swal2-font: inherit !default;
$swal2-font-size: 1rem !default;
// BACKDROP
$swal2-backdrop: rgba($swal2-black, .4) !default;
$swal2-backdrop-transition: background-color .1s !default;
// ICONS
$swal2-icon-size: 5em !default;
$swal2-icon-animations: true !default;
$swal2-icon-margin: 1.25em auto 1.875em !default;
$swal2-icon-zoom: null !default;
$swal2-success: #a5dc86 !default;
$swal2-success-border: rgba($swal2-success, .3) !default;
$swal2-error: #f27474 !default;
$swal2-warning: #f8bb86 !default;
$swal2-info: #3fc3ee !default;
$swal2-question: #87adbd !default;
$swal2-icon-font-family: inherit !default;
// IMAGE
$swal2-image-margin: 1.25em auto !default;
// TITLE
$swal2-title-margin: 0 0 .4em !default;
$swal2-title-color: lighten($swal2-black, 35) !default;
$swal2-title-font-size: 1.875em !default;
// CONTENT
$swal2-content-justify-content: center !default;
$swal2-content-margin: 0 !default;
$swal2-content-pading: 0 !default;
$swal2-content-color: lighten($swal2-black, 33) !default;
$swal2-content-font-size: 1.125em !default;
$swal2-content-font-weight: normal !default;
$swal2-content-line-height: normal !default;
$swal2-content-text-align: center !default;
$swal2-content-word-wrap: break-word !default;
// INPUT
$swal2-input-margin: 1em auto !default;
$swal2-input-width: 100% !default;
$swal2-input-height: 2.625em !default;
$swal2-input-padding: 0 .75em !default;
$swal2-input-border: 1px solid lighten($swal2-black, 85) !default;
$swal2-input-border-radius: .1875em !default;
$swal2-input-box-shadow: inset 0 1px 1px rgba($swal2-black, .06) !default;
$swal2-input-focus-border: 1px solid #b4dbed !default;
$swal2-input-focus-outline: none !default;
$swal2-input-focus-box-shadow: 0 0 3px #c4e6f5 !default;
$swal2-input-font-size: 1.125em !default;
$swal2-input-background: inherit !default;
$swal2-input-color: inherit !default;
$swal2-input-transition: border-color .3s, box-shadow .3s !default;
// TEXTAREA SPECIFIC VARIABLES
$swal2-textarea-height: 6.75em !default;
$swal2-textarea-padding: .75em !default;
// VALIDATION MESSAGE
$swal2-validation-message-justify-content: center !default;
$swal2-validation-message-padding: .625em !default;
$swal2-validation-message-background: lighten($swal2-black, 94) !default;
$swal2-validation-message-color: lighten($swal2-black, 40) !default;
$swal2-validation-message-font-size: 1em !default;
$swal2-validation-message-font-weight: 300 !default;
$swal2-validation-message-icon-background: $swal2-error !default;
$swal2-validation-message-icon-color: $swal2-white !default;
$swal2-validation-message-icon-zoom: null !default;
// PROGRESS STEPS
$swal2-progress-steps-background: inherit !default;
$swal2-progress-steps-margin: 0 0 1.25em !default;
$swal2-progress-steps-padding: 0 !default;
$swal2-progress-steps-font-weight: 600 !default;
$swal2-progress-steps-distance: 2.5em !default;
$swal2-progress-step-width: 2em;
$swal2-progress-step-height: 2em;
$swal2-progress-step-border-radius: 2em;
$swal2-progress-step-background: #add8e6 !default;
$swal2-progress-step-color: $swal2-white !default;
$swal2-active-step-background: #3085d6 !default;
$swal2-active-step-color: $swal2-white !default;
// FOOTER
$swal2-footer-margin: 1.25em 0 0 !default;
$swal2-footer-padding: 1em 0 0 !default;
$swal2-footer-border-color: #eee !default;
$swal2-footer-color: lighten($swal2-black, 33) !default;
$swal2-footer-font-size: 1em !default;
// TIMER POGRESS BAR
$swal2-timer-progress-bar-height: .25em;
$swal2-timer-progress-bar-background: rgba($swal2-black, .2) !default;
// CLOSE BUTTON
$swal2-close-button-align-items: center !default;
$swal2-close-button-justify-content: center !default;
$swal2-close-button-width: 1.2em !default;
$swal2-close-button-height: 1.2em !default;
$swal2-close-button-line-height: 1.2 !default;
$swal2-close-button-position: absolute !default;
$swal2-close-button-gap: 0 !default;
$swal2-close-button-transition: color .1s ease-out !default;
$swal2-close-button-border: none !default;
$swal2-close-button-border-radius: 0 !default;
$swal2-close-button-outline: null !default;
$swal2-close-button-background: transparent !default;
$swal2-close-button-color: lighten($swal2-black, 80) !default;
$swal2-close-button-font-family: serif !default;
$swal2-close-button-font-size: 2.5em !default;
// CLOSE BUTTON:HOVER
$swal2-close-button-hover-transform: none !default;
$swal2-close-button-hover-color: $swal2-error !default;
$swal2-close-button-hover-background: transparent !default;
// ACTIONS
$swal2-actions-flex-wrap: wrap !default;
$swal2-actions-align-items: center !default;
$swal2-actions-justify-content: center !default;
$swal2-actions-width: 100% !default;
$swal2-actions-margin: 1.25em auto 0 !default;
// CONFIRM BUTTON
$swal2-confirm-button-border: 0 !default;
$swal2-confirm-button-border-radius: .25em !default;
$swal2-confirm-button-background-color: #3085d6 !default;
$swal2-confirm-button-color: $swal2-white !default;
$swal2-confirm-button-font-size: 1.0625em !default;
// CANCEL BUTTON
$swal2-cancel-button-border: 0 !default;
$swal2-cancel-button-border-radius: .25em !default;
$swal2-cancel-button-background-color: #aaa !default;
$swal2-cancel-button-color: $swal2-white !default;
$swal2-cancel-button-font-size: 1.0625em !default;
// COMMON VARIABLES FOR CONFIRM AND CANCEL BUTTONS
$swal2-button-darken-hover: rgba($swal2-black, .1) !default;
$swal2-button-darken-active: rgba($swal2-black, .2) !default;
$swal2-button-focus-outline: none !default;
$swal2-button-focus-background-color: null !default;
$swal2-button-focus-box-shadow: 0 0 0 1px $swal2-background, 0 0 0 3px $swal2-outline-color !default;
// TOASTS
$swal2-toast-show-animation: swal2-toast-show .5s !default;
$swal2-toast-hide-animation: swal2-toast-hide .1s forwards !default;
$swal2-toast-border: none !default;
$swal2-toast-box-shadow: 0 0 .625em #d9d9d9 !default;
$swal2-toast-background: $swal2-white !default;
$swal2-toast-close-button-width: .8em !default;
$swal2-toast-close-button-height: .8em !default;
$swal2-toast-close-button-line-height: .8 !default;
$swal2-toast-width: auto !default;
$swal2-toast-padding: .625em !default;
$swal2-toast-title-margin: 0 .6em !default;
$swal2-toast-title-font-size: 1em !default;
$swal2-toast-content-font-size: 1em !default;
$swal2-toast-input-font-size: 1em !default;
$swal2-toast-validation-font-size: 1em !default;
$swal2-toast-buttons-font-size: 1em !default;
$swal2-toast-button-focus-box-shadow: 0 0 0 1px $swal2-background, 0 0 0 3px $swal2-outline-color !default;
$swal2-toast-footer-margin: .5em 0 0 !default;
$swal2-toast-footer-padding: .5em 0 0 !default;
$swal2-toast-footer-font-size: .8em !default;