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.
@@ -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: (value: A extends {
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 function oneOf<A>(p1: Biparser<A> | Parser<A>): Parser<A>;
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":"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"}
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,KAAK,OAAO,CAAC,CAAC,IAAI,CAAC,SAAS;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GACxC,MAAM,IAAI,CAAC,CAAC,EAAE,MAAM,CAAC,SAAS,KAAK,GACjC,CAAC,KAAK,CAAC,EAAE,IAAI,CAAC,CAAC,EAAE,MAAM,CAAC,KAAK,MAAM,GACnC,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,EAAE,MAAM,CAAC,KAAK,MAAM,GACpC,KAAK,CAAA;AAET;;;;;;;;;GASG;AACH,MAAM,MAAM,MAAM,CAAC,CAAC,IAAI,OAAO,CAAC,CAAC,CAAC,GAAG;IACnC,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,OAAO,CAAC,CAAC,CAAC,CAAA;CAClB,CAAA;AAED;;;GAGG;AACH,MAAM,MAAM,cAAc,CAAC,CAAC,IAAI,QAAQ,CAAC,CAAC,CAAC,GAAG;IAAE,QAAQ,CAAC,UAAU,EAAE,IAAI,CAAA;CAAE,CAAA;AAM3E;;;;;;;GAOG;AACH,eAAO,MAAM,OAAO,GAAI,SAAS,MAAM,KAAG,QAAQ,CAAC,EAAE,CA6BnD,CAAA;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,KAAK,GAAI,CAAC,EACrB,OAAO,MAAM,EACb,OAAO,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,UAAU,CAAC,EACxD,OAAO,CAAC,KAAK,EAAE,CAAC,KAAK,MAAM,KAC1B,QAAQ,CAAC,CAAC,CAwBX,CAAA;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,MAAM,GAAI,CAAC,SAAS,MAAM,EACrC,MAAM,CAAC,KACN,QAAQ,CAAC,MAAM,CAAC,CAAC,EAAE,MAAM,CAAC,CAM1B,CAAA;AAEH;;;;;;;;;GASG;AACH,eAAO,MAAM,GAAG,GAAI,CAAC,SAAS,MAAM,EAAE,MAAM,CAAC,KAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,EAAE,MAAM,CAAC,CAkBvE,CAAA;AAEH;;;;GAIG;AACH,eAAO,MAAM,IAAI,EAAE,QAAQ,CAAC,EAAE,CAc7B,CAAA;AAED;;;;;GAKG;AACH,MAAM,MAAM,MAAM,CAAC,CAAC,IAAI;IACtB,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;CAC/C,CAAA;AAED,KAAK,WAAW,GAAG,QAAQ,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,CAAA;AAE9C,KAAK,WAAW,CAAC,CAAC,IAChB,CAAC,SAAS,QAAQ,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,SAAS,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,GAAG,KAAK,CAAA;AAEzE;;;;;GAKG;AACH,eAAO,MAAM,KAAK,GAAI,OAAO,SAAS,aAAa,CAAC,WAAW,CAAC,EAC9D,GAAG,SAAS,OAAO,KAClB,MAAM,CAAC,WAAW,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAcpC,CAAA;AAEF;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,KAAK,EAAE;IAClB,CAAC,CAAC,EAAE,mBAAmB,EAAE;QACvB,IAAI,EAAE,MAAM,CAAC,CAAA;KACd,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,EAAE,CAAC,KAAK,MAAM,CAAC,CAAC,CAAC,CAAA;IACvC,CAAC,CAAC,EAAE,CAAC,EAAE,mBAAmB,EAAE;QAC1B,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,CAAA;KACrB,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC,CAAC,CAAC,CAAA;CAoBvC,CAAA;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,KAAK,GACf,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACnE,SAAS,QAAQ,CAAC,CAAC,CAAC,MAGpB,SAAS,QAAQ,CAAC,CAAC,CAAC,GAAG;IACrB,QAAQ,CAAC,UAAU,CAAC,EAAE,KAAK,CAAA;IAC3B,QAAQ,CAAC,kEAAkE,CAAC,EAAE,KAAK,CAAA;CACpF,KACA,QAAQ,CAAC,CAAC,GAAG,CAAC,CAmBf,CAAA;AAEJ;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,KAAK,GACf,CAAC,EAAE,CAAC,SAAS,MAAM,CAAC,cAAc,CAAC,MAAM,EAAE,OAAO,CAAC,EAClD,QAAQ,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,MAE5B,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAChC,QAAQ,QAAQ,CAAC,CAAC,CAAC,KAClB,cAAc,CAAC,CAAC,GAAG,CAAC,CAgEtB,CAAA;AA4BH;;;;;;;GAOG;AACH,eAAO,MAAM,oBAAoB,GAC9B,CAAC,EAAE,CAAC,EACH,QAAQ,MAAM,CAAC,CAAC,CAAC,EACjB,0BAA0B;IAAE,IAAI,EAAE,CAAC,IAAI,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,KAAK,CAAC,CAAA;CAAE,MAElE,KAAK,GAAG,KAAG,CAAC,GAAG,CAQb,CAAA"}
@@ -110,20 +110,20 @@ export const root = {
110
110
  }),
111
111
  print: (_, state) => Effect.succeed(state),
112
112
  };
113
- export function oneOf(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10) {
114
- const parsers = [p1, p2, p3, p4, p5, p6, p7, p8, p9, p10].filter(p => p !== undefined);
115
- return {
116
- parse: (segments, search) => Array.matchLeft(parsers, {
117
- onEmpty: () => {
118
- const segmentsStr = '/' + Array.join(segments, '/');
119
- return Effect.fail(new ParseError({
120
- message: `No parsers provided for path: ${segmentsStr}`,
121
- }));
122
- },
123
- onNonEmpty: () => Effect.firstSuccessOf(Array.map(parsers, parser => parser.parse(segments, search))),
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
- return {
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: buildUrl(parser),
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
- return pipe(parser.parse(segments, search), Effect.flatMap(([pathValue, remainingSegments]) => {
190
- const searchParams = new URLSearchParams(search ?? '');
191
- const queryRecord = Record.fromEntries(searchParams.entries());
192
- return pipe(queryRecord, Schema.decodeUnknown(schema), Effect.mapError(error => new ParseError({
193
- message: `Query parameter validation failed: ${error.message}`,
194
- expected: 'valid query parameters',
195
- actual: search || 'empty',
196
- })), Effect.map(queryValue =>
197
- /* eslint-disable-next-line @typescript-eslint/consistent-type-assertions */
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.
@@ -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
@@ -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,3 @@
1
+ export { init, update, view, lazy, Model, Message } from './index';
2
+ export type { SelectedOption, NoOp, Orientation, InitConfig, ViewConfig, OptionAttributes, OptionConfig, } from './index';
3
+ //# sourceMappingURL=public.d.ts.map
@@ -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.27.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"