focus-visible.js 2.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475
  1. /*!
  2. * (C) Ionic http://ionicframework.com - MIT License
  3. */
  4. const ION_FOCUSED = 'ion-focused';
  5. const ION_FOCUSABLE = 'ion-focusable';
  6. const FOCUS_KEYS = [
  7. 'Tab',
  8. 'ArrowDown',
  9. 'Space',
  10. 'Escape',
  11. ' ',
  12. 'Shift',
  13. 'Enter',
  14. 'ArrowLeft',
  15. 'ArrowRight',
  16. 'ArrowUp',
  17. 'Home',
  18. 'End',
  19. ];
  20. const startFocusVisible = (rootEl) => {
  21. let currentFocus = [];
  22. let keyboardMode = true;
  23. const ref = rootEl ? rootEl.shadowRoot : document;
  24. const root = rootEl ? rootEl : document.body;
  25. const setFocus = (elements) => {
  26. currentFocus.forEach((el) => el.classList.remove(ION_FOCUSED));
  27. elements.forEach((el) => el.classList.add(ION_FOCUSED));
  28. currentFocus = elements;
  29. };
  30. const pointerDown = () => {
  31. keyboardMode = false;
  32. setFocus([]);
  33. };
  34. const onKeydown = (ev) => {
  35. keyboardMode = FOCUS_KEYS.includes(ev.key);
  36. if (!keyboardMode) {
  37. setFocus([]);
  38. }
  39. };
  40. const onFocusin = (ev) => {
  41. if (keyboardMode && ev.composedPath !== undefined) {
  42. const toFocus = ev.composedPath().filter((el) => {
  43. // TODO(FW-2832): type
  44. if (el.classList) {
  45. return el.classList.contains(ION_FOCUSABLE);
  46. }
  47. return false;
  48. });
  49. setFocus(toFocus);
  50. }
  51. };
  52. const onFocusout = () => {
  53. if (ref.activeElement === root) {
  54. setFocus([]);
  55. }
  56. };
  57. ref.addEventListener('keydown', onKeydown);
  58. ref.addEventListener('focusin', onFocusin);
  59. ref.addEventListener('focusout', onFocusout);
  60. ref.addEventListener('touchstart', pointerDown, { passive: true });
  61. ref.addEventListener('mousedown', pointerDown);
  62. const destroy = () => {
  63. ref.removeEventListener('keydown', onKeydown);
  64. ref.removeEventListener('focusin', onFocusin);
  65. ref.removeEventListener('focusout', onFocusout);
  66. ref.removeEventListener('touchstart', pointerDown);
  67. ref.removeEventListener('mousedown', pointerDown);
  68. };
  69. return {
  70. destroy,
  71. setFocus,
  72. };
  73. };
  74. export { startFocusVisible };