@mgcrea/react-native-tailwind 0.13.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 +33 -30
- 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 +439 -46
- package/dist/babel/plugin/state.d.ts +4 -0
- package/dist/babel/plugin/state.ts +8 -0
- package/dist/babel/plugin/visitors/className.test.ts +313 -0
- package/dist/babel/plugin/visitors/className.ts +36 -8
- package/dist/babel/plugin/visitors/imports.ts +16 -1
- package/dist/babel/plugin/visitors/program.ts +19 -2
- package/dist/babel/plugin/visitors/tw.test.ts +151 -0
- package/dist/babel/utils/directionalModifierProcessing.d.ts +34 -0
- package/dist/babel/utils/directionalModifierProcessing.ts +99 -0
- package/dist/babel/utils/styleInjection.d.ts +16 -0
- package/dist/babel/utils/styleInjection.ts +138 -7
- package/dist/babel/utils/twProcessing.d.ts +2 -0
- package/dist/babel/utils/twProcessing.ts +92 -3
- package/dist/parser/borders.js +1 -1
- package/dist/parser/borders.test.js +1 -1
- package/dist/parser/index.d.ts +3 -2
- 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/modifiers.d.ts +32 -2
- package/dist/parser/modifiers.js +1 -1
- package/dist/parser/modifiers.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 +4 -2
- 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/parser/typography.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 +6 -6
- package/src/babel/config-loader.test.ts +75 -21
- package/src/babel/config-loader.ts +100 -2
- package/src/babel/plugin/state.ts +8 -0
- package/src/babel/plugin/visitors/className.test.ts +313 -0
- package/src/babel/plugin/visitors/className.ts +36 -8
- package/src/babel/plugin/visitors/imports.ts +16 -1
- package/src/babel/plugin/visitors/program.ts +19 -2
- package/src/babel/plugin/visitors/tw.test.ts +151 -0
- package/src/babel/utils/directionalModifierProcessing.ts +99 -0
- package/src/babel/utils/styleInjection.ts +138 -7
- package/src/babel/utils/twProcessing.ts +92 -3
- package/src/parser/borders.test.ts +104 -0
- package/src/parser/borders.ts +50 -7
- package/src/parser/index.ts +8 -5
- package/src/parser/layout.test.ts +168 -0
- package/src/parser/layout.ts +107 -8
- package/src/parser/modifiers.test.ts +206 -0
- package/src/parser/modifiers.ts +62 -3
- package/src/parser/sizing.test.ts +56 -0
- package/src/parser/sizing.ts +20 -15
- package/src/parser/spacing.test.ts +123 -0
- package/src/parser/spacing.ts +30 -15
- package/src/parser/transforms.test.ts +57 -0
- package/src/parser/transforms.ts +7 -3
- package/src/parser/typography.test.ts +8 -0
- package/src/parser/typography.ts +4 -0
- package/src/runtime.test.ts +149 -0
- package/src/runtime.ts +53 -1
|
@@ -254,3 +254,59 @@ describe("parseSizing - comprehensive coverage", () => {
|
|
|
254
254
|
expect(parseSizing("h-full")).toEqual({ height: "100%" });
|
|
255
255
|
});
|
|
256
256
|
});
|
|
257
|
+
|
|
258
|
+
describe("parseSizing - custom spacing", () => {
|
|
259
|
+
const customSpacing = {
|
|
260
|
+
xs: 4,
|
|
261
|
+
sm: 8,
|
|
262
|
+
md: 16,
|
|
263
|
+
lg: 32,
|
|
264
|
+
xl: 64,
|
|
265
|
+
"4": 20, // Override default (16)
|
|
266
|
+
};
|
|
267
|
+
|
|
268
|
+
it("should support custom spacing values for width", () => {
|
|
269
|
+
expect(parseSizing("w-xs", customSpacing)).toEqual({ width: 4 });
|
|
270
|
+
expect(parseSizing("w-sm", customSpacing)).toEqual({ width: 8 });
|
|
271
|
+
expect(parseSizing("w-lg", customSpacing)).toEqual({ width: 32 });
|
|
272
|
+
expect(parseSizing("w-xl", customSpacing)).toEqual({ width: 64 });
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
it("should support custom spacing values for height", () => {
|
|
276
|
+
expect(parseSizing("h-xs", customSpacing)).toEqual({ height: 4 });
|
|
277
|
+
expect(parseSizing("h-md", customSpacing)).toEqual({ height: 16 });
|
|
278
|
+
expect(parseSizing("h-xl", customSpacing)).toEqual({ height: 64 });
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
it("should support custom spacing values for min/max dimensions", () => {
|
|
282
|
+
expect(parseSizing("min-w-sm", customSpacing)).toEqual({ minWidth: 8 });
|
|
283
|
+
expect(parseSizing("min-h-lg", customSpacing)).toEqual({ minHeight: 32 });
|
|
284
|
+
expect(parseSizing("max-w-xl", customSpacing)).toEqual({ maxWidth: 64 });
|
|
285
|
+
expect(parseSizing("max-h-md", customSpacing)).toEqual({ maxHeight: 16 });
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
it("should allow custom spacing to override preset values", () => {
|
|
289
|
+
expect(parseSizing("w-4", customSpacing)).toEqual({ width: 20 }); // Custom 20, not default 16
|
|
290
|
+
expect(parseSizing("h-4", customSpacing)).toEqual({ height: 20 }); // Custom 20, not default 16
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
it("should prefer arbitrary values over custom spacing", () => {
|
|
294
|
+
expect(parseSizing("w-[24px]", customSpacing)).toEqual({ width: 24 }); // Arbitrary wins
|
|
295
|
+
expect(parseSizing("h-[50]", customSpacing)).toEqual({ height: 50 }); // Arbitrary wins
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
it("should fall back to preset scale for unknown custom keys", () => {
|
|
299
|
+
expect(parseSizing("w-8", customSpacing)).toEqual({ width: 32 }); // Not overridden, uses preset
|
|
300
|
+
expect(parseSizing("h-12", customSpacing)).toEqual({ height: 48 }); // Not overridden, uses preset
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
it("should preserve percentage values with custom spacing", () => {
|
|
304
|
+
expect(parseSizing("w-full", customSpacing)).toEqual({ width: "100%" });
|
|
305
|
+
expect(parseSizing("h-1/2", customSpacing)).toEqual({ height: "50%" });
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
it("should work without custom spacing (backward compatible)", () => {
|
|
309
|
+
expect(parseSizing("w-4")).toEqual({ width: 16 }); // Default behavior
|
|
310
|
+
expect(parseSizing("h-8")).toEqual({ height: 32 }); // Default behavior
|
|
311
|
+
});
|
|
312
|
+
});
|
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
|
}
|
|
@@ -349,3 +349,126 @@ describe("parseSpacing - comprehensive coverage", () => {
|
|
|
349
349
|
expect(parseSpacing("pl-[50px]")).toEqual({ paddingLeft: 50 });
|
|
350
350
|
});
|
|
351
351
|
});
|
|
352
|
+
|
|
353
|
+
describe("parseSpacing - logical margin (RTL-aware)", () => {
|
|
354
|
+
it("should parse margin start", () => {
|
|
355
|
+
expect(parseSpacing("ms-0")).toEqual({ marginStart: 0 });
|
|
356
|
+
expect(parseSpacing("ms-4")).toEqual({ marginStart: 16 });
|
|
357
|
+
expect(parseSpacing("ms-8")).toEqual({ marginStart: 32 });
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
it("should parse margin end", () => {
|
|
361
|
+
expect(parseSpacing("me-0")).toEqual({ marginEnd: 0 });
|
|
362
|
+
expect(parseSpacing("me-4")).toEqual({ marginEnd: 16 });
|
|
363
|
+
expect(parseSpacing("me-8")).toEqual({ marginEnd: 32 });
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
it("should parse margin start/end with fractional values", () => {
|
|
367
|
+
expect(parseSpacing("ms-0.5")).toEqual({ marginStart: 2 });
|
|
368
|
+
expect(parseSpacing("me-1.5")).toEqual({ marginEnd: 6 });
|
|
369
|
+
expect(parseSpacing("ms-2.5")).toEqual({ marginStart: 10 });
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
it("should parse margin start/end with arbitrary values", () => {
|
|
373
|
+
expect(parseSpacing("ms-[16px]")).toEqual({ marginStart: 16 });
|
|
374
|
+
expect(parseSpacing("ms-[16]")).toEqual({ marginStart: 16 });
|
|
375
|
+
expect(parseSpacing("me-[24px]")).toEqual({ marginEnd: 24 });
|
|
376
|
+
expect(parseSpacing("me-[24]")).toEqual({ marginEnd: 24 });
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
it("should parse negative margin start/end", () => {
|
|
380
|
+
expect(parseSpacing("-ms-4")).toEqual({ marginStart: -16 });
|
|
381
|
+
expect(parseSpacing("-me-4")).toEqual({ marginEnd: -16 });
|
|
382
|
+
expect(parseSpacing("-ms-8")).toEqual({ marginStart: -32 });
|
|
383
|
+
expect(parseSpacing("-me-8")).toEqual({ marginEnd: -32 });
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
it("should parse negative margin start/end with arbitrary values", () => {
|
|
387
|
+
expect(parseSpacing("-ms-[16px]")).toEqual({ marginStart: -16 });
|
|
388
|
+
expect(parseSpacing("-me-[24]")).toEqual({ marginEnd: -24 });
|
|
389
|
+
});
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
describe("parseSpacing - logical padding (RTL-aware)", () => {
|
|
393
|
+
it("should parse padding start", () => {
|
|
394
|
+
expect(parseSpacing("ps-0")).toEqual({ paddingStart: 0 });
|
|
395
|
+
expect(parseSpacing("ps-4")).toEqual({ paddingStart: 16 });
|
|
396
|
+
expect(parseSpacing("ps-8")).toEqual({ paddingStart: 32 });
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
it("should parse padding end", () => {
|
|
400
|
+
expect(parseSpacing("pe-0")).toEqual({ paddingEnd: 0 });
|
|
401
|
+
expect(parseSpacing("pe-4")).toEqual({ paddingEnd: 16 });
|
|
402
|
+
expect(parseSpacing("pe-8")).toEqual({ paddingEnd: 32 });
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
it("should parse padding start/end with fractional values", () => {
|
|
406
|
+
expect(parseSpacing("ps-0.5")).toEqual({ paddingStart: 2 });
|
|
407
|
+
expect(parseSpacing("pe-1.5")).toEqual({ paddingEnd: 6 });
|
|
408
|
+
expect(parseSpacing("ps-2.5")).toEqual({ paddingStart: 10 });
|
|
409
|
+
});
|
|
410
|
+
|
|
411
|
+
it("should parse padding start/end with arbitrary values", () => {
|
|
412
|
+
expect(parseSpacing("ps-[16px]")).toEqual({ paddingStart: 16 });
|
|
413
|
+
expect(parseSpacing("ps-[16]")).toEqual({ paddingStart: 16 });
|
|
414
|
+
expect(parseSpacing("pe-[24px]")).toEqual({ paddingEnd: 24 });
|
|
415
|
+
expect(parseSpacing("pe-[24]")).toEqual({ paddingEnd: 24 });
|
|
416
|
+
});
|
|
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
|
@@ -69,44 +69,51 @@ function parseArbitrarySpacing(value: string): number | null {
|
|
|
69
69
|
|
|
70
70
|
/**
|
|
71
71
|
* Parse spacing classes (margin, padding, gap)
|
|
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]
|
|
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 {
|
|
75
|
-
//
|
|
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
|
+
|
|
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)
|
|
83
|
+
const marginMatch = cls.match(/^(-?)m([xytrblse]?)-(.+)$/);
|
|
78
84
|
if (marginMatch) {
|
|
79
85
|
const [, negativePrefix, dir, valueStr] = marginMatch;
|
|
80
86
|
const isNegative = negativePrefix === "-";
|
|
81
87
|
|
|
82
|
-
// Try arbitrary value first
|
|
88
|
+
// Try arbitrary value first (highest priority)
|
|
83
89
|
const arbitraryValue = parseArbitrarySpacing(valueStr);
|
|
84
90
|
if (arbitraryValue !== null) {
|
|
85
91
|
const finalValue = isNegative ? -arbitraryValue : arbitraryValue;
|
|
86
92
|
return getMarginStyle(dir, finalValue);
|
|
87
93
|
}
|
|
88
94
|
|
|
89
|
-
// Try
|
|
90
|
-
const scaleValue =
|
|
95
|
+
// Try spacing scale (includes custom spacing)
|
|
96
|
+
const scaleValue = spacingMap[valueStr];
|
|
91
97
|
if (scaleValue !== undefined) {
|
|
92
98
|
const finalValue = isNegative ? -scaleValue : scaleValue;
|
|
93
99
|
return getMarginStyle(dir, finalValue);
|
|
94
100
|
}
|
|
95
101
|
}
|
|
96
102
|
|
|
97
|
-
// Padding: p-4, px-2, pt-8, p-[16px], etc.
|
|
98
|
-
|
|
103
|
+
// Padding: p-4, px-2, pt-8, ps-4, pe-2, p-[16px], etc.
|
|
104
|
+
// s = start (RTL-aware), e = end (RTL-aware)
|
|
105
|
+
const paddingMatch = cls.match(/^p([xytrblse]?)-(.+)$/);
|
|
99
106
|
if (paddingMatch) {
|
|
100
107
|
const [, dir, valueStr] = paddingMatch;
|
|
101
108
|
|
|
102
|
-
// Try arbitrary value first
|
|
109
|
+
// Try arbitrary value first (highest priority)
|
|
103
110
|
const arbitraryValue = parseArbitrarySpacing(valueStr);
|
|
104
111
|
if (arbitraryValue !== null) {
|
|
105
112
|
return getPaddingStyle(dir, arbitraryValue);
|
|
106
113
|
}
|
|
107
114
|
|
|
108
|
-
// Try
|
|
109
|
-
const scaleValue =
|
|
115
|
+
// Try spacing scale (includes custom spacing)
|
|
116
|
+
const scaleValue = spacingMap[valueStr];
|
|
110
117
|
if (scaleValue !== undefined) {
|
|
111
118
|
return getPaddingStyle(dir, scaleValue);
|
|
112
119
|
}
|
|
@@ -117,14 +124,14 @@ export function parseSpacing(cls: string): StyleObject | null {
|
|
|
117
124
|
if (gapMatch) {
|
|
118
125
|
const valueStr = gapMatch[1];
|
|
119
126
|
|
|
120
|
-
// Try arbitrary value first
|
|
127
|
+
// Try arbitrary value first (highest priority)
|
|
121
128
|
const arbitraryValue = parseArbitrarySpacing(valueStr);
|
|
122
129
|
if (arbitraryValue !== null) {
|
|
123
130
|
return { gap: arbitraryValue };
|
|
124
131
|
}
|
|
125
132
|
|
|
126
|
-
// Try
|
|
127
|
-
const scaleValue =
|
|
133
|
+
// Try spacing scale (includes custom spacing)
|
|
134
|
+
const scaleValue = spacingMap[valueStr];
|
|
128
135
|
if (scaleValue !== undefined) {
|
|
129
136
|
return { gap: scaleValue };
|
|
130
137
|
}
|
|
@@ -152,6 +159,10 @@ function getMarginStyle(dir: string, value: number): StyleObject {
|
|
|
152
159
|
return { marginBottom: value };
|
|
153
160
|
case "l":
|
|
154
161
|
return { marginLeft: value };
|
|
162
|
+
case "s":
|
|
163
|
+
return { marginStart: value };
|
|
164
|
+
case "e":
|
|
165
|
+
return { marginEnd: value };
|
|
155
166
|
default:
|
|
156
167
|
return {};
|
|
157
168
|
}
|
|
@@ -176,6 +187,10 @@ function getPaddingStyle(dir: string, value: number): StyleObject {
|
|
|
176
187
|
return { paddingBottom: value };
|
|
177
188
|
case "l":
|
|
178
189
|
return { paddingLeft: value };
|
|
190
|
+
case "s":
|
|
191
|
+
return { paddingStart: value };
|
|
192
|
+
case "e":
|
|
193
|
+
return { paddingEnd: value };
|
|
179
194
|
default:
|
|
180
195
|
return {};
|
|
181
196
|
}
|
|
@@ -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 }] };
|
|
@@ -91,6 +91,14 @@ describe("parseTypography - text alignment", () => {
|
|
|
91
91
|
expect(parseTypography("text-right")).toEqual({ textAlign: "right" });
|
|
92
92
|
expect(parseTypography("text-justify")).toEqual({ textAlign: "justify" });
|
|
93
93
|
});
|
|
94
|
+
|
|
95
|
+
it("should not parse text-start/text-end (handled via directional modifier expansion)", () => {
|
|
96
|
+
// text-start/text-end are now handled by splitModifierClasses() in modifiers.ts
|
|
97
|
+
// They expand to ltr:text-left rtl:text-right and ltr:text-right rtl:text-left
|
|
98
|
+
// respectively for true RTL support
|
|
99
|
+
expect(parseTypography("text-start")).toBeNull();
|
|
100
|
+
expect(parseTypography("text-end")).toBeNull();
|
|
101
|
+
});
|
|
94
102
|
});
|
|
95
103
|
|
|
96
104
|
describe("parseTypography - text decoration", () => {
|
package/src/parser/typography.ts
CHANGED
|
@@ -58,6 +58,10 @@ const FONT_STYLE_MAP: Record<string, StyleObject> = {
|
|
|
58
58
|
};
|
|
59
59
|
|
|
60
60
|
// Text alignment utilities
|
|
61
|
+
// Note: text-start and text-end are handled via automatic expansion to directional modifiers
|
|
62
|
+
// in splitModifierClasses() for true RTL support:
|
|
63
|
+
// text-start -> ltr:text-left rtl:text-right
|
|
64
|
+
// text-end -> ltr:text-right rtl:text-left
|
|
61
65
|
const TEXT_ALIGN_MAP: Record<string, StyleObject> = {
|
|
62
66
|
"text-left": { textAlign: "left" },
|
|
63
67
|
"text-center": { textAlign: "center" },
|