/** * @fileoverview Base Component Class for Rantii * @author retoor * @description Foundation class for all custom HTML elements * @keywords component, web component, custom element, base, foundation */ class BaseComponent extends HTMLElement { constructor() { super(); this.isInitialized = false; this.eventListeners = []; } connectedCallback() { if (!this.isInitialized) { this.init(); this.isInitialized = true; } this.onConnected(); } disconnectedCallback() { this.cleanup(); this.onDisconnected(); } attributeChangedCallback(name, oldValue, newValue) { if (oldValue !== newValue) { this.onAttributeChanged(name, oldValue, newValue); } } init() {} onConnected() {} onDisconnected() {} onAttributeChanged(name, oldValue, newValue) {} render() {} update(data) { Object.assign(this, data); this.render(); } $(selector) { return this.querySelector(selector); } $$(selector) { return this.querySelectorAll(selector); } on(target, event, handler, options = {}) { const boundHandler = handler.bind(this); target.addEventListener(event, boundHandler, options); this.eventListeners.push({ target, event, handler: boundHandler, options }); return boundHandler; } off(target, event, handler) { target.removeEventListener(event, handler); this.eventListeners = this.eventListeners.filter( l => !(l.target === target && l.event === event && l.handler === handler) ); } cleanup() { this.eventListeners.forEach(({ target, event, handler, options }) => { target.removeEventListener(event, handler, options); }); this.eventListeners = []; } emit(eventName, detail = {}) { const event = new CustomEvent(eventName, { bubbles: true, composed: true, detail }); this.dispatchEvent(event); } setHtml(html) { this.innerHTML = html; } setText(text) { this.textContent = text; } show() { this.style.display = ''; this.removeAttribute('hidden'); } hide() { this.style.display = 'none'; } toggle(visible) { if (visible !== undefined) { visible ? this.show() : this.hide(); } else { this.style.display === 'none' ? this.show() : this.hide(); } } addClass(...classNames) { this.classList.add(...classNames); } removeClass(...classNames) { this.classList.remove(...classNames); } toggleClass(className, force) { this.classList.toggle(className, force); } hasClass(className) { return this.classList.contains(className); } setData(key, value) { this.dataset[key] = value; } getData(key) { return this.dataset[key]; } getAttr(name) { return this.getAttribute(name); } setAttr(name, value) { if (value === null || value === undefined) { this.removeAttribute(name); } else { this.setAttribute(name, value); } } hasAttr(name) { return this.hasAttribute(name); } async waitForInit() { return new Promise(resolve => { if (this.isInitialized) { resolve(); } else { const observer = new MutationObserver(() => { if (this.isInitialized) { observer.disconnect(); resolve(); } }); observer.observe(this, { childList: true, subtree: true }); } }); } debounce(func, wait) { let timeout; return (...args) => { clearTimeout(timeout); timeout = setTimeout(() => func.apply(this, args), wait); }; } throttle(func, limit) { let inThrottle; return (...args) => { if (!inThrottle) { func.apply(this, args); inThrottle = true; setTimeout(() => inThrottle = false, limit); } }; } getApp() { return window.app; } getApi() { return window.app?.api; } getAuth() { return window.app?.auth; } getRouter() { return window.app?.router; } getStorage() { return window.app?.storage; } getTheme() { return window.app?.theme; } isLoggedIn() { return window.app?.auth?.isLoggedIn() || false; } getCurrentUser() { return window.app?.auth?.getUser() || null; } } export { BaseComponent };