UNPKG

ripple

Version:

Ripple is an elegant TypeScript UI framework

186 lines (167 loc) • 5.38 kB
import type { Tracked, Derived } from './runtime.js'; import type { Context } from './context.js'; export { Tracked, Derived }; /** One `Context.set` in a component: the context, its value, the parent's chain. */ export type ContextEntry = { k: Context<any>; v: any; n: null | ContextEntry; }; export type Component = { b: null | Block; // The chain of context values set by this component and its ancestors. c: null | ContextEntry; // Effects deferred until the component has rendered, as flat triples: // fn, the block to create it under, the reaction active at the call. e: null | Array<Function | Block | Derived | null>; p: null | Component; m: boolean; }; export type Dependency = { // clock of the tracked value when it was read c: number; t: Tracked | Derived; // next dependency of the same reaction n: null | Dependency; // the reaction that read the tracked value r: Block | Derived; // previous / next subscriber of the same tracked value sp: null | Dependency; sn: null | Dependency; }; export type DeferredTrackedEntry = { b: Block; // boundary block r: number; // request version id }; /** * Anchor sentinel for one-shot appends at the end of `parent` (all-component * children, portal content). Only valid at first render: a block that keeps * inserting relative to its anchor must go through `resolve_anchor` first. */ export type AppendIntoAnchor = { parent: Node; /** Marks the sentinel; a missing-property read on a DOM node is cheaper than `in`. */ into: true; /** * The sentinel is the tail of `parent`: nothing the framework renders * follows what it anchors, so an `@if` there never needs an anchor node. */ tail: boolean; }; export type Block = { co: null | Component; d: null | Dependency; first: null | Block; f: number; fn: any; // creation id; flushes run scheduled blocks in this order i: number; last: null | Block; next: null | Block; p: null | Block; prev: null | Block; s: any; // teardown function /** teardown; runtime-internal teardowns receive the block state */ t: ((state?: any) => void) | null; }; export type TryCatchFunction = (anchor: Node, error: any, reset?: () => void) => void; export type TryPendingFunction = (anchor: Node) => void; /** * The state of a `@try` block or root boundary. Everything the boundary's * pending, catch, request and streaming helpers need lives here, so those * helpers are module functions that compile only when a boundary uses them. */ export type TryState = { /** anchor */ a: Node; /** try function */ fn: (anchor: Node, block?: Block) => void; /** catch function */ c: TryCatchFunction | null; /** pending function */ p: TryPendingFunction | null; /** the catch branch's `reset` callback, created on first use */ r: (() => void) | null; /** pending request count */ n: number; /** request version */ v: number; /** active request ids */ q: Set<number>; /** the try block */ b: Block | null; /** resolved branch */ rb: Block | null; /** pending branch */ pb: Block | null; /** catch branch */ cb: Block | null; /** fragment holding the resolved branch while it is offscreen */ o: DocumentFragment | null; /** has resolved */ h: boolean; /** mode: 0 resolved, 1 pending, 2 catch */ m: 0 | 1 | 2; /** deferred rejections by request id */ d: Map<number, (reason: any) => void>; /** blocks paused on a pending read */ z: Set<Block>; /** a streamed slot this boundary hydrated, until its chunk activates it */ si: string | null; /** the streamed slot errored */ se: boolean; /** the streamed slot carries a hydratable fallback */ sf: boolean; /** streamed slot open marker */ so: Comment | null; /** streamed slot close marker */ sc: Comment | null; }; export type BlockWithTryBoundary = Omit<Block, 's'> & { s: TryState; }; export type BlockWithTryBoundaryAndCatch = Omit<BlockWithTryBoundary, 's'> & { s: TryState & { c: TryCatchFunction }; }; export type RootBoundaryOptions = { pending?: (anchor: Node, props: Record<string, never>, block: Block | null) => void; catch?: (anchor: Node, props: { error: unknown; reset: () => void }, block: Block | null) => void; }; /** * Called by the inline stream runtime when a boundary's chunk arrives after * hydration: receives the chunk template (null for error-only chunks) and a * truthy flag when the unit errored server-side. */ export type StreamBoundaryActivator = ( template: HTMLTemplateElement | null, errored?: number, ) => void; /** * `window.__RIPPLE_B__` — shared between the inline stream runtime and * hydrated try boundaries. A boundary that hydrated a still-pending slot * registers `{ a: activator }` under its unit id; the runtime stores `1` * once the slot has been swapped or handed over. */ export type StreamBoundaryRegistry = Record<string | number, 1 | { a: StreamBoundaryActivator }>; declare global { interface Window { /** streamed-boundary registry, see {@link StreamBoundaryRegistry} */ __RIPPLE_B__?: StreamBoundaryRegistry; /** inline stream runtime entry point: swaps unit `id`'s chunk into its slot */ __RIPPLE_S__?: (id: number, errored?: number) => void; } interface Element { __attributes?: { value?: string; }; __click?: () => void; __ripple_block?: Block; } interface Event { __root?: EventTarget; } interface HTMLSelectElement { __value?: unknown; } }