@typed/ui 1.0.0-beta.4 → 1.0.0-beta.6
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/README.md +53 -9
- package/dist/Alert.d.ts +78 -0
- package/dist/Alert.d.ts.map +1 -0
- package/dist/Alert.js +37 -0
- package/dist/Button.d.ts +144 -0
- package/dist/Button.d.ts.map +1 -0
- package/dist/Button.js +38 -0
- package/dist/Carousel.d.ts +547 -0
- package/dist/Carousel.d.ts.map +1 -0
- package/dist/Carousel.js +387 -0
- package/dist/Checkbox.d.ts +203 -0
- package/dist/Checkbox.d.ts.map +1 -0
- package/dist/Checkbox.js +140 -0
- package/dist/Collection.d.ts +254 -0
- package/dist/Collection.d.ts.map +1 -0
- package/dist/Collection.js +218 -0
- package/dist/Combobox.d.ts +516 -0
- package/dist/Combobox.d.ts.map +1 -0
- package/dist/Combobox.js +371 -0
- package/dist/Component.d.ts +127 -0
- package/dist/Component.d.ts.map +1 -0
- package/dist/Component.js +60 -0
- package/dist/Composite.d.ts +823 -0
- package/dist/Composite.d.ts.map +1 -0
- package/dist/Composite.js +615 -0
- package/dist/Dialog.d.ts +544 -0
- package/dist/Dialog.d.ts.map +1 -0
- package/dist/Dialog.js +355 -0
- package/dist/Disclosure.d.ts +219 -0
- package/dist/Disclosure.d.ts.map +1 -0
- package/dist/Disclosure.js +128 -0
- package/dist/Dom/Events.d.ts +122 -0
- package/dist/Dom/Events.d.ts.map +1 -0
- package/dist/Dom/Events.js +192 -0
- package/dist/Dom/Props.d.ts +161 -0
- package/dist/Dom/Props.d.ts.map +1 -0
- package/dist/Dom/Props.js +110 -0
- package/dist/Dom/Refs.d.ts +58 -0
- package/dist/Dom/Refs.d.ts.map +1 -0
- package/dist/Dom/Refs.js +61 -0
- package/dist/Dom/Render.d.ts +59 -0
- package/dist/Dom/Render.d.ts.map +1 -0
- package/dist/Dom/Render.js +71 -0
- package/dist/Dom/Types.d.ts +570 -0
- package/dist/Dom/Types.d.ts.map +1 -0
- package/dist/Dom/Types.js +1 -0
- package/dist/Dom/index.d.ts +20 -0
- package/dist/Dom/index.d.ts.map +1 -0
- package/dist/Dom/index.js +8 -0
- package/dist/Dom.d.ts +14 -0
- package/dist/Dom.d.ts.map +1 -0
- package/dist/Dom.js +13 -0
- package/dist/Focusable.d.ts +85 -0
- package/dist/Focusable.d.ts.map +1 -0
- package/dist/Focusable.js +35 -0
- package/dist/Form.d.ts +1695 -0
- package/dist/Form.d.ts.map +1 -0
- package/dist/Form.js +987 -0
- package/dist/Grid.d.ts +569 -0
- package/dist/Grid.d.ts.map +1 -0
- package/dist/Grid.js +379 -0
- package/dist/Group.d.ts +147 -0
- package/dist/Group.d.ts.map +1 -0
- package/dist/Group.js +63 -0
- package/dist/Heading.d.ts +86 -0
- package/dist/Heading.d.ts.map +1 -0
- package/dist/Heading.js +48 -0
- package/dist/Hovercard.d.ts +297 -0
- package/dist/Hovercard.d.ts.map +1 -0
- package/dist/Hovercard.js +188 -0
- package/dist/HttpRouter.d.ts +129 -6
- package/dist/HttpRouter.d.ts.map +1 -1
- package/dist/HttpRouter.js +196 -53
- package/dist/Link.d.ts +67 -28
- package/dist/Link.d.ts.map +1 -1
- package/dist/Link.js +91 -37
- package/dist/Listbox.d.ts +437 -0
- package/dist/Listbox.d.ts.map +1 -0
- package/dist/Listbox.js +316 -0
- package/dist/Menu.d.ts +972 -0
- package/dist/Menu.d.ts.map +1 -0
- package/dist/Menu.js +731 -0
- package/dist/Menubar.d.ts +367 -0
- package/dist/Menubar.d.ts.map +1 -0
- package/dist/Menubar.js +265 -0
- package/dist/Meter.d.ts +217 -0
- package/dist/Meter.d.ts.map +1 -0
- package/dist/Meter.js +94 -0
- package/dist/NativeDetails.d.ts +41 -0
- package/dist/NativeDetails.d.ts.map +1 -0
- package/dist/NativeDetails.js +40 -0
- package/dist/NativeDialog.d.ts +64 -0
- package/dist/NativeDialog.d.ts.map +1 -0
- package/dist/NativeDialog.js +50 -0
- package/dist/NativePopover.d.ts +41 -0
- package/dist/NativePopover.d.ts.map +1 -0
- package/dist/NativePopover.js +46 -0
- package/dist/Popover.d.ts +241 -0
- package/dist/Popover.d.ts.map +1 -0
- package/dist/Popover.js +141 -0
- package/dist/RadioGroup.d.ts +432 -0
- package/dist/RadioGroup.d.ts.map +1 -0
- package/dist/RadioGroup.js +291 -0
- package/dist/Role.d.ts +64 -0
- package/dist/Role.d.ts.map +1 -0
- package/dist/Role.js +27 -0
- package/dist/Select.d.ts +529 -0
- package/dist/Select.d.ts.map +1 -0
- package/dist/Select.js +407 -0
- package/dist/Separator.d.ts +54 -0
- package/dist/Separator.d.ts.map +1 -0
- package/dist/Separator.js +26 -0
- package/dist/Slider.d.ts +188 -0
- package/dist/Slider.d.ts.map +1 -0
- package/dist/Slider.js +96 -0
- package/dist/SpinButton.d.ts +188 -0
- package/dist/SpinButton.d.ts.map +1 -0
- package/dist/SpinButton.js +96 -0
- package/dist/Storybook.d.ts +76 -0
- package/dist/Storybook.d.ts.map +1 -0
- package/dist/Storybook.js +102 -0
- package/dist/Switch.d.ts +187 -0
- package/dist/Switch.d.ts.map +1 -0
- package/dist/Switch.js +116 -0
- package/dist/Tab.d.ts +26 -0
- package/dist/Tab.d.ts.map +1 -0
- package/dist/Tab.js +25 -0
- package/dist/Tabs.d.ts +591 -0
- package/dist/Tabs.d.ts.map +1 -0
- package/dist/Tabs.js +346 -0
- package/dist/Toolbar.d.ts +366 -0
- package/dist/Toolbar.d.ts.map +1 -0
- package/dist/Toolbar.js +245 -0
- package/dist/Tooltip.d.ts +296 -0
- package/dist/Tooltip.d.ts.map +1 -0
- package/dist/Tooltip.js +172 -0
- package/dist/Tree.d.ts +591 -0
- package/dist/Tree.d.ts.map +1 -0
- package/dist/Tree.js +434 -0
- package/dist/TreeGrid.d.ts +645 -0
- package/dist/TreeGrid.d.ts.map +1 -0
- package/dist/TreeGrid.js +420 -0
- package/dist/VisuallyHidden.d.ts +54 -0
- package/dist/VisuallyHidden.d.ts.map +1 -0
- package/dist/VisuallyHidden.js +27 -0
- package/dist/WindowSplitter.d.ts +376 -0
- package/dist/WindowSplitter.d.ts.map +1 -0
- package/dist/WindowSplitter.js +222 -0
- package/dist/index.d.ts +49 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +49 -0
- package/package.json +45 -18
- package/src/HttpRouter.test.ts +0 -256
- package/src/HttpRouter.ts +0 -165
- package/src/Link.test.ts +0 -84
- package/src/Link.ts +0 -107
- package/src/index.ts +0 -2
package/dist/Combobox.js
ADDED
|
@@ -0,0 +1,371 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Combobox keeps focus on its native input and exposes the active option through
|
|
3
|
+
* aria-activedescendant. Input, a manual native popover, and options share explicit value, open,
|
|
4
|
+
* and active state.
|
|
5
|
+
*
|
|
6
|
+
* @remarks
|
|
7
|
+
* The module keeps policy, state transitions, and DOM rendering separable so applications can use
|
|
8
|
+
* the state and pure operations without mounting UI, or supply custom hosts without replacing native
|
|
9
|
+
* events and browser-owned focus.
|
|
10
|
+
*
|
|
11
|
+
* @since 1.0.0
|
|
12
|
+
* @category modules
|
|
13
|
+
* @packageDocumentation
|
|
14
|
+
*/
|
|
15
|
+
import * as Effect from "effect/Effect";
|
|
16
|
+
import * as Schema from "effect/Schema";
|
|
17
|
+
import { RefSubject } from "@typed/fx";
|
|
18
|
+
import { EventHandler, html, } from "@typed/template";
|
|
19
|
+
import * as Collection from "./Collection.js";
|
|
20
|
+
import * as Composite from "./Composite.js";
|
|
21
|
+
import * as Dom from "./Dom.js";
|
|
22
|
+
import * as NativePopover from "./NativePopover.js";
|
|
23
|
+
/**
|
|
24
|
+
* Effect Schema used by makeState to encode, decode, and hydrate Combobox state.
|
|
25
|
+
*
|
|
26
|
+
* @remarks
|
|
27
|
+
* ## Why
|
|
28
|
+
*
|
|
29
|
+
* A public schema makes hydration and serialized state use the same runtime validation as direct
|
|
30
|
+
* construction.
|
|
31
|
+
*
|
|
32
|
+
* ## Ownership and lifetime
|
|
33
|
+
*
|
|
34
|
+
* This declaration is data or schema metadata and acquires no resources.
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* ```ts
|
|
38
|
+
* import * as Schema from "effect/Schema";
|
|
39
|
+
* import * as Combobox from "@typed/ui/Combobox";
|
|
40
|
+
*
|
|
41
|
+
* const decodeState = Schema.decodeUnknownEffect(Combobox.StateSchema);
|
|
42
|
+
* ```
|
|
43
|
+
* @since 1.0.0
|
|
44
|
+
* @category schemas
|
|
45
|
+
*/
|
|
46
|
+
export const StateSchema = Schema.Struct({
|
|
47
|
+
id: Schema.String,
|
|
48
|
+
value: Schema.String,
|
|
49
|
+
open: Schema.Boolean,
|
|
50
|
+
activeId: Schema.NullOr(Schema.String),
|
|
51
|
+
orientation: Schema.Literals(["vertical"]),
|
|
52
|
+
loop: Schema.Boolean,
|
|
53
|
+
rtl: Schema.Boolean,
|
|
54
|
+
virtualFocus: Schema.Boolean,
|
|
55
|
+
});
|
|
56
|
+
/**
|
|
57
|
+
* Creates hydrated Combobox state. value defaults to an empty string, open to false, activeId to
|
|
58
|
+
* null, loop to true, and virtual focus is enabled.
|
|
59
|
+
*
|
|
60
|
+
* @remarks
|
|
61
|
+
* ## Why
|
|
62
|
+
*
|
|
63
|
+
* State and collection ownership can be composed and tested independently from any renderer.
|
|
64
|
+
*
|
|
65
|
+
* ## Ownership and lifetime
|
|
66
|
+
*
|
|
67
|
+
* The returned Effect creates the RefSubject when run. That state is renderer-independent;
|
|
68
|
+
* collection registrations belong to the separate Scope that runs register or ref, not to state
|
|
69
|
+
* creation.
|
|
70
|
+
*
|
|
71
|
+
* @example
|
|
72
|
+
* ```ts
|
|
73
|
+
* import * as Effect from "effect/Effect";
|
|
74
|
+
* import * as Combobox from "@typed/ui/Combobox";
|
|
75
|
+
*
|
|
76
|
+
* const program = Effect.scoped(
|
|
77
|
+
* Effect.gen(function* () {
|
|
78
|
+
* const state = yield* Combobox.makeState({ id: "city" });
|
|
79
|
+
* const collection = yield* Combobox.makeCollection();
|
|
80
|
+
* return { state: yield* state, collection: yield* collection };
|
|
81
|
+
* }),
|
|
82
|
+
* );
|
|
83
|
+
* ```
|
|
84
|
+
* @since 1.0.0
|
|
85
|
+
* @category constructors
|
|
86
|
+
*/
|
|
87
|
+
export function makeState(initial) {
|
|
88
|
+
return RefSubject.hydrate(StateSchema, {
|
|
89
|
+
id: initial.id,
|
|
90
|
+
value: initial.value ?? "",
|
|
91
|
+
open: initial.open ?? false,
|
|
92
|
+
activeId: initial.activeId ?? null,
|
|
93
|
+
orientation: "vertical",
|
|
94
|
+
loop: initial.loop ?? true,
|
|
95
|
+
rtl: false,
|
|
96
|
+
virtualFocus: true,
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Creates a scoped Collection for Combobox items.
|
|
101
|
+
*
|
|
102
|
+
* @remarks
|
|
103
|
+
* ## Why
|
|
104
|
+
*
|
|
105
|
+
* State and collection ownership can be composed and tested independently from any renderer.
|
|
106
|
+
*
|
|
107
|
+
* ## Ownership and lifetime
|
|
108
|
+
*
|
|
109
|
+
* The returned Effect allocates the RefSubject in the caller's Scope. Each later registration is
|
|
110
|
+
* owned by the Scope that runs register, independently of this construction Effect.
|
|
111
|
+
*
|
|
112
|
+
* @example
|
|
113
|
+
* ```ts
|
|
114
|
+
* import * as Effect from "effect/Effect";
|
|
115
|
+
* import * as Combobox from "@typed/ui/Combobox";
|
|
116
|
+
*
|
|
117
|
+
* const program = Effect.scoped(
|
|
118
|
+
* Effect.gen(function* () {
|
|
119
|
+
* const collection = yield* Combobox.makeCollection();
|
|
120
|
+
* return yield* collection;
|
|
121
|
+
* }),
|
|
122
|
+
* );
|
|
123
|
+
* ```
|
|
124
|
+
* @since 1.0.0
|
|
125
|
+
* @category constructors
|
|
126
|
+
*/
|
|
127
|
+
export const makeCollection = Collection.makeState;
|
|
128
|
+
/**
|
|
129
|
+
* Stores the input value, clears activeId, and opens the popup in one state update.
|
|
130
|
+
*
|
|
131
|
+
* @remarks
|
|
132
|
+
* ## Why
|
|
133
|
+
*
|
|
134
|
+
* The operation exposes Combobox's transition directly so callers can compose it in Effect
|
|
135
|
+
* programs and native event handlers.
|
|
136
|
+
*
|
|
137
|
+
* ## Ownership and lifetime
|
|
138
|
+
*
|
|
139
|
+
* The returned Effect performs the update or DOM side effect only when run, preserves the declared
|
|
140
|
+
* error and service channels, and retains no resources after completion.
|
|
141
|
+
*
|
|
142
|
+
* ## Example
|
|
143
|
+
*
|
|
144
|
+
* Import with `import { setValue } from "@typed/ui/Combobox";` Extend the [Combobox.makeState
|
|
145
|
+
* runnable setup](/reference/%40typed%2Fui%2FCombobox%23makeState). Inside the linked Effect
|
|
146
|
+
* program invoke `yield* setValue(state, "New York")`, then read state to observe that value,
|
|
147
|
+
* `activeId: null`, and `open: true`.
|
|
148
|
+
* @since 1.0.0
|
|
149
|
+
* @category combinators
|
|
150
|
+
*/
|
|
151
|
+
export function setValue(state, value) {
|
|
152
|
+
return RefSubject.update(state, (current) => ({ ...current, value, activeId: null, open: true }));
|
|
153
|
+
}
|
|
154
|
+
function openPopover(state, collection) {
|
|
155
|
+
return Effect.gen(function* () {
|
|
156
|
+
const current = yield* state;
|
|
157
|
+
const item = collection === undefined
|
|
158
|
+
? undefined
|
|
159
|
+
: visibleItems(yield* collection).find((item) => item.value === current.value);
|
|
160
|
+
const next = yield* RefSubject.update(state, (value) => ({
|
|
161
|
+
...value,
|
|
162
|
+
activeId: item?.id ?? value.activeId,
|
|
163
|
+
open: true,
|
|
164
|
+
}));
|
|
165
|
+
if (item !== undefined && collection !== undefined) {
|
|
166
|
+
yield* Composite.scrollActive({ state, collection });
|
|
167
|
+
}
|
|
168
|
+
return next;
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
function move(state, collection, direction) {
|
|
172
|
+
return Effect.gen(function* () {
|
|
173
|
+
const next = Composite.moveActiveId(visibleItems(yield* collection), yield* state, direction);
|
|
174
|
+
if (next === null)
|
|
175
|
+
return yield* state;
|
|
176
|
+
return yield* RefSubject.update(state, (current) => ({ ...current, activeId: next }));
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
function select(state, id, value) {
|
|
180
|
+
return RefSubject.update(state, (current) => ({ ...current, activeId: id, value, open: false }));
|
|
181
|
+
}
|
|
182
|
+
function selectActive(state, collection) {
|
|
183
|
+
return Effect.gen(function* () {
|
|
184
|
+
const activeId = (yield* state).activeId;
|
|
185
|
+
const item = activeId === null ? undefined : (yield* collection).find((item) => item.id === activeId);
|
|
186
|
+
return item?.value === undefined ? yield* state : yield* select(state, item.id, item.value);
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
function inputProps(options) {
|
|
190
|
+
const id = RefSubject.map(options.state, (state) => state.id);
|
|
191
|
+
const inputId = RefSubject.map(options.state, (state) => `${state.id}-input`);
|
|
192
|
+
const open = RefSubject.map(options.state, (state) => state.open);
|
|
193
|
+
const onkeydown = options.collection === undefined
|
|
194
|
+
? undefined
|
|
195
|
+
: EventHandler.make(Effect.fn(function* (event) {
|
|
196
|
+
const direction = event.key === "ArrowDown" ? "next" : event.key === "ArrowUp" ? "previous" : undefined;
|
|
197
|
+
if (direction !== undefined) {
|
|
198
|
+
event.preventDefault();
|
|
199
|
+
if (!(yield* options.state).open) {
|
|
200
|
+
yield* openPopover(options.state, options.collection);
|
|
201
|
+
}
|
|
202
|
+
yield* move(options.state, options.collection, direction);
|
|
203
|
+
yield* Composite.scrollActive({
|
|
204
|
+
state: options.state,
|
|
205
|
+
collection: options.collection,
|
|
206
|
+
});
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
if (event.key === "Enter") {
|
|
210
|
+
if ((yield* options.state).activeId === null)
|
|
211
|
+
return;
|
|
212
|
+
event.preventDefault();
|
|
213
|
+
yield* selectActive(options.state, options.collection);
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
if (event.key === "Escape") {
|
|
217
|
+
yield* RefSubject.update(options.state, (state) => ({ ...state, open: false }));
|
|
218
|
+
}
|
|
219
|
+
}));
|
|
220
|
+
return () => ({
|
|
221
|
+
id: inputId,
|
|
222
|
+
role: "combobox",
|
|
223
|
+
"aria-autocomplete": "list",
|
|
224
|
+
"aria-controls": id,
|
|
225
|
+
"aria-expanded": open,
|
|
226
|
+
"aria-activedescendant": Composite.activeDescendant(options.state),
|
|
227
|
+
placeholder: options.placeholder,
|
|
228
|
+
".value": RefSubject.map(options.state, (state) => state.value),
|
|
229
|
+
oninput: EventHandler.make(Effect.fn((event) => setValue(options.state, Dom.currentTarget(event).value))),
|
|
230
|
+
onfocus: openPopover(options.state, options.collection),
|
|
231
|
+
onkeydown,
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
function visibleItems(collection) {
|
|
235
|
+
return Collection.byDomOrder(collection).filter((item) => item.element === undefined || item.element.closest("[hidden]") === null);
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Renders the native combobox input, keeps its value property synchronized, opens on focus, and
|
|
239
|
+
* handles Arrow, Enter, and Escape when a collection is supplied.
|
|
240
|
+
*
|
|
241
|
+
* @remarks
|
|
242
|
+
* ## Why
|
|
243
|
+
*
|
|
244
|
+
* The component applies the family behavior while leaving callers free to supply a custom host
|
|
245
|
+
* through the shared DOM boundary.
|
|
246
|
+
*
|
|
247
|
+
* ## Ownership and lifetime
|
|
248
|
+
*
|
|
249
|
+
* The returned Fx installs DOM refs, native listeners, state subscriptions, and optional
|
|
250
|
+
* collection registrations only when rendered. The rendering Scope removes those resources;
|
|
251
|
+
* unrelated nodes and attributes remain caller-owned.
|
|
252
|
+
*
|
|
253
|
+
* ## Example
|
|
254
|
+
*
|
|
255
|
+
* Import with `import { Input } from "@typed/ui/Combobox";` Extend the [Combobox.makeState runnable
|
|
256
|
+
* setup](/reference/%40typed%2Fui%2FCombobox%23makeState). Replace the linked program's final
|
|
257
|
+
* snapshot read with `Input({ state, placeholder: "Choose a city" })`; render that Fx before the
|
|
258
|
+
* same Scope closes.
|
|
259
|
+
* @since 1.0.0
|
|
260
|
+
* @category components
|
|
261
|
+
*/
|
|
262
|
+
export function Input(options, host) {
|
|
263
|
+
return Dom.renderHost()(options, host, inputProps(options), "", (props) => html `<input ...${props} />`);
|
|
264
|
+
}
|
|
265
|
+
function popoverProps(options) {
|
|
266
|
+
const id = RefSubject.map(options.state, (state) => state.id);
|
|
267
|
+
const inputId = RefSubject.map(options.state, (state) => `${state.id}-input`);
|
|
268
|
+
return () => ({
|
|
269
|
+
id,
|
|
270
|
+
role: "listbox",
|
|
271
|
+
"aria-labelledby": inputId,
|
|
272
|
+
popover: "manual",
|
|
273
|
+
ontoggle: EventHandler.make(Effect.fn(function* (event) {
|
|
274
|
+
const open = Dom.toggleState(event) === "open";
|
|
275
|
+
const current = yield* options.state;
|
|
276
|
+
if (current.open === open)
|
|
277
|
+
return current;
|
|
278
|
+
const next = yield* RefSubject.update(options.state, (state) => ({ ...state, open }));
|
|
279
|
+
if (!open || options.collection === undefined)
|
|
280
|
+
return next;
|
|
281
|
+
const item = (yield* options.collection).find((item) => item.value === next.value);
|
|
282
|
+
if (item === undefined)
|
|
283
|
+
return next;
|
|
284
|
+
const selected = yield* RefSubject.update(options.state, (state) => ({
|
|
285
|
+
...state,
|
|
286
|
+
activeId: item.id,
|
|
287
|
+
}));
|
|
288
|
+
yield* Composite.scrollActive({ state: options.state, collection: options.collection });
|
|
289
|
+
return selected;
|
|
290
|
+
})),
|
|
291
|
+
ref: Dom.composeRefs(options.state, NativePopover.ref(options.state)),
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
/**
|
|
295
|
+
* Renders manual-popover listbox content, mirrors native toggle events into open state, and
|
|
296
|
+
* scrolls the selected option when opened.
|
|
297
|
+
*
|
|
298
|
+
* @remarks
|
|
299
|
+
* ## Why
|
|
300
|
+
*
|
|
301
|
+
* The component applies the family behavior while leaving callers free to supply a custom host
|
|
302
|
+
* through the shared DOM boundary.
|
|
303
|
+
*
|
|
304
|
+
* ## Ownership and lifetime
|
|
305
|
+
*
|
|
306
|
+
* The returned Fx installs DOM refs, native listeners, state subscriptions, and optional
|
|
307
|
+
* collection registrations only when rendered. The rendering Scope removes those resources;
|
|
308
|
+
* unrelated nodes and attributes remain caller-owned.
|
|
309
|
+
*
|
|
310
|
+
* ## Example
|
|
311
|
+
*
|
|
312
|
+
* Import with `import { Popover } from "@typed/ui/Combobox";` Extend the [Combobox.makeState
|
|
313
|
+
* runnable setup](/reference/%40typed%2Fui%2FCombobox%23makeState). Replace the linked program's
|
|
314
|
+
* final snapshot read with `Popover({ state, content: "Cities" })`; render that Fx before the same
|
|
315
|
+
* Scope closes.
|
|
316
|
+
* @since 1.0.0
|
|
317
|
+
* @category components
|
|
318
|
+
*/
|
|
319
|
+
export function Popover(options, host) {
|
|
320
|
+
return Dom.renderHost()(options, host, popoverProps(options), options.content, (props, content) => {
|
|
321
|
+
return html `<div ...${props}>${content}</div>`;
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
function itemProps(options) {
|
|
325
|
+
const selected = RefSubject.map(options.state, (state) => state.value === options.value);
|
|
326
|
+
const register = options.collection === undefined
|
|
327
|
+
? undefined
|
|
328
|
+
: Collection.ref(options.collection, {
|
|
329
|
+
id: options.id,
|
|
330
|
+
value: options.value,
|
|
331
|
+
textValue: options.textValue ?? options.value,
|
|
332
|
+
disabled: options.disabled,
|
|
333
|
+
});
|
|
334
|
+
return () => ({
|
|
335
|
+
id: options.id,
|
|
336
|
+
role: "option",
|
|
337
|
+
"aria-selected": selected,
|
|
338
|
+
"aria-disabled": options.disabled ?? false,
|
|
339
|
+
tabindex: Composite.tabIndex(options.state, options.id),
|
|
340
|
+
onclick: options.disabled === true ? Effect.void : select(options.state, options.id, options.value),
|
|
341
|
+
ref: Dom.composeRefs(register, options.ref),
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
/**
|
|
345
|
+
* Renders and optionally registers one option; disabled options expose aria-disabled and do not
|
|
346
|
+
* select on click.
|
|
347
|
+
*
|
|
348
|
+
* @remarks
|
|
349
|
+
* ## Why
|
|
350
|
+
*
|
|
351
|
+
* The component applies the family behavior while leaving callers free to supply a custom host
|
|
352
|
+
* through the shared DOM boundary.
|
|
353
|
+
*
|
|
354
|
+
* ## Ownership and lifetime
|
|
355
|
+
*
|
|
356
|
+
* The returned Fx installs DOM refs, native listeners, state subscriptions, and optional
|
|
357
|
+
* collection registrations only when rendered. The rendering Scope removes those resources;
|
|
358
|
+
* unrelated nodes and attributes remain caller-owned.
|
|
359
|
+
*
|
|
360
|
+
* ## Example
|
|
361
|
+
*
|
|
362
|
+
* Import with `import { Item } from "@typed/ui/Combobox";` Extend the [Combobox.makeState runnable
|
|
363
|
+
* setup](/reference/%40typed%2Fui%2FCombobox%23makeState). Replace the linked program's final
|
|
364
|
+
* snapshot read with `Item({ state, id: "nyc", value: "New York", content: "New York" })`; render
|
|
365
|
+
* that Fx before the same Scope closes.
|
|
366
|
+
* @since 1.0.0
|
|
367
|
+
* @category components
|
|
368
|
+
*/
|
|
369
|
+
export function Item(options, host) {
|
|
370
|
+
return Dom.renderHost()(options, host, itemProps(options), options.content, (props, content) => html `<div ...${props}>${content}</div>`);
|
|
371
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import type * as Effect from "effect/Effect";
|
|
2
|
+
import { Fx } from "@typed/fx";
|
|
3
|
+
import { type Renderable } from "@typed/template";
|
|
4
|
+
type ComponentFx<Yield extends Effect.Effect<any, any, any>, Result extends Renderable.Any> = [
|
|
5
|
+
Fx.Fx<Renderable.Success<Result>, Effect.Error<Yield> | Renderable.Error<Result>, Effect.Services<Yield> | Renderable.Services<Result>>
|
|
6
|
+
] extends [Fx.Fx<infer Success, infer Error, infer Services>] ? Fx.Fx<Success, Error, Services> : never;
|
|
7
|
+
type ComponentResult<Args extends ReadonlyArray<any>, Result> = Args extends readonly [] ? Result : (...args: Args) => Result;
|
|
8
|
+
/**
|
|
9
|
+
* Type-level contracts used by the `component` constructor.
|
|
10
|
+
*
|
|
11
|
+
* @remarks
|
|
12
|
+
* ## Why
|
|
13
|
+
*
|
|
14
|
+
* The namespace keeps the overload-rich generator contract attached to the
|
|
15
|
+
* callable constructor while still making it available to library authors who
|
|
16
|
+
* wrap `component`.
|
|
17
|
+
*
|
|
18
|
+
* ## Ownership and lifetime
|
|
19
|
+
*
|
|
20
|
+
* These types acquire nothing. The `Fx` produced by `component` is lazy; its
|
|
21
|
+
* running Effect `Scope` owns yielded effects, subscriptions, and cleanup.
|
|
22
|
+
*
|
|
23
|
+
* @since 1.0.0
|
|
24
|
+
* @category models
|
|
25
|
+
*/
|
|
26
|
+
export declare namespace component {
|
|
27
|
+
/**
|
|
28
|
+
* The overloaded callable type of `component`.
|
|
29
|
+
*
|
|
30
|
+
* @remarks
|
|
31
|
+
* ## Why
|
|
32
|
+
*
|
|
33
|
+
* `Gen` preserves argument, error, and service inference across generator
|
|
34
|
+
* components and optional post-construction pipe functions. A zero-argument
|
|
35
|
+
* body returns an `Fx`; a body with arguments returns a component function.
|
|
36
|
+
*
|
|
37
|
+
* ## Ownership and lifetime
|
|
38
|
+
*
|
|
39
|
+
* Calling a `Gen` value constructs a lazy `Fx` and starts no work. Resources
|
|
40
|
+
* yielded by the generator belong to the Scope that eventually runs that Fx.
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* ```ts
|
|
44
|
+
* import { component } from "@typed/ui/Component"
|
|
45
|
+
* import { html } from "@typed/template"
|
|
46
|
+
*
|
|
47
|
+
* const define: component.Gen = component
|
|
48
|
+
* const Greeting = define(function* (name: string) {
|
|
49
|
+
* return html`<p>Hello ${name}</p>`
|
|
50
|
+
* })
|
|
51
|
+
* ```
|
|
52
|
+
*
|
|
53
|
+
* @since 1.0.0
|
|
54
|
+
* @category models
|
|
55
|
+
*/
|
|
56
|
+
type Gen = {
|
|
57
|
+
/** Creates a component directly for a zero-argument body. @since 1.0.0 @category constructors */
|
|
58
|
+
<Yield extends Effect.Effect<any, any, any>, const Result extends Renderable.Any>(body: () => Generator<Yield, Result, any>): ComponentFx<Yield, Result>;
|
|
59
|
+
/** Creates a component function while preserving the generator's parameters. @since 1.0.0 @category constructors */
|
|
60
|
+
<Args extends readonly [any, ...ReadonlyArray<any>], Yield extends Effect.Effect<any, any, any>, const Result extends Renderable.Any>(body: (...args: Args) => Generator<Yield, Result, any>): (...args: Args) => ComponentFx<Yield, Result>;
|
|
61
|
+
/** Applies one argument-aware transformation to the generated Fx. @since 1.0.0 @category constructors */
|
|
62
|
+
<Args extends ReadonlyArray<any>, Yield extends Effect.Effect<any, any, any>, const Result extends Renderable.Any, A>(body: (...args: Args) => Generator<Yield, Result, any>, a: (_: ComponentFx<Yield, Result>, ...args: Args) => A): ComponentResult<Args, A>;
|
|
63
|
+
/** Applies two ordered argument-aware transformations to the generated Fx. @since 1.0.0 @category constructors */
|
|
64
|
+
<Args extends ReadonlyArray<any>, Yield extends Effect.Effect<any, any, any>, const Result extends Renderable.Any, A, B>(body: (...args: Args) => Generator<Yield, Result, any>, a: (_: ComponentFx<Yield, Result>, ...args: Args) => A, b: (_: A, ...args: Args) => B): ComponentResult<Args, B>;
|
|
65
|
+
/** Applies three ordered argument-aware transformations to the generated Fx. @since 1.0.0 @category constructors */
|
|
66
|
+
<Args extends ReadonlyArray<any>, Yield extends Effect.Effect<any, any, any>, const Result extends Renderable.Any, A, B, C>(body: (...args: Args) => Generator<Yield, Result, any>, a: (_: ComponentFx<Yield, Result>, ...args: Args) => A, b: (_: A, ...args: Args) => B, c: (_: B, ...args: Args) => C): ComponentResult<Args, C>;
|
|
67
|
+
/** Applies four ordered argument-aware transformations to the generated Fx. @since 1.0.0 @category constructors */
|
|
68
|
+
<Args extends ReadonlyArray<any>, Yield extends Effect.Effect<any, any, any>, const Result extends Renderable.Any, A, B, C, D>(body: (...args: Args) => Generator<Yield, Result, any>, a: (_: ComponentFx<Yield, Result>, ...args: Args) => A, b: (_: A, ...args: Args) => B, c: (_: B, ...args: Args) => C, d: (_: C, ...args: Args) => D): ComponentResult<Args, D>;
|
|
69
|
+
/** Applies five ordered argument-aware transformations to the generated Fx. @since 1.0.0 @category constructors */
|
|
70
|
+
<Args extends ReadonlyArray<any>, Yield extends Effect.Effect<any, any, any>, const Result extends Renderable.Any, A, B, C, D, E>(body: (...args: Args) => Generator<Yield, Result, any>, a: (_: ComponentFx<Yield, Result>, ...args: Args) => A, b: (_: A, ...args: Args) => B, c: (_: B, ...args: Args) => C, d: (_: C, ...args: Args) => D, e: (_: D, ...args: Args) => E): ComponentResult<Args, E>;
|
|
71
|
+
/** Applies six ordered argument-aware transformations to the generated Fx. @since 1.0.0 @category constructors */
|
|
72
|
+
<Args extends ReadonlyArray<any>, Yield extends Effect.Effect<any, any, any>, const Result extends Renderable.Any, A, B, C, D, E, F>(body: (...args: Args) => Generator<Yield, Result, any>, a: (_: ComponentFx<Yield, Result>, ...args: Args) => A, b: (_: A, ...args: Args) => B, c: (_: B, ...args: Args) => C, d: (_: C, ...args: Args) => D, e: (_: D, ...args: Args) => E, f: (_: E, ...args: Args) => F): ComponentResult<Args, F>;
|
|
73
|
+
/** Applies seven ordered argument-aware transformations to the generated Fx. @since 1.0.0 @category constructors */
|
|
74
|
+
<Args extends ReadonlyArray<any>, Yield extends Effect.Effect<any, any, any>, const Result extends Renderable.Any, A, B, C, D, E, F, G>(body: (...args: Args) => Generator<Yield, Result, any>, a: (_: ComponentFx<Yield, Result>, ...args: Args) => A, b: (_: A, ...args: Args) => B, c: (_: B, ...args: Args) => C, d: (_: C, ...args: Args) => D, e: (_: D, ...args: Args) => E, f: (_: E, ...args: Args) => F, g: (_: F, ...args: Args) => G): ComponentResult<Args, G>;
|
|
75
|
+
/** Applies eight ordered argument-aware transformations to the generated Fx. @since 1.0.0 @category constructors */
|
|
76
|
+
<Args extends ReadonlyArray<any>, Yield extends Effect.Effect<any, any, any>, const Result extends Renderable.Any, A, B, C, D, E, F, G, H>(body: (...args: Args) => Generator<Yield, Result, any>, a: (_: ComponentFx<Yield, Result>, ...args: Args) => A, b: (_: A, ...args: Args) => B, c: (_: B, ...args: Args) => C, d: (_: C, ...args: Args) => D, e: (_: D, ...args: Args) => E, f: (_: E, ...args: Args) => F, g: (_: F, ...args: Args) => G, h: (_: G, ...args: Args) => H): ComponentResult<Args, H>;
|
|
77
|
+
/** Applies nine ordered argument-aware transformations to the generated Fx. @since 1.0.0 @category constructors */
|
|
78
|
+
<Args extends ReadonlyArray<any>, Yield extends Effect.Effect<any, any, any>, const Result extends Renderable.Any, A, B, C, D, E, F, G, H, I>(body: (...args: Args) => Generator<Yield, Result, any>, a: (_: ComponentFx<Yield, Result>, ...args: Args) => A, b: (_: A, ...args: Args) => B, c: (_: B, ...args: Args) => C, d: (_: C, ...args: Args) => D, e: (_: D, ...args: Args) => E, f: (_: E, ...args: Args) => F, g: (_: F, ...args: Args) => G, h: (_: G, ...args: Args) => H, i: (_: H, ...args: Args) => I): ComponentResult<Args, I>;
|
|
79
|
+
/** Applies ten ordered argument-aware transformations to the generated Fx. @since 1.0.0 @category constructors */
|
|
80
|
+
<Args extends ReadonlyArray<any>, Yield extends Effect.Effect<any, any, any>, const Result extends Renderable.Any, A, B, C, D, E, F, G, H, I, J>(body: (...args: Args) => Generator<Yield, Result, any>, a: (_: ComponentFx<Yield, Result>, ...args: Args) => A, b: (_: A, ...args: Args) => B, c: (_: B, ...args: Args) => C, d: (_: C, ...args: Args) => D, e: (_: D, ...args: Args) => E, f: (_: E, ...args: Args) => F, g: (_: F, ...args: Args) => G, h: (_: G, ...args: Args) => H, i: (_: H, ...args: Args) => I, j: (_: I, ...args: Args) => J): ComponentResult<Args, J>;
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Creates an Fx component from a generator that may return any Renderable. A
|
|
85
|
+
* zero-arity generator creates an Fx directly; generators with parameters
|
|
86
|
+
* create an Fx function. Additional functions are piped over the generated Fx
|
|
87
|
+
* using the same argument-forwarding behavior as Fx.fn.
|
|
88
|
+
*
|
|
89
|
+
* @remarks
|
|
90
|
+
* ## Why
|
|
91
|
+
*
|
|
92
|
+
* Components often need Effect services before they can return a template,
|
|
93
|
+
* DOM node, render event, or other `Renderable`. This constructor turns that
|
|
94
|
+
* generator into a single `Fx<RenderEvent, E, R>` boundary without introducing
|
|
95
|
+
* a component tree or a second runtime beside Effect.
|
|
96
|
+
*
|
|
97
|
+
* ## Ownership and lifetime
|
|
98
|
+
*
|
|
99
|
+
* Construction is lazy. Each run evaluates the generator inside the running
|
|
100
|
+
* Fx's Effect lifetime; interruption and Scope finalization release everything
|
|
101
|
+
* acquired by yielded effects and by the returned renderable. The constructor
|
|
102
|
+
* does not own DOM outside the render events emitted by that renderable.
|
|
103
|
+
*
|
|
104
|
+
* ## Composition
|
|
105
|
+
*
|
|
106
|
+
* A parameterless generator returns an Fx directly. A generator with
|
|
107
|
+
* parameters returns a function, and optional pipe functions receive the
|
|
108
|
+
* generated Fx followed by the original arguments, matching `Fx.fn`.
|
|
109
|
+
*
|
|
110
|
+
* @example
|
|
111
|
+
* ```ts
|
|
112
|
+
* import { component } from "@typed/ui/Component"
|
|
113
|
+
* import { html } from "@typed/template"
|
|
114
|
+
* import { Effect } from "effect"
|
|
115
|
+
*
|
|
116
|
+
* const Greeting = component(function* (name: string) {
|
|
117
|
+
* const greeting = yield* Effect.succeed("Hello")
|
|
118
|
+
* return html`<p>${greeting} ${name}</p>`
|
|
119
|
+
* })
|
|
120
|
+
* ```
|
|
121
|
+
*
|
|
122
|
+
* @since 1.0.0
|
|
123
|
+
* @category constructors
|
|
124
|
+
*/
|
|
125
|
+
export declare const component: component.Gen;
|
|
126
|
+
export {};
|
|
127
|
+
//# sourceMappingURL=Component.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Component.d.ts","sourceRoot":"","sources":["../src/Component.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,MAAM,eAAe,CAAC;AAC7C,OAAO,EAAE,EAAE,EAAE,MAAM,WAAW,CAAC;AAC/B,OAAO,EAAsB,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAEtE,KAAK,WAAW,CAAC,KAAK,SAAS,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,EAAE,MAAM,SAAS,UAAU,CAAC,GAAG,IAAI;IAC5F,EAAE,CAAC,EAAE,CACH,UAAU,CAAC,OAAO,CAAC,MAAM,CAAC,EAC1B,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,UAAU,CAAC,KAAK,CAAC,MAAM,CAAC,EAC9C,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,UAAU,CAAC,QAAQ,CAAC,MAAM,CAAC,CACrD;CACF,SAAS,CAAC,EAAE,CAAC,EAAE,CAAC,MAAM,OAAO,EAAE,MAAM,KAAK,EAAE,MAAM,QAAQ,CAAC,CAAC,GACzD,EAAE,CAAC,EAAE,CAAC,OAAO,EAAE,KAAK,EAAE,QAAQ,CAAC,GAC/B,KAAK,CAAC;AAEV,KAAK,eAAe,CAAC,IAAI,SAAS,aAAa,CAAC,GAAG,CAAC,EAAE,MAAM,IAAI,IAAI,SAAS,SAAS,EAAE,GACpF,MAAM,GACN,CAAC,GAAG,IAAI,EAAE,IAAI,KAAK,MAAM,CAAC;AAE9B;;;;;;;;;;;;;;;;;GAiBG;AACH,yBAAiB,SAAS,CAAC;IACzB;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA4BG;IACH,KAAY,GAAG,GAAG;QAChB,iGAAiG;QACjG,CAAC,KAAK,SAAS,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,EAAE,KAAK,CAAC,MAAM,SAAS,UAAU,CAAC,GAAG,EAC9E,IAAI,EAAE,MAAM,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,CAAC,GACxC,WAAW,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;QAE9B,oHAAoH;QACpH,CACE,IAAI,SAAS,SAAS,CAAC,GAAG,EAAE,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC,EAClD,KAAK,SAAS,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,EAC1C,KAAK,CAAC,MAAM,SAAS,UAAU,CAAC,GAAG,EAEnC,IAAI,EAAE,CAAC,GAAG,IAAI,EAAE,IAAI,KAAK,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,CAAC,GACrD,CAAC,GAAG,IAAI,EAAE,IAAI,KAAK,WAAW,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;QAEjD,yGAAyG;QACzG,CACE,IAAI,SAAS,aAAa,CAAC,GAAG,CAAC,EAC/B,KAAK,SAAS,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,EAC1C,KAAK,CAAC,MAAM,SAAS,UAAU,CAAC,GAAG,EACnC,CAAC,EAED,IAAI,EAAE,CAAC,GAAG,IAAI,EAAE,IAAI,KAAK,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,CAAC,EACtD,CAAC,EAAE,CAAC,CAAC,EAAE,WAAW,CAAC,KAAK,EAAE,MAAM,CAAC,EAAE,GAAG,IAAI,EAAE,IAAI,KAAK,CAAC,GACrD,eAAe,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;QAE5B,kHAAkH;QAClH,CACE,IAAI,SAAS,aAAa,CAAC,GAAG,CAAC,EAC/B,KAAK,SAAS,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,EAC1C,KAAK,CAAC,MAAM,SAAS,UAAU,CAAC,GAAG,EACnC,CAAC,EACD,CAAC,EAED,IAAI,EAAE,CAAC,GAAG,IAAI,EAAE,IAAI,KAAK,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,CAAC,EACtD,CAAC,EAAE,CAAC,CAAC,EAAE,WAAW,CAAC,KAAK,EAAE,MAAM,CAAC,EAAE,GAAG,IAAI,EAAE,IAAI,KAAK,CAAC,EACtD,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,IAAI,EAAE,IAAI,KAAK,CAAC,GAC5B,eAAe,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;QAE5B,oHAAoH;QACpH,CACE,IAAI,SAAS,aAAa,CAAC,GAAG,CAAC,EAC/B,KAAK,SAAS,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,EAC1C,KAAK,CAAC,MAAM,SAAS,UAAU,CAAC,GAAG,EACnC,CAAC,EACD,CAAC,EACD,CAAC,EAED,IAAI,EAAE,CAAC,GAAG,IAAI,EAAE,IAAI,KAAK,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,CAAC,EACtD,CAAC,EAAE,CAAC,CAAC,EAAE,WAAW,CAAC,KAAK,EAAE,MAAM,CAAC,EAAE,GAAG,IAAI,EAAE,IAAI,KAAK,CAAC,EACtD,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,IAAI,EAAE,IAAI,KAAK,CAAC,EAC7B,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,IAAI,EAAE,IAAI,KAAK,CAAC,GAC5B,eAAe,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;QAE5B,mHAAmH;QACnH,CACE,IAAI,SAAS,aAAa,CAAC,GAAG,CAAC,EAC/B,KAAK,SAAS,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,EAC1C,KAAK,CAAC,MAAM,SAAS,UAAU,CAAC,GAAG,EACnC,CAAC,EACD,CAAC,EACD,CAAC,EACD,CAAC,EAED,IAAI,EAAE,CAAC,GAAG,IAAI,EAAE,IAAI,KAAK,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,CAAC,EACtD,CAAC,EAAE,CAAC,CAAC,EAAE,WAAW,CAAC,KAAK,EAAE,MAAM,CAAC,EAAE,GAAG,IAAI,EAAE,IAAI,KAAK,CAAC,EACtD,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,IAAI,EAAE,IAAI,KAAK,CAAC,EAC7B,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,IAAI,EAAE,IAAI,KAAK,CAAC,EAC7B,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,IAAI,EAAE,IAAI,KAAK,CAAC,GAC5B,eAAe,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;QAE5B,mHAAmH;QACnH,CACE,IAAI,SAAS,aAAa,CAAC,GAAG,CAAC,EAC/B,KAAK,SAAS,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,EAC1C,KAAK,CAAC,MAAM,SAAS,UAAU,CAAC,GAAG,EACnC,CAAC,EACD,CAAC,EACD,CAAC,EACD,CAAC,EACD,CAAC,EAED,IAAI,EAAE,CAAC,GAAG,IAAI,EAAE,IAAI,KAAK,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,CAAC,EACtD,CAAC,EAAE,CAAC,CAAC,EAAE,WAAW,CAAC,KAAK,EAAE,MAAM,CAAC,EAAE,GAAG,IAAI,EAAE,IAAI,KAAK,CAAC,EACtD,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,IAAI,EAAE,IAAI,KAAK,CAAC,EAC7B,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,IAAI,EAAE,IAAI,KAAK,CAAC,EAC7B,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,IAAI,EAAE,IAAI,KAAK,CAAC,EAC7B,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,IAAI,EAAE,IAAI,KAAK,CAAC,GAC5B,eAAe,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;QAE5B,kHAAkH;QAClH,CACE,IAAI,SAAS,aAAa,CAAC,GAAG,CAAC,EAC/B,KAAK,SAAS,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,EAC1C,KAAK,CAAC,MAAM,SAAS,UAAU,CAAC,GAAG,EACnC,CAAC,EACD,CAAC,EACD,CAAC,EACD,CAAC,EACD,CAAC,EACD,CAAC,EAED,IAAI,EAAE,CAAC,GAAG,IAAI,EAAE,IAAI,KAAK,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,CAAC,EACtD,CAAC,EAAE,CAAC,CAAC,EAAE,WAAW,CAAC,KAAK,EAAE,MAAM,CAAC,EAAE,GAAG,IAAI,EAAE,IAAI,KAAK,CAAC,EACtD,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,IAAI,EAAE,IAAI,KAAK,CAAC,EAC7B,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,IAAI,EAAE,IAAI,KAAK,CAAC,EAC7B,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,IAAI,EAAE,IAAI,KAAK,CAAC,EAC7B,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,IAAI,EAAE,IAAI,KAAK,CAAC,EAC7B,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,IAAI,EAAE,IAAI,KAAK,CAAC,GAC5B,eAAe,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;QAE5B,oHAAoH;QACpH,CACE,IAAI,SAAS,aAAa,CAAC,GAAG,CAAC,EAC/B,KAAK,SAAS,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,EAC1C,KAAK,CAAC,MAAM,SAAS,UAAU,CAAC,GAAG,EACnC,CAAC,EACD,CAAC,EACD,CAAC,EACD,CAAC,EACD,CAAC,EACD,CAAC,EACD,CAAC,EAED,IAAI,EAAE,CAAC,GAAG,IAAI,EAAE,IAAI,KAAK,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,CAAC,EACtD,CAAC,EAAE,CAAC,CAAC,EAAE,WAAW,CAAC,KAAK,EAAE,MAAM,CAAC,EAAE,GAAG,IAAI,EAAE,IAAI,KAAK,CAAC,EACtD,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,IAAI,EAAE,IAAI,KAAK,CAAC,EAC7B,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,IAAI,EAAE,IAAI,KAAK,CAAC,EAC7B,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,IAAI,EAAE,IAAI,KAAK,CAAC,EAC7B,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,IAAI,EAAE,IAAI,KAAK,CAAC,EAC7B,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,IAAI,EAAE,IAAI,KAAK,CAAC,EAC7B,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,IAAI,EAAE,IAAI,KAAK,CAAC,GAC5B,eAAe,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;QAE5B,oHAAoH;QACpH,CACE,IAAI,SAAS,aAAa,CAAC,GAAG,CAAC,EAC/B,KAAK,SAAS,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,EAC1C,KAAK,CAAC,MAAM,SAAS,UAAU,CAAC,GAAG,EACnC,CAAC,EACD,CAAC,EACD,CAAC,EACD,CAAC,EACD,CAAC,EACD,CAAC,EACD,CAAC,EACD,CAAC,EAED,IAAI,EAAE,CAAC,GAAG,IAAI,EAAE,IAAI,KAAK,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,CAAC,EACtD,CAAC,EAAE,CAAC,CAAC,EAAE,WAAW,CAAC,KAAK,EAAE,MAAM,CAAC,EAAE,GAAG,IAAI,EAAE,IAAI,KAAK,CAAC,EACtD,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,IAAI,EAAE,IAAI,KAAK,CAAC,EAC7B,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,IAAI,EAAE,IAAI,KAAK,CAAC,EAC7B,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,IAAI,EAAE,IAAI,KAAK,CAAC,EAC7B,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,IAAI,EAAE,IAAI,KAAK,CAAC,EAC7B,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,IAAI,EAAE,IAAI,KAAK,CAAC,EAC7B,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,IAAI,EAAE,IAAI,KAAK,CAAC,EAC7B,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,IAAI,EAAE,IAAI,KAAK,CAAC,GAC5B,eAAe,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;QAE5B,mHAAmH;QACnH,CACE,IAAI,SAAS,aAAa,CAAC,GAAG,CAAC,EAC/B,KAAK,SAAS,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,EAC1C,KAAK,CAAC,MAAM,SAAS,UAAU,CAAC,GAAG,EACnC,CAAC,EACD,CAAC,EACD,CAAC,EACD,CAAC,EACD,CAAC,EACD,CAAC,EACD,CAAC,EACD,CAAC,EACD,CAAC,EAED,IAAI,EAAE,CAAC,GAAG,IAAI,EAAE,IAAI,KAAK,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,CAAC,EACtD,CAAC,EAAE,CAAC,CAAC,EAAE,WAAW,CAAC,KAAK,EAAE,MAAM,CAAC,EAAE,GAAG,IAAI,EAAE,IAAI,KAAK,CAAC,EACtD,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,IAAI,EAAE,IAAI,KAAK,CAAC,EAC7B,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,IAAI,EAAE,IAAI,KAAK,CAAC,EAC7B,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,IAAI,EAAE,IAAI,KAAK,CAAC,EAC7B,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,IAAI,EAAE,IAAI,KAAK,CAAC,EAC7B,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,IAAI,EAAE,IAAI,KAAK,CAAC,EAC7B,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,IAAI,EAAE,IAAI,KAAK,CAAC,EAC7B,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,IAAI,EAAE,IAAI,KAAK,CAAC,EAC7B,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,IAAI,EAAE,IAAI,KAAK,CAAC,GAC5B,eAAe,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;QAE5B,kHAAkH;QAClH,CACE,IAAI,SAAS,aAAa,CAAC,GAAG,CAAC,EAC/B,KAAK,SAAS,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,EAC1C,KAAK,CAAC,MAAM,SAAS,UAAU,CAAC,GAAG,EACnC,CAAC,EACD,CAAC,EACD,CAAC,EACD,CAAC,EACD,CAAC,EACD,CAAC,EACD,CAAC,EACD,CAAC,EACD,CAAC,EACD,CAAC,EAED,IAAI,EAAE,CAAC,GAAG,IAAI,EAAE,IAAI,KAAK,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,CAAC,EACtD,CAAC,EAAE,CAAC,CAAC,EAAE,WAAW,CAAC,KAAK,EAAE,MAAM,CAAC,EAAE,GAAG,IAAI,EAAE,IAAI,KAAK,CAAC,EACtD,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,IAAI,EAAE,IAAI,KAAK,CAAC,EAC7B,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,IAAI,EAAE,IAAI,KAAK,CAAC,EAC7B,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,IAAI,EAAE,IAAI,KAAK,CAAC,EAC7B,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,IAAI,EAAE,IAAI,KAAK,CAAC,EAC7B,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,IAAI,EAAE,IAAI,KAAK,CAAC,EAC7B,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,IAAI,EAAE,IAAI,KAAK,CAAC,EAC7B,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,IAAI,EAAE,IAAI,KAAK,CAAC,EAC7B,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,IAAI,EAAE,IAAI,KAAK,CAAC,EAC7B,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,IAAI,EAAE,IAAI,KAAK,CAAC,GAC5B,eAAe,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;KAC7B,CAAC;CACH;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AACH,eAAO,MAAM,SAAS,EAAE,SAAS,CAAC,GA0BjC,CAAC"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { Fx } from "@typed/fx";
|
|
2
|
+
import { liftRenderableToFx } from "@typed/template";
|
|
3
|
+
/**
|
|
4
|
+
* Creates an Fx component from a generator that may return any Renderable. A
|
|
5
|
+
* zero-arity generator creates an Fx directly; generators with parameters
|
|
6
|
+
* create an Fx function. Additional functions are piped over the generated Fx
|
|
7
|
+
* using the same argument-forwarding behavior as Fx.fn.
|
|
8
|
+
*
|
|
9
|
+
* @remarks
|
|
10
|
+
* ## Why
|
|
11
|
+
*
|
|
12
|
+
* Components often need Effect services before they can return a template,
|
|
13
|
+
* DOM node, render event, or other `Renderable`. This constructor turns that
|
|
14
|
+
* generator into a single `Fx<RenderEvent, E, R>` boundary without introducing
|
|
15
|
+
* a component tree or a second runtime beside Effect.
|
|
16
|
+
*
|
|
17
|
+
* ## Ownership and lifetime
|
|
18
|
+
*
|
|
19
|
+
* Construction is lazy. Each run evaluates the generator inside the running
|
|
20
|
+
* Fx's Effect lifetime; interruption and Scope finalization release everything
|
|
21
|
+
* acquired by yielded effects and by the returned renderable. The constructor
|
|
22
|
+
* does not own DOM outside the render events emitted by that renderable.
|
|
23
|
+
*
|
|
24
|
+
* ## Composition
|
|
25
|
+
*
|
|
26
|
+
* A parameterless generator returns an Fx directly. A generator with
|
|
27
|
+
* parameters returns a function, and optional pipe functions receive the
|
|
28
|
+
* generated Fx followed by the original arguments, matching `Fx.fn`.
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* ```ts
|
|
32
|
+
* import { component } from "@typed/ui/Component"
|
|
33
|
+
* import { html } from "@typed/template"
|
|
34
|
+
* import { Effect } from "effect"
|
|
35
|
+
*
|
|
36
|
+
* const Greeting = component(function* (name: string) {
|
|
37
|
+
* const greeting = yield* Effect.succeed("Hello")
|
|
38
|
+
* return html`<p>${greeting} ${name}</p>`
|
|
39
|
+
* })
|
|
40
|
+
* ```
|
|
41
|
+
*
|
|
42
|
+
* @since 1.0.0
|
|
43
|
+
* @category constructors
|
|
44
|
+
*/
|
|
45
|
+
export const component = function (body, ...pipeables) {
|
|
46
|
+
if (body.length === 0) {
|
|
47
|
+
let result = Fx.gen(function* () {
|
|
48
|
+
const renderable = yield* body();
|
|
49
|
+
return liftRenderableToFx(renderable);
|
|
50
|
+
});
|
|
51
|
+
for (const pipeable of pipeables) {
|
|
52
|
+
result = pipeable(result);
|
|
53
|
+
}
|
|
54
|
+
return result;
|
|
55
|
+
}
|
|
56
|
+
return Fx.fn(function* (...args) {
|
|
57
|
+
const renderable = yield* body(...args);
|
|
58
|
+
return liftRenderableToFx(renderable);
|
|
59
|
+
}, ...pipeables);
|
|
60
|
+
};
|