clava 0.2.0 → 0.2.2
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 +12 -0
- package/dist/index.d.ts +0 -10
- package/dist/index.js +608 -392
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/perfs/component.bench.ts +233 -0
- package/src/index.ts +758 -620
- package/src/utils.ts +146 -34
- package/tests/_utils.ts +2 -2
- package/tests/extend-test.ts +48 -0
- package/tests/language-service-test.ts +3 -0
package/src/index.ts
CHANGED
|
@@ -21,8 +21,6 @@ import type {
|
|
|
21
21
|
Variants,
|
|
22
22
|
} from "./types.ts";
|
|
23
23
|
import {
|
|
24
|
-
type Mode,
|
|
25
|
-
getClassPropertyName,
|
|
26
24
|
htmlObjStyleToStyleValue,
|
|
27
25
|
htmlStyleToStyleValue,
|
|
28
26
|
isHTMLObjStyle,
|
|
@@ -50,6 +48,9 @@ const META_KEY = "__meta";
|
|
|
50
48
|
const SKIP_STYLE_KEYS = Symbol("skipStyleKeys");
|
|
51
49
|
const SKIP_STYLE_VARIANT_VALUES = Symbol("skipStyleVariantValues");
|
|
52
50
|
|
|
51
|
+
// eslint-disable-next-line @typescript-eslint/unbound-method
|
|
52
|
+
const hasOwn = Object.prototype.hasOwnProperty;
|
|
53
|
+
|
|
53
54
|
// Dynamic property access on function requires cast through unknown
|
|
54
55
|
function getComponentMeta(component: AnyComponent): ComponentMeta | undefined {
|
|
55
56
|
return (component as unknown as Record<string, unknown>)[META_KEY] as
|
|
@@ -67,7 +68,7 @@ function setComponentMeta(component: AnyComponent, meta: ComponentMeta): void {
|
|
|
67
68
|
*/
|
|
68
69
|
function assign<T extends object>(target: T, source: T): void {
|
|
69
70
|
for (const key in source) {
|
|
70
|
-
if (!
|
|
71
|
+
if (!hasOwn.call(source, key)) continue;
|
|
71
72
|
(target as Record<string, unknown>)[key] = (
|
|
72
73
|
source as Record<string, unknown>
|
|
73
74
|
)[key];
|
|
@@ -150,30 +151,30 @@ function normalizeStyle(style: unknown): StyleValue {
|
|
|
150
151
|
}
|
|
151
152
|
|
|
152
153
|
/**
|
|
153
|
-
*
|
|
154
|
+
* Pre-extracts the class and (normalized) style from a variant value once at
|
|
155
|
+
* component creation time. Returns `null` if the value contributes nothing.
|
|
154
156
|
*/
|
|
155
|
-
|
|
157
|
+
interface PrebuiltValue {
|
|
156
158
|
class: ClassValue;
|
|
157
|
-
style: StyleValue;
|
|
158
|
-
} {
|
|
159
|
-
return { class: value.class, style: normalizeStyle(value.style) };
|
|
159
|
+
style: StyleValue | null;
|
|
160
160
|
}
|
|
161
161
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
162
|
+
function extractStyleClassPrebuilt(value: StyleClassValue): PrebuiltValue {
|
|
163
|
+
const styleNorm = normalizeStyle(value.style);
|
|
164
|
+
return {
|
|
165
|
+
class: value.class ?? null,
|
|
166
|
+
style: styleNorm && Object.keys(styleNorm).length > 0 ? styleNorm : null,
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function extractClassAndStylePrebuilt(value: unknown): PrebuiltValue {
|
|
170
171
|
if (isStyleClassValue(value)) {
|
|
171
|
-
return
|
|
172
|
+
return extractStyleClassPrebuilt(value);
|
|
172
173
|
}
|
|
173
174
|
if (isRecordObject(value)) {
|
|
174
|
-
return { class: null, style:
|
|
175
|
+
return { class: null, style: null };
|
|
175
176
|
}
|
|
176
|
-
return { class: value as ClassValue, style:
|
|
177
|
+
return { class: value as ClassValue, style: null };
|
|
177
178
|
}
|
|
178
179
|
|
|
179
180
|
/**
|
|
@@ -185,18 +186,19 @@ function collectVariantKeys(
|
|
|
185
186
|
): string[] {
|
|
186
187
|
const keys = new Set<string>();
|
|
187
188
|
|
|
188
|
-
// Collect from extended components
|
|
189
189
|
if (config.extend) {
|
|
190
190
|
for (const ext of config.extend) {
|
|
191
|
-
|
|
192
|
-
|
|
191
|
+
const extKeys = ext.variantKeys as readonly string[];
|
|
192
|
+
for (let i = 0; i < extKeys.length; i++) {
|
|
193
|
+
keys.add(extKeys[i]);
|
|
193
194
|
}
|
|
194
195
|
}
|
|
195
196
|
}
|
|
196
197
|
|
|
197
|
-
// Collect from variants
|
|
198
198
|
if (config.variants) {
|
|
199
|
-
for (const
|
|
199
|
+
for (const key in config.variants) {
|
|
200
|
+
if (!hasOwn.call(config.variants, key)) continue;
|
|
201
|
+
const variant = (config.variants as Record<string, unknown>)[key];
|
|
200
202
|
if (variant === null) {
|
|
201
203
|
keys.delete(key);
|
|
202
204
|
continue;
|
|
@@ -205,9 +207,9 @@ function collectVariantKeys(
|
|
|
205
207
|
}
|
|
206
208
|
}
|
|
207
209
|
|
|
208
|
-
// Collect from computedVariants
|
|
209
210
|
if (config.computedVariants) {
|
|
210
|
-
for (const key
|
|
211
|
+
for (const key in config.computedVariants) {
|
|
212
|
+
if (!hasOwn.call(config.computedVariants, key)) continue;
|
|
211
213
|
keys.add(key);
|
|
212
214
|
}
|
|
213
215
|
}
|
|
@@ -241,26 +243,14 @@ function isVariantValueDisabled(
|
|
|
241
243
|
return variant[valueKey] === null;
|
|
242
244
|
}
|
|
243
245
|
|
|
244
|
-
function filterDisabledVariants(
|
|
245
|
-
config: CVConfig<Variants, ComputedVariants, AnyComponent[]>,
|
|
246
|
-
variants: Record<string, unknown>,
|
|
247
|
-
): Record<string, unknown> {
|
|
248
|
-
const filtered: Record<string, unknown> = {};
|
|
249
|
-
for (const [key, value] of Object.entries(variants)) {
|
|
250
|
-
if (isVariantDisabled(config, key)) continue;
|
|
251
|
-
if (isVariantValueDisabled(config, key, value)) continue;
|
|
252
|
-
filtered[key] = value;
|
|
253
|
-
}
|
|
254
|
-
return filtered;
|
|
255
|
-
}
|
|
256
|
-
|
|
257
246
|
function collectDisabledVariantKeys(
|
|
258
247
|
config: CVConfig<Variants, ComputedVariants, AnyComponent[]>,
|
|
259
248
|
): Set<string> {
|
|
260
249
|
const keys = new Set<string>();
|
|
261
250
|
if (!config.variants) return keys;
|
|
262
|
-
for (const
|
|
263
|
-
if (
|
|
251
|
+
for (const key in config.variants) {
|
|
252
|
+
if (!hasOwn.call(config.variants, key)) continue;
|
|
253
|
+
if ((config.variants as Record<string, unknown>)[key] === null) {
|
|
264
254
|
keys.add(key);
|
|
265
255
|
}
|
|
266
256
|
}
|
|
@@ -272,162 +262,24 @@ function collectDisabledVariantValues(
|
|
|
272
262
|
): Record<string, Set<string>> {
|
|
273
263
|
const values: Record<string, Set<string>> = {};
|
|
274
264
|
if (!config.variants) return values;
|
|
275
|
-
for (const
|
|
265
|
+
for (const key in config.variants) {
|
|
266
|
+
if (!hasOwn.call(config.variants, key)) continue;
|
|
267
|
+
const variant = (config.variants as Record<string, unknown>)[key];
|
|
276
268
|
if (!isRecordObject(variant)) continue;
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
if (!
|
|
280
|
-
|
|
269
|
+
let bucket: Set<string> | undefined;
|
|
270
|
+
for (const variantValue in variant) {
|
|
271
|
+
if (!hasOwn.call(variant, variantValue)) continue;
|
|
272
|
+
if (variant[variantValue] !== null) continue;
|
|
273
|
+
if (!bucket) {
|
|
274
|
+
bucket = new Set<string>();
|
|
275
|
+
values[key] = bucket;
|
|
281
276
|
}
|
|
282
|
-
|
|
277
|
+
bucket.add(variantValue);
|
|
283
278
|
}
|
|
284
279
|
}
|
|
285
280
|
return values;
|
|
286
281
|
}
|
|
287
282
|
|
|
288
|
-
function mergeDisabledVariantValues(
|
|
289
|
-
base: Record<string, Set<string>>,
|
|
290
|
-
override: Record<string, Set<string>>,
|
|
291
|
-
): Record<string, Set<string>> {
|
|
292
|
-
const merged: Record<string, Set<string>> = {};
|
|
293
|
-
for (const [key, values] of Object.entries(base)) {
|
|
294
|
-
merged[key] = new Set(values);
|
|
295
|
-
}
|
|
296
|
-
for (const [key, values] of Object.entries(override)) {
|
|
297
|
-
if (!merged[key]) {
|
|
298
|
-
merged[key] = new Set<string>();
|
|
299
|
-
}
|
|
300
|
-
for (const value of values) {
|
|
301
|
-
merged[key].add(value);
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
return merged;
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
/**
|
|
308
|
-
* Collects static default variants from extended components and the current
|
|
309
|
-
* config. Also handles implicit boolean defaults (when only `false` key
|
|
310
|
-
* exists). This does NOT trigger computed functions - use collectDefaultVariants
|
|
311
|
-
* for that.
|
|
312
|
-
*/
|
|
313
|
-
function collectStaticDefaults(
|
|
314
|
-
config: CVConfig<Variants, ComputedVariants, AnyComponent[]>,
|
|
315
|
-
): Record<string, unknown> {
|
|
316
|
-
const defaults: Record<string, unknown> = {};
|
|
317
|
-
|
|
318
|
-
// Collect static defaults from extended components (via metadata to avoid
|
|
319
|
-
// triggering computed functions)
|
|
320
|
-
if (config.extend) {
|
|
321
|
-
for (const ext of config.extend) {
|
|
322
|
-
const meta = getComponentMeta(ext);
|
|
323
|
-
if (meta) {
|
|
324
|
-
Object.assign(defaults, meta.staticDefaults);
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
// Handle implicit boolean defaults from variants
|
|
330
|
-
// If a variant has a `false` key, default to false when no value is provided
|
|
331
|
-
if (config.variants) {
|
|
332
|
-
for (const [variantName, variantDef] of Object.entries(config.variants)) {
|
|
333
|
-
if (!isRecordObject(variantDef)) continue;
|
|
334
|
-
const keys = Object.keys(variantDef);
|
|
335
|
-
const hasFalse = keys.includes("false");
|
|
336
|
-
if (hasFalse && !defaults[variantName]) {
|
|
337
|
-
defaults[variantName] = false;
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
// Override with current config's static defaults
|
|
343
|
-
if (config.defaultVariants) {
|
|
344
|
-
Object.assign(defaults, config.defaultVariants);
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
return filterDisabledVariants(config, defaults);
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
/**
|
|
351
|
-
* Collects default variants from extended components and the current config.
|
|
352
|
-
* This includes both static defaults and computed defaults (from
|
|
353
|
-
* setDefaultVariants in extended components' computed functions). Priority:
|
|
354
|
-
* parent static < child static < parent computed < child computed.
|
|
355
|
-
*/
|
|
356
|
-
function collectDefaultVariants(
|
|
357
|
-
config: CVConfig<Variants, ComputedVariants, AnyComponent[]>,
|
|
358
|
-
propsVariants: Record<string, unknown> = {},
|
|
359
|
-
): Record<string, unknown> {
|
|
360
|
-
// Start with static defaults (parent static < child static)
|
|
361
|
-
const defaults = collectStaticDefaults(config);
|
|
362
|
-
|
|
363
|
-
// Apply computed defaults from extended components
|
|
364
|
-
// Parent's setDefaultVariants should override child's static defaults
|
|
365
|
-
if (!config.extend) return defaults;
|
|
366
|
-
|
|
367
|
-
// Pass full static defaults (not just this config's defaultVariants)
|
|
368
|
-
// so that intermediate components' defaults are visible to ancestors
|
|
369
|
-
for (const ext of config.extend) {
|
|
370
|
-
const meta = getComponentMeta(ext);
|
|
371
|
-
if (!meta) continue;
|
|
372
|
-
Object.assign(defaults, meta.resolveDefaults(defaults, propsVariants));
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
return filterDisabledVariants(config, defaults);
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
/**
|
|
379
|
-
* Filters out keys with undefined values from an object.
|
|
380
|
-
*/
|
|
381
|
-
function filterUndefined(
|
|
382
|
-
obj: Record<string, unknown>,
|
|
383
|
-
): Record<string, unknown> {
|
|
384
|
-
const result: Record<string, unknown> = {};
|
|
385
|
-
for (const [key, value] of Object.entries(obj)) {
|
|
386
|
-
if (value === undefined) continue;
|
|
387
|
-
result[key] = value;
|
|
388
|
-
}
|
|
389
|
-
return result;
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
/**
|
|
393
|
-
* Resolves variant values by merging defaults with provided props. Props with
|
|
394
|
-
* undefined values are filtered out so they don't override defaults.
|
|
395
|
-
*/
|
|
396
|
-
function resolveVariants(
|
|
397
|
-
config: CVConfig<Variants, ComputedVariants, AnyComponent[]>,
|
|
398
|
-
props: Record<string, unknown> = {},
|
|
399
|
-
): Record<string, unknown> {
|
|
400
|
-
const defaults = collectDefaultVariants(config, props);
|
|
401
|
-
return filterDisabledVariants(config, {
|
|
402
|
-
...defaults,
|
|
403
|
-
...filterUndefined(props),
|
|
404
|
-
});
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
/**
|
|
408
|
-
* Gets the value for a single variant based on the variant definition and the
|
|
409
|
-
* selected value.
|
|
410
|
-
*/
|
|
411
|
-
function getVariantResult(
|
|
412
|
-
variantDef: unknown,
|
|
413
|
-
selectedValue: unknown,
|
|
414
|
-
): { class: ClassValue; style: StyleValue } {
|
|
415
|
-
// Shorthand variant: `disabled: "disabled-class"` means { true: "..." }
|
|
416
|
-
if (!isRecordObject(variantDef)) {
|
|
417
|
-
if (selectedValue === true) {
|
|
418
|
-
return extractClassAndStyle(variantDef);
|
|
419
|
-
}
|
|
420
|
-
return { class: null, style: {} };
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
// Object variant: { sm: "...", lg: "..." }
|
|
424
|
-
const key = String(selectedValue);
|
|
425
|
-
const value = variantDef[key];
|
|
426
|
-
if (value === undefined) return { class: null, style: {} };
|
|
427
|
-
|
|
428
|
-
return extractClassAndStyle(value);
|
|
429
|
-
}
|
|
430
|
-
|
|
431
283
|
/**
|
|
432
284
|
* Extracts classes from fullClass that are not in baseClass. Uses string
|
|
433
285
|
* comparison optimization: if fullClass starts with baseClass, just take the
|
|
@@ -450,206 +302,23 @@ function extractVariantClasses(fullClass: string, baseClass: string): string {
|
|
|
450
302
|
.join(" ");
|
|
451
303
|
}
|
|
452
304
|
|
|
453
|
-
function computeExtendedStyles(
|
|
454
|
-
config: CVConfig<Variants, ComputedVariants, AnyComponent[]>,
|
|
455
|
-
resolvedVariants: Record<string, unknown>,
|
|
456
|
-
overrideVariantKeys: Set<string> = new Set(),
|
|
457
|
-
overrideVariantValues: Record<string, Set<string>> = {},
|
|
458
|
-
): {
|
|
459
|
-
baseClasses: ClassValue[];
|
|
460
|
-
variantClasses: ClassValue[];
|
|
461
|
-
style: StyleValue;
|
|
462
|
-
} {
|
|
463
|
-
const baseClasses: ClassValue[] = [];
|
|
464
|
-
const variantClasses: ClassValue[] = [];
|
|
465
|
-
const style: StyleValue = {};
|
|
466
|
-
|
|
467
|
-
if (!config.extend) return { baseClasses, variantClasses, style };
|
|
468
|
-
const hasOverrideVariantKeys = overrideVariantKeys.size > 0;
|
|
469
|
-
const hasOverrideVariantValues =
|
|
470
|
-
Object.keys(overrideVariantValues).length > 0;
|
|
471
|
-
|
|
472
|
-
for (const ext of config.extend) {
|
|
473
|
-
// Pass actual variant values but mark which keys should skip styling.
|
|
474
|
-
// Using a Symbol property keeps variant values clean for computed functions
|
|
475
|
-
// while still allowing us to skip styling for overridden keys.
|
|
476
|
-
const propsForExt: Record<string | symbol, unknown> = {
|
|
477
|
-
...resolvedVariants,
|
|
478
|
-
};
|
|
479
|
-
if (hasOverrideVariantKeys) {
|
|
480
|
-
propsForExt[SKIP_STYLE_KEYS] = overrideVariantKeys;
|
|
481
|
-
}
|
|
482
|
-
if (hasOverrideVariantValues) {
|
|
483
|
-
propsForExt[SKIP_STYLE_VARIANT_VALUES] = overrideVariantValues;
|
|
484
|
-
}
|
|
485
|
-
|
|
486
|
-
const extResult = ext(
|
|
487
|
-
propsForExt as ComponentProps<Record<string, unknown>>,
|
|
488
|
-
);
|
|
489
|
-
assign(style, normalizeStyle(extResult.style));
|
|
490
|
-
|
|
491
|
-
// Get base class from internal metadata (no variants)
|
|
492
|
-
const meta = getComponentMeta(ext);
|
|
493
|
-
const baseClass = meta?.baseClass ?? "";
|
|
494
|
-
baseClasses.push(baseClass);
|
|
495
|
-
|
|
496
|
-
// Get full class with variants
|
|
497
|
-
const fullClass =
|
|
498
|
-
"className" in extResult ? extResult.className : extResult.class;
|
|
499
|
-
|
|
500
|
-
const variantPortion = extractVariantClasses(fullClass, baseClass);
|
|
501
|
-
if (variantPortion) {
|
|
502
|
-
variantClasses.push(variantPortion);
|
|
503
|
-
}
|
|
504
|
-
}
|
|
505
|
-
|
|
506
|
-
return { baseClasses, variantClasses, style };
|
|
507
|
-
}
|
|
508
|
-
|
|
509
|
-
/**
|
|
510
|
-
* Computes class and style from the component's own variants and
|
|
511
|
-
* computedVariants (not extended components).
|
|
512
|
-
*/
|
|
513
|
-
function computeVariantStyles(
|
|
514
|
-
config: CVConfig<Variants, ComputedVariants, AnyComponent[]>,
|
|
515
|
-
resolvedVariants: Record<string | symbol, unknown>,
|
|
516
|
-
skipStyleKeys: Set<string> = new Set(),
|
|
517
|
-
skipVariantValues: Record<string, Set<string>> = {},
|
|
518
|
-
): { classes: ClassValue[]; style: StyleValue } {
|
|
519
|
-
const classes: ClassValue[] = [];
|
|
520
|
-
const style: StyleValue = {};
|
|
521
|
-
|
|
522
|
-
// Process current component's variants
|
|
523
|
-
if (config.variants) {
|
|
524
|
-
for (const [variantName, variantDef] of Object.entries(config.variants)) {
|
|
525
|
-
// Skip styling for variants that are overridden by child's computedVariants
|
|
526
|
-
if (skipStyleKeys.has(variantName)) continue;
|
|
527
|
-
|
|
528
|
-
const selectedValue = resolvedVariants[variantName];
|
|
529
|
-
if (selectedValue === undefined) continue;
|
|
530
|
-
const selectedKey = getVariantValueKey(selectedValue);
|
|
531
|
-
if (selectedKey && skipVariantValues[variantName]?.has(selectedKey)) {
|
|
532
|
-
continue;
|
|
533
|
-
}
|
|
534
|
-
|
|
535
|
-
const result = getVariantResult(variantDef, selectedValue);
|
|
536
|
-
classes.push(result.class);
|
|
537
|
-
assign(style, result.style);
|
|
538
|
-
}
|
|
539
|
-
}
|
|
540
|
-
|
|
541
|
-
// Process computedVariants
|
|
542
|
-
if (config.computedVariants) {
|
|
543
|
-
for (const [variantName, computeFn] of Object.entries(
|
|
544
|
-
config.computedVariants,
|
|
545
|
-
)) {
|
|
546
|
-
// Skip styling for variants that are overridden by child's computedVariants
|
|
547
|
-
if (skipStyleKeys.has(variantName)) continue;
|
|
548
|
-
|
|
549
|
-
const selectedValue = resolvedVariants[variantName];
|
|
550
|
-
if (selectedValue === undefined) continue;
|
|
551
|
-
const selectedKey = getVariantValueKey(selectedValue);
|
|
552
|
-
if (selectedKey && skipVariantValues[variantName]?.has(selectedKey)) {
|
|
553
|
-
continue;
|
|
554
|
-
}
|
|
555
|
-
|
|
556
|
-
const computedResult = computeFn(selectedValue);
|
|
557
|
-
const result = extractClassAndStyle(computedResult);
|
|
558
|
-
classes.push(result.class);
|
|
559
|
-
assign(style, result.style);
|
|
560
|
-
}
|
|
561
|
-
}
|
|
562
|
-
|
|
563
|
-
return { classes, style };
|
|
564
|
-
}
|
|
565
|
-
|
|
566
|
-
/**
|
|
567
|
-
* Runs the computed function if present, returning classes, styles, and updated
|
|
568
|
-
* variants.
|
|
569
|
-
*/
|
|
570
|
-
function runComputedFunction(
|
|
571
|
-
config: CVConfig<Variants, ComputedVariants, AnyComponent[]>,
|
|
572
|
-
resolvedVariants: Record<string, unknown>,
|
|
573
|
-
propsVariants: Record<string, unknown>,
|
|
574
|
-
): {
|
|
575
|
-
classes: ClassValue[];
|
|
576
|
-
style: StyleValue;
|
|
577
|
-
updatedVariants: Record<string, unknown>;
|
|
578
|
-
} {
|
|
579
|
-
const classes: ClassValue[] = [];
|
|
580
|
-
const style: StyleValue = {};
|
|
581
|
-
const updatedVariants = { ...resolvedVariants };
|
|
582
|
-
|
|
583
|
-
if (!config.computed) {
|
|
584
|
-
return { classes, style, updatedVariants };
|
|
585
|
-
}
|
|
586
|
-
|
|
587
|
-
const context = {
|
|
588
|
-
variants: resolvedVariants,
|
|
589
|
-
setVariants: (newVariants: VariantValues<Record<string, unknown>>) => {
|
|
590
|
-
Object.assign(
|
|
591
|
-
updatedVariants,
|
|
592
|
-
filterDisabledVariants(config, newVariants),
|
|
593
|
-
);
|
|
594
|
-
},
|
|
595
|
-
setDefaultVariants: (
|
|
596
|
-
newDefaults: VariantValues<Record<string, unknown>>,
|
|
597
|
-
) => {
|
|
598
|
-
// Only apply defaults for variants not explicitly set in props
|
|
599
|
-
for (const [key, value] of Object.entries(newDefaults)) {
|
|
600
|
-
if (propsVariants[key] === undefined) {
|
|
601
|
-
if (isVariantDisabled(config, key)) continue;
|
|
602
|
-
if (isVariantValueDisabled(config, key, value)) continue;
|
|
603
|
-
updatedVariants[key] = value;
|
|
604
|
-
}
|
|
605
|
-
}
|
|
606
|
-
},
|
|
607
|
-
addClass: (className: ClassValue) => {
|
|
608
|
-
classes.push(className);
|
|
609
|
-
},
|
|
610
|
-
addStyle: (newStyle: StyleValue) => {
|
|
611
|
-
assign(style, newStyle);
|
|
612
|
-
},
|
|
613
|
-
};
|
|
614
|
-
|
|
615
|
-
const computedResult = config.computed(context);
|
|
616
|
-
if (computedResult != null) {
|
|
617
|
-
const result = extractClassAndStyle(computedResult);
|
|
618
|
-
classes.push(result.class);
|
|
619
|
-
assign(style, result.style);
|
|
620
|
-
}
|
|
621
|
-
|
|
622
|
-
return {
|
|
623
|
-
classes,
|
|
624
|
-
style,
|
|
625
|
-
updatedVariants: filterDisabledVariants(config, updatedVariants),
|
|
626
|
-
};
|
|
627
|
-
}
|
|
628
|
-
|
|
629
305
|
interface NormalizedSource {
|
|
630
306
|
keys: string[];
|
|
631
307
|
variantKeys: string[];
|
|
632
|
-
defaults: Record<string, unknown>;
|
|
633
308
|
isComponent: boolean;
|
|
634
309
|
}
|
|
635
310
|
|
|
636
311
|
const EMPTY_SOURCE: NormalizedSource = {
|
|
637
312
|
keys: [],
|
|
638
313
|
variantKeys: [],
|
|
639
|
-
defaults: {},
|
|
640
314
|
isComponent: false,
|
|
641
315
|
};
|
|
642
316
|
|
|
643
|
-
/**
|
|
644
|
-
* Normalizes a key source (array or component) to an object with keys,
|
|
645
|
-
* variantKeys, defaults, and isComponent flag.
|
|
646
|
-
*/
|
|
647
317
|
function normalizeKeySource(source: unknown): NormalizedSource {
|
|
648
318
|
if (Array.isArray(source)) {
|
|
649
319
|
return {
|
|
650
320
|
keys: source as string[],
|
|
651
321
|
variantKeys: source as string[],
|
|
652
|
-
defaults: {},
|
|
653
322
|
isComponent: false,
|
|
654
323
|
};
|
|
655
324
|
}
|
|
@@ -661,16 +330,14 @@ function normalizeKeySource(source: unknown): NormalizedSource {
|
|
|
661
330
|
if (!("keys" in source)) return EMPTY_SOURCE;
|
|
662
331
|
if (!("variantKeys" in source)) return EMPTY_SOURCE;
|
|
663
332
|
|
|
664
|
-
//
|
|
333
|
+
// Component-provided arrays are immutable metadata — reference directly.
|
|
665
334
|
const typed = source as {
|
|
666
335
|
keys: string[];
|
|
667
336
|
variantKeys: string[];
|
|
668
|
-
getVariants?: () => Record<string, unknown>;
|
|
669
337
|
};
|
|
670
338
|
return {
|
|
671
|
-
keys:
|
|
672
|
-
variantKeys:
|
|
673
|
-
defaults: typed.getVariants?.() ?? {},
|
|
339
|
+
keys: typed.keys,
|
|
340
|
+
variantKeys: typed.variantKeys,
|
|
674
341
|
isComponent: true,
|
|
675
342
|
};
|
|
676
343
|
}
|
|
@@ -685,53 +352,64 @@ function splitPropsImpl(
|
|
|
685
352
|
selfKeys: string[],
|
|
686
353
|
selfIsComponent: boolean,
|
|
687
354
|
props: Record<string, unknown>,
|
|
688
|
-
sources:
|
|
355
|
+
sources: unknown[],
|
|
689
356
|
): Record<string, unknown>[] {
|
|
690
|
-
const
|
|
357
|
+
const sourcesLength = sources.length;
|
|
691
358
|
const results: Record<string, unknown>[] = [];
|
|
692
|
-
|
|
693
|
-
// Track if styling has been claimed by a component
|
|
694
359
|
let stylingClaimed = selfIsComponent;
|
|
695
360
|
|
|
696
|
-
// Self result
|
|
697
361
|
const selfResult: Record<string, unknown> = {};
|
|
698
|
-
|
|
699
|
-
|
|
362
|
+
const selfKeysLength = selfKeys.length;
|
|
363
|
+
for (let i = 0; i < selfKeysLength; i++) {
|
|
364
|
+
const key = selfKeys[i];
|
|
365
|
+
if (key !== undefined && key in props) {
|
|
700
366
|
selfResult[key] = props[key];
|
|
701
367
|
}
|
|
702
368
|
}
|
|
703
369
|
results.push(selfResult);
|
|
704
370
|
|
|
705
|
-
//
|
|
706
|
-
|
|
371
|
+
// Track effective key arrays for the rest computation — for typical inputs
|
|
372
|
+
// a linear scan beats building a Set up-front.
|
|
373
|
+
const effectiveKeyArrays: string[][] = [selfKeys];
|
|
374
|
+
|
|
375
|
+
for (let s = 0; s < sourcesLength; s++) {
|
|
376
|
+
const source = normalizeKeySource(sources[s]);
|
|
707
377
|
const sourceResult: Record<string, unknown> = {};
|
|
708
378
|
|
|
709
|
-
// Determine which keys this source should use
|
|
710
|
-
// Components use variantKeys if styling has already been claimed
|
|
711
|
-
// Arrays always use their listed keys
|
|
712
379
|
const effectiveKeys =
|
|
713
380
|
source.isComponent && stylingClaimed ? source.variantKeys : source.keys;
|
|
714
381
|
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
382
|
+
const effectiveKeysLength = effectiveKeys.length;
|
|
383
|
+
for (let i = 0; i < effectiveKeysLength; i++) {
|
|
384
|
+
const key = effectiveKeys[i];
|
|
385
|
+
if (key !== undefined && key in props) {
|
|
718
386
|
sourceResult[key] = props[key];
|
|
719
387
|
}
|
|
720
388
|
}
|
|
721
389
|
results.push(sourceResult);
|
|
390
|
+
effectiveKeyArrays.push(effectiveKeys);
|
|
722
391
|
|
|
723
|
-
// If this is a component that hasn't claimed styling yet, mark styling as claimed
|
|
724
392
|
if (source.isComponent && !stylingClaimed) {
|
|
725
393
|
stylingClaimed = true;
|
|
726
394
|
}
|
|
727
395
|
}
|
|
728
396
|
|
|
729
|
-
// Rest - keys not used by anyone
|
|
730
397
|
const rest: Record<string, unknown> = {};
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
398
|
+
const propKeys = Object.keys(props);
|
|
399
|
+
const propKeysLength = propKeys.length;
|
|
400
|
+
const groupCount = sourcesLength + 1;
|
|
401
|
+
outer: for (let i = 0; i < propKeysLength; i++) {
|
|
402
|
+
const key = propKeys[i];
|
|
403
|
+
if (key === undefined) continue;
|
|
404
|
+
for (let g = 0; g < groupCount; g++) {
|
|
405
|
+
const arr = effectiveKeyArrays[g];
|
|
406
|
+
if (arr === undefined) continue;
|
|
407
|
+
const arrLength = arr.length;
|
|
408
|
+
for (let j = 0; j < arrLength; j++) {
|
|
409
|
+
if (arr[j] === key) continue outer;
|
|
410
|
+
}
|
|
734
411
|
}
|
|
412
|
+
rest[key] = props[key];
|
|
735
413
|
}
|
|
736
414
|
results.push(rest);
|
|
737
415
|
|
|
@@ -745,16 +423,6 @@ function splitPropsImpl(
|
|
|
745
423
|
* only receive variant props. Arrays receive their listed keys but don't claim
|
|
746
424
|
* styling props. The last element is always the "rest" containing keys not
|
|
747
425
|
* claimed by any source.
|
|
748
|
-
* @example
|
|
749
|
-
* ```ts
|
|
750
|
-
* const [buttonProps, inputProps, rest] = splitProps(
|
|
751
|
-
* props,
|
|
752
|
-
* buttonComponent,
|
|
753
|
-
* inputComponent,
|
|
754
|
-
* );
|
|
755
|
-
* // buttonProps has class/style + button variants
|
|
756
|
-
* // inputProps has only input variants (no class/style)
|
|
757
|
-
* ```
|
|
758
426
|
*/
|
|
759
427
|
export const splitProps: SplitPropsFunction = ((
|
|
760
428
|
props: Record<string, unknown>,
|
|
@@ -762,15 +430,56 @@ export const splitProps: SplitPropsFunction = ((
|
|
|
762
430
|
...sources: unknown[]
|
|
763
431
|
) => {
|
|
764
432
|
const normalizedSource1 = normalizeKeySource(source1);
|
|
765
|
-
const normalizedSources = sources.map(normalizeKeySource);
|
|
766
433
|
return splitPropsImpl(
|
|
767
434
|
normalizedSource1.keys,
|
|
768
435
|
normalizedSource1.isComponent,
|
|
769
436
|
props,
|
|
770
|
-
|
|
437
|
+
sources,
|
|
771
438
|
);
|
|
772
439
|
}) as SplitPropsFunction;
|
|
773
440
|
|
|
441
|
+
/**
|
|
442
|
+
* A pre-built variant. Maps variant value keys (or the literal "true"/"false"
|
|
443
|
+
* strings for boolean variants) to PrebuiltValue. Includes a "shorthand"
|
|
444
|
+
* fallback when the variant is a single class value (treated as `{ true: ... }`).
|
|
445
|
+
*/
|
|
446
|
+
interface PrebuiltVariant {
|
|
447
|
+
// For object variants: map of value -> prebuilt class/style
|
|
448
|
+
values: Record<string, PrebuiltValue> | null;
|
|
449
|
+
// For shorthand variants: the value to use when selectedValue is true
|
|
450
|
+
shorthand: PrebuiltValue | null;
|
|
451
|
+
// Set of value keys that are disabled (value === null in the original variant
|
|
452
|
+
// definition)
|
|
453
|
+
disabledValues: Set<string> | null;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
function buildPrebuiltVariant(variantDef: unknown): PrebuiltVariant {
|
|
457
|
+
if (!isRecordObject(variantDef)) {
|
|
458
|
+
return {
|
|
459
|
+
values: null,
|
|
460
|
+
shorthand: extractClassAndStylePrebuilt(variantDef),
|
|
461
|
+
disabledValues: null,
|
|
462
|
+
};
|
|
463
|
+
}
|
|
464
|
+
const values: Record<string, PrebuiltValue> = {};
|
|
465
|
+
let disabledValues: Set<string> | null = null;
|
|
466
|
+
for (const key in variantDef) {
|
|
467
|
+
if (!hasOwn.call(variantDef, key)) continue;
|
|
468
|
+
const value = variantDef[key];
|
|
469
|
+
if (value === null) {
|
|
470
|
+
if (!disabledValues) disabledValues = new Set<string>();
|
|
471
|
+
disabledValues.add(key);
|
|
472
|
+
continue;
|
|
473
|
+
}
|
|
474
|
+
values[key] = extractClassAndStylePrebuilt(value);
|
|
475
|
+
}
|
|
476
|
+
return {
|
|
477
|
+
values,
|
|
478
|
+
shorthand: null,
|
|
479
|
+
disabledValues,
|
|
480
|
+
};
|
|
481
|
+
}
|
|
482
|
+
|
|
774
483
|
/**
|
|
775
484
|
* Creates the resolveDefaults function for a component. This function returns
|
|
776
485
|
* only the variants set via setDefaultVariants in the computed function. Used
|
|
@@ -778,57 +487,61 @@ export const splitProps: SplitPropsFunction = ((
|
|
|
778
487
|
*/
|
|
779
488
|
function createResolveDefaults(
|
|
780
489
|
config: CVConfig<Variants, ComputedVariants, AnyComponent[]>,
|
|
490
|
+
staticDefaults: Record<string, unknown>,
|
|
781
491
|
): ComponentMeta["resolveDefaults"] {
|
|
492
|
+
const computed = config.computed;
|
|
493
|
+
const extend = config.extend;
|
|
782
494
|
return (childDefaults, userProps = {}) => {
|
|
783
|
-
// Get static defaults (including from extended components)
|
|
784
|
-
const staticDefaults = collectStaticDefaults(config);
|
|
785
|
-
|
|
786
495
|
// Merge: parent static < child static < user props
|
|
787
496
|
// This is what parent's computed will see in `variants`
|
|
788
|
-
const resolvedVariants = {
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
497
|
+
const resolvedVariants: Record<string, unknown> = {};
|
|
498
|
+
Object.assign(resolvedVariants, staticDefaults);
|
|
499
|
+
for (const key in childDefaults) {
|
|
500
|
+
if (!hasOwn.call(childDefaults, key)) continue;
|
|
501
|
+
const v = childDefaults[key];
|
|
502
|
+
if (v === undefined) continue;
|
|
503
|
+
resolvedVariants[key] = v;
|
|
504
|
+
}
|
|
505
|
+
for (const key in userProps) {
|
|
506
|
+
if (!hasOwn.call(userProps, key)) continue;
|
|
507
|
+
const v = userProps[key];
|
|
508
|
+
if (v === undefined) continue;
|
|
509
|
+
resolvedVariants[key] = v;
|
|
510
|
+
}
|
|
793
511
|
|
|
794
512
|
// Track which keys are set via setDefaultVariants
|
|
795
513
|
const computedDefaults: Record<string, unknown> = {};
|
|
796
514
|
|
|
797
515
|
// Propagate to extended components so their computed functions can run
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
for (const ext of config.extend) {
|
|
516
|
+
if (extend) {
|
|
517
|
+
for (const ext of extend) {
|
|
801
518
|
const meta = getComponentMeta(ext);
|
|
802
519
|
if (!meta) continue;
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
520
|
+
const extDefaults = meta.resolveDefaults(childDefaults, userProps);
|
|
521
|
+
for (const k in extDefaults) {
|
|
522
|
+
if (hasOwn.call(extDefaults, k)) {
|
|
523
|
+
computedDefaults[k] = extDefaults[k];
|
|
524
|
+
}
|
|
525
|
+
}
|
|
807
526
|
}
|
|
808
527
|
}
|
|
809
528
|
|
|
810
|
-
if (
|
|
811
|
-
|
|
529
|
+
if (computed) {
|
|
530
|
+
computed({
|
|
812
531
|
variants: resolvedVariants as VariantValues<Record<string, unknown>>,
|
|
813
|
-
setVariants: () => {
|
|
814
|
-
// Not relevant for collecting defaults
|
|
815
|
-
},
|
|
532
|
+
setVariants: () => {},
|
|
816
533
|
setDefaultVariants: (newDefaults) => {
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
534
|
+
for (const key in newDefaults) {
|
|
535
|
+
if (!hasOwn.call(newDefaults, key)) continue;
|
|
536
|
+
const value = (newDefaults as Record<string, unknown>)[key];
|
|
820
537
|
if (userProps[key] !== undefined) continue;
|
|
821
538
|
if (isVariantDisabled(config, key)) continue;
|
|
822
539
|
if (isVariantValueDisabled(config, key, value)) continue;
|
|
823
540
|
computedDefaults[key] = value;
|
|
824
541
|
}
|
|
825
542
|
},
|
|
826
|
-
addClass: () => {
|
|
827
|
-
|
|
828
|
-
},
|
|
829
|
-
addStyle: () => {
|
|
830
|
-
// Not relevant for collecting defaults
|
|
831
|
-
},
|
|
543
|
+
addClass: () => {},
|
|
544
|
+
addStyle: () => {},
|
|
832
545
|
});
|
|
833
546
|
}
|
|
834
547
|
|
|
@@ -853,115 +566,512 @@ export function create({
|
|
|
853
566
|
): CVComponent<V, CV, E> => {
|
|
854
567
|
type MergedVariants = MergeVariants<V, CV, E>;
|
|
855
568
|
|
|
569
|
+
// ----- Pre-computed at creation time -----
|
|
856
570
|
const variantKeys = collectVariantKeys(config);
|
|
857
571
|
const disabledVariantKeys = collectDisabledVariantKeys(config);
|
|
858
572
|
const disabledVariantValues = collectDisabledVariantValues(config);
|
|
573
|
+
const hasDisabledVariantKeys = disabledVariantKeys.size > 0;
|
|
574
|
+
const disabledVariantValueKeys = Object.keys(disabledVariantValues);
|
|
575
|
+
const hasDisabledVariantValues = disabledVariantValueKeys.length > 0;
|
|
576
|
+
const hasAnyDisabled = hasDisabledVariantKeys || hasDisabledVariantValues;
|
|
577
|
+
|
|
859
578
|
const inputPropsKeys = ["class", "className", "style", ...variantKeys];
|
|
860
579
|
|
|
861
|
-
const
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
580
|
+
const extend = config.extend;
|
|
581
|
+
const hasExtend = !!extend && extend.length > 0;
|
|
582
|
+
const variants = config.variants;
|
|
583
|
+
const computedVariantsCfg = config.computedVariants;
|
|
584
|
+
const computed = config.computed;
|
|
585
|
+
const baseStyle = config.style;
|
|
586
|
+
const baseClass: ClassValue =
|
|
587
|
+
config.class === undefined ? null : (config.class as ClassValue);
|
|
588
|
+
|
|
589
|
+
// Pre-build variant entries for fast iteration. For each variant key in
|
|
590
|
+
// `variants`, we have a name and a PrebuiltVariant with normalized values.
|
|
591
|
+
const variantEntryNames: string[] = [];
|
|
592
|
+
const variantEntryDefs: PrebuiltVariant[] = [];
|
|
593
|
+
if (variants) {
|
|
594
|
+
for (const name in variants) {
|
|
595
|
+
if (!hasOwn.call(variants, name)) continue;
|
|
596
|
+
const variant = (variants as Record<string, unknown>)[name];
|
|
597
|
+
if (variant === null) continue;
|
|
598
|
+
variantEntryNames.push(name);
|
|
599
|
+
variantEntryDefs.push(buildPrebuiltVariant(variant));
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
const variantEntryCount = variantEntryNames.length;
|
|
603
|
+
|
|
604
|
+
// Pre-built computed-variants entries.
|
|
605
|
+
const computedVariantNames: string[] = [];
|
|
606
|
+
const computedVariantFns: Array<(value: unknown) => unknown> = [];
|
|
607
|
+
if (computedVariantsCfg) {
|
|
608
|
+
for (const name in computedVariantsCfg) {
|
|
609
|
+
if (!hasOwn.call(computedVariantsCfg, name)) continue;
|
|
610
|
+
computedVariantNames.push(name);
|
|
611
|
+
computedVariantFns.push(
|
|
612
|
+
(computedVariantsCfg as Record<string, (value: unknown) => unknown>)[
|
|
613
|
+
name
|
|
614
|
+
] as (value: unknown) => unknown,
|
|
615
|
+
);
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
const computedVariantCount = computedVariantNames.length;
|
|
619
|
+
|
|
620
|
+
// Pre-compute static defaults. Includes:
|
|
621
|
+
// - extended components' static defaults
|
|
622
|
+
// - implicit boolean defaults (variants with a `false` key default to false)
|
|
623
|
+
// - this config's defaultVariants (overriding the above)
|
|
624
|
+
// Then filtered through disabled-variants.
|
|
625
|
+
const staticDefaults: Record<string, unknown> = {};
|
|
626
|
+
if (extend) {
|
|
627
|
+
for (const ext of extend) {
|
|
628
|
+
const meta = getComponentMeta(ext);
|
|
629
|
+
if (meta) Object.assign(staticDefaults, meta.staticDefaults);
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
if (variants) {
|
|
633
|
+
for (const name in variants) {
|
|
634
|
+
if (!hasOwn.call(variants, name)) continue;
|
|
635
|
+
const variantDef = (variants as Record<string, unknown>)[name];
|
|
636
|
+
if (!isRecordObject(variantDef)) continue;
|
|
637
|
+
if (
|
|
638
|
+
hasOwn.call(variantDef, "false") &&
|
|
639
|
+
staticDefaults[name] === undefined
|
|
640
|
+
) {
|
|
641
|
+
staticDefaults[name] = false;
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
if (config.defaultVariants) {
|
|
646
|
+
Object.assign(staticDefaults, config.defaultVariants);
|
|
647
|
+
}
|
|
648
|
+
if (hasAnyDisabled) {
|
|
649
|
+
// Filter disabled variants in-place
|
|
650
|
+
for (const key in staticDefaults) {
|
|
651
|
+
if (!hasOwn.call(staticDefaults, key)) continue;
|
|
652
|
+
if (disabledVariantKeys.has(key)) {
|
|
653
|
+
delete staticDefaults[key];
|
|
654
|
+
continue;
|
|
655
|
+
}
|
|
656
|
+
if (hasDisabledVariantValues) {
|
|
657
|
+
const value = staticDefaults[key];
|
|
658
|
+
const valueKey = getVariantValueKey(value);
|
|
659
|
+
if (valueKey != null && disabledVariantValues[key]?.has(valueKey)) {
|
|
660
|
+
delete staticDefaults[key];
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
// Pre-build extended component info, so we don't have to call
|
|
667
|
+
// `getComponentMeta` per render.
|
|
668
|
+
const extEntries: AnyComponent[] = extend ? (extend as AnyComponent[]) : [];
|
|
669
|
+
const extBaseClassesArr: string[] = [];
|
|
670
|
+
const extMetas: (ComponentMeta | undefined)[] = [];
|
|
671
|
+
if (extend) {
|
|
672
|
+
for (const ext of extend) {
|
|
673
|
+
const meta = getComponentMeta(ext);
|
|
674
|
+
extMetas.push(meta);
|
|
675
|
+
extBaseClassesArr.push(meta?.baseClass ?? "");
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
const extCount = extEntries.length;
|
|
679
|
+
|
|
680
|
+
// Inlined "filter disabled" - mutates `out` adding only allowed entries.
|
|
681
|
+
// Most components have no disabled variants, in which case we can skip
|
|
682
|
+
// the filter entirely.
|
|
683
|
+
function filterDisabledInto(
|
|
684
|
+
input: Record<string, unknown>,
|
|
685
|
+
out: Record<string, unknown>,
|
|
686
|
+
): void {
|
|
687
|
+
if (!hasAnyDisabled) {
|
|
688
|
+
for (const key in input) {
|
|
689
|
+
if (hasOwn.call(input, key)) out[key] = input[key];
|
|
690
|
+
}
|
|
691
|
+
return;
|
|
692
|
+
}
|
|
693
|
+
for (const key in input) {
|
|
694
|
+
if (!hasOwn.call(input, key)) continue;
|
|
695
|
+
if (disabledVariantKeys.has(key)) continue;
|
|
696
|
+
const value = input[key];
|
|
697
|
+
if (hasDisabledVariantValues) {
|
|
698
|
+
const valueKey = getVariantValueKey(value);
|
|
699
|
+
if (valueKey != null && disabledVariantValues[key]?.has(valueKey)) {
|
|
700
|
+
continue;
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
out[key] = value;
|
|
704
|
+
}
|
|
705
|
+
}
|
|
866
706
|
|
|
707
|
+
// Pre-create default-variants resolver which is referenced during the hot
|
|
708
|
+
// path through extended components' meta. The closure captures
|
|
709
|
+
// staticDefaults, extend, computed, etc.
|
|
710
|
+
const resolveDefaultsFn = createResolveDefaults(config, staticDefaults);
|
|
711
|
+
|
|
712
|
+
// Resolve variants: defaults -> computed defaults from extended -> props.
|
|
713
|
+
function resolveVariantsHot(
|
|
714
|
+
propsVariants: Record<string, unknown>,
|
|
715
|
+
): Record<string, unknown> {
|
|
716
|
+
// Start with static defaults
|
|
717
|
+
const defaults: Record<string, unknown> = {};
|
|
718
|
+
Object.assign(defaults, staticDefaults);
|
|
719
|
+
|
|
720
|
+
// Apply computed defaults from extended components
|
|
721
|
+
if (hasExtend) {
|
|
722
|
+
for (let i = 0; i < extCount; i++) {
|
|
723
|
+
const meta = extMetas[i];
|
|
724
|
+
if (!meta) continue;
|
|
725
|
+
const extComputed = meta.resolveDefaults(defaults, propsVariants);
|
|
726
|
+
for (const k in extComputed) {
|
|
727
|
+
if (hasOwn.call(extComputed, k)) {
|
|
728
|
+
defaults[k] = extComputed[k];
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
// Now merge: defaults < propsVariants (filter undefined)
|
|
735
|
+
// Apply propsVariants on top
|
|
736
|
+
for (const k in propsVariants) {
|
|
737
|
+
if (!hasOwn.call(propsVariants, k)) continue;
|
|
738
|
+
const v = propsVariants[k];
|
|
739
|
+
if (v === undefined) continue;
|
|
740
|
+
defaults[k] = v;
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
if (!hasAnyDisabled) return defaults;
|
|
744
|
+
|
|
745
|
+
// Filter disabled
|
|
746
|
+
const result: Record<string, unknown> = {};
|
|
747
|
+
filterDisabledInto(defaults, result);
|
|
748
|
+
return result;
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
// Hot path: build a fresh result.
|
|
867
752
|
const computeResult = (
|
|
868
753
|
props: ComponentProps<MergedVariants> = {},
|
|
869
754
|
): { className: string; style: StyleValue } => {
|
|
870
|
-
const allClasses: ClassValue[] = [];
|
|
871
|
-
const allStyle: StyleValue = {};
|
|
872
|
-
|
|
873
755
|
// Extract skip style keys from props (set by child's computedVariants)
|
|
874
|
-
const
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
// Extract variant props from input
|
|
756
|
+
const skipStyleKeysIn = (props as Record<symbol, unknown>)[
|
|
757
|
+
SKIP_STYLE_KEYS
|
|
758
|
+
] as Set<string> | undefined;
|
|
759
|
+
const skipStyleVariantValuesIn = (props as Record<symbol, unknown>)[
|
|
760
|
+
SKIP_STYLE_VARIANT_VALUES
|
|
761
|
+
] as Record<string, Set<string>> | undefined;
|
|
762
|
+
|
|
763
|
+
// Extract variant props from input. Also remember the propsVariants for
|
|
764
|
+
// computed-defaults application.
|
|
884
765
|
const variantProps: Record<string, unknown> = {};
|
|
885
|
-
for (
|
|
766
|
+
for (let i = 0; i < variantKeys.length; i++) {
|
|
767
|
+
const key = variantKeys[i];
|
|
886
768
|
if (key in props) {
|
|
887
|
-
variantProps[key] = props[key];
|
|
769
|
+
variantProps[key] = (props as Record<string, unknown>)[key];
|
|
888
770
|
}
|
|
889
771
|
}
|
|
772
|
+
|
|
890
773
|
// Resolve variants with defaults
|
|
891
|
-
let resolvedVariants =
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
)
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
774
|
+
let resolvedVariants = resolveVariantsHot(variantProps);
|
|
775
|
+
|
|
776
|
+
// Run computed function (may update variants and emit class/style)
|
|
777
|
+
let computedClassesArr: ClassValue[] | null = null;
|
|
778
|
+
let computedStyleObj: StyleValue | null = null;
|
|
779
|
+
|
|
780
|
+
if (computed) {
|
|
781
|
+
const updatedVariants: Record<string, unknown> = {};
|
|
782
|
+
Object.assign(updatedVariants, resolvedVariants);
|
|
783
|
+
const cClasses: ClassValue[] = [];
|
|
784
|
+
let cStyle: StyleValue | null = null;
|
|
785
|
+
const ctx = {
|
|
786
|
+
variants: resolvedVariants as VariantValues<Record<string, unknown>>,
|
|
787
|
+
setVariants: (
|
|
788
|
+
newVariants: VariantValues<Record<string, unknown>>,
|
|
789
|
+
) => {
|
|
790
|
+
if (!hasAnyDisabled) {
|
|
791
|
+
Object.assign(updatedVariants, newVariants);
|
|
792
|
+
} else {
|
|
793
|
+
const filtered: Record<string, unknown> = {};
|
|
794
|
+
filterDisabledInto(
|
|
795
|
+
newVariants as Record<string, unknown>,
|
|
796
|
+
filtered,
|
|
797
|
+
);
|
|
798
|
+
Object.assign(updatedVariants, filtered);
|
|
799
|
+
}
|
|
800
|
+
},
|
|
801
|
+
setDefaultVariants: (
|
|
802
|
+
newDefaults: VariantValues<Record<string, unknown>>,
|
|
803
|
+
) => {
|
|
804
|
+
for (const key in newDefaults) {
|
|
805
|
+
if (!hasOwn.call(newDefaults, key)) continue;
|
|
806
|
+
if (variantProps[key] !== undefined) continue;
|
|
807
|
+
const value = (newDefaults as Record<string, unknown>)[key];
|
|
808
|
+
if (hasAnyDisabled) {
|
|
809
|
+
if (disabledVariantKeys.has(key)) continue;
|
|
810
|
+
const valueKey = getVariantValueKey(value);
|
|
811
|
+
if (
|
|
812
|
+
valueKey != null &&
|
|
813
|
+
disabledVariantValues[key]?.has(valueKey)
|
|
814
|
+
) {
|
|
815
|
+
continue;
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
updatedVariants[key] = value;
|
|
819
|
+
}
|
|
820
|
+
},
|
|
821
|
+
addClass: (className: ClassValue) => {
|
|
822
|
+
cClasses.push(className);
|
|
823
|
+
},
|
|
824
|
+
addStyle: (newStyle: StyleValue) => {
|
|
825
|
+
if (!cStyle) cStyle = {};
|
|
826
|
+
assign(cStyle, newStyle);
|
|
827
|
+
},
|
|
828
|
+
};
|
|
829
|
+
const result = computed(ctx);
|
|
830
|
+
if (result != null) {
|
|
831
|
+
const r = extractClassAndStylePrebuilt(result);
|
|
832
|
+
if (r.class != null) cClasses.push(r.class);
|
|
833
|
+
if (r.style) {
|
|
834
|
+
if (!cStyle) cStyle = {};
|
|
835
|
+
assign(cStyle, r.style);
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
if (hasAnyDisabled) {
|
|
839
|
+
const filteredUpdated: Record<string, unknown> = {};
|
|
840
|
+
filterDisabledInto(updatedVariants, filteredUpdated);
|
|
841
|
+
resolvedVariants = filteredUpdated;
|
|
842
|
+
} else {
|
|
843
|
+
resolvedVariants = updatedVariants;
|
|
844
|
+
}
|
|
845
|
+
computedClassesArr = cClasses;
|
|
846
|
+
computedStyleObj = cStyle;
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
// Compute skip-style sets for the extended components and current
|
|
850
|
+
// component. Only allocate when needed.
|
|
851
|
+
const hasSkipKeys = !!skipStyleKeysIn || hasDisabledVariantKeys;
|
|
852
|
+
let currentVariantKeys: Set<string> | null = null;
|
|
853
|
+
if (hasSkipKeys) {
|
|
854
|
+
currentVariantKeys = new Set<string>();
|
|
855
|
+
if (skipStyleKeysIn) {
|
|
856
|
+
for (const k of skipStyleKeysIn) currentVariantKeys.add(k);
|
|
857
|
+
}
|
|
858
|
+
for (const k of disabledVariantKeys) currentVariantKeys.add(k);
|
|
905
859
|
}
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
860
|
+
// computedVariantKeys is currentVariantKeys + computedVariants names
|
|
861
|
+
let computedVariantKeysSet: Set<string> | null = null;
|
|
862
|
+
if (hasExtend) {
|
|
863
|
+
if (currentVariantKeys || computedVariantNames.length > 0) {
|
|
864
|
+
computedVariantKeysSet = new Set<string>();
|
|
865
|
+
if (currentVariantKeys) {
|
|
866
|
+
for (const k of currentVariantKeys) computedVariantKeysSet.add(k);
|
|
867
|
+
}
|
|
868
|
+
for (let i = 0; i < computedVariantNames.length; i++) {
|
|
869
|
+
computedVariantKeysSet.add(computedVariantNames[i]);
|
|
870
|
+
}
|
|
910
871
|
}
|
|
911
872
|
}
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
873
|
+
|
|
874
|
+
// computedVariantValues = mergeDisabledVariantValues(skipIn, disabledValues)
|
|
875
|
+
let computedVariantValues: Record<string, Set<string>> | null = null;
|
|
876
|
+
const hasInValues = !!skipStyleVariantValuesIn;
|
|
877
|
+
const hasDisabledValues = hasDisabledVariantValues;
|
|
878
|
+
if (hasExtend && (hasInValues || hasDisabledValues)) {
|
|
879
|
+
computedVariantValues = {};
|
|
880
|
+
if (hasInValues) {
|
|
881
|
+
for (const k in skipStyleVariantValuesIn) {
|
|
882
|
+
if (!hasOwn.call(skipStyleVariantValuesIn, k)) continue;
|
|
883
|
+
const set = new Set<string>();
|
|
884
|
+
for (const v of skipStyleVariantValuesIn[k]) {
|
|
885
|
+
set.add(v);
|
|
886
|
+
}
|
|
887
|
+
computedVariantValues[k] = set;
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
for (let i = 0; i < disabledVariantValueKeys.length; i++) {
|
|
891
|
+
const k = disabledVariantValueKeys[i];
|
|
892
|
+
let bucket = computedVariantValues[k];
|
|
893
|
+
if (!bucket) {
|
|
894
|
+
bucket = new Set<string>();
|
|
895
|
+
computedVariantValues[k] = bucket;
|
|
896
|
+
}
|
|
897
|
+
for (const v of disabledVariantValues[k]) bucket.add(v);
|
|
898
|
+
}
|
|
935
899
|
}
|
|
936
900
|
|
|
937
|
-
//
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
//
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
901
|
+
// ----- Build classes/styles in proper order -----
|
|
902
|
+
// 1. Extended base classes & their styles (with skip applied)
|
|
903
|
+
// 2. Current base class & base style
|
|
904
|
+
// 3. Extended variant classes
|
|
905
|
+
// 4. Current variants
|
|
906
|
+
// 5. computed results
|
|
907
|
+
// 6. props.class / props.className
|
|
908
|
+
// 7. props.style
|
|
909
|
+
const allClasses: ClassValue[] = [];
|
|
910
|
+
const allStyle: StyleValue = {};
|
|
911
|
+
|
|
912
|
+
// Process extended components
|
|
913
|
+
if (hasExtend) {
|
|
914
|
+
const hasComputedVariantKeysSet =
|
|
915
|
+
!!computedVariantKeysSet && computedVariantKeysSet.size > 0;
|
|
916
|
+
const hasComputedVariantValues =
|
|
917
|
+
!!computedVariantValues &&
|
|
918
|
+
Object.keys(computedVariantValues).length > 0;
|
|
919
|
+
const hasSkipForExt =
|
|
920
|
+
hasComputedVariantKeysSet || hasComputedVariantValues;
|
|
921
|
+
|
|
922
|
+
const extVariantClasses: ClassValue[] = [];
|
|
923
|
+
|
|
924
|
+
for (let i = 0; i < extCount; i++) {
|
|
925
|
+
const ext = extEntries[i];
|
|
926
|
+
const extBaseClass = extBaseClassesArr[i];
|
|
927
|
+
let propsForExt: Record<string | symbol, unknown>;
|
|
928
|
+
if (hasSkipForExt) {
|
|
929
|
+
propsForExt = {};
|
|
930
|
+
// Copy resolvedVariants
|
|
931
|
+
for (const k in resolvedVariants) {
|
|
932
|
+
if (hasOwn.call(resolvedVariants, k)) {
|
|
933
|
+
propsForExt[k] = resolvedVariants[k];
|
|
934
|
+
}
|
|
935
|
+
}
|
|
936
|
+
if (hasComputedVariantKeysSet) {
|
|
937
|
+
propsForExt[SKIP_STYLE_KEYS] = computedVariantKeysSet;
|
|
938
|
+
}
|
|
939
|
+
if (hasComputedVariantValues) {
|
|
940
|
+
propsForExt[SKIP_STYLE_VARIANT_VALUES] = computedVariantValues;
|
|
941
|
+
}
|
|
942
|
+
} else {
|
|
943
|
+
propsForExt = resolvedVariants as Record<string | symbol, unknown>;
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
const extResult = ext(
|
|
947
|
+
propsForExt as ComponentProps<Record<string, unknown>>,
|
|
948
|
+
);
|
|
949
|
+
// ext may be a modal component (.html / .htmlObj), whose style is a
|
|
950
|
+
// CSS string or hyphen-keyed object — normalize before merging.
|
|
951
|
+
if (extResult.style != null) {
|
|
952
|
+
assign(allStyle, normalizeStyle(extResult.style));
|
|
953
|
+
}
|
|
954
|
+
|
|
955
|
+
allClasses.push(extBaseClass);
|
|
956
|
+
const fullClass =
|
|
957
|
+
"className" in extResult ? extResult.className : extResult.class;
|
|
958
|
+
const variantPortion = extractVariantClasses(fullClass, extBaseClass);
|
|
959
|
+
if (variantPortion) extVariantClasses.push(variantPortion);
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
// 2. Current base class
|
|
963
|
+
allClasses.push(baseClass);
|
|
964
|
+
if (baseStyle) assign(allStyle, baseStyle);
|
|
965
|
+
|
|
966
|
+
// 4. Extended variant classes
|
|
967
|
+
for (let i = 0; i < extVariantClasses.length; i++) {
|
|
968
|
+
allClasses.push(extVariantClasses[i]);
|
|
969
|
+
}
|
|
970
|
+
} else {
|
|
971
|
+
// No extends: just current base
|
|
972
|
+
allClasses.push(baseClass);
|
|
973
|
+
if (baseStyle) assign(allStyle, baseStyle);
|
|
957
974
|
}
|
|
958
|
-
|
|
959
|
-
|
|
975
|
+
|
|
976
|
+
// 5. Current component's variants (skip keys overridden)
|
|
977
|
+
// Walk pre-built variant entries
|
|
978
|
+
for (let i = 0; i < variantEntryCount; i++) {
|
|
979
|
+
const variantName = variantEntryNames[i];
|
|
980
|
+
const variant = variantEntryDefs[i];
|
|
981
|
+
if (currentVariantKeys && currentVariantKeys.has(variantName)) continue;
|
|
982
|
+
const selectedValue = resolvedVariants[variantName];
|
|
983
|
+
if (selectedValue === undefined) continue;
|
|
984
|
+
const selectedKey = getVariantValueKey(selectedValue);
|
|
985
|
+
// disabled values from current config:
|
|
986
|
+
if (
|
|
987
|
+
variant.disabledValues &&
|
|
988
|
+
selectedKey != null &&
|
|
989
|
+
variant.disabledValues.has(selectedKey)
|
|
990
|
+
) {
|
|
991
|
+
continue;
|
|
992
|
+
}
|
|
993
|
+
// skipVariantValues comes from skipStyleVariantValuesIn (only relevant
|
|
994
|
+
// if this is being called as an extended component). For top-level it
|
|
995
|
+
// would be undefined.
|
|
996
|
+
if (
|
|
997
|
+
skipStyleVariantValuesIn &&
|
|
998
|
+
selectedKey != null &&
|
|
999
|
+
skipStyleVariantValuesIn[variantName]?.has(selectedKey)
|
|
1000
|
+
) {
|
|
1001
|
+
continue;
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
if (variant.values) {
|
|
1005
|
+
if (selectedKey == null) continue;
|
|
1006
|
+
const v = variant.values[selectedKey];
|
|
1007
|
+
if (!v) continue;
|
|
1008
|
+
if (v.class != null) allClasses.push(v.class);
|
|
1009
|
+
if (v.style) assign(allStyle, v.style);
|
|
1010
|
+
} else if (variant.shorthand) {
|
|
1011
|
+
// shorthand: applies when selectedValue === true
|
|
1012
|
+
if (selectedValue === true) {
|
|
1013
|
+
const v = variant.shorthand;
|
|
1014
|
+
if (v.class != null) allClasses.push(v.class);
|
|
1015
|
+
if (v.style) assign(allStyle, v.style);
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
960
1018
|
}
|
|
961
1019
|
|
|
962
|
-
//
|
|
963
|
-
|
|
964
|
-
|
|
1020
|
+
// computedVariants
|
|
1021
|
+
for (let i = 0; i < computedVariantCount; i++) {
|
|
1022
|
+
const variantName = computedVariantNames[i];
|
|
1023
|
+
const fn = computedVariantFns[i];
|
|
1024
|
+
if (currentVariantKeys && currentVariantKeys.has(variantName)) continue;
|
|
1025
|
+
const selectedValue = resolvedVariants[variantName];
|
|
1026
|
+
if (selectedValue === undefined) continue;
|
|
1027
|
+
const selectedKey = getVariantValueKey(selectedValue);
|
|
1028
|
+
if (
|
|
1029
|
+
skipStyleVariantValuesIn &&
|
|
1030
|
+
selectedKey != null &&
|
|
1031
|
+
skipStyleVariantValuesIn[variantName]?.has(selectedKey)
|
|
1032
|
+
) {
|
|
1033
|
+
continue;
|
|
1034
|
+
}
|
|
1035
|
+
const computedResult = fn(selectedValue);
|
|
1036
|
+
if (computedResult == null) continue;
|
|
1037
|
+
const r = extractClassAndStylePrebuilt(computedResult);
|
|
1038
|
+
if (r.class != null) allClasses.push(r.class);
|
|
1039
|
+
if (r.style) assign(allStyle, r.style);
|
|
1040
|
+
}
|
|
1041
|
+
|
|
1042
|
+
// computed function results
|
|
1043
|
+
if (computedClassesArr) {
|
|
1044
|
+
for (let i = 0; i < computedClassesArr.length; i++) {
|
|
1045
|
+
allClasses.push(computedClassesArr[i]);
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1048
|
+
if (computedStyleObj) assign(allStyle, computedStyleObj);
|
|
1049
|
+
|
|
1050
|
+
// props.class / props.className
|
|
1051
|
+
if ("class" in props)
|
|
1052
|
+
allClasses.push((props as { class: ClassValue }).class);
|
|
1053
|
+
if ("className" in props)
|
|
1054
|
+
allClasses.push((props as { className: ClassValue }).className);
|
|
1055
|
+
|
|
1056
|
+
// props.style
|
|
1057
|
+
const psv = (props as { style?: unknown }).style;
|
|
1058
|
+
if (psv != null) {
|
|
1059
|
+
// Fast path: if it's an object with no keys, skip
|
|
1060
|
+
if (typeof psv === "string") {
|
|
1061
|
+
if (psv.length > 0) {
|
|
1062
|
+
assign(allStyle, htmlStyleToStyleValue(psv));
|
|
1063
|
+
}
|
|
1064
|
+
} else if (typeof psv === "object") {
|
|
1065
|
+
// Could be HTMLObj or JSX form. Don't allocate when empty.
|
|
1066
|
+
let hasAnyKey = false;
|
|
1067
|
+
for (const _ in psv) {
|
|
1068
|
+
hasAnyKey = true;
|
|
1069
|
+
break;
|
|
1070
|
+
}
|
|
1071
|
+
if (hasAnyKey) {
|
|
1072
|
+
assign(allStyle, normalizeStyle(psv));
|
|
1073
|
+
}
|
|
1074
|
+
}
|
|
965
1075
|
}
|
|
966
1076
|
|
|
967
1077
|
return {
|
|
@@ -971,119 +1081,147 @@ export function create({
|
|
|
971
1081
|
};
|
|
972
1082
|
|
|
973
1083
|
const getVariants = (variants?: VariantValues<MergedVariants>) => {
|
|
974
|
-
const variantProps = variants ?? {}
|
|
975
|
-
|
|
1084
|
+
const variantProps = (variants ?? {}) as Record<string, unknown>;
|
|
1085
|
+
let resolvedVariants = resolveVariantsHot(variantProps);
|
|
976
1086
|
// Run computed function to get variants set via setVariants and
|
|
977
1087
|
// setDefaultVariants
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
resolvedVariants
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
1088
|
+
if (computed) {
|
|
1089
|
+
const updatedVariants: Record<string, unknown> = {};
|
|
1090
|
+
Object.assign(updatedVariants, resolvedVariants);
|
|
1091
|
+
const ctx = {
|
|
1092
|
+
variants: resolvedVariants as VariantValues<Record<string, unknown>>,
|
|
1093
|
+
setVariants: (
|
|
1094
|
+
newVariants: VariantValues<Record<string, unknown>>,
|
|
1095
|
+
) => {
|
|
1096
|
+
if (!hasAnyDisabled) {
|
|
1097
|
+
Object.assign(updatedVariants, newVariants);
|
|
1098
|
+
} else {
|
|
1099
|
+
const filtered: Record<string, unknown> = {};
|
|
1100
|
+
filterDisabledInto(
|
|
1101
|
+
newVariants as Record<string, unknown>,
|
|
1102
|
+
filtered,
|
|
1103
|
+
);
|
|
1104
|
+
Object.assign(updatedVariants, filtered);
|
|
1105
|
+
}
|
|
1106
|
+
},
|
|
1107
|
+
setDefaultVariants: (
|
|
1108
|
+
newDefaults: VariantValues<Record<string, unknown>>,
|
|
1109
|
+
) => {
|
|
1110
|
+
for (const key in newDefaults) {
|
|
1111
|
+
if (!hasOwn.call(newDefaults, key)) continue;
|
|
1112
|
+
if (variantProps[key] !== undefined) continue;
|
|
1113
|
+
const value = (newDefaults as Record<string, unknown>)[key];
|
|
1114
|
+
if (hasAnyDisabled) {
|
|
1115
|
+
if (disabledVariantKeys.has(key)) continue;
|
|
1116
|
+
const valueKey = getVariantValueKey(value);
|
|
1117
|
+
if (
|
|
1118
|
+
valueKey != null &&
|
|
1119
|
+
disabledVariantValues[key]?.has(valueKey)
|
|
1120
|
+
) {
|
|
1121
|
+
continue;
|
|
1122
|
+
}
|
|
1123
|
+
}
|
|
1124
|
+
updatedVariants[key] = value;
|
|
1125
|
+
}
|
|
1126
|
+
},
|
|
1127
|
+
addClass: () => {},
|
|
1128
|
+
addStyle: () => {},
|
|
1129
|
+
};
|
|
1130
|
+
computed(ctx);
|
|
1131
|
+
if (hasAnyDisabled) {
|
|
1132
|
+
const filteredUpdated: Record<string, unknown> = {};
|
|
1133
|
+
filterDisabledInto(updatedVariants, filteredUpdated);
|
|
1134
|
+
resolvedVariants = filteredUpdated;
|
|
1135
|
+
} else {
|
|
1136
|
+
resolvedVariants = updatedVariants;
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
return resolvedVariants as VariantValues<MergedVariants>;
|
|
984
1140
|
};
|
|
985
1141
|
|
|
986
|
-
// Compute base class (without variants) - includes extended base classes
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
extendedBaseClasses.push(meta?.baseClass ?? "");
|
|
992
|
-
}
|
|
993
|
-
}
|
|
994
|
-
const baseClass = cx(
|
|
995
|
-
...(extendedBaseClasses as ClsxClassValue[]),
|
|
1142
|
+
// Compute base class (without variants) - includes extended base classes.
|
|
1143
|
+
// Reuses `extBaseClassesArr` (built earlier) so we don't walk `extend` and
|
|
1144
|
+
// call `getComponentMeta` a second time.
|
|
1145
|
+
const computedBaseClass = cx(
|
|
1146
|
+
...(extBaseClassesArr as ClsxClassValue[]),
|
|
996
1147
|
config.class as ClsxClassValue,
|
|
997
1148
|
);
|
|
998
1149
|
|
|
999
|
-
//
|
|
1000
|
-
|
|
1001
|
-
|
|
1150
|
+
// Shared closures across the default and modal components.
|
|
1151
|
+
const classFn = (props: ComponentProps<MergedVariants> = {}) => {
|
|
1152
|
+
return computeResult(props).className;
|
|
1153
|
+
};
|
|
1154
|
+
const meta: ComponentMeta = {
|
|
1155
|
+
baseClass: computedBaseClass,
|
|
1156
|
+
staticDefaults,
|
|
1157
|
+
resolveDefaults: resolveDefaultsFn,
|
|
1158
|
+
};
|
|
1002
1159
|
|
|
1003
|
-
const
|
|
1160
|
+
const initComponent = <
|
|
1004
1161
|
R extends ComponentResult,
|
|
1005
1162
|
T extends ModalComponent<MergedVariants, R>,
|
|
1006
1163
|
>(
|
|
1007
|
-
|
|
1008
|
-
|
|
1164
|
+
c: T,
|
|
1165
|
+
keys: string[],
|
|
1166
|
+
style: T["style"],
|
|
1009
1167
|
): T => {
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
// Store internal metadata hidden from public types
|
|
1020
|
-
setComponentMeta(component, {
|
|
1021
|
-
baseClass,
|
|
1022
|
-
staticDefaults,
|
|
1023
|
-
resolveDefaults: createResolveDefaults(config),
|
|
1024
|
-
});
|
|
1025
|
-
|
|
1026
|
-
return component;
|
|
1168
|
+
c.class = classFn;
|
|
1169
|
+
c.style = style;
|
|
1170
|
+
c.getVariants = getVariants;
|
|
1171
|
+
c.keys = keys;
|
|
1172
|
+
c.variantKeys = variantKeys;
|
|
1173
|
+
c.propKeys = keys;
|
|
1174
|
+
setComponentMeta(c, meta);
|
|
1175
|
+
return c;
|
|
1027
1176
|
};
|
|
1028
1177
|
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
const { className, style } = computeResult(props);
|
|
1049
|
-
|
|
1050
|
-
if (mode === "jsx") {
|
|
1051
|
-
return { className, style: styleValueToJSXStyle(style) };
|
|
1052
|
-
}
|
|
1053
|
-
if (mode === "html") {
|
|
1054
|
-
return { class: className, style: styleValueToHTMLStyle(style) };
|
|
1055
|
-
}
|
|
1056
|
-
// htmlObj
|
|
1057
|
-
return { class: className, style: styleValueToHTMLObjStyle(style) };
|
|
1058
|
-
}) as ModalComponent<MergedVariants, R>;
|
|
1059
|
-
|
|
1060
|
-
component.class = (props: ComponentProps<MergedVariants> = {}) => {
|
|
1061
|
-
return computeResult(props).className;
|
|
1062
|
-
};
|
|
1063
|
-
|
|
1064
|
-
component.style = (props: ComponentProps<MergedVariants> = {}) => {
|
|
1065
|
-
const { style } = computeResult(props);
|
|
1066
|
-
if (mode === "jsx") return styleValueToJSXStyle(style);
|
|
1067
|
-
if (mode === "html") return styleValueToHTMLStyle(style);
|
|
1068
|
-
return styleValueToHTMLObjStyle(style);
|
|
1069
|
-
};
|
|
1070
|
-
return initializeComponent(component, propsKeys);
|
|
1071
|
-
};
|
|
1178
|
+
// Default component
|
|
1179
|
+
const defaultComponent = ((props: ComponentProps<MergedVariants> = {}) => {
|
|
1180
|
+
const { className, style } = computeResult(props);
|
|
1181
|
+
return { class: className, style };
|
|
1182
|
+
}) as CVComponent<V, CV, E>;
|
|
1183
|
+
initComponent(defaultComponent, inputPropsKeys, (props = {}) => {
|
|
1184
|
+
return computeResult(props).style;
|
|
1185
|
+
});
|
|
1186
|
+
|
|
1187
|
+
// JSX component
|
|
1188
|
+
const jsxComponent = ((props: ComponentProps<MergedVariants> = {}) => {
|
|
1189
|
+
const { className, style } = computeResult(props);
|
|
1190
|
+
return { className, style: styleValueToJSXStyle(style) };
|
|
1191
|
+
}) as ModalComponent<MergedVariants, JSXProps>;
|
|
1192
|
+
initComponent(
|
|
1193
|
+
jsxComponent,
|
|
1194
|
+
["className", "style", ...variantKeys],
|
|
1195
|
+
(props = {}) => styleValueToJSXStyle(computeResult(props).style),
|
|
1196
|
+
);
|
|
1072
1197
|
|
|
1073
|
-
|
|
1198
|
+
// HTML component
|
|
1199
|
+
const htmlComponent = ((props: ComponentProps<MergedVariants> = {}) => {
|
|
1200
|
+
const { className, style } = computeResult(props);
|
|
1201
|
+
return { class: className, style: styleValueToHTMLStyle(style) };
|
|
1202
|
+
}) as ModalComponent<MergedVariants, HTMLProps>;
|
|
1203
|
+
initComponent(
|
|
1204
|
+
htmlComponent,
|
|
1205
|
+
["class", "style", ...variantKeys],
|
|
1206
|
+
(props = {}) => styleValueToHTMLStyle(computeResult(props).style),
|
|
1207
|
+
);
|
|
1074
1208
|
|
|
1075
|
-
//
|
|
1076
|
-
const
|
|
1077
|
-
|
|
1078
|
-
|
|
1209
|
+
// HTMLObj component
|
|
1210
|
+
const htmlObjComponent = ((props: ComponentProps<MergedVariants> = {}) => {
|
|
1211
|
+
const { className, style } = computeResult(props);
|
|
1212
|
+
return { class: className, style: styleValueToHTMLObjStyle(style) };
|
|
1213
|
+
}) as ModalComponent<MergedVariants, HTMLObjProps>;
|
|
1214
|
+
initComponent(
|
|
1215
|
+
htmlObjComponent,
|
|
1216
|
+
["class", "style", ...variantKeys],
|
|
1217
|
+
(props = {}) => styleValueToHTMLObjStyle(computeResult(props).style),
|
|
1218
|
+
);
|
|
1079
1219
|
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
component.html = htmlComponent;
|
|
1084
|
-
component.htmlObj = htmlObjComponent;
|
|
1220
|
+
defaultComponent.jsx = jsxComponent;
|
|
1221
|
+
defaultComponent.html = htmlComponent;
|
|
1222
|
+
defaultComponent.htmlObj = htmlObjComponent;
|
|
1085
1223
|
|
|
1086
|
-
return
|
|
1224
|
+
return defaultComponent;
|
|
1087
1225
|
};
|
|
1088
1226
|
|
|
1089
1227
|
return { cv, cx };
|