@zag-js/aria-hidden
Advanced tools
Comparing version 0.2.2 to 0.8.0
@@ -1,3 +0,10 @@ | ||
declare function ariaHidden(targets: Array<HTMLElement | null>, rootEl?: HTMLElement): (() => void) | undefined; | ||
type AriaHiddenOptions = { | ||
rootEl?: HTMLElement; | ||
defer?: boolean; | ||
}; | ||
type MaybeElement = HTMLElement | null; | ||
type Targets = Array<MaybeElement>; | ||
type TargetsOrFn = Targets | (() => Targets); | ||
declare function ariaHidden(targetsOrFn: TargetsOrFn, options?: AriaHiddenOptions): () => void; | ||
export { ariaHidden }; | ||
export { AriaHiddenOptions, ariaHidden }; |
@@ -26,7 +26,7 @@ "use strict"; | ||
module.exports = __toCommonJS(src_exports); | ||
var elementCountMap = /* @__PURE__ */ new WeakMap(); | ||
function isLiveRegion(node, win) { | ||
return node instanceof win.HTMLElement && node.dataset.liveAnnouncer === "true"; | ||
} | ||
function ariaHidden(targets, rootEl) { | ||
var import_dom_query = require("@zag-js/dom-query"); | ||
var refCountMap = /* @__PURE__ */ new WeakMap(); | ||
var observerStack = []; | ||
function ariaHiddenImpl(targets, options = {}) { | ||
const { rootEl } = options; | ||
const exclude = targets.filter(Boolean); | ||
@@ -40,47 +40,64 @@ if (exclude.length === 0) | ||
const root = rootEl ?? doc.body; | ||
const walker = doc.createTreeWalker(root, NodeFilter.SHOW_ELEMENT, { | ||
acceptNode(node2) { | ||
if (isLiveRegion(node2, win)) { | ||
visibleNodes.add(node2); | ||
} | ||
if (visibleNodes.has(node2) || hiddenNodes.has(node2.parentElement)) { | ||
let walk = (root2) => { | ||
for (let element of root2.querySelectorAll("[data-live-announcer], [data-zag-top-layer]")) { | ||
visibleNodes.add(element); | ||
} | ||
let acceptNode = (node) => { | ||
if (visibleNodes.has(node) || hiddenNodes.has(node.parentElement) && node.parentElement.getAttribute("role") !== "row") { | ||
return NodeFilter.FILTER_REJECT; | ||
} | ||
if (node2 instanceof win.HTMLElement && node2.getAttribute("role") === "row") { | ||
return NodeFilter.FILTER_SKIP; | ||
for (let target of visibleNodes) { | ||
if (node.contains(target)) { | ||
return NodeFilter.FILTER_SKIP; | ||
} | ||
} | ||
if (exclude.some((target) => node2.contains(target))) { | ||
return NodeFilter.FILTER_SKIP; | ||
} | ||
return NodeFilter.FILTER_ACCEPT; | ||
}; | ||
let walker = doc.createTreeWalker(root2, NodeFilter.SHOW_ELEMENT, { acceptNode }); | ||
let acceptRoot = acceptNode(root2); | ||
if (acceptRoot === NodeFilter.FILTER_ACCEPT) { | ||
hide(root2); | ||
} | ||
}); | ||
const hide = (node2) => { | ||
let refCount = elementCountMap.get(node2) ?? 0; | ||
if (node2.getAttribute("aria-hidden") === "true" && refCount === 0) { | ||
if (acceptRoot !== NodeFilter.FILTER_REJECT) { | ||
let node = walker.nextNode(); | ||
while (node != null) { | ||
hide(node); | ||
node = walker.nextNode(); | ||
} | ||
} | ||
}; | ||
let hide = (node) => { | ||
let refCount = refCountMap.get(node) ?? 0; | ||
if (node.getAttribute("aria-hidden") === "true" && refCount === 0) { | ||
return; | ||
} | ||
if (refCount === 0) { | ||
node2.setAttribute("aria-hidden", "true"); | ||
node.setAttribute("aria-hidden", "true"); | ||
} | ||
hiddenNodes.add(node2); | ||
elementCountMap.set(node2, refCount + 1); | ||
hiddenNodes.add(node); | ||
refCountMap.set(node, refCount + 1); | ||
}; | ||
let node = walker.nextNode(); | ||
while (node != null) { | ||
hide(node); | ||
node = walker.nextNode(); | ||
if (observerStack.length) { | ||
observerStack[observerStack.length - 1].disconnect(); | ||
} | ||
walk(root); | ||
const observer = new win.MutationObserver((changes) => { | ||
for (let change of changes) { | ||
if (change.type !== "childList" || change.addedNodes.length === 0) | ||
if (change.type !== "childList" || change.addedNodes.length === 0) { | ||
continue; | ||
if (![...visibleNodes, ...hiddenNodes].some((node2) => node2.contains(change.target))) { | ||
for (const node2 of change.addedNodes) { | ||
if (isLiveRegion(node2, win)) { | ||
visibleNodes.add(node2); | ||
} else if (node2 instanceof win.Element) { | ||
hide(node2); | ||
} | ||
if (![...visibleNodes, ...hiddenNodes].some((node) => node.contains(change.target))) { | ||
for (let node of change.removedNodes) { | ||
if (node instanceof win.Element) { | ||
visibleNodes.delete(node); | ||
hiddenNodes.delete(node); | ||
} | ||
} | ||
for (let node of change.addedNodes) { | ||
if ((node instanceof win.HTMLElement || node instanceof win.SVGElement) && (node.dataset.liveAnnouncer === "true" || node.dataset.zagTopLayer === "true")) { | ||
visibleNodes.add(node); | ||
} else if (node instanceof win.Element) { | ||
walk(node); | ||
} | ||
} | ||
} | ||
@@ -90,17 +107,46 @@ } | ||
observer.observe(root, { childList: true, subtree: true }); | ||
let observerWrapper = { | ||
observe() { | ||
observer.observe(root, { childList: true, subtree: true }); | ||
}, | ||
disconnect() { | ||
observer.disconnect(); | ||
} | ||
}; | ||
observerStack.push(observerWrapper); | ||
return () => { | ||
observer.disconnect(); | ||
for (let node2 of hiddenNodes) { | ||
let count = elementCountMap.get(node2); | ||
for (let node of hiddenNodes) { | ||
let count = refCountMap.get(node); | ||
if (count === 1) { | ||
node2.removeAttribute("aria-hidden"); | ||
elementCountMap.delete(node2); | ||
continue; | ||
node.removeAttribute("aria-hidden"); | ||
refCountMap.delete(node); | ||
} else { | ||
refCountMap.set(node, count - 1); | ||
} | ||
if (count !== void 0) { | ||
elementCountMap.set(node2, count - 1); | ||
} | ||
if (observerWrapper === observerStack[observerStack.length - 1]) { | ||
observerStack.pop(); | ||
if (observerStack.length) { | ||
observerStack[observerStack.length - 1].observe(); | ||
} | ||
} else { | ||
observerStack.splice(observerStack.indexOf(observerWrapper), 1); | ||
} | ||
}; | ||
} | ||
function ariaHidden(targetsOrFn, options = {}) { | ||
const { defer } = options; | ||
const func = defer ? import_dom_query.raf : (v) => v(); | ||
const cleanups = []; | ||
cleanups.push( | ||
func(() => { | ||
const targets = typeof targetsOrFn === "function" ? targetsOrFn() : targetsOrFn; | ||
cleanups.push(ariaHiddenImpl(targets, options)); | ||
}) | ||
); | ||
return () => { | ||
cleanups.forEach((fn) => fn?.()); | ||
}; | ||
} | ||
// Annotate the CommonJS export names for ESM import in node: | ||
@@ -107,0 +153,0 @@ 0 && (module.exports = { |
{ | ||
"name": "@zag-js/aria-hidden", | ||
"version": "0.2.2", | ||
"version": "0.8.0", | ||
"description": "Hide targets from screen readers", | ||
@@ -26,2 +26,5 @@ "keywords": [ | ||
"main": "dist/index.js", | ||
"dependencies": { | ||
"@zag-js/dom-query": "0.1.4" | ||
}, | ||
"devDependencies": { | ||
@@ -28,0 +31,0 @@ "clean-package": "2.2.0" |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
12200
287
1
+ Added@zag-js/dom-query@0.1.4
+ Added@zag-js/dom-query@0.1.4(transitive)