/** * Component definition object. * @typedef {Object} ComponentDefinition * @property {SetupFunction} [setup] * Optional setup function that initializes the component's state and returns reactive data. * @property {TemplateFunction | string} template * Required function or string that defines the component's HTML structure. * @property {StyleFunction | string} [style] * Optional function or string that provides CSS styles for the component. * Styles are preserved across DOM diffs via data-e-style markers. * @property {ChildrenMap} [children] * Optional object defining nested child components. */ /** * Setup function that initializes component state. * @callback SetupFunction * @param {ComponentContext} ctx * The component context with props, emitter, and signal factory. * @returns {SetupResult | Promise} * Reactive data and lifecycle hooks. */ /** * Data returned from setup function, may include lifecycle hooks. * @typedef {Record & LifecycleHooks} SetupResult */ /** * Template function that returns HTML markup. * @callback TemplateFunction * @param {ComponentContext & SetupResult} ctx * The merged component context and setup data. * @returns {string | Promise} * HTML template string. */ /** * Style function that returns CSS styles. * @callback StyleFunction * @param {ComponentContext & SetupResult} ctx * The merged component context and setup data. * @returns {string} * CSS styles string. */ /** * Map of CSS selectors to component definitions or registered component names. * @typedef {Record} ChildrenMap */ /** * Context passed to component setup function. * @typedef {Object} ComponentContext * @property {ComponentProps} props * Component properties passed during mounting. * @property {Emitter} emitter * Event emitter instance for component event handling. * @property {SignalFactory} signal * Factory function to create reactive Signal instances. * @description * Plugins may extend this context with additional properties (e.g., `ctx.router`, `ctx.store`). * @see RouterContext - Router plugin injected context. * @see StoreApi - Store plugin injected context. */ /** * Properties passed to a component during mounting. * @typedef {Record} ComponentProps */ /** * Factory function to create reactive Signal instances. * @typedef {(initialValue: T) => Signal} SignalFactory */ /** * Lifecycle hooks that can be returned from setup function. * @typedef {Object} LifecycleHooks * @property {LifecycleHook} [onBeforeMount] * Called before component mounting. * @property {LifecycleHook} [onMount] * Called after component mounting. * @property {LifecycleHook} [onBeforeUpdate] * Called before component update. * @property {LifecycleHook} [onUpdate] * Called after component update. * @property {UnmountHook} [onUnmount] * Called during component unmounting. */ /** * Lifecycle hook function. * @callback LifecycleHook * @param {LifecycleHookContext} ctx * Context with container and component data. * @returns {void | Promise} */ /** * Unmount hook function with cleanup resources. * @callback UnmountHook * @param {UnmountHookContext} ctx * Context with cleanup resources. * @returns {void | Promise} */ /** * Context passed to lifecycle hooks. * @typedef {Object} LifecycleHookContext * @property {HTMLElement} container * The DOM element where the component is mounted. * @property {ComponentContext & SetupResult} context * The component's reactive state and context data. */ /** * Context passed to unmount hook with cleanup resources. * @typedef {Object} UnmountHookContext * @property {HTMLElement} container * The DOM element where the component is mounted. * @property {ComponentContext & SetupResult} context * The component's reactive state and context data. * @property {CleanupResources} cleanup * Object containing cleanup functions and instances. */ /** * Resources available for cleanup during unmount. * @typedef {Object} CleanupResources * @property {UnsubscribeFunction[]} watchers * Signal watcher cleanup functions. * @property {UnsubscribeFunction[]} listeners * Event listener cleanup functions. * @property {MountResult[]} children * Child component instances. */ /** * Result of mounting a component. * @typedef {Object} MountResult * @property {HTMLElement} container * The DOM element where the component is mounted. * @property {ComponentContext & SetupResult} data * The component's reactive state and context data. * @property {UnmountFunction} unmount * Function to clean up and unmount the component. */ /** * Function to unmount a component and clean up resources. * @callback UnmountFunction * @returns {Promise} */ /** * Function to unsubscribe from events or watchers. * @callback UnsubscribeFunction * @returns {void | boolean} */ /** * Plugin interface for extending Eleva. * @typedef {Object} ElevaPlugin * @property {string} name * Unique identifier name for the plugin. * @property {string} [version] * Optional version string for the plugin. * @property {PluginInstallFunction} install * Function that installs the plugin. * @property {PluginUninstallFunction} [uninstall] * Optional function to uninstall the plugin. */ /** * Plugin install function. * @callback PluginInstallFunction * @param {Eleva} eleva * The Eleva instance. * @param {PluginOptions} [options] * Plugin configuration options. * @returns {void | Eleva | unknown} */ /** * Plugin uninstall function. * @callback PluginUninstallFunction * @param {Eleva} eleva * The Eleva instance. * @returns {void | Promise} */ /** * Configuration options passed to a plugin during installation. * @typedef {Record} PluginOptions */ /** * Handler function for DOM events (e.g., click, input, submit). * @typedef {(event: Event) => void} DOMEventHandler */ /** * Common DOM event names (prefixed with @ in templates). * @typedef {'click'|'submit'|'input'|'change'|'focus'|'blur'|'keydown'|'keyup'|'keypress'|'mouseenter'|'mouseleave'|'mouseover'|'mouseout'|'mousedown'|'mouseup'|'touchstart'|'touchend'|'touchmove'|'scroll'|'resize'|'load'|'error'|string} DOMEventName */ /** * @class 🧩 Eleva * @classdesc A modern, signal-based component runtime framework that provides lifecycle hooks, * component styles, and plugin support. Eleva manages component registration, plugin integration, * event handling, and DOM rendering with a focus on performance and developer experience. * * @example * // Basic component creation and mounting * const app = new Eleva("myApp"); * app.component("myComponent", { * setup: (ctx) => ({ count: ctx.signal(0) }), * template: (ctx) => `
Hello ${ctx.props.name}
` * }); * app.mount(document.getElementById("app"), "myComponent", { name: "World" }); * * @example * // Using lifecycle hooks * app.component("lifecycleDemo", { * setup: () => { * return { * onMount: ({ container, context }) => { * console.log('Component mounted!'); * } * }; * }, * template: `
Lifecycle Demo
` * }); */ export class Eleva { /** * Creates a new Eleva instance with the specified name. * * @public * @constructor * @param {string} name - The unique identifier name for this Eleva instance. * @throws {Error} If the name is not provided or is not a string. * * @example * const app = new Eleva("myApp"); * app.component("myComponent", { * setup: (ctx) => ({ count: ctx.signal(0) }), * template: (ctx) => `
Hello ${ctx.props.name}!
` * }); * app.mount(document.getElementById("app"), "myComponent", { name: "World" }); * */ constructor(name: string); /** @public @readonly {string} The unique identifier name for this Eleva instance */ public readonly name: string; /** @public @readonly {Emitter} Event emitter for handling component events */ public readonly emitter: Emitter; /** @public @readonly {typeof Signal} Signal class for creating reactive state */ public readonly signal: typeof Signal; /** @public @readonly {typeof TemplateEngine} TemplateEngine class for template parsing */ public readonly templateEngine: typeof TemplateEngine; /** @public @readonly {Renderer} Renderer for handling DOM updates and patching */ public readonly renderer: Renderer; /** @private {Map} Registry of all component definitions by name */ private _components; /** @private {Map} Collection of installed plugin instances by name */ private _plugins; /** @private {number} Counter for generating unique component IDs */ private _componentCounter; /** * Integrates a plugin with the Eleva framework. * The plugin's install function will be called with the Eleva instance and provided options. * After installation, the plugin will be available for use by components. * * @note Plugins that wrap core methods (e.g., mount) must be uninstalled in reverse order * of installation (LIFO - Last In, First Out) to avoid conflicts. * * @public * @param {ElevaPlugin} plugin - The plugin object which must have an `install` function. * @param {PluginOptions} [options={}] - Optional configuration options for the plugin. * @returns {Eleva | unknown} The Eleva instance (for method chaining) or the result returned by the plugin. * @throws {Error} If plugin does not have an install function. * @see component - Register components after installing plugins. * @see mount - Mount components to the DOM. * @example * app.use(myPlugin, { option1: "value1" }); * * @example * // Correct uninstall order (LIFO) * app.use(PluginA); * app.use(PluginB); * // Uninstall in reverse order: * PluginB.uninstall(app); * PluginA.uninstall(app); */ public use(plugin: ElevaPlugin, options?: PluginOptions): Eleva | unknown; /** * Registers a new component with the Eleva instance. * The component will be available for mounting using its registered name. * * @public * @param {string} name - The unique name of the component to register. * @param {ComponentDefinition} definition - The component definition including setup, template, style, and children. * @returns {Eleva} The Eleva instance (for method chaining). * @throws {Error} If name is not a non-empty string or definition has no template. * @see mount - Mount this component to the DOM. * @example * app.component("myButton", { * template: (ctx) => ``, * style: `button { color: blue; }` * }); */ public component(name: string, definition: ComponentDefinition): Eleva; /** * Mounts a registered component to a DOM element. * This will initialize the component, set up its reactive state, and render it to the DOM. * If the container already has a mounted Eleva instance, it is returned as-is. * Unmount clears the container contents and removes the internal instance marker. * * @public * @async * @param {HTMLElement} container - The DOM element where the component will be mounted. * @param {string | ComponentDefinition} compName - The name of the registered component or a direct component definition. * @param {ComponentProps} [props={}] - Optional properties to pass to the component. * @returns {Promise} * A Promise that resolves to an object containing: * - container: The mounted component's container element * - data: The component's reactive state and context * - unmount: Function to clean up and unmount the component * @throws {Error} If container is not a DOM element or component is not registered. * @throws {Error} If setup function, template function, or style function throws. * @example * const instance = await app.mount(document.getElementById("app"), "myComponent", { text: "Click me" }); * // Later... * await instance.unmount(); */ public mount(container: HTMLElement, compName: string | ComponentDefinition, props?: ComponentProps): Promise; /** * Processes DOM elements for event binding based on attributes starting with "@". * This method attaches event listeners directly to elements and ensures proper cleanup. * Bound `@event` attributes are removed after listeners are attached. * * Handler resolution order: * 1. Direct context property lookup (e.g., context["handleClick"]) * 2. Template expression evaluation via TemplateEngine (e.g., "increment()") * * @private * @param {HTMLElement} container - The container element in which to search for event attributes. * @param {ComponentContext & SetupResult} context - The merged component context and setup data. * @param {UnsubscribeFunction[]} listeners - Array to collect cleanup functions for each event listener. * @returns {void} * @see TemplateEngine.evaluate - Expression evaluation. fallback. */ private _processEvents; /** * Injects styles into the component's container. * Styles are placed in a `