@lynstack/recipe 1.8.0 → 1.9.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/CHANGELOG.md +117 -146
- package/dist/index.d.ts +241 -197
- package/dist/index.js +8 -9
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -7,16 +7,17 @@ type Simplify<Type> = { [Key in keyof Type]: Type[Key]; };
|
|
|
7
7
|
type OptionName<Options> = `${Extract<keyof Options, string | number>}`;
|
|
8
8
|
type BooleanName = "true" | "false";
|
|
9
9
|
type NumberOption<Options> = Extract<keyof Options, number>;
|
|
10
|
-
type BooleanOption<Name extends string> = [Extract<Name, BooleanName>] extends [never] ? never :
|
|
10
|
+
type BooleanOption<Name extends string> = [Extract<Name, BooleanName>] extends [never] ? never : "true" | "false" | boolean;
|
|
11
11
|
type BooleanVariantName<Variants> = { [Name in keyof Variants]: [OptionName<Variants[Name]>] extends [never] ? never : [OptionName<Variants[Name]>] extends [BooleanName] ? Name : never; }[keyof Variants];
|
|
12
12
|
/**
|
|
13
13
|
* The values accepted for one variant: the names of its options, as
|
|
14
14
|
* strings or, for numeric names, as numbers, plus `true`, `false`, `"true"`,
|
|
15
15
|
* and `"false"` when it declares an option named `"true"` or `"false"`.
|
|
16
|
+
* Editors and errors show them by name, such as `"sm" | "md"`.
|
|
16
17
|
*
|
|
17
18
|
* @typeParam Options - The options of the variant, keyed by option name.
|
|
18
19
|
*/
|
|
19
|
-
type VariantOption<Options> = OptionName<Options> | NumberOption<Options> | BooleanOption<OptionName<Options
|
|
20
|
+
type VariantOption<Options> = [Options] extends [unknown] ? OptionName<Options> | NumberOption<Options> | BooleanOption<OptionName<Options>> : never;
|
|
20
21
|
/**
|
|
21
22
|
* The variants a selection names. A variant with a default may be omitted,
|
|
22
23
|
* and so may a boolean variant, whose only options are `"true"` and
|
|
@@ -146,6 +147,8 @@ interface RecipeKind<Value, Accumulator, Result> {
|
|
|
146
147
|
*/
|
|
147
148
|
readonly cache?: boolean | undefined;
|
|
148
149
|
}
|
|
150
|
+
//#endregion
|
|
151
|
+
//#region src/unknown-slots.d.ts
|
|
149
152
|
/**
|
|
150
153
|
* Rejects the slots of each option's values that `Slot` does not name,
|
|
151
154
|
* unless the option's slot names are not known at compile time. A library
|
|
@@ -165,12 +168,201 @@ interface RecipeKind<Value, Accumulator, Result> {
|
|
|
165
168
|
* ```
|
|
166
169
|
*/
|
|
167
170
|
type NoUnknownSlots<Variants, Slot extends string> = NoUnknownComposedSlots<Variants, Slot, never>;
|
|
171
|
+
/**
|
|
172
|
+
* The type that {@link NoUnknownSlots} gives a key of an option that is not
|
|
173
|
+
* one of the slots `Slot`. No value is assignable to it, so the error names
|
|
174
|
+
* both the key and the slots, as `UnknownSlot<"lable", "label" | "root">`.
|
|
175
|
+
*
|
|
176
|
+
* @typeParam Name - The key that names no slot.
|
|
177
|
+
* @typeParam Slot - The names of the slots.
|
|
178
|
+
*/
|
|
179
|
+
interface UnknownSlot<Name, Slot extends string> {
|
|
180
|
+
readonly "~unknownSlot": Name;
|
|
181
|
+
readonly "~slots": Slot;
|
|
182
|
+
}
|
|
168
183
|
/**
|
|
169
184
|
* Rejects the slots of the variants' options that are neither `Slot` nor
|
|
170
|
-
* `InheritedSlot`.
|
|
171
|
-
* are accepted even when the inherited slots are
|
|
185
|
+
* `InheritedSlot`. The recipe's own slots are checked before the inherited
|
|
186
|
+
* ones, so that they are accepted even when the inherited slots are
|
|
187
|
+
* generic.
|
|
188
|
+
*/
|
|
189
|
+
type NoUnknownComposedSlots<Variants, Slot extends string, InheritedSlot extends string> = { readonly [Name in keyof Variants]: { readonly [Option in keyof Variants[Name]]: string extends keyof Variants[Name][Option] ? unknown : { readonly [Unknown in Exclude<Exclude<keyof Variants[Name][Option], Slot>, InheritedSlot>]?: UnknownSlot<Unknown, Slot | InheritedSlot>; }; }; };
|
|
190
|
+
//#endregion
|
|
191
|
+
//#region src/slot-recipe-kind.d.ts
|
|
192
|
+
/**
|
|
193
|
+
* Values for some of a slot recipe's slots, keyed by slot name.
|
|
194
|
+
*
|
|
195
|
+
* @typeParam Slot - The names of the slots.
|
|
196
|
+
* @typeParam Value - The value of a slot.
|
|
197
|
+
*/
|
|
198
|
+
type SlotValues<Slot extends string, Value> = { readonly [Name in Slot]?: Value | undefined; };
|
|
199
|
+
/**
|
|
200
|
+
* The variants of a {@link KindSlotRecipeConfig}: for each variant name,
|
|
201
|
+
* the values of each slot for each of its options.
|
|
202
|
+
*
|
|
203
|
+
* @typeParam Value - The value of a slot.
|
|
204
|
+
*/
|
|
205
|
+
type KindSlotVariants<Value> = KindVariants<SlotValues<string, Value>>;
|
|
206
|
+
/**
|
|
207
|
+
* Values added to some slots when several variants have particular options
|
|
208
|
+
* at the same time.
|
|
209
|
+
*
|
|
210
|
+
* @typeParam Variants - The variant definitions of the slot recipe.
|
|
211
|
+
* @typeParam Slot - The names of the slots.
|
|
212
|
+
* @typeParam Value - The value of a slot.
|
|
213
|
+
*/
|
|
214
|
+
interface KindSlotCompoundVariant<Variants, Slot extends string, Value> {
|
|
215
|
+
/**
|
|
216
|
+
* The options that must all be selected for
|
|
217
|
+
* {@link KindSlotCompoundVariant.value} to apply.
|
|
218
|
+
*/
|
|
219
|
+
readonly variants: KindCompoundCondition<Variants>;
|
|
220
|
+
/** The value added to each slot when the condition matches. */
|
|
221
|
+
readonly value: SlotValues<Slot, Value>;
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* The configuration of a slot recipe made from a {@link RecipeKind}.
|
|
225
|
+
*
|
|
226
|
+
* @typeParam Slot - The names of the slots.
|
|
227
|
+
* @typeParam Value - The value of a slot.
|
|
228
|
+
* @typeParam Variants - The variant definitions, keyed by variant name.
|
|
229
|
+
* @typeParam DefaultedName - The names of the variants that have a default.
|
|
230
|
+
*/
|
|
231
|
+
interface KindSlotRecipeConfig<Slot extends string, Value, Variants extends KindSlotVariants<Value>, DefaultedName extends keyof ComposedVariants<Composed, Variants>, Composed extends readonly ComposableKindSlotRecipe<Value>[] = readonly []> {
|
|
232
|
+
/**
|
|
233
|
+
* Slot recipes whose config the recipe adds to its own, in order, as if
|
|
234
|
+
* it were written in one config: their slots and bases first, the values
|
|
235
|
+
* of each of their options before its own, and their compound variants
|
|
236
|
+
* first. A slot recipe composed several times counts once.
|
|
237
|
+
*/
|
|
238
|
+
readonly composes?: Composed | undefined;
|
|
239
|
+
/**
|
|
240
|
+
* The names of the slots, in the order of the recipe's result, after
|
|
241
|
+
* those of the slot recipes it composes. A name of a property that every
|
|
242
|
+
* object has, such as `toString`, is not supported.
|
|
243
|
+
*/
|
|
244
|
+
readonly slots: readonly Slot[];
|
|
245
|
+
/** The value of each slot that the values of every selection are added to. */
|
|
246
|
+
readonly base?: SlotValues<NoInfer<Slot> | InheritedSlot<Composed>, Value> | undefined;
|
|
247
|
+
/** For each variant name, the values of each slot for each of its options. */
|
|
248
|
+
readonly variants: Variants & SlotVariantsCheck<Variants, NoInfer<Slot>, InheritedSlot<Composed>, Value>;
|
|
249
|
+
/**
|
|
250
|
+
* Values added to some slots when several variants have particular
|
|
251
|
+
* options at the same time, applied in order after the values of the
|
|
252
|
+
* variants' options.
|
|
253
|
+
*/
|
|
254
|
+
readonly compoundVariants?: readonly KindSlotCompoundVariant<NoInfer<ComposedVariants<Composed, Variants>>, NoInfer<Slot> | InheritedSlot<Composed>, Value>[] | undefined;
|
|
255
|
+
/** The option each variant uses when the recipe is called without it. */
|
|
256
|
+
readonly defaultVariants?: WrittenKindDefaults<ComposedVariants<Composed, Variants>, DefaultedName> | undefined;
|
|
257
|
+
/** Whether the recipe caches its results. Defaults to the kind's `cache`. */
|
|
258
|
+
readonly cache?: boolean | undefined;
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* Creates slot recipes of one kind, the function that
|
|
262
|
+
* {@link createSlotRecipeKind} returns.
|
|
263
|
+
*
|
|
264
|
+
* @typeParam Value - The value of a slot.
|
|
265
|
+
* @typeParam Result - What a recipe of this kind returns for each slot.
|
|
266
|
+
* @param config - The slot recipes it composes, and the slots, base values,
|
|
267
|
+
* variants, compound variants, and default variants of the slot recipe,
|
|
268
|
+
* and whether it caches its results.
|
|
269
|
+
* @returns The slot recipe.
|
|
270
|
+
*/
|
|
271
|
+
type CreateKindSlotRecipe<Value, Result> = <const Slot extends string, const Variants extends KindSlotVariants<Value>, const DefaultedName extends keyof ComposedVariants<Composed, Variants> = never, const Composed extends readonly ComposableKindSlotRecipe<Value>[] = readonly []>(config: KindSlotRecipeConfig<Slot, Value, Variants, DefaultedName, Composed>) => ComposedKindRecipe<ComposedVariants<Composed, Variants>, DefaultedName | InheritedDefaultedName<Composed, Variants>, Value, Readonly<Record<Slot | Exclude<Composed[number]["~composition"], undefined>["slots"][number], Result>>, readonly (Slot | Exclude<Composed[number]["~composition"], undefined>["slots"][number])[]>;
|
|
272
|
+
/**
|
|
273
|
+
* Creates a kind of slot recipe from how it turns the values of each slot
|
|
274
|
+
* into that slot's result, and returns the function that creates slot
|
|
275
|
+
* recipes of that kind. A slot recipe maps a selection of variants to the
|
|
276
|
+
* result of each of several slots, such as the class names or the styles of
|
|
277
|
+
* the elements of a component, as `sva` of `@lynstack/class-recipe` does.
|
|
278
|
+
*
|
|
279
|
+
* @remarks
|
|
280
|
+
* A slot recipe reduces the values of each slot as a recipe of the same
|
|
281
|
+
* kind from {@link createRecipeKind} would: `kind.initial` returns the
|
|
282
|
+
* accumulator for the slot's base value, `kind.reduce` adds the slot's
|
|
283
|
+
* value of each variant's selected option, in the order of `variants`, and
|
|
284
|
+
* of each matching compound variant, in the order of `compoundVariants`,
|
|
285
|
+
* and `kind.finish` turns the accumulator into the slot's result. A slot
|
|
286
|
+
* without values gets the result of its accumulator for an `undefined`
|
|
287
|
+
* base, so every slot is in the result.
|
|
288
|
+
*
|
|
289
|
+
* The result is a frozen object of each slot's result, keyed by slot name
|
|
290
|
+
* in the order of `slots`. With the cache, a slot recipe builds it once for
|
|
291
|
+
* each declared selection and returns the same object for the same
|
|
292
|
+
* variants. The `cache` of a slot recipe's config overrides the kind's.
|
|
293
|
+
* Variants, boolean variants, undeclared options, the `TypeError` for a
|
|
294
|
+
* config with the wrong shape, the warning about undeclared names, and the
|
|
295
|
+
* `variantKeys`, `variantOptions`, and `defaultVariants` properties are as
|
|
296
|
+
* in {@link createRecipeKind}. It also
|
|
297
|
+
* warns about a value for a slot that no config lists in `slots`, which is
|
|
298
|
+
* ignored.
|
|
299
|
+
*
|
|
300
|
+
* A slot recipe composes the slot recipes listed in `composes` as a recipe
|
|
301
|
+
* composes recipes, and has the slots of each, theirs first. With
|
|
302
|
+
* `kind.combine`, the values of each slot are combined as a recipe's are.
|
|
303
|
+
*
|
|
304
|
+
* @typeParam Value - The value of a slot, inferred from the `value`
|
|
305
|
+
* parameter of `kind.reduce` or the `base` parameter of `kind.initial`.
|
|
306
|
+
* @typeParam Accumulator - What the values of a slot are reduced to,
|
|
307
|
+
* inferred from `kind.initial`.
|
|
308
|
+
* @typeParam Result - What a slot recipe returns for each slot, inferred
|
|
309
|
+
* from `kind.finish`, or the accumulator without it.
|
|
310
|
+
* @param kind - How a slot recipe turns the values of each slot into its
|
|
311
|
+
* result, and whether it caches its results. The same kind can create
|
|
312
|
+
* recipes with {@link createRecipeKind}.
|
|
313
|
+
* @returns The function that creates slot recipes of this kind.
|
|
314
|
+
*
|
|
315
|
+
* @example
|
|
316
|
+
* ```ts
|
|
317
|
+
* type Style = Readonly<Record<string, string | number>>;
|
|
318
|
+
*
|
|
319
|
+
* const styleKind = {
|
|
320
|
+
* initial: (base?: Style): Record<string, string | number> => ({ ...base }),
|
|
321
|
+
* reduce: (style: Record<string, string | number>, value: Style) =>
|
|
322
|
+
* Object.assign(style, value),
|
|
323
|
+
* finish: (style: Record<string, string | number>): Style =>
|
|
324
|
+
* Object.freeze(style),
|
|
325
|
+
* };
|
|
326
|
+
*
|
|
327
|
+
* const slotStyleRecipe = createSlotRecipeKind(styleKind);
|
|
328
|
+
*
|
|
329
|
+
* const card = slotStyleRecipe({
|
|
330
|
+
* slots: ["root", "title"],
|
|
331
|
+
* base: { root: { padding: 16 }, title: { fontSize: 18 } },
|
|
332
|
+
* variants: {
|
|
333
|
+
* tone: {
|
|
334
|
+
* light: { root: { backgroundColor: "white" } },
|
|
335
|
+
* dark: { root: { backgroundColor: "black" }, title: { color: "white" } },
|
|
336
|
+
* },
|
|
337
|
+
* },
|
|
338
|
+
* compoundVariants: [
|
|
339
|
+
* { variants: { tone: "dark" }, value: { title: { fontWeight: 600 } } },
|
|
340
|
+
* ],
|
|
341
|
+
* defaultVariants: { tone: "light" },
|
|
342
|
+
* });
|
|
343
|
+
*
|
|
344
|
+
* card();
|
|
345
|
+
* // => { root: { padding: 16, backgroundColor: "white" }, title: { fontSize: 18 } }
|
|
346
|
+
*
|
|
347
|
+
* card({ tone: "dark" });
|
|
348
|
+
* // => {
|
|
349
|
+
* // root: { padding: 16, backgroundColor: "black" },
|
|
350
|
+
* // title: { fontSize: 18, color: "white", fontWeight: 600 },
|
|
351
|
+
* // }
|
|
352
|
+
*
|
|
353
|
+
* card.variantOptions; // => { tone: ["light", "dark"] }
|
|
354
|
+
* card.defaultVariants; // => { tone: "light" }
|
|
355
|
+
*
|
|
356
|
+
* const dialog = slotStyleRecipe({
|
|
357
|
+
* composes: [card],
|
|
358
|
+
* slots: ["footer"],
|
|
359
|
+
* variants: { tone: { dark: { footer: { borderColor: "white" } } } },
|
|
360
|
+
* });
|
|
361
|
+
*
|
|
362
|
+
* dialog({ tone: "dark" }).footer; // => { borderColor: "white" }
|
|
363
|
+
* ```
|
|
172
364
|
*/
|
|
173
|
-
|
|
365
|
+
declare function createSlotRecipeKind<Value, Accumulator, Result = Accumulator>(kind: RecipeKind<Value, Accumulator, Result>): CreateKindSlotRecipe<Value, Result>;
|
|
174
366
|
//#endregion
|
|
175
367
|
//#region src/kind-selection.d.ts
|
|
176
368
|
/**
|
|
@@ -193,6 +385,21 @@ type KindSelection<Variants, DefaultedName extends keyof Variants> = string exte
|
|
|
193
385
|
type KindCompoundCondition<Variants> = string extends keyof Variants ? AnySelection : CompoundCondition<Variants>;
|
|
194
386
|
/** The default variants of a recipe, or any for unknown variant names. */
|
|
195
387
|
type KindDefaultVariants<Variants, DefaultedName extends keyof Variants> = string extends keyof Variants ? AnySelection : DefaultVariants<Variants, DefaultedName>;
|
|
388
|
+
/**
|
|
389
|
+
* The type of `defaultVariants`: the defaults as written, checked. While an
|
|
390
|
+
* editor completes them, TypeScript has not yet inferred their names and
|
|
391
|
+
* takes `never`, which would allow no name, so the type then takes an
|
|
392
|
+
* optional default for each variant.
|
|
393
|
+
*/
|
|
394
|
+
type WrittenKindDefaults<Variants, DefaultedName extends keyof Variants> = [DefaultedName] extends [never] ? { readonly [Name in keyof Variants]?: VariantOption<NoInfer<Variants>[Name]>; } : KindDefaultVariants<Variants, DefaultedName>;
|
|
395
|
+
/**
|
|
396
|
+
* The check of the variants of a slot recipe: it rejects the slots that are
|
|
397
|
+
* neither `Slot` nor `InheritedSlot`, and lists every slot in each option,
|
|
398
|
+
* so that an editor completes their names and values. It leaves out a slot
|
|
399
|
+
* named after a property that every object has, such as `toString`: an
|
|
400
|
+
* option that does not give it would still have it, with another type.
|
|
401
|
+
*/
|
|
402
|
+
type SlotVariantsCheck<Variants, Slot extends string, InheritedSlot extends string, Value> = NoUnknownComposedSlots<Variants, Slot, InheritedSlot> & { readonly [Name in keyof Variants]: { readonly [Option in keyof Variants[Name]]: string extends keyof Variants[Name][Option] ? unknown : SlotValues<Exclude<Slot | InheritedSlot, keyof typeof Object.prototype>, Value>; }; };
|
|
196
403
|
//#endregion
|
|
197
404
|
//#region src/composition.d.ts
|
|
198
405
|
/**
|
|
@@ -258,9 +465,13 @@ type ComposedDefaultedName<Composed extends readonly unknown[], DefaultedName> =
|
|
|
258
465
|
/**
|
|
259
466
|
* The names of the variants that the recipes of `Composed` give a default,
|
|
260
467
|
* among the variants of a recipe that composes them with its own
|
|
261
|
-
* `Variants`.
|
|
262
|
-
*
|
|
263
|
-
* generic
|
|
468
|
+
* `Variants`. A library adds them to the recipe's own `DefaultedName`,
|
|
469
|
+
* rather than passing that to {@link ComposedDefaultedName}, so that a
|
|
470
|
+
* function generic over a config can return the recipe, and a recipe that
|
|
471
|
+
* composes nothing has exactly its own defaulted names.
|
|
472
|
+
*
|
|
473
|
+
* @typeParam Composed - The types of the recipes composed.
|
|
474
|
+
* @typeParam Variants - The variant definitions of the recipe's own config.
|
|
264
475
|
*/
|
|
265
476
|
type InheritedDefaultedName<Composed extends readonly unknown[], Variants> = Extract<ComposedPart<Composed, "defaultedName">, keyof ComposedVariants<Composed, Variants>>;
|
|
266
477
|
/**
|
|
@@ -272,9 +483,12 @@ type InheritedDefaultedName<Composed extends readonly unknown[], Variants> = Ext
|
|
|
272
483
|
*/
|
|
273
484
|
type ComposedSlot<Composed extends readonly unknown[], Slot extends string> = Slot | SlotOf<ComposedPart<Composed, "slots">>;
|
|
274
485
|
/**
|
|
275
|
-
* The slots of the slot recipes of `Composed
|
|
276
|
-
*
|
|
277
|
-
*
|
|
486
|
+
* The slots of the slot recipes of `Composed`. Unlike
|
|
487
|
+
* {@link ComposedSlot}, TypeScript relates a config to them while
|
|
488
|
+
* `Composed` is generic, so a library types the slots of a config's `base`
|
|
489
|
+
* and compound variants as `Slot | InheritedSlot<Composed>`.
|
|
490
|
+
*
|
|
491
|
+
* @typeParam Composed - The types of the slot recipes composed.
|
|
278
492
|
*/
|
|
279
493
|
type InheritedSlot<Composed extends readonly ComposableKindSlotRecipe<unknown>[]> = NonNullable<Composed[number]["~composition"]>["slots"][number];
|
|
280
494
|
type SlotOf<Slots> = Slots extends readonly (infer Slot extends string)[] ? Slot : never;
|
|
@@ -356,7 +570,7 @@ interface KindRecipeConfig<Value, Variants extends KindVariants<Value>, Defaulte
|
|
|
356
570
|
*/
|
|
357
571
|
readonly compoundVariants?: readonly KindCompoundVariant<NoInfer<ComposedVariants<Composed, Variants>>, Value>[] | undefined;
|
|
358
572
|
/** The option each variant uses when the recipe is called without it. */
|
|
359
|
-
readonly defaultVariants?:
|
|
573
|
+
readonly defaultVariants?: WrittenKindDefaults<ComposedVariants<Composed, Variants>, DefaultedName> | undefined;
|
|
360
574
|
/** Whether the recipe caches its results. Defaults to the kind's `cache`. */
|
|
361
575
|
readonly cache?: boolean | undefined;
|
|
362
576
|
}
|
|
@@ -425,10 +639,13 @@ type CreateKindRecipe<Value, Result> = <const Variants extends KindVariants<Valu
|
|
|
425
639
|
* each, and `defaultVariants` the option each uses when a selection leaves
|
|
426
640
|
* it out, so that a library can list every selection of a recipe.
|
|
427
641
|
*
|
|
428
|
-
* Creating a recipe
|
|
642
|
+
* Creating a recipe from a config with the wrong shape, such as one without
|
|
643
|
+
* `variants`, throws a `TypeError` that names what is wrong. Creating a
|
|
644
|
+
* recipe also warns once, with `console.warn`, about a default or a
|
|
429
645
|
* compound variant's condition that names a variant or an option that no
|
|
430
|
-
* config of the recipe declares. Such a default is ignored
|
|
431
|
-
*
|
|
646
|
+
* config of the recipe declares. Such a default is ignored. A condition on
|
|
647
|
+
* such a variant never matches, and such an option in a condition's list
|
|
648
|
+
* of options is ignored.
|
|
432
649
|
*
|
|
433
650
|
* With the cache, a recipe builds the result of each declared selection
|
|
434
651
|
* once, and calling it again with the same variants returns the same
|
|
@@ -505,180 +722,6 @@ type CreateKindRecipe<Value, Result> = <const Variants extends KindVariants<Valu
|
|
|
505
722
|
*/
|
|
506
723
|
declare function createRecipeKind<Value, Accumulator, Result = Accumulator>(kind: RecipeKind<Value, Accumulator, Result>): CreateKindRecipe<Value, Result>;
|
|
507
724
|
//#endregion
|
|
508
|
-
//#region src/slot-recipe-kind.d.ts
|
|
509
|
-
/**
|
|
510
|
-
* Values for some of a slot recipe's slots, keyed by slot name.
|
|
511
|
-
*
|
|
512
|
-
* @typeParam Slot - The names of the slots.
|
|
513
|
-
* @typeParam Value - The value of a slot.
|
|
514
|
-
*/
|
|
515
|
-
type SlotValues<Slot extends string, Value> = { readonly [Name in Slot]?: Value | undefined; };
|
|
516
|
-
/**
|
|
517
|
-
* The variants of a {@link KindSlotRecipeConfig}: for each variant name,
|
|
518
|
-
* the values of each slot for each of its options.
|
|
519
|
-
*
|
|
520
|
-
* @typeParam Value - The value of a slot.
|
|
521
|
-
*/
|
|
522
|
-
type KindSlotVariants<Value> = KindVariants<SlotValues<string, Value>>;
|
|
523
|
-
/**
|
|
524
|
-
* Values added to some slots when several variants have particular options
|
|
525
|
-
* at the same time.
|
|
526
|
-
*
|
|
527
|
-
* @typeParam Variants - The variant definitions of the slot recipe.
|
|
528
|
-
* @typeParam Slot - The names of the slots.
|
|
529
|
-
* @typeParam Value - The value of a slot.
|
|
530
|
-
*/
|
|
531
|
-
interface KindSlotCompoundVariant<Variants, Slot extends string, Value> {
|
|
532
|
-
/**
|
|
533
|
-
* The options that must all be selected for
|
|
534
|
-
* {@link KindSlotCompoundVariant.value} to apply.
|
|
535
|
-
*/
|
|
536
|
-
readonly variants: KindCompoundCondition<Variants>;
|
|
537
|
-
/** The value added to each slot when the condition matches. */
|
|
538
|
-
readonly value: SlotValues<Slot, Value>;
|
|
539
|
-
}
|
|
540
|
-
/**
|
|
541
|
-
* The configuration of a slot recipe made from a {@link RecipeKind}.
|
|
542
|
-
*
|
|
543
|
-
* @typeParam Slot - The names of the slots.
|
|
544
|
-
* @typeParam Value - The value of a slot.
|
|
545
|
-
* @typeParam Variants - The variant definitions, keyed by variant name.
|
|
546
|
-
* @typeParam DefaultedName - The names of the variants that have a default.
|
|
547
|
-
*/
|
|
548
|
-
interface KindSlotRecipeConfig<Slot extends string, Value, Variants extends KindSlotVariants<Value>, DefaultedName extends keyof ComposedVariants<Composed, Variants>, Composed extends readonly ComposableKindSlotRecipe<Value>[] = readonly []> {
|
|
549
|
-
/**
|
|
550
|
-
* Slot recipes whose config the recipe adds to its own, in order, as if
|
|
551
|
-
* it were written in one config: their slots and bases first, the values
|
|
552
|
-
* of each of their options before its own, and their compound variants
|
|
553
|
-
* first. A slot recipe composed several times counts once.
|
|
554
|
-
*/
|
|
555
|
-
readonly composes?: Composed | undefined;
|
|
556
|
-
/**
|
|
557
|
-
* The names of the slots, in the order of the recipe's result, after
|
|
558
|
-
* those of the slot recipes it composes.
|
|
559
|
-
*/
|
|
560
|
-
readonly slots: readonly Slot[];
|
|
561
|
-
/** The value of each slot that the values of every selection are added to. */
|
|
562
|
-
readonly base?: SlotValues<NoInfer<Slot> | InheritedSlot<Composed>, Value> | undefined;
|
|
563
|
-
/** For each variant name, the values of each slot for each of its options. */
|
|
564
|
-
readonly variants: Variants & NoUnknownComposedSlots<Variants, NoInfer<Slot>, InheritedSlot<Composed>>;
|
|
565
|
-
/**
|
|
566
|
-
* Values added to some slots when several variants have particular
|
|
567
|
-
* options at the same time, applied in order after the values of the
|
|
568
|
-
* variants' options.
|
|
569
|
-
*/
|
|
570
|
-
readonly compoundVariants?: readonly KindSlotCompoundVariant<NoInfer<ComposedVariants<Composed, Variants>>, NoInfer<Slot> | InheritedSlot<Composed>, Value>[] | undefined;
|
|
571
|
-
/** The option each variant uses when the recipe is called without it. */
|
|
572
|
-
readonly defaultVariants?: KindDefaultVariants<ComposedVariants<Composed, Variants>, DefaultedName> | undefined;
|
|
573
|
-
/** Whether the recipe caches its results. Defaults to the kind's `cache`. */
|
|
574
|
-
readonly cache?: boolean | undefined;
|
|
575
|
-
}
|
|
576
|
-
/**
|
|
577
|
-
* Creates slot recipes of one kind, the function that
|
|
578
|
-
* {@link createSlotRecipeKind} returns.
|
|
579
|
-
*
|
|
580
|
-
* @typeParam Value - The value of a slot.
|
|
581
|
-
* @typeParam Result - What a recipe of this kind returns for each slot.
|
|
582
|
-
* @param config - The slot recipes it composes, and the slots, base values,
|
|
583
|
-
* variants, compound variants, and default variants of the slot recipe,
|
|
584
|
-
* and whether it caches its results.
|
|
585
|
-
* @returns The slot recipe.
|
|
586
|
-
*/
|
|
587
|
-
type CreateKindSlotRecipe<Value, Result> = <const Slot extends string, const Variants extends KindSlotVariants<Value>, const DefaultedName extends keyof ComposedVariants<Composed, Variants> = never, const Composed extends readonly ComposableKindSlotRecipe<Value>[] = readonly []>(config: KindSlotRecipeConfig<Slot, Value, Variants, DefaultedName, Composed>) => ComposedKindRecipe<ComposedVariants<Composed, Variants>, DefaultedName | InheritedDefaultedName<Composed, Variants>, Value, Readonly<Record<Slot | Exclude<Composed[number]["~composition"], undefined>["slots"][number], Result>>, readonly (Slot | Exclude<Composed[number]["~composition"], undefined>["slots"][number])[]>;
|
|
588
|
-
/**
|
|
589
|
-
* Creates a kind of slot recipe from how it turns the values of each slot
|
|
590
|
-
* into that slot's result, and returns the function that creates slot
|
|
591
|
-
* recipes of that kind. A slot recipe maps a selection of variants to the
|
|
592
|
-
* result of each of several slots, such as the class names or the styles of
|
|
593
|
-
* the elements of a component, as `sva` of `@lynstack/class-recipe` does.
|
|
594
|
-
*
|
|
595
|
-
* @remarks
|
|
596
|
-
* A slot recipe reduces the values of each slot as a recipe of the same
|
|
597
|
-
* kind from {@link createRecipeKind} would: `kind.initial` returns the
|
|
598
|
-
* accumulator for the slot's base value, `kind.reduce` adds the slot's
|
|
599
|
-
* value of each variant's selected option, in the order of `variants`, and
|
|
600
|
-
* of each matching compound variant, in the order of `compoundVariants`,
|
|
601
|
-
* and `kind.finish` turns the accumulator into the slot's result. A slot
|
|
602
|
-
* without values gets the result of its accumulator for an `undefined`
|
|
603
|
-
* base, so every slot is in the result.
|
|
604
|
-
*
|
|
605
|
-
* The result is a frozen object of each slot's result, keyed by slot name
|
|
606
|
-
* in the order of `slots`. With the cache, a slot recipe builds it once for
|
|
607
|
-
* each declared selection and returns the same object for the same
|
|
608
|
-
* variants. The `cache` of a slot recipe's config overrides the kind's.
|
|
609
|
-
* Variants, boolean variants, undeclared options, the warning about
|
|
610
|
-
* undeclared names, and the `variantKeys`, `variantOptions`, and
|
|
611
|
-
* `defaultVariants` properties are as in {@link createRecipeKind}. It also
|
|
612
|
-
* warns about a value for a slot that no config lists in `slots`, which is
|
|
613
|
-
* ignored.
|
|
614
|
-
*
|
|
615
|
-
* A slot recipe composes the slot recipes listed in `composes` as a recipe
|
|
616
|
-
* composes recipes, and has the slots of each, theirs first. With
|
|
617
|
-
* `kind.combine`, the values of each slot are combined as a recipe's are.
|
|
618
|
-
*
|
|
619
|
-
* @typeParam Value - The value of a slot, inferred from the `value`
|
|
620
|
-
* parameter of `kind.reduce` or the `base` parameter of `kind.initial`.
|
|
621
|
-
* @typeParam Accumulator - What the values of a slot are reduced to,
|
|
622
|
-
* inferred from `kind.initial`.
|
|
623
|
-
* @typeParam Result - What a slot recipe returns for each slot, inferred
|
|
624
|
-
* from `kind.finish`, or the accumulator without it.
|
|
625
|
-
* @param kind - How a slot recipe turns the values of each slot into its
|
|
626
|
-
* result, and whether it caches its results. The same kind can create
|
|
627
|
-
* recipes with {@link createRecipeKind}.
|
|
628
|
-
* @returns The function that creates slot recipes of this kind.
|
|
629
|
-
*
|
|
630
|
-
* @example
|
|
631
|
-
* ```ts
|
|
632
|
-
* type Style = Readonly<Record<string, string | number>>;
|
|
633
|
-
*
|
|
634
|
-
* const styleKind = {
|
|
635
|
-
* initial: (base?: Style): Record<string, string | number> => ({ ...base }),
|
|
636
|
-
* reduce: (style: Record<string, string | number>, value: Style) =>
|
|
637
|
-
* Object.assign(style, value),
|
|
638
|
-
* finish: (style: Record<string, string | number>): Style =>
|
|
639
|
-
* Object.freeze(style),
|
|
640
|
-
* };
|
|
641
|
-
*
|
|
642
|
-
* const slotStyleRecipe = createSlotRecipeKind(styleKind);
|
|
643
|
-
*
|
|
644
|
-
* const card = slotStyleRecipe({
|
|
645
|
-
* slots: ["root", "title"],
|
|
646
|
-
* base: { root: { padding: 16 }, title: { fontSize: 18 } },
|
|
647
|
-
* variants: {
|
|
648
|
-
* tone: {
|
|
649
|
-
* light: { root: { backgroundColor: "white" } },
|
|
650
|
-
* dark: { root: { backgroundColor: "black" }, title: { color: "white" } },
|
|
651
|
-
* },
|
|
652
|
-
* },
|
|
653
|
-
* compoundVariants: [
|
|
654
|
-
* { variants: { tone: "dark" }, value: { title: { fontWeight: 600 } } },
|
|
655
|
-
* ],
|
|
656
|
-
* defaultVariants: { tone: "light" },
|
|
657
|
-
* });
|
|
658
|
-
*
|
|
659
|
-
* card();
|
|
660
|
-
* // => { root: { padding: 16, backgroundColor: "white" }, title: { fontSize: 18 } }
|
|
661
|
-
*
|
|
662
|
-
* card({ tone: "dark" });
|
|
663
|
-
* // => {
|
|
664
|
-
* // root: { padding: 16, backgroundColor: "black" },
|
|
665
|
-
* // title: { fontSize: 18, color: "white", fontWeight: 600 },
|
|
666
|
-
* // }
|
|
667
|
-
*
|
|
668
|
-
* card.variantOptions; // => { tone: ["light", "dark"] }
|
|
669
|
-
* card.defaultVariants; // => { tone: "light" }
|
|
670
|
-
*
|
|
671
|
-
* const dialog = slotStyleRecipe({
|
|
672
|
-
* composes: [card],
|
|
673
|
-
* slots: ["footer"],
|
|
674
|
-
* variants: { tone: { dark: { footer: { borderColor: "white" } } } },
|
|
675
|
-
* });
|
|
676
|
-
*
|
|
677
|
-
* dialog({ tone: "dark" }).footer; // => { borderColor: "white" }
|
|
678
|
-
* ```
|
|
679
|
-
*/
|
|
680
|
-
declare function createSlotRecipeKind<Value, Accumulator, Result = Accumulator>(kind: RecipeKind<Value, Accumulator, Result>): CreateKindSlotRecipe<Value, Result>;
|
|
681
|
-
//#endregion
|
|
682
725
|
//#region src/recipe-of.d.ts
|
|
683
726
|
/**
|
|
684
727
|
* The names of the variants with a default in a config of type `Config`:
|
|
@@ -706,9 +749,10 @@ interface KindRecipeConfigParts<Value> {
|
|
|
706
749
|
* `isolatedDeclarations`, which cannot infer the type of a call. A config
|
|
707
750
|
* that lists `composes` is rejected, so that the type cannot leave out the
|
|
708
751
|
* recipes it composes.
|
|
709
|
-
*
|
|
710
|
-
*
|
|
711
|
-
*
|
|
752
|
+
*
|
|
753
|
+
* It needs a config whose type is known. In a function generic over the
|
|
754
|
+
* whole config, the recipe's creator cannot infer the variants, so make
|
|
755
|
+
* such a function generic over the variants instead.
|
|
712
756
|
*
|
|
713
757
|
* @typeParam Value - The value of an option, as in `CreateKindRecipe`.
|
|
714
758
|
* @typeParam Result - What a recipe of the kind returns, as in
|
|
@@ -761,10 +805,10 @@ interface KindSlotRecipeConfigParts<Value> {
|
|
|
761
805
|
* on its own, as with `isolatedDeclarations`, which cannot infer the type
|
|
762
806
|
* of a call. A config that lists `composes` is rejected, so that the type
|
|
763
807
|
* cannot leave out the slot recipes it composes.
|
|
764
|
-
*
|
|
765
|
-
*
|
|
766
|
-
*
|
|
767
|
-
* instead.
|
|
808
|
+
*
|
|
809
|
+
* It needs a config whose type is known. In a function generic over the
|
|
810
|
+
* whole config, the slot recipe's creator cannot infer the variants, so
|
|
811
|
+
* make such a function generic over the variants instead.
|
|
768
812
|
*
|
|
769
813
|
* @typeParam Value - The value of a slot, as in `CreateKindSlotRecipe`.
|
|
770
814
|
* @typeParam Result - What a slot recipe of the kind returns for each
|
|
@@ -802,5 +846,5 @@ interface KindSlotRecipeConfigParts<Value> {
|
|
|
802
846
|
*/
|
|
803
847
|
type KindSlotRecipeOf<Value, Result, Config extends KindSlotRecipeConfigParts<Value>, Composed extends readonly ComposableKindSlotRecipe<Value>[] = readonly []> = ComposedKindRecipe<ComposedVariants<Composed, Config["variants"]>, DefaultedNameOf<Config, Composed> | InheritedDefaultedName<Composed, Config["variants"]>, Value, Readonly<Record<Config["slots"][number] | Exclude<Composed[number]["~composition"], undefined>["slots"][number], Result>>, readonly (Config["slots"][number] | Exclude<Composed[number]["~composition"], undefined>["slots"][number])[]>;
|
|
804
848
|
//#endregion
|
|
805
|
-
export { type Composable, type ComposableKindRecipe, type ComposableKindSlotRecipe, type ComposedDefaultedName, type ComposedSlot, type ComposedVariants, type CompoundCondition, type CreateKindRecipe, type CreateKindSlotRecipe, type DefaultVariants, type KindCompoundVariant, type KindRecipe, type KindRecipeConfig, type KindRecipeOf, type KindSelection, type KindSlotCompoundVariant, type KindSlotRecipeConfig, type KindSlotRecipeOf, type KindSlotVariants, type KindVariants, type NoUnknownSlots, type RecipeComposition, type RecipeFunction, type RecipeKind, type SlotValues, type VariantKey, type VariantOption, type VariantSelection, type VariantsOf, createRecipeKind, createSlotRecipeKind };
|
|
849
|
+
export { type Composable, type ComposableKindRecipe, type ComposableKindSlotRecipe, type ComposedDefaultedName, type ComposedSlot, type ComposedVariants, type CompoundCondition, type CreateKindRecipe, type CreateKindSlotRecipe, type DefaultVariants, type InheritedDefaultedName, type InheritedSlot, type KindCompoundVariant, type KindRecipe, type KindRecipeConfig, type KindRecipeOf, type KindSelection, type KindSlotCompoundVariant, type KindSlotRecipeConfig, type KindSlotRecipeOf, type KindSlotVariants, type KindVariants, type NoUnknownSlots, type RecipeComposition, type RecipeFunction, type RecipeKind, type SlotValues, type UnknownSlot, type VariantKey, type VariantOption, type VariantSelection, type VariantsOf, createRecipeKind, createSlotRecipeKind };
|
|
806
850
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.js
CHANGED
|
@@ -3,8 +3,9 @@ const noProps = Object.freeze({});
|
|
|
3
3
|
* Prepares variants for selecting values. A variant that declares an option
|
|
4
4
|
* named `"true"` or `"false"` also declares the other one, with
|
|
5
5
|
* `config.noValue`, and a variant whose only options are those defaults to
|
|
6
|
-
* `"false"`.
|
|
7
|
-
*
|
|
6
|
+
* `"false"`. An undeclared option in a compound variant's list is ignored,
|
|
7
|
+
* and a compound variant that names an undeclared variant, or no declared
|
|
8
|
+
* option of a variant, never matches and is left out.
|
|
8
9
|
*/
|
|
9
10
|
function compileVariants(config) {
|
|
10
11
|
const names = Object.keys(config.variants);
|
|
@@ -48,7 +49,7 @@ function product(numbers) {
|
|
|
48
49
|
return numbers.reduce((result, number) => result * number, 1);
|
|
49
50
|
}
|
|
50
51
|
function withBooleanOptions(valuesByOption, noValue) {
|
|
51
|
-
if (!Object.keys(valuesByOption).some((option) => isBooleanName
|
|
52
|
+
if (!Object.keys(valuesByOption).some((option) => isBooleanName(option))) return valuesByOption;
|
|
52
53
|
return {
|
|
53
54
|
false: noValue,
|
|
54
55
|
true: noValue,
|
|
@@ -69,7 +70,7 @@ function compileCondition(indexByOption, variant, value) {
|
|
|
69
70
|
return [variant, toOptionNames(value).map((option) => indexes?.get(option)).filter((index) => index !== void 0)];
|
|
70
71
|
}
|
|
71
72
|
function isBooleanVariant(indexByOption) {
|
|
72
|
-
return indexByOption !== void 0 && indexByOption.size > 0 && [...indexByOption.keys()].every((option) => isBooleanName
|
|
73
|
+
return indexByOption !== void 0 && indexByOption.size > 0 && [...indexByOption.keys()].every((option) => isBooleanName(option));
|
|
73
74
|
}
|
|
74
75
|
function toOptionName(value) {
|
|
75
76
|
if (typeof value === "string") return value;
|
|
@@ -104,7 +105,7 @@ function defaultVariantsOf(compiled) {
|
|
|
104
105
|
});
|
|
105
106
|
return Object.fromEntries(entries);
|
|
106
107
|
}
|
|
107
|
-
function isBooleanName
|
|
108
|
+
function isBooleanName(option) {
|
|
108
109
|
return option === "true" || option === "false";
|
|
109
110
|
}
|
|
110
111
|
//#endregion
|
|
@@ -410,9 +411,6 @@ function declaredOptionsOf(layers) {
|
|
|
410
411
|
}
|
|
411
412
|
return optionsByName;
|
|
412
413
|
}
|
|
413
|
-
function isBooleanName(option) {
|
|
414
|
-
return option === "true" || option === "false";
|
|
415
|
-
}
|
|
416
414
|
function unknownDefaults(defaultVariants, declared) {
|
|
417
415
|
return Object.entries(defaultVariants).flatMap(([name, value]) => {
|
|
418
416
|
if (value === void 0) return [];
|
|
@@ -445,7 +443,8 @@ function unknownSlots(own, slots) {
|
|
|
445
443
|
* Returns the names in a recipe's own config that none of its layers
|
|
446
444
|
* declares: a variant or option in its defaults or compound variants, and,
|
|
447
445
|
* for a slot recipe, a slot that a value is given to. Such a default or
|
|
448
|
-
* value is left out,
|
|
446
|
+
* value is left out, an undeclared option in a compound variant's list is
|
|
447
|
+
* ignored, and any other such compound variant never matches. Each layer
|
|
449
448
|
* was checked when its own recipe was created, so only `own` is checked.
|
|
450
449
|
*/
|
|
451
450
|
function unknownNames(own, layers, isSlotRecipe) {
|