tailwind-variants 0.0.21 → 0.0.23

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.
@@ -1,18 +0,0 @@
1
- import {falsyToString} from "../utils";
2
-
3
- describe("falsyToString", () => {
4
- test("should return a string when given a boolean", () => {
5
- expect(falsyToString(true)).toBe("true");
6
- expect(falsyToString(false)).toBe("false");
7
- });
8
-
9
- test("should return 0 when given 0", () => {
10
- expect(falsyToString(0)).toBe("0");
11
- });
12
-
13
- test("should return the original value when given a value other than 0 or a boolean", () => {
14
- expect(falsyToString("test")).toBe("test");
15
- expect(falsyToString(4)).toBe(4);
16
- expect(falsyToString(null)).toBe(null);
17
- });
18
- });
package/src/config.d.ts DELETED
@@ -1,16 +0,0 @@
1
- import type {Config as TwMergeConfig} from "tailwind-merge";
2
-
3
- export type TVConfig = {
4
- /**
5
- * Whether to merge the class names with `tailwind-merge` library.
6
- * It's avoid to have duplicate tailwind classes. (Recommended)
7
- * @see https://github.com/dcastil/tailwind-merge/blob/v1.8.1/README.md
8
- * @default true
9
- */
10
- twMerge?: boolean;
11
- /**
12
- * The config object for `tailwind-merge` library.
13
- * @see https://github.com/dcastil/tailwind-merge/blob/v1.8.1/docs/configuration.md
14
- */
15
- twMergeConfig?: TwMergeConfig;
16
- };
package/src/index.d.ts DELETED
@@ -1,83 +0,0 @@
1
- import {TVConfig} from "./config";
2
- import {ClassValue, ClassProp, OmitUndefined, StringToBoolean} from "./utils";
3
-
4
- type TVBaseName = "base";
5
-
6
- type TVScreens = "initial" | "xs" | "sm" | "md" | "lg" | "xl" | "2xl";
7
-
8
- type TVSlots = Record<string, ClassValue> | undefined;
9
-
10
- type TVSlotsWithBase<S extends TVSlots, B extends ClassValue> = B extends undefined
11
- ? keyof S
12
- : keyof S | TVBaseName;
13
-
14
- type SlotsClassValue<S extends TVSlots, B extends ClassValue> = {
15
- [K in TVSlotsWithBase<S, B>]?: ClassValue;
16
- };
17
-
18
- export type TVVariants<S extends TVSlots, B extends ClassValue> = {
19
- [key: string]: {
20
- [key: string]: S extends TVSlots ? SlotsClassValue<S, B> | ClassValue : ClassValue;
21
- };
22
- };
23
-
24
- export type TVCompoundVariants<
25
- V extends TVVariants<S>,
26
- S extends TVSlots,
27
- B extends ClassValue,
28
- > = Array<
29
- {
30
- [K in keyof V]?: StringToBoolean<keyof V[K]> | StringToBoolean<keyof V[K]>[];
31
- } & ClassProp<SlotsClassValue<S, B> | ClassValue>
32
- >;
33
-
34
- export type TVDefaultVariants<V extends TVVariants<S>, S extends TVSlots> = {
35
- [K in keyof V]?: StringToBoolean<keyof V[K]>;
36
- };
37
-
38
- export type TVScreenPropsValue<V extends TVVariants<S>, K extends keyof V> = {
39
- [K2 in TVScreens]?: StringToBoolean<keyof V[K]>;
40
- };
41
-
42
- export type TVProps<V extends TVVariants<S>, S extends TVSlots> = V extends undefined
43
- ? ClassProp<ClassValue>
44
- : {
45
- [K in keyof V]?: StringToBoolean<keyof V[K]> | TVScreenPropsValue<V, K>;
46
- } & ClassProp<ClassValue>;
47
-
48
- export type TVVariantKeys<V extends TVVariants<S>, S extends TVSlots> = V extends Object
49
- ? Array<keyof V>
50
- : undefined;
51
-
52
- export type TVReturnType<V extends TVVariants<S>, S extends TVSlots, B extends ClassValue> = {
53
- (props?: TVProps<V, S>): S extends undefined
54
- ? string
55
- : {[K in TVSlotsWithBase<S, B>]: (slotProps?: ClassProp) => string};
56
- } & Record<"variantkeys", TVVariantKeys<V, S>>;
57
-
58
- export type TV = {
59
- <
60
- V extends TVVariants<S> = undefined,
61
- CV extends TVCompoundVariants<V, S, B> = undefined,
62
- DV extends TVDefaultVariants<V, S> = undefined,
63
- C extends TVConfig,
64
- B extends ClassValue = undefined,
65
- S extends TVSlots = undefined,
66
- >(
67
- options: {
68
- base?: B;
69
- slots?: S;
70
- variants?: V;
71
- compoundVariants?: CV;
72
- defaultVariants?: DV;
73
- },
74
- config?: C,
75
- ): TVReturnType<V, S, B>;
76
- };
77
-
78
- export declare const tv: TV;
79
-
80
- export type VariantProps<Component extends (...args: any) => any> = Omit<
81
- OmitUndefined<Parameters<Component>[0]>,
82
- "class" | "className"
83
- >;
package/src/index.js DELETED
@@ -1,207 +0,0 @@
1
- import {cx, isNotEmptyObject, falsyToString, joinObjects, removeExtraSpaces} from "./utils.js";
2
-
3
- export const tv = (
4
- options,
5
- config = {
6
- twMerge: true,
7
- twMergeConfig: {},
8
- },
9
- ) => {
10
- const {
11
- slots: slotProps = {},
12
- variants = {},
13
- compoundVariants = [],
14
- defaultVariants = {},
15
- } = options;
16
-
17
- const component = (props) => {
18
- if (variants == null && !isNotEmptyObject(slotProps)) {
19
- return cx(options?.base, props?.class, props?.className)(config);
20
- }
21
-
22
- if (compoundVariants && !Array.isArray(compoundVariants)) {
23
- throw new TypeError(
24
- `The "compoundVariants" prop must be an array. Received: ${typeof compoundVariants}`,
25
- );
26
- }
27
-
28
- // add "base" to the slots object
29
- const slots = {
30
- base: options?.base,
31
- ...slotProps,
32
- };
33
-
34
- const getScreenVariantValues = (screen, screenVariantValue, acc = [], slotKey) => {
35
- let result = acc;
36
-
37
- if (typeof screenVariantValue === "string") {
38
- result.push(
39
- removeExtraSpaces(screenVariantValue)
40
- .split(" ")
41
- .map((v) => `${screen}:${v}`),
42
- );
43
- } else if (Array.isArray(screenVariantValue)) {
44
- result.push(screenVariantValue.flatMap((v) => `${screen}:${v}`));
45
- } else if (typeof screenVariantValue === "object" && typeof slotKey === "string") {
46
- const value = screenVariantValue?.[slotKey];
47
-
48
- if (value && typeof value === "string") {
49
- const fixedValue = removeExtraSpaces(value);
50
-
51
- result[slotKey] = result[slotKey]
52
- ? [...result[slotKey], ...fixedValue.split(" ").map((v) => `${screen}:${v}`)]
53
- : fixedValue.split(" ").map((v) => `${screen}:${v}`);
54
- } else if (Array.isArray(value) && value.length > 0) {
55
- result[slotKey] = value.flatMap((v) => `${screen}:${v}`);
56
- }
57
- }
58
-
59
- return result;
60
- };
61
-
62
- const getVariantValue = (variant, slotKey = null) => {
63
- const variantObj = variants?.[variant];
64
-
65
- if (typeof variantObj !== "object" || !isNotEmptyObject(variantObj)) {
66
- return null;
67
- }
68
-
69
- const variantProp = props?.[variant];
70
- let defaultVariantProp = defaultVariants?.[variant];
71
- let screenValues = [];
72
-
73
- if (variantProp === null) return null;
74
-
75
- const variantKey = falsyToString(variantProp);
76
-
77
- // responsive variants
78
- if (typeof variantKey === "object") {
79
- screenValues = Object.keys(variantKey).reduce((acc, screen) => {
80
- const screenVariantKey = variantKey[screen];
81
- const screenVariantValue = variantObj?.[screenVariantKey];
82
-
83
- if (screen === "initial") {
84
- defaultVariantProp = screenVariantKey;
85
-
86
- return acc;
87
- }
88
-
89
- return getScreenVariantValues(screen, screenVariantValue, acc, slotKey);
90
- }, []);
91
- }
92
-
93
- const value = variantObj[variantKey] || variantObj[falsyToString(defaultVariantProp)];
94
-
95
- if (
96
- typeof screenValues === "object" &&
97
- typeof slotKey === "string" &&
98
- screenValues[slotKey]
99
- ) {
100
- return joinObjects(screenValues, value);
101
- }
102
-
103
- return screenValues.length > 0 ? [value, ...screenValues] : value;
104
- };
105
-
106
- const getVariantClassNames = variants ? Object.keys(variants).map(getVariantValue) : null;
107
-
108
- const getVariantClassNamesBySlotKey = (slotKey) => {
109
- if (!variants || typeof variants !== "object") {
110
- return null;
111
- }
112
-
113
- return Object.keys(variants)
114
- .map((variant) => {
115
- const variantValue = getVariantValue(variant, slotKey);
116
-
117
- return slotKey === "base" && typeof variantValue === "string"
118
- ? variantValue
119
- : variantValue && variantValue[slotKey];
120
- })
121
- .filter(Boolean);
122
- };
123
-
124
- const propsWithoutUndefined =
125
- props && Object.fromEntries(Object.entries(props).filter(([, value]) => value !== undefined));
126
-
127
- const getCompoundVariantClassNames = compoundVariants
128
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
129
- ?.filter(({class: tvClass, className: tvClassName, ...compoundVariantOptions}) =>
130
- Object.entries(compoundVariantOptions).every(([key, value]) => {
131
- const initialProp = typeof props?.[key] === "object" ? props[key]?.initial : {};
132
- const compoundProps = {...defaultVariants, ...initialProp, ...propsWithoutUndefined};
133
-
134
- return Array.isArray(value)
135
- ? value.includes(compoundProps[key])
136
- : compoundProps[key] === value;
137
- }),
138
- )
139
- .flatMap(({class: tvClass, className: tvClassName}) => [tvClass, tvClassName]);
140
-
141
- const getCompoundVariantClassNamesBySlot = () => {
142
- const compoundClassNames = getCompoundVariantClassNames;
143
-
144
- if (!Array.isArray(compoundClassNames)) {
145
- return compoundClassNames;
146
- }
147
-
148
- return compoundClassNames.reduce((acc, className) => {
149
- if (typeof className === "string") {
150
- acc.base = cx(acc.base, className)(config);
151
- }
152
-
153
- if (typeof className === "object") {
154
- Object.entries(className).forEach(([slot, className]) => {
155
- acc[slot] = cx(acc[slot], className)(config);
156
- });
157
- }
158
-
159
- return acc;
160
- }, {});
161
- };
162
-
163
- // slots variants
164
- if (isNotEmptyObject(slotProps)) {
165
- const compoundClassNames = getCompoundVariantClassNamesBySlot() ?? [];
166
-
167
- const slotsFns =
168
- typeof slots === "object" && isNotEmptyObject(slots)
169
- ? Object.keys(slots).reduce((acc, slotKey) => {
170
- acc[slotKey] = (slotProps) =>
171
- cx(
172
- slots[slotKey],
173
- getVariantClassNamesBySlotKey(slotKey),
174
- compoundClassNames?.[slotKey],
175
- slotProps?.class,
176
- slotProps?.className,
177
- )(config);
178
-
179
- return acc;
180
- }, {})
181
- : {};
182
-
183
- return {
184
- ...slotsFns,
185
- };
186
- }
187
-
188
- // normal variants
189
- return cx(
190
- options?.base,
191
- getVariantClassNames,
192
- getCompoundVariantClassNames,
193
- props?.class,
194
- props?.className,
195
- )(config);
196
- };
197
-
198
- const getVariantKeys = () => {
199
- if (!variants || typeof variants !== "object") return;
200
-
201
- return Object.keys(variants);
202
- };
203
-
204
- component.variantkeys = getVariantKeys();
205
-
206
- return component;
207
- };
@@ -1,5 +0,0 @@
1
- export type TVTransformer = {
2
- (content: string): string;
3
- };
4
-
5
- export declare const transformer: TVTransformer;
@@ -1,109 +0,0 @@
1
- // Ignore the `initial`
2
- // TODO: should respect tailwind config;
3
- const screens = ["xs", "sm", "md", "lg", "xl", "2xl"];
4
-
5
- const regExp = {
6
- tv: new RegExp(/tv\({[\s\S]*?}\)/g),
7
- tvContent: new RegExp(/\({[\s\S]*?}\)/g),
8
- comment: new RegExp(/\/\*[\s\S]*?\*\/|([^\\:]|^)\/\/.*$/gm),
9
- blankLine: new RegExp(/^\s*$(?:\r\n?|\n)/gm),
10
- };
11
-
12
- const isEmpty = (content) => {
13
- if (!content) return true;
14
- if (Array.isArray(content) && content.length === 0) return true;
15
- if (typeof content === "string" && content.length === 0) return true;
16
- if (typeof content === "object" && Object.keys(content).length === 0) return true;
17
-
18
- return false;
19
- };
20
-
21
- const getCleanContent = (content) => {
22
- const removeComment = content.replace(regExp.comment, "$1").toString();
23
- const removeBlankLine = removeComment.replace(regExp.blankLine, "").toString();
24
-
25
- return removeBlankLine.match(regExp.tv);
26
- };
27
-
28
- const getTVObjects = (content) => {
29
- const tvStrArray = getCleanContent(content);
30
-
31
- if (isEmpty(tvStrArray)) return;
32
-
33
- return tvStrArray.map((item) => {
34
- const str = item.toString();
35
- const tv = str.match(regExp.tvContent).toString();
36
-
37
- // TODO: `eval` should be avoided
38
- return eval(
39
- tv
40
- .replaceAll("`", '"')
41
- .replaceAll("'", '"')
42
- .replaceAll(/\s{2,}/g, " ")
43
- .replaceAll(/(\r\n|\n|\r)/gm, "")
44
- .trim(),
45
- );
46
- });
47
- };
48
-
49
- const transformContent = (tv) => {
50
- const {variants = {}} = tv;
51
-
52
- if (isEmpty(variants)) return;
53
-
54
- let responsive = {};
55
-
56
- for (const [variantKey, variant] of Object.entries(variants)) {
57
- if (isEmpty(variant)) continue;
58
-
59
- responsive[variantKey] = {};
60
-
61
- for (const [variantType, original] of Object.entries(variant)) {
62
- if (isEmpty(original)) continue;
63
-
64
- const originalClasses = original.split(" ");
65
-
66
- if (isEmpty(originalClasses)) continue;
67
-
68
- responsive[variantKey][variantType] = {};
69
-
70
- for (const screen of screens.values()) {
71
- let responsiveClasses = ``;
72
-
73
- originalClasses.forEach((item) => {
74
- responsiveClasses += `${screen}:${item} `;
75
- });
76
-
77
- responsive[variantKey][variantType].original = original;
78
- responsive[variantKey][variantType][screen] = responsiveClasses.trimEnd();
79
- }
80
- }
81
- }
82
-
83
- return responsive;
84
- };
85
-
86
- export const transformer = (content) => {
87
- try {
88
- // TODO: support package alias
89
- const isIncluded = content.includes("tailwind-variants");
90
-
91
- if (!isIncluded) return content;
92
-
93
- const tvs = getTVObjects(content);
94
-
95
- if (isEmpty(tvs)) return content;
96
-
97
- const transformedContent = tvs.map((tv) => {
98
- return transformContent(tv);
99
- });
100
-
101
- const concatedContent = content.concat(
102
- `\n/*\n\n${JSON.stringify(transformedContent, undefined, 2)}\n\n*/\n`,
103
- );
104
-
105
- return concatedContent;
106
- } catch (error) {
107
- return content;
108
- }
109
- };
package/src/utils.d.ts DELETED
@@ -1,29 +0,0 @@
1
- import {TVConfig} from "./config";
2
-
3
- export declare type ClassValue = string | string[] | null | undefined;
4
-
5
- export declare type ClassProp<V extends unknown = ClassValue> =
6
- | {
7
- class: V;
8
- className?: never;
9
- }
10
- | {class?: never; className: V}
11
- | {class?: never; className?: never};
12
-
13
- export declare type OmitUndefined<T> = T extends undefined ? never : T;
14
-
15
- export declare type StringToBoolean<T> = T extends "true" | "false" ? boolean : T;
16
-
17
- export type CxOptions = ClassValue[];
18
-
19
- export type CxReturn = string;
20
-
21
- export declare const cx: <T extends CxOptions>(...classes: T) => (config: TVConfig) => CxReturn;
22
-
23
- export declare const falsyToString: <T extends unknown>(value: T) => string | T;
24
-
25
- export declare const isNotEmptyObject: (obj: object) => boolean;
26
-
27
- export declare const joinObjects: (obj1: object, obj2: object) => object;
28
-
29
- export declare const removeExtraSpaces: (str: string) => string;
package/src/utils.js DELETED
@@ -1,48 +0,0 @@
1
- import {twMerge as twMergeBase, extendTailwindMerge} from "tailwind-merge";
2
-
3
- export const cxBase = (...classes) => classes.flat(Infinity).filter(Boolean).join(" ");
4
-
5
- export const falsyToString = (value) =>
6
- typeof value === "boolean" ? `${value}` : value === 0 ? "0" : value;
7
-
8
- export const isNotEmptyObject = (obj) => obj && Object.keys(obj).length > 0;
9
-
10
- export const cx =
11
- (...classes) =>
12
- (config = {}) => {
13
- if (!config.twMerge) {
14
- return cxBase(classes);
15
- }
16
-
17
- const twMerge = isNotEmptyObject(config.twMergeConfig)
18
- ? extendTailwindMerge(config.twMergeConfig)
19
- : twMergeBase;
20
-
21
- return twMerge(cxBase(classes));
22
- };
23
-
24
- export const joinObjects = (obj1, obj2) => {
25
- const result = {};
26
-
27
- if (typeof obj1 !== "object" || typeof obj2 !== "object") {
28
- return result;
29
- }
30
-
31
- Object.keys(obj1).forEach((key) => {
32
- if (obj2[key]) {
33
- result[key] = cxBase([obj1[key], obj2[key]]);
34
- } else {
35
- result[key] = obj1[key];
36
- }
37
- });
38
-
39
- return result;
40
- };
41
-
42
- export const removeExtraSpaces = (str) => {
43
- if (!str || typeof str !== "string") {
44
- return str;
45
- }
46
-
47
- return str.replace(/\s+/g, " ").trim();
48
- };
package/tsconfig.json DELETED
@@ -1,18 +0,0 @@
1
-
2
- {
3
- "$schema": "https://json.schemastore.org/tsconfig",
4
- "include": ["**/*.ts"],
5
- "exclude": ["node_modules", "dist"],
6
- "compilerOptions": {
7
- "target": "esnext",
8
- "module": "esnext",
9
- "isolatedModules": true,
10
- "esModuleInterop": true,
11
- "moduleResolution": "node",
12
- "resolveJsonModule": true,
13
- "strict": true,
14
- "declaration": true,
15
- "skipLibCheck": true,
16
- "allowJs": true
17
- }
18
- }