clava 0.1.16 → 0.1.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,34 @@
1
1
  # clava
2
2
 
3
+ ## 0.1.17
4
+
5
+ ### Patch Changes
6
+
7
+ - c2f1fa9: Added support for disabling inherited variants and variant values with `null`.
8
+
9
+ ```ts
10
+ const base = cv({
11
+ variants: { size: { sm: "sm", lg: "lg" } },
12
+ defaultVariants: { size: "sm" },
13
+ });
14
+
15
+ const button = cv({
16
+ extend: [base],
17
+ variants: { size: { sm: null } },
18
+ });
19
+
20
+ button({ size: "lg" }); // ✅
21
+ button({ size: "sm" }); // ❌ TypeScript error
22
+ ```
23
+
24
+ Disabled variants and values are excluded from `defaultVariants`, resolved variant props, and applied classes/styles.
25
+
26
+ - a8cc18c: Added `Variant<T, K>` utility type for cross-component variant key constraints.
27
+ - 4e54d51: Required an explicit `style` key for object-based variant/computed outputs.
28
+ - 7e32a54: Reduced runtime overhead in `cv` hot paths.
29
+
30
+ This avoids repeated key-array allocations when merging style objects and when propagating override metadata to extended components. Behavior remains the same while reducing per-call work in frequently executed code paths, especially for components with many style merges or multiple `extend` entries.
31
+
3
32
  ## 0.1.16
4
33
 
5
34
  ### Patch Changes
package/dist/index.d.ts CHANGED
@@ -20245,17 +20245,23 @@ type MergeExtendedVariants<T> = T extends readonly [infer First, ...infer Rest]
20245
20245
  type MergeExtendedComputedVariants<T> = T extends readonly [infer First, ...infer Rest] ? ExtractComputedVariants<First> & MergeExtendedComputedVariants<Rest> : {};
20246
20246
  type ExtractVariants<T> = T extends CVComponent<infer V, any, infer E, any> ? V & MergeExtendedVariants<E> : {};
20247
20247
  type ExtractComputedVariants<T> = T extends CVComponent<any, infer CV, infer E, any> ? CV & Omit<MergeExtendedComputedVariants<E>, keyof CV> : {};
20248
- type MergeVariants<V, CV, E extends AnyComponent[]> = NoInfer<CV> & Omit<NoInfer<V>, keyof CV> & Omit<MergeExtendedVariants<E>, keyof CV> & Omit<MergeExtendedComputedVariants<E>, keyof CV>;
20248
+ type MergeVariantDefinition<Child, Parent> = Child extends Record<string, any> ? Parent extends Record<string, any> ? Omit<Parent, keyof Child> & Child : Child : Child;
20249
+ type MergeVariantMaps<Child, Parent> = { [K in keyof Child | keyof Parent]: K extends keyof Child ? K extends keyof Parent ? MergeVariantDefinition<Child[K], Parent[K]> : Child[K] : K extends keyof Parent ? Parent[K] : never };
20250
+ type MergeExtendedAllVariants<E extends AnyComponent[]> = MergeExtendedVariants<E> & MergeExtendedComputedVariants<E>;
20251
+ type MergeBaseVariants<V, E extends AnyComponent[]> = MergeVariantMaps<NoInfer<V>, MergeExtendedAllVariants<E>>;
20252
+ type MergeVariants<V, CV, E extends AnyComponent[]> = NoInfer<CV> & Omit<MergeBaseVariants<V, E>, keyof CV>;
20249
20253
  type StringToBoolean<T> = T extends "true" | "false" ? boolean : T;
20250
20254
  type VariantValue = ClassValue | StyleClassValue;
20251
- type ExtractVariantValue<T> = T extends ((value: infer V) => any) ? V : T extends ClassValue ? boolean : T extends Record<infer K, any> ? StringToBoolean<K> : never;
20255
+ type NonNullKeys<T> = { [K in keyof T]: T[K] extends null ? never : K }[keyof T];
20256
+ type ExtractVariantValue<T> = T extends null ? never : T extends ((value: infer V) => any) ? V : T extends Record<string, any> ? StringToBoolean<NonNullKeys<T>> : T extends ClassValue ? boolean : never;
20252
20257
  type VariantValues<V> = { [K in keyof V]?: ExtractVariantValue<V[K]> };
20253
20258
  type StyleValue = Properties & {
20254
20259
  [key: `--${string}`]: string;
20255
20260
  };
20256
- type StyleClassValue = StyleValue & {
20261
+ interface StyleClassValue {
20262
+ style?: StyleValue;
20257
20263
  class?: ClassValue;
20258
- };
20264
+ }
20259
20265
  interface ComputedContext<V> {
20260
20266
  variants: VariantValues<V>;
20261
20267
  setVariants: (variants: VariantValues<V>) => void;
@@ -20266,10 +20272,11 @@ interface ComputedContext<V> {
20266
20272
  type Computed<V> = (context: ComputedContext<V>) => VariantValue;
20267
20273
  type ComputedVariant = (value: any) => VariantValue;
20268
20274
  type ComputedVariants = Record<string, ComputedVariant>;
20269
- type Variant = ClassValue | Record<string, VariantValue>;
20270
- type Variants = Record<string, Variant>;
20275
+ type Variant$1 = ClassValue | Record<string, VariantValue>;
20276
+ type Variants = Record<string, Variant$1>;
20271
20277
  type ExtendedVariants<E extends AnyComponent[]> = MergeExtendedVariants<E> & MergeExtendedComputedVariants<E>;
20272
- type ExtendableVariants<V extends Variants, E extends AnyComponent[]> = V & { [K in keyof ExtendedVariants<E>]?: Partial<ExtendedVariants<E>[K]> | Variant };
20278
+ type NullablePartial<T> = T extends Record<string, any> ? { [K in keyof T]?: T[K] | null } : T | null;
20279
+ type ExtendableVariants<V extends Variants, E extends AnyComponent[]> = V & { [K in keyof ExtendedVariants<E>]?: NullablePartial<ExtendedVariants<E>[K]> | Variant$1 };
20273
20280
  //#endregion
20274
20281
  //#region src/utils.d.ts
20275
20282
  declare const MODES: readonly ["jsx", "html", "htmlObj"];
@@ -20277,6 +20284,8 @@ type Mode = (typeof MODES)[number];
20277
20284
  //#endregion
20278
20285
  //#region src/index.d.ts
20279
20286
  type VariantProps<T extends Pick<AnyComponent, "getVariants">> = ReturnType<T["getVariants"]>;
20287
+ type VariantKey<T> = T extends boolean ? "true" | "false" : Extract<T, string>;
20288
+ type Variant<T extends Pick<AnyComponent, "getVariants">, K extends keyof VariantProps<T>> = Record<VariantKey<NonNullable<VariantProps<T>[K]>>, ClassValue | StyleClassValue>;
20280
20289
  interface CVConfig<V extends Variants = {}, CV extends ComputedVariants = {}, E extends AnyComponent[] = []> {
20281
20290
  extend?: E;
20282
20291
  class?: ClassValue;
@@ -20321,5 +20330,5 @@ declare function create<M extends Mode = "jsx">({
20321
20330
  };
20322
20331
  declare const cv: <V extends Variants = {}, CV extends ComputedVariants = {}, const E extends AnyComponent[] = []>(config?: CVConfig<V, CV, E>) => CVComponent<V, CV, E, JSXProps>, cx: (...classes: ClassValue$1[]) => string;
20323
20332
  //#endregion
20324
- export { type CVComponent, CVConfig, type ClassValue, type HTMLObjProps, type HTMLProps, type JSXProps, type StyleClassValue, type StyleValue, VariantProps, create, cv, cx, splitProps };
20333
+ export { type CVComponent, CVConfig, type ClassValue, type HTMLObjProps, type HTMLProps, type JSXProps, type StyleClassValue, type StyleValue, Variant, VariantProps, create, cv, cx, splitProps };
20325
20334
  //# sourceMappingURL=index.d.ts.map
package/dist/index.js CHANGED
@@ -12,7 +12,6 @@ function clsx() {
12
12
  for (var e, t, f = 0, n = "", o = arguments.length; f < o; f++) (e = arguments[f]) && (t = r(e)) && (n && (n += " "), n += t);
13
13
  return n;
14
14
  }
15
- var clsx_default = clsx;
16
15
 
17
16
  //#endregion
18
17
  //#region src/utils.ts
@@ -159,6 +158,7 @@ function isHTMLObjStyle(style) {
159
158
  //#region src/index.ts
160
159
  const META_KEY = "__meta";
161
160
  const SKIP_STYLE_KEYS = Symbol("skipStyleKeys");
161
+ const SKIP_STYLE_VARIANT_VALUES = Symbol("skipStyleVariantValues");
162
162
  function getComponentMeta(component) {
163
163
  return component[META_KEY];
164
164
  }
@@ -170,19 +170,25 @@ function setComponentMeta(component, meta) {
170
170
  * overhead in hot paths where we're building up a result object.
171
171
  */
172
172
  function assign(target, source) {
173
- for (const key of Object.keys(source)) target[key] = source[key];
173
+ for (const key in source) {
174
+ if (!Object.prototype.hasOwnProperty.call(source, key)) continue;
175
+ target[key] = source[key];
176
+ }
174
177
  }
175
- /**
176
- * Checks if a value is a style-class object (has style properties, not just a
177
- * class value).
178
- */
179
- function isStyleClassValue(value) {
178
+ function isRecordObject(value) {
180
179
  if (typeof value !== "object") return false;
181
180
  if (value == null) return false;
182
181
  if (Array.isArray(value)) return false;
183
182
  return true;
184
183
  }
185
184
  /**
185
+ * Checks if a value is a style-class object (`{ style, class? }`).
186
+ */
187
+ function isStyleClassValue(value) {
188
+ if (!isRecordObject(value)) return false;
189
+ return "style" in value || "class" in value;
190
+ }
191
+ /**
186
192
  * Converts any style input (string, JSX object, or HTML object) to a normalized
187
193
  * StyleValue.
188
194
  */
@@ -198,10 +204,9 @@ function normalizeStyle(style) {
198
204
  * Extracts class and style from a style-class value object.
199
205
  */
200
206
  function extractStyleClass(value) {
201
- const { class: cls, ...style } = value;
202
207
  return {
203
- class: cls,
204
- style
208
+ class: value.class,
209
+ style: normalizeStyle(value.style)
205
210
  };
206
211
  }
207
212
  /**
@@ -210,6 +215,10 @@ function extractStyleClass(value) {
210
215
  */
211
216
  function extractClassAndStyle(value) {
212
217
  if (isStyleClassValue(value)) return extractStyleClass(value);
218
+ if (isRecordObject(value)) return {
219
+ class: null,
220
+ style: {}
221
+ };
213
222
  return {
214
223
  class: value,
215
224
  style: {}
@@ -222,10 +231,68 @@ function extractClassAndStyle(value) {
222
231
  function collectVariantKeys(config) {
223
232
  const keys = /* @__PURE__ */ new Set();
224
233
  if (config.extend) for (const ext of config.extend) for (const key of ext.variantKeys) keys.add(key);
225
- if (config.variants) for (const key of Object.keys(config.variants)) keys.add(key);
234
+ if (config.variants) for (const [key, variant] of Object.entries(config.variants)) {
235
+ if (variant === null) {
236
+ keys.delete(key);
237
+ continue;
238
+ }
239
+ keys.add(key);
240
+ }
226
241
  if (config.computedVariants) for (const key of Object.keys(config.computedVariants)) keys.add(key);
227
242
  return Array.from(keys);
228
243
  }
244
+ function isVariantDisabled(config, key) {
245
+ return config.variants?.[key] === null;
246
+ }
247
+ function getVariantValueKey(value) {
248
+ if (typeof value === "string") return value;
249
+ if (typeof value === "number") return String(value);
250
+ if (typeof value === "boolean") return String(value);
251
+ }
252
+ function isVariantValueDisabled(config, key, value) {
253
+ const valueKey = getVariantValueKey(value);
254
+ if (valueKey == null) return false;
255
+ const variant = config.variants?.[key];
256
+ if (!isRecordObject(variant)) return false;
257
+ return variant[valueKey] === null;
258
+ }
259
+ function filterDisabledVariants(config, variants) {
260
+ const filtered = {};
261
+ for (const [key, value] of Object.entries(variants)) {
262
+ if (isVariantDisabled(config, key)) continue;
263
+ if (isVariantValueDisabled(config, key, value)) continue;
264
+ filtered[key] = value;
265
+ }
266
+ return filtered;
267
+ }
268
+ function collectDisabledVariantKeys(config) {
269
+ const keys = /* @__PURE__ */ new Set();
270
+ if (!config.variants) return keys;
271
+ for (const [key, value] of Object.entries(config.variants)) if (value === null) keys.add(key);
272
+ return keys;
273
+ }
274
+ function collectDisabledVariantValues(config) {
275
+ const values = {};
276
+ if (!config.variants) return values;
277
+ for (const [key, variant] of Object.entries(config.variants)) {
278
+ if (!isRecordObject(variant)) continue;
279
+ for (const [variantValue, variantEntry] of Object.entries(variant)) {
280
+ if (variantEntry !== null) continue;
281
+ if (!values[key]) values[key] = /* @__PURE__ */ new Set();
282
+ values[key].add(variantValue);
283
+ }
284
+ }
285
+ return values;
286
+ }
287
+ function mergeDisabledVariantValues(base, override) {
288
+ const merged = {};
289
+ for (const [key, values] of Object.entries(base)) merged[key] = new Set(values);
290
+ for (const [key, values] of Object.entries(override)) {
291
+ if (!merged[key]) merged[key] = /* @__PURE__ */ new Set();
292
+ for (const value of values) merged[key].add(value);
293
+ }
294
+ return merged;
295
+ }
229
296
  /**
230
297
  * Collects static default variants from extended components and the current
231
298
  * config. Also handles implicit boolean defaults (when only `false` key
@@ -239,11 +306,11 @@ function collectStaticDefaults(config) {
239
306
  if (meta) Object.assign(defaults, meta.staticDefaults);
240
307
  }
241
308
  if (config.variants) for (const [variantName, variantDef] of Object.entries(config.variants)) {
242
- if (!isStyleClassValue(variantDef)) continue;
309
+ if (!isRecordObject(variantDef)) continue;
243
310
  if (Object.keys(variantDef).includes("false") && !defaults[variantName]) defaults[variantName] = false;
244
311
  }
245
312
  if (config.defaultVariants) Object.assign(defaults, config.defaultVariants);
246
- return defaults;
313
+ return filterDisabledVariants(config, defaults);
247
314
  }
248
315
  /**
249
316
  * Collects default variants from extended components and the current config.
@@ -259,7 +326,7 @@ function collectDefaultVariants(config, propsVariants = {}) {
259
326
  if (!meta) continue;
260
327
  Object.assign(defaults, meta.resolveDefaults(defaults, propsVariants));
261
328
  }
262
- return defaults;
329
+ return filterDisabledVariants(config, defaults);
263
330
  }
264
331
  /**
265
332
  * Filters out keys with undefined values from an object.
@@ -277,17 +344,17 @@ function filterUndefined(obj) {
277
344
  * undefined values are filtered out so they don't override defaults.
278
345
  */
279
346
  function resolveVariants(config, props = {}) {
280
- return {
347
+ return filterDisabledVariants(config, {
281
348
  ...collectDefaultVariants(config, props),
282
349
  ...filterUndefined(props)
283
- };
350
+ });
284
351
  }
285
352
  /**
286
353
  * Gets the value for a single variant based on the variant definition and the
287
354
  * selected value.
288
355
  */
289
356
  function getVariantResult(variantDef, selectedValue) {
290
- if (!isStyleClassValue(variantDef)) {
357
+ if (!isRecordObject(variantDef)) {
291
358
  if (selectedValue === true) return extractClassAndStyle(variantDef);
292
359
  return {
293
360
  class: null,
@@ -313,7 +380,7 @@ function extractVariantClasses(fullClass, baseClass) {
313
380
  const baseClassSet = new Set(baseClass.split(" ").filter(Boolean));
314
381
  return fullClass.split(" ").filter((c) => c && !baseClassSet.has(c)).join(" ");
315
382
  }
316
- function computeExtendedStyles(config, resolvedVariants, overrideVariantKeys = /* @__PURE__ */ new Set()) {
383
+ function computeExtendedStyles(config, resolvedVariants, overrideVariantKeys = /* @__PURE__ */ new Set(), overrideVariantValues = {}) {
317
384
  const baseClasses = [];
318
385
  const variantClasses = [];
319
386
  const style = {};
@@ -322,9 +389,12 @@ function computeExtendedStyles(config, resolvedVariants, overrideVariantKeys = /
322
389
  variantClasses,
323
390
  style
324
391
  };
392
+ const hasOverrideVariantKeys = overrideVariantKeys.size > 0;
393
+ const hasOverrideVariantValues = Object.keys(overrideVariantValues).length > 0;
325
394
  for (const ext of config.extend) {
326
395
  const propsForExt = { ...resolvedVariants };
327
- if (overrideVariantKeys.size > 0) propsForExt[SKIP_STYLE_KEYS] = overrideVariantKeys;
396
+ if (hasOverrideVariantKeys) propsForExt[SKIP_STYLE_KEYS] = overrideVariantKeys;
397
+ if (hasOverrideVariantValues) propsForExt[SKIP_STYLE_VARIANT_VALUES] = overrideVariantValues;
328
398
  const extResult = ext(propsForExt);
329
399
  assign(style, normalizeStyle(extResult.style));
330
400
  const baseClass = getComponentMeta(ext)?.baseClass ?? "";
@@ -342,13 +412,15 @@ function computeExtendedStyles(config, resolvedVariants, overrideVariantKeys = /
342
412
  * Computes class and style from the component's own variants and
343
413
  * computedVariants (not extended components).
344
414
  */
345
- function computeVariantStyles(config, resolvedVariants, skipStyleKeys = /* @__PURE__ */ new Set()) {
415
+ function computeVariantStyles(config, resolvedVariants, skipStyleKeys = /* @__PURE__ */ new Set(), skipVariantValues = {}) {
346
416
  const classes = [];
347
417
  const style = {};
348
418
  if (config.variants) for (const [variantName, variantDef] of Object.entries(config.variants)) {
349
419
  if (skipStyleKeys.has(variantName)) continue;
350
420
  const selectedValue = resolvedVariants[variantName];
351
421
  if (selectedValue === void 0) continue;
422
+ const selectedKey = getVariantValueKey(selectedValue);
423
+ if (selectedKey && skipVariantValues[variantName]?.has(selectedKey)) continue;
352
424
  const result = getVariantResult(variantDef, selectedValue);
353
425
  classes.push(result.class);
354
426
  assign(style, result.style);
@@ -357,6 +429,8 @@ function computeVariantStyles(config, resolvedVariants, skipStyleKeys = /* @__PU
357
429
  if (skipStyleKeys.has(variantName)) continue;
358
430
  const selectedValue = resolvedVariants[variantName];
359
431
  if (selectedValue === void 0) continue;
432
+ const selectedKey = getVariantValueKey(selectedValue);
433
+ if (selectedKey && skipVariantValues[variantName]?.has(selectedKey)) continue;
360
434
  const result = extractClassAndStyle(computeFn(selectedValue));
361
435
  classes.push(result.class);
362
436
  assign(style, result.style);
@@ -382,10 +456,14 @@ function runComputedFunction(config, resolvedVariants, propsVariants) {
382
456
  const context = {
383
457
  variants: resolvedVariants,
384
458
  setVariants: (newVariants) => {
385
- Object.assign(updatedVariants, newVariants);
459
+ Object.assign(updatedVariants, filterDisabledVariants(config, newVariants));
386
460
  },
387
461
  setDefaultVariants: (newDefaults) => {
388
- for (const [key, value] of Object.entries(newDefaults)) if (propsVariants[key] === void 0) updatedVariants[key] = value;
462
+ for (const [key, value] of Object.entries(newDefaults)) if (propsVariants[key] === void 0) {
463
+ if (isVariantDisabled(config, key)) continue;
464
+ if (isVariantValueDisabled(config, key, value)) continue;
465
+ updatedVariants[key] = value;
466
+ }
389
467
  },
390
468
  addClass: (className) => {
391
469
  classes.push(className);
@@ -403,7 +481,7 @@ function runComputedFunction(config, resolvedVariants, propsVariants) {
403
481
  return {
404
482
  classes,
405
483
  style,
406
- updatedVariants
484
+ updatedVariants: filterDisabledVariants(config, updatedVariants)
407
485
  };
408
486
  }
409
487
  const EMPTY_SOURCE = {
@@ -510,6 +588,8 @@ function createResolveDefaults(config) {
510
588
  setDefaultVariants: (newDefaults) => {
511
589
  for (const [key, value] of Object.entries(newDefaults)) {
512
590
  if (userProps[key] !== void 0) continue;
591
+ if (isVariantDisabled(config, key)) continue;
592
+ if (isVariantValueDisabled(config, key, value)) continue;
513
593
  computedDefaults[key] = value;
514
594
  }
515
595
  },
@@ -523,9 +603,11 @@ function createResolveDefaults(config) {
523
603
  * Creates the cv and cx functions.
524
604
  */
525
605
  function create({ defaultMode = "jsx", transformClass = (className) => className } = {}) {
526
- const cx = (...classes) => transformClass(clsx_default(...classes));
606
+ const cx = (...classes) => transformClass(clsx(...classes));
527
607
  const cv = (config = {}) => {
528
608
  const variantKeys = collectVariantKeys(config);
609
+ const disabledVariantKeys = collectDisabledVariantKeys(config);
610
+ const disabledVariantValues = collectDisabledVariantValues(config);
529
611
  const getPropsKeys = (mode) => [
530
612
  getClassPropertyName(mode),
531
613
  "style",
@@ -535,20 +617,24 @@ function create({ defaultMode = "jsx", transformClass = (className) => className
535
617
  const allClasses = [];
536
618
  const allStyle = {};
537
619
  const skipStyleKeys = props[SKIP_STYLE_KEYS] ?? /* @__PURE__ */ new Set();
620
+ const skipStyleVariantValues = props[SKIP_STYLE_VARIANT_VALUES] ?? {};
538
621
  const variantProps = {};
539
622
  for (const key of variantKeys) if (key in props) variantProps[key] = props[key];
540
623
  let resolvedVariants = resolveVariants(config, variantProps);
541
624
  const computedResult = runComputedFunction(config, resolvedVariants, variantProps);
542
625
  resolvedVariants = computedResult.updatedVariants;
543
- const computedVariantKeys = new Set(skipStyleKeys);
626
+ const currentVariantKeys = new Set(skipStyleKeys);
627
+ for (const key of disabledVariantKeys) currentVariantKeys.add(key);
628
+ const computedVariantKeys = new Set(currentVariantKeys);
544
629
  if (config.computedVariants) for (const key of Object.keys(config.computedVariants)) computedVariantKeys.add(key);
545
- const extendedResult = computeExtendedStyles(config, resolvedVariants, computedVariantKeys);
630
+ const computedVariantValues = mergeDisabledVariantValues(skipStyleVariantValues, disabledVariantValues);
631
+ const extendedResult = computeExtendedStyles(config, resolvedVariants, computedVariantKeys, computedVariantValues);
546
632
  allClasses.push(...extendedResult.baseClasses);
547
633
  assign(allStyle, extendedResult.style);
548
634
  allClasses.push(config.class);
549
635
  if (config.style) assign(allStyle, config.style);
550
636
  allClasses.push(...extendedResult.variantClasses);
551
- const variantsResult = computeVariantStyles(config, resolvedVariants, skipStyleKeys);
637
+ const variantsResult = computeVariantStyles(config, resolvedVariants, currentVariantKeys, computedVariantValues);
552
638
  allClasses.push(...variantsResult.classes);
553
639
  assign(allStyle, variantsResult.style);
554
640
  allClasses.push(...computedResult.classes);