clava 0.3.0 → 0.4.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/src/index.ts CHANGED
@@ -5,14 +5,13 @@ import type {
5
5
  ClassValue,
6
6
  ComponentProps,
7
7
  ComponentResult,
8
- Computed,
9
- ComputedVariants,
10
8
  ExtendableVariants,
11
9
  HTMLObjProps,
12
10
  HTMLProps,
13
11
  JSXProps,
14
12
  MergeVariants,
15
13
  ModalComponent,
14
+ Refine,
16
15
  SplitPropsFunction,
17
16
  StyleClassProps,
18
17
  StyleClassValue,
@@ -42,23 +41,23 @@ type ComputeFn = (
42
41
  skipValues: Record<string, Set<string>> | null,
43
42
  classesOut: ClsxClassValue[],
44
43
  styleOut: StyleValue,
45
- runState?: ComputedRunState,
44
+ runState?: RefineRunState,
46
45
  protectedVariants?: Record<string, unknown> | null,
47
46
  pendingProtectedVariants?: Record<string, unknown> | null,
48
47
  protectedVariantKeys?: Set<string> | null,
49
48
  ) => Record<string, unknown>;
50
49
 
51
- type ResolveComputedFn = (
50
+ type ResolveRefineFn = (
52
51
  resolved: Record<string, unknown>,
53
52
  userVariantProps: Record<string, unknown>,
54
53
  filterOwnVariants?: boolean,
55
- runState?: ComputedRunState,
54
+ runState?: RefineRunState,
56
55
  protectedVariants?: Record<string, unknown> | null,
57
56
  pendingProtectedVariants?: Record<string, unknown> | null,
58
57
  protectedVariantKeys?: Set<string> | null,
59
58
  ) => Record<string, unknown>;
60
59
 
61
- interface ComputedRunState {
60
+ interface RefineRunState {
62
61
  remaining: number;
63
62
  warned: boolean;
64
63
  }
@@ -67,8 +66,8 @@ interface ComputedRunState {
67
66
  interface ComponentMeta {
68
67
  baseClass: string;
69
68
  staticDefaults: Record<string, unknown>;
70
- // Returns variants set via setDefaultVariants in the computed function chain.
71
- // null when this component has no resolveDefaults work to do (no `computed`
69
+ // Returns variants set via setDefaultVariants in the refine function chain.
70
+ // null when this component has no resolveDefaults work to do (no `refine`
72
71
  // and no extends with work).
73
72
  resolveDefaults:
74
73
  | ((
@@ -79,12 +78,18 @@ interface ComponentMeta {
79
78
  // Returns variant classes + style for this component, used by extending
80
79
  // components. Top-level rendering also routes through this.
81
80
  compute: ComputeFn;
82
- resolveComputed: ResolveComputedFn | null;
81
+ resolveRefine: ResolveRefineFn | null;
83
82
  // Reference identity is used to detect mixed-factory `extend`. When a
84
83
  // component is extended by a parent from a different `create()` call, the
85
84
  // parent applies this transform to the extend's contribution before joining,
86
85
  // preserving each factory's transform boundary.
87
86
  transformClass: (className: string) => string;
87
+ // Variant keys whose effective definition in this component's chain is a
88
+ // function. An extending component that supplies a non-function variant for
89
+ // the same key uses this to tell us to skip that key (matching the
90
+ // type-level "function variant is replaced by anything in the child" rule).
91
+ // Empty when no key in this chain is a function variant.
92
+ functionVariantKeys: Set<string>;
88
93
  }
89
94
 
90
95
  const META_KEY = "__meta";
@@ -94,7 +99,7 @@ const EMPTY_DEFAULTS: Record<string, unknown> = Object.freeze({}) as Record<
94
99
  unknown
95
100
  >;
96
101
 
97
- const MAX_COMPUTED_RUNS = 50;
102
+ const MAX_REFINE_RUNS = 50;
98
103
 
99
104
  function areVariantsEqual(
100
105
  a: Record<string, unknown>,
@@ -111,14 +116,14 @@ function areVariantsEqual(
111
116
  return true;
112
117
  }
113
118
 
114
- function warnComputedLimit(runState: ComputedRunState): void {
119
+ function warnRefineLimit(runState: RefineRunState): void {
115
120
  if (runState.warned) return;
116
121
  runState.warned = true;
117
122
  if (process.env.NODE_ENV !== "production") {
118
123
  console.warn(
119
- "Clava: Maximum computed update iterations exceeded. This can happen " +
120
- "when a computed callback calls setVariants or setDefaultVariants, " +
121
- "but one of the variants changes on every run.",
124
+ "Clava: Maximum refine iterations exceeded. This can happen when a " +
125
+ "refine callback calls setVariants or setDefaultVariants, but one " +
126
+ "of the variants changes on every run.",
122
127
  );
123
128
  }
124
129
  }
@@ -202,16 +207,14 @@ export type Variant<
202
207
 
203
208
  export interface CVConfig<
204
209
  V extends Variants = {},
205
- CV extends ComputedVariants = {},
206
210
  E extends AnyComponent[] = [],
207
211
  > {
208
212
  extend?: E;
209
213
  class?: ClassValue;
210
214
  style?: StyleValue;
211
215
  variants?: ExtendableVariants<V, E>;
212
- computedVariants?: CV;
213
- defaultVariants?: VariantValues<MergeVariants<V, CV, E>>;
214
- computed?: Computed<MergeVariants<V, CV, E>>;
216
+ defaultVariants?: VariantValues<MergeVariants<V, E>>;
217
+ refine?: Refine<MergeVariants<V, E>>;
215
218
  }
216
219
 
217
220
  interface CreateParams {
@@ -282,7 +285,7 @@ function extractClassAndStylePrebuilt(value: unknown): PrebuiltValue {
282
285
  * components.
283
286
  */
284
287
  function collectVariantKeys(
285
- config: CVConfig<Variants, ComputedVariants, AnyComponent[]>,
288
+ config: CVConfig<Variants, AnyComponent[]>,
286
289
  ): string[] {
287
290
  const keys = new Set<string>();
288
291
 
@@ -307,18 +310,11 @@ function collectVariantKeys(
307
310
  }
308
311
  }
309
312
 
310
- if (config.computedVariants) {
311
- for (const key in config.computedVariants) {
312
- if (!Object.hasOwn(config.computedVariants, key)) continue;
313
- keys.add(key);
314
- }
315
- }
316
-
317
313
  return Array.from(keys);
318
314
  }
319
315
 
320
316
  function isVariantDisabled(
321
- config: CVConfig<Variants, ComputedVariants, AnyComponent[]>,
317
+ config: CVConfig<Variants, AnyComponent[]>,
322
318
  key: string,
323
319
  ): boolean {
324
320
  return config.variants?.[key] === null;
@@ -332,7 +328,7 @@ function getVariantValueKey(value: unknown): string | undefined {
332
328
  }
333
329
 
334
330
  function isVariantValueDisabled(
335
- config: CVConfig<Variants, ComputedVariants, AnyComponent[]>,
331
+ config: CVConfig<Variants, AnyComponent[]>,
336
332
  key: string,
337
333
  value: unknown,
338
334
  ): boolean {
@@ -344,7 +340,7 @@ function isVariantValueDisabled(
344
340
  }
345
341
 
346
342
  function collectDisabledVariantKeys(
347
- config: CVConfig<Variants, ComputedVariants, AnyComponent[]>,
343
+ config: CVConfig<Variants, AnyComponent[]>,
348
344
  ): Set<string> {
349
345
  const keys = new Set<string>();
350
346
  if (!config.variants) return keys;
@@ -358,7 +354,7 @@ function collectDisabledVariantKeys(
358
354
  }
359
355
 
360
356
  function collectDisabledVariantValues(
361
- config: CVConfig<Variants, ComputedVariants, AnyComponent[]>,
357
+ config: CVConfig<Variants, AnyComponent[]>,
362
358
  ): Record<string, Set<string>> {
363
359
  const values: Record<string, Set<string>> = {};
364
360
  if (!config.variants) return values;
@@ -565,14 +561,10 @@ export function create({
565
561
  }: CreateParams = {}) {
566
562
  const cx = (...classes: ClsxClassValue[]) => transformClass(clsx(...classes));
567
563
 
568
- const cv = <
569
- V extends Variants = {},
570
- CV extends ComputedVariants = {},
571
- const E extends AnyComponent[] = [],
572
- >(
573
- config: CVConfig<V, CV, E> = {},
574
- ): CVComponent<V, CV, E> => {
575
- type MergedVariants = MergeVariants<V, CV, E>;
564
+ const cv = <V extends Variants = {}, const E extends AnyComponent[] = []>(
565
+ config: CVConfig<V, E> = {},
566
+ ): CVComponent<V, E> => {
567
+ type MergedVariants = MergeVariants<V, E>;
576
568
 
577
569
  // ----- Pre-computed at creation time -----
578
570
  const variantKeys = collectVariantKeys(config);
@@ -589,41 +581,34 @@ export function create({
589
581
  const extend = config.extend;
590
582
  const hasExtend = !!extend && extend.length > 0;
591
583
  const variants = config.variants;
592
- const computedVariantsCfg = config.computedVariants;
593
- const computed = config.computed;
584
+ const refine = config.refine;
594
585
  const baseStyle = config.style;
595
586
  const hasBaseStyle = !!baseStyle;
596
587
 
597
- // Pre-build variant entries for fast iteration. For each variant key in
598
- // `variants`, we have a name and a PrebuiltVariant with normalized values.
588
+ // Split `variants` entries into static entries (object/shorthand) and
589
+ // function-variant entries. Static entries are pre-built into
590
+ // PrebuiltVariant for fast iteration. Function-variant entries override
591
+ // any same-key inherited variant (see `staticExtSkipKeys`).
599
592
  const variantEntryNames: string[] = [];
600
593
  const variantEntryDefs: PrebuiltVariant[] = [];
594
+ const functionVariantNames: string[] = [];
595
+ const functionVariantFns: Array<(value: unknown) => unknown> = [];
601
596
  if (variants) {
602
597
  for (const name in variants) {
603
598
  if (!Object.hasOwn(variants, name)) continue;
604
599
  const variant = (variants as Record<string, unknown>)[name];
605
600
  if (variant === null) continue;
601
+ if (typeof variant === "function") {
602
+ functionVariantNames.push(name);
603
+ functionVariantFns.push(variant as (value: unknown) => unknown);
604
+ continue;
605
+ }
606
606
  variantEntryNames.push(name);
607
607
  variantEntryDefs.push(buildPrebuiltVariant(variant));
608
608
  }
609
609
  }
610
610
  const variantEntryCount = variantEntryNames.length;
611
-
612
- // Pre-built computed-variants entries.
613
- const computedVariantNames: string[] = [];
614
- const computedVariantFns: Array<(value: unknown) => unknown> = [];
615
- if (computedVariantsCfg) {
616
- for (const name in computedVariantsCfg) {
617
- if (!Object.hasOwn(computedVariantsCfg, name)) continue;
618
- computedVariantNames.push(name);
619
- computedVariantFns.push(
620
- (computedVariantsCfg as Record<string, (value: unknown) => unknown>)[
621
- name
622
- ] as (value: unknown) => unknown,
623
- );
624
- }
625
- }
626
- const computedVariantCount = computedVariantNames.length;
611
+ const functionVariantCount = functionVariantNames.length;
627
612
 
628
613
  // Pre-compute static defaults. Includes:
629
614
  // - extended components' static defaults
@@ -702,28 +687,80 @@ export function create({
702
687
  }
703
688
  const extCount = extMetas.length;
704
689
 
705
- // Filter to only extends with computed work in their chain. `resolveDefaults`
706
- // and `resolveComputed` are populated from the same transitive condition,
690
+ // Filter to only extends with refine work in their chain. `resolveDefaults`
691
+ // and `resolveRefine` are populated from the same transitive condition,
707
692
  // so one bucket is enough for both resolver paths.
708
- const extMetasWithComputed: ComponentMeta[] = [];
693
+ const extMetasWithRefine: ComponentMeta[] = [];
709
694
  for (let i = 0; i < extCount; i++) {
710
695
  const meta = extMetas[i];
711
696
  if (meta.resolveDefaults) {
712
- extMetasWithComputed.push(meta);
697
+ extMetasWithRefine.push(meta);
698
+ }
699
+ }
700
+ const extMetasWithRefineCount = extMetasWithRefine.length;
701
+ const shouldCollectChangedVariants = extMetasWithRefineCount > 0;
702
+
703
+ // Function variant keys inherited from extends, filtered through this
704
+ // component's own variants: a static (object/shorthand) variant in this
705
+ // component replaces an inherited function variant for the same key.
706
+ // The closure is exposed on `ComponentMeta` so any further extending
707
+ // component can detect "ancestor's effective variant for K is a function"
708
+ // and skip it when overriding K with a non-function.
709
+ const functionVariantKeys = new Set<string>();
710
+ for (let i = 0; i < extCount; i++) {
711
+ const fnKeys = extMetas[i].functionVariantKeys;
712
+ for (const k of fnKeys) {
713
+ if (disabledVariantKeys.has(k)) continue;
714
+ functionVariantKeys.add(k);
715
+ }
716
+ }
717
+ for (let i = 0; i < functionVariantCount; i++) {
718
+ functionVariantKeys.add(functionVariantNames[i]);
719
+ }
720
+ for (let i = 0; i < variantEntryCount; i++) {
721
+ // A static variant in this component replaces an inherited function
722
+ // variant for the same key; from this component onward, the key is no
723
+ // longer a function variant.
724
+ functionVariantKeys.delete(variantEntryNames[i]);
725
+ }
726
+
727
+ // Static-variant keys in this component that override an inherited
728
+ // function variant. Type-level merge says child fully replaces, so the
729
+ // ancestor's function must not run with the child's (object-typed) value.
730
+ let staticVariantsOverridingExtFn: string[] | null = null;
731
+ if (variantEntryCount > 0 && extCount > 0) {
732
+ for (let i = 0; i < variantEntryCount; i++) {
733
+ const name = variantEntryNames[i];
734
+ for (let j = 0; j < extCount; j++) {
735
+ if (extMetas[j].functionVariantKeys.has(name)) {
736
+ if (!staticVariantsOverridingExtFn) {
737
+ staticVariantsOverridingExtFn = [];
738
+ }
739
+ staticVariantsOverridingExtFn.push(name);
740
+ break;
741
+ }
742
+ }
713
743
  }
714
744
  }
715
- const extMetasWithComputedCount = extMetasWithComputed.length;
716
- const shouldCollectChangedVariants = extMetasWithComputedCount > 0;
717
745
 
718
746
  // Pre-compute static skip key/value sets to pass to extends. These never
719
747
  // change across calls — when caller passes no skip sets, we reuse the same
720
748
  // object and avoid Set allocation.
721
749
  let staticExtSkipKeys: Set<string> | null = null;
722
- if (hasDisabledVariantKeys || computedVariantCount > 0) {
750
+ if (
751
+ hasDisabledVariantKeys ||
752
+ functionVariantCount > 0 ||
753
+ staticVariantsOverridingExtFn !== null
754
+ ) {
723
755
  staticExtSkipKeys = new Set<string>();
724
756
  for (const k of disabledVariantKeys) staticExtSkipKeys.add(k);
725
- for (let i = 0; i < computedVariantCount; i++) {
726
- staticExtSkipKeys.add(computedVariantNames[i]);
757
+ for (let i = 0; i < functionVariantCount; i++) {
758
+ staticExtSkipKeys.add(functionVariantNames[i]);
759
+ }
760
+ if (staticVariantsOverridingExtFn) {
761
+ for (const k of staticVariantsOverridingExtFn) {
762
+ staticExtSkipKeys.add(k);
763
+ }
727
764
  }
728
765
  }
729
766
  // Skip values are passed directly to extends. We can reuse the same object
@@ -760,12 +797,12 @@ export function create({
760
797
 
761
798
  // Pre-create resolveDefaults function — used by parents during their
762
799
  // `resolveVariantsHot`. Returns the variants set via setDefaultVariants in
763
- // the computed function chain.
800
+ // the refine function chain.
764
801
  //
765
- // When this component has no `computed` and no `extend` with work, the
802
+ // When this component has no `refine` and no `extend` with work, the
766
803
  // function is null — callers can skip iterating it entirely.
767
804
  const resolveDefaultsFn: ComponentMeta["resolveDefaults"] =
768
- computed || extMetasWithComputedCount > 0
805
+ refine || extMetasWithRefineCount > 0
769
806
  ? (
770
807
  childDefaults: Record<string, unknown>,
771
808
  userProps: Record<string, unknown> = EMPTY_DEFAULTS,
@@ -787,21 +824,21 @@ export function create({
787
824
  resolvedVariants[key] = v;
788
825
  }
789
826
 
790
- const computedDefaults: Record<string, unknown> = {};
827
+ const refineDefaults: Record<string, unknown> = {};
791
828
 
792
- for (let i = 0; i < extMetasWithComputedCount; i++) {
793
- const extDefaults = extMetasWithComputed[i].resolveDefaults!(
829
+ for (let i = 0; i < extMetasWithRefineCount; i++) {
830
+ const extDefaults = extMetasWithRefine[i].resolveDefaults!(
794
831
  childDefaults,
795
832
  userProps,
796
833
  );
797
834
  for (const k in extDefaults) {
798
835
  if (!Object.hasOwn(extDefaults, k)) continue;
799
- computedDefaults[k] = extDefaults[k];
836
+ refineDefaults[k] = extDefaults[k];
800
837
  }
801
838
  }
802
839
 
803
- if (computed) {
804
- // Filter to own variant keys so `computed.ctx.variants` matches
840
+ if (refine) {
841
+ // Filter to own variant keys so `ctx.variants` matches
805
842
  // `VariantValues<V>` when this component is used as an extend by
806
843
  // a parent that adds extra variant keys (those keys would
807
844
  // otherwise leak through `userProps`).
@@ -812,7 +849,7 @@ export function create({
812
849
  ownVariants[k] = resolvedVariants[k];
813
850
  }
814
851
  }
815
- computed({
852
+ refine({
816
853
  variants: ownVariants as VariantValues<Record<string, unknown>>,
817
854
  setVariants: noop,
818
855
  setDefaultVariants: (newDefaults) => {
@@ -822,7 +859,7 @@ export function create({
822
859
  if (userProps[key] !== undefined) continue;
823
860
  if (isVariantDisabled(config, key)) continue;
824
861
  if (isVariantValueDisabled(config, key, value)) continue;
825
- computedDefaults[key] = value;
862
+ refineDefaults[key] = value;
826
863
  }
827
864
  },
828
865
  addClass: noop,
@@ -830,12 +867,12 @@ export function create({
830
867
  });
831
868
  }
832
869
 
833
- return computedDefaults;
870
+ return refineDefaults;
834
871
  }
835
872
  : null;
836
873
 
837
874
  // Hot path: resolve variants by merging static defaults + extends'
838
- // computed defaults + user-provided props.
875
+ // refine defaults + user-provided props.
839
876
  function resolveVariantsHot(
840
877
  propsVariants: Record<string, unknown>,
841
878
  ): Record<string, unknown> {
@@ -843,14 +880,14 @@ export function create({
843
880
  const defaults: Record<string, unknown> = {};
844
881
  Object.assign(defaults, staticDefaults);
845
882
 
846
- // Apply computed defaults from extended components (only those that have
883
+ // Apply refine defaults from extended components (only those that have
847
884
  // actual work to do).
848
- for (let i = 0; i < extMetasWithComputedCount; i++) {
849
- const meta = extMetasWithComputed[i];
850
- const extComputed = meta.resolveDefaults!(defaults, propsVariants);
851
- for (const k in extComputed) {
852
- if (!Object.hasOwn(extComputed, k)) continue;
853
- defaults[k] = extComputed[k];
885
+ for (let i = 0; i < extMetasWithRefineCount; i++) {
886
+ const meta = extMetasWithRefine[i];
887
+ const extDefaults = meta.resolveDefaults!(defaults, propsVariants);
888
+ for (const k in extDefaults) {
889
+ if (!Object.hasOwn(extDefaults, k)) continue;
890
+ defaults[k] = extDefaults[k];
854
891
  }
855
892
  }
856
893
 
@@ -872,7 +909,7 @@ export function create({
872
909
  return result;
873
910
  }
874
911
 
875
- const runComputedContext = (
912
+ const runRefineContext = (
876
913
  resolved: Record<string, unknown>,
877
914
  userVariantProps: Record<string, unknown>,
878
915
  filterOwnVariants: boolean,
@@ -891,12 +928,12 @@ export function create({
891
928
  let cStyle: StyleValue | null = null;
892
929
  let changedVariants: Record<string, unknown> | null = null;
893
930
 
894
- if (computed) {
931
+ if (refine) {
895
932
  let ownVariants = resolved;
896
933
  if (filterOwnVariants) {
897
934
  // When this component is being extended, `resolved` is the parent's
898
935
  // workingResolved (a superset of our variant keys). Filter to our own
899
- // keys for `ctx.variants` so the user's `computed` callback sees the
936
+ // keys for `ctx.variants` so the user's `refine` callback sees the
900
937
  // shape declared by `VariantValues<V>` and not foreign parent keys.
901
938
  const filteredVariants: Record<string, unknown> = {};
902
939
  for (let i = 0; i < variantKeysLength; i++) {
@@ -905,9 +942,9 @@ export function create({
905
942
  }
906
943
  ownVariants = filteredVariants;
907
944
  }
908
- // Lazy-init updatedVariants — many computeds only inspect `variants`
909
- // or call setDefaultVariants for keys the user already set, so the
910
- // copy is unnecessary in the common case.
945
+ // Lazy-init updatedVariants — many refine callbacks only inspect
946
+ // `variants` or call setDefaultVariants for keys the user already set,
947
+ // so the copy is unnecessary in the common case.
911
948
  let updatedVariants: Record<string, unknown> | null = null;
912
949
  const localCClasses: ClassValue[] | null = collectOutput ? [] : null;
913
950
  let localCStyle: StyleValue | null = null;
@@ -1003,7 +1040,7 @@ export function create({
1003
1040
  Object.assign(localCStyle, newStyle);
1004
1041
  },
1005
1042
  };
1006
- const result = computed(ctx);
1043
+ const result = refine(ctx);
1007
1044
  if (collectOutput && result != null) {
1008
1045
  const r = extractClassAndStylePrebuilt(result);
1009
1046
  if (r.class != null) localCClasses?.push(r.class);
@@ -1052,14 +1089,14 @@ export function create({
1052
1089
  pendingProtectedVariants,
1053
1090
  protectedVariantKeys,
1054
1091
  ) => {
1055
- // Run `computed` (if any). May modify resolved variants and emit classes
1092
+ // Run `refine` (if any). May modify resolved variants and emit classes
1056
1093
  // and styles.
1057
1094
  let workingResolved = resolved;
1058
1095
  let cClasses: ClassValue[] | null = null;
1059
1096
  let cStyle: StyleValue | null = null;
1060
1097
  let changedVariants: Record<string, unknown> | null = null;
1061
- if (computed) {
1062
- const computedResult = runComputedContext(
1098
+ if (refine) {
1099
+ const refineResult = runRefineContext(
1063
1100
  resolved,
1064
1101
  userVariantProps,
1065
1102
  true,
@@ -1068,20 +1105,20 @@ export function create({
1068
1105
  pendingProtectedVariants,
1069
1106
  protectedVariantKeys,
1070
1107
  );
1071
- workingResolved = computedResult.workingResolved;
1072
- cClasses = computedResult.classes;
1073
- cStyle = computedResult.style;
1074
- changedVariants = computedResult.changedVariants;
1108
+ workingResolved = refineResult.workingResolved;
1109
+ cClasses = refineResult.classes;
1110
+ cStyle = refineResult.style;
1111
+ changedVariants = refineResult.changedVariants;
1075
1112
  }
1076
1113
 
1077
1114
  // Run extends' contributions first (their full classes + styles) so our
1078
1115
  // own base style and variants apply on top, matching the original
1079
1116
  // ext1 → ext2 → … → current ordering.
1080
1117
  //
1081
- // Pass explicit user values plus computed changes as the extends'
1082
- // `userVariantProps`. This lets more-specific computed decisions stick
1118
+ // Pass explicit user values plus refine changes as the extends'
1119
+ // `userVariantProps`. This lets more-specific refine decisions stick
1083
1120
  // across re-runs while inherited static defaults can still be refined by
1084
- // the extended component's own computed chain.
1121
+ // the extended component's own refine chain.
1085
1122
  if (hasExtend) {
1086
1123
  // Build skip sets to pass to extends. Reuse precomputed values when no
1087
1124
  // caller-provided sets need merging.
@@ -1118,7 +1155,7 @@ export function create({
1118
1155
  }
1119
1156
 
1120
1157
  const extUserVariantProps =
1121
- extMetasWithComputedCount > 0
1158
+ extMetasWithRefineCount > 0
1122
1159
  ? getExtUserVariantProps(
1123
1160
  userVariantProps,
1124
1161
  protectedVariants ?? null,
@@ -1165,9 +1202,9 @@ export function create({
1165
1202
  protectedVariantKeys,
1166
1203
  );
1167
1204
  }
1168
- // Only sync protected variants when a child computed resolver can
1205
+ // Only sync protected variants when a child refine resolver can
1169
1206
  // observe them. Otherwise extUserVariantProps may alias caller props.
1170
- if (protectedVariants && extMetasWithComputedCount > 0) {
1207
+ if (protectedVariants && extMetasWithRefineCount > 0) {
1171
1208
  Object.assign(extUserVariantProps, protectedVariants);
1172
1209
  }
1173
1210
  }
@@ -1177,9 +1214,10 @@ export function create({
1177
1214
  if (hasBaseStyle) Object.assign(styleOut, baseStyle);
1178
1215
 
1179
1216
  // Apply own variants. Skip keys/values come from caller (e.g., parent
1180
- // wants its own computedVariants to override this variant).
1217
+ // wants its own function variant to override this variant).
1181
1218
  // `variantEntryNames` already excludes disabled keys (those with `null`
1182
- // value in config), so we don't re-check `disabledVariantKeys` here.
1219
+ // value in config) and function variants, so we don't re-check
1220
+ // `disabledVariantKeys` here.
1183
1221
  const ownSkipKeys = skipKeys;
1184
1222
  const ownSkipValues = skipValues;
1185
1223
  for (let i = 0; i < variantEntryCount; i++) {
@@ -1217,9 +1255,11 @@ export function create({
1217
1255
  }
1218
1256
  }
1219
1257
 
1220
- // Apply computedVariants.
1221
- for (let i = 0; i < computedVariantCount; i++) {
1222
- const variantName = computedVariantNames[i];
1258
+ // Apply function variants — entries in `variants` whose value is a
1259
+ // function. They run after static variants and override any same-key
1260
+ // inherited variant via `staticExtSkipKeys`.
1261
+ for (let i = 0; i < functionVariantCount; i++) {
1262
+ const variantName = functionVariantNames[i];
1223
1263
  if (ownSkipKeys && ownSkipKeys.has(variantName)) continue;
1224
1264
  const selectedValue = workingResolved[variantName];
1225
1265
  if (selectedValue === undefined) continue;
@@ -1231,7 +1271,7 @@ export function create({
1231
1271
  ) {
1232
1272
  continue;
1233
1273
  }
1234
- const fn = computedVariantFns[i];
1274
+ const fn = functionVariantFns[i];
1235
1275
  const computedResult = fn(selectedValue);
1236
1276
  if (computedResult == null) continue;
1237
1277
  const r = extractClassAndStylePrebuilt(computedResult);
@@ -1239,7 +1279,8 @@ export function create({
1239
1279
  if (r.style) Object.assign(styleOut, r.style);
1240
1280
  }
1241
1281
 
1242
- // Apply `computed` results — must come after own variants/computedVariants.
1282
+ // Apply `refine` results — must come after own variants (static and
1283
+ // function).
1243
1284
  if (cClasses) {
1244
1285
  for (let i = 0; i < cClasses.length; i++) {
1245
1286
  classesOut.push(cClasses[i] as ClsxClassValue);
@@ -1251,7 +1292,7 @@ export function create({
1251
1292
  };
1252
1293
 
1253
1294
  const compute: ComputeFn =
1254
- !computed && extMetasWithComputedCount === 0
1295
+ !refine && extMetasWithRefineCount === 0
1255
1296
  ? (
1256
1297
  resolved,
1257
1298
  userVariantProps,
@@ -1289,7 +1330,7 @@ export function create({
1289
1330
  pendingProtectedVariants,
1290
1331
  protectedVariantKeys,
1291
1332
  ) => {
1292
- runState ??= { remaining: MAX_COMPUTED_RUNS, warned: false };
1333
+ runState ??= { remaining: MAX_REFINE_RUNS, warned: false };
1293
1334
  protectedVariants ??= {};
1294
1335
  protectedVariantKeys ??= new Set<string>();
1295
1336
  let workingResolved = resolved;
@@ -1359,7 +1400,7 @@ export function create({
1359
1400
  if (useDirectOutput && runState.remaining === 0) {
1360
1401
  // Keep the direct output from the last allowed run. Rolling
1361
1402
  // back here would drop it before the fallback copy below.
1362
- warnComputedLimit(runState);
1403
+ warnRefineLimit(runState);
1363
1404
  return nextResolved;
1364
1405
  }
1365
1406
 
@@ -1379,7 +1420,7 @@ export function create({
1379
1420
  isFirstRun = false;
1380
1421
  }
1381
1422
 
1382
- warnComputedLimit(runState);
1423
+ warnRefineLimit(runState);
1383
1424
 
1384
1425
  for (let i = 0; i < lastClasses.length; i++) {
1385
1426
  classesOut.push(lastClasses[i]);
@@ -1388,7 +1429,7 @@ export function create({
1388
1429
  return workingResolved;
1389
1430
  };
1390
1431
 
1391
- const resolveComputedOnce: ResolveComputedFn = (
1432
+ const resolveRefineOnce: ResolveRefineFn = (
1392
1433
  resolved,
1393
1434
  userVariantProps,
1394
1435
  filterOwnVariants = true,
@@ -1399,8 +1440,8 @@ export function create({
1399
1440
  ) => {
1400
1441
  let workingResolved = resolved;
1401
1442
  let changedVariants: Record<string, unknown> | null = null;
1402
- if (computed) {
1403
- const computedResult = runComputedContext(
1443
+ if (refine) {
1444
+ const refineResult = runRefineContext(
1404
1445
  resolved,
1405
1446
  userVariantProps,
1406
1447
  filterOwnVariants,
@@ -1409,21 +1450,21 @@ export function create({
1409
1450
  pendingProtectedVariants,
1410
1451
  protectedVariantKeys,
1411
1452
  );
1412
- workingResolved = computedResult.workingResolved;
1413
- changedVariants = computedResult.changedVariants;
1453
+ workingResolved = refineResult.workingResolved;
1454
+ changedVariants = refineResult.changedVariants;
1414
1455
  }
1415
1456
 
1416
- if (extMetasWithComputedCount > 0) {
1457
+ if (extMetasWithRefineCount > 0) {
1417
1458
  const extUserVariantProps = getExtUserVariantProps(
1418
1459
  userVariantProps,
1419
1460
  protectedVariants ?? null,
1420
1461
  changedVariants,
1421
1462
  );
1422
- for (let i = 0; i < extMetasWithComputedCount; i++) {
1423
- const meta = extMetasWithComputed[i];
1424
- const resolveComputed = meta.resolveComputed;
1425
- if (!resolveComputed) continue;
1426
- workingResolved = resolveComputed(
1463
+ for (let i = 0; i < extMetasWithRefineCount; i++) {
1464
+ const meta = extMetasWithRefine[i];
1465
+ const resolveRefine = meta.resolveRefine;
1466
+ if (!resolveRefine) continue;
1467
+ workingResolved = resolveRefine(
1427
1468
  workingResolved,
1428
1469
  extUserVariantProps,
1429
1470
  true,
@@ -1441,8 +1482,8 @@ export function create({
1441
1482
  return workingResolved;
1442
1483
  };
1443
1484
 
1444
- const resolveComputed: ResolveComputedFn | null =
1445
- computed || extMetasWithComputedCount > 0
1485
+ const resolveRefine: ResolveRefineFn | null =
1486
+ refine || extMetasWithRefineCount > 0
1446
1487
  ? (
1447
1488
  resolved,
1448
1489
  userVariantProps,
@@ -1452,7 +1493,7 @@ export function create({
1452
1493
  pendingProtectedVariants,
1453
1494
  protectedVariantKeys,
1454
1495
  ) => {
1455
- runState ??= { remaining: MAX_COMPUTED_RUNS, warned: false };
1496
+ runState ??= { remaining: MAX_REFINE_RUNS, warned: false };
1456
1497
  protectedVariants ??= {};
1457
1498
  protectedVariantKeys ??= new Set<string>();
1458
1499
  let workingResolved = resolved;
@@ -1461,7 +1502,7 @@ export function create({
1461
1502
  while (runState.remaining > 0) {
1462
1503
  runState.remaining -= 1;
1463
1504
  const nextPendingProtectedVariants: Record<string, unknown> = {};
1464
- const nextResolved = resolveComputedOnce(
1505
+ const nextResolved = resolveRefineOnce(
1465
1506
  workingResolved,
1466
1507
  userVariantProps,
1467
1508
  filterOwnVariants,
@@ -1499,7 +1540,7 @@ export function create({
1499
1540
  }
1500
1541
 
1501
1542
  if (reachedLimit) {
1502
- warnComputedLimit(runState);
1543
+ warnRefineLimit(runState);
1503
1544
  }
1504
1545
 
1505
1546
  return workingResolved;
@@ -1521,7 +1562,7 @@ export function create({
1521
1562
  Object.assign(resolved, staticDefaults);
1522
1563
 
1523
1564
  let userVariantProps: Record<string, unknown>;
1524
- if (extMetasWithComputedCount > 0) {
1565
+ if (extMetasWithRefineCount > 0) {
1525
1566
  // Some extends need a resolveDefaults pass. They expect a variant-only
1526
1567
  // object as `userProps`, so we extract one.
1527
1568
  const variantProps: Record<string, unknown> = {};
@@ -1531,12 +1572,12 @@ export function create({
1531
1572
  variantProps[key] = propsRecord[key];
1532
1573
  }
1533
1574
  }
1534
- for (let i = 0; i < extMetasWithComputedCount; i++) {
1535
- const meta = extMetasWithComputed[i];
1536
- const extComputed = meta.resolveDefaults!(resolved, variantProps);
1537
- for (const k in extComputed) {
1538
- if (!Object.hasOwn(extComputed, k)) continue;
1539
- resolved[k] = extComputed[k];
1575
+ for (let i = 0; i < extMetasWithRefineCount; i++) {
1576
+ const meta = extMetasWithRefine[i];
1577
+ const extDefaults = meta.resolveDefaults!(resolved, variantProps);
1578
+ for (const k in extDefaults) {
1579
+ if (!Object.hasOwn(extDefaults, k)) continue;
1580
+ resolved[k] = extDefaults[k];
1540
1581
  }
1541
1582
  }
1542
1583
  for (const k in variantProps) {
@@ -1610,12 +1651,8 @@ export function create({
1610
1651
  const getVariants = (variants?: VariantValues<MergedVariants>) => {
1611
1652
  const variantProps = variants ?? EMPTY_DEFAULTS;
1612
1653
  let resolvedVariants = resolveVariantsHot(variantProps);
1613
- if (resolveComputed) {
1614
- resolvedVariants = resolveComputed(
1615
- resolvedVariants,
1616
- variantProps,
1617
- false,
1618
- );
1654
+ if (resolveRefine) {
1655
+ resolvedVariants = resolveRefine(resolvedVariants, variantProps, false);
1619
1656
  }
1620
1657
  return resolvedVariants as VariantValues<MergedVariants>;
1621
1658
  };
@@ -1642,8 +1679,9 @@ export function create({
1642
1679
  staticDefaults,
1643
1680
  resolveDefaults: resolveDefaultsFn,
1644
1681
  compute,
1645
- resolveComputed,
1682
+ resolveRefine,
1646
1683
  transformClass,
1684
+ functionVariantKeys,
1647
1685
  };
1648
1686
 
1649
1687
  const initComponent = <
@@ -1667,7 +1705,7 @@ export function create({
1667
1705
  const defaultComponent = ((props: ComponentProps<MergedVariants> = {}) => {
1668
1706
  const { className, style } = computeResult(props);
1669
1707
  return { class: className, style };
1670
- }) as CVComponent<V, CV, E>;
1708
+ }) as CVComponent<V, E>;
1671
1709
  initComponent(defaultComponent, inputPropsKeys, (props = {}) => {
1672
1710
  return computeResult(props).style;
1673
1711
  });