@streetui/dsl 1.0.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/LICENSE +21 -0
- package/README.md +26 -0
- package/dist/index.cjs +421 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +278 -0
- package/dist/index.d.ts +278 -0
- package/dist/index.js +385 -0
- package/dist/index.js.map +1 -0
- package/package.json +47 -0
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
import { ReadonlySignal, Signal } from '@streetui/state';
|
|
2
|
+
import { ApplicationGraph, GraphNode } from '@streetui/graph';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* StreetUI DSL type system.
|
|
6
|
+
* All builder callbacks and option shapes live here.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
type Bindable<T> = T | ReadonlySignal<T> | Signal<T>;
|
|
10
|
+
type TextValue = string | number | boolean;
|
|
11
|
+
type BindableText = TextValue | ReadonlySignal<TextValue>;
|
|
12
|
+
/**
|
|
13
|
+
* Accessibility options shared by every element builder.
|
|
14
|
+
*
|
|
15
|
+
* These map to standard HTML/ARIA attributes and flow straight through to the
|
|
16
|
+
* DOM via the renderer's generic attribute pass — there is no separate ARIA
|
|
17
|
+
* abstraction to keep in sync. Prefer semantic HTML (button/a/input/etc.) and
|
|
18
|
+
* only reach for these when semantics alone are insufficient. `id` (already
|
|
19
|
+
* present on each option type) combined with the deterministic `a11yIds()`
|
|
20
|
+
* helper in `@streetui/core` is how label/description/title associations are
|
|
21
|
+
* wired in an SSR/hydration-safe way.
|
|
22
|
+
*/
|
|
23
|
+
interface A11yOptions {
|
|
24
|
+
/** ARIA role (e.g. 'dialog', 'alert', 'status', 'navigation'). */
|
|
25
|
+
readonly role?: string;
|
|
26
|
+
/** tabindex value. Use 0 to make an element focusable, -1 to remove from tab order. */
|
|
27
|
+
readonly tabIndex?: number;
|
|
28
|
+
/** aria-label — an accessible name when no visible label element exists. */
|
|
29
|
+
readonly ariaLabel?: string;
|
|
30
|
+
/** aria-labelledby — id(s) of the element(s) that label this one. */
|
|
31
|
+
readonly ariaLabelledBy?: string;
|
|
32
|
+
/** aria-describedby — id(s) of the element(s) that describe this one. */
|
|
33
|
+
readonly ariaDescribedBy?: string;
|
|
34
|
+
/** aria-expanded — for disclosure widgets (rendered as the string "true"/"false"). */
|
|
35
|
+
readonly ariaExpanded?: boolean;
|
|
36
|
+
/** aria-controls — id of the element this one controls. */
|
|
37
|
+
readonly ariaControls?: string;
|
|
38
|
+
/** aria-hidden — hide decorative content from assistive tech. */
|
|
39
|
+
readonly ariaHidden?: boolean;
|
|
40
|
+
/** aria-live — announce dynamic changes ('polite' | 'assertive' | 'off'). */
|
|
41
|
+
readonly ariaLive?: 'off' | 'polite' | 'assertive';
|
|
42
|
+
/** aria-current — mark the current item in a set (e.g. 'page' for active nav). */
|
|
43
|
+
readonly ariaCurrent?: boolean | 'page' | 'step' | 'location' | 'date' | 'time';
|
|
44
|
+
/** aria-invalid — mark a form field as failing validation. */
|
|
45
|
+
readonly ariaInvalid?: boolean;
|
|
46
|
+
/** aria-required — mark a form field as required. */
|
|
47
|
+
readonly ariaRequired?: boolean;
|
|
48
|
+
}
|
|
49
|
+
interface TextOptions extends A11yOptions {
|
|
50
|
+
readonly class?: string;
|
|
51
|
+
readonly id?: string;
|
|
52
|
+
}
|
|
53
|
+
interface HeadingOptions extends TextOptions {
|
|
54
|
+
readonly level?: 1 | 2 | 3 | 4 | 5 | 6;
|
|
55
|
+
}
|
|
56
|
+
interface ButtonOptions extends A11yOptions {
|
|
57
|
+
readonly class?: string;
|
|
58
|
+
readonly id?: string;
|
|
59
|
+
readonly disabled?: Bindable<boolean>;
|
|
60
|
+
readonly onClick?: () => void;
|
|
61
|
+
}
|
|
62
|
+
interface InputOptionsBase extends A11yOptions {
|
|
63
|
+
readonly class?: string;
|
|
64
|
+
readonly id?: string;
|
|
65
|
+
readonly type?: 'text' | 'email' | 'password' | 'number' | 'tel' | 'url' | 'search';
|
|
66
|
+
readonly placeholder?: string;
|
|
67
|
+
readonly disabled?: Bindable<boolean>;
|
|
68
|
+
readonly onChange?: (value: string) => void;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Explicitly-controlled input: supply `value` and/or `onInput` yourself.
|
|
72
|
+
* `bind` is disallowed here (typed as `never`) so a two-way `bind` can never be
|
|
73
|
+
* combined with manual `value`/`onInput` wiring — the ambiguity is rejected by
|
|
74
|
+
* the type checker rather than resolved silently at runtime.
|
|
75
|
+
*/
|
|
76
|
+
interface ControlledInputOptions extends InputOptionsBase {
|
|
77
|
+
readonly value?: Bindable<string>;
|
|
78
|
+
readonly onInput?: (value: string) => void;
|
|
79
|
+
readonly bind?: never;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Two-way bound input: `bind` expands to `value` (read) + an input handler that
|
|
83
|
+
* writes the field value back into the signal. Manual `value`/`onInput` are
|
|
84
|
+
* disallowed here to keep the binding unambiguous.
|
|
85
|
+
*/
|
|
86
|
+
interface BoundInputOptions extends InputOptionsBase {
|
|
87
|
+
readonly bind: Signal<string>;
|
|
88
|
+
readonly value?: never;
|
|
89
|
+
readonly onInput?: never;
|
|
90
|
+
}
|
|
91
|
+
type InputOptions = ControlledInputOptions | BoundInputOptions;
|
|
92
|
+
interface LinkOptions extends A11yOptions {
|
|
93
|
+
readonly class?: string;
|
|
94
|
+
readonly id?: string;
|
|
95
|
+
readonly href: string;
|
|
96
|
+
readonly external?: boolean;
|
|
97
|
+
readonly onClick?: () => void;
|
|
98
|
+
}
|
|
99
|
+
interface ImageOptions extends A11yOptions {
|
|
100
|
+
readonly class?: string;
|
|
101
|
+
readonly id?: string;
|
|
102
|
+
readonly src: string;
|
|
103
|
+
readonly alt: string;
|
|
104
|
+
readonly width?: number;
|
|
105
|
+
readonly height?: number;
|
|
106
|
+
}
|
|
107
|
+
interface ContainerOptions extends A11yOptions {
|
|
108
|
+
readonly class?: string;
|
|
109
|
+
readonly id?: string;
|
|
110
|
+
readonly key?: string;
|
|
111
|
+
}
|
|
112
|
+
interface SectionOptions extends ContainerOptions {
|
|
113
|
+
}
|
|
114
|
+
interface FormOptions extends ContainerOptions {
|
|
115
|
+
readonly onSubmit?: (e: Event) => void;
|
|
116
|
+
}
|
|
117
|
+
interface ListOptions extends ContainerOptions {
|
|
118
|
+
}
|
|
119
|
+
type SectionBuilder = (section: SectionDSL) => void;
|
|
120
|
+
type ContainerBuilder = (container: ContainerDSL) => void;
|
|
121
|
+
type PageBuilder = (page: PageDSL) => void;
|
|
122
|
+
type FormBuilder = (form: FormDSL) => void;
|
|
123
|
+
type ListBuilder = (list: ListDSL) => void;
|
|
124
|
+
/** A reactive source of error state (e.g. `resource.error`). `null`/`undefined` means "no error". */
|
|
125
|
+
type ErrorSource = ReadonlySignal<unknown>;
|
|
126
|
+
/** Fallback UI builder — receives the current error and a `retry` callback. */
|
|
127
|
+
type ErrorFallbackBuilder = (fallback: ContainerDSL, error: unknown, retry: () => void) => void;
|
|
128
|
+
interface ErrorBoundaryOptions {
|
|
129
|
+
/** Renders when the boundary is in an error state. */
|
|
130
|
+
readonly fallback: ErrorFallbackBuilder;
|
|
131
|
+
/**
|
|
132
|
+
* Reactive error source(s) to observe — typically a resource's `error` signal.
|
|
133
|
+
* When any becomes non-null, the fallback replaces the body.
|
|
134
|
+
*/
|
|
135
|
+
readonly source?: ErrorSource | ReadonlyArray<ErrorSource>;
|
|
136
|
+
/** Invoked by the fallback's `retry()`, before the body is re-attempted (e.g. `resource.refetch`). */
|
|
137
|
+
readonly onRetry?: () => void;
|
|
138
|
+
}
|
|
139
|
+
interface ContentDSL {
|
|
140
|
+
heading(text: BindableText, options?: HeadingOptions): void;
|
|
141
|
+
text(content: BindableText, options?: TextOptions): void;
|
|
142
|
+
button(label: BindableText, options?: ButtonOptions): void;
|
|
143
|
+
input(options?: InputOptions): void;
|
|
144
|
+
image(options: ImageOptions): void;
|
|
145
|
+
link(label: BindableText, options: LinkOptions): void;
|
|
146
|
+
}
|
|
147
|
+
interface ContainerDSL extends ContentDSL {
|
|
148
|
+
section(key: string, builder: SectionBuilder, options?: SectionOptions): void;
|
|
149
|
+
container(key: string, builder: ContainerBuilder, options?: ContainerOptions): void;
|
|
150
|
+
list(key: string, builder: ListBuilder, options?: ListOptions): void;
|
|
151
|
+
/**
|
|
152
|
+
* Reactive list driven by a Signal<T[]>.
|
|
153
|
+
* When the signal value changes, the list is reconciled against the new items.
|
|
154
|
+
* The renderItem callback receives each item and a ContentDSL to build children.
|
|
155
|
+
*/
|
|
156
|
+
listOf<T>(key: string, items: Signal<T[]> | ReadonlySignal<T[]>, renderItem: (item: T, index: number, content: ContentDSL) => void, options?: ListOptions): void;
|
|
157
|
+
form(key: string, builder: FormBuilder, options?: FormOptions): void;
|
|
158
|
+
/**
|
|
159
|
+
* Conditionally render a subtree based on a boolean condition.
|
|
160
|
+
* When `condition` is a signal, the subtree is mounted/unmounted reactively as
|
|
161
|
+
* the value flips. When true the `builder` subtree is shown; when false it is
|
|
162
|
+
* removed (and its handlers/subscriptions torn down). An optional `elseBuilder`
|
|
163
|
+
* renders while the condition is false. Compiles into the same reactive
|
|
164
|
+
* reconciliation machinery as `listOf` — there is no separate render path.
|
|
165
|
+
*/
|
|
166
|
+
when(condition: Bindable<boolean>, builder: ContainerBuilder, elseBuilder?: ContainerBuilder): void;
|
|
167
|
+
/**
|
|
168
|
+
* Render `builder`, but swap to `options.fallback` when the boundary enters an
|
|
169
|
+
* error state. A boundary enters that state when (a) any observed `source`
|
|
170
|
+
* signal (e.g. a `resource.error`) becomes non-null, or (b) the body builder
|
|
171
|
+
* throws synchronously while building. The fallback receives the current error
|
|
172
|
+
* and a `retry()` callback (which clears the local error, runs `onRetry`, and
|
|
173
|
+
* re-attempts the body). Reuses the same reactive `when()` machinery, so its
|
|
174
|
+
* subtree — and all handlers/subscriptions within it — are torn down on
|
|
175
|
+
* removal. It does NOT trap arbitrary global errors; errors remain observable.
|
|
176
|
+
*/
|
|
177
|
+
errorBoundary(id: string, builder: ContainerBuilder, options: ErrorBoundaryOptions): void;
|
|
178
|
+
}
|
|
179
|
+
interface SectionDSL extends ContainerDSL {
|
|
180
|
+
}
|
|
181
|
+
interface FormDSL extends ContainerDSL {
|
|
182
|
+
}
|
|
183
|
+
interface ListDSL extends ContentDSL {
|
|
184
|
+
item(key: string, builder: ContainerBuilder, options?: ContainerOptions): void;
|
|
185
|
+
}
|
|
186
|
+
interface PageDSL extends ContainerDSL {
|
|
187
|
+
}
|
|
188
|
+
interface AppDSL {
|
|
189
|
+
page(key: string, builder: PageBuilder): void;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* DSL builder implementations.
|
|
194
|
+
*
|
|
195
|
+
* Each builder wraps a GraphNode and provides the fluent API
|
|
196
|
+
* for constructing the Semantic Application Graph via the DSL.
|
|
197
|
+
*
|
|
198
|
+
* Builders do NOT render anything — they only build the graph.
|
|
199
|
+
*/
|
|
200
|
+
|
|
201
|
+
/** Content signature used to detect in-place data changes of a stable item. */
|
|
202
|
+
declare function reactiveListItemSignature(item: unknown): string;
|
|
203
|
+
/** Stable, identity-only reconciliation key for a reactive-list item. */
|
|
204
|
+
declare function reactiveListItemKey(item: unknown, index: number): string;
|
|
205
|
+
declare class ContentBuilderBase implements ContentDSL {
|
|
206
|
+
protected readonly _node: GraphNode;
|
|
207
|
+
protected readonly _graph: ApplicationGraph;
|
|
208
|
+
constructor(_node: GraphNode, _graph: ApplicationGraph);
|
|
209
|
+
heading(text: BindableText, options?: HeadingOptions): void;
|
|
210
|
+
text(content: BindableText, options?: TextOptions): void;
|
|
211
|
+
button(label: BindableText, options?: ButtonOptions): void;
|
|
212
|
+
input(options?: InputOptions): void;
|
|
213
|
+
image(options: ImageOptions): void;
|
|
214
|
+
link(label: BindableText, options: LinkOptions): void;
|
|
215
|
+
}
|
|
216
|
+
declare class ContainerBuilderBase extends ContentBuilderBase implements ContainerDSL {
|
|
217
|
+
section(key: string, builder: SectionBuilder, options?: SectionOptions): void;
|
|
218
|
+
container(key: string, builder: ContainerBuilder, options?: ContainerOptions): void;
|
|
219
|
+
list(key: string, builder: ListBuilder, options?: ListOptions): void;
|
|
220
|
+
listOf<T>(key: string, items: Signal<T[]> | ReadonlySignal<T[]>, renderItem: (item: T, index: number, content: ContentDSL) => void, options?: ListOptions): void;
|
|
221
|
+
form(key: string, builder: FormBuilder, options?: FormOptions): void;
|
|
222
|
+
when(condition: Bindable<boolean>, builder: ContainerBuilder, elseBuilder?: ContainerBuilder): void;
|
|
223
|
+
errorBoundary(id: string, builder: ContainerBuilder, options: ErrorBoundaryOptions): void;
|
|
224
|
+
}
|
|
225
|
+
declare class SectionBuilderImpl extends ContainerBuilderBase implements SectionDSL {
|
|
226
|
+
}
|
|
227
|
+
declare class ContainerBuilderImpl extends ContainerBuilderBase implements ContainerDSL {
|
|
228
|
+
}
|
|
229
|
+
declare class FormBuilderImpl extends ContainerBuilderBase implements FormDSL {
|
|
230
|
+
}
|
|
231
|
+
declare class ListBuilderImpl extends ContentBuilderBase implements ListDSL {
|
|
232
|
+
item(key: string, builder: ContainerBuilder, options?: ContainerOptions): void;
|
|
233
|
+
}
|
|
234
|
+
declare class PageBuilderImpl extends ContainerBuilderBase implements PageDSL {
|
|
235
|
+
}
|
|
236
|
+
declare class AppBuilder implements AppDSL {
|
|
237
|
+
private readonly _graph;
|
|
238
|
+
constructor(_graph: ApplicationGraph);
|
|
239
|
+
page(key: string, builder: PageBuilder): void;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* StreetUI DSL entry point.
|
|
244
|
+
*
|
|
245
|
+
* Usage:
|
|
246
|
+
* import { streetui } from '@streetui/dsl';
|
|
247
|
+
*
|
|
248
|
+
* const app = streetui.app({ name: 'My App' });
|
|
249
|
+
* app.page('home', page => {
|
|
250
|
+
* page.section('hero', section => {
|
|
251
|
+
* section.heading('Welcome');
|
|
252
|
+
* section.button('Click me', { onClick: () => {} });
|
|
253
|
+
* });
|
|
254
|
+
* });
|
|
255
|
+
*
|
|
256
|
+
* const graph = app.build();
|
|
257
|
+
*/
|
|
258
|
+
|
|
259
|
+
interface AppOptions {
|
|
260
|
+
readonly name: string;
|
|
261
|
+
readonly version?: string;
|
|
262
|
+
}
|
|
263
|
+
declare class StreetApp {
|
|
264
|
+
private readonly _graph;
|
|
265
|
+
private readonly _builder;
|
|
266
|
+
constructor(options: AppOptions);
|
|
267
|
+
page(key: string, builder: Parameters<AppBuilder['page']>[1]): this;
|
|
268
|
+
/** Compile to ApplicationGraph — validates and returns the graph. */
|
|
269
|
+
build(): ApplicationGraph;
|
|
270
|
+
/** Access graph before building (useful for inspection). */
|
|
271
|
+
get graph(): ApplicationGraph;
|
|
272
|
+
}
|
|
273
|
+
interface StreetUI {
|
|
274
|
+
app(options: AppOptions): StreetApp;
|
|
275
|
+
}
|
|
276
|
+
declare const streetui: StreetUI;
|
|
277
|
+
|
|
278
|
+
export { type A11yOptions, AppBuilder, type AppDSL, type AppOptions, type Bindable, type BindableText, type BoundInputOptions, type ButtonOptions, type ContainerBuilder, ContainerBuilderImpl, type ContainerDSL, type ContainerOptions, type ContentDSL, type ControlledInputOptions, type ErrorBoundaryOptions, type ErrorFallbackBuilder, type ErrorSource, type FormBuilder, FormBuilderImpl, type FormDSL, type FormOptions, type HeadingOptions, type ImageOptions, type InputOptions, type InputOptionsBase, type LinkOptions, type ListBuilder, ListBuilderImpl, type ListDSL, type ListOptions, type PageBuilder, PageBuilderImpl, type PageDSL, type SectionBuilder, SectionBuilderImpl, type SectionDSL, type SectionOptions, StreetApp, type StreetUI, type TextOptions, type TextValue, reactiveListItemKey, reactiveListItemSignature, streetui };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
import { ReadonlySignal, Signal } from '@streetui/state';
|
|
2
|
+
import { ApplicationGraph, GraphNode } from '@streetui/graph';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* StreetUI DSL type system.
|
|
6
|
+
* All builder callbacks and option shapes live here.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
type Bindable<T> = T | ReadonlySignal<T> | Signal<T>;
|
|
10
|
+
type TextValue = string | number | boolean;
|
|
11
|
+
type BindableText = TextValue | ReadonlySignal<TextValue>;
|
|
12
|
+
/**
|
|
13
|
+
* Accessibility options shared by every element builder.
|
|
14
|
+
*
|
|
15
|
+
* These map to standard HTML/ARIA attributes and flow straight through to the
|
|
16
|
+
* DOM via the renderer's generic attribute pass — there is no separate ARIA
|
|
17
|
+
* abstraction to keep in sync. Prefer semantic HTML (button/a/input/etc.) and
|
|
18
|
+
* only reach for these when semantics alone are insufficient. `id` (already
|
|
19
|
+
* present on each option type) combined with the deterministic `a11yIds()`
|
|
20
|
+
* helper in `@streetui/core` is how label/description/title associations are
|
|
21
|
+
* wired in an SSR/hydration-safe way.
|
|
22
|
+
*/
|
|
23
|
+
interface A11yOptions {
|
|
24
|
+
/** ARIA role (e.g. 'dialog', 'alert', 'status', 'navigation'). */
|
|
25
|
+
readonly role?: string;
|
|
26
|
+
/** tabindex value. Use 0 to make an element focusable, -1 to remove from tab order. */
|
|
27
|
+
readonly tabIndex?: number;
|
|
28
|
+
/** aria-label — an accessible name when no visible label element exists. */
|
|
29
|
+
readonly ariaLabel?: string;
|
|
30
|
+
/** aria-labelledby — id(s) of the element(s) that label this one. */
|
|
31
|
+
readonly ariaLabelledBy?: string;
|
|
32
|
+
/** aria-describedby — id(s) of the element(s) that describe this one. */
|
|
33
|
+
readonly ariaDescribedBy?: string;
|
|
34
|
+
/** aria-expanded — for disclosure widgets (rendered as the string "true"/"false"). */
|
|
35
|
+
readonly ariaExpanded?: boolean;
|
|
36
|
+
/** aria-controls — id of the element this one controls. */
|
|
37
|
+
readonly ariaControls?: string;
|
|
38
|
+
/** aria-hidden — hide decorative content from assistive tech. */
|
|
39
|
+
readonly ariaHidden?: boolean;
|
|
40
|
+
/** aria-live — announce dynamic changes ('polite' | 'assertive' | 'off'). */
|
|
41
|
+
readonly ariaLive?: 'off' | 'polite' | 'assertive';
|
|
42
|
+
/** aria-current — mark the current item in a set (e.g. 'page' for active nav). */
|
|
43
|
+
readonly ariaCurrent?: boolean | 'page' | 'step' | 'location' | 'date' | 'time';
|
|
44
|
+
/** aria-invalid — mark a form field as failing validation. */
|
|
45
|
+
readonly ariaInvalid?: boolean;
|
|
46
|
+
/** aria-required — mark a form field as required. */
|
|
47
|
+
readonly ariaRequired?: boolean;
|
|
48
|
+
}
|
|
49
|
+
interface TextOptions extends A11yOptions {
|
|
50
|
+
readonly class?: string;
|
|
51
|
+
readonly id?: string;
|
|
52
|
+
}
|
|
53
|
+
interface HeadingOptions extends TextOptions {
|
|
54
|
+
readonly level?: 1 | 2 | 3 | 4 | 5 | 6;
|
|
55
|
+
}
|
|
56
|
+
interface ButtonOptions extends A11yOptions {
|
|
57
|
+
readonly class?: string;
|
|
58
|
+
readonly id?: string;
|
|
59
|
+
readonly disabled?: Bindable<boolean>;
|
|
60
|
+
readonly onClick?: () => void;
|
|
61
|
+
}
|
|
62
|
+
interface InputOptionsBase extends A11yOptions {
|
|
63
|
+
readonly class?: string;
|
|
64
|
+
readonly id?: string;
|
|
65
|
+
readonly type?: 'text' | 'email' | 'password' | 'number' | 'tel' | 'url' | 'search';
|
|
66
|
+
readonly placeholder?: string;
|
|
67
|
+
readonly disabled?: Bindable<boolean>;
|
|
68
|
+
readonly onChange?: (value: string) => void;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Explicitly-controlled input: supply `value` and/or `onInput` yourself.
|
|
72
|
+
* `bind` is disallowed here (typed as `never`) so a two-way `bind` can never be
|
|
73
|
+
* combined with manual `value`/`onInput` wiring — the ambiguity is rejected by
|
|
74
|
+
* the type checker rather than resolved silently at runtime.
|
|
75
|
+
*/
|
|
76
|
+
interface ControlledInputOptions extends InputOptionsBase {
|
|
77
|
+
readonly value?: Bindable<string>;
|
|
78
|
+
readonly onInput?: (value: string) => void;
|
|
79
|
+
readonly bind?: never;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Two-way bound input: `bind` expands to `value` (read) + an input handler that
|
|
83
|
+
* writes the field value back into the signal. Manual `value`/`onInput` are
|
|
84
|
+
* disallowed here to keep the binding unambiguous.
|
|
85
|
+
*/
|
|
86
|
+
interface BoundInputOptions extends InputOptionsBase {
|
|
87
|
+
readonly bind: Signal<string>;
|
|
88
|
+
readonly value?: never;
|
|
89
|
+
readonly onInput?: never;
|
|
90
|
+
}
|
|
91
|
+
type InputOptions = ControlledInputOptions | BoundInputOptions;
|
|
92
|
+
interface LinkOptions extends A11yOptions {
|
|
93
|
+
readonly class?: string;
|
|
94
|
+
readonly id?: string;
|
|
95
|
+
readonly href: string;
|
|
96
|
+
readonly external?: boolean;
|
|
97
|
+
readonly onClick?: () => void;
|
|
98
|
+
}
|
|
99
|
+
interface ImageOptions extends A11yOptions {
|
|
100
|
+
readonly class?: string;
|
|
101
|
+
readonly id?: string;
|
|
102
|
+
readonly src: string;
|
|
103
|
+
readonly alt: string;
|
|
104
|
+
readonly width?: number;
|
|
105
|
+
readonly height?: number;
|
|
106
|
+
}
|
|
107
|
+
interface ContainerOptions extends A11yOptions {
|
|
108
|
+
readonly class?: string;
|
|
109
|
+
readonly id?: string;
|
|
110
|
+
readonly key?: string;
|
|
111
|
+
}
|
|
112
|
+
interface SectionOptions extends ContainerOptions {
|
|
113
|
+
}
|
|
114
|
+
interface FormOptions extends ContainerOptions {
|
|
115
|
+
readonly onSubmit?: (e: Event) => void;
|
|
116
|
+
}
|
|
117
|
+
interface ListOptions extends ContainerOptions {
|
|
118
|
+
}
|
|
119
|
+
type SectionBuilder = (section: SectionDSL) => void;
|
|
120
|
+
type ContainerBuilder = (container: ContainerDSL) => void;
|
|
121
|
+
type PageBuilder = (page: PageDSL) => void;
|
|
122
|
+
type FormBuilder = (form: FormDSL) => void;
|
|
123
|
+
type ListBuilder = (list: ListDSL) => void;
|
|
124
|
+
/** A reactive source of error state (e.g. `resource.error`). `null`/`undefined` means "no error". */
|
|
125
|
+
type ErrorSource = ReadonlySignal<unknown>;
|
|
126
|
+
/** Fallback UI builder — receives the current error and a `retry` callback. */
|
|
127
|
+
type ErrorFallbackBuilder = (fallback: ContainerDSL, error: unknown, retry: () => void) => void;
|
|
128
|
+
interface ErrorBoundaryOptions {
|
|
129
|
+
/** Renders when the boundary is in an error state. */
|
|
130
|
+
readonly fallback: ErrorFallbackBuilder;
|
|
131
|
+
/**
|
|
132
|
+
* Reactive error source(s) to observe — typically a resource's `error` signal.
|
|
133
|
+
* When any becomes non-null, the fallback replaces the body.
|
|
134
|
+
*/
|
|
135
|
+
readonly source?: ErrorSource | ReadonlyArray<ErrorSource>;
|
|
136
|
+
/** Invoked by the fallback's `retry()`, before the body is re-attempted (e.g. `resource.refetch`). */
|
|
137
|
+
readonly onRetry?: () => void;
|
|
138
|
+
}
|
|
139
|
+
interface ContentDSL {
|
|
140
|
+
heading(text: BindableText, options?: HeadingOptions): void;
|
|
141
|
+
text(content: BindableText, options?: TextOptions): void;
|
|
142
|
+
button(label: BindableText, options?: ButtonOptions): void;
|
|
143
|
+
input(options?: InputOptions): void;
|
|
144
|
+
image(options: ImageOptions): void;
|
|
145
|
+
link(label: BindableText, options: LinkOptions): void;
|
|
146
|
+
}
|
|
147
|
+
interface ContainerDSL extends ContentDSL {
|
|
148
|
+
section(key: string, builder: SectionBuilder, options?: SectionOptions): void;
|
|
149
|
+
container(key: string, builder: ContainerBuilder, options?: ContainerOptions): void;
|
|
150
|
+
list(key: string, builder: ListBuilder, options?: ListOptions): void;
|
|
151
|
+
/**
|
|
152
|
+
* Reactive list driven by a Signal<T[]>.
|
|
153
|
+
* When the signal value changes, the list is reconciled against the new items.
|
|
154
|
+
* The renderItem callback receives each item and a ContentDSL to build children.
|
|
155
|
+
*/
|
|
156
|
+
listOf<T>(key: string, items: Signal<T[]> | ReadonlySignal<T[]>, renderItem: (item: T, index: number, content: ContentDSL) => void, options?: ListOptions): void;
|
|
157
|
+
form(key: string, builder: FormBuilder, options?: FormOptions): void;
|
|
158
|
+
/**
|
|
159
|
+
* Conditionally render a subtree based on a boolean condition.
|
|
160
|
+
* When `condition` is a signal, the subtree is mounted/unmounted reactively as
|
|
161
|
+
* the value flips. When true the `builder` subtree is shown; when false it is
|
|
162
|
+
* removed (and its handlers/subscriptions torn down). An optional `elseBuilder`
|
|
163
|
+
* renders while the condition is false. Compiles into the same reactive
|
|
164
|
+
* reconciliation machinery as `listOf` — there is no separate render path.
|
|
165
|
+
*/
|
|
166
|
+
when(condition: Bindable<boolean>, builder: ContainerBuilder, elseBuilder?: ContainerBuilder): void;
|
|
167
|
+
/**
|
|
168
|
+
* Render `builder`, but swap to `options.fallback` when the boundary enters an
|
|
169
|
+
* error state. A boundary enters that state when (a) any observed `source`
|
|
170
|
+
* signal (e.g. a `resource.error`) becomes non-null, or (b) the body builder
|
|
171
|
+
* throws synchronously while building. The fallback receives the current error
|
|
172
|
+
* and a `retry()` callback (which clears the local error, runs `onRetry`, and
|
|
173
|
+
* re-attempts the body). Reuses the same reactive `when()` machinery, so its
|
|
174
|
+
* subtree — and all handlers/subscriptions within it — are torn down on
|
|
175
|
+
* removal. It does NOT trap arbitrary global errors; errors remain observable.
|
|
176
|
+
*/
|
|
177
|
+
errorBoundary(id: string, builder: ContainerBuilder, options: ErrorBoundaryOptions): void;
|
|
178
|
+
}
|
|
179
|
+
interface SectionDSL extends ContainerDSL {
|
|
180
|
+
}
|
|
181
|
+
interface FormDSL extends ContainerDSL {
|
|
182
|
+
}
|
|
183
|
+
interface ListDSL extends ContentDSL {
|
|
184
|
+
item(key: string, builder: ContainerBuilder, options?: ContainerOptions): void;
|
|
185
|
+
}
|
|
186
|
+
interface PageDSL extends ContainerDSL {
|
|
187
|
+
}
|
|
188
|
+
interface AppDSL {
|
|
189
|
+
page(key: string, builder: PageBuilder): void;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* DSL builder implementations.
|
|
194
|
+
*
|
|
195
|
+
* Each builder wraps a GraphNode and provides the fluent API
|
|
196
|
+
* for constructing the Semantic Application Graph via the DSL.
|
|
197
|
+
*
|
|
198
|
+
* Builders do NOT render anything — they only build the graph.
|
|
199
|
+
*/
|
|
200
|
+
|
|
201
|
+
/** Content signature used to detect in-place data changes of a stable item. */
|
|
202
|
+
declare function reactiveListItemSignature(item: unknown): string;
|
|
203
|
+
/** Stable, identity-only reconciliation key for a reactive-list item. */
|
|
204
|
+
declare function reactiveListItemKey(item: unknown, index: number): string;
|
|
205
|
+
declare class ContentBuilderBase implements ContentDSL {
|
|
206
|
+
protected readonly _node: GraphNode;
|
|
207
|
+
protected readonly _graph: ApplicationGraph;
|
|
208
|
+
constructor(_node: GraphNode, _graph: ApplicationGraph);
|
|
209
|
+
heading(text: BindableText, options?: HeadingOptions): void;
|
|
210
|
+
text(content: BindableText, options?: TextOptions): void;
|
|
211
|
+
button(label: BindableText, options?: ButtonOptions): void;
|
|
212
|
+
input(options?: InputOptions): void;
|
|
213
|
+
image(options: ImageOptions): void;
|
|
214
|
+
link(label: BindableText, options: LinkOptions): void;
|
|
215
|
+
}
|
|
216
|
+
declare class ContainerBuilderBase extends ContentBuilderBase implements ContainerDSL {
|
|
217
|
+
section(key: string, builder: SectionBuilder, options?: SectionOptions): void;
|
|
218
|
+
container(key: string, builder: ContainerBuilder, options?: ContainerOptions): void;
|
|
219
|
+
list(key: string, builder: ListBuilder, options?: ListOptions): void;
|
|
220
|
+
listOf<T>(key: string, items: Signal<T[]> | ReadonlySignal<T[]>, renderItem: (item: T, index: number, content: ContentDSL) => void, options?: ListOptions): void;
|
|
221
|
+
form(key: string, builder: FormBuilder, options?: FormOptions): void;
|
|
222
|
+
when(condition: Bindable<boolean>, builder: ContainerBuilder, elseBuilder?: ContainerBuilder): void;
|
|
223
|
+
errorBoundary(id: string, builder: ContainerBuilder, options: ErrorBoundaryOptions): void;
|
|
224
|
+
}
|
|
225
|
+
declare class SectionBuilderImpl extends ContainerBuilderBase implements SectionDSL {
|
|
226
|
+
}
|
|
227
|
+
declare class ContainerBuilderImpl extends ContainerBuilderBase implements ContainerDSL {
|
|
228
|
+
}
|
|
229
|
+
declare class FormBuilderImpl extends ContainerBuilderBase implements FormDSL {
|
|
230
|
+
}
|
|
231
|
+
declare class ListBuilderImpl extends ContentBuilderBase implements ListDSL {
|
|
232
|
+
item(key: string, builder: ContainerBuilder, options?: ContainerOptions): void;
|
|
233
|
+
}
|
|
234
|
+
declare class PageBuilderImpl extends ContainerBuilderBase implements PageDSL {
|
|
235
|
+
}
|
|
236
|
+
declare class AppBuilder implements AppDSL {
|
|
237
|
+
private readonly _graph;
|
|
238
|
+
constructor(_graph: ApplicationGraph);
|
|
239
|
+
page(key: string, builder: PageBuilder): void;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* StreetUI DSL entry point.
|
|
244
|
+
*
|
|
245
|
+
* Usage:
|
|
246
|
+
* import { streetui } from '@streetui/dsl';
|
|
247
|
+
*
|
|
248
|
+
* const app = streetui.app({ name: 'My App' });
|
|
249
|
+
* app.page('home', page => {
|
|
250
|
+
* page.section('hero', section => {
|
|
251
|
+
* section.heading('Welcome');
|
|
252
|
+
* section.button('Click me', { onClick: () => {} });
|
|
253
|
+
* });
|
|
254
|
+
* });
|
|
255
|
+
*
|
|
256
|
+
* const graph = app.build();
|
|
257
|
+
*/
|
|
258
|
+
|
|
259
|
+
interface AppOptions {
|
|
260
|
+
readonly name: string;
|
|
261
|
+
readonly version?: string;
|
|
262
|
+
}
|
|
263
|
+
declare class StreetApp {
|
|
264
|
+
private readonly _graph;
|
|
265
|
+
private readonly _builder;
|
|
266
|
+
constructor(options: AppOptions);
|
|
267
|
+
page(key: string, builder: Parameters<AppBuilder['page']>[1]): this;
|
|
268
|
+
/** Compile to ApplicationGraph — validates and returns the graph. */
|
|
269
|
+
build(): ApplicationGraph;
|
|
270
|
+
/** Access graph before building (useful for inspection). */
|
|
271
|
+
get graph(): ApplicationGraph;
|
|
272
|
+
}
|
|
273
|
+
interface StreetUI {
|
|
274
|
+
app(options: AppOptions): StreetApp;
|
|
275
|
+
}
|
|
276
|
+
declare const streetui: StreetUI;
|
|
277
|
+
|
|
278
|
+
export { type A11yOptions, AppBuilder, type AppDSL, type AppOptions, type Bindable, type BindableText, type BoundInputOptions, type ButtonOptions, type ContainerBuilder, ContainerBuilderImpl, type ContainerDSL, type ContainerOptions, type ContentDSL, type ControlledInputOptions, type ErrorBoundaryOptions, type ErrorFallbackBuilder, type ErrorSource, type FormBuilder, FormBuilderImpl, type FormDSL, type FormOptions, type HeadingOptions, type ImageOptions, type InputOptions, type InputOptionsBase, type LinkOptions, type ListBuilder, ListBuilderImpl, type ListDSL, type ListOptions, type PageBuilder, PageBuilderImpl, type PageDSL, type SectionBuilder, SectionBuilderImpl, type SectionDSL, type SectionOptions, StreetApp, type StreetUI, type TextOptions, type TextValue, reactiveListItemKey, reactiveListItemSignature, streetui };
|