@mgcrea/react-native-tailwind 0.4.0 → 0.5.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.
Files changed (71) hide show
  1. package/README.md +443 -13
  2. package/dist/babel/index.cjs +804 -274
  3. package/dist/babel/index.d.ts +2 -1
  4. package/dist/babel/index.ts +319 -16
  5. package/dist/components/Pressable.d.ts +32 -0
  6. package/dist/components/Pressable.js +1 -0
  7. package/dist/components/TextInput.d.ts +56 -0
  8. package/dist/components/TextInput.js +1 -0
  9. package/dist/index.d.ts +9 -2
  10. package/dist/index.js +1 -1
  11. package/dist/parser/aspectRatio.d.ts +16 -0
  12. package/dist/parser/aspectRatio.js +1 -0
  13. package/dist/parser/aspectRatio.test.d.ts +1 -0
  14. package/dist/parser/aspectRatio.test.js +1 -0
  15. package/dist/parser/borders.js +1 -1
  16. package/dist/parser/borders.test.d.ts +1 -0
  17. package/dist/parser/borders.test.js +1 -0
  18. package/dist/parser/colors.d.ts +1 -0
  19. package/dist/parser/colors.js +1 -1
  20. package/dist/parser/colors.test.d.ts +1 -0
  21. package/dist/parser/colors.test.js +1 -0
  22. package/dist/parser/index.d.ts +4 -0
  23. package/dist/parser/index.js +1 -1
  24. package/dist/parser/layout.d.ts +2 -0
  25. package/dist/parser/layout.js +1 -1
  26. package/dist/parser/layout.test.d.ts +1 -0
  27. package/dist/parser/layout.test.js +1 -0
  28. package/dist/parser/modifiers.d.ts +47 -0
  29. package/dist/parser/modifiers.js +1 -0
  30. package/dist/parser/modifiers.test.d.ts +1 -0
  31. package/dist/parser/modifiers.test.js +1 -0
  32. package/dist/parser/shadows.d.ts +26 -0
  33. package/dist/parser/shadows.js +1 -0
  34. package/dist/parser/shadows.test.d.ts +1 -0
  35. package/dist/parser/shadows.test.js +1 -0
  36. package/dist/parser/sizing.test.d.ts +1 -0
  37. package/dist/parser/sizing.test.js +1 -0
  38. package/dist/parser/spacing.d.ts +1 -1
  39. package/dist/parser/spacing.js +1 -1
  40. package/dist/parser/spacing.test.d.ts +1 -0
  41. package/dist/parser/spacing.test.js +1 -0
  42. package/dist/parser/typography.d.ts +2 -1
  43. package/dist/parser/typography.js +1 -1
  44. package/dist/parser/typography.test.d.ts +1 -0
  45. package/dist/parser/typography.test.js +1 -0
  46. package/dist/types.d.ts +5 -2
  47. package/package.json +7 -6
  48. package/src/babel/index.ts +319 -16
  49. package/src/components/Pressable.tsx +46 -0
  50. package/src/components/TextInput.tsx +90 -0
  51. package/src/index.ts +20 -2
  52. package/src/parser/aspectRatio.test.ts +191 -0
  53. package/src/parser/aspectRatio.ts +73 -0
  54. package/src/parser/borders.test.ts +329 -0
  55. package/src/parser/borders.ts +187 -108
  56. package/src/parser/colors.test.ts +335 -0
  57. package/src/parser/colors.ts +117 -6
  58. package/src/parser/index.ts +13 -2
  59. package/src/parser/layout.test.ts +459 -0
  60. package/src/parser/layout.ts +128 -0
  61. package/src/parser/modifiers.test.ts +375 -0
  62. package/src/parser/modifiers.ts +104 -0
  63. package/src/parser/shadows.test.ts +201 -0
  64. package/src/parser/shadows.ts +133 -0
  65. package/src/parser/sizing.test.ts +256 -0
  66. package/src/parser/spacing.test.ts +226 -0
  67. package/src/parser/spacing.ts +93 -138
  68. package/src/parser/typography.test.ts +221 -0
  69. package/src/parser/typography.ts +143 -112
  70. package/src/types.ts +2 -2
  71. package/dist/react-native.d.js +0 -1
@@ -34,6 +34,49 @@ __export(index_exports, {
34
34
  });
35
35
  module.exports = __toCommonJS(index_exports);
36
36
 
37
+ // src/parser/aspectRatio.ts
38
+ var ASPECT_RATIO_PRESETS = {
39
+ "aspect-auto": void 0,
40
+ // Remove aspect ratio
41
+ "aspect-square": 1,
42
+ // 1:1
43
+ "aspect-video": 16 / 9
44
+ // 16:9
45
+ };
46
+ function parseArbitraryAspectRatio(value) {
47
+ const match = value.match(/^\[(\d+)\/(\d+)\]$/);
48
+ if (match) {
49
+ const numerator = Number.parseInt(match[1], 10);
50
+ const denominator = Number.parseInt(match[2], 10);
51
+ if (denominator === 0) {
52
+ if (process.env.NODE_ENV !== "production") {
53
+ console.warn(`[react-native-tailwind] Invalid aspect ratio: ${value}. Denominator cannot be zero.`);
54
+ }
55
+ return null;
56
+ }
57
+ return numerator / denominator;
58
+ }
59
+ return null;
60
+ }
61
+ function parseAspectRatio(cls) {
62
+ if (!cls.startsWith("aspect-")) {
63
+ return null;
64
+ }
65
+ if (cls in ASPECT_RATIO_PRESETS) {
66
+ const aspectRatio2 = ASPECT_RATIO_PRESETS[cls];
67
+ if (aspectRatio2 === void 0) {
68
+ return {};
69
+ }
70
+ return { aspectRatio: aspectRatio2 };
71
+ }
72
+ const arbitraryValue = cls.substring(7);
73
+ const aspectRatio = parseArbitraryAspectRatio(arbitraryValue);
74
+ if (aspectRatio !== null) {
75
+ return { aspectRatio };
76
+ }
77
+ return null;
78
+ }
79
+
37
80
  // src/parser/borders.ts
38
81
  var BORDER_WIDTH_SCALE = {
39
82
  "": 1,
@@ -53,7 +96,58 @@ var BORDER_RADIUS_SCALE = {
53
96
  "3xl": 24,
54
97
  full: 9999
55
98
  };
99
+ var BORDER_WIDTH_PROP_MAP = {
100
+ t: "borderTopWidth",
101
+ r: "borderRightWidth",
102
+ b: "borderBottomWidth",
103
+ l: "borderLeftWidth"
104
+ };
105
+ var BORDER_RADIUS_CORNER_MAP = {
106
+ tl: "borderTopLeftRadius",
107
+ tr: "borderTopRightRadius",
108
+ bl: "borderBottomLeftRadius",
109
+ br: "borderBottomRightRadius"
110
+ };
111
+ var BORDER_RADIUS_SIDE_MAP = {
112
+ t: ["borderTopLeftRadius", "borderTopRightRadius"],
113
+ r: ["borderTopRightRadius", "borderBottomRightRadius"],
114
+ b: ["borderBottomLeftRadius", "borderBottomRightRadius"],
115
+ l: ["borderTopLeftRadius", "borderBottomLeftRadius"]
116
+ };
117
+ function parseArbitraryBorderWidth(value) {
118
+ const pxMatch = value.match(/^\[(\d+)(?:px)?\]$/);
119
+ if (pxMatch) {
120
+ return parseInt(pxMatch[1], 10);
121
+ }
122
+ if (value.startsWith("[") && value.endsWith("]")) {
123
+ if (process.env.NODE_ENV !== "production") {
124
+ console.warn(
125
+ `[react-native-tailwind] Unsupported arbitrary border width value: ${value}. Only px values are supported (e.g., [8px] or [8]).`
126
+ );
127
+ }
128
+ return null;
129
+ }
130
+ return null;
131
+ }
132
+ function parseArbitraryBorderRadius(value) {
133
+ const pxMatch = value.match(/^\[(\d+)(?:px)?\]$/);
134
+ if (pxMatch) {
135
+ return parseInt(pxMatch[1], 10);
136
+ }
137
+ if (value.startsWith("[") && value.endsWith("]")) {
138
+ if (process.env.NODE_ENV !== "production") {
139
+ console.warn(
140
+ `[react-native-tailwind] Unsupported arbitrary border radius value: ${value}. Only px values are supported (e.g., [12px] or [12]).`
141
+ );
142
+ }
143
+ return null;
144
+ }
145
+ return null;
146
+ }
56
147
  function parseBorder(cls) {
148
+ if (cls === "border-solid") return { borderStyle: "solid" };
149
+ if (cls === "border-dotted") return { borderStyle: "dotted" };
150
+ if (cls === "border-dashed") return { borderStyle: "dashed" };
57
151
  if (cls.startsWith("border-")) {
58
152
  return parseBorderWidth(cls);
59
153
  }
@@ -63,42 +157,25 @@ function parseBorder(cls) {
63
157
  if (cls.startsWith("rounded")) {
64
158
  return parseBorderRadius(cls);
65
159
  }
66
- if (cls === "border-solid") return { borderStyle: "solid" };
67
- if (cls === "border-dotted") return { borderStyle: "dotted" };
68
- if (cls === "border-dashed") return { borderStyle: "dashed" };
69
160
  return null;
70
161
  }
71
162
  function parseBorderWidth(cls) {
72
- const allArbMatch = cls.match(/^border-\[(\d+)(?:px)?\]$/);
73
- if (allArbMatch) {
74
- return { borderWidth: parseInt(allArbMatch[1], 10) };
75
- }
76
- const dirArbMatch = cls.match(/^border-([trbl])-\[(\d+)(?:px)?\]$/);
77
- if (dirArbMatch) {
78
- const dir = dirArbMatch[1];
79
- const value = parseInt(dirArbMatch[2], 10);
80
- const propMap = {
81
- t: "borderTopWidth",
82
- r: "borderRightWidth",
83
- b: "borderBottomWidth",
84
- l: "borderLeftWidth"
85
- };
86
- return { [propMap[dir]]: value };
87
- }
88
- const dirMatch = cls.match(/^border-([trbl])-?(\d*)$/);
163
+ const dirMatch = cls.match(/^border-([trbl])(?:-(.+))?$/);
89
164
  if (dirMatch) {
90
165
  const dir = dirMatch[1];
91
- const scaleKey = dirMatch[2] || "";
92
- const value = BORDER_WIDTH_SCALE[scaleKey];
93
- if (typeof value === "number") {
94
- const propMap = {
95
- t: "borderTopWidth",
96
- r: "borderRightWidth",
97
- b: "borderBottomWidth",
98
- l: "borderLeftWidth"
99
- };
100
- return { [propMap[dir]]: value };
166
+ const valueStr = dirMatch[2] || "";
167
+ if (valueStr.startsWith("[")) {
168
+ const arbitraryValue = parseArbitraryBorderWidth(valueStr);
169
+ if (arbitraryValue !== null) {
170
+ return { [BORDER_WIDTH_PROP_MAP[dir]]: arbitraryValue };
171
+ }
172
+ return null;
101
173
  }
174
+ const scaleValue = BORDER_WIDTH_SCALE[valueStr];
175
+ if (scaleValue !== void 0) {
176
+ return { [BORDER_WIDTH_PROP_MAP[dir]]: scaleValue };
177
+ }
178
+ return null;
102
179
  }
103
180
  const allMatch = cls.match(/^border-(\d+)$/);
104
181
  if (allMatch) {
@@ -107,78 +184,76 @@ function parseBorderWidth(cls) {
107
184
  return { borderWidth: value };
108
185
  }
109
186
  }
187
+ const allArbMatch = cls.match(/^border-(\[.+\])$/);
188
+ if (allArbMatch) {
189
+ const arbitraryValue = parseArbitraryBorderWidth(allArbMatch[1]);
190
+ if (arbitraryValue !== null) {
191
+ return { borderWidth: arbitraryValue };
192
+ }
193
+ }
110
194
  return null;
111
195
  }
112
196
  function parseBorderRadius(cls) {
113
- const allArbMatch = cls.match(/^rounded-\[(\d+)(?:px)?\]$/);
114
- if (allArbMatch) {
115
- return { borderRadius: parseInt(allArbMatch[1], 10) };
116
- }
117
- const cornerArbMatch = cls.match(/^rounded-(tl|tr|bl|br)-\[(\d+)(?:px)?\]$/);
118
- if (cornerArbMatch) {
119
- const corner = cornerArbMatch[1];
120
- const value = parseInt(cornerArbMatch[2], 10);
121
- const propMap = {
122
- tl: "borderTopLeftRadius",
123
- tr: "borderTopRightRadius",
124
- bl: "borderBottomLeftRadius",
125
- br: "borderBottomRightRadius"
126
- };
127
- return { [propMap[corner]]: value };
128
- }
129
- const sideArbMatch = cls.match(/^rounded-([trbl])-\[(\d+)(?:px)?\]$/);
130
- if (sideArbMatch) {
131
- const side = sideArbMatch[1];
132
- const value = parseInt(sideArbMatch[2], 10);
133
- const propMap = {
134
- t: ["borderTopLeftRadius", "borderTopRightRadius"],
135
- r: ["borderTopRightRadius", "borderBottomRightRadius"],
136
- b: ["borderBottomLeftRadius", "borderBottomRightRadius"],
137
- l: ["borderTopLeftRadius", "borderBottomLeftRadius"]
138
- };
139
- const result = {};
140
- propMap[side].forEach((prop) => result[prop] = value);
141
- return result;
142
- }
143
- const allMatch = cls.match(/^rounded(-\w+)?$/);
144
- if (allMatch) {
145
- const scaleKey = allMatch[1] ? allMatch[1].substring(1) : "";
146
- const value = BORDER_RADIUS_SCALE[scaleKey];
147
- if (value !== void 0) {
148
- return { borderRadius: value };
197
+ const withoutPrefix = cls.substring(7);
198
+ if (withoutPrefix === "") {
199
+ return { borderRadius: BORDER_RADIUS_SCALE[""] };
200
+ }
201
+ if (!withoutPrefix.startsWith("-")) {
202
+ return null;
203
+ }
204
+ const rest = withoutPrefix.substring(1);
205
+ if (rest === "") {
206
+ return null;
207
+ }
208
+ const cornerMatch = rest.match(/^(tl|tr|bl|br)(?:-(.+))?$/);
209
+ if (cornerMatch) {
210
+ const corner = cornerMatch[1];
211
+ const valueStr = cornerMatch[2] || "";
212
+ if (valueStr.startsWith("[")) {
213
+ const arbitraryValue = parseArbitraryBorderRadius(valueStr);
214
+ if (arbitraryValue !== null) {
215
+ return { [BORDER_RADIUS_CORNER_MAP[corner]]: arbitraryValue };
216
+ }
217
+ return null;
218
+ }
219
+ const scaleValue2 = BORDER_RADIUS_SCALE[valueStr];
220
+ if (scaleValue2 !== void 0) {
221
+ return { [BORDER_RADIUS_CORNER_MAP[corner]]: scaleValue2 };
149
222
  }
223
+ return null;
150
224
  }
151
- const sideMatch = cls.match(/^rounded-([trbl])(?:-(\w+))?$/);
225
+ const sideMatch = rest.match(/^([trbl])(?:-(.+))?$/);
152
226
  if (sideMatch) {
153
227
  const side = sideMatch[1];
154
- const scaleKey = sideMatch[2] || "";
155
- const value = BORDER_RADIUS_SCALE[scaleKey];
228
+ const valueStr = sideMatch[2] || "";
229
+ let value;
230
+ if (valueStr.startsWith("[")) {
231
+ const arbitraryValue = parseArbitraryBorderRadius(valueStr);
232
+ if (arbitraryValue !== null) {
233
+ value = arbitraryValue;
234
+ } else {
235
+ return null;
236
+ }
237
+ } else {
238
+ value = BORDER_RADIUS_SCALE[valueStr];
239
+ }
156
240
  if (value !== void 0) {
157
- const propMap = {
158
- t: ["borderTopLeftRadius", "borderTopRightRadius"],
159
- r: ["borderTopRightRadius", "borderBottomRightRadius"],
160
- b: ["borderBottomLeftRadius", "borderBottomRightRadius"],
161
- l: ["borderTopLeftRadius", "borderBottomLeftRadius"]
162
- };
163
241
  const result = {};
164
- propMap[side].forEach((prop) => result[prop] = value);
242
+ BORDER_RADIUS_SIDE_MAP[side].forEach((prop) => result[prop] = value);
165
243
  return result;
166
244
  }
245
+ return null;
167
246
  }
168
- const cornerMatch = cls.match(/^rounded-(tl|tr|bl|br)(?:-(\w+))?$/);
169
- if (cornerMatch) {
170
- const corner = cornerMatch[1];
171
- const scaleKey = cornerMatch[2] || "";
172
- const value = BORDER_RADIUS_SCALE[scaleKey];
173
- if (value !== void 0) {
174
- const propMap = {
175
- tl: "borderTopLeftRadius",
176
- tr: "borderTopRightRadius",
177
- bl: "borderBottomLeftRadius",
178
- br: "borderBottomRightRadius"
179
- };
180
- return { [propMap[corner]]: value };
247
+ if (rest.startsWith("[")) {
248
+ const arbitraryValue = parseArbitraryBorderRadius(rest);
249
+ if (arbitraryValue !== null) {
250
+ return { borderRadius: arbitraryValue };
181
251
  }
252
+ return null;
253
+ }
254
+ const scaleValue = BORDER_RADIUS_SCALE[rest];
255
+ if (scaleValue !== void 0) {
256
+ return { borderRadius: scaleValue };
182
257
  }
183
258
  return null;
184
259
  }
@@ -289,27 +364,86 @@ var COLORS = {
289
364
  black: "#000000",
290
365
  transparent: "transparent"
291
366
  };
367
+ function applyOpacity(hex, opacity) {
368
+ if (hex === "transparent") {
369
+ return "transparent";
370
+ }
371
+ const cleanHex = hex.replace(/^#/, "");
372
+ const fullHex = cleanHex.length === 3 ? cleanHex.split("").map((char) => char + char).join("") : cleanHex;
373
+ const alpha = Math.round(opacity / 100 * 255);
374
+ const alphaHex = alpha.toString(16).padStart(2, "0").toUpperCase();
375
+ return `#${fullHex.toUpperCase()}${alphaHex}`;
376
+ }
377
+ function parseArbitraryColor(value) {
378
+ const hexMatch = value.match(/^\[#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})\]$/);
379
+ if (hexMatch) {
380
+ const hex = hexMatch[1];
381
+ if (hex.length === 3) {
382
+ const expanded = hex.split("").map((char) => char + char).join("");
383
+ return `#${expanded}`;
384
+ }
385
+ return `#${hex}`;
386
+ }
387
+ if (value.startsWith("[") && value.endsWith("]")) {
388
+ if (process.env.NODE_ENV !== "production") {
389
+ console.warn(
390
+ `[react-native-tailwind] Unsupported arbitrary color value: ${value}. Only hex colors are supported (e.g., [#ff0000], [#f00], or [#ff0000aa]).`
391
+ );
392
+ }
393
+ return null;
394
+ }
395
+ return null;
396
+ }
292
397
  function parseColor(cls, customColors) {
293
398
  const getColor = (key) => {
294
399
  return customColors?.[key] ?? COLORS[key];
295
400
  };
401
+ const parseColorWithOpacity = (colorKey) => {
402
+ const opacityMatch = colorKey.match(/^(.+)\/(\d+)$/);
403
+ if (opacityMatch) {
404
+ const baseColorKey = opacityMatch[1];
405
+ const opacity = Number.parseInt(opacityMatch[2], 10);
406
+ if (opacity < 0 || opacity > 100) {
407
+ if (process.env.NODE_ENV !== "production") {
408
+ console.warn(
409
+ `[react-native-tailwind] Invalid opacity value: ${opacity}. Opacity must be between 0 and 100.`
410
+ );
411
+ }
412
+ return null;
413
+ }
414
+ const arbitraryColor2 = parseArbitraryColor(baseColorKey);
415
+ if (arbitraryColor2 !== null) {
416
+ return applyOpacity(arbitraryColor2, opacity);
417
+ }
418
+ const color = getColor(baseColorKey);
419
+ if (color) {
420
+ return applyOpacity(color, opacity);
421
+ }
422
+ return null;
423
+ }
424
+ const arbitraryColor = parseArbitraryColor(colorKey);
425
+ if (arbitraryColor !== null) {
426
+ return arbitraryColor;
427
+ }
428
+ return getColor(colorKey) ?? null;
429
+ };
296
430
  if (cls.startsWith("bg-")) {
297
431
  const colorKey = cls.substring(3);
298
- const color = getColor(colorKey);
432
+ const color = parseColorWithOpacity(colorKey);
299
433
  if (color) {
300
434
  return { backgroundColor: color };
301
435
  }
302
436
  }
303
437
  if (cls.startsWith("text-")) {
304
438
  const colorKey = cls.substring(5);
305
- const color = getColor(colorKey);
439
+ const color = parseColorWithOpacity(colorKey);
306
440
  if (color) {
307
441
  return { color };
308
442
  }
309
443
  }
310
444
  if (cls.startsWith("border-") && !cls.match(/^border-[0-9]/)) {
311
445
  const colorKey = cls.substring(7);
312
- const color = getColor(colorKey);
446
+ const color = parseColorWithOpacity(colorKey);
313
447
  if (color) {
314
448
  return { borderColor: color };
315
449
  }
@@ -384,10 +518,203 @@ var OVERFLOW_MAP = {
384
518
  "overflow-visible": { overflow: "visible" },
385
519
  "overflow-scroll": { overflow: "scroll" }
386
520
  };
521
+ var Z_INDEX_SCALE = {
522
+ 0: 0,
523
+ 10: 10,
524
+ 20: 20,
525
+ 30: 30,
526
+ 40: 40,
527
+ 50: 50,
528
+ auto: 0
529
+ // React Native doesn't have 'auto', default to 0
530
+ };
531
+ var INSET_SCALE = {
532
+ 0: 0,
533
+ 0.5: 2,
534
+ 1: 4,
535
+ 1.5: 6,
536
+ 2: 8,
537
+ 2.5: 10,
538
+ 3: 12,
539
+ 3.5: 14,
540
+ 4: 16,
541
+ 5: 20,
542
+ 6: 24,
543
+ 8: 32,
544
+ 10: 40,
545
+ 12: 48,
546
+ 16: 64,
547
+ 20: 80,
548
+ 24: 96
549
+ };
387
550
  function parseLayout(cls) {
551
+ if (cls.startsWith("z-")) {
552
+ const zKey = cls.substring(2);
553
+ const zValue = Z_INDEX_SCALE[zKey];
554
+ if (zValue !== void 0) {
555
+ return { zIndex: zValue };
556
+ }
557
+ }
558
+ if (cls.startsWith("top-")) {
559
+ const topKey = cls.substring(4);
560
+ if (topKey === "auto") {
561
+ return {};
562
+ }
563
+ const topValue = INSET_SCALE[topKey];
564
+ if (topValue !== void 0) {
565
+ return { top: topValue };
566
+ }
567
+ }
568
+ if (cls.startsWith("right-")) {
569
+ const rightKey = cls.substring(6);
570
+ if (rightKey === "auto") {
571
+ return {};
572
+ }
573
+ const rightValue = INSET_SCALE[rightKey];
574
+ if (rightValue !== void 0) {
575
+ return { right: rightValue };
576
+ }
577
+ }
578
+ if (cls.startsWith("bottom-")) {
579
+ const bottomKey = cls.substring(7);
580
+ if (bottomKey === "auto") {
581
+ return {};
582
+ }
583
+ const bottomValue = INSET_SCALE[bottomKey];
584
+ if (bottomValue !== void 0) {
585
+ return { bottom: bottomValue };
586
+ }
587
+ }
588
+ if (cls.startsWith("left-")) {
589
+ const leftKey = cls.substring(5);
590
+ if (leftKey === "auto") {
591
+ return {};
592
+ }
593
+ const leftValue = INSET_SCALE[leftKey];
594
+ if (leftValue !== void 0) {
595
+ return { left: leftValue };
596
+ }
597
+ }
598
+ if (cls.startsWith("inset-")) {
599
+ const insetKey = cls.substring(6);
600
+ const insetValue = INSET_SCALE[insetKey];
601
+ if (insetValue !== void 0) {
602
+ return { top: insetValue, right: insetValue, bottom: insetValue, left: insetValue };
603
+ }
604
+ }
605
+ if (cls.startsWith("inset-x-")) {
606
+ const insetKey = cls.substring(8);
607
+ const insetValue = INSET_SCALE[insetKey];
608
+ if (insetValue !== void 0) {
609
+ return { left: insetValue, right: insetValue };
610
+ }
611
+ }
612
+ if (cls.startsWith("inset-y-")) {
613
+ const insetKey = cls.substring(8);
614
+ const insetValue = INSET_SCALE[insetKey];
615
+ if (insetValue !== void 0) {
616
+ return { top: insetValue, bottom: insetValue };
617
+ }
618
+ }
388
619
  return DISPLAY_MAP[cls] ?? FLEX_DIRECTION_MAP[cls] ?? FLEX_WRAP_MAP[cls] ?? FLEX_MAP[cls] ?? GROW_SHRINK_MAP[cls] ?? JUSTIFY_CONTENT_MAP[cls] ?? ALIGN_ITEMS_MAP[cls] ?? ALIGN_SELF_MAP[cls] ?? ALIGN_CONTENT_MAP[cls] ?? POSITION_MAP[cls] ?? OVERFLOW_MAP[cls] ?? null;
389
620
  }
390
621
 
622
+ // src/parser/shadows.ts
623
+ var import_react_native = require("react-native");
624
+ var SHADOW_DEFINITIONS = {
625
+ "shadow-sm": {
626
+ ios: {
627
+ shadowColor: "#000000",
628
+ shadowOffset: { width: 0, height: 1 },
629
+ shadowOpacity: 0.05,
630
+ shadowRadius: 1
631
+ },
632
+ android: {
633
+ elevation: 1
634
+ }
635
+ },
636
+ shadow: {
637
+ ios: {
638
+ shadowColor: "#000000",
639
+ shadowOffset: { width: 0, height: 1 },
640
+ shadowOpacity: 0.1,
641
+ shadowRadius: 2
642
+ },
643
+ android: {
644
+ elevation: 2
645
+ }
646
+ },
647
+ "shadow-md": {
648
+ ios: {
649
+ shadowColor: "#000000",
650
+ shadowOffset: { width: 0, height: 3 },
651
+ shadowOpacity: 0.15,
652
+ shadowRadius: 4
653
+ },
654
+ android: {
655
+ elevation: 4
656
+ }
657
+ },
658
+ "shadow-lg": {
659
+ ios: {
660
+ shadowColor: "#000000",
661
+ shadowOffset: { width: 0, height: 6 },
662
+ shadowOpacity: 0.2,
663
+ shadowRadius: 8
664
+ },
665
+ android: {
666
+ elevation: 8
667
+ }
668
+ },
669
+ "shadow-xl": {
670
+ ios: {
671
+ shadowColor: "#000000",
672
+ shadowOffset: { width: 0, height: 10 },
673
+ shadowOpacity: 0.25,
674
+ shadowRadius: 12
675
+ },
676
+ android: {
677
+ elevation: 12
678
+ }
679
+ },
680
+ "shadow-2xl": {
681
+ ios: {
682
+ shadowColor: "#000000",
683
+ shadowOffset: { width: 0, height: 20 },
684
+ shadowOpacity: 0.3,
685
+ shadowRadius: 24
686
+ },
687
+ android: {
688
+ elevation: 16
689
+ }
690
+ },
691
+ "shadow-none": {
692
+ ios: {
693
+ shadowColor: "transparent",
694
+ shadowOffset: { width: 0, height: 0 },
695
+ shadowOpacity: 0,
696
+ shadowRadius: 0
697
+ },
698
+ android: {
699
+ elevation: 0
700
+ }
701
+ }
702
+ };
703
+ function buildShadowScale() {
704
+ const scale = {};
705
+ for (const [key, value] of Object.entries(SHADOW_DEFINITIONS)) {
706
+ scale[key] = import_react_native.Platform.select(value);
707
+ }
708
+ return scale;
709
+ }
710
+ var SHADOW_SCALE = buildShadowScale();
711
+ function parseShadow(cls) {
712
+ if (cls in SHADOW_SCALE) {
713
+ return SHADOW_SCALE[cls];
714
+ }
715
+ return null;
716
+ }
717
+
391
718
  // src/parser/sizing.ts
392
719
  var SIZE_SCALE = {
393
720
  0: 0,
@@ -599,131 +926,99 @@ var SPACING_SCALE = {
599
926
  80: 320,
600
927
  96: 384
601
928
  };
602
- function parseSpacing(cls) {
603
- if (cls.startsWith("m-") || cls.startsWith("m")) {
604
- return parseMargin(cls);
605
- }
606
- if (cls.startsWith("p-") || cls.startsWith("p")) {
607
- return parsePadding(cls);
929
+ function parseArbitrarySpacing(value) {
930
+ const pxMatch = value.match(/^\[(\d+)(?:px)?\]$/);
931
+ if (pxMatch) {
932
+ return parseInt(pxMatch[1], 10);
608
933
  }
609
- if (cls.startsWith("gap-")) {
610
- return parseGap(cls);
934
+ if (value.startsWith("[") && value.endsWith("]")) {
935
+ if (process.env.NODE_ENV !== "production") {
936
+ console.warn(
937
+ `[react-native-tailwind] Unsupported arbitrary spacing value: ${value}. Only px values are supported (e.g., [16px] or [16]).`
938
+ );
939
+ }
940
+ return null;
611
941
  }
612
942
  return null;
613
943
  }
614
- function parseMargin(cls) {
615
- const allMatch = cls.match(/^m-(\d+(?:\.\d+)?)$/);
616
- if (allMatch) {
617
- const value = SPACING_SCALE[allMatch[1]];
618
- if (value !== void 0) {
619
- return { margin: value };
944
+ function parseSpacing(cls) {
945
+ const marginMatch = cls.match(/^m([xytrbls]?)-(.+)$/);
946
+ if (marginMatch) {
947
+ const [, dir, valueStr] = marginMatch;
948
+ const arbitraryValue = parseArbitrarySpacing(valueStr);
949
+ if (arbitraryValue !== null) {
950
+ return getMarginStyle(dir, arbitraryValue);
620
951
  }
621
- }
622
- const xMatch = cls.match(/^mx-(\d+(?:\.\d+)?)$/);
623
- if (xMatch) {
624
- const value = SPACING_SCALE[xMatch[1]];
625
- if (value !== void 0) {
626
- return { marginHorizontal: value };
952
+ const scaleValue = SPACING_SCALE[valueStr];
953
+ if (scaleValue !== void 0) {
954
+ return getMarginStyle(dir, scaleValue);
627
955
  }
628
956
  }
629
- const yMatch = cls.match(/^my-(\d+(?:\.\d+)?)$/);
630
- if (yMatch) {
631
- const value = SPACING_SCALE[yMatch[1]];
632
- if (value !== void 0) {
633
- return { marginVertical: value };
957
+ const paddingMatch = cls.match(/^p([xytrbls]?)-(.+)$/);
958
+ if (paddingMatch) {
959
+ const [, dir, valueStr] = paddingMatch;
960
+ const arbitraryValue = parseArbitrarySpacing(valueStr);
961
+ if (arbitraryValue !== null) {
962
+ return getPaddingStyle(dir, arbitraryValue);
634
963
  }
635
- }
636
- const tMatch = cls.match(/^mt-(\d+(?:\.\d+)?)$/);
637
- if (tMatch) {
638
- const value = SPACING_SCALE[tMatch[1]];
639
- if (value !== void 0) {
640
- return { marginTop: value };
964
+ const scaleValue = SPACING_SCALE[valueStr];
965
+ if (scaleValue !== void 0) {
966
+ return getPaddingStyle(dir, scaleValue);
641
967
  }
642
968
  }
643
- const rMatch = cls.match(/^mr-(\d+(?:\.\d+)?)$/);
644
- if (rMatch) {
645
- const value = SPACING_SCALE[rMatch[1]];
646
- if (value !== void 0) {
647
- return { marginRight: value };
969
+ const gapMatch = cls.match(/^gap-(.+)$/);
970
+ if (gapMatch) {
971
+ const valueStr = gapMatch[1];
972
+ const arbitraryValue = parseArbitrarySpacing(valueStr);
973
+ if (arbitraryValue !== null) {
974
+ return { gap: arbitraryValue };
648
975
  }
649
- }
650
- const bMatch = cls.match(/^mb-(\d+(?:\.\d+)?)$/);
651
- if (bMatch) {
652
- const value = SPACING_SCALE[bMatch[1]];
653
- if (value !== void 0) {
654
- return { marginBottom: value };
976
+ const scaleValue = SPACING_SCALE[valueStr];
977
+ if (scaleValue !== void 0) {
978
+ return { gap: scaleValue };
655
979
  }
656
980
  }
657
- const lMatch = cls.match(/^ml-(\d+(?:\.\d+)?)$/);
658
- if (lMatch) {
659
- const value = SPACING_SCALE[lMatch[1]];
660
- if (value !== void 0) {
981
+ return null;
982
+ }
983
+ function getMarginStyle(dir, value) {
984
+ switch (dir) {
985
+ case "":
986
+ return { margin: value };
987
+ case "x":
988
+ return { marginHorizontal: value };
989
+ case "y":
990
+ return { marginVertical: value };
991
+ case "t":
992
+ return { marginTop: value };
993
+ case "r":
994
+ return { marginRight: value };
995
+ case "b":
996
+ return { marginBottom: value };
997
+ case "l":
661
998
  return { marginLeft: value };
662
- }
999
+ default:
1000
+ return {};
663
1001
  }
664
- return null;
665
1002
  }
666
- function parsePadding(cls) {
667
- const allMatch = cls.match(/^p-(\d+(?:\.\d+)?)$/);
668
- if (allMatch) {
669
- const value = SPACING_SCALE[allMatch[1]];
670
- if (value !== void 0) {
1003
+ function getPaddingStyle(dir, value) {
1004
+ switch (dir) {
1005
+ case "":
671
1006
  return { padding: value };
672
- }
673
- }
674
- const xMatch = cls.match(/^px-(\d+(?:\.\d+)?)$/);
675
- if (xMatch) {
676
- const value = SPACING_SCALE[xMatch[1]];
677
- if (value !== void 0) {
1007
+ case "x":
678
1008
  return { paddingHorizontal: value };
679
- }
680
- }
681
- const yMatch = cls.match(/^py-(\d+(?:\.\d+)?)$/);
682
- if (yMatch) {
683
- const value = SPACING_SCALE[yMatch[1]];
684
- if (value !== void 0) {
1009
+ case "y":
685
1010
  return { paddingVertical: value };
686
- }
687
- }
688
- const tMatch = cls.match(/^pt-(\d+(?:\.\d+)?)$/);
689
- if (tMatch) {
690
- const value = SPACING_SCALE[tMatch[1]];
691
- if (value !== void 0) {
1011
+ case "t":
692
1012
  return { paddingTop: value };
693
- }
694
- }
695
- const rMatch = cls.match(/^pr-(\d+(?:\.\d+)?)$/);
696
- if (rMatch) {
697
- const value = SPACING_SCALE[rMatch[1]];
698
- if (value !== void 0) {
1013
+ case "r":
699
1014
  return { paddingRight: value };
700
- }
701
- }
702
- const bMatch = cls.match(/^pb-(\d+(?:\.\d+)?)$/);
703
- if (bMatch) {
704
- const value = SPACING_SCALE[bMatch[1]];
705
- if (value !== void 0) {
1015
+ case "b":
706
1016
  return { paddingBottom: value };
707
- }
708
- }
709
- const lMatch = cls.match(/^pl-(\d+(?:\.\d+)?)$/);
710
- if (lMatch) {
711
- const value = SPACING_SCALE[lMatch[1]];
712
- if (value !== void 0) {
1017
+ case "l":
713
1018
  return { paddingLeft: value };
714
- }
1019
+ default:
1020
+ return {};
715
1021
  }
716
- return null;
717
- }
718
- function parseGap(cls) {
719
- const match = cls.match(/^gap-(\d+(?:\.\d+)?)$/);
720
- if (match) {
721
- const value = SPACING_SCALE[match[1]];
722
- if (value !== void 0) {
723
- return { gap: value };
724
- }
725
- }
726
- return null;
727
1022
  }
728
1023
 
729
1024
  // src/parser/typography.ts
@@ -742,99 +1037,142 @@ var FONT_SIZES = {
742
1037
  "8xl": 96,
743
1038
  "9xl": 128
744
1039
  };
1040
+ var FONT_WEIGHT_MAP = {
1041
+ "font-thin": { fontWeight: "100" },
1042
+ "font-extralight": { fontWeight: "200" },
1043
+ "font-light": { fontWeight: "300" },
1044
+ "font-normal": { fontWeight: "400" },
1045
+ "font-medium": { fontWeight: "500" },
1046
+ "font-semibold": { fontWeight: "600" },
1047
+ "font-bold": { fontWeight: "700" },
1048
+ "font-extrabold": { fontWeight: "800" },
1049
+ "font-black": { fontWeight: "900" }
1050
+ };
1051
+ var FONT_STYLE_MAP = {
1052
+ italic: { fontStyle: "italic" },
1053
+ "not-italic": { fontStyle: "normal" }
1054
+ };
1055
+ var TEXT_ALIGN_MAP = {
1056
+ "text-left": { textAlign: "left" },
1057
+ "text-center": { textAlign: "center" },
1058
+ "text-right": { textAlign: "right" },
1059
+ "text-justify": { textAlign: "justify" }
1060
+ };
1061
+ var TEXT_DECORATION_MAP = {
1062
+ underline: { textDecorationLine: "underline" },
1063
+ "line-through": { textDecorationLine: "line-through" },
1064
+ "no-underline": { textDecorationLine: "none" }
1065
+ };
1066
+ var TEXT_TRANSFORM_MAP = {
1067
+ uppercase: { textTransform: "uppercase" },
1068
+ lowercase: { textTransform: "lowercase" },
1069
+ capitalize: { textTransform: "capitalize" },
1070
+ "normal-case": { textTransform: "none" }
1071
+ };
1072
+ var LINE_HEIGHT_MAP = {
1073
+ "leading-none": { lineHeight: 16 },
1074
+ "leading-tight": { lineHeight: 20 },
1075
+ "leading-snug": { lineHeight: 22 },
1076
+ "leading-normal": { lineHeight: 24 },
1077
+ "leading-relaxed": { lineHeight: 28 },
1078
+ "leading-loose": { lineHeight: 32 }
1079
+ };
1080
+ var TRACKING_MAP = {
1081
+ "tracking-tighter": { letterSpacing: -0.8 },
1082
+ "tracking-tight": { letterSpacing: -0.4 },
1083
+ "tracking-normal": { letterSpacing: 0 },
1084
+ "tracking-wide": { letterSpacing: 0.4 },
1085
+ "tracking-wider": { letterSpacing: 0.8 },
1086
+ "tracking-widest": { letterSpacing: 1.6 }
1087
+ };
1088
+ function parseArbitraryFontSize(value) {
1089
+ const pxMatch = value.match(/^\[(\d+)(?:px)?\]$/);
1090
+ if (pxMatch) {
1091
+ return parseInt(pxMatch[1], 10);
1092
+ }
1093
+ if (value.startsWith("[") && value.endsWith("]")) {
1094
+ if (process.env.NODE_ENV !== "production") {
1095
+ console.warn(
1096
+ `[react-native-tailwind] Unsupported arbitrary font size value: ${value}. Only px values are supported (e.g., [18px] or [18]).`
1097
+ );
1098
+ }
1099
+ return null;
1100
+ }
1101
+ return null;
1102
+ }
1103
+ function parseArbitraryLineHeight(value) {
1104
+ const pxMatch = value.match(/^\[(\d+)(?:px)?\]$/);
1105
+ if (pxMatch) {
1106
+ return parseInt(pxMatch[1], 10);
1107
+ }
1108
+ if (value.startsWith("[") && value.endsWith("]")) {
1109
+ if (process.env.NODE_ENV !== "production") {
1110
+ console.warn(
1111
+ `[react-native-tailwind] Unsupported arbitrary line height value: ${value}. Only px values are supported (e.g., [24px] or [24]).`
1112
+ );
1113
+ }
1114
+ return null;
1115
+ }
1116
+ return null;
1117
+ }
745
1118
  function parseTypography(cls) {
746
1119
  if (cls.startsWith("text-")) {
747
1120
  const sizeKey = cls.substring(5);
1121
+ const arbitraryValue = parseArbitraryFontSize(sizeKey);
1122
+ if (arbitraryValue !== null) {
1123
+ return { fontSize: arbitraryValue };
1124
+ }
748
1125
  const fontSize = FONT_SIZES[sizeKey];
749
1126
  if (fontSize !== void 0) {
750
1127
  return { fontSize };
751
1128
  }
752
1129
  }
753
- if (cls === "font-thin") {
754
- return { fontWeight: "100" };
755
- }
756
- if (cls === "font-extralight") {
757
- return { fontWeight: "200" };
758
- }
759
- if (cls === "font-light") {
760
- return { fontWeight: "300" };
761
- }
762
- if (cls === "font-normal") {
763
- return { fontWeight: "400" };
764
- }
765
- if (cls === "font-medium") {
766
- return { fontWeight: "500" };
767
- }
768
- if (cls === "font-semibold") {
769
- return { fontWeight: "600" };
770
- }
771
- if (cls === "font-bold") {
772
- return { fontWeight: "700" };
773
- }
774
- if (cls === "font-extrabold") {
775
- return { fontWeight: "800" };
776
- }
777
- if (cls === "font-black") {
778
- return { fontWeight: "900" };
779
- }
780
- if (cls === "italic") {
781
- return { fontStyle: "italic" };
782
- }
783
- if (cls === "not-italic") {
784
- return { fontStyle: "normal" };
785
- }
786
- if (cls === "text-left") {
787
- return { textAlign: "left" };
788
- }
789
- if (cls === "text-center") {
790
- return { textAlign: "center" };
791
- }
792
- if (cls === "text-right") {
793
- return { textAlign: "right" };
794
- }
795
- if (cls === "text-justify") {
796
- return { textAlign: "justify" };
797
- }
798
- if (cls === "underline") {
799
- return { textDecorationLine: "underline" };
800
- }
801
- if (cls === "line-through") {
802
- return { textDecorationLine: "line-through" };
803
- }
804
- if (cls === "no-underline") {
805
- return { textDecorationLine: "none" };
806
- }
807
- if (cls === "uppercase") {
808
- return { textTransform: "uppercase" };
809
- }
810
- if (cls === "lowercase") {
811
- return { textTransform: "lowercase" };
812
- }
813
- if (cls === "capitalize") {
814
- return { textTransform: "capitalize" };
815
- }
816
- if (cls === "normal-case") {
817
- return { textTransform: "none" };
818
- }
819
- if (cls === "leading-none") {
820
- return { lineHeight: 16 };
1130
+ if (cls.startsWith("leading-")) {
1131
+ const heightKey = cls.substring(8);
1132
+ const arbitraryValue = parseArbitraryLineHeight(heightKey);
1133
+ if (arbitraryValue !== null) {
1134
+ return { lineHeight: arbitraryValue };
1135
+ }
821
1136
  }
822
- if (cls === "leading-tight") {
823
- return { lineHeight: 20 };
1137
+ return FONT_WEIGHT_MAP[cls] ?? FONT_STYLE_MAP[cls] ?? TEXT_ALIGN_MAP[cls] ?? TEXT_DECORATION_MAP[cls] ?? TEXT_TRANSFORM_MAP[cls] ?? LINE_HEIGHT_MAP[cls] ?? TRACKING_MAP[cls] ?? null;
1138
+ }
1139
+
1140
+ // src/parser/modifiers.ts
1141
+ var SUPPORTED_MODIFIERS = ["active", "hover", "focus", "disabled"];
1142
+ function parseModifier(cls) {
1143
+ const colonIndex = cls.indexOf(":");
1144
+ if (colonIndex === -1) {
1145
+ return null;
824
1146
  }
825
- if (cls === "leading-snug") {
826
- return { lineHeight: 22 };
1147
+ const potentialModifier = cls.slice(0, colonIndex);
1148
+ const baseClass = cls.slice(colonIndex + 1);
1149
+ if (!SUPPORTED_MODIFIERS.includes(potentialModifier)) {
1150
+ return null;
827
1151
  }
828
- if (cls === "leading-normal") {
829
- return { lineHeight: 24 };
1152
+ if (baseClass.includes(":")) {
1153
+ return null;
830
1154
  }
831
- if (cls === "leading-relaxed") {
832
- return { lineHeight: 28 };
1155
+ if (!baseClass) {
1156
+ return null;
833
1157
  }
834
- if (cls === "leading-loose") {
835
- return { lineHeight: 32 };
1158
+ return {
1159
+ modifier: potentialModifier,
1160
+ baseClass
1161
+ };
1162
+ }
1163
+ function splitModifierClasses(className) {
1164
+ const classes = className.trim().split(/\s+/).filter(Boolean);
1165
+ const baseClasses = [];
1166
+ const modifierClasses = [];
1167
+ for (const cls of classes) {
1168
+ const parsed = parseModifier(cls);
1169
+ if (parsed) {
1170
+ modifierClasses.push(parsed);
1171
+ } else {
1172
+ baseClasses.push(cls);
1173
+ }
836
1174
  }
837
- return null;
1175
+ return { baseClasses, modifierClasses };
838
1176
  }
839
1177
 
840
1178
  // src/parser/index.ts
@@ -850,11 +1188,13 @@ function parseClassName(className, customColors) {
850
1188
  function parseClass(cls, customColors) {
851
1189
  const parsers = [
852
1190
  parseSpacing,
1191
+ parseBorder,
853
1192
  (cls2) => parseColor(cls2, customColors),
854
1193
  parseLayout,
855
1194
  parseTypography,
856
- parseBorder,
857
- parseSizing
1195
+ parseSizing,
1196
+ parseShadow,
1197
+ parseAspectRatio
858
1198
  ];
859
1199
  for (const parser of parsers) {
860
1200
  const result = parser(cls);
@@ -953,12 +1293,16 @@ function extractCustomColors(filename) {
953
1293
  var STYLES_IDENTIFIER = "_twStyles";
954
1294
  var SUPPORTED_CLASS_ATTRIBUTES = [
955
1295
  "className",
1296
+ "containerClassName",
956
1297
  "contentContainerClassName",
957
1298
  "columnWrapperClassName",
958
1299
  "ListHeaderComponentClassName",
959
1300
  "ListFooterComponentClassName"
960
1301
  ];
961
1302
  function getTargetStyleProp(attributeName) {
1303
+ if (attributeName === "containerClassName") {
1304
+ return "containerStyle";
1305
+ }
962
1306
  if (attributeName === "contentContainerClassName") {
963
1307
  return "contentContainerStyle";
964
1308
  }
@@ -973,6 +1317,33 @@ function getTargetStyleProp(attributeName) {
973
1317
  }
974
1318
  return "style";
975
1319
  }
1320
+ function getComponentModifierSupport(jsxElement, t) {
1321
+ if (!t.isJSXOpeningElement(jsxElement)) {
1322
+ return null;
1323
+ }
1324
+ const name = jsxElement.name;
1325
+ let componentName = null;
1326
+ if (t.isJSXIdentifier(name)) {
1327
+ componentName = name.name;
1328
+ }
1329
+ if (t.isJSXMemberExpression(name)) {
1330
+ const property = name.property;
1331
+ if (t.isJSXIdentifier(property)) {
1332
+ componentName = property.name;
1333
+ }
1334
+ }
1335
+ if (!componentName) {
1336
+ return null;
1337
+ }
1338
+ switch (componentName) {
1339
+ case "Pressable":
1340
+ return { component: "Pressable", supportedModifiers: ["active", "hover", "focus", "disabled"] };
1341
+ case "TextInput":
1342
+ return { component: "TextInput", supportedModifiers: ["focus", "disabled"] };
1343
+ default:
1344
+ return null;
1345
+ }
1346
+ }
976
1347
  function processDynamicExpression(expression, state, t) {
977
1348
  if (t.isTemplateLiteral(expression)) {
978
1349
  return processTemplateLiteral(expression, state, t);
@@ -1069,6 +1440,77 @@ function processStringOrExpression(node, state, t) {
1069
1440
  }
1070
1441
  return null;
1071
1442
  }
1443
+ function processStaticClassNameWithModifiers(className, state, t) {
1444
+ const { baseClasses, modifierClasses } = splitModifierClasses(className);
1445
+ let baseStyleExpression = null;
1446
+ if (baseClasses.length > 0) {
1447
+ const baseClassName = baseClasses.join(" ");
1448
+ const baseStyleObject = parseClassName2(baseClassName, state.customColors);
1449
+ const baseStyleKey = generateStyleKey2(baseClassName);
1450
+ state.styleRegistry.set(baseStyleKey, baseStyleObject);
1451
+ baseStyleExpression = t.memberExpression(t.identifier(STYLES_IDENTIFIER), t.identifier(baseStyleKey));
1452
+ }
1453
+ const modifiersByType = /* @__PURE__ */ new Map();
1454
+ for (const mod of modifierClasses) {
1455
+ if (!modifiersByType.has(mod.modifier)) {
1456
+ modifiersByType.set(mod.modifier, []);
1457
+ }
1458
+ const modGroup = modifiersByType.get(mod.modifier);
1459
+ if (modGroup) {
1460
+ modGroup.push(mod);
1461
+ }
1462
+ }
1463
+ const styleArrayElements = [];
1464
+ if (baseStyleExpression) {
1465
+ styleArrayElements.push(baseStyleExpression);
1466
+ }
1467
+ for (const [modifierType, modifiers] of modifiersByType) {
1468
+ const modifierClassNames = modifiers.map((m) => m.baseClass).join(" ");
1469
+ const modifierStyleObject = parseClassName2(modifierClassNames, state.customColors);
1470
+ const modifierStyleKey = generateStyleKey2(`${modifierType}_${modifierClassNames}`);
1471
+ state.styleRegistry.set(modifierStyleKey, modifierStyleObject);
1472
+ const stateProperty = getStatePropertyForModifier(modifierType);
1473
+ const conditionalExpression = t.logicalExpression(
1474
+ "&&",
1475
+ t.identifier(stateProperty),
1476
+ t.memberExpression(t.identifier(STYLES_IDENTIFIER), t.identifier(modifierStyleKey))
1477
+ );
1478
+ styleArrayElements.push(conditionalExpression);
1479
+ }
1480
+ if (styleArrayElements.length === 1) {
1481
+ return styleArrayElements[0];
1482
+ }
1483
+ return t.arrayExpression(styleArrayElements);
1484
+ }
1485
+ function getStatePropertyForModifier(modifier) {
1486
+ switch (modifier) {
1487
+ case "active":
1488
+ return "pressed";
1489
+ case "hover":
1490
+ return "hovered";
1491
+ case "focus":
1492
+ return "focused";
1493
+ case "disabled":
1494
+ return "disabled";
1495
+ default:
1496
+ return "pressed";
1497
+ }
1498
+ }
1499
+ function createStyleFunction(styleExpression, modifierTypes, t) {
1500
+ const paramProperties = [];
1501
+ const usedStateProps = /* @__PURE__ */ new Set();
1502
+ for (const modifierType of modifierTypes) {
1503
+ const stateProperty = getStatePropertyForModifier(modifierType);
1504
+ if (!usedStateProps.has(stateProperty)) {
1505
+ usedStateProps.add(stateProperty);
1506
+ paramProperties.push(
1507
+ t.objectProperty(t.identifier(stateProperty), t.identifier(stateProperty), false, true)
1508
+ );
1509
+ }
1510
+ }
1511
+ const param = t.objectPattern(paramProperties);
1512
+ return t.arrowFunctionExpression([param], styleExpression);
1513
+ }
1072
1514
  function reactNativeTailwindBabelPlugin({
1073
1515
  types: t
1074
1516
  }) {
@@ -1126,6 +1568,69 @@ function reactNativeTailwindBabelPlugin({
1126
1568
  return;
1127
1569
  }
1128
1570
  state.hasClassNames = true;
1571
+ const { baseClasses, modifierClasses } = splitModifierClasses(className);
1572
+ if (modifierClasses.length > 0) {
1573
+ const jsxOpeningElement = path2.parent;
1574
+ const componentSupport = getComponentModifierSupport(jsxOpeningElement, t);
1575
+ if (componentSupport) {
1576
+ const usedModifiers = Array.from(new Set(modifierClasses.map((m) => m.modifier)));
1577
+ const unsupportedModifiers = usedModifiers.filter(
1578
+ (mod) => !componentSupport.supportedModifiers.includes(mod)
1579
+ );
1580
+ if (unsupportedModifiers.length > 0) {
1581
+ if (process.env.NODE_ENV !== "production") {
1582
+ console.warn(
1583
+ `[react-native-tailwind] Modifiers (${unsupportedModifiers.map((m) => `${m}:`).join(", ")}) are not supported on ${componentSupport.component} component at ${state.file.opts.filename ?? "unknown"}. Supported modifiers: ${componentSupport.supportedModifiers.join(", ")}`
1584
+ );
1585
+ }
1586
+ const supportedModifierClasses = modifierClasses.filter(
1587
+ (m) => componentSupport.supportedModifiers.includes(m.modifier)
1588
+ );
1589
+ if (supportedModifierClasses.length === 0) {
1590
+ } else {
1591
+ const filteredClassName = baseClasses.join(" ") + " " + supportedModifierClasses.map((m) => `${m.modifier}:${m.baseClass}`).join(" ");
1592
+ const styleExpression = processStaticClassNameWithModifiers(
1593
+ filteredClassName.trim(),
1594
+ state,
1595
+ t
1596
+ );
1597
+ const modifierTypes = Array.from(new Set(supportedModifierClasses.map((m) => m.modifier)));
1598
+ const styleFunctionExpression = createStyleFunction(styleExpression, modifierTypes, t);
1599
+ const parent2 = path2.parent;
1600
+ const styleAttribute2 = parent2.attributes.find(
1601
+ (attr) => t.isJSXAttribute(attr) && attr.name.name === targetStyleProp
1602
+ );
1603
+ if (styleAttribute2) {
1604
+ mergeStyleFunctionAttribute(path2, styleAttribute2, styleFunctionExpression, t);
1605
+ } else {
1606
+ replaceWithStyleFunctionAttribute(path2, styleFunctionExpression, targetStyleProp, t);
1607
+ }
1608
+ return;
1609
+ }
1610
+ } else {
1611
+ const styleExpression = processStaticClassNameWithModifiers(className, state, t);
1612
+ const modifierTypes = usedModifiers;
1613
+ const styleFunctionExpression = createStyleFunction(styleExpression, modifierTypes, t);
1614
+ const parent2 = path2.parent;
1615
+ const styleAttribute2 = parent2.attributes.find(
1616
+ (attr) => t.isJSXAttribute(attr) && attr.name.name === targetStyleProp
1617
+ );
1618
+ if (styleAttribute2) {
1619
+ mergeStyleFunctionAttribute(path2, styleAttribute2, styleFunctionExpression, t);
1620
+ } else {
1621
+ replaceWithStyleFunctionAttribute(path2, styleFunctionExpression, targetStyleProp, t);
1622
+ }
1623
+ return;
1624
+ }
1625
+ } else {
1626
+ if (process.env.NODE_ENV !== "production") {
1627
+ const usedModifiers = Array.from(new Set(modifierClasses.map((m) => m.modifier)));
1628
+ console.warn(
1629
+ `[react-native-tailwind] Modifiers (${usedModifiers.map((m) => `${m}:`).join(", ")}) can only be used on compatible components (Pressable, TextInput). Found on unsupported element at ${state.file.opts.filename ?? "unknown"}`
1630
+ );
1631
+ }
1632
+ }
1633
+ }
1129
1634
  const styleObject = parseClassName2(className, state.customColors);
1130
1635
  const styleKey = generateStyleKey2(className);
1131
1636
  state.styleRegistry.set(styleKey, styleObject);
@@ -1219,6 +1724,31 @@ function mergeDynamicStyleAttribute(classNamePath, styleAttribute, result, t) {
1219
1724
  styleAttribute.value = t.jsxExpressionContainer(styleArray);
1220
1725
  classNamePath.remove();
1221
1726
  }
1727
+ function replaceWithStyleFunctionAttribute(classNamePath, styleFunctionExpression, targetStyleProp, t) {
1728
+ const styleAttribute = t.jsxAttribute(
1729
+ t.jsxIdentifier(targetStyleProp),
1730
+ t.jsxExpressionContainer(styleFunctionExpression)
1731
+ );
1732
+ classNamePath.replaceWith(styleAttribute);
1733
+ }
1734
+ function mergeStyleFunctionAttribute(classNamePath, styleAttribute, styleFunctionExpression, t) {
1735
+ const existingStyle = styleAttribute.value.expression;
1736
+ if (t.isArrowFunctionExpression(existingStyle) || t.isFunctionExpression(existingStyle)) {
1737
+ const paramIdentifier = t.identifier("_state");
1738
+ const newFunctionCall = t.callExpression(styleFunctionExpression, [paramIdentifier]);
1739
+ const existingFunctionCall = t.callExpression(existingStyle, [paramIdentifier]);
1740
+ const mergedArray = t.arrayExpression([newFunctionCall, existingFunctionCall]);
1741
+ const wrapperFunction = t.arrowFunctionExpression([paramIdentifier], mergedArray);
1742
+ styleAttribute.value = t.jsxExpressionContainer(wrapperFunction);
1743
+ } else {
1744
+ const paramIdentifier = t.identifier("_state");
1745
+ const functionCall = t.callExpression(styleFunctionExpression, [paramIdentifier]);
1746
+ const mergedArray = t.arrayExpression([functionCall, existingStyle]);
1747
+ const wrapperFunction = t.arrowFunctionExpression([paramIdentifier], mergedArray);
1748
+ styleAttribute.value = t.jsxExpressionContainer(wrapperFunction);
1749
+ }
1750
+ classNamePath.remove();
1751
+ }
1222
1752
  function injectStyles(path2, styleRegistry, t) {
1223
1753
  const styleProperties = [];
1224
1754
  for (const [key, styleObject] of styleRegistry) {