EVOLUTION-MANAGER
Edit File: keydown-handler.js
import { clickConfirm } from './staticMethods/dom.js' import { DismissReason } from './utils/DismissReason.js' import * as dom from './utils/dom/index.js' import { callIfFunction } from './utils/utils.js' /** * @param {GlobalState} globalState */ export const removeKeydownHandler = (globalState) => { if (globalState.keydownTarget && globalState.keydownHandlerAdded) { globalState.keydownTarget.removeEventListener('keydown', globalState.keydownHandler, { capture: globalState.keydownListenerCapture, }) globalState.keydownHandlerAdded = false } } /** * @param {GlobalState} globalState * @param {SweetAlertOptions} innerParams * @param {*} dismissWith */ export const addKeydownHandler = (globalState, innerParams, dismissWith) => { removeKeydownHandler(globalState) if (!innerParams.toast) { globalState.keydownHandler = (e) => keydownHandler(innerParams, e, dismissWith) globalState.keydownTarget = innerParams.keydownListenerCapture ? window : dom.getPopup() globalState.keydownListenerCapture = innerParams.keydownListenerCapture globalState.keydownTarget.addEventListener('keydown', globalState.keydownHandler, { capture: globalState.keydownListenerCapture, }) globalState.keydownHandlerAdded = true } } /** * @param {number} index * @param {number} increment */ export const setFocus = (index, increment) => { const focusableElements = dom.getFocusableElements() // search for visible elements and select the next possible match if (focusableElements.length) { index = index + increment // rollover to first item if (index === focusableElements.length) { index = 0 // go to last item } else if (index === -1) { index = focusableElements.length - 1 } focusableElements[index].focus() return } // no visible focusable elements, focus the popup dom.getPopup()?.focus() } const arrowKeysNextButton = ['ArrowRight', 'ArrowDown'] const arrowKeysPreviousButton = ['ArrowLeft', 'ArrowUp'] /** * @param {SweetAlertOptions} innerParams * @param {KeyboardEvent} event * @param {Function} dismissWith */ const keydownHandler = (innerParams, event, dismissWith) => { if (!innerParams) { return // This instance has already been destroyed } // Ignore keydown during IME composition // https://developer.mozilla.org/en-US/docs/Web/API/Document/keydown_event#ignoring_keydown_during_ime_composition // https://github.com/sweetalert2/sweetalert2/issues/720 // https://github.com/sweetalert2/sweetalert2/issues/2406 if (event.isComposing || event.keyCode === 229) { return } if (innerParams.stopKeydownPropagation) { event.stopPropagation() } // ENTER if (event.key === 'Enter') { handleEnter(event, innerParams) } // TAB else if (event.key === 'Tab') { handleTab(event) } // ARROWS - switch focus between buttons else if ([...arrowKeysNextButton, ...arrowKeysPreviousButton].includes(event.key)) { handleArrows(event.key) } // ESC else if (event.key === 'Escape') { handleEsc(event, innerParams, dismissWith) } } /** * @param {KeyboardEvent} event * @param {SweetAlertOptions} innerParams */ const handleEnter = (event, innerParams) => { // https://github.com/sweetalert2/sweetalert2/issues/2386 if (!callIfFunction(innerParams.allowEnterKey)) { return } const input = dom.getInput(dom.getPopup(), innerParams.input) if (event.target && input && event.target instanceof HTMLElement && event.target.outerHTML === input.outerHTML) { if (['textarea', 'file'].includes(innerParams.input)) { return // do not submit } clickConfirm() event.preventDefault() } } /** * @param {KeyboardEvent} event */ const handleTab = (event) => { const targetElement = event.target const focusableElements = dom.getFocusableElements() let btnIndex = -1 for (let i = 0; i < focusableElements.length; i++) { if (targetElement === focusableElements[i]) { btnIndex = i break } } // Cycle to the next button if (!event.shiftKey) { setFocus(btnIndex, 1) } // Cycle to the prev button else { setFocus(btnIndex, -1) } event.stopPropagation() event.preventDefault() } /** * @param {string} key */ const handleArrows = (key) => { const actions = dom.getActions() const confirmButton = dom.getConfirmButton() const denyButton = dom.getDenyButton() const cancelButton = dom.getCancelButton() if (!actions || !confirmButton || !denyButton || !cancelButton) { return } /** @type HTMLElement[] */ const buttons = [confirmButton, denyButton, cancelButton] if (document.activeElement instanceof HTMLElement && !buttons.includes(document.activeElement)) { return } const sibling = arrowKeysNextButton.includes(key) ? 'nextElementSibling' : 'previousElementSibling' let buttonToFocus = document.activeElement if (!buttonToFocus) { return } for (let i = 0; i < actions.children.length; i++) { buttonToFocus = buttonToFocus[sibling] if (!buttonToFocus) { return } if (buttonToFocus instanceof HTMLButtonElement && dom.isVisible(buttonToFocus)) { break } } if (buttonToFocus instanceof HTMLButtonElement) { buttonToFocus.focus() } } /** * @param {KeyboardEvent} event * @param {SweetAlertOptions} innerParams * @param {Function} dismissWith */ const handleEsc = (event, innerParams, dismissWith) => { if (callIfFunction(innerParams.allowEscapeKey)) { event.preventDefault() dismissWith(DismissReason.esc) } }