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/src/index.ts
CHANGED
|
@@ -13,6 +13,7 @@ import type {
|
|
|
13
13
|
ClassValue,
|
|
14
14
|
ComponentProps,
|
|
15
15
|
ComponentResult,
|
|
16
|
+
DefaultVariants,
|
|
16
17
|
ExtendableVariants,
|
|
17
18
|
HTMLObjProps,
|
|
18
19
|
HTMLProps,
|
|
@@ -53,6 +54,8 @@ type ComputeFn = (
|
|
|
53
54
|
protectedVariants?: Record<string, unknown> | null,
|
|
54
55
|
pendingProtectedVariants?: Record<string, unknown> | null,
|
|
55
56
|
protectedVariantKeys?: Set<string> | null,
|
|
57
|
+
defaultResolved?: Record<string, unknown>,
|
|
58
|
+
renderOnly?: boolean,
|
|
56
59
|
) => Record<string, unknown>;
|
|
57
60
|
|
|
58
61
|
type ResolveRefineFn = (
|
|
@@ -63,21 +66,18 @@ type ResolveRefineFn = (
|
|
|
63
66
|
protectedVariants?: Record<string, unknown> | null,
|
|
64
67
|
pendingProtectedVariants?: Record<string, unknown> | null,
|
|
65
68
|
protectedVariantKeys?: Set<string> | null,
|
|
69
|
+
defaultResolved?: Record<string, unknown>,
|
|
66
70
|
) => Record<string, unknown>;
|
|
67
71
|
|
|
72
|
+
type ComputedDefaultVariantFn = (
|
|
73
|
+
defaultValue: unknown,
|
|
74
|
+
variants: Readonly<Record<string, unknown>>,
|
|
75
|
+
) => unknown;
|
|
76
|
+
|
|
68
77
|
// Internal metadata stored on components but hidden from public types.
|
|
69
78
|
interface ComponentMeta {
|
|
70
79
|
baseClass: string;
|
|
71
80
|
staticDefaults: Record<string, unknown>;
|
|
72
|
-
// Returns variants set via setDefaultVariants in the refine function chain.
|
|
73
|
-
// null when this component has no resolveDefaults work to do (no `refine`
|
|
74
|
-
// and no extends with work).
|
|
75
|
-
resolveDefaults:
|
|
76
|
-
| ((
|
|
77
|
-
childDefaults: Record<string, unknown>,
|
|
78
|
-
userProps?: Record<string, unknown>,
|
|
79
|
-
) => Record<string, unknown>)
|
|
80
|
-
| null;
|
|
81
81
|
// Returns variant classes + style for this component, used by extending
|
|
82
82
|
// components. Top-level rendering also routes through this.
|
|
83
83
|
compute: ComputeFn;
|
|
@@ -93,6 +93,10 @@ interface ComponentMeta {
|
|
|
93
93
|
// type-level "function variant is replaced by anything in the child" rule).
|
|
94
94
|
// Empty when no key in this chain is a function variant.
|
|
95
95
|
functionVariantKeys: Set<string>;
|
|
96
|
+
// Variant keys with computed defaults anywhere in this component's chain.
|
|
97
|
+
// Child components use this to preserve inherited computed defaults through
|
|
98
|
+
// `defaultValue` without preserving their own prior computed result.
|
|
99
|
+
computedDefaultKeys: Set<string>;
|
|
96
100
|
}
|
|
97
101
|
|
|
98
102
|
const META_KEY = "__meta";
|
|
@@ -168,6 +172,31 @@ function mergeVariants(
|
|
|
168
172
|
return changed;
|
|
169
173
|
}
|
|
170
174
|
|
|
175
|
+
function mergeProtectedIntoBase(
|
|
176
|
+
baseResolved: Record<string, unknown>,
|
|
177
|
+
protectedVariants: Record<string, unknown> | null | undefined,
|
|
178
|
+
): Record<string, unknown> {
|
|
179
|
+
if (!protectedVariants) {
|
|
180
|
+
return baseResolved;
|
|
181
|
+
}
|
|
182
|
+
let hasProtected = false;
|
|
183
|
+
for (const key in protectedVariants) {
|
|
184
|
+
if (!Object.hasOwn(protectedVariants, key)) continue;
|
|
185
|
+
hasProtected = true;
|
|
186
|
+
break;
|
|
187
|
+
}
|
|
188
|
+
if (!hasProtected) {
|
|
189
|
+
return baseResolved;
|
|
190
|
+
}
|
|
191
|
+
const resolved: Record<string, unknown> = {};
|
|
192
|
+
Object.assign(resolved, baseResolved);
|
|
193
|
+
for (const key in protectedVariants) {
|
|
194
|
+
if (!Object.hasOwn(protectedVariants, key)) continue;
|
|
195
|
+
resolved[key] = protectedVariants[key];
|
|
196
|
+
}
|
|
197
|
+
return resolved;
|
|
198
|
+
}
|
|
199
|
+
|
|
171
200
|
// Components carry internal metadata on a non-public property so user-facing
|
|
172
201
|
// component types stay clean.
|
|
173
202
|
function getComponentMeta(component: AnyComponent): ComponentMeta | undefined {
|
|
@@ -189,12 +218,64 @@ export type {
|
|
|
189
218
|
CVComponent,
|
|
190
219
|
};
|
|
191
220
|
|
|
221
|
+
/**
|
|
222
|
+
* Extracts the variant props inferred for a Clava component. Use it to add a
|
|
223
|
+
* component's variant props to framework component props.
|
|
224
|
+
*
|
|
225
|
+
* @example
|
|
226
|
+
* ```ts
|
|
227
|
+
* import { type VariantProps, cv } from "clava";
|
|
228
|
+
* import type { ComponentProps } from "react";
|
|
229
|
+
*
|
|
230
|
+
* const button = cv({
|
|
231
|
+
* variants: {
|
|
232
|
+
* size: { sm: "button-sm", lg: "button-lg" },
|
|
233
|
+
* disabled: { true: "button-disabled", false: "" },
|
|
234
|
+
* },
|
|
235
|
+
* });
|
|
236
|
+
*
|
|
237
|
+
* interface ButtonProps
|
|
238
|
+
* extends ComponentProps<"button">,
|
|
239
|
+
* VariantProps<typeof button> {}
|
|
240
|
+
*
|
|
241
|
+
* const props: ButtonProps = {
|
|
242
|
+
* size: "lg",
|
|
243
|
+
* disabled: true,
|
|
244
|
+
* };
|
|
245
|
+
* ```
|
|
246
|
+
*/
|
|
192
247
|
export type VariantProps<T extends Pick<AnyComponent, "getVariants">> =
|
|
193
248
|
ReturnType<T["getVariants"]>;
|
|
194
249
|
|
|
195
250
|
// Variant props expose booleans, but variant object keys are always strings.
|
|
196
251
|
type VariantKey<T> = T extends boolean ? "true" | "false" : Extract<T, string>;
|
|
197
252
|
|
|
253
|
+
/**
|
|
254
|
+
* Constrains a variant map to the same value keys as a variant on another
|
|
255
|
+
* component. Boolean variants are represented with `"true"` and `"false"`
|
|
256
|
+
* object keys.
|
|
257
|
+
*
|
|
258
|
+
* @example
|
|
259
|
+
* ```ts
|
|
260
|
+
* import { type Variant, cv } from "clava";
|
|
261
|
+
*
|
|
262
|
+
* const button = cv({
|
|
263
|
+
* variants: {
|
|
264
|
+
* size: { sm: "button-sm", lg: "button-lg" },
|
|
265
|
+
* },
|
|
266
|
+
* });
|
|
267
|
+
*
|
|
268
|
+
* const icon = cv({
|
|
269
|
+
* extend: [button],
|
|
270
|
+
* variants: {
|
|
271
|
+
* size: {
|
|
272
|
+
* sm: "icon-sm",
|
|
273
|
+
* lg: "icon-lg",
|
|
274
|
+
* } satisfies Variant<typeof button, "size">,
|
|
275
|
+
* },
|
|
276
|
+
* });
|
|
277
|
+
* ```
|
|
278
|
+
*/
|
|
198
279
|
export type Variant<
|
|
199
280
|
T extends Pick<AnyComponent, "getVariants">,
|
|
200
281
|
K extends keyof VariantProps<T>,
|
|
@@ -203,6 +284,32 @@ export type Variant<
|
|
|
203
284
|
ClassValue | StyleClassValue
|
|
204
285
|
>;
|
|
205
286
|
|
|
287
|
+
/**
|
|
288
|
+
* The configuration object accepted by `cv()`. It defines base class/style
|
|
289
|
+
* output, variants, default variants, component extensions, and refinement
|
|
290
|
+
* logic.
|
|
291
|
+
*
|
|
292
|
+
* @example
|
|
293
|
+
* ```ts
|
|
294
|
+
* import { type CVConfig, cv } from "clava";
|
|
295
|
+
*
|
|
296
|
+
* const config: CVConfig<{
|
|
297
|
+
* tone: { info: string; danger: string };
|
|
298
|
+
* }> = {
|
|
299
|
+
* variants: {
|
|
300
|
+
* tone: {
|
|
301
|
+
* info: "alert-info",
|
|
302
|
+
* danger: "alert-danger",
|
|
303
|
+
* },
|
|
304
|
+
* },
|
|
305
|
+
* defaultVariants: {
|
|
306
|
+
* tone: "info",
|
|
307
|
+
* },
|
|
308
|
+
* };
|
|
309
|
+
*
|
|
310
|
+
* const alert = cv(config);
|
|
311
|
+
* ```
|
|
312
|
+
*/
|
|
206
313
|
export interface CVConfig<
|
|
207
314
|
V extends Variants = {},
|
|
208
315
|
E extends AnyComponent[] = [],
|
|
@@ -211,7 +318,7 @@ export interface CVConfig<
|
|
|
211
318
|
class?: ClassValue;
|
|
212
319
|
style?: StyleValue;
|
|
213
320
|
variants?: ExtendableVariants<V, E>;
|
|
214
|
-
defaultVariants?:
|
|
321
|
+
defaultVariants?: DefaultVariants<MergeVariants<V, E>>;
|
|
215
322
|
refine?: Refine<MergeVariants<V, E>>;
|
|
216
323
|
}
|
|
217
324
|
|
|
@@ -219,6 +326,11 @@ interface CreateParams {
|
|
|
219
326
|
transformClass?: (className: string) => string;
|
|
220
327
|
}
|
|
221
328
|
|
|
329
|
+
interface VariantConfigLike {
|
|
330
|
+
extend?: AnyComponent[];
|
|
331
|
+
variants?: Record<string, unknown>;
|
|
332
|
+
}
|
|
333
|
+
|
|
222
334
|
function isRecordObject(value: unknown): value is Record<string, unknown> {
|
|
223
335
|
if (typeof value !== "object") return false;
|
|
224
336
|
if (value == null) return false;
|
|
@@ -282,9 +394,7 @@ function extractClassAndStylePrebuilt(value: unknown): PrebuiltValue {
|
|
|
282
394
|
* Gets all variant keys from a component's config, including extended
|
|
283
395
|
* components.
|
|
284
396
|
*/
|
|
285
|
-
function collectVariantKeys(
|
|
286
|
-
config: CVConfig<Variants, AnyComponent[]>,
|
|
287
|
-
): string[] {
|
|
397
|
+
function collectVariantKeys(config: VariantConfigLike): string[] {
|
|
288
398
|
const keys = new Set<string>();
|
|
289
399
|
|
|
290
400
|
if (config.extend) {
|
|
@@ -299,7 +409,7 @@ function collectVariantKeys(
|
|
|
299
409
|
if (config.variants) {
|
|
300
410
|
for (const key in config.variants) {
|
|
301
411
|
if (!Object.hasOwn(config.variants, key)) continue;
|
|
302
|
-
const variant =
|
|
412
|
+
const variant = config.variants[key];
|
|
303
413
|
if (variant === null) {
|
|
304
414
|
keys.delete(key);
|
|
305
415
|
continue;
|
|
@@ -311,13 +421,6 @@ function collectVariantKeys(
|
|
|
311
421
|
return Array.from(keys);
|
|
312
422
|
}
|
|
313
423
|
|
|
314
|
-
function isVariantDisabled(
|
|
315
|
-
config: CVConfig<Variants, AnyComponent[]>,
|
|
316
|
-
key: string,
|
|
317
|
-
): boolean {
|
|
318
|
-
return config.variants?.[key] === null;
|
|
319
|
-
}
|
|
320
|
-
|
|
321
424
|
function getVariantValueKey(value: unknown): string | undefined {
|
|
322
425
|
if (typeof value === "string") {
|
|
323
426
|
return value;
|
|
@@ -331,28 +434,14 @@ function getVariantValueKey(value: unknown): string | undefined {
|
|
|
331
434
|
return undefined;
|
|
332
435
|
}
|
|
333
436
|
|
|
334
|
-
function
|
|
335
|
-
config: CVConfig<Variants, AnyComponent[]>,
|
|
336
|
-
key: string,
|
|
337
|
-
value: unknown,
|
|
338
|
-
): boolean {
|
|
339
|
-
const valueKey = getVariantValueKey(value);
|
|
340
|
-
if (valueKey == null) return false;
|
|
341
|
-
const variant = config.variants?.[key];
|
|
342
|
-
if (!isRecordObject(variant)) return false;
|
|
343
|
-
return variant[valueKey] === null;
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
function collectDisabledVariantKeys(
|
|
347
|
-
config: CVConfig<Variants, AnyComponent[]>,
|
|
348
|
-
): Set<string> {
|
|
437
|
+
function collectDisabledVariantKeys(config: VariantConfigLike): Set<string> {
|
|
349
438
|
const keys = new Set<string>();
|
|
350
439
|
if (!config.variants) {
|
|
351
440
|
return keys;
|
|
352
441
|
}
|
|
353
442
|
for (const key in config.variants) {
|
|
354
443
|
if (!Object.hasOwn(config.variants, key)) continue;
|
|
355
|
-
if (
|
|
444
|
+
if (config.variants[key] === null) {
|
|
356
445
|
keys.add(key);
|
|
357
446
|
}
|
|
358
447
|
}
|
|
@@ -360,7 +449,7 @@ function collectDisabledVariantKeys(
|
|
|
360
449
|
}
|
|
361
450
|
|
|
362
451
|
function collectDisabledVariantValues(
|
|
363
|
-
config:
|
|
452
|
+
config: VariantConfigLike,
|
|
364
453
|
): Record<string, Set<string>> {
|
|
365
454
|
const values: Record<string, Set<string>> = {};
|
|
366
455
|
if (!config.variants) {
|
|
@@ -368,7 +457,7 @@ function collectDisabledVariantValues(
|
|
|
368
457
|
}
|
|
369
458
|
for (const key in config.variants) {
|
|
370
459
|
if (!Object.hasOwn(config.variants, key)) continue;
|
|
371
|
-
const variant =
|
|
460
|
+
const variant = config.variants[key];
|
|
372
461
|
if (!isRecordObject(variant)) continue;
|
|
373
462
|
let bucket: Set<string> | undefined;
|
|
374
463
|
for (const variantValue in variant) {
|
|
@@ -628,10 +717,19 @@ export function create({
|
|
|
628
717
|
const variantEntryCount = variantEntryNames.length;
|
|
629
718
|
const functionVariantCount = functionVariantNames.length;
|
|
630
719
|
|
|
720
|
+
const computedDefaultNames: string[] = [];
|
|
721
|
+
const computedDefaultFns: ComputedDefaultVariantFn[] = [];
|
|
722
|
+
const defaultVariants = config.defaultVariants as
|
|
723
|
+
| Record<string, unknown>
|
|
724
|
+
| undefined;
|
|
725
|
+
|
|
631
726
|
// Pre-compute static defaults. Includes:
|
|
632
727
|
// - extended components' static defaults
|
|
633
728
|
// - implicit boolean defaults (variants with a `false` key default to false)
|
|
634
|
-
// - this config's defaultVariants (overriding the above)
|
|
729
|
+
// - this config's literal defaultVariants (overriding the above)
|
|
730
|
+
//
|
|
731
|
+
// Function entries in defaultVariants are computed defaults. They run in
|
|
732
|
+
// the refine loop so they can react to setVariants updates.
|
|
635
733
|
// Then filtered through disabled-variants.
|
|
636
734
|
const staticDefaults: Record<string, unknown> = {};
|
|
637
735
|
if (extend) {
|
|
@@ -655,9 +753,23 @@ export function create({
|
|
|
655
753
|
}
|
|
656
754
|
}
|
|
657
755
|
}
|
|
658
|
-
if (
|
|
659
|
-
|
|
756
|
+
if (defaultVariants) {
|
|
757
|
+
for (const name in defaultVariants) {
|
|
758
|
+
if (!Object.hasOwn(defaultVariants, name)) continue;
|
|
759
|
+
const value = defaultVariants[name];
|
|
760
|
+
if (typeof value === "function") {
|
|
761
|
+
computedDefaultNames.push(name);
|
|
762
|
+
computedDefaultFns.push(value as ComputedDefaultVariantFn);
|
|
763
|
+
continue;
|
|
764
|
+
}
|
|
765
|
+
if (value === undefined) {
|
|
766
|
+
Reflect.deleteProperty(staticDefaults, name);
|
|
767
|
+
continue;
|
|
768
|
+
}
|
|
769
|
+
staticDefaults[name] = value;
|
|
770
|
+
}
|
|
660
771
|
}
|
|
772
|
+
const computedDefaultCount = computedDefaultNames.length;
|
|
661
773
|
if (hasAnyDisabled) {
|
|
662
774
|
// Filter disabled variants in-place
|
|
663
775
|
for (const key in staticDefaults) {
|
|
@@ -707,13 +819,21 @@ export function create({
|
|
|
707
819
|
}
|
|
708
820
|
const extCount = extMetas.length;
|
|
709
821
|
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
822
|
+
const inheritedComputedDefaultKeys = new Set<string>();
|
|
823
|
+
for (let i = 0; i < extCount; i++) {
|
|
824
|
+
const keys = extMetas[i].computedDefaultKeys;
|
|
825
|
+
for (const key of keys) {
|
|
826
|
+
inheritedComputedDefaultKeys.add(key);
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
// Filter to only extends with computed default or refine work in their
|
|
831
|
+
// chain. Those are the components that can change resolved variants across
|
|
832
|
+
// fixed-point iterations.
|
|
713
833
|
const extMetasWithRefine: ComponentMeta[] = [];
|
|
714
834
|
for (let i = 0; i < extCount; i++) {
|
|
715
835
|
const meta = extMetas[i];
|
|
716
|
-
if (meta.
|
|
836
|
+
if (meta.resolveRefine) {
|
|
717
837
|
extMetasWithRefine.push(meta);
|
|
718
838
|
}
|
|
719
839
|
}
|
|
@@ -727,7 +847,8 @@ export function create({
|
|
|
727
847
|
// frame is captured at creation time but the underlying `.stack` string is
|
|
728
848
|
// formatted lazily on first access, so component creation stays cheap
|
|
729
849
|
// unless the warning actually fires.
|
|
730
|
-
const canTriggerRefineWarning =
|
|
850
|
+
const canTriggerRefineWarning =
|
|
851
|
+
!!refine || computedDefaultCount > 0 || extMetasWithRefineCount > 0;
|
|
731
852
|
const creationFrame = canTriggerRefineWarning
|
|
732
853
|
? captureCreationFrame(cv)
|
|
733
854
|
: undefined;
|
|
@@ -756,6 +877,11 @@ export function create({
|
|
|
756
877
|
functionVariantKeys.delete(variantEntryNames[i]);
|
|
757
878
|
}
|
|
758
879
|
|
|
880
|
+
const computedDefaultKeys = new Set(inheritedComputedDefaultKeys);
|
|
881
|
+
for (let i = 0; i < computedDefaultCount; i++) {
|
|
882
|
+
computedDefaultKeys.add(computedDefaultNames[i]);
|
|
883
|
+
}
|
|
884
|
+
|
|
759
885
|
// Static-variant keys in this component that override an inherited
|
|
760
886
|
// function variant. Type-level merge says child fully replaces, so the
|
|
761
887
|
// ancestor's function must not run with the child's (object-typed) value.
|
|
@@ -831,84 +957,62 @@ export function create({
|
|
|
831
957
|
}
|
|
832
958
|
}
|
|
833
959
|
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
// userProps is contractually variant-only (callers pre-filter
|
|
847
|
-
// when starting from a full props object).
|
|
848
|
-
const resolvedVariants: Record<string, unknown> = {};
|
|
849
|
-
Object.assign(resolvedVariants, staticDefaults);
|
|
850
|
-
for (const key in childDefaults) {
|
|
851
|
-
if (!Object.hasOwn(childDefaults, key)) continue;
|
|
852
|
-
const v = childDefaults[key];
|
|
853
|
-
if (v === undefined) continue;
|
|
854
|
-
resolvedVariants[key] = v;
|
|
855
|
-
}
|
|
856
|
-
for (const key in userProps) {
|
|
857
|
-
if (!Object.hasOwn(userProps, key)) continue;
|
|
858
|
-
const v = userProps[key];
|
|
859
|
-
if (v === undefined) continue;
|
|
860
|
-
resolvedVariants[key] = v;
|
|
861
|
-
}
|
|
960
|
+
const isOwnDisabledValue = (key: string, value: unknown): boolean => {
|
|
961
|
+
if (disabledVariantKeys.has(key)) {
|
|
962
|
+
return true;
|
|
963
|
+
}
|
|
964
|
+
if (hasDisabledVariantValues) {
|
|
965
|
+
const valueKey = getVariantValueKey(value);
|
|
966
|
+
if (valueKey != null && disabledVariantValues[key]?.has(valueKey)) {
|
|
967
|
+
return true;
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
return false;
|
|
971
|
+
};
|
|
862
972
|
|
|
863
|
-
|
|
973
|
+
const filterOwnDisabledVariants = (
|
|
974
|
+
input: Record<string, unknown>,
|
|
975
|
+
fallback: Record<string, unknown>,
|
|
976
|
+
): Record<string, unknown> => {
|
|
977
|
+
if (!hasAnyDisabled) {
|
|
978
|
+
return input;
|
|
979
|
+
}
|
|
864
980
|
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
981
|
+
let hasOwnDisabledValue = false;
|
|
982
|
+
for (const key in input) {
|
|
983
|
+
if (!Object.hasOwn(input, key)) continue;
|
|
984
|
+
const value = input[key];
|
|
985
|
+
if (isOwnDisabledValue(key, value)) {
|
|
986
|
+
hasOwnDisabledValue = true;
|
|
987
|
+
break;
|
|
988
|
+
}
|
|
989
|
+
}
|
|
990
|
+
if (!hasOwnDisabledValue) {
|
|
991
|
+
return input;
|
|
992
|
+
}
|
|
875
993
|
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
for (const key in newDefaults) {
|
|
893
|
-
if (!Object.hasOwn(newDefaults, key)) continue;
|
|
894
|
-
const value = (newDefaults as Record<string, unknown>)[key];
|
|
895
|
-
if (userProps[key] !== undefined) continue;
|
|
896
|
-
if (isVariantDisabled(config, key)) continue;
|
|
897
|
-
if (isVariantValueDisabled(config, key, value)) continue;
|
|
898
|
-
refineDefaults[key] = value;
|
|
899
|
-
}
|
|
900
|
-
},
|
|
901
|
-
addClass: noop,
|
|
902
|
-
addStyle: noop,
|
|
903
|
-
});
|
|
904
|
-
}
|
|
994
|
+
const filtered: Record<string, unknown> = {};
|
|
995
|
+
for (const key in input) {
|
|
996
|
+
if (!Object.hasOwn(input, key)) continue;
|
|
997
|
+
const value = input[key];
|
|
998
|
+
if (!isOwnDisabledValue(key, value)) {
|
|
999
|
+
filtered[key] = value;
|
|
1000
|
+
continue;
|
|
1001
|
+
}
|
|
1002
|
+
const fallbackValue = fallback[key];
|
|
1003
|
+
if (
|
|
1004
|
+
fallbackValue !== undefined &&
|
|
1005
|
+
!isOwnDisabledValue(key, fallbackValue)
|
|
1006
|
+
) {
|
|
1007
|
+
filtered[key] = fallbackValue;
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
905
1010
|
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
: null;
|
|
1011
|
+
return filtered;
|
|
1012
|
+
};
|
|
909
1013
|
|
|
910
1014
|
// Hot path: resolve variants by merging static defaults + extends'
|
|
911
|
-
//
|
|
1015
|
+
// static defaults + user-provided props.
|
|
912
1016
|
function resolveVariantsHot(
|
|
913
1017
|
propsVariants: Record<string, unknown>,
|
|
914
1018
|
): Record<string, unknown> {
|
|
@@ -916,17 +1020,6 @@ export function create({
|
|
|
916
1020
|
const defaults: Record<string, unknown> = {};
|
|
917
1021
|
Object.assign(defaults, staticDefaults);
|
|
918
1022
|
|
|
919
|
-
// Apply refine defaults from extended components (only those that have
|
|
920
|
-
// actual work to do).
|
|
921
|
-
for (let i = 0; i < extMetasWithRefineCount; i++) {
|
|
922
|
-
const meta = extMetasWithRefine[i];
|
|
923
|
-
const extDefaults = meta.resolveDefaults!(defaults, propsVariants);
|
|
924
|
-
for (const k in extDefaults) {
|
|
925
|
-
if (!Object.hasOwn(extDefaults, k)) continue;
|
|
926
|
-
defaults[k] = extDefaults[k];
|
|
927
|
-
}
|
|
928
|
-
}
|
|
929
|
-
|
|
930
1023
|
// Apply propsVariants on top (filter undefined). propsVariants is
|
|
931
1024
|
// contractually variant-only here — callers building from a full props
|
|
932
1025
|
// object filter to variant keys before calling.
|
|
@@ -947,11 +1040,97 @@ export function create({
|
|
|
947
1040
|
return result;
|
|
948
1041
|
}
|
|
949
1042
|
|
|
1043
|
+
const runComputedDefaults = (
|
|
1044
|
+
resolved: Record<string, unknown>,
|
|
1045
|
+
defaultResolved: Record<string, unknown>,
|
|
1046
|
+
userVariantProps: Record<string, unknown>,
|
|
1047
|
+
filterOwnVariants: boolean,
|
|
1048
|
+
protectedVariantKeys: Set<string> | null | undefined,
|
|
1049
|
+
): {
|
|
1050
|
+
workingResolved: Record<string, unknown>;
|
|
1051
|
+
changedVariants: Record<string, unknown> | null;
|
|
1052
|
+
} => {
|
|
1053
|
+
if (computedDefaultCount === 0) {
|
|
1054
|
+
return { workingResolved: resolved, changedVariants: null };
|
|
1055
|
+
}
|
|
1056
|
+
|
|
1057
|
+
let ownVariants = filterOwnVariants ? null : resolved;
|
|
1058
|
+
const getOwnVariants = (): Record<string, unknown> => {
|
|
1059
|
+
if (ownVariants) {
|
|
1060
|
+
return ownVariants;
|
|
1061
|
+
}
|
|
1062
|
+
const filteredVariants: Record<string, unknown> = {};
|
|
1063
|
+
for (let i = 0; i < variantKeysLength; i++) {
|
|
1064
|
+
const key = variantKeys[i];
|
|
1065
|
+
if (Object.hasOwn(resolved, key)) {
|
|
1066
|
+
filteredVariants[key] = resolved[key];
|
|
1067
|
+
}
|
|
1068
|
+
}
|
|
1069
|
+
ownVariants = filteredVariants;
|
|
1070
|
+
return filteredVariants;
|
|
1071
|
+
};
|
|
1072
|
+
|
|
1073
|
+
let updatedVariants: Record<string, unknown> | null = null;
|
|
1074
|
+
let changedVariants: Record<string, unknown> | null = null;
|
|
1075
|
+
const ensureUpdated = (): Record<string, unknown> => {
|
|
1076
|
+
if (updatedVariants) {
|
|
1077
|
+
return updatedVariants;
|
|
1078
|
+
}
|
|
1079
|
+
const updated: Record<string, unknown> = {};
|
|
1080
|
+
Object.assign(updated, resolved);
|
|
1081
|
+
updatedVariants = updated;
|
|
1082
|
+
return updated;
|
|
1083
|
+
};
|
|
1084
|
+
|
|
1085
|
+
for (let i = 0; i < computedDefaultCount; i++) {
|
|
1086
|
+
const key = computedDefaultNames[i];
|
|
1087
|
+
if (Object.hasOwn(userVariantProps, key)) {
|
|
1088
|
+
if (userVariantProps[key] !== undefined) continue;
|
|
1089
|
+
}
|
|
1090
|
+
if (protectedVariantKeys?.has(key)) continue;
|
|
1091
|
+
|
|
1092
|
+
const variantSnapshot = getOwnVariants();
|
|
1093
|
+
const defaultValue = inheritedComputedDefaultKeys.has(key)
|
|
1094
|
+
? variantSnapshot[key]
|
|
1095
|
+
: defaultResolved[key];
|
|
1096
|
+
const value = computedDefaultFns[i](defaultValue, variantSnapshot);
|
|
1097
|
+
if (hasAnyDisabled) {
|
|
1098
|
+
if (disabledVariantKeys.has(key)) continue;
|
|
1099
|
+
const valueKey = getVariantValueKey(value);
|
|
1100
|
+
if (valueKey != null && disabledVariantValues[key]?.has(valueKey)) {
|
|
1101
|
+
continue;
|
|
1102
|
+
}
|
|
1103
|
+
}
|
|
1104
|
+
|
|
1105
|
+
if (value === undefined) {
|
|
1106
|
+
if (!Object.hasOwn(variantSnapshot, key)) continue;
|
|
1107
|
+
if (shouldCollectChangedVariants) {
|
|
1108
|
+
changedVariants ??= {};
|
|
1109
|
+
changedVariants[key] = value;
|
|
1110
|
+
}
|
|
1111
|
+
Reflect.deleteProperty(ensureUpdated(), key);
|
|
1112
|
+
continue;
|
|
1113
|
+
}
|
|
1114
|
+
if (Object.is(variantSnapshot[key], value)) continue;
|
|
1115
|
+
if (shouldCollectChangedVariants) {
|
|
1116
|
+
changedVariants ??= {};
|
|
1117
|
+
changedVariants[key] = value;
|
|
1118
|
+
}
|
|
1119
|
+
ensureUpdated()[key] = value;
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1122
|
+
return {
|
|
1123
|
+
workingResolved: updatedVariants ?? resolved,
|
|
1124
|
+
changedVariants,
|
|
1125
|
+
};
|
|
1126
|
+
};
|
|
1127
|
+
|
|
950
1128
|
const runRefineContext = (
|
|
951
1129
|
resolved: Record<string, unknown>,
|
|
952
1130
|
userVariantProps: Record<string, unknown>,
|
|
953
1131
|
filterOwnVariants: boolean,
|
|
954
1132
|
collectOutput: boolean,
|
|
1133
|
+
applyVariantUpdates: boolean,
|
|
955
1134
|
protectedVariants: Record<string, unknown> | null | undefined,
|
|
956
1135
|
pendingProtectedVariants: Record<string, unknown> | null | undefined,
|
|
957
1136
|
protectedVariantKeys: Set<string> | null | undefined,
|
|
@@ -983,8 +1162,7 @@ export function create({
|
|
|
983
1162
|
ownVariants = filteredVariants;
|
|
984
1163
|
}
|
|
985
1164
|
// Lazy-init updatedVariants — many refine callbacks only inspect
|
|
986
|
-
// `variants
|
|
987
|
-
// so the copy is unnecessary in the common case.
|
|
1165
|
+
// `variants`, so the copy is unnecessary in the common case.
|
|
988
1166
|
let updatedVariants: Record<string, unknown> | null = null;
|
|
989
1167
|
const localCClasses: ClassValue[] | null = collectOutput ? [] : null;
|
|
990
1168
|
let localCStyle: StyleValue | null = null;
|
|
@@ -1021,6 +1199,9 @@ export function create({
|
|
|
1021
1199
|
setVariants: (
|
|
1022
1200
|
newVariants: VariantValues<Record<string, unknown>>,
|
|
1023
1201
|
) => {
|
|
1202
|
+
if (!applyVariantUpdates) {
|
|
1203
|
+
return;
|
|
1204
|
+
}
|
|
1024
1205
|
if (!hasAnyDisabled) {
|
|
1025
1206
|
for (const key in newVariants) {
|
|
1026
1207
|
if (!Object.hasOwn(newVariants, key)) continue;
|
|
@@ -1049,32 +1230,6 @@ export function create({
|
|
|
1049
1230
|
ensureUpdated()[key] = value;
|
|
1050
1231
|
}
|
|
1051
1232
|
},
|
|
1052
|
-
setDefaultVariants: (
|
|
1053
|
-
newDefaults: VariantValues<Record<string, unknown>>,
|
|
1054
|
-
) => {
|
|
1055
|
-
for (const key in newDefaults) {
|
|
1056
|
-
if (!Object.hasOwn(newDefaults, key)) continue;
|
|
1057
|
-
if (userVariantProps[key] !== undefined) continue;
|
|
1058
|
-
if (protectedVariantKeys?.has(key)) continue;
|
|
1059
|
-
const value = (newDefaults as Record<string, unknown>)[key];
|
|
1060
|
-
if (hasAnyDisabled) {
|
|
1061
|
-
if (disabledVariantKeys.has(key)) continue;
|
|
1062
|
-
const valueKey = getVariantValueKey(value);
|
|
1063
|
-
if (
|
|
1064
|
-
valueKey != null &&
|
|
1065
|
-
disabledVariantValues[key]?.has(valueKey)
|
|
1066
|
-
) {
|
|
1067
|
-
continue;
|
|
1068
|
-
}
|
|
1069
|
-
}
|
|
1070
|
-
if (Object.is(getCurrentVariantValue(key), value)) continue;
|
|
1071
|
-
setChangedVariant(key, value);
|
|
1072
|
-
if (pendingProtectedVariants) {
|
|
1073
|
-
pendingProtectedVariants[key] = value;
|
|
1074
|
-
}
|
|
1075
|
-
ensureUpdated()[key] = value;
|
|
1076
|
-
}
|
|
1077
|
-
},
|
|
1078
1233
|
addClass: (className: ClassValue) => {
|
|
1079
1234
|
localCClasses?.push(className);
|
|
1080
1235
|
},
|
|
@@ -1138,37 +1293,17 @@ export function create({
|
|
|
1138
1293
|
protectedVariants,
|
|
1139
1294
|
pendingProtectedVariants,
|
|
1140
1295
|
protectedVariantKeys,
|
|
1296
|
+
defaultResolved = resolved,
|
|
1297
|
+
renderOnly = false,
|
|
1141
1298
|
) => {
|
|
1142
|
-
// Run `refine` (if any). May modify resolved variants and emit classes
|
|
1143
|
-
// and styles.
|
|
1144
1299
|
let workingResolved = resolved;
|
|
1145
1300
|
let cClasses: ClassValue[] | null = null;
|
|
1146
1301
|
let cStyle: StyleValue | null = null;
|
|
1147
1302
|
let changedVariants: Record<string, unknown> | null = null;
|
|
1148
|
-
if (refine) {
|
|
1149
|
-
const refineResult = runRefineContext(
|
|
1150
|
-
resolved,
|
|
1151
|
-
userVariantProps,
|
|
1152
|
-
true,
|
|
1153
|
-
true,
|
|
1154
|
-
protectedVariants,
|
|
1155
|
-
pendingProtectedVariants,
|
|
1156
|
-
protectedVariantKeys,
|
|
1157
|
-
);
|
|
1158
|
-
workingResolved = refineResult.workingResolved;
|
|
1159
|
-
cClasses = refineResult.classes;
|
|
1160
|
-
cStyle = refineResult.style;
|
|
1161
|
-
changedVariants = refineResult.changedVariants;
|
|
1162
|
-
}
|
|
1163
1303
|
|
|
1164
1304
|
// Run extends' contributions first (their full classes + styles) so our
|
|
1165
1305
|
// own base style and variants apply on top, matching the original
|
|
1166
1306
|
// ext1 → ext2 → … → current ordering.
|
|
1167
|
-
//
|
|
1168
|
-
// Pass explicit user values plus refine changes as the extends'
|
|
1169
|
-
// `userVariantProps`. This lets more-specific refine decisions stick
|
|
1170
|
-
// across re-runs while inherited static defaults can still be refined by
|
|
1171
|
-
// the extended component's own refine chain.
|
|
1172
1307
|
if (hasExtend) {
|
|
1173
1308
|
// Build skip sets to pass to extends. Reuse precomputed values when no
|
|
1174
1309
|
// caller-provided sets need merging.
|
|
@@ -1233,6 +1368,8 @@ export function create({
|
|
|
1233
1368
|
protectedVariants,
|
|
1234
1369
|
pendingProtectedVariants,
|
|
1235
1370
|
protectedVariantKeys,
|
|
1371
|
+
defaultResolved,
|
|
1372
|
+
renderOnly,
|
|
1236
1373
|
);
|
|
1237
1374
|
if (extClasses.length > 0) {
|
|
1238
1375
|
const joined = clsx(extClasses);
|
|
@@ -1254,8 +1391,14 @@ export function create({
|
|
|
1254
1391
|
protectedVariants,
|
|
1255
1392
|
pendingProtectedVariants,
|
|
1256
1393
|
protectedVariantKeys,
|
|
1394
|
+
defaultResolved,
|
|
1395
|
+
renderOnly,
|
|
1257
1396
|
);
|
|
1258
1397
|
}
|
|
1398
|
+
workingResolved = filterOwnDisabledVariants(
|
|
1399
|
+
workingResolved,
|
|
1400
|
+
defaultResolved,
|
|
1401
|
+
);
|
|
1259
1402
|
// Only sync protected variants when a child refine resolver can
|
|
1260
1403
|
// observe them. Otherwise extUserVariantProps may alias caller props.
|
|
1261
1404
|
if (protectedVariants && extMetasWithRefineCount > 0) {
|
|
@@ -1264,6 +1407,42 @@ export function create({
|
|
|
1264
1407
|
}
|
|
1265
1408
|
}
|
|
1266
1409
|
|
|
1410
|
+
// Run own computed defaults after extended components so defaults resolve
|
|
1411
|
+
// from base to child. They still run before this component's `refine`.
|
|
1412
|
+
if (!renderOnly && computedDefaultCount > 0) {
|
|
1413
|
+
const computedResult = runComputedDefaults(
|
|
1414
|
+
workingResolved,
|
|
1415
|
+
defaultResolved,
|
|
1416
|
+
userVariantProps,
|
|
1417
|
+
true,
|
|
1418
|
+
protectedVariantKeys,
|
|
1419
|
+
);
|
|
1420
|
+
workingResolved = computedResult.workingResolved;
|
|
1421
|
+
changedVariants = computedResult.changedVariants;
|
|
1422
|
+
}
|
|
1423
|
+
|
|
1424
|
+
// Run own `refine` (if any). May modify resolved variants and emit
|
|
1425
|
+
// classes and styles that are applied after this component's variants.
|
|
1426
|
+
if (refine) {
|
|
1427
|
+
const refineResult = runRefineContext(
|
|
1428
|
+
workingResolved,
|
|
1429
|
+
userVariantProps,
|
|
1430
|
+
true,
|
|
1431
|
+
true,
|
|
1432
|
+
!renderOnly,
|
|
1433
|
+
protectedVariants,
|
|
1434
|
+
pendingProtectedVariants,
|
|
1435
|
+
protectedVariantKeys,
|
|
1436
|
+
);
|
|
1437
|
+
workingResolved = refineResult.workingResolved;
|
|
1438
|
+
cClasses = refineResult.classes;
|
|
1439
|
+
cStyle = refineResult.style;
|
|
1440
|
+
if (refineResult.changedVariants) {
|
|
1441
|
+
changedVariants ??= {};
|
|
1442
|
+
Object.assign(changedVariants, refineResult.changedVariants);
|
|
1443
|
+
}
|
|
1444
|
+
}
|
|
1445
|
+
|
|
1267
1446
|
// Apply own base style (after extends' styles, matching original order).
|
|
1268
1447
|
if (hasBaseStyle) {
|
|
1269
1448
|
Object.assign(styleOut, baseStyle);
|
|
@@ -1362,7 +1541,7 @@ export function create({
|
|
|
1362
1541
|
};
|
|
1363
1542
|
|
|
1364
1543
|
const compute: ComputeFn =
|
|
1365
|
-
!refine && extMetasWithRefineCount === 0
|
|
1544
|
+
!refine && computedDefaultCount === 0 && extMetasWithRefineCount === 0
|
|
1366
1545
|
? computeOnce
|
|
1367
1546
|
: (
|
|
1368
1547
|
resolved,
|
|
@@ -1375,7 +1554,25 @@ export function create({
|
|
|
1375
1554
|
protectedVariants,
|
|
1376
1555
|
pendingProtectedVariants,
|
|
1377
1556
|
protectedVariantKeys,
|
|
1557
|
+
incomingDefaultResolved = resolved,
|
|
1558
|
+
renderOnly = false,
|
|
1378
1559
|
) => {
|
|
1560
|
+
if (renderOnly) {
|
|
1561
|
+
return computeOnce(
|
|
1562
|
+
resolved,
|
|
1563
|
+
userVariantProps,
|
|
1564
|
+
skipKeys,
|
|
1565
|
+
skipValues,
|
|
1566
|
+
classesOut,
|
|
1567
|
+
styleOut,
|
|
1568
|
+
runState,
|
|
1569
|
+
protectedVariants,
|
|
1570
|
+
pendingProtectedVariants,
|
|
1571
|
+
protectedVariantKeys,
|
|
1572
|
+
incomingDefaultResolved,
|
|
1573
|
+
true,
|
|
1574
|
+
);
|
|
1575
|
+
}
|
|
1379
1576
|
runState ??= { remaining: MAX_REFINE_RUNS };
|
|
1380
1577
|
protectedVariants ??= {};
|
|
1381
1578
|
protectedVariantKeys ??= new Set<string>();
|
|
@@ -1404,6 +1601,10 @@ export function create({
|
|
|
1404
1601
|
? classesOut
|
|
1405
1602
|
: [];
|
|
1406
1603
|
const nextStyle: StyleValue = useDirectOutput ? styleOut : {};
|
|
1604
|
+
const defaultResolved = mergeProtectedIntoBase(
|
|
1605
|
+
incomingDefaultResolved,
|
|
1606
|
+
protectedVariants,
|
|
1607
|
+
);
|
|
1407
1608
|
const nextResolved = computeOnce(
|
|
1408
1609
|
workingResolved,
|
|
1409
1610
|
userVariantProps,
|
|
@@ -1415,6 +1616,7 @@ export function create({
|
|
|
1415
1616
|
protectedVariants,
|
|
1416
1617
|
nextPendingProtectedVariants,
|
|
1417
1618
|
protectedVariantKeys,
|
|
1619
|
+
defaultResolved,
|
|
1418
1620
|
);
|
|
1419
1621
|
|
|
1420
1622
|
let protectedChanged: boolean;
|
|
@@ -1437,7 +1639,30 @@ export function create({
|
|
|
1437
1639
|
(nextResolved === workingResolved ||
|
|
1438
1640
|
areVariantsEqual(workingResolved, nextResolved))
|
|
1439
1641
|
) {
|
|
1440
|
-
if (
|
|
1642
|
+
if (nextResolved !== workingResolved) {
|
|
1643
|
+
if (useDirectOutput) {
|
|
1644
|
+
classesOut.length = classCount;
|
|
1645
|
+
for (const key in styleOut) {
|
|
1646
|
+
if (Object.hasOwn(styleOut, key)) {
|
|
1647
|
+
Reflect.deleteProperty(styleOut, key);
|
|
1648
|
+
}
|
|
1649
|
+
}
|
|
1650
|
+
}
|
|
1651
|
+
computeOnce(
|
|
1652
|
+
nextResolved,
|
|
1653
|
+
userVariantProps,
|
|
1654
|
+
skipKeys,
|
|
1655
|
+
skipValues,
|
|
1656
|
+
classesOut,
|
|
1657
|
+
styleOut,
|
|
1658
|
+
runState,
|
|
1659
|
+
protectedVariants,
|
|
1660
|
+
null,
|
|
1661
|
+
protectedVariantKeys,
|
|
1662
|
+
defaultResolved,
|
|
1663
|
+
true,
|
|
1664
|
+
);
|
|
1665
|
+
} else if (!useDirectOutput) {
|
|
1441
1666
|
for (let i = 0; i < nextClasses.length; i++) {
|
|
1442
1667
|
classesOut.push(nextClasses[i]);
|
|
1443
1668
|
}
|
|
@@ -1508,22 +1733,10 @@ export function create({
|
|
|
1508
1733
|
protectedVariants,
|
|
1509
1734
|
pendingProtectedVariants,
|
|
1510
1735
|
protectedVariantKeys,
|
|
1736
|
+
defaultResolved = resolved,
|
|
1511
1737
|
) => {
|
|
1512
1738
|
let workingResolved = resolved;
|
|
1513
1739
|
let changedVariants: Record<string, unknown> | null = null;
|
|
1514
|
-
if (refine) {
|
|
1515
|
-
const refineResult = runRefineContext(
|
|
1516
|
-
resolved,
|
|
1517
|
-
userVariantProps,
|
|
1518
|
-
filterOwnVariants,
|
|
1519
|
-
false,
|
|
1520
|
-
protectedVariants,
|
|
1521
|
-
pendingProtectedVariants,
|
|
1522
|
-
protectedVariantKeys,
|
|
1523
|
-
);
|
|
1524
|
-
workingResolved = refineResult.workingResolved;
|
|
1525
|
-
changedVariants = refineResult.changedVariants;
|
|
1526
|
-
}
|
|
1527
1740
|
|
|
1528
1741
|
if (extMetasWithRefineCount > 0) {
|
|
1529
1742
|
const extUserVariantProps = getExtUserVariantProps(
|
|
@@ -1543,6 +1756,11 @@ export function create({
|
|
|
1543
1756
|
protectedVariants,
|
|
1544
1757
|
pendingProtectedVariants,
|
|
1545
1758
|
protectedVariantKeys,
|
|
1759
|
+
defaultResolved,
|
|
1760
|
+
);
|
|
1761
|
+
workingResolved = filterOwnDisabledVariants(
|
|
1762
|
+
workingResolved,
|
|
1763
|
+
defaultResolved,
|
|
1546
1764
|
);
|
|
1547
1765
|
if (protectedVariants) {
|
|
1548
1766
|
Object.assign(extUserVariantProps, protectedVariants);
|
|
@@ -1550,11 +1768,40 @@ export function create({
|
|
|
1550
1768
|
}
|
|
1551
1769
|
}
|
|
1552
1770
|
|
|
1771
|
+
if (computedDefaultCount > 0) {
|
|
1772
|
+
const computedResult = runComputedDefaults(
|
|
1773
|
+
workingResolved,
|
|
1774
|
+
defaultResolved,
|
|
1775
|
+
userVariantProps,
|
|
1776
|
+
filterOwnVariants,
|
|
1777
|
+
protectedVariantKeys,
|
|
1778
|
+
);
|
|
1779
|
+
workingResolved = computedResult.workingResolved;
|
|
1780
|
+
changedVariants = computedResult.changedVariants;
|
|
1781
|
+
}
|
|
1782
|
+
if (refine) {
|
|
1783
|
+
const refineResult = runRefineContext(
|
|
1784
|
+
workingResolved,
|
|
1785
|
+
userVariantProps,
|
|
1786
|
+
filterOwnVariants,
|
|
1787
|
+
false,
|
|
1788
|
+
true,
|
|
1789
|
+
protectedVariants,
|
|
1790
|
+
pendingProtectedVariants,
|
|
1791
|
+
protectedVariantKeys,
|
|
1792
|
+
);
|
|
1793
|
+
workingResolved = refineResult.workingResolved;
|
|
1794
|
+
if (refineResult.changedVariants) {
|
|
1795
|
+
changedVariants ??= {};
|
|
1796
|
+
Object.assign(changedVariants, refineResult.changedVariants);
|
|
1797
|
+
}
|
|
1798
|
+
}
|
|
1799
|
+
|
|
1553
1800
|
return workingResolved;
|
|
1554
1801
|
};
|
|
1555
1802
|
|
|
1556
1803
|
const resolveRefine: ResolveRefineFn | null =
|
|
1557
|
-
refine || extMetasWithRefineCount > 0
|
|
1804
|
+
refine || computedDefaultCount > 0 || extMetasWithRefineCount > 0
|
|
1558
1805
|
? (
|
|
1559
1806
|
resolved,
|
|
1560
1807
|
userVariantProps,
|
|
@@ -1563,6 +1810,7 @@ export function create({
|
|
|
1563
1810
|
protectedVariants,
|
|
1564
1811
|
pendingProtectedVariants,
|
|
1565
1812
|
protectedVariantKeys,
|
|
1813
|
+
incomingDefaultResolved = resolved,
|
|
1566
1814
|
) => {
|
|
1567
1815
|
runState ??= { remaining: MAX_REFINE_RUNS };
|
|
1568
1816
|
protectedVariants ??= {};
|
|
@@ -1577,6 +1825,10 @@ export function create({
|
|
|
1577
1825
|
while (runState.remaining > 0) {
|
|
1578
1826
|
runState.remaining -= 1;
|
|
1579
1827
|
const nextPendingProtectedVariants: Record<string, unknown> = {};
|
|
1828
|
+
const defaultResolved = mergeProtectedIntoBase(
|
|
1829
|
+
incomingDefaultResolved,
|
|
1830
|
+
protectedVariants,
|
|
1831
|
+
);
|
|
1580
1832
|
const nextResolved = resolveRefineOnce(
|
|
1581
1833
|
workingResolved,
|
|
1582
1834
|
userVariantProps,
|
|
@@ -1585,6 +1837,7 @@ export function create({
|
|
|
1585
1837
|
protectedVariants,
|
|
1586
1838
|
nextPendingProtectedVariants,
|
|
1587
1839
|
protectedVariantKeys,
|
|
1840
|
+
defaultResolved,
|
|
1588
1841
|
);
|
|
1589
1842
|
let protectedChanged: boolean;
|
|
1590
1843
|
if (pendingProtectedVariants) {
|
|
@@ -1646,17 +1899,11 @@ export function create({
|
|
|
1646
1899
|
): { className: string; style: StyleValue } => {
|
|
1647
1900
|
const propsRecord = props as Record<string, unknown>;
|
|
1648
1901
|
|
|
1649
|
-
// Inline resolve: avoids allocating a separate variantProps object for
|
|
1650
|
-
// the common case where no extends need a resolveDefaults pass.
|
|
1651
|
-
// resolveVariantsHot would also work here but assumes its input is
|
|
1652
|
-
// variant-only (it uses for-in for speed).
|
|
1653
1902
|
let resolved: Record<string, unknown> = {};
|
|
1654
1903
|
Object.assign(resolved, staticDefaults);
|
|
1655
1904
|
|
|
1656
1905
|
let userVariantProps: Record<string, unknown>;
|
|
1657
|
-
if (extMetasWithRefineCount > 0) {
|
|
1658
|
-
// Some extends need a resolveDefaults pass. They expect a variant-only
|
|
1659
|
-
// object as `userProps`, so we extract one.
|
|
1906
|
+
if (refine || computedDefaultCount > 0 || extMetasWithRefineCount > 0) {
|
|
1660
1907
|
const variantProps: Record<string, unknown> = {};
|
|
1661
1908
|
for (let i = 0; i < variantKeysLength; i++) {
|
|
1662
1909
|
const key = variantKeys[i];
|
|
@@ -1664,14 +1911,6 @@ export function create({
|
|
|
1664
1911
|
variantProps[key] = propsRecord[key];
|
|
1665
1912
|
}
|
|
1666
1913
|
}
|
|
1667
|
-
for (let i = 0; i < extMetasWithRefineCount; i++) {
|
|
1668
|
-
const meta = extMetasWithRefine[i];
|
|
1669
|
-
const extDefaults = meta.resolveDefaults!(resolved, variantProps);
|
|
1670
|
-
for (const k in extDefaults) {
|
|
1671
|
-
if (!Object.hasOwn(extDefaults, k)) continue;
|
|
1672
|
-
resolved[k] = extDefaults[k];
|
|
1673
|
-
}
|
|
1674
|
-
}
|
|
1675
1914
|
for (const k in variantProps) {
|
|
1676
1915
|
if (!Object.hasOwn(variantProps, k)) continue;
|
|
1677
1916
|
const v = variantProps[k];
|
|
@@ -1769,11 +2008,11 @@ export function create({
|
|
|
1769
2008
|
const meta: ComponentMeta = {
|
|
1770
2009
|
baseClass: computedBaseClass,
|
|
1771
2010
|
staticDefaults,
|
|
1772
|
-
resolveDefaults: resolveDefaultsFn,
|
|
1773
2011
|
compute,
|
|
1774
2012
|
resolveRefine,
|
|
1775
2013
|
transformClass,
|
|
1776
2014
|
functionVariantKeys,
|
|
2015
|
+
computedDefaultKeys,
|
|
1777
2016
|
};
|
|
1778
2017
|
|
|
1779
2018
|
const initComponent = <
|
|
@@ -1845,6 +2084,4 @@ export function create({
|
|
|
1845
2084
|
return { cv, cx };
|
|
1846
2085
|
}
|
|
1847
2086
|
|
|
1848
|
-
function noop() {}
|
|
1849
|
-
|
|
1850
2087
|
export const { cv, cx } = create();
|