UNPKG

hooktml

Version:

A reactive HTML component library with hooks-based lifecycle management

77 lines (67 loc) 2.54 kB
import { kebabToCamel, pluralize, camelToKebab } from './strings.js' import { isArray, isHTMLElement } from './type-guards.js' import { getConfig } from '../core/config.js' /** * Checks if an element has the same component class as its ancestor * @param {Element | HTMLElement} element - The element to check * @param {string} componentName - The component name to check against * @returns {boolean} Whether the element has the same component class */ export const hasSameComponent = (element, componentName) => { const { formattedPrefix } = getConfig() return element.classList.contains(componentName) || (isHTMLElement(element) && ( element.getAttribute(`${formattedPrefix}use-component`) === componentName )) } /** * Adds a child to a pluralized key * @param {Record<string, Element | Element[]>} children - The children object * @param {string} key - The key to add the child to * @param {Element} child - The child to add */ export const addPluralizedChild = (children, key, child) => { const pluralKey = pluralize(key) if (pluralKey in children) { // If the pluralized key already exists, just push the new child if (isArray(children[pluralKey])) { children[pluralKey].push(child) } } else { // Create a new array with first child and new child children[pluralKey] = [/** @type {Element} */(children[key]), child] } } /** * Extracts children from an element's subtree based on component name * @param {Element} element - The root element * @param {string} componentName - The PascalCase component name * @returns {Record<string, Element | Element[]>} The extracted children */ export const extractChildren = (element, componentName) => { const { formattedPrefix } = getConfig() const prefix = `${formattedPrefix}${camelToKebab(componentName)}-` /** @type {Record<string, Element | Element[]>} */ const children = {} // Get all descendants const descendants = Array.from(element.getElementsByTagName('*')) // Use some to short-circuit when the component is found descendants.some((child) => { if (hasSameComponent(child, componentName)) { return true } // Check all attributes Array.from(child.attributes).forEach(({ name }) => { if (name.startsWith(prefix)) { const key = kebabToCamel(name.slice(prefix.length)) if (children[key]) { addPluralizedChild(children, key, child) } else { children[key] = child } } }) return false }) return children }