@navikt/ds-react
Version:
React components from the Norwegian Labour and Welfare Administration.
141 lines • 5.55 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.hideNonTargetElements = hideNonTargetElements;
/**
* Modified version of `aria-hidden`-package.
* - Removed "inert"-functionality.
* - Removed flexibility for different data-attributes.
* https://github.com/theKashey/aria-hidden/blob/720e8a8e1cfa047bd299a929d95d47ac860a5c1a/src/index.ts
*/
const owner_1 = require("./owner");
let ariaHiddenCounter = new WeakMap();
let markerCounter = new WeakMap();
let uncontrolledElementsSet = new WeakSet();
let lockCount = 0;
const controlAttribute = "aria-hidden";
const markerName = "data-aksel-hidden";
/**
* Unwraps a Shadow DOM host to find the actual Element in the light DOM.
*/
function unwrapHost(node) {
return (node &&
(node.host || unwrapHost(node.parentNode)));
}
/**
* Corrects the target elements by unwrapping Shadow DOM hosts if necessary.
*
* @param parent - The parent HTMLElement to check containment against.
* @param targets - An array of target Elements to correct.
* @returns An array of corrected Elements that are contained within the parent.
*/
function correctElements(parent, targets) {
return targets
.map((target) => {
if (parent.contains(target)) {
return target;
}
const correctedTarget = unwrapHost(target);
if (parent.contains(correctedTarget)) {
return correctedTarget;
}
return null;
})
.filter((x) => x !== null);
}
/**
* Applies the aria-hidden attribute to all elements in the body except the specified avoid elements.
*/
function applyAttributeToOthers(uncorrectedAvoidElements, body) {
const avoidElements = correctElements(body, uncorrectedAvoidElements);
const elementsToAvoidWithParents = new Set();
const elementsToAvoidUpdating = new Set(avoidElements);
const hiddenElements = [];
avoidElements.forEach(addToAvoidList);
applyAttributes(body);
elementsToAvoidWithParents.clear();
function addToAvoidList(el) {
if (!el || elementsToAvoidWithParents.has(el)) {
return;
}
elementsToAvoidWithParents.add(el);
if (el.parentNode) {
addToAvoidList(el.parentNode);
}
}
function applyAttributes(parent) {
if (!parent || elementsToAvoidUpdating.has(parent)) {
return;
}
const parentChildren = parent.children;
for (let index = 0; index < parentChildren.length; index += 1) {
const node = parentChildren[index];
if (elementsToAvoidWithParents.has(node)) {
applyAttributes(node);
}
else {
const attr = node.getAttribute(controlAttribute);
/*
* We only check for falsy values here since since arbitrary values
* (e.g. "true", "foo", "") are all valid for indicating that the element is already hidden.
*/
const alreadyHidden = attr !== null && attr !== "false";
const counterValue = (ariaHiddenCounter.get(node) || 0) + 1;
const markerValue = (markerCounter.get(node) || 0) + 1;
ariaHiddenCounter.set(node, counterValue);
markerCounter.set(node, markerValue);
hiddenElements.push(node);
if (counterValue === 1 && alreadyHidden) {
uncontrolledElementsSet.add(node);
}
if (markerValue === 1) {
node.setAttribute(markerName, "");
}
if (!alreadyHidden) {
node.setAttribute(controlAttribute, "true");
}
}
}
}
lockCount += 1;
/* Cleanup */
return () => {
for (const element of hiddenElements) {
const currentCounterValue = ariaHiddenCounter.get(element) || 0;
const counterValue = currentCounterValue - 1;
const markerValue = (markerCounter.get(element) || 0) - 1;
ariaHiddenCounter.set(element, counterValue);
markerCounter.set(element, markerValue);
if (!counterValue) {
if (!uncontrolledElementsSet.has(element)) {
element.removeAttribute(controlAttribute);
}
uncontrolledElementsSet.delete(element);
}
if (!markerValue) {
element.removeAttribute(markerName);
}
}
lockCount -= 1;
/* Reset */
if (!lockCount) {
ariaHiddenCounter = new WeakMap();
uncontrolledElementsSet = new WeakSet();
markerCounter = new WeakMap();
}
};
}
/**
* Hides all elements in the document body for assertive technologies except the specified elements with `aria-hidden`.
* @param avoidElements - An array of elements to avoid hiding.
* @returns A function that, when called, will undo the hiding of elements.
*/
function hideNonTargetElements(avoidElements) {
const body = (0, owner_1.ownerDocument)(avoidElements[0]).body;
/**
* Assume that elements with `aria-live` or `script` tags should not be hidden.
* This ensures that live regions and scripts continue to function properly.
*/
const ingoredElements = Array.from(body.querySelectorAll("[aria-live], script"));
return applyAttributeToOthers(avoidElements.concat(ingoredElements), body);
}
//# sourceMappingURL=hideNonTargetElements.js.map