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

461 lines (460 loc) 17.4 kB
import { Be, be } from '../be.js'; var domMethods; (function (domMethods) { domMethods["update"] = "update"; domMethods["append"] = "append"; domMethods["prepend"] = "prepend"; domMethods["insert"] = "insert"; domMethods["afterBegin"] = "afterBegin"; domMethods["afterEnd"] = "afterEnd"; domMethods["beforeBegin"] = "beforeBegin"; domMethods["beforeEnd"] = "beforeEnd"; domMethods["remove"] = "remove"; domMethods["wrap"] = "wrap"; domMethods["normalize"] = "normalize"; domMethods["replace"] = "replace"; domMethods["clear"] = "clear"; domMethods["unwrap"] = "unwrap"; })(domMethods || (domMethods = {})); /** * Handles DOM manipulation operations for Be elements. */ export class DomHandler { beElement; static methods = Object.values(domMethods); constructor(element) { this.beElement = element; } methods = DomHandler.methods; /** * Handles various DOM operations on the element(s). * @param actions An object specifying the DOM actions to perform. * @param actions.update - HTML content to update the element(s) with. * @param actions.append - Content to append to the element(s). * @param actions.prepend - Content to prepend to the element(s). * @param actions.remove - If true, removes the element(s) from the DOM. * @param actions.replace - Content to replace the element(s) with. * @param actions.clear - If true, clears the content of the element(s). * @returns The Be instance for method chaining. */ handle(actions) { Object.entries(actions).forEach(([method, props]) => { switch (method) { case 'update': // be('.test').dom({update: {content: '<p>Updated</p>', callback: () => {}}}) this.update(props.content, props.callback); break; case 'append': this.append(props.content, props.callback); break; case 'prepend': this.prepend(props.content, props.callback); break; case 'replace': this.replace(props.content, props.callback); break; case 'remove': this.remove(props.callback); break; case 'clear': this.clear(props.callback); break; case 'normalize': this.normalize(props.callback); break; case 'wrap': this.wrap(props.tag, props.callback); break; case 'unwrap': this.unwrap(props.callback); break; } }); return this.beElement; } /** * Updates the content of the element(s). * @param content - The new content to set. * @param callback - Optional callback function. * @returns The Be instance for method chaining. * @example * // HTML: <div id="test"></div> * const beInstance = be('#test'); * beInstance.update('<p>Updated content</p>'); // Updates the content of the element */ update(content, callback) { this.beElement.eachNode((el) => { if (el) { el.innerHTML = content; callback?.({ fragment: content, be: be(el), root: this.beElement }); } }); return this.beElement; } /** * Appends content to the element(s). * @param content - The content to append (string, HTMLElement, or Be instance). * @param callback - Optional callback function to execute after appending. * @returns The Be instance for method chaining. * @example * // HTML: <div id="test"></div> * const beInstance = be('#test'); * beInstance.append('<span>Appended</span>'); // Appends content to the element */ append(content, callback) { const ret = []; this.beElement.eachNode((el) => { const normalizedContent = this.normalizeContent(content); if (normalizedContent instanceof DocumentFragment) { el.appendChild(normalizedContent); } else { ret.push(normalizedContent); el.appendChild(normalizedContent); } }); callback?.({ fragment: content, be: be(ret), root: this.beElement }); return this.beElement; } /** * Prepends content to the element(s). * @param content - The content to prepend (string, HTMLElement, or Be instance). * @param callback - Optional callback function to execute after prepending. * @returns The Be instance for method chaining. * @example * // HTML: <div id="test"></div> * const beInstance = be('#test'); * beInstance.prepend('<span>Prepended</span>'); // Prepends content to the element */ prepend(content, callback) { const ret = []; this.beElement.eachNode((el) => { const normalizedContent = this.normalizeContent(content); if (normalizedContent instanceof DocumentFragment) { el.insertBefore(normalizedContent, el.firstChild); } else { ret.push(normalizedContent); el.insertBefore(normalizedContent, el.firstChild); } }); callback?.({ fragment: content, be: be(ret), root: this.beElement }); return this.beElement; } /** * Inserts content into the element(s) at a specified position. * @param mode - The position to insert the content ('afterbegin', 'afterend', 'beforebegin', 'beforeend'). * @param element - The content to insert (string, HTMLElement, or Be instance). * @param callback - Optional callback function to execute after insertion. * @returns The Be instance for method chaining. * @example * // HTML: <div id="test"></div> * const beInstance = be('#test'); * beInstance.insert('afterbegin', '<span>Inserted</span>'); // Inserts content at the beginning */ insert(mode, element, callback) { switch (mode) { case 'afterbegin': return this.afterBegin(element, callback); case 'afterend': return this.afterEnd(element, callback); case 'beforebegin': return this.beforeBegin(element, callback); case 'beforeend': return this.beforeEnd(element, callback); default: throw new Error(`Invalid mode: ${mode}`); } } /** * Inserts content at the beginning of the element(s). * @param content - The content to insert (string, HTMLElement, or Be instance). * @param callback - Optional callback function to execute after insertion. * @returns The Be instance for method chaining. */ afterBegin(content, callback) { this.beElement.eachNode((el) => { this.adjacentElement(el, content, 'afterbegin'); callback?.({ fragment: content, be: be(el), root: this.beElement }); }); return this.beElement; } /** * Inserts content after the element(s). * @param content - The content to insert (string, HTMLElement, or Be instance). * @param callback - Optional callback function to execute after insertion. * @returns The Be instance for method chaining. */ afterEnd(content, callback) { this.beElement.eachNode((el) => { // Insérer après l'élément cible el.parentNode?.insertBefore(this.normalizeContent(content), el.nextSibling); callback?.({ fragment: content, be: be(el), root: this.beElement }); }); return this.beElement; } /** * Inserts content before the element(s). * @param content - The content to insert (string, HTMLElement, or Be instance). * @param callback - Optional callback function to execute after insertion. * @returns The Be instance for method chaining. */ beforeBegin(content, callback) { this.beElement.eachNode((el) => { // Insérer avant l'élément cible el.parentNode?.insertBefore(this.normalizeContent(content), el); callback?.({ fragment: content, be: be(el), root: this.beElement }); }); return this.beElement; } /** * Inserts content at the end of the element(s). * @param content - The content to insert (string, HTMLElement, or Be instance). * @param callback - Optional callback function to execute after insertion. * @returns The Be instance for method chaining. */ beforeEnd(content, callback) { this.beElement.eachNode((el) => { // Insérer à la fin de l'élément cible el.appendChild(this.normalizeContent(content)); callback?.({ fragment: content, be: be(el), root: this.beElement }); }); return this.beElement; } /** * Replaces the element(s) with new content. * @param content - The content to replace the element(s) with (string, HTMLElement, or Be instance). * @param callback - Optional callback function to execute after replacement. * @returns The Be instance for method chaining. */ replace(content, callback) { const ret = []; this.beElement.eachNode((el) => { const normalizedContent = this.normalizeContent(content); if (normalizedContent instanceof DocumentFragment) { el.replaceWith(...normalizedContent.childNodes); } else { ret.push(normalizedContent); el.replaceWith(normalizedContent); } }); callback?.({ fragment: content, be: be(ret), root: this.beElement }); return this.beElement; } /** * Removes the element(s) from the DOM. * @param callback - Optional callback function to execute after removal. * @returns The Be instance for method chaining. * @example * // HTML: <div id="test"><span>To be removed</span></div> * const beInstance = be('#test span'); * beInstance.remove(); // Removes the span element */ remove(callback) { this.beElement.eachNode((el) => { el.remove(); callback?.({ fragment: undefined, be: be(el), root: this.beElement }); }); return this.beElement; } /** * Clears the content of the element(s). * @param callback - Optional callback function to execute after clearing. * @returns The Be instance for method chaining. * @example * // HTML: <div id="test"><span>Content</span></div> * const beInstance = be('#test'); * beInstance.clear(); // Clears the content of the div */ clear(callback) { this.beElement.eachNode((el) => { const fragment = el.innerHTML; el.innerHTML = ''; callback?.({ fragment, be: be(el), root: this.beElement }); }); return this.beElement; } /** * Normalizes the content of the element(s). * @param callback - Optional callback function to execute after normalization. * @returns The Be instance for method chaining. */ normalize(callback) { this.beElement.eachNode((el) => { el.normalize(); callback?.({ fragment: undefined, be: be(el), root: this.beElement }); }); return this.beElement; } /** * Wraps the element(s) with a new element. * @param tag - The tag name of the wrapper element (default is 'div'). * @param callback - Optional callback function to execute after wrapping. * @returns The Be instance for method chaining. * @example * // HTML: <div id="test"></div> * const beInstance = be('#test'); * beInstance.wrap('section'); // Wraps the div with a <section> element */ wrap(tag = 'div', callback) { // wrap in tag this.beElement.eachNode((el) => { const wrapper = document.createElement(tag); el.insertAdjacentElement('beforebegin', wrapper); // "afterbegin" | "afterend" | "beforebegin" | "beforeend" wrapper.appendChild(el); callback?.({ fragment: tag, be: be(el), root: this.beElement }); }); return this.beElement; } /** * Removes the parent element of the selected element(s), keeping the selected element(s) in the DOM. * @param callback - Optional callback function to execute after unwrapping. * @returns The Be instance for method chaining. * @example * // HTML: <div id="wrapper"><span id="child">Content</span></div> * const beInstance = be('#child'); * beInstance.unwrap(); // Removes the <div id="wrapper">, keeping <span id="child"> */ unwrap(callback) { this.beElement.eachNode((el) => { const parent = el.parentElement; if (parent) { // Move the element itself before the parent while (el.firstChild) { parent.insertBefore(el.firstChild, el); } // Remove the parent after moving its children parent.replaceWith(...Array.from(parent.childNodes)); } callback?.({ fragment: undefined, be: be(el), root: this.beElement }); }); return this.beElement; } adjacentElement(element, content, mode) { const normalizedContent = this.normalizeContent(content); if (typeof content === 'string') { // Si le contenu est une chaîne HTML, utilisez insertAdjacentHTML element.insertAdjacentHTML(mode, content); } else if (normalizedContent instanceof HTMLElement) { // Si le contenu est un élément DOM, utilisez insertAdjacentElement if (mode === 'afterend') { element.parentNode?.insertBefore(normalizedContent, element.nextSibling); } else if (mode === 'beforebegin') { element.parentNode?.insertBefore(normalizedContent, element); } else { element.insertAdjacentElement(mode, normalizedContent); } } else if (normalizedContent instanceof DocumentFragment) { // Si le contenu est un fragment, insérez chaque nœud Array.from(normalizedContent.childNodes).forEach((node) => { if (mode === 'afterbegin' || mode === 'beforeend') { element.appendChild(node); } else if (mode === 'afterend') { element.parentNode?.insertBefore(node, element.nextSibling); } else if (mode === 'beforebegin') { element.parentNode?.insertBefore(node, element); } }); } } normalizeContent(content) { if (typeof content === 'string') { // Si le contenu est une chaîne HTML, créez un fragment DOM const template = document.createElement('template'); template.innerHTML = content.trim(); return template.content; } else if (content instanceof Be) { // Si le contenu est une instance de Be, utilisez son premier nœud if (Array.isArray(content.node)) { const fragment = document.createDocumentFragment(); content.node.forEach((node) => { if (node instanceof HTMLElement) { fragment.appendChild(node); } }); return fragment; } else if (content.node instanceof HTMLElement) { return content.node; } throw new Error('Invalid Be instance: no valid node found.'); } else { // Sinon, le contenu est déjà un HTMLElement return content; } } insertContent(el, content, mode) { const normalizedContent = this.normalizeContent(content); if (mode === 'afterEnd') { el.parentNode?.insertBefore(normalizedContent, el.nextSibling); } else if (mode === 'beforeEnd') { el.appendChild(normalizedContent); } } valueOf() { if (this.beElement.isWhat !== 'element') return null; return this.beElement.inputNode.innerHTML; } }