@zag-js/aria-hidden
Version:
Hide targets from screen readers
108 lines (107 loc) • 3.98 kB
JavaScript
// src/walk-tree-outside.ts
import { findControlledElements, isHTMLElement } from "@zag-js/dom-query";
var counterMap = /* @__PURE__ */ new WeakMap();
var uncontrolledNodes = /* @__PURE__ */ new WeakMap();
var markerMap = {};
var lockCount = 0;
var unwrapHost = (node) => node && (node.host || unwrapHost(node.parentNode));
var correctTargets = (parent, targets) => targets.map((target) => {
if (parent.contains(target)) return target;
const correctedTarget = unwrapHost(target);
if (correctedTarget && parent.contains(correctedTarget)) {
return correctedTarget;
}
console.error("[zag-js > ariaHidden] target", target, "in not contained inside", parent, ". Doing nothing");
return null;
}).filter((x) => Boolean(x));
var ignoreableNodes = /* @__PURE__ */ new Set(["script", "output", "status", "next-route-announcer"]);
var isIgnoredNode = (node) => {
if (ignoreableNodes.has(node.localName)) return true;
if (node.role === "status") return true;
if (node.hasAttribute("aria-live")) return true;
return node.matches("[data-live-announcer]");
};
var walkTreeOutside = (originalTarget, props) => {
const { parentNode, markerName, controlAttribute, explicitBooleanValue, followControlledElements = true } = props;
const targets = correctTargets(parentNode, Array.isArray(originalTarget) ? originalTarget : [originalTarget]);
markerMap[markerName] || (markerMap[markerName] = /* @__PURE__ */ new WeakMap());
const markerCounter = markerMap[markerName];
const hiddenNodes = [];
const elementsToKeep = /* @__PURE__ */ new Set();
const elementsToStop = new Set(targets);
const keep = (el) => {
if (!el || elementsToKeep.has(el)) return;
elementsToKeep.add(el);
keep(el.parentNode);
};
targets.forEach((target) => {
keep(target);
if (followControlledElements && isHTMLElement(target)) {
findControlledElements(target, (controlledElement) => {
keep(controlledElement);
});
}
});
const deep = (parent) => {
if (!parent || elementsToStop.has(parent)) {
return;
}
Array.prototype.forEach.call(parent.children, (node) => {
if (elementsToKeep.has(node)) {
deep(node);
} else {
try {
if (isIgnoredNode(node)) return;
const attr = node.getAttribute(controlAttribute);
const alreadyHidden = explicitBooleanValue ? attr === "true" : attr !== null && attr !== "false";
const counterValue = (counterMap.get(node) || 0) + 1;
const markerValue = (markerCounter.get(node) || 0) + 1;
counterMap.set(node, counterValue);
markerCounter.set(node, markerValue);
hiddenNodes.push(node);
if (counterValue === 1 && alreadyHidden) {
uncontrolledNodes.set(node, true);
}
if (markerValue === 1) {
node.setAttribute(markerName, "");
}
if (!alreadyHidden) {
node.setAttribute(controlAttribute, explicitBooleanValue ? "true" : "");
}
} catch (e) {
console.error("[zag-js > ariaHidden] cannot operate on ", node, e);
}
}
});
};
deep(parentNode);
elementsToKeep.clear();
lockCount++;
return () => {
hiddenNodes.forEach((node) => {
const counterValue = counterMap.get(node) - 1;
const markerValue = markerCounter.get(node) - 1;
counterMap.set(node, counterValue);
markerCounter.set(node, markerValue);
if (!counterValue) {
if (!uncontrolledNodes.has(node)) {
node.removeAttribute(controlAttribute);
}
uncontrolledNodes.delete(node);
}
if (!markerValue) {
node.removeAttribute(markerName);
}
});
lockCount--;
if (!lockCount) {
counterMap = /* @__PURE__ */ new WeakMap();
counterMap = /* @__PURE__ */ new WeakMap();
uncontrolledNodes = /* @__PURE__ */ new WeakMap();
markerMap = {};
}
};
};
export {
walkTreeOutside
};