orn-ui 0.2.9 → 0.3.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 (552) hide show
  1. package/AGENTS.md +67 -52
  2. package/README.md +5 -9
  3. package/lib/commonjs/atoms/Avatar.js +77 -5
  4. package/lib/commonjs/atoms/Avatar.js.map +1 -1
  5. package/lib/commonjs/atoms/Badge.js +17 -8
  6. package/lib/commonjs/atoms/Badge.js.map +1 -1
  7. package/lib/commonjs/atoms/Button.js +108 -39
  8. package/lib/commonjs/atoms/Button.js.map +1 -1
  9. package/lib/commonjs/atoms/Card.js +24 -2
  10. package/lib/commonjs/atoms/Card.js.map +1 -1
  11. package/lib/commonjs/atoms/Checkbox.js +41 -10
  12. package/lib/commonjs/atoms/Checkbox.js.map +1 -1
  13. package/lib/commonjs/atoms/Divider.js +30 -5
  14. package/lib/commonjs/atoms/Divider.js.map +1 -1
  15. package/lib/commonjs/atoms/EmptyState.js +58 -20
  16. package/lib/commonjs/atoms/EmptyState.js.map +1 -1
  17. package/lib/commonjs/atoms/Fab.js +32 -12
  18. package/lib/commonjs/atoms/Fab.js.map +1 -1
  19. package/lib/commonjs/atoms/Gradient.js +102 -36
  20. package/lib/commonjs/atoms/Gradient.js.map +1 -1
  21. package/lib/commonjs/atoms/IconButton.js +25 -4
  22. package/lib/commonjs/atoms/IconButton.js.map +1 -1
  23. package/lib/commonjs/atoms/Image.js +61 -25
  24. package/lib/commonjs/atoms/Image.js.map +1 -1
  25. package/lib/commonjs/atoms/Input.js +105 -17
  26. package/lib/commonjs/atoms/Input.js.map +1 -1
  27. package/lib/commonjs/atoms/KeyValueRow.js +37 -9
  28. package/lib/commonjs/atoms/KeyValueRow.js.map +1 -1
  29. package/lib/commonjs/atoms/PressableScale.js +25 -5
  30. package/lib/commonjs/atoms/PressableScale.js.map +1 -1
  31. package/lib/commonjs/atoms/ShinyText.js +29 -29
  32. package/lib/commonjs/atoms/Skeleton.js +28 -13
  33. package/lib/commonjs/atoms/Skeleton.js.map +1 -1
  34. package/lib/commonjs/atoms/Spinner.js +81 -22
  35. package/lib/commonjs/atoms/Spinner.js.map +1 -1
  36. package/lib/commonjs/atoms/Text.js +56 -5
  37. package/lib/commonjs/atoms/Text.js.map +1 -1
  38. package/lib/commonjs/atoms/Transition.js +46 -43
  39. package/lib/commonjs/atoms/Transition.js.map +1 -1
  40. package/lib/commonjs/atoms/index.js +6 -0
  41. package/lib/commonjs/atoms/index.js.map +1 -1
  42. package/lib/commonjs/atoms/inputValidation.js +56 -0
  43. package/lib/commonjs/atoms/inputValidation.js.map +1 -0
  44. package/lib/commonjs/icons/Icon.js +4 -4
  45. package/lib/commonjs/icons/defaultIcons.js +576 -24
  46. package/lib/commonjs/icons/defaultIcons.js.map +1 -1
  47. package/lib/commonjs/icons/index.js +6 -0
  48. package/lib/commonjs/icons/index.js.map +1 -1
  49. package/lib/commonjs/molecules/AvatarHeader.js +47 -14
  50. package/lib/commonjs/molecules/AvatarHeader.js.map +1 -1
  51. package/lib/commonjs/molecules/FormActions.js +25 -9
  52. package/lib/commonjs/molecules/FormActions.js.map +1 -1
  53. package/lib/commonjs/molecules/InfoRow.js +30 -14
  54. package/lib/commonjs/molecules/InfoRow.js.map +1 -1
  55. package/lib/commonjs/molecules/OptionCard.js +37 -22
  56. package/lib/commonjs/molecules/OptionCard.js.map +1 -1
  57. package/lib/commonjs/molecules/SegmentedControl.js +103 -14
  58. package/lib/commonjs/molecules/SegmentedControl.js.map +1 -1
  59. package/lib/commonjs/molecules/Stepper.js +61 -16
  60. package/lib/commonjs/molecules/Stepper.js.map +1 -1
  61. package/lib/commonjs/molecules/Steps.js +105 -41
  62. package/lib/commonjs/molecules/Steps.js.map +1 -1
  63. package/lib/commonjs/molecules/SymmetricGrid.js +26 -18
  64. package/lib/commonjs/molecules/SymmetricGrid.js.map +1 -1
  65. package/lib/commonjs/organisms/Alert.js +49 -35
  66. package/lib/commonjs/organisms/Alert.js.map +1 -1
  67. package/lib/commonjs/organisms/AlertProvider.js +10 -10
  68. package/lib/commonjs/organisms/BottomSheet.js +137 -85
  69. package/lib/commonjs/organisms/BottomSheet.js.map +1 -1
  70. package/lib/commonjs/organisms/Chart.js +197 -156
  71. package/lib/commonjs/organisms/Chart.js.map +1 -1
  72. package/lib/commonjs/organisms/DateField.js +36 -20
  73. package/lib/commonjs/organisms/DateField.js.map +1 -1
  74. package/lib/commonjs/organisms/DatePicker.js +56 -33
  75. package/lib/commonjs/organisms/DatePicker.js.map +1 -1
  76. package/lib/commonjs/organisms/List.js +62 -34
  77. package/lib/commonjs/organisms/List.js.map +1 -1
  78. package/lib/commonjs/organisms/Modal.js +120 -82
  79. package/lib/commonjs/organisms/Modal.js.map +1 -1
  80. package/lib/commonjs/organisms/NavigationBar.js +63 -24
  81. package/lib/commonjs/organisms/NavigationBar.js.map +1 -1
  82. package/lib/commonjs/organisms/OptionWheel.js +64 -64
  83. package/lib/commonjs/organisms/OptionWheel.js.map +1 -1
  84. package/lib/commonjs/organisms/ReorderableList.js +48 -27
  85. package/lib/commonjs/organisms/ReorderableList.js.map +1 -1
  86. package/lib/commonjs/organisms/Screen.js +68 -35
  87. package/lib/commonjs/organisms/Screen.js.map +1 -1
  88. package/lib/commonjs/organisms/SearchList.js +56 -26
  89. package/lib/commonjs/organisms/SearchList.js.map +1 -1
  90. package/lib/commonjs/organisms/Select.js +45 -24
  91. package/lib/commonjs/organisms/Select.js.map +1 -1
  92. package/lib/commonjs/organisms/Slides.js +36 -23
  93. package/lib/commonjs/organisms/Slides.js.map +1 -1
  94. package/lib/commonjs/organisms/ThemeToggle.js +10 -9
  95. package/lib/commonjs/organisms/ThemeToggle.js.map +1 -1
  96. package/lib/commonjs/organisms/Timeline.js +73 -56
  97. package/lib/commonjs/organisms/Timeline.js.map +1 -1
  98. package/lib/commonjs/organisms/Toast.js +46 -15
  99. package/lib/commonjs/organisms/Toast.js.map +1 -1
  100. package/lib/commonjs/organisms/ToastProvider.js +14 -10
  101. package/lib/commonjs/organisms/ToastProvider.js.map +1 -1
  102. package/lib/commonjs/organisms/Wizard.js +40 -15
  103. package/lib/commonjs/organisms/Wizard.js.map +1 -1
  104. package/lib/commonjs/organisms/chart/frame.js +25 -25
  105. package/lib/commonjs/organisms/chart/frame.js.map +1 -1
  106. package/lib/commonjs/organisms/chart/marks.js +42 -42
  107. package/lib/commonjs/organisms/chart/math.js +84 -85
  108. package/lib/commonjs/organisms/chart/math.js.map +1 -1
  109. package/lib/commonjs/organisms/chart/palette.js +28 -28
  110. package/lib/commonjs/organisms/imperativeBridge.js +5 -5
  111. package/lib/commonjs/organisms/imperativeBridge.js.map +1 -1
  112. package/lib/commonjs/organisms/useSlides.js +53 -21
  113. package/lib/commonjs/organisms/useSlides.js.map +1 -1
  114. package/lib/commonjs/safe-area/SafeAreaUIProvider.js +9 -66
  115. package/lib/commonjs/safe-area/SafeAreaUIProvider.js.map +1 -1
  116. package/lib/commonjs/safe-area/index.js.map +1 -1
  117. package/lib/commonjs/theme/KeyboardAvoidance.js +206 -0
  118. package/lib/commonjs/theme/KeyboardAvoidance.js.map +1 -0
  119. package/lib/commonjs/theme/NativeOverlay.js +263 -0
  120. package/lib/commonjs/theme/NativeOverlay.js.map +1 -0
  121. package/lib/commonjs/theme/UIProvider.js +86 -33
  122. package/lib/commonjs/theme/UIProvider.js.map +1 -1
  123. package/lib/commonjs/theme/colors.js +17 -17
  124. package/lib/commonjs/theme/colors.js.map +1 -1
  125. package/lib/commonjs/theme/createStyles.js +19 -16
  126. package/lib/commonjs/theme/createStyles.js.map +1 -1
  127. package/lib/commonjs/theme/createTheme.js +6 -6
  128. package/lib/commonjs/theme/index.js +6 -0
  129. package/lib/commonjs/theme/index.js.map +1 -1
  130. package/lib/commonjs/theme/insets.js +1 -1
  131. package/lib/commonjs/theme/labels.js +29 -1
  132. package/lib/commonjs/theme/labels.js.map +1 -1
  133. package/lib/commonjs/theme/palettes.js +25 -23
  134. package/lib/commonjs/theme/palettes.js.map +1 -1
  135. package/lib/commonjs/theme/tokens.js +28 -14
  136. package/lib/commonjs/theme/tokens.js.map +1 -1
  137. package/lib/commonjs/theme/useKeyboardField.js +36 -0
  138. package/lib/commonjs/theme/useKeyboardField.js.map +1 -0
  139. package/lib/commonjs/theme/useReduceMotion.js +31 -0
  140. package/lib/commonjs/theme/useReduceMotion.js.map +1 -0
  141. package/lib/module/atoms/Avatar.js +79 -8
  142. package/lib/module/atoms/Avatar.js.map +1 -1
  143. package/lib/module/atoms/Badge.js +17 -8
  144. package/lib/module/atoms/Badge.js.map +1 -1
  145. package/lib/module/atoms/Button.js +110 -41
  146. package/lib/module/atoms/Button.js.map +1 -1
  147. package/lib/module/atoms/Card.js +24 -2
  148. package/lib/module/atoms/Card.js.map +1 -1
  149. package/lib/module/atoms/Checkbox.js +42 -11
  150. package/lib/module/atoms/Checkbox.js.map +1 -1
  151. package/lib/module/atoms/Divider.js +31 -6
  152. package/lib/module/atoms/Divider.js.map +1 -1
  153. package/lib/module/atoms/EmptyState.js +58 -20
  154. package/lib/module/atoms/EmptyState.js.map +1 -1
  155. package/lib/module/atoms/Fab.js +33 -13
  156. package/lib/module/atoms/Fab.js.map +1 -1
  157. package/lib/module/atoms/Gradient.js +103 -37
  158. package/lib/module/atoms/Gradient.js.map +1 -1
  159. package/lib/module/atoms/IconButton.js +25 -4
  160. package/lib/module/atoms/IconButton.js.map +1 -1
  161. package/lib/module/atoms/Image.js +61 -25
  162. package/lib/module/atoms/Image.js.map +1 -1
  163. package/lib/module/atoms/Input.js +108 -20
  164. package/lib/module/atoms/Input.js.map +1 -1
  165. package/lib/module/atoms/KeyValueRow.js +37 -9
  166. package/lib/module/atoms/KeyValueRow.js.map +1 -1
  167. package/lib/module/atoms/PressableScale.js +25 -5
  168. package/lib/module/atoms/PressableScale.js.map +1 -1
  169. package/lib/module/atoms/ShinyText.js +29 -29
  170. package/lib/module/atoms/Skeleton.js +30 -13
  171. package/lib/module/atoms/Skeleton.js.map +1 -1
  172. package/lib/module/atoms/Spinner.js +82 -23
  173. package/lib/module/atoms/Spinner.js.map +1 -1
  174. package/lib/module/atoms/Text.js +56 -5
  175. package/lib/module/atoms/Text.js.map +1 -1
  176. package/lib/module/atoms/Transition.js +41 -42
  177. package/lib/module/atoms/Transition.js.map +1 -1
  178. package/lib/module/atoms/index.js +1 -1
  179. package/lib/module/atoms/index.js.map +1 -1
  180. package/lib/module/atoms/inputValidation.js +52 -0
  181. package/lib/module/atoms/inputValidation.js.map +1 -0
  182. package/lib/module/icons/Icon.js +4 -4
  183. package/lib/module/icons/defaultIcons.js +574 -24
  184. package/lib/module/icons/defaultIcons.js.map +1 -1
  185. package/lib/module/icons/index.js +1 -1
  186. package/lib/module/icons/index.js.map +1 -1
  187. package/lib/module/molecules/AvatarHeader.js +47 -14
  188. package/lib/module/molecules/AvatarHeader.js.map +1 -1
  189. package/lib/module/molecules/FormActions.js +25 -9
  190. package/lib/module/molecules/FormActions.js.map +1 -1
  191. package/lib/module/molecules/InfoRow.js +30 -14
  192. package/lib/module/molecules/InfoRow.js.map +1 -1
  193. package/lib/module/molecules/OptionCard.js +37 -22
  194. package/lib/module/molecules/OptionCard.js.map +1 -1
  195. package/lib/module/molecules/SegmentedControl.js +107 -18
  196. package/lib/module/molecules/SegmentedControl.js.map +1 -1
  197. package/lib/module/molecules/Stepper.js +63 -18
  198. package/lib/module/molecules/Stepper.js.map +1 -1
  199. package/lib/module/molecules/Steps.js +107 -43
  200. package/lib/module/molecules/Steps.js.map +1 -1
  201. package/lib/module/molecules/SymmetricGrid.js +26 -18
  202. package/lib/module/molecules/SymmetricGrid.js.map +1 -1
  203. package/lib/module/organisms/Alert.js +51 -37
  204. package/lib/module/organisms/Alert.js.map +1 -1
  205. package/lib/module/organisms/AlertProvider.js +10 -10
  206. package/lib/module/organisms/BottomSheet.js +142 -88
  207. package/lib/module/organisms/BottomSheet.js.map +1 -1
  208. package/lib/module/organisms/Chart.js +199 -158
  209. package/lib/module/organisms/Chart.js.map +1 -1
  210. package/lib/module/organisms/DateField.js +36 -20
  211. package/lib/module/organisms/DateField.js.map +1 -1
  212. package/lib/module/organisms/DatePicker.js +56 -33
  213. package/lib/module/organisms/DatePicker.js.map +1 -1
  214. package/lib/module/organisms/List.js +62 -34
  215. package/lib/module/organisms/List.js.map +1 -1
  216. package/lib/module/organisms/Modal.js +122 -84
  217. package/lib/module/organisms/Modal.js.map +1 -1
  218. package/lib/module/organisms/NavigationBar.js +64 -25
  219. package/lib/module/organisms/NavigationBar.js.map +1 -1
  220. package/lib/module/organisms/OptionWheel.js +64 -64
  221. package/lib/module/organisms/OptionWheel.js.map +1 -1
  222. package/lib/module/organisms/ReorderableList.js +48 -27
  223. package/lib/module/organisms/ReorderableList.js.map +1 -1
  224. package/lib/module/organisms/Screen.js +70 -37
  225. package/lib/module/organisms/Screen.js.map +1 -1
  226. package/lib/module/organisms/SearchList.js +56 -26
  227. package/lib/module/organisms/SearchList.js.map +1 -1
  228. package/lib/module/organisms/Select.js +46 -25
  229. package/lib/module/organisms/Select.js.map +1 -1
  230. package/lib/module/organisms/Slides.js +37 -24
  231. package/lib/module/organisms/Slides.js.map +1 -1
  232. package/lib/module/organisms/ThemeToggle.js +11 -10
  233. package/lib/module/organisms/ThemeToggle.js.map +1 -1
  234. package/lib/module/organisms/Timeline.js +73 -56
  235. package/lib/module/organisms/Timeline.js.map +1 -1
  236. package/lib/module/organisms/Toast.js +47 -16
  237. package/lib/module/organisms/Toast.js.map +1 -1
  238. package/lib/module/organisms/ToastProvider.js +15 -11
  239. package/lib/module/organisms/ToastProvider.js.map +1 -1
  240. package/lib/module/organisms/Wizard.js +41 -16
  241. package/lib/module/organisms/Wizard.js.map +1 -1
  242. package/lib/module/organisms/chart/frame.js +25 -25
  243. package/lib/module/organisms/chart/frame.js.map +1 -1
  244. package/lib/module/organisms/chart/marks.js +42 -42
  245. package/lib/module/organisms/chart/math.js +84 -85
  246. package/lib/module/organisms/chart/math.js.map +1 -1
  247. package/lib/module/organisms/chart/palette.js +28 -28
  248. package/lib/module/organisms/imperativeBridge.js +5 -5
  249. package/lib/module/organisms/imperativeBridge.js.map +1 -1
  250. package/lib/module/organisms/useSlides.js +53 -21
  251. package/lib/module/organisms/useSlides.js.map +1 -1
  252. package/lib/module/safe-area/SafeAreaUIProvider.js +10 -65
  253. package/lib/module/safe-area/SafeAreaUIProvider.js.map +1 -1
  254. package/lib/module/safe-area/index.js +1 -0
  255. package/lib/module/safe-area/index.js.map +1 -1
  256. package/lib/module/theme/KeyboardAvoidance.js +198 -0
  257. package/lib/module/theme/KeyboardAvoidance.js.map +1 -0
  258. package/lib/module/theme/NativeOverlay.js +257 -0
  259. package/lib/module/theme/NativeOverlay.js.map +1 -0
  260. package/lib/module/theme/UIProvider.js +88 -35
  261. package/lib/module/theme/UIProvider.js.map +1 -1
  262. package/lib/module/theme/colors.js +17 -17
  263. package/lib/module/theme/colors.js.map +1 -1
  264. package/lib/module/theme/createStyles.js +20 -16
  265. package/lib/module/theme/createStyles.js.map +1 -1
  266. package/lib/module/theme/createTheme.js +6 -6
  267. package/lib/module/theme/index.js +1 -1
  268. package/lib/module/theme/index.js.map +1 -1
  269. package/lib/module/theme/insets.js +1 -1
  270. package/lib/module/theme/labels.js +29 -1
  271. package/lib/module/theme/labels.js.map +1 -1
  272. package/lib/module/theme/palettes.js +25 -23
  273. package/lib/module/theme/palettes.js.map +1 -1
  274. package/lib/module/theme/tokens.js +27 -13
  275. package/lib/module/theme/tokens.js.map +1 -1
  276. package/lib/module/theme/useKeyboardField.js +33 -0
  277. package/lib/module/theme/useKeyboardField.js.map +1 -0
  278. package/lib/module/theme/useReduceMotion.js +28 -0
  279. package/lib/module/theme/useReduceMotion.js.map +1 -0
  280. package/lib/typescript/atoms/Avatar.d.ts +35 -5
  281. package/lib/typescript/atoms/Avatar.d.ts.map +1 -1
  282. package/lib/typescript/atoms/Badge.d.ts +14 -3
  283. package/lib/typescript/atoms/Badge.d.ts.map +1 -1
  284. package/lib/typescript/atoms/Button.d.ts +34 -3
  285. package/lib/typescript/atoms/Button.d.ts.map +1 -1
  286. package/lib/typescript/atoms/Card.d.ts +13 -2
  287. package/lib/typescript/atoms/Card.d.ts.map +1 -1
  288. package/lib/typescript/atoms/Checkbox.d.ts +26 -1
  289. package/lib/typescript/atoms/Checkbox.d.ts.map +1 -1
  290. package/lib/typescript/atoms/Divider.d.ts +18 -1
  291. package/lib/typescript/atoms/Divider.d.ts.map +1 -1
  292. package/lib/typescript/atoms/EmptyState.d.ts +30 -3
  293. package/lib/typescript/atoms/EmptyState.d.ts.map +1 -1
  294. package/lib/typescript/atoms/Fab.d.ts +24 -4
  295. package/lib/typescript/atoms/Fab.d.ts.map +1 -1
  296. package/lib/typescript/atoms/Gradient.d.ts +21 -15
  297. package/lib/typescript/atoms/Gradient.d.ts.map +1 -1
  298. package/lib/typescript/atoms/IconButton.d.ts +18 -4
  299. package/lib/typescript/atoms/IconButton.d.ts.map +1 -1
  300. package/lib/typescript/atoms/Image.d.ts +39 -17
  301. package/lib/typescript/atoms/Image.d.ts.map +1 -1
  302. package/lib/typescript/atoms/Input.d.ts +57 -7
  303. package/lib/typescript/atoms/Input.d.ts.map +1 -1
  304. package/lib/typescript/atoms/KeyValueRow.d.ts +16 -4
  305. package/lib/typescript/atoms/KeyValueRow.d.ts.map +1 -1
  306. package/lib/typescript/atoms/PressableScale.d.ts +12 -5
  307. package/lib/typescript/atoms/PressableScale.d.ts.map +1 -1
  308. package/lib/typescript/atoms/ShinyText.d.ts +17 -17
  309. package/lib/typescript/atoms/ShinyText.d.ts.map +1 -1
  310. package/lib/typescript/atoms/Skeleton.d.ts +12 -7
  311. package/lib/typescript/atoms/Skeleton.d.ts.map +1 -1
  312. package/lib/typescript/atoms/Spinner.d.ts +29 -1
  313. package/lib/typescript/atoms/Spinner.d.ts.map +1 -1
  314. package/lib/typescript/atoms/Text.d.ts +46 -0
  315. package/lib/typescript/atoms/Text.d.ts.map +1 -1
  316. package/lib/typescript/atoms/Transition.d.ts +19 -15
  317. package/lib/typescript/atoms/Transition.d.ts.map +1 -1
  318. package/lib/typescript/atoms/index.d.ts +3 -3
  319. package/lib/typescript/atoms/index.d.ts.map +1 -1
  320. package/lib/typescript/atoms/inputValidation.d.ts +29 -0
  321. package/lib/typescript/atoms/inputValidation.d.ts.map +1 -0
  322. package/lib/typescript/icons/Icon.d.ts +4 -4
  323. package/lib/typescript/icons/defaultIcons.d.ts +19 -2
  324. package/lib/typescript/icons/defaultIcons.d.ts.map +1 -1
  325. package/lib/typescript/icons/index.d.ts +2 -2
  326. package/lib/typescript/icons/index.d.ts.map +1 -1
  327. package/lib/typescript/icons/types.d.ts +13 -3
  328. package/lib/typescript/icons/types.d.ts.map +1 -1
  329. package/lib/typescript/molecules/AvatarHeader.d.ts +23 -4
  330. package/lib/typescript/molecules/AvatarHeader.d.ts.map +1 -1
  331. package/lib/typescript/molecules/FormActions.d.ts +21 -4
  332. package/lib/typescript/molecules/FormActions.d.ts.map +1 -1
  333. package/lib/typescript/molecules/InfoRow.d.ts +17 -3
  334. package/lib/typescript/molecules/InfoRow.d.ts.map +1 -1
  335. package/lib/typescript/molecules/OptionCard.d.ts +20 -5
  336. package/lib/typescript/molecules/OptionCard.d.ts.map +1 -1
  337. package/lib/typescript/molecules/SegmentedControl.d.ts +18 -2
  338. package/lib/typescript/molecules/SegmentedControl.d.ts.map +1 -1
  339. package/lib/typescript/molecules/Stepper.d.ts +23 -7
  340. package/lib/typescript/molecules/Stepper.d.ts.map +1 -1
  341. package/lib/typescript/molecules/Steps.d.ts +18 -4
  342. package/lib/typescript/molecules/Steps.d.ts.map +1 -1
  343. package/lib/typescript/molecules/SymmetricGrid.d.ts +15 -5
  344. package/lib/typescript/molecules/SymmetricGrid.d.ts.map +1 -1
  345. package/lib/typescript/organisms/Alert.d.ts +28 -2
  346. package/lib/typescript/organisms/Alert.d.ts.map +1 -1
  347. package/lib/typescript/organisms/AlertProvider.d.ts +10 -10
  348. package/lib/typescript/organisms/AlertProvider.d.ts.map +1 -1
  349. package/lib/typescript/organisms/BottomSheet.d.ts +20 -5
  350. package/lib/typescript/organisms/BottomSheet.d.ts.map +1 -1
  351. package/lib/typescript/organisms/Chart.d.ts +86 -75
  352. package/lib/typescript/organisms/Chart.d.ts.map +1 -1
  353. package/lib/typescript/organisms/DateField.d.ts +17 -3
  354. package/lib/typescript/organisms/DateField.d.ts.map +1 -1
  355. package/lib/typescript/organisms/DatePicker.d.ts +7 -11
  356. package/lib/typescript/organisms/DatePicker.d.ts.map +1 -1
  357. package/lib/typescript/organisms/List.d.ts +30 -3
  358. package/lib/typescript/organisms/List.d.ts.map +1 -1
  359. package/lib/typescript/organisms/Modal.d.ts +30 -3
  360. package/lib/typescript/organisms/Modal.d.ts.map +1 -1
  361. package/lib/typescript/organisms/NavigationBar.d.ts +26 -7
  362. package/lib/typescript/organisms/NavigationBar.d.ts.map +1 -1
  363. package/lib/typescript/organisms/OptionWheel.d.ts +20 -16
  364. package/lib/typescript/organisms/OptionWheel.d.ts.map +1 -1
  365. package/lib/typescript/organisms/ReorderableList.d.ts +20 -11
  366. package/lib/typescript/organisms/ReorderableList.d.ts.map +1 -1
  367. package/lib/typescript/organisms/Screen.d.ts +27 -8
  368. package/lib/typescript/organisms/Screen.d.ts.map +1 -1
  369. package/lib/typescript/organisms/SearchList.d.ts +46 -3
  370. package/lib/typescript/organisms/SearchList.d.ts.map +1 -1
  371. package/lib/typescript/organisms/Select.d.ts +26 -6
  372. package/lib/typescript/organisms/Select.d.ts.map +1 -1
  373. package/lib/typescript/organisms/Slides.d.ts +44 -26
  374. package/lib/typescript/organisms/Slides.d.ts.map +1 -1
  375. package/lib/typescript/organisms/ThemeToggle.d.ts +6 -2
  376. package/lib/typescript/organisms/ThemeToggle.d.ts.map +1 -1
  377. package/lib/typescript/organisms/Timeline.d.ts +26 -9
  378. package/lib/typescript/organisms/Timeline.d.ts.map +1 -1
  379. package/lib/typescript/organisms/Toast.d.ts +19 -4
  380. package/lib/typescript/organisms/Toast.d.ts.map +1 -1
  381. package/lib/typescript/organisms/ToastProvider.d.ts +8 -8
  382. package/lib/typescript/organisms/ToastProvider.d.ts.map +1 -1
  383. package/lib/typescript/organisms/Wizard.d.ts +24 -6
  384. package/lib/typescript/organisms/Wizard.d.ts.map +1 -1
  385. package/lib/typescript/organisms/chart/frame.d.ts +21 -21
  386. package/lib/typescript/organisms/chart/frame.d.ts.map +1 -1
  387. package/lib/typescript/organisms/chart/marks.d.ts +28 -28
  388. package/lib/typescript/organisms/chart/marks.d.ts.map +1 -1
  389. package/lib/typescript/organisms/chart/math.d.ts +69 -70
  390. package/lib/typescript/organisms/chart/math.d.ts.map +1 -1
  391. package/lib/typescript/organisms/chart/palette.d.ts +2 -2
  392. package/lib/typescript/organisms/chart/palette.d.ts.map +1 -1
  393. package/lib/typescript/organisms/imperativeBridge.d.ts +4 -4
  394. package/lib/typescript/organisms/useSlides.d.ts +8 -16
  395. package/lib/typescript/organisms/useSlides.d.ts.map +1 -1
  396. package/lib/typescript/safe-area/SafeAreaUIProvider.d.ts +9 -7
  397. package/lib/typescript/safe-area/SafeAreaUIProvider.d.ts.map +1 -1
  398. package/lib/typescript/safe-area/index.d.ts.map +1 -1
  399. package/lib/typescript/theme/KeyboardAvoidance.d.ts +62 -0
  400. package/lib/typescript/theme/KeyboardAvoidance.d.ts.map +1 -0
  401. package/lib/typescript/theme/NativeOverlay.d.ts +43 -0
  402. package/lib/typescript/theme/NativeOverlay.d.ts.map +1 -0
  403. package/lib/typescript/theme/UIProvider.d.ts +38 -20
  404. package/lib/typescript/theme/UIProvider.d.ts.map +1 -1
  405. package/lib/typescript/theme/colors.d.ts +13 -13
  406. package/lib/typescript/theme/colors.d.ts.map +1 -1
  407. package/lib/typescript/theme/createStyles.d.ts +20 -14
  408. package/lib/typescript/theme/createStyles.d.ts.map +1 -1
  409. package/lib/typescript/theme/createTheme.d.ts +5 -5
  410. package/lib/typescript/theme/index.d.ts +1 -1
  411. package/lib/typescript/theme/index.d.ts.map +1 -1
  412. package/lib/typescript/theme/insets.d.ts +1 -1
  413. package/lib/typescript/theme/insets.d.ts.map +1 -1
  414. package/lib/typescript/theme/labels.d.ts +57 -2
  415. package/lib/typescript/theme/labels.d.ts.map +1 -1
  416. package/lib/typescript/theme/palettes.d.ts +25 -19
  417. package/lib/typescript/theme/palettes.d.ts.map +1 -1
  418. package/lib/typescript/theme/tokens.d.ts +15 -8
  419. package/lib/typescript/theme/tokens.d.ts.map +1 -1
  420. package/lib/typescript/theme/types.d.ts +7 -7
  421. package/lib/typescript/theme/types.d.ts.map +1 -1
  422. package/lib/typescript/theme/useKeyboardField.d.ts +17 -0
  423. package/lib/typescript/theme/useKeyboardField.d.ts.map +1 -0
  424. package/lib/typescript/theme/useReduceMotion.d.ts +7 -0
  425. package/lib/typescript/theme/useReduceMotion.d.ts.map +1 -0
  426. package/llms.txt +4 -4
  427. package/package.json +15 -14
  428. package/registry/alert-provider.json +4 -3
  429. package/registry/alert.json +3 -2
  430. package/registry/avatar-header.json +1 -1
  431. package/registry/avatar.json +3 -2
  432. package/registry/badge.json +1 -1
  433. package/registry/bottom-sheet.json +1 -1
  434. package/registry/button.json +3 -2
  435. package/registry/card.json +1 -1
  436. package/registry/chart.json +5 -5
  437. package/registry/checkbox.json +3 -2
  438. package/registry/core.json +31 -15
  439. package/registry/date-field.json +1 -1
  440. package/registry/date-picker.json +3 -2
  441. package/registry/divider.json +1 -1
  442. package/registry/empty-state.json +4 -2
  443. package/registry/fab.json +3 -2
  444. package/registry/form-actions.json +3 -2
  445. package/registry/gradient.json +1 -1
  446. package/registry/icon-button.json +3 -2
  447. package/registry/image.json +1 -1
  448. package/registry/index.json +79 -61
  449. package/registry/info-row.json +1 -1
  450. package/registry/input.json +5 -1
  451. package/registry/key-value-row.json +1 -1
  452. package/registry/list.json +3 -1
  453. package/registry/manifest.json +3 -1
  454. package/registry/modal.json +1 -1
  455. package/registry/navigation-bar.json +1 -1
  456. package/registry/option-card.json +3 -2
  457. package/registry/option-wheel.json +1 -1
  458. package/registry/pressable-scale.json +3 -2
  459. package/registry/reorderable-list.json +1 -1
  460. package/registry/screen.json +1 -1
  461. package/registry/search-list.json +7 -1
  462. package/registry/segmented-control.json +1 -1
  463. package/registry/select.json +1 -1
  464. package/registry/shiny-text.json +1 -1
  465. package/registry/skeleton.json +3 -2
  466. package/registry/slides.json +2 -2
  467. package/registry/spinner.json +1 -1
  468. package/registry/stepper.json +1 -1
  469. package/registry/steps.json +1 -1
  470. package/registry/symmetric-grid.json +1 -1
  471. package/registry/theme-toggle.json +1 -1
  472. package/registry/timeline.json +1 -1
  473. package/registry/title.json +1 -1
  474. package/registry/toast-provider.json +2 -2
  475. package/registry/toast.json +1 -1
  476. package/registry/transition.json +1 -1
  477. package/registry/wizard.json +1 -1
  478. package/src/atoms/Avatar.tsx +117 -26
  479. package/src/atoms/Badge.tsx +20 -9
  480. package/src/atoms/Button.tsx +102 -30
  481. package/src/atoms/Card.tsx +23 -3
  482. package/src/atoms/Checkbox.tsx +49 -12
  483. package/src/atoms/Divider.tsx +34 -4
  484. package/src/atoms/EmptyState.tsx +63 -16
  485. package/src/atoms/Fab.tsx +56 -9
  486. package/src/atoms/Gradient.tsx +92 -41
  487. package/src/atoms/IconButton.tsx +27 -4
  488. package/src/atoms/Image.tsx +74 -28
  489. package/src/atoms/Input.tsx +129 -19
  490. package/src/atoms/KeyValueRow.tsx +72 -28
  491. package/src/atoms/PressableScale.tsx +31 -7
  492. package/src/atoms/ShinyText.tsx +31 -31
  493. package/src/atoms/Skeleton.tsx +37 -17
  494. package/src/atoms/Spinner.tsx +87 -24
  495. package/src/atoms/Text.tsx +66 -6
  496. package/src/atoms/Transition.tsx +40 -44
  497. package/src/atoms/index.ts +3 -3
  498. package/src/atoms/inputValidation.ts +59 -0
  499. package/src/icons/Icon.tsx +4 -4
  500. package/src/icons/defaultIcons.tsx +445 -22
  501. package/src/icons/index.ts +2 -2
  502. package/src/icons/types.ts +26 -4
  503. package/src/molecules/AvatarHeader.tsx +88 -34
  504. package/src/molecules/FormActions.tsx +36 -6
  505. package/src/molecules/InfoRow.tsx +39 -12
  506. package/src/molecules/OptionCard.tsx +55 -20
  507. package/src/molecules/SegmentedControl.tsx +135 -15
  508. package/src/molecules/Stepper.tsx +73 -19
  509. package/src/molecules/Steps.tsx +121 -39
  510. package/src/molecules/SymmetricGrid.tsx +28 -12
  511. package/src/organisms/Alert.tsx +97 -26
  512. package/src/organisms/AlertProvider.tsx +13 -13
  513. package/src/organisms/BottomSheet.tsx +115 -43
  514. package/src/organisms/Chart.tsx +223 -165
  515. package/src/organisms/DateField.tsx +40 -15
  516. package/src/organisms/DatePicker.tsx +53 -32
  517. package/src/organisms/List.tsx +64 -15
  518. package/src/organisms/Modal.tsx +122 -63
  519. package/src/organisms/NavigationBar.tsx +73 -23
  520. package/src/organisms/OptionWheel.tsx +70 -66
  521. package/src/organisms/ReorderableList.tsx +49 -27
  522. package/src/organisms/Screen.tsx +79 -42
  523. package/src/organisms/SearchList.tsx +84 -20
  524. package/src/organisms/Select.tsx +63 -29
  525. package/src/organisms/Slides.tsx +63 -44
  526. package/src/organisms/ThemeToggle.tsx +15 -6
  527. package/src/organisms/Timeline.tsx +79 -62
  528. package/src/organisms/Toast.tsx +50 -15
  529. package/src/organisms/ToastProvider.tsx +17 -13
  530. package/src/organisms/Wizard.tsx +58 -21
  531. package/src/organisms/chart/frame.tsx +35 -35
  532. package/src/organisms/chart/marks.tsx +53 -53
  533. package/src/organisms/chart/math.ts +93 -94
  534. package/src/organisms/chart/palette.ts +28 -28
  535. package/src/organisms/imperativeBridge.ts +5 -5
  536. package/src/organisms/useSlides.ts +66 -31
  537. package/src/safe-area/SafeAreaUIProvider.tsx +9 -70
  538. package/src/safe-area/index.ts +1 -0
  539. package/src/theme/KeyboardAvoidance.tsx +216 -0
  540. package/src/theme/NativeOverlay.tsx +269 -0
  541. package/src/theme/UIProvider.tsx +109 -46
  542. package/src/theme/colors.ts +21 -21
  543. package/src/theme/createStyles.ts +23 -19
  544. package/src/theme/createTheme.ts +6 -6
  545. package/src/theme/index.ts +1 -1
  546. package/src/theme/insets.ts +1 -1
  547. package/src/theme/labels.ts +85 -2
  548. package/src/theme/palettes.ts +33 -25
  549. package/src/theme/tokens.ts +27 -13
  550. package/src/theme/types.ts +7 -7
  551. package/src/theme/useKeyboardField.ts +33 -0
  552. package/src/theme/useReduceMotion.ts +27 -0
@@ -9,10 +9,12 @@
9
9
  "files": [
10
10
  {
11
11
  "path": "atoms/EmptyState.tsx",
12
- "content": "import React, { memo } from 'react';\nimport { Text, View, type ViewStyle } from 'react-native';\nimport { createStyles } from '../theme/createStyles';\nimport { useAllowFontScaling, useColors, useLabels } from '../theme/UIProvider';\nimport { Icon } from '../icons/Icon';\nimport type { IconName } from '../icons/types';\n\nexport interface EmptyStateProps {\n title?: string;\n description?: string;\n iconName?: IconName;\n icon?: React.ReactNode;\n style?: ViewStyle;\n}\n\nconst useStyles = createStyles((theme) => ({\n container: {\n flex: 1,\n justifyContent: 'center',\n alignItems: 'center',\n padding: theme.tokens.spacing.xxl - 4,\n minHeight: 200,\n },\n title: {\n fontSize: theme.tokens.fontSize.lg,\n fontWeight: theme.tokens.fontWeight.semibold,\n color: theme.colors.textLight,\n marginTop: theme.tokens.spacing.md - 2,\n textAlign: 'center',\n },\n description: {\n fontSize: theme.tokens.fontSize.md,\n color: theme.colors.textLight,\n marginTop: theme.tokens.spacing.xs + 1,\n textAlign: 'center',\n },\n}));\n\nexport const EmptyState = memo(\n ({ title, description, iconName = 'search', icon, style }: EmptyStateProps) => {\n const colors = useColors();\n const styles = useStyles();\n const labels = useLabels();\n const allowFontScaling = useAllowFontScaling();\n const resolvedTitle = title ?? labels.noResultsTitle;\n const resolvedDescription = description ?? labels.noResultsDescription;\n return (\n <View style={[styles.container, style]} accessible accessibilityRole=\"text\">\n {icon ?? <Icon name={iconName} size={48} color={colors.textLight} />}\n <Text allowFontScaling={allowFontScaling} style={styles.title}>\n {resolvedTitle}\n </Text>\n <Text allowFontScaling={allowFontScaling} style={styles.description}>\n {resolvedDescription}\n </Text>\n </View>\n );\n }\n);\nEmptyState.displayName = 'EmptyState';\n"
12
+ "content": "import React, { memo } from 'react';\nimport { Text, View, type StyleProp, type ViewStyle } from 'react-native';\nimport { createStyles } from '../theme/createStyles';\nimport { useAllowFontScaling, useColors, useLabels } from '../theme/UIProvider';\nimport { Icon } from '../icons/Icon';\nimport type { IconName } from '../icons/types';\nimport { Button, type ButtonVariant } from './Button';\n\nexport interface EmptyStateAction {\n label: string;\n onPress: () => void;\n /** @default 'primary' */\n variant?: ButtonVariant;\n}\n\nexport interface EmptyStateProps {\n /** Main line. @default labels.noResultsTitle */\n title?: string;\n /** Supporting line under the title. @default labels.noResultsDescription */\n description?: string;\n /** Built-in or custom icon name. Ignored when `icon` is passed. @default 'search' */\n iconName?: IconName;\n /** Any node in place of the icon (an Image, an illustration). */\n icon?: React.ReactNode;\n /** The next step, shown as a button under the text (e.g. \"Add client\"). */\n action?: EmptyStateAction;\n style?: StyleProp<ViewStyle>;\n testID?: string;\n}\n\n/** Icon size: the block's focal point without competing with the title. */\nconst ICON_SIZE = 48;\n/** Minimum height: the block doesn't get squashed inside a short empty list. */\nconst MIN_HEIGHT = 200;\n\nconst useStyles = createStyles((theme) => ({\n container: {\n flex: 1,\n justifyContent: 'center',\n alignItems: 'center',\n padding: theme.tokens.spacing.xl,\n minHeight: MIN_HEIGHT,\n },\n title: {\n fontSize: theme.tokens.fontSize.lg,\n fontWeight: theme.tokens.fontWeight.semibold,\n lineHeight: theme.tokens.lineHeight.lg,\n color: theme.colors.text,\n marginTop: theme.tokens.spacing.md,\n textAlign: 'center',\n },\n description: {\n fontSize: theme.tokens.fontSize.md,\n lineHeight: theme.tokens.lineHeight.md,\n color: theme.colors.textLight,\n marginTop: theme.tokens.spacing.xs,\n textAlign: 'center',\n },\n message: { alignItems: 'center' },\n action: { marginTop: theme.tokens.spacing.lg },\n}));\n\n/**\n * EmptyState\n * Centered icon + title + description for a list or screen with no data.\n *\n * Gestalt, proximity: title and description sit close together (`spacing.xs`)\n * and the icon one step further (`spacing.md`), so they read as a message\n * with its image and not as three loose pieces. Similarity: the title in\n * `text` and the description in `textLight` mark the hierarchy. The action,\n * if any, is set further apart (`spacing.lg`): it is the next step, not part\n * of the message.\n * The message is a single accessibility node (title and description together;\n * the icon is decorative) and the button a separate, focusable one.\n */\nexport const EmptyState = memo(\n ({ title, description, iconName = 'search', icon, action, style, testID }: EmptyStateProps) => {\n const colors = useColors();\n const styles = useStyles();\n const labels = useLabels();\n const allowFontScaling = useAllowFontScaling();\n const resolvedTitle = title ?? labels.noResultsTitle;\n const resolvedDescription = description ?? labels.noResultsDescription;\n return (\n <View style={[styles.container, style]} testID={testID}>\n <View style={styles.message} accessible accessibilityRole=\"text\">\n {icon ?? <Icon name={iconName} size={ICON_SIZE} color={colors.textLight} />}\n <Text allowFontScaling={allowFontScaling} style={styles.title}>\n {resolvedTitle}\n </Text>\n <Text allowFontScaling={allowFontScaling} style={styles.description}>\n {resolvedDescription}\n </Text>\n </View>\n {action && (\n <Button\n title={action.label}\n onPress={action.onPress}\n variant={action.variant ?? 'primary'}\n style={styles.action}\n />\n )}\n </View>\n );\n }\n);\nEmptyState.displayName = 'EmptyState';\n"
13
13
  }
14
14
  ],
15
15
  "registryDependencies": [
16
- "core"
16
+ "core",
17
+ "button",
18
+ "transition"
17
19
  ]
18
20
  }
package/registry/fab.json CHANGED
@@ -9,11 +9,12 @@
9
9
  "files": [
10
10
  {
11
11
  "path": "atoms/Fab.tsx",
12
- "content": "import React, { memo } from 'react';\nimport { type GestureResponderEvent, type StyleProp, type ViewStyle } from 'react-native';\nimport { PressableScale } from './PressableScale';\nimport { Icon } from '../icons/Icon';\nimport type { IconName } from '../icons/types';\nimport { createStyles } from '../theme/createStyles';\nimport { useColors } from '../theme/UIProvider';\n\nexport interface FabProps {\n onPress: (e: GestureResponderEvent) => void;\n iconName?: IconName;\n icon?: React.ReactNode;\n size?: number;\n bottom?: number;\n right?: number;\n color?: string;\n style?: StyleProp<ViewStyle>;\n accessibilityLabel: string;\n}\n\nconst useStyles = createStyles((theme) => ({\n fab: {\n position: 'absolute',\n justifyContent: 'center',\n alignItems: 'center',\n ...theme.tokens.shadow.lg,\n },\n}));\n\n/**\n * Fab\n * Botón flotante circular. `bottom`/`right` en vez del `tabBarHeight`\n * obligatorio de la versión original — el consumidor decide su propio\n * offset (con o sin tab bar).\n */\nexport const Fab = memo(\n ({ onPress, iconName = 'plus', icon, size = 60, bottom = 20, right = 20, color, style, accessibilityLabel }: FabProps) => {\n const styles = useStyles();\n const colors = useColors();\n return (\n <PressableScale\n onPress={onPress}\n style={[\n styles.fab,\n {\n width: size,\n height: size,\n borderRadius: size / 2,\n bottom,\n right,\n backgroundColor: color ?? colors.primary,\n },\n style,\n ]}\n accessibilityRole=\"button\"\n accessibilityLabel={accessibilityLabel}\n >\n {icon ?? <Icon name={iconName} size={size * 0.5} color={colors.onPrimary} />}\n </PressableScale>\n );\n }\n);\nFab.displayName = 'Fab';\n"
12
+ "content": "import React, { memo } from 'react';\nimport { type GestureResponderEvent, type StyleProp, type ViewStyle } from 'react-native';\nimport { PressableScale } from './PressableScale';\nimport { Icon } from '../icons/Icon';\nimport { onColor } from '../icons/defaultIcons';\nimport type { IconName } from '../icons/types';\nimport { createStyles } from '../theme/createStyles';\nimport { useColors, useInsets, useTheme } from '../theme/UIProvider';\n\nexport interface FabProps {\n /** Called when the button is pressed. */\n onPress: (e: GestureResponderEvent) => void;\n /** Built-in or custom icon name. Ignored when `icon` is passed. @default 'plus' */\n iconName?: IconName;\n /** Any node in place of the icon. */\n icon?: React.ReactNode;\n /** Diameter in pt. @default 60 */\n size?: number;\n /** Distance from the bottom edge, on top of the safe area. @default tokens.spacing.xl (20) */\n bottom?: number;\n /** Distance from the right edge, on top of the safe area. @default tokens.spacing.xl (20) */\n right?: number;\n /**\n * Adds the safe-area insets to `bottom`/`right`, so the button clears the\n * home indicator. Pass false above a NavigationBar or tab bar, which\n * already covers that edge. @default true\n */\n safeArea?: boolean;\n /** Fill color. The icon switches to black or white for contrast. @default theme.colors.primary */\n color?: string;\n style?: StyleProp<ViewStyle>;\n /** Required: the button has no visible text. */\n accessibilityLabel: string;\n testID?: string;\n}\n\n/** Default diameter: comfortably above the 44pt touch minimum (Fitts). */\nconst DEFAULT_SIZE = 60;\n/** The glyph takes up half the circle. */\nconst ICON_RATIO = 0.5;\n\nconst useStyles = createStyles((theme) => ({\n fab: {\n position: 'absolute',\n justifyContent: 'center',\n alignItems: 'center',\n ...theme.tokens.shadow.lg,\n },\n}));\n\n/**\n * Fab\n * Circular floating button for the screen's main action.\n * `bottom`/`right` instead of the required `tabBarHeight` of the original\n * version — the consumer decides its own offset (with or without a tab bar).\n *\n * Gestalt, figure/ground: it is the only thing that floats (`lg` shadow) and\n * carries the accent, so it stands apart from the content passing beneath it\n * (Von Restorff: one per screen). Fitts: large and in the thumb's corner,\n * above the safe area so it doesn't compete with the home gesture.\n */\nexport const Fab = memo(\n ({\n onPress,\n iconName = 'plus',\n icon,\n size = DEFAULT_SIZE,\n bottom,\n right,\n safeArea = true,\n color,\n style,\n accessibilityLabel,\n testID,\n }: FabProps) => {\n const styles = useStyles();\n const colors = useColors();\n const theme = useTheme();\n const insets = useInsets();\n const fill = color ?? colors.primary;\n // With the theme color, its `onPrimary`; with a custom one, black or white\n // depending on contrast (a cyan with a white icon was unreadable).\n const glyph = color ? onColor(color) : colors.onPrimary;\n const edge = theme.tokens.spacing.xl;\n\n return (\n <PressableScale\n onPress={onPress}\n style={[\n styles.fab,\n {\n width: size,\n height: size,\n borderRadius: size / 2,\n bottom: (bottom ?? edge) + (safeArea ? insets.bottom : 0),\n right: (right ?? edge) + (safeArea ? insets.right : 0),\n backgroundColor: fill,\n },\n style,\n ]}\n accessibilityRole=\"button\"\n accessibilityLabel={accessibilityLabel}\n testID={testID}\n >\n {icon ?? <Icon name={iconName} size={size * ICON_RATIO} color={glyph} />}\n </PressableScale>\n );\n }\n);\nFab.displayName = 'Fab';\n"
13
13
  }
14
14
  ],
15
15
  "registryDependencies": [
16
16
  "core",
17
- "pressable-scale"
17
+ "pressable-scale",
18
+ "transition"
18
19
  ]
19
20
  }
@@ -9,11 +9,12 @@
9
9
  "files": [
10
10
  {
11
11
  "path": "molecules/FormActions.tsx",
12
- "content": "import React, { memo } from 'react';\nimport { View, type StyleProp, type ViewStyle } from 'react-native';\nimport { Button } from '../atoms/Button';\nimport type { IconName } from '../icons/types';\nimport { useLabels } from '../theme/UIProvider';\n\nexport interface FormActionsProps {\n /** If omitted, only the secondary button is shown. */\n primaryLabel?: string;\n onPrimaryPress?: () => void;\n primaryDisabled?: boolean;\n primaryLoading?: boolean;\n primaryLeftIconName?: IconName;\n secondaryLabel?: string;\n onSecondaryPress: () => void;\n style?: StyleProp<ViewStyle>;\n}\n\n/**\n * FormActions\n * Par de botones: uno primario (guardar/editar) y uno ghost (cerrar/cancelar).\n * Uso típico: al final de un form, o en el footer de un Modal.\n */\nexport const FormActions = memo(\n ({\n primaryLabel,\n onPrimaryPress,\n primaryDisabled = false,\n primaryLoading = false,\n primaryLeftIconName,\n secondaryLabel,\n onSecondaryPress,\n style,\n }: FormActionsProps) => {\n const labels = useLabels();\n return (\n <View style={[{ gap: 12 }, style]}>\n {primaryLabel && onPrimaryPress && (\n <Button\n title={primaryLabel}\n onPress={onPrimaryPress}\n disabled={primaryDisabled}\n loading={primaryLoading}\n leftIconName={primaryLeftIconName}\n />\n )}\n <Button title={secondaryLabel ?? labels.close} onPress={onSecondaryPress} variant=\"ghost\" />\n </View>\n );\n }\n);\nFormActions.displayName = 'FormActions';\n"
12
+ "content": "import React, { memo } from 'react';\nimport { View, type StyleProp, type ViewStyle } from 'react-native';\nimport { Button } from '../atoms/Button';\nimport type { IconName } from '../icons/types';\nimport { useLabels } from '../theme/UIProvider';\nimport { createStyles } from '../theme/createStyles';\n\nexport interface FormActionsProps {\n /** Label of the main action (\"Save\"). The primary button shows only with both this and `onPrimaryPress`. */\n primaryLabel?: string;\n /** Main action. */\n onPrimaryPress?: () => void;\n /** Blocks the main action, e.g. while the form is invalid. @default false */\n primaryDisabled?: boolean;\n /**\n * The main action is running: shows a spinner and ignores presses, so it\n * can't be sent twice. The secondary button stays usable. @default false\n */\n primaryLoading?: boolean;\n /** Icon before the primary label. */\n primaryLeftIconName?: IconName;\n /** Label of the ghost button. @default labels.close */\n secondaryLabel?: string;\n /** Close / cancel action. */\n onSecondaryPress: () => void;\n style?: StyleProp<ViewStyle>;\n /** Root testID; the buttons get `<testID>-primary` and `<testID>-secondary`. */\n testID?: string;\n}\n\nconst useStyles = createStyles((theme) => ({\n root: { gap: theme.tokens.spacing.md },\n}));\n\n/**\n * FormActions\n * Pair of buttons: a primary one (save/edit) and a ghost one (close/cancel).\n * Typical use: at the end of a form, or in a Modal's footer.\n *\n * Gestalt, proximity: both buttons sit close together and apart from the\n * fields, so they read as the form's closing actions. Von Restorff: only the\n * primary is filled; the secondary is ghost, so the main path is obvious and\n * the two never compete (Hick: one clear choice). Fitts: full width, primary\n * first, closest to the content it acts on.\n */\nexport const FormActions = memo(\n ({\n primaryLabel,\n onPrimaryPress,\n primaryDisabled = false,\n primaryLoading = false,\n primaryLeftIconName,\n secondaryLabel,\n onSecondaryPress,\n style,\n testID,\n }: FormActionsProps) => {\n const labels = useLabels();\n const styles = useStyles();\n return (\n <View style={[styles.root, style]} testID={testID}>\n {!!primaryLabel && !!onPrimaryPress && (\n <Button\n title={primaryLabel}\n onPress={onPrimaryPress}\n disabled={primaryDisabled}\n loading={primaryLoading}\n leftIconName={primaryLeftIconName}\n testID={testID && `${testID}-primary`}\n />\n )}\n <Button\n title={secondaryLabel ?? labels.close}\n onPress={onSecondaryPress}\n variant=\"ghost\"\n testID={testID && `${testID}-secondary`}\n />\n </View>\n );\n }\n);\nFormActions.displayName = 'FormActions';\n"
13
13
  }
14
14
  ],
15
15
  "registryDependencies": [
16
16
  "core",
17
- "button"
17
+ "button",
18
+ "transition"
18
19
  ]
19
20
  }
@@ -9,7 +9,7 @@
9
9
  "files": [
10
10
  {
11
11
  "path": "atoms/Gradient.tsx",
12
- "content": "import React, { memo, useMemo } from 'react';\nimport { StyleSheet, View, type StyleProp, type ViewStyle } from 'react-native';\n\nexport type GradientDirection = 'vertical' | 'horizontal' | 'diagonal' | 'diagonal-reverse';\n\nexport interface GradientProps {\n /** Paradas del degradado. Uno solo pinta plano; ninguno no pinta nada. */\n colors: string[];\n /**\n * 'vertical' va de arriba a abajo, 'horizontal' de izquierda a derecha,\n * 'diagonal' de arriba-izquierda a abajo-derecha y 'diagonal-reverse' de\n * arriba-derecha a abajo-izquierda.\n * @default 'vertical'\n */\n direction?: GradientDirection;\n /**\n * Bandas por tramo (un tramo = un par de colores consecutivos). Más bandas\n * = transición más suave y más vistas en el árbol.\n * @default 24\n */\n steps?: number;\n /** Contenido por encima del degradado. */\n children?: React.ReactNode;\n style?: StyleProp<ViewStyle>;\n testID?: string;\n}\n\nconst DEFAULT_STEPS = 24;\n\nconst styles = StyleSheet.create({\n // Recorta el plano rotado de las diagonales y el degradado al borderRadius.\n root: { overflow: 'hidden' },\n segment: { flex: 1 },\n band: { flex: 1 },\n // Plano √2 centrado: rotado 45° cubre las esquinas sin medir el contenedor.\n diagonalPlane: { position: 'absolute', top: '-21%', left: '-21%', width: '142%', height: '142%' },\n});\n\n/**\n * Gradient\n * Degradado lineal sin dependencias: React Native no trae uno en el rango\n * soportado (Expo SDK 54–57) y `experimental_backgroundImage` no está en toda\n * esa ventana, así que se compone con bandas.\n *\n * Cada tramo pinta el color inicial y encima N bandas del final con opacidad\n * de 0 a 1. El compositor mezcla alpha linealmente: el resultado es la\n * interpolación sRGB entre paradas sin parsear el color, y por eso vale\n * cualquier formato que acepte RN (hex, `rgba()`, nombre).\n */\nexport const Gradient = memo(\n ({ colors, direction = 'vertical', steps = DEFAULT_STEPS, children, style, testID }: GradientProps) => {\n const isRow = direction === 'horizontal';\n const isDiagonal = direction === 'diagonal' || direction === 'diagonal-reverse';\n // rotate positivo gira en horario (Y crece hacia abajo): -45° manda el eje\n // del degradado hacia abajo-derecha.\n const rotate = direction === 'diagonal' ? '-45deg' : '45deg';\n const bandCount = Math.max(2, Math.floor(steps));\n\n const painted = useMemo(() => {\n if (colors.length === 0) return null;\n if (colors.length === 1) {\n return <View style={[StyleSheet.absoluteFill, { backgroundColor: colors[0] }]} />;\n }\n\n const segments = colors.slice(0, -1).map((from, index) => {\n const to = colors[index + 1] as string;\n return (\n <View\n key={`${from}-${to}-${index}`}\n style={[styles.segment, { backgroundColor: from, flexDirection: isRow ? 'row' : 'column' }]}\n >\n {Array.from({ length: bandCount }, (_, band) => (\n <View key={band} style={[styles.band, { backgroundColor: to, opacity: band / (bandCount - 1) }]} />\n ))}\n </View>\n );\n });\n\n const stack = (\n <View style={[StyleSheet.absoluteFill, { flexDirection: isRow ? 'row' : 'column' }]}>{segments}</View>\n );\n\n if (!isDiagonal) return stack;\n return <View style={[styles.diagonalPlane, { transform: [{ rotate }] }]}>{segments}</View>;\n }, [colors, isRow, isDiagonal, rotate, bandCount]);\n\n return (\n <View testID={testID} style={[styles.root, style]}>\n {/* pointerEvents va en el pintado, no en la raíz: el degradado no debe\n robarle los toques a lo que el consumidor ponga encima. */}\n <View style={StyleSheet.absoluteFill} pointerEvents=\"none\">\n {painted}\n </View>\n {children}\n </View>\n );\n }\n);\nGradient.displayName = 'Gradient';\n"
12
+ "content": "import React, { memo, useMemo, useState } from 'react';\nimport { StyleSheet, View, type LayoutChangeEvent, type StyleProp, type ViewStyle } from 'react-native';\n\nexport type GradientDirection = 'vertical' | 'horizontal' | 'diagonal' | 'diagonal-reverse';\n\nexport interface GradientProps {\n /** Gradient stops. A single color paints flat; none paints nothing. */\n colors: string[];\n /**\n * 'vertical' runs top to bottom, 'horizontal' left to right, 'diagonal'\n * top-left to bottom-right and 'diagonal-reverse' top-right to bottom-left.\n * @default 'vertical'\n */\n direction?: GradientDirection;\n /**\n * Bands per segment (a segment = a pair of consecutive colors). More bands\n * = a smoother transition and more views in the tree.\n * @default 48\n */\n steps?: number;\n /** Content rendered on top of the gradient. */\n children?: React.ReactNode;\n style?: StyleProp<ViewStyle>;\n testID?: string;\n}\n\nconst DEFAULT_STEPS = 48;\n\nconst styles = StyleSheet.create({\n // Clips the rotated plane of the diagonals and the gradient to the borderRadius.\n root: { overflow: 'hidden' },\n // Centered √2 plane: covers the corners of a SQUARE container without\n // measuring it. Only used until the first onLayout; after that the plane is\n // sized with the real diagonal (see `diagonalBox`).\n diagonalPlane: { position: 'absolute', top: '-21%', left: '-21%', width: '142%', height: '142%' },\n});\n\n/**\n * Opacities of stacked layers: layer k (from 1 to n-1) starts at k/n and\n * runs to the end of the segment; stacked with \"over\", the total opacity in\n * band k is k/(n-1), same as side-by-side bands. The difference: no layer\n * edge sits next to a gap, so the antialiasing of an edge (especially when\n * rotated) blends with the color of the layer below and not with what is\n * behind the gradient — without the thin lines between bands.\n */\nfunction layerOpacities(n: number): number[] {\n const out: number[] = [];\n let prev = 0;\n for (let k = 1; k < n; k += 1) {\n const total = k / (n - 1);\n out.push(prev >= 1 ? 1 : 1 - (1 - total) / (1 - prev));\n prev = total;\n }\n return out;\n}\n\n/** Square with the diagonal as its side, centered: rotated 45° it covers any rectangle. */\nfunction diagonalBox(width: number, height: number): ViewStyle {\n const side = Math.hypot(width, height);\n return { position: 'absolute', width: side, height: side, left: (width - side) / 2, top: (height - side) / 2 };\n}\n\n/**\n * Gradient\n * Dependency-free linear gradient: React Native doesn't ship one in the\n * supported range (Expo SDK 54–57) and `experimental_backgroundImage` isn't\n * available across that whole window, so it is composed from bands.\n *\n * Each segment paints the start color and, on top, layers of the end color\n * that accumulate (see `layerOpacities`). The compositor blends alpha linearly:\n * the result is the sRGB interpolation between stops without parsing the\n * color, which is why any format RN accepts works (hex, `rgba()`, name). The\n * layers run to the end of the plane so no thin lines are left between\n * bands, and the diagonals rotate a square the size of the measured diagonal\n * to cover any rectangle, not just squares.\n *\n * Gestalt, figure and ground: content on top must contrast with ALL the\n * stops, not just the first — white text that reads well over the blue at\n * the start can get lost over the light blue at the end.\n */\nexport const Gradient = memo(\n ({ colors, direction = 'vertical', steps = DEFAULT_STEPS, children, style, testID }: GradientProps) => {\n const isRow = direction === 'horizontal';\n const isDiagonal = direction === 'diagonal' || direction === 'diagonal-reverse';\n // positive rotate turns clockwise (Y grows downward): -45° sends the\n // gradient axis down-right.\n const rotate = direction === 'diagonal' ? '-45deg' : '45deg';\n const bandCount = Math.max(2, Math.floor(steps));\n const [size, setSize] = useState<{ width: number; height: number } | null>(null);\n const onLayout = isDiagonal\n ? (e: LayoutChangeEvent) => {\n const { width, height } = e.nativeEvent.layout;\n setSize((prev) => (prev && prev.width === width && prev.height === height ? prev : { width, height }));\n }\n : undefined;\n\n const painted = useMemo(() => {\n if (colors.length === 0) return null;\n if (colors.length === 1) {\n return <View style={[StyleSheet.absoluteFill, { backgroundColor: colors[0] }]} />;\n }\n\n const segmentCount = colors.length - 1;\n const opacities = layerOpacities(bandCount);\n // From `start` (fraction of the axis) to the end of the plane.\n const from = (start: number): ViewStyle =>\n isRow\n ? { position: 'absolute', top: 0, bottom: 0, right: 0, left: `${start * 100}%` }\n : { position: 'absolute', left: 0, right: 0, bottom: 0, top: `${start * 100}%` };\n\n // Segments are stacked too: each one covers the previous from its start,\n // and since it begins with the color the previous one ended with, the\n // edge between segments doesn't show the background.\n const segments = colors.slice(0, -1).map((start, index) => {\n const to = colors[index + 1] as string;\n const segStart = index / segmentCount;\n const segLength = 1 / segmentCount;\n // The segment runs from segStart to the END of the plane; its layers\n // sit at fractions of that remaining length.\n const rest = 1 - segStart;\n return (\n <View key={`${start}-${to}-${index}`} style={[from(segStart), { backgroundColor: start }]}>\n {opacities.map((opacity, k) => (\n <View\n key={k}\n style={[from((((k + 1) / bandCount) * segLength) / rest), { backgroundColor: to, opacity }]}\n />\n ))}\n </View>\n );\n });\n\n if (!isDiagonal) return <View style={StyleSheet.absoluteFill}>{segments}</View>;\n const plane = size ? diagonalBox(size.width, size.height) : styles.diagonalPlane;\n return <View style={[plane, { transform: [{ rotate }] }]}>{segments}</View>;\n }, [colors, isRow, isDiagonal, rotate, bandCount, size]);\n\n return (\n <View testID={testID} style={[styles.root, style]} onLayout={onLayout}>\n {/* pointerEvents goes on the painted layer, not the root: the gradient\n must not steal touches from whatever the consumer places on top. */}\n <View style={StyleSheet.absoluteFill} pointerEvents=\"none\">\n {painted}\n </View>\n {children}\n </View>\n );\n }\n);\nGradient.displayName = 'Gradient';\n"
13
13
  }
14
14
  ],
15
15
  "registryDependencies": [
@@ -9,11 +9,12 @@
9
9
  "files": [
10
10
  {
11
11
  "path": "atoms/IconButton.tsx",
12
- "content": "import React, { memo } from 'react';\nimport { type GestureResponderEvent, type StyleProp, type ViewStyle } from 'react-native';\nimport { PressableScale } from './PressableScale';\nimport { Icon } from '../icons/Icon';\nimport type { IconName } from '../icons/types';\nimport { createStyles } from '../theme/createStyles';\n\nexport interface IconButtonProps {\n iconName?: IconName;\n icon?: React.ReactNode;\n onPress?: (e: GestureResponderEvent) => void;\n size?: number;\n color?: string;\n disabled?: boolean;\n style?: StyleProp<ViewStyle>;\n accessibilityLabel: string;\n testID?: string;\n}\n\nconst useStyles = createStyles((theme) => ({\n hitArea: {\n padding: theme.tokens.spacing.sm,\n alignItems: 'center',\n justifyContent: 'center',\n },\n}));\n\n/**\n * IconButton\n * Botón de solo ícono con área táctil mínima de 44x44 garantizada por el\n * padding (icon 24 + padding 8*2 = 40; sube a 44 con size>=24 default RN).\n * `accessibilityLabel` es obligatorio: sin texto visible, es la única forma\n * de que un lector de pantalla anuncie qué hace el botón.\n */\nexport const IconButton = memo(\n ({ iconName, icon, onPress, size = 24, color, disabled, style, accessibilityLabel, testID }: IconButtonProps) => {\n const styles = useStyles();\n return (\n <PressableScale\n onPress={onPress}\n disabled={disabled}\n style={[styles.hitArea, { opacity: disabled ? 0.5 : 1 }, style]}\n accessibilityRole=\"button\"\n accessibilityLabel={accessibilityLabel}\n accessibilityState={{ disabled: !!disabled }}\n testID={testID}\n >\n {icon ?? (iconName && <Icon name={iconName} size={size} color={color} />)}\n </PressableScale>\n );\n }\n);\nIconButton.displayName = 'IconButton';\n"
12
+ "content": "import React, { memo } from 'react';\nimport { type GestureResponderEvent, type StyleProp, type ViewStyle } from 'react-native';\nimport { PressableScale } from './PressableScale';\nimport { Icon } from '../icons/Icon';\nimport type { IconName } from '../icons/types';\nimport { createStyles } from '../theme/createStyles';\nimport { useTheme } from '../theme/UIProvider';\n\nexport interface IconButtonProps {\n /** Icon from the library's set. Ignored when `icon` is passed. */\n iconName?: IconName;\n /** Custom node in place of the icon. Takes precedence over `iconName`. */\n icon?: React.ReactNode;\n /** Called on press. Not called while `disabled`. */\n onPress?: (e: GestureResponderEvent) => void;\n /** Icon size in pt. The touch area is padded up to 44pt with `hitSlop`. @default 24 */\n size?: number;\n /** Icon color. Defaults to `colors.text`. */\n color?: string;\n /** Dims the button by half and ignores presses. */\n disabled?: boolean;\n style?: StyleProp<ViewStyle>;\n /** Required: the button has no visible text, so this is all a screen reader announces. */\n accessibilityLabel: string;\n testID?: string;\n}\n\nconst useStyles = createStyles((theme) => ({\n hitArea: {\n padding: theme.tokens.spacing.sm,\n alignItems: 'center',\n justifyContent: 'center',\n },\n}));\n\n/** Minimum touch area recommended by Apple (44pt) and Material (48dp ≈ 44pt). */\nconst MIN_TOUCH = 44;\n\n/**\n * IconButton\n * Icon-only button. `accessibilityLabel` is required: with no visible text,\n * it is the only way for a screen reader to announce what it does.\n *\n * Fitts: the visible area is icon + `spacing.sm` per side (24 + 16 = 40pt\n * with the default size, 32pt with `size={16}`). `hitSlop` makes up the\n * rest up to 44pt without enlarging the layout, so a row of icons keeps its\n * rhythm and each one is still easy to tap.\n *\n * Gestalt, figure and ground: it has no background of its own, the icon is\n * the figure over whatever is behind it; that is why the default color is\n * `colors.text` (guaranteed AA contrast) and `disabled` dims it by half.\n */\nexport const IconButton = memo(\n ({ iconName, icon, onPress, size = 24, color, disabled, style, accessibilityLabel, testID }: IconButtonProps) => {\n const styles = useStyles();\n const theme = useTheme();\n const visible = size + theme.tokens.spacing.sm * 2;\n const extra = Math.max(0, Math.ceil((MIN_TOUCH - visible) / 2));\n const hitSlop = extra > 0 ? { top: extra, bottom: extra, left: extra, right: extra } : undefined;\n return (\n <PressableScale\n onPress={onPress}\n disabled={disabled}\n hitSlop={hitSlop}\n style={[styles.hitArea, { opacity: disabled ? 0.5 : 1 }, style]}\n accessibilityRole=\"button\"\n accessibilityLabel={accessibilityLabel}\n accessibilityState={{ disabled: !!disabled }}\n testID={testID}\n >\n {icon ?? (iconName && <Icon name={iconName} size={size} color={color} />)}\n </PressableScale>\n );\n }\n);\nIconButton.displayName = 'IconButton';\n"
13
13
  }
14
14
  ],
15
15
  "registryDependencies": [
16
16
  "core",
17
- "pressable-scale"
17
+ "pressable-scale",
18
+ "transition"
18
19
  ]
19
20
  }
@@ -10,7 +10,7 @@
10
10
  "files": [
11
11
  {
12
12
  "path": "atoms/Image.tsx",
13
- "content": "import React, { memo, useCallback, useEffect, useState } from 'react';\nimport {\n Image as RNImage,\n Platform,\n StyleSheet,\n View,\n type ImageErrorEventData,\n type ImageResizeMode,\n type ImageSourcePropType,\n type NativeSyntheticEvent,\n type StyleProp,\n type ViewStyle,\n} from 'react-native';\nimport { createStyles } from '../theme/createStyles';\nimport { Spinner } from './Spinner';\n\nexport interface ImageProps {\n source: ImageSourcePropType;\n width?: number;\n height?: number;\n /** @default 'cover' */\n resizeMode?: ImageResizeMode;\n /** Corner radius, same pattern as Avatar/Card. @default 0 */\n radius?: number;\n /** What to show while loading. `false` to show nothing. @default true (small spinner) */\n loading?: React.ReactNode | boolean;\n /** What to show if loading fails. @default a gray placeholder, no content */\n fallback?: React.ReactNode;\n /**\n * 'high' triggers Image.prefetch() on mount, before <Image> starts\n * its own load cycle — meant for the above-the-fold image of a\n * screen (the first one the user sees). No cost if it's already cached.\n * @default 'normal'\n */\n priority?: 'low' | 'normal' | 'high';\n style?: StyleProp<ViewStyle>;\n onLoadEnd?: () => void;\n onError?: (error: NativeSyntheticEvent<ImageErrorEventData>) => void;\n testID?: string;\n}\n\nconst useStyles = createStyles((theme) => ({\n container: { overflow: 'hidden', backgroundColor: theme.colors.skeleton },\n overlay: { alignItems: 'center', justifyContent: 'center' },\n fallback: { flex: 1, backgroundColor: theme.colors.skeleton },\n}));\n\nfunction sourceUri(source: ImageSourcePropType): string | undefined {\n return typeof source === 'object' && source !== null && 'uri' in source ? source.uri : undefined;\n}\n\n/**\n * Image\n * Wrapper cero-dependencias sobre <Image> de RN: agrega estado de carga y\n * error (con los mismos placeholders que el resto de la librería), y expone\n * `priority=\"high\"` para adelantar la descarga vía Image.prefetch(). El\n * cache real (disco/memoria) lo maneja el motor de imágenes nativo de RN —\n * esto no lo reimplementa, solo no lo deja apagado por accidente: en iOS\n * fuerza `cache=\"force-cache\"` (usa el disco aunque el header HTTP no lo\n * pida); en Android el cache de disco de la imagen nativa ya está siempre\n * activo, no hay knob equivalente que tocar.\n */\nexport const Image = memo(\n ({\n source,\n width,\n height,\n resizeMode = 'cover',\n radius = 0,\n loading = true,\n fallback,\n priority = 'normal',\n style,\n onLoadEnd,\n onError,\n testID,\n }: ImageProps) => {\n const styles = useStyles();\n const [status, setStatus] = useState<'loading' | 'loaded' | 'error'>('loading');\n const uri = sourceUri(source);\n\n useEffect(() => {\n // Resetea el estado si cambia la imagen (misma instancia de Image,\n // source distinto: p.ej. avatar de un usuario a otro en la misma fila).\n setStatus('loading');\n }, [uri ?? source]);\n\n useEffect(() => {\n if (priority === 'high' && uri) {\n RNImage.prefetch(uri).catch(() => {});\n }\n }, [priority, uri]);\n\n const handleError = useCallback(\n (e: NativeSyntheticEvent<ImageErrorEventData>) => {\n setStatus('error');\n onError?.(e);\n },\n [onError]\n );\n\n const handleLoadEnd = useCallback(() => {\n setStatus('loaded');\n onLoadEnd?.();\n }, [onLoadEnd]);\n\n return (\n <View style={[styles.container, { width, height, borderRadius: radius }, style]} testID={testID}>\n {status !== 'error' && (\n <RNImage\n source={source}\n resizeMode={resizeMode}\n style={StyleSheet.absoluteFill}\n fadeDuration={Platform.OS === 'android' ? 150 : undefined}\n {...(Platform.OS === 'ios' ? { cache: 'force-cache' as const } : null)}\n onLoadEnd={handleLoadEnd}\n onError={handleError}\n />\n )}\n {status === 'loading' && loading !== false && (\n <View style={[StyleSheet.absoluteFill, styles.overlay]}>\n {loading === true ? <Spinner size=\"small\" fullscreen={false} /> : loading}\n </View>\n )}\n {status === 'error' && (fallback ?? <View style={styles.fallback} />)}\n </View>\n );\n }\n);\nImage.displayName = 'Image';\n\n/**\n * Precarga una URL antes de montar el componente (p.ej. al recibir la\n * respuesta de un listado, antes de navegar al detalle). Re-exporta\n * Image.prefetch de RN — no hay nada zero-dep que agregarle encima.\n */\nexport const prefetchImage = RNImage.prefetch;\n"
13
+ "content": "import React, { memo, useCallback, useEffect, useState } from 'react';\nimport {\n Image as RNImage,\n Platform,\n StyleSheet,\n View,\n type ImageProps as RNImageProps,\n type ImageResizeMode,\n type ImageSourcePropType,\n type StyleProp,\n type ViewStyle,\n} from 'react-native';\nimport { createStyles } from '../theme/createStyles';\nimport { useColors, useTheme } from '../theme/UIProvider';\nimport { Icon } from '../icons/Icon';\nimport { Spinner } from './Spinner';\n\n/**\n * The event RN's Image passes to `onError`. Taken from its own prop type:\n * react-native 0.88 (Expo SDK 58) renamed the exported name\n * (`ImageErrorEventData` -> `ImageErrorEvent`).\n */\ntype ImageErrorEvent = Parameters<NonNullable<RNImageProps['onError']>>[0];\n\nexport interface ImageProps {\n /** Image to load (same as React Native's `source`). */\n source: ImageSourcePropType;\n /** Width of the image slot, in pt. */\n width?: number;\n /** Height of the image slot, in pt. */\n height?: number;\n /** How the image fits its slot (same as React Native). @default 'cover' */\n resizeMode?: ImageResizeMode;\n /** Corner radius, same pattern as Avatar/Card. @default 0 */\n radius?: number;\n /** What to show while loading. `false` to show nothing. @default true (small spinner) */\n loading?: React.ReactNode | boolean;\n /** What to show if loading fails. @default placeholder with a warning icon */\n fallback?: React.ReactNode;\n /**\n * 'high' triggers Image.prefetch() on mount, before <Image> starts\n * its own load cycle — meant for the above-the-fold image of a\n * screen (the first one the user sees). No cost if it's already cached.\n * @default 'normal'\n */\n priority?: 'low' | 'normal' | 'high';\n style?: StyleProp<ViewStyle>;\n /** Called when loading finishes, successfully or not (same as RN). */\n onLoadEnd?: () => void;\n /** Describes the image for screen readers. Without it the image is decorative. */\n accessibilityLabel?: string;\n /** Called when loading fails; the `fallback` is shown. */\n onError?: (error: ImageErrorEvent) => void;\n testID?: string;\n}\n\nconst useStyles = createStyles((theme) => ({\n container: { overflow: 'hidden' },\n // Only while loading or on failure: once the image has loaded, a PNG with\n // transparency (a logo) let this gray show through.\n placeholder: { backgroundColor: theme.colors.skeleton },\n overlay: { alignItems: 'center', justifyContent: 'center' },\n fallback: { flex: 1, backgroundColor: theme.colors.skeleton, alignItems: 'center', justifyContent: 'center' },\n}));\n\nfunction sourceUri(source: ImageSourcePropType): string | undefined {\n return typeof source === 'object' && source !== null && 'uri' in source ? source.uri : undefined;\n}\n\n/**\n * Image\n * Zero-dependency wrapper over RN's <Image>: adds loading and error state\n * (with the same placeholders as the rest of the library), and exposes\n * `priority=\"high\"` to start the download early via Image.prefetch(). The\n * actual cache (disk/memory) is handled by RN's native image engine — this\n * doesn't reimplement it, it just keeps it from being accidentally turned off:\n * on iOS it forces `cache=\"force-cache\"` (uses the disk even when the HTTP\n * header doesn't ask for it); on Android the native image's disk cache is\n * always on, there is no equivalent knob to touch.\n *\n * Gestalt, closure: the container takes its final size from the first frame\n * (`skeleton` background), so the layout doesn't jump when the image arrives\n * and the eye completes the shape it will fill. If loading fails, the same\n * slot shows a warning icon: the state is visible, not a silent gray.\n *\n * Success is detected with `onLoad`, not `onLoadEnd`: RN also calls\n * `onLoadEnd` on failure, and using it for \"loaded\" overwrote the error.\n */\nexport const Image = memo(\n ({\n source,\n width,\n height,\n resizeMode = 'cover',\n radius = 0,\n loading = true,\n fallback,\n priority = 'normal',\n style,\n onLoadEnd,\n onError,\n testID,\n accessibilityLabel,\n }: ImageProps) => {\n const styles = useStyles();\n const colors = useColors();\n const theme = useTheme();\n const [status, setStatus] = useState<'loading' | 'loaded' | 'error'>('loading');\n const uri = sourceUri(source);\n\n useEffect(() => {\n // Resets the state if the image changes (same Image instance, different\n // source: e.g. one user's avatar to another's in the same row).\n setStatus('loading');\n }, [uri ?? source]);\n\n useEffect(() => {\n if (priority === 'high' && uri) {\n RNImage.prefetch(uri).catch(() => {});\n }\n }, [priority, uri]);\n\n const handleError = useCallback(\n (e: ImageErrorEvent) => {\n setStatus('error');\n onError?.(e);\n },\n [onError]\n );\n\n const handleLoad = useCallback(() => {\n setStatus('loaded');\n }, []);\n\n return (\n <View\n style={[styles.container, status !== 'loaded' && styles.placeholder, { width, height, borderRadius: radius }, style]}\n testID={testID}\n accessible={!!accessibilityLabel}\n accessibilityRole={accessibilityLabel ? 'image' : undefined}\n accessibilityLabel={accessibilityLabel}\n >\n {status !== 'error' && (\n <RNImage\n source={source}\n resizeMode={resizeMode}\n style={StyleSheet.absoluteFill}\n fadeDuration={Platform.OS === 'android' ? theme.tokens.duration.fast : undefined}\n {...(Platform.OS === 'ios' ? { cache: 'force-cache' as const } : null)}\n onLoad={handleLoad}\n onLoadEnd={onLoadEnd}\n onError={handleError}\n />\n )}\n {status === 'loading' && loading !== false && (\n <View style={[StyleSheet.absoluteFill, styles.overlay]}>\n {loading === true ? <Spinner size=\"small\" fullscreen={false} /> : loading}\n </View>\n )}\n {status === 'error' && (\n // Custom fallback or the default: always centered in the slot.\n <View style={styles.fallback}>\n {fallback ?? (\n <Icon\n name=\"warning\"\n size={Math.max(16, Math.min(32, Math.round(Math.min(width ?? 64, height ?? 64) * 0.3)))}\n color={colors.textLight}\n />\n )}\n </View>\n )}\n </View>\n );\n }\n);\nImage.displayName = 'Image';\n\n/**\n * Preloads a URL before mounting the component (e.g. on receiving a list\n * response, before navigating to the detail). Re-exports RN's\n * Image.prefetch — there is nothing zero-dep to add on top.\n */\nexport const prefetchImage = RNImage.prefetch;\n"
14
14
  }
15
15
  ],
16
16
  "registryDependencies": [