clava 0.4.2 → 0.6.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 +85 -2
- package/README.md +78 -31
- package/dist/index.d.ts +193 -3
- package/dist/index.js +187 -115
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/index.ts +451 -214
- package/src/refine-warning.ts +2 -2
- package/src/types.ts +129 -2
- package/tests/component-api.test.ts +81 -55
- package/tests/extend.test.ts +44 -10
- package/tests/language-service.test.ts +12 -1
- package/tests/prototype-pollution.test.ts +3 -4
- package/tests/refine.test.ts +267 -369
- package/tests/variants-inference.test.ts +149 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,88 @@
|
|
|
1
1
|
# clava
|
|
2
2
|
|
|
3
|
+
## 0.6.0
|
|
4
|
+
|
|
5
|
+
### Computed default variant parameters
|
|
6
|
+
|
|
7
|
+
**BREAKING** if you're using computed `defaultVariants` functions in [`cv`](https://clava.style/docs/reference/cv).
|
|
8
|
+
|
|
9
|
+
Computed `defaultVariants` functions now receive `defaultValue` and `variants` as separate parameters instead of a context object.
|
|
10
|
+
|
|
11
|
+
Before:
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
const button = cv({
|
|
15
|
+
variants: {
|
|
16
|
+
size: { sm: "sm", lg: "lg" },
|
|
17
|
+
intent: { neutral: "neutral", brand: "brand" },
|
|
18
|
+
},
|
|
19
|
+
defaultVariants: {
|
|
20
|
+
intent: ({ defaultValue, variants }) =>
|
|
21
|
+
variants.size === "lg" ? "neutral" : defaultValue,
|
|
22
|
+
},
|
|
23
|
+
});
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
After:
|
|
27
|
+
|
|
28
|
+
```ts
|
|
29
|
+
const button = cv({
|
|
30
|
+
variants: {
|
|
31
|
+
size: { sm: "sm", lg: "lg" },
|
|
32
|
+
intent: { neutral: "neutral", brand: "brand" },
|
|
33
|
+
},
|
|
34
|
+
defaultVariants: {
|
|
35
|
+
intent: (defaultValue, variants) =>
|
|
36
|
+
variants.size === "lg" ? "neutral" : defaultValue,
|
|
37
|
+
},
|
|
38
|
+
});
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### Other updates
|
|
42
|
+
|
|
43
|
+
- Documented Clava's public TypeScript helper declarations with inline examples.
|
|
44
|
+
|
|
45
|
+
## 0.5.0
|
|
46
|
+
|
|
47
|
+
### Computed default variants
|
|
48
|
+
|
|
49
|
+
**BREAKING** if you're using `setDefaultVariants` in [`cv`](https://clava.style/docs/reference/cv) `refine` callbacks.
|
|
50
|
+
|
|
51
|
+
The `setDefaultVariants` function has been removed from `refine`. Define dependent defaults directly in `defaultVariants` by passing a function for the variant key. The function receives the current `defaultValue` for that key and the resolved `variants` snapshot.
|
|
52
|
+
|
|
53
|
+
Computed default variants run before `refine`, so `refine` callbacks now read computed default values during their first pass. If a variant value itself is a function, return that function from a computed default variant instead of passing it directly as a static default.
|
|
54
|
+
|
|
55
|
+
Before:
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
const button = cv({
|
|
59
|
+
variants: {
|
|
60
|
+
size: { sm: "sm", lg: "lg" },
|
|
61
|
+
intent: { neutral: "neutral", brand: "brand" },
|
|
62
|
+
},
|
|
63
|
+
refine({ variants, setDefaultVariants }) {
|
|
64
|
+
if (variants.size === "lg") {
|
|
65
|
+
setDefaultVariants({ intent: "neutral" });
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
});
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
After:
|
|
72
|
+
|
|
73
|
+
```ts
|
|
74
|
+
const button = cv({
|
|
75
|
+
variants: {
|
|
76
|
+
size: { sm: "sm", lg: "lg" },
|
|
77
|
+
intent: { neutral: "neutral", brand: "brand" },
|
|
78
|
+
},
|
|
79
|
+
defaultVariants: {
|
|
80
|
+
intent: ({ defaultValue, variants }) =>
|
|
81
|
+
variants.size === "lg" ? "neutral" : defaultValue,
|
|
82
|
+
},
|
|
83
|
+
});
|
|
84
|
+
```
|
|
85
|
+
|
|
3
86
|
## 0.4.2
|
|
4
87
|
|
|
5
88
|
- Improved [`refine`](https://clava.style/docs/reference/refine) iteration warnings to show the latest changing variant values with a shorter component creation stack.
|
|
@@ -70,7 +153,7 @@ Before:
|
|
|
70
153
|
```ts
|
|
71
154
|
const button = cv({
|
|
72
155
|
variants: { size: { sm: "sm", lg: "lg" } },
|
|
73
|
-
computed
|
|
156
|
+
computed({ variants, addClass }) {
|
|
74
157
|
if (variants.size === "lg") {
|
|
75
158
|
addClass("is-large");
|
|
76
159
|
}
|
|
@@ -83,7 +166,7 @@ After:
|
|
|
83
166
|
```ts
|
|
84
167
|
const button = cv({
|
|
85
168
|
variants: { size: { sm: "sm", lg: "lg" } },
|
|
86
|
-
refine
|
|
169
|
+
refine({ variants, addClass }) {
|
|
87
170
|
if (variants.size === "lg") {
|
|
88
171
|
addClass("is-large");
|
|
89
172
|
}
|
package/README.md
CHANGED
|
@@ -24,6 +24,7 @@ import type { Variant, VariantProps } from "clava";
|
|
|
24
24
|
- [Solid](#solid)
|
|
25
25
|
- [`create()` And `cx()`](#create-and-cx)
|
|
26
26
|
- [Type Helpers](#type-helpers)
|
|
27
|
+
- [Comparison](#comparison)
|
|
27
28
|
- [API Summary](#api-summary)
|
|
28
29
|
|
|
29
30
|
## Install
|
|
@@ -230,17 +231,19 @@ Variant values can be class values, arrays, `{ class, style }` objects, or [func
|
|
|
230
231
|
|
|
231
232
|
## Function Variants
|
|
232
233
|
|
|
233
|
-
Add a function as a variant value when the prop should generate class/style output dynamically. The function's parameter type defines the prop type.
|
|
234
|
+
Add a function as a variant value when the prop should generate class/style output dynamically. The function's parameter type defines the prop type. Method syntax works well for multi-line callbacks; compact one-line callbacks can stay as arrow-function property values.
|
|
234
235
|
|
|
235
236
|
```ts
|
|
236
237
|
const grid = cv({
|
|
237
238
|
class: "grid",
|
|
238
239
|
variants: {
|
|
239
|
-
columns
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
240
|
+
columns(value: number) {
|
|
241
|
+
return {
|
|
242
|
+
class: `grid-cols-${value}`,
|
|
243
|
+
style: { "--grid-columns": `${value}` },
|
|
244
|
+
};
|
|
245
|
+
},
|
|
246
|
+
color(value: string | null) {
|
|
244
247
|
return value ? `text-${value}` : "text-current";
|
|
245
248
|
},
|
|
246
249
|
},
|
|
@@ -316,7 +319,9 @@ You can extend any component mode, including `baseButton.jsx`, `baseButton.html`
|
|
|
316
319
|
|
|
317
320
|
## Refine
|
|
318
321
|
|
|
319
|
-
Use `
|
|
322
|
+
Use computed `defaultVariants` for dependent defaults. A function entry receives the current default value for that key as the first parameter and the resolved variants snapshot as the second parameter, then returns the next default value.
|
|
323
|
+
|
|
324
|
+
Use `refine` for final variant overrides and class/style adjustments. It receives the resolved variant values for the component and can return class/style output.
|
|
320
325
|
|
|
321
326
|
```ts
|
|
322
327
|
const toolbarButton = cv({
|
|
@@ -328,21 +333,15 @@ const toolbarButton = cv({
|
|
|
328
333
|
},
|
|
329
334
|
loading: "toolbar-button-loading",
|
|
330
335
|
},
|
|
331
|
-
|
|
332
|
-
variants
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
addStyle,
|
|
337
|
-
}) => {
|
|
336
|
+
defaultVariants: {
|
|
337
|
+
intent: (defaultValue, variants) =>
|
|
338
|
+
variants.size === "lg" ? "neutral" : defaultValue,
|
|
339
|
+
},
|
|
340
|
+
refine({ variants, setVariants, addClass, addStyle }) {
|
|
338
341
|
if (variants.loading) {
|
|
339
342
|
setVariants({ pressed: false });
|
|
340
343
|
}
|
|
341
344
|
|
|
342
|
-
if (variants.size === "lg") {
|
|
343
|
-
setDefaultVariants({ intent: "neutral" });
|
|
344
|
-
}
|
|
345
|
-
|
|
346
345
|
if (variants.pressed && variants.intent === "brand") {
|
|
347
346
|
addClass("toolbar-button-brand-pressed");
|
|
348
347
|
addStyle({ transform: "translateY(1px)" });
|
|
@@ -353,9 +352,24 @@ const toolbarButton = cv({
|
|
|
353
352
|
});
|
|
354
353
|
```
|
|
355
354
|
|
|
356
|
-
|
|
355
|
+
Computed `defaultVariants` do not override a prop the user explicitly passed unless that prop value is `undefined`. Return `defaultValue` to preserve the inherited or static default value. Return `undefined` to clear the default value. If a variant's value is a function, return that function from a computed default:
|
|
356
|
+
|
|
357
|
+
```ts
|
|
358
|
+
const transform = (value: string) => value.toUpperCase();
|
|
359
|
+
|
|
360
|
+
const input = cv({
|
|
361
|
+
variants: {
|
|
362
|
+
transform: (fn: (value: string) => string) => fn("example"),
|
|
363
|
+
},
|
|
364
|
+
defaultVariants: {
|
|
365
|
+
transform: () => transform,
|
|
366
|
+
},
|
|
367
|
+
});
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
`setVariants()` overrides explicit props. `addClass()` and `addStyle()` append output without changing resolved variant values. `getVariants()` includes values changed by computed `defaultVariants` and `setVariants()`.
|
|
357
371
|
|
|
358
|
-
When a `refine` callback changes variants, Clava re-runs the refine chain so later reads see the latest values. Re-runs are capped at 50 iterations, after which Clava stops and logs a warning in development.
|
|
372
|
+
When a computed default or `refine` callback changes variants, Clava re-runs the refine chain so later reads see the latest values. Re-runs are capped at 50 iterations, after which Clava stops and logs a warning in development.
|
|
359
373
|
|
|
360
374
|
## Splitting Props
|
|
361
375
|
|
|
@@ -373,17 +387,18 @@ const button = cv({
|
|
|
373
387
|
lg: "button-lg",
|
|
374
388
|
},
|
|
375
389
|
},
|
|
376
|
-
})
|
|
390
|
+
});
|
|
377
391
|
|
|
378
|
-
|
|
392
|
+
interface ButtonProps
|
|
393
|
+
extends ComponentProps<"button">, VariantProps<typeof button> {}
|
|
379
394
|
|
|
380
395
|
function Button(props: ButtonProps) {
|
|
381
396
|
const [variantProps, buttonProps] = splitProps(props, button);
|
|
382
|
-
return <button {...buttonProps} {...button(variantProps)} />;
|
|
397
|
+
return <button {...buttonProps} {...button.jsx(variantProps)} />;
|
|
383
398
|
}
|
|
384
399
|
```
|
|
385
400
|
|
|
386
|
-
The first component source claims variant props plus styling props
|
|
401
|
+
The first component source claims variant props plus the styling props exposed by that component's `propKeys`. The base component claims `class`, `className`, and `style`; mode-specific components claim their own styling props. Later component sources receive only their variant props. Array sources receive exactly the listed keys and do not claim styling props.
|
|
387
402
|
|
|
388
403
|
```ts
|
|
389
404
|
const [buttonProps, fieldProps, rest] = splitProps(props, button, field);
|
|
@@ -420,14 +435,14 @@ const button = cv({
|
|
|
420
435
|
md: "button-md",
|
|
421
436
|
},
|
|
422
437
|
},
|
|
423
|
-
})
|
|
438
|
+
});
|
|
424
439
|
|
|
425
440
|
interface ButtonProps
|
|
426
441
|
extends ComponentProps<"button">, VariantProps<typeof button> {}
|
|
427
442
|
|
|
428
443
|
function Button(props: ButtonProps) {
|
|
429
444
|
const [variantProps, buttonProps] = splitProps(props, button);
|
|
430
|
-
return <button {...buttonProps} {...button(variantProps)} />;
|
|
445
|
+
return <button {...buttonProps} {...button.jsx(variantProps)} />;
|
|
431
446
|
}
|
|
432
447
|
```
|
|
433
448
|
|
|
@@ -448,13 +463,14 @@ const button = cv({
|
|
|
448
463
|
md: "button-md",
|
|
449
464
|
},
|
|
450
465
|
},
|
|
451
|
-
})
|
|
466
|
+
});
|
|
452
467
|
|
|
453
|
-
|
|
468
|
+
interface ButtonProps
|
|
469
|
+
extends ComponentProps<"button">, VariantProps<typeof button> {}
|
|
454
470
|
|
|
455
471
|
function Button(props: ButtonProps) {
|
|
456
472
|
const [variantProps, buttonProps] = splitProps(props, button);
|
|
457
|
-
return <button {...buttonProps} {...button(variantProps)} />;
|
|
473
|
+
return <button {...buttonProps} {...button.htmlObj(variantProps)} />;
|
|
458
474
|
}
|
|
459
475
|
```
|
|
460
476
|
|
|
@@ -493,7 +509,8 @@ Use `VariantProps<typeof component>` to add a Clava component's variant props to
|
|
|
493
509
|
import type { ComponentProps } from "react";
|
|
494
510
|
import type { VariantProps } from "clava";
|
|
495
511
|
|
|
496
|
-
|
|
512
|
+
interface ButtonProps
|
|
513
|
+
extends ComponentProps<"button">, VariantProps<typeof button> {}
|
|
497
514
|
```
|
|
498
515
|
|
|
499
516
|
Use `Variant<typeof component, "key">` to constrain a new variant map to the same values as another component's variant.
|
|
@@ -523,6 +540,36 @@ const icon = cv({
|
|
|
523
540
|
|
|
524
541
|
The package also exports `ClassValue`, `StyleValue`, `StyleClassProps`, `StyleClassValue`, `JSXProps`, `HTMLProps`, `HTMLObjProps`, `CVComponent`, and `CVConfig`.
|
|
525
542
|
|
|
543
|
+
## Comparison
|
|
544
|
+
|
|
545
|
+
This table compares Clava with the packages used by the repository's alternative benchmark: `class-variance-authority@0.7.1`, `cva@1.0.0-beta.4`, `tailwind-variants/lite@3.2.2`, and `tailwind-variants@3.2.2`.
|
|
546
|
+
|
|
547
|
+
| Feature | Clava | CVA v0 | CVA v1 beta | TV Lite | TV |
|
|
548
|
+
| ---------------------------------------------------- | -------------- | ------------------ | ------------------ | ------------------ | ------------------ |
|
|
549
|
+
| Typed variants and default variants | Yes | Yes | Yes | Yes | Yes |
|
|
550
|
+
| Boolean shorthand variants | Yes | No | No | No | No |
|
|
551
|
+
| Component extension or composition | `extend` | No helper | `compose()` | `extend` | `extend` |
|
|
552
|
+
| Cross-variant conditions | `refine()` | `compoundVariants` | `compoundVariants` | `compoundVariants` | `compoundVariants` |
|
|
553
|
+
| Function variant values | Yes | No | No | No | No |
|
|
554
|
+
| Computed default variants | Yes | No | No | No | No |
|
|
555
|
+
| Class and style prop output | Yes | Classes only | Classes only | Classes only | Classes only |
|
|
556
|
+
| JSX, HTML string, and hyphenated-object output modes | Yes | No | No | No | No |
|
|
557
|
+
| Built-in prop splitting for framework props | `splitProps()` | No | No | No | No |
|
|
558
|
+
| Dedicated slots API | No | No | No | Yes | Yes |
|
|
559
|
+
| Built-in Tailwind conflict merging | No | No | No | No | Yes |
|
|
560
|
+
|
|
561
|
+
The `pnpm perf-alternatives` benchmark resolves a composed Tailwind-style button with inherited variants, defaults, and cross-variant conditions. On Node v24.14.1, Vitest reported these results, where higher ops/sec is better:
|
|
562
|
+
|
|
563
|
+
| Package | Ops/sec | Relative to Clava |
|
|
564
|
+
| -------------------------------- | --------: | ----------------: |
|
|
565
|
+
| `clava@0.5.0` | 1,040,314 | 1.00x |
|
|
566
|
+
| `class-variance-authority@0.7.1` | 426,132 | 2.44x slower |
|
|
567
|
+
| `tailwind-variants/lite@3.2.2` | 369,732 | 2.81x slower |
|
|
568
|
+
| `tailwind-variants@3.2.2` | 273,503 | 3.80x slower |
|
|
569
|
+
| `cva@1.0.0-beta.4` | 211,474 | 4.92x slower |
|
|
570
|
+
|
|
571
|
+
Benchmark results vary by runtime and hardware, so treat them as a reproducible snapshot of this repository's composed-variant case rather than a universal ranking.
|
|
572
|
+
|
|
526
573
|
## API Summary
|
|
527
574
|
|
|
528
575
|
`cv(config?)` creates a typed Clava component. Supported config keys are `extend`, `class`, `style`, `variants`, `defaultVariants`, and `refine`.
|
|
@@ -539,7 +586,7 @@ The package also exports `ClassValue`, `StyleValue`, `StyleClassProps`, `StyleCl
|
|
|
539
586
|
|
|
540
587
|
`component.style(props?)` returns only the resolved style value for that component mode.
|
|
541
588
|
|
|
542
|
-
`component.getVariants(props?)` returns resolved variant values after static defaults, inherited defaults, and `refine` updates.
|
|
589
|
+
`component.getVariants(props?)` returns resolved variant values after static defaults, inherited defaults, computed defaults, and `refine` updates.
|
|
543
590
|
|
|
544
591
|
`component.propKeys` lists style props plus variant props for that component mode.
|
|
545
592
|
|
package/dist/index.d.ts
CHANGED
|
@@ -2,22 +2,84 @@ import { ClassValue as ClassValue$1 } from "clsx";
|
|
|
2
2
|
import * as CSS from "csstype";
|
|
3
3
|
|
|
4
4
|
//#region src/types.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* A class value accepted by Clava. It supports strings, numbers, booleans,
|
|
7
|
+
* nullish values, and nested arrays.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```ts
|
|
11
|
+
* import type { ClassValue } from "clava";
|
|
12
|
+
*
|
|
13
|
+
* const className: ClassValue = [
|
|
14
|
+
* "button",
|
|
15
|
+
* false && "button-hidden",
|
|
16
|
+
* ["button-primary"],
|
|
17
|
+
* ];
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
5
20
|
type ClassValue = string | number | boolean | null | undefined | void | ClassValue[];
|
|
6
21
|
type JSXCSSProperties = CSS.Properties<string | number>;
|
|
7
22
|
type HTMLCSSProperties = CSS.PropertiesHyphen<string | number>;
|
|
8
23
|
type StyleProperty = JSXCSSProperties | HTMLCSSProperties | string;
|
|
24
|
+
/**
|
|
25
|
+
* The prop object returned by a component's `.jsx()` mode.
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```ts
|
|
29
|
+
* import { type JSXProps, cv } from "clava";
|
|
30
|
+
*
|
|
31
|
+
* const button = cv({ class: "button" });
|
|
32
|
+
* const props: JSXProps = button.jsx();
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
9
35
|
interface JSXProps {
|
|
10
36
|
className: string;
|
|
11
37
|
style: JSXCSSProperties;
|
|
12
38
|
}
|
|
39
|
+
/**
|
|
40
|
+
* The prop object returned by a component's `.html()` mode. The `style` value
|
|
41
|
+
* is serialized as an HTML style string.
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* ```ts
|
|
45
|
+
* import { type HTMLProps, cv } from "clava";
|
|
46
|
+
*
|
|
47
|
+
* const button = cv({ style: { color: "red" } });
|
|
48
|
+
* const props: HTMLProps = button.html();
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
13
51
|
interface HTMLProps {
|
|
14
52
|
class: string;
|
|
15
53
|
style: string;
|
|
16
54
|
}
|
|
55
|
+
/**
|
|
56
|
+
* The prop object returned by a component's `.htmlObj()` mode. The `style`
|
|
57
|
+
* value uses hyphenated CSS property names.
|
|
58
|
+
*
|
|
59
|
+
* @example
|
|
60
|
+
* ```ts
|
|
61
|
+
* import { type HTMLObjProps, cv } from "clava";
|
|
62
|
+
*
|
|
63
|
+
* const button = cv({ style: { fontSize: "16px" } });
|
|
64
|
+
* const props: HTMLObjProps = button.htmlObj();
|
|
65
|
+
* ```
|
|
66
|
+
*/
|
|
17
67
|
interface HTMLObjProps {
|
|
18
68
|
class: string;
|
|
19
69
|
style: HTMLCSSProperties;
|
|
20
70
|
}
|
|
71
|
+
/**
|
|
72
|
+
* The default prop object returned by a Clava component. It uses `class`
|
|
73
|
+
* rather than `className` and keeps styles as a normalized object.
|
|
74
|
+
*
|
|
75
|
+
* @example
|
|
76
|
+
* ```ts
|
|
77
|
+
* import { type StyleClassProps, cv } from "clava";
|
|
78
|
+
*
|
|
79
|
+
* const button = cv({ class: "button" });
|
|
80
|
+
* const props: StyleClassProps = button();
|
|
81
|
+
* ```
|
|
82
|
+
*/
|
|
21
83
|
interface StyleClassProps {
|
|
22
84
|
class: string;
|
|
23
85
|
style: StyleValue;
|
|
@@ -61,6 +123,25 @@ interface ModalComponent<V, R extends ComponentResult> {
|
|
|
61
123
|
variantKeys: (keyof V)[];
|
|
62
124
|
propKeys: (keyof V | ComponentPropKey<R>)[];
|
|
63
125
|
}
|
|
126
|
+
/**
|
|
127
|
+
* A callable Clava component returned by `cv()`. It includes the default
|
|
128
|
+
* output mode plus `.jsx()`, `.html()`, and `.htmlObj()` mode helpers.
|
|
129
|
+
*
|
|
130
|
+
* @example
|
|
131
|
+
* ```ts
|
|
132
|
+
* import { type CVComponent, cv } from "clava";
|
|
133
|
+
*
|
|
134
|
+
* const button: CVComponent<{
|
|
135
|
+
* size: { sm: string; lg: string };
|
|
136
|
+
* }> = cv({
|
|
137
|
+
* variants: {
|
|
138
|
+
* size: { sm: "button-sm", lg: "button-lg" },
|
|
139
|
+
* },
|
|
140
|
+
* });
|
|
141
|
+
*
|
|
142
|
+
* button.jsx({ size: "lg" });
|
|
143
|
+
* ```
|
|
144
|
+
*/
|
|
64
145
|
interface CVComponent<V extends Variants = {}, E extends AnyComponent[] = [], R extends ComponentResult = StyleClassProps> extends ModalComponent<MergeVariants<V, E>, R> {
|
|
65
146
|
jsx: ModalComponent<MergeVariants<V, E>, JSXProps>;
|
|
66
147
|
html: ModalComponent<MergeVariants<V, E>, HTMLProps>;
|
|
@@ -76,10 +157,42 @@ type StringToBoolean<T> = T extends "true" | "false" ? boolean : T;
|
|
|
76
157
|
type VariantValue = ClassValue | StyleClassValue;
|
|
77
158
|
type NonNullKeys<T> = { [K in keyof T]: T[K] extends null ? never : K }[keyof T];
|
|
78
159
|
type ExtractVariantValue<T> = T extends null ? never : T extends ((value: infer V) => any) ? V : T extends readonly unknown[] ? boolean : T extends Record<string, any> ? StringToBoolean<NonNullKeys<T>> : T extends ClassValue ? boolean : never;
|
|
79
|
-
type VariantValues<V> = { [K in keyof V]?: ExtractVariantValue<V[K]> };
|
|
160
|
+
type VariantValues<V> = { [K in keyof V]?: ExtractVariantValue<V[K]> | undefined };
|
|
161
|
+
type ComputedDefaultVariant<V, K extends keyof V> = (defaultValue: ExtractVariantValue<V[K]> | undefined, variants: Readonly<VariantValues<V>>) => ExtractVariantValue<V[K]> | undefined;
|
|
162
|
+
type NonFunctionVariantValue<T> = Exclude<T, (...args: any[]) => any>;
|
|
163
|
+
type DefaultVariantValue<V, K extends keyof V> = [NonFunctionVariantValue<ExtractVariantValue<V[K]>>] extends [never] ? ComputedDefaultVariant<V, K> : NonFunctionVariantValue<ExtractVariantValue<V[K]>> | ComputedDefaultVariant<V, K>;
|
|
164
|
+
type DefaultVariants<V> = { [K in keyof V]?: DefaultVariantValue<V, K> | undefined };
|
|
165
|
+
/**
|
|
166
|
+
* A normalized style object accepted by Clava config, variant, and refine
|
|
167
|
+
* style entries. CSS custom properties are supported with string values.
|
|
168
|
+
*
|
|
169
|
+
* @example
|
|
170
|
+
* ```ts
|
|
171
|
+
* import type { StyleValue } from "clava";
|
|
172
|
+
*
|
|
173
|
+
* const style: StyleValue = {
|
|
174
|
+
* color: "red",
|
|
175
|
+
* "--button-accent": "oklch(62% 0.2 250)",
|
|
176
|
+
* };
|
|
177
|
+
* ```
|
|
178
|
+
*/
|
|
80
179
|
type StyleValue = CSS.Properties & {
|
|
81
180
|
[key: `--${string}`]: string;
|
|
82
181
|
};
|
|
182
|
+
/**
|
|
183
|
+
* A value that contributes both class and style output from a base config,
|
|
184
|
+
* variant value, function variant, or refine callback.
|
|
185
|
+
*
|
|
186
|
+
* @example
|
|
187
|
+
* ```ts
|
|
188
|
+
* import type { StyleClassValue } from "clava";
|
|
189
|
+
*
|
|
190
|
+
* const tone: StyleClassValue = {
|
|
191
|
+
* class: "button-primary",
|
|
192
|
+
* style: { color: "white" },
|
|
193
|
+
* };
|
|
194
|
+
* ```
|
|
195
|
+
*/
|
|
83
196
|
interface StyleClassValue {
|
|
84
197
|
style?: StyleValue;
|
|
85
198
|
class?: ClassValue;
|
|
@@ -87,7 +200,6 @@ interface StyleClassValue {
|
|
|
87
200
|
interface RefineContext<V> {
|
|
88
201
|
variants: VariantValues<V>;
|
|
89
202
|
setVariants: (variants: VariantValues<V>) => void;
|
|
90
|
-
setDefaultVariants: (variants: VariantValues<V>) => void;
|
|
91
203
|
addClass: (className: ClassValue) => void;
|
|
92
204
|
addStyle: (style: StyleValue) => void;
|
|
93
205
|
}
|
|
@@ -98,15 +210,93 @@ type NullablePartial<T> = T extends Record<string, any> ? { [K in keyof T]?: T[K
|
|
|
98
210
|
type ExtendableVariants<V extends Variants, E extends AnyComponent[]> = V & { [K in keyof MergeExtendedVariants<E>]?: NullablePartial<MergeExtendedVariants<E>[K]> | Variant$1 };
|
|
99
211
|
//#endregion
|
|
100
212
|
//#region src/index.d.ts
|
|
213
|
+
/**
|
|
214
|
+
* Extracts the variant props inferred for a Clava component. Use it to add a
|
|
215
|
+
* component's variant props to framework component props.
|
|
216
|
+
*
|
|
217
|
+
* @example
|
|
218
|
+
* ```ts
|
|
219
|
+
* import { type VariantProps, cv } from "clava";
|
|
220
|
+
* import type { ComponentProps } from "react";
|
|
221
|
+
*
|
|
222
|
+
* const button = cv({
|
|
223
|
+
* variants: {
|
|
224
|
+
* size: { sm: "button-sm", lg: "button-lg" },
|
|
225
|
+
* disabled: { true: "button-disabled", false: "" },
|
|
226
|
+
* },
|
|
227
|
+
* });
|
|
228
|
+
*
|
|
229
|
+
* interface ButtonProps
|
|
230
|
+
* extends ComponentProps<"button">,
|
|
231
|
+
* VariantProps<typeof button> {}
|
|
232
|
+
*
|
|
233
|
+
* const props: ButtonProps = {
|
|
234
|
+
* size: "lg",
|
|
235
|
+
* disabled: true,
|
|
236
|
+
* };
|
|
237
|
+
* ```
|
|
238
|
+
*/
|
|
101
239
|
type VariantProps<T extends Pick<AnyComponent, "getVariants">> = ReturnType<T["getVariants"]>;
|
|
102
240
|
type VariantKey<T> = T extends boolean ? "true" | "false" : Extract<T, string>;
|
|
241
|
+
/**
|
|
242
|
+
* Constrains a variant map to the same value keys as a variant on another
|
|
243
|
+
* component. Boolean variants are represented with `"true"` and `"false"`
|
|
244
|
+
* object keys.
|
|
245
|
+
*
|
|
246
|
+
* @example
|
|
247
|
+
* ```ts
|
|
248
|
+
* import { type Variant, cv } from "clava";
|
|
249
|
+
*
|
|
250
|
+
* const button = cv({
|
|
251
|
+
* variants: {
|
|
252
|
+
* size: { sm: "button-sm", lg: "button-lg" },
|
|
253
|
+
* },
|
|
254
|
+
* });
|
|
255
|
+
*
|
|
256
|
+
* const icon = cv({
|
|
257
|
+
* extend: [button],
|
|
258
|
+
* variants: {
|
|
259
|
+
* size: {
|
|
260
|
+
* sm: "icon-sm",
|
|
261
|
+
* lg: "icon-lg",
|
|
262
|
+
* } satisfies Variant<typeof button, "size">,
|
|
263
|
+
* },
|
|
264
|
+
* });
|
|
265
|
+
* ```
|
|
266
|
+
*/
|
|
103
267
|
type Variant<T extends Pick<AnyComponent, "getVariants">, K extends keyof VariantProps<T>> = Record<VariantKey<NonNullable<VariantProps<T>[K]>>, ClassValue | StyleClassValue>;
|
|
268
|
+
/**
|
|
269
|
+
* The configuration object accepted by `cv()`. It defines base class/style
|
|
270
|
+
* output, variants, default variants, component extensions, and refinement
|
|
271
|
+
* logic.
|
|
272
|
+
*
|
|
273
|
+
* @example
|
|
274
|
+
* ```ts
|
|
275
|
+
* import { type CVConfig, cv } from "clava";
|
|
276
|
+
*
|
|
277
|
+
* const config: CVConfig<{
|
|
278
|
+
* tone: { info: string; danger: string };
|
|
279
|
+
* }> = {
|
|
280
|
+
* variants: {
|
|
281
|
+
* tone: {
|
|
282
|
+
* info: "alert-info",
|
|
283
|
+
* danger: "alert-danger",
|
|
284
|
+
* },
|
|
285
|
+
* },
|
|
286
|
+
* defaultVariants: {
|
|
287
|
+
* tone: "info",
|
|
288
|
+
* },
|
|
289
|
+
* };
|
|
290
|
+
*
|
|
291
|
+
* const alert = cv(config);
|
|
292
|
+
* ```
|
|
293
|
+
*/
|
|
104
294
|
interface CVConfig<V extends Variants = {}, E extends AnyComponent[] = []> {
|
|
105
295
|
extend?: E;
|
|
106
296
|
class?: ClassValue;
|
|
107
297
|
style?: StyleValue;
|
|
108
298
|
variants?: ExtendableVariants<V, E>;
|
|
109
|
-
defaultVariants?:
|
|
299
|
+
defaultVariants?: DefaultVariants<MergeVariants<V, E>>;
|
|
110
300
|
refine?: Refine<MergeVariants<V, E>>;
|
|
111
301
|
}
|
|
112
302
|
interface CreateParams {
|