@typed/ui 1.0.0-beta.1 → 1.0.0-beta.11
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 +54 -10
- 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 +198 -55
- 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 +48 -20
- package/src/HttpRouter.test.ts +0 -294
- package/src/HttpRouter.ts +0 -168
- package/src/Link.test.ts +0 -84
- package/src/Link.ts +0 -107
- package/src/index.ts +0 -2
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { chainEvent, isEventKey } from "./Events.js";
|
|
2
|
+
import { composeRefs } from "./Refs.js";
|
|
3
|
+
/**
|
|
4
|
+
* Merges user host props with component-required internal props.
|
|
5
|
+
*
|
|
6
|
+
* @remarks
|
|
7
|
+
* ## Why
|
|
8
|
+
* Internal accessibility/state props must remain authoritative, but user
|
|
9
|
+
* events still run first and may cancel component behavior. Disabled hosts
|
|
10
|
+
* suppress user activation handlers. Refs compose instead of overwriting one
|
|
11
|
+
* another.
|
|
12
|
+
*
|
|
13
|
+
* ## Ownership and lifetime
|
|
14
|
+
* The function allocates only a merged object. Listener and ref effects are
|
|
15
|
+
* run by the eventual template Scope. `composeRefs` rejects two hydration
|
|
16
|
+
* owners because one element can restore only one hydration protocol.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```ts
|
|
20
|
+
* import { mergeProps } from "@typed/ui/Dom/Props"
|
|
21
|
+
* import { EventHandler } from "@typed/template"
|
|
22
|
+
* import { Effect } from "effect"
|
|
23
|
+
*
|
|
24
|
+
* const props = mergeProps(
|
|
25
|
+
* {
|
|
26
|
+
* class: "external",
|
|
27
|
+
* onclick: EventHandler.make(() => Effect.log("user activation"))
|
|
28
|
+
* },
|
|
29
|
+
* { role: "button", "aria-disabled": false }
|
|
30
|
+
* )
|
|
31
|
+
* ```
|
|
32
|
+
*
|
|
33
|
+
* @since 1.0.0
|
|
34
|
+
* @category Prop composition
|
|
35
|
+
*/
|
|
36
|
+
export function mergeProps(user, internal) {
|
|
37
|
+
if (!user)
|
|
38
|
+
return internal;
|
|
39
|
+
const merged = { ...user, ...internal };
|
|
40
|
+
const internalRecord = internal;
|
|
41
|
+
const mergedRecord = merged;
|
|
42
|
+
const activationDisabled = internalRecord["aria-disabled"] === true || internalRecord["?disabled"] === true;
|
|
43
|
+
for (const [key, value] of Object.entries(user)) {
|
|
44
|
+
if (isEventKey(key)) {
|
|
45
|
+
mergedRecord[key] = chainEvent(activationDisabled && (key === "onclick" || key === "@click")
|
|
46
|
+
? undefined
|
|
47
|
+
: value, internalRecord[key]);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
mergedRecord["ref"] = composeRefs(internalRecord["ref"], user["ref"]);
|
|
51
|
+
return merged;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Reads a property from an unknown generic value without throwing on nullish input.
|
|
55
|
+
*
|
|
56
|
+
* @remarks
|
|
57
|
+
* ## Why
|
|
58
|
+
* Generic option construction needs runtime behavior matching the public `Property` type.
|
|
59
|
+
*
|
|
60
|
+
* ## Ownership and lifetime
|
|
61
|
+
* Pure read; no value is retained.
|
|
62
|
+
*
|
|
63
|
+
* @since 1.0.0
|
|
64
|
+
* @category Property inspection
|
|
65
|
+
*/
|
|
66
|
+
export function getProperty(value, key) {
|
|
67
|
+
return (value === null || value === undefined ? undefined : Reflect.get(Object(value), key));
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Builds typed fallback helpers for component-owned host props.
|
|
71
|
+
*
|
|
72
|
+
* @remarks
|
|
73
|
+
* ## Why
|
|
74
|
+
* Components can distinguish an absent option from valid falsy values.
|
|
75
|
+
*
|
|
76
|
+
* ## Ownership and lifetime
|
|
77
|
+
* The helper closes over `options` only during synchronous prop construction.
|
|
78
|
+
*
|
|
79
|
+
* @since 1.0.0
|
|
80
|
+
* @category Internal prop defaults
|
|
81
|
+
*/
|
|
82
|
+
export function makeInternalPropsHelpers(options) {
|
|
83
|
+
return {
|
|
84
|
+
property: (key, fallback) => {
|
|
85
|
+
const value = getProperty(options, key);
|
|
86
|
+
return (value === null || value === undefined ? fallback : value);
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Extracts top-level refs and event handlers from component options.
|
|
92
|
+
*
|
|
93
|
+
* @remarks
|
|
94
|
+
* ## Why
|
|
95
|
+
* Convenience event/ref options can enter the same deterministic host merge as `props`.
|
|
96
|
+
*
|
|
97
|
+
* ## Ownership and lifetime
|
|
98
|
+
* Returns a shallow object. The rendering Scope owns any contained listener/ref work.
|
|
99
|
+
*
|
|
100
|
+
* @since 1.0.0
|
|
101
|
+
* @category Prop forwarding
|
|
102
|
+
*/
|
|
103
|
+
export function forwardHostProps(options) {
|
|
104
|
+
const props = {};
|
|
105
|
+
for (const [key, value] of Object.entries(options)) {
|
|
106
|
+
if (key === "ref" || isEventKey(key))
|
|
107
|
+
props[key] = value;
|
|
108
|
+
}
|
|
109
|
+
return props;
|
|
110
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import * as Effect from "effect/Effect";
|
|
2
|
+
import { RefSubject } from "@typed/fx";
|
|
3
|
+
import type { Renderable } from "@typed/template";
|
|
4
|
+
type RefInput = (element: never) => unknown;
|
|
5
|
+
type RefTarget<Ref> = NonNullable<Ref> extends (element: infer Target) => unknown ? Target : unknown;
|
|
6
|
+
type RefResult<Ref> = NonNullable<Ref> extends (...args: Array<never>) => infer Result ? Result : never;
|
|
7
|
+
type RefError<Ref> = Renderable.Error<NonNullable<Ref> | RefResult<Ref>>;
|
|
8
|
+
type RefServices<Ref> = Renderable.Services<NonNullable<Ref> | RefResult<Ref>>;
|
|
9
|
+
type HydrationProtocol<Ref> = NonNullable<Ref> extends RefSubject.HydrationRef<infer E, infer R> ? RefSubject.HydrationRef<E, R> : unknown;
|
|
10
|
+
/**
|
|
11
|
+
* Ref callback produced by combining two compatible element refs.
|
|
12
|
+
*
|
|
13
|
+
* @remarks
|
|
14
|
+
* ## Why
|
|
15
|
+
* User and component refs can observe the exact same DOM element without one
|
|
16
|
+
* overwriting the other, while their error/service channels remain visible.
|
|
17
|
+
*
|
|
18
|
+
* ## Ownership and lifetime
|
|
19
|
+
* The callback sequences both refs inside the element's rendering Scope. At
|
|
20
|
+
* most one input may carry `HydrationRef` ownership; that protocol is retained
|
|
21
|
+
* on the composed callback.
|
|
22
|
+
*
|
|
23
|
+
* @since 1.0.0
|
|
24
|
+
* @category Ref composition
|
|
25
|
+
*/
|
|
26
|
+
export type ComposedRef<First, Second> = ((element: RefTarget<First> & RefTarget<Second>) => Effect.Effect<void, RefError<First> | RefError<Second>, RefServices<First> | RefServices<Second>>) & HydrationProtocol<First> & HydrationProtocol<Second>;
|
|
27
|
+
/**
|
|
28
|
+
* Composes two element refs in order.
|
|
29
|
+
*
|
|
30
|
+
* @remarks
|
|
31
|
+
* ## Why
|
|
32
|
+
* A component's internal ref and a caller's ref both need the real mounted
|
|
33
|
+
* node. Each return value may be void, Effect, Stream, or Fx, and composition
|
|
34
|
+
* normalizes those forms without hiding failures or services.
|
|
35
|
+
*
|
|
36
|
+
* ## Ownership and lifetime
|
|
37
|
+
* The first ref runs before the second. Streams and Fx values are drained in
|
|
38
|
+
* the caller's Scope and interrupted on unmount. The function throws
|
|
39
|
+
* synchronously when both refs claim hydration ownership because applying two
|
|
40
|
+
* serialized states to one element is ambiguous.
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* ```ts
|
|
44
|
+
* import { composeRefs } from "@typed/ui/Dom/Refs"
|
|
45
|
+
* import { Effect } from "effect"
|
|
46
|
+
*
|
|
47
|
+
* const ref = composeRefs(
|
|
48
|
+
* (element: HTMLButtonElement) => Effect.sync(() => element.focus()),
|
|
49
|
+
* (element: HTMLButtonElement) => console.log(element)
|
|
50
|
+
* )
|
|
51
|
+
* ```
|
|
52
|
+
*
|
|
53
|
+
* @since 1.0.0
|
|
54
|
+
* @category Ref composition
|
|
55
|
+
*/
|
|
56
|
+
export declare function composeRefs<const First extends RefInput | null | undefined, const Second extends RefInput | null | undefined = undefined>(first: First, second?: Second): ComposedRef<First, Second> | undefined;
|
|
57
|
+
export {};
|
|
58
|
+
//# sourceMappingURL=Refs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Refs.d.ts","sourceRoot":"","sources":["../../src/Dom/Refs.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAExC,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAEvC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAElD,KAAK,QAAQ,GAAG,CAAC,OAAO,EAAE,KAAK,KAAK,OAAO,CAAC;AAC5C,KAAK,SAAS,CAAC,GAAG,IAChB,WAAW,CAAC,GAAG,CAAC,SAAS,CAAC,OAAO,EAAE,MAAM,MAAM,KAAK,OAAO,GAAG,MAAM,GAAG,OAAO,CAAC;AACjF,KAAK,SAAS,CAAC,GAAG,IAChB,WAAW,CAAC,GAAG,CAAC,SAAS,CAAC,GAAG,IAAI,EAAE,KAAK,CAAC,KAAK,CAAC,KAAK,MAAM,MAAM,GAAG,MAAM,GAAG,KAAK,CAAC;AACpF,KAAK,QAAQ,CAAC,GAAG,IAAI,UAAU,CAAC,KAAK,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC;AACzE,KAAK,WAAW,CAAC,GAAG,IAAI,UAAU,CAAC,QAAQ,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC;AAC/E,KAAK,iBAAiB,CAAC,GAAG,IACxB,WAAW,CAAC,GAAG,CAAC,SAAS,UAAU,CAAC,YAAY,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,CAAC,GAC9D,UAAU,CAAC,YAAY,CAAC,CAAC,EAAE,CAAC,CAAC,GAC7B,OAAO,CAAC;AAEd;;;;;;;;;;;;;;;GAeG;AACH,MAAM,MAAM,WAAW,CAAC,KAAK,EAAE,MAAM,IAAI,CAAC,CACxC,OAAO,EAAE,SAAS,CAAC,KAAK,CAAC,GAAG,SAAS,CAAC,MAAM,CAAC,KAC1C,MAAM,CAAC,MAAM,CAChB,IAAI,EACJ,QAAQ,CAAC,KAAK,CAAC,GAAG,QAAQ,CAAC,MAAM,CAAC,EAClC,WAAW,CAAC,KAAK,CAAC,GAAG,WAAW,CAAC,MAAM,CAAC,CACzC,CAAC,GACA,iBAAiB,CAAC,KAAK,CAAC,GACxB,iBAAiB,CAAC,MAAM,CAAC,CAAC;AAE5B;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,WAAW,CACzB,KAAK,CAAC,KAAK,SAAS,QAAQ,GAAG,IAAI,GAAG,SAAS,EAC/C,KAAK,CAAC,MAAM,SAAS,QAAQ,GAAG,IAAI,GAAG,SAAS,GAAG,SAAS,EAC5D,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,WAAW,CAAC,KAAK,EAAE,MAAM,CAAC,GAAG,SAAS,CAoBvE"}
|
package/dist/Dom/Refs.js
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import * as Effect from "effect/Effect";
|
|
2
|
+
import * as Stream from "effect/Stream";
|
|
3
|
+
import { RefSubject } from "@typed/fx";
|
|
4
|
+
import { drain, isFx } from "@typed/fx/Fx";
|
|
5
|
+
/**
|
|
6
|
+
* Composes two element refs in order.
|
|
7
|
+
*
|
|
8
|
+
* @remarks
|
|
9
|
+
* ## Why
|
|
10
|
+
* A component's internal ref and a caller's ref both need the real mounted
|
|
11
|
+
* node. Each return value may be void, Effect, Stream, or Fx, and composition
|
|
12
|
+
* normalizes those forms without hiding failures or services.
|
|
13
|
+
*
|
|
14
|
+
* ## Ownership and lifetime
|
|
15
|
+
* The first ref runs before the second. Streams and Fx values are drained in
|
|
16
|
+
* the caller's Scope and interrupted on unmount. The function throws
|
|
17
|
+
* synchronously when both refs claim hydration ownership because applying two
|
|
18
|
+
* serialized states to one element is ambiguous.
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```ts
|
|
22
|
+
* import { composeRefs } from "@typed/ui/Dom/Refs"
|
|
23
|
+
* import { Effect } from "effect"
|
|
24
|
+
*
|
|
25
|
+
* const ref = composeRefs(
|
|
26
|
+
* (element: HTMLButtonElement) => Effect.sync(() => element.focus()),
|
|
27
|
+
* (element: HTMLButtonElement) => console.log(element)
|
|
28
|
+
* )
|
|
29
|
+
* ```
|
|
30
|
+
*
|
|
31
|
+
* @since 1.0.0
|
|
32
|
+
* @category Ref composition
|
|
33
|
+
*/
|
|
34
|
+
export function composeRefs(first, second) {
|
|
35
|
+
if (!first && !second)
|
|
36
|
+
return undefined;
|
|
37
|
+
const hydrationOwners = [first, second].filter(RefSubject.isHydrationRef);
|
|
38
|
+
if (hydrationOwners.length > 1) {
|
|
39
|
+
throw new TypeError("Only one hydration ref can own an element");
|
|
40
|
+
}
|
|
41
|
+
const composed = Effect.fn((element) => Effect.andThen(runRef(first, element), runRef(second, element)));
|
|
42
|
+
const hydrationOwner = hydrationOwners[0];
|
|
43
|
+
const ref = hydrationOwner === undefined
|
|
44
|
+
? composed
|
|
45
|
+
: Object.assign(composed, {
|
|
46
|
+
[RefSubject.HydrationRefTypeId]: hydrationOwner[RefSubject.HydrationRefTypeId],
|
|
47
|
+
});
|
|
48
|
+
return ref;
|
|
49
|
+
}
|
|
50
|
+
function runRef(ref, element) {
|
|
51
|
+
if (!ref)
|
|
52
|
+
return Effect.void;
|
|
53
|
+
const result = ref(element);
|
|
54
|
+
if (Effect.isEffect(result))
|
|
55
|
+
return Effect.asVoid(result);
|
|
56
|
+
if (Stream.isStream(result))
|
|
57
|
+
return Stream.runDrain(result);
|
|
58
|
+
if (isFx(result))
|
|
59
|
+
return drain(result);
|
|
60
|
+
return Effect.void;
|
|
61
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { type RenderHostProps } from "./Props.js";
|
|
2
|
+
import type { HostComponent, HostOptions, HostOverride, HostProps, HostResult, InternalPropsHelpers, RenderableInput } from "./Types.js";
|
|
3
|
+
/**
|
|
4
|
+
* Creates the host-rendering boundary for a particular DOM element type.
|
|
5
|
+
*
|
|
6
|
+
* @remarks
|
|
7
|
+
* ## Why
|
|
8
|
+
* Components can compute required props once, merge them with caller props,
|
|
9
|
+
* then delegate either to a custom host or to a native-template fallback. This
|
|
10
|
+
* exposes the boundary needed for external renderers without a framework adapter.
|
|
11
|
+
*
|
|
12
|
+
* ## Ownership and lifetime
|
|
13
|
+
* The returned component is lazy. Its Scope owns the chosen Effect/Fx result,
|
|
14
|
+
* merged listener/ref work, and only the render-event range that result emits.
|
|
15
|
+
* A custom host owns output it creates and must honor the supplied props.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```ts
|
|
19
|
+
* import { renderHost } from "@typed/ui/Dom/Render"
|
|
20
|
+
* import { html } from "@typed/template"
|
|
21
|
+
*
|
|
22
|
+
* const renderButton = renderHost<HTMLButtonElement>()
|
|
23
|
+
* const button = renderButton(
|
|
24
|
+
* { props: { class: "action" } },
|
|
25
|
+
* undefined,
|
|
26
|
+
* () => ({ type: "button" }),
|
|
27
|
+
* "Save",
|
|
28
|
+
* (props, content) => html`<button ...${props}>${content}</button>`
|
|
29
|
+
* )
|
|
30
|
+
* ```
|
|
31
|
+
*
|
|
32
|
+
* @since 1.0.0
|
|
33
|
+
* @category Host rendering
|
|
34
|
+
*/
|
|
35
|
+
export declare function renderHost<Element extends globalThis.Element>(): <const Options extends HostOptions<Element>, const Internal extends HostProps<Element>, const Content extends RenderableInput, const Fallback extends HostResult, const Host extends HostResult = never>(options: Options, host: HostOverride<RenderHostProps<Options, Internal>, Content, Host> | undefined, buildInternal: (helpers: InternalPropsHelpers<Options>) => Internal, content: Content, fallback: (props: RenderHostProps<Options, Internal>, content: Content) => Fallback) => HostComponent<Options | Host>;
|
|
36
|
+
/**
|
|
37
|
+
* Renders a native `div` host from supplied props and content.
|
|
38
|
+
*
|
|
39
|
+
* @remarks
|
|
40
|
+
* ## Why
|
|
41
|
+
* Many behavior-only components need a standards-based neutral fallback while
|
|
42
|
+
* still allowing callers to replace the host through `renderHost`.
|
|
43
|
+
*
|
|
44
|
+
* ## Ownership and lifetime
|
|
45
|
+
* The returned template is lazy. Its Scope owns reactive parts and the created
|
|
46
|
+
* div; it does not claim surrounding or externally supplied DOM.
|
|
47
|
+
*
|
|
48
|
+
* @example
|
|
49
|
+
* ```ts
|
|
50
|
+
* import { renderDivHost } from "@typed/ui/Dom/Render"
|
|
51
|
+
*
|
|
52
|
+
* const host = renderDivHost({ role: "group", class: "cluster" }, "Actions")
|
|
53
|
+
* ```
|
|
54
|
+
*
|
|
55
|
+
* @since 1.0.0
|
|
56
|
+
* @category Host rendering
|
|
57
|
+
*/
|
|
58
|
+
export declare function renderDivHost<const Props extends HostProps<HTMLDivElement>, const Content extends RenderableInput>(props: Props, content: Content): HostComponent<Props | Content>;
|
|
59
|
+
//# sourceMappingURL=Render.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Render.d.ts","sourceRoot":"","sources":["../../src/Dom/Render.ts"],"names":[],"mappings":"AAGA,OAAO,EAKL,KAAK,eAAe,EACrB,MAAM,YAAY,CAAC;AACpB,OAAO,KAAK,EAEV,aAAa,EACb,WAAW,EACX,YAAY,EACZ,SAAS,EACT,UAAU,EACV,oBAAoB,EACpB,eAAe,EAChB,MAAM,YAAY,CAAC;AAEpB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAgB,UAAU,CAAC,OAAO,SAAS,UAAU,CAAC,OAAO,MAEzD,KAAK,CAAC,OAAO,SAAS,WAAW,CAAC,OAAO,CAAC,EAC1C,KAAK,CAAC,QAAQ,SAAS,SAAS,CAAC,OAAO,CAAC,EACzC,KAAK,CAAC,OAAO,SAAS,eAAe,EACrC,KAAK,CAAC,QAAQ,SAAS,UAAU,EACjC,KAAK,CAAC,IAAI,SAAS,UAAU,GAAG,KAAK,WAE5B,OAAO,QACV,YAAY,CAAC,eAAe,CAAC,OAAO,EAAE,QAAQ,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,GAAG,SAAS,iBAClE,CAAC,OAAO,EAAE,oBAAoB,CAAC,OAAO,CAAC,KAAK,QAAQ,WAC1D,OAAO,YACN,CAAC,KAAK,EAAE,eAAe,CAAC,OAAO,EAAE,QAAQ,CAAC,EAAE,OAAO,EAAE,OAAO,KAAK,QAAQ,KAClF,aAAa,CAAC,OAAO,GAAG,IAAI,CAAC,CASjC;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,aAAa,CAC3B,KAAK,CAAC,KAAK,SAAS,SAAS,CAAC,cAAc,CAAC,EAC7C,KAAK,CAAC,OAAO,SAAS,eAAe,EACrC,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,GAAG,aAAa,CAAC,KAAK,GAAG,OAAO,CAAC,CAEhE"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import * as Effect from "effect/Effect";
|
|
2
|
+
import { fromEffect } from "@typed/fx/Fx";
|
|
3
|
+
import { html } from "@typed/template";
|
|
4
|
+
import { forwardHostProps, getProperty, makeInternalPropsHelpers, mergeProps, } from "./Props.js";
|
|
5
|
+
/**
|
|
6
|
+
* Creates the host-rendering boundary for a particular DOM element type.
|
|
7
|
+
*
|
|
8
|
+
* @remarks
|
|
9
|
+
* ## Why
|
|
10
|
+
* Components can compute required props once, merge them with caller props,
|
|
11
|
+
* then delegate either to a custom host or to a native-template fallback. This
|
|
12
|
+
* exposes the boundary needed for external renderers without a framework adapter.
|
|
13
|
+
*
|
|
14
|
+
* ## Ownership and lifetime
|
|
15
|
+
* The returned component is lazy. Its Scope owns the chosen Effect/Fx result,
|
|
16
|
+
* merged listener/ref work, and only the render-event range that result emits.
|
|
17
|
+
* A custom host owns output it creates and must honor the supplied props.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```ts
|
|
21
|
+
* import { renderHost } from "@typed/ui/Dom/Render"
|
|
22
|
+
* import { html } from "@typed/template"
|
|
23
|
+
*
|
|
24
|
+
* const renderButton = renderHost<HTMLButtonElement>()
|
|
25
|
+
* const button = renderButton(
|
|
26
|
+
* { props: { class: "action" } },
|
|
27
|
+
* undefined,
|
|
28
|
+
* () => ({ type: "button" }),
|
|
29
|
+
* "Save",
|
|
30
|
+
* (props, content) => html`<button ...${props}>${content}</button>`
|
|
31
|
+
* )
|
|
32
|
+
* ```
|
|
33
|
+
*
|
|
34
|
+
* @since 1.0.0
|
|
35
|
+
* @category Host rendering
|
|
36
|
+
*/
|
|
37
|
+
export function renderHost() {
|
|
38
|
+
return function (options, host, buildInternal, content, fallback) {
|
|
39
|
+
const props = mergeProps(mergeProps(getProperty(options, "props"), forwardHostProps(options)), buildInternal(makeInternalPropsHelpers(options)));
|
|
40
|
+
const rendered = host ? host(props, content) : fallback(props, content);
|
|
41
|
+
return componentBoundary(rendered);
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Renders a native `div` host from supplied props and content.
|
|
46
|
+
*
|
|
47
|
+
* @remarks
|
|
48
|
+
* ## Why
|
|
49
|
+
* Many behavior-only components need a standards-based neutral fallback while
|
|
50
|
+
* still allowing callers to replace the host through `renderHost`.
|
|
51
|
+
*
|
|
52
|
+
* ## Ownership and lifetime
|
|
53
|
+
* The returned template is lazy. Its Scope owns reactive parts and the created
|
|
54
|
+
* div; it does not claim surrounding or externally supplied DOM.
|
|
55
|
+
*
|
|
56
|
+
* @example
|
|
57
|
+
* ```ts
|
|
58
|
+
* import { renderDivHost } from "@typed/ui/Dom/Render"
|
|
59
|
+
*
|
|
60
|
+
* const host = renderDivHost({ role: "group", class: "cluster" }, "Actions")
|
|
61
|
+
* ```
|
|
62
|
+
*
|
|
63
|
+
* @since 1.0.0
|
|
64
|
+
* @category Host rendering
|
|
65
|
+
*/
|
|
66
|
+
export function renderDivHost(props, content) {
|
|
67
|
+
return html `<div ...${props}>${content}</div>`;
|
|
68
|
+
}
|
|
69
|
+
function componentBoundary(value) {
|
|
70
|
+
return (Effect.isEffect(value) ? fromEffect(value) : value);
|
|
71
|
+
}
|