@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
355 lines (354 loc) • 11.9 kB
JavaScript
import { AttrHandler } from './modules/attrs.js';
import { StylesHandler } from './modules/styles.js';
import { BeUtils } from './utils.js';
import { DataHandler } from './modules/data.js';
import { EventsHandler } from './modules/events.js';
import { ClassesHandler } from './modules/classes.js';
import { DomHandler } from './modules/dom.js';
import { PositionHandler } from './modules/position.js';
import { WalkHandler } from './modules/walk.js';
import { TextHandler } from './modules/text.js';
import { TimersHandler } from './modules/timers.js';
import { HttpHandler } from './modules/http.js';
export class Be {
inputNode;
isWhat;
//
timerOut = null;
timerInterval = null;
// styles
styles;
styleHandler;
setStyle;
getStyle;
unsetStyle;
// dataSet
data;
dataHandler;
setData;
getData;
deleteData;
getKey;
// attributes
attrs;
attrHandler;
setAttr;
getAttr;
deleteAttr;
// position
position;
positionHandler;
clonePosition;
overlapPosition;
snapTo;
// dom
dom;
domHandler;
update;
append;
prepend;
insert;
afterBegin;
afterEnd;
beforeBegin;
beforeEnd;
remove;
replace;
clear;
normalize;
wrap;
unwrap;
// text
text;
textHandler;
appendText;
prependText;
updateText;
replaceText;
removeText;
clearText;
normalizeText;
wrapText;
// events
events;
eventHandler;
on;
off;
fire;
// classes
classes;
classesHandler;
addClass;
removeClass;
toggleClass;
replaceClass;
// walk
walk;
walkHandler;
up;
next;
without;
previous;
siblings;
children;
closest;
lastChild;
firstChild;
find;
findAll;
// timers
timers;
timerHandler;
timeout;
interval;
clearTimeout;
clearInterval;
// http
http;
httpHandler;
updateHttp;
insertHttp;
constructor(input) {
if (input instanceof Be) {
return input;
}
this.inputNode = Be.getNode(input);
this.isWhat =
typeof this.inputNode === 'string'
? 'qy'
: Array.isArray(this.inputNode)
? 'array'
: 'element';
// styles
this.styleHandler = new StylesHandler(this);
this.styles = this.handle(this.styleHandler);
this.attach(StylesHandler, 'Style');
// dataSet
this.dataHandler = new DataHandler(this);
this.data = this.handle(this.dataHandler);
this.attach(DataHandler, 'Data');
// attributes
this.attrHandler = new AttrHandler(this);
this.attrs = this.handle(this.attrHandler);
this.attach(AttrHandler, 'Attr');
// position
this.positionHandler = new PositionHandler(this);
this.position = this.handle(this.positionHandler);
this.attach(PositionHandler);
// text
this.textHandler = new TextHandler(this);
this.text = this.handle(this.textHandler);
this.attach(TextHandler, 'Text');
// dom and handle
this.domHandler = new DomHandler(this);
this.dom = this.handle(this.domHandler);
this.attach(DomHandler);
// events
this.eventHandler = new EventsHandler(this);
this.events = this.handle(this.eventHandler);
this.attach(EventsHandler);
// classes
this.classesHandler = new ClassesHandler(this);
this.classes = this.handle(this.classesHandler);
this.attach(ClassesHandler, 'Class');
// walk
this.walkHandler = new WalkHandler(this);
this.walk = this.handle(this.walkHandler);
this.attach(WalkHandler);
// timers
this.timerHandler = new TimersHandler(this);
this.timers = this.handle(this.timerHandler);
this.attach(TimersHandler);
// http
this.httpHandler = new HttpHandler(this);
this.http = this.handle(this.httpHandler);
this.attach(HttpHandler, 'Http');
}
/**
* Normalizes the input to ensure `node` is always an HTMLElement or an array of HTMLElements.
* @param input - The input to normalize (string, HTMLElement, or array of HTMLElements).
* @returns A valid HTMLElement or an array of HTMLElements.
*/
static getNode(input) {
if (typeof input === 'string') {
// Si `input` est une chaîne, sélectionnez les éléments correspondants
const elements = Array.from(document.querySelectorAll(input));
return elements.length === 1 ? elements[0] : elements;
}
else if (input instanceof HTMLElement) {
// Si `input` est un seul élément DOM, retournez-le
return input;
}
else if (Array.isArray(input)) {
// Si `input` est un tableau, filtrez pour ne garder que les éléments DOM valides
return input.filter((n) => n instanceof HTMLElement);
}
throw new Error('Invalid input: must be a string, HTMLElement, or an array of HTMLElements.');
}
static elem(node) {
return new Be(node);
}
static createBe(tagOrHtml, options) {
const test = BeUtils.isHTML(tagOrHtml, { returnHTMLelement: true });
const testIsTag = test.isHtml && !tagOrHtml.includes(' ') && tagOrHtml.length < 15;
let ret;
if (test.isHtml && test.beElem) {
ret = test.beElem;
}
else if (testIsTag) {
const el = document.createElement(tagOrHtml);
ret = be(el);
}
else {
const el = document.createElement('div');
ret = be(el);
}
if (options?.style)
ret.setStyle(options.style);
if (options?.attributes)
ret.setAttr(options.attributes);
if (options?.className)
ret.addClass(options.className);
return ret;
}
/**
* Creates a new `Be` element based on the provided string or HTMLElement.
* If the input is an HTMLElement, it creates a new `Be` element and sets it as the child of the provided element.
* If the input is a string, it checks if it is a valid HTML string and creates a new `Be` element based on it.
* If the input is neither a string nor an HTMLElement, it creates a new `Be` element with the default tag.
*
* @param str - The string or HTMLElement to create the `Be` element from.
* @param options - Additional options for creating the `Be` element.
* @returns The created `Be` element.
*/
static toBe(str, options = {}) {
const { tag = 'div' } = options;
let beElem;
if (str instanceof HTMLElement) {
beElem = be(str);
}
else if (typeof str === 'string') {
const trimmed = str.trim();
if (trimmed.startsWith('<') && trimmed.endsWith('>') && trimmed.includes('</')) {
// Parse as HTML
const parser = new DOMParser();
const doc = parser.parseFromString(trimmed, 'text/html');
const element = doc.body.firstElementChild || document.createElement(tag);
beElem = createBe(tag);
beElem.update(element.innerHTML);
// Apply styles
const styles = element.getAttribute('style');
if (styles) {
const styleObj = Object.fromEntries(styles
.split(';')
.filter((style) => style.trim())
.map((style) => {
const [key, value] = style.split(':').map((s) => s.trim());
return [key, value];
}));
beElem.setStyle(styleObj);
}
// Apply other attributes
Array.from(element.attributes).forEach((attr) => {
if (attr.name !== 'style') {
beElem.setAttr(attr.name, attr.value);
}
});
}
else {
// Create a Be element with the default tag and set text content
beElem = be(document.createElement(tag));
beElem.update(str);
}
}
else {
// Handle non-string, non-HTMLElement input
beElem = createBe(tag);
}
return beElem;
}
fetch(options) {
return fetch(options.url, {
method: options.method || 'GET',
body: options.data ? JSON.stringify(options.data) : undefined,
headers: options.headers || {}
}).then((response) => response.json());
}
/**
* Iterates over nodes based on the type of `this.isWhat` and applies a callback function to each node.
*
* @param callback - A function to be executed for each node. Receives the current node as an argument.
* @param firstChild - Optional. If `true`, stops further iteration after the first child is processed.
*
* The behavior of the method depends on the value of `this.isWhat`:
* - `'element'`: Applies the callback to a single HTMLElement (`this.inputNode`).
* - `'array'`: Iterates over an array of HTMLElements (`this.inputNode`) and applies the callback to each.
* - `'qy'`: Selects elements using a query selector string (`this.inputNode`) and applies the callback to each.
*/
eachNode(callback, firstChild) {
switch (this.isWhat) {
case 'element':
BeUtils.applyCallback(this.inputNode, callback);
break;
case 'array':
this.inputNode.forEach((lo) => {
BeUtils.applyCallback(lo, callback);
if (firstChild)
return;
});
break;
case 'qy':
document.querySelectorAll(this.inputNode).forEach((el) => {
callback(el);
if (firstChild)
return;
});
break;
}
}
get html() {
return this.isWhat === 'element' ? this.inputNode.innerHTML : null;
}
get node() {
switch (this.isWhat) {
case 'element':
return this.inputNode;
case 'array':
return Array.from(this.inputNode);
case 'qy':
return Array.from(document.querySelectorAll(this.inputNode));
}
}
attach(Handler, suffix = '') {
const fromMethods = Handler.methods || [];
fromMethods.forEach((method) => {
const handler = new Handler(this);
const methodName = suffix ? method + suffix : method;
if (!(method in handler)) {
console.error(`Method ${method} not found in ${Handler.name}`, handler);
}
else if (methodName in this) {
if (!handler) {
console.error(`Handler ${Handler.name} not found`, handler);
}
this[methodName] = (...args) => {
return handler[method].apply(handler, args);
};
}
});
}
handle(cl) {
return cl.handle.bind(cl);
}
}
/** set exports as root */
export const be = Be.elem;
export const toBe = Be.toBe;
export const beId = (id) => Be.elem(`#${id}`);
export const createBe = Be.createBe;
// be('.test').dom.update('content', () => {}); // should return be('.test').dom.
// relies on dom.handle // should return be('.test')
/* be('.test').dom({
update: { content: '<p>Updated</p>', callback: () => {} },
append: { content: '<p>Appended</p>', callback: () => {} }
}); */