slot-variants 1.5.0 → 1.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +67 -5
- package/SKILL.md +10 -0
- package/dist/eslint-plugin.cjs +36 -1
- package/dist/eslint-plugin.d.ts +2 -0
- package/dist/eslint-plugin.js +36 -1
- package/dist/index.cjs +30 -7
- package/dist/index.d.ts +36 -6
- package/dist/index.js +29 -7
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -12,10 +12,11 @@ npm install slot-variants
|
|
|
12
12
|
|
|
13
13
|
## Overview
|
|
14
14
|
|
|
15
|
-
`slot-variants` exports
|
|
15
|
+
`slot-variants` exports three functions:
|
|
16
16
|
|
|
17
17
|
- **`sv()`** - creates variant-based class name generators with optional slots
|
|
18
18
|
- **`cn()`** - a utility for conditionally merging class names
|
|
19
|
+
- **`createSV()`** - builds a pre-configured `sv()` with shared config defaults
|
|
19
20
|
|
|
20
21
|
`sv()` is a drop-in replacement for [CVA](https://cva.style/) (just rename `cva` to `sv`) and covers the core feature set of [tailwind-variants](https://www.tailwind-variants.org/) (`tv`) with a simpler API. See [Migrating from CVA / tailwind-variants](#migrating-from-cva--tailwind-variants) for details.
|
|
21
22
|
|
|
@@ -68,7 +69,7 @@ cn(['foo', ['bar', 'baz']]); // 'foo bar baz'
|
|
|
68
69
|
cn({ foo: true, bar: false, baz: true }); // 'foo baz'
|
|
69
70
|
|
|
70
71
|
// Mixed
|
|
71
|
-
cn('base', ['responsive'], { active: true }); // 'base active
|
|
72
|
+
cn('base', ['responsive'], { active: true }); // 'base responsive active'
|
|
72
73
|
|
|
73
74
|
// Falsy values are filtered out
|
|
74
75
|
cn('foo', null, undefined, false, 'bar'); // 'foo bar'
|
|
@@ -733,6 +734,52 @@ const button = sv('px-4 py-2 bg-blue-500', {
|
|
|
733
734
|
|
|
734
735
|
The `postProcess` function is applied to each slot's final class string independently.
|
|
735
736
|
|
|
737
|
+
### Shared Defaults with `createSV()`
|
|
738
|
+
|
|
739
|
+
`createSV(defaults)` returns a pre-configured `sv()` that merges `defaults` into every config-based call. This avoids repeating the same options — most commonly `postProcess: twMerge` — across every component:
|
|
740
|
+
|
|
741
|
+
```typescript
|
|
742
|
+
import { createSV } from 'slot-variants';
|
|
743
|
+
import { twMerge } from 'tailwind-merge';
|
|
744
|
+
|
|
745
|
+
export const customSV = createSV({
|
|
746
|
+
postProcess: twMerge,
|
|
747
|
+
cacheSize: 512
|
|
748
|
+
});
|
|
749
|
+
|
|
750
|
+
// twMerge is applied without restating it per component
|
|
751
|
+
const button = customSV('px-4 py-2 bg-blue-500', {
|
|
752
|
+
variants: {
|
|
753
|
+
size: {
|
|
754
|
+
sm: 'px-2 py-1 text-sm',
|
|
755
|
+
lg: 'px-6 py-3 text-lg'
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
});
|
|
759
|
+
```
|
|
760
|
+
|
|
761
|
+
`defaults` accepts **any** config option (`base`, `variants`, `slots`, `compoundVariants`, `cacheSize`, `introspection`, `postProcess`, …). The merge is shallow and **a per-call value always wins** over the matching default — there is no deep merging of variants or compound rules:
|
|
762
|
+
|
|
763
|
+
```typescript
|
|
764
|
+
const customSV = createSV({ postProcess: twMerge });
|
|
765
|
+
|
|
766
|
+
// This component opts out of twMerge entirely
|
|
767
|
+
const raw = customSV('btn', {
|
|
768
|
+
variants: { size: { sm: 'text-sm' } },
|
|
769
|
+
postProcess: (className) => className // overrides the default
|
|
770
|
+
});
|
|
771
|
+
```
|
|
772
|
+
|
|
773
|
+
Calls with no config object are forwarded straight to `cn()`-style merging and never see the defaults:
|
|
774
|
+
|
|
775
|
+
```typescript
|
|
776
|
+
const customSV = createSV({ postProcess: (c) => c.toUpperCase() });
|
|
777
|
+
|
|
778
|
+
customSV('flex', 'items-center'); // 'flex items-center' — defaults are skipped
|
|
779
|
+
```
|
|
780
|
+
|
|
781
|
+
A factory-level `introspection: true` is reflected in each component's return type, so the [introspection](#introspection) API is available without setting the flag on every config. A per-call `introspection: false` opts an individual component back out.
|
|
782
|
+
|
|
736
783
|
### Caching
|
|
737
784
|
|
|
738
785
|
Results are cached automatically for performance. The default cache size is **256** entries.
|
|
@@ -945,6 +992,7 @@ When used on an `sv()` definition without slots, `SlotClassProps` resolves to `{
|
|
|
945
992
|
| `VariantProps<T, E>` | Extracts variant props from an `sv()` return type, optionally excluding keys in `E` |
|
|
946
993
|
| `VariantValue<T, K>` | Extracts the value union for a single variant key `K`, without `undefined` |
|
|
947
994
|
| `SlotClassProps<T>` | Extracts the per-slot class injection shape from an `sv()` return type |
|
|
995
|
+
| `SV<DI>` | The shape of an `sv()` function, with the factory's introspection default `DI`; the return type of `createSV()` |
|
|
948
996
|
|
|
949
997
|
### Return Type
|
|
950
998
|
|
|
@@ -986,6 +1034,8 @@ Class values inside the config (`base`, `variants`, `slots`, and `compound*` `cl
|
|
|
986
1034
|
|
|
987
1035
|
- **`slot-variants/no-shared-tokens`** — flags class tokens that appear in every value of an exhaustively-covered variant, where “exhaustive” means the variant has a statically defined default value, is listed in `requiredVariants`, or `requiredVariants` is `true` (every variant required). Those tokens are constant in the rendered output, so they belong in `base` or the corresponding `slots[slot]` entry rather than being repeated in every variant value. The rule only analyzes `sv()` calls with a config, compares tokens per-slot, skips non-exhaustive variants, single-value variants, boolean shorthand, undefined or dynamic defaults, and dynamic or partially-analyzable variant value records, and reports every repeated occurrence that should be lifted out.
|
|
988
1036
|
|
|
1037
|
+
- **`slot-variants/require-top-level-config`** — flags `sv()` calls made with a config object that aren't at the module top level. The config form compiles the variant function and seeds its cache once; nesting it inside a function (a component body, an arrow, an object method, etc.) rebuilds that work — and throws away the variant cache — on every call, so the config form belongs at module scope. The cn-style calling convention of `sv()` (and every `cn()` call) carries no config and is left alone, as are calls inside top-level blocks or conditionals, which still run only at module load.
|
|
1038
|
+
|
|
989
1039
|
Only calls where `sv` or `cn` is a named import from `'slot-variants'` are analyzed. `no-conflicting-classes` skips dynamic inputs silently to avoid false positives; `no-dynamic-classes` is the opposite — it flags exactly those positions so the static analyzer can fully reason about every call. `no-shared-tokens` sits between them: it needs a fully statically analyzable, exhaustive variant before it can prove a token is constant across every value. `no-empty-classes` and `no-redundant-spaces` are independent and complement the structural rules: they cover empty and badly-shaped literals reachable from a call's arguments, regardless of whether the surrounding call is fully static.
|
|
990
1040
|
|
|
991
1041
|
### ESLint (flat config)
|
|
@@ -1011,7 +1061,8 @@ export default [
|
|
|
1011
1061
|
'slot-variants/no-dynamic-classes': 'error',
|
|
1012
1062
|
'slot-variants/no-empty-classes': 'error',
|
|
1013
1063
|
'slot-variants/no-redundant-spaces': 'error',
|
|
1014
|
-
'slot-variants/no-shared-tokens': 'error'
|
|
1064
|
+
'slot-variants/no-shared-tokens': 'error',
|
|
1065
|
+
'slot-variants/require-top-level-config': 'error'
|
|
1015
1066
|
}
|
|
1016
1067
|
}
|
|
1017
1068
|
];
|
|
@@ -1027,7 +1078,8 @@ export default [
|
|
|
1027
1078
|
"slot-variants/no-dynamic-classes": "error",
|
|
1028
1079
|
"slot-variants/no-empty-classes": "error",
|
|
1029
1080
|
"slot-variants/no-redundant-spaces": "error",
|
|
1030
|
-
"slot-variants/no-shared-tokens": "error"
|
|
1081
|
+
"slot-variants/no-shared-tokens": "error",
|
|
1082
|
+
"slot-variants/require-top-level-config": "error"
|
|
1031
1083
|
}
|
|
1032
1084
|
}
|
|
1033
1085
|
```
|
|
@@ -1193,7 +1245,7 @@ Key differences to be aware of:
|
|
|
1193
1245
|
| --- | --- | --- |
|
|
1194
1246
|
| Slot return type | Always functions: `slot({ class: '...' })` | Strings by default; functions for slots listed in `multiSlots` |
|
|
1195
1247
|
| `extend` (composition) | Supported | Not supported |
|
|
1196
|
-
| Built-in `twMerge` | Enabled by default | Use `postProcess: twMerge` |
|
|
1248
|
+
| Built-in `twMerge` | Enabled by default | Use `postProcess: twMerge`, globally via `createSV` |
|
|
1197
1249
|
|
|
1198
1250
|
**Slot return type** is the most significant difference. In `tv()`, each slot returns a function that can accept additional props. In `sv()`, slots resolve to strings directly — use the `class` prop with a slot object for per-slot overrides, or list a slot in [`multiSlots`](#multi-slots) to expose it as a `tv`-style reconfigurable function:
|
|
1199
1251
|
|
|
@@ -1219,3 +1271,13 @@ const button = sv({
|
|
|
1219
1271
|
postProcess: twMerge
|
|
1220
1272
|
});
|
|
1221
1273
|
```
|
|
1274
|
+
|
|
1275
|
+
To enable it globally — the closest equivalent to `tv`'s default `twMerge` — wrap it once with [`createSV()`](#shared-defaults-with-createsv) and import the result everywhere:
|
|
1276
|
+
|
|
1277
|
+
```typescript
|
|
1278
|
+
// custom-sv.ts
|
|
1279
|
+
import { createSV } from 'slot-variants';
|
|
1280
|
+
import { twMerge } from 'tailwind-merge';
|
|
1281
|
+
|
|
1282
|
+
export const customSV = createSV({ postProcess: twMerge });
|
|
1283
|
+
```
|
package/SKILL.md
CHANGED
|
@@ -107,6 +107,15 @@ import { twMerge } from 'tailwind-merge';
|
|
|
107
107
|
sv('px-4 py-2', { variants: { size: { lg: 'px-6 py-3' } }, postProcess: twMerge });
|
|
108
108
|
```
|
|
109
109
|
|
|
110
|
+
To avoid restating `postProcess` (or any config default) per component, wrap `sv` once with `createSV`. Defaults are shallow merged into every config-based call and a per-call value always wins:
|
|
111
|
+
|
|
112
|
+
```typescript
|
|
113
|
+
import { createSV } from 'slot-variants';
|
|
114
|
+
import { twMerge } from 'tailwind-merge';
|
|
115
|
+
|
|
116
|
+
const customSV = createSV({ postProcess: twMerge, cacheSize: 512 });
|
|
117
|
+
```
|
|
118
|
+
|
|
110
119
|
### 9. Leverage Caching for Performance
|
|
111
120
|
|
|
112
121
|
The library caches results automatically (default 256 entries). Each cache entry is one distinct combination of resolved variant values:
|
|
@@ -196,5 +205,6 @@ Class values inside the config (`base`, `variants` values, `slots` values, and `
|
|
|
196
205
|
- `VariantProps<T, E>` — Extract variant props from an `sv()` return, optionally excluding keys
|
|
197
206
|
- `VariantValue<T, K>` — Extract the value union for a single variant key, without `undefined`
|
|
198
207
|
- `SlotClassProps<T>` — Extract the per-slot class injection shape from an `sv()` return type
|
|
208
|
+
- `SV<DI>` — The shape of an `sv()` function (the return type of `createSV()`), carrying the factory's introspection default `DI`
|
|
199
209
|
|
|
200
210
|
Functions are imported as named values; types via `import type { ... } from 'slot-variants'`.
|
package/dist/eslint-plugin.cjs
CHANGED
|
@@ -1215,12 +1215,47 @@ var noEmptyClasses = {
|
|
|
1215
1215
|
});
|
|
1216
1216
|
}
|
|
1217
1217
|
};
|
|
1218
|
+
var isInsideFunctionScope = (scope) => {
|
|
1219
|
+
let current = scope;
|
|
1220
|
+
while (current) {
|
|
1221
|
+
if (current.type === "function") {
|
|
1222
|
+
return true;
|
|
1223
|
+
}
|
|
1224
|
+
current = current.upper;
|
|
1225
|
+
}
|
|
1226
|
+
return false;
|
|
1227
|
+
};
|
|
1228
|
+
var requireTopLevelConfig = {
|
|
1229
|
+
meta: {
|
|
1230
|
+
type: "problem",
|
|
1231
|
+
docs: {
|
|
1232
|
+
description: "Require sv() calls with a config object to be at the module top level",
|
|
1233
|
+
recommended: true,
|
|
1234
|
+
url: DOCS_URL
|
|
1235
|
+
},
|
|
1236
|
+
schema: [],
|
|
1237
|
+
messages: {
|
|
1238
|
+
nested: "sv() with a config object must be called at the module top level, not nested inside a function \u2014 otherwise its compiled config and variant cache are rebuilt on every call."
|
|
1239
|
+
}
|
|
1240
|
+
},
|
|
1241
|
+
create(context) {
|
|
1242
|
+
return createTrackedCallListeners(context, (node, call) => {
|
|
1243
|
+
if (!call.config) {
|
|
1244
|
+
return;
|
|
1245
|
+
}
|
|
1246
|
+
if (isInsideFunctionScope(context.sourceCode.getScope(node))) {
|
|
1247
|
+
context.report({ node, messageId: "nested" });
|
|
1248
|
+
}
|
|
1249
|
+
});
|
|
1250
|
+
}
|
|
1251
|
+
};
|
|
1218
1252
|
var rules = {
|
|
1219
1253
|
"no-conflicting-classes": noConflictingClasses,
|
|
1220
1254
|
"no-dynamic-classes": noDynamicClasses,
|
|
1221
1255
|
"no-empty-classes": noEmptyClasses,
|
|
1222
1256
|
"no-redundant-spaces": noRedundantSpaces,
|
|
1223
|
-
"no-shared-tokens": noSharedTokens
|
|
1257
|
+
"no-shared-tokens": noSharedTokens,
|
|
1258
|
+
"require-top-level-config": requireTopLevelConfig
|
|
1224
1259
|
};
|
|
1225
1260
|
var meta = { name: "slot-variants" };
|
|
1226
1261
|
var recommendedRules = {};
|
package/dist/eslint-plugin.d.ts
CHANGED
|
@@ -9,6 +9,7 @@ declare const rules: {
|
|
|
9
9
|
'no-empty-classes': Rule.RuleModule;
|
|
10
10
|
'no-redundant-spaces': Rule.RuleModule;
|
|
11
11
|
'no-shared-tokens': Rule.RuleModule;
|
|
12
|
+
'require-top-level-config': Rule.RuleModule;
|
|
12
13
|
};
|
|
13
14
|
declare const plugin: {
|
|
14
15
|
meta: {
|
|
@@ -20,6 +21,7 @@ declare const plugin: {
|
|
|
20
21
|
'no-empty-classes': Rule.RuleModule;
|
|
21
22
|
'no-redundant-spaces': Rule.RuleModule;
|
|
22
23
|
'no-shared-tokens': Rule.RuleModule;
|
|
24
|
+
'require-top-level-config': Rule.RuleModule;
|
|
23
25
|
};
|
|
24
26
|
configs: Record<string, Linter.Config>;
|
|
25
27
|
};
|
package/dist/eslint-plugin.js
CHANGED
|
@@ -1190,12 +1190,47 @@ var noEmptyClasses = {
|
|
|
1190
1190
|
});
|
|
1191
1191
|
}
|
|
1192
1192
|
};
|
|
1193
|
+
var isInsideFunctionScope = (scope) => {
|
|
1194
|
+
let current = scope;
|
|
1195
|
+
while (current) {
|
|
1196
|
+
if (current.type === "function") {
|
|
1197
|
+
return true;
|
|
1198
|
+
}
|
|
1199
|
+
current = current.upper;
|
|
1200
|
+
}
|
|
1201
|
+
return false;
|
|
1202
|
+
};
|
|
1203
|
+
var requireTopLevelConfig = {
|
|
1204
|
+
meta: {
|
|
1205
|
+
type: "problem",
|
|
1206
|
+
docs: {
|
|
1207
|
+
description: "Require sv() calls with a config object to be at the module top level",
|
|
1208
|
+
recommended: true,
|
|
1209
|
+
url: DOCS_URL
|
|
1210
|
+
},
|
|
1211
|
+
schema: [],
|
|
1212
|
+
messages: {
|
|
1213
|
+
nested: "sv() with a config object must be called at the module top level, not nested inside a function \u2014 otherwise its compiled config and variant cache are rebuilt on every call."
|
|
1214
|
+
}
|
|
1215
|
+
},
|
|
1216
|
+
create(context) {
|
|
1217
|
+
return createTrackedCallListeners(context, (node, call) => {
|
|
1218
|
+
if (!call.config) {
|
|
1219
|
+
return;
|
|
1220
|
+
}
|
|
1221
|
+
if (isInsideFunctionScope(context.sourceCode.getScope(node))) {
|
|
1222
|
+
context.report({ node, messageId: "nested" });
|
|
1223
|
+
}
|
|
1224
|
+
});
|
|
1225
|
+
}
|
|
1226
|
+
};
|
|
1193
1227
|
var rules = {
|
|
1194
1228
|
"no-conflicting-classes": noConflictingClasses,
|
|
1195
1229
|
"no-dynamic-classes": noDynamicClasses,
|
|
1196
1230
|
"no-empty-classes": noEmptyClasses,
|
|
1197
1231
|
"no-redundant-spaces": noRedundantSpaces,
|
|
1198
|
-
"no-shared-tokens": noSharedTokens
|
|
1232
|
+
"no-shared-tokens": noSharedTokens,
|
|
1233
|
+
"require-top-level-config": requireTopLevelConfig
|
|
1199
1234
|
};
|
|
1200
1235
|
var meta = { name: "slot-variants" };
|
|
1201
1236
|
var recommendedRules = {};
|
package/dist/index.cjs
CHANGED
|
@@ -21,6 +21,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
23
|
cn: () => cn,
|
|
24
|
+
createSV: () => createSV,
|
|
24
25
|
sv: () => sv
|
|
25
26
|
});
|
|
26
27
|
module.exports = __toCommonJS(index_exports);
|
|
@@ -34,6 +35,13 @@ var append = (result, value) => {
|
|
|
34
35
|
}
|
|
35
36
|
return `${result} ${value}`;
|
|
36
37
|
};
|
|
38
|
+
var appendArray = (result, array) => {
|
|
39
|
+
const flattened = cn(...array);
|
|
40
|
+
if (flattened) {
|
|
41
|
+
return append(result, flattened);
|
|
42
|
+
}
|
|
43
|
+
return result;
|
|
44
|
+
};
|
|
37
45
|
var appendRecord = (result, record) => {
|
|
38
46
|
let current = result;
|
|
39
47
|
for (const key of keys(record)) {
|
|
@@ -44,11 +52,8 @@ var appendRecord = (result, record) => {
|
|
|
44
52
|
return current;
|
|
45
53
|
};
|
|
46
54
|
function cn(...args) {
|
|
47
|
-
let index = 0;
|
|
48
55
|
let result = "";
|
|
49
|
-
|
|
50
|
-
const item = args[index];
|
|
51
|
-
index++;
|
|
56
|
+
for (const item of args) {
|
|
52
57
|
if (!item) {
|
|
53
58
|
continue;
|
|
54
59
|
}
|
|
@@ -57,7 +62,7 @@ function cn(...args) {
|
|
|
57
62
|
continue;
|
|
58
63
|
}
|
|
59
64
|
if (isArray(item)) {
|
|
60
|
-
|
|
65
|
+
result = appendArray(result, item);
|
|
61
66
|
continue;
|
|
62
67
|
}
|
|
63
68
|
if (typeof item === "object") {
|
|
@@ -674,12 +679,15 @@ var runVariantResult = (config, props) => {
|
|
|
674
679
|
}
|
|
675
680
|
return applyMultiSlots(config, props, result);
|
|
676
681
|
};
|
|
677
|
-
|
|
682
|
+
var sv = ((...args) => {
|
|
678
683
|
const last = args.at(-1);
|
|
679
684
|
if (!isConfig(last)) {
|
|
680
685
|
return cn(...args);
|
|
681
686
|
}
|
|
682
687
|
const config = compileConfig(args.slice(0, -1), last);
|
|
688
|
+
return createVariantFn(config);
|
|
689
|
+
});
|
|
690
|
+
var createVariantFn = (config) => {
|
|
683
691
|
const variantFn = (props = {}) => runVariantResult(config, props);
|
|
684
692
|
if (!config.introspection) {
|
|
685
693
|
return variantFn;
|
|
@@ -701,9 +709,24 @@ function sv(...args) {
|
|
|
701
709
|
clearCache: () => config.cache.clear(),
|
|
702
710
|
getCacheSize: () => config.cache.size
|
|
703
711
|
});
|
|
704
|
-
}
|
|
712
|
+
};
|
|
713
|
+
var createSV = (defaults = {}) => {
|
|
714
|
+
const configuredSv = (...args) => {
|
|
715
|
+
const last = args.at(-1);
|
|
716
|
+
if (!isConfig(last)) {
|
|
717
|
+
return cn(...args);
|
|
718
|
+
}
|
|
719
|
+
const config = compileConfig(args.slice(0, -1), {
|
|
720
|
+
...defaults,
|
|
721
|
+
...last
|
|
722
|
+
});
|
|
723
|
+
return createVariantFn(config);
|
|
724
|
+
};
|
|
725
|
+
return configuredSv;
|
|
726
|
+
};
|
|
705
727
|
// Annotate the CommonJS export names for ESM import in node:
|
|
706
728
|
0 && (module.exports = {
|
|
707
729
|
cn,
|
|
730
|
+
createSV,
|
|
708
731
|
sv
|
|
709
732
|
});
|
package/dist/index.d.ts
CHANGED
|
@@ -108,6 +108,18 @@ type VariantFn<S extends MaybeSlots, V extends MaybeVariants<S>, RV extends Requ
|
|
|
108
108
|
(...args: [RequiredVariantKeys<V, RV>] extends [never] ? [props?: Prettify<Props<S, V, RV, P>> | undefined] : [props: Prettify<Props<S, V, RV, P>>]): ReturnValue<S, V, MS>;
|
|
109
109
|
} & (I extends true ? Prettify<IntrospectionValues<S, V, RV, P, MS>> : unknown);
|
|
110
110
|
type NonConfigClassArg<T> = T extends Record<string, unknown> ? Exclude<StringKeyof<T>, ConfigKey> extends never ? never : T : T;
|
|
111
|
+
/**
|
|
112
|
+
* The shape of an `sv()` function. Mirrors the overloads of the exported `sv`,
|
|
113
|
+
* with the introspection default `DI` baked in by `createSV()` so configs that
|
|
114
|
+
* omit `introspection` inherit the factory default in their return type.
|
|
115
|
+
*/
|
|
116
|
+
type SV<DI extends boolean = false> = {
|
|
117
|
+
<S extends MaybeSlots = undefined, V extends MaybeVariants<S> = undefined, RV extends RequiredVariants<V> = false, P extends MaybePresets<S, V> = undefined, MS extends MultiSlots<S> = false, I extends boolean = DI>(config: Config<S, V, RV, P, MS, I>): VariantFn<S, V, RV, P, MS, I>;
|
|
118
|
+
<S extends MaybeSlots = undefined, V extends MaybeVariants<S> = undefined, RV extends RequiredVariants<V> = false, P extends MaybePresets<S, V> = undefined, MS extends MultiSlots<S> = false, I extends boolean = DI>(...args: [...ClassValue[], Config<S, V, RV, P, MS, I>]): VariantFn<S, V, RV, P, MS, I>;
|
|
119
|
+
<const T extends ClassValue[]>(...args: T & {
|
|
120
|
+
[K in keyof T]: NonConfigClassArg<T[K]>;
|
|
121
|
+
}): string;
|
|
122
|
+
};
|
|
111
123
|
/**
|
|
112
124
|
* Extracts the variant props object from an `sv()` return type
|
|
113
125
|
*
|
|
@@ -215,10 +227,28 @@ type SlotClassProps<T extends AnyFn> = ReturnType<T> extends string ? Partial<Re
|
|
|
215
227
|
* const { base, header, body } = card();
|
|
216
228
|
* ```
|
|
217
229
|
*/
|
|
218
|
-
declare
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
230
|
+
declare const sv: SV;
|
|
231
|
+
type RawConfig = Config<MaybeSlots, MaybeVariants<MaybeSlots>, RequiredVariants<MaybeVariants<MaybeSlots>>, MaybePresets<MaybeSlots, MaybeVariants<MaybeSlots>>, MultiSlots<MaybeSlots>, boolean>;
|
|
232
|
+
/**
|
|
233
|
+
* Builds a pre-configured `sv()` function whose `defaults` are applied to every
|
|
234
|
+
* config-based call. The defaults accept any config option and are shallow
|
|
235
|
+
* merged so a per-call value always wins; calls with no config are forwarded
|
|
236
|
+
* to `cn()`-style merging untouched.
|
|
237
|
+
*
|
|
238
|
+
* @example
|
|
239
|
+
* ```ts
|
|
240
|
+
* import { twMerge } from 'tailwind-merge';
|
|
241
|
+
*
|
|
242
|
+
* const customSV = createSV({ postProcess: twMerge, cacheSize: 512 });
|
|
243
|
+
*
|
|
244
|
+
* // twMerge is applied without restating it per component
|
|
245
|
+
* const button = customSV('px-4 py-2', {
|
|
246
|
+
* variants: { size: { sm: 'px-2', lg: 'px-6' } }
|
|
247
|
+
* });
|
|
248
|
+
* ```
|
|
249
|
+
*/
|
|
250
|
+
declare const createSV: <I extends boolean = false>(defaults?: RawConfig & {
|
|
251
|
+
introspection?: I | undefined;
|
|
252
|
+
}) => SV<I>;
|
|
223
253
|
|
|
224
|
-
export { type ClassValue, type SlotClassProps, type VariantProps, type VariantValue, cn, sv };
|
|
254
|
+
export { type ClassValue, type SV, type SlotClassProps, type VariantProps, type VariantValue, cn, createSV, sv };
|
package/dist/index.js
CHANGED
|
@@ -7,6 +7,13 @@ var append = (result, value) => {
|
|
|
7
7
|
}
|
|
8
8
|
return `${result} ${value}`;
|
|
9
9
|
};
|
|
10
|
+
var appendArray = (result, array) => {
|
|
11
|
+
const flattened = cn(...array);
|
|
12
|
+
if (flattened) {
|
|
13
|
+
return append(result, flattened);
|
|
14
|
+
}
|
|
15
|
+
return result;
|
|
16
|
+
};
|
|
10
17
|
var appendRecord = (result, record) => {
|
|
11
18
|
let current = result;
|
|
12
19
|
for (const key of keys(record)) {
|
|
@@ -17,11 +24,8 @@ var appendRecord = (result, record) => {
|
|
|
17
24
|
return current;
|
|
18
25
|
};
|
|
19
26
|
function cn(...args) {
|
|
20
|
-
let index = 0;
|
|
21
27
|
let result = "";
|
|
22
|
-
|
|
23
|
-
const item = args[index];
|
|
24
|
-
index++;
|
|
28
|
+
for (const item of args) {
|
|
25
29
|
if (!item) {
|
|
26
30
|
continue;
|
|
27
31
|
}
|
|
@@ -30,7 +34,7 @@ function cn(...args) {
|
|
|
30
34
|
continue;
|
|
31
35
|
}
|
|
32
36
|
if (isArray(item)) {
|
|
33
|
-
|
|
37
|
+
result = appendArray(result, item);
|
|
34
38
|
continue;
|
|
35
39
|
}
|
|
36
40
|
if (typeof item === "object") {
|
|
@@ -647,12 +651,15 @@ var runVariantResult = (config, props) => {
|
|
|
647
651
|
}
|
|
648
652
|
return applyMultiSlots(config, props, result);
|
|
649
653
|
};
|
|
650
|
-
|
|
654
|
+
var sv = ((...args) => {
|
|
651
655
|
const last = args.at(-1);
|
|
652
656
|
if (!isConfig(last)) {
|
|
653
657
|
return cn(...args);
|
|
654
658
|
}
|
|
655
659
|
const config = compileConfig(args.slice(0, -1), last);
|
|
660
|
+
return createVariantFn(config);
|
|
661
|
+
});
|
|
662
|
+
var createVariantFn = (config) => {
|
|
656
663
|
const variantFn = (props = {}) => runVariantResult(config, props);
|
|
657
664
|
if (!config.introspection) {
|
|
658
665
|
return variantFn;
|
|
@@ -674,8 +681,23 @@ function sv(...args) {
|
|
|
674
681
|
clearCache: () => config.cache.clear(),
|
|
675
682
|
getCacheSize: () => config.cache.size
|
|
676
683
|
});
|
|
677
|
-
}
|
|
684
|
+
};
|
|
685
|
+
var createSV = (defaults = {}) => {
|
|
686
|
+
const configuredSv = (...args) => {
|
|
687
|
+
const last = args.at(-1);
|
|
688
|
+
if (!isConfig(last)) {
|
|
689
|
+
return cn(...args);
|
|
690
|
+
}
|
|
691
|
+
const config = compileConfig(args.slice(0, -1), {
|
|
692
|
+
...defaults,
|
|
693
|
+
...last
|
|
694
|
+
});
|
|
695
|
+
return createVariantFn(config);
|
|
696
|
+
};
|
|
697
|
+
return configuredSv;
|
|
698
|
+
};
|
|
678
699
|
export {
|
|
679
700
|
cn,
|
|
701
|
+
createSV,
|
|
680
702
|
sv
|
|
681
703
|
};
|