rnwind 0.0.1 → 0.0.2

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 (330) hide show
  1. package/lib/cjs/core/parser/animation.cjs +427 -0
  2. package/lib/cjs/core/parser/animation.cjs.map +1 -0
  3. package/lib/cjs/core/parser/animation.d.ts +126 -0
  4. package/lib/cjs/core/parser/border-dispatcher.cjs +180 -0
  5. package/lib/cjs/core/parser/border-dispatcher.cjs.map +1 -0
  6. package/lib/cjs/core/parser/border-dispatcher.d.ts +15 -0
  7. package/lib/cjs/core/parser/case-convert.cjs +15 -0
  8. package/lib/cjs/core/parser/case-convert.cjs.map +1 -0
  9. package/lib/cjs/core/parser/case-convert.d.ts +6 -0
  10. package/lib/cjs/core/parser/color-properties-dispatcher.cjs +84 -0
  11. package/lib/cjs/core/parser/color-properties-dispatcher.cjs.map +1 -0
  12. package/lib/cjs/core/parser/color-properties-dispatcher.d.ts +19 -0
  13. package/lib/cjs/core/parser/color.cjs +164 -0
  14. package/lib/cjs/core/parser/color.cjs.map +1 -0
  15. package/lib/cjs/core/parser/color.d.ts +12 -0
  16. package/lib/cjs/core/parser/constants.cjs +21 -0
  17. package/lib/cjs/core/parser/constants.cjs.map +1 -0
  18. package/lib/cjs/core/parser/constants.d.ts +8 -0
  19. package/lib/cjs/core/parser/declaration.cjs +347 -0
  20. package/lib/cjs/core/parser/declaration.cjs.map +1 -0
  21. package/lib/cjs/core/parser/declaration.d.ts +15 -0
  22. package/lib/cjs/core/parser/gradient.cjs +132 -0
  23. package/lib/cjs/core/parser/gradient.cjs.map +1 -0
  24. package/lib/cjs/core/parser/gradient.d.ts +59 -0
  25. package/lib/cjs/core/parser/haptics.cjs +73 -0
  26. package/lib/cjs/core/parser/haptics.cjs.map +1 -0
  27. package/lib/cjs/core/parser/haptics.d.ts +47 -0
  28. package/lib/cjs/core/parser/index.d.ts +8 -0
  29. package/lib/cjs/core/parser/keyframes.cjs +95 -0
  30. package/lib/cjs/core/parser/keyframes.cjs.map +1 -0
  31. package/lib/cjs/core/parser/keyframes.d.ts +26 -0
  32. package/lib/cjs/core/parser/layout-dispatcher.cjs +100 -0
  33. package/lib/cjs/core/parser/layout-dispatcher.cjs.map +1 -0
  34. package/lib/cjs/core/parser/layout-dispatcher.d.ts +14 -0
  35. package/lib/cjs/core/parser/length.cjs +96 -0
  36. package/lib/cjs/core/parser/length.cjs.map +1 -0
  37. package/lib/cjs/core/parser/length.d.ts +48 -0
  38. package/lib/cjs/core/parser/motion-dispatcher.cjs +77 -0
  39. package/lib/cjs/core/parser/motion-dispatcher.cjs.map +1 -0
  40. package/lib/cjs/core/parser/motion-dispatcher.d.ts +11 -0
  41. package/lib/cjs/core/parser/property.cjs +22 -0
  42. package/lib/cjs/core/parser/property.cjs.map +1 -0
  43. package/lib/cjs/core/parser/property.d.ts +8 -0
  44. package/lib/cjs/core/parser/safe-area.cjs +404 -0
  45. package/lib/cjs/core/parser/safe-area.cjs.map +1 -0
  46. package/lib/cjs/core/parser/safe-area.d.ts +39 -0
  47. package/lib/cjs/core/parser/selector.cjs +22 -0
  48. package/lib/cjs/core/parser/selector.cjs.map +1 -0
  49. package/lib/cjs/core/parser/selector.d.ts +11 -0
  50. package/lib/cjs/core/parser/shorthand.cjs +156 -0
  51. package/lib/cjs/core/parser/shorthand.cjs.map +1 -0
  52. package/lib/cjs/core/parser/shorthand.d.ts +61 -0
  53. package/lib/cjs/core/parser/text-truncate.cjs +78 -0
  54. package/lib/cjs/core/parser/text-truncate.cjs.map +1 -0
  55. package/lib/cjs/core/parser/text-truncate.d.ts +44 -0
  56. package/lib/cjs/core/parser/theme-vars.cjs +414 -0
  57. package/lib/cjs/core/parser/theme-vars.cjs.map +1 -0
  58. package/lib/cjs/core/parser/theme-vars.d.ts +61 -0
  59. package/lib/cjs/core/parser/tokens.cjs +304 -0
  60. package/lib/cjs/core/parser/tokens.cjs.map +1 -0
  61. package/lib/cjs/core/parser/tokens.d.ts +45 -0
  62. package/lib/cjs/core/parser/transform.cjs +198 -0
  63. package/lib/cjs/core/parser/transform.cjs.map +1 -0
  64. package/lib/cjs/core/parser/transform.d.ts +36 -0
  65. package/lib/cjs/core/parser/tw-parser.cjs +1567 -0
  66. package/lib/cjs/core/parser/tw-parser.cjs.map +1 -0
  67. package/lib/cjs/core/parser/tw-parser.d.ts +194 -0
  68. package/lib/cjs/core/parser/types.d.ts +37 -0
  69. package/lib/cjs/core/parser/typography-dispatcher.cjs +93 -0
  70. package/lib/cjs/core/parser/typography-dispatcher.cjs.map +1 -0
  71. package/lib/cjs/core/parser/typography-dispatcher.d.ts +11 -0
  72. package/lib/cjs/core/parser/typography.cjs +97 -0
  73. package/lib/cjs/core/parser/typography.cjs.map +1 -0
  74. package/lib/cjs/core/parser/typography.d.ts +43 -0
  75. package/lib/cjs/core/style-builder/build-style.cjs +397 -0
  76. package/lib/cjs/core/style-builder/build-style.cjs.map +1 -0
  77. package/lib/cjs/core/style-builder/build-style.d.ts +54 -0
  78. package/lib/cjs/core/style-builder/index.d.ts +3 -0
  79. package/lib/cjs/core/style-builder/union-builder.cjs +326 -0
  80. package/lib/cjs/core/style-builder/union-builder.cjs.map +1 -0
  81. package/lib/cjs/core/style-builder/union-builder.d.ts +128 -0
  82. package/lib/cjs/core/types.d.ts +14 -0
  83. package/lib/cjs/metro/dts.cjs +127 -0
  84. package/lib/cjs/metro/dts.cjs.map +1 -0
  85. package/lib/cjs/metro/dts.d.ts +16 -0
  86. package/lib/cjs/metro/index.cjs +19 -0
  87. package/lib/cjs/metro/index.cjs.map +1 -0
  88. package/lib/cjs/metro/index.d.ts +9 -0
  89. package/lib/cjs/metro/resolver.cjs +47 -0
  90. package/lib/cjs/metro/resolver.cjs.map +1 -0
  91. package/lib/cjs/metro/resolver.d.ts +22 -0
  92. package/lib/cjs/metro/state.cjs +251 -0
  93. package/lib/cjs/metro/state.cjs.map +1 -0
  94. package/lib/cjs/metro/state.d.ts +72 -0
  95. package/lib/cjs/metro/transform-ast.cjs +1255 -0
  96. package/lib/cjs/metro/transform-ast.cjs.map +1 -0
  97. package/lib/cjs/metro/transform-ast.d.ts +73 -0
  98. package/lib/cjs/metro/transformer.cjs +345 -0
  99. package/lib/cjs/metro/transformer.cjs.map +1 -0
  100. package/lib/cjs/metro/transformer.d.ts +47 -0
  101. package/lib/cjs/metro/warn-unknown-classes.cjs +86 -0
  102. package/lib/cjs/metro/warn-unknown-classes.cjs.map +1 -0
  103. package/lib/cjs/metro/warn-unknown-classes.d.ts +21 -0
  104. package/lib/cjs/metro/with-config.cjs +196 -0
  105. package/lib/cjs/metro/with-config.cjs.map +1 -0
  106. package/lib/cjs/metro/with-config.d.ts +57 -0
  107. package/lib/cjs/runtime/chain-handlers.cjs +37 -0
  108. package/lib/cjs/runtime/chain-handlers.cjs.map +1 -0
  109. package/lib/cjs/runtime/chain-handlers.d.ts +33 -0
  110. package/lib/cjs/runtime/components/rnwind-provider.cjs +98 -0
  111. package/lib/cjs/runtime/components/rnwind-provider.cjs.map +1 -0
  112. package/lib/cjs/runtime/components/rnwind-provider.d.ts +84 -0
  113. package/lib/cjs/runtime/gradient-types.d.ts +58 -0
  114. package/lib/cjs/runtime/haptics.cjs +113 -0
  115. package/lib/cjs/runtime/haptics.cjs.map +1 -0
  116. package/lib/cjs/runtime/haptics.d.ts +48 -0
  117. package/lib/cjs/runtime/hooks/use-css.cjs +21 -0
  118. package/lib/cjs/runtime/hooks/use-css.cjs.map +1 -0
  119. package/lib/cjs/runtime/hooks/use-css.d.ts +11 -0
  120. package/lib/cjs/runtime/hooks/use-interact.cjs +46 -0
  121. package/lib/cjs/runtime/hooks/use-interact.cjs.map +1 -0
  122. package/lib/cjs/runtime/hooks/use-interact.d.ts +42 -0
  123. package/lib/cjs/runtime/hooks/use-scheme.cjs +68 -0
  124. package/lib/cjs/runtime/hooks/use-scheme.cjs.map +1 -0
  125. package/lib/cjs/runtime/hooks/use-scheme.d.ts +34 -0
  126. package/lib/cjs/runtime/index.cjs +45 -0
  127. package/lib/cjs/runtime/index.cjs.map +1 -0
  128. package/lib/cjs/runtime/index.d.ts +27 -0
  129. package/lib/cjs/runtime/interactive-box.cjs +35 -0
  130. package/lib/cjs/runtime/interactive-box.cjs.map +1 -0
  131. package/lib/cjs/runtime/interactive-box.d.ts +40 -0
  132. package/lib/cjs/runtime/lookup-css.cjs +542 -0
  133. package/lib/cjs/runtime/lookup-css.cjs.map +1 -0
  134. package/lib/cjs/runtime/lookup-css.d.ts +164 -0
  135. package/lib/cjs/runtime/types.d.ts +29 -0
  136. package/lib/cjs/testing/index.cjs +367 -0
  137. package/lib/cjs/testing/index.cjs.map +1 -0
  138. package/lib/cjs/testing/index.d.ts +145 -0
  139. package/lib/esm/core/parser/animation.d.ts +126 -0
  140. package/lib/esm/core/parser/animation.mjs +408 -0
  141. package/lib/esm/core/parser/animation.mjs.map +1 -0
  142. package/lib/esm/core/parser/border-dispatcher.d.ts +15 -0
  143. package/lib/esm/core/parser/border-dispatcher.mjs +178 -0
  144. package/lib/esm/core/parser/border-dispatcher.mjs.map +1 -0
  145. package/lib/esm/core/parser/case-convert.d.ts +6 -0
  146. package/lib/esm/core/parser/case-convert.mjs +13 -0
  147. package/lib/esm/core/parser/case-convert.mjs.map +1 -0
  148. package/lib/esm/core/parser/color-properties-dispatcher.d.ts +19 -0
  149. package/lib/esm/core/parser/color-properties-dispatcher.mjs +82 -0
  150. package/lib/esm/core/parser/color-properties-dispatcher.mjs.map +1 -0
  151. package/lib/esm/core/parser/color.d.ts +12 -0
  152. package/lib/esm/core/parser/color.mjs +162 -0
  153. package/lib/esm/core/parser/color.mjs.map +1 -0
  154. package/lib/esm/core/parser/constants.d.ts +8 -0
  155. package/lib/esm/core/parser/constants.mjs +13 -0
  156. package/lib/esm/core/parser/constants.mjs.map +1 -0
  157. package/lib/esm/core/parser/declaration.d.ts +15 -0
  158. package/lib/esm/core/parser/declaration.mjs +345 -0
  159. package/lib/esm/core/parser/declaration.mjs.map +1 -0
  160. package/lib/esm/core/parser/gradient.d.ts +59 -0
  161. package/lib/esm/core/parser/gradient.mjs +130 -0
  162. package/lib/esm/core/parser/gradient.mjs.map +1 -0
  163. package/lib/esm/core/parser/haptics.d.ts +47 -0
  164. package/lib/esm/core/parser/haptics.mjs +71 -0
  165. package/lib/esm/core/parser/haptics.mjs.map +1 -0
  166. package/lib/esm/core/parser/index.d.ts +8 -0
  167. package/lib/esm/core/parser/keyframes.d.ts +26 -0
  168. package/lib/esm/core/parser/keyframes.mjs +91 -0
  169. package/lib/esm/core/parser/keyframes.mjs.map +1 -0
  170. package/lib/esm/core/parser/layout-dispatcher.d.ts +14 -0
  171. package/lib/esm/core/parser/layout-dispatcher.mjs +98 -0
  172. package/lib/esm/core/parser/layout-dispatcher.mjs.map +1 -0
  173. package/lib/esm/core/parser/length.d.ts +48 -0
  174. package/lib/esm/core/parser/length.mjs +90 -0
  175. package/lib/esm/core/parser/length.mjs.map +1 -0
  176. package/lib/esm/core/parser/motion-dispatcher.d.ts +11 -0
  177. package/lib/esm/core/parser/motion-dispatcher.mjs +75 -0
  178. package/lib/esm/core/parser/motion-dispatcher.mjs.map +1 -0
  179. package/lib/esm/core/parser/property.d.ts +8 -0
  180. package/lib/esm/core/parser/property.mjs +20 -0
  181. package/lib/esm/core/parser/property.mjs.map +1 -0
  182. package/lib/esm/core/parser/safe-area.d.ts +39 -0
  183. package/lib/esm/core/parser/safe-area.mjs +402 -0
  184. package/lib/esm/core/parser/safe-area.mjs.map +1 -0
  185. package/lib/esm/core/parser/selector.d.ts +11 -0
  186. package/lib/esm/core/parser/selector.mjs +20 -0
  187. package/lib/esm/core/parser/selector.mjs.map +1 -0
  188. package/lib/esm/core/parser/shorthand.d.ts +61 -0
  189. package/lib/esm/core/parser/shorthand.mjs +148 -0
  190. package/lib/esm/core/parser/shorthand.mjs.map +1 -0
  191. package/lib/esm/core/parser/text-truncate.d.ts +44 -0
  192. package/lib/esm/core/parser/text-truncate.mjs +75 -0
  193. package/lib/esm/core/parser/text-truncate.mjs.map +1 -0
  194. package/lib/esm/core/parser/theme-vars.d.ts +61 -0
  195. package/lib/esm/core/parser/theme-vars.mjs +409 -0
  196. package/lib/esm/core/parser/theme-vars.mjs.map +1 -0
  197. package/lib/esm/core/parser/tokens.d.ts +45 -0
  198. package/lib/esm/core/parser/tokens.mjs +298 -0
  199. package/lib/esm/core/parser/tokens.mjs.map +1 -0
  200. package/lib/esm/core/parser/transform.d.ts +36 -0
  201. package/lib/esm/core/parser/transform.mjs +193 -0
  202. package/lib/esm/core/parser/transform.mjs.map +1 -0
  203. package/lib/esm/core/parser/tw-parser.d.ts +194 -0
  204. package/lib/esm/core/parser/tw-parser.mjs +1565 -0
  205. package/lib/esm/core/parser/tw-parser.mjs.map +1 -0
  206. package/lib/esm/core/parser/types.d.ts +37 -0
  207. package/lib/esm/core/parser/typography-dispatcher.d.ts +11 -0
  208. package/lib/esm/core/parser/typography-dispatcher.mjs +91 -0
  209. package/lib/esm/core/parser/typography-dispatcher.mjs.map +1 -0
  210. package/lib/esm/core/parser/typography.d.ts +43 -0
  211. package/lib/esm/core/parser/typography.mjs +91 -0
  212. package/lib/esm/core/parser/typography.mjs.map +1 -0
  213. package/lib/esm/core/style-builder/build-style.d.ts +54 -0
  214. package/lib/esm/core/style-builder/build-style.mjs +395 -0
  215. package/lib/esm/core/style-builder/build-style.mjs.map +1 -0
  216. package/lib/esm/core/style-builder/index.d.ts +3 -0
  217. package/lib/esm/core/style-builder/union-builder.d.ts +128 -0
  218. package/lib/esm/core/style-builder/union-builder.mjs +324 -0
  219. package/lib/esm/core/style-builder/union-builder.mjs.map +1 -0
  220. package/lib/esm/core/types.d.ts +14 -0
  221. package/lib/esm/metro/dts.d.ts +16 -0
  222. package/lib/esm/metro/dts.mjs +125 -0
  223. package/lib/esm/metro/dts.mjs.map +1 -0
  224. package/lib/esm/metro/index.d.ts +9 -0
  225. package/lib/esm/metro/index.mjs +6 -0
  226. package/lib/esm/metro/index.mjs.map +1 -0
  227. package/lib/esm/metro/resolver.d.ts +22 -0
  228. package/lib/esm/metro/resolver.mjs +43 -0
  229. package/lib/esm/metro/resolver.mjs.map +1 -0
  230. package/lib/esm/metro/state.d.ts +72 -0
  231. package/lib/esm/metro/state.mjs +243 -0
  232. package/lib/esm/metro/state.mjs.map +1 -0
  233. package/lib/esm/metro/transform-ast.d.ts +73 -0
  234. package/lib/esm/metro/transform-ast.mjs +1234 -0
  235. package/lib/esm/metro/transform-ast.mjs.map +1 -0
  236. package/lib/esm/metro/transformer.d.ts +47 -0
  237. package/lib/esm/metro/transformer.mjs +322 -0
  238. package/lib/esm/metro/transformer.mjs.map +1 -0
  239. package/lib/esm/metro/warn-unknown-classes.d.ts +21 -0
  240. package/lib/esm/metro/warn-unknown-classes.mjs +84 -0
  241. package/lib/esm/metro/warn-unknown-classes.mjs.map +1 -0
  242. package/lib/esm/metro/with-config.d.ts +57 -0
  243. package/lib/esm/metro/with-config.mjs +194 -0
  244. package/lib/esm/metro/with-config.mjs.map +1 -0
  245. package/lib/esm/runtime/chain-handlers.d.ts +33 -0
  246. package/lib/esm/runtime/chain-handlers.mjs +34 -0
  247. package/lib/esm/runtime/chain-handlers.mjs.map +1 -0
  248. package/lib/esm/runtime/components/rnwind-provider.d.ts +84 -0
  249. package/lib/esm/runtime/components/rnwind-provider.mjs +94 -0
  250. package/lib/esm/runtime/components/rnwind-provider.mjs.map +1 -0
  251. package/lib/esm/runtime/gradient-types.d.ts +58 -0
  252. package/lib/esm/runtime/haptics.d.ts +48 -0
  253. package/lib/esm/runtime/haptics.mjs +110 -0
  254. package/lib/esm/runtime/haptics.mjs.map +1 -0
  255. package/lib/esm/runtime/hooks/use-css.d.ts +11 -0
  256. package/lib/esm/runtime/hooks/use-css.mjs +19 -0
  257. package/lib/esm/runtime/hooks/use-css.mjs.map +1 -0
  258. package/lib/esm/runtime/hooks/use-interact.d.ts +42 -0
  259. package/lib/esm/runtime/hooks/use-interact.mjs +44 -0
  260. package/lib/esm/runtime/hooks/use-interact.mjs.map +1 -0
  261. package/lib/esm/runtime/hooks/use-scheme.d.ts +34 -0
  262. package/lib/esm/runtime/hooks/use-scheme.mjs +63 -0
  263. package/lib/esm/runtime/hooks/use-scheme.mjs.map +1 -0
  264. package/lib/esm/runtime/index.d.ts +27 -0
  265. package/lib/esm/runtime/index.mjs +18 -0
  266. package/lib/esm/runtime/index.mjs.map +1 -0
  267. package/lib/esm/runtime/interactive-box.d.ts +40 -0
  268. package/lib/esm/runtime/interactive-box.mjs +33 -0
  269. package/lib/esm/runtime/interactive-box.mjs.map +1 -0
  270. package/lib/esm/runtime/lookup-css.d.ts +164 -0
  271. package/lib/esm/runtime/lookup-css.mjs +531 -0
  272. package/lib/esm/runtime/lookup-css.mjs.map +1 -0
  273. package/lib/esm/runtime/types.d.ts +29 -0
  274. package/lib/esm/testing/index.d.ts +145 -0
  275. package/lib/esm/testing/index.mjs +344 -0
  276. package/lib/esm/testing/index.mjs.map +1 -0
  277. package/package.json +79 -13
  278. package/preset.css +1171 -0
  279. package/src/core/parser/animation.ts +404 -0
  280. package/src/core/parser/border-dispatcher.ts +176 -0
  281. package/src/core/parser/case-convert.ts +10 -0
  282. package/src/core/parser/color-properties-dispatcher.ts +78 -0
  283. package/src/core/parser/color.ts +157 -0
  284. package/src/core/parser/constants.ts +11 -0
  285. package/src/core/parser/declaration.ts +340 -0
  286. package/src/core/parser/gradient.ts +148 -0
  287. package/src/core/parser/haptics.ts +88 -0
  288. package/src/core/parser/index.ts +8 -0
  289. package/src/core/parser/keyframes.ts +84 -0
  290. package/src/core/parser/layout-dispatcher.ts +92 -0
  291. package/src/core/parser/length.ts +100 -0
  292. package/src/core/parser/motion-dispatcher.ts +89 -0
  293. package/src/core/parser/property.ts +15 -0
  294. package/src/core/parser/safe-area.ts +404 -0
  295. package/src/core/parser/selector.ts +17 -0
  296. package/src/core/parser/shorthand.ts +152 -0
  297. package/src/core/parser/text-truncate.ts +79 -0
  298. package/src/core/parser/theme-vars.ts +412 -0
  299. package/src/core/parser/tokens.ts +286 -0
  300. package/src/core/parser/transform.ts +195 -0
  301. package/src/core/parser/tw-parser.ts +1709 -0
  302. package/src/core/parser/types.ts +45 -0
  303. package/src/core/parser/typography-dispatcher.ts +83 -0
  304. package/src/core/parser/typography.ts +83 -0
  305. package/src/core/style-builder/build-style.ts +442 -0
  306. package/src/core/style-builder/index.ts +3 -0
  307. package/src/core/style-builder/union-builder.ts +328 -0
  308. package/src/core/types.ts +15 -0
  309. package/src/metro/dts.ts +128 -0
  310. package/src/metro/index.ts +9 -0
  311. package/src/metro/resolver.ts +42 -0
  312. package/src/metro/state.ts +257 -0
  313. package/src/metro/transform-ast.ts +1498 -0
  314. package/src/metro/transformer.ts +347 -0
  315. package/src/metro/warn-unknown-classes.ts +79 -0
  316. package/src/metro/with-config.ts +229 -0
  317. package/src/runtime/chain-handlers.ts +47 -0
  318. package/src/runtime/components/rnwind-provider.tsx +144 -0
  319. package/src/runtime/gradient-types.ts +60 -0
  320. package/src/runtime/haptics.ts +120 -0
  321. package/src/runtime/hooks/use-css.ts +16 -0
  322. package/src/runtime/hooks/use-interact.ts +65 -0
  323. package/src/runtime/hooks/use-scheme.ts +63 -0
  324. package/src/runtime/index.ts +54 -0
  325. package/src/runtime/interactive-box.tsx +57 -0
  326. package/src/runtime/lookup-css.ts +628 -0
  327. package/src/runtime/types.ts +32 -0
  328. package/src/testing/index.ts +507 -0
  329. package/src/types/tailwindcss-node.d.ts +33 -0
  330. package/src/index.ts +0 -1
@@ -0,0 +1,404 @@
1
+ import type {
2
+ Animation,
3
+ AnimationDirection,
4
+ AnimationFillMode,
5
+ AnimationIterationCount,
6
+ AnimationName,
7
+ AnimationPlayState,
8
+ EasingFunction,
9
+ PropertyId,
10
+ Time,
11
+ Transition,
12
+ } from 'lightningcss'
13
+ import { kebabToCamel } from './case-convert'
14
+ import type { RNEntry } from './types'
15
+
16
+ /**
17
+ * Convert one PropertyId ident into the camelCase RN style key
18
+ * Reanimated v4's CSS engine matches against. Tailwind emits CSS names
19
+ * in kebab-case (`background-color`); RN stores `backgroundColor` in
20
+ * the actual style object, and Reanimated only fires a transition when
21
+ * the watched key matches the changed key — so kebab-case here would
22
+ * silently no-op every multi-word color/border transition.
23
+ *
24
+ * Special cases:
25
+ * - `'all'` passes through unchanged.
26
+ * - `--tw-*` Tailwind internal custom props are dropped — they have
27
+ * no RN equivalent and Reanimated can't watch them.
28
+ * - Other `--user-defined` custom props pass through verbatim.
29
+ * @param property Typed property identifier.
30
+ * @returns RN-style key, or null when the property has no RN equivalent.
31
+ */
32
+ function propertyIdToString(property: PropertyId): string | null {
33
+ // Custom properties surface in two shapes: either as `{property:
34
+ // 'custom', value: {name: '--x'}}` or — when lightningcss recognises
35
+ // the leading `--` directly in propertyId — as `{property: '--x'}`.
36
+ // Handle both, drop Tailwind internals, keep user customs verbatim.
37
+ if (property.property === 'custom' && 'value' in property) {
38
+ const { value } = property
39
+ const customName = value && typeof value === 'object' && 'name' in value && typeof value.name === 'string' ? value.name : null
40
+ if (!customName) return null
41
+ return customName.startsWith('--tw-') ? null : customName
42
+ }
43
+ if (typeof property.property === 'string' && property.property.startsWith('--')) {
44
+ return property.property.startsWith('--tw-') ? null : property.property
45
+ }
46
+ if (property.property === 'all') return 'all'
47
+ return kebabToCamel(property.property)
48
+ }
49
+
50
+ /**
51
+ * Collapse a single-element list into its scalar, otherwise return the
52
+ * list unchanged. Reanimated accepts both forms; the scalar is the
53
+ * common case.
54
+ * @param values Values to collapse.
55
+ * @returns A scalar when the list has one element, otherwise the list.
56
+ */
57
+ function singleOrArray<T>(values: readonly T[]): T | readonly T[] {
58
+ if (values.length === 1) return values[0]!
59
+ return values
60
+ }
61
+
62
+ /**
63
+ * Render a number without trailing noise (strips IEEE drift beyond 4
64
+ * decimals, then removes trailing zeros).
65
+ * @param value Number to format.
66
+ * @returns Compact string form.
67
+ */
68
+ function formatNumber(value: number): string {
69
+ const rounded = Math.round(value * 10_000) / 10_000
70
+ return String(rounded)
71
+ }
72
+
73
+ /**
74
+ * Reanimated v4's CSS engine doesn't accept `cubic-bezier(...)` as a
75
+ * string — only the predefined keywords `linear`, `ease`, `ease-in`,
76
+ * `ease-out`, `ease-in-out`, `step-start`, `step-end`. Tailwind's
77
+ * `transition-colors` / `ease-in-out` / etc. emit the CSS-standard
78
+ * Material curves as cubic-bezier; we snap those to the closest
79
+ * predefined keyword by matching the well-known control-point shapes,
80
+ * falling back to `ease-in-out` (the most common Tailwind default).
81
+ *
82
+ * Direct matches:
83
+ * - `cubic-bezier(0.25, 0.1, 0.25, 1)` → `ease` (CSS spec default)
84
+ * - `cubic-bezier(0.4, 0, 1, 1)` → `ease-in`
85
+ * - `cubic-bezier(0, 0, 0.2, 1)` → `ease-out`
86
+ * - `cubic-bezier(0.4, 0, 0.2, 1)` → `ease-in-out` (Material standard)
87
+ * - linear shape (`x1=y1=0, x2=y2=1`) → `linear`
88
+ *
89
+ * Anything else: classify by control-point shape — front-loaded curves
90
+ * → `ease-in`, back-loaded → `ease-out`, both → `ease-in-out`.
91
+ * @param x1 First control-point x (0–1).
92
+ * @param y1 First control-point y (0–1).
93
+ * @param x2 Second control-point x (0–1).
94
+ * @param y2 Second control-point y (0–1).
95
+ * @returns Closest matching CSS easing keyword.
96
+ */
97
+ function snapCubicBezierToKeyword(x1: number, y1: number, x2: number, y2: number): string {
98
+ if (matchesBezier(x1, y1, x2, y2, 0, 0, 1, 1)) return 'linear'
99
+ if (matchesBezier(x1, y1, x2, y2, 0.25, 0.1, 0.25, 1)) return 'ease'
100
+ if (matchesBezier(x1, y1, x2, y2, 0.4, 0, 1, 1)) return 'ease-in'
101
+ if (matchesBezier(x1, y1, x2, y2, 0, 0, 0.2, 1)) return 'ease-out'
102
+ if (matchesBezier(x1, y1, x2, y2, 0.4, 0, 0.2, 1)) return 'ease-in-out'
103
+ // Heuristic for unknown bezier shapes:
104
+ // - x1 ≈ 0 → starts straight (decelerates) → ease-out
105
+ // - x2 ≈ 1 → ends straight (accelerates) → ease-in
106
+ // - both small, both medium → ease-in-out
107
+ const startsFlat = x1 < 0.1
108
+ const endsFlat = x2 > 0.9
109
+ if (startsFlat && !endsFlat) return 'ease-out'
110
+ if (!startsFlat && endsFlat) return 'ease-in'
111
+ return 'ease-in-out'
112
+ }
113
+
114
+ /**
115
+ * Approximate equality of two cubic-bezier control-point sets — IEEE
116
+ * float noise from lightningcss / culori means literal `===` rarely
117
+ * holds, so we tolerate a tiny epsilon.
118
+ * @param ax1 Actual first x.
119
+ * @param ay1 Actual first y.
120
+ * @param ax2 Actual second x.
121
+ * @param ay2 Actual second y.
122
+ * @param tx1 Target first x.
123
+ * @param ty1 Target first y.
124
+ * @param tx2 Target second x.
125
+ * @param ty2 Target second y.
126
+ * @returns Whether the two beziers are component-wise within 0.01 of each other.
127
+ */
128
+ function matchesBezier(
129
+ ax1: number,
130
+ ay1: number,
131
+ ax2: number,
132
+ ay2: number,
133
+ tx1: number,
134
+ ty1: number,
135
+ tx2: number,
136
+ ty2: number,
137
+ ): boolean {
138
+ const tolerance = 0.01
139
+ return (
140
+ Math.abs(ax1 - tx1) < tolerance &&
141
+ Math.abs(ay1 - ty1) < tolerance &&
142
+ Math.abs(ax2 - tx2) < tolerance &&
143
+ Math.abs(ay2 - ty2) < tolerance
144
+ )
145
+ }
146
+
147
+ /**
148
+ * Serialize a lightningcss `EasingFunction` into a CSS string Reanimated
149
+ * v4's CSS engine understands. Reanimated accepts the same timing-function
150
+ * strings as CSS transitions/animations, so we emit the canonical CSS
151
+ * form.
152
+ * @param fn Typed easing function.
153
+ * @returns CSS string, e.g. `'linear'` / `'ease-in-out'` / `'cubic-bezier(0.4, 0, 0.2, 1)'`.
154
+ */
155
+ export function easingFunctionToString(fn: EasingFunction): string {
156
+ switch (fn.type) {
157
+ case 'linear': {
158
+ return 'linear'
159
+ }
160
+ case 'ease':
161
+ case 'ease-in':
162
+ case 'ease-out':
163
+ case 'ease-in-out': {
164
+ return fn.type
165
+ }
166
+ case 'cubic-bezier': {
167
+ return snapCubicBezierToKeyword(fn.x1, fn.y1, fn.x2, fn.y2)
168
+ }
169
+ case 'steps': {
170
+ const pos = fn.position?.type ?? 'end'
171
+ return `steps(${fn.count}, ${pos})`
172
+ }
173
+ default: {
174
+ return 'ease'
175
+ }
176
+ }
177
+ }
178
+
179
+ /**
180
+ * Format a `Time` value into a CSS string (`150ms`, `1s`). Reanimated's
181
+ * CSS engine accepts either `ms` or `s`; we pick `ms` unless the value
182
+ * is a whole-second multiple for readability.
183
+ * @param time Typed time value.
184
+ * @returns CSS time string.
185
+ */
186
+ export function timeToString(time: Time): string {
187
+ if (time.type === 'milliseconds') return `${formatNumber(time.value)}ms`
188
+ return `${formatNumber(time.value)}s`
189
+ }
190
+
191
+ /**
192
+ * Convert a single `animation-name` value to a string. Tailwind's
193
+ * `animate-none` produces `{ type: 'none' }` which we drop.
194
+ * @param name Typed animation name.
195
+ * @returns Keyframe identifier, or null when `none`.
196
+ */
197
+ export function animationNameToString(name: AnimationName): string | null {
198
+ if (name.type === 'none') return null
199
+ if (name.type === 'ident') return name.value
200
+ if (name.type === 'string') return name.value
201
+ return null
202
+ }
203
+
204
+ /**
205
+ * Convert a single `animation-iteration-count` value to the CSS shape
206
+ * Reanimated wants: either a finite integer or the `'infinite'` string.
207
+ * @param count Typed iteration count.
208
+ * @returns Number or `'infinite'`.
209
+ */
210
+ export function iterationCountToValue(count: AnimationIterationCount): number | string {
211
+ if (count.type === 'infinite') return 'infinite'
212
+ return count.value
213
+ }
214
+
215
+ /**
216
+ * Decompose the `animation: <name> <duration> <timing> <iteration> …`
217
+ * shorthand into the per-property RN entries Reanimated consumes. A single
218
+ * `animation` declaration can name multiple animations — we emit the first
219
+ * one (Tailwind's `animate-*` utilities always emit exactly one).
220
+ * @param animations Parsed animation list from lightningcss.
221
+ * @returns RN entries, empty when the shorthand names `none`.
222
+ */
223
+ export function animationShorthandToEntries(animations: readonly Animation[]): readonly RNEntry[] {
224
+ const [first] = animations
225
+ if (!first) return []
226
+ const name = animationNameToString(first.name)
227
+ if (!name) return []
228
+ const entries: RNEntry[] = [['animationName', name]]
229
+ if (first.duration) entries.push(['animationDuration', timeToString(first.duration)])
230
+ if (first.timingFunction) entries.push(['animationTimingFunction', easingFunctionToString(first.timingFunction)])
231
+ if (first.iterationCount) entries.push(['animationIterationCount', iterationCountToValue(first.iterationCount)])
232
+ if (first.direction && first.direction !== 'normal') entries.push(['animationDirection', first.direction])
233
+ if (first.fillMode && first.fillMode !== 'none') entries.push(['animationFillMode', first.fillMode])
234
+ if (first.delay && first.delay.value !== 0) entries.push(['animationDelay', timeToString(first.delay)])
235
+ if (first.playState && first.playState !== 'running') entries.push(['animationPlayState', first.playState])
236
+ return entries
237
+ }
238
+
239
+ /**
240
+ * Emit `animationName` for a standalone `animation-name: spin` declaration.
241
+ * @param names Typed `animation-name` list.
242
+ * @returns Single-entry `animationName` or empty when `none`.
243
+ */
244
+ export function animationNameEntries(names: readonly AnimationName[]): readonly RNEntry[] {
245
+ const [first] = names
246
+ if (!first) return []
247
+ const name = animationNameToString(first)
248
+ if (!name) return []
249
+ return [['animationName', name]]
250
+ }
251
+
252
+ /**
253
+ * Emit `animationDuration` for a standalone `animation-duration` declaration.
254
+ * @param durations Typed duration list.
255
+ * @returns Single-entry list.
256
+ */
257
+ export function animationDurationEntries(durations: readonly Time[]): readonly RNEntry[] {
258
+ const [first] = durations
259
+ if (!first) return []
260
+ return [['animationDuration', timeToString(first)]]
261
+ }
262
+
263
+ /**
264
+ * Emit `animationTimingFunction` for a standalone `animation-timing-function`.
265
+ * @param fns Typed timing-function list.
266
+ * @returns Single-entry list.
267
+ */
268
+ export function animationTimingFunctionEntries(fns: readonly EasingFunction[]): readonly RNEntry[] {
269
+ const [first] = fns
270
+ if (!first) return []
271
+ return [['animationTimingFunction', easingFunctionToString(first)]]
272
+ }
273
+
274
+ /**
275
+ * Emit `animationIterationCount` for a standalone declaration.
276
+ * @param counts Typed iteration-count list.
277
+ * @returns Single-entry list.
278
+ */
279
+ export function animationIterationCountEntries(counts: readonly AnimationIterationCount[]): readonly RNEntry[] {
280
+ const [first] = counts
281
+ if (!first) return []
282
+ return [['animationIterationCount', iterationCountToValue(first)]]
283
+ }
284
+
285
+ /**
286
+ * Emit `animationDelay` for a standalone declaration.
287
+ * @param delays Typed delay list.
288
+ * @returns Single-entry list.
289
+ */
290
+ export function animationDelayEntries(delays: readonly Time[]): readonly RNEntry[] {
291
+ const [first] = delays
292
+ if (!first) return []
293
+ return [['animationDelay', timeToString(first)]]
294
+ }
295
+
296
+ /**
297
+ * Emit `animationDirection` for a standalone declaration.
298
+ * @param directions Typed direction list.
299
+ * @returns Single-entry list.
300
+ */
301
+ export function animationDirectionEntries(directions: readonly AnimationDirection[]): readonly RNEntry[] {
302
+ const [first] = directions
303
+ if (!first) return []
304
+ return [['animationDirection', first]]
305
+ }
306
+
307
+ /**
308
+ * Emit `animationFillMode` for a standalone declaration.
309
+ * @param modes Typed fill-mode list.
310
+ * @returns Single-entry list.
311
+ */
312
+ export function animationFillModeEntries(modes: readonly AnimationFillMode[]): readonly RNEntry[] {
313
+ const [first] = modes
314
+ if (!first) return []
315
+ return [['animationFillMode', first]]
316
+ }
317
+
318
+ /**
319
+ * Emit `animationPlayState` for a standalone declaration.
320
+ * @param states Typed play-state list.
321
+ * @returns Single-entry list.
322
+ */
323
+ export function animationPlayStateEntries(states: readonly AnimationPlayState[]): readonly RNEntry[] {
324
+ const [first] = states
325
+ if (!first) return []
326
+ return [['animationPlayState', first]]
327
+ }
328
+
329
+ /**
330
+ * Convert the `transition-property` list into an RN-consumable form.
331
+ * Reanimated accepts either a string (`'all'`, `'opacity'`) or an array
332
+ * of property names. We emit the array shape even for singletons so the
333
+ * runtime can handle it uniformly.
334
+ * @param properties Typed transition-property list.
335
+ * @returns Single-entry list with `transitionProperty`.
336
+ */
337
+ export function transitionPropertyEntries(properties: readonly PropertyId[]): readonly RNEntry[] {
338
+ const names = properties.map((p) => propertyIdToString(p)).filter((name): name is string => name !== null)
339
+ if (names.length === 0) return []
340
+ if (names.length === 1) return [['transitionProperty', names[0]!]]
341
+ return [['transitionProperty', names]]
342
+ }
343
+
344
+ /**
345
+ * Decompose the `transition: <prop> <duration> <timing> <delay>` shorthand
346
+ * into the per-property RN entries Reanimated consumes. A declaration can
347
+ * name multiple transitions; we emit one entry per slot, collapsing
348
+ * identical slots into a scalar.
349
+ * @param transitions Parsed transition list from lightningcss.
350
+ * @returns RN entries — zero or more.
351
+ */
352
+ export function transitionShorthandToEntries(transitions: readonly Transition[]): readonly RNEntry[] {
353
+ if (transitions.length === 0) return []
354
+ const properties: string[] = []
355
+ const durations: string[] = []
356
+ const timings: string[] = []
357
+ const delays: string[] = []
358
+ for (const t of transitions) {
359
+ const name = propertyIdToString(t.property)
360
+ if (name) properties.push(name)
361
+ if (t.duration) durations.push(timeToString(t.duration))
362
+ if (t.timingFunction) timings.push(easingFunctionToString(t.timingFunction))
363
+ if (t.delay) delays.push(timeToString(t.delay))
364
+ }
365
+ const entries: RNEntry[] = []
366
+ if (properties.length > 0) entries.push(['transitionProperty', singleOrArray(properties)])
367
+ if (durations.length > 0) entries.push(['transitionDuration', singleOrArray(durations)])
368
+ if (timings.length > 0) entries.push(['transitionTimingFunction', singleOrArray(timings)])
369
+ if (delays.some((d) => d !== '0s')) entries.push(['transitionDelay', singleOrArray(delays)])
370
+ return entries
371
+ }
372
+
373
+ /**
374
+ * Emit `transitionDuration` for a standalone declaration.
375
+ * @param durations Typed duration list.
376
+ * @returns Single-entry list.
377
+ */
378
+ export function transitionDurationEntries(durations: readonly Time[]): readonly RNEntry[] {
379
+ if (durations.length === 0) return []
380
+ const strings = durations.map((d) => timeToString(d))
381
+ return [['transitionDuration', singleOrArray(strings)]]
382
+ }
383
+
384
+ /**
385
+ * Emit `transitionTimingFunction` for a standalone declaration.
386
+ * @param fns Typed timing-function list.
387
+ * @returns Single-entry list.
388
+ */
389
+ export function transitionTimingFunctionEntries(fns: readonly EasingFunction[]): readonly RNEntry[] {
390
+ if (fns.length === 0) return []
391
+ const strings = fns.map((f) => easingFunctionToString(f))
392
+ return [['transitionTimingFunction', singleOrArray(strings)]]
393
+ }
394
+
395
+ /**
396
+ * Emit `transitionDelay` for a standalone declaration.
397
+ * @param delays Typed delay list.
398
+ * @returns Single-entry list.
399
+ */
400
+ export function transitionDelayEntries(delays: readonly Time[]): readonly RNEntry[] {
401
+ if (delays.length === 0) return []
402
+ const strings = delays.map((d) => timeToString(d))
403
+ return [['transitionDelay', singleOrArray(strings)]]
404
+ }
@@ -0,0 +1,176 @@
1
+ import type { Declaration as LcDeclaration } from 'lightningcss'
2
+ import { kebabToCamel } from './case-convert'
3
+ import { cssColorToString } from './color'
4
+ import type { RNEntry } from './types'
5
+
6
+ /**
7
+ * Build an inline-color pair from a `{start, end}`-shaped value.
8
+ * @param leftKey RN key for the start side.
9
+ * @param rightKey RN key for the end side.
10
+ * @param value Typed `{start, end}` color value.
11
+ * @returns Two RN entries.
12
+ */
13
+ function colorPair(leftKey: string, rightKey: string, value: unknown): readonly RNEntry[] {
14
+ const tagged = value as { start?: unknown; end?: unknown }
15
+ return [
16
+ [leftKey, cssColorToString(tagged.start as never)],
17
+ [rightKey, cssColorToString(tagged.end as never)],
18
+ ]
19
+ }
20
+
21
+ /**
22
+ * Coerce a border-width-shaped length to a pixel number. Drops
23
+ * percentages — RN borders don't accept them.
24
+ * @param value Typed length value (`{type: 'length', value: {unit, value}}`).
25
+ * @returns Pixel number, or null when unrepresentable.
26
+ */
27
+ function lengthToPxValue(value: unknown): number | null {
28
+ if (typeof value !== 'object' || value === null) return null
29
+ const tagged = value as {
30
+ type?: string
31
+ value?: { type?: string; value?: { unit?: string; value?: number } } | { unit?: string; value?: number }
32
+ }
33
+ const inner =
34
+ tagged.type === 'length'
35
+ ? (tagged.value as { type?: string; value?: { unit?: string; value?: number } })?.value
36
+ : tagged.value
37
+ if (!inner || typeof (inner as { value?: number }).value !== 'number') return null
38
+ const dim = inner as { unit?: string; value: number }
39
+ if (dim.unit === 'px') return dim.value
40
+ if (dim.unit === 'rem' || dim.unit === 'em') return dim.value * 16
41
+ return dim.value
42
+ }
43
+
44
+ /**
45
+ * Build an inline-width pair from a `{start, end}`-shaped value.
46
+ * @param leftKey RN key for the start side.
47
+ * @param rightKey RN key for the end side.
48
+ * @param value Typed `{start, end}` length value.
49
+ * @returns Two RN entries, or empty when either side is unrepresentable.
50
+ */
51
+ function widthPair(leftKey: string, rightKey: string, value: unknown): readonly RNEntry[] {
52
+ const tagged = value as { start?: unknown; end?: unknown }
53
+ const start = lengthToPxValue(tagged.start)
54
+ const end = lengthToPxValue(tagged.end)
55
+ if (start === null || end === null) return []
56
+ return [
57
+ [leftKey, start],
58
+ [rightKey, end],
59
+ ]
60
+ }
61
+
62
+ /**
63
+ * Expand a `border-width` shorthand to RN longhands. Collapses to the
64
+ * `borderWidth` shorthand when all four sides match.
65
+ * @param value Typed border-width value with `{top, right, bottom, left}`.
66
+ * @returns RN entries.
67
+ */
68
+ function borderWidthShorthand(value: unknown): readonly RNEntry[] {
69
+ const tagged = value as { top?: unknown; right?: unknown; bottom?: unknown; left?: unknown }
70
+ const top = lengthToPxValue(tagged.top)
71
+ const right = lengthToPxValue(tagged.right)
72
+ const bottom = lengthToPxValue(tagged.bottom)
73
+ const left = lengthToPxValue(tagged.left)
74
+ if (top === null || right === null || bottom === null || left === null) return []
75
+ if (top === right && right === bottom && bottom === left) return [['borderWidth', top]]
76
+ return [
77
+ ['borderTopWidth', top],
78
+ ['borderRightWidth', right],
79
+ ['borderBottomWidth', bottom],
80
+ ['borderLeftWidth', left],
81
+ ]
82
+ }
83
+
84
+ /**
85
+ * Map CSS `border-style` keywords to the strings RN accepts. RN
86
+ * supports only `solid` / `dashed` / `dotted` / `none` — fall back to
87
+ * `solid` for everything else.
88
+ * @param css CSS border-style keyword.
89
+ * @returns RN border-style.
90
+ */
91
+ function mapBorderStyle(css: string): string {
92
+ if (css === 'dashed' || css === 'dotted' || css === 'none') return css
93
+ return 'solid'
94
+ }
95
+
96
+ /**
97
+ * Dispatch border-* longhands and their logical-direction variants to
98
+ * RN style entries. Returns `null` for any property the dispatcher
99
+ * doesn't handle so the caller can fall through.
100
+ *
101
+ * Logical-direction inputs (`border-inline-*`, `border-block-*`,
102
+ * `border-inline-start-*`, …) lower to RN's physical pairs
103
+ * (`borderLeft*` / `borderRight*` / `borderTop*` / `borderBottom*`)
104
+ * since RN doesn't honor logical directions at the style level.
105
+ * @param decl One lightningcss declaration.
106
+ * @returns RN entries when the property matched, else `null`.
107
+ */
108
+ export function dispatchBorderDeclaration(decl: LcDeclaration): readonly RNEntry[] | null {
109
+ switch (decl.property) {
110
+ case 'border-inline-color': {
111
+ return colorPair('borderLeftColor', 'borderRightColor', decl.value)
112
+ }
113
+ case 'border-block-color': {
114
+ return colorPair('borderTopColor', 'borderBottomColor', decl.value)
115
+ }
116
+ case 'border-inline-start-color': {
117
+ return [['borderLeftColor', cssColorToString(decl.value)]]
118
+ }
119
+ case 'border-inline-end-color': {
120
+ return [['borderRightColor', cssColorToString(decl.value)]]
121
+ }
122
+ case 'border-block-start-color': {
123
+ return [['borderTopColor', cssColorToString(decl.value)]]
124
+ }
125
+ case 'border-block-end-color': {
126
+ return [['borderBottomColor', cssColorToString(decl.value)]]
127
+ }
128
+ case 'border-width': {
129
+ return borderWidthShorthand(decl.value)
130
+ }
131
+ case 'border-top-width':
132
+ case 'border-right-width':
133
+ case 'border-bottom-width':
134
+ case 'border-left-width': {
135
+ const v = lengthToPxValue(decl.value)
136
+ return v === null ? [] : [[kebabToCamel(decl.property), v]]
137
+ }
138
+ case 'border-inline-width': {
139
+ return widthPair('borderLeftWidth', 'borderRightWidth', decl.value)
140
+ }
141
+ case 'border-block-width': {
142
+ return widthPair('borderTopWidth', 'borderBottomWidth', decl.value)
143
+ }
144
+ case 'border-inline-start-width': {
145
+ const v = lengthToPxValue(decl.value)
146
+ return v === null ? [] : [['borderLeftWidth', v]]
147
+ }
148
+ case 'border-inline-end-width': {
149
+ const v = lengthToPxValue(decl.value)
150
+ return v === null ? [] : [['borderRightWidth', v]]
151
+ }
152
+ case 'border-block-start-width': {
153
+ const v = lengthToPxValue(decl.value)
154
+ return v === null ? [] : [['borderTopWidth', v]]
155
+ }
156
+ case 'border-block-end-width': {
157
+ const v = lengthToPxValue(decl.value)
158
+ return v === null ? [] : [['borderBottomWidth', v]]
159
+ }
160
+ case 'border-style': {
161
+ const styleValue = (decl.value as { top?: string }).top
162
+ if (typeof styleValue !== 'string') return []
163
+ return [['borderStyle', mapBorderStyle(styleValue)]]
164
+ }
165
+ case 'border-top-style':
166
+ case 'border-right-style':
167
+ case 'border-bottom-style':
168
+ case 'border-left-style': {
169
+ if (typeof decl.value !== 'string') return []
170
+ return [['borderStyle', mapBorderStyle(decl.value)]]
171
+ }
172
+ default: {
173
+ return null
174
+ }
175
+ }
176
+ }
@@ -0,0 +1,10 @@
1
+ import { KEBAB_BOUNDARY_REGEX } from './constants'
2
+
3
+ /**
4
+ * Kebab-case to camelCase — `border-radius` → `borderRadius`.
5
+ * @param name Kebab-case CSS property name.
6
+ * @returns camelCase RN key.
7
+ */
8
+ export function kebabToCamel(name: string): string {
9
+ return name.replaceAll(KEBAB_BOUNDARY_REGEX, (_, c: string) => c.toUpperCase())
10
+ }
@@ -0,0 +1,78 @@
1
+ import type { Declaration as LcDeclaration } from 'lightningcss'
2
+ import { cssColorToString } from './color'
3
+ import type { RNEntry } from './types'
4
+
5
+ /**
6
+ * Build a `[key, hex]` entry from a typed CssColor. Drops keyword colors
7
+ * (e.g. `'currentcolor'` strings — RN can't render them).
8
+ * @param key RN style key (camelCase).
9
+ * @param value Typed `CssColor`-shaped value.
10
+ * @returns Single-entry list or empty.
11
+ */
12
+ function colorEntry(key: string, value: unknown): readonly RNEntry[] {
13
+ if (value === null || value === undefined) return []
14
+ if (typeof value === 'string') return [[key, value]]
15
+ const hex = cssColorToString(value as never)
16
+ if (!hex || hex === 'transparent' || hex === 'currentColor') return [[key, hex]]
17
+ return [[key, hex]]
18
+ }
19
+
20
+ /**
21
+ * Several typed color properties wrap their `CssColor` payload inside a
22
+ * `{type: 'color', value: CssColor}` envelope. Unwrap so the inner color
23
+ * reaches {@link cssColorToString}.
24
+ * @param value Either a `CssColor` directly or a `{type: 'color', value}` wrapper.
25
+ * @returns Unwrapped `CssColor` (or the input untouched).
26
+ */
27
+ function unwrapTaggedColor(value: unknown): unknown {
28
+ if (typeof value !== 'object' || value === null) return value
29
+ const tagged = value as { type?: string; value?: unknown }
30
+ if (tagged.type === 'color') return tagged.value
31
+ return value
32
+ }
33
+
34
+ /**
35
+ * Dispatch color-typed CSS properties (text-decoration-color, fill,
36
+ * stroke, caret-color, outline-color, …) to RN-compatible style entries.
37
+ *
38
+ * lightningcss emits two shapes for color properties:
39
+ * - bare `CssColor` value (e.g. `text-decoration-color`, `outline-color`)
40
+ * - wrapped `{type: 'color', value: CssColor}` (e.g. `fill`, `stroke`,
41
+ * `caret-color`, `accent-color`)
42
+ * We unwrap both and run them through {@link cssColorToString} to land
43
+ * at hex/rgba.
44
+ *
45
+ * Returns `null` for any property the dispatcher doesn't handle so the
46
+ * caller can fall through to the next dispatcher.
47
+ * @param decl One lightningcss declaration.
48
+ * @returns RN entries when the property matched, else `null`.
49
+ */
50
+ export function dispatchColorPropertyDeclaration(decl: LcDeclaration): readonly RNEntry[] | null {
51
+ switch (decl.property) {
52
+ case 'text-decoration-color': {
53
+ return colorEntry('textDecorationColor', decl.value)
54
+ }
55
+ case 'caret-color': {
56
+ return colorEntry('caretColor', unwrapTaggedColor(decl.value))
57
+ }
58
+ case 'fill': {
59
+ return colorEntry('fill', unwrapTaggedColor(decl.value))
60
+ }
61
+ case 'stroke': {
62
+ return colorEntry('stroke', unwrapTaggedColor(decl.value))
63
+ }
64
+ case 'outline-color': {
65
+ // RN doesn't render outlines, but tooling like react-native-web /
66
+ // a11y overlays read it — keep so cross-platform code carries the
67
+ // value through.
68
+ return colorEntry('outlineColor', decl.value)
69
+ }
70
+ case 'accent-color': {
71
+ // RN has no native accent color; skip silently.
72
+ return []
73
+ }
74
+ default: {
75
+ return null
76
+ }
77
+ }
78
+ }