foldkit 0.16.0 → 0.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -8
- package/dist/{fieldValidation.d.ts → fieldValidation/index.d.ts} +9 -9
- package/dist/fieldValidation/index.d.ts.map +1 -0
- package/dist/{fieldValidation.js → fieldValidation/index.js} +5 -4
- package/dist/fieldValidation/public.d.ts +3 -0
- package/dist/fieldValidation/public.d.ts.map +1 -0
- package/dist/fieldValidation/public.js +1 -0
- package/dist/{html.d.ts → html/index.d.ts} +40 -4
- package/dist/html/index.d.ts.map +1 -0
- package/dist/{html.js → html/index.js} +31 -4
- package/dist/html/public.d.ts +3 -0
- package/dist/html/public.d.ts.map +1 -0
- package/dist/html/public.js +1 -0
- package/dist/index.d.ts +9 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -8
- package/dist/{navigation.d.ts → navigation/index.d.ts} +1 -1
- package/dist/navigation/index.d.ts.map +1 -0
- package/dist/navigation/public.d.ts +2 -0
- package/dist/navigation/public.d.ts.map +1 -0
- package/dist/navigation/public.js +1 -0
- package/dist/route/index.d.ts +2 -0
- package/dist/route/index.d.ts.map +1 -0
- package/dist/{parser.d.ts → route/parser.d.ts} +1 -1
- package/dist/route/parser.d.ts.map +1 -0
- package/dist/route/public.d.ts +3 -0
- package/dist/route/public.d.ts.map +1 -0
- package/dist/route/public.js +1 -0
- package/dist/runtime/browserListeners.d.ts.map +1 -1
- package/dist/runtime/browserListeners.js +2 -2
- package/dist/runtime/public.d.ts +5 -0
- package/dist/runtime/public.d.ts.map +1 -0
- package/dist/runtime/public.js +2 -0
- package/dist/runtime/runtime.d.ts +1 -1
- package/dist/runtime/runtime.d.ts.map +1 -1
- package/dist/runtime/urlRequest.d.ts +4 -4
- package/dist/runtime/urlRequest.d.ts.map +1 -1
- package/dist/runtime/urlRequest.js +3 -2
- package/dist/schema/index.d.ts +26 -0
- package/dist/schema/index.d.ts.map +1 -0
- package/dist/schema/index.js +14 -0
- package/dist/schema/public.d.ts +3 -0
- package/dist/schema/public.d.ts.map +1 -0
- package/dist/schema/public.js +1 -0
- package/dist/{struct.d.ts → struct/index.d.ts} +2 -1
- package/dist/struct/index.d.ts.map +1 -0
- package/dist/struct/index.js +3 -0
- package/dist/struct/public.d.ts +2 -0
- package/dist/struct/public.d.ts.map +1 -0
- package/dist/struct/public.js +1 -0
- package/dist/task/index.d.ts +109 -0
- package/dist/task/index.d.ts.map +1 -0
- package/dist/task/index.js +168 -0
- package/dist/task/public.d.ts +2 -0
- package/dist/task/public.d.ts.map +1 -0
- package/dist/task/public.js +1 -0
- package/dist/ui/dialog/index.d.ts +46 -0
- package/dist/ui/dialog/index.d.ts.map +1 -0
- package/dist/ui/dialog/index.js +67 -0
- package/dist/ui/dialog/public.d.ts +3 -0
- package/dist/ui/dialog/public.d.ts.map +1 -0
- package/dist/ui/dialog/public.js +1 -0
- package/dist/ui/disclosure/index.d.ts +47 -0
- package/dist/ui/disclosure/index.d.ts.map +1 -0
- package/dist/ui/disclosure/index.js +90 -0
- package/dist/ui/disclosure/public.d.ts +3 -0
- package/dist/ui/disclosure/public.d.ts.map +1 -0
- package/dist/ui/disclosure/public.js +1 -0
- package/dist/ui/index.d.ts +4 -1
- package/dist/ui/index.d.ts.map +1 -1
- package/dist/ui/index.js +4 -1
- package/dist/ui/keyboard.d.ts +4 -0
- package/dist/ui/keyboard.d.ts.map +1 -0
- package/dist/ui/keyboard.js +7 -0
- package/dist/ui/menu/index.d.ts +136 -0
- package/dist/ui/menu/index.d.ts.map +1 -0
- package/dist/ui/menu/index.js +297 -0
- package/dist/ui/menu/public.d.ts +3 -0
- package/dist/ui/menu/public.d.ts.map +1 -0
- package/dist/ui/menu/public.js +1 -0
- package/dist/ui/tabs/index.d.ts +76 -0
- package/dist/ui/tabs/index.d.ts.map +1 -0
- package/dist/ui/{tabs.js → tabs/index.js} +25 -25
- package/dist/ui/tabs/public.d.ts +3 -0
- package/dist/ui/tabs/public.d.ts.map +1 -0
- package/dist/ui/tabs/public.js +1 -0
- package/dist/{url.d.ts → url/index.d.ts} +2 -2
- package/dist/url/index.d.ts.map +1 -0
- package/dist/{url.js → url/index.js} +1 -1
- package/dist/url/public.d.ts +2 -0
- package/dist/url/public.d.ts.map +1 -0
- package/dist/url/public.js +1 -0
- package/package.json +32 -19
- package/dist/fieldValidation.d.ts.map +0 -1
- package/dist/html.d.ts.map +0 -1
- package/dist/navigation.d.ts.map +0 -1
- package/dist/parser.d.ts.map +0 -1
- package/dist/route.d.ts +0 -2
- package/dist/route.d.ts.map +0 -1
- package/dist/schema.d.ts +0 -20
- package/dist/schema.d.ts.map +0 -1
- package/dist/schema.js +0 -16
- package/dist/struct.d.ts.map +0 -1
- package/dist/struct.js +0 -2
- package/dist/task.d.ts +0 -64
- package/dist/task.d.ts.map +0 -1
- package/dist/task.js +0 -87
- package/dist/ui/tabs.d.ts +0 -65
- package/dist/ui/tabs.d.ts.map +0 -1
- package/dist/url.d.ts.map +0 -1
- /package/dist/{navigation.js → navigation/index.js} +0 -0
- /package/dist/{route.js → route/index.js} +0 -0
- /package/dist/{parser.js → route/parser.js} +0 -0
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { Schema as S } from 'effect';
|
|
2
|
+
import { ts } from '../schema';
|
|
2
3
|
import { Url } from '../url';
|
|
3
4
|
/** A URL request to a page within the application (same origin). */
|
|
4
|
-
export const Internal =
|
|
5
|
+
export const Internal = ts('Internal', {
|
|
5
6
|
url: Url,
|
|
6
7
|
});
|
|
7
8
|
/** A URL request to an external page (different origin). */
|
|
8
|
-
export const External =
|
|
9
|
+
export const External = ts('External', {
|
|
9
10
|
href: S.String,
|
|
10
11
|
});
|
|
11
12
|
/** Union of `Internal` and `External` URL request types. */
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Schema as S } from 'effect';
|
|
2
|
+
/** A `TaggedStruct` schema that can be called directly as a constructor: `Foo({ count: 1 })` instead of `Foo.make({ count: 1 })`. */
|
|
3
|
+
export type CallableTaggedStruct<Tag extends string, Fields extends S.Struct.Fields> = S.TaggedStruct<Tag, Fields> & (keyof Fields extends never ? (value?: Parameters<S.TaggedStruct<Tag, Fields>['make']>[0] | void) => S.Simplify<S.Struct.Type<{
|
|
4
|
+
readonly _tag: S.tag<Tag>;
|
|
5
|
+
} & Fields>> : (value: Parameters<S.TaggedStruct<Tag, Fields>['make']>[0]) => S.Simplify<S.Struct.Type<{
|
|
6
|
+
readonly _tag: S.tag<Tag>;
|
|
7
|
+
} & Fields>>);
|
|
8
|
+
/**
|
|
9
|
+
* A wrapper around Effect Schema.taggedStruct that returns a callable schema.
|
|
10
|
+
*
|
|
11
|
+
* Abbreviated as `ts` because it's used so frequently throughout Foldkit applications.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```typescript
|
|
15
|
+
* // Simple tag — callable with no args
|
|
16
|
+
* const Reset = ts('Reset')
|
|
17
|
+
* Reset() // { _tag: 'Reset' }
|
|
18
|
+
*
|
|
19
|
+
* // Tag with fields — callable with fields
|
|
20
|
+
* const SetCount = ts('SetCount', { count: S.Number })
|
|
21
|
+
* SetCount({ count: 1 }) // { _tag: 'SetCount', count: 1 }
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
export declare function ts<Tag extends string>(tag: Tag): CallableTaggedStruct<Tag, {}>;
|
|
25
|
+
export declare function ts<Tag extends string, Fields extends S.Struct.Fields>(tag: Tag, fields: Fields): CallableTaggedStruct<Tag, Fields>;
|
|
26
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/schema/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC,EAAE,MAAM,QAAQ,CAAA;AAEpC,qIAAqI;AACrI,MAAM,MAAM,oBAAoB,CAC9B,GAAG,SAAS,MAAM,EAClB,MAAM,SAAS,CAAC,CAAC,MAAM,CAAC,MAAM,IAC5B,CAAC,CAAC,YAAY,CAAC,GAAG,EAAE,MAAM,CAAC,GAC7B,CAAC,MAAM,MAAM,SAAS,KAAK,GACvB,CACE,KAAK,CAAC,EAAE,UAAU,CAAC,CAAC,CAAC,YAAY,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,KAC9D,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC;IAAE,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;CAAE,GAAG,MAAM,CAAC,CAAC,GACtE,CACE,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,YAAY,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,KACtD,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC;IAAE,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;CAAE,GAAG,MAAM,CAAC,CAAC,CAAC,CAAA;AAe7E;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,EAAE,CAAC,GAAG,SAAS,MAAM,EAAE,GAAG,EAAE,GAAG,GAAG,oBAAoB,CAAC,GAAG,EAAE,EAAE,CAAC,CAAA;AAC/E,wBAAgB,EAAE,CAAC,GAAG,SAAS,MAAM,EAAE,MAAM,SAAS,CAAC,CAAC,MAAM,CAAC,MAAM,EACnE,GAAG,EAAE,GAAG,EACR,MAAM,EAAE,MAAM,GACb,oBAAoB,CAAC,GAAG,EAAE,MAAM,CAAC,CAAA"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Schema as S } from 'effect';
|
|
2
|
+
const makeCallable = (schema) =>
|
|
3
|
+
/* eslint-disable-next-line @typescript-eslint/consistent-type-assertions */
|
|
4
|
+
new Proxy(schema, {
|
|
5
|
+
apply(_target, _thisArg, argumentsList) {
|
|
6
|
+
return schema.make(argumentsList[0]);
|
|
7
|
+
},
|
|
8
|
+
get(target, property, receiver) {
|
|
9
|
+
return Reflect.get(target, property, receiver);
|
|
10
|
+
},
|
|
11
|
+
});
|
|
12
|
+
export function ts(tag, fields = {}) {
|
|
13
|
+
return makeCallable(S.TaggedStruct(tag, fields));
|
|
14
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"public.d.ts","sourceRoot":"","sources":["../../src/schema/public.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,EAAE,MAAM,SAAS,CAAA;AAC5B,YAAY,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { ts } from './index';
|
|
@@ -7,9 +7,10 @@ type StrictKeys<O, T> = T extends Record<string, any> ? Exclude<keyof T, keyof O
|
|
|
7
7
|
type Evolved<O, T> = {
|
|
8
8
|
[K in keyof O]: K extends keyof T ? T[K] extends (a: any) => infer R ? R : O[K] : O[K];
|
|
9
9
|
};
|
|
10
|
+
/** Immutably updates fields of a struct by applying transform functions. Wraps Effect's `Struct.evolve` with stricter key checking. */
|
|
10
11
|
export declare const evo: {
|
|
11
12
|
<O, const T extends EvolveTransform<O>>(t: StrictKeys<O, T>): (obj: O) => Evolved<O, T>;
|
|
12
13
|
<O, const T extends EvolveTransform<O>>(obj: O, t: StrictKeys<O, T>): Evolved<O, T>;
|
|
13
14
|
};
|
|
14
15
|
export {};
|
|
15
|
-
//# sourceMappingURL=
|
|
16
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/struct/index.ts"],"names":[],"mappings":"AAEA,KAAK,eAAe,CAAC,CAAC,IAAI,OAAO,CAAC;KAC/B,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;CAClC,CAAC,CAAA;AAEF,KAAK,UAAU,CAAC,CAAC,EAAE,CAAC,IAClB,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GACzB,OAAO,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,CAAC,SAAS,KAAK,GACrC,CAAC,GACD,CAAC,GAAG;KACD,CAAC,IAAI,gBAAgB,OAAO,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,CAAC,GAAG,MAAM,EAAE,GAAG,KAAK;CACnE,GACH,KAAK,CAAA;AAEX,KAAK,OAAO,CAAC,CAAC,EAAE,CAAC,IAAI;KAClB,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,SAAS,MAAM,CAAC,GAC7B,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,EAAE,GAAG,KAAK,MAAM,CAAC,GAC9B,CAAC,GACD,CAAC,CAAC,CAAC,CAAC,GACN,CAAC,CAAC,CAAC,CAAC;CACT,CAAA;AAED,uIAAuI;AACvI,eAAO,MAAM,GAAG,EAAE;IAChB,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,SAAS,eAAe,CAAC,CAAC,CAAC,EACpC,CAAC,EAAE,UAAU,CAAC,CAAC,EAAE,CAAC,CAAC,GAClB,CAAC,GAAG,EAAE,CAAC,KAAK,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;IAC5B,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,SAAS,eAAe,CAAC,CAAC,CAAC,EACpC,GAAG,EAAE,CAAC,EACN,CAAC,EAAE,UAAU,CAAC,CAAC,EAAE,CAAC,CAAC,GAClB,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;CACD,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"public.d.ts","sourceRoot":"","sources":["../../src/struct/public.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,SAAS,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { evo } from './index';
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { DateTime, Duration, Effect } from 'effect';
|
|
2
|
+
/**
|
|
3
|
+
* Creates a command that gets the current UTC time and passes it to a message constructor.
|
|
4
|
+
* This is similar to Elm's `Task.perform` with `Time.now`.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```typescript
|
|
8
|
+
* Task.getTime(utc => GotTime({ utc }))
|
|
9
|
+
* ```
|
|
10
|
+
*/
|
|
11
|
+
export declare const getTime: <Message>(f: (utc: DateTime.Utc) => Message) => Effect.Effect<Message>;
|
|
12
|
+
/**
|
|
13
|
+
* Creates a command that gets the system timezone and passes it to a message constructor.
|
|
14
|
+
* This is similar to Elm's `Task.perform` with `Time.here`.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```typescript
|
|
18
|
+
* Task.getTimeZone(zone => GotTimeZone({ zone }))
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
export declare const getTimeZone: <Message>(f: (zone: DateTime.TimeZone) => Message) => Effect.Effect<Message>;
|
|
22
|
+
/**
|
|
23
|
+
* Creates a command that gets the current time in the system timezone and passes it to a message constructor.
|
|
24
|
+
* This combines both time and timezone in a single task.
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```typescript
|
|
28
|
+
* Task.getZonedTime(zoned => GotTime({ zoned }))
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
export declare const getZonedTime: <Message>(f: (zoned: DateTime.Zoned) => Message) => Effect.Effect<Message>;
|
|
32
|
+
/**
|
|
33
|
+
* Creates a command that gets the current time in a specific timezone and passes it to a message constructor.
|
|
34
|
+
* If the timezone is invalid, the effect will fail with an error string.
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* ```typescript
|
|
38
|
+
* Task.getZonedTimeIn('America/New_York', zoned => GotNYTime({ zoned }))
|
|
39
|
+
* ```
|
|
40
|
+
*/
|
|
41
|
+
export declare const getZonedTimeIn: <Message>(zoneId: string, f: (zoned: DateTime.Zoned) => Message) => Effect.Effect<Message, string>;
|
|
42
|
+
/**
|
|
43
|
+
* Creates a command that focuses an element by selector and passes the result to a message constructor.
|
|
44
|
+
* Passes true if the element was found and focused, false otherwise.
|
|
45
|
+
* Uses requestAnimationFrame to ensure the DOM tree is updated and nodes exist before attempting to focus.
|
|
46
|
+
* This follows the same approach as Elm's Browser.Dom.focus.
|
|
47
|
+
*
|
|
48
|
+
* @example
|
|
49
|
+
* ```typescript
|
|
50
|
+
* Task.focus('#email-input', success => InputFocused({ success }))
|
|
51
|
+
* ```
|
|
52
|
+
*/
|
|
53
|
+
export declare const focus: <Message>(selector: string, f: (success: boolean) => Message) => Effect.Effect<Message>;
|
|
54
|
+
/**
|
|
55
|
+
* Creates a command that opens a dialog element as a modal using `showModal()`.
|
|
56
|
+
* Passes true if the element was found and opened, false otherwise.
|
|
57
|
+
* Uses requestAnimationFrame to ensure the DOM tree is updated and nodes exist before attempting to show.
|
|
58
|
+
*
|
|
59
|
+
* @example
|
|
60
|
+
* ```typescript
|
|
61
|
+
* Task.showModal('#my-dialog', success => ModalOpened({ success }))
|
|
62
|
+
* ```
|
|
63
|
+
*/
|
|
64
|
+
export declare const showModal: <Message>(selector: string, f: (success: boolean) => Message) => Effect.Effect<Message>;
|
|
65
|
+
/**
|
|
66
|
+
* Creates a command that closes a dialog element using `.close()`.
|
|
67
|
+
* Passes true if the element was found and closed, false otherwise.
|
|
68
|
+
* Uses requestAnimationFrame to ensure the DOM tree is updated and nodes exist before attempting to close.
|
|
69
|
+
*
|
|
70
|
+
* @example
|
|
71
|
+
* ```typescript
|
|
72
|
+
* Task.closeModal('#my-dialog', success => ModalClosed({ success }))
|
|
73
|
+
* ```
|
|
74
|
+
*/
|
|
75
|
+
export declare const closeModal: <Message>(selector: string, f: (success: boolean) => Message) => Effect.Effect<Message>;
|
|
76
|
+
/**
|
|
77
|
+
* Creates a command that resolves to a message after a delay.
|
|
78
|
+
* Useful for debouncing, such as clearing a typeahead search query.
|
|
79
|
+
*
|
|
80
|
+
* @example
|
|
81
|
+
* ```typescript
|
|
82
|
+
* Task.delay(350, () => SearchCleared({ version: model.searchVersion }))
|
|
83
|
+
* Task.delay(Duration.seconds(1), () => TimedOut())
|
|
84
|
+
* ```
|
|
85
|
+
*/
|
|
86
|
+
export declare const delay: <Message>(duration: Duration.DurationInput, f: () => Message) => Effect.Effect<Message>;
|
|
87
|
+
/**
|
|
88
|
+
* Creates a command that generates a random integer between min (inclusive) and max (exclusive)
|
|
89
|
+
* and passes it to a message constructor.
|
|
90
|
+
*
|
|
91
|
+
* @example
|
|
92
|
+
* ```typescript
|
|
93
|
+
* Task.randomInt(0, 100, value => GotRandom({ value }))
|
|
94
|
+
* ```
|
|
95
|
+
*/
|
|
96
|
+
/**
|
|
97
|
+
* Creates a command that scrolls an element into view by selector and passes the result to a message constructor.
|
|
98
|
+
* Passes true if the element was found and scrolled, false otherwise.
|
|
99
|
+
* Uses requestAnimationFrame to ensure the DOM tree is updated and nodes exist before attempting to scroll.
|
|
100
|
+
* Uses `{ block: 'nearest' }` to avoid unnecessary scrolling when the element is already visible.
|
|
101
|
+
*
|
|
102
|
+
* @example
|
|
103
|
+
* ```typescript
|
|
104
|
+
* Task.scrollIntoView('#active-item', success => ItemScrolled({ success }))
|
|
105
|
+
* ```
|
|
106
|
+
*/
|
|
107
|
+
export declare const scrollIntoView: <Message>(selector: string, f: (success: boolean) => Message) => Effect.Effect<Message>;
|
|
108
|
+
export declare const randomInt: <Message>(min: number, max: number, f: (value: number) => Message) => Effect.Effect<Message>;
|
|
109
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/task/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAU,MAAM,QAAQ,CAAA;AAE3D;;;;;;;;GAQG;AACH,eAAO,MAAM,OAAO,GAAI,OAAO,EAC7B,GAAG,CAAC,GAAG,EAAE,QAAQ,CAAC,GAAG,KAAK,OAAO,KAChC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAgC,CAAA;AAExD;;;;;;;;GAQG;AACH,eAAO,MAAM,WAAW,GAAI,OAAO,EACjC,GAAG,CAAC,IAAI,EAAE,QAAQ,CAAC,QAAQ,KAAK,OAAO,KACtC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAmD,CAAA;AAE3E;;;;;;;;GAQG;AACH,eAAO,MAAM,YAAY,GAAI,OAAO,EAClC,GAAG,CAAC,KAAK,EAAE,QAAQ,CAAC,KAAK,KAAK,OAAO,KACpC,MAAM,CAAC,MAAM,CAAC,OAAO,CAMpB,CAAA;AAEJ;;;;;;;;GAQG;AACH,eAAO,MAAM,cAAc,GAAI,OAAO,EACpC,QAAQ,MAAM,EACd,GAAG,CAAC,KAAK,EAAE,QAAQ,CAAC,KAAK,KAAK,OAAO,KACpC,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,MAAM,CAS5B,CAAA;AAEJ;;;;;;;;;;GAUG;AACH,eAAO,MAAM,KAAK,GAAI,OAAO,EAC3B,UAAU,MAAM,EAChB,GAAG,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,KAC/B,MAAM,CAAC,MAAM,CAAC,OAAO,CAWpB,CAAA;AAEJ;;;;;;;;;GASG;AACH,eAAO,MAAM,SAAS,GAAI,OAAO,EAC/B,UAAU,MAAM,EAChB,GAAG,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,KAC/B,MAAM,CAAC,MAAM,CAAC,OAAO,CAWpB,CAAA;AAEJ;;;;;;;;;GASG;AACH,eAAO,MAAM,UAAU,GAAI,OAAO,EAChC,UAAU,MAAM,EAChB,GAAG,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,KAC/B,MAAM,CAAC,MAAM,CAAC,OAAO,CAWpB,CAAA;AAEJ;;;;;;;;;GASG;AACH,eAAO,MAAM,KAAK,GAAI,OAAO,EAC3B,UAAU,QAAQ,CAAC,aAAa,EAChC,GAAG,MAAM,OAAO,KACf,MAAM,CAAC,MAAM,CAAC,OAAO,CAIpB,CAAA;AAEJ;;;;;;;;GAQG;AACH;;;;;;;;;;GAUG;AACH,eAAO,MAAM,cAAc,GAAI,OAAO,EACpC,UAAU,MAAM,EAChB,GAAG,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,KAC/B,MAAM,CAAC,MAAM,CAAC,OAAO,CAWpB,CAAA;AAEJ,eAAO,MAAM,SAAS,GAAI,OAAO,EAC/B,KAAK,MAAM,EACX,KAAK,MAAM,EACX,GAAG,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,KAC5B,MAAM,CAAC,MAAM,CAAC,OAAO,CAC6C,CAAA"}
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { DateTime, Effect, Option } from 'effect';
|
|
2
|
+
/**
|
|
3
|
+
* Creates a command that gets the current UTC time and passes it to a message constructor.
|
|
4
|
+
* This is similar to Elm's `Task.perform` with `Time.now`.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```typescript
|
|
8
|
+
* Task.getTime(utc => GotTime({ utc }))
|
|
9
|
+
* ```
|
|
10
|
+
*/
|
|
11
|
+
export const getTime = (f) => Effect.map(DateTime.now, f);
|
|
12
|
+
/**
|
|
13
|
+
* Creates a command that gets the system timezone and passes it to a message constructor.
|
|
14
|
+
* This is similar to Elm's `Task.perform` with `Time.here`.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```typescript
|
|
18
|
+
* Task.getTimeZone(zone => GotTimeZone({ zone }))
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
export const getTimeZone = (f) => Effect.sync(() => f(DateTime.zoneMakeLocal()));
|
|
22
|
+
/**
|
|
23
|
+
* Creates a command that gets the current time in the system timezone and passes it to a message constructor.
|
|
24
|
+
* This combines both time and timezone in a single task.
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```typescript
|
|
28
|
+
* Task.getZonedTime(zoned => GotTime({ zoned }))
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
export const getZonedTime = (f) => Effect.gen(function* () {
|
|
32
|
+
const utc = yield* DateTime.now;
|
|
33
|
+
const zone = DateTime.zoneMakeLocal();
|
|
34
|
+
const zoned = DateTime.setZone(utc, zone);
|
|
35
|
+
return f(zoned);
|
|
36
|
+
});
|
|
37
|
+
/**
|
|
38
|
+
* Creates a command that gets the current time in a specific timezone and passes it to a message constructor.
|
|
39
|
+
* If the timezone is invalid, the effect will fail with an error string.
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* ```typescript
|
|
43
|
+
* Task.getZonedTimeIn('America/New_York', zoned => GotNYTime({ zoned }))
|
|
44
|
+
* ```
|
|
45
|
+
*/
|
|
46
|
+
export const getZonedTimeIn = (zoneId, f) => Effect.gen(function* () {
|
|
47
|
+
const utc = yield* DateTime.now;
|
|
48
|
+
const maybeZone = DateTime.zoneMakeNamed(zoneId);
|
|
49
|
+
if (Option.isNone(maybeZone)) {
|
|
50
|
+
return yield* Effect.fail(`Invalid timezone: ${zoneId}`);
|
|
51
|
+
}
|
|
52
|
+
const zoned = DateTime.setZone(utc, maybeZone.value);
|
|
53
|
+
return f(zoned);
|
|
54
|
+
});
|
|
55
|
+
/**
|
|
56
|
+
* Creates a command that focuses an element by selector and passes the result to a message constructor.
|
|
57
|
+
* Passes true if the element was found and focused, false otherwise.
|
|
58
|
+
* Uses requestAnimationFrame to ensure the DOM tree is updated and nodes exist before attempting to focus.
|
|
59
|
+
* This follows the same approach as Elm's Browser.Dom.focus.
|
|
60
|
+
*
|
|
61
|
+
* @example
|
|
62
|
+
* ```typescript
|
|
63
|
+
* Task.focus('#email-input', success => InputFocused({ success }))
|
|
64
|
+
* ```
|
|
65
|
+
*/
|
|
66
|
+
export const focus = (selector, f) => Effect.async((resume) => {
|
|
67
|
+
requestAnimationFrame(() => {
|
|
68
|
+
const element = document.querySelector(selector);
|
|
69
|
+
if (element instanceof HTMLElement) {
|
|
70
|
+
element.focus();
|
|
71
|
+
resume(Effect.succeed(f(true)));
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
resume(Effect.succeed(f(false)));
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
/**
|
|
79
|
+
* Creates a command that opens a dialog element as a modal using `showModal()`.
|
|
80
|
+
* Passes true if the element was found and opened, false otherwise.
|
|
81
|
+
* Uses requestAnimationFrame to ensure the DOM tree is updated and nodes exist before attempting to show.
|
|
82
|
+
*
|
|
83
|
+
* @example
|
|
84
|
+
* ```typescript
|
|
85
|
+
* Task.showModal('#my-dialog', success => ModalOpened({ success }))
|
|
86
|
+
* ```
|
|
87
|
+
*/
|
|
88
|
+
export const showModal = (selector, f) => Effect.async((resume) => {
|
|
89
|
+
requestAnimationFrame(() => {
|
|
90
|
+
const element = document.querySelector(selector);
|
|
91
|
+
if (element instanceof HTMLDialogElement) {
|
|
92
|
+
element.showModal();
|
|
93
|
+
resume(Effect.succeed(f(true)));
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
resume(Effect.succeed(f(false)));
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
/**
|
|
101
|
+
* Creates a command that closes a dialog element using `.close()`.
|
|
102
|
+
* Passes true if the element was found and closed, false otherwise.
|
|
103
|
+
* Uses requestAnimationFrame to ensure the DOM tree is updated and nodes exist before attempting to close.
|
|
104
|
+
*
|
|
105
|
+
* @example
|
|
106
|
+
* ```typescript
|
|
107
|
+
* Task.closeModal('#my-dialog', success => ModalClosed({ success }))
|
|
108
|
+
* ```
|
|
109
|
+
*/
|
|
110
|
+
export const closeModal = (selector, f) => Effect.async((resume) => {
|
|
111
|
+
requestAnimationFrame(() => {
|
|
112
|
+
const element = document.querySelector(selector);
|
|
113
|
+
if (element instanceof HTMLDialogElement) {
|
|
114
|
+
element.close();
|
|
115
|
+
resume(Effect.succeed(f(true)));
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
resume(Effect.succeed(f(false)));
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
/**
|
|
123
|
+
* Creates a command that resolves to a message after a delay.
|
|
124
|
+
* Useful for debouncing, such as clearing a typeahead search query.
|
|
125
|
+
*
|
|
126
|
+
* @example
|
|
127
|
+
* ```typescript
|
|
128
|
+
* Task.delay(350, () => SearchCleared({ version: model.searchVersion }))
|
|
129
|
+
* Task.delay(Duration.seconds(1), () => TimedOut())
|
|
130
|
+
* ```
|
|
131
|
+
*/
|
|
132
|
+
export const delay = (duration, f) => Effect.gen(function* () {
|
|
133
|
+
yield* Effect.sleep(duration);
|
|
134
|
+
return f();
|
|
135
|
+
});
|
|
136
|
+
/**
|
|
137
|
+
* Creates a command that generates a random integer between min (inclusive) and max (exclusive)
|
|
138
|
+
* and passes it to a message constructor.
|
|
139
|
+
*
|
|
140
|
+
* @example
|
|
141
|
+
* ```typescript
|
|
142
|
+
* Task.randomInt(0, 100, value => GotRandom({ value }))
|
|
143
|
+
* ```
|
|
144
|
+
*/
|
|
145
|
+
/**
|
|
146
|
+
* Creates a command that scrolls an element into view by selector and passes the result to a message constructor.
|
|
147
|
+
* Passes true if the element was found and scrolled, false otherwise.
|
|
148
|
+
* Uses requestAnimationFrame to ensure the DOM tree is updated and nodes exist before attempting to scroll.
|
|
149
|
+
* Uses `{ block: 'nearest' }` to avoid unnecessary scrolling when the element is already visible.
|
|
150
|
+
*
|
|
151
|
+
* @example
|
|
152
|
+
* ```typescript
|
|
153
|
+
* Task.scrollIntoView('#active-item', success => ItemScrolled({ success }))
|
|
154
|
+
* ```
|
|
155
|
+
*/
|
|
156
|
+
export const scrollIntoView = (selector, f) => Effect.async((resume) => {
|
|
157
|
+
requestAnimationFrame(() => {
|
|
158
|
+
const element = document.querySelector(selector);
|
|
159
|
+
if (element instanceof HTMLElement) {
|
|
160
|
+
element.scrollIntoView({ block: 'nearest' });
|
|
161
|
+
resume(Effect.succeed(f(true)));
|
|
162
|
+
}
|
|
163
|
+
else {
|
|
164
|
+
resume(Effect.succeed(f(false)));
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
});
|
|
168
|
+
export const randomInt = (min, max, f) => Effect.sync(() => f(Math.floor(Math.random() * (max - min)) + min));
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"public.d.ts","sourceRoot":"","sources":["../../src/task/public.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,OAAO,EACP,WAAW,EACX,YAAY,EACZ,cAAc,EACd,KAAK,EACL,SAAS,EACT,UAAU,EACV,KAAK,EACL,cAAc,EACd,SAAS,GACV,MAAM,SAAS,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { getTime, getTimeZone, getZonedTime, getZonedTimeIn, focus, showModal, closeModal, delay, scrollIntoView, randomInt, } from './index';
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { Schema as S } from 'effect';
|
|
2
|
+
import type { Html } from '../../html';
|
|
3
|
+
import type { Command } from '../../runtime/runtime';
|
|
4
|
+
/** Schema for the dialog component's state, tracking its unique ID and open/closed status. */
|
|
5
|
+
export declare const Model: S.Struct<{
|
|
6
|
+
id: typeof S.String;
|
|
7
|
+
isOpen: typeof S.Boolean;
|
|
8
|
+
}>;
|
|
9
|
+
export type Model = typeof Model.Type;
|
|
10
|
+
/** Sent when the dialog should open. Triggers the showModal command. */
|
|
11
|
+
export declare const Opened: import("../../schema").CallableTaggedStruct<"Opened", {}>;
|
|
12
|
+
/** Sent when the dialog should close (Escape key, backdrop click, or programmatic). Triggers the closeModal command. */
|
|
13
|
+
export declare const Closed: import("../../schema").CallableTaggedStruct<"Closed", {}>;
|
|
14
|
+
/** Placeholder message used when no action is needed, such as after a showModal or closeModal command completes. */
|
|
15
|
+
export declare const NoOp: import("../../schema").CallableTaggedStruct<"NoOp", {}>;
|
|
16
|
+
/** Union of all messages the dialog component can produce. */
|
|
17
|
+
export declare const Message: S.Union<[import("../../schema").CallableTaggedStruct<"Opened", {}>, import("../../schema").CallableTaggedStruct<"Closed", {}>, import("../../schema").CallableTaggedStruct<"NoOp", {}>]>;
|
|
18
|
+
export type Opened = typeof Opened.Type;
|
|
19
|
+
export type Closed = typeof Closed.Type;
|
|
20
|
+
export type NoOp = typeof NoOp.Type;
|
|
21
|
+
export type Message = typeof Message.Type;
|
|
22
|
+
/** Configuration for creating a dialog model with `init`. */
|
|
23
|
+
export type InitConfig = Readonly<{
|
|
24
|
+
id: string;
|
|
25
|
+
isOpen?: boolean;
|
|
26
|
+
}>;
|
|
27
|
+
/** Creates an initial dialog model from a config. Defaults to closed. */
|
|
28
|
+
export declare const init: (config: InitConfig) => Model;
|
|
29
|
+
/** Processes a dialog message and returns the next model and commands. */
|
|
30
|
+
export declare const update: (model: Model, message: Message) => [Model, ReadonlyArray<Command<Message>>];
|
|
31
|
+
/** Returns the ID used for `aria-labelledby` on the dialog. Apply this to your title element. */
|
|
32
|
+
export declare const titleId: (model: Model) => string;
|
|
33
|
+
/** Returns the ID used for `aria-describedby` on the dialog. Apply this to your description element. */
|
|
34
|
+
export declare const descriptionId: (model: Model) => string;
|
|
35
|
+
/** Configuration for rendering a dialog with `view`. */
|
|
36
|
+
export type ViewConfig<Message> = Readonly<{
|
|
37
|
+
model: Model;
|
|
38
|
+
toMessage: (message: Closed | NoOp) => Message;
|
|
39
|
+
panelContent: Html;
|
|
40
|
+
panelClassName: string;
|
|
41
|
+
backdropClassName: string;
|
|
42
|
+
className?: string;
|
|
43
|
+
}>;
|
|
44
|
+
/** Renders a headless dialog component backed by the native `<dialog>` element with `showModal()`. */
|
|
45
|
+
export declare const view: <Message>(config: ViewConfig<Message>) => Html;
|
|
46
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/ui/dialog/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,MAAM,IAAI,CAAC,EAAE,MAAM,QAAQ,CAAA;AAGxD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAA;AACtC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAA;AAOpD,8FAA8F;AAC9F,eAAO,MAAM,KAAK;;;EAGhB,CAAA;AAEF,MAAM,MAAM,KAAK,GAAG,OAAO,KAAK,CAAC,IAAI,CAAA;AAIrC,wEAAwE;AACxE,eAAO,MAAM,MAAM,2DAAe,CAAA;AAClC,wHAAwH;AACxH,eAAO,MAAM,MAAM,2DAAe,CAAA;AAClC,oHAAoH;AACpH,eAAO,MAAM,IAAI,yDAAa,CAAA;AAE9B,8DAA8D;AAC9D,eAAO,MAAM,OAAO,0LAAgC,CAAA;AAEpD,MAAM,MAAM,MAAM,GAAG,OAAO,MAAM,CAAC,IAAI,CAAA;AACvC,MAAM,MAAM,MAAM,GAAG,OAAO,MAAM,CAAC,IAAI,CAAA;AACvC,MAAM,MAAM,IAAI,GAAG,OAAO,IAAI,CAAC,IAAI,CAAA;AAEnC,MAAM,MAAM,OAAO,GAAG,OAAO,OAAO,CAAC,IAAI,CAAA;AAIzC,6DAA6D;AAC7D,MAAM,MAAM,UAAU,GAAG,QAAQ,CAAC;IAChC,EAAE,EAAE,MAAM,CAAA;IACV,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB,CAAC,CAAA;AAEF,yEAAyE;AACzE,eAAO,MAAM,IAAI,GAAI,QAAQ,UAAU,KAAG,KAGxC,CAAA;AAMF,0EAA0E;AAC1E,eAAO,MAAM,MAAM,GACjB,OAAO,KAAK,EACZ,SAAS,OAAO,KACf,CAAC,KAAK,EAAE,aAAa,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CA4BvC,CAAA;AAIH,iGAAiG;AACjG,eAAO,MAAM,OAAO,GAAI,OAAO,KAAK,KAAG,MAA6B,CAAA;AAEpE,wGAAwG;AACxG,eAAO,MAAM,aAAa,GAAI,OAAO,KAAK,KAAG,MAAmC,CAAA;AAEhF,wDAAwD;AACxD,MAAM,MAAM,UAAU,CAAC,OAAO,IAAI,QAAQ,CAAC;IACzC,KAAK,EAAE,KAAK,CAAA;IACZ,SAAS,EAAE,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,KAAK,OAAO,CAAA;IAC9C,YAAY,EAAE,IAAI,CAAA;IAClB,cAAc,EAAE,MAAM,CAAA;IACtB,iBAAiB,EAAE,MAAM,CAAA;IACzB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,CAAC,CAAA;AAEF,sGAAsG;AACtG,eAAO,MAAM,IAAI,GAAI,OAAO,EAAE,QAAQ,UAAU,CAAC,OAAO,CAAC,KAAG,IA6C3D,CAAA"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { Match as M, Option, Schema as S } from 'effect';
|
|
2
|
+
import { html } from '../../html';
|
|
3
|
+
import { ts } from '../../schema';
|
|
4
|
+
import { evo } from '../../struct';
|
|
5
|
+
import * as Task from '../../task';
|
|
6
|
+
// MODEL
|
|
7
|
+
/** Schema for the dialog component's state, tracking its unique ID and open/closed status. */
|
|
8
|
+
export const Model = S.Struct({
|
|
9
|
+
id: S.String,
|
|
10
|
+
isOpen: S.Boolean,
|
|
11
|
+
});
|
|
12
|
+
// MESSAGE
|
|
13
|
+
/** Sent when the dialog should open. Triggers the showModal command. */
|
|
14
|
+
export const Opened = ts('Opened');
|
|
15
|
+
/** Sent when the dialog should close (Escape key, backdrop click, or programmatic). Triggers the closeModal command. */
|
|
16
|
+
export const Closed = ts('Closed');
|
|
17
|
+
/** Placeholder message used when no action is needed, such as after a showModal or closeModal command completes. */
|
|
18
|
+
export const NoOp = ts('NoOp');
|
|
19
|
+
/** Union of all messages the dialog component can produce. */
|
|
20
|
+
export const Message = S.Union(Opened, Closed, NoOp);
|
|
21
|
+
/** Creates an initial dialog model from a config. Defaults to closed. */
|
|
22
|
+
export const init = (config) => ({
|
|
23
|
+
id: config.id,
|
|
24
|
+
isOpen: config.isOpen ?? false,
|
|
25
|
+
});
|
|
26
|
+
// UPDATE
|
|
27
|
+
const dialogSelector = (id) => `#${id}`;
|
|
28
|
+
/** Processes a dialog message and returns the next model and commands. */
|
|
29
|
+
export const update = (model, message) => M.value(message).pipe(M.withReturnType(), M.tagsExhaustive({
|
|
30
|
+
Opened: () => {
|
|
31
|
+
const maybeShowCommand = Option.liftPredicate(Task.showModal(dialogSelector(model.id), () => NoOp()), () => !model.isOpen);
|
|
32
|
+
return [
|
|
33
|
+
evo(model, { isOpen: () => true }),
|
|
34
|
+
Option.toArray(maybeShowCommand),
|
|
35
|
+
];
|
|
36
|
+
},
|
|
37
|
+
Closed: () => {
|
|
38
|
+
const maybeCloseCommand = Option.liftPredicate(Task.closeModal(dialogSelector(model.id), () => NoOp()), () => model.isOpen);
|
|
39
|
+
return [
|
|
40
|
+
evo(model, { isOpen: () => false }),
|
|
41
|
+
Option.toArray(maybeCloseCommand),
|
|
42
|
+
];
|
|
43
|
+
},
|
|
44
|
+
NoOp: () => [model, []],
|
|
45
|
+
}));
|
|
46
|
+
// VIEW
|
|
47
|
+
/** Returns the ID used for `aria-labelledby` on the dialog. Apply this to your title element. */
|
|
48
|
+
export const titleId = (model) => `${model.id}-title`;
|
|
49
|
+
/** Returns the ID used for `aria-describedby` on the dialog. Apply this to your description element. */
|
|
50
|
+
export const descriptionId = (model) => `${model.id}-description`;
|
|
51
|
+
/** Renders a headless dialog component backed by the native `<dialog>` element with `showModal()`. */
|
|
52
|
+
export const view = (config) => {
|
|
53
|
+
const { AriaDescribedBy, AriaLabelledBy, Class, DataAttribute, Id, OnCancel, OnClick, keyed, } = html();
|
|
54
|
+
const { model: { id, isOpen }, toMessage, panelContent, panelClassName, backdropClassName, className, } = config;
|
|
55
|
+
const dialogAttributes = [
|
|
56
|
+
Id(id),
|
|
57
|
+
AriaLabelledBy(`${id}-title`),
|
|
58
|
+
AriaDescribedBy(`${id}-description`),
|
|
59
|
+
OnCancel(toMessage(Closed())),
|
|
60
|
+
...(isOpen ? [DataAttribute('open', '')] : []),
|
|
61
|
+
...(className ? [Class(className)] : []),
|
|
62
|
+
];
|
|
63
|
+
const backdrop = keyed('div')(`${id}-backdrop`, [Class(backdropClassName), OnClick(toMessage(Closed()))], []);
|
|
64
|
+
const panel = keyed('div')(`${id}-panel`, [Class(panelClassName)], [panelContent]);
|
|
65
|
+
const content = isOpen ? [backdrop, panel] : [];
|
|
66
|
+
return keyed('dialog')(id, dialogAttributes, content);
|
|
67
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"public.d.ts","sourceRoot":"","sources":["../../../src/ui/dialog/public.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,IAAI,EACJ,MAAM,EACN,IAAI,EACJ,OAAO,EACP,aAAa,EACb,KAAK,EACL,OAAO,EACP,MAAM,EACN,MAAM,EACN,IAAI,GACL,MAAM,SAAS,CAAA;AAEhB,YAAY,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { init, update, view, titleId, descriptionId, Model, Message, Opened, Closed, NoOp, } from './index';
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { Schema as S } from 'effect';
|
|
2
|
+
import type { Html, TagName } from '../../html';
|
|
3
|
+
import type { Command } from '../../runtime/runtime';
|
|
4
|
+
/** Schema for the disclosure component's state, tracking its unique ID and open/closed status. */
|
|
5
|
+
export declare const Model: S.Struct<{
|
|
6
|
+
id: typeof S.String;
|
|
7
|
+
isOpen: typeof S.Boolean;
|
|
8
|
+
}>;
|
|
9
|
+
export type Model = typeof Model.Type;
|
|
10
|
+
/** Sent when the disclosure button is clicked. Toggles the open/closed state. */
|
|
11
|
+
export declare const Toggled: import("../../schema").CallableTaggedStruct<"Toggled", {}>;
|
|
12
|
+
/** Sent to explicitly close the disclosure, regardless of its current state. */
|
|
13
|
+
export declare const Closed: import("../../schema").CallableTaggedStruct<"Closed", {}>;
|
|
14
|
+
/** Placeholder message used when no action is needed, such as after a focus command completes. */
|
|
15
|
+
export declare const NoOp: import("../../schema").CallableTaggedStruct<"NoOp", {}>;
|
|
16
|
+
/** Union of all messages the disclosure component can produce. */
|
|
17
|
+
export declare const Message: S.Union<[import("../../schema").CallableTaggedStruct<"Toggled", {}>, import("../../schema").CallableTaggedStruct<"Closed", {}>, import("../../schema").CallableTaggedStruct<"NoOp", {}>]>;
|
|
18
|
+
export type Toggled = typeof Toggled.Type;
|
|
19
|
+
export type Closed = typeof Closed.Type;
|
|
20
|
+
export type NoOp = typeof NoOp.Type;
|
|
21
|
+
export type Message = typeof Message.Type;
|
|
22
|
+
/** Configuration for creating a disclosure model with `init`. */
|
|
23
|
+
export type InitConfig = Readonly<{
|
|
24
|
+
id: string;
|
|
25
|
+
isOpen?: boolean;
|
|
26
|
+
}>;
|
|
27
|
+
/** Creates an initial disclosure model from a config. Defaults to closed. */
|
|
28
|
+
export declare const init: (config: InitConfig) => Model;
|
|
29
|
+
/** Processes a disclosure message and returns the next model and commands. */
|
|
30
|
+
export declare const update: (model: Model, message: Message) => [Model, ReadonlyArray<Command<Message>>];
|
|
31
|
+
/** Configuration for rendering a disclosure with `view`. */
|
|
32
|
+
export type ViewConfig<Message> = Readonly<{
|
|
33
|
+
model: Model;
|
|
34
|
+
toMessage: (message: Toggled | Closed | NoOp) => Message;
|
|
35
|
+
buttonClassName: string;
|
|
36
|
+
buttonContent: Html;
|
|
37
|
+
panelClassName: string;
|
|
38
|
+
panelContent: Html;
|
|
39
|
+
isDisabled?: boolean;
|
|
40
|
+
persistPanel?: boolean;
|
|
41
|
+
buttonElement?: TagName;
|
|
42
|
+
panelElement?: TagName;
|
|
43
|
+
className?: string;
|
|
44
|
+
}>;
|
|
45
|
+
/** Renders a headless disclosure component with accessible ARIA attributes and keyboard support. */
|
|
46
|
+
export declare const view: <Message>(config: ViewConfig<Message>) => Html;
|
|
47
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/ui/disclosure/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,MAAM,IAAI,CAAC,EAAE,MAAM,QAAQ,CAAA;AAGxD,OAAO,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,YAAY,CAAA;AAC/C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAA;AAOpD,kGAAkG;AAClG,eAAO,MAAM,KAAK;;;EAGhB,CAAA;AAEF,MAAM,MAAM,KAAK,GAAG,OAAO,KAAK,CAAC,IAAI,CAAA;AAIrC,iFAAiF;AACjF,eAAO,MAAM,OAAO,4DAAgB,CAAA;AACpC,gFAAgF;AAChF,eAAO,MAAM,MAAM,2DAAe,CAAA;AAClC,kGAAkG;AAClG,eAAO,MAAM,IAAI,yDAAa,CAAA;AAE9B,kEAAkE;AAClE,eAAO,MAAM,OAAO,2LAAiC,CAAA;AAErD,MAAM,MAAM,OAAO,GAAG,OAAO,OAAO,CAAC,IAAI,CAAA;AACzC,MAAM,MAAM,MAAM,GAAG,OAAO,MAAM,CAAC,IAAI,CAAA;AACvC,MAAM,MAAM,IAAI,GAAG,OAAO,IAAI,CAAC,IAAI,CAAA;AAEnC,MAAM,MAAM,OAAO,GAAG,OAAO,OAAO,CAAC,IAAI,CAAA;AAIzC,iEAAiE;AACjE,MAAM,MAAM,UAAU,GAAG,QAAQ,CAAC;IAChC,EAAE,EAAE,MAAM,CAAA;IACV,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB,CAAC,CAAA;AAEF,6EAA6E;AAC7E,eAAO,MAAM,IAAI,GAAI,QAAQ,UAAU,KAAG,KAGxC,CAAA;AAQF,8EAA8E;AAC9E,eAAO,MAAM,MAAM,GACjB,OAAO,KAAK,EACZ,SAAS,OAAO,KACf,CAAC,KAAK,EAAE,aAAa,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CA4BvC,CAAA;AAIH,4DAA4D;AAC5D,MAAM,MAAM,UAAU,CAAC,OAAO,IAAI,QAAQ,CAAC;IACzC,KAAK,EAAE,KAAK,CAAA;IACZ,SAAS,EAAE,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,KAAK,OAAO,CAAA;IACxD,eAAe,EAAE,MAAM,CAAA;IACvB,aAAa,EAAE,IAAI,CAAA;IACnB,cAAc,EAAE,MAAM,CAAA;IACtB,YAAY,EAAE,IAAI,CAAA;IAClB,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,CAAC,CAAA;AAEF,oGAAoG;AACpG,eAAO,MAAM,IAAI,GAAI,OAAO,EAAE,QAAQ,UAAU,CAAC,OAAO,CAAC,KAAG,IAyF3D,CAAA"}
|