@zag-js/aria-hidden 0.2.1 → 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,85 +23,129 @@ __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) {
31
- var _a;
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;
32
31
  const exclude = targets.filter(Boolean);
33
32
  if (exclude.length === 0)
34
33
  return;
35
34
  const doc = exclude[0].ownerDocument || document;
36
- const win = (_a = doc.defaultView) != null ? _a : window;
35
+ const win = doc.defaultView ?? window;
37
36
  const visibleNodes = new Set(exclude);
38
37
  const hiddenNodes = /* @__PURE__ */ new Set();
39
- const root = rootEl != null ? rootEl : doc.body;
40
- const walker = doc.createTreeWalker(root, NodeFilter.SHOW_ELEMENT, {
41
- acceptNode(node2) {
42
- if (isLiveRegion(node2, win)) {
43
- visibleNodes.add(node2);
44
- }
45
- if (visibleNodes.has(node2) || hiddenNodes.has(node2.parentElement)) {
38
+ const root = rootEl ?? doc.body;
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") {
46
45
  return NodeFilter.FILTER_REJECT;
47
46
  }
48
- if (node2 instanceof win.HTMLElement && node2.getAttribute("role") === "row") {
49
- return NodeFilter.FILTER_SKIP;
50
- }
51
- if (exclude.some((target) => node2.contains(target))) {
52
- return NodeFilter.FILTER_SKIP;
47
+ for (let target of visibleNodes) {
48
+ if (node.contains(target)) {
49
+ return NodeFilter.FILTER_SKIP;
50
+ }
53
51
  }
54
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);
55
58
  }
56
- });
57
- const hide = (node2) => {
58
- var _a2;
59
- let refCount = (_a2 = elementCountMap.get(node2)) != null ? _a2 : 0;
60
- 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) {
61
70
  return;
62
71
  }
63
72
  if (refCount === 0) {
64
- node2.setAttribute("aria-hidden", "true");
73
+ node.setAttribute("aria-hidden", "true");
65
74
  }
66
- hiddenNodes.add(node2);
67
- elementCountMap.set(node2, refCount + 1);
75
+ hiddenNodes.add(node);
76
+ refCountMap.set(node, refCount + 1);
68
77
  };
69
- let node = walker.nextNode();
70
- while (node != null) {
71
- hide(node);
72
- node = walker.nextNode();
78
+ if (observerStack.length) {
79
+ observerStack[observerStack.length - 1].disconnect();
73
80
  }
81
+ walk(root);
74
82
  const observer = new win.MutationObserver((changes) => {
75
83
  for (let change of changes) {
76
- if (change.type !== "childList" || change.addedNodes.length === 0)
84
+ if (change.type !== "childList" || change.addedNodes.length === 0) {
77
85
  continue;
78
- if (![...visibleNodes, ...hiddenNodes].some((node2) => node2.contains(change.target))) {
79
- for (const node2 of change.addedNodes) {
80
- if (isLiveRegion(node2, win)) {
81
- visibleNodes.add(node2);
82
- } else if (node2 instanceof win.Element) {
83
- 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);
84
99
  }
85
100
  }
86
101
  }
87
102
  }
88
103
  });
89
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);
90
114
  return () => {
91
115
  observer.disconnect();
92
- for (let node2 of hiddenNodes) {
93
- let count = elementCountMap.get(node2);
116
+ for (let node of hiddenNodes) {
117
+ let count = refCountMap.get(node);
94
118
  if (count === 1) {
95
- node2.removeAttribute("aria-hidden");
96
- elementCountMap.delete(node2);
97
- continue;
119
+ node.removeAttribute("aria-hidden");
120
+ refCountMap.delete(node);
121
+ } else {
122
+ refCountMap.set(node, count - 1);
98
123
  }
99
- if (count !== void 0) {
100
- 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();
101
129
  }
130
+ } else {
131
+ observerStack.splice(observerStack.indexOf(observerWrapper), 1);
102
132
  }
103
133
  };
104
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
+ }
105
149
  // Annotate the CommonJS export names for ESM import in node:
106
150
  0 && (module.exports = {
107
151
  ariaHidden
package/dist/index.mjs CHANGED
@@ -1,83 +1,127 @@
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) {
7
- var _a;
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;
8
7
  const exclude = targets.filter(Boolean);
9
8
  if (exclude.length === 0)
10
9
  return;
11
10
  const doc = exclude[0].ownerDocument || document;
12
- const win = (_a = doc.defaultView) != null ? _a : window;
11
+ const win = doc.defaultView ?? window;
13
12
  const visibleNodes = new Set(exclude);
14
13
  const hiddenNodes = /* @__PURE__ */ new Set();
15
- const root = rootEl != null ? rootEl : doc.body;
16
- const walker = doc.createTreeWalker(root, NodeFilter.SHOW_ELEMENT, {
17
- acceptNode(node2) {
18
- if (isLiveRegion(node2, win)) {
19
- visibleNodes.add(node2);
20
- }
21
- if (visibleNodes.has(node2) || hiddenNodes.has(node2.parentElement)) {
14
+ const root = rootEl ?? doc.body;
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") {
22
21
  return NodeFilter.FILTER_REJECT;
23
22
  }
24
- if (node2 instanceof win.HTMLElement && node2.getAttribute("role") === "row") {
25
- return NodeFilter.FILTER_SKIP;
26
- }
27
- if (exclude.some((target) => node2.contains(target))) {
28
- return NodeFilter.FILTER_SKIP;
23
+ for (let target of visibleNodes) {
24
+ if (node.contains(target)) {
25
+ return NodeFilter.FILTER_SKIP;
26
+ }
29
27
  }
30
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);
31
34
  }
32
- });
33
- const hide = (node2) => {
34
- var _a2;
35
- let refCount = (_a2 = elementCountMap.get(node2)) != null ? _a2 : 0;
36
- 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) {
37
46
  return;
38
47
  }
39
48
  if (refCount === 0) {
40
- node2.setAttribute("aria-hidden", "true");
49
+ node.setAttribute("aria-hidden", "true");
41
50
  }
42
- hiddenNodes.add(node2);
43
- elementCountMap.set(node2, refCount + 1);
51
+ hiddenNodes.add(node);
52
+ refCountMap.set(node, refCount + 1);
44
53
  };
45
- let node = walker.nextNode();
46
- while (node != null) {
47
- hide(node);
48
- node = walker.nextNode();
54
+ if (observerStack.length) {
55
+ observerStack[observerStack.length - 1].disconnect();
49
56
  }
57
+ walk(root);
50
58
  const observer = new win.MutationObserver((changes) => {
51
59
  for (let change of changes) {
52
- if (change.type !== "childList" || change.addedNodes.length === 0)
60
+ if (change.type !== "childList" || change.addedNodes.length === 0) {
53
61
  continue;
54
- if (![...visibleNodes, ...hiddenNodes].some((node2) => node2.contains(change.target))) {
55
- for (const node2 of change.addedNodes) {
56
- if (isLiveRegion(node2, win)) {
57
- visibleNodes.add(node2);
58
- } else if (node2 instanceof win.Element) {
59
- 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);
60
75
  }
61
76
  }
62
77
  }
63
78
  }
64
79
  });
65
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);
66
90
  return () => {
67
91
  observer.disconnect();
68
- for (let node2 of hiddenNodes) {
69
- let count = elementCountMap.get(node2);
92
+ for (let node of hiddenNodes) {
93
+ let count = refCountMap.get(node);
70
94
  if (count === 1) {
71
- node2.removeAttribute("aria-hidden");
72
- elementCountMap.delete(node2);
73
- continue;
95
+ node.removeAttribute("aria-hidden");
96
+ refCountMap.delete(node);
97
+ } else {
98
+ refCountMap.set(node, count - 1);
74
99
  }
75
- if (count !== void 0) {
76
- 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();
77
105
  }
106
+ } else {
107
+ observerStack.splice(observerStack.indexOf(observerWrapper), 1);
78
108
  }
79
109
  };
80
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
+ }
81
125
  export {
82
126
  ariaHidden
83
127
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zag-js/aria-hidden",
3
- "version": "0.2.1",
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
  },