{"_id":"@blockquote/frontend-utilities","_rev":"4-3ad25c5652c6a54a0e4f6b936872dee4","name":"@blockquote/frontend-utilities","dist-tags":{"latest":"1.1.0"},"versions":{"1.0.0":{"name":"@blockquote/frontend-utilities","version":"1.0.0","keywords":["dom","events","dispatchEvent","treeWalker","utilities","front-end","snapshots"],"author":{"name":"Oscar Marina"},"license":"MIT","_id":"@blockquote/frontend-utilities@1.0.0","maintainers":[{"name":"oscarmarina","email":"oscar7600@outlook.com"}],"homepage":"https://github.com/oscarmarina/frontend-utilities","bugs":{"url":"https://github.com/oscarmarina/frontend-utilities/issues"},"dist":{"shasum":"119f52f57ff309c138b121da25f677a08ab7d52f","tarball":"https://registry.npmjs.org/@blockquote/frontend-utilities/-/frontend-utilities-1.0.0.tgz","fileCount":30,"integrity":"sha512-CdpaczCFgZyeu7ak6jmfa5j2SAg3xEoM3Qr08ltYUrNFBoLTSOtuD6FxlZY5ESD2HZ1HxYrMi0HTMhq5BNaPIA==","signatures":[{"sig":"MEQCIB9jGd8h/Wip9MlvCArpiStyXdBacsSyQAvKhgd/9/cRAiApx533cRBxA09s+RhRrkgERB3krMGTdUgdGiCQlbPxvw==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":36839},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"default":"./dist/index.js"},"./frontend-utilities.js":{"default":"./dist/index.js"},"./src/frontend-utilities.js":{"default":"./src/index.js"}},"gitHead":"5f694ebe7dc741c4467842d1d5ac596a82408914","scripts":{"test":"vitest","build":"vite build && tsc","format":"npm run format:eslint && npm run format:prettier","prepare":"husky","format:eslint":"eslint . --fix","format:prettier":"prettier \"**/*.{js,ts,json,html}\" --write --ignore-path .prettierignore"},"_npmUser":{"name":"oscarmarina","email":"oscar7600@outlook.com"},"prettier":{"overrides":[{"files":"*.{scss,css}","options":{"printWidth":280,"singleQuote":false}}],"printWidth":100,"singleQuote":true,"trailingComma":"es5","bracketSpacing":false,"bracketSameLine":true,"htmlWhitespaceSensitivity":"ignore"},"repository":{"url":"git+https://github.com/oscarmarina/frontend-utilities.git","type":"git"},"_npmVersion":"10.9.0","description":"Utilities for managing the DOM, handling events, and performing various common tasks in frontend development.","directories":{},"lint-staged":{"**/*.{js,ts,html}":["npm run format:eslint"],"**/*.{js,ts,json,html}":["npm run format:prettier"]},"sideEffects":false,"_nodeVersion":"22.11.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"lit":"^3.2.1","vite":"^5.4.11","husky":"^9.1.7","eslint":"^9.15.0","vitest":"^2.1.5","globals":"^15.12.0","prettier":"^3.3.3","@vitest/ui":"^2.1.5","playwright":"^1.49.0","lint-staged":"^15.2.10","@types/mocha":"^10.0.10","ts-lit-plugin":"^2.0.2","@vitest/browser":"^2.1.5","@open-wc/testing":"^4.0.0","eslint-plugin-wc":"^2.2.0","eslint-plugin-lit":"^1.15.0","typescript-eslint":"^8.15.0","@html-eslint/parser":"^0.27.0","@vitest/coverage-v8":"^2.1.5","eslint-plugin-import":"^2.31.0","eslint-config-prettier":"^9.1.0","eslint-plugin-lit-a11y":"^4.1.4","@typescript-eslint/parser":"^8.15.0","@vitest/coverage-istanbul":"^2.1.5","@html-eslint/eslint-plugin":"^0.27.0","@blockquote/rollup-plugin-externalize-source-dependencies":"^1.0.0"},"_npmOperationalInternal":{"tmp":"tmp/frontend-utilities_1.0.0_1732440433096_0.8849986753745458","host":"s3://npm-registry-packages"}},"1.0.1":{"name":"@blockquote/frontend-utilities","version":"1.0.1","keywords":["dom","events","dispatchEvent","treeWalker","utilities","front-end","snapshots"],"author":{"name":"Oscar Marina"},"license":"MIT","_id":"@blockquote/frontend-utilities@1.0.1","maintainers":[{"name":"oscarmarina","email":"oscar7600@outlook.com"}],"homepage":"https://github.com/oscarmarina/frontend-utilities","bugs":{"url":"https://github.com/oscarmarina/frontend-utilities/issues"},"dist":{"shasum":"b4c060b6500ec1d519e8f820c6090fe4d9fa7687","tarball":"https://registry.npmjs.org/@blockquote/frontend-utilities/-/frontend-utilities-1.0.1.tgz","fileCount":30,"integrity":"sha512-bVgTqSkGPtPE3esMICNzbeJlz3wVGpFPzcU9xN5L2yWfa7m6pa4OBoGVNh8jtCeGhW7Z8gramF0SKM06VptuJg==","signatures":[{"sig":"MEQCICrkAKFVokVpBBN5mloWkknoZT+tg73Gx/LiR1yX1/EMAiBFQXWCJ6Px7Cv869mhBAnOeUHBBxgwTJ79vRGty1jygg==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":36839},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"default":"./dist/index.js"},"./frontend-utilities.js":{"default":"./dist/index.js"},"./src/frontend-utilities.js":{"default":"./src/index.js"}},"gitHead":"df814e2754cf8693c47d7a24eb8ff56733397858","scripts":{"test":"vitest","build":"vite build && tsc","format":"npm run format:eslint && npm run format:prettier","prepare":"husky","format:eslint":"eslint . --fix","format:prettier":"prettier \"**/*.{js,ts,json,html}\" --write --ignore-path .prettierignore"},"_npmUser":{"name":"oscarmarina","email":"oscar7600@outlook.com"},"prettier":{"overrides":[{"files":"*.{scss,css}","options":{"printWidth":280,"singleQuote":false}}],"printWidth":100,"singleQuote":true,"trailingComma":"es5","bracketSpacing":false,"bracketSameLine":true,"htmlWhitespaceSensitivity":"ignore"},"repository":{"url":"git+https://github.com/oscarmarina/frontend-utilities.git","type":"git"},"_npmVersion":"10.9.0","description":"Utilities for managing the DOM, handling events, and performing various common tasks in frontend development.","directories":{},"lint-staged":{"**/*.{js,ts,html}":["npm run format:eslint"],"**/*.{js,ts,json,html}":["npm run format:prettier"]},"sideEffects":false,"_nodeVersion":"22.11.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"lit":"^3.2.1","vite":"^5.4.11","husky":"^9.1.7","eslint":"^9.15.0","vitest":"^2.1.5","globals":"^15.12.0","prettier":"^3.3.3","@vitest/ui":"^2.1.5","playwright":"^1.49.0","lint-staged":"^15.2.10","@types/mocha":"^10.0.10","ts-lit-plugin":"^2.0.2","@vitest/browser":"^2.1.5","@open-wc/testing":"^4.0.0","eslint-plugin-wc":"^2.2.0","eslint-plugin-lit":"^1.15.0","typescript-eslint":"^8.15.0","@html-eslint/parser":"^0.27.0","@vitest/coverage-v8":"^2.1.5","eslint-plugin-import":"^2.31.0","eslint-config-prettier":"^9.1.0","eslint-plugin-lit-a11y":"^4.1.4","@typescript-eslint/parser":"^8.15.0","@vitest/coverage-istanbul":"^2.1.5","@html-eslint/eslint-plugin":"^0.27.0","@blockquote/rollup-plugin-externalize-source-dependencies":"^1.0.0"},"_npmOperationalInternal":{"tmp":"tmp/frontend-utilities_1.0.1_1732461562760_0.6445534606515864","host":"s3://npm-registry-packages"}},"1.0.2":{"name":"@blockquote/frontend-utilities","version":"1.0.2","keywords":["dom","events","dispatchEvent","treeWalker","utilities","front-end","snapshots"],"author":{"name":"Oscar Marina"},"license":"MIT","_id":"@blockquote/frontend-utilities@1.0.2","maintainers":[{"name":"oscarmarina","email":"oscar7600@outlook.com"}],"homepage":"https://github.com/oscarmarina/frontend-utilities","bugs":{"url":"https://github.com/oscarmarina/frontend-utilities/issues"},"dist":{"shasum":"6f29a6fc49fa4fcf5387d75ea4ebd331df1ec46c","tarball":"https://registry.npmjs.org/@blockquote/frontend-utilities/-/frontend-utilities-1.0.2.tgz","fileCount":30,"integrity":"sha512-L/JUHyfSWbD10wVMfaZvoa9kapO2Ks42NdCuIcSyAZXZ/RztgF0kItB/wL7a+S+HaJ0FPDHC3JZUUj4tVXBQ/Q==","signatures":[{"sig":"MEUCIQC+S4SyJDVldA0jofNvsWwQOtRxAyTm4m71V0QoUWLPMgIgDXfkrUsJqF2apyFZwvjJue3oYbhzTyg7h1HgKcIvGPw=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":36839},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"default":"./dist/index.js"},"./frontend-utilities.js":{"default":"./dist/index.js"},"./src/frontend-utilities.js":{"default":"./src/index.js"}},"gitHead":"33779e0ac60ada64bf3bc8443e6f684a60f2db1f","scripts":{"test":"vitest","build":"vite build && tsc","format":"npm run format:eslint && npm run format:prettier","prepare":"husky","format:eslint":"eslint . --fix","format:prettier":"prettier \"**/*.{js,ts,json,html}\" --write --ignore-path .prettierignore"},"_npmUser":{"name":"oscarmarina","email":"oscar7600@outlook.com"},"prettier":{"overrides":[{"files":"*.{scss,css}","options":{"printWidth":280,"singleQuote":false}}],"printWidth":100,"singleQuote":true,"trailingComma":"es5","bracketSpacing":false,"bracketSameLine":true,"htmlWhitespaceSensitivity":"ignore"},"repository":{"url":"git+https://github.com/oscarmarina/frontend-utilities.git","type":"git"},"_npmVersion":"10.9.0","description":"Utilities for managing the DOM, handling events, and performing various common tasks in frontend development.","directories":{},"lint-staged":{"**/*.{js,ts,html}":["npm run format:eslint"],"**/*.{js,ts,json,html}":["npm run format:prettier"]},"sideEffects":false,"_nodeVersion":"22.11.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"lit":"^3.2.1","vite":"^5.4.11","husky":"^9.1.7","eslint":"^9.15.0","vitest":"^2.1.5","globals":"^15.12.0","prettier":"^3.3.3","@vitest/ui":"^2.1.5","playwright":"^1.49.0","lint-staged":"^15.2.10","@types/mocha":"^10.0.10","ts-lit-plugin":"^2.0.2","@vitest/browser":"^2.1.5","@open-wc/testing":"^4.0.0","eslint-plugin-wc":"^2.2.0","eslint-plugin-lit":"^1.15.0","typescript-eslint":"^8.15.0","@html-eslint/parser":"^0.27.0","@vitest/coverage-v8":"^2.1.5","eslint-plugin-import":"^2.31.0","eslint-config-prettier":"^9.1.0","eslint-plugin-lit-a11y":"^4.1.4","@typescript-eslint/parser":"^8.15.0","@vitest/coverage-istanbul":"^2.1.5","@html-eslint/eslint-plugin":"^0.27.0","@blockquote/rollup-plugin-externalize-source-dependencies":"^1.0.0"},"_npmOperationalInternal":{"tmp":"tmp/frontend-utilities_1.0.2_1732461886103_0.35757595248490004","host":"s3://npm-registry-packages"}},"1.1.0":{"name":"@blockquote/frontend-utilities","version":"1.1.0","description":"Utilities for managing the DOM, handling events, and performing various common tasks in frontend development.","keywords":["dom","events","dispatchEvent","treeWalker","utilities","front-end","snapshots"],"homepage":"https://github.com/oscarmarina/frontend-utilities","repository":{"type":"git","url":"git+https://github.com/oscarmarina/frontend-utilities.git"},"license":"MIT","author":{"name":"Oscar Marina"},"sideEffects":false,"type":"module","exports":{".":{"default":"./dist/index.js"},"./frontend-utilities.js":{"default":"./dist/index.js"},"./src/frontend-utilities.js":{"default":"./src/index.js"}},"main":"./dist/index.js","module":"./dist/index.js","scripts":{"build":"vite build && tsc","format:eslint":"eslint . --fix","format:prettier":"prettier \"**/*.{js,ts,json,html}\" --write --ignore-path .prettierignore","format":"npm run format:eslint && npm run format:prettier","prepare":"husky","test":"vitest"},"lint-staged":{"**/*.{js,ts,html}":["npm run format:eslint"],"**/*.{js,ts,json,html}":["npm run format:prettier"]},"prettier":{"bracketSameLine":true,"bracketSpacing":false,"htmlWhitespaceSensitivity":"ignore","printWidth":100,"singleQuote":true,"trailingComma":"es5","overrides":[{"files":"*.{scss,css}","options":{"printWidth":280,"singleQuote":false}}]},"devDependencies":{"@blockquote/rollup-plugin-externalize-source-dependencies":"^1.0.0","@html-eslint/eslint-plugin":"^0.40.0","@html-eslint/parser":"^0.40.0","@open-wc/testing":"^4.0.0","@types/mocha":"^10.0.10","@typescript-eslint/parser":"^8.31.1","@vitest/browser":"^3.1.2","@vitest/coverage-istanbul":"^3.1.2","@vitest/coverage-v8":"^3.1.2","@vitest/ui":"^3.1.2","eslint":"^9.26.0","eslint-config-prettier":"^10.1.2","eslint-plugin-import":"^2.31.0","eslint-plugin-lit":"^2.1.1","eslint-plugin-lit-a11y":"^4.1.4","eslint-plugin-wc":"^3.0.1","globals":"^16.0.0","husky":"^9.1.7","lint-staged":"^15.5.1","lit":"^3.3.0","playwright":"^1.52.0","prettier":"^3.5.3","ts-lit-plugin":"^2.0.2","typescript-eslint":"^8.31.1","vite":"^6.3.4","vitest":"^3.1.2"},"publishConfig":{"access":"public"},"_id":"@blockquote/frontend-utilities@1.1.0","gitHead":"f2d563e1a2eb6388977509d4362f6b3d2eb8386f","types":"./dist/index.d.ts","bugs":{"url":"https://github.com/oscarmarina/frontend-utilities/issues"},"_nodeVersion":"22.15.0","_npmVersion":"10.9.2","dist":{"integrity":"sha512-2MH5/33rO2An9V81NW63J6vA08dT1woVDTOOcL2CFgmNdduHjJbeeYiPTKWwmjcc8E0ECmIpNP0bEPROd9cT+Q==","shasum":"63ebbc47ff9e8cd8c89c97a0749c60e8de02bab3","tarball":"https://registry.npmjs.org/@blockquote/frontend-utilities/-/frontend-utilities-1.1.0.tgz","fileCount":32,"unpackedSize":41356,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQDzPIH+yjxOV15S21tR0ZFwmc38O19kZycrKFk2dFFMqwIgdrIIXZg7IiyCVeYNsFGTGf6WPm28DXO0O++UZ3FrAD8="}]},"_npmUser":{"name":"oscarmarina","email":"oscar7600@outlook.com"},"directories":{},"maintainers":[{"name":"oscarmarina","email":"oscar7600@outlook.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/frontend-utilities_1.1.0_1746384750358_0.5333731173670977"},"_hasShrinkwrap":false}},"time":{"created":"2024-11-24T09:27:12.967Z","modified":"2025-05-04T18:52:30.729Z","1.0.0":"2024-11-24T09:27:13.279Z","1.0.1":"2024-11-24T15:19:22.931Z","1.0.2":"2024-11-24T15:24:46.305Z","1.1.0":"2025-05-04T18:52:30.550Z"},"bugs":{"url":"https://github.com/oscarmarina/frontend-utilities/issues"},"author":{"name":"Oscar Marina"},"license":"MIT","homepage":"https://github.com/oscarmarina/frontend-utilities","keywords":["dom","events","dispatchEvent","treeWalker","utilities","front-end","snapshots"],"repository":{"type":"git","url":"git+https://github.com/oscarmarina/frontend-utilities.git"},"description":"Utilities for managing the DOM, handling events, and performing various common tasks in frontend development.","maintainers":[{"name":"oscarmarina","email":"oscar7600@outlook.com"}],"readme":"# Utilities for managing the DOM, handling events, and performing various common tasks in frontend development.\n\nThis repository provides a set of utility functions designed to facilitate DOM manipulation, event handling, and other common tasks in frontend development. These functions address various needs such as re-dispatching events, checking element visibility, and traversing the DOM tree.\n\n## Table of Contents\n\n- [_Re-dispatch Event Functions_](#re-dispatch-event-functions)\n  - [redispatchEvent](#redispatchevent)\n- [_Element Visibility and Focus Functions_](#element-visibility-and-focus-functions)\n  - [isElementInvisible](#iselementinvisible)\n  - [isFocusable](#isfocusable)\n- [_DOM Traversal Functions_](#dom-traversal-functions)\n  - [walkComposedTree](#walkcomposedtree)\n    - [getFirstAndLastItems](#getfirstandlastitems)\n  - [getDeepActiveElement](#getdeepactiveelement)\n  - [deepContains](#deepcontains)\n  - [composedAncestors](#composedancestors)\n- [_Event Handling Functions_](#event-handling-functions)\n  - [isClickInsideRect](#isclickinsiderect)\n- [_Snapshots Functions_](#snapshots-functions)\n  - [htmlStructureSnapshot](#htmlstructuresnapshot)\n- [_Miscellaneous Functions_](#miscellaneous-functions)\n  - [randomID](#randomid)\n  - [urlToPlainObject](#urltoplainobject)\n\n## Re-dispatch Event Functions\n\n### `redispatchEvent`\n\n```js\n/**\n * Re-dispatches an event from the provided element.\n *\n * This function is useful for forwarding non-composed events, such as `change`\n * events.\n *\n * @example\n * class MyInput extends LitElement {\n *   render() {\n *     return html`<input @change=${this.redispatchEvent}>`;\n *   }\n *\n *   protected redispatchEvent(event: Event) {\n *     redispatchEvent(this, event);\n *   }\n * }\n *\n * @example\n * class MyDialog extends LitElement {\n *   render() {\n *     return html`<dialog @close=${this.redispatchEvent}>...</dialog>`;\n *   }\n *\n *   protected redispatchEvent(ev: Event) {\n *     redispatchEvent(this, ev, { cancelable: true });\n *   }\n * }\n *\n * @param {Element} element - The element to dispatch the event from.\n * @param {Event|string} ev - The event to re-dispatch. If it's a string, a new Event is created.\n * @param {Object} [options={}] - An object with properties to override in the new event.\n * @returns {boolean} - Whether or not the event was dispatched (if cancelable).\n */\nexport const redispatchEvent = (element, ev, options) => {\n  if (typeof ev === 'string') {\n    const eventType = ev;\n    const newEvent = new CustomEvent(eventType);\n    return redispatchEventFromEvent(element, newEvent, options);\n  }\n  return redispatchEventFromEvent(element, ev, options);\n};\n```\n\n## Element Visibility and Focus Functions\n\n### `isElementInvisible`\n\n```js\n/**\n * Determines if an element should be ignored by a screen reader.\n * @param {Element} element - The DOM element to check.\n * @param {Array} [exceptions=['dialog', '[popover]']] - Array of Elements to ignore when checking the element.\n * @returns {boolean} True if the element should be ignored by a screen reader, false otherwise.\n */\nexport const isElementInvisible = (element, exceptions = ['dialog', '[popover]']) => {\n  if (!element || !(element instanceof HTMLElement)) {\n    return false;\n  }\n\n  if (element.matches(exceptions.join(','))) {\n    return false;\n  }\n\n  const {display, visibility, opacity} = window.getComputedStyle(element);\n  const isStyleHidden =\n    display === 'none' || visibility === 'hidden' || visibility === 'collapse' || opacity === '0';\n  const isAttributeHidden = element.matches('[disabled], [hidden], [inert], [aria-hidden=\"true\"]');\n\n  return isStyleHidden || isAttributeHidden;\n};\n```\n\n### `isFocusable`\n\n```js\n/**\n * Checks if an element is focusable. An element is considered focusable if it matches\n * standard focusable elements criteria (such as buttons, inputs, etc., that are not disabled\n * and do not have a negative tabindex) or is a custom element with a shadow root that delegates focus.\n *\n * @param {Element} element - The DOM element to check for focusability.\n * @returns {boolean} True if the element is focusable, false otherwise.\n */\nexport const isFocusable = (element) => {\n  if (!(element instanceof HTMLElement)) {\n    return false;\n  }\n\n  // https://stackoverflow.com/a/30753870/76472\n  const knownFocusableElements = `a[href],area[href],button:not([disabled]),details,iframe,object,input:not([disabled]),select:not([disabled]),textarea:not([disabled]),[contentEditable=\"true\"],[tabindex]:not([tabindex^=\"-\"]),audio[controls],video[controls]`;\n\n  if (element.matches(knownFocusableElements)) {\n    return true;\n  }\n\n  const isDisabledCustomElement =\n    element.localName.includes('-') && element.matches('[disabled], [aria-disabled=\"true\"]');\n  if (isDisabledCustomElement) {\n    return false;\n  }\n  return /** @type {ShadowRoot | *} */ (element.shadowRoot)?.delegatesFocus ?? false;\n};\n```\n\n## DOM Traversal Functions\n\n### `walkComposedTree`\n\n```js\n/**\n * Traverse the composed tree from the root, selecting elements that meet the provided filter criteria.\n * You can pass [NodeFilter](https://developer.mozilla.org/en-US/docs/Web/API/NodeFilter) or 0 to retrieve all nodes.\n *\n * @param {Object} options - The options object.\n * @param {Node} options.root - The root node for traversal. **Required**.\n * @param {number} [options.whatToShow=0] - NodeFilter code for node types to include. Defaults to `0`, which selects all node types.\n * @param {function} [options.filterAccept=(node: Node) => true] - Filters nodes. Child nodes are considered even if the parent does not satisfy the filter. Defaults to a function that always returns `true`.\n * @param {function} [options.filterReject=(node: Node) => false] - Filters nodes to reject. Any node in the subtree based at this node is not included. Defaults to a function that always returns `false`.\n * @returns {IterableIterator<Node>} An iterator yielding nodes meeting the filter criteria.\n */\nexport function* walkComposedTree({\n  root,\n  whatToShow = 0,\n  filterAccept = () => true,\n  filterReject = () => false,\n}) {\n  if ((whatToShow && root.nodeType !== whatToShow) || filterReject(root)) {\n    return;\n  }\n\n  if (filterAccept(root)) {\n    yield root;\n  }\n\n  const children =\n    root instanceof HTMLElement && root.shadowRoot\n      ? root.shadowRoot.children\n      : root instanceof HTMLSlotElement\n        ? root.assignedNodes({flatten: true})\n        : root.childNodes;\n\n  for (const child of children) {\n    yield* walkComposedTree({root: child, whatToShow, filterAccept, filterReject});\n  }\n}\n```\n\n### `getFirstAndLastItems`\n\n```js\n/**\n * Retrieves the first and last children of a node using a TreeWalker.\n *\n * @param {IterableIterator<HTMLElement>} walker - The TreeWalker object used to traverse the node's children.\n * @returns {[first: HTMLElement|null, last: HTMLElement|null]} An array containing the first and last children. If no children are found, `null` is returned for both.\n */\nexport const getFirstAndLastItems = (walker) => {\n  let firstFocusableChild = null;\n  let lastFocusableChild = null;\n\n  for (const currentNode of walker) {\n    if (!firstFocusableChild) {\n      firstFocusableChild = currentNode;\n    }\n    lastFocusableChild = currentNode;\n  }\n\n  return [firstFocusableChild, lastFocusableChild];\n};\n```\n\n### `getDeepActiveElement`\n\n```js\n/**\n * Returns the deepest active element, considering Shadow DOM subtrees\n * @param {Document | ShadowRoot} root - The root element to start the search from.\n * @returns {Element} The deepest active element or body element if no active element is found.\n */\nexport const getDeepActiveElement = (root = document) => {\n  const activeEl = root?.activeElement;\n  if (activeEl) {\n    if (activeEl.shadowRoot) {\n      return getDeepActiveElement(activeEl.shadowRoot) ?? activeEl;\n    }\n    return activeEl;\n  }\n  return document.body;\n};\n```\n\n### `deepContains`\n\n```js\n/**\n * Returns true if the first node contains the second, even if the second node\n * is in a shadow tree.\n *\n * The standard Node.contains() function does not account for Shadow DOM, and\n * returns false if the supplied target node is sitting inside a shadow tree\n * within the container.\n *\n * @param {Node} container - The container to search within.\n * @param {Node} target - The node that may be inside the container.\n * @returns {boolean} - True if the container contains the target node.\n */\nexport const deepContains = (container, target) => {\n  /** @type {any} */\n  let current = target;\n  while (current) {\n    const parent = current.assignedSlot || current.parentNode || current.host;\n    if (parent === container) {\n      return true;\n    }\n    current = parent;\n  }\n  return false;\n};\n```\n\n### `composedAncestors`\n\n```js\n/**\n * Return the ancestors of the given node in the composed tree.\n *\n * In the composed tree, the ancestor of a node assigned to a slot is that slot,\n * not the node's DOM ancestor. The ancestor of a shadow root is its host.\n *\n * @author Jan Miksovsky\n * @see https://github.com/JanMiksovsky/elix/blob/main/src/core/dom.js\n *\n * @param {Node} node\n * @returns {Iterable<Node>}\n */\nexport function* composedAncestors(node) {\n  for (let /** @type {Node|null} */ current = node; current; ) {\n    const next =\n      current instanceof HTMLElement && current.assignedSlot\n        ? current.assignedSlot\n        : current instanceof ShadowRoot\n          ? current.host\n          : current.parentNode;\n    if (next) {\n      yield next;\n    }\n    current = next;\n  }\n}\n```\n\n## Event Handling Functions\n\n### `isClickInsideRect`\n\n```js\n/**\n * Checks if a click event occurred inside a given bounding rectangle.\n *\n * @param {DOMRect} rect - The bounding rectangle, typically obtained from `element.getBoundingClientRect()`.\n * @param {PointerEvent} ev - The click event.\n * @returns {boolean} True if the click occurred inside the rectangle, false otherwise.\n */\nexport const isClickInsideRect = (rect, ev) => {\n  const {top, left, height, width} = rect;\n  const {clientX, clientY} = ev;\n  return clientY >= top && clientY <= top + height && clientX >= left && clientX <= left + width;\n};\n```\n\n## Snapshots Functions\n\n### `htmlStructureSnapshot`\n\n```js\n/**\n * Returns the outerHTML or innerHTML of a node after removing specified attributes from it and its children.\n *\n * If the initial node is an HTMLElement, the function returns the outerHTML of the node.\n * If the initial node is a ShadowRoot, the function returns the innerHTML of the node.\n *\n * @param {Node} node - The initial node.\n * @param {string[]} [ignoreAttributes=[]] - An optional array of strings representing attributes to ignore.\n * @returns {string} The outerHTML or innerHTML of the node after removing the specified attributes.\n */\nexport const htmlStructureSnapshot = (node, ignoreAttributes = []) => {\n  const isHTMLElement = node instanceof HTMLElement;\n  const isShadowRoot = node instanceof ShadowRoot;\n\n  if (isHTMLElement || isShadowRoot) {\n    if (ignoreAttributes.length > 0) {\n      removeAttributes(node, ignoreAttributes);\n    }\n\n    const htmlContent = isShadowRoot ? node.innerHTML : node.outerHTML;\n    return removeCommentsAndFormat(htmlContent);\n  }\n  return '';\n};\n```\n\n## Miscellaneous\n\n### `randomID`\n\n```js\n/**\n * Generates a random alphanumeric string of a specified length.\n *\n * @param {number} [length=10] - The length of the random string to generate. Default is 10.\n * @returns {string} A random alphanumeric string of the specified length.\n */\nexport const randomID = (length = 10) => Math.random().toString(36).substring(2, length);\n```\n\n### `urlToPlainObject`\n\n```js\n/**\n * Converts a URL object to a plain object.\n *\n * @author Cory LaViska\n * @see https://www.abeautifulsite.net/posts/converting-a-url-object-to-a-plain-object-in-java-script\n *\n * @param {URL|string} url - The URL object to parse.\n * @returns {Object} An object representing the parsed URL.\n */\nexport const urlToPlainObject = (url) => {\n  const urlObject = typeof url === 'string' ? new URL(url) : url;\n  const plainObject = {};\n\n  for (const key in urlObject) {\n    if (typeof urlObject[key] === 'string') {\n      plainObject[key] = urlObject[key];\n    }\n  }\n  return plainObject;\n};\n```\n\n### `LRUMap`\n\n```js\n/**\n * A limited-capacity map with a least-recently-used (LRU) eviction policy.\n * Includes the operations `get`, `set`, and `has`, which behave the same as on a `Map`.\n *\n * @param {number} [capacity=10] - The maximum number of entries allowed in the map.\n */\nconst map = new LRUMap(2);\nmap.set('a', 1);\nmap.set('b', 2);\nmap.set('c', 3);\n```\n\n---\n\n## Author Information\n\n- Original Authors: _@material/web, Jan Miksovsky, Cory LaViska_\n\n---\n\n**Scaffold generated using**:\n\n> [npm init @blockquote/wc](https://github.com/oscarmarina/create-wc)\n","readmeFilename":"README.md"}