clava 0.4.1 → 0.5.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 +46 -0
- package/README.md +26 -15
- package/dist/index.d.ts +10 -3
- package/dist/index.js +302 -170
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/index.ts +415 -338
- package/src/refine-warning.ts +161 -0
- package/src/types.ts +24 -2
- package/tests/build.test.ts +1 -0
- package/tests/component-api.test.ts +81 -55
- package/tests/extend.test.ts +44 -10
- package/tests/prototype-pollution.test.ts +3 -4
- package/tests/refine-warning.test.ts +28 -0
- package/tests/refine.test.ts +300 -181
- package/tests/variants-inference.test.ts +81 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,51 @@
|
|
|
1
1
|
# clava
|
|
2
2
|
|
|
3
|
+
## 0.5.0
|
|
4
|
+
|
|
5
|
+
### Computed default variants
|
|
6
|
+
|
|
7
|
+
**BREAKING** if you're using `setDefaultVariants` in [`cv`](https://clava.style/docs/reference/cv) `refine` callbacks.
|
|
8
|
+
|
|
9
|
+
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.
|
|
10
|
+
|
|
11
|
+
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.
|
|
12
|
+
|
|
13
|
+
Before:
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
const button = cv({
|
|
17
|
+
variants: {
|
|
18
|
+
size: { sm: "sm", lg: "lg" },
|
|
19
|
+
intent: { neutral: "neutral", brand: "brand" },
|
|
20
|
+
},
|
|
21
|
+
refine: ({ variants, setDefaultVariants }) => {
|
|
22
|
+
if (variants.size === "lg") {
|
|
23
|
+
setDefaultVariants({ intent: "neutral" });
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
After:
|
|
30
|
+
|
|
31
|
+
```ts
|
|
32
|
+
const button = cv({
|
|
33
|
+
variants: {
|
|
34
|
+
size: { sm: "sm", lg: "lg" },
|
|
35
|
+
intent: { neutral: "neutral", brand: "brand" },
|
|
36
|
+
},
|
|
37
|
+
defaultVariants: {
|
|
38
|
+
intent: ({ defaultValue, variants }) =>
|
|
39
|
+
variants.size === "lg" ? "neutral" : defaultValue,
|
|
40
|
+
},
|
|
41
|
+
});
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## 0.4.2
|
|
45
|
+
|
|
46
|
+
- Improved [`refine`](https://clava.style/docs/reference/refine) iteration warnings to show the latest changing variant values with a shorter component creation stack.
|
|
47
|
+
- Fixed [`setDefaultVariants`](https://clava.style/docs/reference/refine#setdefaultvariants) calls with stable values to avoid extra [`refine`](https://clava.style/docs/reference/refine) passes.
|
|
48
|
+
|
|
3
49
|
## 0.4.1
|
|
4
50
|
|
|
5
51
|
### Improved refine iteration warning with debugging context
|
package/README.md
CHANGED
|
@@ -316,7 +316,9 @@ You can extend any component mode, including `baseButton.jsx`, `baseButton.html`
|
|
|
316
316
|
|
|
317
317
|
## Refine
|
|
318
318
|
|
|
319
|
-
Use `
|
|
319
|
+
Use computed `defaultVariants` for dependent defaults. A function entry receives the current default value for that key plus the resolved variants snapshot, and returns the next default value.
|
|
320
|
+
|
|
321
|
+
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
322
|
|
|
321
323
|
```ts
|
|
322
324
|
const toolbarButton = cv({
|
|
@@ -328,21 +330,15 @@ const toolbarButton = cv({
|
|
|
328
330
|
},
|
|
329
331
|
loading: "toolbar-button-loading",
|
|
330
332
|
},
|
|
331
|
-
|
|
332
|
-
variants
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
addStyle,
|
|
337
|
-
}) => {
|
|
333
|
+
defaultVariants: {
|
|
334
|
+
intent: ({ defaultValue, variants }) =>
|
|
335
|
+
variants.size === "lg" ? "neutral" : defaultValue,
|
|
336
|
+
},
|
|
337
|
+
refine: ({ variants, setVariants, addClass, addStyle }) => {
|
|
338
338
|
if (variants.loading) {
|
|
339
339
|
setVariants({ pressed: false });
|
|
340
340
|
}
|
|
341
341
|
|
|
342
|
-
if (variants.size === "lg") {
|
|
343
|
-
setDefaultVariants({ intent: "neutral" });
|
|
344
|
-
}
|
|
345
|
-
|
|
346
342
|
if (variants.pressed && variants.intent === "brand") {
|
|
347
343
|
addClass("toolbar-button-brand-pressed");
|
|
348
344
|
addStyle({ transform: "translateY(1px)" });
|
|
@@ -353,9 +349,24 @@ const toolbarButton = cv({
|
|
|
353
349
|
});
|
|
354
350
|
```
|
|
355
351
|
|
|
356
|
-
|
|
352
|
+
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:
|
|
353
|
+
|
|
354
|
+
```ts
|
|
355
|
+
const transform = (value: string) => value.toUpperCase();
|
|
356
|
+
|
|
357
|
+
const input = cv({
|
|
358
|
+
variants: {
|
|
359
|
+
transform: (fn: (value: string) => string) => fn("example"),
|
|
360
|
+
},
|
|
361
|
+
defaultVariants: {
|
|
362
|
+
transform: () => transform,
|
|
363
|
+
},
|
|
364
|
+
});
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
`setVariants()` overrides explicit props. `addClass()` and `addStyle()` append output without changing resolved variant values. `getVariants()` includes values changed by computed `defaultVariants` and `setVariants()`.
|
|
357
368
|
|
|
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.
|
|
369
|
+
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
370
|
|
|
360
371
|
## Splitting Props
|
|
361
372
|
|
|
@@ -539,7 +550,7 @@ The package also exports `ClassValue`, `StyleValue`, `StyleClassProps`, `StyleCl
|
|
|
539
550
|
|
|
540
551
|
`component.style(props?)` returns only the resolved style value for that component mode.
|
|
541
552
|
|
|
542
|
-
`component.getVariants(props?)` returns resolved variant values after static defaults, inherited defaults, and `refine` updates.
|
|
553
|
+
`component.getVariants(props?)` returns resolved variant values after static defaults, inherited defaults, computed defaults, and `refine` updates.
|
|
543
554
|
|
|
544
555
|
`component.propKeys` lists style props plus variant props for that component mode.
|
|
545
556
|
|
package/dist/index.d.ts
CHANGED
|
@@ -76,7 +76,15 @@ type StringToBoolean<T> = T extends "true" | "false" ? boolean : T;
|
|
|
76
76
|
type VariantValue = ClassValue | StyleClassValue;
|
|
77
77
|
type NonNullKeys<T> = { [K in keyof T]: T[K] extends null ? never : K }[keyof T];
|
|
78
78
|
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]> };
|
|
79
|
+
type VariantValues<V> = { [K in keyof V]?: ExtractVariantValue<V[K]> | undefined };
|
|
80
|
+
interface DefaultVariantContext<V, K extends keyof V> {
|
|
81
|
+
defaultValue: ExtractVariantValue<V[K]> | undefined;
|
|
82
|
+
variants: Readonly<VariantValues<V>>;
|
|
83
|
+
}
|
|
84
|
+
type ComputedDefaultVariant<V, K extends keyof V> = (context: DefaultVariantContext<V, K>) => ExtractVariantValue<V[K]> | undefined;
|
|
85
|
+
type NonFunctionVariantValue<T> = Exclude<T, (...args: any[]) => any>;
|
|
86
|
+
type DefaultVariantValue<V, K extends keyof V> = [NonFunctionVariantValue<ExtractVariantValue<V[K]>>] extends [never] ? ComputedDefaultVariant<V, K> : NonFunctionVariantValue<ExtractVariantValue<V[K]>> | ComputedDefaultVariant<V, K>;
|
|
87
|
+
type DefaultVariants<V> = { [K in keyof V]?: DefaultVariantValue<V, K> | undefined };
|
|
80
88
|
type StyleValue = CSS.Properties & {
|
|
81
89
|
[key: `--${string}`]: string;
|
|
82
90
|
};
|
|
@@ -87,7 +95,6 @@ interface StyleClassValue {
|
|
|
87
95
|
interface RefineContext<V> {
|
|
88
96
|
variants: VariantValues<V>;
|
|
89
97
|
setVariants: (variants: VariantValues<V>) => void;
|
|
90
|
-
setDefaultVariants: (variants: VariantValues<V>) => void;
|
|
91
98
|
addClass: (className: ClassValue) => void;
|
|
92
99
|
addStyle: (style: StyleValue) => void;
|
|
93
100
|
}
|
|
@@ -106,7 +113,7 @@ interface CVConfig<V extends Variants = {}, E extends AnyComponent[] = []> {
|
|
|
106
113
|
class?: ClassValue;
|
|
107
114
|
style?: StyleValue;
|
|
108
115
|
variants?: ExtendableVariants<V, E>;
|
|
109
|
-
defaultVariants?:
|
|
116
|
+
defaultVariants?: DefaultVariants<MergeVariants<V, E>>;
|
|
110
117
|
refine?: Refine<MergeVariants<V, E>>;
|
|
111
118
|
}
|
|
112
119
|
interface CreateParams {
|