streetui 1.6.1 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin.cjs +1 -1
- package/dist/bin.cjs.map +1 -1
- package/dist/bin.js +1 -1
- package/dist/bin.js.map +1 -1
- package/dist/{compile-CEom4hkp.d.cts → compile-CJJQYi71.d.cts} +215 -3
- package/dist/{compile-CEom4hkp.d.ts → compile-CJJQYi71.d.ts} +215 -3
- package/dist/create-bin.cjs +1 -1
- package/dist/create-bin.cjs.map +1 -1
- package/dist/create-bin.js +1 -1
- package/dist/create-bin.js.map +1 -1
- package/dist/{hydration-diagnostics-D286jpZZ.d.cts → hydration-diagnostics-BQeehq3i.d.cts} +1 -1
- package/dist/{hydration-diagnostics-YrG-OJJe.d.ts → hydration-diagnostics-DNAJumYF.d.ts} +1 -1
- package/dist/index.cjs +402 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +94 -7
- package/dist/index.d.ts +94 -7
- package/dist/index.js +390 -3
- package/dist/index.js.map +1 -1
- package/dist/{server-ZBh5DO-z.d.cts → server-CnLEnlYU.d.cts} +17 -1
- package/dist/{server-jbB-7oN-.d.ts → server-DNlg7ou2.d.ts} +17 -1
- package/dist/server.cjs +1025 -865
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +2 -2
- package/dist/server.d.ts +2 -2
- package/dist/server.js +1025 -865
- package/dist/server.js.map +1 -1
- package/dist/testing.cjs +1273 -3
- package/dist/testing.cjs.map +1 -1
- package/dist/testing.d.cts +63 -4
- package/dist/testing.d.ts +63 -4
- package/dist/testing.js +1267 -3
- package/dist/testing.js.map +1 -1
- package/package.json +1 -1
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* (`streetui --version`) — the consolidated test-suite pins all three to the
|
|
8
8
|
* same coordinated release so they can never silently drift apart.
|
|
9
9
|
*/
|
|
10
|
-
declare const VERSION = "1.
|
|
10
|
+
declare const VERSION = "1.8.0";
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* StreetUI reactive signals — framework-owned reactivity, no external libraries.
|
|
@@ -195,7 +195,7 @@ declare function formatDiagnostic(d: Diagnostic): string;
|
|
|
195
195
|
* in the Semantic Application Graph.
|
|
196
196
|
*/
|
|
197
197
|
|
|
198
|
-
type SemanticNodeType = 'application' | 'page' | 'section' | 'container' | 'heading' | 'text' | 'button' | 'input' | 'form' | 'list' | 'list-item' | 'image' | 'link' | 'component' | 'slot' | 'fragment' | 'reactive-list' | 'conditional';
|
|
198
|
+
type SemanticNodeType = 'application' | 'page' | 'section' | 'container' | 'heading' | 'text' | 'button' | 'input' | 'form' | 'list' | 'list-item' | 'image' | 'link' | 'component' | 'slot' | 'fragment' | 'reactive-list' | 'conditional' | 'portal';
|
|
199
199
|
interface NodeMetadata {
|
|
200
200
|
readonly createdAt: number;
|
|
201
201
|
readonly [key: string]: unknown;
|
|
@@ -341,6 +341,111 @@ interface SerializedGraph {
|
|
|
341
341
|
readonly root: SerializedNode;
|
|
342
342
|
}
|
|
343
343
|
|
|
344
|
+
/**
|
|
345
|
+
* First-class StreetUI-native components (§3–§9).
|
|
346
|
+
*
|
|
347
|
+
* A component is a *reusable unit that owns its own local state and lifecycle*.
|
|
348
|
+
* It is NOT a virtual-DOM element, a second reactive system, or a second
|
|
349
|
+
* renderer. It compiles into the EXISTING pipeline: `container.component(...)`
|
|
350
|
+
* creates a reserved `'component'` GraphNode (already a `SemanticNodeType`,
|
|
351
|
+
* already mapped to `<div>`), runs the component's `setup` synchronously during
|
|
352
|
+
* the same build-time descent every other builder uses, and routes any cleanups
|
|
353
|
+
* the setup registers into the node's `NodeInstance` at mount (so they run,
|
|
354
|
+
* children-first, when the component leaves the graph).
|
|
355
|
+
*
|
|
356
|
+
* Design (derived from the existing DSL architecture, not copied from React/Vue):
|
|
357
|
+
*
|
|
358
|
+
* const UserCard = component<{ name: Signal<string> }>((props, ctx) => {
|
|
359
|
+
* // ── setup: runs ONCE per instance, at build time ──
|
|
360
|
+
* const open = signal(false);
|
|
361
|
+
* ctx.effect(() => { ... }); // auto-disposed on unmount
|
|
362
|
+
* ctx.onCleanup(() => { ... }); // explicit teardown hook
|
|
363
|
+
* // ── render: fills the component's own container scope ──
|
|
364
|
+
* return (content) => {
|
|
365
|
+
* content.text(props.name); // fine-grained: signal prop, no re-setup
|
|
366
|
+
* content.when(open, (c) => c.text('expanded'));
|
|
367
|
+
* ctx.renderChildren(content); // where slotted children go (§6)
|
|
368
|
+
* };
|
|
369
|
+
* }, { name: 'UserCard' });
|
|
370
|
+
*
|
|
371
|
+
* page.component('card-1', UserCard, { name }, (slot) => slot.text('child'));
|
|
372
|
+
*
|
|
373
|
+
* Props are ordinary typed values (§5 — typing is a TypeScript concern, no
|
|
374
|
+
* runtime schema). Passing a `Signal<T>` prop and binding it in the render body
|
|
375
|
+
* gives fine-grained updates (§13) WITHOUT re-running `setup`: only the bound
|
|
376
|
+
* node mutates when the signal changes. `setup` re-runs only when the component
|
|
377
|
+
* is genuinely rebuilt (removed + re-created by a keyed list / conditional),
|
|
378
|
+
* at which point the previous instance is disposed first — so cleanup stays
|
|
379
|
+
* correct.
|
|
380
|
+
*/
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* The render half of a component: fills the component's own container scope.
|
|
384
|
+
* Returned by `setup` so that per-instance state created in `setup` is captured
|
|
385
|
+
* by closure and the render body can read it.
|
|
386
|
+
*/
|
|
387
|
+
type ComponentRender = (content: ContainerDSL) => void;
|
|
388
|
+
/**
|
|
389
|
+
* Lifecycle + composition surface handed to a component's `setup`. Local
|
|
390
|
+
* reactive state is created with the EXISTING `signal`/`derived`/`effect`/
|
|
391
|
+
* `batch` primitives (§8) — `ctx` only adds ownership: anything registered here
|
|
392
|
+
* is torn down automatically when the component leaves the graph (§9).
|
|
393
|
+
*/
|
|
394
|
+
interface ComponentContext {
|
|
395
|
+
/** This instance's stable identity key (the `key` passed at the call site). */
|
|
396
|
+
readonly key: string;
|
|
397
|
+
/**
|
|
398
|
+
* Register a teardown callback. Runs when the component unmounts (children
|
|
399
|
+
* first, then this — mirroring `NodeInstance.dispose`). Use for resources,
|
|
400
|
+
* `form.dispose`, subscriptions, timers, etc.
|
|
401
|
+
*/
|
|
402
|
+
onCleanup(fn: () => void): void;
|
|
403
|
+
/**
|
|
404
|
+
* Run a reactive effect owned by this component. Wraps the framework's
|
|
405
|
+
* `effect()`; the returned unsubscribe is auto-tracked and disposed on
|
|
406
|
+
* unmount, so component effects never leak (the pain point the audit ranked
|
|
407
|
+
* #1). The effect may itself return a cleanup, exactly like `effect()`.
|
|
408
|
+
*/
|
|
409
|
+
effect(fn: () => void | (() => void)): void;
|
|
410
|
+
/**
|
|
411
|
+
* Render the caller-supplied children into `content` at this point (§6 native
|
|
412
|
+
* child composition / slots). No-op when the call site passed no children.
|
|
413
|
+
* Call it wherever the component wants its slotted content to appear.
|
|
414
|
+
*/
|
|
415
|
+
renderChildren(content: ContainerDSL): void;
|
|
416
|
+
}
|
|
417
|
+
/**
|
|
418
|
+
* A component's setup function: receives typed props and the lifecycle context,
|
|
419
|
+
* creates any local state, and returns the render function. Runs synchronously
|
|
420
|
+
* during the build-time descent (like `errorBoundary`'s callback), so it must be
|
|
421
|
+
* SSR-safe — on the server it runs at render time and its cleanups run when the
|
|
422
|
+
* SSR root is disposed.
|
|
423
|
+
*/
|
|
424
|
+
type ComponentSetup<P> = (props: P, ctx: ComponentContext) => ComponentRender;
|
|
425
|
+
/**
|
|
426
|
+
* The opaque, reusable definition produced by `component(...)`. Carries the
|
|
427
|
+
* `setup` and an inspectable `name`; the brand lets the builder method (and
|
|
428
|
+
* DevTools) recognise a definition at runtime without a class.
|
|
429
|
+
*/
|
|
430
|
+
interface ComponentDefinition<P> {
|
|
431
|
+
readonly __streetui_component: true;
|
|
432
|
+
readonly name: string;
|
|
433
|
+
readonly setup: ComponentSetup<P>;
|
|
434
|
+
}
|
|
435
|
+
/**
|
|
436
|
+
* Define a reusable component. Returns a `ComponentDefinition` you render with
|
|
437
|
+
* `container.component(key, def, props, children?)`. This is a pure factory — it
|
|
438
|
+
* builds no graph and runs no `setup`; instantiation happens per call site.
|
|
439
|
+
*
|
|
440
|
+
* @param setup Runs once per instance: create local state, return the render fn.
|
|
441
|
+
* @param options.name Human-readable name for DevTools/`data-streetui-component`.
|
|
442
|
+
*/
|
|
443
|
+
declare function component<P = Record<string, never>>(setup: ComponentSetup<P>, options?: {
|
|
444
|
+
name?: string;
|
|
445
|
+
}): ComponentDefinition<P>;
|
|
446
|
+
/** Runtime guard: is `value` a component definition? */
|
|
447
|
+
declare function isComponentDefinition(value: unknown): value is ComponentDefinition<unknown>;
|
|
448
|
+
|
|
344
449
|
/**
|
|
345
450
|
* StreetUI DSL type system.
|
|
346
451
|
* All builder callbacks and option shapes live here.
|
|
@@ -385,6 +490,8 @@ interface A11yOptions {
|
|
|
385
490
|
readonly ariaInvalid?: boolean;
|
|
386
491
|
/** aria-required — mark a form field as required. */
|
|
387
492
|
readonly ariaRequired?: boolean;
|
|
493
|
+
/** aria-modal — mark a dialog as modal (content outside is inert to AT). */
|
|
494
|
+
readonly ariaModal?: boolean;
|
|
388
495
|
}
|
|
389
496
|
interface TextOptions extends A11yOptions {
|
|
390
497
|
readonly class?: string;
|
|
@@ -456,6 +563,30 @@ interface FormOptions extends ContainerOptions {
|
|
|
456
563
|
}
|
|
457
564
|
interface ListOptions extends ContainerOptions {
|
|
458
565
|
}
|
|
566
|
+
/** Options for a plain portal (mount children into `document.body`). */
|
|
567
|
+
interface PortalOptions extends ContainerOptions {
|
|
568
|
+
}
|
|
569
|
+
/**
|
|
570
|
+
* Options shared by every overlay (dialog/popover/tooltip/dropdown/toast).
|
|
571
|
+
*
|
|
572
|
+
* An overlay is a portal + a reactive `when(open, …)` panel + focus/keyboard
|
|
573
|
+
* behavior. `open` drives visibility; the framework never mutates it — closing
|
|
574
|
+
* is cooperative: `onClose` fires on Escape (when `closeOnEscape`) and the app
|
|
575
|
+
* flips its own `open` signal there. Per-kind defaults (role, modality, focus,
|
|
576
|
+
* escape, restore) apply unless overridden here.
|
|
577
|
+
*/
|
|
578
|
+
interface OverlayOptions extends ContainerOptions {
|
|
579
|
+
/** Reactive open/visibility state. When it flips, the panel mounts/unmounts. */
|
|
580
|
+
readonly open: Bindable<boolean>;
|
|
581
|
+
/** Requested-close callback (fired on Escape when `closeOnEscape`). Flip `open` here. */
|
|
582
|
+
readonly onClose?: () => void;
|
|
583
|
+
/** Restore focus to the previously-focused element on close. Default: per-kind. */
|
|
584
|
+
readonly restoreFocus?: boolean;
|
|
585
|
+
/** id of the element to focus first when the overlay opens (else first focusable). */
|
|
586
|
+
readonly initialFocusId?: string;
|
|
587
|
+
/** Escape key invokes `onClose`. Default: per-kind. */
|
|
588
|
+
readonly closeOnEscape?: boolean;
|
|
589
|
+
}
|
|
459
590
|
type SectionBuilder = (section: SectionDSL) => void;
|
|
460
591
|
type ContainerBuilder = (container: ContainerDSL) => void;
|
|
461
592
|
type PageBuilder = (page: PageDSL) => void;
|
|
@@ -515,6 +646,54 @@ interface ContainerDSL extends ContentDSL {
|
|
|
515
646
|
* removal. It does NOT trap arbitrary global errors; errors remain observable.
|
|
516
647
|
*/
|
|
517
648
|
errorBoundary(id: string, builder: ContainerBuilder, options: ErrorBoundaryOptions): void;
|
|
649
|
+
/**
|
|
650
|
+
* Render `builder`'s subtree into `document.body` instead of inline at this
|
|
651
|
+
* position (a neutral inline anchor is left behind). On the server there is no
|
|
652
|
+
* body, so the content renders inline; hydration relocates it to a body
|
|
653
|
+
* container to match the browser. Use for content that must escape overflow/
|
|
654
|
+
* stacking contexts (overlays, toasts). Cleanup removes the body container.
|
|
655
|
+
*/
|
|
656
|
+
portal(key: string, builder: ContainerBuilder, options?: PortalOptions): void;
|
|
657
|
+
/**
|
|
658
|
+
* Modal dialog: portal + `when(open, …)` panel with `role="dialog"`,
|
|
659
|
+
* `aria-modal="true"`, focus trap + containment, Escape-to-close, and focus
|
|
660
|
+
* restore on close. `builder` fills the dialog panel.
|
|
661
|
+
*/
|
|
662
|
+
dialog(key: string, options: OverlayOptions, builder: ContainerBuilder): void;
|
|
663
|
+
/**
|
|
664
|
+
* Non-modal popover: portal + `when(open, …)` panel with `role="dialog"`.
|
|
665
|
+
* Moves focus into the panel on open and restores it on close, but does not
|
|
666
|
+
* trap or contain focus. Escape closes by default.
|
|
667
|
+
*/
|
|
668
|
+
popover(key: string, options: OverlayOptions, builder: ContainerBuilder): void;
|
|
669
|
+
/**
|
|
670
|
+
* Tooltip: portal + `when(open, …)` panel with `role="tooltip"`. Non-modal
|
|
671
|
+
* and does not steal focus (tooltips describe another element); no Escape
|
|
672
|
+
* handling by default.
|
|
673
|
+
*/
|
|
674
|
+
tooltip(key: string, options: OverlayOptions, builder: ContainerBuilder): void;
|
|
675
|
+
/**
|
|
676
|
+
* Dropdown menu: portal + `when(open, …)` panel with `role="menu"`. Non-modal;
|
|
677
|
+
* moves focus into the menu on open, Escape closes, focus restored on close.
|
|
678
|
+
*/
|
|
679
|
+
dropdown(key: string, options: OverlayOptions, builder: ContainerBuilder): void;
|
|
680
|
+
/**
|
|
681
|
+
* Toast: portal + `when(open, …)` panel with `role="status"` and
|
|
682
|
+
* `aria-live="polite"`. Non-modal and never steals focus; no Escape handling.
|
|
683
|
+
*/
|
|
684
|
+
toast(key: string, options: OverlayOptions, builder: ContainerBuilder): void;
|
|
685
|
+
/**
|
|
686
|
+
* Instantiate a reusable `component()` at this position (§3–§9). Creates a
|
|
687
|
+
* `'component'` node (rendered as a `<div>` wrapper), runs the definition's
|
|
688
|
+
* `setup(props, ctx)` synchronously to obtain its render function, and fills
|
|
689
|
+
* the component's own container scope with it. Any `ctx.effect`/`ctx.onCleanup`
|
|
690
|
+
* registered by the setup is torn down automatically when the component leaves
|
|
691
|
+
* the graph. `props` are strongly typed by the definition's generic; pass
|
|
692
|
+
* `Signal` props for fine-grained updates that do NOT re-run `setup` (§13).
|
|
693
|
+
* The optional `children` builder is rendered wherever the component calls
|
|
694
|
+
* `ctx.renderChildren` (§6 native child composition).
|
|
695
|
+
*/
|
|
696
|
+
component<P>(key: string, def: ComponentDefinition<P>, props: P, children?: ContainerBuilder): void;
|
|
518
697
|
}
|
|
519
698
|
interface SectionDSL extends ContainerDSL {
|
|
520
699
|
}
|
|
@@ -576,6 +755,39 @@ declare class ContainerBuilderBase extends ContentBuilderBase implements Contain
|
|
|
576
755
|
form(key: string, builder: FormBuilder, options?: FormOptions): void;
|
|
577
756
|
when(condition: Bindable<boolean>, builder: ContainerBuilder, elseBuilder?: ContainerBuilder): void;
|
|
578
757
|
errorBoundary(id: string, builder: ContainerBuilder, options: ErrorBoundaryOptions): void;
|
|
758
|
+
portal(key: string, builder: ContainerBuilder, options?: PortalOptions): void;
|
|
759
|
+
/**
|
|
760
|
+
* Shared assembly for every overlay kind: a `portal` node whose single child
|
|
761
|
+
* is a `when(open, panel)` conditional. The panel container carries the
|
|
762
|
+
* kind's ARIA semantics; `builder` fills it. An `__overlay__<portalId>`
|
|
763
|
+
* descriptor is registered so the renderer wires focus/keyboard behavior to
|
|
764
|
+
* the same `open` signal that drives the panel. Reuses existing primitives
|
|
765
|
+
* (portal + when + container) — no new render path.
|
|
766
|
+
*/
|
|
767
|
+
private _overlay;
|
|
768
|
+
dialog(key: string, options: OverlayOptions, builder: ContainerBuilder): void;
|
|
769
|
+
popover(key: string, options: OverlayOptions, builder: ContainerBuilder): void;
|
|
770
|
+
tooltip(key: string, options: OverlayOptions, builder: ContainerBuilder): void;
|
|
771
|
+
dropdown(key: string, options: OverlayOptions, builder: ContainerBuilder): void;
|
|
772
|
+
toast(key: string, options: OverlayOptions, builder: ContainerBuilder): void;
|
|
773
|
+
/**
|
|
774
|
+
* Instantiate a reusable component (§3–§9). Creates a `'component'` node
|
|
775
|
+
* (rendered as a `<div>` wrapper — preserves the one-node/one-element
|
|
776
|
+
* positional-hydration invariant), then runs `def.setup(props, ctx)`
|
|
777
|
+
* synchronously to obtain the render function and fills the component's own
|
|
778
|
+
* container scope with it — structurally identical to `container`/
|
|
779
|
+
* `errorBoundary`. Cleanups the setup registers via `ctx.effect`/
|
|
780
|
+
* `ctx.onCleanup` are collected into a closure and exposed to the renderer
|
|
781
|
+
* through a `__component__<id>` handler (mirroring `__overlay__`); the mount/
|
|
782
|
+
* hydrate paths read it and route each into `NodeInstance.trackCleanup`, so
|
|
783
|
+
* teardown runs (children-first) when the component leaves the graph.
|
|
784
|
+
*
|
|
785
|
+
* `setup` runs once per instance here at build time. When this component sits
|
|
786
|
+
* inside a keyed list / conditional, a rebuild disposes the old instance
|
|
787
|
+
* (running its cleanups + pruning its `__component__` entry) and re-runs this
|
|
788
|
+
* method for the new node — so re-invocation is safe and leak-free.
|
|
789
|
+
*/
|
|
790
|
+
component<P>(key: string, def: ComponentDefinition<P>, props: P, children?: ContainerBuilder): void;
|
|
579
791
|
}
|
|
580
792
|
declare class SectionBuilderImpl extends ContainerBuilderBase implements SectionDSL {
|
|
581
793
|
}
|
|
@@ -668,4 +880,4 @@ declare function compile(app: StreetApp, options?: CompileOptions): CompiledAppl
|
|
|
668
880
|
*/
|
|
669
881
|
declare function compileGraph(graph: ApplicationGraph, options?: CompileOptions): CompiledApplication;
|
|
670
882
|
|
|
671
|
-
export { type
|
|
883
|
+
export { type InputOptionsBase as $, type ApplicationId as A, BaseNode as B, type CompiledApplication as C, DiagnosticCollector as D, type Diagnostic as E, DiagnosticError as F, GraphNode as G, type DiagnosticLocation as H, type DiagnosticSeverity as I, type ErrorBoundaryOptions as J, type ErrorFallbackBuilder as K, type ErrorSource as L, type EventDescriptor as M, type FormBuilder as N, FormBuilderImpl as O, type PageDSL as P, type FormDSL as Q, type ReadonlySignal as R, StreetApp as S, type FormOptions as T, type Unsubscribe as U, VERSION as V, type GraphNodeData as W, type HandlerFn as X, type HeadingOptions as Y, type ImageOptions as Z, type InputOptions as _, type ComponentDefinition as a, type LinkOptions as a0, type ListBuilder as a1, ListBuilderImpl as a2, type ListDSL as a3, type ListOptions as a4, type ListPlanEntry as a5, type NodeId as a6, type NodeMetadata as a7, type OverlayOptions as a8, type PageBuilder as a9, isBatching as aA, isComponentDefinition as aB, nextId as aC, nodeIdPrefix as aD, observerCount as aE, reactiveListItemKey as aF, reactiveListItemSignature as aG, resetIdCounter as aH, signal as aI, signalKind as aJ, streetui as aK, PageBuilderImpl as aa, type PortalOptions as ab, type PropValue as ac, type Props as ad, type ReactiveConsumer as ae, type ReactiveSource as af, type SectionBuilder as ag, SectionBuilderImpl as ah, type SectionDSL as ai, type SectionOptions as aj, type SerializedGraph as ak, type SerializedNode as al, type StateRef as am, type StreetUI as an, type TextOptions as ao, type TextValue as ap, batch as aq, compile as ar, compileGraph as as, component as at, createNodeId as au, derived as av, effect as aw, formatDiagnostic as ax, generateApplicationId as ay, generateNodeId as az, type ContainerBuilder as b, Signal as c, type Subscriber as d, ApplicationGraph as e, type SemanticNodeType as f, type ContainerDSL as g, type SignalKind as h, type A11yOptions as i, AppBuilder as j, type AppDSL as k, type AppOptions as l, type ApplicationGraphOptions as m, type Bindable as n, type BindableText as o, type BoundInputOptions as p, type ButtonOptions as q, type CompileOptions as r, type ComponentContext as s, type ComponentRender as t, type ComponentSetup as u, ContainerBuilderImpl as v, type ContainerOptions as w, type ContentDSL as x, type ControlledInputOptions as y, DerivedSignal as z };
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* (`streetui --version`) — the consolidated test-suite pins all three to the
|
|
8
8
|
* same coordinated release so they can never silently drift apart.
|
|
9
9
|
*/
|
|
10
|
-
declare const VERSION = "1.
|
|
10
|
+
declare const VERSION = "1.8.0";
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* StreetUI reactive signals — framework-owned reactivity, no external libraries.
|
|
@@ -195,7 +195,7 @@ declare function formatDiagnostic(d: Diagnostic): string;
|
|
|
195
195
|
* in the Semantic Application Graph.
|
|
196
196
|
*/
|
|
197
197
|
|
|
198
|
-
type SemanticNodeType = 'application' | 'page' | 'section' | 'container' | 'heading' | 'text' | 'button' | 'input' | 'form' | 'list' | 'list-item' | 'image' | 'link' | 'component' | 'slot' | 'fragment' | 'reactive-list' | 'conditional';
|
|
198
|
+
type SemanticNodeType = 'application' | 'page' | 'section' | 'container' | 'heading' | 'text' | 'button' | 'input' | 'form' | 'list' | 'list-item' | 'image' | 'link' | 'component' | 'slot' | 'fragment' | 'reactive-list' | 'conditional' | 'portal';
|
|
199
199
|
interface NodeMetadata {
|
|
200
200
|
readonly createdAt: number;
|
|
201
201
|
readonly [key: string]: unknown;
|
|
@@ -341,6 +341,111 @@ interface SerializedGraph {
|
|
|
341
341
|
readonly root: SerializedNode;
|
|
342
342
|
}
|
|
343
343
|
|
|
344
|
+
/**
|
|
345
|
+
* First-class StreetUI-native components (§3–§9).
|
|
346
|
+
*
|
|
347
|
+
* A component is a *reusable unit that owns its own local state and lifecycle*.
|
|
348
|
+
* It is NOT a virtual-DOM element, a second reactive system, or a second
|
|
349
|
+
* renderer. It compiles into the EXISTING pipeline: `container.component(...)`
|
|
350
|
+
* creates a reserved `'component'` GraphNode (already a `SemanticNodeType`,
|
|
351
|
+
* already mapped to `<div>`), runs the component's `setup` synchronously during
|
|
352
|
+
* the same build-time descent every other builder uses, and routes any cleanups
|
|
353
|
+
* the setup registers into the node's `NodeInstance` at mount (so they run,
|
|
354
|
+
* children-first, when the component leaves the graph).
|
|
355
|
+
*
|
|
356
|
+
* Design (derived from the existing DSL architecture, not copied from React/Vue):
|
|
357
|
+
*
|
|
358
|
+
* const UserCard = component<{ name: Signal<string> }>((props, ctx) => {
|
|
359
|
+
* // ── setup: runs ONCE per instance, at build time ──
|
|
360
|
+
* const open = signal(false);
|
|
361
|
+
* ctx.effect(() => { ... }); // auto-disposed on unmount
|
|
362
|
+
* ctx.onCleanup(() => { ... }); // explicit teardown hook
|
|
363
|
+
* // ── render: fills the component's own container scope ──
|
|
364
|
+
* return (content) => {
|
|
365
|
+
* content.text(props.name); // fine-grained: signal prop, no re-setup
|
|
366
|
+
* content.when(open, (c) => c.text('expanded'));
|
|
367
|
+
* ctx.renderChildren(content); // where slotted children go (§6)
|
|
368
|
+
* };
|
|
369
|
+
* }, { name: 'UserCard' });
|
|
370
|
+
*
|
|
371
|
+
* page.component('card-1', UserCard, { name }, (slot) => slot.text('child'));
|
|
372
|
+
*
|
|
373
|
+
* Props are ordinary typed values (§5 — typing is a TypeScript concern, no
|
|
374
|
+
* runtime schema). Passing a `Signal<T>` prop and binding it in the render body
|
|
375
|
+
* gives fine-grained updates (§13) WITHOUT re-running `setup`: only the bound
|
|
376
|
+
* node mutates when the signal changes. `setup` re-runs only when the component
|
|
377
|
+
* is genuinely rebuilt (removed + re-created by a keyed list / conditional),
|
|
378
|
+
* at which point the previous instance is disposed first — so cleanup stays
|
|
379
|
+
* correct.
|
|
380
|
+
*/
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* The render half of a component: fills the component's own container scope.
|
|
384
|
+
* Returned by `setup` so that per-instance state created in `setup` is captured
|
|
385
|
+
* by closure and the render body can read it.
|
|
386
|
+
*/
|
|
387
|
+
type ComponentRender = (content: ContainerDSL) => void;
|
|
388
|
+
/**
|
|
389
|
+
* Lifecycle + composition surface handed to a component's `setup`. Local
|
|
390
|
+
* reactive state is created with the EXISTING `signal`/`derived`/`effect`/
|
|
391
|
+
* `batch` primitives (§8) — `ctx` only adds ownership: anything registered here
|
|
392
|
+
* is torn down automatically when the component leaves the graph (§9).
|
|
393
|
+
*/
|
|
394
|
+
interface ComponentContext {
|
|
395
|
+
/** This instance's stable identity key (the `key` passed at the call site). */
|
|
396
|
+
readonly key: string;
|
|
397
|
+
/**
|
|
398
|
+
* Register a teardown callback. Runs when the component unmounts (children
|
|
399
|
+
* first, then this — mirroring `NodeInstance.dispose`). Use for resources,
|
|
400
|
+
* `form.dispose`, subscriptions, timers, etc.
|
|
401
|
+
*/
|
|
402
|
+
onCleanup(fn: () => void): void;
|
|
403
|
+
/**
|
|
404
|
+
* Run a reactive effect owned by this component. Wraps the framework's
|
|
405
|
+
* `effect()`; the returned unsubscribe is auto-tracked and disposed on
|
|
406
|
+
* unmount, so component effects never leak (the pain point the audit ranked
|
|
407
|
+
* #1). The effect may itself return a cleanup, exactly like `effect()`.
|
|
408
|
+
*/
|
|
409
|
+
effect(fn: () => void | (() => void)): void;
|
|
410
|
+
/**
|
|
411
|
+
* Render the caller-supplied children into `content` at this point (§6 native
|
|
412
|
+
* child composition / slots). No-op when the call site passed no children.
|
|
413
|
+
* Call it wherever the component wants its slotted content to appear.
|
|
414
|
+
*/
|
|
415
|
+
renderChildren(content: ContainerDSL): void;
|
|
416
|
+
}
|
|
417
|
+
/**
|
|
418
|
+
* A component's setup function: receives typed props and the lifecycle context,
|
|
419
|
+
* creates any local state, and returns the render function. Runs synchronously
|
|
420
|
+
* during the build-time descent (like `errorBoundary`'s callback), so it must be
|
|
421
|
+
* SSR-safe — on the server it runs at render time and its cleanups run when the
|
|
422
|
+
* SSR root is disposed.
|
|
423
|
+
*/
|
|
424
|
+
type ComponentSetup<P> = (props: P, ctx: ComponentContext) => ComponentRender;
|
|
425
|
+
/**
|
|
426
|
+
* The opaque, reusable definition produced by `component(...)`. Carries the
|
|
427
|
+
* `setup` and an inspectable `name`; the brand lets the builder method (and
|
|
428
|
+
* DevTools) recognise a definition at runtime without a class.
|
|
429
|
+
*/
|
|
430
|
+
interface ComponentDefinition<P> {
|
|
431
|
+
readonly __streetui_component: true;
|
|
432
|
+
readonly name: string;
|
|
433
|
+
readonly setup: ComponentSetup<P>;
|
|
434
|
+
}
|
|
435
|
+
/**
|
|
436
|
+
* Define a reusable component. Returns a `ComponentDefinition` you render with
|
|
437
|
+
* `container.component(key, def, props, children?)`. This is a pure factory — it
|
|
438
|
+
* builds no graph and runs no `setup`; instantiation happens per call site.
|
|
439
|
+
*
|
|
440
|
+
* @param setup Runs once per instance: create local state, return the render fn.
|
|
441
|
+
* @param options.name Human-readable name for DevTools/`data-streetui-component`.
|
|
442
|
+
*/
|
|
443
|
+
declare function component<P = Record<string, never>>(setup: ComponentSetup<P>, options?: {
|
|
444
|
+
name?: string;
|
|
445
|
+
}): ComponentDefinition<P>;
|
|
446
|
+
/** Runtime guard: is `value` a component definition? */
|
|
447
|
+
declare function isComponentDefinition(value: unknown): value is ComponentDefinition<unknown>;
|
|
448
|
+
|
|
344
449
|
/**
|
|
345
450
|
* StreetUI DSL type system.
|
|
346
451
|
* All builder callbacks and option shapes live here.
|
|
@@ -385,6 +490,8 @@ interface A11yOptions {
|
|
|
385
490
|
readonly ariaInvalid?: boolean;
|
|
386
491
|
/** aria-required — mark a form field as required. */
|
|
387
492
|
readonly ariaRequired?: boolean;
|
|
493
|
+
/** aria-modal — mark a dialog as modal (content outside is inert to AT). */
|
|
494
|
+
readonly ariaModal?: boolean;
|
|
388
495
|
}
|
|
389
496
|
interface TextOptions extends A11yOptions {
|
|
390
497
|
readonly class?: string;
|
|
@@ -456,6 +563,30 @@ interface FormOptions extends ContainerOptions {
|
|
|
456
563
|
}
|
|
457
564
|
interface ListOptions extends ContainerOptions {
|
|
458
565
|
}
|
|
566
|
+
/** Options for a plain portal (mount children into `document.body`). */
|
|
567
|
+
interface PortalOptions extends ContainerOptions {
|
|
568
|
+
}
|
|
569
|
+
/**
|
|
570
|
+
* Options shared by every overlay (dialog/popover/tooltip/dropdown/toast).
|
|
571
|
+
*
|
|
572
|
+
* An overlay is a portal + a reactive `when(open, …)` panel + focus/keyboard
|
|
573
|
+
* behavior. `open` drives visibility; the framework never mutates it — closing
|
|
574
|
+
* is cooperative: `onClose` fires on Escape (when `closeOnEscape`) and the app
|
|
575
|
+
* flips its own `open` signal there. Per-kind defaults (role, modality, focus,
|
|
576
|
+
* escape, restore) apply unless overridden here.
|
|
577
|
+
*/
|
|
578
|
+
interface OverlayOptions extends ContainerOptions {
|
|
579
|
+
/** Reactive open/visibility state. When it flips, the panel mounts/unmounts. */
|
|
580
|
+
readonly open: Bindable<boolean>;
|
|
581
|
+
/** Requested-close callback (fired on Escape when `closeOnEscape`). Flip `open` here. */
|
|
582
|
+
readonly onClose?: () => void;
|
|
583
|
+
/** Restore focus to the previously-focused element on close. Default: per-kind. */
|
|
584
|
+
readonly restoreFocus?: boolean;
|
|
585
|
+
/** id of the element to focus first when the overlay opens (else first focusable). */
|
|
586
|
+
readonly initialFocusId?: string;
|
|
587
|
+
/** Escape key invokes `onClose`. Default: per-kind. */
|
|
588
|
+
readonly closeOnEscape?: boolean;
|
|
589
|
+
}
|
|
459
590
|
type SectionBuilder = (section: SectionDSL) => void;
|
|
460
591
|
type ContainerBuilder = (container: ContainerDSL) => void;
|
|
461
592
|
type PageBuilder = (page: PageDSL) => void;
|
|
@@ -515,6 +646,54 @@ interface ContainerDSL extends ContentDSL {
|
|
|
515
646
|
* removal. It does NOT trap arbitrary global errors; errors remain observable.
|
|
516
647
|
*/
|
|
517
648
|
errorBoundary(id: string, builder: ContainerBuilder, options: ErrorBoundaryOptions): void;
|
|
649
|
+
/**
|
|
650
|
+
* Render `builder`'s subtree into `document.body` instead of inline at this
|
|
651
|
+
* position (a neutral inline anchor is left behind). On the server there is no
|
|
652
|
+
* body, so the content renders inline; hydration relocates it to a body
|
|
653
|
+
* container to match the browser. Use for content that must escape overflow/
|
|
654
|
+
* stacking contexts (overlays, toasts). Cleanup removes the body container.
|
|
655
|
+
*/
|
|
656
|
+
portal(key: string, builder: ContainerBuilder, options?: PortalOptions): void;
|
|
657
|
+
/**
|
|
658
|
+
* Modal dialog: portal + `when(open, …)` panel with `role="dialog"`,
|
|
659
|
+
* `aria-modal="true"`, focus trap + containment, Escape-to-close, and focus
|
|
660
|
+
* restore on close. `builder` fills the dialog panel.
|
|
661
|
+
*/
|
|
662
|
+
dialog(key: string, options: OverlayOptions, builder: ContainerBuilder): void;
|
|
663
|
+
/**
|
|
664
|
+
* Non-modal popover: portal + `when(open, …)` panel with `role="dialog"`.
|
|
665
|
+
* Moves focus into the panel on open and restores it on close, but does not
|
|
666
|
+
* trap or contain focus. Escape closes by default.
|
|
667
|
+
*/
|
|
668
|
+
popover(key: string, options: OverlayOptions, builder: ContainerBuilder): void;
|
|
669
|
+
/**
|
|
670
|
+
* Tooltip: portal + `when(open, …)` panel with `role="tooltip"`. Non-modal
|
|
671
|
+
* and does not steal focus (tooltips describe another element); no Escape
|
|
672
|
+
* handling by default.
|
|
673
|
+
*/
|
|
674
|
+
tooltip(key: string, options: OverlayOptions, builder: ContainerBuilder): void;
|
|
675
|
+
/**
|
|
676
|
+
* Dropdown menu: portal + `when(open, …)` panel with `role="menu"`. Non-modal;
|
|
677
|
+
* moves focus into the menu on open, Escape closes, focus restored on close.
|
|
678
|
+
*/
|
|
679
|
+
dropdown(key: string, options: OverlayOptions, builder: ContainerBuilder): void;
|
|
680
|
+
/**
|
|
681
|
+
* Toast: portal + `when(open, …)` panel with `role="status"` and
|
|
682
|
+
* `aria-live="polite"`. Non-modal and never steals focus; no Escape handling.
|
|
683
|
+
*/
|
|
684
|
+
toast(key: string, options: OverlayOptions, builder: ContainerBuilder): void;
|
|
685
|
+
/**
|
|
686
|
+
* Instantiate a reusable `component()` at this position (§3–§9). Creates a
|
|
687
|
+
* `'component'` node (rendered as a `<div>` wrapper), runs the definition's
|
|
688
|
+
* `setup(props, ctx)` synchronously to obtain its render function, and fills
|
|
689
|
+
* the component's own container scope with it. Any `ctx.effect`/`ctx.onCleanup`
|
|
690
|
+
* registered by the setup is torn down automatically when the component leaves
|
|
691
|
+
* the graph. `props` are strongly typed by the definition's generic; pass
|
|
692
|
+
* `Signal` props for fine-grained updates that do NOT re-run `setup` (§13).
|
|
693
|
+
* The optional `children` builder is rendered wherever the component calls
|
|
694
|
+
* `ctx.renderChildren` (§6 native child composition).
|
|
695
|
+
*/
|
|
696
|
+
component<P>(key: string, def: ComponentDefinition<P>, props: P, children?: ContainerBuilder): void;
|
|
518
697
|
}
|
|
519
698
|
interface SectionDSL extends ContainerDSL {
|
|
520
699
|
}
|
|
@@ -576,6 +755,39 @@ declare class ContainerBuilderBase extends ContentBuilderBase implements Contain
|
|
|
576
755
|
form(key: string, builder: FormBuilder, options?: FormOptions): void;
|
|
577
756
|
when(condition: Bindable<boolean>, builder: ContainerBuilder, elseBuilder?: ContainerBuilder): void;
|
|
578
757
|
errorBoundary(id: string, builder: ContainerBuilder, options: ErrorBoundaryOptions): void;
|
|
758
|
+
portal(key: string, builder: ContainerBuilder, options?: PortalOptions): void;
|
|
759
|
+
/**
|
|
760
|
+
* Shared assembly for every overlay kind: a `portal` node whose single child
|
|
761
|
+
* is a `when(open, panel)` conditional. The panel container carries the
|
|
762
|
+
* kind's ARIA semantics; `builder` fills it. An `__overlay__<portalId>`
|
|
763
|
+
* descriptor is registered so the renderer wires focus/keyboard behavior to
|
|
764
|
+
* the same `open` signal that drives the panel. Reuses existing primitives
|
|
765
|
+
* (portal + when + container) — no new render path.
|
|
766
|
+
*/
|
|
767
|
+
private _overlay;
|
|
768
|
+
dialog(key: string, options: OverlayOptions, builder: ContainerBuilder): void;
|
|
769
|
+
popover(key: string, options: OverlayOptions, builder: ContainerBuilder): void;
|
|
770
|
+
tooltip(key: string, options: OverlayOptions, builder: ContainerBuilder): void;
|
|
771
|
+
dropdown(key: string, options: OverlayOptions, builder: ContainerBuilder): void;
|
|
772
|
+
toast(key: string, options: OverlayOptions, builder: ContainerBuilder): void;
|
|
773
|
+
/**
|
|
774
|
+
* Instantiate a reusable component (§3–§9). Creates a `'component'` node
|
|
775
|
+
* (rendered as a `<div>` wrapper — preserves the one-node/one-element
|
|
776
|
+
* positional-hydration invariant), then runs `def.setup(props, ctx)`
|
|
777
|
+
* synchronously to obtain the render function and fills the component's own
|
|
778
|
+
* container scope with it — structurally identical to `container`/
|
|
779
|
+
* `errorBoundary`. Cleanups the setup registers via `ctx.effect`/
|
|
780
|
+
* `ctx.onCleanup` are collected into a closure and exposed to the renderer
|
|
781
|
+
* through a `__component__<id>` handler (mirroring `__overlay__`); the mount/
|
|
782
|
+
* hydrate paths read it and route each into `NodeInstance.trackCleanup`, so
|
|
783
|
+
* teardown runs (children-first) when the component leaves the graph.
|
|
784
|
+
*
|
|
785
|
+
* `setup` runs once per instance here at build time. When this component sits
|
|
786
|
+
* inside a keyed list / conditional, a rebuild disposes the old instance
|
|
787
|
+
* (running its cleanups + pruning its `__component__` entry) and re-runs this
|
|
788
|
+
* method for the new node — so re-invocation is safe and leak-free.
|
|
789
|
+
*/
|
|
790
|
+
component<P>(key: string, def: ComponentDefinition<P>, props: P, children?: ContainerBuilder): void;
|
|
579
791
|
}
|
|
580
792
|
declare class SectionBuilderImpl extends ContainerBuilderBase implements SectionDSL {
|
|
581
793
|
}
|
|
@@ -668,4 +880,4 @@ declare function compile(app: StreetApp, options?: CompileOptions): CompiledAppl
|
|
|
668
880
|
*/
|
|
669
881
|
declare function compileGraph(graph: ApplicationGraph, options?: CompileOptions): CompiledApplication;
|
|
670
882
|
|
|
671
|
-
export { type
|
|
883
|
+
export { type InputOptionsBase as $, type ApplicationId as A, BaseNode as B, type CompiledApplication as C, DiagnosticCollector as D, type Diagnostic as E, DiagnosticError as F, GraphNode as G, type DiagnosticLocation as H, type DiagnosticSeverity as I, type ErrorBoundaryOptions as J, type ErrorFallbackBuilder as K, type ErrorSource as L, type EventDescriptor as M, type FormBuilder as N, FormBuilderImpl as O, type PageDSL as P, type FormDSL as Q, type ReadonlySignal as R, StreetApp as S, type FormOptions as T, type Unsubscribe as U, VERSION as V, type GraphNodeData as W, type HandlerFn as X, type HeadingOptions as Y, type ImageOptions as Z, type InputOptions as _, type ComponentDefinition as a, type LinkOptions as a0, type ListBuilder as a1, ListBuilderImpl as a2, type ListDSL as a3, type ListOptions as a4, type ListPlanEntry as a5, type NodeId as a6, type NodeMetadata as a7, type OverlayOptions as a8, type PageBuilder as a9, isBatching as aA, isComponentDefinition as aB, nextId as aC, nodeIdPrefix as aD, observerCount as aE, reactiveListItemKey as aF, reactiveListItemSignature as aG, resetIdCounter as aH, signal as aI, signalKind as aJ, streetui as aK, PageBuilderImpl as aa, type PortalOptions as ab, type PropValue as ac, type Props as ad, type ReactiveConsumer as ae, type ReactiveSource as af, type SectionBuilder as ag, SectionBuilderImpl as ah, type SectionDSL as ai, type SectionOptions as aj, type SerializedGraph as ak, type SerializedNode as al, type StateRef as am, type StreetUI as an, type TextOptions as ao, type TextValue as ap, batch as aq, compile as ar, compileGraph as as, component as at, createNodeId as au, derived as av, effect as aw, formatDiagnostic as ax, generateApplicationId as ay, generateNodeId as az, type ContainerBuilder as b, Signal as c, type Subscriber as d, ApplicationGraph as e, type SemanticNodeType as f, type ContainerDSL as g, type SignalKind as h, type A11yOptions as i, AppBuilder as j, type AppDSL as k, type AppOptions as l, type ApplicationGraphOptions as m, type Bindable as n, type BindableText as o, type BoundInputOptions as p, type ButtonOptions as q, type CompileOptions as r, type ComponentContext as s, type ComponentRender as t, type ComponentSetup as u, ContainerBuilderImpl as v, type ContainerOptions as w, type ContentDSL as x, type ControlledInputOptions as y, DerivedSignal as z };
|