first commit
This commit is contained in:
8
public/assets/sweetalert2/src/utils/DismissReason.js
Normal file
8
public/assets/sweetalert2/src/utils/DismissReason.js
Normal file
@@ -0,0 +1,8 @@
|
||||
/** @type {Record<DismissReason, DismissReason>} */
|
||||
export const DismissReason = Object.freeze({
|
||||
cancel: 'cancel',
|
||||
backdrop: 'backdrop',
|
||||
close: 'close',
|
||||
esc: 'esc',
|
||||
timer: 'timer',
|
||||
})
|
||||
94
public/assets/sweetalert2/src/utils/EventEmitter.js
Normal file
94
public/assets/sweetalert2/src/utils/EventEmitter.js
Normal file
@@ -0,0 +1,94 @@
|
||||
// Source: https://gist.github.com/mudge/5830382?permalink_comment_id=2691957#gistcomment-2691957
|
||||
|
||||
export default class EventEmitter {
|
||||
constructor() {
|
||||
/** @type {Events} */
|
||||
this.events = {}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} eventName
|
||||
* @returns {EventHandlers}
|
||||
*/
|
||||
_getHandlersByEventName(eventName) {
|
||||
if (typeof this.events[eventName] === 'undefined') {
|
||||
// not Set because we need to keep the FIFO order
|
||||
// https://github.com/sweetalert2/sweetalert2/pull/2763#discussion_r1748990334
|
||||
this.events[eventName] = []
|
||||
}
|
||||
return this.events[eventName]
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} eventName
|
||||
* @param {EventHandler} eventHandler
|
||||
*/
|
||||
on(eventName, eventHandler) {
|
||||
const currentHandlers = this._getHandlersByEventName(eventName)
|
||||
if (!currentHandlers.includes(eventHandler)) {
|
||||
currentHandlers.push(eventHandler)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} eventName
|
||||
* @param {EventHandler} eventHandler
|
||||
*/
|
||||
once(eventName, eventHandler) {
|
||||
/**
|
||||
* @param {...any} args
|
||||
*/
|
||||
const onceFn = (...args) => {
|
||||
this.removeListener(eventName, onceFn)
|
||||
// @ts-ignore
|
||||
eventHandler.apply(this, args)
|
||||
}
|
||||
this.on(eventName, onceFn)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} eventName
|
||||
* @param {...any} args
|
||||
*/
|
||||
emit(eventName, ...args) {
|
||||
this._getHandlersByEventName(eventName).forEach(
|
||||
/**
|
||||
* @param {EventHandler} eventHandler
|
||||
*/
|
||||
(eventHandler) => {
|
||||
try {
|
||||
// @ts-ignore
|
||||
eventHandler.apply(this, args)
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} eventName
|
||||
* @param {EventHandler} eventHandler
|
||||
*/
|
||||
removeListener(eventName, eventHandler) {
|
||||
const currentHandlers = this._getHandlersByEventName(eventName)
|
||||
const index = currentHandlers.indexOf(eventHandler)
|
||||
if (index > -1) {
|
||||
currentHandlers.splice(index, 1)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} eventName
|
||||
*/
|
||||
removeAllListeners(eventName) {
|
||||
if (this.events[eventName] !== undefined) {
|
||||
// https://github.com/sweetalert2/sweetalert2/pull/2763#discussion_r1749239222
|
||||
this.events[eventName].length = 0
|
||||
}
|
||||
}
|
||||
|
||||
reset() {
|
||||
this.events = {}
|
||||
}
|
||||
}
|
||||
71
public/assets/sweetalert2/src/utils/Timer.js
Normal file
71
public/assets/sweetalert2/src/utils/Timer.js
Normal file
@@ -0,0 +1,71 @@
|
||||
export default class Timer {
|
||||
/**
|
||||
* @param {() => void} callback
|
||||
* @param {number} delay
|
||||
*/
|
||||
constructor(callback, delay) {
|
||||
this.callback = callback
|
||||
this.remaining = delay
|
||||
this.running = false
|
||||
|
||||
this.start()
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {number}
|
||||
*/
|
||||
start() {
|
||||
if (!this.running) {
|
||||
this.running = true
|
||||
this.started = new Date()
|
||||
this.id = setTimeout(this.callback, this.remaining)
|
||||
}
|
||||
return this.remaining
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {number}
|
||||
*/
|
||||
stop() {
|
||||
if (this.started && this.running) {
|
||||
this.running = false
|
||||
clearTimeout(this.id)
|
||||
this.remaining -= new Date().getTime() - this.started.getTime()
|
||||
}
|
||||
return this.remaining
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} n
|
||||
* @returns {number}
|
||||
*/
|
||||
increase(n) {
|
||||
const running = this.running
|
||||
if (running) {
|
||||
this.stop()
|
||||
}
|
||||
this.remaining += n
|
||||
if (running) {
|
||||
this.start()
|
||||
}
|
||||
return this.remaining
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {number}
|
||||
*/
|
||||
getTimerLeft() {
|
||||
if (this.running) {
|
||||
this.stop()
|
||||
this.start()
|
||||
}
|
||||
return this.remaining
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {boolean}
|
||||
*/
|
||||
isRunning() {
|
||||
return this.running
|
||||
}
|
||||
}
|
||||
33
public/assets/sweetalert2/src/utils/aria.js
Normal file
33
public/assets/sweetalert2/src/utils/aria.js
Normal file
@@ -0,0 +1,33 @@
|
||||
import { getContainer } from './dom/getters.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
|
||||
// reader’s list of elements (headings, form controls, landmarks, etc.) in the document.
|
||||
|
||||
export const setAriaHidden = () => {
|
||||
const container = getContainer()
|
||||
const bodyChildren = Array.from(document.body.children)
|
||||
bodyChildren.forEach((el) => {
|
||||
if (el.contains(container)) {
|
||||
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 = Array.from(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')
|
||||
}
|
||||
})
|
||||
}
|
||||
97
public/assets/sweetalert2/src/utils/classes.js
Normal file
97
public/assets/sweetalert2/src/utils/classes.js
Normal file
@@ -0,0 +1,97 @@
|
||||
export const swalPrefix = 'swal2-'
|
||||
|
||||
/**
|
||||
* @typedef {Record<SwalClass, string>} SwalClasses
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {'success' | 'warning' | 'info' | 'question' | 'error'} SwalIcon
|
||||
* @typedef {Record<SwalIcon, string>} SwalIcons
|
||||
*/
|
||||
|
||||
/** @type {SwalClass[]} */
|
||||
const classNames = [
|
||||
'container',
|
||||
'shown',
|
||||
'height-auto',
|
||||
'iosfix',
|
||||
'popup',
|
||||
'modal',
|
||||
'no-backdrop',
|
||||
'no-transition',
|
||||
'toast',
|
||||
'toast-shown',
|
||||
'show',
|
||||
'hide',
|
||||
'close',
|
||||
'title',
|
||||
'html-container',
|
||||
'actions',
|
||||
'confirm',
|
||||
'deny',
|
||||
'cancel',
|
||||
'footer',
|
||||
'icon',
|
||||
'icon-content',
|
||||
'image',
|
||||
'input',
|
||||
'file',
|
||||
'range',
|
||||
'select',
|
||||
'radio',
|
||||
'checkbox',
|
||||
'label',
|
||||
'textarea',
|
||||
'inputerror',
|
||||
'input-label',
|
||||
'validation-message',
|
||||
'progress-steps',
|
||||
'active-progress-step',
|
||||
'progress-step',
|
||||
'progress-step-line',
|
||||
'loader',
|
||||
'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',
|
||||
'draggable',
|
||||
'dragging',
|
||||
]
|
||||
|
||||
export const swalClasses = classNames.reduce((acc, className) => {
|
||||
acc[className] = swalPrefix + className
|
||||
return acc
|
||||
}, /** @type {SwalClasses} */ ({}))
|
||||
|
||||
/** @type {SwalIcon[]} */
|
||||
const icons = ['success', 'warning', 'info', 'question', 'error']
|
||||
|
||||
export const iconTypes = icons.reduce((acc, icon) => {
|
||||
acc[icon] = swalPrefix + icon
|
||||
return acc
|
||||
}, /** @type {SwalIcons} */ ({}))
|
||||
@@ -0,0 +1,23 @@
|
||||
export default {
|
||||
/**
|
||||
* @param {string} string
|
||||
* @param {string} [validationMessage]
|
||||
* @returns {Promise<string | void>}
|
||||
*/
|
||||
email: (string, validationMessage) => {
|
||||
return /^[a-zA-Z0-9.+_'-]+@[a-zA-Z0-9.-]+\.[a-zA-Z0-9-]+$/.test(string)
|
||||
? Promise.resolve()
|
||||
: Promise.resolve(validationMessage || 'Invalid email address')
|
||||
},
|
||||
/**
|
||||
* @param {string} string
|
||||
* @param {string} [validationMessage]
|
||||
* @returns {Promise<string | void>}
|
||||
*/
|
||||
url: (string, validationMessage) => {
|
||||
// taken from https://stackoverflow.com/a/3809435 with a small change from #1306 and #2013
|
||||
return /^https?:\/\/(www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-z]{2,63}\b([-a-zA-Z0-9@:%_+.~#?&/=]*)$/.test(string)
|
||||
? Promise.resolve()
|
||||
: Promise.resolve(validationMessage || 'Invalid URL')
|
||||
},
|
||||
}
|
||||
365
public/assets/sweetalert2/src/utils/dom/domUtils.js
Normal file
365
public/assets/sweetalert2/src/utils/dom/domUtils.js
Normal file
@@ -0,0 +1,365 @@
|
||||
import { iconTypes, swalClasses } from '../classes.js'
|
||||
import { warn } from '../utils.js'
|
||||
import { getCancelButton, getConfirmButton, getDenyButton, getTimerProgressBar } from './getters.js'
|
||||
|
||||
/**
|
||||
* Securely set innerHTML of an element
|
||||
* https://github.com/sweetalert2/sweetalert2/issues/1926
|
||||
*
|
||||
* @param {HTMLElement} elem
|
||||
* @param {string} html
|
||||
*/
|
||||
export const setInnerHtml = (elem, html) => {
|
||||
elem.textContent = ''
|
||||
if (html) {
|
||||
const parser = new DOMParser()
|
||||
const parsed = parser.parseFromString(html, `text/html`)
|
||||
const head = parsed.querySelector('head')
|
||||
if (head) {
|
||||
Array.from(head.childNodes).forEach((child) => {
|
||||
elem.appendChild(child)
|
||||
})
|
||||
}
|
||||
const body = parsed.querySelector('body')
|
||||
if (body) {
|
||||
Array.from(body.childNodes).forEach((child) => {
|
||||
if (child instanceof HTMLVideoElement || child instanceof HTMLAudioElement) {
|
||||
elem.appendChild(child.cloneNode(true)) // https://github.com/sweetalert2/sweetalert2/issues/2507
|
||||
} else {
|
||||
elem.appendChild(child)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {HTMLElement} elem
|
||||
* @param {string} className
|
||||
* @returns {boolean}
|
||||
*/
|
||||
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
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {HTMLElement} elem
|
||||
* @param {SweetAlertOptions} params
|
||||
*/
|
||||
const removeCustomClasses = (elem, params) => {
|
||||
Array.from(elem.classList).forEach((className) => {
|
||||
if (
|
||||
!Object.values(swalClasses).includes(className) &&
|
||||
!Object.values(iconTypes).includes(className) &&
|
||||
!Object.values(params.showClass || {}).includes(className)
|
||||
) {
|
||||
elem.classList.remove(className)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {HTMLElement} elem
|
||||
* @param {SweetAlertOptions} params
|
||||
* @param {string} className
|
||||
*/
|
||||
export const applyCustomClass = (elem, params, className) => {
|
||||
removeCustomClasses(elem, params)
|
||||
|
||||
if (!params.customClass) {
|
||||
return
|
||||
}
|
||||
|
||||
const customClass = params.customClass[/** @type {keyof SweetAlertCustomClass} */ (className)]
|
||||
|
||||
if (!customClass) {
|
||||
return
|
||||
}
|
||||
|
||||
if (typeof customClass !== 'string' && !customClass.forEach) {
|
||||
warn(`Invalid type of customClass.${className}! Expected string or iterable object, got "${typeof customClass}"`)
|
||||
return
|
||||
}
|
||||
|
||||
addClass(elem, customClass)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {HTMLElement} popup
|
||||
* @param {import('./renderers/renderInput').InputClass | SweetAlertInput} inputClass
|
||||
* @returns {HTMLInputElement | null}
|
||||
*/
|
||||
export const getInput = (popup, inputClass) => {
|
||||
if (!inputClass) {
|
||||
return null
|
||||
}
|
||||
switch (inputClass) {
|
||||
case 'select':
|
||||
case 'textarea':
|
||||
case 'file':
|
||||
return popup.querySelector(`.${swalClasses.popup} > .${swalClasses[inputClass]}`)
|
||||
case 'checkbox':
|
||||
return popup.querySelector(`.${swalClasses.popup} > .${swalClasses.checkbox} input`)
|
||||
case 'radio':
|
||||
return (
|
||||
popup.querySelector(`.${swalClasses.popup} > .${swalClasses.radio} input:checked`) ||
|
||||
popup.querySelector(`.${swalClasses.popup} > .${swalClasses.radio} input:first-child`)
|
||||
)
|
||||
case 'range':
|
||||
return popup.querySelector(`.${swalClasses.popup} > .${swalClasses.range} input`)
|
||||
default:
|
||||
return popup.querySelector(`.${swalClasses.popup} > .${swalClasses.input}`)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement} 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
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {HTMLElement | HTMLElement[] | null} target
|
||||
* @param {string | string[] | readonly string[] | undefined} classList
|
||||
* @param {boolean} condition
|
||||
*/
|
||||
export const toggleClass = (target, classList, condition) => {
|
||||
if (!target || !classList) {
|
||||
return
|
||||
}
|
||||
if (typeof classList === 'string') {
|
||||
classList = classList.split(/\s+/).filter(Boolean)
|
||||
}
|
||||
classList.forEach((className) => {
|
||||
if (Array.isArray(target)) {
|
||||
target.forEach((elem) => {
|
||||
if (condition) {
|
||||
elem.classList.add(className)
|
||||
} else {
|
||||
elem.classList.remove(className)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
if (condition) {
|
||||
target.classList.add(className)
|
||||
} else {
|
||||
target.classList.remove(className)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {HTMLElement | HTMLElement[] | null} target
|
||||
* @param {string | string[] | readonly string[] | undefined} classList
|
||||
*/
|
||||
export const addClass = (target, classList) => {
|
||||
toggleClass(target, classList, true)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {HTMLElement | HTMLElement[] | null} target
|
||||
* @param {string | string[] | readonly string[] | undefined} classList
|
||||
*/
|
||||
export const removeClass = (target, classList) => {
|
||||
toggleClass(target, classList, false)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get direct child of an element by class name
|
||||
*
|
||||
* @param {HTMLElement} elem
|
||||
* @param {string} className
|
||||
* @returns {HTMLElement | undefined}
|
||||
*/
|
||||
export const getDirectChildByClass = (elem, className) => {
|
||||
const children = Array.from(elem.children)
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
const child = children[i]
|
||||
if (child instanceof HTMLElement && hasClass(child, className)) {
|
||||
return child
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {HTMLElement} elem
|
||||
* @param {string} property
|
||||
* @param {string | number | null | undefined} value
|
||||
*/
|
||||
export const applyNumericalStyle = (elem, property, value) => {
|
||||
if (value === `${parseInt(`${value}`)}`) {
|
||||
value = parseInt(value)
|
||||
}
|
||||
if (value || parseInt(`${value}`) === 0) {
|
||||
elem.style.setProperty(property, typeof value === 'number' ? `${value}px` : /** @type {string} */ (value))
|
||||
} else {
|
||||
elem.style.removeProperty(property)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {HTMLElement | null} elem
|
||||
* @param {string} display
|
||||
*/
|
||||
export const show = (elem, display = 'flex') => {
|
||||
if (!elem) {
|
||||
return
|
||||
}
|
||||
|
||||
elem.style.display = display
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {HTMLElement | null} elem
|
||||
*/
|
||||
export const hide = (elem) => {
|
||||
if (!elem) {
|
||||
return
|
||||
}
|
||||
|
||||
elem.style.display = 'none'
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {HTMLElement | null} elem
|
||||
* @param {string} display
|
||||
*/
|
||||
export const showWhenInnerHtmlPresent = (elem, display = 'block') => {
|
||||
if (!elem) {
|
||||
return
|
||||
}
|
||||
new MutationObserver(() => {
|
||||
toggle(elem, elem.innerHTML, display)
|
||||
}).observe(elem, { childList: true, subtree: true })
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {HTMLElement} parent
|
||||
* @param {string} selector
|
||||
* @param {string} property
|
||||
* @param {string} value
|
||||
*/
|
||||
export const setStyle = (parent, selector, property, value) => {
|
||||
/** @type {HTMLElement | null} */
|
||||
const el = parent.querySelector(selector)
|
||||
if (el) {
|
||||
el.style.setProperty(property, value)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {HTMLElement} elem
|
||||
* @param {boolean | string | null | undefined} condition
|
||||
* @param {string} display
|
||||
*/
|
||||
export const toggle = (elem, condition, display = 'flex') => {
|
||||
if (condition) {
|
||||
show(elem, display)
|
||||
} else {
|
||||
hide(elem)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* borrowed from jquery $(elem).is(':visible') implementation
|
||||
*
|
||||
* @param {HTMLElement | null} elem
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export const isVisible = (elem) => Boolean(elem && (elem.offsetWidth || elem.offsetHeight || elem.getClientRects().length))
|
||||
|
||||
/**
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export const allButtonsAreHidden = () =>
|
||||
!isVisible(getConfirmButton()) && !isVisible(getDenyButton()) && !isVisible(getCancelButton())
|
||||
|
||||
/**
|
||||
* @param {HTMLElement} elem
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export const isScrollable = (elem) => Boolean(elem.scrollHeight > elem.clientHeight)
|
||||
|
||||
/**
|
||||
* @param {HTMLElement} element
|
||||
* @param {HTMLElement} stopElement
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export const selfOrParentIsScrollable = (element, stopElement) => {
|
||||
let parent = /** @type {HTMLElement | null} */ (element)
|
||||
while (parent && parent !== stopElement) {
|
||||
if (isScrollable(parent)) {
|
||||
return true
|
||||
}
|
||||
parent = parent.parentElement
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* borrowed from https://stackoverflow.com/a/46352119
|
||||
*
|
||||
* @param {HTMLElement} elem
|
||||
* @returns {boolean}
|
||||
*/
|
||||
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
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} timer
|
||||
* @param {boolean} reset
|
||||
*/
|
||||
export const animateTimerProgressBar = (timer, reset = false) => {
|
||||
const timerProgressBar = getTimerProgressBar()
|
||||
if (!timerProgressBar) {
|
||||
return
|
||||
}
|
||||
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()
|
||||
if (!timerProgressBar) {
|
||||
return
|
||||
}
|
||||
const timerProgressBarWidth = parseInt(window.getComputedStyle(timerProgressBar).width)
|
||||
timerProgressBar.style.removeProperty('transition')
|
||||
timerProgressBar.style.width = '100%'
|
||||
const timerProgressBarFullWidth = parseInt(window.getComputedStyle(timerProgressBar).width)
|
||||
const timerProgressBarPercent = (timerProgressBarWidth / timerProgressBarFullWidth) * 100
|
||||
timerProgressBar.style.width = `${timerProgressBarPercent}%`
|
||||
}
|
||||
198
public/assets/sweetalert2/src/utils/dom/getters.js
Normal file
198
public/assets/sweetalert2/src/utils/dom/getters.js
Normal file
@@ -0,0 +1,198 @@
|
||||
import { swalClasses } from '../classes.js'
|
||||
import { hasClass, isVisible } from './domUtils.js'
|
||||
|
||||
/**
|
||||
* Gets the popup container which contains the backdrop and the popup itself.
|
||||
*
|
||||
* @returns {HTMLElement | null}
|
||||
*/
|
||||
export const getContainer = () => document.body.querySelector(`.${swalClasses.container}`)
|
||||
|
||||
/**
|
||||
* @param {string} selectorString
|
||||
* @returns {HTMLElement | null}
|
||||
*/
|
||||
export const elementBySelector = (selectorString) => {
|
||||
const container = getContainer()
|
||||
return container ? container.querySelector(selectorString) : null
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} className
|
||||
* @returns {HTMLElement | null}
|
||||
*/
|
||||
const elementByClass = (className) => {
|
||||
return elementBySelector(`.${className}`)
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {HTMLElement | null}
|
||||
*/
|
||||
export const getPopup = () => elementByClass(swalClasses.popup)
|
||||
|
||||
/**
|
||||
* @returns {HTMLElement | null}
|
||||
*/
|
||||
export const getIcon = () => elementByClass(swalClasses.icon)
|
||||
|
||||
/**
|
||||
* @returns {HTMLElement | null}
|
||||
*/
|
||||
export const getIconContent = () => elementByClass(swalClasses['icon-content'])
|
||||
|
||||
/**
|
||||
* @returns {HTMLElement | null}
|
||||
*/
|
||||
export const getTitle = () => elementByClass(swalClasses.title)
|
||||
|
||||
/**
|
||||
* @returns {HTMLElement | null}
|
||||
*/
|
||||
export const getHtmlContainer = () => elementByClass(swalClasses['html-container'])
|
||||
|
||||
/**
|
||||
* @returns {HTMLElement | null}
|
||||
*/
|
||||
export const getImage = () => elementByClass(swalClasses.image)
|
||||
|
||||
/**
|
||||
* @returns {HTMLElement | null}
|
||||
*/
|
||||
export const getProgressSteps = () => elementByClass(swalClasses['progress-steps'])
|
||||
|
||||
/**
|
||||
* @returns {HTMLElement | null}
|
||||
*/
|
||||
export const getValidationMessage = () => elementByClass(swalClasses['validation-message'])
|
||||
|
||||
/**
|
||||
* @returns {HTMLButtonElement | null}
|
||||
*/
|
||||
export const getConfirmButton = () =>
|
||||
/** @type {HTMLButtonElement} */ (elementBySelector(`.${swalClasses.actions} .${swalClasses.confirm}`))
|
||||
|
||||
/**
|
||||
* @returns {HTMLButtonElement | null}
|
||||
*/
|
||||
export const getCancelButton = () =>
|
||||
/** @type {HTMLButtonElement} */ (elementBySelector(`.${swalClasses.actions} .${swalClasses.cancel}`))
|
||||
|
||||
/**
|
||||
* @returns {HTMLButtonElement | null}
|
||||
*/
|
||||
export const getDenyButton = () =>
|
||||
/** @type {HTMLButtonElement} */ (elementBySelector(`.${swalClasses.actions} .${swalClasses.deny}`))
|
||||
|
||||
/**
|
||||
* @returns {HTMLElement | null}
|
||||
*/
|
||||
export const getInputLabel = () => elementByClass(swalClasses['input-label'])
|
||||
|
||||
/**
|
||||
* @returns {HTMLElement | null}
|
||||
*/
|
||||
export const getLoader = () => elementBySelector(`.${swalClasses.loader}`)
|
||||
|
||||
/**
|
||||
* @returns {HTMLElement | null}
|
||||
*/
|
||||
export const getActions = () => elementByClass(swalClasses.actions)
|
||||
|
||||
/**
|
||||
* @returns {HTMLElement | null}
|
||||
*/
|
||||
export const getFooter = () => elementByClass(swalClasses.footer)
|
||||
|
||||
/**
|
||||
* @returns {HTMLElement | null}
|
||||
*/
|
||||
export const getTimerProgressBar = () => elementByClass(swalClasses['timer-progress-bar'])
|
||||
|
||||
/**
|
||||
* @returns {HTMLElement | null}
|
||||
*/
|
||||
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
|
||||
`
|
||||
/**
|
||||
* @returns {HTMLElement[]}
|
||||
*/
|
||||
export const getFocusableElements = () => {
|
||||
const popup = getPopup()
|
||||
if (!popup) {
|
||||
return []
|
||||
}
|
||||
/** @type {NodeListOf<HTMLElement>} */
|
||||
const focusableElementsWithTabindex = popup.querySelectorAll('[tabindex]:not([tabindex="-1"]):not([tabindex="0"])')
|
||||
const focusableElementsWithTabindexSorted = Array.from(focusableElementsWithTabindex)
|
||||
// sort according to tabindex
|
||||
.sort((a, b) => {
|
||||
const tabindexA = parseInt(a.getAttribute('tabindex') || '0')
|
||||
const tabindexB = parseInt(b.getAttribute('tabindex') || '0')
|
||||
if (tabindexA > tabindexB) {
|
||||
return 1
|
||||
} else if (tabindexA < tabindexB) {
|
||||
return -1
|
||||
}
|
||||
return 0
|
||||
})
|
||||
|
||||
/** @type {NodeListOf<HTMLElement>} */
|
||||
const otherFocusableElements = popup.querySelectorAll(focusable)
|
||||
const otherFocusableElementsFiltered = Array.from(otherFocusableElements).filter(
|
||||
(el) => el.getAttribute('tabindex') !== '-1'
|
||||
)
|
||||
|
||||
return [...new Set(focusableElementsWithTabindexSorted.concat(otherFocusableElementsFiltered))].filter((el) =>
|
||||
isVisible(el)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export const isModal = () => {
|
||||
return (
|
||||
hasClass(document.body, swalClasses.shown) &&
|
||||
!hasClass(document.body, swalClasses['toast-shown']) &&
|
||||
!hasClass(document.body, swalClasses['no-backdrop'])
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export const isToast = () => {
|
||||
const popup = getPopup()
|
||||
if (!popup) {
|
||||
return false
|
||||
}
|
||||
return hasClass(popup, swalClasses.toast)
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export const isLoading = () => {
|
||||
const popup = getPopup()
|
||||
if (!popup) {
|
||||
return false
|
||||
}
|
||||
return popup.hasAttribute('data-loading')
|
||||
}
|
||||
5
public/assets/sweetalert2/src/utils/dom/index.js
Normal file
5
public/assets/sweetalert2/src/utils/dom/index.js
Normal file
@@ -0,0 +1,5 @@
|
||||
export * from './domUtils.js'
|
||||
export * from './init.js'
|
||||
export * from './getters.js'
|
||||
export * from './parseHtmlToContainer.js'
|
||||
export * from './renderers/render.js'
|
||||
193
public/assets/sweetalert2/src/utils/dom/init.js
Normal file
193
public/assets/sweetalert2/src/utils/dom/init.js
Normal file
@@ -0,0 +1,193 @@
|
||||
import globalState from '../../globalState.js'
|
||||
import { swalClasses } from '../classes.js'
|
||||
import { isNodeEnv } from '../isNodeEnv.js'
|
||||
import { error } from '../utils.js'
|
||||
import { addClass, getDirectChildByClass, removeClass, setInnerHtml } from './domUtils.js'
|
||||
import { getContainer, getPopup } from './getters.js'
|
||||
|
||||
const sweetHTML = `
|
||||
<div aria-labelledby="${swalClasses.title}" aria-describedby="${swalClasses['html-container']}" class="${swalClasses.popup}" tabindex="-1">
|
||||
<button type="button" class="${swalClasses.close}"></button>
|
||||
<ul class="${swalClasses['progress-steps']}"></ul>
|
||||
<div class="${swalClasses.icon}"></div>
|
||||
<img class="${swalClasses.image}" />
|
||||
<h2 class="${swalClasses.title}" id="${swalClasses.title}"></h2>
|
||||
<div class="${swalClasses['html-container']}" id="${swalClasses['html-container']}"></div>
|
||||
<input class="${swalClasses.input}" id="${swalClasses.input}" />
|
||||
<input type="file" class="${swalClasses.file}" />
|
||||
<div class="${swalClasses.range}">
|
||||
<input type="range" />
|
||||
<output></output>
|
||||
</div>
|
||||
<select class="${swalClasses.select}" id="${swalClasses.select}"></select>
|
||||
<div class="${swalClasses.radio}"></div>
|
||||
<label class="${swalClasses.checkbox}">
|
||||
<input type="checkbox" id="${swalClasses.checkbox}" />
|
||||
<span class="${swalClasses.label}"></span>
|
||||
</label>
|
||||
<textarea class="${swalClasses.textarea}" id="${swalClasses.textarea}"></textarea>
|
||||
<div class="${swalClasses['validation-message']}" id="${swalClasses['validation-message']}"></div>
|
||||
<div class="${swalClasses.actions}">
|
||||
<div class="${swalClasses.loader}"></div>
|
||||
<button type="button" class="${swalClasses.confirm}"></button>
|
||||
<button type="button" class="${swalClasses.deny}"></button>
|
||||
<button type="button" class="${swalClasses.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, '')
|
||||
|
||||
/**
|
||||
* @returns {boolean}
|
||||
*/
|
||||
const resetOldContainer = () => {
|
||||
const oldContainer = getContainer()
|
||||
if (!oldContainer) {
|
||||
return false
|
||||
}
|
||||
|
||||
oldContainer.remove()
|
||||
removeClass(
|
||||
[document.documentElement, document.body],
|
||||
[
|
||||
swalClasses['no-backdrop'],
|
||||
swalClasses['toast-shown'],
|
||||
// @ts-ignore: 'has-column' is not defined in swalClasses but may be set dynamically
|
||||
swalClasses['has-column'],
|
||||
]
|
||||
)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
const resetValidationMessage = () => {
|
||||
if (globalState.currentInstance) {
|
||||
globalState.currentInstance.resetValidationMessage()
|
||||
}
|
||||
}
|
||||
|
||||
const addInputChangeListeners = () => {
|
||||
const popup = getPopup()
|
||||
if (!popup) {
|
||||
return
|
||||
}
|
||||
|
||||
const input = getDirectChildByClass(popup, swalClasses.input)
|
||||
const file = getDirectChildByClass(popup, swalClasses.file)
|
||||
/** @type {HTMLInputElement | null} */
|
||||
const range = popup.querySelector(`.${swalClasses.range} input`)
|
||||
/** @type {HTMLOutputElement | null} */
|
||||
const rangeOutput = popup.querySelector(`.${swalClasses.range} output`)
|
||||
const select = getDirectChildByClass(popup, swalClasses.select)
|
||||
/** @type {HTMLInputElement | null} */
|
||||
const checkbox = popup.querySelector(`.${swalClasses.checkbox} input`)
|
||||
const textarea = getDirectChildByClass(popup, swalClasses.textarea)
|
||||
|
||||
if (input) {
|
||||
input.oninput = resetValidationMessage
|
||||
}
|
||||
if (file) {
|
||||
file.onchange = resetValidationMessage
|
||||
}
|
||||
if (select) {
|
||||
select.onchange = resetValidationMessage
|
||||
}
|
||||
if (checkbox) {
|
||||
checkbox.onchange = resetValidationMessage
|
||||
}
|
||||
if (textarea) {
|
||||
textarea.oninput = resetValidationMessage
|
||||
}
|
||||
|
||||
if (range && rangeOutput) {
|
||||
range.oninput = () => {
|
||||
resetValidationMessage()
|
||||
rangeOutput.value = range.value
|
||||
}
|
||||
|
||||
range.onchange = () => {
|
||||
resetValidationMessage()
|
||||
rangeOutput.value = range.value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string | HTMLElement} target
|
||||
* @returns {HTMLElement}
|
||||
*/
|
||||
const getTarget = (target) => {
|
||||
if (typeof target === 'string') {
|
||||
const element = document.querySelector(target)
|
||||
if (!element) {
|
||||
throw new Error(`Target element "${target}" not found`)
|
||||
}
|
||||
return /** @type {HTMLElement} */ (element)
|
||||
}
|
||||
return target
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {SweetAlertOptions} params
|
||||
*/
|
||||
const setupAccessibility = (params) => {
|
||||
const popup = getPopup()
|
||||
if (!popup) {
|
||||
return
|
||||
}
|
||||
|
||||
popup.setAttribute('role', params.toast ? 'alert' : 'dialog')
|
||||
popup.setAttribute('aria-live', params.toast ? 'polite' : 'assertive')
|
||||
if (!params.toast) {
|
||||
popup.setAttribute('aria-modal', 'true')
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {HTMLElement} targetElement
|
||||
*/
|
||||
const setupRTL = (targetElement) => {
|
||||
if (window.getComputedStyle(targetElement).direction === 'rtl') {
|
||||
addClass(getContainer(), swalClasses.rtl)
|
||||
globalState.isRTL = true
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add modal + backdrop to DOM
|
||||
*
|
||||
* @param {SweetAlertOptions} params
|
||||
*/
|
||||
export const init = (params) => {
|
||||
// Clean up the old popup container if it exists
|
||||
const oldContainerExisted = resetOldContainer()
|
||||
|
||||
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)
|
||||
|
||||
container.dataset['swal2Theme'] = params.theme
|
||||
|
||||
const targetElement = getTarget(params.target || 'body')
|
||||
targetElement.appendChild(container)
|
||||
|
||||
if (params.topLayer) {
|
||||
container.setAttribute('popover', '')
|
||||
container.showPopover()
|
||||
}
|
||||
|
||||
setupAccessibility(params)
|
||||
setupRTL(targetElement)
|
||||
addInputChangeListeners()
|
||||
}
|
||||
239
public/assets/sweetalert2/src/utils/dom/inputUtils.js
Normal file
239
public/assets/sweetalert2/src/utils/dom/inputUtils.js
Normal file
@@ -0,0 +1,239 @@
|
||||
import { showLoading } from '../../staticMethods/showLoading.js'
|
||||
import { swalClasses } from '../classes.js'
|
||||
import { asPromise, error, hasToPromiseFn, isPromise } from '../utils.js'
|
||||
import { getDirectChildByClass } from './domUtils.js'
|
||||
import * as dom from './index.js'
|
||||
|
||||
/**
|
||||
* @param {SweetAlert} instance
|
||||
* @param {SweetAlertOptions} params
|
||||
*/
|
||||
export const handleInputOptionsAndValue = (instance, params) => {
|
||||
if (params.input === 'select' || params.input === 'radio') {
|
||||
handleInputOptions(instance, params)
|
||||
} else if (
|
||||
['text', 'email', 'number', 'tel', 'textarea'].some((i) => i === params.input) &&
|
||||
(hasToPromiseFn(params.inputValue) || isPromise(params.inputValue))
|
||||
) {
|
||||
showLoading(dom.getConfirmButton())
|
||||
handleInputValue(instance, params)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {SweetAlert} instance
|
||||
* @param {SweetAlertOptions} innerParams
|
||||
* @returns {SweetAlertInputValue}
|
||||
*/
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {HTMLInputElement} input
|
||||
* @returns {number}
|
||||
*/
|
||||
const getCheckboxValue = (input) => (input.checked ? 1 : 0)
|
||||
|
||||
/**
|
||||
* @param {HTMLInputElement} input
|
||||
* @returns {string | null}
|
||||
*/
|
||||
const getRadioValue = (input) => (input.checked ? input.value : null)
|
||||
|
||||
/**
|
||||
* @param {HTMLInputElement} input
|
||||
* @returns {FileList | File | null}
|
||||
*/
|
||||
const getFileValue = (input) =>
|
||||
input.files && input.files.length ? (input.getAttribute('multiple') !== null ? input.files : input.files[0]) : null
|
||||
|
||||
/**
|
||||
* @param {SweetAlert} instance
|
||||
* @param {SweetAlertOptions} params
|
||||
*/
|
||||
const handleInputOptions = (instance, params) => {
|
||||
const popup = dom.getPopup()
|
||||
if (!popup) {
|
||||
return
|
||||
}
|
||||
/**
|
||||
* @param {*} inputOptions
|
||||
*/
|
||||
const processInputOptions = (inputOptions) => {
|
||||
if (params.input === 'select') {
|
||||
populateSelectOptions(popup, formatInputOptions(inputOptions), params)
|
||||
} else if (params.input === 'radio') {
|
||||
populateRadioOptions(popup, formatInputOptions(inputOptions), params)
|
||||
}
|
||||
}
|
||||
if (hasToPromiseFn(params.inputOptions) || isPromise(params.inputOptions)) {
|
||||
showLoading(dom.getConfirmButton())
|
||||
asPromise(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}`)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {SweetAlert} instance
|
||||
* @param {SweetAlertOptions} params
|
||||
*/
|
||||
const handleInputValue = (instance, params) => {
|
||||
const input = instance.getInput()
|
||||
if (!input) {
|
||||
return
|
||||
}
|
||||
dom.hide(input)
|
||||
asPromise(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()
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {HTMLElement} popup
|
||||
* @param {InputOptionFlattened[]} inputOptions
|
||||
* @param {SweetAlertOptions} params
|
||||
*/
|
||||
function populateSelectOptions(popup, inputOptions, params) {
|
||||
const select = getDirectChildByClass(popup, swalClasses.select)
|
||||
if (!select) {
|
||||
return
|
||||
}
|
||||
/**
|
||||
* @param {HTMLElement} parent
|
||||
* @param {string} optionLabel
|
||||
* @param {string} optionValue
|
||||
*/
|
||||
const renderOption = (parent, optionLabel, optionValue) => {
|
||||
const option = document.createElement('option')
|
||||
option.value = optionValue
|
||||
dom.setInnerHtml(option, optionLabel)
|
||||
option.selected = isSelected(optionValue, params.inputValue)
|
||||
parent.appendChild(option)
|
||||
}
|
||||
inputOptions.forEach((inputOption) => {
|
||||
const optionValue = inputOption[0]
|
||||
const optionLabel = inputOption[1]
|
||||
// <optgroup> spec:
|
||||
// https://www.w3.org/TR/html401/interact/forms.html#h-17.6
|
||||
// "...all OPTGROUP elements must be specified directly within a SELECT element (i.e., groups may not be nested)..."
|
||||
// check whether this is a <optgroup>
|
||||
if (Array.isArray(optionLabel)) {
|
||||
// if it is an array, then it is an <optgroup>
|
||||
const optgroup = document.createElement('optgroup')
|
||||
optgroup.label = optionValue
|
||||
optgroup.disabled = false // not configurable for now
|
||||
select.appendChild(optgroup)
|
||||
optionLabel.forEach((o) => renderOption(optgroup, o[1], o[0]))
|
||||
} else {
|
||||
// case of <option>
|
||||
renderOption(select, optionLabel, optionValue)
|
||||
}
|
||||
})
|
||||
select.focus()
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {HTMLElement} popup
|
||||
* @param {InputOptionFlattened[]} inputOptions
|
||||
* @param {SweetAlertOptions} params
|
||||
*/
|
||||
function populateRadioOptions(popup, inputOptions, params) {
|
||||
const radio = getDirectChildByClass(popup, swalClasses.radio)
|
||||
if (!radio) {
|
||||
return
|
||||
}
|
||||
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 (isSelected(radioValue, params.inputValue)) {
|
||||
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
|
||||
* @typedef {string[]} InputOptionFlattened
|
||||
* @returns {InputOptionFlattened[]}
|
||||
*/
|
||||
const formatInputOptions = (inputOptions) => {
|
||||
/** @type {InputOptionFlattened[]} */
|
||||
const result = []
|
||||
if (inputOptions instanceof Map) {
|
||||
inputOptions.forEach((value, key) => {
|
||||
let valueFormatted = value
|
||||
if (typeof valueFormatted === 'object') {
|
||||
// case of <optgroup>
|
||||
valueFormatted = formatInputOptions(valueFormatted)
|
||||
}
|
||||
result.push([key, valueFormatted])
|
||||
})
|
||||
} else {
|
||||
Object.keys(inputOptions).forEach((key) => {
|
||||
let valueFormatted = inputOptions[key]
|
||||
if (typeof valueFormatted === 'object') {
|
||||
// case of <optgroup>
|
||||
valueFormatted = formatInputOptions(valueFormatted)
|
||||
}
|
||||
result.push([key, valueFormatted])
|
||||
})
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} optionValue
|
||||
* @param {SweetAlertInputValue} inputValue
|
||||
* @returns {boolean}
|
||||
*/
|
||||
const isSelected = (optionValue, inputValue) => {
|
||||
return Boolean(inputValue) && (inputValue !== null && inputValue !== undefined) && inputValue.toString() === optionValue.toString()
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
import { setInnerHtml } from './domUtils.js'
|
||||
|
||||
/**
|
||||
* @param {HTMLElement | object | string} param
|
||||
* @param {HTMLElement} target
|
||||
*/
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {object} param
|
||||
* @param {HTMLElement} target
|
||||
*/
|
||||
const handleObject = (param, target) => {
|
||||
// JQuery element(s)
|
||||
if ('jquery' in param) {
|
||||
handleJqueryElem(target, param)
|
||||
}
|
||||
|
||||
// For other objects use their string representation
|
||||
else {
|
||||
setInnerHtml(target, param.toString())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {HTMLElement} target
|
||||
* @param {any} elem
|
||||
*/
|
||||
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))
|
||||
}
|
||||
}
|
||||
37
public/assets/sweetalert2/src/utils/dom/renderers/render.js
Normal file
37
public/assets/sweetalert2/src/utils/dom/renderers/render.js
Normal file
@@ -0,0 +1,37 @@
|
||||
import globalState from '../../../globalState.js'
|
||||
import { getPopup } from '../getters.js'
|
||||
import { renderActions } from './renderActions.js'
|
||||
import { renderCloseButton } from './renderCloseButton.js'
|
||||
import { renderContainer } from './renderContainer.js'
|
||||
import { renderContent } from './renderContent.js'
|
||||
import { renderFooter } from './renderFooter.js'
|
||||
import { renderIcon } from './renderIcon.js'
|
||||
import { renderImage } from './renderImage.js'
|
||||
import { renderPopup } from './renderPopup.js'
|
||||
import { renderProgressSteps } from './renderProgressSteps.js'
|
||||
import { renderTitle } from './renderTitle.js'
|
||||
|
||||
/**
|
||||
* @param {SweetAlert} instance
|
||||
* @param {SweetAlertOptions} params
|
||||
*/
|
||||
export const render = (instance, params) => {
|
||||
renderPopup(instance, params)
|
||||
renderContainer(instance, params)
|
||||
|
||||
renderProgressSteps(instance, params)
|
||||
renderIcon(instance, params)
|
||||
renderImage(instance, params)
|
||||
renderTitle(instance, params)
|
||||
renderCloseButton(instance, params)
|
||||
|
||||
renderContent(instance, params)
|
||||
renderActions(instance, params)
|
||||
renderFooter(instance, params)
|
||||
|
||||
const popup = getPopup()
|
||||
if (typeof params.didRender === 'function' && popup) {
|
||||
params.didRender(popup)
|
||||
}
|
||||
globalState.eventEmitter?.emit('didRender', popup)
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
import { swalClasses } from '../../classes.js'
|
||||
import * as dom from '../../dom/index.js'
|
||||
import { capitalizeFirstLetter } from '../../utils.js'
|
||||
|
||||
/**
|
||||
* @param {SweetAlert} instance
|
||||
* @param {SweetAlertOptions} params
|
||||
*/
|
||||
export const renderActions = (instance, params) => {
|
||||
const actions = dom.getActions()
|
||||
const loader = dom.getLoader()
|
||||
if (!actions || !loader) {
|
||||
return
|
||||
}
|
||||
|
||||
// Actions (buttons) wrapper
|
||||
if (!params.showConfirmButton && !params.showDenyButton && !params.showCancelButton) {
|
||||
dom.hide(actions)
|
||||
} else {
|
||||
dom.show(actions)
|
||||
}
|
||||
|
||||
// Custom class
|
||||
dom.applyCustomClass(actions, params, 'actions')
|
||||
|
||||
// Render all the buttons
|
||||
renderButtons(actions, loader, params)
|
||||
|
||||
// Loader
|
||||
dom.setInnerHtml(loader, params.loaderHtml || '')
|
||||
dom.applyCustomClass(loader, params, 'loader')
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {HTMLElement} actions
|
||||
* @param {HTMLElement} loader
|
||||
* @param {SweetAlertOptions} params
|
||||
*/
|
||||
function renderButtons(actions, loader, params) {
|
||||
const confirmButton = dom.getConfirmButton()
|
||||
const denyButton = dom.getDenyButton()
|
||||
const cancelButton = dom.getCancelButton()
|
||||
if (!confirmButton || !denyButton || !cancelButton) {
|
||||
return
|
||||
}
|
||||
|
||||
// Render buttons
|
||||
renderButton(confirmButton, 'confirm', params)
|
||||
renderButton(denyButton, 'deny', params)
|
||||
renderButton(cancelButton, 'cancel', params)
|
||||
handleButtonsStyling(confirmButton, denyButton, cancelButton, params)
|
||||
|
||||
if (params.reverseButtons) {
|
||||
if (params.toast) {
|
||||
actions.insertBefore(cancelButton, confirmButton)
|
||||
actions.insertBefore(denyButton, confirmButton)
|
||||
} else {
|
||||
actions.insertBefore(cancelButton, loader)
|
||||
actions.insertBefore(denyButton, loader)
|
||||
actions.insertBefore(confirmButton, loader)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {HTMLElement} confirmButton
|
||||
* @param {HTMLElement} denyButton
|
||||
* @param {HTMLElement} cancelButton
|
||||
* @param {SweetAlertOptions} params
|
||||
*/
|
||||
function handleButtonsStyling(confirmButton, denyButton, cancelButton, params) {
|
||||
if (!params.buttonsStyling) {
|
||||
dom.removeClass([confirmButton, denyButton, cancelButton], swalClasses.styled)
|
||||
return
|
||||
}
|
||||
|
||||
dom.addClass([confirmButton, denyButton, cancelButton], swalClasses.styled)
|
||||
|
||||
// Apply custom background colors to action buttons
|
||||
if (params.confirmButtonColor) {
|
||||
confirmButton.style.setProperty('--swal2-confirm-button-background-color', params.confirmButtonColor)
|
||||
}
|
||||
if (params.denyButtonColor) {
|
||||
denyButton.style.setProperty('--swal2-deny-button-background-color', params.denyButtonColor)
|
||||
}
|
||||
if (params.cancelButtonColor) {
|
||||
cancelButton.style.setProperty('--swal2-cancel-button-background-color', params.cancelButtonColor)
|
||||
}
|
||||
|
||||
// Apply the outline color to action buttons
|
||||
applyOutlineColor(confirmButton)
|
||||
applyOutlineColor(denyButton)
|
||||
applyOutlineColor(cancelButton)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {HTMLElement} button
|
||||
*/
|
||||
function applyOutlineColor(button) {
|
||||
const buttonStyle = window.getComputedStyle(button)
|
||||
if (buttonStyle.getPropertyValue('--swal2-action-button-focus-box-shadow')) {
|
||||
// If the button already has a custom outline color, no need to change it
|
||||
return
|
||||
}
|
||||
const outlineColor = buttonStyle.backgroundColor.replace(/rgba?\((\d+), (\d+), (\d+).*/, 'rgba($1, $2, $3, 0.5)')
|
||||
button.style.setProperty(
|
||||
'--swal2-action-button-focus-box-shadow',
|
||||
buttonStyle.getPropertyValue('--swal2-outline').replace(/ rgba\(.*/, ` ${outlineColor}`)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {HTMLElement} button
|
||||
* @param {'confirm' | 'deny' | 'cancel'} buttonType
|
||||
* @param {SweetAlertOptions} params
|
||||
*/
|
||||
function renderButton(button, buttonType, params) {
|
||||
const buttonName = /** @type {'Confirm' | 'Deny' | 'Cancel'} */ (capitalizeFirstLetter(buttonType))
|
||||
|
||||
dom.toggle(button, params[`show${buttonName}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`)
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
import * as dom from '../../dom/index.js'
|
||||
|
||||
/**
|
||||
* @param {SweetAlert} instance
|
||||
* @param {SweetAlertOptions} params
|
||||
*/
|
||||
export const renderCloseButton = (instance, params) => {
|
||||
const closeButton = dom.getCloseButton()
|
||||
if (!closeButton) {
|
||||
return
|
||||
}
|
||||
|
||||
dom.setInnerHtml(closeButton, params.closeButtonHtml || '')
|
||||
|
||||
// Custom class
|
||||
dom.applyCustomClass(closeButton, params, 'closeButton')
|
||||
|
||||
dom.toggle(closeButton, params.showCloseButton)
|
||||
closeButton.setAttribute('aria-label', params.closeButtonAriaLabel || '')
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
import { swalClasses } from '../../classes.js'
|
||||
import * as dom from '../../dom/index.js'
|
||||
import { warn } from '../../utils.js'
|
||||
|
||||
/**
|
||||
* @param {SweetAlert} instance
|
||||
* @param {SweetAlertOptions} params
|
||||
*/
|
||||
export const renderContainer = (instance, params) => {
|
||||
const container = dom.getContainer()
|
||||
|
||||
if (!container) {
|
||||
return
|
||||
}
|
||||
|
||||
handleBackdropParam(container, params.backdrop)
|
||||
|
||||
handlePositionParam(container, params.position)
|
||||
handleGrowParam(container, params.grow)
|
||||
|
||||
// Custom class
|
||||
dom.applyCustomClass(container, params, 'container')
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {HTMLElement} container
|
||||
* @param {SweetAlertOptions['backdrop']} backdrop
|
||||
*/
|
||||
function handleBackdropParam(container, backdrop) {
|
||||
if (typeof backdrop === 'string') {
|
||||
container.style.background = backdrop
|
||||
} else if (!backdrop) {
|
||||
dom.addClass([document.documentElement, document.body], swalClasses['no-backdrop'])
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {HTMLElement} container
|
||||
* @param {SweetAlertOptions['position']} position
|
||||
*/
|
||||
function handlePositionParam(container, position) {
|
||||
if (!position) {
|
||||
return
|
||||
}
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {HTMLElement} container
|
||||
* @param {SweetAlertOptions['grow']} grow
|
||||
*/
|
||||
function handleGrowParam(container, grow) {
|
||||
if (!grow) {
|
||||
return
|
||||
}
|
||||
dom.addClass(container, swalClasses[`grow-${grow}`])
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
import * as dom from '../../dom/index.js'
|
||||
import { renderInput } from './renderInput.js'
|
||||
|
||||
/**
|
||||
* @param {SweetAlert} instance
|
||||
* @param {SweetAlertOptions} params
|
||||
*/
|
||||
export const renderContent = (instance, params) => {
|
||||
const htmlContainer = dom.getHtmlContainer()
|
||||
if (!htmlContainer) {
|
||||
return
|
||||
}
|
||||
|
||||
dom.showWhenInnerHtmlPresent(htmlContainer)
|
||||
|
||||
dom.applyCustomClass(htmlContainer, params, 'htmlContainer')
|
||||
|
||||
// Content as HTML
|
||||
if (params.html) {
|
||||
dom.parseHtmlToContainer(params.html, htmlContainer)
|
||||
dom.show(htmlContainer, 'block')
|
||||
}
|
||||
|
||||
// Content as plain text
|
||||
else if (params.text) {
|
||||
htmlContainer.textContent = params.text
|
||||
dom.show(htmlContainer, 'block')
|
||||
}
|
||||
|
||||
// No content
|
||||
else {
|
||||
dom.hide(htmlContainer)
|
||||
}
|
||||
|
||||
renderInput(instance, params)
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import * as dom from '../../dom/index.js'
|
||||
|
||||
/**
|
||||
* @param {SweetAlert} instance
|
||||
* @param {SweetAlertOptions} params
|
||||
*/
|
||||
export const renderFooter = (instance, params) => {
|
||||
const footer = dom.getFooter()
|
||||
if (!footer) {
|
||||
return
|
||||
}
|
||||
|
||||
dom.showWhenInnerHtmlPresent(footer)
|
||||
|
||||
dom.toggle(footer, Boolean(params.footer), 'block')
|
||||
|
||||
if (params.footer) {
|
||||
dom.parseHtmlToContainer(params.footer, footer)
|
||||
}
|
||||
|
||||
// Custom class
|
||||
dom.applyCustomClass(footer, params, 'footer')
|
||||
}
|
||||
164
public/assets/sweetalert2/src/utils/dom/renderers/renderIcon.js
Normal file
164
public/assets/sweetalert2/src/utils/dom/renderers/renderIcon.js
Normal file
@@ -0,0 +1,164 @@
|
||||
import privateProps from '../../../privateProps.js'
|
||||
import { iconTypes, swalClasses } from '../../classes.js'
|
||||
import * as dom from '../../dom/index.js'
|
||||
import { error } from '../../utils.js'
|
||||
|
||||
/**
|
||||
* @param {SweetAlert} instance
|
||||
* @param {SweetAlertOptions} params
|
||||
*/
|
||||
export const renderIcon = (instance, params) => {
|
||||
const innerParams = privateProps.innerParams.get(instance)
|
||||
const icon = dom.getIcon()
|
||||
if (!icon) {
|
||||
return
|
||||
}
|
||||
|
||||
// if the given icon already rendered, apply the styling without re-rendering the icon
|
||||
if (innerParams && params.icon === innerParams.icon) {
|
||||
// Custom or default content
|
||||
setContent(icon, params)
|
||||
|
||||
applyStyles(icon, params)
|
||||
return
|
||||
}
|
||||
|
||||
if (!params.icon && !params.iconHtml) {
|
||||
dom.hide(icon)
|
||||
return
|
||||
}
|
||||
|
||||
if (params.icon && Object.keys(iconTypes).indexOf(params.icon) === -1) {
|
||||
error(`Unknown icon! Expected "success", "error", "warning", "info" or "question", got "${params.icon}"`)
|
||||
dom.hide(icon)
|
||||
return
|
||||
}
|
||||
|
||||
dom.show(icon)
|
||||
|
||||
// Custom or default content
|
||||
setContent(icon, params)
|
||||
|
||||
applyStyles(icon, params)
|
||||
|
||||
// Animate icon
|
||||
dom.addClass(icon, params.showClass && params.showClass.icon)
|
||||
|
||||
// Re-adjust the success icon on system theme change
|
||||
const colorSchemeQueryList = window.matchMedia('(prefers-color-scheme: dark)')
|
||||
colorSchemeQueryList.addEventListener('change', adjustSuccessIconBackgroundColor)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {HTMLElement} icon
|
||||
* @param {SweetAlertOptions} params
|
||||
*/
|
||||
const applyStyles = (icon, params) => {
|
||||
for (const [iconType, iconClassName] of Object.entries(iconTypes)) {
|
||||
if (params.icon !== iconType) {
|
||||
dom.removeClass(icon, iconClassName)
|
||||
}
|
||||
}
|
||||
dom.addClass(icon, params.icon && iconTypes[params.icon])
|
||||
|
||||
// Icon color
|
||||
setColor(icon, params)
|
||||
|
||||
// Success icon background color
|
||||
adjustSuccessIconBackgroundColor()
|
||||
|
||||
// Custom class
|
||||
dom.applyCustomClass(icon, params, 'icon')
|
||||
}
|
||||
|
||||
// Adjust success icon background color to match the popup background color
|
||||
const adjustSuccessIconBackgroundColor = () => {
|
||||
const popup = dom.getPopup()
|
||||
if (!popup) {
|
||||
return
|
||||
}
|
||||
const popupBackgroundColor = window.getComputedStyle(popup).getPropertyValue('background-color')
|
||||
/** @type {NodeListOf<HTMLElement>} */
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {SweetAlertOptions} params
|
||||
* @returns {string}
|
||||
*/
|
||||
const successIconHtml = (params) => `
|
||||
${params.animation ? '<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>
|
||||
${params.animation ? '<div class="swal2-success-fix"></div>' : ''}
|
||||
${params.animation ? '<div class="swal2-success-circular-line-right"></div>' : ''}
|
||||
`
|
||||
|
||||
const errorIconHtml = `
|
||||
<span class="swal2-x-mark">
|
||||
<span class="swal2-x-mark-line-left"></span>
|
||||
<span class="swal2-x-mark-line-right"></span>
|
||||
</span>
|
||||
`
|
||||
|
||||
/**
|
||||
* @param {HTMLElement} icon
|
||||
* @param {SweetAlertOptions} params
|
||||
*/
|
||||
const setContent = (icon, params) => {
|
||||
if (!params.icon && !params.iconHtml) {
|
||||
return
|
||||
}
|
||||
let oldContent = icon.innerHTML
|
||||
let newContent = ''
|
||||
if (params.iconHtml) {
|
||||
newContent = iconContent(params.iconHtml)
|
||||
} else if (params.icon === 'success') {
|
||||
newContent = successIconHtml(params)
|
||||
oldContent = oldContent.replace(/ style=".*?"/g, '') // undo adjustSuccessIconBackgroundColor()
|
||||
} else if (params.icon === 'error') {
|
||||
newContent = errorIconHtml
|
||||
} else if (params.icon) {
|
||||
const defaultIconHtml = {
|
||||
question: '?',
|
||||
warning: '!',
|
||||
info: 'i',
|
||||
}
|
||||
newContent = iconContent(defaultIconHtml[params.icon])
|
||||
}
|
||||
|
||||
if (oldContent.trim() !== newContent.trim()) {
|
||||
dom.setInnerHtml(icon, newContent)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {HTMLElement} icon
|
||||
* @param {SweetAlertOptions} params
|
||||
*/
|
||||
const setColor = (icon, params) => {
|
||||
if (!params.iconColor) {
|
||||
return
|
||||
}
|
||||
icon.style.color = params.iconColor
|
||||
icon.style.borderColor = params.iconColor
|
||||
for (const sel of [
|
||||
'.swal2-success-line-tip',
|
||||
'.swal2-success-line-long',
|
||||
'.swal2-x-mark-line-left',
|
||||
'.swal2-x-mark-line-right',
|
||||
]) {
|
||||
dom.setStyle(icon, sel, 'background-color', params.iconColor)
|
||||
}
|
||||
dom.setStyle(icon, '.swal2-success-ring', 'border-color', params.iconColor)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} content
|
||||
* @returns {string}
|
||||
*/
|
||||
const iconContent = (content) => `<div class="${swalClasses['icon-content']}">${content}</div>`
|
||||
@@ -0,0 +1,32 @@
|
||||
import { swalClasses } from '../../classes.js'
|
||||
import * as dom from '../../dom/index.js'
|
||||
|
||||
/**
|
||||
* @param {SweetAlert} instance
|
||||
* @param {SweetAlertOptions} params
|
||||
*/
|
||||
export const renderImage = (instance, params) => {
|
||||
const image = dom.getImage()
|
||||
if (!image) {
|
||||
return
|
||||
}
|
||||
|
||||
if (!params.imageUrl) {
|
||||
dom.hide(image)
|
||||
return
|
||||
}
|
||||
|
||||
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')
|
||||
}
|
||||
353
public/assets/sweetalert2/src/utils/dom/renderers/renderInput.js
Normal file
353
public/assets/sweetalert2/src/utils/dom/renderers/renderInput.js
Normal file
@@ -0,0 +1,353 @@
|
||||
/// <reference path="../../../../sweetalert2.d.ts"/>
|
||||
|
||||
/**
|
||||
* @typedef { HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement } Input
|
||||
* @typedef { 'input' | 'file' | 'range' | 'select' | 'radio' | 'checkbox' | 'textarea' } InputClass
|
||||
*/
|
||||
import privateProps from '../../../privateProps.js'
|
||||
import { swalClasses } from '../../classes.js'
|
||||
import * as dom from '../../dom/index.js'
|
||||
import { error, isPromise, warn } from '../../utils.js'
|
||||
|
||||
/** @type {InputClass[]} */
|
||||
const inputClasses = ['input', 'file', 'range', 'select', 'radio', 'checkbox', 'textarea']
|
||||
|
||||
/**
|
||||
* @param {SweetAlert} instance
|
||||
* @param {SweetAlertOptions} params
|
||||
*/
|
||||
export const renderInput = (instance, params) => {
|
||||
const popup = dom.getPopup()
|
||||
if (!popup) {
|
||||
return
|
||||
}
|
||||
const innerParams = privateProps.innerParams.get(instance)
|
||||
const rerender = !innerParams || params.input !== innerParams.input
|
||||
|
||||
inputClasses.forEach((inputClass) => {
|
||||
const inputContainer = dom.getDirectChildByClass(popup, swalClasses[inputClass])
|
||||
|
||||
if (!inputContainer) {
|
||||
return
|
||||
}
|
||||
|
||||
// set attributes
|
||||
setAttributes(inputClass, params.inputAttributes)
|
||||
|
||||
// set class
|
||||
inputContainer.className = swalClasses[inputClass]
|
||||
|
||||
if (rerender) {
|
||||
dom.hide(inputContainer)
|
||||
}
|
||||
})
|
||||
|
||||
if (params.input) {
|
||||
if (rerender) {
|
||||
showInput(params)
|
||||
}
|
||||
// set custom class
|
||||
setCustomClass(params)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {SweetAlertOptions} params
|
||||
*/
|
||||
const showInput = (params) => {
|
||||
if (!params.input) {
|
||||
return
|
||||
}
|
||||
|
||||
if (!renderInputType[params.input]) {
|
||||
error(`Unexpected type of input! Expected ${Object.keys(renderInputType).join(' | ')}, got "${params.input}"`)
|
||||
return
|
||||
}
|
||||
|
||||
const inputContainer = getInputContainer(params.input)
|
||||
if (!inputContainer) {
|
||||
return
|
||||
}
|
||||
|
||||
const input = renderInputType[params.input](inputContainer, params)
|
||||
dom.show(inputContainer)
|
||||
|
||||
// input autofocus
|
||||
if (params.inputAutoFocus) {
|
||||
setTimeout(() => {
|
||||
dom.focusInput(input)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {HTMLInputElement} input
|
||||
*/
|
||||
const removeAttributes = (input) => {
|
||||
for (let i = 0; i < input.attributes.length; i++) {
|
||||
const attrName = input.attributes[i].name
|
||||
if (!['id', 'type', 'value', 'style'].includes(attrName)) {
|
||||
input.removeAttribute(attrName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {InputClass} inputClass
|
||||
* @param {SweetAlertOptions['inputAttributes']} inputAttributes
|
||||
*/
|
||||
const setAttributes = (inputClass, inputAttributes) => {
|
||||
const popup = dom.getPopup()
|
||||
if (!popup) {
|
||||
return
|
||||
}
|
||||
|
||||
const input = dom.getInput(popup, inputClass)
|
||||
if (!input) {
|
||||
return
|
||||
}
|
||||
|
||||
removeAttributes(input)
|
||||
|
||||
for (const attr in inputAttributes) {
|
||||
input.setAttribute(attr, inputAttributes[attr])
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {SweetAlertOptions} params
|
||||
*/
|
||||
const setCustomClass = (params) => {
|
||||
if (!params.input) {
|
||||
return
|
||||
}
|
||||
const inputContainer = getInputContainer(params.input)
|
||||
if (inputContainer) {
|
||||
dom.applyCustomClass(inputContainer, params, 'input')
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {HTMLInputElement | HTMLTextAreaElement} input
|
||||
* @param {SweetAlertOptions} params
|
||||
*/
|
||||
const setInputPlaceholder = (input, params) => {
|
||||
if (!input.placeholder && params.inputPlaceholder) {
|
||||
input.placeholder = params.inputPlaceholder
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Input} input
|
||||
* @param {Input} prependTo
|
||||
* @param {SweetAlertOptions} params
|
||||
*/
|
||||
const setInputLabel = (input, prependTo, params) => {
|
||||
if (params.inputLabel) {
|
||||
const label = document.createElement('label')
|
||||
const labelClass = swalClasses['input-label']
|
||||
label.setAttribute('for', input.id)
|
||||
label.className = labelClass
|
||||
if (typeof params.customClass === 'object') {
|
||||
dom.addClass(label, params.customClass.inputLabel)
|
||||
}
|
||||
label.innerText = params.inputLabel
|
||||
prependTo.insertAdjacentElement('beforebegin', label)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {SweetAlertInput} inputType
|
||||
* @returns {HTMLElement | undefined}
|
||||
*/
|
||||
const getInputContainer = (inputType) => {
|
||||
const popup = dom.getPopup()
|
||||
if (!popup) {
|
||||
return
|
||||
}
|
||||
|
||||
return dom.getDirectChildByClass(popup, swalClasses[/** @type {SwalClass} */ (inputType)] || swalClasses.input)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {HTMLInputElement | HTMLOutputElement | HTMLTextAreaElement} input
|
||||
* @param {SweetAlertOptions['inputValue']} inputValue
|
||||
*/
|
||||
const checkAndSetInputValue = (input, inputValue) => {
|
||||
if (['string', 'number'].includes(typeof inputValue)) {
|
||||
input.value = `${inputValue}`
|
||||
} else if (!isPromise(inputValue)) {
|
||||
warn(`Unexpected type of inputValue! Expected "string", "number" or "Promise", got "${typeof inputValue}"`)
|
||||
}
|
||||
}
|
||||
|
||||
/** @type {Record<SweetAlertInput, (input: Input | HTMLElement, params: SweetAlertOptions) => Input>} */
|
||||
const renderInputType = {}
|
||||
|
||||
/**
|
||||
* @param {Input | HTMLElement} input
|
||||
* @param {SweetAlertOptions} params
|
||||
* @returns {Input}
|
||||
*/
|
||||
renderInputType.text =
|
||||
renderInputType.email =
|
||||
renderInputType.password =
|
||||
renderInputType.number =
|
||||
renderInputType.tel =
|
||||
renderInputType.url =
|
||||
renderInputType.search =
|
||||
renderInputType.date =
|
||||
renderInputType['datetime-local'] =
|
||||
renderInputType.time =
|
||||
renderInputType.week =
|
||||
renderInputType.month =
|
||||
/** @type {(input: Input | HTMLElement, params: SweetAlertOptions) => Input} */
|
||||
(input, params) => {
|
||||
const inputElement = /** @type {HTMLInputElement} */ (input)
|
||||
checkAndSetInputValue(inputElement, params.inputValue)
|
||||
setInputLabel(inputElement, inputElement, params)
|
||||
setInputPlaceholder(inputElement, params)
|
||||
inputElement.type = /** @type {string} */ (params.input)
|
||||
return inputElement
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Input | HTMLElement} input
|
||||
* @param {SweetAlertOptions} params
|
||||
* @returns {Input}
|
||||
*/
|
||||
renderInputType.file = (input, params) => {
|
||||
const inputElement = /** @type {HTMLInputElement} */ (input)
|
||||
setInputLabel(inputElement, inputElement, params)
|
||||
setInputPlaceholder(inputElement, params)
|
||||
return inputElement
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Input | HTMLElement} range
|
||||
* @param {SweetAlertOptions} params
|
||||
* @returns {Input}
|
||||
*/
|
||||
renderInputType.range = (range, params) => {
|
||||
const rangeContainer = /** @type {HTMLElement} */ (range)
|
||||
const rangeInput = rangeContainer.querySelector('input')
|
||||
const rangeOutput = rangeContainer.querySelector('output')
|
||||
if (rangeInput) {
|
||||
checkAndSetInputValue(rangeInput, params.inputValue)
|
||||
rangeInput.type = /** @type {string} */ (params.input)
|
||||
setInputLabel(rangeInput, /** @type {Input} */ (range), params)
|
||||
}
|
||||
if (rangeOutput) {
|
||||
checkAndSetInputValue(rangeOutput, params.inputValue)
|
||||
}
|
||||
return /** @type {Input} */ (range)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Input | HTMLElement} select
|
||||
* @param {SweetAlertOptions} params
|
||||
* @returns {Input}
|
||||
*/
|
||||
renderInputType.select = (select, params) => {
|
||||
const selectElement = /** @type {HTMLSelectElement} */ (select)
|
||||
selectElement.textContent = ''
|
||||
if (params.inputPlaceholder) {
|
||||
const placeholder = document.createElement('option')
|
||||
dom.setInnerHtml(placeholder, params.inputPlaceholder)
|
||||
placeholder.value = ''
|
||||
placeholder.disabled = true
|
||||
placeholder.selected = true
|
||||
selectElement.appendChild(placeholder)
|
||||
}
|
||||
setInputLabel(selectElement, selectElement, params)
|
||||
return selectElement
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Input | HTMLElement} radio
|
||||
* @returns {Input}
|
||||
*/
|
||||
renderInputType.radio = (radio) => {
|
||||
const radioElement = /** @type {HTMLElement} */ (radio)
|
||||
radioElement.textContent = ''
|
||||
return /** @type {Input} */ (radio)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Input | HTMLElement} checkboxContainer
|
||||
* @param {SweetAlertOptions} params
|
||||
* @returns {Input}
|
||||
*/
|
||||
renderInputType.checkbox = (checkboxContainer, params) => {
|
||||
const popup = dom.getPopup()
|
||||
if (!popup) {
|
||||
throw new Error('Popup not found')
|
||||
}
|
||||
const checkbox = dom.getInput(popup, 'checkbox')
|
||||
if (!checkbox) {
|
||||
throw new Error('Checkbox input not found')
|
||||
}
|
||||
checkbox.value = '1'
|
||||
checkbox.checked = Boolean(params.inputValue)
|
||||
const containerElement = /** @type {HTMLElement} */ (checkboxContainer)
|
||||
const label = containerElement.querySelector('span')
|
||||
if (label) {
|
||||
const placeholderOrLabel = params.inputPlaceholder || params.inputLabel
|
||||
if (placeholderOrLabel) {
|
||||
dom.setInnerHtml(label, placeholderOrLabel)
|
||||
}
|
||||
}
|
||||
return checkbox
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Input | HTMLElement} textarea
|
||||
* @param {SweetAlertOptions} params
|
||||
* @returns {Input}
|
||||
*/
|
||||
renderInputType.textarea = (textarea, params) => {
|
||||
const textareaElement = /** @type {HTMLTextAreaElement} */ (textarea)
|
||||
checkAndSetInputValue(textareaElement, params.inputValue)
|
||||
setInputPlaceholder(textareaElement, params)
|
||||
setInputLabel(textareaElement, textareaElement, params)
|
||||
|
||||
/**
|
||||
* @param {HTMLElement} el
|
||||
* @returns {number}
|
||||
*/
|
||||
const getMargin = (el) =>
|
||||
parseInt(window.getComputedStyle(el).marginLeft) + parseInt(window.getComputedStyle(el).marginRight)
|
||||
|
||||
// https://github.com/sweetalert2/sweetalert2/issues/2291
|
||||
setTimeout(() => {
|
||||
// https://github.com/sweetalert2/sweetalert2/issues/1699
|
||||
if ('MutationObserver' in window) {
|
||||
const popup = dom.getPopup()
|
||||
if (!popup) {
|
||||
return
|
||||
}
|
||||
const initialPopupWidth = parseInt(window.getComputedStyle(popup).width)
|
||||
const textareaResizeHandler = () => {
|
||||
// check if texarea is still in document (i.e. popup wasn't closed in the meantime)
|
||||
if (!document.body.contains(textareaElement)) {
|
||||
return
|
||||
}
|
||||
const textareaWidth = textareaElement.offsetWidth + getMargin(textareaElement)
|
||||
const popupElement = dom.getPopup()
|
||||
if (popupElement) {
|
||||
if (textareaWidth > initialPopupWidth) {
|
||||
popupElement.style.width = `${textareaWidth}px`
|
||||
} else {
|
||||
dom.applyNumericalStyle(popupElement, 'width', params.width)
|
||||
}
|
||||
}
|
||||
}
|
||||
new MutationObserver(textareaResizeHandler).observe(textareaElement, {
|
||||
attributes: true,
|
||||
attributeFilter: ['style'],
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
return textareaElement
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
import { swalClasses } from '../../classes.js'
|
||||
import { addDraggableListeners, removeDraggableListeners } from '../../draggable.js'
|
||||
import * as dom from '../../dom/index.js'
|
||||
|
||||
/**
|
||||
* @param {SweetAlert} instance
|
||||
* @param {SweetAlertOptions} params
|
||||
*/
|
||||
export const renderPopup = (instance, params) => {
|
||||
const container = dom.getContainer()
|
||||
const popup = dom.getPopup()
|
||||
if (!container || !popup) {
|
||||
return
|
||||
}
|
||||
|
||||
// Width
|
||||
// https://github.com/sweetalert2/sweetalert2/issues/2170
|
||||
if (params.toast) {
|
||||
dom.applyNumericalStyle(container, 'width', params.width)
|
||||
popup.style.width = '100%'
|
||||
const loader = dom.getLoader()
|
||||
if (loader) {
|
||||
popup.insertBefore(loader, dom.getIcon())
|
||||
}
|
||||
} else {
|
||||
dom.applyNumericalStyle(popup, 'width', params.width)
|
||||
}
|
||||
|
||||
// Padding
|
||||
dom.applyNumericalStyle(popup, 'padding', params.padding)
|
||||
|
||||
// Color
|
||||
if (params.color) {
|
||||
popup.style.color = params.color
|
||||
}
|
||||
|
||||
// Background
|
||||
if (params.background) {
|
||||
popup.style.background = params.background
|
||||
}
|
||||
|
||||
dom.hide(dom.getValidationMessage())
|
||||
|
||||
// Classes
|
||||
addClasses(popup, params)
|
||||
|
||||
if (params.draggable && !params.toast) {
|
||||
dom.addClass(popup, swalClasses.draggable)
|
||||
addDraggableListeners(popup)
|
||||
} else {
|
||||
dom.removeClass(popup, swalClasses.draggable)
|
||||
removeDraggableListeners(popup)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {HTMLElement} popup
|
||||
* @param {SweetAlertOptions} params
|
||||
*/
|
||||
const addClasses = (popup, params) => {
|
||||
const showClass = params.showClass || {}
|
||||
// Default Class + showClass when updating Swal.update({})
|
||||
popup.className = `${swalClasses.popup} ${dom.isVisible(popup) ? 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')
|
||||
// TODO: remove in the next major
|
||||
if (typeof params.customClass === 'string') {
|
||||
dom.addClass(popup, params.customClass)
|
||||
}
|
||||
|
||||
// Icon class (#1842)
|
||||
if (params.icon) {
|
||||
dom.addClass(popup, swalClasses[`icon-${params.icon}`])
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
import { swalClasses } from '../../classes.js'
|
||||
import * as dom from '../../dom/index.js'
|
||||
import { warn } from '../../utils.js'
|
||||
|
||||
/**
|
||||
* @param {SweetAlert} instance
|
||||
* @param {SweetAlertOptions} params
|
||||
*/
|
||||
export const renderProgressSteps = (instance, params) => {
|
||||
const progressStepsContainer = dom.getProgressSteps()
|
||||
if (!progressStepsContainer) {
|
||||
return
|
||||
}
|
||||
|
||||
const { progressSteps, currentProgressStep } = params
|
||||
|
||||
if (!progressSteps || progressSteps.length === 0 || currentProgressStep === undefined) {
|
||||
dom.hide(progressStepsContainer)
|
||||
return
|
||||
}
|
||||
|
||||
dom.show(progressStepsContainer)
|
||||
progressStepsContainer.textContent = ''
|
||||
if (currentProgressStep >= progressSteps.length) {
|
||||
warn(
|
||||
'Invalid currentProgressStep parameter, it should be less than progressSteps.length ' +
|
||||
'(currentProgressStep like JS arrays starts from 0)'
|
||||
)
|
||||
}
|
||||
|
||||
progressSteps.forEach((step, index) => {
|
||||
const stepEl = createStepElement(step)
|
||||
progressStepsContainer.appendChild(stepEl)
|
||||
if (index === currentProgressStep) {
|
||||
dom.addClass(stepEl, swalClasses['active-progress-step'])
|
||||
}
|
||||
|
||||
if (index !== progressSteps.length - 1) {
|
||||
const lineEl = createLineElement(params)
|
||||
progressStepsContainer.appendChild(lineEl)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} step
|
||||
* @returns {HTMLLIElement}
|
||||
*/
|
||||
const createStepElement = (step) => {
|
||||
const stepEl = document.createElement('li')
|
||||
dom.addClass(stepEl, swalClasses['progress-step'])
|
||||
dom.setInnerHtml(stepEl, step)
|
||||
return stepEl
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {SweetAlertOptions} params
|
||||
* @returns {HTMLLIElement}
|
||||
*/
|
||||
const createLineElement = (params) => {
|
||||
const lineEl = document.createElement('li')
|
||||
dom.addClass(lineEl, swalClasses['progress-step-line'])
|
||||
if (params.progressStepsDistance) {
|
||||
dom.applyNumericalStyle(lineEl, 'width', params.progressStepsDistance)
|
||||
}
|
||||
return lineEl
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import * as dom from '../../dom/index.js'
|
||||
|
||||
/**
|
||||
* @param {SweetAlert} instance
|
||||
* @param {SweetAlertOptions} params
|
||||
*/
|
||||
export const renderTitle = (instance, params) => {
|
||||
const title = dom.getTitle()
|
||||
if (!title) {
|
||||
return
|
||||
}
|
||||
|
||||
dom.showWhenInnerHtmlPresent(title)
|
||||
|
||||
dom.toggle(title, Boolean(params.title || params.titleText), 'block')
|
||||
|
||||
if (params.title) {
|
||||
dom.parseHtmlToContainer(params.title, title)
|
||||
}
|
||||
|
||||
if (params.titleText) {
|
||||
title.innerText = params.titleText
|
||||
}
|
||||
|
||||
// Custom class
|
||||
dom.applyCustomClass(title, params, 'title')
|
||||
}
|
||||
99
public/assets/sweetalert2/src/utils/draggable.js
Normal file
99
public/assets/sweetalert2/src/utils/draggable.js
Normal file
@@ -0,0 +1,99 @@
|
||||
import globalState from '../globalState.js'
|
||||
import * as dom from './dom/index.js'
|
||||
|
||||
let dragging = false
|
||||
let mousedownX = 0
|
||||
let mousedownY = 0
|
||||
let initialX = 0
|
||||
let initialY = 0
|
||||
|
||||
/**
|
||||
* @param {HTMLElement} popup
|
||||
*/
|
||||
export const addDraggableListeners = (popup) => {
|
||||
popup.addEventListener('mousedown', down)
|
||||
document.body.addEventListener('mousemove', move)
|
||||
popup.addEventListener('mouseup', up)
|
||||
|
||||
popup.addEventListener('touchstart', down)
|
||||
document.body.addEventListener('touchmove', move)
|
||||
popup.addEventListener('touchend', up)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {HTMLElement} popup
|
||||
*/
|
||||
export const removeDraggableListeners = (popup) => {
|
||||
popup.removeEventListener('mousedown', down)
|
||||
document.body.removeEventListener('mousemove', move)
|
||||
popup.removeEventListener('mouseup', up)
|
||||
|
||||
popup.removeEventListener('touchstart', down)
|
||||
document.body.removeEventListener('touchmove', move)
|
||||
popup.removeEventListener('touchend', up)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {MouseEvent | TouchEvent} event
|
||||
*/
|
||||
const down = (event) => {
|
||||
const popup = dom.getPopup()
|
||||
|
||||
if (!popup) {
|
||||
return
|
||||
}
|
||||
|
||||
const icon = dom.getIcon()
|
||||
if (event.target === popup || (icon && icon.contains(/** @type {HTMLElement} */ (event.target)))) {
|
||||
dragging = true
|
||||
const clientXY = getClientXY(event)
|
||||
mousedownX = clientXY.clientX
|
||||
mousedownY = clientXY.clientY
|
||||
initialX = parseInt(popup.style.insetInlineStart) || 0
|
||||
initialY = parseInt(popup.style.insetBlockStart) || 0
|
||||
dom.addClass(popup, 'swal2-dragging')
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {MouseEvent | TouchEvent} event
|
||||
*/
|
||||
const move = (event) => {
|
||||
const popup = dom.getPopup()
|
||||
|
||||
if (!popup) {
|
||||
return
|
||||
}
|
||||
|
||||
if (dragging) {
|
||||
let { clientX, clientY } = getClientXY(event)
|
||||
const deltaX = clientX - mousedownX
|
||||
// In RTL mode, negate the horizontal delta since insetInlineStart refers to the right edge
|
||||
popup.style.insetInlineStart = `${initialX + (globalState.isRTL ? -deltaX : deltaX)}px`
|
||||
popup.style.insetBlockStart = `${initialY + (clientY - mousedownY)}px`
|
||||
}
|
||||
}
|
||||
|
||||
const up = () => {
|
||||
const popup = dom.getPopup()
|
||||
|
||||
dragging = false
|
||||
dom.removeClass(popup, 'swal2-dragging')
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {MouseEvent | TouchEvent} event
|
||||
* @returns {{ clientX: number, clientY: number }}
|
||||
*/
|
||||
const getClientXY = (event) => {
|
||||
let clientX = 0,
|
||||
clientY = 0
|
||||
if (event.type.startsWith('mouse')) {
|
||||
clientX = /** @type {MouseEvent} */ (event).clientX
|
||||
clientY = /** @type {MouseEvent} */ (event).clientY
|
||||
} else if (event.type.startsWith('touch')) {
|
||||
clientX = /** @type {TouchEvent} */ (event).touches[0].clientX
|
||||
clientY = /** @type {TouchEvent} */ (event).touches[0].clientY
|
||||
}
|
||||
return { clientX, clientY }
|
||||
}
|
||||
265
public/assets/sweetalert2/src/utils/getTemplateParams.js
Normal file
265
public/assets/sweetalert2/src/utils/getTemplateParams.js
Normal file
@@ -0,0 +1,265 @@
|
||||
import defaultParams from './params.js'
|
||||
import { capitalizeFirstLetter, warn } from './utils.js'
|
||||
|
||||
const swalStringParams = ['swal-title', 'swal-html', 'swal-footer']
|
||||
|
||||
/**
|
||||
* @param {SweetAlertOptions} params
|
||||
* @returns {SweetAlertOptions}
|
||||
*/
|
||||
export const getTemplateParams = (params) => {
|
||||
const template =
|
||||
typeof params.template === 'string'
|
||||
? /** @type {HTMLTemplateElement} */ (document.querySelector(params.template))
|
||||
: params.template
|
||||
if (!template) {
|
||||
return {}
|
||||
}
|
||||
/** @type {DocumentFragment} */
|
||||
const templateContent = template.content
|
||||
|
||||
showWarningsForElements(templateContent)
|
||||
|
||||
const result = Object.assign(
|
||||
getSwalParams(templateContent),
|
||||
getSwalFunctionParams(templateContent),
|
||||
getSwalButtons(templateContent),
|
||||
getSwalImage(templateContent),
|
||||
getSwalIcon(templateContent),
|
||||
getSwalInput(templateContent),
|
||||
getSwalStringParams(templateContent, swalStringParams)
|
||||
)
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {DocumentFragment} templateContent
|
||||
* @returns {Record<string, string | boolean | number>}
|
||||
*/
|
||||
const getSwalParams = (templateContent) => {
|
||||
/** @type {Record<string, string | boolean | number>} */
|
||||
const result = {}
|
||||
/** @type {HTMLElement[]} */
|
||||
const swalParams = Array.from(templateContent.querySelectorAll('swal-param'))
|
||||
swalParams.forEach((param) => {
|
||||
showWarningsForAttributes(param, ['name', 'value'])
|
||||
const paramName = /** @type {keyof SweetAlertOptions} */ (param.getAttribute('name'))
|
||||
const value = param.getAttribute('value')
|
||||
if (!paramName || !value) {
|
||||
return
|
||||
}
|
||||
if (
|
||||
paramName in defaultParams &&
|
||||
typeof defaultParams[/** @type {keyof typeof defaultParams} */ (paramName)] === 'boolean'
|
||||
) {
|
||||
result[paramName] = value !== 'false'
|
||||
} else if (
|
||||
paramName in defaultParams &&
|
||||
typeof defaultParams[/** @type {keyof typeof defaultParams} */ (paramName)] === 'object'
|
||||
) {
|
||||
result[paramName] = JSON.parse(value)
|
||||
} else {
|
||||
result[paramName] = value
|
||||
}
|
||||
})
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {DocumentFragment} templateContent
|
||||
* @returns {Record<string, () => void>}
|
||||
*/
|
||||
const getSwalFunctionParams = (templateContent) => {
|
||||
/** @type {Record<string, () => void>} */
|
||||
const result = {}
|
||||
/** @type {HTMLElement[]} */
|
||||
const swalFunctions = Array.from(templateContent.querySelectorAll('swal-function-param'))
|
||||
swalFunctions.forEach((param) => {
|
||||
const paramName = /** @type {keyof SweetAlertOptions} */ param.getAttribute('name')
|
||||
const value = param.getAttribute('value')
|
||||
if (!paramName || !value) {
|
||||
return
|
||||
}
|
||||
result[paramName] = new Function(`return ${value}`)()
|
||||
})
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {DocumentFragment} templateContent
|
||||
* @returns {Record<string, string | boolean>}
|
||||
*/
|
||||
const getSwalButtons = (templateContent) => {
|
||||
/** @type {Record<string, string | boolean>} */
|
||||
const result = {}
|
||||
/** @type {HTMLElement[]} */
|
||||
const swalButtons = Array.from(templateContent.querySelectorAll('swal-button'))
|
||||
swalButtons.forEach((button) => {
|
||||
showWarningsForAttributes(button, ['type', 'color', 'aria-label'])
|
||||
const type = button.getAttribute('type')
|
||||
if (!type || !['confirm', 'cancel', 'deny'].includes(type)) {
|
||||
return
|
||||
}
|
||||
result[`${type}ButtonText`] = button.innerHTML
|
||||
result[`show${capitalizeFirstLetter(type)}Button`] = true
|
||||
if (button.hasAttribute('color')) {
|
||||
const color = button.getAttribute('color')
|
||||
if (color !== null) {
|
||||
result[`${type}ButtonColor`] = color
|
||||
}
|
||||
}
|
||||
if (button.hasAttribute('aria-label')) {
|
||||
const ariaLabel = button.getAttribute('aria-label')
|
||||
if (ariaLabel !== null) {
|
||||
result[`${type}ButtonAriaLabel`] = ariaLabel
|
||||
}
|
||||
}
|
||||
})
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {DocumentFragment} templateContent
|
||||
* @returns {Pick<SweetAlertOptions, 'imageUrl' | 'imageWidth' | 'imageHeight' | 'imageAlt'>}
|
||||
*/
|
||||
const getSwalImage = (templateContent) => {
|
||||
const result = {}
|
||||
/** @type {HTMLElement | null} */
|
||||
const image = templateContent.querySelector('swal-image')
|
||||
if (image) {
|
||||
showWarningsForAttributes(image, ['src', 'width', 'height', 'alt'])
|
||||
if (image.hasAttribute('src')) {
|
||||
result.imageUrl = image.getAttribute('src') || undefined
|
||||
}
|
||||
if (image.hasAttribute('width')) {
|
||||
result.imageWidth = image.getAttribute('width') || undefined
|
||||
}
|
||||
if (image.hasAttribute('height')) {
|
||||
result.imageHeight = image.getAttribute('height') || undefined
|
||||
}
|
||||
if (image.hasAttribute('alt')) {
|
||||
result.imageAlt = image.getAttribute('alt') || undefined
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {DocumentFragment} templateContent
|
||||
* @returns {object}
|
||||
*/
|
||||
const getSwalIcon = (templateContent) => {
|
||||
const result = {}
|
||||
/** @type {HTMLElement | null} */
|
||||
const icon = templateContent.querySelector('swal-icon')
|
||||
if (icon) {
|
||||
showWarningsForAttributes(icon, ['type', 'color'])
|
||||
if (icon.hasAttribute('type')) {
|
||||
result.icon = icon.getAttribute('type')
|
||||
}
|
||||
if (icon.hasAttribute('color')) {
|
||||
result.iconColor = icon.getAttribute('color')
|
||||
}
|
||||
result.iconHtml = icon.innerHTML
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {DocumentFragment} templateContent
|
||||
* @returns {object}
|
||||
*/
|
||||
const getSwalInput = (templateContent) => {
|
||||
/** @type {Record<string, any>} */
|
||||
const result = {}
|
||||
/** @type {HTMLElement | null} */
|
||||
const input = templateContent.querySelector('swal-input')
|
||||
if (input) {
|
||||
showWarningsForAttributes(input, ['type', 'label', 'placeholder', 'value'])
|
||||
result.input = input.getAttribute('type') || 'text'
|
||||
if (input.hasAttribute('label')) {
|
||||
result.inputLabel = input.getAttribute('label')
|
||||
}
|
||||
if (input.hasAttribute('placeholder')) {
|
||||
result.inputPlaceholder = input.getAttribute('placeholder')
|
||||
}
|
||||
if (input.hasAttribute('value')) {
|
||||
result.inputValue = input.getAttribute('value')
|
||||
}
|
||||
}
|
||||
/** @type {HTMLElement[]} */
|
||||
const inputOptions = Array.from(templateContent.querySelectorAll('swal-input-option'))
|
||||
if (inputOptions.length) {
|
||||
result.inputOptions = {}
|
||||
inputOptions.forEach((option) => {
|
||||
showWarningsForAttributes(option, ['value'])
|
||||
const optionValue = option.getAttribute('value')
|
||||
if (!optionValue) {
|
||||
return
|
||||
}
|
||||
const optionName = option.innerHTML
|
||||
result.inputOptions[optionValue] = optionName
|
||||
})
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {DocumentFragment} templateContent
|
||||
* @param {string[]} paramNames
|
||||
* @returns {Record<string, string>}
|
||||
*/
|
||||
const getSwalStringParams = (templateContent, paramNames) => {
|
||||
/** @type {Record<string, string>} */
|
||||
const result = {}
|
||||
for (const i in paramNames) {
|
||||
const paramName = paramNames[i]
|
||||
/** @type {HTMLElement | null} */
|
||||
const tag = templateContent.querySelector(paramName)
|
||||
if (tag) {
|
||||
showWarningsForAttributes(tag, [])
|
||||
result[paramName.replace(/^swal-/, '')] = tag.innerHTML.trim()
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {DocumentFragment} templateContent
|
||||
*/
|
||||
const showWarningsForElements = (templateContent) => {
|
||||
const allowedElements = swalStringParams.concat([
|
||||
'swal-param',
|
||||
'swal-function-param',
|
||||
'swal-button',
|
||||
'swal-image',
|
||||
'swal-icon',
|
||||
'swal-input',
|
||||
'swal-input-option',
|
||||
])
|
||||
Array.from(templateContent.children).forEach((el) => {
|
||||
const tagName = el.tagName.toLowerCase()
|
||||
if (!allowedElements.includes(tagName)) {
|
||||
warn(`Unrecognized element <${tagName}>`)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {HTMLElement} el
|
||||
* @param {string[]} allowedAttributes
|
||||
*/
|
||||
const showWarningsForAttributes = (el, allowedAttributes) => {
|
||||
Array.from(el.attributes).forEach((attribute) => {
|
||||
if (allowedAttributes.indexOf(attribute.name) === -1) {
|
||||
warn([
|
||||
`Unrecognized attribute "${attribute.name}" on <${el.tagName.toLowerCase()}>.`,
|
||||
`${
|
||||
allowedAttributes.length
|
||||
? `Allowed attributes are: ${allowedAttributes.join(', ')}`
|
||||
: 'To set the value, use HTML within the element.'
|
||||
}`,
|
||||
])
|
||||
}
|
||||
})
|
||||
}
|
||||
112
public/assets/sweetalert2/src/utils/iosFix.js
Normal file
112
public/assets/sweetalert2/src/utils/iosFix.js
Normal file
@@ -0,0 +1,112 @@
|
||||
import { swalClasses } from '../utils/classes.js'
|
||||
import * as dom from './dom/index.js'
|
||||
|
||||
// @ts-ignore
|
||||
export const isSafariOrIOS = typeof window !== 'undefined' && Boolean(window.GestureEvent) // true for Safari desktop + all iOS browsers https://stackoverflow.com/a/70585394
|
||||
|
||||
/**
|
||||
* Fix iOS scrolling
|
||||
* http://stackoverflow.com/q/39626302
|
||||
*/
|
||||
export const iOSfix = () => {
|
||||
if (isSafariOrIOS && !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()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* https://github.com/sweetalert2/sweetalert2/issues/1246
|
||||
*/
|
||||
const lockBodyScroll = () => {
|
||||
const container = dom.getContainer()
|
||||
if (!container) {
|
||||
return
|
||||
}
|
||||
/** @type {boolean} */
|
||||
let preventTouchMove
|
||||
/**
|
||||
* @param {TouchEvent} event
|
||||
*/
|
||||
container.ontouchstart = (event) => {
|
||||
preventTouchMove = shouldPreventTouchMove(event)
|
||||
}
|
||||
/**
|
||||
* @param {TouchEvent} event
|
||||
*/
|
||||
container.ontouchmove = (event) => {
|
||||
if (preventTouchMove) {
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {TouchEvent} event
|
||||
* @returns {boolean}
|
||||
*/
|
||||
const shouldPreventTouchMove = (event) => {
|
||||
const target = event.target
|
||||
const container = dom.getContainer()
|
||||
const htmlContainer = dom.getHtmlContainer()
|
||||
if (!container || !htmlContainer) {
|
||||
return false
|
||||
}
|
||||
if (isStylus(event) || isZoom(event)) {
|
||||
return false
|
||||
}
|
||||
if (target === container) {
|
||||
return true
|
||||
}
|
||||
if (
|
||||
!dom.isScrollable(container) &&
|
||||
target instanceof HTMLElement &&
|
||||
!dom.selfOrParentIsScrollable(target, htmlContainer) && // #2823
|
||||
target.tagName !== 'INPUT' && // #1603
|
||||
target.tagName !== 'TEXTAREA' && // #2266
|
||||
!(
|
||||
dom.isScrollable(htmlContainer) && // #1944
|
||||
htmlContainer.contains(target)
|
||||
)
|
||||
) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* https://github.com/sweetalert2/sweetalert2/issues/1786
|
||||
*
|
||||
* @param {TouchEvent} event
|
||||
* @returns {boolean}
|
||||
*/
|
||||
const isStylus = (event) => {
|
||||
return Boolean(
|
||||
event.touches &&
|
||||
event.touches.length &&
|
||||
// @ts-ignore - touchType is not a standard property
|
||||
event.touches[0].touchType === 'stylus'
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* https://github.com/sweetalert2/sweetalert2/issues/1891
|
||||
*
|
||||
* @param {TouchEvent} event
|
||||
* @returns {boolean}
|
||||
*/
|
||||
const isZoom = (event) => {
|
||||
return event.touches && event.touches.length > 1
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
6
public/assets/sweetalert2/src/utils/isNodeEnv.js
Normal file
6
public/assets/sweetalert2/src/utils/isNodeEnv.js
Normal file
@@ -0,0 +1,6 @@
|
||||
/**
|
||||
* Detect Node env
|
||||
*
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export const isNodeEnv = () => typeof window === 'undefined' || typeof document === 'undefined'
|
||||
139
public/assets/sweetalert2/src/utils/openPopup.js
Normal file
139
public/assets/sweetalert2/src/utils/openPopup.js
Normal file
@@ -0,0 +1,139 @@
|
||||
import globalState from '../globalState.js'
|
||||
import { setAriaHidden } from './aria.js'
|
||||
import { swalClasses } from './classes.js'
|
||||
import * as dom from './dom/index.js'
|
||||
import { iOSfix } from './iosFix.js'
|
||||
import { replaceScrollbarWithPadding } from './scrollbar.js'
|
||||
|
||||
export const SHOW_CLASS_TIMEOUT = 10
|
||||
|
||||
/**
|
||||
* Open popup, add necessary classes and styles, fix scrollbar
|
||||
*
|
||||
* @param {SweetAlertOptions} params
|
||||
*/
|
||||
export const openPopup = (params) => {
|
||||
const container = dom.getContainer()
|
||||
const popup = dom.getPopup()
|
||||
|
||||
if (!container || !popup) {
|
||||
return
|
||||
}
|
||||
|
||||
if (typeof params.willOpen === 'function') {
|
||||
params.willOpen(popup)
|
||||
}
|
||||
globalState.eventEmitter?.emit('willOpen', popup)
|
||||
|
||||
const bodyStyles = window.getComputedStyle(document.body)
|
||||
const initialBodyOverflow = bodyStyles.overflowY
|
||||
addClasses(container, popup, params)
|
||||
|
||||
// scrolling is 'hidden' until animation is done, after that 'auto'
|
||||
setTimeout(() => {
|
||||
setScrollingVisibility(container, popup)
|
||||
}, SHOW_CLASS_TIMEOUT)
|
||||
|
||||
if (dom.isModal()) {
|
||||
// Using ternary instead of ?? operator for Webpack 4 compatibility
|
||||
fixScrollContainer(
|
||||
container,
|
||||
params.scrollbarPadding !== undefined ? params.scrollbarPadding : false,
|
||||
initialBodyOverflow
|
||||
)
|
||||
setAriaHidden()
|
||||
}
|
||||
|
||||
if (!dom.isToast() && !globalState.previousActiveElement) {
|
||||
globalState.previousActiveElement = document.activeElement
|
||||
}
|
||||
|
||||
if (typeof params.didOpen === 'function') {
|
||||
const didOpen = params.didOpen
|
||||
setTimeout(() => didOpen(popup))
|
||||
}
|
||||
globalState.eventEmitter?.emit('didOpen', popup)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Event} event
|
||||
*/
|
||||
const swalOpenAnimationFinished = (event) => {
|
||||
const popup = dom.getPopup()
|
||||
if (!popup || event.target !== popup) {
|
||||
return
|
||||
}
|
||||
const container = dom.getContainer()
|
||||
if (!container) {
|
||||
return
|
||||
}
|
||||
popup.removeEventListener('animationend', swalOpenAnimationFinished)
|
||||
popup.removeEventListener('transitionend', swalOpenAnimationFinished)
|
||||
container.style.overflowY = 'auto'
|
||||
|
||||
// no-transition is added in init() in case one swal is opened right after another
|
||||
dom.removeClass(container, swalClasses['no-transition'])
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {HTMLElement} container
|
||||
* @param {HTMLElement} popup
|
||||
*/
|
||||
const setScrollingVisibility = (container, popup) => {
|
||||
if (dom.hasCssAnimation(popup)) {
|
||||
container.style.overflowY = 'hidden'
|
||||
popup.addEventListener('animationend', swalOpenAnimationFinished)
|
||||
popup.addEventListener('transitionend', swalOpenAnimationFinished)
|
||||
} else {
|
||||
container.style.overflowY = 'auto'
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {HTMLElement} container
|
||||
* @param {boolean} scrollbarPadding
|
||||
* @param {string} initialBodyOverflow
|
||||
*/
|
||||
const fixScrollContainer = (container, scrollbarPadding, initialBodyOverflow) => {
|
||||
iOSfix()
|
||||
|
||||
if (scrollbarPadding && initialBodyOverflow !== 'hidden') {
|
||||
replaceScrollbarWithPadding(initialBodyOverflow)
|
||||
}
|
||||
|
||||
// sweetalert2/issues/1247
|
||||
setTimeout(() => {
|
||||
container.scrollTop = 0
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {HTMLElement} container
|
||||
* @param {HTMLElement} popup
|
||||
* @param {SweetAlertOptions} params
|
||||
*/
|
||||
const addClasses = (container, popup, params) => {
|
||||
if (params.showClass?.backdrop) {
|
||||
dom.addClass(container, params.showClass.backdrop)
|
||||
}
|
||||
if (params.animation) {
|
||||
// this workaround with opacity is needed for https://github.com/sweetalert2/sweetalert2/issues/2059
|
||||
popup.style.setProperty('opacity', '0', 'important')
|
||||
dom.show(popup, 'grid')
|
||||
setTimeout(() => {
|
||||
// Animate popup right after showing it
|
||||
if (params.showClass?.popup) {
|
||||
dom.addClass(popup, params.showClass.popup)
|
||||
}
|
||||
// and remove the opacity workaround
|
||||
popup.style.removeProperty('opacity')
|
||||
}, SHOW_CLASS_TIMEOUT) // 10ms in order to fix #2062
|
||||
} else {
|
||||
dom.show(popup, 'grid')
|
||||
}
|
||||
|
||||
dom.addClass([document.documentElement, document.body], swalClasses.shown)
|
||||
if (params.heightAuto && params.backdrop && !params.toast) {
|
||||
dom.addClass([document.documentElement, document.body], swalClasses['height-auto'])
|
||||
}
|
||||
}
|
||||
269
public/assets/sweetalert2/src/utils/params.js
Normal file
269
public/assets/sweetalert2/src/utils/params.js
Normal file
@@ -0,0 +1,269 @@
|
||||
import { warn, warnAboutDeprecation } from '../utils/utils.js'
|
||||
|
||||
export const defaultParams = {
|
||||
title: '',
|
||||
titleText: '',
|
||||
text: '',
|
||||
html: '',
|
||||
footer: '',
|
||||
icon: undefined,
|
||||
iconColor: undefined,
|
||||
iconHtml: undefined,
|
||||
template: undefined,
|
||||
toast: false,
|
||||
draggable: false,
|
||||
animation: true,
|
||||
theme: 'light',
|
||||
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: {},
|
||||
target: 'body',
|
||||
color: undefined,
|
||||
backdrop: true,
|
||||
heightAuto: true,
|
||||
allowOutsideClick: true,
|
||||
allowEscapeKey: true,
|
||||
allowEnterKey: true,
|
||||
stopKeydownPropagation: true,
|
||||
keydownListenerCapture: false,
|
||||
showConfirmButton: true,
|
||||
showDenyButton: false,
|
||||
showCancelButton: false,
|
||||
preConfirm: undefined,
|
||||
preDeny: undefined,
|
||||
confirmButtonText: 'OK',
|
||||
confirmButtonAriaLabel: '',
|
||||
confirmButtonColor: undefined,
|
||||
denyButtonText: 'No',
|
||||
denyButtonAriaLabel: '',
|
||||
denyButtonColor: undefined,
|
||||
cancelButtonText: 'Cancel',
|
||||
cancelButtonAriaLabel: '',
|
||||
cancelButtonColor: undefined,
|
||||
buttonsStyling: true,
|
||||
reverseButtons: false,
|
||||
focusConfirm: true,
|
||||
focusDeny: false,
|
||||
focusCancel: false,
|
||||
returnFocus: true,
|
||||
showCloseButton: false,
|
||||
closeButtonHtml: '×',
|
||||
closeButtonAriaLabel: 'Close this dialog',
|
||||
loaderHtml: '',
|
||||
showLoaderOnConfirm: false,
|
||||
showLoaderOnDeny: false,
|
||||
imageUrl: undefined,
|
||||
imageWidth: undefined,
|
||||
imageHeight: undefined,
|
||||
imageAlt: '',
|
||||
timer: undefined,
|
||||
timerProgressBar: false,
|
||||
width: undefined,
|
||||
padding: undefined,
|
||||
background: undefined,
|
||||
input: undefined,
|
||||
inputPlaceholder: '',
|
||||
inputLabel: '',
|
||||
inputValue: '',
|
||||
inputOptions: {},
|
||||
inputAutoFocus: true,
|
||||
inputAutoTrim: true,
|
||||
inputAttributes: {},
|
||||
inputValidator: undefined,
|
||||
returnInputValueOnDeny: false,
|
||||
validationMessage: undefined,
|
||||
grow: false,
|
||||
position: 'center',
|
||||
progressSteps: [],
|
||||
currentProgressStep: undefined,
|
||||
progressStepsDistance: undefined,
|
||||
willOpen: undefined,
|
||||
didOpen: undefined,
|
||||
didRender: undefined,
|
||||
willClose: undefined,
|
||||
didClose: undefined,
|
||||
didDestroy: undefined,
|
||||
scrollbarPadding: true,
|
||||
topLayer: false,
|
||||
}
|
||||
|
||||
export const updatableParams = [
|
||||
'allowEscapeKey',
|
||||
'allowOutsideClick',
|
||||
'background',
|
||||
'buttonsStyling',
|
||||
'cancelButtonAriaLabel',
|
||||
'cancelButtonColor',
|
||||
'cancelButtonText',
|
||||
'closeButtonAriaLabel',
|
||||
'closeButtonHtml',
|
||||
'color',
|
||||
'confirmButtonAriaLabel',
|
||||
'confirmButtonColor',
|
||||
'confirmButtonText',
|
||||
'currentProgressStep',
|
||||
'customClass',
|
||||
'denyButtonAriaLabel',
|
||||
'denyButtonColor',
|
||||
'denyButtonText',
|
||||
'didClose',
|
||||
'didDestroy',
|
||||
'draggable',
|
||||
'footer',
|
||||
'hideClass',
|
||||
'html',
|
||||
'icon',
|
||||
'iconColor',
|
||||
'iconHtml',
|
||||
'imageAlt',
|
||||
'imageHeight',
|
||||
'imageUrl',
|
||||
'imageWidth',
|
||||
'preConfirm',
|
||||
'preDeny',
|
||||
'progressSteps',
|
||||
'returnFocus',
|
||||
'reverseButtons',
|
||||
'showCancelButton',
|
||||
'showCloseButton',
|
||||
'showConfirmButton',
|
||||
'showDenyButton',
|
||||
'text',
|
||||
'title',
|
||||
'titleText',
|
||||
'theme',
|
||||
'willClose',
|
||||
]
|
||||
|
||||
/** @type {Record<string, string | undefined>} */
|
||||
export const deprecatedParams = {
|
||||
allowEnterKey: undefined,
|
||||
}
|
||||
|
||||
const toastIncompatibleParams = [
|
||||
'allowOutsideClick',
|
||||
'allowEnterKey',
|
||||
'backdrop',
|
||||
'draggable',
|
||||
'focusConfirm',
|
||||
'focusDeny',
|
||||
'focusCancel',
|
||||
'returnFocus',
|
||||
'heightAuto',
|
||||
'keydownListenerCapture',
|
||||
]
|
||||
|
||||
/**
|
||||
* Is valid parameter
|
||||
*
|
||||
* @param {string} paramName
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export const isValidParameter = (paramName) => {
|
||||
return Object.prototype.hasOwnProperty.call(defaultParams, paramName)
|
||||
}
|
||||
|
||||
/**
|
||||
* Is valid parameter for Swal.update() method
|
||||
*
|
||||
* @param {string} paramName
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export const isUpdatableParameter = (paramName) => {
|
||||
return updatableParams.indexOf(paramName) !== -1
|
||||
}
|
||||
|
||||
/**
|
||||
* Is deprecated parameter
|
||||
*
|
||||
* @param {string} paramName
|
||||
* @returns {string | undefined}
|
||||
*/
|
||||
export const isDeprecatedParameter = (paramName) => {
|
||||
return deprecatedParams[paramName]
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} param
|
||||
*/
|
||||
const checkIfParamIsValid = (param) => {
|
||||
if (!isValidParameter(param)) {
|
||||
warn(`Unknown parameter "${param}"`)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} param
|
||||
*/
|
||||
const checkIfToastParamIsValid = (param) => {
|
||||
if (toastIncompatibleParams.includes(param)) {
|
||||
warn(`The parameter "${param}" is incompatible with toasts`)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} param
|
||||
*/
|
||||
const checkIfParamIsDeprecated = (param) => {
|
||||
const isDeprecated = isDeprecatedParameter(param)
|
||||
if (isDeprecated) {
|
||||
warnAboutDeprecation(param, isDeprecated)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Show relevant warnings for given params
|
||||
*
|
||||
* @param {SweetAlertOptions} params
|
||||
*/
|
||||
export const showWarningsForParams = (params) => {
|
||||
if (params.backdrop === false && params.allowOutsideClick) {
|
||||
warn('"allowOutsideClick" parameter requires `backdrop` parameter to be set to `true`')
|
||||
}
|
||||
|
||||
if (
|
||||
params.theme &&
|
||||
![
|
||||
'light',
|
||||
'dark',
|
||||
'auto',
|
||||
'minimal',
|
||||
'borderless',
|
||||
'bootstrap-4',
|
||||
'bootstrap-4-light',
|
||||
'bootstrap-4-dark',
|
||||
'bootstrap-5',
|
||||
'bootstrap-5-light',
|
||||
'bootstrap-5-dark',
|
||||
'material-ui',
|
||||
'material-ui-light',
|
||||
'material-ui-dark',
|
||||
'embed-iframe',
|
||||
'bulma',
|
||||
'bulma-light',
|
||||
'bulma-dark',
|
||||
].includes(params.theme)
|
||||
) {
|
||||
warn(`Invalid theme "${params.theme}"`)
|
||||
}
|
||||
|
||||
for (const param in params) {
|
||||
checkIfParamIsValid(param)
|
||||
|
||||
if (params.toast) {
|
||||
checkIfToastParamIsValid(param)
|
||||
}
|
||||
|
||||
checkIfParamIsDeprecated(param)
|
||||
}
|
||||
}
|
||||
|
||||
export default defaultParams
|
||||
48
public/assets/sweetalert2/src/utils/scrollbar.js
Normal file
48
public/assets/sweetalert2/src/utils/scrollbar.js
Normal file
@@ -0,0 +1,48 @@
|
||||
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
|
||||
*
|
||||
* @returns {number}
|
||||
*/
|
||||
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
|
||||
}
|
||||
|
||||
/**
|
||||
* Remember state in cases where opening and handling a modal will fiddle with it.
|
||||
* @type {number | null}
|
||||
*/
|
||||
let previousBodyPadding = null
|
||||
|
||||
/**
|
||||
* @param {string} initialBodyOverflow
|
||||
*/
|
||||
export const replaceScrollbarWithPadding = (initialBodyOverflow) => {
|
||||
// for queues, do not do this more than once
|
||||
if (previousBodyPadding !== null) {
|
||||
return
|
||||
}
|
||||
// if the body has overflow
|
||||
if (
|
||||
document.body.scrollHeight > window.innerHeight ||
|
||||
initialBodyOverflow === 'scroll' // https://github.com/sweetalert2/sweetalert2/issues/2663
|
||||
) {
|
||||
// add padding so the content doesn't shift after removal of scrollbar
|
||||
previousBodyPadding = parseInt(window.getComputedStyle(document.body).getPropertyValue('padding-right'))
|
||||
document.body.style.paddingRight = `${previousBodyPadding + measureScrollbar()}px`
|
||||
}
|
||||
}
|
||||
|
||||
export const undoReplaceScrollbarWithPadding = () => {
|
||||
if (previousBodyPadding !== null) {
|
||||
document.body.style.paddingRight = `${previousBodyPadding}px`
|
||||
previousBodyPadding = null
|
||||
}
|
||||
}
|
||||
61
public/assets/sweetalert2/src/utils/setParameters.js
Normal file
61
public/assets/sweetalert2/src/utils/setParameters.js
Normal file
@@ -0,0 +1,61 @@
|
||||
import defaultInputValidators from './defaultInputValidators.js'
|
||||
import * as dom from './dom/index.js'
|
||||
import { warn } from './utils.js'
|
||||
|
||||
/**
|
||||
* @param {SweetAlertOptions} params
|
||||
*/
|
||||
function setDefaultInputValidators(params) {
|
||||
// Use default `inputValidator` for supported input types if not provided
|
||||
if (params.inputValidator) {
|
||||
return
|
||||
}
|
||||
if (params.input === 'email') {
|
||||
params.inputValidator = defaultInputValidators['email']
|
||||
}
|
||||
if (params.input === 'url') {
|
||||
params.inputValidator = defaultInputValidators['url']
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {SweetAlertOptions} params
|
||||
*/
|
||||
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 {SweetAlertOptions} params
|
||||
*/
|
||||
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'
|
||||
)
|
||||
}
|
||||
|
||||
validateCustomTargetElement(params)
|
||||
|
||||
// Replace newlines with <br> in title
|
||||
if (typeof params.title === 'string') {
|
||||
params.title = params.title.split('\n').join('<br />')
|
||||
}
|
||||
|
||||
dom.init(params)
|
||||
}
|
||||
88
public/assets/sweetalert2/src/utils/utils.js
Normal file
88
public/assets/sweetalert2/src/utils/utils.js
Normal file
@@ -0,0 +1,88 @@
|
||||
export const consolePrefix = 'SweetAlert2:'
|
||||
|
||||
/**
|
||||
* Capitalize the first letter of a string
|
||||
*
|
||||
* @param {string} str
|
||||
* @returns {string}
|
||||
*/
|
||||
export const capitalizeFirstLetter = (str) => str.charAt(0).toUpperCase() + str.slice(1)
|
||||
|
||||
/**
|
||||
* Standardize console warnings
|
||||
*
|
||||
* @param {string | string[]} message
|
||||
*/
|
||||
export const warn = (message) => {
|
||||
console.warn(`${consolePrefix} ${typeof message === 'object' ? message.join(' ') : message}`)
|
||||
}
|
||||
|
||||
/**
|
||||
* Standardize console errors
|
||||
*
|
||||
* @param {string} message
|
||||
*/
|
||||
export const error = (message) => {
|
||||
console.error(`${consolePrefix} ${message}`)
|
||||
}
|
||||
|
||||
/**
|
||||
* Private global state for `warnOnce`
|
||||
*
|
||||
* @type {string[]}
|
||||
* @private
|
||||
*/
|
||||
const previousWarnOnceMessages = []
|
||||
|
||||
/**
|
||||
* Show a console warning, but only if it hasn't already been shown
|
||||
*
|
||||
* @param {string} message
|
||||
*/
|
||||
export const warnOnce = (message) => {
|
||||
if (!previousWarnOnceMessages.includes(message)) {
|
||||
previousWarnOnceMessages.push(message)
|
||||
warn(message)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Show a one-time console warning about deprecated params/methods
|
||||
*
|
||||
* @param {string} deprecatedParam
|
||||
* @param {string?} useInstead
|
||||
*/
|
||||
export const warnAboutDeprecation = (deprecatedParam, useInstead = null) => {
|
||||
warnOnce(
|
||||
`"${deprecatedParam}" is deprecated and will be removed in the next major release.${
|
||||
useInstead ? ` 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
|
||||
* @returns {*}
|
||||
*/
|
||||
export const callIfFunction = (arg) => (typeof arg === 'function' ? arg() : arg)
|
||||
|
||||
/**
|
||||
* @param {*} arg
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export const hasToPromiseFn = (arg) => arg && typeof arg.toPromise === 'function'
|
||||
|
||||
/**
|
||||
* @param {*} arg
|
||||
* @returns {Promise<*>}
|
||||
*/
|
||||
export const asPromise = (arg) => (hasToPromiseFn(arg) ? arg.toPromise() : Promise.resolve(arg))
|
||||
|
||||
/**
|
||||
* @param {*} arg
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export const isPromise = (arg) => arg && Promise.resolve(arg) === arg
|
||||
Reference in New Issue
Block a user