@typed/ui 1.0.0-beta.4 → 1.0.0-beta.5
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/Meter.d.ts
ADDED
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import * as Effect from "effect/Effect";
|
|
2
|
+
import * as Schema from "effect/Schema";
|
|
3
|
+
import { RefSubject } from "@typed/fx";
|
|
4
|
+
import { type Renderable } from "@typed/template";
|
|
5
|
+
import * as Dom from "./Dom.js";
|
|
6
|
+
import type { HostResult } from "./Dom/Types.js";
|
|
7
|
+
/** Current value displayed by the meter.
|
|
8
|
+
* @remarks
|
|
9
|
+
* ## Why
|
|
10
|
+
* Renderer-independent state can be updated and tested before a meter mounts.
|
|
11
|
+
* ## Ownership and lifetime
|
|
12
|
+
* Plain state acquires no resources; the hydrated RefSubject owns observation.
|
|
13
|
+
* @since 1.0.0
|
|
14
|
+
* @category state
|
|
15
|
+
*/
|
|
16
|
+
export interface State {
|
|
17
|
+
/** Finite scalar reflected to the native meter value.
|
|
18
|
+
* @remarks
|
|
19
|
+
* ## Why
|
|
20
|
+
* One value drives DOM output and state consumers consistently.
|
|
21
|
+
* ## Ownership and lifetime
|
|
22
|
+
* Plain data retains no resources.
|
|
23
|
+
* @since 1.0.0
|
|
24
|
+
* @category state
|
|
25
|
+
*/
|
|
26
|
+
readonly value: number;
|
|
27
|
+
}
|
|
28
|
+
/** Initial finite meter value.
|
|
29
|
+
* @remarks
|
|
30
|
+
* ## Why
|
|
31
|
+
* Explicit input makes the hydration snapshot deterministic.
|
|
32
|
+
* ## Ownership and lifetime
|
|
33
|
+
* Configuration is inert and copied into state.
|
|
34
|
+
* @since 1.0.0
|
|
35
|
+
* @category state
|
|
36
|
+
*/
|
|
37
|
+
export interface InitialState {
|
|
38
|
+
/** Value used for the initial snapshot.
|
|
39
|
+
* @remarks
|
|
40
|
+
* ## Why
|
|
41
|
+
* The native meter needs a concrete value independent of its range metadata.
|
|
42
|
+
* ## Ownership and lifetime
|
|
43
|
+
* Plain data acquires no resources.
|
|
44
|
+
* @since 1.0.0
|
|
45
|
+
* @category state
|
|
46
|
+
*/
|
|
47
|
+
readonly value: number;
|
|
48
|
+
}
|
|
49
|
+
/** Schema for serializing finite meter state.
|
|
50
|
+
* @remarks
|
|
51
|
+
* ## Why
|
|
52
|
+
* Rejecting non-finite values keeps SSR and native DOM output valid.
|
|
53
|
+
* ## Ownership and lifetime
|
|
54
|
+
* The immutable schema acquires no resources.
|
|
55
|
+
* @since 1.0.0
|
|
56
|
+
* @category schemas
|
|
57
|
+
*/
|
|
58
|
+
export declare const StateSchema: Schema.Struct<{
|
|
59
|
+
readonly value: Schema.Finite;
|
|
60
|
+
}>;
|
|
61
|
+
/** Creates hydrated meter state.
|
|
62
|
+
* @remarks
|
|
63
|
+
* ## Why
|
|
64
|
+
* The state remains testable and reusable without rendering a UI component.
|
|
65
|
+
* ## Ownership and lifetime
|
|
66
|
+
* The caller's Effect Scope owns the hydrated RefSubject.
|
|
67
|
+
* @example
|
|
68
|
+
* ```ts
|
|
69
|
+
* import { Effect } from "effect"
|
|
70
|
+
* import * as Meter from "@typed/ui/Meter"
|
|
71
|
+
*
|
|
72
|
+
* const program = Effect.gen(function* () {
|
|
73
|
+
* return yield* Meter.makeState({ value: 0.72 })
|
|
74
|
+
* })
|
|
75
|
+
* ```
|
|
76
|
+
* @since 1.0.0
|
|
77
|
+
* @category constructors
|
|
78
|
+
*/
|
|
79
|
+
export declare function makeState(initial: InitialState): Effect.Effect<RefSubject.HydratedRefSubject<{
|
|
80
|
+
readonly value: number;
|
|
81
|
+
}, Schema.SchemaError, never, never>, never, import("effect/Scope").Scope>;
|
|
82
|
+
/** Updates the displayed meter value.
|
|
83
|
+
* @remarks
|
|
84
|
+
* ## Why
|
|
85
|
+
* State transitions stay outside rendering and preserve typed error/services.
|
|
86
|
+
* ## Ownership and lifetime
|
|
87
|
+
* The Effect uses the existing RefSubject lifetime and acquires no resource.
|
|
88
|
+
* @example
|
|
89
|
+
* ```ts
|
|
90
|
+
* import { Effect } from "effect"
|
|
91
|
+
* import * as Meter from "@typed/ui/Meter"
|
|
92
|
+
*
|
|
93
|
+
* const program = Effect.gen(function* () {
|
|
94
|
+
* const state = yield* Meter.makeState({ value: 0 })
|
|
95
|
+
* yield* Meter.setValue(state, 0.75)
|
|
96
|
+
* })
|
|
97
|
+
* ```
|
|
98
|
+
* @since 1.0.0
|
|
99
|
+
* @category state
|
|
100
|
+
*/
|
|
101
|
+
export declare function setValue<E, R>(state: RefSubject.RefSubject<State, E, R>, value: number): Effect.Effect<State, E, R>;
|
|
102
|
+
/** Native meter range, threshold, content, and state options.
|
|
103
|
+
* @remarks
|
|
104
|
+
* ## Why
|
|
105
|
+
* These mirror the platform meter element rather than reimplementing its
|
|
106
|
+
* accessibility and range interpretation.
|
|
107
|
+
* ## Ownership and lifetime
|
|
108
|
+
* Options are inert; rendering owns reactive subscriptions by Scope.
|
|
109
|
+
* @since 1.0.0
|
|
110
|
+
* @category models
|
|
111
|
+
*/
|
|
112
|
+
export interface MeterOptions extends Dom.HostOptions<HTMLMeterElement> {
|
|
113
|
+
/** Hydrated value state borrowed by the element.
|
|
114
|
+
* @remarks
|
|
115
|
+
* ## Why
|
|
116
|
+
* One source keeps the native value and application state aligned.
|
|
117
|
+
* ## Ownership and lifetime
|
|
118
|
+
* Its original Scope owns it; the component only subscribes while mounted.
|
|
119
|
+
* @since 1.0.0
|
|
120
|
+
* @category state
|
|
121
|
+
*/
|
|
122
|
+
readonly state: RefSubject.HydratedRefSubject<State, Schema.SchemaError>;
|
|
123
|
+
/** Lower bound of the measured range.
|
|
124
|
+
* @remarks
|
|
125
|
+
* ## Why
|
|
126
|
+
* Native meter semantics use the bound to interpret `value`.
|
|
127
|
+
* ## Ownership and lifetime
|
|
128
|
+
* Dynamic attributes follow the component Scope.
|
|
129
|
+
* @since 1.0.0
|
|
130
|
+
* @category attributes
|
|
131
|
+
*/
|
|
132
|
+
readonly min?: Renderable.Any<number | null | undefined>;
|
|
133
|
+
/** Upper bound of the measured range.
|
|
134
|
+
* @remarks
|
|
135
|
+
* ## Why
|
|
136
|
+
* Native meter semantics use the bound to interpret `value`.
|
|
137
|
+
* ## Ownership and lifetime
|
|
138
|
+
* Dynamic attributes follow the component Scope.
|
|
139
|
+
* @since 1.0.0
|
|
140
|
+
* @category attributes
|
|
141
|
+
*/
|
|
142
|
+
readonly max?: Renderable.Any<number | null | undefined>;
|
|
143
|
+
/** Boundary below which values are considered low.
|
|
144
|
+
* @remarks
|
|
145
|
+
* ## Why
|
|
146
|
+
* The browser uses this native threshold with `high` and `optimum`.
|
|
147
|
+
* ## Ownership and lifetime
|
|
148
|
+
* Dynamic attributes follow the component Scope.
|
|
149
|
+
* @since 1.0.0
|
|
150
|
+
* @category attributes
|
|
151
|
+
*/
|
|
152
|
+
readonly low?: Renderable.Any<number | null | undefined>;
|
|
153
|
+
/** Boundary above which values are considered high.
|
|
154
|
+
* @remarks
|
|
155
|
+
* ## Why
|
|
156
|
+
* It preserves native threshold semantics and accessibility exposure.
|
|
157
|
+
* ## Ownership and lifetime
|
|
158
|
+
* Dynamic attributes follow the component Scope.
|
|
159
|
+
* @since 1.0.0
|
|
160
|
+
* @category attributes
|
|
161
|
+
*/
|
|
162
|
+
readonly high?: Renderable.Any<number | null | undefined>;
|
|
163
|
+
/** Preferred value within the meter range.
|
|
164
|
+
* @remarks
|
|
165
|
+
* ## Why
|
|
166
|
+
* Native rendering interprets threshold quality relative to this value.
|
|
167
|
+
* ## Ownership and lifetime
|
|
168
|
+
* Dynamic attributes follow the component Scope.
|
|
169
|
+
* @since 1.0.0
|
|
170
|
+
* @category attributes
|
|
171
|
+
*/
|
|
172
|
+
readonly optimum?: Renderable.Any<number | null | undefined>;
|
|
173
|
+
/** Fallback content rendered inside the meter.
|
|
174
|
+
* @remarks
|
|
175
|
+
* ## Why
|
|
176
|
+
* Text content provides useful output where meter rendering is unavailable.
|
|
177
|
+
* ## Ownership and lifetime
|
|
178
|
+
* Dynamic content follows the component Scope.
|
|
179
|
+
* @since 1.0.0
|
|
180
|
+
* @category content
|
|
181
|
+
*/
|
|
182
|
+
readonly content?: Renderable.Any;
|
|
183
|
+
}
|
|
184
|
+
declare function internalProps<const Options extends MeterOptions>(options: Options): ({ property }: Dom.InternalPropsHelpers<Options>) => {
|
|
185
|
+
readonly value: RefSubject.Computed<number, Schema.SchemaError, never>;
|
|
186
|
+
readonly min: Exclude<Dom.Property<Options, "min">, Dom.Nullish> | undefined;
|
|
187
|
+
readonly max: Exclude<Dom.Property<Options, "max">, Dom.Nullish> | undefined;
|
|
188
|
+
readonly low: Exclude<Dom.Property<Options, "low">, Dom.Nullish> | undefined;
|
|
189
|
+
readonly high: Exclude<Dom.Property<Options, "high">, Dom.Nullish> | undefined;
|
|
190
|
+
readonly optimum: Exclude<Dom.Property<Options, "optimum">, Dom.Nullish> | undefined;
|
|
191
|
+
readonly ref: RefSubject.HydratedRefSubject<State, Schema.SchemaError, never, never>;
|
|
192
|
+
};
|
|
193
|
+
type MeterInternalProps<Options extends MeterOptions> = ReturnType<ReturnType<typeof internalProps<Options>>>;
|
|
194
|
+
/** Renders a native `<meter>` synchronized with hydrated state.
|
|
195
|
+
* @remarks
|
|
196
|
+
* ## Why
|
|
197
|
+
* The platform owns range semantics and accessibility while Typed composes
|
|
198
|
+
* reactive state and custom hosts around the real element.
|
|
199
|
+
* ## Ownership and lifetime
|
|
200
|
+
* Running the Fx owns attribute/state subscriptions until its Scope closes. A
|
|
201
|
+
* custom host must preserve native meter attributes and the hydration ref.
|
|
202
|
+
* @example
|
|
203
|
+
* ```ts
|
|
204
|
+
* import { Effect } from "effect"
|
|
205
|
+
* import * as Meter from "@typed/ui/Meter"
|
|
206
|
+
*
|
|
207
|
+
* const program = Effect.gen(function* () {
|
|
208
|
+
* const state = yield* Meter.makeState({ value: 64 })
|
|
209
|
+
* return Meter.Meter({ state, min: 0, max: 100 })
|
|
210
|
+
* })
|
|
211
|
+
* ```
|
|
212
|
+
* @since 1.0.0
|
|
213
|
+
* @category components
|
|
214
|
+
*/
|
|
215
|
+
export declare function Meter<const Options extends MeterOptions, const Host extends HostResult = never>(options: Options, host?: Dom.HostOverride<Dom.RenderHostProps<Options, MeterInternalProps<Options>>, Options["content"], Host>): import("./Dom/Types.js").HostComponent<Host | Options>;
|
|
216
|
+
export {};
|
|
217
|
+
//# sourceMappingURL=Meter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Meter.d.ts","sourceRoot":"","sources":["../src/Meter.ts"],"names":[],"mappings":"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"}
|
package/dist/Meter.js
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import * as Schema from "effect/Schema";
|
|
2
|
+
import { RefSubject } from "@typed/fx";
|
|
3
|
+
import { html } from "@typed/template";
|
|
4
|
+
import * as Dom from "./Dom.js";
|
|
5
|
+
/** Schema for serializing finite meter state.
|
|
6
|
+
* @remarks
|
|
7
|
+
* ## Why
|
|
8
|
+
* Rejecting non-finite values keeps SSR and native DOM output valid.
|
|
9
|
+
* ## Ownership and lifetime
|
|
10
|
+
* The immutable schema acquires no resources.
|
|
11
|
+
* @since 1.0.0
|
|
12
|
+
* @category schemas
|
|
13
|
+
*/
|
|
14
|
+
export const StateSchema = Schema.Struct({ value: Schema.Finite });
|
|
15
|
+
/** Creates hydrated meter state.
|
|
16
|
+
* @remarks
|
|
17
|
+
* ## Why
|
|
18
|
+
* The state remains testable and reusable without rendering a UI component.
|
|
19
|
+
* ## Ownership and lifetime
|
|
20
|
+
* The caller's Effect Scope owns the hydrated RefSubject.
|
|
21
|
+
* @example
|
|
22
|
+
* ```ts
|
|
23
|
+
* import { Effect } from "effect"
|
|
24
|
+
* import * as Meter from "@typed/ui/Meter"
|
|
25
|
+
*
|
|
26
|
+
* const program = Effect.gen(function* () {
|
|
27
|
+
* return yield* Meter.makeState({ value: 0.72 })
|
|
28
|
+
* })
|
|
29
|
+
* ```
|
|
30
|
+
* @since 1.0.0
|
|
31
|
+
* @category constructors
|
|
32
|
+
*/
|
|
33
|
+
export function makeState(initial) {
|
|
34
|
+
return RefSubject.hydrate(StateSchema, initial);
|
|
35
|
+
}
|
|
36
|
+
/** Updates the displayed meter value.
|
|
37
|
+
* @remarks
|
|
38
|
+
* ## Why
|
|
39
|
+
* State transitions stay outside rendering and preserve typed error/services.
|
|
40
|
+
* ## Ownership and lifetime
|
|
41
|
+
* The Effect uses the existing RefSubject lifetime and acquires no resource.
|
|
42
|
+
* @example
|
|
43
|
+
* ```ts
|
|
44
|
+
* import { Effect } from "effect"
|
|
45
|
+
* import * as Meter from "@typed/ui/Meter"
|
|
46
|
+
*
|
|
47
|
+
* const program = Effect.gen(function* () {
|
|
48
|
+
* const state = yield* Meter.makeState({ value: 0 })
|
|
49
|
+
* yield* Meter.setValue(state, 0.75)
|
|
50
|
+
* })
|
|
51
|
+
* ```
|
|
52
|
+
* @since 1.0.0
|
|
53
|
+
* @category state
|
|
54
|
+
*/
|
|
55
|
+
export function setValue(state, value) {
|
|
56
|
+
return RefSubject.update(state, (current) => ({ ...current, value }));
|
|
57
|
+
}
|
|
58
|
+
function internalProps(options) {
|
|
59
|
+
return ({ property }) => ({
|
|
60
|
+
value: RefSubject.map(options.state, (state) => state.value),
|
|
61
|
+
min: property("min", undefined),
|
|
62
|
+
max: property("max", undefined),
|
|
63
|
+
low: property("low", undefined),
|
|
64
|
+
high: property("high", undefined),
|
|
65
|
+
optimum: property("optimum", undefined),
|
|
66
|
+
ref: options.state,
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
/** Renders a native `<meter>` synchronized with hydrated state.
|
|
70
|
+
* @remarks
|
|
71
|
+
* ## Why
|
|
72
|
+
* The platform owns range semantics and accessibility while Typed composes
|
|
73
|
+
* reactive state and custom hosts around the real element.
|
|
74
|
+
* ## Ownership and lifetime
|
|
75
|
+
* Running the Fx owns attribute/state subscriptions until its Scope closes. A
|
|
76
|
+
* custom host must preserve native meter attributes and the hydration ref.
|
|
77
|
+
* @example
|
|
78
|
+
* ```ts
|
|
79
|
+
* import { Effect } from "effect"
|
|
80
|
+
* import * as Meter from "@typed/ui/Meter"
|
|
81
|
+
*
|
|
82
|
+
* const program = Effect.gen(function* () {
|
|
83
|
+
* const state = yield* Meter.makeState({ value: 64 })
|
|
84
|
+
* return Meter.Meter({ state, min: 0, max: 100 })
|
|
85
|
+
* })
|
|
86
|
+
* ```
|
|
87
|
+
* @since 1.0.0
|
|
88
|
+
* @category components
|
|
89
|
+
*/
|
|
90
|
+
export function Meter(options, host) {
|
|
91
|
+
return Dom.renderHost()(options, host, internalProps(options), options.content ?? "", (props, content) => {
|
|
92
|
+
return html `<meter ...${props}>${content}</meter>`;
|
|
93
|
+
});
|
|
94
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import * as Effect from "effect/Effect";
|
|
2
|
+
import type * as Scope from "effect/Scope";
|
|
3
|
+
import { RefSubject } from "@typed/fx";
|
|
4
|
+
interface State {
|
|
5
|
+
readonly open: boolean;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Creates a scoped ref that synchronizes state to a native `<details>` element.
|
|
9
|
+
*
|
|
10
|
+
* @remarks
|
|
11
|
+
* ## Why
|
|
12
|
+
*
|
|
13
|
+
* `<details>` already owns disclosure rendering and keyboard behavior. This ref
|
|
14
|
+
* only applies pushed `open` state to that real element; the public Disclosure
|
|
15
|
+
* component listens to native `toggle` events for the reverse direction.
|
|
16
|
+
*
|
|
17
|
+
* ## Ownership and lifetime
|
|
18
|
+
*
|
|
19
|
+
* Applying the ref forks one observer in the current Effect Scope. Scope
|
|
20
|
+
* finalization interrupts it; the state remains owned by its original Scope and
|
|
21
|
+
* the DOM element remains owned by its renderer. Use only one hydration ref
|
|
22
|
+
* owner for an element.
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```ts
|
|
26
|
+
* import { Effect } from "effect"
|
|
27
|
+
* import * as Disclosure from "@typed/ui/Disclosure"
|
|
28
|
+
* import * as NativeDetails from "@typed/ui/NativeDetails"
|
|
29
|
+
*
|
|
30
|
+
* const program = Effect.gen(function* () {
|
|
31
|
+
* const state = yield* Disclosure.makeState()
|
|
32
|
+
* return NativeDetails.ref(state)
|
|
33
|
+
* })
|
|
34
|
+
* ```
|
|
35
|
+
*
|
|
36
|
+
* @since 1.0.0
|
|
37
|
+
* @category refs
|
|
38
|
+
*/
|
|
39
|
+
export declare function ref<S extends State, E, R>(state: RefSubject.RefSubject<S, E, R>): (element: HTMLDetailsElement) => Effect.Effect<void, E, R | Scope.Scope>;
|
|
40
|
+
export {};
|
|
41
|
+
//# sourceMappingURL=NativeDetails.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"NativeDetails.d.ts","sourceRoot":"","sources":["../src/NativeDetails.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,KAAK,KAAK,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAM,UAAU,EAAE,MAAM,WAAW,CAAC;AAE3C,UAAU,KAAK;IACb,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;CACxB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAgB,GAAG,CAAC,CAAC,SAAS,KAAK,EAAE,CAAC,EAAE,CAAC,EACvC,KAAK,EAAE,UAAU,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,GACpC,CAAC,OAAO,EAAE,kBAAkB,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,CAe1E"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import * as Effect from "effect/Effect";
|
|
2
|
+
import { Fx } from "@typed/fx";
|
|
3
|
+
/**
|
|
4
|
+
* Creates a scoped ref that synchronizes state to a native `<details>` element.
|
|
5
|
+
*
|
|
6
|
+
* @remarks
|
|
7
|
+
* ## Why
|
|
8
|
+
*
|
|
9
|
+
* `<details>` already owns disclosure rendering and keyboard behavior. This ref
|
|
10
|
+
* only applies pushed `open` state to that real element; the public Disclosure
|
|
11
|
+
* component listens to native `toggle` events for the reverse direction.
|
|
12
|
+
*
|
|
13
|
+
* ## Ownership and lifetime
|
|
14
|
+
*
|
|
15
|
+
* Applying the ref forks one observer in the current Effect Scope. Scope
|
|
16
|
+
* finalization interrupts it; the state remains owned by its original Scope and
|
|
17
|
+
* the DOM element remains owned by its renderer. Use only one hydration ref
|
|
18
|
+
* owner for an element.
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```ts
|
|
22
|
+
* import { Effect } from "effect"
|
|
23
|
+
* import * as Disclosure from "@typed/ui/Disclosure"
|
|
24
|
+
* import * as NativeDetails from "@typed/ui/NativeDetails"
|
|
25
|
+
*
|
|
26
|
+
* const program = Effect.gen(function* () {
|
|
27
|
+
* const state = yield* Disclosure.makeState()
|
|
28
|
+
* return NativeDetails.ref(state)
|
|
29
|
+
* })
|
|
30
|
+
* ```
|
|
31
|
+
*
|
|
32
|
+
* @since 1.0.0
|
|
33
|
+
* @category refs
|
|
34
|
+
*/
|
|
35
|
+
export function ref(state) {
|
|
36
|
+
return Effect.fn((element) => Effect.asVoid(Effect.forkScoped(Fx.observe(state, Effect.fn((value) => Effect.sync(() => {
|
|
37
|
+
if (element.open !== value.open)
|
|
38
|
+
element.open = value.open;
|
|
39
|
+
}))))));
|
|
40
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import * as Effect from "effect/Effect";
|
|
2
|
+
import type * as Scope from "effect/Scope";
|
|
3
|
+
import { RefSubject } from "@typed/fx";
|
|
4
|
+
interface State {
|
|
5
|
+
readonly open: boolean;
|
|
6
|
+
}
|
|
7
|
+
/** Native dialog synchronization mode.
|
|
8
|
+
* @remarks
|
|
9
|
+
* ## Why
|
|
10
|
+
* The choice maps directly to `showModal()` or `show()` rather than simulating
|
|
11
|
+
* modality in application code.
|
|
12
|
+
* ## Ownership and lifetime
|
|
13
|
+
* Options are inert and retain no resources.
|
|
14
|
+
* @since 1.0.0
|
|
15
|
+
* @category models
|
|
16
|
+
*/
|
|
17
|
+
export interface Options {
|
|
18
|
+
/** Whether opening uses the modal top layer; defaults to true.
|
|
19
|
+
* @remarks
|
|
20
|
+
* ## Why
|
|
21
|
+
* Modal and non-modal dialogs have different focus, inertness, and dismissal
|
|
22
|
+
* behavior owned by the browser.
|
|
23
|
+
* ## Ownership and lifetime
|
|
24
|
+
* The flag is read during synchronization and retains no resources.
|
|
25
|
+
* @since 1.0.0
|
|
26
|
+
* @category behavior
|
|
27
|
+
*/
|
|
28
|
+
readonly modal?: boolean;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Creates a scoped ref that drives a real `HTMLDialogElement` from state.
|
|
32
|
+
*
|
|
33
|
+
* @remarks
|
|
34
|
+
* ## Why
|
|
35
|
+
*
|
|
36
|
+
* The ref delegates open/close, top-layer placement, focus handling, and modal
|
|
37
|
+
* inertness to `showModal()`, `show()`, and `close()`. It does not reproduce a
|
|
38
|
+
* dialog with ordinary divs or synthetic events.
|
|
39
|
+
*
|
|
40
|
+
* ## Ownership and lifetime
|
|
41
|
+
*
|
|
42
|
+
* Applying the ref forks an observer in the current Effect Scope. Finalization
|
|
43
|
+
* interrupts that observer but does not close state owned elsewhere or remove
|
|
44
|
+
* the element. `Dialog.Content` handles native `cancel`, `close`, and `toggle`
|
|
45
|
+
* events and composes exactly one hydration owner.
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* ```ts
|
|
49
|
+
* import { Effect } from "effect"
|
|
50
|
+
* import * as Dialog from "@typed/ui/Dialog"
|
|
51
|
+
* import * as NativeDialog from "@typed/ui/NativeDialog"
|
|
52
|
+
*
|
|
53
|
+
* const program = Effect.gen(function* () {
|
|
54
|
+
* const state = yield* Dialog.makeState()
|
|
55
|
+
* return NativeDialog.ref(state, { modal: true })
|
|
56
|
+
* })
|
|
57
|
+
* ```
|
|
58
|
+
*
|
|
59
|
+
* @since 1.0.0
|
|
60
|
+
* @category refs
|
|
61
|
+
*/
|
|
62
|
+
export declare function ref<S extends State, E, R>(state: RefSubject.RefSubject<S, E, R>, options?: Options): (element: HTMLDialogElement) => Effect.Effect<void, E, R | Scope.Scope>;
|
|
63
|
+
export {};
|
|
64
|
+
//# sourceMappingURL=NativeDialog.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"NativeDialog.d.ts","sourceRoot":"","sources":["../src/NativeDialog.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,KAAK,KAAK,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAM,UAAU,EAAE,MAAM,WAAW,CAAC;AAE3C,UAAU,KAAK;IACb,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;CACxB;AAED;;;;;;;;;GASG;AACH,MAAM,WAAW,OAAO;IACtB;;;;;;;;;OASG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAgB,GAAG,CAAC,CAAC,SAAS,KAAK,EAAE,CAAC,EAAE,CAAC,EACvC,KAAK,EAAE,UAAU,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EACrC,OAAO,GAAE,OAAY,GACpB,CAAC,OAAO,EAAE,iBAAiB,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,CAWzE"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import * as Effect from "effect/Effect";
|
|
2
|
+
import { Fx } from "@typed/fx";
|
|
3
|
+
/**
|
|
4
|
+
* Creates a scoped ref that drives a real `HTMLDialogElement` from state.
|
|
5
|
+
*
|
|
6
|
+
* @remarks
|
|
7
|
+
* ## Why
|
|
8
|
+
*
|
|
9
|
+
* The ref delegates open/close, top-layer placement, focus handling, and modal
|
|
10
|
+
* inertness to `showModal()`, `show()`, and `close()`. It does not reproduce a
|
|
11
|
+
* dialog with ordinary divs or synthetic events.
|
|
12
|
+
*
|
|
13
|
+
* ## Ownership and lifetime
|
|
14
|
+
*
|
|
15
|
+
* Applying the ref forks an observer in the current Effect Scope. Finalization
|
|
16
|
+
* interrupts that observer but does not close state owned elsewhere or remove
|
|
17
|
+
* the element. `Dialog.Content` handles native `cancel`, `close`, and `toggle`
|
|
18
|
+
* events and composes exactly one hydration owner.
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```ts
|
|
22
|
+
* import { Effect } from "effect"
|
|
23
|
+
* import * as Dialog from "@typed/ui/Dialog"
|
|
24
|
+
* import * as NativeDialog from "@typed/ui/NativeDialog"
|
|
25
|
+
*
|
|
26
|
+
* const program = Effect.gen(function* () {
|
|
27
|
+
* const state = yield* Dialog.makeState()
|
|
28
|
+
* return NativeDialog.ref(state, { modal: true })
|
|
29
|
+
* })
|
|
30
|
+
* ```
|
|
31
|
+
*
|
|
32
|
+
* @since 1.0.0
|
|
33
|
+
* @category refs
|
|
34
|
+
*/
|
|
35
|
+
export function ref(state, options = {}) {
|
|
36
|
+
return Effect.fn((element) => Effect.asVoid(Effect.forkScoped(Fx.observe(state, Effect.fn((value) => Effect.sync(() => synchronize(element, value.open, options)))))));
|
|
37
|
+
}
|
|
38
|
+
function synchronize(element, open, options) {
|
|
39
|
+
if (open) {
|
|
40
|
+
if (element.open)
|
|
41
|
+
return;
|
|
42
|
+
if (options.modal === false)
|
|
43
|
+
element.show();
|
|
44
|
+
else
|
|
45
|
+
element.showModal();
|
|
46
|
+
}
|
|
47
|
+
else if (element.open) {
|
|
48
|
+
element.close();
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import * as Effect from "effect/Effect";
|
|
2
|
+
import type * as Scope from "effect/Scope";
|
|
3
|
+
import { RefSubject } from "@typed/fx";
|
|
4
|
+
interface State {
|
|
5
|
+
readonly open: boolean;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Creates a scoped ref that synchronizes state with the native Popover API.
|
|
9
|
+
*
|
|
10
|
+
* @remarks
|
|
11
|
+
* ## Why
|
|
12
|
+
*
|
|
13
|
+
* The browser owns top-layer placement and popover lifecycle. The ref checks
|
|
14
|
+
* `:popover-open` before calling `showPopover()` or `hidePopover()`, avoiding
|
|
15
|
+
* invalid duplicate transitions while preserving native toggle events.
|
|
16
|
+
*
|
|
17
|
+
* ## Ownership and lifetime
|
|
18
|
+
*
|
|
19
|
+
* Applying the ref forks one observer in the current Effect Scope. Closing the
|
|
20
|
+
* Scope interrupts it; it does not remove the host or close the caller-owned
|
|
21
|
+
* state. The host must support the Popover API and retain its `popover`
|
|
22
|
+
* attribute. Only one hydration owner may be composed for the element.
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```ts
|
|
26
|
+
* import { Effect } from "effect"
|
|
27
|
+
* import * as NativePopover from "@typed/ui/NativePopover"
|
|
28
|
+
* import * as Popover from "@typed/ui/Popover"
|
|
29
|
+
*
|
|
30
|
+
* const program = Effect.gen(function* () {
|
|
31
|
+
* const state = yield* Popover.makeState()
|
|
32
|
+
* return NativePopover.ref(state)
|
|
33
|
+
* })
|
|
34
|
+
* ```
|
|
35
|
+
*
|
|
36
|
+
* @since 1.0.0
|
|
37
|
+
* @category refs
|
|
38
|
+
*/
|
|
39
|
+
export declare function ref<S extends State, E, R>(state: RefSubject.RefSubject<S, E, R>): (element: HTMLElement) => Effect.Effect<void, E, R | Scope.Scope>;
|
|
40
|
+
export {};
|
|
41
|
+
//# sourceMappingURL=NativePopover.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"NativePopover.d.ts","sourceRoot":"","sources":["../src/NativePopover.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,KAAK,KAAK,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAM,UAAU,EAAE,MAAM,WAAW,CAAC;AAE3C,UAAU,KAAK;IACb,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;CACxB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAgB,GAAG,CAAC,CAAC,SAAS,KAAK,EAAE,CAAC,EAAE,CAAC,EACvC,KAAK,EAAE,UAAU,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,GACpC,CAAC,OAAO,EAAE,WAAW,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,CAWnE"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import * as Effect from "effect/Effect";
|
|
2
|
+
import { Fx } from "@typed/fx";
|
|
3
|
+
/**
|
|
4
|
+
* Creates a scoped ref that synchronizes state with the native Popover API.
|
|
5
|
+
*
|
|
6
|
+
* @remarks
|
|
7
|
+
* ## Why
|
|
8
|
+
*
|
|
9
|
+
* The browser owns top-layer placement and popover lifecycle. The ref checks
|
|
10
|
+
* `:popover-open` before calling `showPopover()` or `hidePopover()`, avoiding
|
|
11
|
+
* invalid duplicate transitions while preserving native toggle events.
|
|
12
|
+
*
|
|
13
|
+
* ## Ownership and lifetime
|
|
14
|
+
*
|
|
15
|
+
* Applying the ref forks one observer in the current Effect Scope. Closing the
|
|
16
|
+
* Scope interrupts it; it does not remove the host or close the caller-owned
|
|
17
|
+
* state. The host must support the Popover API and retain its `popover`
|
|
18
|
+
* attribute. Only one hydration owner may be composed for the element.
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```ts
|
|
22
|
+
* import { Effect } from "effect"
|
|
23
|
+
* import * as NativePopover from "@typed/ui/NativePopover"
|
|
24
|
+
* import * as Popover from "@typed/ui/Popover"
|
|
25
|
+
*
|
|
26
|
+
* const program = Effect.gen(function* () {
|
|
27
|
+
* const state = yield* Popover.makeState()
|
|
28
|
+
* return NativePopover.ref(state)
|
|
29
|
+
* })
|
|
30
|
+
* ```
|
|
31
|
+
*
|
|
32
|
+
* @since 1.0.0
|
|
33
|
+
* @category refs
|
|
34
|
+
*/
|
|
35
|
+
export function ref(state) {
|
|
36
|
+
return Effect.fn((element) => Effect.asVoid(Effect.forkScoped(Fx.observe(state, Effect.fn((value) => Effect.sync(() => synchronize(element, value.open)))))));
|
|
37
|
+
}
|
|
38
|
+
function synchronize(element, open) {
|
|
39
|
+
if (open) {
|
|
40
|
+
if (!element.matches(":popover-open"))
|
|
41
|
+
element.showPopover();
|
|
42
|
+
}
|
|
43
|
+
else if (element.matches(":popover-open")) {
|
|
44
|
+
element.hidePopover();
|
|
45
|
+
}
|
|
46
|
+
}
|