@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
JavaScript
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;
}
}