Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.
 
 
 

206 строки
5.5 KiB

  1. import { clickConfirm } from './staticMethods/dom.js'
  2. import { DismissReason } from './utils/DismissReason.js'
  3. import * as dom from './utils/dom/index.js'
  4. import { callIfFunction } from './utils/utils.js'
  5. /**
  6. * @param {GlobalState} globalState
  7. */
  8. export const removeKeydownHandler = (globalState) => {
  9. if (globalState.keydownTarget && globalState.keydownHandlerAdded) {
  10. globalState.keydownTarget.removeEventListener('keydown', globalState.keydownHandler, {
  11. capture: globalState.keydownListenerCapture,
  12. })
  13. globalState.keydownHandlerAdded = false
  14. }
  15. }
  16. /**
  17. * @param {GlobalState} globalState
  18. * @param {SweetAlertOptions} innerParams
  19. * @param {*} dismissWith
  20. */
  21. export const addKeydownHandler = (globalState, innerParams, dismissWith) => {
  22. removeKeydownHandler(globalState)
  23. if (!innerParams.toast) {
  24. globalState.keydownHandler = (e) => keydownHandler(innerParams, e, dismissWith)
  25. globalState.keydownTarget = innerParams.keydownListenerCapture ? window : dom.getPopup()
  26. globalState.keydownListenerCapture = innerParams.keydownListenerCapture
  27. globalState.keydownTarget.addEventListener('keydown', globalState.keydownHandler, {
  28. capture: globalState.keydownListenerCapture,
  29. })
  30. globalState.keydownHandlerAdded = true
  31. }
  32. }
  33. /**
  34. * @param {number} index
  35. * @param {number} increment
  36. */
  37. export const setFocus = (index, increment) => {
  38. const focusableElements = dom.getFocusableElements()
  39. // search for visible elements and select the next possible match
  40. if (focusableElements.length) {
  41. index = index + increment
  42. // rollover to first item
  43. if (index === focusableElements.length) {
  44. index = 0
  45. // go to last item
  46. } else if (index === -1) {
  47. index = focusableElements.length - 1
  48. }
  49. focusableElements[index].focus()
  50. return
  51. }
  52. // no visible focusable elements, focus the popup
  53. dom.getPopup()?.focus()
  54. }
  55. const arrowKeysNextButton = ['ArrowRight', 'ArrowDown']
  56. const arrowKeysPreviousButton = ['ArrowLeft', 'ArrowUp']
  57. /**
  58. * @param {SweetAlertOptions} innerParams
  59. * @param {KeyboardEvent} event
  60. * @param {Function} dismissWith
  61. */
  62. const keydownHandler = (innerParams, event, dismissWith) => {
  63. if (!innerParams) {
  64. return // This instance has already been destroyed
  65. }
  66. // Ignore keydown during IME composition
  67. // https://developer.mozilla.org/en-US/docs/Web/API/Document/keydown_event#ignoring_keydown_during_ime_composition
  68. // https://github.com/sweetalert2/sweetalert2/issues/720
  69. // https://github.com/sweetalert2/sweetalert2/issues/2406
  70. if (event.isComposing || event.keyCode === 229) {
  71. return
  72. }
  73. if (innerParams.stopKeydownPropagation) {
  74. event.stopPropagation()
  75. }
  76. // ENTER
  77. if (event.key === 'Enter') {
  78. handleEnter(event, innerParams)
  79. }
  80. // TAB
  81. else if (event.key === 'Tab') {
  82. handleTab(event)
  83. }
  84. // ARROWS - switch focus between buttons
  85. else if ([...arrowKeysNextButton, ...arrowKeysPreviousButton].includes(event.key)) {
  86. handleArrows(event.key)
  87. }
  88. // ESC
  89. else if (event.key === 'Escape') {
  90. handleEsc(event, innerParams, dismissWith)
  91. }
  92. }
  93. /**
  94. * @param {KeyboardEvent} event
  95. * @param {SweetAlertOptions} innerParams
  96. */
  97. const handleEnter = (event, innerParams) => {
  98. // https://github.com/sweetalert2/sweetalert2/issues/2386
  99. if (!callIfFunction(innerParams.allowEnterKey)) {
  100. return
  101. }
  102. const input = dom.getInput(dom.getPopup(), innerParams.input)
  103. if (event.target && input && event.target instanceof HTMLElement && event.target.outerHTML === input.outerHTML) {
  104. if (['textarea', 'file'].includes(innerParams.input)) {
  105. return // do not submit
  106. }
  107. clickConfirm()
  108. event.preventDefault()
  109. }
  110. }
  111. /**
  112. * @param {KeyboardEvent} event
  113. */
  114. const handleTab = (event) => {
  115. const targetElement = event.target
  116. const focusableElements = dom.getFocusableElements()
  117. let btnIndex = -1
  118. for (let i = 0; i < focusableElements.length; i++) {
  119. if (targetElement === focusableElements[i]) {
  120. btnIndex = i
  121. break
  122. }
  123. }
  124. // Cycle to the next button
  125. if (!event.shiftKey) {
  126. setFocus(btnIndex, 1)
  127. }
  128. // Cycle to the prev button
  129. else {
  130. setFocus(btnIndex, -1)
  131. }
  132. event.stopPropagation()
  133. event.preventDefault()
  134. }
  135. /**
  136. * @param {string} key
  137. */
  138. const handleArrows = (key) => {
  139. const actions = dom.getActions()
  140. const confirmButton = dom.getConfirmButton()
  141. const denyButton = dom.getDenyButton()
  142. const cancelButton = dom.getCancelButton()
  143. if (!actions || !confirmButton || !denyButton || !cancelButton) {
  144. return
  145. }
  146. /** @type HTMLElement[] */
  147. const buttons = [confirmButton, denyButton, cancelButton]
  148. if (document.activeElement instanceof HTMLElement && !buttons.includes(document.activeElement)) {
  149. return
  150. }
  151. const sibling = arrowKeysNextButton.includes(key) ? 'nextElementSibling' : 'previousElementSibling'
  152. let buttonToFocus = document.activeElement
  153. if (!buttonToFocus) {
  154. return
  155. }
  156. for (let i = 0; i < actions.children.length; i++) {
  157. buttonToFocus = buttonToFocus[sibling]
  158. if (!buttonToFocus) {
  159. return
  160. }
  161. if (buttonToFocus instanceof HTMLButtonElement && dom.isVisible(buttonToFocus)) {
  162. break
  163. }
  164. }
  165. if (buttonToFocus instanceof HTMLButtonElement) {
  166. buttonToFocus.focus()
  167. }
  168. }
  169. /**
  170. * @param {KeyboardEvent} event
  171. * @param {SweetAlertOptions} innerParams
  172. * @param {Function} dismissWith
  173. */
  174. const handleEsc = (event, innerParams, dismissWith) => {
  175. if (callIfFunction(innerParams.allowEscapeKey)) {
  176. event.preventDefault()
  177. dismissWith(DismissReason.esc)
  178. }
  179. }