@typed/ui 1.0.0-beta.0 → 1.0.0-beta.10
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 +78 -26
- package/dist/Alert.d.ts +72 -0
- package/dist/Alert.d.ts.map +1 -0
- package/dist/Alert.js +40 -0
- package/dist/Button.d.ts +100 -0
- package/dist/Button.d.ts.map +1 -0
- package/dist/Button.js +42 -0
- package/dist/Carousel.d.ts +346 -0
- package/dist/Carousel.d.ts.map +1 -0
- package/dist/Carousel.js +264 -0
- package/dist/Checkbox.d.ts +168 -0
- package/dist/Checkbox.d.ts.map +1 -0
- package/dist/Checkbox.js +146 -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 +367 -0
- package/dist/Combobox.d.ts.map +1 -0
- package/dist/Combobox.js +300 -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 +357 -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 +1727 -0
- package/dist/Form.d.ts.map +1 -0
- package/dist/Form.js +1142 -0
- package/dist/Grid.d.ts +388 -0
- package/dist/Grid.d.ts.map +1 -0
- package/dist/Grid.js +284 -0
- package/dist/Group.d.ts +128 -0
- package/dist/Group.d.ts.map +1 -0
- package/dist/Group.js +71 -0
- package/dist/Heading.d.ts +87 -0
- package/dist/Heading.d.ts.map +1 -0
- package/dist/Heading.js +58 -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 +199 -56
- package/dist/Link.d.ts +63 -28
- package/dist/Link.d.ts.map +1 -1
- package/dist/Link.js +84 -37
- package/dist/Listbox.d.ts +305 -0
- package/dist/Listbox.d.ts.map +1 -0
- package/dist/Listbox.js +245 -0
- package/dist/Menu.d.ts +663 -0
- package/dist/Menu.d.ts.map +1 -0
- package/dist/Menu.js +569 -0
- package/dist/Menubar.d.ts +249 -0
- package/dist/Menubar.d.ts.map +1 -0
- package/dist/Menubar.js +207 -0
- package/dist/Meter.d.ts +157 -0
- package/dist/Meter.d.ts.map +1 -0
- package/dist/Meter.js +87 -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 +66 -0
- package/dist/NativeDialog.d.ts.map +1 -0
- package/dist/NativeDialog.js +88 -0
- package/dist/NativePopover.d.ts +43 -0
- package/dist/NativePopover.d.ts.map +1 -0
- package/dist/NativePopover.js +84 -0
- package/dist/Popover.d.ts +240 -0
- package/dist/Popover.d.ts.map +1 -0
- package/dist/Popover.js +140 -0
- package/dist/RadioGroup.d.ts +330 -0
- package/dist/RadioGroup.d.ts.map +1 -0
- package/dist/RadioGroup.js +241 -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 +418 -0
- package/dist/Select.d.ts.map +1 -0
- package/dist/Select.js +357 -0
- package/dist/Separator.d.ts +58 -0
- package/dist/Separator.d.ts.map +1 -0
- package/dist/Separator.js +32 -0
- package/dist/Slider.d.ts +141 -0
- package/dist/Slider.d.ts.map +1 -0
- package/dist/Slider.js +101 -0
- package/dist/SpinButton.d.ts +141 -0
- package/dist/SpinButton.d.ts.map +1 -0
- package/dist/SpinButton.js +101 -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 +148 -0
- package/dist/Switch.d.ts.map +1 -0
- package/dist/Switch.js +110 -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 +411 -0
- package/dist/Tabs.d.ts.map +1 -0
- package/dist/Tabs.js +262 -0
- package/dist/Toolbar.d.ts +248 -0
- package/dist/Toolbar.d.ts.map +1 -0
- package/dist/Toolbar.js +187 -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 +405 -0
- package/dist/Tree.d.ts.map +1 -0
- package/dist/Tree.js +333 -0
- package/dist/TreeGrid.d.ts +426 -0
- package/dist/TreeGrid.d.ts.map +1 -0
- package/dist/TreeGrid.js +308 -0
- package/dist/VisuallyHidden.d.ts +68 -0
- package/dist/VisuallyHidden.d.ts.map +1 -0
- package/dist/VisuallyHidden.js +44 -0
- package/dist/WindowSplitter.d.ts +336 -0
- package/dist/WindowSplitter.d.ts.map +1 -0
- package/dist/WindowSplitter.js +305 -0
- package/dist/index.d.ts +48 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +48 -0
- package/package.json +47 -14
- package/src/HttpRouter.test.ts +0 -294
- package/src/HttpRouter.ts +0 -168
- package/src/Link.test.ts +0 -85
- package/src/Link.ts +0 -107
- package/src/index.ts +0 -2
- package/tsconfig.json +0 -6
package/dist/Slider.js
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Native range input publishing numeric updates during input.
|
|
3
|
+
* Keep programmatic values coherent with bounds and control expensive downstream work separately.
|
|
4
|
+
*
|
|
5
|
+
* Read the [Slider guide](/explore/ui-slider) for a complete example.
|
|
6
|
+
*
|
|
7
|
+
* [APG interaction reference](https://www.w3.org/WAI/ARIA/apg/patterns/slider/).
|
|
8
|
+
* @since 1.0.0
|
|
9
|
+
* @category Overview
|
|
10
|
+
* @packageDocumentation
|
|
11
|
+
*/
|
|
12
|
+
import * as Effect from "effect/Effect";
|
|
13
|
+
import * as Schema from "effect/Schema";
|
|
14
|
+
import { RefSubject } from "@typed/fx";
|
|
15
|
+
import { EventHandler, html } from "@typed/template";
|
|
16
|
+
import * as Dom from "./Dom.js";
|
|
17
|
+
/**
|
|
18
|
+
*/
|
|
19
|
+
export const StateSchema = Schema.Struct({ value: Schema.Finite });
|
|
20
|
+
/**
|
|
21
|
+
* Creates hydrated slider state.
|
|
22
|
+
* @remarks
|
|
23
|
+
* State can be composed and tested independently of the renderer.
|
|
24
|
+
* The calling Effect Scope owns the returned RefSubject.
|
|
25
|
+
* @example
|
|
26
|
+
* ```ts
|
|
27
|
+
* import { Effect } from "effect"
|
|
28
|
+
* import * as Slider from "@typed/ui/Slider"
|
|
29
|
+
*
|
|
30
|
+
* const program = Effect.gen(function* () {
|
|
31
|
+
* return yield* Slider.makeState({ value: 50 })
|
|
32
|
+
* })
|
|
33
|
+
* ```
|
|
34
|
+
* @since 1.0.0
|
|
35
|
+
* @category State construction
|
|
36
|
+
*/
|
|
37
|
+
export function makeState(initial) {
|
|
38
|
+
return RefSubject.hydrate(StateSchema, initial);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Assigns a range value without clamping or snapping it.
|
|
42
|
+
*
|
|
43
|
+
* @remarks
|
|
44
|
+
* min, max, and step belong to the native input options and are not consulted here. Validate or
|
|
45
|
+
* normalize programmatic values before assignment so the subject and browser-sanitized range
|
|
46
|
+
* value stay consistent.
|
|
47
|
+
* @since 1.0.0
|
|
48
|
+
* @category State transitions
|
|
49
|
+
*/
|
|
50
|
+
export function setValue(state, value) {
|
|
51
|
+
return RefSubject.update(state, (current) => ({ ...current, value }));
|
|
52
|
+
}
|
|
53
|
+
function internalProps(options) {
|
|
54
|
+
return ({ property }) => ({
|
|
55
|
+
type: "range",
|
|
56
|
+
value: RefSubject.map(options.state, (state) => state.value),
|
|
57
|
+
".value": RefSubject.map(options.state, (state) => String(state.value)),
|
|
58
|
+
min: property("min", undefined),
|
|
59
|
+
max: property("max", undefined),
|
|
60
|
+
step: property("step", undefined),
|
|
61
|
+
oninput: EventHandler.make(Effect.fn((event) => setValue(options.state, Dom.currentTarget(event).valueAsNumber))),
|
|
62
|
+
ref: options.state,
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Renders a native range input whose input events update numeric state.
|
|
67
|
+
*
|
|
68
|
+
* @remarks
|
|
69
|
+
* Dragging publishes through input, so expensive downstream work should control its own update
|
|
70
|
+
* rate. Native min/max/step, keyboard interaction, and pointer behavior stay with the browser.
|
|
71
|
+
* Supply a label and a unit-bearing readout; a custom non-input host must implement the
|
|
72
|
+
* interaction itself.
|
|
73
|
+
*
|
|
74
|
+
* @example
|
|
75
|
+
* ```ts
|
|
76
|
+
* import { RefSubject } from "@typed/fx";
|
|
77
|
+
* import { html } from "@typed/template";
|
|
78
|
+
* import { component } from "@typed/template";
|
|
79
|
+
* import * as Slider from "@typed/ui/Slider";
|
|
80
|
+
*
|
|
81
|
+
* export const ZoomControl = component(function* () {
|
|
82
|
+
* const state = yield* Slider.makeState({ value: 100 });
|
|
83
|
+
* const percentage = RefSubject.map(state, ({ value }) => `${value}%`);
|
|
84
|
+
* return html`<div class="zoom-control">
|
|
85
|
+
* <label for="preview-zoom">Preview zoom</label>
|
|
86
|
+
* ${Slider.Slider({
|
|
87
|
+
* state, min: 50, max: 200, step: 10,
|
|
88
|
+
* props: { id: "preview-zoom", name: "zoom", "aria-valuetext": percentage },
|
|
89
|
+
* })}
|
|
90
|
+
* <output for="preview-zoom">${percentage}</output>
|
|
91
|
+
* </div>`;
|
|
92
|
+
* });
|
|
93
|
+
* ```
|
|
94
|
+
* @since 1.0.0
|
|
95
|
+
* @category Native controls
|
|
96
|
+
*/
|
|
97
|
+
export function Slider(options, host) {
|
|
98
|
+
return Dom.renderHost()(options, host, internalProps(options), "", (props) => {
|
|
99
|
+
return html `<input ...${props} />`;
|
|
100
|
+
});
|
|
101
|
+
}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Native number entry publishing valueAsNumber at the change boundary.
|
|
3
|
+
* This thin primitive does not maintain draft strings or filter invalid numeric writes.
|
|
4
|
+
*
|
|
5
|
+
* Read the [SpinButton guide](/explore/ui-spin-button) for a complete example.
|
|
6
|
+
*
|
|
7
|
+
* [APG interaction reference](https://www.w3.org/WAI/ARIA/apg/patterns/spinbutton/).
|
|
8
|
+
* @since 1.0.0
|
|
9
|
+
* @category Overview
|
|
10
|
+
* @packageDocumentation
|
|
11
|
+
*/
|
|
12
|
+
import * as Effect from "effect/Effect";
|
|
13
|
+
import * as Schema from "effect/Schema";
|
|
14
|
+
import { RefSubject } from "@typed/fx";
|
|
15
|
+
import { EventHandler, type Renderable } from "@typed/template";
|
|
16
|
+
import * as Dom from "./Dom.js";
|
|
17
|
+
import type { HostResult } from "./Dom/Types.js";
|
|
18
|
+
/**
|
|
19
|
+
*/
|
|
20
|
+
export interface State {
|
|
21
|
+
/**
|
|
22
|
+
*/
|
|
23
|
+
readonly value: number;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
*/
|
|
27
|
+
export interface InitialState {
|
|
28
|
+
/**
|
|
29
|
+
*/
|
|
30
|
+
readonly value: number;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
*/
|
|
34
|
+
export declare const StateSchema: Schema.Struct<{
|
|
35
|
+
readonly value: Schema.Finite;
|
|
36
|
+
}>;
|
|
37
|
+
/**
|
|
38
|
+
* Creates hydrated spin-button state.
|
|
39
|
+
* @remarks
|
|
40
|
+
* State remains renderer-independent and directly testable.
|
|
41
|
+
* The calling Effect Scope owns the returned RefSubject.
|
|
42
|
+
* @example
|
|
43
|
+
* ```ts
|
|
44
|
+
* import { Effect } from "effect"
|
|
45
|
+
* import * as SpinButton from "@typed/ui/SpinButton"
|
|
46
|
+
*
|
|
47
|
+
* const program = Effect.gen(function* () {
|
|
48
|
+
* return yield* SpinButton.makeState({ value: 3 })
|
|
49
|
+
* })
|
|
50
|
+
* ```
|
|
51
|
+
* @since 1.0.0
|
|
52
|
+
* @category State construction
|
|
53
|
+
*/
|
|
54
|
+
export declare function makeState(initial: InitialState): Effect.Effect<RefSubject.HydratedRefSubject<{
|
|
55
|
+
readonly value: number;
|
|
56
|
+
}, Schema.SchemaError, never, never>, never, import("effect/Scope").Scope>;
|
|
57
|
+
/**
|
|
58
|
+
* Assigns a numeric value without bounds checks or draft filtering.
|
|
59
|
+
*
|
|
60
|
+
* @remarks
|
|
61
|
+
* The helper does not consult min/max/step or reject NaN. The hydration schema describes finite
|
|
62
|
+
* snapshots, but this update is not a decoder. Choose Form.NumberInput or an explicit draft
|
|
63
|
+
* model when invalid text needs structured feedback.
|
|
64
|
+
* @since 1.0.0
|
|
65
|
+
* @category State transitions
|
|
66
|
+
*/
|
|
67
|
+
export declare function setValue<E, R>(state: RefSubject.RefSubject<State, E, R>, value: number): Effect.Effect<State, E, R>;
|
|
68
|
+
/**
|
|
69
|
+
*/
|
|
70
|
+
export interface SpinButtonOptions extends Dom.HostOptions<HTMLInputElement> {
|
|
71
|
+
/**
|
|
72
|
+
* Hydrated value state.
|
|
73
|
+
* @since 1.0.0
|
|
74
|
+
* @category State connection
|
|
75
|
+
*/
|
|
76
|
+
readonly state: RefSubject.HydratedRefSubject<State, Schema.SchemaError>;
|
|
77
|
+
/**
|
|
78
|
+
* Native minimum value.
|
|
79
|
+
* @since 1.0.0
|
|
80
|
+
* @category Numeric bounds
|
|
81
|
+
*/
|
|
82
|
+
readonly min?: Renderable.Any<number | null | undefined>;
|
|
83
|
+
/**
|
|
84
|
+
* Native maximum value.
|
|
85
|
+
* @since 1.0.0
|
|
86
|
+
* @category Numeric bounds
|
|
87
|
+
*/
|
|
88
|
+
readonly max?: Renderable.Any<number | null | undefined>;
|
|
89
|
+
/**
|
|
90
|
+
* Native step value or `"any"`.
|
|
91
|
+
* @since 1.0.0
|
|
92
|
+
* @category Numeric bounds
|
|
93
|
+
*/
|
|
94
|
+
readonly step?: Renderable.Any<number | "any" | null | undefined>;
|
|
95
|
+
}
|
|
96
|
+
declare function internalProps<const Options extends SpinButtonOptions>(options: Options): ({ property }: Dom.InternalPropsHelpers<Options>) => {
|
|
97
|
+
readonly type: "number";
|
|
98
|
+
readonly value: RefSubject.Computed<number, Schema.SchemaError, never>;
|
|
99
|
+
readonly ".value": RefSubject.Computed<string, Schema.SchemaError, never>;
|
|
100
|
+
readonly min: Exclude<Dom.Property<Options, "min">, Dom.Nullish> | undefined;
|
|
101
|
+
readonly max: Exclude<Dom.Property<Options, "max">, Dom.Nullish> | undefined;
|
|
102
|
+
readonly step: Exclude<Dom.Property<Options, "step">, Dom.Nullish> | undefined;
|
|
103
|
+
readonly onchange: EventHandler.EventHandler<Event, Schema.SchemaError, never>;
|
|
104
|
+
readonly ref: RefSubject.HydratedRefSubject<State, Schema.SchemaError, never, never>;
|
|
105
|
+
};
|
|
106
|
+
type SpinButtonInternalProps<Options extends SpinButtonOptions> = ReturnType<ReturnType<typeof internalProps<Options>>>;
|
|
107
|
+
/**
|
|
108
|
+
* Renders a native number input that writes valueAsNumber on change.
|
|
109
|
+
*
|
|
110
|
+
* @remarks
|
|
111
|
+
* State updates occur at the change boundary rather than every input event. Empty or invalid
|
|
112
|
+
* native text can yield NaN; the handler does not filter it before assignment. This thin
|
|
113
|
+
* primitive does not preserve a separate draft string, create field errors, or clamp
|
|
114
|
+
* programmatic writes.
|
|
115
|
+
*
|
|
116
|
+
* @example
|
|
117
|
+
* ```ts
|
|
118
|
+
* import { RefSubject } from "@typed/fx";
|
|
119
|
+
* import { html } from "@typed/template";
|
|
120
|
+
* import { component } from "@typed/template";
|
|
121
|
+
* import * as SpinButton from "@typed/ui/SpinButton";
|
|
122
|
+
*
|
|
123
|
+
* export const CopyCount = component(function* () {
|
|
124
|
+
* const state = yield* SpinButton.makeState({ value: 1 });
|
|
125
|
+
* const summary = RefSubject.map(state, ({ value }) => `Copies requested: ${value}`);
|
|
126
|
+
* return html`<div class="copy-count">
|
|
127
|
+
* <label for="print-copies">Number of copies</label>
|
|
128
|
+
* ${SpinButton.SpinButton({
|
|
129
|
+
* state, min: 1, max: 100, step: 1,
|
|
130
|
+
* props: { id: "print-copies", name: "copies", required: true },
|
|
131
|
+
* })}
|
|
132
|
+
* <p>${summary}</p>
|
|
133
|
+
* </div>`;
|
|
134
|
+
* });
|
|
135
|
+
* ```
|
|
136
|
+
* @since 1.0.0
|
|
137
|
+
* @category Native controls
|
|
138
|
+
*/
|
|
139
|
+
export declare function SpinButton<const Options extends SpinButtonOptions, const Host extends HostResult = never>(options: Options, host?: Dom.HostOverride<Dom.RenderHostProps<Options, SpinButtonInternalProps<Options>>, "", Host>): import("./Dom/Types.js").HostComponent<Host | Options>;
|
|
140
|
+
export {};
|
|
141
|
+
//# sourceMappingURL=SpinButton.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SpinButton.d.ts","sourceRoot":"","sources":["../src/SpinButton.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACvC,OAAO,EAAE,YAAY,EAAQ,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAC;AACtE,OAAO,KAAK,GAAG,MAAM,UAAU,CAAC;AAChC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAEjD;GACG;AACH,MAAM,WAAW,KAAK;IACpB;OACG;IACH,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED;GACG;AACH,MAAM,WAAW,YAAY;IAC3B;OACG;IACH,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED;GACG;AACH,eAAO,MAAM,WAAW;;EAA0C,CAAC;AAEnE;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,YAAY;;2EAE9C;AAED;;;;;;;;;GASG;AACH,wBAAgB,QAAQ,CAAC,CAAC,EAAE,CAAC,EAC3B,KAAK,EAAE,UAAU,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC,EACzC,KAAK,EAAE,MAAM,GACZ,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC,CAE5B;AAED;GACG;AACH,MAAM,WAAW,iBAAkB,SAAQ,GAAG,CAAC,WAAW,CAAC,gBAAgB,CAAC;IAC1E;;;;OAIG;IACH,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC,kBAAkB,CAAC,KAAK,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC;IACzE;;;;OAIG;IACH,QAAQ,CAAC,GAAG,CAAC,EAAE,UAAU,CAAC,GAAG,CAAC,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC;IACzD;;;;OAIG;IACH,QAAQ,CAAC,GAAG,CAAC,EAAE,UAAU,CAAC,GAAG,CAAC,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC;IACzD;;;;OAIG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE,UAAU,CAAC,GAAG,CAAC,MAAM,GAAG,KAAK,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC;CACnE;AAED,iBAAS,aAAa,CAAC,KAAK,CAAC,OAAO,SAAS,iBAAiB,EAAE,OAAO,EAAE,OAAO,kBACxD,GAAG,CAAC,oBAAoB,CAAC,OAAO,CAAC;aAEnD,IAAI,EAAE,QAAQ;aACd,KAAK;aACL,QAAQ;aACR,GAAG;aACH,GAAG;aACH,IAAI;aACJ,QAAQ;aAKR,GAAG;EAER;AACD,KAAK,uBAAuB,CAAC,OAAO,SAAS,iBAAiB,IAAI,UAAU,CAC1E,UAAU,CAAC,OAAO,aAAa,CAAC,OAAO,CAAC,CAAC,CAC1C,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAgB,UAAU,CACxB,KAAK,CAAC,OAAO,SAAS,iBAAiB,EACvC,KAAK,CAAC,IAAI,SAAS,UAAU,GAAG,KAAK,EAErC,OAAO,EAAE,OAAO,EAChB,IAAI,CAAC,EAAE,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,eAAe,CAAC,OAAO,EAAE,uBAAuB,CAAC,OAAO,CAAC,CAAC,EAAE,EAAE,EAAE,IAAI,CAAC,0DAWlG"}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Native number entry publishing valueAsNumber at the change boundary.
|
|
3
|
+
* This thin primitive does not maintain draft strings or filter invalid numeric writes.
|
|
4
|
+
*
|
|
5
|
+
* Read the [SpinButton guide](/explore/ui-spin-button) for a complete example.
|
|
6
|
+
*
|
|
7
|
+
* [APG interaction reference](https://www.w3.org/WAI/ARIA/apg/patterns/spinbutton/).
|
|
8
|
+
* @since 1.0.0
|
|
9
|
+
* @category Overview
|
|
10
|
+
* @packageDocumentation
|
|
11
|
+
*/
|
|
12
|
+
import * as Effect from "effect/Effect";
|
|
13
|
+
import * as Schema from "effect/Schema";
|
|
14
|
+
import { RefSubject } from "@typed/fx";
|
|
15
|
+
import { EventHandler, html } from "@typed/template";
|
|
16
|
+
import * as Dom from "./Dom.js";
|
|
17
|
+
/**
|
|
18
|
+
*/
|
|
19
|
+
export const StateSchema = Schema.Struct({ value: Schema.Finite });
|
|
20
|
+
/**
|
|
21
|
+
* Creates hydrated spin-button state.
|
|
22
|
+
* @remarks
|
|
23
|
+
* State remains renderer-independent and directly testable.
|
|
24
|
+
* The calling Effect Scope owns the returned RefSubject.
|
|
25
|
+
* @example
|
|
26
|
+
* ```ts
|
|
27
|
+
* import { Effect } from "effect"
|
|
28
|
+
* import * as SpinButton from "@typed/ui/SpinButton"
|
|
29
|
+
*
|
|
30
|
+
* const program = Effect.gen(function* () {
|
|
31
|
+
* return yield* SpinButton.makeState({ value: 3 })
|
|
32
|
+
* })
|
|
33
|
+
* ```
|
|
34
|
+
* @since 1.0.0
|
|
35
|
+
* @category State construction
|
|
36
|
+
*/
|
|
37
|
+
export function makeState(initial) {
|
|
38
|
+
return RefSubject.hydrate(StateSchema, initial);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Assigns a numeric value without bounds checks or draft filtering.
|
|
42
|
+
*
|
|
43
|
+
* @remarks
|
|
44
|
+
* The helper does not consult min/max/step or reject NaN. The hydration schema describes finite
|
|
45
|
+
* snapshots, but this update is not a decoder. Choose Form.NumberInput or an explicit draft
|
|
46
|
+
* model when invalid text needs structured feedback.
|
|
47
|
+
* @since 1.0.0
|
|
48
|
+
* @category State transitions
|
|
49
|
+
*/
|
|
50
|
+
export function setValue(state, value) {
|
|
51
|
+
return RefSubject.update(state, (current) => ({ ...current, value }));
|
|
52
|
+
}
|
|
53
|
+
function internalProps(options) {
|
|
54
|
+
return ({ property }) => ({
|
|
55
|
+
type: "number",
|
|
56
|
+
value: RefSubject.map(options.state, (state) => state.value),
|
|
57
|
+
".value": RefSubject.map(options.state, (state) => String(state.value)),
|
|
58
|
+
min: property("min", undefined),
|
|
59
|
+
max: property("max", undefined),
|
|
60
|
+
step: property("step", undefined),
|
|
61
|
+
onchange: EventHandler.make(Effect.fn((event) => setValue(options.state, Dom.currentTarget(event).valueAsNumber))),
|
|
62
|
+
ref: options.state,
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Renders a native number input that writes valueAsNumber on change.
|
|
67
|
+
*
|
|
68
|
+
* @remarks
|
|
69
|
+
* State updates occur at the change boundary rather than every input event. Empty or invalid
|
|
70
|
+
* native text can yield NaN; the handler does not filter it before assignment. This thin
|
|
71
|
+
* primitive does not preserve a separate draft string, create field errors, or clamp
|
|
72
|
+
* programmatic writes.
|
|
73
|
+
*
|
|
74
|
+
* @example
|
|
75
|
+
* ```ts
|
|
76
|
+
* import { RefSubject } from "@typed/fx";
|
|
77
|
+
* import { html } from "@typed/template";
|
|
78
|
+
* import { component } from "@typed/template";
|
|
79
|
+
* import * as SpinButton from "@typed/ui/SpinButton";
|
|
80
|
+
*
|
|
81
|
+
* export const CopyCount = component(function* () {
|
|
82
|
+
* const state = yield* SpinButton.makeState({ value: 1 });
|
|
83
|
+
* const summary = RefSubject.map(state, ({ value }) => `Copies requested: ${value}`);
|
|
84
|
+
* return html`<div class="copy-count">
|
|
85
|
+
* <label for="print-copies">Number of copies</label>
|
|
86
|
+
* ${SpinButton.SpinButton({
|
|
87
|
+
* state, min: 1, max: 100, step: 1,
|
|
88
|
+
* props: { id: "print-copies", name: "copies", required: true },
|
|
89
|
+
* })}
|
|
90
|
+
* <p>${summary}</p>
|
|
91
|
+
* </div>`;
|
|
92
|
+
* });
|
|
93
|
+
* ```
|
|
94
|
+
* @since 1.0.0
|
|
95
|
+
* @category Native controls
|
|
96
|
+
*/
|
|
97
|
+
export function SpinButton(options, host) {
|
|
98
|
+
return Dom.renderHost()(options, host, internalProps(options), "", (props) => {
|
|
99
|
+
return html `<input ...${props} />`;
|
|
100
|
+
});
|
|
101
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import * as Scope from "effect/Scope";
|
|
2
|
+
import { Fx } from "@typed/fx";
|
|
3
|
+
import { type RenderEvent, type RenderTemplate } from "@typed/template";
|
|
4
|
+
/**
|
|
5
|
+
* Live Storybook mount and its explicit asynchronous disposer.
|
|
6
|
+
*
|
|
7
|
+
* @remarks
|
|
8
|
+
* ## Why
|
|
9
|
+
* Stories need access to the actual canvas for assertions and deterministic
|
|
10
|
+
* teardown of a long-lived reactive render.
|
|
11
|
+
*
|
|
12
|
+
* ## Ownership and lifetime
|
|
13
|
+
* The mount owns its private Scope and render fiber. `dispose` interrupts the
|
|
14
|
+
* fiber and closes the Scope exactly once; callers own appending/removing the
|
|
15
|
+
* returned canvas. If the canvas is connected, disposal does not disconnect the
|
|
16
|
+
* removal observer; removing the canvas is the observer's cleanup boundary.
|
|
17
|
+
*
|
|
18
|
+
* @since 1.0.0
|
|
19
|
+
* @category Story lifetime
|
|
20
|
+
*/
|
|
21
|
+
export interface MountedStory {
|
|
22
|
+
/** Detached div containing the story's real rendered DOM. */
|
|
23
|
+
readonly canvas: HTMLDivElement;
|
|
24
|
+
/** Idempotently closes rendering; remove a connected canvas to release its observer. */
|
|
25
|
+
readonly dispose: () => Promise<void>;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Mounts a Typed component in an isolated Storybook canvas.
|
|
29
|
+
*
|
|
30
|
+
* The mount stays alive after its first render so reactive attributes and event
|
|
31
|
+
* handlers remain attached. It is released explicitly or when Storybook
|
|
32
|
+
* removes the canvas from its document.
|
|
33
|
+
*
|
|
34
|
+
* @remarks
|
|
35
|
+
* ## Why
|
|
36
|
+
*
|
|
37
|
+
* Waiting only for the first `RenderEvent` and then ending the fiber would
|
|
38
|
+
* detach reactive attributes and event handlers. This helper keeps the Typed
|
|
39
|
+
* renderer alive while still giving tests an explicit disposal boundary.
|
|
40
|
+
*
|
|
41
|
+
* ## Ownership and lifetime
|
|
42
|
+
*
|
|
43
|
+
* `mount` creates an unsafe Scope dedicated to this story, starts the render
|
|
44
|
+
* fiber, and resolves only after the first event. `dispose` interrupts and
|
|
45
|
+
* closes it idempotently. After the canvas has once been connected, a
|
|
46
|
+
* document-scoped MutationObserver disposes it when removed and then disconnects.
|
|
47
|
+
* Calling `dispose` while the canvas is still connected does **not** disconnect
|
|
48
|
+
* that observer: it continues retaining the document, canvas, and disposal
|
|
49
|
+
* closure until the canvas is removed. Tests or hosts that keep a disposed
|
|
50
|
+
* canvas connected must remove it themselves to release that observer. If
|
|
51
|
+
* rendering fails before the first event, Scope cleanup runs before rejection
|
|
52
|
+
* and no removal observer is installed.
|
|
53
|
+
*
|
|
54
|
+
* ## Platform requirements
|
|
55
|
+
*
|
|
56
|
+
* This is a browser/testing API. The provided Document supplies element and
|
|
57
|
+
* MutationObserver implementations. The canvas is returned detached so
|
|
58
|
+
* Storybook or a test controls where it enters the DOM.
|
|
59
|
+
*
|
|
60
|
+
* @example
|
|
61
|
+
* ```ts
|
|
62
|
+
* import { mount } from "@typed/ui/Storybook"
|
|
63
|
+
* import { html } from "@typed/template"
|
|
64
|
+
*
|
|
65
|
+
* const story = await mount(html`<button type="button">Save</button>`)
|
|
66
|
+
* document.body.append(story.canvas)
|
|
67
|
+
* // Run interactions and assertions against story.canvas.
|
|
68
|
+
* await story.dispose()
|
|
69
|
+
* story.canvas.remove() // disconnects the removal observer
|
|
70
|
+
* ```
|
|
71
|
+
*
|
|
72
|
+
* @since 1.0.0
|
|
73
|
+
* @category Story mounting
|
|
74
|
+
*/
|
|
75
|
+
export declare function mount<E>(content: Fx.Fx<RenderEvent, E, Scope.Scope | RenderTemplate>, document?: Document): Promise<MountedStory>;
|
|
76
|
+
//# sourceMappingURL=Storybook.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Storybook.d.ts","sourceRoot":"","sources":["../src/Storybook.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;AACtC,OAAO,EAAE,EAAE,EAAE,MAAM,WAAW,CAAC;AAC/B,OAAO,EAGL,KAAK,WAAW,EAChB,KAAK,cAAc,EACpB,MAAM,iBAAiB,CAAC;AAEzB;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,WAAW,YAAY;IAC3B,6DAA6D;IAC7D,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC;IAChC,wFAAwF;IACxF,QAAQ,CAAC,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CACvC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AACH,wBAAsB,KAAK,CAAC,CAAC,EAC3B,OAAO,EAAE,EAAE,CAAC,EAAE,CAAC,WAAW,EAAE,CAAC,EAAE,KAAK,CAAC,KAAK,GAAG,cAAc,CAAC,EAC5D,QAAQ,GAAE,QAA8B,GACvC,OAAO,CAAC,YAAY,CAAC,CAqCvB"}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import * as Deferred from "effect/Deferred";
|
|
2
|
+
import * as Effect from "effect/Effect";
|
|
3
|
+
import * as Exit from "effect/Exit";
|
|
4
|
+
import * as Fiber from "effect/Fiber";
|
|
5
|
+
import * as Scope from "effect/Scope";
|
|
6
|
+
import { Fx } from "@typed/fx";
|
|
7
|
+
import { DomRenderTemplate, render as renderDom, } from "@typed/template";
|
|
8
|
+
/**
|
|
9
|
+
* Mounts a Typed component in an isolated Storybook canvas.
|
|
10
|
+
*
|
|
11
|
+
* The mount stays alive after its first render so reactive attributes and event
|
|
12
|
+
* handlers remain attached. It is released explicitly or when Storybook
|
|
13
|
+
* removes the canvas from its document.
|
|
14
|
+
*
|
|
15
|
+
* @remarks
|
|
16
|
+
* ## Why
|
|
17
|
+
*
|
|
18
|
+
* Waiting only for the first `RenderEvent` and then ending the fiber would
|
|
19
|
+
* detach reactive attributes and event handlers. This helper keeps the Typed
|
|
20
|
+
* renderer alive while still giving tests an explicit disposal boundary.
|
|
21
|
+
*
|
|
22
|
+
* ## Ownership and lifetime
|
|
23
|
+
*
|
|
24
|
+
* `mount` creates an unsafe Scope dedicated to this story, starts the render
|
|
25
|
+
* fiber, and resolves only after the first event. `dispose` interrupts and
|
|
26
|
+
* closes it idempotently. After the canvas has once been connected, a
|
|
27
|
+
* document-scoped MutationObserver disposes it when removed and then disconnects.
|
|
28
|
+
* Calling `dispose` while the canvas is still connected does **not** disconnect
|
|
29
|
+
* that observer: it continues retaining the document, canvas, and disposal
|
|
30
|
+
* closure until the canvas is removed. Tests or hosts that keep a disposed
|
|
31
|
+
* canvas connected must remove it themselves to release that observer. If
|
|
32
|
+
* rendering fails before the first event, Scope cleanup runs before rejection
|
|
33
|
+
* and no removal observer is installed.
|
|
34
|
+
*
|
|
35
|
+
* ## Platform requirements
|
|
36
|
+
*
|
|
37
|
+
* This is a browser/testing API. The provided Document supplies element and
|
|
38
|
+
* MutationObserver implementations. The canvas is returned detached so
|
|
39
|
+
* Storybook or a test controls where it enters the DOM.
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* ```ts
|
|
43
|
+
* import { mount } from "@typed/ui/Storybook"
|
|
44
|
+
* import { html } from "@typed/template"
|
|
45
|
+
*
|
|
46
|
+
* const story = await mount(html`<button type="button">Save</button>`)
|
|
47
|
+
* document.body.append(story.canvas)
|
|
48
|
+
* // Run interactions and assertions against story.canvas.
|
|
49
|
+
* await story.dispose()
|
|
50
|
+
* story.canvas.remove() // disconnects the removal observer
|
|
51
|
+
* ```
|
|
52
|
+
*
|
|
53
|
+
* @since 1.0.0
|
|
54
|
+
* @category Story mounting
|
|
55
|
+
*/
|
|
56
|
+
export async function mount(content, document = globalThis.document) {
|
|
57
|
+
const canvas = document.createElement("div");
|
|
58
|
+
const scope = Scope.makeUnsafe();
|
|
59
|
+
const mounted = Deferred.makeUnsafe();
|
|
60
|
+
let disposed = false;
|
|
61
|
+
let fiber;
|
|
62
|
+
const dispose = async () => {
|
|
63
|
+
if (disposed)
|
|
64
|
+
return;
|
|
65
|
+
disposed = true;
|
|
66
|
+
if (fiber !== undefined) {
|
|
67
|
+
await Effect.runPromise(Fiber.interrupt(fiber));
|
|
68
|
+
}
|
|
69
|
+
await Effect.runPromise(Scope.close(scope, Exit.void));
|
|
70
|
+
};
|
|
71
|
+
fiber = Effect.runFork(Scope.provide(Fx.drain(renderDom(content, canvas).pipe(Fx.tap(() => Deferred.succeed(mounted, undefined)))).pipe(Effect.provide(DomRenderTemplate.using(document))), scope));
|
|
72
|
+
try {
|
|
73
|
+
const endedBeforeMount = Effect.andThen(Fiber.join(fiber), Effect.die("Story completed before rendering any content"));
|
|
74
|
+
await Effect.runPromise(Effect.raceFirst(Deferred.await(mounted), endedBeforeMount));
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
await dispose();
|
|
78
|
+
throw error;
|
|
79
|
+
}
|
|
80
|
+
disposeWhenRemoved(canvas, document, dispose);
|
|
81
|
+
return { canvas, dispose };
|
|
82
|
+
}
|
|
83
|
+
function disposeWhenRemoved(canvas, document, dispose) {
|
|
84
|
+
const MutationObserver = document.defaultView?.MutationObserver;
|
|
85
|
+
if (MutationObserver === undefined)
|
|
86
|
+
return;
|
|
87
|
+
let connected = canvas.isConnected;
|
|
88
|
+
const observer = new MutationObserver(() => {
|
|
89
|
+
if (canvas.isConnected) {
|
|
90
|
+
connected = true;
|
|
91
|
+
}
|
|
92
|
+
else if (connected) {
|
|
93
|
+
observer.disconnect();
|
|
94
|
+
void dispose();
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
observer.observe(document, { childList: true, subtree: true });
|
|
98
|
+
queueMicrotask(() => {
|
|
99
|
+
if (canvas.isConnected)
|
|
100
|
+
connected = true;
|
|
101
|
+
});
|
|
102
|
+
}
|
package/dist/Switch.d.ts
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A button-backed on/off setting with a stable accessible name.
|
|
3
|
+
* The internal click toggles boolean state; persistence and form serialization remain explicit.
|
|
4
|
+
*
|
|
5
|
+
* Read the [Switch guide](/explore/ui-switch) for a complete example.
|
|
6
|
+
*
|
|
7
|
+
* [APG interaction reference](https://www.w3.org/WAI/ARIA/apg/patterns/switch/).
|
|
8
|
+
* @since 1.0.0
|
|
9
|
+
* @category Overview
|
|
10
|
+
* @packageDocumentation
|
|
11
|
+
*/
|
|
12
|
+
import * as Effect from "effect/Effect";
|
|
13
|
+
import * as Schema from "effect/Schema";
|
|
14
|
+
import { RefSubject } from "@typed/fx";
|
|
15
|
+
import { type Renderable } from "@typed/template";
|
|
16
|
+
import * as Dom from "./Dom.js";
|
|
17
|
+
import type { HostResult } from "./Dom/Types.js";
|
|
18
|
+
/**
|
|
19
|
+
*/
|
|
20
|
+
export interface State {
|
|
21
|
+
/**
|
|
22
|
+
*/
|
|
23
|
+
readonly checked: boolean;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
*/
|
|
27
|
+
export interface InitialState {
|
|
28
|
+
/**
|
|
29
|
+
*/
|
|
30
|
+
readonly checked?: boolean;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
*/
|
|
34
|
+
export declare const StateSchema: Schema.Struct<{
|
|
35
|
+
readonly checked: Schema.Boolean;
|
|
36
|
+
}>;
|
|
37
|
+
/**
|
|
38
|
+
* Creates hydrated switch state independent of rendering.
|
|
39
|
+
* @remarks
|
|
40
|
+
* Domain code and tests can own the state while any renderer consumes it.
|
|
41
|
+
* The caller's Effect Scope owns the returned RefSubject.
|
|
42
|
+
* @example
|
|
43
|
+
* ```ts
|
|
44
|
+
* import { Effect } from "effect"
|
|
45
|
+
* import * as Switch from "@typed/ui/Switch"
|
|
46
|
+
*
|
|
47
|
+
* const program = Effect.gen(function* () {
|
|
48
|
+
* return yield* Switch.makeState({ checked: true })
|
|
49
|
+
* })
|
|
50
|
+
* ```
|
|
51
|
+
* @since 1.0.0
|
|
52
|
+
* @category State construction
|
|
53
|
+
*/
|
|
54
|
+
export declare function makeState(initial?: InitialState): Effect.Effect<RefSubject.HydratedRefSubject<{
|
|
55
|
+
readonly checked: boolean;
|
|
56
|
+
}, Schema.SchemaError, never, never>, never, import("effect/Scope").Scope>;
|
|
57
|
+
/**
|
|
58
|
+
* Sets the switch to the requested checked state.
|
|
59
|
+
* @example
|
|
60
|
+
* ```ts
|
|
61
|
+
* import { Effect } from "effect"
|
|
62
|
+
* import * as Switch from "@typed/ui/Switch"
|
|
63
|
+
*
|
|
64
|
+
* const program = Effect.gen(function* () {
|
|
65
|
+
* const state = yield* Switch.makeState()
|
|
66
|
+
* yield* Switch.setChecked(state, true)
|
|
67
|
+
* })
|
|
68
|
+
* ```
|
|
69
|
+
* @since 1.0.0
|
|
70
|
+
* @category State transitions
|
|
71
|
+
*/
|
|
72
|
+
export declare function setChecked<E, R>(state: RefSubject.RefSubject<State, E, R>, checked: boolean): Effect.Effect<State, E, R>;
|
|
73
|
+
/**
|
|
74
|
+
* Toggles the switch between checked and unchecked.
|
|
75
|
+
* @example
|
|
76
|
+
* ```ts
|
|
77
|
+
* import { Effect } from "effect"
|
|
78
|
+
* import * as Switch from "@typed/ui/Switch"
|
|
79
|
+
*
|
|
80
|
+
* const program = Effect.gen(function* () {
|
|
81
|
+
* const state = yield* Switch.makeState({ checked: true })
|
|
82
|
+
* yield* Switch.toggle(state)
|
|
83
|
+
* })
|
|
84
|
+
* ```
|
|
85
|
+
* @since 1.0.0
|
|
86
|
+
* @category State transitions
|
|
87
|
+
*/
|
|
88
|
+
export declare function toggle<E, R>(state: RefSubject.RefSubject<State, E, R>): Effect.Effect<State, E, R>;
|
|
89
|
+
/**
|
|
90
|
+
*/
|
|
91
|
+
export interface SwitchOptions extends Dom.HostOptions<HTMLButtonElement> {
|
|
92
|
+
/**
|
|
93
|
+
* Hydrated state synchronized with `aria-checked`.
|
|
94
|
+
* @since 1.0.0
|
|
95
|
+
* @category State connection
|
|
96
|
+
*/
|
|
97
|
+
readonly state: RefSubject.HydratedRefSubject<State, Schema.SchemaError>;
|
|
98
|
+
/**
|
|
99
|
+
* Visible content and accessible name.
|
|
100
|
+
* @since 1.0.0
|
|
101
|
+
* @category Rendered content
|
|
102
|
+
*/
|
|
103
|
+
readonly content: Renderable.Any;
|
|
104
|
+
}
|
|
105
|
+
declare function internalProps<const Options extends SwitchOptions>(options: Options): ({ property }: Dom.InternalPropsHelpers<Options>) => {
|
|
106
|
+
readonly type: "button";
|
|
107
|
+
readonly role: "switch";
|
|
108
|
+
readonly "aria-checked": RefSubject.Computed<boolean, Schema.SchemaError, never>;
|
|
109
|
+
readonly "?disabled": false | Exclude<Dom.Property<Options, "disabled">, Dom.Nullish>;
|
|
110
|
+
readonly onclick: Effect.Effect<State, Schema.SchemaError, never>;
|
|
111
|
+
readonly ref: RefSubject.HydratedRefSubject<State, Schema.SchemaError, never, never>;
|
|
112
|
+
};
|
|
113
|
+
type SwitchInternalProps<Options extends SwitchOptions> = ReturnType<ReturnType<typeof internalProps<Options>>>;
|
|
114
|
+
/**
|
|
115
|
+
* Renders a button-backed ARIA switch.
|
|
116
|
+
* @remarks
|
|
117
|
+
* The component combines native button activation with a renderer-independent
|
|
118
|
+
* boolean state and real DOM click events.
|
|
119
|
+
* Running the Fx owns listener/state subscriptions in its Effect Scope. A
|
|
120
|
+
* custom host must preserve button type, role, `aria-checked`, click handler,
|
|
121
|
+
* and the single hydration ref owner.
|
|
122
|
+
* @example
|
|
123
|
+
* ```ts
|
|
124
|
+
* import { RefSubject } from "@typed/fx";
|
|
125
|
+
* import { html } from "@typed/template";
|
|
126
|
+
* import { component } from "@typed/template";
|
|
127
|
+
* import * as Switch from "@typed/ui/Switch";
|
|
128
|
+
*
|
|
129
|
+
* export const PreviewSetting = component(function* () {
|
|
130
|
+
* const state = yield* Switch.makeState({ checked: true });
|
|
131
|
+
* const status = RefSubject.map(state, ({ checked }) => checked ? "On" : "Off");
|
|
132
|
+
* return html`<section>
|
|
133
|
+
* ${Switch.Switch({
|
|
134
|
+
* state,
|
|
135
|
+
* content: "Show live preview",
|
|
136
|
+
* props: { class: "preview-switch", "aria-describedby": "preview-setting-help" },
|
|
137
|
+
* })}
|
|
138
|
+
* <span aria-hidden="true">${status}</span>
|
|
139
|
+
* <p id="preview-setting-help">Updates the preview while you edit.</p>
|
|
140
|
+
* </section>`;
|
|
141
|
+
* });
|
|
142
|
+
* ```
|
|
143
|
+
* @since 1.0.0
|
|
144
|
+
* @category Native controls
|
|
145
|
+
*/
|
|
146
|
+
export declare function Switch<const Options extends SwitchOptions, const Host extends HostResult = never>(options: Options, host?: Dom.HostOverride<Dom.RenderHostProps<Options, SwitchInternalProps<Options>>, Options["content"], Host>): import("./Dom/Types.js").HostComponent<Host | Options>;
|
|
147
|
+
export {};
|
|
148
|
+
//# sourceMappingURL=Switch.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Switch.d.ts","sourceRoot":"","sources":["../src/Switch.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACvC,OAAO,EAAQ,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,KAAK,GAAG,MAAM,UAAU,CAAC;AAChC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAEjD;GACG;AACH,MAAM,WAAW,KAAK;IACpB;OACG;IACH,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;CAC3B;AAED;GACG;AACH,MAAM,WAAW,YAAY;IAC3B;OACG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED;GACG;AACH,eAAO,MAAM,WAAW;;EAA6C,CAAC;AAEtE;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,SAAS,CAAC,OAAO,GAAE,YAAiB;;2EAEnD;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,UAAU,CAAC,CAAC,EAAE,CAAC,EAC7B,KAAK,EAAE,UAAU,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC,EACzC,OAAO,EAAE,OAAO,GACf,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC,CAE5B;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,MAAM,CAAC,CAAC,EAAE,CAAC,EACzB,KAAK,EAAE,UAAU,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC,GACxC,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC,CAE5B;AAED;GACG;AACH,MAAM,WAAW,aAAc,SAAQ,GAAG,CAAC,WAAW,CAAC,iBAAiB,CAAC;IACvE;;;;OAIG;IACH,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC,kBAAkB,CAAC,KAAK,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC;IACzE;;;;OAIG;IACH,QAAQ,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG,CAAC;CAClC;AAED,iBAAS,aAAa,CAAC,KAAK,CAAC,OAAO,SAAS,aAAa,EAAE,OAAO,EAAE,OAAO,kBACpD,GAAG,CAAC,oBAAoB,CAAC,OAAO,CAAC;aAEnD,IAAI,EAAE,QAAQ;aACd,IAAI,EAAE,QAAQ;aACd,cAAc;aACd,WAAW;aACX,OAAO;aACP,GAAG;EAER;AACD,KAAK,mBAAmB,CAAC,OAAO,SAAS,aAAa,IAAI,UAAU,CAClE,UAAU,CAAC,OAAO,aAAa,CAAC,OAAO,CAAC,CAAC,CAC1C,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAgB,MAAM,CAAC,KAAK,CAAC,OAAO,SAAS,aAAa,EAAE,KAAK,CAAC,IAAI,SAAS,UAAU,GAAG,KAAK,EAC/F,OAAO,EAAE,OAAO,EAChB,IAAI,CAAC,EAAE,GAAG,CAAC,YAAY,CACrB,GAAG,CAAC,eAAe,CAAC,OAAO,EAAE,mBAAmB,CAAC,OAAO,CAAC,CAAC,EAC1D,OAAO,CAAC,SAAS,CAAC,EAClB,IAAI,CACL,0DAWF"}
|