EVOLUTION-MANAGER
Edit File: inputUtils.js
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 {Record<string, any>} 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 {Record<string, any>} 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 !!inputValue && inputValue.toString() === optionValue.toString() }