@latte-macchiat-io/latte-vanilla-components 0.0.326 → 0.0.328

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@latte-macchiat-io/latte-vanilla-components",
3
- "version": "0.0.326",
3
+ "version": "0.0.328",
4
4
  "description": "Beautiful components for amazing projects, with a touch of Vanilla 🥤",
5
5
  "type": "module",
6
6
  "main": "./src/index.ts",
@@ -5,10 +5,7 @@ import { recipe, RecipeVariants } from '@vanilla-extract/recipes';
5
5
  import { queries } from '../../styles/mediaqueries';
6
6
  import { themeContract } from '../../theme/contract.css';
7
7
 
8
- import { generateResponsive } from '../../utils/generateResponsive';
9
8
  import { generateResponsiveMedia } from '../../utils/generateResponsiveMedia';
10
- import { generateResponsiveMediaCalc } from '../../utils/generateResponsiveMediaCalc';
11
- import { mergeResponsiveMedias } from '../../utils/mergeResponsiveMedias';
12
9
 
13
10
  export const carouselRecipe = recipe(
14
11
  {
@@ -98,11 +95,9 @@ export const carouselNav = recipe(
98
95
  pointerEvents: 'none',
99
96
  justifyContent: 'flex-end',
100
97
 
101
- '@media': {
102
- ...generateResponsiveMedia({
103
- gap: themeContract.carousel.nav.gap,
104
- }),
105
- },
98
+ '@media': generateResponsiveMedia({
99
+ gap: themeContract.carousel.nav.gap,
100
+ }),
106
101
  },
107
102
 
108
103
  variants: {
@@ -122,14 +117,34 @@ export const carouselNav = recipe(
122
117
 
123
118
  navPositionVertical: {
124
119
  top: {
125
- '@media': generateResponsiveMediaCalc('bottom', '100%', themeContract.carousel.nav.positionVerticalOffset, '+'),
120
+ '@media': generateResponsiveMedia(
121
+ {}, // pas de props "normales" ici
122
+ [
123
+ {
124
+ property: 'bottom',
125
+ base: '100%',
126
+ offset: themeContract.carousel.nav.positionVerticalOffset,
127
+ operator: '+',
128
+ },
129
+ ]
130
+ ),
126
131
  },
127
132
  center: {
128
133
  top: '50%',
129
134
  transform: 'translate(0%, -50%)',
130
135
  },
131
136
  bottom: {
132
- '@media': generateResponsiveMediaCalc('top', '100%', themeContract.carousel.nav.positionVerticalOffset, '+'),
137
+ '@media': generateResponsiveMedia(
138
+ {}, // pas de props "normales" ici non plus
139
+ [
140
+ {
141
+ property: 'top',
142
+ base: '100%',
143
+ offset: themeContract.carousel.nav.positionVerticalOffset,
144
+ operator: '+',
145
+ },
146
+ ]
147
+ ),
133
148
  },
134
149
  },
135
150
  },
@@ -167,12 +182,10 @@ export const carouselNavButton = style(
167
182
  pointerEvents: 'none',
168
183
  },
169
184
 
170
- '@media': {
171
- ...generateResponsiveMedia({
172
- width: themeContract.carousel.nav.width,
173
- height: themeContract.carousel.nav.height,
174
- }),
175
- },
185
+ '@media': generateResponsiveMedia({
186
+ width: themeContract.carousel.nav.width,
187
+ height: themeContract.carousel.nav.height,
188
+ }),
176
189
  },
177
190
  'carousel-nav-button'
178
191
  );
@@ -186,7 +199,7 @@ export const carouselBullets = style({
186
199
  position: 'absolute',
187
200
  justifyContent: 'center',
188
201
 
189
- '@media': generateResponsive(
202
+ '@media': generateResponsiveMedia(
190
203
  {
191
204
  gap: themeContract.carousel.bullet.gap,
192
205
  },
@@ -214,12 +227,10 @@ export const carouselBullet = style(
214
227
  transform: 'scale(1.2)',
215
228
  },
216
229
 
217
- '@media': {
218
- ...generateResponsiveMedia({
219
- width: themeContract.carousel.bullet.width,
220
- height: themeContract.carousel.bullet.height,
221
- }),
222
- },
230
+ '@media': generateResponsiveMedia({
231
+ width: themeContract.carousel.bullet.width,
232
+ height: themeContract.carousel.bullet.height,
233
+ }),
223
234
  },
224
235
  'carousel-bullet'
225
236
  );
@@ -1,46 +1,50 @@
1
- // utils/generateResponsiveMedia.ts
1
+ // utils/generateResponsive.ts
2
2
  import { queries } from '../styles/mediaqueries';
3
3
 
4
- /**
5
- * Breakpoint keys you support (should match your queries object).
6
- */
4
+ // Define the supported breakpoint keys
7
5
  type BreakpointKey = 'mobile' | 'sm' | 'md' | 'lg' | 'xl' | '2xl';
8
- const BPS: BreakpointKey[] = ['mobile', 'sm', 'md', 'lg', 'xl', '2xl'];
9
6
 
10
- /**
11
- * Responsive values can be a plain string/number or anything with a toString().
12
- */
7
+ // A responsive value can be a string, number, or anything with a toString method
13
8
  type ResponsiveValue = string | number | { toString(): string };
9
+
10
+ // Map of breakpoint keys to responsive values
14
11
  type BreakpointMap = Partial<Record<BreakpointKey, ResponsiveValue>>;
15
12
 
13
+ // Ordered list of breakpoints to iterate over
14
+ const BPS: BreakpointKey[] = ['mobile', 'sm', 'md', 'lg', 'xl', '2xl'];
15
+
16
16
  /**
17
- * Generate responsive media queries for a set of CSS properties.
17
+ * Generates a fully responsive CSS object for Vanilla Extract
18
18
  *
19
- * Example:
20
- * generateResponsiveMedia({
21
- * width: { mobile: '100%', md: '50%' },
22
- * padding: '20px'
23
- * })
24
- *
25
- * => {
26
- * '@media (min-width:...)': { width: '50%', padding: '20px' },
27
- * ...
28
- * }
19
+ * @param props - normal CSS properties, either a single value or responsive map
20
+ * @param calcProps - optional array of calc() rules for dynamic calculations
21
+ * @returns an object ready to use under `@media` in Vanilla Extract
29
22
  */
30
- export function generateResponsiveMedia(properties: Record<string, ResponsiveValue | BreakpointMap>) {
23
+ export function generateResponsiveMedia(
24
+ props: Record<string, ResponsiveValue | BreakpointMap>,
25
+ calcProps?: Array<{
26
+ property: string;
27
+ base: ResponsiveValue | BreakpointMap;
28
+ offset: ResponsiveValue | BreakpointMap;
29
+ operator?: string; // + - * / etc.
30
+ }>
31
+ ): Record<string, Record<string, string>> {
31
32
  const result: Record<string, Record<string, string>> = {};
32
33
 
34
+ // Helper to convert a value to a proper CSS string
33
35
  const toCssValue = (v: ResponsiveValue) => {
34
36
  const s = String(v);
35
- // if it's a CSS variable token, wrap with var(...)
37
+ // If the value is a CSS variable, wrap it with var()
36
38
  return s.startsWith('--') ? `var(${s})` : s;
37
39
  };
38
40
 
39
- for (const [cssProp, valOrMap] of Object.entries(properties)) {
41
+ // 1️⃣ Process normal CSS properties
42
+ for (const [cssProp, valOrMap] of Object.entries(props)) {
40
43
  const isMapLike =
41
44
  valOrMap && typeof valOrMap === 'object' && !Array.isArray(valOrMap) && Object.keys(valOrMap).some((k) => BPS.includes(k as BreakpointKey));
42
45
 
43
46
  if (isMapLike) {
47
+ // Responsive map provided
44
48
  const map = valOrMap as BreakpointMap;
45
49
  for (const bp of BPS) {
46
50
  const token = map[bp];
@@ -50,7 +54,7 @@ export function generateResponsiveMedia(properties: Record<string, ResponsiveVal
50
54
  result[media][cssProp] = toCssValue(token);
51
55
  }
52
56
  } else {
53
- // single value => apply to all breakpoints
57
+ // Single value, apply to all breakpoints
54
58
  const token = valOrMap as ResponsiveValue;
55
59
  for (const bp of BPS) {
56
60
  const media = queries[bp as keyof typeof queries];
@@ -60,6 +64,29 @@ export function generateResponsiveMedia(properties: Record<string, ResponsiveVal
60
64
  }
61
65
  }
62
66
 
67
+ // 2️⃣ Process calc() properties if provided
68
+ if (calcProps) {
69
+ for (const { property, base, offset, operator = '+' } of calcProps) {
70
+ for (const bp of BPS) {
71
+ const media = queries[bp as keyof typeof queries];
72
+
73
+ // Safely extract the base value for the current breakpoint
74
+ const baseValue =
75
+ typeof base === 'object' && !Array.isArray(base) ? ((base as BreakpointMap)[bp] ?? Object.values(base as BreakpointMap)[0]) : base;
76
+
77
+ // Safely extract the offset value for the current breakpoint
78
+ const offsetValue =
79
+ typeof offset === 'object' && !Array.isArray(offset)
80
+ ? ((offset as BreakpointMap)[bp] ?? Object.values(offset as BreakpointMap)[0])
81
+ : offset;
82
+
83
+ if (!result[media]) result[media] = {};
84
+ // Build the calc() CSS value
85
+ result[media][property] = `calc(${baseValue} ${operator} ${offsetValue})`;
86
+ }
87
+ }
88
+ }
89
+
63
90
  return result;
64
91
  }
65
92
 
@@ -1,80 +0,0 @@
1
- // utils/generateResponsive.ts
2
- import { queries } from '../styles/mediaqueries';
3
-
4
- type BreakpointKey = 'mobile' | 'sm' | 'md' | 'lg' | 'xl' | '2xl';
5
- type ResponsiveValue = string | number | { toString(): string };
6
- type BreakpointMap = Partial<Record<BreakpointKey, ResponsiveValue>>;
7
-
8
- const BPS: BreakpointKey[] = ['mobile', 'sm', 'md', 'lg', 'xl', '2xl'];
9
-
10
- /**
11
- * Generate fully responsive CSS object for Vanilla Extract
12
- *
13
- * @param props - normal CSS properties (string/number or responsive map)
14
- * @param calcProps - optional array of calc() rules
15
- * @returns object ready for `@media` in Vanilla Extract
16
- */
17
- export function generateResponsive(
18
- props: Record<string, ResponsiveValue | BreakpointMap>,
19
- calcProps?: Array<{
20
- property: string;
21
- base: ResponsiveValue | BreakpointMap;
22
- offset: ResponsiveValue | BreakpointMap;
23
- operator?: string; // + - * / ...
24
- }>
25
- ): Record<string, Record<string, string>> {
26
- const result: Record<string, Record<string, string>> = {};
27
-
28
- const toCssValue = (v: ResponsiveValue) => {
29
- const s = String(v);
30
- return s.startsWith('--') ? `var(${s})` : s;
31
- };
32
-
33
- // 1️⃣ Normal props
34
- for (const [cssProp, valOrMap] of Object.entries(props)) {
35
- const isMapLike =
36
- valOrMap && typeof valOrMap === 'object' && !Array.isArray(valOrMap) && Object.keys(valOrMap).some((k) => BPS.includes(k as BreakpointKey));
37
-
38
- if (isMapLike) {
39
- const map = valOrMap as BreakpointMap;
40
- for (const bp of BPS) {
41
- const token = map[bp];
42
- if (token === undefined) continue;
43
- const media = queries[bp as keyof typeof queries];
44
- if (!result[media]) result[media] = {};
45
- result[media][cssProp] = toCssValue(token);
46
- }
47
- } else {
48
- // single value => apply to all breakpoints
49
- const token = valOrMap as ResponsiveValue;
50
- for (const bp of BPS) {
51
- const media = queries[bp as keyof typeof queries];
52
- if (!result[media]) result[media] = {};
53
- result[media][cssProp] = toCssValue(token);
54
- }
55
- }
56
- }
57
-
58
- // 2️⃣ Calc props
59
- if (calcProps) {
60
- for (const { property, base, offset, operator = '+' } of calcProps) {
61
- for (const bp of BPS) {
62
- const media = queries[bp as keyof typeof queries];
63
-
64
- // ✅ TypeScript safe access
65
- const baseValue =
66
- typeof base === 'object' && !Array.isArray(base) ? ((base as BreakpointMap)[bp] ?? Object.values(base as BreakpointMap)[0]) : base;
67
-
68
- const offsetValue =
69
- typeof offset === 'object' && !Array.isArray(offset)
70
- ? ((offset as BreakpointMap)[bp] ?? Object.values(offset as BreakpointMap)[0])
71
- : offset;
72
-
73
- if (!result[media]) result[media] = {};
74
- result[media][property] = `calc(${baseValue} ${operator} ${offsetValue})`;
75
- }
76
- }
77
- }
78
-
79
- return result;
80
- }
@@ -1,32 +0,0 @@
1
- import { queries } from '../styles/mediaqueries';
2
-
3
- /**
4
- * Génère un objet @media pour Vanilla Extract avec des valeurs calculées (calc)
5
- * Supporte les valeurs responsive issues du thème pour les deux opérandes.
6
- *
7
- * @param property - la propriété CSS (ex: 'top', 'bottom', 'marginTop'…)
8
- * @param baseValue - soit une string, soit un objet responsive (themeContract.*)
9
- * @param offsetValues - un objet responsive avec des offsets (ex: themeContract.carousel.nav.positionVerticalOffset)
10
- * @param operator - opérateur de calcul ('+' ou '-') [par défaut '+']
11
- * @returns Un objet directement utilisable dans `@media`
12
- */
13
- export function generateResponsiveMediaCalc(
14
- property: string,
15
- baseValue: string | Record<string, string>,
16
- offsetValues: Record<string, string>,
17
- operator: string = '+'
18
- ) {
19
- const result: Record<string, Record<string, string>> = {};
20
-
21
- for (const [bp, query] of Object.entries(queries)) {
22
- const base = typeof baseValue === 'string' ? baseValue : (baseValue[bp] ?? Object.values(baseValue)[0]); // fallback pour mobile
23
- const offset = offsetValues[bp];
24
- if (offset) {
25
- result[query] = {
26
- [property]: `calc(${base} ${operator} ${offset})`,
27
- };
28
- }
29
- }
30
-
31
- return result;
32
- }
@@ -1,21 +0,0 @@
1
- /**
2
- * Fusionne plusieurs objets responsive générés pour Vanilla Extract
3
- * sans écraser les valeurs existantes des mêmes breakpoints.
4
- *
5
- * Exemple :
6
- * mergeResponsiveMedias(a, b) => combine les clés communes
7
- */
8
- export function mergeResponsiveMedias(...mediaObjects: Record<string, Record<string, string>>[]): Record<string, Record<string, string>> {
9
- const merged: Record<string, Record<string, string>> = {};
10
-
11
- for (const obj of mediaObjects) {
12
- for (const [query, styles] of Object.entries(obj)) {
13
- merged[query] = {
14
- ...(merged[query] || {}),
15
- ...styles,
16
- };
17
- }
18
- }
19
-
20
- return merged;
21
- }