responsive-class-variants 1.0.1 → 1.1.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 CHANGED
@@ -1,6 +1,14 @@
1
1
  # Responsive Class Variants
2
2
 
3
- rcv helps you create responsive class variants
3
+ rcv helps you create responsive class variants. It handles the logic of generating the classes and prefixes them with the breakpoint name. You will still need to make sure, that the CSS is available for the breakpoints you use.
4
+
5
+ ## Features
6
+
7
+ - Handles the logic of generating the classes and prefixes them with the breakpoint name.
8
+ - You just need to provide the base classes, the variants and optionally compound variants.
9
+ - **Slots support**: Create multiple class-generating functions for different parts of a component.
10
+ - You can use the default breakpoints (sm, md, lg, xl) or provide your own.
11
+ - You can pass an optional onComplete callback to the createRcv function. This callback will be called with the generated classes. Helpful if you want to pass your classes to a library like twMerge.
4
12
 
5
13
  ## Installation
6
14
 
@@ -10,6 +18,19 @@ pnpm add responsive-class-variants
10
18
 
11
19
  ## Usage
12
20
 
21
+ rcv is a function that takes a config object and returns a function that takes a props object and returns a string of classes. The props object is an object with the keys of the variants and the values are the values of the variants.
22
+
23
+ The config object has the following properties:
24
+
25
+ - base: The base classes that are always applied.
26
+ - variants: An object with the keys of the variants and the values are the values of the variants.
27
+ - compoundVariants: An array of compound variants that apply additional classes when multiple variants have specific values.
28
+ - onComplete: An optional callback function that receives the generated classes and returns the final classes.
29
+
30
+ rcv works very well with tailwindcss but it can be used with any CSS solution.
31
+
32
+ ### Basic Usage (Single Component):
33
+
13
34
  ```ts
14
35
  const getButtonVariants = rcv({
15
36
  base: "px-4 py-2 rounded",
@@ -19,21 +40,236 @@ const getButtonVariants = rcv({
19
40
  secondary: "bg-gray-200 text-gray-800"
20
41
  },
21
42
  size: {
22
- sm: "text-sm",
23
- lg: "text-lg"
43
+ small: "text-sm",
44
+ large: "text-lg"
24
45
  },
25
46
  disabled: {
26
47
  true: "opacity-50 cursor-not-allowed"
48
+ },
49
+ error: {
50
+ true: "bg-red-500 text-white"
51
+ }
52
+ },
53
+ compoundVariants: [
54
+ {
55
+ disabled: true,
56
+ error: true,
57
+ className: "opacity-50 cursor-not-allowed"
27
58
  }
28
- }
59
+ ]
29
60
  });
30
61
 
31
62
  // Usage:
32
- getButtonVariants({ intent: "primary", size: "lg", disabled: true })
63
+ getButtonVariants({ intent: "primary", size: "large", disabled: true })
33
64
  // Or with responsive values:
34
65
  getButtonVariants({ intent: { initial: "primary", md: "secondary" } })
66
+ ```
67
+
68
+ ### Slots (Multi-Part Components):
69
+
70
+ When you need to style multiple parts of a component, you can use slots. This is perfect for complex components like cards, alerts, or modals.
71
+
72
+ #### Simple Slots
73
+
74
+ ```ts
75
+ const getCardVariants = rcv({
76
+ slots: {
77
+ base: "rounded-xl p-8 bg-white dark:bg-gray-900",
78
+ title: "text-xl font-bold text-gray-900 dark:text-white",
79
+ content: "text-gray-700 dark:text-gray-300"
80
+ }
81
+ });
82
+
83
+ // Usage - destructure the slot functions
84
+ const { base, title, content } = getCardVariants;
85
+
86
+ // Apply to your JSX - no arguments needed for simple slots!
87
+ <div className={base()}>
88
+ <h2 className={title()}>Card Title</h2>
89
+ <p className={content()}>Card content goes here</p>
90
+ </div>
91
+ ```
92
+
93
+ #### Slots with Variants
94
+
95
+ Variants can target specific slots by using objects instead of strings:
96
+
97
+ ```ts
98
+ const getCardVariants = rcv({
99
+ slots: {
100
+ base: "rounded-xl p-8 bg-white dark:bg-gray-900",
101
+ title: "text-xl font-bold text-gray-900 dark:text-white",
102
+ content: "text-gray-700 dark:text-gray-300"
103
+ },
104
+ variants: {
105
+ shadow: {
106
+ none: {},
107
+ sm: { base: "shadow-sm" },
108
+ md: { base: "shadow-md" },
109
+ lg: { base: "shadow-lg" }
110
+ },
111
+ size: {
112
+ sm: {
113
+ title: "text-lg",
114
+ content: "text-sm"
115
+ },
116
+ lg: {
117
+ title: "text-2xl",
118
+ content: "text-lg"
119
+ }
120
+ }
121
+ }
122
+ });
123
+
124
+ const { base, title, content } = getCardVariants;
125
+
126
+ // Usage with variants
127
+ <div className={base({ shadow: "md", size: "lg" })}>
128
+ <h2 className={title({ shadow: "md", size: "lg" })}>Large Card Title</h2>
129
+ <p className={content({ shadow: "md", size: "lg" })}>Larger content text</p>
130
+ </div>
131
+ ```
132
+
133
+ #### Slots with Compound Variants
134
+
135
+ Compound variants can also target specific slots using the `class` property:
136
+
137
+ ```ts
138
+ const getAlertVariants = rcv({
139
+ slots: {
140
+ root: "rounded py-3 px-5 mb-4",
141
+ title: "font-bold mb-1",
142
+ message: "text-sm"
143
+ },
144
+ variants: {
145
+ variant: {
146
+ outlined: { root: "border" },
147
+ filled: {}
148
+ },
149
+ severity: {
150
+ error: {},
151
+ success: {},
152
+ warning: {}
153
+ }
154
+ },
155
+ compoundVariants: [
156
+ {
157
+ variant: "outlined",
158
+ severity: "error",
159
+ class: {
160
+ root: "border-red-700 dark:border-red-500",
161
+ title: "text-red-700 dark:text-red-500",
162
+ message: "text-red-600 dark:text-red-500"
163
+ }
164
+ },
165
+ {
166
+ variant: "filled",
167
+ severity: "success",
168
+ class: {
169
+ root: "bg-green-100 dark:bg-green-800",
170
+ title: "text-green-900 dark:text-green-50",
171
+ message: "text-green-700 dark:text-green-200"
172
+ }
173
+ }
174
+ ]
175
+ });
176
+
177
+ const { root, title, message } = getAlertVariants;
178
+
179
+ // Usage
180
+ <div className={root({ variant: "outlined", severity: "error" })}>
181
+ <h3 className={title({ variant: "outlined", severity: "error" })}>Error!</h3>
182
+ <p className={message({ variant: "outlined", severity: "error" })}>Something went wrong</p>
183
+ </div>
184
+ ```
185
+
186
+ #### Slots with Responsive Values
35
187
 
36
- ```
188
+ Slots work seamlessly with responsive values:
189
+
190
+ ```ts
191
+ const getCardVariants = rcv({
192
+ slots: {
193
+ base: "rounded-xl p-4 bg-white",
194
+ title: "font-bold text-gray-900"
195
+ },
196
+ variants: {
197
+ size: {
198
+ sm: {
199
+ base: "p-2",
200
+ title: "text-sm"
201
+ },
202
+ lg: {
203
+ base: "p-8",
204
+ title: "text-2xl"
205
+ }
206
+ }
207
+ }
208
+ });
209
+
210
+ const { base, title } = getCardVariants;
211
+
212
+ // Responsive usage
213
+ <div className={base({ size: { initial: "sm", md: "lg" } })}>
214
+ <h2 className={title({ size: { initial: "sm", md: "lg" } })}>Responsive Card</h2>
215
+ </div>
216
+ ```
217
+
218
+ ### With css classes (like BEM or any other naming convention):
219
+
220
+ ```ts
221
+ const getButtonVariants = rcv({
222
+ base: "btn",
223
+ variants: {
224
+ intent: {
225
+ primary: "btn--primary",
226
+ secondary: "btn--secondary"
227
+ },
228
+ size: {
229
+ small: "btn--sm",
230
+ large: "btn--lg"
231
+ },
232
+ disabled: {
233
+ true: "btn--disabled"
234
+ },
235
+ error: {
236
+ true: "btn--error"
237
+ }
238
+ },
239
+ compoundVariants: [
240
+ {
241
+ disabled: true,
242
+ error: true,
243
+ className: "btn--disabled--error"
244
+ }
245
+ ]
246
+ });
247
+ ```
248
+
249
+ Because of the tailwind JIT compiler, you need to make sure, that all possible classes are available with your component. Let's say you have a button component and you want to use the `size` variant responsively. You need to make sure, that the `small` and `large` classes are available with your component. You can e.g. define a `SIZES` object to define the classes for each size and breakpoints. This example assumes you have the default breakpoints (sm, md, lg, xl).
250
+
251
+ ```ts
252
+ const SIZES = {
253
+ sm: {
254
+ sm: "sm:text-sm",
255
+ lg: "sm:text-lg"
256
+ },
257
+ md: {
258
+ sm: "md:text-sm",
259
+ lg: "md:text-lg"
260
+ },
261
+ lg: {
262
+ sm: "lg:text-sm",
263
+ lg: "lg:text-lg"
264
+ },
265
+ xl: {
266
+ sm: "xl:text-sm",
267
+ lg: "xl:text-lg"
268
+ }
269
+ }
270
+ ```
271
+
272
+ The structure doesn't really matter, the classes just need to be in the compiled javascript to be picked up by the JIT compiler.
37
273
 
38
274
  ## Custom breakpoints (via createRcv)
39
275
 
@@ -52,12 +288,55 @@ const getButtonVariants = rcv({
52
288
 
53
289
  // Usage with custom breakpoints:
54
290
  getButtonVariants({ intent: { initial: "primary", mobile: "secondary", desktop: "primary" } })
291
+
292
+ // Works with slots too:
293
+ const getCardVariants = rcv({
294
+ slots: {
295
+ base: "rounded-xl p-4 bg-white",
296
+ title: "font-bold text-gray-900"
297
+ },
298
+ variants: {
299
+ size: {
300
+ sm: { base: "p-2", title: "text-sm" },
301
+ lg: { base: "p-8", title: "text-2xl" }
302
+ }
303
+ }
304
+ });
55
305
  ```
56
306
 
57
307
  ## onComplete callback (via createRcv)
58
308
 
59
- You can pass an optional onComplete callback to the createRcv function. This callback will be called with the generated classes. Helpful if you want to pass your classes to a library like tailwindMerge.
309
+ You can pass an optional onComplete callback to the createRcv function. This callback will be called with the generated classes. Helpful if you want to pass your classes to a library like tailwind Merge.
60
310
 
61
311
  ```ts
62
312
  const rcv = createRcv(['mobile', 'tablet', 'desktop'], (classes) => twMerge(classes));
63
313
  ```
314
+
315
+ ## Typescript helpers
316
+
317
+ rcv provides a helper type to make it easier to type your component props.
318
+
319
+ If you use the default breakpoints (sm, md, lg, xl), you can use the `ResponsiveValue` type to make existing props responsive.
320
+
321
+ ```ts
322
+ type ButtonProps = {
323
+ intent: "primary" | "secondary";
324
+ size: ResponsiveValue<"sm" | "lg">;
325
+ disabled: boolean;
326
+ error: boolean;
327
+ };
328
+ ```
329
+
330
+ If you use custom breakpoints you need to pass the breakpoints to the `ResponsiveValue` type.
331
+
332
+ ```ts
333
+ import { createRcv, type ResponsiveValue as RcvResponsiveValue } from "responsive-class-variants";
334
+
335
+ const breakpoints = ["tablet", "desktop", "wide"] as const;
336
+
337
+ export const customRcv = createRcv(breakpoints);
338
+
339
+ type Breakpoints = (typeof breakpoints)[number];
340
+
341
+ export type ResponsiveValue<T> = RcvResponsiveValue<T, Breakpoints>;
342
+ ```
package/dist/index.d.ts CHANGED
@@ -42,63 +42,34 @@ export declare const mapResponsiveValue: <V, T, B extends string = DefaultBreakp
42
42
  /**
43
43
  * Start of rcv and types
44
44
  */
45
- type VariantValue = Record<string, string>;
45
+ type VariantValue = Record<string, string | Record<string, string>>;
46
46
  type VariantConfig = Record<string, VariantValue>;
47
47
  type StringBoolean = "true" | "false";
48
- type BooleanVariant = Partial<Record<StringBoolean, string>>;
48
+ type BooleanVariant = Partial<Record<StringBoolean, string | Record<string, string>>>;
49
49
  type VariantPropValue<T, B extends string> = T extends BooleanVariant ? ResponsiveValue<boolean, B> | undefined : T extends Record<string, unknown> ? ResponsiveValue<keyof T, B> : never;
50
50
  type VariantProps<T extends VariantConfig, B extends string> = {
51
- [K in keyof T]: VariantPropValue<T[K], B>;
51
+ [K in keyof T]?: VariantPropValue<T[K], B>;
52
52
  } & {
53
53
  className?: string;
54
54
  };
55
- type ResponsiveClassesConfig<T extends VariantConfig, B extends string> = {
55
+ type CompoundVariantWithSlots<T extends VariantConfig, S extends string, B extends string> = Partial<VariantProps<T, B>> & {
56
+ class?: Partial<Record<S, string>>;
57
+ className?: string;
58
+ };
59
+ export declare function rcv<T extends VariantConfig, S extends Record<string, string>, B extends string = DefaultBreakpoints>(config: {
60
+ slots: S;
61
+ variants?: T;
62
+ compoundVariants?: CompoundVariantWithSlots<T, keyof S & string, B>[];
63
+ onComplete?: (classes: string) => string;
64
+ }): {
65
+ [K in keyof S]: (props?: VariantProps<T, B>) => string;
66
+ };
67
+ export declare function rcv<T extends VariantConfig, B extends string = DefaultBreakpoints>(config: {
56
68
  base: string;
57
69
  variants?: T;
58
70
  compoundVariants?: Partial<VariantProps<T, B>>[];
59
71
  onComplete?: (classes: string) => string;
60
- };
61
- /**
62
- * Creates a function that generates classes based on variant configurations and responsive props
63
- *
64
- * @template T - Type extending VariantConfig (Record of variant names to their possible values and corresponding classes)
65
- * @template B - The breakpoints type
66
- *
67
- * @param config - Configuration object for variants
68
- * @param config.base - Base classes that are always applied
69
- * @param config.variants - Object containing variant definitions where each key is a variant name
70
- * and value is either a string of class names, an object mapping variant values to class names,
71
- * or an object with true/false keys for boolean variants
72
- * @param config.compoundVariants - Optional array of compound variants that apply additional classes
73
- * when multiple variants have specific values
74
- * @param config.onComplete - Optional callback function that receives the generated classes and returns the final classes
75
- *
76
- * @returns A function that accepts variant props and returns classes with twMerge
77
- *
78
- * @example
79
- * const getButtonVariants = rcv({
80
- * base: "px-4 py-2 rounded",
81
- * variants: {
82
- * intent: {
83
- * primary: "bg-blue-500 text-white",
84
- * secondary: "bg-gray-200 text-gray-800"
85
- * },
86
- * size: {
87
- * sm: "text-sm",
88
- * lg: "text-lg"
89
- * },
90
- * disabled: {
91
- * true: "opacity-50 cursor-not-allowed"
92
- * }
93
- * }
94
- * });
95
- *
96
- * // Usage:
97
- * getButtonVariants({ intent: "primary", size: "lg", disabled: true })
98
- * // Or with responsive values:
99
- * getButtonVariants({ intent: { initial: "primary", md: "secondary" } })
100
- */
101
- export declare const rcv: <T extends VariantConfig, B extends string = DefaultBreakpoints>({ base, variants, compoundVariants, onComplete, }: ResponsiveClassesConfig<T, B>) => ({ className, ...props }: VariantProps<T, B>) => string;
72
+ }): (props: VariantProps<T, B>) => string;
102
73
  /**
103
74
  * Creates a custom rcv function with custom breakpoints and an optional onComplete callback
104
75
  *
@@ -123,5 +94,18 @@ export declare const rcv: <T extends VariantConfig, B extends string = DefaultBr
123
94
  * // Usage with custom breakpoints:
124
95
  * getButtonVariants({ intent: { initial: "primary", mobile: "secondary", desktop: "primary" } })
125
96
  */
126
- export declare const createRcv: <B extends string>(_breakpoints?: readonly B[], onComplete?: (classes: string) => string) => <T extends VariantConfig>(config: ResponsiveClassesConfig<T, B>) => ({ className, ...props }: VariantProps<T, B>) => string;
97
+ export declare const createRcv: <B extends string>(_breakpoints?: readonly B[], onComplete?: (classes: string) => string) => {
98
+ <T extends VariantConfig, S extends Record<string, string>>(config: {
99
+ slots: S;
100
+ variants?: T;
101
+ compoundVariants?: CompoundVariantWithSlots<T, keyof S & string, B>[];
102
+ onComplete?: (classes: string) => string;
103
+ }): { [K in keyof S]: (props?: VariantProps<T, B>) => string; };
104
+ <T extends VariantConfig>(config: {
105
+ base: string;
106
+ variants?: T;
107
+ compoundVariants?: Partial<VariantProps<T, B>>[];
108
+ onComplete?: (classes: string) => string;
109
+ }): (props: VariantProps<T, B>) => string;
110
+ };
127
111
  export {};
package/dist/index.js CHANGED
@@ -39,87 +39,121 @@ export const mapResponsiveValue = (value, mapper) => isSingularValue(value)
39
39
  breakpoint,
40
40
  mapper(value),
41
41
  ]));
42
- /**
43
- * Creates a function that generates classes based on variant configurations and responsive props
44
- *
45
- * @template T - Type extending VariantConfig (Record of variant names to their possible values and corresponding classes)
46
- * @template B - The breakpoints type
47
- *
48
- * @param config - Configuration object for variants
49
- * @param config.base - Base classes that are always applied
50
- * @param config.variants - Object containing variant definitions where each key is a variant name
51
- * and value is either a string of class names, an object mapping variant values to class names,
52
- * or an object with true/false keys for boolean variants
53
- * @param config.compoundVariants - Optional array of compound variants that apply additional classes
54
- * when multiple variants have specific values
55
- * @param config.onComplete - Optional callback function that receives the generated classes and returns the final classes
56
- *
57
- * @returns A function that accepts variant props and returns classes with twMerge
58
- *
59
- * @example
60
- * const getButtonVariants = rcv({
61
- * base: "px-4 py-2 rounded",
62
- * variants: {
63
- * intent: {
64
- * primary: "bg-blue-500 text-white",
65
- * secondary: "bg-gray-200 text-gray-800"
66
- * },
67
- * size: {
68
- * sm: "text-sm",
69
- * lg: "text-lg"
70
- * },
71
- * disabled: {
72
- * true: "opacity-50 cursor-not-allowed"
73
- * }
74
- * }
75
- * });
76
- *
77
- * // Usage:
78
- * getButtonVariants({ intent: "primary", size: "lg", disabled: true })
79
- * // Or with responsive values:
80
- * getButtonVariants({ intent: { initial: "primary", md: "secondary" } })
81
- */
82
- export const rcv = ({ base, variants, compoundVariants, onComplete, }) => ({ className, ...props }) => {
83
- const responsiveClasses = Object.entries(props)
42
+ // Helper functions for slots
43
+ const isSlotsConfig = (config) => {
44
+ return "slots" in config;
45
+ };
46
+ const prefixClasses = (classes, prefix) => classes
47
+ .split(" ")
48
+ .map((className) => `${prefix}:${className}`)
49
+ .join(" ");
50
+ // Helper function to get variant value for a specific slot or base
51
+ const getVariantValue = (variants, key, value, slotName) => {
52
+ const variant = variants?.[key];
53
+ const variantValue = variant?.[value];
54
+ // Handle string values
55
+ if (typeof variantValue === "string") {
56
+ return variantValue;
57
+ }
58
+ // Handle object values (slot-specific classes)
59
+ if (typeof variantValue === "object" &&
60
+ variantValue !== null &&
61
+ slotName &&
62
+ slotName in variantValue) {
63
+ const slotSpecificValue = variantValue[slotName];
64
+ if (typeof slotSpecificValue === "string") {
65
+ return slotSpecificValue;
66
+ }
67
+ }
68
+ return undefined;
69
+ };
70
+ // Helper function to process responsive values
71
+ const processResponsiveValue = (variants, key, value, slotName) => {
72
+ return Object.entries(value)
73
+ .map(([breakpoint, breakpointValue]) => {
74
+ const variantValue = getVariantValue(variants, key, breakpointValue, slotName);
75
+ if (!variantValue)
76
+ return undefined;
77
+ // If the breakpoint is initial, return without prefix
78
+ if (breakpoint === "initial") {
79
+ return variantValue;
80
+ }
81
+ // Otherwise, return with breakpoint prefix
82
+ return prefixClasses(variantValue, breakpoint);
83
+ })
84
+ .filter(Boolean)
85
+ .join(" ");
86
+ };
87
+ // Helper function to process variant props into classes
88
+ const processVariantProps = (props, variants, slotName) => {
89
+ return Object.entries(props)
84
90
  .map(([key, propValue]) => {
85
- const variant = variants?.[key];
86
91
  const value = typeof propValue === "boolean" ? String(propValue) : propValue;
87
92
  // Handle undefined values
88
93
  if (!value)
89
94
  return undefined;
90
- const variantValue = variant?.[value];
91
- // Handle string values
92
- if (typeof variantValue === "string") {
93
- return variantValue;
95
+ // Handle singular values
96
+ if (typeof value === "string") {
97
+ return getVariantValue(variants, key, value, slotName);
94
98
  }
95
99
  // Handle responsive values
96
- return Object.entries(value)
97
- .map(([breakpoint, value]) => {
98
- // If the breakpoint is initial, return the variant value without breakpoint prefix
99
- if (breakpoint === "initial") {
100
- return variants?.[key]?.[value];
101
- }
102
- // Otherwise, return the variant value with the breakpoint prefix
103
- return variants?.[key]?.[value]
104
- ?.split(" ")
105
- .map((className) => `${breakpoint}:${className}`)
106
- .join(" ");
107
- })
108
- .join(" ");
100
+ return processResponsiveValue(variants, key, value, slotName);
109
101
  })
110
102
  .filter(Boolean)
111
103
  .join(" ");
104
+ };
105
+ // Helper function to match compound variants
106
+ const matchesCompoundVariant = (compound, props) => {
107
+ return Object.entries(compound).every(([key, value]) => props[key] === String(value) ||
108
+ props[key] === value);
109
+ };
110
+ const createSlotFunction = (slotConfig, variants, compoundVariants, onComplete, slotName) => ({ className, ...props } = {}) => {
111
+ const responsiveClasses = processVariantProps(props, variants, slotName);
112
112
  const compoundClasses = compoundVariants
113
- ?.map(({ className, ...compound }) => {
114
- if (Object.entries(compound).every(([key, value]) => props[key] === String(value) || props[key] === value)) {
115
- return className;
113
+ ?.map(({ class: slotClasses, className: compoundClassName, ...compound }) => {
114
+ if (matchesCompoundVariant(compound, props)) {
115
+ // If compound variant has slot-specific classes, use those for this slot
116
+ if (slotClasses &&
117
+ typeof slotClasses === "object" &&
118
+ slotClasses[slotName]) {
119
+ return slotClasses[slotName];
120
+ }
121
+ // Otherwise use the general className
122
+ return compoundClassName;
116
123
  }
117
124
  return undefined;
118
125
  })
119
126
  .filter(Boolean);
120
- const classes = clsx(base, responsiveClasses, compoundClasses, className);
127
+ const classes = clsx(slotConfig, responsiveClasses, compoundClasses, className);
121
128
  return onComplete ? onComplete(classes) : classes;
122
129
  };
130
+ export function rcv(config) {
131
+ // Check if config is a slots config
132
+ if (isSlotsConfig(config)) {
133
+ const { slots, variants, compoundVariants, onComplete } = config;
134
+ const slotFunctions = {};
135
+ // Create slot functions for each slot - ensure all slots are always present
136
+ for (const [slotName, slotConfig] of Object.entries(slots)) {
137
+ slotFunctions[slotName] = createSlotFunction(slotConfig, variants, compoundVariants, onComplete, slotName);
138
+ }
139
+ return slotFunctions;
140
+ }
141
+ // If config is not a slots config, create a base function
142
+ const { base, variants, compoundVariants, onComplete } = config;
143
+ return ({ className, ...props }) => {
144
+ const responsiveClasses = processVariantProps(props, variants);
145
+ const compoundClasses = compoundVariants
146
+ ?.map(({ className: compoundClassName, ...compound }) => {
147
+ if (matchesCompoundVariant(compound, props)) {
148
+ return compoundClassName;
149
+ }
150
+ return undefined;
151
+ })
152
+ .filter(Boolean);
153
+ const classes = clsx(base, responsiveClasses, compoundClasses, className);
154
+ return onComplete ? onComplete(classes) : classes;
155
+ };
156
+ }
123
157
  /**
124
158
  * Creates a custom rcv function with custom breakpoints and an optional onComplete callback
125
159
  *
@@ -145,6 +179,20 @@ export const rcv = ({ base, variants, compoundVariants, onComplete, }) => ({ cla
145
179
  * getButtonVariants({ intent: { initial: "primary", mobile: "secondary", desktop: "primary" } })
146
180
  */
147
181
  export const createRcv = (_breakpoints, onComplete) => {
148
- return (config) => rcv({ ...config, onComplete });
182
+ function customRcv(config) {
183
+ if (isSlotsConfig(config)) {
184
+ return rcv({
185
+ ...config,
186
+ onComplete: onComplete || config.onComplete,
187
+ });
188
+ }
189
+ else {
190
+ return rcv({
191
+ ...config,
192
+ onComplete: onComplete || config.onComplete,
193
+ });
194
+ }
195
+ }
196
+ return customRcv;
149
197
  };
150
198
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"/","sources":["index.ts"],"names":[],"mappings":"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","sourcesContent":["import { clsx } from \"clsx\";\n\nexport type DefaultBreakpoints = \"sm\" | \"md\" | \"lg\" | \"xl\";\nexport type Breakpoints = DefaultBreakpoints;\n\nexport type BreakpointsMap<V, B extends string = DefaultBreakpoints> = {\n\tinitial: V;\n} & Partial<{\n\t[breakpoint in B]: V;\n}>;\n\nexport type ResponsiveValue<T, B extends string = DefaultBreakpoints> =\n\t| T\n\t| BreakpointsMap<T, B>;\n\nconst isSingularValue = <A, B extends string>(\n\tvalue: ResponsiveValue<A, B>,\n): value is A => !isBreakpointsMap(value);\n\nconst isBreakpointsMap = <A, B extends string>(\n\tvalue: ResponsiveValue<A, B>,\n): value is BreakpointsMap<A, B> =>\n\ttypeof value === \"object\" && value != null && !Array.isArray(value);\n\n/**\n * Maps a ResponsiveValue to a new ResponsiveValue using the provided mapper function. Singular values are passed through as is.\n *\n * @template V The type of the original value\n * @template T The type of the mapped value\n * @template B The type of breakpoints\n * @param {ResponsiveValue<V, B>} value - The original ResponsiveValue to be mapped\n * @param {function(V): T} mapper - A function that maps a ResponsiveValue to a new ResponsiveValue\n * @returns {ResponsiveValue<T, B>} A new ResponsiveValue with the mapped values\n *\n *\n * @example\n * const sizes = {\n * initial: 'md',\n * sm: 'lg',\n * }\n *\n * const output = mapResponsiveValue(sizes, size => {\n *\tswitch (size) {\n *\t\tcase 'initial':\n *\t\treturn 'sm';\n *\t\tcase 'sm':\n *\t\t\treturn 'md';\n *\t\t}\n *\t});\n *\n * // console.log(output)\n * {\n *\tinitial: 'sm',\n *\tsm: 'md',\n * }\n */\nexport const mapResponsiveValue = <V, T, B extends string = DefaultBreakpoints>(\n\tvalue: ResponsiveValue<V, B>,\n\tmapper: (value: V) => T,\n): ResponsiveValue<T, B> =>\n\tisSingularValue(value)\n\t\t? mapper(value)\n\t\t: (Object.fromEntries(\n\t\t\t\tObject.entries(value).map(([breakpoint, value]) => [\n\t\t\t\t\tbreakpoint,\n\t\t\t\t\tmapper(value),\n\t\t\t\t]),\n\t\t\t) as BreakpointsMap<T, B>);\n\n/**\n * Start of rcv and types\n */\n\ntype VariantValue = Record<string, string>;\ntype VariantConfig = Record<string, VariantValue>;\n\ntype StringBoolean = \"true\" | \"false\";\ntype BooleanVariant = Partial<Record<StringBoolean, string>>;\n\ntype VariantPropValue<T, B extends string> = T extends BooleanVariant\n\t? ResponsiveValue<boolean, B> | undefined\n\t: T extends Record<string, unknown>\n\t\t? ResponsiveValue<keyof T, B>\n\t\t: never;\n\ntype VariantProps<T extends VariantConfig, B extends string> = {\n\t[K in keyof T]: VariantPropValue<T[K], B>;\n} & {\n\tclassName?: string;\n};\n\ntype ResponsiveClassesConfig<T extends VariantConfig, B extends string> = {\n\tbase: string;\n\tvariants?: T;\n\tcompoundVariants?: Partial<VariantProps<T, B>>[];\n\tonComplete?: (classes: string) => string;\n};\n\n/**\n * Creates a function that generates classes based on variant configurations and responsive props\n *\n * @template T - Type extending VariantConfig (Record of variant names to their possible values and corresponding classes)\n * @template B - The breakpoints type\n *\n * @param config - Configuration object for variants\n * @param config.base - Base classes that are always applied\n * @param config.variants - Object containing variant definitions where each key is a variant name\n * and value is either a string of class names, an object mapping variant values to class names,\n * or an object with true/false keys for boolean variants\n * @param config.compoundVariants - Optional array of compound variants that apply additional classes\n * when multiple variants have specific values\n * @param config.onComplete - Optional callback function that receives the generated classes and returns the final classes\n *\n * @returns A function that accepts variant props and returns classes with twMerge\n *\n * @example\n * const getButtonVariants = rcv({\n * base: \"px-4 py-2 rounded\",\n * variants: {\n * intent: {\n * primary: \"bg-blue-500 text-white\",\n * secondary: \"bg-gray-200 text-gray-800\"\n * },\n * size: {\n * sm: \"text-sm\",\n * lg: \"text-lg\"\n * },\n * disabled: {\n * true: \"opacity-50 cursor-not-allowed\"\n * }\n * }\n * });\n *\n * // Usage:\n * getButtonVariants({ intent: \"primary\", size: \"lg\", disabled: true })\n * // Or with responsive values:\n * getButtonVariants({ intent: { initial: \"primary\", md: \"secondary\" } })\n */\nexport const rcv =\n\t<T extends VariantConfig, B extends string = DefaultBreakpoints>({\n\t\tbase,\n\t\tvariants,\n\t\tcompoundVariants,\n\t\tonComplete,\n\t}: ResponsiveClassesConfig<T, B>) =>\n\t({ className, ...props }: VariantProps<T, B>) => {\n\t\tconst responsiveClasses = Object.entries(props)\n\t\t\t.map(([key, propValue]: [keyof T, VariantPropValue<T[keyof T], B>]) => {\n\t\t\t\tconst variant = variants?.[key];\n\t\t\t\tconst value =\n\t\t\t\t\ttypeof propValue === \"boolean\" ? String(propValue) : propValue;\n\n\t\t\t\t// Handle undefined values\n\t\t\t\tif (!value) return undefined;\n\n\t\t\t\tconst variantValue = variant?.[value as keyof VariantValue];\n\n\t\t\t\t// Handle string values\n\t\t\t\tif (typeof variantValue === \"string\") {\n\t\t\t\t\treturn variantValue;\n\t\t\t\t}\n\n\t\t\t\t// Handle responsive values\n\t\t\t\treturn Object.entries(value as Partial<BreakpointsMap<T, B>>)\n\t\t\t\t\t.map(([breakpoint, value]) => {\n\t\t\t\t\t\t// If the breakpoint is initial, return the variant value without breakpoint prefix\n\t\t\t\t\t\tif (breakpoint === \"initial\") {\n\t\t\t\t\t\t\treturn variants?.[key]?.[value as keyof typeof variant];\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// Otherwise, return the variant value with the breakpoint prefix\n\t\t\t\t\t\treturn variants?.[key]?.[value as keyof typeof variant]\n\t\t\t\t\t\t\t?.split(\" \")\n\t\t\t\t\t\t\t.map((className) => `${breakpoint}:${className}`)\n\t\t\t\t\t\t\t.join(\" \");\n\t\t\t\t\t})\n\t\t\t\t\t.join(\" \");\n\t\t\t})\n\t\t\t.filter(Boolean)\n\t\t\t.join(\" \");\n\n\t\tconst compoundClasses = compoundVariants\n\t\t\t?.map(({ className, ...compound }) => {\n\t\t\t\tif (\n\t\t\t\t\tObject.entries(compound).every(\n\t\t\t\t\t\t([key, value]) =>\n\t\t\t\t\t\t\tprops[key] === String(value) || props[key] === value,\n\t\t\t\t\t)\n\t\t\t\t) {\n\t\t\t\t\treturn className;\n\t\t\t\t}\n\t\t\t\treturn undefined;\n\t\t\t})\n\t\t\t.filter(Boolean);\n\n\t\tconst classes = clsx(base, responsiveClasses, compoundClasses, className);\n\t\treturn onComplete ? onComplete(classes) : classes;\n\t};\n\n/**\n * Creates a custom rcv function with custom breakpoints and an optional onComplete callback\n *\n * @template B - The custom breakpoints type\n * @param breakpoints - Optional array of custom breakpoint names\n * @param onComplete - Optional callback function that receives the generated classes and returns the final classes\n * @returns A function that creates rcv with custom breakpoints\n *\n * @example\n * const customRcv = createRcv(['mobile', 'tablet', 'desktop']);\n *\n * const getButtonVariants = customRcv({\n * base: \"px-4 py-2 rounded\",\n * variants: {\n * intent: {\n * primary: \"bg-blue-500 text-white\",\n * secondary: \"bg-gray-200 text-gray-800\"\n * }\n * }\n * });\n *\n * // Usage with custom breakpoints:\n * getButtonVariants({ intent: { initial: \"primary\", mobile: \"secondary\", desktop: \"primary\" } })\n */\n\nexport const createRcv = <B extends string>(\n\t_breakpoints?: readonly B[],\n\tonComplete?: (classes: string) => string,\n) => {\n\treturn <T extends VariantConfig>(config: ResponsiveClassesConfig<T, B>) =>\n\t\trcv<T, B>({ ...config, onComplete });\n};\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"/","sources":["index.ts"],"names":[],"mappings":"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","sourcesContent":["import { clsx } from \"clsx\";\n\nexport type DefaultBreakpoints = \"sm\" | \"md\" | \"lg\" | \"xl\";\nexport type Breakpoints = DefaultBreakpoints;\n\nexport type BreakpointsMap<V, B extends string = DefaultBreakpoints> = {\n\tinitial: V;\n} & Partial<{\n\t[breakpoint in B]: V;\n}>;\n\nexport type ResponsiveValue<T, B extends string = DefaultBreakpoints> =\n\t| T\n\t| BreakpointsMap<T, B>;\n\nconst isSingularValue = <A, B extends string>(\n\tvalue: ResponsiveValue<A, B>,\n): value is A => !isBreakpointsMap(value);\n\nconst isBreakpointsMap = <A, B extends string>(\n\tvalue: ResponsiveValue<A, B>,\n): value is BreakpointsMap<A, B> =>\n\ttypeof value === \"object\" && value != null && !Array.isArray(value);\n\n/**\n * Maps a ResponsiveValue to a new ResponsiveValue using the provided mapper function. Singular values are passed through as is.\n *\n * @template V The type of the original value\n * @template T The type of the mapped value\n * @template B The type of breakpoints\n * @param {ResponsiveValue<V, B>} value - The original ResponsiveValue to be mapped\n * @param {function(V): T} mapper - A function that maps a ResponsiveValue to a new ResponsiveValue\n * @returns {ResponsiveValue<T, B>} A new ResponsiveValue with the mapped values\n *\n *\n * @example\n * const sizes = {\n * initial: 'md',\n * sm: 'lg',\n * }\n *\n * const output = mapResponsiveValue(sizes, size => {\n *\tswitch (size) {\n *\t\tcase 'initial':\n *\t\treturn 'sm';\n *\t\tcase 'sm':\n *\t\t\treturn 'md';\n *\t\t}\n *\t});\n *\n * // console.log(output)\n * {\n *\tinitial: 'sm',\n *\tsm: 'md',\n * }\n */\nexport const mapResponsiveValue = <V, T, B extends string = DefaultBreakpoints>(\n\tvalue: ResponsiveValue<V, B>,\n\tmapper: (value: V) => T,\n): ResponsiveValue<T, B> =>\n\tisSingularValue(value)\n\t\t? mapper(value)\n\t\t: (Object.fromEntries(\n\t\t\t\tObject.entries(value).map(([breakpoint, value]) => [\n\t\t\t\t\tbreakpoint,\n\t\t\t\t\tmapper(value),\n\t\t\t\t]),\n\t\t\t) as BreakpointsMap<T, B>);\n\n/**\n * Start of rcv and types\n */\n\ntype VariantValue = Record<string, string | Record<string, string>>;\ntype VariantConfig = Record<string, VariantValue>;\n\ntype StringBoolean = \"true\" | \"false\";\ntype BooleanVariant = Partial<\n\tRecord<StringBoolean, string | Record<string, string>>\n>;\n\ntype VariantPropValue<T, B extends string> = T extends BooleanVariant\n\t? ResponsiveValue<boolean, B> | undefined\n\t: T extends Record<string, unknown>\n\t\t? ResponsiveValue<keyof T, B>\n\t\t: never;\n\ntype VariantProps<T extends VariantConfig, B extends string> = {\n\t[K in keyof T]?: VariantPropValue<T[K], B>;\n} & {\n\tclassName?: string;\n};\n\n// Slot configuration types\ntype SlotConfig = string;\n\ntype SlotsConfig<S extends Record<string, string>> = S;\n\ntype CompoundVariantWithSlots<\n\tT extends VariantConfig,\n\tS extends string,\n\tB extends string,\n> = Partial<VariantProps<T, B>> & {\n\tclass?: Partial<Record<S, string>>;\n\tclassName?: string;\n};\n\ntype ResponsiveClassesConfigBase<T extends VariantConfig, B extends string> = {\n\tbase: string;\n\tvariants?: T;\n\tcompoundVariants?: Partial<VariantProps<T, B>>[];\n\tonComplete?: (classes: string) => string;\n};\n\ntype ResponsiveClassesConfigSlots<\n\tT extends VariantConfig,\n\tS extends Record<string, string>,\n\tB extends string,\n> = {\n\tslots: SlotsConfig<S>;\n\tvariants?: T;\n\tcompoundVariants?: CompoundVariantWithSlots<T, keyof S & string, B>[];\n\tonComplete?: (classes: string) => string;\n};\n\ntype ResponsiveClassesConfig<T extends VariantConfig, B extends string> =\n\t| ResponsiveClassesConfigBase<T, B>\n\t| ResponsiveClassesConfigSlots<T, Record<string, string>, B>;\n\n// Helper functions for slots\nconst isSlotsConfig = <T extends VariantConfig, B extends string>(\n\tconfig: ResponsiveClassesConfig<T, B>,\n): config is ResponsiveClassesConfigSlots<T, Record<string, string>, B> => {\n\treturn \"slots\" in config;\n};\n\nconst prefixClasses = (classes: string, prefix: string) =>\n\tclasses\n\t\t.split(\" \")\n\t\t.map((className) => `${prefix}:${className}`)\n\t\t.join(\" \");\n\n// Helper function to get variant value for a specific slot or base\nconst getVariantValue = <T extends VariantConfig>(\n\tvariants: T | undefined,\n\tkey: keyof T,\n\tvalue: string,\n\tslotName?: string,\n) => {\n\tconst variant = variants?.[key];\n\tconst variantValue = variant?.[value as keyof VariantValue];\n\n\t// Handle string values\n\tif (typeof variantValue === \"string\") {\n\t\treturn variantValue;\n\t}\n\n\t// Handle object values (slot-specific classes)\n\tif (\n\t\ttypeof variantValue === \"object\" &&\n\t\tvariantValue !== null &&\n\t\tslotName &&\n\t\tslotName in variantValue\n\t) {\n\t\tconst slotSpecificValue = variantValue[slotName];\n\t\tif (typeof slotSpecificValue === \"string\") {\n\t\t\treturn slotSpecificValue;\n\t\t}\n\t}\n\n\treturn undefined;\n};\n\n// Helper function to process responsive values\nconst processResponsiveValue = <T extends VariantConfig, B extends string>(\n\tvariants: T | undefined,\n\tkey: keyof T,\n\tvalue: Partial<BreakpointsMap<T, B>>,\n\tslotName?: string,\n) => {\n\treturn Object.entries(value)\n\t\t.map(([breakpoint, breakpointValue]) => {\n\t\t\tconst variantValue = getVariantValue(\n\t\t\t\tvariants,\n\t\t\t\tkey,\n\t\t\t\tbreakpointValue as string,\n\t\t\t\tslotName,\n\t\t\t);\n\n\t\t\tif (!variantValue) return undefined;\n\n\t\t\t// If the breakpoint is initial, return without prefix\n\t\t\tif (breakpoint === \"initial\") {\n\t\t\t\treturn variantValue;\n\t\t\t}\n\n\t\t\t// Otherwise, return with breakpoint prefix\n\t\t\treturn prefixClasses(variantValue, breakpoint);\n\t\t})\n\t\t.filter(Boolean)\n\t\t.join(\" \");\n};\n\n// Helper function to process variant props into classes\nconst processVariantProps = <T extends VariantConfig, B extends string>(\n\tprops: Omit<VariantProps<T, B>, \"className\">,\n\tvariants: T | undefined,\n\tslotName?: string,\n) => {\n\treturn Object.entries(props)\n\t\t.map(([key, propValue]: [keyof T, VariantPropValue<T[keyof T], B>]) => {\n\t\t\tconst value =\n\t\t\t\ttypeof propValue === \"boolean\" ? String(propValue) : propValue;\n\n\t\t\t// Handle undefined values\n\t\t\tif (!value) return undefined;\n\n\t\t\t// Handle singular values\n\t\t\tif (typeof value === \"string\") {\n\t\t\t\treturn getVariantValue(variants, key, value, slotName);\n\t\t\t}\n\n\t\t\t// Handle responsive values\n\t\t\treturn processResponsiveValue(\n\t\t\t\tvariants,\n\t\t\t\tkey,\n\t\t\t\tvalue as Partial<BreakpointsMap<T, B>>,\n\t\t\t\tslotName,\n\t\t\t);\n\t\t})\n\t\t.filter(Boolean)\n\t\t.join(\" \");\n};\n\n// Helper function to match compound variants\nconst matchesCompoundVariant = <T extends VariantConfig, B extends string>(\n\tcompound: Omit<CompoundVariantWithSlots<T, string, B>, \"className\" | \"class\">,\n\tprops: Omit<VariantProps<T, B>, \"className\">,\n) => {\n\treturn Object.entries(compound).every(\n\t\t([key, value]) =>\n\t\t\tprops[key as keyof typeof props] === String(value) ||\n\t\t\tprops[key as keyof typeof props] === value,\n\t);\n};\n\nconst createSlotFunction =\n\t<T extends VariantConfig, B extends string>(\n\t\tslotConfig: SlotConfig,\n\t\tvariants: T | undefined,\n\t\tcompoundVariants: CompoundVariantWithSlots<T, string, B>[] | undefined,\n\t\tonComplete: ((classes: string) => string) | undefined,\n\t\tslotName: string,\n\t) =>\n\t({ className, ...props }: VariantProps<T, B> = {} as VariantProps<T, B>) => {\n\t\tconst responsiveClasses = processVariantProps(props, variants, slotName);\n\n\t\tconst compoundClasses = compoundVariants\n\t\t\t?.map(\n\t\t\t\t({ class: slotClasses, className: compoundClassName, ...compound }) => {\n\t\t\t\t\tif (matchesCompoundVariant(compound, props)) {\n\t\t\t\t\t\t// If compound variant has slot-specific classes, use those for this slot\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\tslotClasses &&\n\t\t\t\t\t\t\ttypeof slotClasses === \"object\" &&\n\t\t\t\t\t\t\tslotClasses[slotName]\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\treturn slotClasses[slotName];\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// Otherwise use the general className\n\t\t\t\t\t\treturn compoundClassName;\n\t\t\t\t\t}\n\t\t\t\t\treturn undefined;\n\t\t\t\t},\n\t\t\t)\n\t\t\t.filter(Boolean);\n\n\t\tconst classes = clsx(\n\t\t\tslotConfig,\n\t\t\tresponsiveClasses,\n\t\t\tcompoundClasses,\n\t\t\tclassName,\n\t\t);\n\t\treturn onComplete ? onComplete(classes) : classes;\n\t};\n\n// Function overloads for rcv\nexport function rcv<\n\tT extends VariantConfig,\n\tS extends Record<string, string>,\n\tB extends string = DefaultBreakpoints,\n>(config: {\n\tslots: S;\n\tvariants?: T;\n\tcompoundVariants?: CompoundVariantWithSlots<T, keyof S & string, B>[];\n\tonComplete?: (classes: string) => string;\n}): {\n\t[K in keyof S]: (props?: VariantProps<T, B>) => string;\n};\n\nexport function rcv<\n\tT extends VariantConfig,\n\tB extends string = DefaultBreakpoints,\n>(config: {\n\tbase: string;\n\tvariants?: T;\n\tcompoundVariants?: Partial<VariantProps<T, B>>[];\n\tonComplete?: (classes: string) => string;\n}): (props: VariantProps<T, B>) => string;\n\nexport function rcv<\n\tT extends VariantConfig,\n\tS extends Record<string, string>,\n\tB extends string = DefaultBreakpoints,\n>(\n\tconfig:\n\t\t| ResponsiveClassesConfig<T, B>\n\t\t| {\n\t\t\t\tslots: S;\n\t\t\t\tvariants?: T;\n\t\t\t\tcompoundVariants?: CompoundVariantWithSlots<T, keyof S & string, B>[];\n\t\t\t\tonComplete?: (classes: string) => string;\n\t\t },\n) {\n\t// Check if config is a slots config\n\tif (isSlotsConfig(config)) {\n\t\tconst { slots, variants, compoundVariants, onComplete } = config;\n\t\tconst slotFunctions = {} as {\n\t\t\t[K in keyof S]: (props?: VariantProps<T, B>) => string;\n\t\t};\n\n\t\t// Create slot functions for each slot - ensure all slots are always present\n\t\tfor (const [slotName, slotConfig] of Object.entries(slots)) {\n\t\t\tslotFunctions[slotName as keyof S] = createSlotFunction<T, B>(\n\t\t\t\tslotConfig,\n\t\t\t\tvariants,\n\t\t\t\tcompoundVariants,\n\t\t\t\tonComplete,\n\t\t\t\tslotName,\n\t\t\t);\n\t\t}\n\n\t\treturn slotFunctions;\n\t}\n\n\t// If config is not a slots config, create a base function\n\tconst { base, variants, compoundVariants, onComplete } = config;\n\treturn ({ className, ...props }: VariantProps<T, B>) => {\n\t\tconst responsiveClasses = processVariantProps(props, variants);\n\n\t\tconst compoundClasses = compoundVariants\n\t\t\t?.map(({ className: compoundClassName, ...compound }) => {\n\t\t\t\tif (\n\t\t\t\t\tmatchesCompoundVariant(\n\t\t\t\t\t\tcompound as Omit<\n\t\t\t\t\t\t\tCompoundVariantWithSlots<T, string, B>,\n\t\t\t\t\t\t\t\"className\" | \"class\"\n\t\t\t\t\t\t>,\n\t\t\t\t\t\tprops,\n\t\t\t\t\t)\n\t\t\t\t) {\n\t\t\t\t\treturn compoundClassName;\n\t\t\t\t}\n\t\t\t\treturn undefined;\n\t\t\t})\n\t\t\t.filter(Boolean);\n\n\t\tconst classes = clsx(base, responsiveClasses, compoundClasses, className);\n\t\treturn onComplete ? onComplete(classes) : classes;\n\t};\n}\n\n/**\n * Creates a custom rcv function with custom breakpoints and an optional onComplete callback\n *\n * @template B - The custom breakpoints type\n * @param breakpoints - Optional array of custom breakpoint names\n * @param onComplete - Optional callback function that receives the generated classes and returns the final classes\n * @returns A function that creates rcv with custom breakpoints\n *\n * @example\n * const customRcv = createRcv(['mobile', 'tablet', 'desktop']);\n *\n * const getButtonVariants = customRcv({\n * base: \"px-4 py-2 rounded\",\n * variants: {\n * intent: {\n * primary: \"bg-blue-500 text-white\",\n * secondary: \"bg-gray-200 text-gray-800\"\n * }\n * }\n * });\n *\n * // Usage with custom breakpoints:\n * getButtonVariants({ intent: { initial: \"primary\", mobile: \"secondary\", desktop: \"primary\" } })\n */\n\nexport const createRcv = <B extends string>(\n\t_breakpoints?: readonly B[],\n\tonComplete?: (classes: string) => string,\n) => {\n\tfunction customRcv<\n\t\tT extends VariantConfig,\n\t\tS extends Record<string, string>,\n\t>(config: {\n\t\tslots: S;\n\t\tvariants?: T;\n\t\tcompoundVariants?: CompoundVariantWithSlots<T, keyof S & string, B>[];\n\t\tonComplete?: (classes: string) => string;\n\t}): {\n\t\t[K in keyof S]: (props?: VariantProps<T, B>) => string;\n\t};\n\n\tfunction customRcv<T extends VariantConfig>(config: {\n\t\tbase: string;\n\t\tvariants?: T;\n\t\tcompoundVariants?: Partial<VariantProps<T, B>>[];\n\t\tonComplete?: (classes: string) => string;\n\t}): (props: VariantProps<T, B>) => string;\n\n\tfunction customRcv<T extends VariantConfig, S extends Record<string, string>>(\n\t\tconfig:\n\t\t\t| ResponsiveClassesConfig<T, B>\n\t\t\t| {\n\t\t\t\t\tslots: S;\n\t\t\t\t\tvariants?: T;\n\t\t\t\t\tcompoundVariants?: CompoundVariantWithSlots<T, keyof S & string, B>[];\n\t\t\t\t\tonComplete?: (classes: string) => string;\n\t\t\t },\n\t) {\n\t\tif (isSlotsConfig(config)) {\n\t\t\treturn rcv<T, S, B>({\n\t\t\t\t...config,\n\t\t\t\tonComplete: onComplete || config.onComplete,\n\t\t\t} as {\n\t\t\t\tslots: S;\n\t\t\t\tvariants?: T;\n\t\t\t\tcompoundVariants?: CompoundVariantWithSlots<T, keyof S & string, B>[];\n\t\t\t\tonComplete?: (classes: string) => string;\n\t\t\t});\n\t\t} else {\n\t\t\treturn rcv<T, B>({\n\t\t\t\t...config,\n\t\t\t\tonComplete: onComplete || config.onComplete,\n\t\t\t});\n\t\t}\n\t}\n\n\treturn customRcv;\n};\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "responsive-class-variants",
3
- "version": "1.0.1",
3
+ "version": "1.1.1",
4
4
  "description": "rcv helps you create responsive class variants",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",