@kobalte/core
Version:
Unstyled components and primitives for building accessible web apps and design systems with SolidJS.
126 lines (123 loc) • 3.9 kB
JavaScript
import { DATA_TOP_LAYER_ATTR } from './ZKYDDHM6.js';
import { DATA_LIVE_ANNOUNCER_ATTR } from './YA7DCYMB.js';
import { access } from '@kobalte/utils';
import { createEffect, onCleanup } from 'solid-js';
function createHideOutside(props) {
createEffect(() => {
if (access(props.isDisabled)) {
return;
}
onCleanup(ariaHideOutside(access(props.targets), access(props.root)));
});
}
var refCountMap = /* @__PURE__ */ new WeakMap();
var observerStack = [];
function ariaHideOutside(targets, root = document.body) {
const visibleNodes = new Set(targets);
const hiddenNodes = /* @__PURE__ */ new Set();
const walk = (root2) => {
for (const element of root2.querySelectorAll(
`[${DATA_LIVE_ANNOUNCER_ATTR}], [${DATA_TOP_LAYER_ATTR}]`
)) {
visibleNodes.add(element);
}
const acceptNode = (node) => {
if (visibleNodes.has(node) || node.parentElement && hiddenNodes.has(node.parentElement) && node.parentElement.getAttribute("role") !== "row") {
return NodeFilter.FILTER_REJECT;
}
for (const target of visibleNodes) {
if (node.contains(target)) {
return NodeFilter.FILTER_SKIP;
}
}
return NodeFilter.FILTER_ACCEPT;
};
const walker = document.createTreeWalker(root2, NodeFilter.SHOW_ELEMENT, {
acceptNode
});
const acceptRoot = acceptNode(root2);
if (acceptRoot === NodeFilter.FILTER_ACCEPT) {
hide(root2);
}
if (acceptRoot !== NodeFilter.FILTER_REJECT) {
let node = walker.nextNode();
while (node != null) {
hide(node);
node = walker.nextNode();
}
}
};
const hide = (node) => {
const refCount = refCountMap.get(node) ?? 0;
if (node.getAttribute("aria-hidden") === "true" && refCount === 0) {
return;
}
if (refCount === 0) {
node.setAttribute("aria-hidden", "true");
}
hiddenNodes.add(node);
refCountMap.set(node, refCount + 1);
};
if (observerStack.length) {
observerStack[observerStack.length - 1].disconnect();
}
walk(root);
const observer = new MutationObserver((changes) => {
for (const change of changes) {
if (change.type !== "childList" || change.addedNodes.length === 0) {
continue;
}
if (![...visibleNodes, ...hiddenNodes].some(
(node) => node.contains(change.target)
)) {
for (const node of change.removedNodes) {
if (node instanceof Element) {
visibleNodes.delete(node);
hiddenNodes.delete(node);
}
}
for (const node of change.addedNodes) {
if ((node instanceof HTMLElement || node instanceof SVGElement) && (node.dataset.liveAnnouncer === "true" || node.dataset.reactAriaTopLayer === "true")) {
visibleNodes.add(node);
} else if (node instanceof Element) {
walk(node);
}
}
}
}
});
observer.observe(root, { childList: true, subtree: true });
const observerWrapper = {
observe() {
observer.observe(root, { childList: true, subtree: true });
},
disconnect() {
observer.disconnect();
}
};
observerStack.push(observerWrapper);
return () => {
observer.disconnect();
for (const node of hiddenNodes) {
const count = refCountMap.get(node);
if (count == null) {
return;
}
if (count === 1) {
node.removeAttribute("aria-hidden");
refCountMap.delete(node);
} else {
refCountMap.set(node, 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);
}
};
}
export { ariaHideOutside, createHideOutside };