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
@@ -8,62 +8,116 @@ import type { IconName } from '../icons/types';
8
8
  export interface AvatarHeaderProps {
9
9
  /** Icon to show inside the circle. If omitted, `initials` is used instead. */
10
10
  iconName?: IconName;
11
+ /** Icon color. Keep AA contrast against `primarySoft`, the circle's fill. @default colors.primary */
11
12
  iconColor?: string;
12
- /** Short text (e.g. 2 letters) shown when `iconName` isn't provided. */
13
+ /** Short text (e.g. 2 letters) shown when `iconName` isn't provided. Without either, a `user` icon. */
13
14
  initials?: string;
15
+ /** Main heading of the screen or modal; announced as a header. */
14
16
  title: string;
17
+ /** Supporting line under the title. A string gets the muted style; a node renders as is. */
15
18
  subtitle?: React.ReactNode;
19
+ /**
20
+ * Third and last line, the least important detail ("Joined March 2023").
21
+ * Three lines is the limit: beyond that the header stops being scannable.
22
+ */
23
+ caption?: string;
16
24
  style?: StyleProp<ViewStyle>;
25
+ testID?: string;
17
26
  }
18
27
 
28
+ const CIRCLE = 80;
29
+ const ICON_SIZE = 40;
30
+
19
31
  const useStyles = createStyles((theme) => ({
20
- container: { alignItems: 'center', marginBottom: theme.tokens.spacing.xl },
32
+ // No outer margin: the space below belongs to the parent's layout.
33
+ container: { alignItems: 'center' },
21
34
  avatar: {
22
- width: 80,
23
- height: 80,
24
- borderRadius: 40,
35
+ width: CIRCLE,
36
+ height: CIRCLE,
37
+ borderRadius: CIRCLE / 2,
25
38
  backgroundColor: theme.colors.primarySoft,
26
39
  justifyContent: 'center',
27
40
  alignItems: 'center',
28
41
  marginBottom: theme.tokens.spacing.md,
29
42
  },
30
- initials: { fontSize: theme.tokens.fontSize.xxl, fontWeight: theme.tokens.fontWeight.bold, color: theme.colors.primaryText },
31
- title: { fontSize: theme.tokens.fontSize.xl, fontWeight: theme.tokens.fontWeight.bold, textAlign: 'center', color: theme.colors.text, marginBottom: theme.tokens.spacing.xs },
32
- subtitle: { fontSize: theme.tokens.fontSize.md, color: theme.colors.textLight },
43
+ initials: {
44
+ fontSize: theme.tokens.fontSize.xxl,
45
+ fontWeight: theme.tokens.fontWeight.bold,
46
+ color: theme.colors.primaryText,
47
+ },
48
+ title: {
49
+ fontSize: theme.tokens.fontSize.xl,
50
+ lineHeight: theme.tokens.lineHeight.lg,
51
+ fontWeight: theme.tokens.fontWeight.bold,
52
+ textAlign: 'center',
53
+ color: theme.colors.text,
54
+ marginBottom: theme.tokens.spacing.xs,
55
+ },
56
+ subtitle: {
57
+ fontSize: theme.tokens.fontSize.md,
58
+ lineHeight: theme.tokens.lineHeight.md,
59
+ color: theme.colors.textLight,
60
+ textAlign: 'center',
61
+ },
62
+ caption: {
63
+ fontSize: theme.tokens.fontSize.sm,
64
+ lineHeight: theme.tokens.lineHeight.sm,
65
+ color: theme.colors.textLight,
66
+ textAlign: 'center',
67
+ marginTop: theme.tokens.spacing.xxs,
68
+ },
33
69
  }));
34
70
 
35
71
  /**
36
72
  * AvatarHeader
37
- * Círculo 80x80 con ícono o iniciales + título/subtítulo centrado. Header
38
- * típico de modales de detalle (cliente, producto, etc).
73
+ * 80x80 circle with an icon or initials + centered title/subtitle. Typical
74
+ * header of detail modals (client, product, etc).
75
+ *
76
+ * Gestalt, symmetry and focal point: everything sits on one centered axis and
77
+ * the circle is the single large shape, so the eye lands there first and then
78
+ * drops to the title. Proximity: title and subtitle are closer to each other
79
+ * than to the circle, reading as one name + its detail. Hierarchy by size:
80
+ * title, subtitle, caption — at most three lines of text. Long text wraps
81
+ * centered, so the axis holds. No outer margin: the parent sets the spacing.
82
+ *
83
+ * Accessibility: the title is a header; the circle is decorative (the
84
+ * initials would only repeat the title).
39
85
  */
40
- export const AvatarHeader = memo(({ iconName, iconColor, initials, title, subtitle, style }: AvatarHeaderProps) => {
41
- const colors = useColors();
42
- const styles = useStyles();
43
- const allowFontScaling = useAllowFontScaling();
86
+ export const AvatarHeader = memo(
87
+ ({ iconName, iconColor, initials, title, subtitle, caption, style, testID }: AvatarHeaderProps) => {
88
+ const colors = useColors();
89
+ const styles = useStyles();
90
+ const allowFontScaling = useAllowFontScaling();
91
+ const fallbackIcon: IconName | undefined = iconName ?? (initials ? undefined : 'user');
44
92
 
45
- return (
46
- <View style={[styles.container, style]}>
47
- <View style={styles.avatar}>
48
- {iconName ? (
49
- <Icon name={iconName} size={40} color={iconColor || colors.primary} />
93
+ return (
94
+ <View style={[styles.container, style]} testID={testID}>
95
+ <View style={styles.avatar} accessibilityElementsHidden importantForAccessibility="no-hide-descendants">
96
+ {fallbackIcon ? (
97
+ <Icon name={fallbackIcon} size={ICON_SIZE} color={iconColor ?? colors.primary} />
98
+ ) : (
99
+ <Text allowFontScaling={allowFontScaling} style={styles.initials}>
100
+ {initials}
101
+ </Text>
102
+ )}
103
+ </View>
104
+ <Text allowFontScaling={allowFontScaling} style={styles.title} accessibilityRole="header">
105
+ {title}
106
+ </Text>
107
+ {typeof subtitle === 'string' ? (
108
+ <Text allowFontScaling={allowFontScaling} style={styles.subtitle}>
109
+ {subtitle}
110
+ </Text>
50
111
  ) : (
51
- <Text allowFontScaling={allowFontScaling} style={styles.initials}>
52
- {initials}
112
+ subtitle
113
+ )}
114
+ {!!caption && (
115
+ <Text allowFontScaling={allowFontScaling} style={styles.caption}>
116
+ {caption}
53
117
  </Text>
54
118
  )}
55
119
  </View>
56
- <Text allowFontScaling={allowFontScaling} style={styles.title}>
57
- {title}
58
- </Text>
59
- {typeof subtitle === 'string' ? (
60
- <Text allowFontScaling={allowFontScaling} style={styles.subtitle}>
61
- {subtitle}
62
- </Text>
63
- ) : (
64
- subtitle
65
- )}
66
- </View>
67
- );
68
- });
120
+ );
121
+ }
122
+ );
69
123
  AvatarHeader.displayName = 'AvatarHeader';
@@ -3,23 +3,45 @@ import { View, type StyleProp, type ViewStyle } from 'react-native';
3
3
  import { Button } from '../atoms/Button';
4
4
  import type { IconName } from '../icons/types';
5
5
  import { useLabels } from '../theme/UIProvider';
6
+ import { createStyles } from '../theme/createStyles';
6
7
 
7
8
  export interface FormActionsProps {
8
- /** If omitted, only the secondary button is shown. */
9
+ /** Label of the main action ("Save"). The primary button shows only with both this and `onPrimaryPress`. */
9
10
  primaryLabel?: string;
11
+ /** Main action. */
10
12
  onPrimaryPress?: () => void;
13
+ /** Blocks the main action, e.g. while the form is invalid. @default false */
11
14
  primaryDisabled?: boolean;
15
+ /**
16
+ * The main action is running: shows a spinner and ignores presses, so it
17
+ * can't be sent twice. The secondary button stays usable. @default false
18
+ */
12
19
  primaryLoading?: boolean;
20
+ /** Icon before the primary label. */
13
21
  primaryLeftIconName?: IconName;
22
+ /** Label of the ghost button. @default labels.close */
14
23
  secondaryLabel?: string;
24
+ /** Close / cancel action. */
15
25
  onSecondaryPress: () => void;
16
26
  style?: StyleProp<ViewStyle>;
27
+ /** Root testID; the buttons get `<testID>-primary` and `<testID>-secondary`. */
28
+ testID?: string;
17
29
  }
18
30
 
31
+ const useStyles = createStyles((theme) => ({
32
+ root: { gap: theme.tokens.spacing.md },
33
+ }));
34
+
19
35
  /**
20
36
  * FormActions
21
- * Par de botones: uno primario (guardar/editar) y uno ghost (cerrar/cancelar).
22
- * Uso típico: al final de un form, o en el footer de un Modal.
37
+ * Pair of buttons: a primary one (save/edit) and a ghost one (close/cancel).
38
+ * Typical use: at the end of a form, or in a Modal's footer.
39
+ *
40
+ * Gestalt, proximity: both buttons sit close together and apart from the
41
+ * fields, so they read as the form's closing actions. Von Restorff: only the
42
+ * primary is filled; the secondary is ghost, so the main path is obvious and
43
+ * the two never compete (Hick: one clear choice). Fitts: full width, primary
44
+ * first, closest to the content it acts on.
23
45
  */
24
46
  export const FormActions = memo(
25
47
  ({
@@ -31,20 +53,28 @@ export const FormActions = memo(
31
53
  secondaryLabel,
32
54
  onSecondaryPress,
33
55
  style,
56
+ testID,
34
57
  }: FormActionsProps) => {
35
58
  const labels = useLabels();
59
+ const styles = useStyles();
36
60
  return (
37
- <View style={[{ gap: 12 }, style]}>
38
- {primaryLabel && onPrimaryPress && (
61
+ <View style={[styles.root, style]} testID={testID}>
62
+ {!!primaryLabel && !!onPrimaryPress && (
39
63
  <Button
40
64
  title={primaryLabel}
41
65
  onPress={onPrimaryPress}
42
66
  disabled={primaryDisabled}
43
67
  loading={primaryLoading}
44
68
  leftIconName={primaryLeftIconName}
69
+ testID={testID && `${testID}-primary`}
45
70
  />
46
71
  )}
47
- <Button title={secondaryLabel ?? labels.close} onPress={onSecondaryPress} variant="ghost" />
72
+ <Button
73
+ title={secondaryLabel ?? labels.close}
74
+ onPress={onSecondaryPress}
75
+ variant="ghost"
76
+ testID={testID && `${testID}-secondary`}
77
+ />
48
78
  </View>
49
79
  );
50
80
  }
@@ -7,43 +7,70 @@ import type { IconName } from '../icons/types';
7
7
  import { Avatar } from '../atoms/Avatar';
8
8
 
9
9
  export interface InfoRowProps {
10
+ /** Icon that identifies the kind of data at a glance (decorative for screen readers). */
10
11
  icon: IconName;
12
+ /** What the value is ("Email", "Address"). */
11
13
  label: string;
14
+ /** The data itself. Empty, `null` or `undefined` shows `placeholder`. */
12
15
  value: string | null | undefined;
16
+ /** Text shown, in italics, when there is no value ("No phone number"). */
13
17
  placeholder: string;
14
18
  style?: StyleProp<ViewStyle>;
19
+ testID?: string;
15
20
  }
16
21
 
22
+ const ICON_BADGE = 40;
23
+ const ICON_SIZE = 20;
24
+
17
25
  const useStyles = createStyles((theme) => ({
18
- row: { flexDirection: 'row', alignItems: 'center' },
19
- icon: { marginRight: theme.tokens.spacing.md },
26
+ row: { flexDirection: 'row', alignItems: 'center', gap: theme.tokens.spacing.md },
20
27
  textContainer: { flex: 1 },
21
- label: { fontSize: theme.tokens.fontSize.sm, color: theme.colors.textLight, marginBottom: 2 },
22
- value: { fontSize: theme.tokens.fontSize.md + 1, color: theme.colors.text, fontWeight: theme.tokens.fontWeight.medium },
23
- placeholder: { color: theme.colors.textLight, fontStyle: 'italic' },
28
+ label: {
29
+ fontSize: theme.tokens.fontSize.sm,
30
+ lineHeight: theme.tokens.lineHeight.sm,
31
+ color: theme.colors.textLight,
32
+ marginBottom: theme.tokens.spacing.xxs,
33
+ },
34
+ value: {
35
+ fontSize: theme.tokens.fontSize.md,
36
+ lineHeight: theme.tokens.lineHeight.md,
37
+ color: theme.colors.text,
38
+ fontWeight: theme.tokens.fontWeight.medium,
39
+ },
40
+ placeholder: { color: theme.colors.textLight, fontStyle: 'italic', fontWeight: theme.tokens.fontWeight.regular },
24
41
  }));
25
42
 
26
43
  /**
27
44
  * InfoRow
28
- * Ícono circular a la izquierda + label/valor apilado a la derecha. Muestra
29
- * `placeholder` en itálica si no hay valor. Pensado para modales de detalle.
45
+ * Circular icon on the left + stacked label/value on the right. Shows
46
+ * `placeholder` in italics when there is no value. Meant for detail modals.
47
+ *
48
+ * Gestalt, proximity: label and value sit 2pt apart and the icon a token
49
+ * further, so each row reads as one fact; stack rows with a larger gap.
50
+ * Similarity: every label shares the small muted style and every value the
51
+ * strong one, so a list of rows scans by column. A missing value changes
52
+ * style (muted italics) so it can't be mistaken for data.
53
+ *
54
+ * Accessibility: the row is a single element ("Email, jane@example.com");
55
+ * the icon is decorative.
30
56
  */
31
- export const InfoRow = memo(({ icon, label, value, placeholder, style }: InfoRowProps) => {
57
+ export const InfoRow = memo(({ icon, label, value, placeholder, style, testID }: InfoRowProps) => {
32
58
  const colors = useColors();
33
59
  const styles = useStyles();
34
60
  const allowFontScaling = useAllowFontScaling();
61
+ const shown = value || placeholder;
35
62
 
36
63
  return (
37
- <View style={[styles.row, style]}>
38
- <Avatar size={40} backgroundColor={colors.inputBackground} style={styles.icon}>
39
- <Icon name={icon} size={20} color={colors.textLight} />
64
+ <View style={[styles.row, style]} accessible accessibilityLabel={`${label}, ${shown}`} testID={testID}>
65
+ <Avatar size={ICON_BADGE} backgroundColor={colors.inputBackground}>
66
+ <Icon name={icon} size={ICON_SIZE} color={colors.textLight} />
40
67
  </Avatar>
41
68
  <View style={styles.textContainer}>
42
69
  <Text allowFontScaling={allowFontScaling} style={styles.label}>
43
70
  {label}
44
71
  </Text>
45
72
  <Text allowFontScaling={allowFontScaling} style={[styles.value, !value && styles.placeholder]}>
46
- {value || placeholder}
73
+ {shown}
47
74
  </Text>
48
75
  </View>
49
76
  </View>
@@ -7,12 +7,21 @@ import { PressableScale } from '../atoms/PressableScale';
7
7
  import type { IconName } from '../icons/types';
8
8
 
9
9
  export interface OptionCardProps {
10
+ /** Name of the option. It is also the start of its accessible name. */
10
11
  label: string;
11
- /** Secondary line: the detail that decides the choice ("arrives in 3 days"). */
12
+ /**
13
+ * Secondary line: the detail that decides the choice ("arrives in 3 days").
14
+ * Screen readers read it right after the label, not as a hint, because
15
+ * hints can be turned off and this detail is part of the choice.
16
+ */
12
17
  description?: string;
18
+ /** Icon that helps recognize the option. Avoid `check`: it reads as "selected". */
13
19
  iconName?: IconName;
20
+ /** Whether this is the chosen option of its group. */
14
21
  isSelected: boolean;
22
+ /** Called on press; the group decides which option is selected. */
15
23
  onPress: () => void;
24
+ /** Dims the card and ignores presses. @default false */
16
25
  disabled?: boolean;
17
26
  /** 'vertical': icon on top, text centered below. 'horizontal': in a row. @default 'horizontal' */
18
27
  layout?: 'vertical' | 'horizontal';
@@ -20,43 +29,70 @@ export interface OptionCardProps {
20
29
  testID?: string;
21
30
  }
22
31
 
32
+ const ICON_SIZE = 24;
33
+ /** Same height for every vertical card in a row, even with a short label. */
34
+ const VERTICAL_MIN_HEIGHT = 100;
35
+ const DISABLED_OPACITY = 0.5;
36
+ /** The selected border is thicker; its padding gives back the difference so content doesn't shift. */
37
+ const BORDER = 1;
38
+ const BORDER_SELECTED = 1.5;
39
+
23
40
  const useStyles = createStyles((theme) => ({
24
41
  card: {
25
- // Las tarjetas se reparten la fila que las contenga, sean dos o cinco.
42
+ // The cards share whatever row contains them, whether two or five.
26
43
  flex: 1,
27
44
  alignItems: 'center',
28
45
  justifyContent: 'center',
29
- backgroundColor: theme.colors.surface,
46
+ // Raised: in dark it stays a visible tile on a surface (Card does the same).
47
+ backgroundColor: theme.colors.surfaceRaised,
30
48
  borderRadius: theme.tokens.radius.md,
31
- borderWidth: 1,
49
+ borderWidth: BORDER,
32
50
  borderColor: theme.colors.border,
51
+ padding: theme.tokens.spacing.md,
33
52
  ...theme.tokens.shadow.sm,
34
53
  },
35
- cardHorizontal: { flexDirection: 'row', padding: theme.tokens.spacing.md - 2, gap: theme.tokens.spacing.sm },
36
- cardVertical: { padding: theme.tokens.spacing.md - 2, gap: 6, minHeight: 100 },
54
+ cardHorizontal: { flexDirection: 'row', gap: theme.tokens.spacing.sm },
55
+ cardVertical: { gap: theme.tokens.spacing.sm, minHeight: VERTICAL_MIN_HEIGHT },
37
56
  cardSelected: {
38
57
  borderColor: theme.colors.primary,
39
58
  backgroundColor: theme.colors.primarySoft,
40
- borderWidth: 1.5,
59
+ borderWidth: BORDER_SELECTED,
60
+ padding: theme.tokens.spacing.md - (BORDER_SELECTED - BORDER),
41
61
  elevation: 0,
42
62
  shadowOpacity: 0,
43
63
  },
44
- cardDisabled: { opacity: 0.5 },
64
+ cardDisabled: { opacity: DISABLED_OPACITY },
45
65
  text: { flexShrink: 1 },
46
66
  textVertical: { alignItems: 'center' },
47
- label: { color: theme.colors.textLight, fontWeight: theme.tokens.fontWeight.medium },
48
- labelVertical: { fontSize: theme.tokens.fontSize.xs, textAlign: 'center' },
67
+ label: {
68
+ color: theme.colors.text,
69
+ fontSize: theme.tokens.fontSize.md,
70
+ lineHeight: theme.tokens.lineHeight.md,
71
+ fontWeight: theme.tokens.fontWeight.medium,
72
+ },
73
+ labelVertical: { textAlign: 'center' },
49
74
  labelSelected: { color: theme.colors.primaryText },
50
- description: { fontSize: theme.tokens.fontSize.xs, color: theme.colors.textLight, marginTop: 2 },
75
+ description: {
76
+ fontSize: theme.tokens.fontSize.sm,
77
+ lineHeight: theme.tokens.lineHeight.sm,
78
+ color: theme.colors.textLight,
79
+ marginTop: theme.tokens.spacing.xxs,
80
+ },
51
81
  descriptionVertical: { textAlign: 'center' },
52
82
  }));
53
83
 
54
84
  /**
55
85
  * OptionCard
56
- * Opción excluyente con ícono y label que resalta cuando `isSelected`. Es un
57
- * radio con cara de tarjeta, así que se anuncia como tal: envolver el grupo en
58
- * una View con `accessibilityRole="radiogroup"` para que el lector de pantalla
59
- * lea "1 de 3".
86
+ * Mutually exclusive option with an icon and label that highlights when
87
+ * `isSelected`. It is a radio with a card's face, so it announces itself as
88
+ * one: wrap the group in a View with `accessibilityRole="radiogroup"` so the
89
+ * screen reader reads "1 of 3".
90
+ *
91
+ * Gestalt, similarity: every card in a group shares shape, size and type, so
92
+ * they read as alternatives of one question. Von Restorff / figure-ground: the
93
+ * selected one is the only filled card with an accent border and no shadow —
94
+ * it stands out without the others losing legibility (their labels keep the
95
+ * `text` color). The thicker selected border doesn't move the content.
60
96
  */
61
97
  export const OptionCard = memo(
62
98
  ({
@@ -75,8 +111,8 @@ export const OptionCard = memo(
75
111
  const allowFontScaling = useAllowFontScaling();
76
112
  const isVertical = layout === 'vertical';
77
113
 
78
- // Feedback por transform y no por opacity: con elevation, animar la opacity
79
- // dispara un bug de Android que redibuja la sombra como un borde oscuro.
114
+ // Feedback through transform, not opacity: with elevation, animating opacity
115
+ // triggers an Android bug that redraws the shadow as a dark border.
80
116
  return (
81
117
  <PressableScale
82
118
  style={[
@@ -90,11 +126,10 @@ export const OptionCard = memo(
90
126
  disabled={disabled}
91
127
  accessibilityRole="radio"
92
128
  accessibilityState={{ selected: isSelected, checked: isSelected, disabled }}
93
- accessibilityLabel={label}
94
- accessibilityHint={description}
129
+ accessibilityLabel={description ? `${label}, ${description}` : label}
95
130
  testID={testID}
96
131
  >
97
- {!!iconName && <Icon name={iconName} size={24} color={isSelected ? colors.primary : colors.textLight} />}
132
+ {!!iconName && <Icon name={iconName} size={ICON_SIZE} color={isSelected ? colors.primary : colors.textLight} />}
98
133
 
99
134
  <View style={[styles.text, isVertical && styles.textVertical]}>
100
135
  <Text
@@ -1,46 +1,103 @@
1
- import React, { memo } from 'react';
2
- import { Text, TouchableOpacity, View, type StyleProp, type ViewStyle } from 'react-native';
1
+ import React, { memo, useEffect, useRef, useState } from 'react';
2
+ import {
3
+ Animated,
4
+ Easing,
5
+ I18nManager,
6
+ Text,
7
+ TouchableOpacity,
8
+ View,
9
+ type LayoutChangeEvent,
10
+ type StyleProp,
11
+ type ViewStyle,
12
+ } from 'react-native';
3
13
  import { createStyles } from '../theme/createStyles';
4
- import { useAllowFontScaling } from '../theme/UIProvider';
14
+ import { useAllowFontScaling, useTheme } from '../theme/UIProvider';
15
+ import { useReduceMotion } from '../theme/useReduceMotion';
5
16
 
6
17
  export interface SegmentedControlOption<T extends string = string> {
7
18
  value: T;
19
+ /** Visible text. Kept to one line (ellipsized); screen readers get it whole. */
8
20
  label: string;
21
+ /** Disables only this segment. */
9
22
  disabled?: boolean;
10
23
  }
11
24
 
12
25
  export interface SegmentedControlProps<T extends string = string> {
26
+ /** The choices, all visible at once. Two to four read best (Hick); more belong in a Select. */
13
27
  options: SegmentedControlOption<T>[];
28
+ /** The selected option's value. */
14
29
  value: T;
30
+ /** Called with the new value; not called when pressing the segment already selected. */
15
31
  onChange: (value: T) => void;
32
+ /** Disables the whole control. @default false */
16
33
  disabled?: boolean;
17
34
  style?: StyleProp<ViewStyle>;
18
35
  testID?: string;
19
36
  }
20
37
 
38
+ /** Inset between the track and the segments. */
39
+ const TRACK_PADDING = 3;
40
+ /** Minimum touch target (Fitts): the segment plus the track inset reach it. */
41
+ const MIN_TOUCH = 44;
42
+ const DISABLED_OPACITY = 0.5;
43
+ const HIT_SLOP = { top: TRACK_PADDING, bottom: TRACK_PADDING };
44
+
21
45
  const useStyles = createStyles((theme) => ({
22
46
  track: {
23
47
  flexDirection: 'row',
24
48
  backgroundColor: theme.colors.inputBackground,
25
49
  borderRadius: theme.tokens.radius.md,
26
- padding: 3,
50
+ padding: TRACK_PADDING,
27
51
  },
28
52
  segment: {
29
53
  flex: 1,
54
+ minHeight: MIN_TOUCH - TRACK_PADDING * 2,
30
55
  paddingVertical: theme.tokens.spacing.sm,
56
+ paddingHorizontal: theme.tokens.spacing.xs,
31
57
  borderRadius: theme.tokens.radius.sm,
32
58
  alignItems: 'center',
59
+ justifyContent: 'center',
60
+ },
61
+ // Light: a white segment with a shadow over the grey track. Dark: shadows
62
+ // don't show and `surface` is darker than the track (the segment looked
63
+ // sunken), so the selected one takes the soft accent fill instead.
64
+ // One shared indicator slides under the segments; until the track is
65
+ // measured, the selected segment paints this same fill itself.
66
+ indicator: {
67
+ position: 'absolute',
68
+ top: TRACK_PADDING,
69
+ bottom: TRACK_PADDING,
70
+ start: TRACK_PADDING,
71
+ borderRadius: theme.tokens.radius.sm,
72
+ },
73
+ segmentActive:
74
+ theme.scheme === 'dark'
75
+ ? { backgroundColor: theme.colors.primarySoft }
76
+ : { backgroundColor: theme.colors.surface, ...theme.tokens.shadow.sm },
77
+ segmentDisabled: { opacity: DISABLED_OPACITY },
78
+ label: {
79
+ fontSize: theme.tokens.fontSize.md,
80
+ lineHeight: theme.tokens.lineHeight.md,
81
+ fontWeight: theme.tokens.fontWeight.medium,
82
+ color: theme.colors.textLight,
33
83
  },
34
- segmentActive: { backgroundColor: theme.colors.surface, ...theme.tokens.shadow.sm },
35
- segmentDisabled: { opacity: 0.5 },
36
- label: { fontSize: theme.tokens.fontSize.sm, fontWeight: theme.tokens.fontWeight.medium, color: theme.colors.textLight },
37
84
  labelActive: { color: theme.colors.primaryText, fontWeight: theme.tokens.fontWeight.semibold },
38
85
  }));
39
86
 
40
87
  /**
41
88
  * SegmentedControl
42
- * Elige una opción entre pocas, todas visibles. No conoce ningún estado
43
- * global: recibe `value` y avisa por `onChange`.
89
+ * Picks one option among a few, all visible. It knows no global state:
90
+ * it receives `value` and reports through `onChange`.
91
+ *
92
+ * Gestalt, common region: one track holds every segment, so they read as a
93
+ * single question. Similarity: equal widths and one line each, so no option
94
+ * looks more important. Figure-ground: the selected segment is raised (surface
95
+ * + shadow) above the sunken track; in dark, where shadows vanish, it takes
96
+ * the soft accent fill. Fitts: every segment reaches 44pt.
97
+ *
98
+ * Continuity: the selection is one indicator that slides from the old segment
99
+ * to the new one, so the eye follows the change instead of seeing one fill
100
+ * vanish and another appear. With Reduce Motion it jumps.
44
101
  */
45
102
  function SegmentedControlInner<T extends string>({
46
103
  options,
@@ -51,25 +108,88 @@ function SegmentedControlInner<T extends string>({
51
108
  testID,
52
109
  }: SegmentedControlProps<T>) {
53
110
  const styles = useStyles();
111
+ const theme = useTheme();
54
112
  const allowFontScaling = useAllowFontScaling();
113
+ const reduceMotion = useReduceMotion();
114
+ const [trackWidth, setTrackWidth] = useState(0);
115
+ const offset = useRef(new Animated.Value(0)).current;
116
+ const placed = useRef(false);
117
+
118
+ const index = options.findIndex((option) => option.value === value);
119
+ const segmentWidth = trackWidth > 0 && options.length > 0 ? (trackWidth - TRACK_PADDING * 2) / options.length : 0;
120
+ const showIndicator = segmentWidth > 0 && index >= 0;
121
+ // transform isn't mirrored in RTL: the indicator starts at the right edge
122
+ // (`start`), so it moves left.
123
+ const target = index * segmentWidth * (I18nManager.isRTL ? -1 : 1);
124
+
125
+ useEffect(() => {
126
+ if (!showIndicator) return;
127
+ // First placement (and Reduce Motion) jumps; only a change of value slides.
128
+ if (!placed.current || reduceMotion) {
129
+ placed.current = true;
130
+ offset.setValue(target);
131
+ return;
132
+ }
133
+ const animation = Animated.timing(offset, {
134
+ toValue: target,
135
+ duration: theme.tokens.duration.base,
136
+ easing: Easing.out(Easing.cubic),
137
+ useNativeDriver: true,
138
+ });
139
+ animation.start();
140
+ return () => animation.stop();
141
+ }, [target, showIndicator, reduceMotion, offset, theme.tokens.duration.base]);
142
+
143
+ // A new width (rotation, resize) places the indicator without animating.
144
+ const onLayout = (e: LayoutChangeEvent) => {
145
+ const width = e.nativeEvent.layout.width;
146
+ if (width === trackWidth) return;
147
+ placed.current = false;
148
+ setTrackWidth(width);
149
+ };
150
+
151
+ const activeDisabled = disabled || !!options[index]?.disabled;
55
152
 
56
153
  return (
57
- <View style={[styles.track, style]} accessibilityRole="radiogroup" testID={testID}>
154
+ <View style={[styles.track, style]} accessibilityRole="radiogroup" testID={testID} onLayout={onLayout}>
155
+ {showIndicator && (
156
+ <Animated.View
157
+ pointerEvents="none"
158
+ testID={testID && `${testID}-indicator`}
159
+ style={[
160
+ styles.indicator,
161
+ styles.segmentActive,
162
+ { width: segmentWidth, transform: [{ translateX: offset }] },
163
+ activeDisabled && styles.segmentDisabled,
164
+ ]}
165
+ />
166
+ )}
58
167
  {options.map((option) => {
59
168
  const active = option.value === value;
60
169
  const isDisabled = disabled || !!option.disabled;
61
170
  return (
62
171
  <TouchableOpacity
63
172
  key={option.value}
64
- style={[styles.segment, active && styles.segmentActive, isDisabled && styles.segmentDisabled]}
65
- onPress={() => onChange(option.value)}
173
+ style={[
174
+ styles.segment,
175
+ active && !showIndicator && styles.segmentActive,
176
+ isDisabled && styles.segmentDisabled,
177
+ ]}
178
+ onPress={() => {
179
+ if (!active) onChange(option.value);
180
+ }}
66
181
  disabled={isDisabled}
182
+ hitSlop={HIT_SLOP}
67
183
  accessibilityRole="radio"
68
184
  accessibilityState={{ selected: active, checked: active, disabled: isDisabled }}
69
185
  accessibilityLabel={option.label}
70
186
  testID={testID && `${testID}-${option.value}`}
71
187
  >
72
- <Text allowFontScaling={allowFontScaling} style={[styles.label, active && styles.labelActive]}>
188
+ <Text
189
+ allowFontScaling={allowFontScaling}
190
+ numberOfLines={1}
191
+ style={[styles.label, active && styles.labelActive]}
192
+ >
73
193
  {option.label}
74
194
  </Text>
75
195
  </TouchableOpacity>
@@ -79,6 +199,6 @@ function SegmentedControlInner<T extends string>({
79
199
  );
80
200
  }
81
201
 
82
- // memo() pierde los genéricos al envolver: el cast los devuelve para que
83
- // `value` y `onChange` sigan atados al union de `options`.
202
+ // memo() loses generics when wrapping: the cast restores them so that
203
+ // `value` and `onChange` stay tied to the union of `options`.
84
204
  export const SegmentedControl = memo(SegmentedControlInner) as typeof SegmentedControlInner;