utils.js 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
  2. require("../../../_virtual/_rolldown/runtime.js");
  3. const require_aria = require("../../../utils/dom/aria.js");
  4. const require_tokens = require("./tokens.js");
  5. let vue = require("vue");
  6. //#region ../../packages/components/focus-trap/src/utils.ts
  7. const focusReason = (0, vue.ref)();
  8. const lastUserFocusTimestamp = (0, vue.ref)(0);
  9. const lastAutomatedFocusTimestamp = (0, vue.ref)(0);
  10. let focusReasonUserCount = 0;
  11. const obtainAllFocusableElements = (element) => {
  12. const nodes = [];
  13. const walker = document.createTreeWalker(element, NodeFilter.SHOW_ELEMENT, { acceptNode: (node) => {
  14. const isHiddenInput = node.tagName === "INPUT" && node.type === "hidden";
  15. if (node.disabled || node.hidden || isHiddenInput) return NodeFilter.FILTER_SKIP;
  16. return node.tabIndex >= 0 || node === document.activeElement ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP;
  17. } });
  18. while (walker.nextNode()) nodes.push(walker.currentNode);
  19. return nodes;
  20. };
  21. const getVisibleElement = (elements, container) => {
  22. for (const element of elements) if (!isHidden(element, container)) return element;
  23. };
  24. const isHidden = (element, container) => {
  25. if (process.env.NODE_ENV === "test") return false;
  26. if (getComputedStyle(element).visibility === "hidden") return true;
  27. while (element) {
  28. if (container && element === container) return false;
  29. if (getComputedStyle(element).display === "none") return true;
  30. element = element.parentElement;
  31. }
  32. return false;
  33. };
  34. const getEdges = (container) => {
  35. const focusable = obtainAllFocusableElements(container);
  36. return [getVisibleElement(focusable, container), getVisibleElement(focusable.reverse(), container)];
  37. };
  38. const isSelectable = (element) => {
  39. return element instanceof HTMLInputElement && "select" in element;
  40. };
  41. const tryFocus = (element, shouldSelect) => {
  42. if (element) {
  43. const prevFocusedElement = document.activeElement;
  44. require_aria.focusElement(element, { preventScroll: true });
  45. lastAutomatedFocusTimestamp.value = window.performance.now();
  46. if (element !== prevFocusedElement && isSelectable(element) && shouldSelect) element.select();
  47. }
  48. };
  49. function removeFromStack(list, item) {
  50. const copy = [...list];
  51. const idx = list.indexOf(item);
  52. if (idx !== -1) copy.splice(idx, 1);
  53. return copy;
  54. }
  55. const createFocusableStack = () => {
  56. let stack = [];
  57. const push = (layer) => {
  58. const currentLayer = stack[0];
  59. if (currentLayer && layer !== currentLayer) currentLayer.pause();
  60. stack = removeFromStack(stack, layer);
  61. stack.unshift(layer);
  62. };
  63. const remove = (layer) => {
  64. stack = removeFromStack(stack, layer);
  65. stack[0]?.resume?.();
  66. };
  67. return {
  68. push,
  69. remove
  70. };
  71. };
  72. const focusFirstDescendant = (elements, shouldSelect = false) => {
  73. const prevFocusedElement = document.activeElement;
  74. for (const element of elements) {
  75. tryFocus(element, shouldSelect);
  76. if (document.activeElement !== prevFocusedElement) return;
  77. }
  78. };
  79. const focusableStack = createFocusableStack();
  80. const isFocusCausedByUserEvent = () => {
  81. return lastUserFocusTimestamp.value > lastAutomatedFocusTimestamp.value;
  82. };
  83. const notifyFocusReasonPointer = () => {
  84. focusReason.value = "pointer";
  85. lastUserFocusTimestamp.value = window.performance.now();
  86. };
  87. const notifyFocusReasonKeydown = () => {
  88. focusReason.value = "keyboard";
  89. lastUserFocusTimestamp.value = window.performance.now();
  90. };
  91. const useFocusReason = () => {
  92. (0, vue.onMounted)(() => {
  93. if (focusReasonUserCount === 0) {
  94. document.addEventListener("mousedown", notifyFocusReasonPointer);
  95. document.addEventListener("touchstart", notifyFocusReasonPointer);
  96. document.addEventListener("keydown", notifyFocusReasonKeydown);
  97. }
  98. focusReasonUserCount++;
  99. });
  100. (0, vue.onBeforeUnmount)(() => {
  101. focusReasonUserCount--;
  102. if (focusReasonUserCount <= 0) {
  103. document.removeEventListener("mousedown", notifyFocusReasonPointer);
  104. document.removeEventListener("touchstart", notifyFocusReasonPointer);
  105. document.removeEventListener("keydown", notifyFocusReasonKeydown);
  106. }
  107. });
  108. return {
  109. focusReason,
  110. lastUserFocusTimestamp,
  111. lastAutomatedFocusTimestamp
  112. };
  113. };
  114. const createFocusOutPreventedEvent = (detail) => {
  115. return new CustomEvent(require_tokens.FOCUSOUT_PREVENTED, {
  116. ...require_tokens.FOCUSOUT_PREVENTED_OPTS,
  117. detail
  118. });
  119. };
  120. //#endregion
  121. exports.createFocusOutPreventedEvent = createFocusOutPreventedEvent;
  122. exports.focusFirstDescendant = focusFirstDescendant;
  123. exports.focusableStack = focusableStack;
  124. exports.getEdges = getEdges;
  125. exports.getVisibleElement = getVisibleElement;
  126. exports.isFocusCausedByUserEvent = isFocusCausedByUserEvent;
  127. exports.isHidden = isHidden;
  128. exports.obtainAllFocusableElements = obtainAllFocusableElements;
  129. exports.tryFocus = tryFocus;
  130. exports.useFocusReason = useFocusReason;
  131. //# sourceMappingURL=utils.js.map