foldkit 0.27.0 → 0.28.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/dist/route/parser.d.ts +11 -14
- package/dist/route/parser.d.ts.map +1 -1
- package/dist/route/parser.js +44 -48
- package/dist/ui/index.d.ts +1 -0
- package/dist/ui/index.d.ts.map +1 -1
- package/dist/ui/index.js +1 -0
- package/dist/ui/radioGroup/index.d.ts +72 -0
- package/dist/ui/radioGroup/index.d.ts.map +1 -0
- package/dist/ui/radioGroup/index.js +147 -0
- package/dist/ui/radioGroup/public.d.ts +3 -0
- package/dist/ui/radioGroup/public.d.ts.map +1 -0
- package/dist/ui/radioGroup/public.js +1 -0
- package/package.json +5 -1
package/dist/route/parser.d.ts
CHANGED
|
@@ -32,18 +32,22 @@ export type Biparser<A> = {
|
|
|
32
32
|
parse: (segments: ReadonlyArray<string>, search?: string) => Effect.Effect<ParseResult<A>, ParseError>;
|
|
33
33
|
print: (value: A, state: PrintState) => Effect.Effect<PrintState, ParseError>;
|
|
34
34
|
};
|
|
35
|
+
type BuildFn<A> = A extends {
|
|
36
|
+
_tag: string;
|
|
37
|
+
} ? keyof Omit<A, '_tag'> extends never ? (value?: Omit<A, '_tag'>) => string : (value: Omit<A, '_tag'>) => string : never;
|
|
35
38
|
/**
|
|
36
39
|
* A parser with a `build` method that can reconstruct URLs from parsed values.
|
|
37
40
|
*
|
|
38
41
|
* Created by applying `mapTo` to a `Biparser`, binding it to a tagged
|
|
39
42
|
* type constructor so parsed values carry a discriminant tag and URLs can be
|
|
40
43
|
* built from tag payloads.
|
|
44
|
+
*
|
|
45
|
+
* Routers are callable — `homeRouter()` or `personRouter({ id: 42 })` —
|
|
46
|
+
* and also expose `.build` as an alias and `.parse` for URL matching.
|
|
41
47
|
*/
|
|
42
|
-
export type Router<A> = {
|
|
48
|
+
export type Router<A> = BuildFn<A> & {
|
|
43
49
|
parse: (segments: ReadonlyArray<string>, search?: string) => Effect.Effect<ParseResult<A>, ParseError>;
|
|
44
|
-
build:
|
|
45
|
-
_tag: string;
|
|
46
|
-
} ? Omit<A, '_tag'> : never) => string;
|
|
50
|
+
build: BuildFn<A>;
|
|
47
51
|
};
|
|
48
52
|
/**
|
|
49
53
|
* A `Biparser` that has been terminated (e.g. by `query`) and cannot
|
|
@@ -104,22 +108,15 @@ export declare const root: Biparser<{}>;
|
|
|
104
108
|
export type Parser<A> = {
|
|
105
109
|
parse: (segments: ReadonlyArray<string>, search?: string) => Effect.Effect<ParseResult<A>, ParseError>;
|
|
106
110
|
};
|
|
111
|
+
type ParserInput = Biparser<any> | Parser<any>;
|
|
112
|
+
type InferParsed<P> = P extends Biparser<infer A> ? A : P extends Parser<infer A> ? A : never;
|
|
107
113
|
/**
|
|
108
114
|
* Combines multiple parsers, trying each in order until one succeeds.
|
|
109
115
|
*
|
|
110
116
|
* Returns a `Parser` (parse-only) since the union of different route
|
|
111
117
|
* shapes cannot provide a single unified print function.
|
|
112
118
|
*/
|
|
113
|
-
export declare
|
|
114
|
-
export declare function oneOf<A, B = never>(p1: Biparser<A> | Parser<A>, p2: Biparser<B> | Parser<B>): Parser<A | B>;
|
|
115
|
-
export declare function oneOf<A, B = never, C = never>(p1: Biparser<A> | Parser<A>, p2: Biparser<B> | Parser<B>, p3: Biparser<C> | Parser<C>): Parser<A | B | C>;
|
|
116
|
-
export declare function oneOf<A, B = never, C = never, D = never>(p1: Biparser<A> | Parser<A>, p2: Biparser<B> | Parser<B>, p3: Biparser<C> | Parser<C>, p4: Biparser<D> | Parser<D>): Parser<A | B | C | D>;
|
|
117
|
-
export declare function oneOf<A, B = never, C = never, D = never, E = never>(p1: Biparser<A> | Parser<A>, p2: Biparser<B> | Parser<B>, p3: Biparser<C> | Parser<C>, p4: Biparser<D> | Parser<D>, p5: Biparser<E> | Parser<E>): Parser<A | B | C | D | E>;
|
|
118
|
-
export declare function oneOf<A, B = never, C = never, D = never, E = never, F = never>(p1: Biparser<A> | Parser<A>, p2: Biparser<B> | Parser<B>, p3: Biparser<C> | Parser<C>, p4: Biparser<D> | Parser<D>, p5: Biparser<E> | Parser<E>, p6: Biparser<F> | Parser<F>): Parser<A | B | C | D | E | F>;
|
|
119
|
-
export declare function oneOf<A, B = never, C = never, D = never, E = never, F = never, G = never>(p1: Biparser<A> | Parser<A>, p2: Biparser<B> | Parser<B>, p3: Biparser<C> | Parser<C>, p4: Biparser<D> | Parser<D>, p5: Biparser<E> | Parser<E>, p6: Biparser<F> | Parser<F>, p7: Biparser<G> | Parser<G>): Parser<A | B | C | D | E | F | G>;
|
|
120
|
-
export declare function oneOf<A, B = never, C = never, D = never, E = never, F = never, G = never, H = never>(p1: Biparser<A> | Parser<A>, p2: Biparser<B> | Parser<B>, p3: Biparser<C> | Parser<C>, p4: Biparser<D> | Parser<D>, p5: Biparser<E> | Parser<E>, p6: Biparser<F> | Parser<F>, p7: Biparser<G> | Parser<G>, p8: Biparser<H> | Parser<H>): Parser<A | B | C | D | E | F | G | H>;
|
|
121
|
-
export declare function oneOf<A, B = never, C = never, D = never, E = never, F = never, G = never, H = never, I = never>(p1: Biparser<A> | Parser<A>, p2: Biparser<B> | Parser<B>, p3: Biparser<C> | Parser<C>, p4: Biparser<D> | Parser<D>, p5: Biparser<E> | Parser<E>, p6: Biparser<F> | Parser<F>, p7: Biparser<G> | Parser<G>, p8: Biparser<H> | Parser<H>, p9: Biparser<I> | Parser<I>): Parser<A | B | C | D | E | F | G | H | I>;
|
|
122
|
-
export declare function oneOf<A, B = never, C = never, D = never, E = never, F = never, G = never, H = never, I = never, J = never>(p1: Biparser<A> | Parser<A>, p2: Biparser<B> | Parser<B>, p3: Biparser<C> | Parser<C>, p4: Biparser<D> | Parser<D>, p5: Biparser<E> | Parser<E>, p6: Biparser<F> | Parser<F>, p7: Biparser<G> | Parser<G>, p8: Biparser<H> | Parser<H>, p9: Biparser<I> | Parser<I>, p10: Biparser<J> | Parser<J>): Parser<A | B | C | D | E | F | G | H | I | J>;
|
|
119
|
+
export declare const oneOf: <Parsers extends ReadonlyArray<ParserInput>>(...parsers: Parsers) => Parser<InferParsed<Parsers[number]>>;
|
|
123
120
|
/**
|
|
124
121
|
* Converts a `Biparser` into a `Router` by mapping parsed values to a
|
|
125
122
|
* tagged type constructor.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../../src/route/parser.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,MAAM,EAGN,MAAM,EACN,MAAM,EAIP,MAAM,QAAQ,CAAA;AAEf,OAAO,EAAE,GAAG,EAAE,MAAM,QAAQ,CAAA;;;;AAE5B;;;;;GAKG;AACH,qBAAa,UAAW,SAAQ,gBAA+B;IAC7D,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAC3B,CAAC;CAAG;AAEL;;GAEG;AACH,MAAM,MAAM,WAAW,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC,CAAA;AAEvD,KAAK,UAAU,GAAG;IAChB,QAAQ,EAAE,aAAa,CAAC,MAAM,CAAC,CAAA;IAC/B,WAAW,EAAE,eAAe,CAAA;CAC7B,CAAA;AAED;;;GAGG;AACH,MAAM,MAAM,QAAQ,CAAC,CAAC,IAAI;IACxB,KAAK,EAAE,CACL,QAAQ,EAAE,aAAa,CAAC,MAAM,CAAC,EAC/B,MAAM,CAAC,EAAE,MAAM,KACZ,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,UAAU,CAAC,CAAA;IAC9C,KAAK,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,UAAU,KAAK,MAAM,CAAC,MAAM,CAAC,UAAU,EAAE,UAAU,CAAC,CAAA;CAC9E,CAAA;AAED
|
|
1
|
+
{"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../../src/route/parser.ts"],"names":[],"mappings":"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"}
|
package/dist/route/parser.js
CHANGED
|
@@ -110,20 +110,20 @@ export const root = {
|
|
|
110
110
|
}),
|
|
111
111
|
print: (_, state) => Effect.succeed(state),
|
|
112
112
|
};
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
}
|
|
126
|
-
}
|
|
113
|
+
/**
|
|
114
|
+
* Combines multiple parsers, trying each in order until one succeeds.
|
|
115
|
+
*
|
|
116
|
+
* Returns a `Parser` (parse-only) since the union of different route
|
|
117
|
+
* shapes cannot provide a single unified print function.
|
|
118
|
+
*/
|
|
119
|
+
export const oneOf = (...parsers) => ({
|
|
120
|
+
parse: (segments, search) => Array.matchLeft(parsers, {
|
|
121
|
+
onEmpty: () => Effect.fail(new ParseError({
|
|
122
|
+
message: `No parsers provided for path: /${Array.join(segments, '/')}`,
|
|
123
|
+
})),
|
|
124
|
+
onNonEmpty: () => Effect.firstSuccessOf(Array.map(parsers, parser => parser.parse(segments, search))),
|
|
125
|
+
}),
|
|
126
|
+
});
|
|
127
127
|
/**
|
|
128
128
|
* Converts a `Biparser` into a `Router` by mapping parsed values to a
|
|
129
129
|
* tagged type constructor.
|
|
@@ -138,15 +138,17 @@ export function oneOf(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10) {
|
|
|
138
138
|
*/
|
|
139
139
|
export const mapTo = (appRouteConstructor) => {
|
|
140
140
|
return (parser) => {
|
|
141
|
-
|
|
141
|
+
const build = buildUrl(parser);
|
|
142
|
+
const router = Object.assign(build, {
|
|
142
143
|
parse: (segments, search) => pipe(parser.parse(segments, search), Effect.map(([value, remaining]) => {
|
|
143
144
|
const result = appRouteConstructor.make.length === 0
|
|
144
145
|
? appRouteConstructor.make()
|
|
145
146
|
: appRouteConstructor.make(value);
|
|
146
147
|
return [result, remaining];
|
|
147
148
|
})),
|
|
148
|
-
build
|
|
149
|
-
};
|
|
149
|
+
build,
|
|
150
|
+
});
|
|
151
|
+
return router;
|
|
150
152
|
};
|
|
151
153
|
};
|
|
152
154
|
/**
|
|
@@ -185,35 +187,31 @@ export const slash = (parserB) => (parserA) => ({
|
|
|
185
187
|
*/
|
|
186
188
|
export const query = (schema) => (parser) => {
|
|
187
189
|
const queryParser = {
|
|
188
|
-
parse: (segments, search) => {
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
[{ ...pathValue, ...queryValue }, remainingSegments]));
|
|
199
|
-
}));
|
|
200
|
-
},
|
|
201
|
-
print: (value, state) => pipe(parser.print(value, state), Effect.flatMap(newState => {
|
|
202
|
-
return pipe(Schema.encode(schema)(value), Effect.map(queryValue => {
|
|
203
|
-
const newQueryParams = new URLSearchParams(newState.queryParams);
|
|
204
|
-
pipe(queryValue, Record.toEntries, Array.forEach(([key, val]) => {
|
|
205
|
-
if (Predicate.isNotNullable(val)) {
|
|
206
|
-
newQueryParams.set(key, val.toString());
|
|
207
|
-
}
|
|
208
|
-
}));
|
|
209
|
-
return {
|
|
210
|
-
...newState,
|
|
211
|
-
queryParams: newQueryParams,
|
|
212
|
-
};
|
|
213
|
-
}), Effect.mapError(error => new ParseError({
|
|
214
|
-
message: `Query parameter encoding failed: ${error.message}`,
|
|
215
|
-
})));
|
|
190
|
+
parse: (segments, search) => pipe(parser.parse(segments, search), Effect.flatMap(([pathValue, remainingSegments]) => {
|
|
191
|
+
const searchParams = new URLSearchParams(search ?? '');
|
|
192
|
+
const queryRecord = Record.fromEntries(searchParams.entries());
|
|
193
|
+
return pipe(queryRecord, Schema.decodeUnknown(schema), Effect.mapError(error => new ParseError({
|
|
194
|
+
message: `Query parameter validation failed: ${error.message}`,
|
|
195
|
+
expected: 'valid query parameters',
|
|
196
|
+
actual: search || 'empty',
|
|
197
|
+
})), Effect.map(queryValue =>
|
|
198
|
+
/* eslint-disable-next-line @typescript-eslint/consistent-type-assertions */
|
|
199
|
+
[{ ...pathValue, ...queryValue }, remainingSegments]));
|
|
216
200
|
})),
|
|
201
|
+
print: (value, state) => pipe(parser.print(value, state), Effect.flatMap(newState => pipe(Schema.encode(schema)(value), Effect.map(queryValue => {
|
|
202
|
+
const newQueryParams = new URLSearchParams(newState.queryParams);
|
|
203
|
+
pipe(queryValue, Record.toEntries, Array.forEach(([key, val]) => {
|
|
204
|
+
if (Predicate.isNotNullable(val)) {
|
|
205
|
+
newQueryParams.set(key, val.toString());
|
|
206
|
+
}
|
|
207
|
+
}));
|
|
208
|
+
return {
|
|
209
|
+
...newState,
|
|
210
|
+
queryParams: newQueryParams,
|
|
211
|
+
};
|
|
212
|
+
}), Effect.mapError(error => new ParseError({
|
|
213
|
+
message: `Query parameter encoding failed: ${error.message}`,
|
|
214
|
+
}))))),
|
|
217
215
|
};
|
|
218
216
|
return makeTerminalParser(queryParser);
|
|
219
217
|
};
|
|
@@ -228,9 +226,7 @@ const complete = ([value, remaining]) => Array.match(remaining, {
|
|
|
228
226
|
}));
|
|
229
227
|
},
|
|
230
228
|
});
|
|
231
|
-
const parseUrl = (parser) => (url) =>
|
|
232
|
-
return pipe(pathToSegments(url.pathname), segments => parser.parse(segments, Option.getOrUndefined(url.search)), Effect.flatMap(complete));
|
|
233
|
-
};
|
|
229
|
+
const parseUrl = (parser) => (url) => pipe(pathToSegments(url.pathname), segments => parser.parse(segments, Option.getOrUndefined(url.search)), Effect.flatMap(complete));
|
|
234
230
|
/**
|
|
235
231
|
* Parses a URL against a parser, falling back to a not-found route if no
|
|
236
232
|
* parser matches.
|
package/dist/ui/index.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ export * as Disclosure from './disclosure/public';
|
|
|
4
4
|
export * as Listbox from './listbox/public';
|
|
5
5
|
export * as Menu from './menu/public';
|
|
6
6
|
export * as Popover from './popover/public';
|
|
7
|
+
export * as RadioGroup from './radioGroup/public';
|
|
7
8
|
export * as Switch from './switch/public';
|
|
8
9
|
export * as Tabs from './tabs/public';
|
|
9
10
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/ui/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ui/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,QAAQ,MAAM,mBAAmB,CAAA;AAC7C,OAAO,KAAK,MAAM,MAAM,iBAAiB,CAAA;AACzC,OAAO,KAAK,UAAU,MAAM,qBAAqB,CAAA;AACjD,OAAO,KAAK,OAAO,MAAM,kBAAkB,CAAA;AAC3C,OAAO,KAAK,IAAI,MAAM,eAAe,CAAA;AACrC,OAAO,KAAK,OAAO,MAAM,kBAAkB,CAAA;AAC3C,OAAO,KAAK,MAAM,MAAM,iBAAiB,CAAA;AACzC,OAAO,KAAK,IAAI,MAAM,eAAe,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ui/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,QAAQ,MAAM,mBAAmB,CAAA;AAC7C,OAAO,KAAK,MAAM,MAAM,iBAAiB,CAAA;AACzC,OAAO,KAAK,UAAU,MAAM,qBAAqB,CAAA;AACjD,OAAO,KAAK,OAAO,MAAM,kBAAkB,CAAA;AAC3C,OAAO,KAAK,IAAI,MAAM,eAAe,CAAA;AACrC,OAAO,KAAK,OAAO,MAAM,kBAAkB,CAAA;AAC3C,OAAO,KAAK,UAAU,MAAM,qBAAqB,CAAA;AACjD,OAAO,KAAK,MAAM,MAAM,iBAAiB,CAAA;AACzC,OAAO,KAAK,IAAI,MAAM,eAAe,CAAA"}
|
package/dist/ui/index.js
CHANGED
|
@@ -4,5 +4,6 @@ export * as Disclosure from './disclosure/public';
|
|
|
4
4
|
export * as Listbox from './listbox/public';
|
|
5
5
|
export * as Menu from './menu/public';
|
|
6
6
|
export * as Popover from './popover/public';
|
|
7
|
+
export * as RadioGroup from './radioGroup/public';
|
|
7
8
|
export * as Switch from './switch/public';
|
|
8
9
|
export * as Tabs from './tabs/public';
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { Schema as S } from 'effect';
|
|
2
|
+
import type { Command } from '../../command';
|
|
3
|
+
import type { Attribute } from '../../html';
|
|
4
|
+
import type { Html } from '../../html';
|
|
5
|
+
/** Controls the radio group layout direction and which arrow keys navigate between options. */
|
|
6
|
+
export declare const Orientation: S.Literal<["Horizontal", "Vertical"]>;
|
|
7
|
+
export type Orientation = typeof Orientation.Type;
|
|
8
|
+
/** Schema for the radio group component's state, tracking the selected value and orientation. */
|
|
9
|
+
export declare const Model: S.Struct<{
|
|
10
|
+
id: typeof S.String;
|
|
11
|
+
selectedValue: S.OptionFromSelf<typeof S.String>;
|
|
12
|
+
orientation: S.Literal<["Horizontal", "Vertical"]>;
|
|
13
|
+
}>;
|
|
14
|
+
export type Model = typeof Model.Type;
|
|
15
|
+
/** Sent when a radio option is selected via click or keyboard navigation. */
|
|
16
|
+
export declare const SelectedOption: import("../../schema").CallableTaggedStruct<"SelectedOption", {
|
|
17
|
+
value: typeof S.String;
|
|
18
|
+
index: typeof S.Number;
|
|
19
|
+
}>;
|
|
20
|
+
/** Placeholder message used when no action is needed. */
|
|
21
|
+
export declare const NoOp: import("../../schema").CallableTaggedStruct<"NoOp", {}>;
|
|
22
|
+
/** Union of all messages the radio group component can produce. */
|
|
23
|
+
export declare const Message: S.Union<[import("../../schema").CallableTaggedStruct<"SelectedOption", {
|
|
24
|
+
value: typeof S.String;
|
|
25
|
+
index: typeof S.Number;
|
|
26
|
+
}>, import("../../schema").CallableTaggedStruct<"NoOp", {}>]>;
|
|
27
|
+
export type SelectedOption = typeof SelectedOption.Type;
|
|
28
|
+
export type NoOp = typeof NoOp.Type;
|
|
29
|
+
export type Message = typeof Message.Type;
|
|
30
|
+
/** Configuration for creating a radio group model with `init`. */
|
|
31
|
+
export type InitConfig = Readonly<{
|
|
32
|
+
id: string;
|
|
33
|
+
selectedValue?: string;
|
|
34
|
+
orientation?: Orientation;
|
|
35
|
+
}>;
|
|
36
|
+
/** Creates an initial radio group model from a config. Defaults to no selection and vertical orientation. */
|
|
37
|
+
export declare const init: (config: InitConfig) => Model;
|
|
38
|
+
/** Processes a radio group message and returns the next model and commands. */
|
|
39
|
+
export declare const update: (model: Model, message: Message) => [Model, ReadonlyArray<Command<Message>>];
|
|
40
|
+
/** Attribute groups the radio group component provides for each option's `content` callback. */
|
|
41
|
+
export type OptionAttributes<Message> = Readonly<{
|
|
42
|
+
option: ReadonlyArray<Attribute<Message>>;
|
|
43
|
+
label: ReadonlyArray<Attribute<Message>>;
|
|
44
|
+
description: ReadonlyArray<Attribute<Message>>;
|
|
45
|
+
}>;
|
|
46
|
+
/** Configuration for an individual radio option. */
|
|
47
|
+
export type OptionConfig<Message> = Readonly<{
|
|
48
|
+
value: string;
|
|
49
|
+
content: (attributes: OptionAttributes<Message>) => Html;
|
|
50
|
+
}>;
|
|
51
|
+
/** Configuration for rendering a radio group with `view`. */
|
|
52
|
+
export type ViewConfig<Message, Option extends string> = Readonly<{
|
|
53
|
+
model: Model;
|
|
54
|
+
toMessage: (message: SelectedOption | NoOp) => Message;
|
|
55
|
+
options: ReadonlyArray<Option>;
|
|
56
|
+
optionToConfig: (option: Option, context: Readonly<{
|
|
57
|
+
isSelected: boolean;
|
|
58
|
+
isActive: boolean;
|
|
59
|
+
isDisabled: boolean;
|
|
60
|
+
}>) => OptionConfig<Message>;
|
|
61
|
+
isOptionDisabled?: (option: Option, index: number) => boolean;
|
|
62
|
+
orientation?: Orientation;
|
|
63
|
+
className?: string;
|
|
64
|
+
name?: string;
|
|
65
|
+
isDisabled?: boolean;
|
|
66
|
+
}>;
|
|
67
|
+
/** Renders an accessible radio group by building ARIA attribute groups and delegating layout to the consumer's `optionToConfig` callback. */
|
|
68
|
+
export declare const view: <Message, RadioOption extends string>(config: ViewConfig<Message, RadioOption>) => Html;
|
|
69
|
+
/** Creates a memoized radio group view. Static config is captured in a closure;
|
|
70
|
+
* only `model` and `toMessage` are compared per render via `createLazy`. */
|
|
71
|
+
export declare const lazy: <Message, RadioOption extends string>(staticConfig: Omit<ViewConfig<Message, RadioOption>, "model" | "toMessage">) => ((model: Model, toMessage: ViewConfig<Message, RadioOption>["toMessage"]) => Html);
|
|
72
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/ui/radioGroup/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { Array, Effect, Match as M, Option, Predicate, Schema as S, String, pipe, } from 'effect';
|
|
2
|
+
import { html } from '../../html';
|
|
3
|
+
import { createLazy } from '../../html/lazy';
|
|
4
|
+
import { m } from '../../message';
|
|
5
|
+
import { evo } from '../../struct';
|
|
6
|
+
import * as Task from '../../task';
|
|
7
|
+
import { keyToIndex } from '../keyboard';
|
|
8
|
+
// MODEL
|
|
9
|
+
/** Controls the radio group layout direction and which arrow keys navigate between options. */
|
|
10
|
+
export const Orientation = S.Literal('Horizontal', 'Vertical');
|
|
11
|
+
/** Schema for the radio group component's state, tracking the selected value and orientation. */
|
|
12
|
+
export const Model = S.Struct({
|
|
13
|
+
id: S.String,
|
|
14
|
+
selectedValue: S.OptionFromSelf(S.String),
|
|
15
|
+
orientation: Orientation,
|
|
16
|
+
});
|
|
17
|
+
// MESSAGE
|
|
18
|
+
/** Sent when a radio option is selected via click or keyboard navigation. */
|
|
19
|
+
export const SelectedOption = m('SelectedOption', {
|
|
20
|
+
value: S.String,
|
|
21
|
+
index: S.Number,
|
|
22
|
+
});
|
|
23
|
+
/** Placeholder message used when no action is needed. */
|
|
24
|
+
export const NoOp = m('NoOp');
|
|
25
|
+
/** Union of all messages the radio group component can produce. */
|
|
26
|
+
export const Message = S.Union(SelectedOption, NoOp);
|
|
27
|
+
/** Creates an initial radio group model from a config. Defaults to no selection and vertical orientation. */
|
|
28
|
+
export const init = (config) => ({
|
|
29
|
+
id: config.id,
|
|
30
|
+
selectedValue: Option.fromNullable(config.selectedValue),
|
|
31
|
+
orientation: config.orientation ?? 'Vertical',
|
|
32
|
+
});
|
|
33
|
+
// UPDATE
|
|
34
|
+
const optionId = (id, index) => `${id}-option-${index}`;
|
|
35
|
+
/** Processes a radio group message and returns the next model and commands. */
|
|
36
|
+
export const update = (model, message) => M.value(message).pipe(M.withReturnType(), M.tagsExhaustive({
|
|
37
|
+
SelectedOption: ({ value, index }) => {
|
|
38
|
+
const selector = `#${optionId(model.id, index)}`;
|
|
39
|
+
return [
|
|
40
|
+
evo(model, { selectedValue: () => Option.some(value) }),
|
|
41
|
+
[Task.focus(selector).pipe(Effect.ignore, Effect.as(NoOp()))],
|
|
42
|
+
];
|
|
43
|
+
},
|
|
44
|
+
NoOp: () => [model, []],
|
|
45
|
+
}));
|
|
46
|
+
const labelId = (id, index) => `${id}-option-${index}-label`;
|
|
47
|
+
const descriptionId = (id, index) => `${id}-option-${index}-description`;
|
|
48
|
+
/** Renders an accessible radio group by building ARIA attribute groups and delegating layout to the consumer's `optionToConfig` callback. */
|
|
49
|
+
export const view = (config) => {
|
|
50
|
+
const { div, input, AriaChecked, AriaDescribedBy, AriaDisabled, AriaLabelledBy, AriaOrientation, Class, DataAttribute, Id, Name, OnClick, OnKeyDownPreventDefault, Role, Tabindex, Type, Value, } = html();
|
|
51
|
+
const { model, model: { id, selectedValue }, toMessage, options, optionToConfig, isOptionDisabled: isOptionDisabledFn, orientation = model.orientation, className, name, isDisabled: isGroupDisabled = false, } = config;
|
|
52
|
+
const isDisabled = (index) => {
|
|
53
|
+
if (isGroupDisabled) {
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
56
|
+
if (!isOptionDisabledFn) {
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
return pipe(options, Array.get(index), Option.exists(option => isOptionDisabledFn(option, index)));
|
|
60
|
+
};
|
|
61
|
+
const selectedIndex = Option.flatMap(selectedValue, value => Array.findFirstIndex(options, option => optionToConfig(option, {
|
|
62
|
+
isSelected: false,
|
|
63
|
+
isActive: false,
|
|
64
|
+
isDisabled: false,
|
|
65
|
+
}).value === value));
|
|
66
|
+
const focusedIndex = pipe(selectedIndex, Option.getOrElse(() => pipe(options.length, Array.makeBy(index => index), Array.findFirst(Predicate.not(isDisabled)), Option.getOrElse(() => 0))));
|
|
67
|
+
const { nextKey, previousKey } = M.value(orientation).pipe(M.when('Horizontal', () => ({
|
|
68
|
+
nextKey: 'ArrowRight',
|
|
69
|
+
previousKey: 'ArrowLeft',
|
|
70
|
+
})), M.when('Vertical', () => ({
|
|
71
|
+
nextKey: 'ArrowDown',
|
|
72
|
+
previousKey: 'ArrowUp',
|
|
73
|
+
})), M.exhaustive);
|
|
74
|
+
const optionValues = Array.map(options, (option, index) => optionToConfig(option, {
|
|
75
|
+
isSelected: Option.exists(selectedIndex, selectedIdx => selectedIdx === index),
|
|
76
|
+
isActive: index === focusedIndex,
|
|
77
|
+
isDisabled: isDisabled(index),
|
|
78
|
+
}).value);
|
|
79
|
+
const resolveKeyIndex = keyToIndex(nextKey, previousKey, options.length, focusedIndex, isDisabled);
|
|
80
|
+
const handleKeyDown = (currentIndex) => (key) => M.value(key).pipe(M.whenOr(nextKey, previousKey, 'Home', 'End', 'PageUp', 'PageDown', () => {
|
|
81
|
+
const nextIndex = resolveKeyIndex(key);
|
|
82
|
+
return pipe(optionValues, Array.get(nextIndex), Option.map(value => toMessage(SelectedOption({ value, index: nextIndex }))));
|
|
83
|
+
}), M.when(' ', () => pipe(optionValues, Array.get(currentIndex), Option.map(value => toMessage(SelectedOption({ value, index: currentIndex }))))), M.orElse(() => Option.none()));
|
|
84
|
+
const renderedOptions = Array.map(options, (option, index) => {
|
|
85
|
+
const isSelected = Option.exists(selectedIndex, selectedIdx => selectedIdx === index);
|
|
86
|
+
const isFocusable = index === focusedIndex;
|
|
87
|
+
const isOptionDisabled = isDisabled(index);
|
|
88
|
+
const optionConfig = optionToConfig(option, {
|
|
89
|
+
isSelected,
|
|
90
|
+
isActive: isFocusable,
|
|
91
|
+
isDisabled: isOptionDisabled,
|
|
92
|
+
});
|
|
93
|
+
const checkedAttributes = isSelected ? [DataAttribute('checked', '')] : [];
|
|
94
|
+
const activeAttributes = isFocusable ? [DataAttribute('active', '')] : [];
|
|
95
|
+
const disabledAttributes = isOptionDisabled
|
|
96
|
+
? [AriaDisabled(true), DataAttribute('disabled', '')]
|
|
97
|
+
: [];
|
|
98
|
+
const optionAttributes = [
|
|
99
|
+
Id(optionId(id, index)),
|
|
100
|
+
Role('radio'),
|
|
101
|
+
AriaChecked(isSelected),
|
|
102
|
+
AriaLabelledBy(labelId(id, index)),
|
|
103
|
+
AriaDescribedBy(descriptionId(id, index)),
|
|
104
|
+
Tabindex(isFocusable ? 0 : -1),
|
|
105
|
+
...checkedAttributes,
|
|
106
|
+
...activeAttributes,
|
|
107
|
+
...disabledAttributes,
|
|
108
|
+
...(isOptionDisabled
|
|
109
|
+
? []
|
|
110
|
+
: [
|
|
111
|
+
OnClick(toMessage(SelectedOption({ value: optionConfig.value, index }))),
|
|
112
|
+
OnKeyDownPreventDefault(handleKeyDown(index)),
|
|
113
|
+
]),
|
|
114
|
+
];
|
|
115
|
+
const labelAttributes = [
|
|
116
|
+
Id(labelId(id, index)),
|
|
117
|
+
];
|
|
118
|
+
const descriptionAttributes = [
|
|
119
|
+
Id(descriptionId(id, index)),
|
|
120
|
+
];
|
|
121
|
+
return optionConfig.content({
|
|
122
|
+
option: optionAttributes,
|
|
123
|
+
label: labelAttributes,
|
|
124
|
+
description: descriptionAttributes,
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
const hiddenInputs = pipe(name, Option.fromNullable, Option.flatMap(inputName => pipe(selectedValue, Option.map(value => input([Type('hidden'), Name(inputName), Value(value)])))), Option.match({
|
|
128
|
+
onNone: () => [],
|
|
129
|
+
onSome: hiddenInput => [hiddenInput],
|
|
130
|
+
}));
|
|
131
|
+
const groupAttributes = [
|
|
132
|
+
Role('radiogroup'),
|
|
133
|
+
AriaOrientation(String.toLowerCase(orientation)),
|
|
134
|
+
...(className ? [Class(className)] : []),
|
|
135
|
+
];
|
|
136
|
+
return div(groupAttributes, [...renderedOptions, ...hiddenInputs]);
|
|
137
|
+
};
|
|
138
|
+
/** Creates a memoized radio group view. Static config is captured in a closure;
|
|
139
|
+
* only `model` and `toMessage` are compared per render via `createLazy`. */
|
|
140
|
+
export const lazy = (staticConfig) => {
|
|
141
|
+
const lazyView = createLazy();
|
|
142
|
+
return (model, toMessage) => lazyView((currentModel, currentToMessage) => view({
|
|
143
|
+
...staticConfig,
|
|
144
|
+
model: currentModel,
|
|
145
|
+
toMessage: currentToMessage,
|
|
146
|
+
}), [model, toMessage]);
|
|
147
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"public.d.ts","sourceRoot":"","sources":["../../../src/ui/radioGroup/public.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AAElE,YAAY,EACV,cAAc,EACd,IAAI,EACJ,WAAW,EACX,UAAU,EACV,UAAU,EACV,gBAAgB,EAChB,YAAY,GACb,MAAM,SAAS,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { init, update, view, lazy, Model, Message } from './index';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "foldkit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.28.0",
|
|
4
4
|
"description": "Elm-inspired UI framework powered by Effect",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -87,6 +87,10 @@
|
|
|
87
87
|
"types": "./dist/ui/popover/public.d.ts",
|
|
88
88
|
"import": "./dist/ui/popover/public.js"
|
|
89
89
|
},
|
|
90
|
+
"./ui/radioGroup": {
|
|
91
|
+
"types": "./dist/ui/radioGroup/public.d.ts",
|
|
92
|
+
"import": "./dist/ui/radioGroup/public.js"
|
|
93
|
+
},
|
|
90
94
|
"./ui/switch": {
|
|
91
95
|
"types": "./dist/ui/switch/public.d.ts",
|
|
92
96
|
"import": "./dist/ui/switch/public.js"
|