@mgcrea/react-native-tailwind 0.14.0 → 0.15.0
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 +12 -8
- package/dist/babel/config-loader.d.ts +10 -0
- package/dist/babel/config-loader.test.ts +75 -21
- package/dist/babel/config-loader.ts +100 -2
- package/dist/babel/index.cjs +101 -33
- package/dist/parser/index.d.ts +1 -0
- package/dist/parser/index.js +1 -1
- package/dist/parser/layout.d.ts +3 -1
- package/dist/parser/layout.js +1 -1
- package/dist/parser/layout.test.js +1 -1
- package/dist/parser/sizing.d.ts +3 -1
- package/dist/parser/sizing.js +1 -1
- package/dist/parser/sizing.test.js +1 -1
- package/dist/parser/spacing.d.ts +3 -1
- package/dist/parser/spacing.js +1 -1
- package/dist/parser/spacing.test.js +1 -1
- package/dist/parser/transforms.d.ts +3 -1
- package/dist/parser/transforms.js +1 -1
- package/dist/parser/transforms.test.js +1 -1
- package/dist/runtime.cjs +1 -1
- package/dist/runtime.cjs.map +3 -3
- package/dist/runtime.d.ts +2 -0
- package/dist/runtime.js +1 -1
- package/dist/runtime.js.map +3 -3
- package/dist/runtime.test.js +1 -1
- package/package.json +1 -1
- package/src/babel/config-loader.test.ts +75 -21
- package/src/babel/config-loader.ts +100 -2
- package/src/parser/index.ts +6 -5
- package/src/parser/layout.test.ts +94 -0
- package/src/parser/layout.ts +17 -12
- package/src/parser/sizing.test.ts +56 -0
- package/src/parser/sizing.ts +20 -15
- package/src/parser/spacing.test.ts +57 -0
- package/src/parser/spacing.ts +15 -10
- package/src/parser/transforms.test.ts +57 -0
- package/src/parser/transforms.ts +7 -3
- package/src/runtime.test.ts +149 -0
- package/src/runtime.ts +53 -1
package/src/parser/sizing.ts
CHANGED
|
@@ -95,8 +95,13 @@ function parseArbitrarySize(value: string): number | string | null {
|
|
|
95
95
|
|
|
96
96
|
/**
|
|
97
97
|
* Parse sizing classes
|
|
98
|
+
* @param cls - The class name to parse
|
|
99
|
+
* @param customSpacing - Optional custom spacing values from tailwind.config (shared with spacing utilities)
|
|
98
100
|
*/
|
|
99
|
-
export function parseSizing(cls: string): StyleObject | null {
|
|
101
|
+
export function parseSizing(cls: string, customSpacing?: Record<string, number>): StyleObject | null {
|
|
102
|
+
// Merge custom spacing with defaults (custom takes precedence)
|
|
103
|
+
const sizeMap = customSpacing ? { ...SIZE_SCALE, ...customSpacing } : SIZE_SCALE;
|
|
104
|
+
|
|
100
105
|
// Width
|
|
101
106
|
if (cls.startsWith("w-")) {
|
|
102
107
|
const sizeKey = cls.substring(2);
|
|
@@ -106,7 +111,7 @@ export function parseSizing(cls: string): StyleObject | null {
|
|
|
106
111
|
return { width: `${RUNTIME_DIMENSIONS_MARKER}width}}` } as StyleObject;
|
|
107
112
|
}
|
|
108
113
|
|
|
109
|
-
// Arbitrary values: w-[123px], w-[50%]
|
|
114
|
+
// Arbitrary values: w-[123px], w-[50%] (highest priority)
|
|
110
115
|
const arbitrarySize = parseArbitrarySize(sizeKey);
|
|
111
116
|
if (arbitrarySize !== null) {
|
|
112
117
|
return { width: arbitrarySize };
|
|
@@ -118,8 +123,8 @@ export function parseSizing(cls: string): StyleObject | null {
|
|
|
118
123
|
return { width: percentage };
|
|
119
124
|
}
|
|
120
125
|
|
|
121
|
-
// Numeric widths: w-4, w-8, etc.
|
|
122
|
-
const numericSize =
|
|
126
|
+
// Numeric widths: w-4, w-8, etc. (includes custom spacing)
|
|
127
|
+
const numericSize = sizeMap[sizeKey];
|
|
123
128
|
if (numericSize !== undefined) {
|
|
124
129
|
return { width: numericSize };
|
|
125
130
|
}
|
|
@@ -139,7 +144,7 @@ export function parseSizing(cls: string): StyleObject | null {
|
|
|
139
144
|
return { height: `${RUNTIME_DIMENSIONS_MARKER}height}}` } as StyleObject;
|
|
140
145
|
}
|
|
141
146
|
|
|
142
|
-
// Arbitrary values: h-[123px], h-[50%]
|
|
147
|
+
// Arbitrary values: h-[123px], h-[50%] (highest priority)
|
|
143
148
|
const arbitrarySize = parseArbitrarySize(sizeKey);
|
|
144
149
|
if (arbitrarySize !== null) {
|
|
145
150
|
return { height: arbitrarySize };
|
|
@@ -151,8 +156,8 @@ export function parseSizing(cls: string): StyleObject | null {
|
|
|
151
156
|
return { height: percentage };
|
|
152
157
|
}
|
|
153
158
|
|
|
154
|
-
// Numeric heights: h-4, h-8, etc.
|
|
155
|
-
const numericSize =
|
|
159
|
+
// Numeric heights: h-4, h-8, etc. (includes custom spacing)
|
|
160
|
+
const numericSize = sizeMap[sizeKey];
|
|
156
161
|
if (numericSize !== undefined) {
|
|
157
162
|
return { height: numericSize };
|
|
158
163
|
}
|
|
@@ -167,7 +172,7 @@ export function parseSizing(cls: string): StyleObject | null {
|
|
|
167
172
|
if (cls.startsWith("min-w-")) {
|
|
168
173
|
const sizeKey = cls.substring(6);
|
|
169
174
|
|
|
170
|
-
// Arbitrary values: min-w-[123px], min-w-[50%]
|
|
175
|
+
// Arbitrary values: min-w-[123px], min-w-[50%] (highest priority)
|
|
171
176
|
const arbitrarySize = parseArbitrarySize(sizeKey);
|
|
172
177
|
if (arbitrarySize !== null) {
|
|
173
178
|
return { minWidth: arbitrarySize };
|
|
@@ -178,7 +183,7 @@ export function parseSizing(cls: string): StyleObject | null {
|
|
|
178
183
|
return { minWidth: percentage };
|
|
179
184
|
}
|
|
180
185
|
|
|
181
|
-
const numericSize =
|
|
186
|
+
const numericSize = sizeMap[sizeKey];
|
|
182
187
|
if (numericSize !== undefined) {
|
|
183
188
|
return { minWidth: numericSize };
|
|
184
189
|
}
|
|
@@ -188,7 +193,7 @@ export function parseSizing(cls: string): StyleObject | null {
|
|
|
188
193
|
if (cls.startsWith("min-h-")) {
|
|
189
194
|
const sizeKey = cls.substring(6);
|
|
190
195
|
|
|
191
|
-
// Arbitrary values: min-h-[123px], min-h-[50%]
|
|
196
|
+
// Arbitrary values: min-h-[123px], min-h-[50%] (highest priority)
|
|
192
197
|
const arbitrarySize = parseArbitrarySize(sizeKey);
|
|
193
198
|
if (arbitrarySize !== null) {
|
|
194
199
|
return { minHeight: arbitrarySize };
|
|
@@ -199,7 +204,7 @@ export function parseSizing(cls: string): StyleObject | null {
|
|
|
199
204
|
return { minHeight: percentage };
|
|
200
205
|
}
|
|
201
206
|
|
|
202
|
-
const numericSize =
|
|
207
|
+
const numericSize = sizeMap[sizeKey];
|
|
203
208
|
if (numericSize !== undefined) {
|
|
204
209
|
return { minHeight: numericSize };
|
|
205
210
|
}
|
|
@@ -209,7 +214,7 @@ export function parseSizing(cls: string): StyleObject | null {
|
|
|
209
214
|
if (cls.startsWith("max-w-")) {
|
|
210
215
|
const sizeKey = cls.substring(6);
|
|
211
216
|
|
|
212
|
-
// Arbitrary values: max-w-[123px], max-w-[50%]
|
|
217
|
+
// Arbitrary values: max-w-[123px], max-w-[50%] (highest priority)
|
|
213
218
|
const arbitrarySize = parseArbitrarySize(sizeKey);
|
|
214
219
|
if (arbitrarySize !== null) {
|
|
215
220
|
return { maxWidth: arbitrarySize };
|
|
@@ -220,7 +225,7 @@ export function parseSizing(cls: string): StyleObject | null {
|
|
|
220
225
|
return { maxWidth: percentage };
|
|
221
226
|
}
|
|
222
227
|
|
|
223
|
-
const numericSize =
|
|
228
|
+
const numericSize = sizeMap[sizeKey];
|
|
224
229
|
if (numericSize !== undefined) {
|
|
225
230
|
return { maxWidth: numericSize };
|
|
226
231
|
}
|
|
@@ -230,7 +235,7 @@ export function parseSizing(cls: string): StyleObject | null {
|
|
|
230
235
|
if (cls.startsWith("max-h-")) {
|
|
231
236
|
const sizeKey = cls.substring(6);
|
|
232
237
|
|
|
233
|
-
// Arbitrary values: max-h-[123px], max-h-[50%]
|
|
238
|
+
// Arbitrary values: max-h-[123px], max-h-[50%] (highest priority)
|
|
234
239
|
const arbitrarySize = parseArbitrarySize(sizeKey);
|
|
235
240
|
if (arbitrarySize !== null) {
|
|
236
241
|
return { maxHeight: arbitrarySize };
|
|
@@ -241,7 +246,7 @@ export function parseSizing(cls: string): StyleObject | null {
|
|
|
241
246
|
return { maxHeight: percentage };
|
|
242
247
|
}
|
|
243
248
|
|
|
244
|
-
const numericSize =
|
|
249
|
+
const numericSize = sizeMap[sizeKey];
|
|
245
250
|
if (numericSize !== undefined) {
|
|
246
251
|
return { maxHeight: numericSize };
|
|
247
252
|
}
|
|
@@ -415,3 +415,60 @@ describe("parseSpacing - logical padding (RTL-aware)", () => {
|
|
|
415
415
|
expect(parseSpacing("pe-[24]")).toEqual({ paddingEnd: 24 });
|
|
416
416
|
});
|
|
417
417
|
});
|
|
418
|
+
|
|
419
|
+
describe("parseSpacing - custom spacing", () => {
|
|
420
|
+
const customSpacing = {
|
|
421
|
+
xs: 4,
|
|
422
|
+
sm: 8,
|
|
423
|
+
md: 16,
|
|
424
|
+
lg: 32,
|
|
425
|
+
xl: 64,
|
|
426
|
+
"4": 20, // Override default (16)
|
|
427
|
+
};
|
|
428
|
+
|
|
429
|
+
it("should support custom spacing values for margin", () => {
|
|
430
|
+
expect(parseSpacing("m-xs", customSpacing)).toEqual({ margin: 4 });
|
|
431
|
+
expect(parseSpacing("m-sm", customSpacing)).toEqual({ margin: 8 });
|
|
432
|
+
expect(parseSpacing("m-lg", customSpacing)).toEqual({ margin: 32 });
|
|
433
|
+
expect(parseSpacing("mx-xl", customSpacing)).toEqual({ marginHorizontal: 64 });
|
|
434
|
+
expect(parseSpacing("mt-md", customSpacing)).toEqual({ marginTop: 16 });
|
|
435
|
+
});
|
|
436
|
+
|
|
437
|
+
it("should support custom spacing values for padding", () => {
|
|
438
|
+
expect(parseSpacing("p-xs", customSpacing)).toEqual({ padding: 4 });
|
|
439
|
+
expect(parseSpacing("p-sm", customSpacing)).toEqual({ padding: 8 });
|
|
440
|
+
expect(parseSpacing("px-lg", customSpacing)).toEqual({ paddingHorizontal: 32 });
|
|
441
|
+
expect(parseSpacing("pt-xl", customSpacing)).toEqual({ paddingTop: 64 });
|
|
442
|
+
});
|
|
443
|
+
|
|
444
|
+
it("should support custom spacing values for gap", () => {
|
|
445
|
+
expect(parseSpacing("gap-xs", customSpacing)).toEqual({ gap: 4 });
|
|
446
|
+
expect(parseSpacing("gap-md", customSpacing)).toEqual({ gap: 16 });
|
|
447
|
+
expect(parseSpacing("gap-xl", customSpacing)).toEqual({ gap: 64 });
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
it("should allow custom spacing to override preset values", () => {
|
|
451
|
+
expect(parseSpacing("m-4", customSpacing)).toEqual({ margin: 20 }); // Custom 20, not default 16
|
|
452
|
+
});
|
|
453
|
+
|
|
454
|
+
it("should prefer arbitrary values over custom spacing", () => {
|
|
455
|
+
expect(parseSpacing("m-[24px]", customSpacing)).toEqual({ margin: 24 }); // Arbitrary wins
|
|
456
|
+
expect(parseSpacing("p-[50]", customSpacing)).toEqual({ padding: 50 }); // Arbitrary wins
|
|
457
|
+
});
|
|
458
|
+
|
|
459
|
+
it("should support negative margins with custom spacing", () => {
|
|
460
|
+
expect(parseSpacing("-m-xs", customSpacing)).toEqual({ margin: -4 });
|
|
461
|
+
expect(parseSpacing("-m-lg", customSpacing)).toEqual({ margin: -32 });
|
|
462
|
+
expect(parseSpacing("-mx-xl", customSpacing)).toEqual({ marginHorizontal: -64 });
|
|
463
|
+
});
|
|
464
|
+
|
|
465
|
+
it("should fall back to preset scale for unknown custom keys", () => {
|
|
466
|
+
expect(parseSpacing("m-8", customSpacing)).toEqual({ margin: 32 }); // Not overridden, uses preset
|
|
467
|
+
expect(parseSpacing("p-12", customSpacing)).toEqual({ padding: 48 }); // Not overridden, uses preset
|
|
468
|
+
});
|
|
469
|
+
|
|
470
|
+
it("should work without custom spacing (backward compatible)", () => {
|
|
471
|
+
expect(parseSpacing("m-4")).toEqual({ margin: 16 }); // Default behavior
|
|
472
|
+
expect(parseSpacing("p-8")).toEqual({ padding: 32 }); // Default behavior
|
|
473
|
+
});
|
|
474
|
+
});
|
package/src/parser/spacing.ts
CHANGED
|
@@ -70,8 +70,13 @@ function parseArbitrarySpacing(value: string): number | null {
|
|
|
70
70
|
/**
|
|
71
71
|
* Parse spacing classes (margin, padding, gap)
|
|
72
72
|
* Examples: m-4, mx-2, mt-8, p-4, px-2, pt-8, gap-4, m-[16px], pl-[4.5px], -m-4, -mt-[10px], ms-4, pe-2
|
|
73
|
+
* @param cls - The class name to parse
|
|
74
|
+
* @param customSpacing - Optional custom spacing values from tailwind.config
|
|
73
75
|
*/
|
|
74
|
-
export function parseSpacing(cls: string): StyleObject | null {
|
|
76
|
+
export function parseSpacing(cls: string, customSpacing?: Record<string, number>): StyleObject | null {
|
|
77
|
+
// Merge custom spacing with defaults (custom takes precedence)
|
|
78
|
+
const spacingMap = customSpacing ? { ...SPACING_SCALE, ...customSpacing } : SPACING_SCALE;
|
|
79
|
+
|
|
75
80
|
// Margin: m-4, mx-2, mt-8, ms-4, me-2, m-[16px], -m-4, -mt-2, etc.
|
|
76
81
|
// Supports negative values for margins (but not padding or gap)
|
|
77
82
|
// s = start (RTL-aware), e = end (RTL-aware)
|
|
@@ -80,15 +85,15 @@ export function parseSpacing(cls: string): StyleObject | null {
|
|
|
80
85
|
const [, negativePrefix, dir, valueStr] = marginMatch;
|
|
81
86
|
const isNegative = negativePrefix === "-";
|
|
82
87
|
|
|
83
|
-
// Try arbitrary value first
|
|
88
|
+
// Try arbitrary value first (highest priority)
|
|
84
89
|
const arbitraryValue = parseArbitrarySpacing(valueStr);
|
|
85
90
|
if (arbitraryValue !== null) {
|
|
86
91
|
const finalValue = isNegative ? -arbitraryValue : arbitraryValue;
|
|
87
92
|
return getMarginStyle(dir, finalValue);
|
|
88
93
|
}
|
|
89
94
|
|
|
90
|
-
// Try
|
|
91
|
-
const scaleValue =
|
|
95
|
+
// Try spacing scale (includes custom spacing)
|
|
96
|
+
const scaleValue = spacingMap[valueStr];
|
|
92
97
|
if (scaleValue !== undefined) {
|
|
93
98
|
const finalValue = isNegative ? -scaleValue : scaleValue;
|
|
94
99
|
return getMarginStyle(dir, finalValue);
|
|
@@ -101,14 +106,14 @@ export function parseSpacing(cls: string): StyleObject | null {
|
|
|
101
106
|
if (paddingMatch) {
|
|
102
107
|
const [, dir, valueStr] = paddingMatch;
|
|
103
108
|
|
|
104
|
-
// Try arbitrary value first
|
|
109
|
+
// Try arbitrary value first (highest priority)
|
|
105
110
|
const arbitraryValue = parseArbitrarySpacing(valueStr);
|
|
106
111
|
if (arbitraryValue !== null) {
|
|
107
112
|
return getPaddingStyle(dir, arbitraryValue);
|
|
108
113
|
}
|
|
109
114
|
|
|
110
|
-
// Try
|
|
111
|
-
const scaleValue =
|
|
115
|
+
// Try spacing scale (includes custom spacing)
|
|
116
|
+
const scaleValue = spacingMap[valueStr];
|
|
112
117
|
if (scaleValue !== undefined) {
|
|
113
118
|
return getPaddingStyle(dir, scaleValue);
|
|
114
119
|
}
|
|
@@ -119,14 +124,14 @@ export function parseSpacing(cls: string): StyleObject | null {
|
|
|
119
124
|
if (gapMatch) {
|
|
120
125
|
const valueStr = gapMatch[1];
|
|
121
126
|
|
|
122
|
-
// Try arbitrary value first
|
|
127
|
+
// Try arbitrary value first (highest priority)
|
|
123
128
|
const arbitraryValue = parseArbitrarySpacing(valueStr);
|
|
124
129
|
if (arbitraryValue !== null) {
|
|
125
130
|
return { gap: arbitraryValue };
|
|
126
131
|
}
|
|
127
132
|
|
|
128
|
-
// Try
|
|
129
|
-
const scaleValue =
|
|
133
|
+
// Try spacing scale (includes custom spacing)
|
|
134
|
+
const scaleValue = spacingMap[valueStr];
|
|
130
135
|
if (scaleValue !== undefined) {
|
|
131
136
|
return { gap: scaleValue };
|
|
132
137
|
}
|
|
@@ -316,3 +316,60 @@ describe("parseTransform - case sensitivity", () => {
|
|
|
316
316
|
expect(parseTransform("ROTATE-45")).toBeNull();
|
|
317
317
|
});
|
|
318
318
|
});
|
|
319
|
+
|
|
320
|
+
describe("parseTransform - custom spacing", () => {
|
|
321
|
+
const customSpacing = {
|
|
322
|
+
xs: 4,
|
|
323
|
+
sm: 8,
|
|
324
|
+
md: 16,
|
|
325
|
+
lg: 32,
|
|
326
|
+
xl: 64,
|
|
327
|
+
"4": 20, // Override default (16)
|
|
328
|
+
};
|
|
329
|
+
|
|
330
|
+
it("should support custom spacing values for translateX", () => {
|
|
331
|
+
expect(parseTransform("translate-x-xs", customSpacing)).toEqual({ transform: [{ translateX: 4 }] });
|
|
332
|
+
expect(parseTransform("translate-x-sm", customSpacing)).toEqual({ transform: [{ translateX: 8 }] });
|
|
333
|
+
expect(parseTransform("translate-x-lg", customSpacing)).toEqual({ transform: [{ translateX: 32 }] });
|
|
334
|
+
expect(parseTransform("translate-x-xl", customSpacing)).toEqual({ transform: [{ translateX: 64 }] });
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
it("should support custom spacing values for translateY", () => {
|
|
338
|
+
expect(parseTransform("translate-y-xs", customSpacing)).toEqual({ transform: [{ translateY: 4 }] });
|
|
339
|
+
expect(parseTransform("translate-y-md", customSpacing)).toEqual({ transform: [{ translateY: 16 }] });
|
|
340
|
+
expect(parseTransform("translate-y-xl", customSpacing)).toEqual({ transform: [{ translateY: 64 }] });
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
it("should support negative custom spacing for translate", () => {
|
|
344
|
+
expect(parseTransform("-translate-x-sm", customSpacing)).toEqual({ transform: [{ translateX: -8 }] });
|
|
345
|
+
expect(parseTransform("-translate-y-lg", customSpacing)).toEqual({ transform: [{ translateY: -32 }] });
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
it("should allow custom spacing to override preset values", () => {
|
|
349
|
+
expect(parseTransform("translate-x-4", customSpacing)).toEqual({ transform: [{ translateX: 20 }] }); // Custom 20, not default 16
|
|
350
|
+
expect(parseTransform("translate-y-4", customSpacing)).toEqual({ transform: [{ translateY: 20 }] }); // Custom 20, not default 16
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
it("should prefer arbitrary values over custom spacing", () => {
|
|
354
|
+
expect(parseTransform("translate-x-[24px]", customSpacing)).toEqual({ transform: [{ translateX: 24 }] }); // Arbitrary wins
|
|
355
|
+
expect(parseTransform("translate-y-[50]", customSpacing)).toEqual({ transform: [{ translateY: 50 }] }); // Arbitrary wins
|
|
356
|
+
});
|
|
357
|
+
|
|
358
|
+
it("should fall back to preset scale for unknown custom keys", () => {
|
|
359
|
+
expect(parseTransform("translate-x-8", customSpacing)).toEqual({ transform: [{ translateX: 32 }] }); // Not overridden, uses preset
|
|
360
|
+
expect(parseTransform("translate-y-12", customSpacing)).toEqual({ transform: [{ translateY: 48 }] }); // Not overridden, uses preset
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
it("should work without custom spacing (backward compatible)", () => {
|
|
364
|
+
expect(parseTransform("translate-x-4")).toEqual({ transform: [{ translateX: 16 }] }); // Default behavior
|
|
365
|
+
expect(parseTransform("translate-y-8")).toEqual({ transform: [{ translateY: 32 }] }); // Default behavior
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
it("should not affect non-translate transforms", () => {
|
|
369
|
+
// Scale, rotate, skew, perspective should not use custom spacing
|
|
370
|
+
expect(parseTransform("scale-110", customSpacing)).toEqual({ transform: [{ scale: 1.1 }] });
|
|
371
|
+
expect(parseTransform("rotate-45", customSpacing)).toEqual({ transform: [{ rotate: "45deg" }] });
|
|
372
|
+
expect(parseTransform("skew-x-6", customSpacing)).toEqual({ transform: [{ skewX: "6deg" }] });
|
|
373
|
+
expect(parseTransform("perspective-500", customSpacing)).toEqual({ transform: [{ perspective: 500 }] });
|
|
374
|
+
});
|
|
375
|
+
});
|
package/src/parser/transforms.ts
CHANGED
|
@@ -164,8 +164,12 @@ function parseArbitraryPerspective(value: string): number | null {
|
|
|
164
164
|
/**
|
|
165
165
|
* Parse transform classes
|
|
166
166
|
* Each transform class returns a transform array with a single transform object
|
|
167
|
+
* @param cls - The class name to parse
|
|
168
|
+
* @param customSpacing - Optional custom spacing values from tailwind.config (for translate utilities)
|
|
167
169
|
*/
|
|
168
|
-
export function parseTransform(cls: string): StyleObject | null {
|
|
170
|
+
export function parseTransform(cls: string, customSpacing?: Record<string, number>): StyleObject | null {
|
|
171
|
+
// Merge custom spacing with defaults for translate utilities
|
|
172
|
+
const spacingMap = customSpacing ? { ...SPACING_SCALE, ...customSpacing } : SPACING_SCALE;
|
|
169
173
|
// Transform origin warning (not supported in React Native)
|
|
170
174
|
if (cls.startsWith("origin-")) {
|
|
171
175
|
/* v8 ignore next 5 */
|
|
@@ -320,7 +324,7 @@ export function parseTransform(cls: string): StyleObject | null {
|
|
|
320
324
|
return { transform: [{ translateX: value }] };
|
|
321
325
|
}
|
|
322
326
|
|
|
323
|
-
const translateValue =
|
|
327
|
+
const translateValue = spacingMap[translateKey];
|
|
324
328
|
if (translateValue !== undefined) {
|
|
325
329
|
const value = isNegative ? -translateValue : translateValue;
|
|
326
330
|
return { transform: [{ translateX: value }] };
|
|
@@ -346,7 +350,7 @@ export function parseTransform(cls: string): StyleObject | null {
|
|
|
346
350
|
return { transform: [{ translateY: value }] };
|
|
347
351
|
}
|
|
348
352
|
|
|
349
|
-
const translateValue =
|
|
353
|
+
const translateValue = spacingMap[translateKey];
|
|
350
354
|
if (translateValue !== undefined) {
|
|
351
355
|
const value = isNegative ? -translateValue : translateValue;
|
|
352
356
|
return { transform: [{ translateY: value }] };
|
package/src/runtime.test.ts
CHANGED
|
@@ -209,6 +209,155 @@ describe("runtime", () => {
|
|
|
209
209
|
backgroundColor: "#007AFF",
|
|
210
210
|
});
|
|
211
211
|
});
|
|
212
|
+
|
|
213
|
+
it("should set custom fontFamily", () => {
|
|
214
|
+
setConfig({
|
|
215
|
+
theme: {
|
|
216
|
+
extend: {
|
|
217
|
+
fontFamily: {
|
|
218
|
+
display: "Inter",
|
|
219
|
+
body: ["Roboto", "sans-serif"], // Array format - takes first value
|
|
220
|
+
},
|
|
221
|
+
},
|
|
222
|
+
},
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
const theme = getCustomTheme();
|
|
226
|
+
expect(theme.fontFamily).toEqual({
|
|
227
|
+
display: "Inter",
|
|
228
|
+
body: "Roboto",
|
|
229
|
+
});
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
it("should set custom fontSize", () => {
|
|
233
|
+
setConfig({
|
|
234
|
+
theme: {
|
|
235
|
+
extend: {
|
|
236
|
+
fontSize: {
|
|
237
|
+
tiny: 10,
|
|
238
|
+
small: "12px",
|
|
239
|
+
medium: 16,
|
|
240
|
+
large: "24",
|
|
241
|
+
},
|
|
242
|
+
},
|
|
243
|
+
},
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
const theme = getCustomTheme();
|
|
247
|
+
expect(theme.fontSize).toEqual({
|
|
248
|
+
tiny: 10,
|
|
249
|
+
small: 12,
|
|
250
|
+
medium: 16,
|
|
251
|
+
large: 24,
|
|
252
|
+
});
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
it("should set custom spacing", () => {
|
|
256
|
+
setConfig({
|
|
257
|
+
theme: {
|
|
258
|
+
extend: {
|
|
259
|
+
spacing: {
|
|
260
|
+
xs: 4,
|
|
261
|
+
sm: "8px",
|
|
262
|
+
md: 16,
|
|
263
|
+
lg: "2rem", // 2rem = 32px
|
|
264
|
+
},
|
|
265
|
+
},
|
|
266
|
+
},
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
const theme = getCustomTheme();
|
|
270
|
+
expect(theme.spacing).toEqual({
|
|
271
|
+
xs: 4,
|
|
272
|
+
sm: 8,
|
|
273
|
+
md: 16,
|
|
274
|
+
lg: 32,
|
|
275
|
+
});
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
it("should use custom fontSize in parsing", () => {
|
|
279
|
+
setConfig({
|
|
280
|
+
theme: {
|
|
281
|
+
extend: {
|
|
282
|
+
fontSize: {
|
|
283
|
+
tiny: 10,
|
|
284
|
+
},
|
|
285
|
+
},
|
|
286
|
+
},
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
const result = tw`text-tiny`;
|
|
290
|
+
expect(result?.style).toEqual({
|
|
291
|
+
fontSize: 10,
|
|
292
|
+
});
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
it("should use custom spacing in parsing", () => {
|
|
296
|
+
setConfig({
|
|
297
|
+
theme: {
|
|
298
|
+
extend: {
|
|
299
|
+
spacing: {
|
|
300
|
+
xs: 4,
|
|
301
|
+
},
|
|
302
|
+
},
|
|
303
|
+
},
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
const result = tw`m-xs p-xs`;
|
|
307
|
+
expect(result?.style).toEqual({
|
|
308
|
+
margin: 4,
|
|
309
|
+
padding: 4,
|
|
310
|
+
});
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
it("should reset fontSize and spacing when config is cleared", () => {
|
|
314
|
+
setConfig({
|
|
315
|
+
theme: {
|
|
316
|
+
extend: {
|
|
317
|
+
fontSize: { tiny: 10 },
|
|
318
|
+
spacing: { xs: 4 },
|
|
319
|
+
},
|
|
320
|
+
},
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
let theme = getCustomTheme();
|
|
324
|
+
expect(theme.fontSize).toEqual({ tiny: 10 });
|
|
325
|
+
expect(theme.spacing).toEqual({ xs: 4 });
|
|
326
|
+
|
|
327
|
+
setConfig({});
|
|
328
|
+
|
|
329
|
+
theme = getCustomTheme();
|
|
330
|
+
expect(theme.fontSize).toEqual({});
|
|
331
|
+
expect(theme.spacing).toEqual({});
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
it("should handle all theme extensions together", () => {
|
|
335
|
+
setConfig({
|
|
336
|
+
theme: {
|
|
337
|
+
extend: {
|
|
338
|
+
colors: { primary: "#007AFF" },
|
|
339
|
+
fontFamily: { display: "Inter" },
|
|
340
|
+
fontSize: { tiny: 10 },
|
|
341
|
+
spacing: { xs: 4 },
|
|
342
|
+
},
|
|
343
|
+
},
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
const theme = getCustomTheme();
|
|
347
|
+
expect(theme.colors).toEqual({ primary: "#007AFF" });
|
|
348
|
+
expect(theme.fontFamily).toEqual({ display: "Inter" });
|
|
349
|
+
expect(theme.fontSize).toEqual({ tiny: 10 });
|
|
350
|
+
expect(theme.spacing).toEqual({ xs: 4 });
|
|
351
|
+
|
|
352
|
+
// Test that parsing uses all of them
|
|
353
|
+
const result = tw`bg-primary font-display text-tiny m-xs`;
|
|
354
|
+
expect(result?.style).toEqual({
|
|
355
|
+
backgroundColor: "#007AFF",
|
|
356
|
+
fontFamily: "Inter",
|
|
357
|
+
fontSize: 10,
|
|
358
|
+
margin: 4,
|
|
359
|
+
});
|
|
360
|
+
});
|
|
212
361
|
});
|
|
213
362
|
|
|
214
363
|
describe("cache", () => {
|
package/src/runtime.ts
CHANGED
|
@@ -12,12 +12,19 @@ export type RuntimeConfig = {
|
|
|
12
12
|
extend?: {
|
|
13
13
|
colors?: Record<string, string | Record<string, string>>;
|
|
14
14
|
fontFamily?: Record<string, string | string[]>;
|
|
15
|
+
fontSize?: Record<string, string | number>;
|
|
16
|
+
spacing?: Record<string, string | number>;
|
|
15
17
|
};
|
|
16
18
|
};
|
|
17
19
|
};
|
|
18
20
|
|
|
19
21
|
// Global custom theme configuration
|
|
20
|
-
const globalCustomTheme: CustomTheme = {
|
|
22
|
+
const globalCustomTheme: CustomTheme = {
|
|
23
|
+
colors: {},
|
|
24
|
+
fontFamily: {},
|
|
25
|
+
fontSize: {},
|
|
26
|
+
spacing: {},
|
|
27
|
+
};
|
|
21
28
|
|
|
22
29
|
// Simple memoization cache
|
|
23
30
|
const styleCache = new Map<string, TwStyle>();
|
|
@@ -72,6 +79,51 @@ export function setConfig(config: RuntimeConfig): void {
|
|
|
72
79
|
globalCustomTheme.fontFamily = {};
|
|
73
80
|
}
|
|
74
81
|
|
|
82
|
+
// Extract custom fontSize
|
|
83
|
+
if (config.theme?.extend?.fontSize) {
|
|
84
|
+
const fontSizeResult: Record<string, number> = {};
|
|
85
|
+
for (const [key, value] of Object.entries(config.theme.extend.fontSize)) {
|
|
86
|
+
if (typeof value === "number") {
|
|
87
|
+
fontSizeResult[key] = value;
|
|
88
|
+
} else if (typeof value === "string") {
|
|
89
|
+
// Parse string values like "18px" or "18" to number
|
|
90
|
+
const parsed = parseFloat(value.replace(/px$/, ""));
|
|
91
|
+
if (!isNaN(parsed)) {
|
|
92
|
+
fontSizeResult[key] = parsed;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
globalCustomTheme.fontSize = fontSizeResult;
|
|
97
|
+
} else {
|
|
98
|
+
globalCustomTheme.fontSize = {};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Extract custom spacing
|
|
102
|
+
if (config.theme?.extend?.spacing) {
|
|
103
|
+
const spacingResult: Record<string, number> = {};
|
|
104
|
+
for (const [key, value] of Object.entries(config.theme.extend.spacing)) {
|
|
105
|
+
if (typeof value === "number") {
|
|
106
|
+
spacingResult[key] = value;
|
|
107
|
+
} else if (typeof value === "string") {
|
|
108
|
+
// Parse string values: "18rem" -> 288, "16px" -> 16, "16" -> 16
|
|
109
|
+
let parsed: number;
|
|
110
|
+
if (value.endsWith("rem")) {
|
|
111
|
+
// Convert rem to px (1rem = 16px)
|
|
112
|
+
parsed = parseFloat(value.replace(/rem$/, "")) * 16;
|
|
113
|
+
} else {
|
|
114
|
+
// Parse px or unitless values
|
|
115
|
+
parsed = parseFloat(value.replace(/px$/, ""));
|
|
116
|
+
}
|
|
117
|
+
if (!isNaN(parsed)) {
|
|
118
|
+
spacingResult[key] = parsed;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
globalCustomTheme.spacing = spacingResult;
|
|
123
|
+
} else {
|
|
124
|
+
globalCustomTheme.spacing = {};
|
|
125
|
+
}
|
|
126
|
+
|
|
75
127
|
// Clear cache when config changes
|
|
76
128
|
styleCache.clear();
|
|
77
129
|
}
|