@mgcrea/react-native-tailwind 0.12.0 → 0.13.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 +29 -2014
- package/dist/babel/config-loader.d.ts +3 -0
- package/dist/babel/config-loader.test.ts +2 -2
- package/dist/babel/config-loader.ts +37 -2
- package/dist/babel/index.cjs +2855 -2434
- package/dist/babel/plugin/componentScope.d.ts +26 -0
- package/dist/babel/plugin/componentScope.ts +87 -0
- package/dist/babel/plugin/state.d.ts +119 -0
- package/dist/babel/plugin/state.ts +177 -0
- package/dist/babel/plugin/visitors/className.d.ts +11 -0
- package/{src/babel/plugin.test.ts → dist/babel/plugin/visitors/className.test.ts} +74 -674
- package/dist/babel/plugin/visitors/className.ts +624 -0
- package/dist/babel/plugin/visitors/className.windowDimensions.test.ts +406 -0
- package/dist/babel/plugin/visitors/imports.d.ts +11 -0
- package/dist/babel/plugin/visitors/imports.test.ts +88 -0
- package/dist/babel/plugin/visitors/imports.ts +101 -0
- package/dist/babel/plugin/visitors/program.d.ts +15 -0
- package/dist/babel/plugin/visitors/program.test.ts +325 -0
- package/dist/babel/plugin/visitors/program.ts +99 -0
- package/dist/babel/plugin/visitors/tw.d.ts +16 -0
- package/dist/babel/plugin/visitors/tw.test.ts +620 -0
- package/dist/babel/plugin/visitors/tw.ts +148 -0
- package/dist/babel/plugin.d.ts +3 -96
- package/dist/babel/plugin.test.ts +470 -0
- package/dist/babel/plugin.ts +28 -953
- package/dist/babel/utils/colorSchemeModifierProcessing.ts +11 -0
- package/dist/babel/utils/componentSupport.test.ts +20 -7
- package/dist/babel/utils/componentSupport.ts +2 -0
- package/dist/babel/utils/modifierProcessing.ts +21 -0
- package/dist/babel/utils/platformModifierProcessing.ts +11 -0
- package/dist/babel/utils/styleInjection.d.ts +15 -0
- package/dist/babel/utils/styleInjection.ts +172 -17
- package/dist/babel/utils/twProcessing.ts +11 -0
- package/dist/babel/utils/windowDimensionsProcessing.d.ts +56 -0
- package/dist/babel/utils/windowDimensionsProcessing.ts +121 -0
- package/dist/components/TouchableOpacity.d.ts +35 -0
- package/dist/components/TouchableOpacity.js +1 -0
- package/dist/components/index.d.ts +3 -0
- package/dist/components/index.js +1 -0
- package/dist/config/markers.d.ts +5 -0
- package/dist/config/markers.js +1 -0
- package/dist/index.d.ts +2 -5
- package/dist/index.js +1 -1
- package/dist/parser/borders.d.ts +3 -1
- package/dist/parser/borders.js +1 -1
- package/dist/parser/borders.test.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/layout.js +1 -1
- package/dist/parser/layout.test.js +1 -1
- package/dist/parser/sizing.js +1 -1
- package/dist/parser/typography.d.ts +2 -1
- package/dist/parser/typography.js +1 -1
- package/dist/parser/typography.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/babel/config-loader.test.ts +2 -2
- package/src/babel/config-loader.ts +37 -2
- package/src/babel/plugin/componentScope.ts +87 -0
- package/src/babel/plugin/state.ts +177 -0
- package/src/babel/plugin/visitors/className.test.ts +1312 -0
- package/src/babel/plugin/visitors/className.ts +624 -0
- package/src/babel/plugin/visitors/className.windowDimensions.test.ts +406 -0
- package/src/babel/plugin/visitors/imports.test.ts +88 -0
- package/src/babel/plugin/visitors/imports.ts +101 -0
- package/src/babel/plugin/visitors/program.test.ts +325 -0
- package/src/babel/plugin/visitors/program.ts +99 -0
- package/src/babel/plugin/visitors/tw.test.ts +620 -0
- package/src/babel/plugin/visitors/tw.ts +148 -0
- package/src/babel/plugin.ts +28 -953
- package/src/babel/utils/colorSchemeModifierProcessing.ts +11 -0
- package/src/babel/utils/componentSupport.test.ts +20 -7
- package/src/babel/utils/componentSupport.ts +2 -0
- package/src/babel/utils/modifierProcessing.ts +21 -0
- package/src/babel/utils/platformModifierProcessing.ts +11 -0
- package/src/babel/utils/styleInjection.ts +172 -17
- package/src/babel/utils/twProcessing.ts +11 -0
- package/src/babel/utils/windowDimensionsProcessing.ts +121 -0
- package/src/components/TouchableOpacity.tsx +71 -0
- package/src/components/index.ts +3 -0
- package/src/config/markers.ts +5 -0
- package/src/index.ts +4 -5
- package/src/parser/borders.test.ts +58 -0
- package/src/parser/borders.ts +18 -3
- package/src/parser/colors.test.ts +249 -0
- package/src/parser/colors.ts +38 -0
- package/src/parser/index.ts +4 -3
- package/src/parser/layout.test.ts +61 -0
- package/src/parser/layout.ts +55 -1
- package/src/parser/sizing.ts +11 -0
- package/src/parser/typography.test.ts +102 -0
- package/src/parser/typography.ts +61 -15
|
@@ -327,3 +327,61 @@ describe("parseBorder - comprehensive coverage", () => {
|
|
|
327
327
|
});
|
|
328
328
|
});
|
|
329
329
|
});
|
|
330
|
+
|
|
331
|
+
describe("parseBorder - color pattern detection", () => {
|
|
332
|
+
it("should return null for directional border colors with preset values", () => {
|
|
333
|
+
// These should be handled by parseColor
|
|
334
|
+
expect(parseBorder("border-t-red-500")).toBeNull();
|
|
335
|
+
expect(parseBorder("border-r-blue-500")).toBeNull();
|
|
336
|
+
expect(parseBorder("border-b-green-500")).toBeNull();
|
|
337
|
+
expect(parseBorder("border-l-yellow-500")).toBeNull();
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
it("should return null for directional border colors with basic values", () => {
|
|
341
|
+
// These should be handled by parseColor
|
|
342
|
+
expect(parseBorder("border-t-white")).toBeNull();
|
|
343
|
+
expect(parseBorder("border-r-black")).toBeNull();
|
|
344
|
+
expect(parseBorder("border-b-transparent")).toBeNull();
|
|
345
|
+
expect(parseBorder("border-l-white")).toBeNull();
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
it("should return null for directional border colors with arbitrary hex values", () => {
|
|
349
|
+
// These should be handled by parseColor
|
|
350
|
+
expect(parseBorder("border-t-[#ff0000]")).toBeNull();
|
|
351
|
+
expect(parseBorder("border-r-[#3B82F6]")).toBeNull();
|
|
352
|
+
expect(parseBorder("border-b-[#abc]")).toBeNull();
|
|
353
|
+
expect(parseBorder("border-l-[#00FF00AA]")).toBeNull();
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
it("should return null for directional border colors with opacity", () => {
|
|
357
|
+
// These should be handled by parseColor
|
|
358
|
+
expect(parseBorder("border-t-red-500/50")).toBeNull();
|
|
359
|
+
expect(parseBorder("border-r-blue-500/80")).toBeNull();
|
|
360
|
+
expect(parseBorder("border-b-[#ff0000]/60")).toBeNull();
|
|
361
|
+
expect(parseBorder("border-l-black/25")).toBeNull();
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
it("should return null for directional border colors with custom colors", () => {
|
|
365
|
+
// These should be handled by parseColor (assuming brand-primary is a custom color)
|
|
366
|
+
expect(parseBorder("border-t-brand-primary")).toBeNull();
|
|
367
|
+
expect(parseBorder("border-r-accent")).toBeNull();
|
|
368
|
+
expect(parseBorder("border-b-brand-secondary")).toBeNull();
|
|
369
|
+
expect(parseBorder("border-l-custom")).toBeNull();
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
it("should still handle directional border widths correctly", () => {
|
|
373
|
+
// These should NOT be detected as color patterns
|
|
374
|
+
expect(parseBorder("border-t-2")).toEqual({ borderTopWidth: 2 });
|
|
375
|
+
expect(parseBorder("border-r-4")).toEqual({ borderRightWidth: 4 });
|
|
376
|
+
expect(parseBorder("border-b-8")).toEqual({ borderBottomWidth: 8 });
|
|
377
|
+
expect(parseBorder("border-l-0")).toEqual({ borderLeftWidth: 0 });
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
it("should still handle directional border width arbitrary values", () => {
|
|
381
|
+
// These should NOT be detected as color patterns
|
|
382
|
+
expect(parseBorder("border-t-[3px]")).toEqual({ borderTopWidth: 3 });
|
|
383
|
+
expect(parseBorder("border-r-[5px]")).toEqual({ borderRightWidth: 5 });
|
|
384
|
+
expect(parseBorder("border-b-[10]")).toEqual({ borderBottomWidth: 10 });
|
|
385
|
+
expect(parseBorder("border-l-[8px]")).toEqual({ borderLeftWidth: 8 });
|
|
386
|
+
});
|
|
387
|
+
});
|
package/src/parser/borders.ts
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import type { StyleObject } from "../types";
|
|
6
|
+
import { parseColor } from "./colors";
|
|
6
7
|
|
|
7
8
|
// Border width scale
|
|
8
9
|
export const BORDER_WIDTH_SCALE: Record<string, number> = {
|
|
@@ -108,8 +109,10 @@ function parseArbitraryBorderRadius(value: string): number | null {
|
|
|
108
109
|
|
|
109
110
|
/**
|
|
110
111
|
* Parse border classes
|
|
112
|
+
* @param cls - The class name to parse
|
|
113
|
+
* @param customColors - Optional custom colors from tailwind.config (used to detect color patterns)
|
|
111
114
|
*/
|
|
112
|
-
export function parseBorder(cls: string): StyleObject | null {
|
|
115
|
+
export function parseBorder(cls: string, customColors?: Record<string, string>): StyleObject | null {
|
|
113
116
|
// Border style (must come before parseBorderWidth)
|
|
114
117
|
if (cls === "border-solid") return { borderStyle: "solid" };
|
|
115
118
|
if (cls === "border-dotted") return { borderStyle: "dotted" };
|
|
@@ -117,7 +120,7 @@ export function parseBorder(cls: string): StyleObject | null {
|
|
|
117
120
|
|
|
118
121
|
// Border width (border-0, border-t, border-[8px], etc.)
|
|
119
122
|
if (cls.startsWith("border-")) {
|
|
120
|
-
return parseBorderWidth(cls);
|
|
123
|
+
return parseBorderWidth(cls, customColors);
|
|
121
124
|
}
|
|
122
125
|
|
|
123
126
|
if (cls === "border") {
|
|
@@ -134,14 +137,26 @@ export function parseBorder(cls: string): StyleObject | null {
|
|
|
134
137
|
|
|
135
138
|
/**
|
|
136
139
|
* Parse border width classes
|
|
140
|
+
* @param cls - The class name to parse
|
|
141
|
+
* @param customColors - Optional custom colors (passed to parseColor for pattern detection)
|
|
137
142
|
*/
|
|
138
|
-
function parseBorderWidth(cls: string): StyleObject | null {
|
|
143
|
+
function parseBorderWidth(cls: string, customColors?: Record<string, string>): StyleObject | null {
|
|
139
144
|
// Directional borders: border-t, border-t-2, border-t-[8px]
|
|
145
|
+
// Note: border-x and border-y are handled by parseColor for colors only
|
|
140
146
|
const dirMatch = cls.match(/^border-([trbl])(?:-(.+))?$/);
|
|
141
147
|
if (dirMatch) {
|
|
142
148
|
const dir = dirMatch[1];
|
|
143
149
|
const valueStr = dirMatch[2] || ""; // empty string for border-t
|
|
144
150
|
|
|
151
|
+
// If it's a color pattern, let parseColor handle it
|
|
152
|
+
// Try to parse as color - if it succeeds, return null (let parseColor handle it)
|
|
153
|
+
if (valueStr) {
|
|
154
|
+
const colorResult = parseColor(cls, customColors);
|
|
155
|
+
if (colorResult !== null) {
|
|
156
|
+
return null; // It's a color, let parseColor handle it
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
145
160
|
// Try arbitrary value first (if it starts with [)
|
|
146
161
|
if (valueStr.startsWith("[")) {
|
|
147
162
|
const arbitraryValue = parseArbitraryBorderWidth(valueStr);
|
|
@@ -391,3 +391,252 @@ describe("parseColor - opacity modifiers", () => {
|
|
|
391
391
|
});
|
|
392
392
|
});
|
|
393
393
|
});
|
|
394
|
+
|
|
395
|
+
describe("parseColor - directional border colors", () => {
|
|
396
|
+
it("should parse directional border colors with preset values", () => {
|
|
397
|
+
expect(parseColor("border-t-red-500")).toEqual({ borderTopColor: COLORS["red-500"] });
|
|
398
|
+
expect(parseColor("border-r-blue-500")).toEqual({ borderRightColor: COLORS["blue-500"] });
|
|
399
|
+
expect(parseColor("border-b-green-500")).toEqual({ borderBottomColor: COLORS["green-500"] });
|
|
400
|
+
expect(parseColor("border-l-yellow-500")).toEqual({ borderLeftColor: COLORS["yellow-500"] });
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
it("should parse directional border colors with basic values", () => {
|
|
404
|
+
expect(parseColor("border-t-white")).toEqual({ borderTopColor: "#FFFFFF" });
|
|
405
|
+
expect(parseColor("border-r-black")).toEqual({ borderRightColor: "#000000" });
|
|
406
|
+
expect(parseColor("border-b-transparent")).toEqual({ borderBottomColor: "transparent" });
|
|
407
|
+
expect(parseColor("border-l-white")).toEqual({ borderLeftColor: "#FFFFFF" });
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
it("should parse directional border colors with arbitrary 6-digit hex values", () => {
|
|
411
|
+
expect(parseColor("border-t-[#ff0000]")).toEqual({ borderTopColor: "#ff0000" });
|
|
412
|
+
expect(parseColor("border-r-[#3B82F6]")).toEqual({ borderRightColor: "#3B82F6" });
|
|
413
|
+
expect(parseColor("border-b-[#00ff00]")).toEqual({ borderBottomColor: "#00ff00" });
|
|
414
|
+
expect(parseColor("border-l-[#cccccc]")).toEqual({ borderLeftColor: "#cccccc" });
|
|
415
|
+
});
|
|
416
|
+
|
|
417
|
+
it("should parse directional border colors with arbitrary 3-digit hex values", () => {
|
|
418
|
+
expect(parseColor("border-t-[#f00]")).toEqual({ borderTopColor: "#ff0000" });
|
|
419
|
+
expect(parseColor("border-r-[#abc]")).toEqual({ borderRightColor: "#aabbcc" });
|
|
420
|
+
expect(parseColor("border-b-[#123]")).toEqual({ borderBottomColor: "#112233" });
|
|
421
|
+
expect(parseColor("border-l-[#999]")).toEqual({ borderLeftColor: "#999999" });
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
it("should parse directional border colors with arbitrary 8-digit hex values (with alpha)", () => {
|
|
425
|
+
expect(parseColor("border-t-[#ff0000aa]")).toEqual({ borderTopColor: "#ff0000aa" });
|
|
426
|
+
expect(parseColor("border-r-[#0000FF50]")).toEqual({ borderRightColor: "#0000FF50" });
|
|
427
|
+
expect(parseColor("border-b-[#00FF0080]")).toEqual({ borderBottomColor: "#00FF0080" });
|
|
428
|
+
expect(parseColor("border-l-[#FFFFFF00]")).toEqual({ borderLeftColor: "#FFFFFF00" });
|
|
429
|
+
});
|
|
430
|
+
|
|
431
|
+
it("should parse directional border colors with opacity modifiers", () => {
|
|
432
|
+
expect(parseColor("border-t-red-500/50")).toEqual({
|
|
433
|
+
borderTopColor: applyOpacity(COLORS["red-500"], 50),
|
|
434
|
+
});
|
|
435
|
+
expect(parseColor("border-r-blue-500/80")).toEqual({
|
|
436
|
+
borderRightColor: applyOpacity(COLORS["blue-500"], 80),
|
|
437
|
+
});
|
|
438
|
+
expect(parseColor("border-b-green-500/30")).toEqual({
|
|
439
|
+
borderBottomColor: applyOpacity(COLORS["green-500"], 30),
|
|
440
|
+
});
|
|
441
|
+
expect(parseColor("border-l-black/25")).toEqual({
|
|
442
|
+
borderLeftColor: applyOpacity(COLORS.black, 25),
|
|
443
|
+
});
|
|
444
|
+
});
|
|
445
|
+
|
|
446
|
+
it("should parse directional border colors with arbitrary hex and opacity", () => {
|
|
447
|
+
expect(parseColor("border-t-[#ff0000]/50")).toEqual({ borderTopColor: "#FF000080" });
|
|
448
|
+
expect(parseColor("border-r-[#3B82F6]/80")).toEqual({ borderRightColor: "#3B82F6CC" });
|
|
449
|
+
expect(parseColor("border-b-[#abc]/60")).toEqual({ borderBottomColor: "#AABBCC99" });
|
|
450
|
+
expect(parseColor("border-l-[#000]/100")).toEqual({ borderLeftColor: "#000000FF" });
|
|
451
|
+
});
|
|
452
|
+
|
|
453
|
+
it("should parse directional border colors with custom colors", () => {
|
|
454
|
+
const customColors = {
|
|
455
|
+
"brand-primary": "#FF6B6B",
|
|
456
|
+
"brand-secondary": "#4ECDC4",
|
|
457
|
+
accent: "#FFE66D",
|
|
458
|
+
};
|
|
459
|
+
|
|
460
|
+
expect(parseColor("border-t-brand-primary", customColors)).toEqual({
|
|
461
|
+
borderTopColor: "#FF6B6B",
|
|
462
|
+
});
|
|
463
|
+
expect(parseColor("border-r-brand-secondary", customColors)).toEqual({
|
|
464
|
+
borderRightColor: "#4ECDC4",
|
|
465
|
+
});
|
|
466
|
+
expect(parseColor("border-b-accent", customColors)).toEqual({ borderBottomColor: "#FFE66D" });
|
|
467
|
+
expect(parseColor("border-l-brand-primary", customColors)).toEqual({
|
|
468
|
+
borderLeftColor: "#FF6B6B",
|
|
469
|
+
});
|
|
470
|
+
});
|
|
471
|
+
|
|
472
|
+
it("should parse directional border colors with custom colors and opacity", () => {
|
|
473
|
+
const customColors = { "brand-primary": "#FF6B6B" };
|
|
474
|
+
expect(parseColor("border-t-brand-primary/50", customColors)).toEqual({
|
|
475
|
+
borderTopColor: "#FF6B6B80",
|
|
476
|
+
});
|
|
477
|
+
expect(parseColor("border-l-brand-primary/75", customColors)).toEqual({
|
|
478
|
+
borderLeftColor: "#FF6B6BBF",
|
|
479
|
+
});
|
|
480
|
+
});
|
|
481
|
+
|
|
482
|
+
it("should not match border width classes", () => {
|
|
483
|
+
// These should be handled by parseBorder
|
|
484
|
+
expect(parseColor("border-t-2")).toBeNull();
|
|
485
|
+
expect(parseColor("border-r-4")).toBeNull();
|
|
486
|
+
expect(parseColor("border-b-8")).toBeNull();
|
|
487
|
+
expect(parseColor("border-l-0")).toBeNull();
|
|
488
|
+
});
|
|
489
|
+
|
|
490
|
+
it("should not match border width arbitrary values", () => {
|
|
491
|
+
// These should be handled by parseBorder
|
|
492
|
+
expect(parseColor("border-t-[3px]")).toBeNull();
|
|
493
|
+
expect(parseColor("border-r-[8px]")).toBeNull();
|
|
494
|
+
expect(parseColor("border-b-[5]")).toBeNull();
|
|
495
|
+
expect(parseColor("border-l-[10px]")).toBeNull();
|
|
496
|
+
});
|
|
497
|
+
|
|
498
|
+
it("should return null for invalid directional border color values", () => {
|
|
499
|
+
expect(parseColor("border-t-invalid")).toBeNull();
|
|
500
|
+
expect(parseColor("border-r-notacolor")).toBeNull();
|
|
501
|
+
expect(parseColor("border-b-xyz")).toBeNull();
|
|
502
|
+
expect(parseColor("border-l-wrongcolor")).toBeNull();
|
|
503
|
+
});
|
|
504
|
+
|
|
505
|
+
it("should handle all directions with same color", () => {
|
|
506
|
+
const color = COLORS["blue-500"];
|
|
507
|
+
expect(parseColor("border-t-blue-500")).toEqual({ borderTopColor: color });
|
|
508
|
+
expect(parseColor("border-r-blue-500")).toEqual({ borderRightColor: color });
|
|
509
|
+
expect(parseColor("border-b-blue-500")).toEqual({ borderBottomColor: color });
|
|
510
|
+
expect(parseColor("border-l-blue-500")).toEqual({ borderLeftColor: color });
|
|
511
|
+
});
|
|
512
|
+
|
|
513
|
+
it("should handle all color families for directional borders", () => {
|
|
514
|
+
const families = ["gray", "red", "blue", "green", "yellow", "purple", "pink", "orange", "indigo"];
|
|
515
|
+
families.forEach((family) => {
|
|
516
|
+
expect(parseColor(`border-t-${family}-500`)).toBeTruthy();
|
|
517
|
+
expect(parseColor(`border-r-${family}-500`)).toBeTruthy();
|
|
518
|
+
expect(parseColor(`border-b-${family}-500`)).toBeTruthy();
|
|
519
|
+
expect(parseColor(`border-l-${family}-500`)).toBeTruthy();
|
|
520
|
+
});
|
|
521
|
+
});
|
|
522
|
+
|
|
523
|
+
it("should handle directional borders with all opacity levels", () => {
|
|
524
|
+
expect(parseColor("border-t-black/0")).toEqual({ borderTopColor: "#00000000" });
|
|
525
|
+
expect(parseColor("border-t-black/25")).toEqual({ borderTopColor: "#00000040" });
|
|
526
|
+
expect(parseColor("border-t-black/50")).toEqual({ borderTopColor: "#00000080" });
|
|
527
|
+
expect(parseColor("border-t-black/75")).toEqual({ borderTopColor: "#000000BF" });
|
|
528
|
+
expect(parseColor("border-t-black/100")).toEqual({ borderTopColor: "#000000FF" });
|
|
529
|
+
});
|
|
530
|
+
});
|
|
531
|
+
|
|
532
|
+
describe("parseColor - border-x and border-y colors", () => {
|
|
533
|
+
it("should parse border-x colors (horizontal: left + right)", () => {
|
|
534
|
+
expect(parseColor("border-x-red-500")).toEqual({
|
|
535
|
+
borderLeftColor: COLORS["red-500"],
|
|
536
|
+
borderRightColor: COLORS["red-500"],
|
|
537
|
+
});
|
|
538
|
+
expect(parseColor("border-x-blue-500")).toEqual({
|
|
539
|
+
borderLeftColor: COLORS["blue-500"],
|
|
540
|
+
borderRightColor: COLORS["blue-500"],
|
|
541
|
+
});
|
|
542
|
+
});
|
|
543
|
+
|
|
544
|
+
it("should parse border-y colors (vertical: top + bottom)", () => {
|
|
545
|
+
expect(parseColor("border-y-green-500")).toEqual({
|
|
546
|
+
borderTopColor: COLORS["green-500"],
|
|
547
|
+
borderBottomColor: COLORS["green-500"],
|
|
548
|
+
});
|
|
549
|
+
expect(parseColor("border-y-yellow-500")).toEqual({
|
|
550
|
+
borderTopColor: COLORS["yellow-500"],
|
|
551
|
+
borderBottomColor: COLORS["yellow-500"],
|
|
552
|
+
});
|
|
553
|
+
});
|
|
554
|
+
|
|
555
|
+
it("should parse border-x with basic colors", () => {
|
|
556
|
+
expect(parseColor("border-x-white")).toEqual({
|
|
557
|
+
borderLeftColor: "#FFFFFF",
|
|
558
|
+
borderRightColor: "#FFFFFF",
|
|
559
|
+
});
|
|
560
|
+
expect(parseColor("border-x-black")).toEqual({
|
|
561
|
+
borderLeftColor: "#000000",
|
|
562
|
+
borderRightColor: "#000000",
|
|
563
|
+
});
|
|
564
|
+
});
|
|
565
|
+
|
|
566
|
+
it("should parse border-y with basic colors", () => {
|
|
567
|
+
expect(parseColor("border-y-white")).toEqual({
|
|
568
|
+
borderTopColor: "#FFFFFF",
|
|
569
|
+
borderBottomColor: "#FFFFFF",
|
|
570
|
+
});
|
|
571
|
+
expect(parseColor("border-y-transparent")).toEqual({
|
|
572
|
+
borderTopColor: "transparent",
|
|
573
|
+
borderBottomColor: "transparent",
|
|
574
|
+
});
|
|
575
|
+
});
|
|
576
|
+
|
|
577
|
+
it("should parse border-x with opacity", () => {
|
|
578
|
+
expect(parseColor("border-x-red-500/50")).toEqual({
|
|
579
|
+
borderLeftColor: applyOpacity(COLORS["red-500"], 50),
|
|
580
|
+
borderRightColor: applyOpacity(COLORS["red-500"], 50),
|
|
581
|
+
});
|
|
582
|
+
});
|
|
583
|
+
|
|
584
|
+
it("should parse border-y with opacity", () => {
|
|
585
|
+
expect(parseColor("border-y-blue-500/80")).toEqual({
|
|
586
|
+
borderTopColor: applyOpacity(COLORS["blue-500"], 80),
|
|
587
|
+
borderBottomColor: applyOpacity(COLORS["blue-500"], 80),
|
|
588
|
+
});
|
|
589
|
+
});
|
|
590
|
+
|
|
591
|
+
it("should parse border-x with arbitrary hex colors", () => {
|
|
592
|
+
expect(parseColor("border-x-[#ff0000]")).toEqual({
|
|
593
|
+
borderLeftColor: "#ff0000",
|
|
594
|
+
borderRightColor: "#ff0000",
|
|
595
|
+
});
|
|
596
|
+
expect(parseColor("border-x-[#abc]")).toEqual({
|
|
597
|
+
borderLeftColor: "#aabbcc",
|
|
598
|
+
borderRightColor: "#aabbcc",
|
|
599
|
+
});
|
|
600
|
+
});
|
|
601
|
+
|
|
602
|
+
it("should parse border-y with arbitrary hex colors", () => {
|
|
603
|
+
expect(parseColor("border-y-[#00ff00]")).toEqual({
|
|
604
|
+
borderTopColor: "#00ff00",
|
|
605
|
+
borderBottomColor: "#00ff00",
|
|
606
|
+
});
|
|
607
|
+
expect(parseColor("border-y-[#123]")).toEqual({
|
|
608
|
+
borderTopColor: "#112233",
|
|
609
|
+
borderBottomColor: "#112233",
|
|
610
|
+
});
|
|
611
|
+
});
|
|
612
|
+
|
|
613
|
+
it("should parse border-x with custom colors", () => {
|
|
614
|
+
const customColors = { "brand-primary": "#FF6B6B" };
|
|
615
|
+
expect(parseColor("border-x-brand-primary", customColors)).toEqual({
|
|
616
|
+
borderLeftColor: "#FF6B6B",
|
|
617
|
+
borderRightColor: "#FF6B6B",
|
|
618
|
+
});
|
|
619
|
+
});
|
|
620
|
+
|
|
621
|
+
it("should parse border-y with custom colors", () => {
|
|
622
|
+
const customColors = { accent: "#FFE66D" };
|
|
623
|
+
expect(parseColor("border-y-accent", customColors)).toEqual({
|
|
624
|
+
borderTopColor: "#FFE66D",
|
|
625
|
+
borderBottomColor: "#FFE66D",
|
|
626
|
+
});
|
|
627
|
+
});
|
|
628
|
+
|
|
629
|
+
it("should not match border-x/border-y width classes", () => {
|
|
630
|
+
expect(parseColor("border-x-2")).toBeNull();
|
|
631
|
+
expect(parseColor("border-y-4")).toBeNull();
|
|
632
|
+
expect(parseColor("border-x-0")).toBeNull();
|
|
633
|
+
expect(parseColor("border-y-8")).toBeNull();
|
|
634
|
+
});
|
|
635
|
+
|
|
636
|
+
it("should not match border-x/border-y arbitrary width values", () => {
|
|
637
|
+
expect(parseColor("border-x-[3px]")).toBeNull();
|
|
638
|
+
expect(parseColor("border-y-[5px]")).toBeNull();
|
|
639
|
+
expect(parseColor("border-x-[10]")).toBeNull();
|
|
640
|
+
expect(parseColor("border-y-[8px]")).toBeNull();
|
|
641
|
+
});
|
|
642
|
+
});
|
package/src/parser/colors.ts
CHANGED
|
@@ -178,5 +178,43 @@ export function parseColor(cls: string, customColors?: Record<string, string>):
|
|
|
178
178
|
}
|
|
179
179
|
}
|
|
180
180
|
|
|
181
|
+
// Directional border colors: border-t-red-500, border-l-blue-500/50, border-r-[#ff0000]
|
|
182
|
+
const dirBorderMatch = cls.match(/^border-([trblxy])-(.+)$/);
|
|
183
|
+
if (dirBorderMatch) {
|
|
184
|
+
const dir = dirBorderMatch[1];
|
|
185
|
+
const colorKey = dirBorderMatch[2];
|
|
186
|
+
|
|
187
|
+
// Skip arbitrary values that don't look like colors (e.g., border-t-[3px] is width)
|
|
188
|
+
if (colorKey.startsWith("[") && !colorKey.startsWith("[#")) {
|
|
189
|
+
return null;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const color = parseColorWithOpacity(colorKey);
|
|
193
|
+
if (color) {
|
|
194
|
+
// Map direction to React Native property/properties
|
|
195
|
+
// x and y apply to multiple sides (RN doesn't have borderHorizontalColor/borderVerticalColor)
|
|
196
|
+
if (dir === "x") {
|
|
197
|
+
return {
|
|
198
|
+
borderLeftColor: color,
|
|
199
|
+
borderRightColor: color,
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
if (dir === "y") {
|
|
203
|
+
return {
|
|
204
|
+
borderTopColor: color,
|
|
205
|
+
borderBottomColor: color,
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
const propMap: Record<string, string> = {
|
|
210
|
+
t: "borderTopColor",
|
|
211
|
+
r: "borderRightColor",
|
|
212
|
+
b: "borderBottomColor",
|
|
213
|
+
l: "borderLeftColor",
|
|
214
|
+
};
|
|
215
|
+
return { [propMap[dir]]: color };
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
181
219
|
return null;
|
|
182
220
|
}
|
package/src/parser/index.ts
CHANGED
|
@@ -20,6 +20,7 @@ import { parseTypography } from "./typography";
|
|
|
20
20
|
export type CustomTheme = {
|
|
21
21
|
colors?: Record<string, string>;
|
|
22
22
|
fontFamily?: Record<string, string>;
|
|
23
|
+
fontSize?: Record<string, number>;
|
|
23
24
|
};
|
|
24
25
|
|
|
25
26
|
/**
|
|
@@ -49,13 +50,13 @@ export function parseClassName(className: string, customTheme?: CustomTheme): St
|
|
|
49
50
|
export function parseClass(cls: string, customTheme?: CustomTheme): StyleObject {
|
|
50
51
|
// Try each parser in order
|
|
51
52
|
// Note: parseBorder must come before parseColor to avoid border-[3px] being parsed as a color
|
|
52
|
-
// parseColor and parseTypography get custom theme
|
|
53
|
+
// parseBorder, parseColor and parseTypography get custom theme
|
|
53
54
|
const parsers: Array<(cls: string) => StyleObject | null> = [
|
|
54
55
|
parseSpacing,
|
|
55
|
-
parseBorder,
|
|
56
|
+
(cls: string) => parseBorder(cls, customTheme?.colors),
|
|
56
57
|
(cls: string) => parseColor(cls, customTheme?.colors),
|
|
57
58
|
parseLayout,
|
|
58
|
-
(cls: string) => parseTypography(cls, customTheme?.fontFamily),
|
|
59
|
+
(cls: string) => parseTypography(cls, customTheme?.fontFamily, customTheme?.fontSize),
|
|
59
60
|
parseSizing,
|
|
60
61
|
parseShadow,
|
|
61
62
|
parseAspectRatio,
|
|
@@ -73,6 +73,67 @@ describe("parseLayout - flex grow/shrink utilities", () => {
|
|
|
73
73
|
it("should parse shrink-0", () => {
|
|
74
74
|
expect(parseLayout("shrink-0")).toEqual({ flexShrink: 0 });
|
|
75
75
|
});
|
|
76
|
+
|
|
77
|
+
it("should parse grow with arbitrary values", () => {
|
|
78
|
+
expect(parseLayout("grow-[1.5]")).toEqual({ flexGrow: 1.5 });
|
|
79
|
+
expect(parseLayout("grow-[2]")).toEqual({ flexGrow: 2 });
|
|
80
|
+
expect(parseLayout("grow-[0.5]")).toEqual({ flexGrow: 0.5 });
|
|
81
|
+
expect(parseLayout("grow-[3]")).toEqual({ flexGrow: 3 });
|
|
82
|
+
expect(parseLayout("grow-[0]")).toEqual({ flexGrow: 0 });
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it("should parse shrink with arbitrary values", () => {
|
|
86
|
+
expect(parseLayout("shrink-[0.5]")).toEqual({ flexShrink: 0.5 });
|
|
87
|
+
expect(parseLayout("shrink-[2]")).toEqual({ flexShrink: 2 });
|
|
88
|
+
expect(parseLayout("shrink-[1.5]")).toEqual({ flexShrink: 1.5 });
|
|
89
|
+
expect(parseLayout("shrink-[3]")).toEqual({ flexShrink: 3 });
|
|
90
|
+
expect(parseLayout("shrink-[0]")).toEqual({ flexShrink: 0 });
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it("should parse CSS-style flex-grow aliases", () => {
|
|
94
|
+
expect(parseLayout("flex-grow")).toEqual({ flexGrow: 1 });
|
|
95
|
+
expect(parseLayout("flex-grow-0")).toEqual({ flexGrow: 0 });
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it("should parse CSS-style flex-shrink aliases", () => {
|
|
99
|
+
expect(parseLayout("flex-shrink")).toEqual({ flexShrink: 1 });
|
|
100
|
+
expect(parseLayout("flex-shrink-0")).toEqual({ flexShrink: 0 });
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it("should parse CSS-style flex-grow with arbitrary values", () => {
|
|
104
|
+
expect(parseLayout("flex-grow-[1.5]")).toEqual({ flexGrow: 1.5 });
|
|
105
|
+
expect(parseLayout("flex-grow-[2]")).toEqual({ flexGrow: 2 });
|
|
106
|
+
expect(parseLayout("flex-grow-[0.5]")).toEqual({ flexGrow: 0.5 });
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it("should parse CSS-style flex-shrink with arbitrary values", () => {
|
|
110
|
+
expect(parseLayout("flex-shrink-[0.5]")).toEqual({ flexShrink: 0.5 });
|
|
111
|
+
expect(parseLayout("flex-shrink-[2]")).toEqual({ flexShrink: 2 });
|
|
112
|
+
expect(parseLayout("flex-shrink-[1.5]")).toEqual({ flexShrink: 1.5 });
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it("should handle edge case values", () => {
|
|
116
|
+
expect(parseLayout("grow-[0.1]")).toEqual({ flexGrow: 0.1 });
|
|
117
|
+
expect(parseLayout("grow-[10]")).toEqual({ flexGrow: 10 });
|
|
118
|
+
expect(parseLayout("shrink-[0.01]")).toEqual({ flexShrink: 0.01 });
|
|
119
|
+
expect(parseLayout("shrink-[100]")).toEqual({ flexShrink: 100 });
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it("should parse Tailwind shorthand decimals (no leading zero)", () => {
|
|
123
|
+
expect(parseLayout("grow-[.5]")).toEqual({ flexGrow: 0.5 });
|
|
124
|
+
expect(parseLayout("grow-[.75]")).toEqual({ flexGrow: 0.75 });
|
|
125
|
+
expect(parseLayout("shrink-[.5]")).toEqual({ flexShrink: 0.5 });
|
|
126
|
+
expect(parseLayout("shrink-[.25]")).toEqual({ flexShrink: 0.25 });
|
|
127
|
+
expect(parseLayout("flex-grow-[.5]")).toEqual({ flexGrow: 0.5 });
|
|
128
|
+
expect(parseLayout("flex-shrink-[.5]")).toEqual({ flexShrink: 0.5 });
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it("should reject negative values", () => {
|
|
132
|
+
expect(parseLayout("grow-[-1]")).toBeNull();
|
|
133
|
+
expect(parseLayout("shrink-[-1]")).toBeNull();
|
|
134
|
+
expect(parseLayout("flex-grow-[-2]")).toBeNull();
|
|
135
|
+
expect(parseLayout("flex-shrink-[-0.5]")).toBeNull();
|
|
136
|
+
});
|
|
76
137
|
});
|
|
77
138
|
|
|
78
139
|
describe("parseLayout - justify content utilities", () => {
|
package/src/parser/layout.ts
CHANGED
|
@@ -60,6 +60,31 @@ function parseArbitraryZIndex(value: string): number | null {
|
|
|
60
60
|
return null;
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
+
/**
|
|
64
|
+
* Parse arbitrary grow/shrink value: [1.5], [2], [0.5], [.5]
|
|
65
|
+
* Returns number for valid non-negative values, null otherwise
|
|
66
|
+
*/
|
|
67
|
+
function parseArbitraryGrowShrink(value: string): number | null {
|
|
68
|
+
// Match: [1.5], [2], [0], [0.5], [.5] (non-negative decimals, optional leading digit)
|
|
69
|
+
const match = value.match(/^\[(\d+(?:\.\d+)?|\.\d+)\]$/);
|
|
70
|
+
if (match) {
|
|
71
|
+
return parseFloat(match[1]);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Warn about invalid formats (negative values, unsupported formats)
|
|
75
|
+
if (value.startsWith("[") && value.endsWith("]")) {
|
|
76
|
+
/* v8 ignore next 5 */
|
|
77
|
+
if (process.env.NODE_ENV !== "production") {
|
|
78
|
+
console.warn(
|
|
79
|
+
`[react-native-tailwind] Invalid arbitrary grow/shrink value: ${value}. Only non-negative numbers are supported (e.g., [1.5], [2], [0.5], [.5]).`,
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
|
|
63
88
|
// Display utilities
|
|
64
89
|
const DISPLAY_MAP: Record<string, StyleObject> = {
|
|
65
90
|
flex: { display: "flex" },
|
|
@@ -88,12 +113,17 @@ const FLEX_MAP: Record<string, StyleObject> = {
|
|
|
88
113
|
"flex-none": { flex: 0 },
|
|
89
114
|
};
|
|
90
115
|
|
|
91
|
-
// Flex grow/shrink utilities
|
|
116
|
+
// Flex grow/shrink utilities (includes CSS-style aliases)
|
|
92
117
|
const GROW_SHRINK_MAP: Record<string, StyleObject> = {
|
|
93
118
|
grow: { flexGrow: 1 },
|
|
94
119
|
"grow-0": { flexGrow: 0 },
|
|
95
120
|
shrink: { flexShrink: 1 },
|
|
96
121
|
"shrink-0": { flexShrink: 0 },
|
|
122
|
+
// CSS-style aliases
|
|
123
|
+
"flex-grow": { flexGrow: 1 },
|
|
124
|
+
"flex-grow-0": { flexGrow: 0 },
|
|
125
|
+
"flex-shrink": { flexShrink: 1 },
|
|
126
|
+
"flex-shrink-0": { flexShrink: 0 },
|
|
97
127
|
};
|
|
98
128
|
|
|
99
129
|
// Justify content utilities
|
|
@@ -357,6 +387,30 @@ export function parseLayout(cls: string): StyleObject | null {
|
|
|
357
387
|
}
|
|
358
388
|
}
|
|
359
389
|
|
|
390
|
+
// Flex grow: grow-[1.5], flex-grow-[2], etc. (arbitrary values)
|
|
391
|
+
if (cls.startsWith("grow-") || cls.startsWith("flex-grow-")) {
|
|
392
|
+
const prefix = cls.startsWith("flex-grow-") ? "flex-grow-" : "grow-";
|
|
393
|
+
const growKey = cls.substring(prefix.length);
|
|
394
|
+
|
|
395
|
+
// Arbitrary values: grow-[1.5], flex-grow-[2]
|
|
396
|
+
const arbitraryGrow = parseArbitraryGrowShrink(growKey);
|
|
397
|
+
if (arbitraryGrow !== null) {
|
|
398
|
+
return { flexGrow: arbitraryGrow };
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
// Flex shrink: shrink-[0.5], flex-shrink-[1], etc. (arbitrary values)
|
|
403
|
+
if (cls.startsWith("shrink-") || cls.startsWith("flex-shrink-")) {
|
|
404
|
+
const prefix = cls.startsWith("flex-shrink-") ? "flex-shrink-" : "shrink-";
|
|
405
|
+
const shrinkKey = cls.substring(prefix.length);
|
|
406
|
+
|
|
407
|
+
// Arbitrary values: shrink-[0.5], flex-shrink-[1]
|
|
408
|
+
const arbitraryShrink = parseArbitraryGrowShrink(shrinkKey);
|
|
409
|
+
if (arbitraryShrink !== null) {
|
|
410
|
+
return { flexShrink: arbitraryShrink };
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
|
|
360
414
|
// Try each lookup table in order
|
|
361
415
|
return (
|
|
362
416
|
DISPLAY_MAP[cls] ??
|
package/src/parser/sizing.ts
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* Sizing utilities (width, height, min/max)
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
+
import { RUNTIME_DIMENSIONS_MARKER } from "../config/markers";
|
|
5
6
|
import type { StyleObject } from "../types";
|
|
6
7
|
|
|
7
8
|
// Size scale (in pixels/percentages)
|
|
@@ -100,6 +101,11 @@ export function parseSizing(cls: string): StyleObject | null {
|
|
|
100
101
|
if (cls.startsWith("w-")) {
|
|
101
102
|
const sizeKey = cls.substring(2);
|
|
102
103
|
|
|
104
|
+
// Screen width (requires runtime hook)
|
|
105
|
+
if (sizeKey === "screen") {
|
|
106
|
+
return { width: `${RUNTIME_DIMENSIONS_MARKER}width}}` } as StyleObject;
|
|
107
|
+
}
|
|
108
|
+
|
|
103
109
|
// Arbitrary values: w-[123px], w-[50%]
|
|
104
110
|
const arbitrarySize = parseArbitrarySize(sizeKey);
|
|
105
111
|
if (arbitrarySize !== null) {
|
|
@@ -128,6 +134,11 @@ export function parseSizing(cls: string): StyleObject | null {
|
|
|
128
134
|
if (cls.startsWith("h-")) {
|
|
129
135
|
const sizeKey = cls.substring(2);
|
|
130
136
|
|
|
137
|
+
// Screen height (requires runtime hook)
|
|
138
|
+
if (sizeKey === "screen") {
|
|
139
|
+
return { height: `${RUNTIME_DIMENSIONS_MARKER}height}}` } as StyleObject;
|
|
140
|
+
}
|
|
141
|
+
|
|
131
142
|
// Arbitrary values: h-[123px], h-[50%]
|
|
132
143
|
const arbitrarySize = parseArbitrarySize(sizeKey);
|
|
133
144
|
if (arbitrarySize !== null) {
|