@zag-js/aria-hidden 0.65.1 → 0.66.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -1,10 +1,9 @@
1
- interface AriaHiddenOptions {
2
- rootEl?: HTMLElement;
3
- defer?: boolean;
4
- }
5
1
  type MaybeElement = HTMLElement | null;
6
2
  type Targets = Array<MaybeElement>;
7
3
  type TargetsOrFn = Targets | (() => Targets);
8
- declare function ariaHidden(targetsOrFn: TargetsOrFn, options?: AriaHiddenOptions): () => void;
4
+ type Options = {
5
+ defer?: boolean;
6
+ };
7
+ declare function ariaHidden(targetsOrFn: TargetsOrFn, options?: Options): () => void;
9
8
 
10
- export { type AriaHiddenOptions, ariaHidden };
9
+ export { ariaHidden };
package/dist/index.d.ts CHANGED
@@ -1,10 +1,9 @@
1
- interface AriaHiddenOptions {
2
- rootEl?: HTMLElement;
3
- defer?: boolean;
4
- }
5
1
  type MaybeElement = HTMLElement | null;
6
2
  type Targets = Array<MaybeElement>;
7
3
  type TargetsOrFn = Targets | (() => Targets);
8
- declare function ariaHidden(targetsOrFn: TargetsOrFn, options?: AriaHiddenOptions): () => void;
4
+ type Options = {
5
+ defer?: boolean;
6
+ };
7
+ declare function ariaHidden(targetsOrFn: TargetsOrFn, options?: Options): () => void;
9
8
 
10
- export { type AriaHiddenOptions, ariaHidden };
9
+ export { ariaHidden };
package/dist/index.js CHANGED
@@ -23,122 +23,20 @@ __export(src_exports, {
23
23
  ariaHidden: () => ariaHidden
24
24
  });
25
25
  module.exports = __toCommonJS(src_exports);
26
- var import_dom_query = require("@zag-js/dom-query");
27
- var refCountMap = /* @__PURE__ */ new WeakMap();
28
- var observerStack = [];
29
- function ariaHiddenImpl(targets, options = {}) {
30
- const { rootEl } = options;
31
- const exclude = targets.filter(Boolean);
32
- if (exclude.length === 0) return;
33
- const doc = exclude[0].ownerDocument || document;
34
- const win = doc.defaultView ?? window;
35
- const visibleNodes = new Set(exclude);
36
- const hiddenNodes = /* @__PURE__ */ new Set();
37
- const root = rootEl ?? doc.body;
38
- let walk = (root2) => {
39
- for (let element of root2.querySelectorAll("[data-live-announcer], [data-zag-top-layer]")) {
40
- visibleNodes.add(element);
41
- }
42
- let acceptNode = (node) => {
43
- if (visibleNodes.has(node) || hiddenNodes.has(node.parentElement) && node.parentElement.getAttribute("role") !== "row") {
44
- return NodeFilter.FILTER_REJECT;
45
- }
46
- for (let target of visibleNodes) {
47
- if (node.contains(target)) {
48
- return NodeFilter.FILTER_SKIP;
49
- }
50
- }
51
- return NodeFilter.FILTER_ACCEPT;
52
- };
53
- let walker = doc.createTreeWalker(root2, NodeFilter.SHOW_ELEMENT, { acceptNode });
54
- let acceptRoot = acceptNode(root2);
55
- if (acceptRoot === NodeFilter.FILTER_ACCEPT) {
56
- hide(root2);
57
- }
58
- if (acceptRoot !== NodeFilter.FILTER_REJECT) {
59
- let node = walker.nextNode();
60
- while (node != null) {
61
- hide(node);
62
- node = walker.nextNode();
63
- }
64
- }
65
- };
66
- let hide = (node) => {
67
- let refCount = refCountMap.get(node) ?? 0;
68
- if (node.getAttribute("aria-hidden") === "true" && refCount === 0) {
69
- return;
70
- }
71
- if (refCount === 0) {
72
- node.setAttribute("aria-hidden", "true");
73
- }
74
- hiddenNodes.add(node);
75
- refCountMap.set(node, refCount + 1);
76
- };
77
- if (observerStack.length) {
78
- observerStack[observerStack.length - 1].disconnect();
79
- }
80
- walk(root);
81
- const observer = new win.MutationObserver((changes) => {
82
- for (let change of changes) {
83
- if (change.type !== "childList" || change.addedNodes.length === 0) {
84
- continue;
85
- }
86
- if (![...visibleNodes, ...hiddenNodes].some((node) => node.contains(change.target))) {
87
- for (let node of change.removedNodes) {
88
- if (node instanceof win.Element) {
89
- visibleNodes.delete(node);
90
- hiddenNodes.delete(node);
91
- }
92
- }
93
- for (let node of change.addedNodes) {
94
- if ((node instanceof win.HTMLElement || node instanceof win.SVGElement) && (node.dataset.liveAnnouncer === "true" || node.dataset.zagTopLayer === "true")) {
95
- visibleNodes.add(node);
96
- } else if (node instanceof win.Element) {
97
- walk(node);
98
- }
99
- }
100
- }
101
- }
102
- });
103
- observer.observe(root, { childList: true, subtree: true });
104
- let observerWrapper = {
105
- observe() {
106
- observer.observe(root, { childList: true, subtree: true });
107
- },
108
- disconnect() {
109
- observer.disconnect();
110
- }
111
- };
112
- observerStack.push(observerWrapper);
113
- return () => {
114
- observer.disconnect();
115
- for (let node of hiddenNodes) {
116
- let count = refCountMap.get(node);
117
- if (count === 1) {
118
- node.removeAttribute("aria-hidden");
119
- refCountMap.delete(node);
120
- } else {
121
- refCountMap.set(node, count - 1);
122
- }
123
- }
124
- if (observerWrapper === observerStack[observerStack.length - 1]) {
125
- observerStack.pop();
126
- if (observerStack.length) {
127
- observerStack[observerStack.length - 1].observe();
128
- }
129
- } else {
130
- observerStack.splice(observerStack.indexOf(observerWrapper), 1);
131
- }
132
- };
133
- }
26
+ var import_aria_hidden = require("aria-hidden");
27
+ var raf = (fn) => {
28
+ const frameId = requestAnimationFrame(() => fn());
29
+ return () => cancelAnimationFrame(frameId);
30
+ };
134
31
  function ariaHidden(targetsOrFn, options = {}) {
135
- const { defer } = options;
136
- const func = defer ? import_dom_query.raf : (v) => v();
32
+ const { defer = true } = options;
33
+ const func = defer ? raf : (v) => v();
137
34
  const cleanups = [];
138
35
  cleanups.push(
139
36
  func(() => {
140
37
  const targets = typeof targetsOrFn === "function" ? targetsOrFn() : targetsOrFn;
141
- cleanups.push(ariaHiddenImpl(targets, options));
38
+ const elements = targets.filter(Boolean);
39
+ cleanups.push((0, import_aria_hidden.hideOthers)(elements));
142
40
  })
143
41
  );
144
42
  return () => {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["// Credits: https://github.com/adobe/react-spectrum/blob/main/packages/@react-aria/overlays/src/ariaHideOutside.ts\nimport { raf } from \"@zag-js/dom-query\"\n\nconst refCountMap = new WeakMap<Element, number>()\nconst observerStack: any[] = []\n\nexport interface AriaHiddenOptions {\n rootEl?: HTMLElement\n defer?: boolean\n}\n\ntype MaybeElement = HTMLElement | null\ntype Targets = Array<MaybeElement>\ntype TargetsOrFn = Targets | (() => Targets)\n\nfunction ariaHiddenImpl(targets: Targets, options: AriaHiddenOptions = {}) {\n const { rootEl } = options\n\n const exclude = targets.filter(Boolean) as HTMLElement[]\n if (exclude.length === 0) return\n\n const doc = exclude[0].ownerDocument || document\n const win = doc.defaultView ?? window\n\n const visibleNodes = new Set<Element>(exclude)\n const hiddenNodes = new Set<Element>()\n\n const root = rootEl ?? doc.body\n\n let walk = (root: Element) => {\n // Keep live announcer and top layer elements (e.g. toasts) visible.\n for (let element of root.querySelectorAll(\"[data-live-announcer], [data-zag-top-layer]\")) {\n visibleNodes.add(element)\n }\n\n let acceptNode = (node: Element) => {\n // Skip this node and its children if it is one of the target nodes, or a live announcer.\n // Also skip children of already hidden nodes, as aria-hidden is recursive. An exception is\n // made for elements with role=\"row\" since VoiceOver on iOS has issues hiding elements with role=\"row\".\n // For that case we want to hide the cells inside as well (https://bugs.webkit.org/show_bug.cgi?id=222623).\n if (\n visibleNodes.has(node) ||\n (hiddenNodes.has(node.parentElement!) && node.parentElement!.getAttribute(\"role\") !== \"row\")\n ) {\n return NodeFilter.FILTER_REJECT\n }\n\n // Skip this node but continue to children if one of the targets is inside the node.\n for (let target of visibleNodes) {\n if (node.contains(target)) {\n return NodeFilter.FILTER_SKIP\n }\n }\n\n return NodeFilter.FILTER_ACCEPT\n }\n\n let walker = doc.createTreeWalker(root, NodeFilter.SHOW_ELEMENT, { acceptNode })\n\n // TreeWalker does not include the root.\n let acceptRoot = acceptNode(root)\n if (acceptRoot === NodeFilter.FILTER_ACCEPT) {\n hide(root)\n }\n\n if (acceptRoot !== NodeFilter.FILTER_REJECT) {\n let node = walker.nextNode() as Element\n while (node != null) {\n hide(node)\n node = walker.nextNode() as Element\n }\n }\n }\n\n let hide = (node: Element) => {\n let refCount = refCountMap.get(node) ?? 0\n\n // If already aria-hidden, and the ref count is zero, then this element\n // was already hidden and there's nothing for us to do.\n if (node.getAttribute(\"aria-hidden\") === \"true\" && refCount === 0) {\n return\n }\n\n if (refCount === 0) {\n node.setAttribute(\"aria-hidden\", \"true\")\n }\n\n hiddenNodes.add(node)\n refCountMap.set(node, refCount + 1)\n }\n\n if (observerStack.length) {\n observerStack[observerStack.length - 1].disconnect()\n }\n\n walk(root)\n\n const observer = new win.MutationObserver((changes) => {\n for (let change of changes) {\n if (change.type !== \"childList\" || change.addedNodes.length === 0) {\n continue\n }\n\n // If the parent element of the added nodes is not within one of the targets,\n // and not already inside a hidden node, hide all of the new children.\n if (![...visibleNodes, ...hiddenNodes].some((node) => node.contains(change.target))) {\n for (let node of change.removedNodes) {\n if (node instanceof win.Element) {\n visibleNodes.delete(node)\n hiddenNodes.delete(node)\n }\n }\n\n for (let node of change.addedNodes) {\n if (\n (node instanceof win.HTMLElement || node instanceof win.SVGElement) &&\n (node.dataset.liveAnnouncer === \"true\" || node.dataset.zagTopLayer === \"true\")\n ) {\n visibleNodes.add(node)\n } else if (node instanceof win.Element) {\n walk(node)\n }\n }\n }\n }\n })\n\n observer.observe(root, { childList: true, subtree: true })\n\n let observerWrapper = {\n observe() {\n observer.observe(root, { childList: true, subtree: true })\n },\n disconnect() {\n observer.disconnect()\n },\n }\n\n observerStack.push(observerWrapper)\n\n return () => {\n observer.disconnect()\n\n for (let node of hiddenNodes) {\n let count = refCountMap.get(node)\n if (count === 1) {\n node.removeAttribute(\"aria-hidden\")\n refCountMap.delete(node)\n } else {\n refCountMap.set(node, count! - 1)\n }\n }\n\n // Remove this observer from the stack, and start the previous one.\n if (observerWrapper === observerStack[observerStack.length - 1]) {\n observerStack.pop()\n if (observerStack.length) {\n observerStack[observerStack.length - 1].observe()\n }\n } else {\n observerStack.splice(observerStack.indexOf(observerWrapper), 1)\n }\n }\n}\n\nexport function ariaHidden(targetsOrFn: TargetsOrFn, options: AriaHiddenOptions = {}) {\n const { defer } = options\n const func = defer ? raf : (v: any) => v()\n const cleanups: (VoidFunction | undefined)[] = []\n cleanups.push(\n func(() => {\n const targets = typeof targetsOrFn === \"function\" ? targetsOrFn() : targetsOrFn\n cleanups.push(ariaHiddenImpl(targets, options))\n }),\n )\n return () => {\n cleanups.forEach((fn) => fn?.())\n }\n}\n"],"mappings":"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","names":["root"]}
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import { hideOthers } from \"aria-hidden\"\n\nconst raf = (fn: VoidFunction) => {\n const frameId = requestAnimationFrame(() => fn())\n return () => cancelAnimationFrame(frameId)\n}\n\ntype MaybeElement = HTMLElement | null\ntype Targets = Array<MaybeElement>\ntype TargetsOrFn = Targets | (() => Targets)\n\ntype Options = {\n defer?: boolean\n}\n\nexport function ariaHidden(targetsOrFn: TargetsOrFn, options: Options = {}) {\n const { defer = true } = options\n const func = defer ? raf : (v: any) => v()\n const cleanups: (VoidFunction | undefined)[] = []\n cleanups.push(\n func(() => {\n const targets = typeof targetsOrFn === \"function\" ? targetsOrFn() : targetsOrFn\n const elements = targets.filter(Boolean) as HTMLElement[]\n cleanups.push(hideOthers(elements))\n }),\n )\n return () => {\n cleanups.forEach((fn) => fn?.())\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,yBAA2B;AAE3B,IAAM,MAAM,CAAC,OAAqB;AAChC,QAAM,UAAU,sBAAsB,MAAM,GAAG,CAAC;AAChD,SAAO,MAAM,qBAAqB,OAAO;AAC3C;AAUO,SAAS,WAAW,aAA0B,UAAmB,CAAC,GAAG;AAC1E,QAAM,EAAE,QAAQ,KAAK,IAAI;AACzB,QAAM,OAAO,QAAQ,MAAM,CAAC,MAAW,EAAE;AACzC,QAAM,WAAyC,CAAC;AAChD,WAAS;AAAA,IACP,KAAK,MAAM;AACT,YAAM,UAAU,OAAO,gBAAgB,aAAa,YAAY,IAAI;AACpE,YAAM,WAAW,QAAQ,OAAO,OAAO;AACvC,eAAS,SAAK,+BAAW,QAAQ,CAAC;AAAA,IACpC,CAAC;AAAA,EACH;AACA,SAAO,MAAM;AACX,aAAS,QAAQ,CAAC,OAAO,KAAK,CAAC;AAAA,EACjC;AACF;","names":[]}
package/dist/index.mjs CHANGED
@@ -1,120 +1,18 @@
1
1
  // src/index.ts
2
- import { raf } from "@zag-js/dom-query";
3
- var refCountMap = /* @__PURE__ */ new WeakMap();
4
- var observerStack = [];
5
- function ariaHiddenImpl(targets, options = {}) {
6
- const { rootEl } = options;
7
- const exclude = targets.filter(Boolean);
8
- if (exclude.length === 0) return;
9
- const doc = exclude[0].ownerDocument || document;
10
- const win = doc.defaultView ?? window;
11
- const visibleNodes = new Set(exclude);
12
- const hiddenNodes = /* @__PURE__ */ new Set();
13
- const root = rootEl ?? doc.body;
14
- let walk = (root2) => {
15
- for (let element of root2.querySelectorAll("[data-live-announcer], [data-zag-top-layer]")) {
16
- visibleNodes.add(element);
17
- }
18
- let acceptNode = (node) => {
19
- if (visibleNodes.has(node) || hiddenNodes.has(node.parentElement) && node.parentElement.getAttribute("role") !== "row") {
20
- return NodeFilter.FILTER_REJECT;
21
- }
22
- for (let target of visibleNodes) {
23
- if (node.contains(target)) {
24
- return NodeFilter.FILTER_SKIP;
25
- }
26
- }
27
- return NodeFilter.FILTER_ACCEPT;
28
- };
29
- let walker = doc.createTreeWalker(root2, NodeFilter.SHOW_ELEMENT, { acceptNode });
30
- let acceptRoot = acceptNode(root2);
31
- if (acceptRoot === NodeFilter.FILTER_ACCEPT) {
32
- hide(root2);
33
- }
34
- if (acceptRoot !== NodeFilter.FILTER_REJECT) {
35
- let node = walker.nextNode();
36
- while (node != null) {
37
- hide(node);
38
- node = walker.nextNode();
39
- }
40
- }
41
- };
42
- let hide = (node) => {
43
- let refCount = refCountMap.get(node) ?? 0;
44
- if (node.getAttribute("aria-hidden") === "true" && refCount === 0) {
45
- return;
46
- }
47
- if (refCount === 0) {
48
- node.setAttribute("aria-hidden", "true");
49
- }
50
- hiddenNodes.add(node);
51
- refCountMap.set(node, refCount + 1);
52
- };
53
- if (observerStack.length) {
54
- observerStack[observerStack.length - 1].disconnect();
55
- }
56
- walk(root);
57
- const observer = new win.MutationObserver((changes) => {
58
- for (let change of changes) {
59
- if (change.type !== "childList" || change.addedNodes.length === 0) {
60
- continue;
61
- }
62
- if (![...visibleNodes, ...hiddenNodes].some((node) => node.contains(change.target))) {
63
- for (let node of change.removedNodes) {
64
- if (node instanceof win.Element) {
65
- visibleNodes.delete(node);
66
- hiddenNodes.delete(node);
67
- }
68
- }
69
- for (let node of change.addedNodes) {
70
- if ((node instanceof win.HTMLElement || node instanceof win.SVGElement) && (node.dataset.liveAnnouncer === "true" || node.dataset.zagTopLayer === "true")) {
71
- visibleNodes.add(node);
72
- } else if (node instanceof win.Element) {
73
- walk(node);
74
- }
75
- }
76
- }
77
- }
78
- });
79
- observer.observe(root, { childList: true, subtree: true });
80
- let observerWrapper = {
81
- observe() {
82
- observer.observe(root, { childList: true, subtree: true });
83
- },
84
- disconnect() {
85
- observer.disconnect();
86
- }
87
- };
88
- observerStack.push(observerWrapper);
89
- return () => {
90
- observer.disconnect();
91
- for (let node of hiddenNodes) {
92
- let count = refCountMap.get(node);
93
- if (count === 1) {
94
- node.removeAttribute("aria-hidden");
95
- refCountMap.delete(node);
96
- } else {
97
- refCountMap.set(node, count - 1);
98
- }
99
- }
100
- if (observerWrapper === observerStack[observerStack.length - 1]) {
101
- observerStack.pop();
102
- if (observerStack.length) {
103
- observerStack[observerStack.length - 1].observe();
104
- }
105
- } else {
106
- observerStack.splice(observerStack.indexOf(observerWrapper), 1);
107
- }
108
- };
109
- }
2
+ import { hideOthers } from "aria-hidden";
3
+ var raf = (fn) => {
4
+ const frameId = requestAnimationFrame(() => fn());
5
+ return () => cancelAnimationFrame(frameId);
6
+ };
110
7
  function ariaHidden(targetsOrFn, options = {}) {
111
- const { defer } = options;
8
+ const { defer = true } = options;
112
9
  const func = defer ? raf : (v) => v();
113
10
  const cleanups = [];
114
11
  cleanups.push(
115
12
  func(() => {
116
13
  const targets = typeof targetsOrFn === "function" ? targetsOrFn() : targetsOrFn;
117
- cleanups.push(ariaHiddenImpl(targets, options));
14
+ const elements = targets.filter(Boolean);
15
+ cleanups.push(hideOthers(elements));
118
16
  })
119
17
  );
120
18
  return () => {
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["// Credits: https://github.com/adobe/react-spectrum/blob/main/packages/@react-aria/overlays/src/ariaHideOutside.ts\nimport { raf } from \"@zag-js/dom-query\"\n\nconst refCountMap = new WeakMap<Element, number>()\nconst observerStack: any[] = []\n\nexport interface AriaHiddenOptions {\n rootEl?: HTMLElement\n defer?: boolean\n}\n\ntype MaybeElement = HTMLElement | null\ntype Targets = Array<MaybeElement>\ntype TargetsOrFn = Targets | (() => Targets)\n\nfunction ariaHiddenImpl(targets: Targets, options: AriaHiddenOptions = {}) {\n const { rootEl } = options\n\n const exclude = targets.filter(Boolean) as HTMLElement[]\n if (exclude.length === 0) return\n\n const doc = exclude[0].ownerDocument || document\n const win = doc.defaultView ?? window\n\n const visibleNodes = new Set<Element>(exclude)\n const hiddenNodes = new Set<Element>()\n\n const root = rootEl ?? doc.body\n\n let walk = (root: Element) => {\n // Keep live announcer and top layer elements (e.g. toasts) visible.\n for (let element of root.querySelectorAll(\"[data-live-announcer], [data-zag-top-layer]\")) {\n visibleNodes.add(element)\n }\n\n let acceptNode = (node: Element) => {\n // Skip this node and its children if it is one of the target nodes, or a live announcer.\n // Also skip children of already hidden nodes, as aria-hidden is recursive. An exception is\n // made for elements with role=\"row\" since VoiceOver on iOS has issues hiding elements with role=\"row\".\n // For that case we want to hide the cells inside as well (https://bugs.webkit.org/show_bug.cgi?id=222623).\n if (\n visibleNodes.has(node) ||\n (hiddenNodes.has(node.parentElement!) && node.parentElement!.getAttribute(\"role\") !== \"row\")\n ) {\n return NodeFilter.FILTER_REJECT\n }\n\n // Skip this node but continue to children if one of the targets is inside the node.\n for (let target of visibleNodes) {\n if (node.contains(target)) {\n return NodeFilter.FILTER_SKIP\n }\n }\n\n return NodeFilter.FILTER_ACCEPT\n }\n\n let walker = doc.createTreeWalker(root, NodeFilter.SHOW_ELEMENT, { acceptNode })\n\n // TreeWalker does not include the root.\n let acceptRoot = acceptNode(root)\n if (acceptRoot === NodeFilter.FILTER_ACCEPT) {\n hide(root)\n }\n\n if (acceptRoot !== NodeFilter.FILTER_REJECT) {\n let node = walker.nextNode() as Element\n while (node != null) {\n hide(node)\n node = walker.nextNode() as Element\n }\n }\n }\n\n let hide = (node: Element) => {\n let refCount = refCountMap.get(node) ?? 0\n\n // If already aria-hidden, and the ref count is zero, then this element\n // was already hidden and there's nothing for us to do.\n if (node.getAttribute(\"aria-hidden\") === \"true\" && refCount === 0) {\n return\n }\n\n if (refCount === 0) {\n node.setAttribute(\"aria-hidden\", \"true\")\n }\n\n hiddenNodes.add(node)\n refCountMap.set(node, refCount + 1)\n }\n\n if (observerStack.length) {\n observerStack[observerStack.length - 1].disconnect()\n }\n\n walk(root)\n\n const observer = new win.MutationObserver((changes) => {\n for (let change of changes) {\n if (change.type !== \"childList\" || change.addedNodes.length === 0) {\n continue\n }\n\n // If the parent element of the added nodes is not within one of the targets,\n // and not already inside a hidden node, hide all of the new children.\n if (![...visibleNodes, ...hiddenNodes].some((node) => node.contains(change.target))) {\n for (let node of change.removedNodes) {\n if (node instanceof win.Element) {\n visibleNodes.delete(node)\n hiddenNodes.delete(node)\n }\n }\n\n for (let node of change.addedNodes) {\n if (\n (node instanceof win.HTMLElement || node instanceof win.SVGElement) &&\n (node.dataset.liveAnnouncer === \"true\" || node.dataset.zagTopLayer === \"true\")\n ) {\n visibleNodes.add(node)\n } else if (node instanceof win.Element) {\n walk(node)\n }\n }\n }\n }\n })\n\n observer.observe(root, { childList: true, subtree: true })\n\n let observerWrapper = {\n observe() {\n observer.observe(root, { childList: true, subtree: true })\n },\n disconnect() {\n observer.disconnect()\n },\n }\n\n observerStack.push(observerWrapper)\n\n return () => {\n observer.disconnect()\n\n for (let node of hiddenNodes) {\n let count = refCountMap.get(node)\n if (count === 1) {\n node.removeAttribute(\"aria-hidden\")\n refCountMap.delete(node)\n } else {\n refCountMap.set(node, count! - 1)\n }\n }\n\n // Remove this observer from the stack, and start the previous one.\n if (observerWrapper === observerStack[observerStack.length - 1]) {\n observerStack.pop()\n if (observerStack.length) {\n observerStack[observerStack.length - 1].observe()\n }\n } else {\n observerStack.splice(observerStack.indexOf(observerWrapper), 1)\n }\n }\n}\n\nexport function ariaHidden(targetsOrFn: TargetsOrFn, options: AriaHiddenOptions = {}) {\n const { defer } = options\n const func = defer ? raf : (v: any) => v()\n const cleanups: (VoidFunction | undefined)[] = []\n cleanups.push(\n func(() => {\n const targets = typeof targetsOrFn === \"function\" ? targetsOrFn() : targetsOrFn\n cleanups.push(ariaHiddenImpl(targets, options))\n }),\n )\n return () => {\n cleanups.forEach((fn) => fn?.())\n }\n}\n"],"mappings":"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","names":["root"]}
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import { hideOthers } from \"aria-hidden\"\n\nconst raf = (fn: VoidFunction) => {\n const frameId = requestAnimationFrame(() => fn())\n return () => cancelAnimationFrame(frameId)\n}\n\ntype MaybeElement = HTMLElement | null\ntype Targets = Array<MaybeElement>\ntype TargetsOrFn = Targets | (() => Targets)\n\ntype Options = {\n defer?: boolean\n}\n\nexport function ariaHidden(targetsOrFn: TargetsOrFn, options: Options = {}) {\n const { defer = true } = options\n const func = defer ? raf : (v: any) => v()\n const cleanups: (VoidFunction | undefined)[] = []\n cleanups.push(\n func(() => {\n const targets = typeof targetsOrFn === \"function\" ? targetsOrFn() : targetsOrFn\n const elements = targets.filter(Boolean) as HTMLElement[]\n cleanups.push(hideOthers(elements))\n }),\n )\n return () => {\n cleanups.forEach((fn) => fn?.())\n }\n}\n"],"mappings":";AAAA,SAAS,kBAAkB;AAE3B,IAAM,MAAM,CAAC,OAAqB;AAChC,QAAM,UAAU,sBAAsB,MAAM,GAAG,CAAC;AAChD,SAAO,MAAM,qBAAqB,OAAO;AAC3C;AAUO,SAAS,WAAW,aAA0B,UAAmB,CAAC,GAAG;AAC1E,QAAM,EAAE,QAAQ,KAAK,IAAI;AACzB,QAAM,OAAO,QAAQ,MAAM,CAAC,MAAW,EAAE;AACzC,QAAM,WAAyC,CAAC;AAChD,WAAS;AAAA,IACP,KAAK,MAAM;AACT,YAAM,UAAU,OAAO,gBAAgB,aAAa,YAAY,IAAI;AACpE,YAAM,WAAW,QAAQ,OAAO,OAAO;AACvC,eAAS,KAAK,WAAW,QAAQ,CAAC;AAAA,IACpC,CAAC;AAAA,EACH;AACA,SAAO,MAAM;AACX,aAAS,QAAQ,CAAC,OAAO,KAAK,CAAC;AAAA,EACjC;AACF;","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zag-js/aria-hidden",
3
- "version": "0.65.1",
3
+ "version": "0.66.1",
4
4
  "description": "Hide targets from screen readers",
5
5
  "keywords": [
6
6
  "js",
@@ -25,7 +25,7 @@
25
25
  "clean-package": "../../../clean-package.config.json",
26
26
  "main": "dist/index.js",
27
27
  "dependencies": {
28
- "@zag-js/dom-query": "0.65.1"
28
+ "aria-hidden": "1.2.4"
29
29
  },
30
30
  "devDependencies": {
31
31
  "clean-package": "2.2.0"
package/src/index.ts CHANGED
@@ -1,176 +1,27 @@
1
- // Credits: https://github.com/adobe/react-spectrum/blob/main/packages/@react-aria/overlays/src/ariaHideOutside.ts
2
- import { raf } from "@zag-js/dom-query"
1
+ import { hideOthers } from "aria-hidden"
3
2
 
4
- const refCountMap = new WeakMap<Element, number>()
5
- const observerStack: any[] = []
6
-
7
- export interface AriaHiddenOptions {
8
- rootEl?: HTMLElement
9
- defer?: boolean
3
+ const raf = (fn: VoidFunction) => {
4
+ const frameId = requestAnimationFrame(() => fn())
5
+ return () => cancelAnimationFrame(frameId)
10
6
  }
11
7
 
12
8
  type MaybeElement = HTMLElement | null
13
9
  type Targets = Array<MaybeElement>
14
10
  type TargetsOrFn = Targets | (() => Targets)
15
11
 
16
- function ariaHiddenImpl(targets: Targets, options: AriaHiddenOptions = {}) {
17
- const { rootEl } = options
18
-
19
- const exclude = targets.filter(Boolean) as HTMLElement[]
20
- if (exclude.length === 0) return
21
-
22
- const doc = exclude[0].ownerDocument || document
23
- const win = doc.defaultView ?? window
24
-
25
- const visibleNodes = new Set<Element>(exclude)
26
- const hiddenNodes = new Set<Element>()
27
-
28
- const root = rootEl ?? doc.body
29
-
30
- let walk = (root: Element) => {
31
- // Keep live announcer and top layer elements (e.g. toasts) visible.
32
- for (let element of root.querySelectorAll("[data-live-announcer], [data-zag-top-layer]")) {
33
- visibleNodes.add(element)
34
- }
35
-
36
- let acceptNode = (node: Element) => {
37
- // Skip this node and its children if it is one of the target nodes, or a live announcer.
38
- // Also skip children of already hidden nodes, as aria-hidden is recursive. An exception is
39
- // made for elements with role="row" since VoiceOver on iOS has issues hiding elements with role="row".
40
- // For that case we want to hide the cells inside as well (https://bugs.webkit.org/show_bug.cgi?id=222623).
41
- if (
42
- visibleNodes.has(node) ||
43
- (hiddenNodes.has(node.parentElement!) && node.parentElement!.getAttribute("role") !== "row")
44
- ) {
45
- return NodeFilter.FILTER_REJECT
46
- }
47
-
48
- // Skip this node but continue to children if one of the targets is inside the node.
49
- for (let target of visibleNodes) {
50
- if (node.contains(target)) {
51
- return NodeFilter.FILTER_SKIP
52
- }
53
- }
54
-
55
- return NodeFilter.FILTER_ACCEPT
56
- }
57
-
58
- let walker = doc.createTreeWalker(root, NodeFilter.SHOW_ELEMENT, { acceptNode })
59
-
60
- // TreeWalker does not include the root.
61
- let acceptRoot = acceptNode(root)
62
- if (acceptRoot === NodeFilter.FILTER_ACCEPT) {
63
- hide(root)
64
- }
65
-
66
- if (acceptRoot !== NodeFilter.FILTER_REJECT) {
67
- let node = walker.nextNode() as Element
68
- while (node != null) {
69
- hide(node)
70
- node = walker.nextNode() as Element
71
- }
72
- }
73
- }
74
-
75
- let hide = (node: Element) => {
76
- let refCount = refCountMap.get(node) ?? 0
77
-
78
- // If already aria-hidden, and the ref count is zero, then this element
79
- // was already hidden and there's nothing for us to do.
80
- if (node.getAttribute("aria-hidden") === "true" && refCount === 0) {
81
- return
82
- }
83
-
84
- if (refCount === 0) {
85
- node.setAttribute("aria-hidden", "true")
86
- }
87
-
88
- hiddenNodes.add(node)
89
- refCountMap.set(node, refCount + 1)
90
- }
91
-
92
- if (observerStack.length) {
93
- observerStack[observerStack.length - 1].disconnect()
94
- }
95
-
96
- walk(root)
97
-
98
- const observer = new win.MutationObserver((changes) => {
99
- for (let change of changes) {
100
- if (change.type !== "childList" || change.addedNodes.length === 0) {
101
- continue
102
- }
103
-
104
- // If the parent element of the added nodes is not within one of the targets,
105
- // and not already inside a hidden node, hide all of the new children.
106
- if (![...visibleNodes, ...hiddenNodes].some((node) => node.contains(change.target))) {
107
- for (let node of change.removedNodes) {
108
- if (node instanceof win.Element) {
109
- visibleNodes.delete(node)
110
- hiddenNodes.delete(node)
111
- }
112
- }
113
-
114
- for (let node of change.addedNodes) {
115
- if (
116
- (node instanceof win.HTMLElement || node instanceof win.SVGElement) &&
117
- (node.dataset.liveAnnouncer === "true" || node.dataset.zagTopLayer === "true")
118
- ) {
119
- visibleNodes.add(node)
120
- } else if (node instanceof win.Element) {
121
- walk(node)
122
- }
123
- }
124
- }
125
- }
126
- })
127
-
128
- observer.observe(root, { childList: true, subtree: true })
129
-
130
- let observerWrapper = {
131
- observe() {
132
- observer.observe(root, { childList: true, subtree: true })
133
- },
134
- disconnect() {
135
- observer.disconnect()
136
- },
137
- }
138
-
139
- observerStack.push(observerWrapper)
140
-
141
- return () => {
142
- observer.disconnect()
143
-
144
- for (let node of hiddenNodes) {
145
- let count = refCountMap.get(node)
146
- if (count === 1) {
147
- node.removeAttribute("aria-hidden")
148
- refCountMap.delete(node)
149
- } else {
150
- refCountMap.set(node, count! - 1)
151
- }
152
- }
153
-
154
- // Remove this observer from the stack, and start the previous one.
155
- if (observerWrapper === observerStack[observerStack.length - 1]) {
156
- observerStack.pop()
157
- if (observerStack.length) {
158
- observerStack[observerStack.length - 1].observe()
159
- }
160
- } else {
161
- observerStack.splice(observerStack.indexOf(observerWrapper), 1)
162
- }
163
- }
12
+ type Options = {
13
+ defer?: boolean
164
14
  }
165
15
 
166
- export function ariaHidden(targetsOrFn: TargetsOrFn, options: AriaHiddenOptions = {}) {
167
- const { defer } = options
16
+ export function ariaHidden(targetsOrFn: TargetsOrFn, options: Options = {}) {
17
+ const { defer = true } = options
168
18
  const func = defer ? raf : (v: any) => v()
169
19
  const cleanups: (VoidFunction | undefined)[] = []
170
20
  cleanups.push(
171
21
  func(() => {
172
22
  const targets = typeof targetsOrFn === "function" ? targetsOrFn() : targetsOrFn
173
- cleanups.push(ariaHiddenImpl(targets, options))
23
+ const elements = targets.filter(Boolean) as HTMLElement[]
24
+ cleanups.push(hideOthers(elements))
174
25
  }),
175
26
  )
176
27
  return () => {