UNPKG

shark-mvc

Version:

Shark-MVC is a small MVC framework born to make our small JS apps easier to build.

1,089 lines (875 loc) 43.5 kB
// ── Shared interfaces ───────────────────────────────────────────────────────── /** Describes a single auto-listener entry stored in `__listeners`. */ interface JawListenerEntry { context?: string; handler: ((...args: any[]) => any) | string; originalHandler?: string; target: "DOM" | "controller" | "component"; type: "autoListener"; smartUnlisten?: boolean; } /** Describes an item used in array-form calls to `set()`. */ interface JawSetItem { name: string; value: any; } /** Describes an entry in the `__validMethodList` built by `_extractValidMethodList`. */ interface JawValidMethod { handlerName: string; handlerHolder: Jaw; _from?: string; depthList?: number[]; minDepth?: number; targetType?: "singleNode" | "multiNode" | "manager"; } // ── Jaw ─────────────────────────────────────────────────────────────────────── /** * Represents an element of the MVC model (View or Controller). * Extends the native `EventTarget` and adds Shark-specific methods. */ export declare class Jaw extends EventTarget { /** Read-only identifier for this Jaw instance. Set from `jawId` in the constructor. */ readonly className: string; /** Read-only identifier alias for `className`. */ readonly id: string; /** If the routing method is set to "url", this will be the URL displayed when this Jaw is active. */ route: string | null; /** Whether this Jaw has been initialized via `init()`. */ isInitialized: boolean; /** The options object used in the most recent open operation. */ currentOpenOptions: Record<string, any>; /** * Reactive proxy over `__store`. Properties defined here trigger auto-render when changed, * if `Shark.settings.enableJawStoreAutoRender` is enabled. */ $store: Record<string, any>; /** @internal Internal data store, backing `$store` and `get()`/`set()`. */ protected __store: Record<string, any>; /** @internal Fields registered for auto-rendering. */ protected __renderFields: string[]; /** @internal Map of child templates used during rendering. */ protected __childTemplates: Record<string, string>; /** @internal Map of dynamic data bindings. */ protected __dynamicDataBindings: Record<string, any>; /** @internal Registry of event listeners assigned by `smartListen`. */ protected __listeners: Record<string, Record<string, JawListenerEntry>>; /** @internal Named methods added to this Jaw. */ protected __methods: Record<string, any>; /** @internal View binding map (primary). */ protected __viewBinds: Record<string, any>; /** @internal View binding map (secondary). */ protected __viewBinds2: Record<string, any>; /** @internal Hash map used for change-detection in `__singleSet`. */ protected __storeHashMap: Map<string, any>; /** @internal List of valid handler methods built by `smartListen`. */ protected __validMethodList?: JawValidMethod[]; /** * The list of Components currently active on this Jaw. * Use `addComponent` / `addComponents` to modify it. */ readonly activeComponents: Jaw[]; /** * Creates a new Jaw instance. * * @param jawId - The identifier for this Jaw. If omitted/null/empty, defaults to the * camelCase class name of the subclass. */ constructor(jawId?: string | null); /** * @deprecated Pass only `jawId`. The `jawDefinition` parameter will be removed in a future version. */ // eslint-disable-next-line @typescript-eslint/unified-signatures constructor(jawId: string | null | undefined, jawDefinition: object); /** * Add a single Component to this Jaw. * * @param component - The Component to add, either as a string ID or a Component instance. * Passing a string is deprecated; prefer passing the instance directly. * @returns The updated list of active Components. */ addComponent(component: Jaw | string): Jaw[]; /** * Add multiple Components to this Jaw. * * @param components - Array of Components to add (as instances or string IDs). * @returns The updated list of active Components. */ addComponents(components: (Jaw | string)[]): Jaw[]; /** * Retrieve one or more values from the internal data store. * * - Called with no arguments: returns the entire `__store` object. * - Called with a string: returns the value for that key. * - Called with an array of strings: returns an array of the corresponding values. */ get(itemName?: string): any; get(itemName: string[]): any[]; get(itemName: undefined): Record<string, any>; /** Called when this Jaw is used for the first time. Override in subclasses. */ init(): void; /** @internal Extracts valid `*Handler` methods from a list of Components, recursively. */ protected _extractComponentsValidMethodList( componentList: Jaw[], processedComponentList: string[], notProcessedComponentList: string[] ): JawValidMethod[]; /** @internal Extracts valid `*Handler` methods from a target object and its prototype chain. */ protected _extractValidMethodList(targetObject: Jaw): JawValidMethod[]; /** @internal Initialises Components, calling `init()` on each if not already done. */ protected _initComponents(componentList?: Jaw[] | null, processedComponentList?: string[]): void; /** * Store one or more values in this Jaw's internal data store. * * Accepted signatures: * - `set(name, value)` — set a single named value. * - `set(object)` — merge all properties of the object into the store. * - `set(array)` — each item must have `{ name, value }`. */ set(nameOrItems: string, value: any): void; set(nameOrItems: Record<string, any>): void; set(nameOrItems: JawSetItem[]): void; /** @internal Sets a single key on the store, returns `1` if the value changed, `0` otherwise. */ protected __singleSet(name: string, value: any): 0 | 1; /** * Dispatch an event from this Jaw using jQuery's `.trigger()`. */ emit(eventName: string, eventData?: any): JQuery; /** * Remove an event listener from this Jaw using jQuery's `.off()`. */ off(eventName: string, eventHandler: (...args: any[]) => any): JQuery; /** * Attach an event listener to this Jaw using jQuery's `.on()`. */ on(eventName: string, eventHandler: (...args: any[]) => any): JQuery; /** * Automatically match and assign DOM and controller event listeners based on the * naming convention `selectorOrControllerName_eventNameHandler`. * * @param container - Optional CSS selector or jQuery selection to restrict parsing scope. * @param selfAssign - If `true`, the container itself is treated as a potential target. */ smartListen(container?: string | JQuery, selfAssign?: boolean): void; /** @internal Experimental v2 of `smartListen`. Work in progress. */ smartListen2(container?: string | JQuery, selfAssign?: boolean): void; /** * Remove all listeners previously assigned by `smartListen`. */ smartUnlisten(container?: string | JQuery): void; /** * Internal tracing utility. Delegates to `Shark.trace` if available. */ trace(message: any, level?: string): void; /** * Update the DOM portions bound to a reference model path after the model's dirty * properties have changed. */ updateDisplay(referenceModelPath: string, sourceEvent?: Event): void; /** @internal Utility to compute the DOM depth of a node (distance from BODY). */ static __getNodeDepth(node: Node): number; } // ── RenderableJaw ───────────────────────────────────────────────────────────── /** * The base class for View, Component and PageTemplate. * Adds template management on top of Jaw. */ export declare class RenderableJaw extends Jaw { /** The default template HTML string (raw, before parsing). */ templateContent: string; /** The type identifier for this class (overridden in subclasses). */ type: string; /** * Map of all registered templates, keyed by name. * Each entry contains `{ content, parsed, parsedContent }`. * The default template is stored under the key `"__default__"`. * Non-configurable, non-writable (contents are mutable). */ readonly templates: Record<string, { content: string; parsed: boolean; parsedContent: string }>; /** * Map of fully-expanded template strings keyed by name. * Populated by `parseTemplates()`. The default template is under `"__default__"`. * Non-configurable, non-writable (contents are mutable). */ readonly parsedTemplates: Record<string, string>; /** * The parsed default template string. * @deprecated Prefer `parsedTemplates["__default__"]`. Kept for back-compat. */ protected __parsedTemplate: string | null; /** * `true` after `parseTemplates()` has run at least once. * @deprecated Prefer checking `templates["__default__"].parsed`. Kept for back-compat. */ protected __templatesParsed?: boolean; /** @internal Raw template map (legacy). Prefer `templates`. */ protected __templates: Record<string, string>; constructor(jawId?: string | null); /** @deprecated Pass only `jawId`. */ // eslint-disable-next-line @typescript-eslint/unified-signatures constructor(jawId: string | null | undefined, jawDefinition: object); /** * Add a named HTML template to this RenderableJaw. * * @param name - Template name. Pass `""` to register the default template (only one allowed). * Cannot be `"__default__"` (reserved) or contain spaces. * @param content - The raw HTML template string. * @returns The internal `__templates` map, or `undefined` if validation failed. */ addTemplate(name: string, content: string): Record<string, string> | undefined; /** * Set the default HTML template for this RenderableJaw. * Shorthand for `addTemplate("", content)`. * * @param content - The raw HTML template string. * @returns The internal `__templates` map, or `undefined` if validation failed. */ setTemplate(content: string): Record<string, string> | undefined; /** * Parse all registered templates, resolving: * - PageTemplate slot injection (`<shark-template>`) * - Component expansion (`<shark-xxx-component>`) including named slot content and `prop-*` attributes * - `data-sh-model` → `data-shguid` binding * - Auto-bound Shark `$store` properties * - `SmartModel` bound properties * - Dynamic sections (`<shark-section>`) * - `[[renderer.field]]` syntax * * This is a no-op if the template has already been parsed, unless `force: true` is passed. * Called automatically by `PreactRenderer.render()` before the first render. * * @param options.force - If `true`, re-parses even already-parsed templates. Default: `false`. * @param options.sharkModelGuidPlaceHolder - Placeholder for `data-shguid` on Mustache model refs. * Default: `"__ph__"`. */ parseTemplates(options?: { force?: boolean; sharkModelGuidPlaceHolder?: string }): void; } // ── Component ───────────────────────────────────────────────────────────────── /** * The class to extend to create Component classes. * Components are sub-parts of a View, used to render and manage portions of the UI. * * A Component's HTML template can contain: * - `<slot name="..."></slot>` placeholders, filled at the call site with named slot content. * - `{{props.xxx}}` placeholders, filled with values passed via `prop-xxx="value"` attributes. */ export declare class Component extends RenderableJaw { /** Always `"component"` — non-writable and non-configurable. */ readonly type: "component"; /** * Props passed to this component instance at the call site via `prop-*` attributes. * Populated at render time; writable. */ props: Record<string, any>; /** * Creates a new Component instance and automatically registers it with Shark * via `Shark.registerComponent()`. * * @param jawId - Identifier for this Component. Defaults to the camelCase subclass name. */ constructor(jawId?: string | null); /** @deprecated Pass only `jawId`. */ // eslint-disable-next-line @typescript-eslint/unified-signatures constructor(jawId: string | null | undefined, jawDefinition: object); } // ── Controller ──────────────────────────────────────────────────────────────── /** * The class to extend to create Controller classes. * Controllers manage application logic and are registered globally with Shark. */ export declare class Controller extends Jaw { /** Always `"controller"` — non-writable and non-configurable. */ readonly type: "controller"; /** * Creates a new Controller instance and automatically registers it with Shark * via `Shark.registerController()`. * * @param jawId - Identifier for this Controller. Defaults to the camelCase subclass name. */ constructor(jawId?: string | null); /** @deprecated Pass only `jawId`. */ // eslint-disable-next-line @typescript-eslint/unified-signatures constructor(jawId: string | null | undefined, jawDefinition: object); } // ── PageTemplate ────────────────────────────────────────────────────────────── /** * The class to extend to create PageTemplate classes. * A PageTemplate defines a shared layout shell with named `<slot>` placeholders * that individual Views fill via `<shark-template name="..." slot="...">`. * * PageTemplates can only have a single template — use `setTemplate()`. * Calling `addTemplate()` throws an error. */ export declare class PageTemplate extends RenderableJaw { /** Always `"pageTemplate"` — non-writable and non-configurable. */ readonly type: "pageTemplate"; /** * Creates a new PageTemplate instance. * * @param jawId - Identifier for this PageTemplate. Defaults to the camelCase subclass name. */ constructor(jawId?: string | null); /** * @throws Always throws — PageTemplates support only one template. Use `setTemplate()`. */ addTemplate(name: string, content: string): never; } // ── View ────────────────────────────────────────────────────────────────────── /** * The class to extend to create View classes. * A View can only have a single template; use `setTemplate()` to set it. */ export declare class View extends RenderableJaw { /** * The main route associated with this View. * @deprecated In favour of the SwimWay router. */ $mainRoute: string; /** Always `"view"` — non-writable and non-configurable. */ readonly type: "view"; /** * Creates a new View instance and automatically registers it with Shark * via `Shark.registerView()`. * * @param jawId - Identifier for this View. Defaults to the camelCase subclass name. */ constructor(jawId?: string | null); /** @deprecated Pass only `jawId`. */ // eslint-disable-next-line @typescript-eslint/unified-signatures constructor(jawId: string | null | undefined, jawDefinition: object); /** * @deprecated Views support only one template. Use `setTemplate()` instead. * This override ignores `name` and delegates to `setTemplate(content)`. */ addTemplate(name: string, content: string): Record<string, string> | undefined; } // ── Shark ───────────────────────────────────────────────────────────────────── export declare class Shark { // ── Trace level constants ───────────────────────────────────────────────── static readonly TRACE_ERROR: "error"; static readonly TRACE_INFO: "info"; static readonly TRACE_LOG: "log"; static readonly TRACE_NONE: "none"; static readonly TRACE_WARN: "warn"; // ── Global state ────────────────────────────────────────────────────────── /** The global reactive store. Properties set here trigger auto-render on bound Views. */ static $store: Record<string, any>; /** The currently active page descriptor. */ static activePage: { id: string }; /** Application-level data bag. */ static appData: Record<string, any>; /** Whether Shark has been initialized. */ static isInitialized: boolean; /** Localization labels. */ static labels: Record<string, any>; /** Registered page templates, keyed by name. */ static pageTemplates: Record<string, PageTemplate>; /** Current trace level. Defaults to `TRACE_WARN`. */ static traceLevel: string; // ── Lifecycle ───────────────────────────────────────────────────────────── /** * Initialize the Shark framework. * * @param options.labels - Localization labels object. * @param options.remoteCallTimeOutDuration - Milliseconds before aborting remote calls. Default: 15000. * @param options.templateRenderer - Renderer class to use. Default: PreactRenderer. * @param forceReinit - If `true`, re-runs initialization even if already done. */ static init(options?: { labels?: Record<string, any>; remoteCallTimeOutDuration?: number; templateRenderer?: any; [key: string]: any; }, forceReinit?: boolean): void; /** * Render the given jaw (or the current active View if omitted) to the screen. * * @param currJaw - The jaw to render. Defaults to the currently active View. * @param options - Renderer options passed through to the active renderer. */ static render(currJaw?: Jaw, options?: Record<string, any>): boolean; // ── Navigation ──────────────────────────────────────────────────────────── /** * Open a registered View, making it the active page. * * @param pageIdOrJaw - The View instance or its string ID. * @param options - Options forwarded to the View's `open()` / `init()` lifecycle. */ static openPage(pageIdOrJaw: string | Jaw, options?: Record<string, any>): boolean; /** * Activate a View without necessarily making it the visible page. */ static activateView(pageIdOrJaw: string | Jaw, options?: Record<string, any>): boolean; /** * Navigate to a SwimWay route. * * @param route - The route string or SwimRoute object to navigate to. */ static navigateToRoute(route: string | object): void; /** * Parse the current `document.location` and return the View name encoded in the URL. */ static parseRequestedViewFromLocation(): string; // ── Store ───────────────────────────────────────────────────────────────── /** * Retrieve one or more values from the global store. * * @param itemName - Key name or array of key names. If omitted, returns the whole store. */ static get(itemName?: string | string[]): any; /** * Set one or more values on the global store. * Triggers auto-render on any Views that reference the changed keys. * * @param nameOrObject - Property name or plain object of `{ name: value }` pairs. * @param value - Value to assign (only used when `nameOrObject` is a string). */ static set(nameOrObject: string | Record<string, any>, value?: any): any; // ── Registration ────────────────────────────────────────────────────────── /** Register a Component so it can be referenced in templates. */ static registerComponent(jaw: Component): void; /** Register a Controller. */ static registerController(jaw: Controller): void; /** Register a SmartModel class under a given name. */ static registerModel(modelName: string, modelClass: typeof SmartModel): void; /** * Register a PageTemplate so Views can reference it by name. * Call this after `setTemplate()` on the PageTemplate instance. */ static registerPageTemplate(jaw: PageTemplate): void; /** Register a View so it can be opened via `openPage()`. */ static registerView(jaw: View): void; // ── Model helpers ───────────────────────────────────────────────────────── /** * Return a new instance of a registered model, optionally pre-populated with data. * * @param modelNameOrClass - The registered name string or the model class itself. * @param modelData - Data to import into the new instance. */ static getInstance(modelNameOrClass: string | typeof SmartModel, modelData?: object): SmartModel; /** * Return an array of model instances from an array of data objects. */ static getInstanceList(modelNameOrClass: string | typeof SmartModel, dataList: object[]): SmartModel[]; /** * Retrieve a model instance registered in the global store by dot-notation path. */ static getReferenceModel(path: string): SmartModel | null; // ── Utilities ───────────────────────────────────────────────────────────── /** Bind a View's DOM to a model using a CSS container selector. */ static bindViewToModel(containerCSSSelector: string): void; /** Generate a UUID string. */ static generateUUID(): string; /** * Emit a trace message if the current `traceLevel` allows it. * * @param args - Message and optional data to log. */ static trace(...args: any[]): void; /** * Return `true` if the given level should be emitted at the current `traceLevel`. */ static shouldTrace(level: string): boolean; /** * Update URLSearchParams in the current URL without adding a browser history entry. */ static setURLSearchParams( urlSearchParams: string | object | URLSearchParams | null, replace?: boolean, routeObject?: object | null ): void; } // ── SmartModel ──────────────────────────────────────────────────────────────── /** Describes a single dirty property entry, tracking the original value before changes. */ interface SmartModelDirtyEntry { property: string; originalValue: any; } /** Options accepted by `export` / `__export`. */ interface SmartModelExportOptions { /** If provided, only the listed property names will be exported. */ fields?: string[]; } /** Options accepted by `import` / `__import`. */ interface SmartModelImportOptions { /** If `true`, properties present in `data` but missing on the model will be created. Default: `false`. */ createMissingProperties?: boolean; /** If `true`, empty/falsy values in `data` are still matched onto the model. Default: `true`. */ matchEmptyParameters?: boolean; } /** Options accepted by `importField`. */ interface SmartModelImportFieldOptions { /** If `true`, `undefined`/`null` values are accepted. Default: `false`. */ acceptUndefined?: boolean; /** Explicit data type for parsing. `"auto"` performs no conversion. Default: `"auto"`. */ dataType?: "auto" | "boolean" | "date" | "number"; /** Fallback value used when the parsed number is `NaN`. Only for `dataType: "number"`. */ defaultValue?: number; /** If `true`, date values are parsed as UTC and converted to local time. Default: `true`. */ useUTC?: boolean; } /** * Payload dispatched with `"dataChanged"` and `"<propertyName>Changed"` events. */ interface SmartModelChangeEventData { /** The `__modelName` of the model that triggered the change. */ modelName: string | undefined; /** The new value assigned to the property. */ newValue: any; /** The previous value of the property before the change. */ oldValue: any; /** The dot-notation path of the property that changed. */ property: string; } /** Describes an item used in array-form calls to `set()`. */ interface SmartModelSetItem { name: string; value: any; } /** Inferred type map produced by `SmartModel.inferTypes()`. */ type SmartModelInferredTypes = Record< string, "number" | "datetime" | "boolean" | "string" | "array" | "object" | typeof SmartModel >; /** * Base class for reactive data models in the Shark MVC framework. * * @fires SmartModel#dataChanged - Emitted whenever a property changes value. * @fires SmartModel#<propertyName>Changed - Emitted for the specific changed property. */ export declare class SmartModel extends EventTarget { /** Unique GUID assigned at construction. Non-writable. */ readonly __shguid: string; /** Whether any property of this model differs from its original/cleaned value. */ __isDirty: boolean; /** List of properties that have changed since the last `clean()` call. */ __dirtyProperties: SmartModelDirtyEntry[]; /** Auto-bound property names (reserved for internal use). */ __boundProperties: string[]; /** The model structure passed at construction, if any. */ __modelStructure: object | undefined; /** The renderer instance associated with this model. */ __renderer: any; // ── Private methods exposed via Object.defineProperty ──────────────────── protected readonly __clean: (properties?: string[]) => SmartModelDirtyEntry[]; protected readonly __export: (options?: SmartModelExportOptions) => Record<string, any>; protected readonly __import: (data: object, options?: SmartModelImportOptions) => any; protected readonly __restore: (properties?: string[]) => void; protected readonly __setPropertyDirtiness: (propertyName: string, eventData: SmartModelChangeEventData) => void; protected readonly __toJSON: (forcePrivateProperties?: boolean) => Record<string, any>; /** * Creates a new SmartModel instance. */ constructor(data?: object, options?: SmartModelImportOptions, modelStructure?: object); /** Replace the renderer instance for this model. */ addRenderer(renderer: { setModel(model: SmartModel): void; [key: string]: any }): void; /** * Clear dirty state for all properties, or only the specified ones. * After this call, `__isDirty` is recomputed. */ clean(properties?: string[]): SmartModelDirtyEntry[]; /** Dispatch an event from this model using jQuery's `.trigger()`. */ emit(eventName: string, eventData?: any): JQuery; /** * Export all public, non-function properties to a plain object. * Properties starting with `__` or `jQuery` are excluded by default. */ export(options?: SmartModelExportOptions): Record<string, any>; /** Export an array field as a JSON string. */ exportArrayField(field: string, format?: "JSON"): string; protected __exportArrayField(field: string, format?: "JSON"): string; /** Export a boolean field as a number (`1`/`0`) or string. */ exportBooleanField(field: string, format?: "number" | "string"): number | string; protected __exportBooleanField(field: string, format?: "number" | "string"): number | string; /** Export a date field as a formatted string. */ exportDateField(field: string, format?: "ISO", useUTC?: boolean): string; protected __exportDateField(field: string, format?: "ISO", useUTC?: boolean): string; /** Export a numeric field, parsing strings to floats if necessary. */ exportNumberField(field: string, format?: string): number; protected __exportNumberField(field: string, format?: string): number; /** Export an object field as a JSON string. */ exportObjectField(field: string, format?: "JSON"): string; protected __exportObjectField(field: string, format?: "JSON"): string; /** Import data from a plain object onto this model. */ import(data: object, options?: SmartModelImportOptions): any; /** Import and parse a boolean field from a raw value. */ importBooleanField(field: string, value: any): boolean; protected __importBooleanField(field: string, value: any): boolean; /** Import and parse a date field, converting it to a `moment` object. */ importDateField(field: string, value: any, useUTC?: boolean): boolean; protected __importDateField(field: string, value: any, useUTC?: boolean): boolean; /** Import and parse a numeric field. */ importNumberField(field: string, value: any, defaultValue?: number): boolean; protected __importNumberField(field: string, value: any, defaultValue?: number): boolean; /** * Unified import helper that dispatches to the correct typed import method * based on `options.dataType`. */ importField(field: string, value: any, options?: SmartModelImportFieldOptions): boolean; /** * Inspect an instance of this model class and infer the data type of each public property. * Results are also logged to the console. */ static inferTypes(): void; /** * Check whether this model has unsaved/unclean property changes. * * @param properties - A single property path or array of paths to check. * @param mode - `"any"` (default): `true` if at least one listed property is dirty. * `"all"`: `true` only if every listed property is dirty. */ isDirty(properties?: string | string[] | null, mode?: "any" | "all"): boolean; /** Remove an event listener from this model using jQuery's `.off()`. */ off(eventName: string, eventHandler: (...args: any[]) => any): JQuery; /** Attach an event listener to this model using jQuery's `.on()`. */ on(eventName: string, eventHandler: (...args: any[]) => any): JQuery; /** * Register a property as bound to a View (called internally by `parseTemplates`). * * @param propertyName - The model property name. * @param viewClassName - The `className` of the View that references this property. */ registerBoundProperty(propertyName: string, viewClassName: string): void; /** Restore dirty properties to their original values and reset `__isDirty` to `false`. */ restore(properties?: string[]): void; protected __singleSet(name: string, value: any, eventTargetNode?: Element | null): void; /** * Assign a value to one or more model properties. * * @fires SmartModel#dataChanged * @fires SmartModel#<propertyName>Changed */ set(name: string, value: any, eventTargetNode?: Element | null): void; set(name: Record<string, any>, eventTargetNode?: Element | null): void; set(name: SmartModelSetItem[], eventTargetNode?: Element | null): void; /** Serialize this model to a plain object. */ toJSON(forcePrivateProperties?: boolean): Record<string, any>; /** Serialize this model to a JSON string. */ toString(forcePrivateProperties?: boolean): string; /** @experimental Sync a single property value to all matching `[data-sh-prop]` DOM elements. */ updateScreenWithModelValue( name: string, eventTargetNode: Element | null, value: any, eventData: SmartModelChangeEventData | string ): void; /** @experimental @dangerous Sync all public properties to their matching `[data-sh-prop]` DOM elements. */ updateScreenWithModelValues(): void; // ── Optional lifecycle hooks ────────────────────────────────────────────── /** Called whenever any property changes. Override in subclasses. */ onDataChanged?(eventData: SmartModelChangeEventData): void; /** Catch-all index signature for dynamic `on<PropertyName>Changed` hooks. */ [key: string]: any; } // ── SwimRoute ───────────────────────────────────────────────────────────────── /** Data object accepted by the `SwimRoute` constructor. */ interface SwimRouteData { /** Route pattern, optionally containing dynamic segments prefixed with `:` (e.g. `"/user/:id"`). */ route: string; /** The View instance or class associated with this route. */ view: any; } /** * Represents a single application route, pairing a URL pattern with a View * and providing dynamic parameter extraction. */ export declare class SwimRoute { /** The route pattern string (e.g. `"/user/:id/orders/:orderId"`). */ route: string; /** The View instance or class associated with this route. */ view: any; /** The last matched URL for this route. */ url: string; /** The dynamic parameters extracted from the last successful `extractRouteParams()` call. */ readonly params: Record<string, string>; constructor(data: SwimRouteData); /** * Match a URL against this route's pattern and extract any dynamic segment values. * * @param url - The URL to match. Falls back to `this.url` if omitted. */ extractRouteParams(url?: string): void; } // ── SwimWay ─────────────────────────────────────────────────────────────────── /** * The basic router for the Shark environment. * Fun Fact: "SwimWay" is the name given by researchers to a route followed in the ocean by sharks. */ export declare class SwimWay { /** * @param routingMethod - One of `"pathname"` (default), `"url"`, or `"hash"`. */ constructor(routingMethod?: "pathname" | "url" | "hash"); /** Add a single route. Returns the new total number of routes. */ addRoute(route: SwimRouteData | SwimRoute): number; /** Add multiple routes at once. Returns the new total number of routes. */ addRoutes(routes: (SwimRouteData | SwimRoute)[]): number; /** Navigate to a route by path string or SwimRoute object. */ navigateTo(route: string | SwimRoute): boolean; /** * Parse `document.location` and return the View name encoded in the current URL. */ getRequestedViewFromLocation(): string; /** * Update URLSearchParams in the current URL without creating a new browser history entry. * * @param urlSearchParams - A string, plain object, or `URLSearchParams` instance. */ setURLSearchParams(urlSearchParams: string | object | URLSearchParams): void; } // ── Utils ───────────────────────────────────────────────────────────────────── export declare class Utils { /** * Return a random number between 0 and `max`, or between `minOrMax` and `max`. */ static betterRandom(minOrMax: number, max?: number): number; /** * Return a random integer between 0 and `max`, or between `minOrMax` and `max`. */ static betterRandomInt(minOrMax: number, max?: number): number; /** Convert a camelCase string to dash-separated lowercase words. */ static camelToDashed(inputString: string): string; /** Compute a simple numeric hash of any serializable value. */ static computeHash(value: any): number; /** Convert a dash-separated string to camelCase. */ static dashedToCamel(inputString: string): string; /** Decode common HTML entities in a string. */ static decodeHtmlEntities(str: string): string; /** Deep-merge any number of objects into a new object. */ static deepMerge(...objects: object[]): object; /** Return an object containing only the properties that differ between two objects. */ static extractDifferentProperties(firstObject: object, secondObject: object): object; /** * Parse the attributes of an HTML opening tag string into a plain object. * * - `data-*` attributes are grouped under `result.dataset` (camelCase keys, no `data-` prefix). * - `prop-*` attributes are grouped under `result.props` (camelCase keys, no `prop-` prefix). * These are **not** passed to the HTML tag root; they are used exclusively for `{{props.xxx}}` * interpolation in component templates. * - All other attributes are returned as camelCase top-level keys. */ static extractPropertiesFromHTMLString(html: string): { dataset?: Record<string, any>; props?: Record<string, any>; [key: string]: any; }; /** * Extract all top-level `<shark-xxx>` component tags from a template string. * * Unlike a simple regex, this function correctly handles nested shark components * by counting open/close depth, so slot content containing other `<shark-yyy>` tags * is never truncated. * * @param template - The HTML template string to scan. * @returns Array of full tag strings for every top-level shark component found. */ static extractSharkComponentTags(template: string): string[]; /** Flatten a nested object into a single-level object (non-recursive values only). */ static flatten(objectToFlatten: object): object; /** Generate a random GUID string, optionally with a prefix and/or suffix. */ static generateGUID(prefix?: string, suffix?: string): string; /** * Get a property value from an object by dot-notation path. * Returns `undefined` if the path does not exist. */ static getDeepProperty(object: object, path: string): any; /** * Cross-reference `sourceList` items against named lookup lists, attaching matched * objects as `__<propertyName>` properties (looks for `*Guid` properties as foreign keys). */ static matchLists(sourceList: any[], matchLists: Record<string, any[]>, options?: object): void; /** * Copy properties from `sourceObject` onto `targetObject`, with configurable rules * for missing properties, empty values, and undefined values. */ static matchProperties( targetObject: object, sourceObject: object, options?: { createMissingProperties?: boolean; matchEmptyParameters?: boolean; matchUndefinedParameters?: boolean; exclude?: string[]; } ): void; /** * Merge the attributes of `includeTag` onto the root element of `templateHtml`. * * - `class` values are concatenated. * - `style` values are merged (includeTag wins on conflicts). * - `data-*` attributes are merged (includeTag wins on conflicts). * - `prop-*` attributes are **excluded** — they are never serialized onto the HTML tag. * - All other attributes are overwritten by `includeTag`. */ static mergeAttributes(templateHtml: string, includeTag: string): string; /** Merge two inline style strings, with `includeStyle` winning on conflicts. */ static mergeStyle(templateStyle: string, includeStyle: string): string; /** Move the caret to the end of an input or textarea element. */ static moveCaretToEnd(target: HTMLInputElement | HTMLTextAreaElement): void; /** Flatten a (possibly nested) object into an array of dot-notation property paths. */ static objectToFlatMap(object: object, parent?: string): string[]; /** Safely parse a JSON string, returning a typed fallback on failure. */ static parseJSON(data: string, expectedType?: "array" | "object" | string): any; /** Add `_index` and `_displayIndex` properties to every item in an array (mutates in place). */ static reindexList(list: any[]): void; /** * Replace `null` or `undefined` values in an object or array with `replaceVal` (mutates in place). */ static replaceNullValue(objectToParse: object | any[], replaceVal: any): void; /** * Resolve named slot content and `prop-*` interpolation for a component template. * * Given the component's own template HTML and the full usage tag from the parent * template, this function: * 1. Extracts `<slot name="...">content</slot>` children from the usage tag and * substitutes them into matching `<slot name="..."></slot>` placeholders in the * component template. * 2. Extracts `prop-*` attributes from the usage tag and substitutes them into * `{{props.xxx}}` placeholders in the component template. * * Unmatched slots and unmatched `{{props.xxx}}` placeholders are left unchanged * as implicit fallback content. * * @param componentTemplate - The component's own HTML template string. * @param componentUsageTag - The full usage tag as written in the parent template, * e.g. `<shark-foo prop-id="bar"><slot name="title">Hi</slot></shark-foo>`. * @returns The resolved template string. */ static resolveComponentSlots(componentTemplate: string, componentUsageTag: string): string; /** JSON-stringify a value, safely handling circular references and `Map` instances. */ static safeStringify(sourceValue: any): string; /** * Shorthand for `searchArrayForProperty`. */ static safp( list: any[], property: string, value: any, searchType?: boolean | "first" | "all" | "index" ): object | any[] | number | null; /** * Search an array for the first object (or all objects) whose named property equals `value`. * * @param searchType - `"first"` (default): return the first match. * `"all"`: return an array of all matches. * `"index"`: return the index of the first match. * Passing `true` is equivalent to `"first"`; `false` to `"all"`. */ static searchArrayForProperty( list: any[], property: string, value: any, searchType?: boolean | "first" | "all" | "index" ): object | any[] | number | null; /** * Assign a value to a property on an object, creating intermediate objects as needed. */ static setDeepProperty(object: object, path: string, value: any, depth?: number): object; /** Legacy trace utility. Prefer `Shark.trace()`. */ static trace(content: string): void; /** Lowercase the first character of a string. */ static toCamelCase(value: string): string; /** Uppercase the first character of a string, optionally lowercasing the rest. */ static toTitleCase(value: string, forceLowerCase?: boolean): string; /** Pad a number with leading zeros to a given total length. */ static zeroFill(number: number, length: number): string; }