UNPKG

@medyll/idae-be

Version:

A modern, lightweight, and extensible DOM manipulation library built with TypeScript. Designed for precise element targeting and manipulation using a callback-based approach. Features include advanced DOM traversal, event handling, style management, attri

335 lines (334 loc) 11.6 kB
import { Be } from '../be.js'; /** * Enum representing the available walker methods. */ export var walkerMethods; (function (walkerMethods) { walkerMethods["up"] = "up"; walkerMethods["next"] = "next"; walkerMethods["previous"] = "previous"; walkerMethods["siblings"] = "siblings"; walkerMethods["children"] = "children"; walkerMethods["closest"] = "closest"; walkerMethods["lastChild"] = "lastChild"; walkerMethods["firstChild"] = "firstChild"; walkerMethods["find"] = "find"; walkerMethods["findAll"] = "findAll"; walkerMethods["without"] = "without"; })(walkerMethods || (walkerMethods = {})); /** * Handles DOM traversal operations for Be elements. */ export class WalkHandler { static methods = Object.values(walkerMethods); beElement; /** * Creates an instance of WalkHandler. * @param beElement - The Be element to operate on. */ constructor(beElement) { this.beElement = beElement; } methods = WalkHandler.methods; valueOf() { return this.beElement; } /** * Handles multiple walk operations. * @param actions - The actions to perform. * @returns The Be instance for method chaining. */ handle(actions) { Object.entries(actions).forEach(([method, props]) => { if (method in this) { this[method](props); } }); return this.beElement; } /** * Traverses up the DOM tree. * @param qy - Optional selector or callback function. * @param callback - Optional callback function. * @returns The Be instance for method chaining. * @example * // HTML: <div id="child"></div><div id="parent"><div id="child"></div></div> * const beInstance = be('#child'); * beInstance.up(); // Traverses to the parent element */ up(qy, callback) { if (typeof qy === 'function') { callback = qy; qy = undefined; } return this.methodize('up')(qy, callback); } /** * Traverses to the next sibling element. * @param qy - Optional selector or callback function. * @param callback - Optional callback function. * @returns The Be instance for method chaining. * @example * // HTML: <div id="sibling1"></div><div id="sibling2"></div> * const beInstance = be('#sibling1'); * beInstance.next(); // Traverses to the next sibling */ next(qy, callback) { if (typeof qy === 'function') { callback = qy; qy = undefined; } return this.methodize('next')(qy, callback); } /** * Traverses to the previous sibling element. * @param qy - Optional selector or callback function. * @param callback - Optional callback function. * @returns The Be instance for method chaining. * @example * // HTML: <div id="sibling1"></div><div id="sibling2"></div> * const beInstance = be('#sibling2'); * beInstance.previous(); // Traverses to the previous sibling */ previous(qy, callback) { if (typeof qy === 'function') { callback = qy; qy = undefined; } return this.methodize('previous')(qy, callback); } /** * Filters out elements that match the given selector. * @param qy - The selector to match elements against for removal. * @param callback - Optional callback function. * @returns The Be instance for method chaining. */ without(qy, callback) { const ret = []; this.beElement.eachNode((el) => { if (!el.matches(qy)) { ret.push(el); } }); const resultBe = Be.elem(ret); callback?.({ root: this.beElement, be: resultBe, fragment: 'result', requested: resultBe }); return this.beElement; } /** * Gets all sibling elements. * @param qy - Optional selector or callback function. * @param callback - Optional callback function. * @returns The Be instance for method chaining. * @example * // HTML: <div id="sibling1"></div><div id="sibling2"></div> * const beInstance = be('#sibling1'); * beInstance.siblings(); // Finds all sibling elements */ siblings(qy, callback) { if (typeof qy === 'function') { callback = qy; qy = undefined; } const ret = []; this.beElement.eachNode((el) => { if (el.parentNode) { const siblings = Array.from(el.parentNode.children).filter((child) => child !== el); ret.push(...siblings.filter((sibling) => !qy || sibling.matches(qy))); } }); callback?.({ root: this.beElement, be: Be.elem(ret), fragment: 'result', requested: Be.elem(ret) }); return this.beElement; } /** * Gets all child elements. * @param qy - Optional selector or callback function. * @param callback - Optional callback function. * @returns The Be instance for method chaining. * @example * // HTML: <div id="parent"><div id="child"></div></div> * const beInstance = be('#parent'); * beInstance.children(); // Finds all child elements */ children(qy, callback) { if (typeof qy === 'function') { callback = qy; qy = undefined; } return this.methodize('children')(qy, callback); } /** * Finds the closest ancestor that matches the selector. * @param qy - Optional selector or callback function. * @param callback - Optional callback function. * @returns The Be instance for method chaining. * @example * // HTML: <div id="ancestor"><div id="parent"><div id="child"></div></div></div> * const beInstance = be('#child'); * beInstance.closest('#ancestor'); // Finds the closest ancestor matching the selector */ closest(qy, callback) { if (typeof qy === 'function') { callback = qy; qy = undefined; } return this.methodize('closest')(qy, callback); } /** * Gets the last child element. * @param qy - Optional selector or callback function. * @param callback - Optional callback function. * @returns The Be instance for method chaining. */ lastChild(qy, callback) { if (typeof qy === 'function') { callback = qy; qy = undefined; } return this.methodize('lastChild')(qy, callback); } /** * Gets the first child element. * @param qy - Optional selector or callback function. * @param callback - Optional callback function. * @returns The Be instance for method chaining. */ firstChild(qy, callback) { if (typeof qy === 'function') { callback = qy; qy = undefined; } return this.methodize('firstChild')(qy, callback); } /** * Finds the first descendant that matches the selector. * @param qy - The selector to match against. * @param callback - Optional callback function. * @returns The Be instance for method chaining. */ find(qy, callback) { const ret = []; this.beElement.eachNode((el) => { const found = el.querySelector(qy); if (found) ret.push(found); }); const resultBe = Be.elem(ret); // Encapsule les résultats dans une instance de Be callback?.({ root: this.beElement, be: resultBe, fragment: 'result', requested: resultBe }); return resultBe; } /** * Finds all descendants that match the selector. * @param qy - The selector to match against. * @param callback - Optional callback function. * @returns The Be instance for method chaining. */ findAll(qy, callback) { const ret = []; this.beElement.eachNode((el) => { ret.push(...Array.from(el.querySelectorAll(qy))); }); callback?.({ root: this.beElement, be: Be.elem(ret), fragment: 'result', requested: Be.elem(ret) }); return this.beElement; } /** * Helper method to create a function for each walk method. * @param method - The walk method to create a function for. * @returns A function that performs the specified walk method. */ methodize(method) { return (qy, callback) => { try { const ret = []; this.beElement.eachNode((el) => { const result = this.selectWhile(el, method, qy); if (result) ret.push(...(Array.isArray(result) ? result : [result])); }); const resultBe = Be.elem(ret); callback?.({ root: this.beElement, be: resultBe, fragment: 'result', requested: resultBe }); return resultBe; } catch (e) { console.error(`Error in methodize for ${method}:`, e); } return this.beElement; }; } /** * Helper method to select elements based on the specified method and selector. * @param element - The starting element. * @param direction - The direction to traverse. * @param selector - Optional selector to filter elements. * @returns The selected HTMLElement or null if not found. */ selectWhile(element, direction, selector) { const dict = { up: 'parentElement', next: 'nextElementSibling', previous: 'previousElementSibling', siblings: 'parentElement', children: 'children', firstChild: 'firstElementChild', lastChild: 'lastElementChild', closest: 'closest' }; const property = dict[direction]; // Handle recursive traversal for `up` if (direction === 'up') { let current = element; while (current) { current = current[property]; if (!selector || (current && current.matches(selector))) { return current; } } return null; } // Handle `siblings` if (direction === 'siblings') { const parent = element.parentElement; if (!parent) return []; const siblings = Array.from(parent.children).filter((child) => child !== element); return selector ? siblings.filter((sibling) => sibling.matches(selector)) : siblings; } // Handle `children` if (direction === 'children') { const children = Array.from(element.children); return selector ? children.filter((child) => child.matches(selector)) : children; } // Handle `closest` if (direction === 'closest') { const closest = element.closest(selector ?? '*'); return closest; } // Handle single-step traversal (e.g., `next`, `previous`, `firstChild`, `lastChild`) const target = element[property]; return target && (!selector || target.matches(selector)) ? target : null; } }