@zag-js/aria-hidden 0.2.2 → 0.8.0

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.ts CHANGED
@@ -1,3 +1,10 @@
1
- declare function ariaHidden(targets: Array<HTMLElement | null>, rootEl?: HTMLElement): (() => void) | undefined;
1
+ type AriaHiddenOptions = {
2
+ rootEl?: HTMLElement;
3
+ defer?: boolean;
4
+ };
5
+ type MaybeElement = HTMLElement | null;
6
+ type Targets = Array<MaybeElement>;
7
+ type TargetsOrFn = Targets | (() => Targets);
8
+ declare function ariaHidden(targetsOrFn: TargetsOrFn, options?: AriaHiddenOptions): () => void;
2
9
 
3
- export { ariaHidden };
10
+ export { AriaHiddenOptions, ariaHidden };
package/dist/index.js CHANGED
@@ -23,11 +23,11 @@ __export(src_exports, {
23
23
  ariaHidden: () => ariaHidden
24
24
  });
25
25
  module.exports = __toCommonJS(src_exports);
26
- var elementCountMap = /* @__PURE__ */ new WeakMap();
27
- function isLiveRegion(node, win) {
28
- return node instanceof win.HTMLElement && node.dataset.liveAnnouncer === "true";
29
- }
30
- function ariaHidden(targets, rootEl) {
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
31
  const exclude = targets.filter(Boolean);
32
32
  if (exclude.length === 0)
33
33
  return;
@@ -36,70 +36,116 @@ function ariaHidden(targets, rootEl) {
36
36
  const visibleNodes = new Set(exclude);
37
37
  const hiddenNodes = /* @__PURE__ */ new Set();
38
38
  const root = rootEl ?? doc.body;
39
- const walker = doc.createTreeWalker(root, NodeFilter.SHOW_ELEMENT, {
40
- acceptNode(node2) {
41
- if (isLiveRegion(node2, win)) {
42
- visibleNodes.add(node2);
43
- }
44
- if (visibleNodes.has(node2) || hiddenNodes.has(node2.parentElement)) {
39
+ let walk = (root2) => {
40
+ for (let element of root2.querySelectorAll("[data-live-announcer], [data-zag-top-layer]")) {
41
+ visibleNodes.add(element);
42
+ }
43
+ let acceptNode = (node) => {
44
+ if (visibleNodes.has(node) || hiddenNodes.has(node.parentElement) && node.parentElement.getAttribute("role") !== "row") {
45
45
  return NodeFilter.FILTER_REJECT;
46
46
  }
47
- if (node2 instanceof win.HTMLElement && node2.getAttribute("role") === "row") {
48
- return NodeFilter.FILTER_SKIP;
49
- }
50
- if (exclude.some((target) => node2.contains(target))) {
51
- return NodeFilter.FILTER_SKIP;
47
+ for (let target of visibleNodes) {
48
+ if (node.contains(target)) {
49
+ return NodeFilter.FILTER_SKIP;
50
+ }
52
51
  }
53
52
  return NodeFilter.FILTER_ACCEPT;
53
+ };
54
+ let walker = doc.createTreeWalker(root2, NodeFilter.SHOW_ELEMENT, { acceptNode });
55
+ let acceptRoot = acceptNode(root2);
56
+ if (acceptRoot === NodeFilter.FILTER_ACCEPT) {
57
+ hide(root2);
54
58
  }
55
- });
56
- const hide = (node2) => {
57
- let refCount = elementCountMap.get(node2) ?? 0;
58
- if (node2.getAttribute("aria-hidden") === "true" && refCount === 0) {
59
+ if (acceptRoot !== NodeFilter.FILTER_REJECT) {
60
+ let node = walker.nextNode();
61
+ while (node != null) {
62
+ hide(node);
63
+ node = walker.nextNode();
64
+ }
65
+ }
66
+ };
67
+ let hide = (node) => {
68
+ let refCount = refCountMap.get(node) ?? 0;
69
+ if (node.getAttribute("aria-hidden") === "true" && refCount === 0) {
59
70
  return;
60
71
  }
61
72
  if (refCount === 0) {
62
- node2.setAttribute("aria-hidden", "true");
73
+ node.setAttribute("aria-hidden", "true");
63
74
  }
64
- hiddenNodes.add(node2);
65
- elementCountMap.set(node2, refCount + 1);
75
+ hiddenNodes.add(node);
76
+ refCountMap.set(node, refCount + 1);
66
77
  };
67
- let node = walker.nextNode();
68
- while (node != null) {
69
- hide(node);
70
- node = walker.nextNode();
78
+ if (observerStack.length) {
79
+ observerStack[observerStack.length - 1].disconnect();
71
80
  }
81
+ walk(root);
72
82
  const observer = new win.MutationObserver((changes) => {
73
83
  for (let change of changes) {
74
- if (change.type !== "childList" || change.addedNodes.length === 0)
84
+ if (change.type !== "childList" || change.addedNodes.length === 0) {
75
85
  continue;
76
- if (![...visibleNodes, ...hiddenNodes].some((node2) => node2.contains(change.target))) {
77
- for (const node2 of change.addedNodes) {
78
- if (isLiveRegion(node2, win)) {
79
- visibleNodes.add(node2);
80
- } else if (node2 instanceof win.Element) {
81
- hide(node2);
86
+ }
87
+ if (![...visibleNodes, ...hiddenNodes].some((node) => node.contains(change.target))) {
88
+ for (let node of change.removedNodes) {
89
+ if (node instanceof win.Element) {
90
+ visibleNodes.delete(node);
91
+ hiddenNodes.delete(node);
92
+ }
93
+ }
94
+ for (let node of change.addedNodes) {
95
+ if ((node instanceof win.HTMLElement || node instanceof win.SVGElement) && (node.dataset.liveAnnouncer === "true" || node.dataset.zagTopLayer === "true")) {
96
+ visibleNodes.add(node);
97
+ } else if (node instanceof win.Element) {
98
+ walk(node);
82
99
  }
83
100
  }
84
101
  }
85
102
  }
86
103
  });
87
104
  observer.observe(root, { childList: true, subtree: true });
105
+ let observerWrapper = {
106
+ observe() {
107
+ observer.observe(root, { childList: true, subtree: true });
108
+ },
109
+ disconnect() {
110
+ observer.disconnect();
111
+ }
112
+ };
113
+ observerStack.push(observerWrapper);
88
114
  return () => {
89
115
  observer.disconnect();
90
- for (let node2 of hiddenNodes) {
91
- let count = elementCountMap.get(node2);
116
+ for (let node of hiddenNodes) {
117
+ let count = refCountMap.get(node);
92
118
  if (count === 1) {
93
- node2.removeAttribute("aria-hidden");
94
- elementCountMap.delete(node2);
95
- continue;
119
+ node.removeAttribute("aria-hidden");
120
+ refCountMap.delete(node);
121
+ } else {
122
+ refCountMap.set(node, count - 1);
96
123
  }
97
- if (count !== void 0) {
98
- elementCountMap.set(node2, count - 1);
124
+ }
125
+ if (observerWrapper === observerStack[observerStack.length - 1]) {
126
+ observerStack.pop();
127
+ if (observerStack.length) {
128
+ observerStack[observerStack.length - 1].observe();
99
129
  }
130
+ } else {
131
+ observerStack.splice(observerStack.indexOf(observerWrapper), 1);
100
132
  }
101
133
  };
102
134
  }
135
+ function ariaHidden(targetsOrFn, options = {}) {
136
+ const { defer } = options;
137
+ const func = defer ? import_dom_query.raf : (v) => v();
138
+ const cleanups = [];
139
+ cleanups.push(
140
+ func(() => {
141
+ const targets = typeof targetsOrFn === "function" ? targetsOrFn() : targetsOrFn;
142
+ cleanups.push(ariaHiddenImpl(targets, options));
143
+ })
144
+ );
145
+ return () => {
146
+ cleanups.forEach((fn) => fn?.());
147
+ };
148
+ }
103
149
  // Annotate the CommonJS export names for ESM import in node:
104
150
  0 && (module.exports = {
105
151
  ariaHidden
package/dist/index.mjs CHANGED
@@ -1,9 +1,9 @@
1
1
  // src/index.ts
2
- var elementCountMap = /* @__PURE__ */ new WeakMap();
3
- function isLiveRegion(node, win) {
4
- return node instanceof win.HTMLElement && node.dataset.liveAnnouncer === "true";
5
- }
6
- function ariaHidden(targets, rootEl) {
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
7
  const exclude = targets.filter(Boolean);
8
8
  if (exclude.length === 0)
9
9
  return;
@@ -12,70 +12,116 @@ function ariaHidden(targets, rootEl) {
12
12
  const visibleNodes = new Set(exclude);
13
13
  const hiddenNodes = /* @__PURE__ */ new Set();
14
14
  const root = rootEl ?? doc.body;
15
- const walker = doc.createTreeWalker(root, NodeFilter.SHOW_ELEMENT, {
16
- acceptNode(node2) {
17
- if (isLiveRegion(node2, win)) {
18
- visibleNodes.add(node2);
19
- }
20
- if (visibleNodes.has(node2) || hiddenNodes.has(node2.parentElement)) {
15
+ let walk = (root2) => {
16
+ for (let element of root2.querySelectorAll("[data-live-announcer], [data-zag-top-layer]")) {
17
+ visibleNodes.add(element);
18
+ }
19
+ let acceptNode = (node) => {
20
+ if (visibleNodes.has(node) || hiddenNodes.has(node.parentElement) && node.parentElement.getAttribute("role") !== "row") {
21
21
  return NodeFilter.FILTER_REJECT;
22
22
  }
23
- if (node2 instanceof win.HTMLElement && node2.getAttribute("role") === "row") {
24
- return NodeFilter.FILTER_SKIP;
25
- }
26
- if (exclude.some((target) => node2.contains(target))) {
27
- return NodeFilter.FILTER_SKIP;
23
+ for (let target of visibleNodes) {
24
+ if (node.contains(target)) {
25
+ return NodeFilter.FILTER_SKIP;
26
+ }
28
27
  }
29
28
  return NodeFilter.FILTER_ACCEPT;
29
+ };
30
+ let walker = doc.createTreeWalker(root2, NodeFilter.SHOW_ELEMENT, { acceptNode });
31
+ let acceptRoot = acceptNode(root2);
32
+ if (acceptRoot === NodeFilter.FILTER_ACCEPT) {
33
+ hide(root2);
30
34
  }
31
- });
32
- const hide = (node2) => {
33
- let refCount = elementCountMap.get(node2) ?? 0;
34
- if (node2.getAttribute("aria-hidden") === "true" && refCount === 0) {
35
+ if (acceptRoot !== NodeFilter.FILTER_REJECT) {
36
+ let node = walker.nextNode();
37
+ while (node != null) {
38
+ hide(node);
39
+ node = walker.nextNode();
40
+ }
41
+ }
42
+ };
43
+ let hide = (node) => {
44
+ let refCount = refCountMap.get(node) ?? 0;
45
+ if (node.getAttribute("aria-hidden") === "true" && refCount === 0) {
35
46
  return;
36
47
  }
37
48
  if (refCount === 0) {
38
- node2.setAttribute("aria-hidden", "true");
49
+ node.setAttribute("aria-hidden", "true");
39
50
  }
40
- hiddenNodes.add(node2);
41
- elementCountMap.set(node2, refCount + 1);
51
+ hiddenNodes.add(node);
52
+ refCountMap.set(node, refCount + 1);
42
53
  };
43
- let node = walker.nextNode();
44
- while (node != null) {
45
- hide(node);
46
- node = walker.nextNode();
54
+ if (observerStack.length) {
55
+ observerStack[observerStack.length - 1].disconnect();
47
56
  }
57
+ walk(root);
48
58
  const observer = new win.MutationObserver((changes) => {
49
59
  for (let change of changes) {
50
- if (change.type !== "childList" || change.addedNodes.length === 0)
60
+ if (change.type !== "childList" || change.addedNodes.length === 0) {
51
61
  continue;
52
- if (![...visibleNodes, ...hiddenNodes].some((node2) => node2.contains(change.target))) {
53
- for (const node2 of change.addedNodes) {
54
- if (isLiveRegion(node2, win)) {
55
- visibleNodes.add(node2);
56
- } else if (node2 instanceof win.Element) {
57
- hide(node2);
62
+ }
63
+ if (![...visibleNodes, ...hiddenNodes].some((node) => node.contains(change.target))) {
64
+ for (let node of change.removedNodes) {
65
+ if (node instanceof win.Element) {
66
+ visibleNodes.delete(node);
67
+ hiddenNodes.delete(node);
68
+ }
69
+ }
70
+ for (let node of change.addedNodes) {
71
+ if ((node instanceof win.HTMLElement || node instanceof win.SVGElement) && (node.dataset.liveAnnouncer === "true" || node.dataset.zagTopLayer === "true")) {
72
+ visibleNodes.add(node);
73
+ } else if (node instanceof win.Element) {
74
+ walk(node);
58
75
  }
59
76
  }
60
77
  }
61
78
  }
62
79
  });
63
80
  observer.observe(root, { childList: true, subtree: true });
81
+ let observerWrapper = {
82
+ observe() {
83
+ observer.observe(root, { childList: true, subtree: true });
84
+ },
85
+ disconnect() {
86
+ observer.disconnect();
87
+ }
88
+ };
89
+ observerStack.push(observerWrapper);
64
90
  return () => {
65
91
  observer.disconnect();
66
- for (let node2 of hiddenNodes) {
67
- let count = elementCountMap.get(node2);
92
+ for (let node of hiddenNodes) {
93
+ let count = refCountMap.get(node);
68
94
  if (count === 1) {
69
- node2.removeAttribute("aria-hidden");
70
- elementCountMap.delete(node2);
71
- continue;
95
+ node.removeAttribute("aria-hidden");
96
+ refCountMap.delete(node);
97
+ } else {
98
+ refCountMap.set(node, count - 1);
72
99
  }
73
- if (count !== void 0) {
74
- elementCountMap.set(node2, count - 1);
100
+ }
101
+ if (observerWrapper === observerStack[observerStack.length - 1]) {
102
+ observerStack.pop();
103
+ if (observerStack.length) {
104
+ observerStack[observerStack.length - 1].observe();
75
105
  }
106
+ } else {
107
+ observerStack.splice(observerStack.indexOf(observerWrapper), 1);
76
108
  }
77
109
  };
78
110
  }
111
+ function ariaHidden(targetsOrFn, options = {}) {
112
+ const { defer } = options;
113
+ const func = defer ? raf : (v) => v();
114
+ const cleanups = [];
115
+ cleanups.push(
116
+ func(() => {
117
+ const targets = typeof targetsOrFn === "function" ? targetsOrFn() : targetsOrFn;
118
+ cleanups.push(ariaHiddenImpl(targets, options));
119
+ })
120
+ );
121
+ return () => {
122
+ cleanups.forEach((fn) => fn?.());
123
+ };
124
+ }
79
125
  export {
80
126
  ariaHidden
81
127
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zag-js/aria-hidden",
3
- "version": "0.2.2",
3
+ "version": "0.8.0",
4
4
  "description": "Hide targets from screen readers",
5
5
  "keywords": [
6
6
  "js",
@@ -23,6 +23,9 @@
23
23
  },
24
24
  "clean-package": "../../../clean-package.config.json",
25
25
  "main": "dist/index.js",
26
+ "dependencies": {
27
+ "@zag-js/dom-query": "0.1.4"
28
+ },
26
29
  "devDependencies": {
27
30
  "clean-package": "2.2.0"
28
31
  },