UNPKG

tiny-essentials

Version:

Collection of small, essential scripts designed to be used across various projects. These simple utilities are crafted for speed, ease of use, and versatility.

190 lines (189 loc) 6.41 kB
import TinyHtml from '../TinyHtml.mjs'; /** * @typedef {string|string[]|Set<string>} ClassSetter * Represents a set of CSS classes that can be applied to an element. * - Can be a single class name string. * - Can be an array of class name strings. * - Can be a Set of class name strings. */ /** * TinyHtmlTemplate is a base helper class that extends TinyHtml and is designed * to simplify the creation of reusable HTML element templates. * * It is primarily intended as a foundation for specialized UI components * where consistent structure and styling are needed, similar in spirit to how React * components abstract HTML into reusable elements. * * As a result, TinyHtmlTemplate provides a small-scale, component-like * abstraction for HTML elements, serving as a lightweight building block * for higher-level UI helpers. * * @template {Element} TinyHtmlT * @extends TinyHtml<TinyHtmlT> */ class TinyHtmlTemplate extends TinyHtml { /** @type {Set<string>} */ #classes = new Set(); /** @type {Set<string>} */ #mainClass = new Set(); /** @returns {TinyHtmlT} */ get el() { // @ts-ignore return this.get(0); } /** * Updates the element's `className` attribute by combining * the current set of classes with the main class. */ #setClassString() { const classes = `${Array.from(this.#classes).join(' ')} ${Array.from(this.#mainClass).join(' ')}`.trim(); if (!classes) { this.removeAttr('className'); return; } this.setAttr('className', classes); } /** * Ensures a given value is a valid ClassSetter. * @param {ClassSetter} value * @returns {Set<string>} */ static #normalizeClasses(value) { if (typeof value === 'string') { return new Set(value.trim().split(/\s+/).filter(Boolean)); } if (Array.isArray(value) || value instanceof Set) { for (const v of value) { if (typeof v !== 'string') { throw new TypeError(`Class names must be strings. Got: ${typeof v}`); } } return new Set(value); } throw new TypeError(`Invalid ClassSetter. Expected string | string[] | Set<string>, got ${typeof value}`); } /** * Returns the combined list of all classes (regular + main). * @returns {string[]} */ get fullClass() { return [...Array.from(this.#classes), ...Array.from(this.#mainClass)]; } /** * Gets the current list of CSS classes applied to this element. * @returns {string[]} */ get classes() { return [...Array.from(this.#classes)]; } /** * Sets the list of CSS classes applied to this element. * Automatically updates the element's `className` attribute. * @param {ClassSetter} value */ set classes(value) { this.#classes = TinyHtmlTemplate.#normalizeClasses(value); this.#setClassString(); } /** * Gets the main CSS class applied to this element. * This class is always present in addition to the regular classes. * @returns {string[]} */ get mainClass() { return [...Array.from(this.#mainClass)]; } /** * Sets the main CSS class applied to this element. * Automatically updates the element's `className` attribute. * @param {ClassSetter} value */ set mainClass(value) { this.#mainClass = TinyHtmlTemplate.#normalizeClasses(value); this.#setClassString(); } /** * Creates a new TinyHtmlTemplate instance. * @param {TinyHtmlT} tag - The HTML tag name to create (e.g., 'div', 'span'). * @param {ClassSetter} [tags=[]] - Initial CSS classes to apply. * @param {ClassSetter} [mainClass=[]] - Main CSS classes to apply. */ constructor(tag, tags = [], mainClass = []) { super(tag); this.mainClass = mainClass; this.classes = tags; } /** * Add one or more CSS classes to the element. * @param {...string|string[]} tags * @returns {this} */ // @ts-ignore addClass(...tags) { for (const tag of tags.flat()) { if (typeof tag !== 'string') throw new TypeError(`Class name must be a string. Got: ${typeof tag}`); this.#classes.add(tag); } this.#setClassString(); return this; } /** * Remove one or more CSS classes from the element. * @param {...string|string[]} tags * @returns {this} */ // @ts-ignore removeClass(...tags) { for (const tag of tags.flat()) { if (typeof tag !== 'string') throw new TypeError(`Class name must be a string. Got: ${typeof tag}`); this.#classes.delete(tag); } this.#setClassString(); return this; } /** * Toggles the presence of a given CSS class on the element. * @param {string} tag - The class name to toggle. * @returns {this} */ // @ts-ignore toggleClass(tag) { if (typeof tag !== 'string') throw new TypeError(`Class name must be a string. Got: ${typeof tag}`); if (this.#classes.has(tag)) return this.removeClass(tag); else return this.addClass(tag); } /** * Replaces an existing CSS class with a new one. * Behaves like DOMTokenList.replace: * - Only replaces if oldClass exists. * - Returns true if replacement occurred, false otherwise. * @param {string} oldClass - The class to replace. * @param {string} newClass - The new class to add. * @returns {boolean} True if the replacement occurred, false otherwise. */ replaceClass(oldClass, newClass) { if (typeof oldClass !== 'string' || typeof newClass !== 'string') throw new TypeError('Both oldClass and newClass must be strings.'); if (!this.#classes.has(oldClass)) return false; this.#classes.delete(oldClass); this.#classes.add(newClass); this.#setClassString(); return true; } /** * Clears all CSS classes from the element. * @returns {this} */ resetClasses() { this.#classes.clear(); this.removeAttr('className'); return this; } } export default TinyHtmlTemplate;