@mgcrea/react-native-tailwind 0.15.4 → 0.15.6
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/dist/babel/index.cjs +93 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/parser/colors.js +1 -1
- package/dist/parser/colors.test.js +1 -1
- package/dist/parser/index.d.ts +1 -0
- package/dist/parser/index.js +1 -1
- package/dist/parser/outline.d.ts +9 -0
- package/dist/parser/outline.js +1 -0
- package/dist/parser/outline.test.js +1 -0
- package/dist/parser/shadows.d.ts +1 -1
- package/dist/parser/shadows.test.js +1 -1
- package/dist/parser/sizing.js +1 -1
- package/dist/parser/sizing.test.js +1 -1
- package/dist/parser/spacing.js +1 -1
- package/dist/parser/spacing.test.js +1 -1
- package/dist/runtime.cjs +1 -1
- package/dist/runtime.cjs.map +4 -4
- package/dist/runtime.js +1 -1
- package/dist/runtime.js.map +4 -4
- package/package.json +1 -1
- package/src/index.ts +1 -0
- package/src/parser/colors.test.ts +1 -1
- package/src/parser/colors.ts +19 -0
- package/src/parser/index.ts +3 -0
- package/src/parser/outline.test.ts +57 -0
- package/src/parser/outline.ts +101 -0
- package/src/parser/shadows.test.ts +1 -1
- package/src/parser/shadows.ts +1 -1
- package/src/parser/sizing.test.ts +92 -0
- package/src/parser/sizing.ts +29 -0
- package/src/parser/spacing.test.ts +24 -0
- package/src/parser/spacing.ts +1 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mgcrea/react-native-tailwind",
|
|
3
|
-
"version": "0.15.
|
|
3
|
+
"version": "0.15.6",
|
|
4
4
|
"description": "Compile-time Tailwind CSS for React Native with zero runtime overhead",
|
|
5
5
|
"author": "Olivier Louvignes <olivier@mgcrea.io> (https://github.com/mgcrea)",
|
|
6
6
|
"homepage": "https://github.com/mgcrea/react-native-tailwind#readme",
|
package/src/index.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { describe, expect, it } from "vitest";
|
|
2
|
-
import { COLORS, parseColor } from "./colors";
|
|
3
2
|
import { applyOpacity } from "../utils/colorUtils";
|
|
3
|
+
import { COLORS, parseColor } from "./colors";
|
|
4
4
|
|
|
5
5
|
describe("COLORS", () => {
|
|
6
6
|
it("should export complete color palette", () => {
|
package/src/parser/colors.ts
CHANGED
|
@@ -116,6 +116,25 @@ export function parseColor(cls: string, customColors?: Record<string, string>):
|
|
|
116
116
|
}
|
|
117
117
|
}
|
|
118
118
|
|
|
119
|
+
// Outline color: outline-blue-500, outline-blue-500/50, outline-[#ff0000]/80
|
|
120
|
+
if (cls.startsWith("outline-") && !cls.match(/^outline-[0-9]/) && !cls.startsWith("outline-offset-")) {
|
|
121
|
+
const colorKey = cls.substring(8); // "outline-".length = 8
|
|
122
|
+
|
|
123
|
+
// Skip outline-style values
|
|
124
|
+
if (["solid", "dashed", "dotted", "none"].includes(colorKey)) {
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Skip arbitrary values that don't look like colors (e.g., outline-[3px] is width)
|
|
129
|
+
if (colorKey.startsWith("[") && !colorKey.startsWith("[#")) {
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
const color = parseColorWithOpacity(colorKey);
|
|
133
|
+
if (color) {
|
|
134
|
+
return { outlineColor: color };
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
119
138
|
// Directional border colors: border-t-red-500, border-l-blue-500/50, border-r-[#ff0000]
|
|
120
139
|
const dirBorderMatch = cls.match(/^border-([trblxy])-(.+)$/);
|
|
121
140
|
if (dirBorderMatch) {
|
package/src/parser/index.ts
CHANGED
|
@@ -9,6 +9,7 @@ import { parseAspectRatio } from "./aspectRatio";
|
|
|
9
9
|
import { parseBorder } from "./borders";
|
|
10
10
|
import { parseColor } from "./colors";
|
|
11
11
|
import { parseLayout } from "./layout";
|
|
12
|
+
import { parseOutline } from "./outline";
|
|
12
13
|
import { parseShadow } from "./shadows";
|
|
13
14
|
import { parseSizing } from "./sizing";
|
|
14
15
|
import { parseSpacing } from "./spacing";
|
|
@@ -56,6 +57,7 @@ export function parseClass(cls: string, customTheme?: CustomTheme): StyleObject
|
|
|
56
57
|
const parsers: Array<(cls: string) => StyleObject | null> = [
|
|
57
58
|
(cls: string) => parseSpacing(cls, customTheme?.spacing),
|
|
58
59
|
(cls: string) => parseBorder(cls, customTheme?.colors),
|
|
60
|
+
parseOutline,
|
|
59
61
|
(cls: string) => parseColor(cls, customTheme?.colors),
|
|
60
62
|
(cls: string) => parseLayout(cls, customTheme?.spacing),
|
|
61
63
|
(cls: string) => parseTypography(cls, customTheme?.fontFamily, customTheme?.fontSize),
|
|
@@ -86,6 +88,7 @@ export { parseAspectRatio } from "./aspectRatio";
|
|
|
86
88
|
export { parseBorder } from "./borders";
|
|
87
89
|
export { parseColor } from "./colors";
|
|
88
90
|
export { parseLayout } from "./layout";
|
|
91
|
+
export { parseOutline } from "./outline";
|
|
89
92
|
export { parsePlaceholderClass, parsePlaceholderClasses } from "./placeholder";
|
|
90
93
|
export { parseShadow } from "./shadows";
|
|
91
94
|
export { parseSizing } from "./sizing";
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { parseOutline } from "./outline";
|
|
3
|
+
|
|
4
|
+
describe("parseOutline", () => {
|
|
5
|
+
it("should parse outline shorthand", () => {
|
|
6
|
+
expect(parseOutline("outline")).toEqual({
|
|
7
|
+
outlineWidth: 1,
|
|
8
|
+
outlineStyle: "solid",
|
|
9
|
+
});
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
it("should parse outline-none", () => {
|
|
13
|
+
expect(parseOutline("outline-none")).toEqual({ outlineWidth: 0 });
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it("should parse outline width with preset values", () => {
|
|
17
|
+
expect(parseOutline("outline-0")).toEqual({ outlineWidth: 0 });
|
|
18
|
+
expect(parseOutline("outline-2")).toEqual({ outlineWidth: 2 });
|
|
19
|
+
expect(parseOutline("outline-4")).toEqual({ outlineWidth: 4 });
|
|
20
|
+
expect(parseOutline("outline-8")).toEqual({ outlineWidth: 8 });
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it("should parse outline width with arbitrary values", () => {
|
|
24
|
+
expect(parseOutline("outline-[5px]")).toEqual({ outlineWidth: 5 });
|
|
25
|
+
expect(parseOutline("outline-[10]")).toEqual({ outlineWidth: 10 });
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it("should parse outline style", () => {
|
|
29
|
+
expect(parseOutline("outline-solid")).toEqual({ outlineStyle: "solid" });
|
|
30
|
+
expect(parseOutline("outline-dashed")).toEqual({ outlineStyle: "dashed" });
|
|
31
|
+
expect(parseOutline("outline-dotted")).toEqual({ outlineStyle: "dotted" });
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it("should parse outline offset with preset values", () => {
|
|
35
|
+
expect(parseOutline("outline-offset-0")).toEqual({ outlineOffset: 0 });
|
|
36
|
+
expect(parseOutline("outline-offset-2")).toEqual({ outlineOffset: 2 });
|
|
37
|
+
expect(parseOutline("outline-offset-4")).toEqual({ outlineOffset: 4 });
|
|
38
|
+
expect(parseOutline("outline-offset-8")).toEqual({ outlineOffset: 8 });
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it("should parse outline offset with arbitrary values", () => {
|
|
42
|
+
expect(parseOutline("outline-offset-[3px]")).toEqual({ outlineOffset: 3 });
|
|
43
|
+
expect(parseOutline("outline-offset-[5]")).toEqual({ outlineOffset: 5 });
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it("should return null for invalid outline values", () => {
|
|
47
|
+
expect(parseOutline("outline-invalid")).toBeNull();
|
|
48
|
+
expect(parseOutline("outline-3")).toBeNull(); // Not in scale
|
|
49
|
+
expect(parseOutline("outline-offset-3")).toBeNull(); // Not in scale
|
|
50
|
+
expect(parseOutline("outline-[5rem]")).toBeNull(); // Unsupported unit
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it("should return null for outline colors (handled by parseColor)", () => {
|
|
54
|
+
expect(parseOutline("outline-red-500")).toBeNull();
|
|
55
|
+
expect(parseOutline("outline-[#ff0000]")).toBeNull();
|
|
56
|
+
});
|
|
57
|
+
});
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Outline utilities (outline width, style, offset)
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { StyleObject } from "../types";
|
|
6
|
+
import { BORDER_WIDTH_SCALE } from "./borders";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Parse arbitrary outline width/offset value: [8px], [4]
|
|
10
|
+
* Returns number for px values, null for unsupported formats
|
|
11
|
+
*/
|
|
12
|
+
function parseArbitraryOutlineValue(value: string): number | null {
|
|
13
|
+
// Match: [8px] or [8] (pixels only)
|
|
14
|
+
const pxMatch = value.match(/^\[(\d+)(?:px)?\]$/);
|
|
15
|
+
if (pxMatch) {
|
|
16
|
+
return parseInt(pxMatch[1], 10);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Warn about unsupported formats
|
|
20
|
+
if (value.startsWith("[") && value.endsWith("]")) {
|
|
21
|
+
/* v8 ignore next 5 */
|
|
22
|
+
if (process.env.NODE_ENV !== "production") {
|
|
23
|
+
console.warn(
|
|
24
|
+
`[react-native-tailwind] Unsupported arbitrary outline value: ${value}. Only px values are supported (e.g., [8px] or [8]).`,
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Parse outline classes
|
|
35
|
+
* @param cls - The class name to parse
|
|
36
|
+
*/
|
|
37
|
+
export function parseOutline(cls: string): StyleObject | null {
|
|
38
|
+
// Shorthand: outline (width: 1, style: solid)
|
|
39
|
+
if (cls === "outline") {
|
|
40
|
+
return { outlineWidth: 1, outlineStyle: "solid" };
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Outline none
|
|
44
|
+
if (cls === "outline-none") {
|
|
45
|
+
return { outlineWidth: 0 };
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Outline style
|
|
49
|
+
if (cls === "outline-solid") return { outlineStyle: "solid" };
|
|
50
|
+
if (cls === "outline-dotted") return { outlineStyle: "dotted" };
|
|
51
|
+
if (cls === "outline-dashed") return { outlineStyle: "dashed" };
|
|
52
|
+
|
|
53
|
+
// Outline offset: outline-offset-2, outline-offset-[3px]
|
|
54
|
+
if (cls.startsWith("outline-offset-")) {
|
|
55
|
+
const valueStr = cls.substring(15); // "outline-offset-".length = 15
|
|
56
|
+
|
|
57
|
+
// Try arbitrary value first
|
|
58
|
+
if (valueStr.startsWith("[")) {
|
|
59
|
+
const arbitraryValue = parseArbitraryOutlineValue(valueStr);
|
|
60
|
+
if (arbitraryValue !== null) {
|
|
61
|
+
return { outlineOffset: arbitraryValue };
|
|
62
|
+
}
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Try preset scale (reuse border width scale for consistency with default Tailwind)
|
|
67
|
+
const scaleValue = BORDER_WIDTH_SCALE[valueStr];
|
|
68
|
+
if (scaleValue !== undefined) {
|
|
69
|
+
return { outlineOffset: scaleValue };
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Outline width: outline-0, outline-2, outline-[5px]
|
|
76
|
+
// Must handle potential collision with outline-red-500 (colors)
|
|
77
|
+
// Logic: if it matches width pattern, return width. If it looks like color, return null (let parseColor handle it)
|
|
78
|
+
|
|
79
|
+
const widthMatch = cls.match(/^outline-(\d+)$/);
|
|
80
|
+
if (widthMatch) {
|
|
81
|
+
const value = BORDER_WIDTH_SCALE[widthMatch[1]];
|
|
82
|
+
if (value !== undefined) {
|
|
83
|
+
return { outlineWidth: value };
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const arbMatch = cls.match(/^outline-(\[.+\])$/);
|
|
88
|
+
if (arbMatch) {
|
|
89
|
+
// Check if it's a color first? No, colors usually look like [#...] or [rgb(...)]
|
|
90
|
+
// parseArbitraryOutlineValue only accepts [123] or [123px]
|
|
91
|
+
// If it fails, it might be a color, so we return null
|
|
92
|
+
const arbitraryValue = parseArbitraryOutlineValue(arbMatch[1]);
|
|
93
|
+
if (arbitraryValue !== null) {
|
|
94
|
+
return { outlineWidth: arbitraryValue };
|
|
95
|
+
}
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// If it's outline-{color}, return null so parseColor (called later in index.ts) handles it
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { describe, expect, it } from "vitest";
|
|
2
|
-
import { SHADOW_COLORS, SHADOW_SCALE, parseShadow } from "./shadows";
|
|
3
2
|
import { applyOpacity } from "../utils/colorUtils";
|
|
3
|
+
import { SHADOW_COLORS, SHADOW_SCALE, parseShadow } from "./shadows";
|
|
4
4
|
|
|
5
5
|
describe("SHADOW_SCALE", () => {
|
|
6
6
|
it("should export complete shadow scale", () => {
|
package/src/parser/shadows.ts
CHANGED
|
@@ -253,6 +253,16 @@ describe("parseSizing - comprehensive coverage", () => {
|
|
|
253
253
|
expect(parseSizing("w-full")).toEqual({ width: "100%" });
|
|
254
254
|
expect(parseSizing("h-full")).toEqual({ height: "100%" });
|
|
255
255
|
});
|
|
256
|
+
|
|
257
|
+
it("should parse px (1 pixel) across all sizing utilities", () => {
|
|
258
|
+
expect(parseSizing("w-px")).toEqual({ width: 1 });
|
|
259
|
+
expect(parseSizing("h-px")).toEqual({ height: 1 });
|
|
260
|
+
expect(parseSizing("size-px")).toEqual({ width: 1, height: 1 });
|
|
261
|
+
expect(parseSizing("min-w-px")).toEqual({ minWidth: 1 });
|
|
262
|
+
expect(parseSizing("min-h-px")).toEqual({ minHeight: 1 });
|
|
263
|
+
expect(parseSizing("max-w-px")).toEqual({ maxWidth: 1 });
|
|
264
|
+
expect(parseSizing("max-h-px")).toEqual({ maxHeight: 1 });
|
|
265
|
+
});
|
|
256
266
|
});
|
|
257
267
|
|
|
258
268
|
describe("parseSizing - custom spacing", () => {
|
|
@@ -310,3 +320,85 @@ describe("parseSizing - custom spacing", () => {
|
|
|
310
320
|
expect(parseSizing("h-8")).toEqual({ height: 32 }); // Default behavior
|
|
311
321
|
});
|
|
312
322
|
});
|
|
323
|
+
|
|
324
|
+
describe("parseSizing - size (width + height)", () => {
|
|
325
|
+
it("should parse size with numeric values", () => {
|
|
326
|
+
expect(parseSizing("size-0")).toEqual({ width: 0, height: 0 });
|
|
327
|
+
expect(parseSizing("size-4")).toEqual({ width: 16, height: 16 });
|
|
328
|
+
expect(parseSizing("size-8")).toEqual({ width: 32, height: 32 });
|
|
329
|
+
expect(parseSizing("size-96")).toEqual({ width: 384, height: 384 });
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
it("should parse size with fractional values", () => {
|
|
333
|
+
expect(parseSizing("size-0.5")).toEqual({ width: 2, height: 2 });
|
|
334
|
+
expect(parseSizing("size-1.5")).toEqual({ width: 6, height: 6 });
|
|
335
|
+
expect(parseSizing("size-2.5")).toEqual({ width: 10, height: 10 });
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
it("should parse size with percentage values", () => {
|
|
339
|
+
expect(parseSizing("size-full")).toEqual({ width: "100%", height: "100%" });
|
|
340
|
+
expect(parseSizing("size-1/2")).toEqual({ width: "50%", height: "50%" });
|
|
341
|
+
expect(parseSizing("size-1/3")).toEqual({ width: "33.333333%", height: "33.333333%" });
|
|
342
|
+
expect(parseSizing("size-2/3")).toEqual({ width: "66.666667%", height: "66.666667%" });
|
|
343
|
+
expect(parseSizing("size-1/4")).toEqual({ width: "25%", height: "25%" });
|
|
344
|
+
expect(parseSizing("size-3/4")).toEqual({ width: "75%", height: "75%" });
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
it("should parse size with special values", () => {
|
|
348
|
+
expect(parseSizing("size-auto")).toEqual({ width: "auto", height: "auto" });
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
it("should parse size with arbitrary pixel values", () => {
|
|
352
|
+
expect(parseSizing("size-[123px]")).toEqual({ width: 123, height: 123 });
|
|
353
|
+
expect(parseSizing("size-[200]")).toEqual({ width: 200, height: 200 });
|
|
354
|
+
expect(parseSizing("size-[50px]")).toEqual({ width: 50, height: 50 });
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
it("should parse size with arbitrary percentage values", () => {
|
|
358
|
+
expect(parseSizing("size-[50%]")).toEqual({ width: "50%", height: "50%" });
|
|
359
|
+
expect(parseSizing("size-[33.333%]")).toEqual({ width: "33.333%", height: "33.333%" });
|
|
360
|
+
expect(parseSizing("size-[85%]")).toEqual({ width: "85%", height: "85%" });
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
it("should support custom spacing values for size", () => {
|
|
364
|
+
const customSpacing = { sm: 8, md: 16, lg: 32, xl: 64 };
|
|
365
|
+
expect(parseSizing("size-sm", customSpacing)).toEqual({ width: 8, height: 8 });
|
|
366
|
+
expect(parseSizing("size-md", customSpacing)).toEqual({ width: 16, height: 16 });
|
|
367
|
+
expect(parseSizing("size-lg", customSpacing)).toEqual({ width: 32, height: 32 });
|
|
368
|
+
expect(parseSizing("size-xl", customSpacing)).toEqual({ width: 64, height: 64 });
|
|
369
|
+
});
|
|
370
|
+
|
|
371
|
+
it("should allow custom spacing to override preset values", () => {
|
|
372
|
+
const customSpacing = { "4": 20 }; // Override default (16)
|
|
373
|
+
expect(parseSizing("size-4", customSpacing)).toEqual({ width: 20, height: 20 });
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
it("should prefer arbitrary values over custom spacing", () => {
|
|
377
|
+
expect(parseSizing("size-[24px]", { "4": 20 })).toEqual({ width: 24, height: 24 });
|
|
378
|
+
expect(parseSizing("size-[50]", { sm: 8 })).toEqual({ width: 50, height: 50 });
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
it("should handle all fractional percentage variants", () => {
|
|
382
|
+
expect(parseSizing("size-1/5")).toEqual({ width: "20%", height: "20%" });
|
|
383
|
+
expect(parseSizing("size-2/5")).toEqual({ width: "40%", height: "40%" });
|
|
384
|
+
expect(parseSizing("size-3/5")).toEqual({ width: "60%", height: "60%" });
|
|
385
|
+
expect(parseSizing("size-4/5")).toEqual({ width: "80%", height: "80%" });
|
|
386
|
+
expect(parseSizing("size-1/6")).toEqual({ width: "16.666667%", height: "16.666667%" });
|
|
387
|
+
expect(parseSizing("size-5/6")).toEqual({ width: "83.333333%", height: "83.333333%" });
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
it("should handle edge case size values", () => {
|
|
391
|
+
expect(parseSizing("size-0")).toEqual({ width: 0, height: 0 });
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
it("should return null for invalid size values", () => {
|
|
395
|
+
expect(parseSizing("size-invalid")).toBeNull();
|
|
396
|
+
expect(parseSizing("size-999")).toBeNull();
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
it("should return null for arbitrary values with unsupported units", () => {
|
|
400
|
+
expect(parseSizing("size-[16rem]")).toBeNull();
|
|
401
|
+
expect(parseSizing("size-[2em]")).toBeNull();
|
|
402
|
+
expect(parseSizing("size-[50vh]")).toBeNull();
|
|
403
|
+
});
|
|
404
|
+
});
|
package/src/parser/sizing.ts
CHANGED
|
@@ -7,6 +7,7 @@ import type { StyleObject } from "../types";
|
|
|
7
7
|
|
|
8
8
|
// Size scale (in pixels/percentages)
|
|
9
9
|
export const SIZE_SCALE: Record<string, number> = {
|
|
10
|
+
px: 1,
|
|
10
11
|
0: 0,
|
|
11
12
|
0.5: 2,
|
|
12
13
|
1: 4,
|
|
@@ -102,6 +103,34 @@ export function parseSizing(cls: string, customSpacing?: Record<string, number>)
|
|
|
102
103
|
// Merge custom spacing with defaults (custom takes precedence)
|
|
103
104
|
const sizeMap = customSpacing ? { ...SIZE_SCALE, ...customSpacing } : SIZE_SCALE;
|
|
104
105
|
|
|
106
|
+
// Size (both width AND height)
|
|
107
|
+
if (cls.startsWith("size-")) {
|
|
108
|
+
const sizeKey = cls.substring(5); // "size-".length = 5
|
|
109
|
+
|
|
110
|
+
// Arbitrary values: size-[123px], size-[50%] (highest priority)
|
|
111
|
+
const arbitrarySize = parseArbitrarySize(sizeKey);
|
|
112
|
+
if (arbitrarySize !== null) {
|
|
113
|
+
return { width: arbitrarySize, height: arbitrarySize };
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Percentage sizes: size-full, size-1/2, etc.
|
|
117
|
+
const percentage = SIZE_PERCENTAGES[sizeKey];
|
|
118
|
+
if (percentage) {
|
|
119
|
+
return { width: percentage, height: percentage };
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Numeric sizes: size-4, size-8, etc. (includes custom spacing)
|
|
123
|
+
const numericSize = sizeMap[sizeKey];
|
|
124
|
+
if (numericSize !== undefined) {
|
|
125
|
+
return { width: numericSize, height: numericSize };
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Special values
|
|
129
|
+
if (sizeKey === "auto") {
|
|
130
|
+
return { width: "auto", height: "auto" };
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
105
134
|
// Width
|
|
106
135
|
if (cls.startsWith("w-")) {
|
|
107
136
|
const sizeKey = cls.substring(2);
|
|
@@ -231,6 +231,30 @@ describe("parseSpacing - edge cases", () => {
|
|
|
231
231
|
expect(parseSpacing("gap-0")).toEqual({ gap: 0 });
|
|
232
232
|
});
|
|
233
233
|
|
|
234
|
+
it("should parse px (1 pixel) across all spacing utilities", () => {
|
|
235
|
+
expect(parseSpacing("m-px")).toEqual({ margin: 1 });
|
|
236
|
+
expect(parseSpacing("mx-px")).toEqual({ marginHorizontal: 1 });
|
|
237
|
+
expect(parseSpacing("my-px")).toEqual({ marginVertical: 1 });
|
|
238
|
+
expect(parseSpacing("mt-px")).toEqual({ marginTop: 1 });
|
|
239
|
+
expect(parseSpacing("mr-px")).toEqual({ marginRight: 1 });
|
|
240
|
+
expect(parseSpacing("mb-px")).toEqual({ marginBottom: 1 });
|
|
241
|
+
expect(parseSpacing("ml-px")).toEqual({ marginLeft: 1 });
|
|
242
|
+
expect(parseSpacing("ms-px")).toEqual({ marginStart: 1 });
|
|
243
|
+
expect(parseSpacing("me-px")).toEqual({ marginEnd: 1 });
|
|
244
|
+
expect(parseSpacing("p-px")).toEqual({ padding: 1 });
|
|
245
|
+
expect(parseSpacing("px-px")).toEqual({ paddingHorizontal: 1 });
|
|
246
|
+
expect(parseSpacing("py-px")).toEqual({ paddingVertical: 1 });
|
|
247
|
+
expect(parseSpacing("pt-px")).toEqual({ paddingTop: 1 });
|
|
248
|
+
expect(parseSpacing("pr-px")).toEqual({ paddingRight: 1 });
|
|
249
|
+
expect(parseSpacing("pb-px")).toEqual({ paddingBottom: 1 });
|
|
250
|
+
expect(parseSpacing("pl-px")).toEqual({ paddingLeft: 1 });
|
|
251
|
+
expect(parseSpacing("ps-px")).toEqual({ paddingStart: 1 });
|
|
252
|
+
expect(parseSpacing("pe-px")).toEqual({ paddingEnd: 1 });
|
|
253
|
+
expect(parseSpacing("gap-px")).toEqual({ gap: 1 });
|
|
254
|
+
expect(parseSpacing("-m-px")).toEqual({ margin: -1 });
|
|
255
|
+
expect(parseSpacing("-mt-px")).toEqual({ marginTop: -1 });
|
|
256
|
+
});
|
|
257
|
+
|
|
234
258
|
it("should not match partial class names", () => {
|
|
235
259
|
expect(parseSpacing("sm-4")).toBeNull();
|
|
236
260
|
expect(parseSpacing("margin-4")).toBeNull();
|