/** * ModuleLoader - Carga dinámica de módulos * * Responsabilidades: * - Cargar el módulo correcto según la base * - Manejar cache de módulos * - Gestionar lifecycle (mount/unmount) */ import { BaseModule, type ModuleContext } from './registry.ts'; import { getModuleConfig, isModuleEnabled } from './configs/index.ts'; import { StandardModule } from './standard/index.ts'; import type { Store } from '@/state/store.ts'; import type { AppState, BaseType } from '@/types/index.ts'; export interface LoaderTargets { container: HTMLElement; leftPanel: HTMLElement; groupsBar: HTMLElement; showDetail: (mrf: string) => void; } export class ModuleLoader { private store: Store; private currentModule: BaseModule | null = null; private currentBase: BaseType | null = null; constructor(store: Store) { this.store = store; } /** * Cargar módulo para una base */ async load(base: BaseType, targets: LoaderTargets): Promise { const config = getModuleConfig(base); // Verificar si el módulo está habilitado if (!isModuleEnabled(base)) { this.showDisabledMessage(targets.container, config.name); targets.leftPanel.innerHTML = ''; targets.groupsBar.innerHTML = ''; return; } // Si ya está cargado el mismo módulo, solo re-renderizar if (this.currentBase === base && this.currentModule) { this.currentModule.render(); return; } // Unmount módulo actual this.currentModule?.unmount(); this.currentModule = null; this.currentBase = null; // Show loading targets.container.innerHTML = '
Cargando...
'; // Crear contexto const ctx: ModuleContext = { container: targets.container, leftPanel: targets.leftPanel, groupsBar: targets.groupsBar, store: this.store, config, showDetail: targets.showDetail }; // Crear módulo según tipo let module: BaseModule; switch (config.renderType) { case 'standard': module = new StandardModule(ctx); break; case 'chat': case 'custom': // Carga dinámica de módulos custom if (config.customModule) { try { const { default: CustomModule } = await config.customModule(); module = new CustomModule(ctx); } catch (error) { console.error(`Failed to load custom module for ${base}:`, error); this.showErrorMessage(targets.container, `Error cargando módulo ${config.name}`); return; } } else { this.showDisabledMessage(targets.container, config.name); return; } break; default: console.error(`Unknown render type: ${config.renderType}`); this.showErrorMessage(targets.container, 'Tipo de módulo desconocido'); return; } // Mount módulo try { await module.mount(); this.currentModule = module; this.currentBase = base; } catch (error) { console.error(`Failed to mount module for ${base}:`, error); this.showErrorMessage(targets.container, `Error inicializando ${config.name}`); } } /** * Re-renderizar módulo actual (ej: cuando cambia la vista) */ rerender(): void { if (this.currentModule) { this.currentModule.render(); } } /** * Re-renderizar sidebar del módulo actual */ rerenderSidebar(): void { if (this.currentModule) { this.currentModule.renderSidebar(); } } /** * Obtener módulo actual */ getCurrentModule(): BaseModule | null { return this.currentModule; } /** * Obtener base actual */ getCurrentBase(): BaseType | null { return this.currentBase; } /** * Unmount módulo actual */ unmount(): void { this.currentModule?.unmount(); this.currentModule = null; this.currentBase = null; } private showDisabledMessage(container: HTMLElement, moduleName: string): void { container.innerHTML = `
🚧
${moduleName}
Próximamente
`; } private showErrorMessage(container: HTMLElement, message: string): void { container.innerHTML = `
⚠️
${message}
`; } } export default ModuleLoader;