clava 0.3.0 → 0.4.1

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 CHANGED
@@ -1,5 +1,91 @@
1
1
  # clava
2
2
 
3
+ ## 0.4.1
4
+
5
+ ### Improved refine iteration warning with debugging context
6
+
7
+ When the [`refine`](https://clava.style/docs/reference/refine) iteration cap is hit, the development warning now lists the variant keys that did not stabilize and includes the stack trace of the [`cv`](https://clava.style/docs/reference/cv) call that defined the component. This makes it easier to find the offending component without searching through the codebase.
8
+
9
+ ```txt
10
+ Clava: Maximum refine iterations exceeded. This can happen when a refine callback calls setVariants or setDefaultVariants, but one of the variants changes on every run.
11
+ Variant(s) that did not stabilize: size.
12
+ Component created at:
13
+ at toolbarButton (src/components/toolbar.ts:42:18)
14
+ ...
15
+ ```
16
+
17
+ The frame is captured at component creation but formatted lazily, so component creation stays cheap unless the warning actually fires, and the capture is skipped entirely for components that cannot enter the refine loop. The whole warning is wrapped in a `process.env.NODE_ENV !== "production"` guard, so bundlers that inline that constant strip the warning machinery from production builds.
18
+
19
+ ### Other updates
20
+
21
+ - Removed refine warning-only code from production bundles when bundlers statically replace `process.env.NODE_ENV`.
22
+
23
+ ## 0.4.0
24
+
25
+ ### Removed `computedVariants` in favor of function values in `variants`
26
+
27
+ **BREAKING** if you're using the `computedVariants` config option.
28
+
29
+ Define a function directly inside [`variants`](https://clava.style/docs/reference/cv#variants) — it now acts as a function variant. The function's parameter type defines the prop type and replaces any inherited variant for the same key.
30
+
31
+ Before:
32
+
33
+ ```ts
34
+ const grid = cv({
35
+ variants: {
36
+ color: { red: "text-red", blue: "text-blue" },
37
+ },
38
+ computedVariants: {
39
+ columns: (value: number) => `grid-cols-${value}`,
40
+ },
41
+ });
42
+ ```
43
+
44
+ After:
45
+
46
+ ```ts
47
+ const grid = cv({
48
+ variants: {
49
+ color: { red: "text-red", blue: "text-blue" },
50
+ columns: (value: number) => `grid-cols-${value}`,
51
+ },
52
+ });
53
+ ```
54
+
55
+ ### Renamed `computed` to `refine`
56
+
57
+ **BREAKING** if you use the `computed` config field on [`cv`](https://clava.style/docs/reference/cv).
58
+
59
+ The `computed` field previously collided with `computedVariants`, even though the two have very different semantics: `computedVariants` is a map of pure per-variant transformer functions, while `computed` is a single imperative callback that can mutate variants, set defaults, and emit class/style output across re-runs until variants stabilize. The new name `refine` describes that iterative refinement and removes the collision.
60
+
61
+ Rename the `computed` field to `refine`. The callback signature and context (`variants`, `setVariants`, `setDefaultVariants`, `addClass`, `addStyle`) are unchanged.
62
+
63
+ Before:
64
+
65
+ ```ts
66
+ const button = cv({
67
+ variants: { size: { sm: "sm", lg: "lg" } },
68
+ computed: ({ variants, addClass }) => {
69
+ if (variants.size === "lg") {
70
+ addClass("is-large");
71
+ }
72
+ },
73
+ });
74
+ ```
75
+
76
+ After:
77
+
78
+ ```ts
79
+ const button = cv({
80
+ variants: { size: { sm: "sm", lg: "lg" } },
81
+ refine: ({ variants, addClass }) => {
82
+ if (variants.size === "lg") {
83
+ addClass("is-large");
84
+ }
85
+ },
86
+ });
87
+ ```
88
+
3
89
  ## 0.3.0
4
90
 
5
91
  ### Removed `keys`
package/README.md CHANGED
@@ -16,9 +16,9 @@ import type { Variant, VariantProps } from "clava";
16
16
  - [Output Modes](#output-modes)
17
17
  - [Classes And Styles](#classes-and-styles)
18
18
  - [Variants](#variants)
19
+ - [Function Variants](#function-variants)
19
20
  - [Extending Components](#extending-components)
20
- - [Computed Variants](#computed-variants)
21
- - [Computed Logic](#computed-logic)
21
+ - [Refine](#refine)
22
22
  - [Splitting Props](#splitting-props)
23
23
  - [React](#react)
24
24
  - [Solid](#solid)
@@ -78,7 +78,7 @@ button({ size: "lg", disabled: true, fluid: true, className: "mt-2" });
78
78
  // }
79
79
  ```
80
80
 
81
- Variant prop types are inferred from the `variants`, `computedVariants`, `defaultVariants`, and `extend` configuration. Invalid variant keys and values are TypeScript errors and are ignored at runtime.
81
+ Variant prop types are inferred from the `variants`, `defaultVariants`, and `extend` configuration. Invalid variant keys and values are TypeScript errors and are ignored at runtime.
82
82
 
83
83
  Input props may use `class` or `className` in any output mode. Both are appended to the generated class string.
84
84
 
@@ -152,7 +152,7 @@ const card = cv({
152
152
  });
153
153
  ```
154
154
 
155
- Variant and computed style results must use an explicit `{ style }` wrapper. A raw object like `{ backgroundColor: "red" }` is not a style result by itself.
155
+ Style results from `variants` and `refine` must use an explicit `{ style }` wrapper. A raw object like `{ backgroundColor: "red" }` is not a style result by itself.
156
156
 
157
157
  ```ts
158
158
  const chip = cv({
@@ -226,7 +226,34 @@ item({ active: true, interactive: true }).class;
226
226
  // "item-active item-interactive focus-visible:ring"
227
227
  ```
228
228
 
229
- Variant values can be class values, arrays, or `{ class, style }` objects. Use `null` in an extending component to disable inherited variants or inherited variant values.
229
+ Variant values can be class values, arrays, `{ class, style }` objects, or [functions](#function-variants). Use `null` in an extending component to disable inherited variants or inherited variant values.
230
+
231
+ ## Function Variants
232
+
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
+
235
+ ```ts
236
+ const grid = cv({
237
+ class: "grid",
238
+ variants: {
239
+ columns: (value: number) => ({
240
+ class: `grid-cols-${value}`,
241
+ style: { "--grid-columns": `${value}` },
242
+ }),
243
+ color: (value: string | null) => {
244
+ return value ? `text-${value}` : "text-current";
245
+ },
246
+ },
247
+ });
248
+
249
+ grid({ columns: 3, color: null });
250
+ // {
251
+ // class: "grid grid-cols-3 text-current",
252
+ // style: { "--grid-columns": "3" },
253
+ // }
254
+ ```
255
+
256
+ Function variants can return any class value, `{ class, style }`, a default Clava component result, `null`, or `undefined`. A function variant with the same key as an extended variant replaces that inherited variant's prop type and output.
230
257
 
231
258
  ## Extending Components
232
259
 
@@ -287,36 +314,9 @@ const plainButton = cv({
287
314
 
288
315
  You can extend any component mode, including `baseButton.jsx`, `baseButton.html`, and `baseButton.htmlObj`.
289
316
 
290
- ## Computed Variants
291
-
292
- Use `computedVariants` when a prop value should generate class/style output dynamically. The function parameter defines the prop type.
293
-
294
- ```ts
295
- const grid = cv({
296
- class: "grid",
297
- computedVariants: {
298
- columns: (value: number) => ({
299
- class: `grid-cols-${value}`,
300
- style: { "--grid-columns": `${value}` },
301
- }),
302
- color: (value: string | null) => {
303
- return value ? `text-${value}` : "text-current";
304
- },
305
- },
306
- });
307
-
308
- grid({ columns: 3, color: null });
309
- // {
310
- // class: "grid grid-cols-3 text-current",
311
- // style: { "--grid-columns": "3" },
312
- // }
313
- ```
314
-
315
- Computed variants can return any class value, `{ class, style }`, a default Clava component result, `null`, or `undefined`. A `computedVariants` entry with the same key as an extended variant replaces that inherited variant's prop type and output.
316
-
317
- ## Computed Logic
317
+ ## Refine
318
318
 
319
- Use `computed` for compound conditions, dependent defaults, and final class/style adjustments. It receives the resolved variant values for the component and can return class/style output.
319
+ Use `refine` for compound conditions, dependent defaults, and final class/style adjustments. It receives the resolved variant values for the component and can return class/style output.
320
320
 
321
321
  ```ts
322
322
  const toolbarButton = cv({
@@ -328,7 +328,7 @@ const toolbarButton = cv({
328
328
  },
329
329
  loading: "toolbar-button-loading",
330
330
  },
331
- computed: ({
331
+ refine: ({
332
332
  variants,
333
333
  setVariants,
334
334
  setDefaultVariants,
@@ -355,7 +355,7 @@ const toolbarButton = cv({
355
355
 
356
356
  `setVariants()` overrides explicit props. `setDefaultVariants()` overrides static `defaultVariants` and inherited defaults, but it does not override a prop the user explicitly passed unless that prop value is `undefined`. `addClass()` and `addStyle()` append output without changing resolved variant values. `getVariants()` includes values changed by `setVariants()` and `setDefaultVariants()`.
357
357
 
358
- When a computed callback changes variants, Clava re-runs the computed 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.
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.
359
359
 
360
360
  ## Splitting Props
361
361
 
@@ -525,7 +525,7 @@ The package also exports `ClassValue`, `StyleValue`, `StyleClassProps`, `StyleCl
525
525
 
526
526
  ## API Summary
527
527
 
528
- `cv(config?)` creates a typed Clava component. Supported config keys are `extend`, `class`, `style`, `variants`, `computedVariants`, `defaultVariants`, and `computed`.
528
+ `cv(config?)` creates a typed Clava component. Supported config keys are `extend`, `class`, `style`, `variants`, `defaultVariants`, and `refine`.
529
529
 
530
530
  `component(props?)` returns `{ class, style }` with normalized camelCase style keys.
531
531
 
@@ -539,7 +539,7 @@ The package also exports `ClassValue`, `StyleValue`, `StyleClassProps`, `StyleCl
539
539
 
540
540
  `component.style(props?)` returns only the resolved style value for that component mode.
541
541
 
542
- `component.getVariants(props?)` returns resolved variant values after static defaults, inherited defaults, and computed variant updates.
542
+ `component.getVariants(props?)` returns resolved variant values after static defaults, inherited defaults, and `refine` updates.
543
543
 
544
544
  `component.propKeys` lists style props plus variant props for that component mode.
545
545
 
package/dist/index.d.ts CHANGED
@@ -30,11 +30,11 @@ type ComponentProps<V = {}> = VariantValues<V> & NullableComponentResult;
30
30
  type GetVariants<V> = (variants?: VariantValues<V>) => VariantValues<V>;
31
31
  type ComponentPropKey<R extends ComponentResult> = keyof R | (R extends StyleClassProps ? "className" : never);
32
32
  type KeySourceArray = readonly string[];
33
- type KeySourceComponent = {
33
+ interface KeySourceComponent {
34
34
  propKeys: readonly string[];
35
35
  variantKeys: readonly string[];
36
36
  getVariants: () => Record<string, unknown>;
37
- };
37
+ }
38
38
  type KeySource = KeySourceArray | KeySourceComponent;
39
39
  type IsComponent<S> = S extends {
40
40
  getVariants: () => unknown;
@@ -61,21 +61,17 @@ interface ModalComponent<V, R extends ComponentResult> {
61
61
  variantKeys: (keyof V)[];
62
62
  propKeys: (keyof V | ComponentPropKey<R>)[];
63
63
  }
64
- interface CVComponent<V extends Variants = {}, CV extends ComputedVariants = {}, E extends AnyComponent[] = [], R extends ComponentResult = StyleClassProps> extends ModalComponent<MergeVariants<V, CV, E>, R> {
65
- jsx: ModalComponent<MergeVariants<V, CV, E>, JSXProps>;
66
- html: ModalComponent<MergeVariants<V, CV, E>, HTMLProps>;
67
- htmlObj: ModalComponent<MergeVariants<V, CV, E>, HTMLObjProps>;
64
+ interface CVComponent<V extends Variants = {}, E extends AnyComponent[] = [], R extends ComponentResult = StyleClassProps> extends ModalComponent<MergeVariants<V, E>, R> {
65
+ jsx: ModalComponent<MergeVariants<V, E>, JSXProps>;
66
+ html: ModalComponent<MergeVariants<V, E>, HTMLProps>;
67
+ htmlObj: ModalComponent<MergeVariants<V, E>, HTMLObjProps>;
68
68
  }
69
- type AnyComponent = CVComponent<any, any, any, any> | ModalComponent<any, any>;
69
+ type AnyComponent = CVComponent<any, any, any> | ModalComponent<any, any>;
70
70
  type MergeExtendedVariants<T> = T extends readonly [infer First, ...infer Rest] ? ExtractVariants<First> & MergeExtendedVariants<Rest> : {};
71
- type MergeExtendedComputedVariants<T> = T extends readonly [infer First, ...infer Rest] ? ExtractComputedVariants<First> & MergeExtendedComputedVariants<Rest> : {};
72
- type ExtractVariants<T> = T extends CVComponent<infer V, any, infer E, any> ? V & MergeExtendedVariants<E> : {};
73
- type ExtractComputedVariants<T> = T extends CVComponent<any, infer CV, infer E, any> ? CV & Omit<MergeExtendedComputedVariants<E>, keyof CV> : {};
74
- type MergeVariantDefinition<Child, Parent> = Child extends Record<string, any> ? Parent extends Record<string, any> ? Omit<Parent, keyof Child> & Child : Child : Child;
71
+ type ExtractVariants<T> = T extends CVComponent<infer V, infer E, any> ? MergeVariantMaps<V, MergeExtendedVariants<E>> : {};
72
+ type MergeVariantDefinition<Child, Parent> = Child extends ((...args: any[]) => any) ? Child : Parent extends ((...args: any[]) => any) ? Child : Child extends Record<string, any> ? Parent extends Record<string, any> ? Omit<Parent, keyof Child> & Child : Child : Child;
75
73
  type MergeVariantMaps<Child, Parent> = Omit<Parent, keyof Child> & Child & { [K in keyof Child & keyof Parent]: MergeVariantDefinition<Child[K], Parent[K]> };
76
- type MergeExtendedAllVariants<E extends AnyComponent[]> = MergeExtendedVariants<E> & MergeExtendedComputedVariants<E>;
77
- type MergeBaseVariants<V, E extends AnyComponent[]> = MergeVariantMaps<NoInfer<V>, MergeExtendedAllVariants<E>>;
78
- type MergeVariants<V, CV, E extends AnyComponent[]> = NoInfer<CV> & Omit<MergeBaseVariants<V, E>, keyof CV>;
74
+ type MergeVariants<V, E extends AnyComponent[]> = MergeVariantMaps<NoInfer<V>, MergeExtendedVariants<E>>;
79
75
  type StringToBoolean<T> = T extends "true" | "false" ? boolean : T;
80
76
  type VariantValue = ClassValue | StyleClassValue;
81
77
  type NonNullKeys<T> = { [K in keyof T]: T[K] extends null ? never : K }[keyof T];
@@ -88,34 +84,30 @@ interface StyleClassValue {
88
84
  style?: StyleValue;
89
85
  class?: ClassValue;
90
86
  }
91
- interface ComputedContext<V> {
87
+ interface RefineContext<V> {
92
88
  variants: VariantValues<V>;
93
89
  setVariants: (variants: VariantValues<V>) => void;
94
90
  setDefaultVariants: (variants: VariantValues<V>) => void;
95
91
  addClass: (className: ClassValue) => void;
96
92
  addStyle: (style: StyleValue) => void;
97
93
  }
98
- type Computed<V> = (context: ComputedContext<V>) => VariantValue;
99
- type ComputedVariant = (value: any) => VariantValue;
100
- type ComputedVariants = Record<string, ComputedVariant>;
101
- type Variant$1 = ClassValue | Record<string, VariantValue>;
94
+ type Refine<V> = (context: RefineContext<V>) => VariantValue;
95
+ type Variant$1 = ClassValue | Record<string, VariantValue> | ((value: any) => VariantValue);
102
96
  type Variants = Record<string, Variant$1>;
103
- type ExtendedVariants<E extends AnyComponent[]> = MergeExtendedVariants<E> & MergeExtendedComputedVariants<E>;
104
97
  type NullablePartial<T> = T extends Record<string, any> ? { [K in keyof T]?: T[K] | null } : T | null;
105
- type ExtendableVariants<V extends Variants, E extends AnyComponent[]> = V & { [K in keyof ExtendedVariants<E>]?: NullablePartial<ExtendedVariants<E>[K]> | Variant$1 };
98
+ type ExtendableVariants<V extends Variants, E extends AnyComponent[]> = V & { [K in keyof MergeExtendedVariants<E>]?: NullablePartial<MergeExtendedVariants<E>[K]> | Variant$1 };
106
99
  //#endregion
107
100
  //#region src/index.d.ts
108
101
  type VariantProps<T extends Pick<AnyComponent, "getVariants">> = ReturnType<T["getVariants"]>;
109
102
  type VariantKey<T> = T extends boolean ? "true" | "false" : Extract<T, string>;
110
103
  type Variant<T extends Pick<AnyComponent, "getVariants">, K extends keyof VariantProps<T>> = Record<VariantKey<NonNullable<VariantProps<T>[K]>>, ClassValue | StyleClassValue>;
111
- interface CVConfig<V extends Variants = {}, CV extends ComputedVariants = {}, E extends AnyComponent[] = []> {
104
+ interface CVConfig<V extends Variants = {}, E extends AnyComponent[] = []> {
112
105
  extend?: E;
113
106
  class?: ClassValue;
114
107
  style?: StyleValue;
115
108
  variants?: ExtendableVariants<V, E>;
116
- computedVariants?: CV;
117
- defaultVariants?: VariantValues<MergeVariants<V, CV, E>>;
118
- computed?: Computed<MergeVariants<V, CV, E>>;
109
+ defaultVariants?: VariantValues<MergeVariants<V, E>>;
110
+ refine?: Refine<MergeVariants<V, E>>;
119
111
  }
120
112
  interface CreateParams {
121
113
  transformClass?: (className: string) => string;
@@ -135,10 +127,10 @@ declare const splitProps: SplitPropsFunction;
135
127
  declare function create({
136
128
  transformClass
137
129
  }?: CreateParams): {
138
- cv: <V extends Variants = {}, CV extends ComputedVariants = {}, const E extends AnyComponent[] = []>(config?: CVConfig<V, CV, E>) => CVComponent<V, CV, E>;
130
+ cv: <V extends Variants = {}, const E extends AnyComponent[] = []>(config?: CVConfig<V, E>) => CVComponent<V, E>;
139
131
  cx: (...classes: ClassValue$1[]) => string;
140
132
  };
141
- declare const cv: <V extends Variants = {}, CV extends ComputedVariants = {}, const E extends AnyComponent[] = []>(config?: CVConfig<V, CV, E>) => CVComponent<V, CV, E>, cx: (...classes: ClassValue$1[]) => string;
133
+ declare const cv: <V extends Variants = {}, const E extends AnyComponent[] = []>(config?: CVConfig<V, E>) => CVComponent<V, E>, cx: (...classes: ClassValue$1[]) => string;
142
134
  //#endregion
143
135
  export { type CVComponent, CVConfig, type ClassValue, type HTMLObjProps, type HTMLProps, type JSXProps, type StyleClassProps, type StyleClassValue, type StyleValue, Variant, VariantProps, create, cv, cx, splitProps };
144
136
  //# sourceMappingURL=index.d.ts.map