/** * Shared, pluggable primitive for `model` directive modifiers. * * Modifiers come in two flavours: * - **value transforms** (`trim`, `number`, custom) — pure value→value functions * applied at the write-back boundary in the JSX runtime, so every binding path * (default `model`, named `model:name`, generic fallback, custom processors, * components) and every platform (DOM, Lynx, SSR) honors them with no extra code. * - **timing** (`lazy`, `debounce`) — a declarative hint platforms map to their own * event model (DOM `input`→`change`; Lynx `bindinput`/blur). Debounce scheduling * is platform-agnostic ({@link createDebounceScheduler}) but the platform owns the * event wiring and cleanup, since the cancel handle is tied to its listener lifecycle. * * This module is dependency-free (mirrors `platform.ts`) so it can be imported from * any layer without circular-init concerns. */ export type ModelModifierTiming = 'lazy' | 'debounce'; export interface ModelModifierContext { /** The raw modifier value, e.g. `true`, `300`, or a custom config object. */ option: unknown; /** The full modifiers object, so a transform can read sibling flags. */ modifiers: Record; } export interface ModelModifierDef { /** * Platform-agnostic value transform applied at write-back. Receives the raw * value the platform extracted and returns the value to write to the binding. */ transform?: (value: any, ctx: ModelModifierContext) => any; /** * Declarative timing hint platforms map to their own event model. Modifiers * with a `timing` (and no `transform`) are skipped by {@link applyModelTransforms}. */ timing?: ModelModifierTiming; } /** * Register a `model` modifier (public API), symmetric with `registerModelProcessor`. * * A modifier is either a value transform, a timing hint, or both. Built-ins * (`trim`, `number`, `lazy`, `debounce`) are registered through this same * mechanism. To make a custom modifier type-check in JSX, also augment the * matching capability group ({@link ValueModelModifiers} for transforms, * {@link TimingModelModifiers} for timing) via declaration merging. * * @returns An unregister function that removes this modifier. * * @example * ```ts * declare module '@sigx/runtime-core' { * interface ValueModelModifiers { uppercase?: boolean } * } * registerModelModifier('uppercase', { * transform: (v) => typeof v === 'string' ? v.toUpperCase() : v, * }); * ``` */ export declare function registerModelModifier(name: string, def: ModelModifierDef): () => void; /** Look up a registered modifier definition (internal). */ export declare function getModelModifier(name: string): ModelModifierDef | undefined; /** * Apply every active value-transform in `modifiers` to `value`, in authoring * order (the key order of the `modelModifiers` object literal). Falsy/nullish * modifier options and timing-only modifiers are skipped. */ export declare function applyModelTransforms(value: any, modifiers: Record | undefined): any; /** Resolved, platform-neutral timing contract derived from a modifiers object. */ export interface ResolvedTiming { /** Sync on the platform's lazy event (DOM `change`) instead of the eager one. */ lazy: boolean; /** Trailing-edge debounce in ms, or `null` when no debounce is requested. */ debounceMs: number | null; } /** * Derive the timing contract from a modifiers object by consulting each * registered modifier's `timing` hint — so platforms need no hardcoded knowledge * of which modifier names imply which timing. `debounce: true` ⇒ 300ms. */ export declare function resolveTiming(modifiers: Record | undefined): ResolvedTiming; /** * Wrap a model write-back handler so registered value-transforms run before the * real write, on every path and platform. The wrapper also carries the modifiers * object (under {@link MODIFIERS_TAG}) so the platform can read the timing hints. * Returns the handler unchanged when there are no modifiers. */ export declare function wrapModelWriteBack(handler: (v: any) => void, modifiers: Record | undefined): (v: any) => void; /** Read the modifiers tag off a write-back handler (internal, platform-side). */ export declare function getHandlerModifiers(handler: unknown): Record | undefined; /** Trailing-edge debounce scheduler. The platform owns wiring + cleanup. */ export interface DebounceScheduler { /** Schedule (or re-schedule) a trailing-edge call with the latest value. */ invoke: (value: any) => void; /** Cancel any pending call (e.g. on handler replacement / unmount). */ cancel: () => void; } /** * Create a trailing-edge debounce scheduler. Scheduling is platform-agnostic * (`setTimeout`); platforms call this and own the cancel wiring so a pending * write can't fire after the handler is torn down. */ export declare function createDebounceScheduler(fn: (v: any) => void, ms: number): DebounceScheduler; /** * Value-transform modifiers. Meaningful only where the bound value is (or can be) * a string — text/number/range/textarea/select. Augment to add custom transforms. */ export interface ValueModelModifiers { /** Strip leading/trailing whitespace before write-back. */ trim?: boolean; /** Coerce the value to a number (no-op if not numeric). */ number?: boolean; } /** * Timing modifiers — change *when* write-back fires. Meaningful on every * model-bound element. Augment to add custom timing modifiers. */ export interface TimingModelModifiers { /** Sync on `change` (blur/enter) instead of every keystroke. */ lazy?: boolean; /** Delay write-back by N ms (`true` ⇒ 300ms). */ debounce?: number | boolean; } /** * Full modifier set for value-bearing form elements * (text/number/range/textarea/select) — value transforms + timing. */ export interface ModelModifiers extends ValueModelModifiers, TimingModelModifiers { } /** * Modifier set for toggle elements (checkbox/radio) — timing only. * `trim`/`number` are intentionally absent (no-ops for boolean/array values). */ export interface ToggleModelModifiers extends TimingModelModifiers { } //# sourceMappingURL=model-modifiers.d.ts.map