/** * KTUI - Free & Open-Source Tailwind UI Components by Keenthemes * Copyright 2025 by Keenthemes Inc */ declare global { interface Window { KTGlobalComponentsConfig: object; } } import KTData from '../helpers/data'; import KTDom from '../helpers/dom'; import KTUtils from '../helpers/utils'; import { KTOptionType } from '../types'; export default class KTComponent { protected _dataOptionPrefix: string = 'kt-'; protected _name: string; protected _defaultConfig: object; protected _config: object; protected _events: Map> = new Map(); protected _uid: string | null = null; protected _element: HTMLElement | null = null; /** * Check if component should skip initialization * Returns true if element already has an instance and is still connected to DOM * Returns false if element should be initialized (no instance or disconnected) * @param element The element to check * @returns true if initialization should be skipped, false otherwise */ protected _shouldSkipInit(element: HTMLElement): boolean { if (!KTData.has(element, this._name)) { return false; } const existingInstance = KTData.get(element, this._name) as KTComponent; // If element is not connected to DOM, dispose old instance and allow reinitialization if (element.isConnected === false) { if (existingInstance && typeof existingInstance.dispose === 'function') { existingInstance.dispose(); } return false; } // Element is connected and has instance, skip initialization return true; } protected _init(element: HTMLElement | null) { element = KTDom.getElement(element); if (!element) { return; } this._element = element; this._events = new Map(); this._uid = KTUtils.geUID(this._name); this._element.setAttribute(`data-kt-${this._name}-initialized`, 'true'); KTData.set(this._element, this._name, this); } protected async _fireEvent( eventType: string, payload: object | null = null, ): Promise { const callbacks = this._events.get(eventType); if (callbacks instanceof Map == false) { return; } await Promise.all( Array.from(callbacks.values()) .filter((callable) => { return typeof callable === 'function'; }) .map((callable) => { return Promise.resolve(callable(payload)); }), ); } protected _dispatchEvent( eventType: string, payload: object | null = null, ): void { const event = new CustomEvent(eventType, { detail: { payload }, bubbles: true, cancelable: true, composed: false, }); if (!this._element) return; this._element.dispatchEvent(event); } protected _getOption(name: string): KTOptionType { const value = this._config[name as keyof object]; if (!this._element) { return value as KTOptionType; } const reponsiveValue = KTDom.getCssProp( this._element, `--kt-${this._name}-${KTUtils.camelReverseCase(name)}`, ); return reponsiveValue || value; } protected _getGlobalConfig(): object { if ( window.KTGlobalComponentsConfig && (window.KTGlobalComponentsConfig as object)[this._name as keyof object] ) { return (window.KTGlobalComponentsConfig as object)[ this._name as keyof object ] as object; } else { return {}; } } protected _buildConfig(config: object = {}): void { if (!this._element) return; this._config = { ...this._defaultConfig, ...this._getGlobalConfig(), ...KTDom.getDataAttributes( this._element, this._dataOptionPrefix + this._name, ), ...config, }; } /** * Merge config into the existing _config in place. Use when re-applying config to an * already-initialized instance so handlers that hold a reference to _config see updates. */ protected _mergeConfig(config: object): void { if ( config && typeof config === 'object' && Object.keys(config).length > 0 ) { Object.assign(this._config, config); } } public dispose(): void { if (!this._element) return; this._element.removeAttribute(`data-kt-${this._name}-initialized`); KTData.remove(this._element, this._name); } public on(eventType: string, callback: CallableFunction): string { const eventId = KTUtils.geUID(); if (!this._events.get(eventType)) { this._events.set(eventType, new Map()); } const eventMap = this._events.get(eventType); if (!eventMap) { return eventId; } eventMap.set(eventId, callback); return eventId; } public off(eventType: string, eventId: string): void { this._events.get(eventType)?.delete(eventId); } public getOption(name: string): KTOptionType { return this._getOption(name as keyof object); } public getElement(): HTMLElement | null { if (!this._element) return null; return this._element; } }