nestable-tailwind-variants 0.1.2 → 0.1.4
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 +71 -50
- package/dist/composeNtv.d.ts +47 -0
- package/dist/composeNtv.d.ts.map +1 -0
- package/dist/composeNtv.js +55 -0
- package/dist/composeNtv.js.map +1 -0
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/ntv.d.ts +1 -60
- package/dist/ntv.d.ts.map +1 -1
- package/dist/ntv.js +2 -54
- package/dist/ntv.js.map +1 -1
- package/dist/types.d.ts +17 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/utils.d.ts +2 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +4 -0
- package/dist/utils.js.map +1 -0
- package/package.json +7 -6
package/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# nestable-tailwind-variants
|
|
2
2
|
|
|
3
|
-
A variant styling library for Tailwind CSS that
|
|
3
|
+
A variant styling library for Tailwind CSS that expresses complex style combinations through nested condition definitions instead of flat `compoundVariants` patterns.
|
|
4
4
|
|
|
5
|
-
Inspired by [React Spectrum's
|
|
5
|
+
Inspired by [React Spectrum's conditional styles](https://react-spectrum.adobe.com/styling#conditional-styles), with some ideas from [tailwind-variants](https://www.tailwind-variants.org/).
|
|
6
6
|
|
|
7
7
|
## Installation
|
|
8
8
|
|
|
@@ -12,68 +12,42 @@ npm install nestable-tailwind-variants
|
|
|
12
12
|
|
|
13
13
|
## Basic Usage
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
The `ntv` function creates a style function from a nested style definition. Conditions can be nested to express compound states like "primary variant when hovered":
|
|
16
16
|
|
|
17
17
|
```tsx
|
|
18
18
|
import { ntv } from 'nestable-tailwind-variants';
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
variant: {
|
|
23
|
-
primary: 'bg-blue-500 text-white',
|
|
24
|
-
secondary: 'bg-gray-200 text-gray-800',
|
|
25
|
-
},
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
button({ variant: 'primary' });
|
|
29
|
-
// => 'px-4 py-2 rounded font-medium bg-blue-500 text-white'
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
Class conflicts are automatically resolved by [tailwind-merge](https://github.com/dcastil/tailwind-merge).
|
|
33
|
-
|
|
34
|
-
Boolean conditions starting with `is` or `allows` can be used directly without nesting:
|
|
35
|
-
|
|
36
|
-
```tsx
|
|
37
|
-
const button = ntv<{ isDisabled?: boolean }>({
|
|
38
|
-
default: 'bg-blue-500',
|
|
39
|
-
isDisabled: 'bg-gray-300 cursor-not-allowed',
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
button({ isDisabled: true });
|
|
43
|
-
// => 'bg-gray-300 cursor-not-allowed'
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
## Nested Conditions
|
|
47
|
-
|
|
48
|
-
Nest conditions to define styles that apply when multiple conditions are true.
|
|
49
|
-
|
|
50
|
-
```tsx
|
|
51
|
-
interface CardStyleProps {
|
|
52
|
-
variant?: 'elevated';
|
|
20
|
+
interface ButtonStyleProps {
|
|
21
|
+
variant?: 'primary' | 'secondary';
|
|
53
22
|
isHovered?: boolean;
|
|
54
23
|
}
|
|
55
24
|
|
|
56
|
-
const
|
|
25
|
+
const button = ntv<ButtonStyleProps>({
|
|
26
|
+
default: 'px-4 py-2 rounded font-medium',
|
|
57
27
|
variant: {
|
|
58
|
-
|
|
59
|
-
default: '
|
|
60
|
-
isHovered: '
|
|
28
|
+
primary: {
|
|
29
|
+
default: 'bg-blue-500 text-white',
|
|
30
|
+
isHovered: 'bg-blue-600',
|
|
31
|
+
},
|
|
32
|
+
secondary: {
|
|
33
|
+
default: 'bg-gray-200 text-gray-800',
|
|
34
|
+
isHovered: 'bg-gray-300',
|
|
61
35
|
},
|
|
62
36
|
},
|
|
63
37
|
});
|
|
64
38
|
|
|
65
|
-
|
|
66
|
-
// => '
|
|
39
|
+
button({ variant: 'primary' });
|
|
40
|
+
// => 'px-4 py-2 rounded font-medium bg-blue-500 text-white'
|
|
67
41
|
|
|
68
|
-
|
|
69
|
-
// => '
|
|
42
|
+
button({ variant: 'primary', isHovered: true });
|
|
43
|
+
// => 'px-4 py-2 rounded font-medium bg-blue-600 text-white'
|
|
70
44
|
```
|
|
71
45
|
|
|
72
|
-
|
|
46
|
+
Conditions at the same level are mutually exclusive and ordered—the last matching condition takes precedence. Class conflicts are automatically resolved by [tailwind-merge](https://github.com/dcastil/tailwind-merge).
|
|
73
47
|
|
|
74
|
-
##
|
|
48
|
+
## Why Nested?
|
|
75
49
|
|
|
76
|
-
|
|
50
|
+
When combining multiple conditions like `variant` + `isHovered`, tailwind-variants requires a flat `compoundVariants` array.
|
|
77
51
|
|
|
78
52
|
**tailwind-variants:**
|
|
79
53
|
|
|
@@ -97,6 +71,8 @@ const button = tv({
|
|
|
97
71
|
});
|
|
98
72
|
```
|
|
99
73
|
|
|
74
|
+
With nestable-tailwind-variants, you can nest conditions directly under each variant.
|
|
75
|
+
|
|
100
76
|
**nestable-tailwind-variants:**
|
|
101
77
|
|
|
102
78
|
```tsx
|
|
@@ -123,11 +99,28 @@ const button = ntv<ButtonStyleProps>({
|
|
|
123
99
|
});
|
|
124
100
|
```
|
|
125
101
|
|
|
126
|
-
Nesting
|
|
102
|
+
Nesting keeps related styles grouped together, making it easier to see which hover/pressed states belong to which variant.
|
|
103
|
+
|
|
104
|
+
## With React Aria Components
|
|
105
|
+
|
|
106
|
+
Since `ntv` returns a function, it works directly with [React Aria Components](https://react-aria.adobe.com/)' render props:
|
|
107
|
+
|
|
108
|
+
```tsx
|
|
109
|
+
import { Checkbox, type CheckboxRenderProps } from 'react-aria-components';
|
|
110
|
+
import { ntv } from 'nestable-tailwind-variants';
|
|
111
|
+
|
|
112
|
+
<Checkbox
|
|
113
|
+
className={ntv<CheckboxRenderProps>({
|
|
114
|
+
default: 'bg-gray-100',
|
|
115
|
+
isHovered: 'bg-gray-200',
|
|
116
|
+
isSelected: 'bg-gray-900',
|
|
117
|
+
})}
|
|
118
|
+
/>;
|
|
119
|
+
```
|
|
127
120
|
|
|
128
121
|
## Composing Styles
|
|
129
122
|
|
|
130
|
-
Combine multiple style functions using `composeNtv
|
|
123
|
+
Combine multiple style functions using `composeNtv`. This is useful for reusing common styles across multiple components:
|
|
131
124
|
|
|
132
125
|
```tsx
|
|
133
126
|
import { ntv, composeNtv } from 'nestable-tailwind-variants';
|
|
@@ -153,6 +146,34 @@ button({ size: 'lg', variant: 'primary' });
|
|
|
153
146
|
// => 'rounded font-medium px-4 py-2 text-lg bg-blue-500 text-white'
|
|
154
147
|
```
|
|
155
148
|
|
|
149
|
+
## VS Code Integration
|
|
150
|
+
|
|
151
|
+
With [Tailwind CSS IntelliSense](https://marketplace.visualstudio.com/items?itemName=bradlc.vscode-tailwindcss) installed, add the following to your VS Code settings (`settings.json`) to enable autocomplete in `ntv` calls:
|
|
152
|
+
|
|
153
|
+
```json
|
|
154
|
+
{
|
|
155
|
+
"tailwindCSS.classFunctions": ["ntv"]
|
|
156
|
+
}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
## ESLint Integration
|
|
160
|
+
|
|
161
|
+
To lint Tailwind classes inside `ntv` calls with [eslint-plugin-better-tailwindcss](https://github.com/schoero/eslint-plugin-better-tailwindcss), extend the default callees in your ESLint configuration:
|
|
162
|
+
|
|
163
|
+
```js
|
|
164
|
+
import { getDefaultCallees } from 'eslint-plugin-better-tailwindcss/defaults';
|
|
165
|
+
|
|
166
|
+
export default [
|
|
167
|
+
{
|
|
168
|
+
settings: {
|
|
169
|
+
'better-tailwindcss': {
|
|
170
|
+
callees: [...getDefaultCallees(), ['ntv', [{ match: 'objectValues' }]]],
|
|
171
|
+
},
|
|
172
|
+
},
|
|
173
|
+
},
|
|
174
|
+
];
|
|
175
|
+
```
|
|
176
|
+
|
|
156
177
|
## API
|
|
157
178
|
|
|
158
179
|
### `ntv<Props>(style)`
|
|
@@ -162,7 +183,7 @@ Creates a style function from a nested style definition.
|
|
|
162
183
|
- `style` - Style definition object
|
|
163
184
|
- `default` - Base styles (skipped when other conditions match at the same level)
|
|
164
185
|
- `[variantKey]` - Style definitions for each variant value
|
|
165
|
-
- `is*` / `allows*` -
|
|
186
|
+
- `is*` / `allows*` - Styles applied when the boolean prop is `true`
|
|
166
187
|
- Returns `(props: Partial<Props>) => string`
|
|
167
188
|
|
|
168
189
|
### `composeNtv(...fns)`
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { NTVConfig, StyleFunction } from './types.js';
|
|
2
|
+
type UnionToIntersection<U> = (U extends unknown ? (k: U) => void : never) extends (k: infer I) => void ? I : never;
|
|
3
|
+
type ExtractProps<T> = T extends StyleFunction<infer P> ? P : never;
|
|
4
|
+
/**
|
|
5
|
+
* Creates a customized `composeNtv` function with the specified options.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```ts
|
|
9
|
+
* // Disable tailwind-merge
|
|
10
|
+
* const composeNtvNoMerge = createComposeNtv({ twMerge: false });
|
|
11
|
+
*
|
|
12
|
+
* // With custom tailwind-merge config
|
|
13
|
+
* const customComposeNtv = createComposeNtv({
|
|
14
|
+
* twMergeConfig: {
|
|
15
|
+
* extend: {
|
|
16
|
+
* theme: {
|
|
17
|
+
* shadow: ['100', '200', '300'],
|
|
18
|
+
* },
|
|
19
|
+
* },
|
|
20
|
+
* },
|
|
21
|
+
* });
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
export declare function createComposeNtv(options?: NTVConfig): <T extends StyleFunction[]>(...fns: T) => StyleFunction<UnionToIntersection<ExtractProps<T[number]>>>;
|
|
25
|
+
/**
|
|
26
|
+
* Composes multiple style functions into a single function.
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* ```ts
|
|
30
|
+
* const baseButton = ntv<{ size?: 'sm' | 'lg' }>({
|
|
31
|
+
* default: 'rounded font-medium',
|
|
32
|
+
* size: { sm: 'px-2 py-1 text-sm', lg: 'px-4 py-2 text-lg' },
|
|
33
|
+
* });
|
|
34
|
+
*
|
|
35
|
+
* const coloredButton = ntv<{ variant?: 'primary' | 'secondary' }>({
|
|
36
|
+
* variant: { primary: 'bg-blue-500 text-white', secondary: 'bg-gray-200' },
|
|
37
|
+
* });
|
|
38
|
+
*
|
|
39
|
+
* const button = composeNtv(baseButton, coloredButton);
|
|
40
|
+
*
|
|
41
|
+
* button({ size: 'lg', variant: 'primary' });
|
|
42
|
+
* // => 'rounded font-medium px-4 py-2 text-lg bg-blue-500 text-white'
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
45
|
+
export declare const composeNtv: <T extends StyleFunction[]>(...fns: T) => StyleFunction<UnionToIntersection<ExtractProps<T[number]>>>;
|
|
46
|
+
export {};
|
|
47
|
+
//# sourceMappingURL=composeNtv.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"composeNtv.d.ts","sourceRoot":"","sources":["../src/composeNtv.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAG3D,KAAK,mBAAmB,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS,OAAO,GAAG,CAAC,CAAC,EAAE,CAAC,KAAK,IAAI,GAAG,KAAK,CAAC,SAAS,CACjF,CAAC,EAAE,MAAM,CAAC,KACP,IAAI,GACL,CAAC,GACD,KAAK,CAAC;AAEV,KAAK,YAAY,CAAC,CAAC,IAAI,CAAC,SAAS,aAAa,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;AAEpE;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,GAAE,SAAc,IAS3B,CAAC,SAAS,aAAa,EAAE,EAClD,GAAG,KAAK,CAAC,KACR,aAAa,CAAC,mBAAmB,CAAC,YAAY,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAG/D;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,UAAU,GA3BM,CAAC,SAAS,aAAa,EAAE,UAC1C,CAAC,KACR,aAAa,CAAC,mBAAmB,CAAC,YAAY,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAyBnB,CAAC"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { extendTailwindMerge, twMerge } from 'tailwind-merge';
|
|
2
|
+
import { joinClasses } from './utils.js';
|
|
3
|
+
/**
|
|
4
|
+
* Creates a customized `composeNtv` function with the specified options.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```ts
|
|
8
|
+
* // Disable tailwind-merge
|
|
9
|
+
* const composeNtvNoMerge = createComposeNtv({ twMerge: false });
|
|
10
|
+
*
|
|
11
|
+
* // With custom tailwind-merge config
|
|
12
|
+
* const customComposeNtv = createComposeNtv({
|
|
13
|
+
* twMergeConfig: {
|
|
14
|
+
* extend: {
|
|
15
|
+
* theme: {
|
|
16
|
+
* shadow: ['100', '200', '300'],
|
|
17
|
+
* },
|
|
18
|
+
* },
|
|
19
|
+
* },
|
|
20
|
+
* });
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
export function createComposeNtv(options = {}) {
|
|
24
|
+
const { twMerge: useTwMerge = true, twMergeConfig } = options;
|
|
25
|
+
const mergeClasses = useTwMerge
|
|
26
|
+
? twMergeConfig
|
|
27
|
+
? extendTailwindMerge(twMergeConfig)
|
|
28
|
+
: twMerge
|
|
29
|
+
: joinClasses;
|
|
30
|
+
return function composeNtv(...fns) {
|
|
31
|
+
return (props) => mergeClasses(...fns.map((fn) => fn(props)));
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Composes multiple style functions into a single function.
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* ```ts
|
|
39
|
+
* const baseButton = ntv<{ size?: 'sm' | 'lg' }>({
|
|
40
|
+
* default: 'rounded font-medium',
|
|
41
|
+
* size: { sm: 'px-2 py-1 text-sm', lg: 'px-4 py-2 text-lg' },
|
|
42
|
+
* });
|
|
43
|
+
*
|
|
44
|
+
* const coloredButton = ntv<{ variant?: 'primary' | 'secondary' }>({
|
|
45
|
+
* variant: { primary: 'bg-blue-500 text-white', secondary: 'bg-gray-200' },
|
|
46
|
+
* });
|
|
47
|
+
*
|
|
48
|
+
* const button = composeNtv(baseButton, coloredButton);
|
|
49
|
+
*
|
|
50
|
+
* button({ size: 'lg', variant: 'primary' });
|
|
51
|
+
* // => 'rounded font-medium px-4 py-2 text-lg bg-blue-500 text-white'
|
|
52
|
+
* ```
|
|
53
|
+
*/
|
|
54
|
+
export const composeNtv = createComposeNtv();
|
|
55
|
+
//# sourceMappingURL=composeNtv.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"composeNtv.js","sourceRoot":"","sources":["../src/composeNtv.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAE9D,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAUzC;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,UAAU,gBAAgB,CAAC,UAAqB,EAAE;IACtD,MAAM,EAAE,OAAO,EAAE,UAAU,GAAG,IAAI,EAAE,aAAa,EAAE,GAAG,OAAO,CAAC;IAE9D,MAAM,YAAY,GAAG,UAAU;QAC7B,CAAC,CAAC,aAAa;YACb,CAAC,CAAC,mBAAmB,CAAC,aAAa,CAAC;YACpC,CAAC,CAAC,OAAO;QACX,CAAC,CAAC,WAAW,CAAC;IAEhB,OAAO,SAAS,UAAU,CACxB,GAAG,GAAM;QAET,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,YAAY,CAAC,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAChE,CAAC,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,gBAAgB,EAAE,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export
|
|
1
|
+
export { composeNtv, createComposeNtv } from './composeNtv.js';
|
|
2
|
+
export { createNTV, ntv } from './ntv.js';
|
|
3
|
+
export type { NTVConfig, TWMergeConfig } from './types.js';
|
|
3
4
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAC/D,OAAO,EAAE,SAAS,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAC1C,YAAY,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC"}
|
package/dist/index.js
CHANGED
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAC/D,OAAO,EAAE,SAAS,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC"}
|
package/dist/ntv.d.ts
CHANGED
|
@@ -1,18 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export type TWMergeConfig = Parameters<typeof extendTailwindMerge>[0];
|
|
3
|
-
export interface NTVConfig {
|
|
4
|
-
/**
|
|
5
|
-
* Whether to use `tailwind-merge` to resolve conflicting Tailwind classes.
|
|
6
|
-
* @see https://github.com/dcastil/tailwind-merge
|
|
7
|
-
* @default true
|
|
8
|
-
*/
|
|
9
|
-
twMerge?: boolean;
|
|
10
|
-
/**
|
|
11
|
-
* Custom configuration for `tailwind-merge`.
|
|
12
|
-
* @see https://github.com/dcastil/tailwind-merge/blob/main/docs/configuration.md
|
|
13
|
-
*/
|
|
14
|
-
twMergeConfig?: TWMergeConfig;
|
|
15
|
-
}
|
|
1
|
+
import type { NTVConfig, StyleFunction } from './types.js';
|
|
16
2
|
type BooleanConditionKey = `is${Capitalize<string>}` | `allows${Capitalize<string>}`;
|
|
17
3
|
type ExtractBooleanKeys<Props> = {
|
|
18
4
|
[K in keyof Props]: K extends BooleanConditionKey ? K : never;
|
|
@@ -28,9 +14,6 @@ type StyleDefinition<Props> = {
|
|
|
28
14
|
[V in Extract<Props[K], string>]?: NestedStyleValue<Props>;
|
|
29
15
|
};
|
|
30
16
|
};
|
|
31
|
-
type StyleFunction<P = Record<string, unknown>> = (props: Partial<P>) => string;
|
|
32
|
-
type UnionToIntersection<U> = (U extends unknown ? (k: U) => void : never) extends (k: infer I) => void ? I : never;
|
|
33
|
-
type ExtractProps<T> = T extends StyleFunction<infer P> ? P : never;
|
|
34
17
|
/**
|
|
35
18
|
* Creates a customized `ntv` function with the specified options.
|
|
36
19
|
*
|
|
@@ -74,47 +57,5 @@ export declare function createNTV(options?: NTVConfig): <Props extends Record<st
|
|
|
74
57
|
* ```
|
|
75
58
|
*/
|
|
76
59
|
export declare const ntv: <Props extends Record<string, any>>(style: StyleDefinition<Props>) => StyleFunction<Props>;
|
|
77
|
-
/**
|
|
78
|
-
* Creates a customized `composeNtv` function with the specified options.
|
|
79
|
-
*
|
|
80
|
-
* @example
|
|
81
|
-
* ```ts
|
|
82
|
-
* // Disable tailwind-merge
|
|
83
|
-
* const composeNtvNoMerge = createComposeNtv({ twMerge: false });
|
|
84
|
-
*
|
|
85
|
-
* // With custom tailwind-merge config
|
|
86
|
-
* const customComposeNtv = createComposeNtv({
|
|
87
|
-
* twMergeConfig: {
|
|
88
|
-
* extend: {
|
|
89
|
-
* theme: {
|
|
90
|
-
* shadow: ['100', '200', '300'],
|
|
91
|
-
* },
|
|
92
|
-
* },
|
|
93
|
-
* },
|
|
94
|
-
* });
|
|
95
|
-
* ```
|
|
96
|
-
*/
|
|
97
|
-
export declare function createComposeNtv(options?: NTVConfig): <T extends StyleFunction[]>(...fns: T) => StyleFunction<UnionToIntersection<ExtractProps<T[number]>>>;
|
|
98
|
-
/**
|
|
99
|
-
* Composes multiple style functions into a single function.
|
|
100
|
-
*
|
|
101
|
-
* @example
|
|
102
|
-
* ```ts
|
|
103
|
-
* const baseButton = ntv<{ size?: 'sm' | 'lg' }>({
|
|
104
|
-
* default: 'rounded font-medium',
|
|
105
|
-
* size: { sm: 'px-2 py-1 text-sm', lg: 'px-4 py-2 text-lg' },
|
|
106
|
-
* });
|
|
107
|
-
*
|
|
108
|
-
* const coloredButton = ntv<{ variant?: 'primary' | 'secondary' }>({
|
|
109
|
-
* variant: { primary: 'bg-blue-500 text-white', secondary: 'bg-gray-200' },
|
|
110
|
-
* });
|
|
111
|
-
*
|
|
112
|
-
* const button = composeNtv(baseButton, coloredButton);
|
|
113
|
-
*
|
|
114
|
-
* button({ size: 'lg', variant: 'primary' });
|
|
115
|
-
* // => 'rounded font-medium px-4 py-2 text-lg bg-blue-500 text-white'
|
|
116
|
-
* ```
|
|
117
|
-
*/
|
|
118
|
-
export declare const composeNtv: <T extends StyleFunction[]>(...fns: T) => StyleFunction<UnionToIntersection<ExtractProps<T[number]>>>;
|
|
119
60
|
export {};
|
|
120
61
|
//# sourceMappingURL=ntv.d.ts.map
|
package/dist/ntv.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ntv.d.ts","sourceRoot":"","sources":["../src/ntv.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ntv.d.ts","sourceRoot":"","sources":["../src/ntv.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAG3D,KAAK,mBAAmB,GAAG,KAAK,UAAU,CAAC,MAAM,CAAC,EAAE,GAAG,SAAS,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;AAErF,KAAK,kBAAkB,CAAC,KAAK,IAAI;KAC9B,CAAC,IAAI,MAAM,KAAK,GAAG,CAAC,SAAS,mBAAmB,GAAG,CAAC,GAAG,KAAK;CAC9D,CAAC,MAAM,KAAK,CAAC,CAAC;AAEf,KAAK,kBAAkB,CAAC,KAAK,IAAI,OAAO,CAAC,MAAM,KAAK,EAAE,kBAAkB,CAAC,KAAK,CAAC,CAAC,CAAC;AAEjF,KAAK,gBAAgB,CAAC,KAAK,IAAI,MAAM,GAAG,eAAe,CAAC,KAAK,CAAC,CAAC;AAE/D,KAAK,eAAe,CAAC,KAAK,IAAI;IAC5B,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,GAAG;KACD,CAAC,IAAI,kBAAkB,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,gBAAgB,CAAC,KAAK,CAAC;CAC3D,GAAG;KACD,CAAC,IAAI,kBAAkB,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE;SAChC,CAAC,IAAI,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,gBAAgB,CAAC,KAAK,CAAC;KAC3D;CACF,CAAC;AAgFF;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,SAAS,CAAC,OAAO,GAAE,SAAc,IAS3B,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EACnD,OAAO,eAAe,CAAC,KAAK,CAAC,KAC5B,aAAa,CAAC,KAAK,CAAC,CAaxB;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,GAAG,GAtCM,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,SAC5C,eAAe,CAAC,KAAK,CAAC,KAC5B,aAAa,CAAC,KAAK,CAoCM,CAAC"}
|
package/dist/ntv.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { extendTailwindMerge, twMerge } from 'tailwind-merge';
|
|
2
|
+
import { joinClasses } from './utils.js';
|
|
2
3
|
function isStyleDefinition(value) {
|
|
3
4
|
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
4
5
|
}
|
|
@@ -58,9 +59,6 @@ function evaluateDefinition(definition, context) {
|
|
|
58
59
|
}
|
|
59
60
|
}
|
|
60
61
|
}
|
|
61
|
-
function joinClasses(...classes) {
|
|
62
|
-
return classes.filter(Boolean).join(' ');
|
|
63
|
-
}
|
|
64
62
|
/**
|
|
65
63
|
* Creates a customized `ntv` function with the specified options.
|
|
66
64
|
*
|
|
@@ -122,54 +120,4 @@ export function createNTV(options = {}) {
|
|
|
122
120
|
* ```
|
|
123
121
|
*/
|
|
124
122
|
export const ntv = createNTV();
|
|
125
|
-
/**
|
|
126
|
-
* Creates a customized `composeNtv` function with the specified options.
|
|
127
|
-
*
|
|
128
|
-
* @example
|
|
129
|
-
* ```ts
|
|
130
|
-
* // Disable tailwind-merge
|
|
131
|
-
* const composeNtvNoMerge = createComposeNtv({ twMerge: false });
|
|
132
|
-
*
|
|
133
|
-
* // With custom tailwind-merge config
|
|
134
|
-
* const customComposeNtv = createComposeNtv({
|
|
135
|
-
* twMergeConfig: {
|
|
136
|
-
* extend: {
|
|
137
|
-
* theme: {
|
|
138
|
-
* shadow: ['100', '200', '300'],
|
|
139
|
-
* },
|
|
140
|
-
* },
|
|
141
|
-
* },
|
|
142
|
-
* });
|
|
143
|
-
* ```
|
|
144
|
-
*/
|
|
145
|
-
export function createComposeNtv(options = {}) {
|
|
146
|
-
const { twMerge: useTwMerge = true, twMergeConfig } = options;
|
|
147
|
-
const mergeClasses = useTwMerge
|
|
148
|
-
? twMergeConfig
|
|
149
|
-
? extendTailwindMerge(twMergeConfig)
|
|
150
|
-
: twMerge
|
|
151
|
-
: joinClasses;
|
|
152
|
-
return (...fns) => (props) => mergeClasses(...fns.map((fn) => fn(props)));
|
|
153
|
-
}
|
|
154
|
-
/**
|
|
155
|
-
* Composes multiple style functions into a single function.
|
|
156
|
-
*
|
|
157
|
-
* @example
|
|
158
|
-
* ```ts
|
|
159
|
-
* const baseButton = ntv<{ size?: 'sm' | 'lg' }>({
|
|
160
|
-
* default: 'rounded font-medium',
|
|
161
|
-
* size: { sm: 'px-2 py-1 text-sm', lg: 'px-4 py-2 text-lg' },
|
|
162
|
-
* });
|
|
163
|
-
*
|
|
164
|
-
* const coloredButton = ntv<{ variant?: 'primary' | 'secondary' }>({
|
|
165
|
-
* variant: { primary: 'bg-blue-500 text-white', secondary: 'bg-gray-200' },
|
|
166
|
-
* });
|
|
167
|
-
*
|
|
168
|
-
* const button = composeNtv(baseButton, coloredButton);
|
|
169
|
-
*
|
|
170
|
-
* button({ size: 'lg', variant: 'primary' });
|
|
171
|
-
* // => 'rounded font-medium px-4 py-2 text-lg bg-blue-500 text-white'
|
|
172
|
-
* ```
|
|
173
|
-
*/
|
|
174
|
-
export const composeNtv = createComposeNtv();
|
|
175
123
|
//# sourceMappingURL=ntv.js.map
|
package/dist/ntv.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ntv.js","sourceRoot":"","sources":["../src/ntv.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"ntv.js","sourceRoot":"","sources":["../src/ntv.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { extendTailwindMerge } from 'tailwind-merge';
|
|
2
|
+
export type TWMergeConfig = Parameters<typeof extendTailwindMerge>[0];
|
|
3
|
+
export interface NTVConfig {
|
|
4
|
+
/**
|
|
5
|
+
* Whether to use `tailwind-merge` to resolve conflicting Tailwind classes.
|
|
6
|
+
* @see https://github.com/dcastil/tailwind-merge
|
|
7
|
+
* @default true
|
|
8
|
+
*/
|
|
9
|
+
twMerge?: boolean;
|
|
10
|
+
/**
|
|
11
|
+
* Custom configuration for `tailwind-merge`.
|
|
12
|
+
* @see https://github.com/dcastil/tailwind-merge/blob/main/docs/configuration.md
|
|
13
|
+
*/
|
|
14
|
+
twMergeConfig?: TWMergeConfig;
|
|
15
|
+
}
|
|
16
|
+
export type StyleFunction<P = Record<string, unknown>> = (props: Partial<P>) => string;
|
|
17
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAErD,MAAM,MAAM,aAAa,GAAG,UAAU,CAAC,OAAO,mBAAmB,CAAC,CAAC,CAAC,CAAC,CAAC;AAEtE,MAAM,WAAW,SAAS;IACxB;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAElB;;;OAGG;IACH,aAAa,CAAC,EAAE,aAAa,CAAC;CAC/B;AAED,MAAM,MAAM,aAAa,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC"}
|
package/dist/utils.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,wBAAgB,WAAW,CAAC,GAAG,OAAO,EAAE,MAAM,EAAE,GAAG,MAAM,CAExD"}
|
package/dist/utils.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,WAAW,CAAC,GAAG,OAAiB;IAC9C,OAAO,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC3B,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nestable-tailwind-variants",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "A variant styling library for Tailwind CSS that
|
|
3
|
+
"version": "0.1.4",
|
|
4
|
+
"description": "A variant styling library for Tailwind CSS that expresses complex style combinations through nested condition definitions instead of flat `compoundVariants` patterns.",
|
|
5
5
|
"homepage": "https://github.com/yuheiy/nestable-tailwind-variants#readme",
|
|
6
6
|
"bugs": {
|
|
7
7
|
"url": "https://github.com/yuheiy/nestable-tailwind-variants/issues"
|
|
@@ -28,15 +28,15 @@
|
|
|
28
28
|
"@commitlint/config-conventional": "^20.3.1",
|
|
29
29
|
"bumpp": "^10.3.2",
|
|
30
30
|
"lint-staged": "^16.2.7",
|
|
31
|
-
"oxfmt": "^0.
|
|
32
|
-
"oxlint": "^1.
|
|
31
|
+
"oxfmt": "^0.24.0",
|
|
32
|
+
"oxlint": "^1.39.0",
|
|
33
33
|
"typescript": "^5.9.3",
|
|
34
|
-
"vitest": "^4.0.
|
|
34
|
+
"vitest": "^4.0.17",
|
|
35
35
|
"node": "runtime:^24.12.0"
|
|
36
36
|
},
|
|
37
37
|
"lint-staged": {
|
|
38
38
|
"*": "oxfmt --no-error-on-unmatched-pattern",
|
|
39
|
-
"*.{js,jsx,ts,tsx,mjs,cjs}": "
|
|
39
|
+
"*.{js,jsx,ts,tsx,mjs,cjs}": "node --run lint"
|
|
40
40
|
},
|
|
41
41
|
"devEngines": {
|
|
42
42
|
"runtime": {
|
|
@@ -47,6 +47,7 @@
|
|
|
47
47
|
},
|
|
48
48
|
"scripts": {
|
|
49
49
|
"build": "tsc",
|
|
50
|
+
"clean": "git clean -fx dist/",
|
|
50
51
|
"format": "oxfmt",
|
|
51
52
|
"format:check": "oxfmt --check",
|
|
52
53
|
"lint": "oxlint",
|