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,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
}
})
}