orn-ui 0.1.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 (407) hide show
  1. package/README.md +107 -0
  2. package/bin/orn-ui.mjs +228 -0
  3. package/lib/commonjs/atoms/Avatar.js +42 -0
  4. package/lib/commonjs/atoms/Avatar.js.map +1 -0
  5. package/lib/commonjs/atoms/Badge.js +82 -0
  6. package/lib/commonjs/atoms/Badge.js.map +1 -0
  7. package/lib/commonjs/atoms/Button.js +222 -0
  8. package/lib/commonjs/atoms/Button.js.map +1 -0
  9. package/lib/commonjs/atoms/Card.js +33 -0
  10. package/lib/commonjs/atoms/Card.js.map +1 -0
  11. package/lib/commonjs/atoms/Checkbox.js +107 -0
  12. package/lib/commonjs/atoms/Checkbox.js.map +1 -0
  13. package/lib/commonjs/atoms/Divider.js +27 -0
  14. package/lib/commonjs/atoms/Divider.js.map +1 -0
  15. package/lib/commonjs/atoms/EmptyState.js +69 -0
  16. package/lib/commonjs/atoms/EmptyState.js.map +1 -0
  17. package/lib/commonjs/atoms/Fab.js +62 -0
  18. package/lib/commonjs/atoms/Fab.js.map +1 -0
  19. package/lib/commonjs/atoms/IconButton.js +60 -0
  20. package/lib/commonjs/atoms/IconButton.js.map +1 -0
  21. package/lib/commonjs/atoms/Image.js +113 -0
  22. package/lib/commonjs/atoms/Image.js.map +1 -0
  23. package/lib/commonjs/atoms/Input.js +173 -0
  24. package/lib/commonjs/atoms/Input.js.map +1 -0
  25. package/lib/commonjs/atoms/KeyValueRow.js +60 -0
  26. package/lib/commonjs/atoms/KeyValueRow.js.map +1 -0
  27. package/lib/commonjs/atoms/PressableScale.js +60 -0
  28. package/lib/commonjs/atoms/PressableScale.js.map +1 -0
  29. package/lib/commonjs/atoms/Spinner.js +156 -0
  30. package/lib/commonjs/atoms/Spinner.js.map +1 -0
  31. package/lib/commonjs/atoms/Text.js +63 -0
  32. package/lib/commonjs/atoms/Text.js.map +1 -0
  33. package/lib/commonjs/atoms/index.js +135 -0
  34. package/lib/commonjs/atoms/index.js.map +1 -0
  35. package/lib/commonjs/icons/Icon.js +40 -0
  36. package/lib/commonjs/icons/Icon.js.map +1 -0
  37. package/lib/commonjs/icons/defaultIcons.js +386 -0
  38. package/lib/commonjs/icons/defaultIcons.js.map +1 -0
  39. package/lib/commonjs/icons/index.js +20 -0
  40. package/lib/commonjs/icons/index.js.map +1 -0
  41. package/lib/commonjs/icons/types.js +6 -0
  42. package/lib/commonjs/icons/types.js.map +1 -0
  43. package/lib/commonjs/index.js +61 -0
  44. package/lib/commonjs/index.js.map +1 -0
  45. package/lib/commonjs/molecules/AvatarHeader.js +87 -0
  46. package/lib/commonjs/molecules/AvatarHeader.js.map +1 -0
  47. package/lib/commonjs/molecules/FormActions.js +47 -0
  48. package/lib/commonjs/molecules/FormActions.js.map +1 -0
  49. package/lib/commonjs/molecules/InfoRow.js +83 -0
  50. package/lib/commonjs/molecules/InfoRow.js.map +1 -0
  51. package/lib/commonjs/molecules/OptionCard.js +98 -0
  52. package/lib/commonjs/molecules/OptionCard.js.map +1 -0
  53. package/lib/commonjs/molecules/Stepper.js +104 -0
  54. package/lib/commonjs/molecules/Stepper.js.map +1 -0
  55. package/lib/commonjs/molecules/Steps.js +220 -0
  56. package/lib/commonjs/molecules/Steps.js.map +1 -0
  57. package/lib/commonjs/molecules/ThemeToggle.js +91 -0
  58. package/lib/commonjs/molecules/ThemeToggle.js.map +1 -0
  59. package/lib/commonjs/molecules/index.js +55 -0
  60. package/lib/commonjs/molecules/index.js.map +1 -0
  61. package/lib/commonjs/organisms/Alert.js +223 -0
  62. package/lib/commonjs/organisms/Alert.js.map +1 -0
  63. package/lib/commonjs/organisms/AlertProvider.js +109 -0
  64. package/lib/commonjs/organisms/AlertProvider.js.map +1 -0
  65. package/lib/commonjs/organisms/BottomSheet.js +154 -0
  66. package/lib/commonjs/organisms/BottomSheet.js.map +1 -0
  67. package/lib/commonjs/organisms/DateField.js +147 -0
  68. package/lib/commonjs/organisms/DateField.js.map +1 -0
  69. package/lib/commonjs/organisms/DatePicker.js +253 -0
  70. package/lib/commonjs/organisms/DatePicker.js.map +1 -0
  71. package/lib/commonjs/organisms/List.js +143 -0
  72. package/lib/commonjs/organisms/List.js.map +1 -0
  73. package/lib/commonjs/organisms/Modal.js +286 -0
  74. package/lib/commonjs/organisms/Modal.js.map +1 -0
  75. package/lib/commonjs/organisms/Screen.js +75 -0
  76. package/lib/commonjs/organisms/Screen.js.map +1 -0
  77. package/lib/commonjs/organisms/SearchList.js +180 -0
  78. package/lib/commonjs/organisms/SearchList.js.map +1 -0
  79. package/lib/commonjs/organisms/Select.js +178 -0
  80. package/lib/commonjs/organisms/Select.js.map +1 -0
  81. package/lib/commonjs/organisms/Toast.js +157 -0
  82. package/lib/commonjs/organisms/Toast.js.map +1 -0
  83. package/lib/commonjs/organisms/ToastProvider.js +131 -0
  84. package/lib/commonjs/organisms/ToastProvider.js.map +1 -0
  85. package/lib/commonjs/organisms/Wizard.js +116 -0
  86. package/lib/commonjs/organisms/Wizard.js.map +1 -0
  87. package/lib/commonjs/organisms/index.js +127 -0
  88. package/lib/commonjs/organisms/index.js.map +1 -0
  89. package/lib/commonjs/package.json +1 -0
  90. package/lib/commonjs/theme/UIProvider.js +110 -0
  91. package/lib/commonjs/theme/UIProvider.js.map +1 -0
  92. package/lib/commonjs/theme/createStyles.js +42 -0
  93. package/lib/commonjs/theme/createStyles.js.map +1 -0
  94. package/lib/commonjs/theme/createTheme.js +50 -0
  95. package/lib/commonjs/theme/createTheme.js.map +1 -0
  96. package/lib/commonjs/theme/index.js +151 -0
  97. package/lib/commonjs/theme/index.js.map +1 -0
  98. package/lib/commonjs/theme/insets.js +15 -0
  99. package/lib/commonjs/theme/insets.js.map +1 -0
  100. package/lib/commonjs/theme/labels.js +18 -0
  101. package/lib/commonjs/theme/labels.js.map +1 -0
  102. package/lib/commonjs/theme/palettes.js +42 -0
  103. package/lib/commonjs/theme/palettes.js.map +1 -0
  104. package/lib/commonjs/theme/tokens.js +120 -0
  105. package/lib/commonjs/theme/tokens.js.map +1 -0
  106. package/lib/commonjs/theme/types.js +6 -0
  107. package/lib/commonjs/theme/types.js.map +1 -0
  108. package/lib/module/atoms/Avatar.js +37 -0
  109. package/lib/module/atoms/Avatar.js.map +1 -0
  110. package/lib/module/atoms/Badge.js +77 -0
  111. package/lib/module/atoms/Badge.js.map +1 -0
  112. package/lib/module/atoms/Button.js +217 -0
  113. package/lib/module/atoms/Button.js.map +1 -0
  114. package/lib/module/atoms/Card.js +28 -0
  115. package/lib/module/atoms/Card.js.map +1 -0
  116. package/lib/module/atoms/Checkbox.js +102 -0
  117. package/lib/module/atoms/Checkbox.js.map +1 -0
  118. package/lib/module/atoms/Divider.js +22 -0
  119. package/lib/module/atoms/Divider.js.map +1 -0
  120. package/lib/module/atoms/EmptyState.js +64 -0
  121. package/lib/module/atoms/EmptyState.js.map +1 -0
  122. package/lib/module/atoms/Fab.js +57 -0
  123. package/lib/module/atoms/Fab.js.map +1 -0
  124. package/lib/module/atoms/IconButton.js +55 -0
  125. package/lib/module/atoms/IconButton.js.map +1 -0
  126. package/lib/module/atoms/Image.js +108 -0
  127. package/lib/module/atoms/Image.js.map +1 -0
  128. package/lib/module/atoms/Input.js +168 -0
  129. package/lib/module/atoms/Input.js.map +1 -0
  130. package/lib/module/atoms/KeyValueRow.js +55 -0
  131. package/lib/module/atoms/KeyValueRow.js.map +1 -0
  132. package/lib/module/atoms/PressableScale.js +55 -0
  133. package/lib/module/atoms/PressableScale.js.map +1 -0
  134. package/lib/module/atoms/Spinner.js +151 -0
  135. package/lib/module/atoms/Spinner.js.map +1 -0
  136. package/lib/module/atoms/Text.js +58 -0
  137. package/lib/module/atoms/Text.js.map +1 -0
  138. package/lib/module/atoms/index.js +18 -0
  139. package/lib/module/atoms/index.js.map +1 -0
  140. package/lib/module/icons/Icon.js +35 -0
  141. package/lib/module/icons/Icon.js.map +1 -0
  142. package/lib/module/icons/defaultIcons.js +381 -0
  143. package/lib/module/icons/defaultIcons.js.map +1 -0
  144. package/lib/module/icons/index.js +5 -0
  145. package/lib/module/icons/index.js.map +1 -0
  146. package/lib/module/icons/types.js +4 -0
  147. package/lib/module/icons/types.js.map +1 -0
  148. package/lib/module/index.js +8 -0
  149. package/lib/module/index.js.map +1 -0
  150. package/lib/module/molecules/AvatarHeader.js +82 -0
  151. package/lib/module/molecules/AvatarHeader.js.map +1 -0
  152. package/lib/module/molecules/FormActions.js +42 -0
  153. package/lib/module/molecules/FormActions.js.map +1 -0
  154. package/lib/module/molecules/InfoRow.js +78 -0
  155. package/lib/module/molecules/InfoRow.js.map +1 -0
  156. package/lib/module/molecules/OptionCard.js +93 -0
  157. package/lib/module/molecules/OptionCard.js.map +1 -0
  158. package/lib/module/molecules/Stepper.js +99 -0
  159. package/lib/module/molecules/Stepper.js.map +1 -0
  160. package/lib/module/molecules/Steps.js +215 -0
  161. package/lib/module/molecules/Steps.js.map +1 -0
  162. package/lib/module/molecules/ThemeToggle.js +86 -0
  163. package/lib/module/molecules/ThemeToggle.js.map +1 -0
  164. package/lib/module/molecules/index.js +10 -0
  165. package/lib/module/molecules/index.js.map +1 -0
  166. package/lib/module/organisms/Alert.js +218 -0
  167. package/lib/module/organisms/Alert.js.map +1 -0
  168. package/lib/module/organisms/AlertProvider.js +103 -0
  169. package/lib/module/organisms/AlertProvider.js.map +1 -0
  170. package/lib/module/organisms/BottomSheet.js +149 -0
  171. package/lib/module/organisms/BottomSheet.js.map +1 -0
  172. package/lib/module/organisms/DateField.js +142 -0
  173. package/lib/module/organisms/DateField.js.map +1 -0
  174. package/lib/module/organisms/DatePicker.js +247 -0
  175. package/lib/module/organisms/DatePicker.js.map +1 -0
  176. package/lib/module/organisms/List.js +138 -0
  177. package/lib/module/organisms/List.js.map +1 -0
  178. package/lib/module/organisms/Modal.js +281 -0
  179. package/lib/module/organisms/Modal.js.map +1 -0
  180. package/lib/module/organisms/Screen.js +70 -0
  181. package/lib/module/organisms/Screen.js.map +1 -0
  182. package/lib/module/organisms/SearchList.js +175 -0
  183. package/lib/module/organisms/SearchList.js.map +1 -0
  184. package/lib/module/organisms/Select.js +173 -0
  185. package/lib/module/organisms/Select.js.map +1 -0
  186. package/lib/module/organisms/Toast.js +152 -0
  187. package/lib/module/organisms/Toast.js.map +1 -0
  188. package/lib/module/organisms/ToastProvider.js +125 -0
  189. package/lib/module/organisms/ToastProvider.js.map +1 -0
  190. package/lib/module/organisms/Wizard.js +111 -0
  191. package/lib/module/organisms/Wizard.js.map +1 -0
  192. package/lib/module/organisms/index.js +16 -0
  193. package/lib/module/organisms/index.js.map +1 -0
  194. package/lib/module/package.json +1 -0
  195. package/lib/module/theme/UIProvider.js +97 -0
  196. package/lib/module/theme/UIProvider.js.map +1 -0
  197. package/lib/module/theme/createStyles.js +38 -0
  198. package/lib/module/theme/createStyles.js.map +1 -0
  199. package/lib/module/theme/createTheme.js +45 -0
  200. package/lib/module/theme/createTheme.js.map +1 -0
  201. package/lib/module/theme/index.js +10 -0
  202. package/lib/module/theme/index.js.map +1 -0
  203. package/lib/module/theme/insets.js +11 -0
  204. package/lib/module/theme/insets.js.map +1 -0
  205. package/lib/module/theme/labels.js +14 -0
  206. package/lib/module/theme/labels.js.map +1 -0
  207. package/lib/module/theme/palettes.js +38 -0
  208. package/lib/module/theme/palettes.js.map +1 -0
  209. package/lib/module/theme/tokens.js +117 -0
  210. package/lib/module/theme/tokens.js.map +1 -0
  211. package/lib/module/theme/types.js +4 -0
  212. package/lib/module/theme/types.js.map +1 -0
  213. package/lib/typescript/atoms/Avatar.d.ts +15 -0
  214. package/lib/typescript/atoms/Avatar.d.ts.map +1 -0
  215. package/lib/typescript/atoms/Badge.d.ts +19 -0
  216. package/lib/typescript/atoms/Badge.d.ts.map +1 -0
  217. package/lib/typescript/atoms/Button.d.ts +30 -0
  218. package/lib/typescript/atoms/Button.d.ts.map +1 -0
  219. package/lib/typescript/atoms/Card.d.ts +8 -0
  220. package/lib/typescript/atoms/Card.d.ts.map +1 -0
  221. package/lib/typescript/atoms/Checkbox.d.ts +11 -0
  222. package/lib/typescript/atoms/Checkbox.d.ts.map +1 -0
  223. package/lib/typescript/atoms/Divider.d.ts +8 -0
  224. package/lib/typescript/atoms/Divider.d.ts.map +1 -0
  225. package/lib/typescript/atoms/EmptyState.d.ts +12 -0
  226. package/lib/typescript/atoms/EmptyState.d.ts.map +1 -0
  227. package/lib/typescript/atoms/Fab.d.ts +22 -0
  228. package/lib/typescript/atoms/Fab.d.ts.map +1 -0
  229. package/lib/typescript/atoms/IconButton.d.ts +23 -0
  230. package/lib/typescript/atoms/IconButton.d.ts.map +1 -0
  231. package/lib/typescript/atoms/Image.d.ts +45 -0
  232. package/lib/typescript/atoms/Image.d.ts.map +1 -0
  233. package/lib/typescript/atoms/Input.d.ts +24 -0
  234. package/lib/typescript/atoms/Input.d.ts.map +1 -0
  235. package/lib/typescript/atoms/KeyValueRow.d.ts +17 -0
  236. package/lib/typescript/atoms/KeyValueRow.d.ts.map +1 -0
  237. package/lib/typescript/atoms/PressableScale.d.ts +15 -0
  238. package/lib/typescript/atoms/PressableScale.d.ts.map +1 -0
  239. package/lib/typescript/atoms/Spinner.d.ts +20 -0
  240. package/lib/typescript/atoms/Spinner.d.ts.map +1 -0
  241. package/lib/typescript/atoms/Text.d.ts +13 -0
  242. package/lib/typescript/atoms/Text.d.ts.map +1 -0
  243. package/lib/typescript/atoms/index.d.ts +31 -0
  244. package/lib/typescript/atoms/index.d.ts.map +1 -0
  245. package/lib/typescript/icons/Icon.d.ts +18 -0
  246. package/lib/typescript/icons/Icon.d.ts.map +1 -0
  247. package/lib/typescript/icons/defaultIcons.d.ts +5 -0
  248. package/lib/typescript/icons/defaultIcons.d.ts.map +1 -0
  249. package/lib/typescript/icons/index.d.ts +5 -0
  250. package/lib/typescript/icons/index.d.ts.map +1 -0
  251. package/lib/typescript/icons/types.d.ts +10 -0
  252. package/lib/typescript/icons/types.d.ts.map +1 -0
  253. package/lib/typescript/index.d.ts +6 -0
  254. package/lib/typescript/index.d.ts.map +1 -0
  255. package/lib/typescript/molecules/AvatarHeader.d.ts +20 -0
  256. package/lib/typescript/molecules/AvatarHeader.d.ts.map +1 -0
  257. package/lib/typescript/molecules/FormActions.d.ts +21 -0
  258. package/lib/typescript/molecules/FormActions.d.ts.map +1 -0
  259. package/lib/typescript/molecules/InfoRow.d.ts +17 -0
  260. package/lib/typescript/molecules/InfoRow.d.ts.map +1 -0
  261. package/lib/typescript/molecules/OptionCard.d.ts +20 -0
  262. package/lib/typescript/molecules/OptionCard.d.ts.map +1 -0
  263. package/lib/typescript/molecules/Stepper.d.ts +15 -0
  264. package/lib/typescript/molecules/Stepper.d.ts.map +1 -0
  265. package/lib/typescript/molecules/Steps.d.ts +29 -0
  266. package/lib/typescript/molecules/Steps.d.ts.map +1 -0
  267. package/lib/typescript/molecules/ThemeToggle.d.ts +15 -0
  268. package/lib/typescript/molecules/ThemeToggle.d.ts.map +1 -0
  269. package/lib/typescript/molecules/index.d.ts +15 -0
  270. package/lib/typescript/molecules/index.d.ts.map +1 -0
  271. package/lib/typescript/organisms/Alert.d.ts +29 -0
  272. package/lib/typescript/organisms/Alert.d.ts.map +1 -0
  273. package/lib/typescript/organisms/AlertProvider.d.ts +32 -0
  274. package/lib/typescript/organisms/AlertProvider.d.ts.map +1 -0
  275. package/lib/typescript/organisms/BottomSheet.d.ts +15 -0
  276. package/lib/typescript/organisms/BottomSheet.d.ts.map +1 -0
  277. package/lib/typescript/organisms/DateField.d.ts +26 -0
  278. package/lib/typescript/organisms/DateField.d.ts.map +1 -0
  279. package/lib/typescript/organisms/DatePicker.d.ts +49 -0
  280. package/lib/typescript/organisms/DatePicker.d.ts.map +1 -0
  281. package/lib/typescript/organisms/List.d.ts +35 -0
  282. package/lib/typescript/organisms/List.d.ts.map +1 -0
  283. package/lib/typescript/organisms/Modal.d.ts +25 -0
  284. package/lib/typescript/organisms/Modal.d.ts.map +1 -0
  285. package/lib/typescript/organisms/Screen.d.ts +26 -0
  286. package/lib/typescript/organisms/Screen.d.ts.map +1 -0
  287. package/lib/typescript/organisms/SearchList.d.ts +40 -0
  288. package/lib/typescript/organisms/SearchList.d.ts.map +1 -0
  289. package/lib/typescript/organisms/Select.d.ts +25 -0
  290. package/lib/typescript/organisms/Select.d.ts.map +1 -0
  291. package/lib/typescript/organisms/Toast.d.ts +28 -0
  292. package/lib/typescript/organisms/Toast.d.ts.map +1 -0
  293. package/lib/typescript/organisms/ToastProvider.d.ts +36 -0
  294. package/lib/typescript/organisms/ToastProvider.d.ts.map +1 -0
  295. package/lib/typescript/organisms/Wizard.d.ts +40 -0
  296. package/lib/typescript/organisms/Wizard.d.ts.map +1 -0
  297. package/lib/typescript/organisms/index.d.ts +27 -0
  298. package/lib/typescript/organisms/index.d.ts.map +1 -0
  299. package/lib/typescript/theme/UIProvider.d.ts +47 -0
  300. package/lib/typescript/theme/UIProvider.d.ts.map +1 -0
  301. package/lib/typescript/theme/createStyles.d.ts +21 -0
  302. package/lib/typescript/theme/createStyles.d.ts.map +1 -0
  303. package/lib/typescript/theme/createTheme.d.ts +9 -0
  304. package/lib/typescript/theme/createTheme.d.ts.map +1 -0
  305. package/lib/typescript/theme/index.d.ts +14 -0
  306. package/lib/typescript/theme/index.d.ts.map +1 -0
  307. package/lib/typescript/theme/insets.d.ts +9 -0
  308. package/lib/typescript/theme/insets.d.ts.map +1 -0
  309. package/lib/typescript/theme/labels.d.ts +13 -0
  310. package/lib/typescript/theme/labels.d.ts.map +1 -0
  311. package/lib/typescript/theme/palettes.d.ts +24 -0
  312. package/lib/typescript/theme/palettes.d.ts.map +1 -0
  313. package/lib/typescript/theme/tokens.d.ts +41 -0
  314. package/lib/typescript/theme/tokens.d.ts.map +1 -0
  315. package/lib/typescript/theme/types.d.ts +24 -0
  316. package/lib/typescript/theme/types.d.ts.map +1 -0
  317. package/package.json +311 -0
  318. package/registry/alert-provider.json +21 -0
  319. package/registry/alert.json +19 -0
  320. package/registry/avatar-header.json +18 -0
  321. package/registry/avatar.json +18 -0
  322. package/registry/badge.json +18 -0
  323. package/registry/bottom-sheet.json +18 -0
  324. package/registry/button.json +18 -0
  325. package/registry/card.json +18 -0
  326. package/registry/checkbox.json +18 -0
  327. package/registry/core.json +64 -0
  328. package/registry/date-field.json +24 -0
  329. package/registry/date-picker.json +23 -0
  330. package/registry/divider.json +18 -0
  331. package/registry/empty-state.json +18 -0
  332. package/registry/fab.json +19 -0
  333. package/registry/form-actions.json +19 -0
  334. package/registry/icon-button.json +19 -0
  335. package/registry/image.json +20 -0
  336. package/registry/index.json +465 -0
  337. package/registry/info-row.json +19 -0
  338. package/registry/input.json +18 -0
  339. package/registry/key-value-row.json +18 -0
  340. package/registry/list.json +20 -0
  341. package/registry/manifest.json +263 -0
  342. package/registry/modal.json +21 -0
  343. package/registry/option-card.json +18 -0
  344. package/registry/pressable-scale.json +18 -0
  345. package/registry/screen.json +18 -0
  346. package/registry/search-list.json +23 -0
  347. package/registry/select.json +18 -0
  348. package/registry/spinner.json +18 -0
  349. package/registry/stepper.json +18 -0
  350. package/registry/steps.json +18 -0
  351. package/registry/theme-toggle.json +18 -0
  352. package/registry/title.json +21 -0
  353. package/registry/toast-provider.json +20 -0
  354. package/registry/toast.json +18 -0
  355. package/registry/wizard.json +20 -0
  356. package/src/atoms/Avatar.tsx +40 -0
  357. package/src/atoms/Badge.tsx +55 -0
  358. package/src/atoms/Button.tsx +186 -0
  359. package/src/atoms/Card.tsx +27 -0
  360. package/src/atoms/Checkbox.tsx +75 -0
  361. package/src/atoms/Divider.tsx +14 -0
  362. package/src/atoms/EmptyState.tsx +60 -0
  363. package/src/atoms/Fab.tsx +63 -0
  364. package/src/atoms/IconButton.tsx +53 -0
  365. package/src/atoms/Image.tsx +137 -0
  366. package/src/atoms/Input.tsx +166 -0
  367. package/src/atoms/KeyValueRow.tsx +48 -0
  368. package/src/atoms/PressableScale.tsx +52 -0
  369. package/src/atoms/Spinner.tsx +127 -0
  370. package/src/atoms/Text.tsx +58 -0
  371. package/src/atoms/index.ts +30 -0
  372. package/src/icons/Icon.tsx +27 -0
  373. package/src/icons/defaultIcons.tsx +292 -0
  374. package/src/icons/index.ts +4 -0
  375. package/src/icons/types.ts +27 -0
  376. package/src/index.ts +5 -0
  377. package/src/molecules/AvatarHeader.tsx +69 -0
  378. package/src/molecules/FormActions.tsx +52 -0
  379. package/src/molecules/InfoRow.tsx +52 -0
  380. package/src/molecules/OptionCard.tsx +82 -0
  381. package/src/molecules/Stepper.tsx +76 -0
  382. package/src/molecules/Steps.tsx +198 -0
  383. package/src/molecules/ThemeToggle.tsx +67 -0
  384. package/src/molecules/index.ts +14 -0
  385. package/src/organisms/Alert.tsx +162 -0
  386. package/src/organisms/AlertProvider.tsx +125 -0
  387. package/src/organisms/BottomSheet.tsx +95 -0
  388. package/src/organisms/DateField.tsx +134 -0
  389. package/src/organisms/DatePicker.tsx +255 -0
  390. package/src/organisms/List.tsx +154 -0
  391. package/src/organisms/Modal.tsx +251 -0
  392. package/src/organisms/Screen.tsx +84 -0
  393. package/src/organisms/SearchList.tsx +186 -0
  394. package/src/organisms/Select.tsx +156 -0
  395. package/src/organisms/Toast.tsx +135 -0
  396. package/src/organisms/ToastProvider.tsx +166 -0
  397. package/src/organisms/Wizard.tsx +137 -0
  398. package/src/organisms/index.ts +26 -0
  399. package/src/theme/UIProvider.tsx +129 -0
  400. package/src/theme/createStyles.ts +40 -0
  401. package/src/theme/createTheme.ts +49 -0
  402. package/src/theme/index.ts +22 -0
  403. package/src/theme/insets.ts +9 -0
  404. package/src/theme/labels.ts +23 -0
  405. package/src/theme/palettes.ts +53 -0
  406. package/src/theme/tokens.ts +113 -0
  407. package/src/theme/types.ts +27 -0
@@ -0,0 +1,18 @@
1
+ {
2
+ "name": "Toast",
3
+ "slug": "toast",
4
+ "type": "component",
5
+ "category": "organisms",
6
+ "exportedNames": [
7
+ "Toast"
8
+ ],
9
+ "files": [
10
+ {
11
+ "path": "organisms/Toast.tsx",
12
+ "content": "import React, { memo, useEffect, useRef } from 'react';\nimport { Animated, Pressable, Text, View, type StyleProp, type ViewStyle } from 'react-native';\nimport { createStyles } from '../theme/createStyles';\nimport { useAllowFontScaling, useColors } from '../theme/UIProvider';\nimport { Icon } from '../icons/Icon';\nimport type { IconName } from '../icons/types';\n\nexport type ToastVariant = 'success' | 'error' | 'warning' | 'info';\n\nexport interface ToastProps {\n title: string;\n message?: string;\n variant?: ToastVariant;\n /** Se dispara al tocar el toast (además de cerrarlo). */\n onPress?: () => void;\n onDismiss?: () => void;\n /** Oculta la X de cerrar. El toast igual se auto-descarta por tiempo. */\n hideCloseButton?: boolean;\n style?: StyleProp<ViewStyle>;\n testID?: string;\n}\n\nconst ICON_BY_VARIANT: Record<ToastVariant, IconName> = {\n success: 'check',\n error: 'alert',\n warning: 'warning',\n info: 'info',\n};\n\nconst useStyles = createStyles((theme) => ({\n container: {\n flexDirection: 'row',\n alignItems: 'center',\n gap: theme.tokens.spacing.md,\n backgroundColor: theme.colors.surface,\n borderRadius: theme.tokens.radius.lg,\n borderWidth: 1,\n borderColor: theme.colors.border,\n paddingVertical: theme.tokens.spacing.md,\n paddingHorizontal: theme.tokens.spacing.lg,\n ...theme.tokens.shadow.lg,\n },\n accent: {\n position: 'absolute',\n left: 0,\n top: 0,\n bottom: 0,\n width: 4,\n borderTopLeftRadius: theme.tokens.radius.lg,\n borderBottomLeftRadius: theme.tokens.radius.lg,\n },\n text: { flex: 1, gap: 2 },\n title: {\n fontSize: theme.tokens.fontSize.md,\n fontWeight: theme.tokens.fontWeight.semibold,\n color: theme.colors.text,\n },\n message: { fontSize: theme.tokens.fontSize.sm, color: theme.colors.textLight, lineHeight: theme.tokens.lineHeight.sm },\n}));\n\n/**\n * Toast\n * Notificación efímera. Normalmente no se usa directo: `ToastProvider` +\n * `useToast()` la montan y la descartan por vos. Se expone igual por si\n * querés colocarla vos mismo dentro de una pantalla concreta.\n */\nexport const Toast = memo(\n ({ title, message, variant = 'info', onPress, onDismiss, hideCloseButton, style, testID }: ToastProps) => {\n const colors = useColors();\n const styles = useStyles();\n const allowFontScaling = useAllowFontScaling();\n\n const accentColor: Record<ToastVariant, string> = {\n success: colors.success,\n error: colors.error,\n warning: colors.warning,\n info: colors.secondary,\n };\n\n const body = (\n <View style={[styles.container, style]} testID={testID} accessibilityRole=\"alert\">\n <View style={[styles.accent, { backgroundColor: accentColor[variant] }]} />\n <Icon name={ICON_BY_VARIANT[variant]} size={22} color={accentColor[variant]} />\n <View style={styles.text}>\n <Text allowFontScaling={allowFontScaling} style={styles.title}>\n {title}\n </Text>\n {!!message && (\n <Text allowFontScaling={allowFontScaling} style={styles.message}>\n {message}\n </Text>\n )}\n </View>\n {!hideCloseButton && !!onDismiss && (\n <Pressable onPress={onDismiss} accessibilityRole=\"button\" accessibilityLabel=\"Dismiss\" hitSlop={8}>\n <Icon name=\"close\" size={18} color={colors.textLight} />\n </Pressable>\n )}\n </View>\n );\n\n if (!onPress) return body;\n\n return (\n <Pressable onPress={onPress} accessibilityRole=\"button\" accessibilityLabel={title}>\n {body}\n </Pressable>\n );\n }\n);\nToast.displayName = 'Toast';\n\n/** Envuelve un Toast con su animación de entrada/salida. Uso interno del provider. */\nexport const AnimatedToast = memo(\n ({ children, onHeight }: { children: React.ReactNode; onHeight?: (h: number) => void }) => {\n const progress = useRef(new Animated.Value(0)).current;\n\n useEffect(() => {\n Animated.spring(progress, { toValue: 1, useNativeDriver: true, speed: 14, bounciness: 4 }).start();\n }, [progress]);\n\n return (\n <Animated.View\n onLayout={(e) => onHeight?.(e.nativeEvent.layout.height)}\n style={{\n opacity: progress,\n transform: [{ translateY: progress.interpolate({ inputRange: [0, 1], outputRange: [-16, 0] }) }],\n }}\n >\n {children}\n </Animated.View>\n );\n }\n);\nAnimatedToast.displayName = 'AnimatedToast';\n"
13
+ }
14
+ ],
15
+ "registryDependencies": [
16
+ "core"
17
+ ]
18
+ }
@@ -0,0 +1,20 @@
1
+ {
2
+ "name": "Wizard",
3
+ "slug": "wizard",
4
+ "type": "component",
5
+ "category": "organisms",
6
+ "exportedNames": [
7
+ "Wizard"
8
+ ],
9
+ "files": [
10
+ {
11
+ "path": "organisms/Wizard.tsx",
12
+ "content": "import React, { useCallback, useMemo, useState } from 'react';\nimport { ScrollView, View, type StyleProp, type ViewStyle } from 'react-native';\nimport { createStyles } from '../theme/createStyles';\nimport { Steps, type StepItem } from '../molecules/Steps';\nimport { Button } from '../atoms/Button';\n\nexport interface WizardStep extends StepItem {\n content: React.ReactNode;\n /** false bloquea el avance desde este paso (validación). @default true */\n canGoNext?: boolean;\n}\n\nexport interface WizardProps {\n steps: WizardStep[];\n /** Paso actual (controlado). Si se omite, el Wizard maneja su propio estado. */\n current?: number;\n onStepChange?: (index: number) => void;\n onFinish?: () => void;\n /** @default 'horizontal' */\n orientation?: 'horizontal' | 'vertical';\n completedIndicator?: 'check' | 'number';\n /** Permite volver a un paso ya completado tocándolo en el indicador. @default true */\n allowStepNavigation?: boolean;\n backLabel?: string;\n nextLabel?: string;\n finishLabel?: string;\n /**\n * El contenido del paso scrollea cuando no entra. Ponelo en false sólo si\n * un paso renderiza su propia lista virtualizada (anidar una\n * VirtualizedList dentro de un ScrollView rompe la virtualización).\n * @default true\n */\n scrollableContent?: boolean;\n style?: StyleProp<ViewStyle>;\n}\n\nconst useStyles = createStyles((theme) => ({\n container: { flex: 1, gap: theme.tokens.spacing.xl },\n content: { flex: 1 },\n contentInner: { flexGrow: 1, paddingBottom: theme.tokens.spacing.sm },\n footer: { flexDirection: 'row', gap: theme.tokens.spacing.md },\n footerButton: { flex: 1 },\n}));\n\n/**\n * Wizard\n * Flujo multi-paso: indicador (Steps) + contenido del paso actual + barra de\n * navegación. Funciona controlado (`current` + `onStepChange`) o por su\n * cuenta. `canGoNext: false` en un paso deshabilita el avance, que es donde\n * se engancha la validación del formulario de ese paso.\n */\nexport function Wizard({\n steps,\n current: controlledCurrent,\n onStepChange,\n onFinish,\n orientation = 'horizontal',\n completedIndicator = 'check',\n allowStepNavigation = true,\n backLabel = 'Back',\n nextLabel = 'Next',\n finishLabel = 'Finish',\n scrollableContent = true,\n style,\n}: WizardProps) {\n const styles = useStyles();\n const [uncontrolled, setUncontrolled] = useState(0);\n const isControlled = controlledCurrent !== undefined;\n const current = isControlled ? controlledCurrent : uncontrolled;\n\n const goTo = useCallback(\n (index: number) => {\n const clamped = Math.max(0, Math.min(index, steps.length - 1));\n if (!isControlled) setUncontrolled(clamped);\n onStepChange?.(clamped);\n },\n [isControlled, onStepChange, steps.length]\n );\n\n const stepItems = useMemo(\n () => steps.map(({ label, description }) => ({ label, description })),\n [steps]\n );\n\n const activeStep = steps[current];\n const isFirst = current === 0;\n const isLast = current === steps.length - 1;\n const canGoNext = activeStep?.canGoNext !== false;\n\n const handleNext = () => {\n if (isLast) onFinish?.();\n else goTo(current + 1);\n };\n\n return (\n <View style={[styles.container, style]}>\n <Steps\n steps={stepItems}\n current={current}\n orientation={orientation}\n completedIndicator={completedIndicator}\n // Sólo se puede volver atrás tocando el indicador; saltar hacia\n // adelante se saltearía la validación de los pasos intermedios.\n onStepPress={allowStepNavigation ? (index) => index < current && goTo(index) : undefined}\n />\n\n {/* El contenido scrollea en vez de desbordar: con el teclado abierto un\n form largo se comía el footer y los botones quedaban pintados encima\n de los campos. */}\n {scrollableContent ? (\n <ScrollView\n style={styles.content}\n contentContainerStyle={styles.contentInner}\n keyboardShouldPersistTaps=\"handled\"\n automaticallyAdjustKeyboardInsets\n showsVerticalScrollIndicator={false}\n >\n {activeStep?.content}\n </ScrollView>\n ) : (\n <View style={styles.content}>{activeStep?.content}</View>\n )}\n\n <View style={styles.footer}>\n {!isFirst && (\n <Button title={backLabel} variant=\"outline\" onPress={() => goTo(current - 1)} style={styles.footerButton} />\n )}\n <Button\n title={isLast ? finishLabel : nextLabel}\n onPress={handleNext}\n disabled={!canGoNext}\n style={styles.footerButton}\n />\n </View>\n </View>\n );\n}\n"
13
+ }
14
+ ],
15
+ "registryDependencies": [
16
+ "core",
17
+ "steps",
18
+ "button"
19
+ ]
20
+ }
@@ -0,0 +1,40 @@
1
+ import React, { memo } from 'react';
2
+ import { StyleSheet, View, type StyleProp, type ViewStyle } from 'react-native';
3
+ import { useColors } from '../theme/UIProvider';
4
+
5
+ export interface AvatarProps {
6
+ size?: number;
7
+ backgroundColor?: string;
8
+ children: React.ReactNode;
9
+ style?: StyleProp<ViewStyle>;
10
+ }
11
+
12
+ const styles = StyleSheet.create({
13
+ circle: { justifyContent: 'center', alignItems: 'center' },
14
+ });
15
+
16
+ /**
17
+ * Avatar
18
+ * Círculo con ícono/iniciales adentro. Usado a la izquierda de filas de
19
+ * lista o como header de detalle (junto a AvatarHeader).
20
+ */
21
+ export const Avatar = memo(({ size = 44, backgroundColor, children, style }: AvatarProps) => {
22
+ const colors = useColors();
23
+ return (
24
+ <View
25
+ style={[
26
+ styles.circle,
27
+ {
28
+ width: size,
29
+ height: size,
30
+ borderRadius: size / 2,
31
+ backgroundColor: backgroundColor || colors.primary + '15',
32
+ },
33
+ style,
34
+ ]}
35
+ >
36
+ {children}
37
+ </View>
38
+ );
39
+ });
40
+ Avatar.displayName = 'Avatar';
@@ -0,0 +1,55 @@
1
+ import React, { memo } from 'react';
2
+ import { Text, View, type StyleProp, type ViewStyle } from 'react-native';
3
+ import { createStyles } from '../theme/createStyles';
4
+ import { useAllowFontScaling, useTheme } from '../theme/UIProvider';
5
+
6
+ export type BadgeVariant = 'success' | 'error' | 'warning' | 'info' | 'neutral';
7
+
8
+ export interface BadgeProps {
9
+ label: string;
10
+ variant?: BadgeVariant;
11
+ /** Override manual, gana sobre `variant`. */
12
+ backgroundColor?: string;
13
+ textColor?: string;
14
+ style?: StyleProp<ViewStyle>;
15
+ }
16
+
17
+ const useStyles = createStyles((theme) => ({
18
+ badge: {
19
+ paddingHorizontal: theme.tokens.spacing.md - 2,
20
+ paddingVertical: theme.tokens.spacing.xs + 1,
21
+ borderRadius: theme.tokens.radius.full,
22
+ alignSelf: 'flex-start',
23
+ },
24
+ text: { fontSize: theme.tokens.fontSize.xs, fontWeight: theme.tokens.fontWeight.bold, letterSpacing: 0.3 },
25
+ }));
26
+
27
+ /**
28
+ * Badge
29
+ * Pill de estado. `variant` cubre los casos comunes (éxito/error/advertencia/
30
+ * info/neutral); `backgroundColor`/`textColor` quedan para casos que no
31
+ * encajan en el set semántico.
32
+ */
33
+ export const Badge = memo(({ label, variant = 'neutral', backgroundColor, textColor, style }: BadgeProps) => {
34
+ const theme = useTheme();
35
+ const styles = useStyles();
36
+ const allowFontScaling = useAllowFontScaling();
37
+ const { colors } = theme;
38
+
39
+ const palette: Record<BadgeVariant, { bg: string; fg: string }> = {
40
+ success: { bg: colors.success + '20', fg: colors.success },
41
+ error: { bg: colors.error + '20', fg: colors.error },
42
+ warning: { bg: colors.warning + '20', fg: colors.warning },
43
+ info: { bg: colors.secondary + '20', fg: colors.secondary },
44
+ neutral: { bg: colors.inputBackground, fg: colors.textLight },
45
+ };
46
+
47
+ return (
48
+ <View style={[styles.badge, { backgroundColor: backgroundColor ?? palette[variant].bg }, style]}>
49
+ <Text allowFontScaling={allowFontScaling} style={[styles.text, { color: textColor ?? palette[variant].fg }]}>
50
+ {label}
51
+ </Text>
52
+ </View>
53
+ );
54
+ });
55
+ Badge.displayName = 'Badge';
@@ -0,0 +1,186 @@
1
+ import React, { memo, useRef } from 'react';
2
+ import {
3
+ ActivityIndicator,
4
+ Animated,
5
+ Pressable,
6
+ Text,
7
+ type GestureResponderEvent,
8
+ type StyleProp,
9
+ type TextStyle,
10
+ type ViewStyle,
11
+ } from 'react-native';
12
+ import { createStyles } from '../theme/createStyles';
13
+ import { useAllowFontScaling, useColors, useTheme } from '../theme/UIProvider';
14
+ import { Icon } from '../icons/Icon';
15
+ import type { IconName } from '../icons/types';
16
+
17
+ const AnimatedPressable = Animated.createAnimatedComponent(Pressable);
18
+
19
+ export type ButtonVariant = 'primary' | 'secondary' | 'outline' | 'ghost' | 'link' | 'destructive';
20
+ export type ButtonSize = 'sm' | 'md' | 'lg';
21
+
22
+ export interface ButtonProps {
23
+ title: string;
24
+ onPress: (e: GestureResponderEvent) => void;
25
+ variant?: ButtonVariant;
26
+ size?: ButtonSize;
27
+ loading?: boolean;
28
+ disabled?: boolean;
29
+ style?: StyleProp<ViewStyle>;
30
+ textStyle?: StyleProp<TextStyle>;
31
+ leftIcon?: React.ReactNode;
32
+ rightIcon?: React.ReactNode;
33
+ leftIconName?: IconName;
34
+ rightIconName?: IconName;
35
+ iconColor?: string;
36
+ testID?: string;
37
+ accessibilityLabel?: string;
38
+ }
39
+
40
+ const useStyles = createStyles((theme) => ({
41
+ base: {
42
+ flexDirection: 'row',
43
+ alignItems: 'center',
44
+ justifyContent: 'center',
45
+ borderRadius: theme.tokens.radius.lg,
46
+ borderWidth: 0,
47
+ },
48
+ sm: { paddingVertical: theme.tokens.spacing.sm, paddingHorizontal: theme.tokens.spacing.md },
49
+ md: { paddingVertical: theme.tokens.spacing.lg - 1, paddingHorizontal: theme.tokens.spacing.xl },
50
+ lg: { paddingVertical: theme.tokens.spacing.lg + 2, paddingHorizontal: theme.tokens.spacing.xxl },
51
+ link: { paddingVertical: theme.tokens.spacing.xs, paddingHorizontal: 0, justifyContent: 'flex-start' },
52
+ outline: { borderWidth: 1 },
53
+ filledShadow: { ...theme.tokens.shadow.md, shadowColor: '#000' },
54
+ text: { fontWeight: theme.tokens.fontWeight.semibold, textAlign: 'center' },
55
+ textSm: { fontSize: theme.tokens.fontSize.sm },
56
+ textMd: { fontSize: theme.tokens.fontSize.lg },
57
+ textLg: { fontSize: theme.tokens.fontSize.xl },
58
+ underline: { textDecorationLine: 'underline' },
59
+ iconLeft: { marginRight: theme.tokens.spacing.sm },
60
+ iconRight: { marginLeft: theme.tokens.spacing.sm },
61
+ }));
62
+
63
+ function resolveColors(variant: ButtonVariant, disabled: boolean, colors: ReturnType<typeof useColors>) {
64
+ if (disabled) {
65
+ return { background: colors.inputBackground, text: colors.textLight, border: 'transparent' };
66
+ }
67
+ switch (variant) {
68
+ case 'outline':
69
+ return { background: 'transparent', text: colors.primary, border: colors.primary };
70
+ case 'ghost':
71
+ return { background: 'transparent', text: colors.primary, border: 'transparent' };
72
+ case 'link':
73
+ return { background: 'transparent', text: colors.primary, border: 'transparent' };
74
+ case 'secondary':
75
+ return { background: colors.secondary, text: colors.white, border: 'transparent' };
76
+ case 'destructive':
77
+ return { background: colors.error, text: colors.white, border: 'transparent' };
78
+ case 'primary':
79
+ default:
80
+ return { background: colors.primary, text: colors.white, border: 'transparent' };
81
+ }
82
+ }
83
+
84
+ /**
85
+ * Button
86
+ * Único componente de botón de la librería. `variant` decide el estilo
87
+ * semántico, `size` la densidad. El feedback de "presionado" anima solo
88
+ * `transform` (Animated.spring, useNativeDriver) — nunca opacity ni layout.
89
+ */
90
+ export const Button = memo(
91
+ ({
92
+ title,
93
+ onPress,
94
+ variant = 'primary',
95
+ size = 'md',
96
+ loading = false,
97
+ disabled = false,
98
+ style,
99
+ textStyle,
100
+ leftIcon,
101
+ rightIcon,
102
+ leftIconName,
103
+ rightIconName,
104
+ iconColor,
105
+ testID,
106
+ accessibilityLabel,
107
+ }: ButtonProps) => {
108
+ const styles = useStyles();
109
+ const colors = useColors();
110
+ const theme = useTheme();
111
+ const allowFontScaling = useAllowFontScaling();
112
+ const scale = useRef(new Animated.Value(1)).current;
113
+ const isDisabled = disabled || loading;
114
+ const isFilled = variant === 'primary' || variant === 'secondary' || variant === 'destructive';
115
+
116
+ const resolved = resolveColors(variant, disabled, colors);
117
+ const effectiveIconColor = iconColor ?? resolved.text;
118
+
119
+ const animateTo = (value: number) => {
120
+ Animated.spring(scale, { toValue: value, useNativeDriver: true, speed: 50, bounciness: 6 }).start();
121
+ };
122
+
123
+ const containerStyle: StyleProp<ViewStyle> = [
124
+ styles.base,
125
+ styles[size],
126
+ variant === 'link' && styles.link,
127
+ variant === 'outline' && [styles.outline, { borderColor: resolved.border }],
128
+ { backgroundColor: resolved.background },
129
+ isFilled && !disabled && styles.filledShadow,
130
+ { opacity: isDisabled ? 0.7 : 1 },
131
+ style,
132
+ ];
133
+
134
+ const labelStyle: StyleProp<TextStyle> = [
135
+ styles.text,
136
+ size === 'sm' ? styles.textSm : size === 'lg' ? styles.textLg : styles.textMd,
137
+ { color: resolved.text },
138
+ variant === 'link' && styles.underline,
139
+ textStyle,
140
+ ];
141
+
142
+ const iconSize = size === 'sm' ? 16 : size === 'lg' ? 22 : 20;
143
+
144
+ return (
145
+ // El Pressable ES el contenedor animado, no un wrapper alrededor de uno.
146
+ // Con un Animated.View interno, un `style` de layout (flex, width,
147
+ // margin) caía en el hijo mientras el Pressable — que es quien ocupa
148
+ // lugar en el layout del padre — quedaba sin él: el ancho colapsaba y
149
+ // el label desaparecía (visible en los botones en fila del Alert).
150
+ <AnimatedPressable
151
+ onPress={onPress}
152
+ onPressIn={() => {
153
+ if (!isDisabled) animateTo(0.95);
154
+ }}
155
+ onPressOut={() => {
156
+ if (!isDisabled) animateTo(1);
157
+ }}
158
+ disabled={isDisabled}
159
+ testID={testID}
160
+ accessibilityRole="button"
161
+ accessibilityLabel={accessibilityLabel ?? title}
162
+ accessibilityState={{ disabled: isDisabled, busy: loading }}
163
+ style={[containerStyle, { transform: [{ scale }] }]}
164
+ >
165
+ {loading ? (
166
+ <ActivityIndicator size="small" color={resolved.text} />
167
+ ) : (
168
+ <>
169
+ {leftIcon}
170
+ {!leftIcon && leftIconName && (
171
+ <Icon name={leftIconName} size={iconSize} color={effectiveIconColor} style={styles.iconLeft} />
172
+ )}
173
+ <Text allowFontScaling={allowFontScaling} style={labelStyle}>
174
+ {title}
175
+ </Text>
176
+ {rightIcon}
177
+ {!rightIcon && rightIconName && (
178
+ <Icon name={rightIconName} size={iconSize} color={effectiveIconColor} style={styles.iconRight} />
179
+ )}
180
+ </>
181
+ )}
182
+ </AnimatedPressable>
183
+ );
184
+ }
185
+ );
186
+ Button.displayName = 'Button';
@@ -0,0 +1,27 @@
1
+ import React, { memo } from 'react';
2
+ import { View, type ViewProps, type ViewStyle } from 'react-native';
3
+ import { createStyles } from '../theme/createStyles';
4
+
5
+ export interface CardProps extends ViewProps {
6
+ children: React.ReactNode;
7
+ style?: ViewStyle;
8
+ }
9
+
10
+ const useStyles = createStyles((theme) => ({
11
+ card: {
12
+ backgroundColor: theme.colors.surface,
13
+ borderRadius: theme.tokens.radius.xl,
14
+ padding: theme.tokens.spacing.lg,
15
+ ...theme.tokens.shadow.sm,
16
+ },
17
+ }));
18
+
19
+ export const Card = memo(({ children, style, ...props }: CardProps) => {
20
+ const styles = useStyles();
21
+ return (
22
+ <View style={[styles.card, style]} {...props}>
23
+ {children}
24
+ </View>
25
+ );
26
+ });
27
+ Card.displayName = 'Card';
@@ -0,0 +1,75 @@
1
+ import React, { memo, useEffect, useRef } from 'react';
2
+ import { Animated, Text, TouchableOpacity } from 'react-native';
3
+ import { createStyles } from '../theme/createStyles';
4
+ import { useAllowFontScaling, useColors } from '../theme/UIProvider';
5
+ import { Icon } from '../icons/Icon';
6
+
7
+ export interface CheckboxProps {
8
+ value: boolean;
9
+ onValueChange: (value: boolean) => void;
10
+ label?: string;
11
+ color?: string;
12
+ disabled?: boolean;
13
+ testID?: string;
14
+ }
15
+
16
+ const useStyles = createStyles((theme) => ({
17
+ container: { flexDirection: 'row', alignItems: 'center', marginBottom: theme.tokens.spacing.sm },
18
+ box: {
19
+ width: 24,
20
+ height: 24,
21
+ borderRadius: theme.tokens.radius.sm,
22
+ borderWidth: 2,
23
+ justifyContent: 'center',
24
+ alignItems: 'center',
25
+ marginRight: theme.tokens.spacing.sm + 2,
26
+ },
27
+ label: { fontSize: theme.tokens.fontSize.md, color: theme.colors.text },
28
+ }));
29
+
30
+ export const Checkbox = memo(({ value, onValueChange, label, color, disabled, testID }: CheckboxProps) => {
31
+ const colors = useColors();
32
+ const styles = useStyles();
33
+ const allowFontScaling = useAllowFontScaling();
34
+ const activeColor = color || colors.primary;
35
+ const progress = useRef(new Animated.Value(value ? 1 : 0)).current;
36
+ const checkScale = useRef(new Animated.Value(value ? 1 : 0)).current;
37
+
38
+ useEffect(() => {
39
+ Animated.timing(progress, { toValue: value ? 1 : 0, duration: 150, useNativeDriver: false }).start();
40
+ Animated.spring(checkScale, {
41
+ toValue: value ? 1 : 0,
42
+ useNativeDriver: true,
43
+ speed: 40,
44
+ bounciness: 10,
45
+ }).start();
46
+ }, [value]);
47
+
48
+ const backgroundColor = progress.interpolate({ inputRange: [0, 1], outputRange: [colors.surface, activeColor] });
49
+ const borderColor = progress.interpolate({ inputRange: [0, 1], outputRange: [colors.border, activeColor] });
50
+
51
+ return (
52
+ <TouchableOpacity
53
+ style={styles.container}
54
+ activeOpacity={0.7}
55
+ disabled={disabled}
56
+ onPress={() => onValueChange(!value)}
57
+ accessibilityRole="checkbox"
58
+ accessibilityState={{ checked: value, disabled: !!disabled }}
59
+ accessibilityLabel={label}
60
+ testID={testID}
61
+ >
62
+ <Animated.View style={[styles.box, { backgroundColor, borderColor, opacity: disabled ? 0.5 : 1 }]}>
63
+ <Animated.View style={{ transform: [{ scale: checkScale }] }}>
64
+ <Icon name="check" size={16} color={colors.white} />
65
+ </Animated.View>
66
+ </Animated.View>
67
+ {label && (
68
+ <Text allowFontScaling={allowFontScaling} style={styles.label}>
69
+ {label}
70
+ </Text>
71
+ )}
72
+ </TouchableOpacity>
73
+ );
74
+ });
75
+ Checkbox.displayName = 'Checkbox';
@@ -0,0 +1,14 @@
1
+ import React, { memo } from 'react';
2
+ import { View, type StyleProp, type ViewStyle } from 'react-native';
3
+ import { useColors } from '../theme/UIProvider';
4
+
5
+ export interface DividerProps {
6
+ style?: StyleProp<ViewStyle>;
7
+ testID?: string;
8
+ }
9
+
10
+ export const Divider = memo(({ style, testID }: DividerProps) => {
11
+ const colors = useColors();
12
+ return <View testID={testID} style={[{ height: 1, backgroundColor: colors.border, width: '100%' }, style]} />;
13
+ });
14
+ Divider.displayName = 'Divider';
@@ -0,0 +1,60 @@
1
+ import React, { memo } from 'react';
2
+ import { Text, View, type ViewStyle } from 'react-native';
3
+ import { createStyles } from '../theme/createStyles';
4
+ import { useAllowFontScaling, useColors, useLabels } from '../theme/UIProvider';
5
+ import { Icon } from '../icons/Icon';
6
+ import type { IconName } from '../icons/types';
7
+
8
+ export interface EmptyStateProps {
9
+ title?: string;
10
+ description?: string;
11
+ iconName?: IconName;
12
+ icon?: React.ReactNode;
13
+ style?: ViewStyle;
14
+ }
15
+
16
+ const useStyles = createStyles((theme) => ({
17
+ container: {
18
+ flex: 1,
19
+ justifyContent: 'center',
20
+ alignItems: 'center',
21
+ padding: theme.tokens.spacing.xxl - 4,
22
+ minHeight: 200,
23
+ },
24
+ title: {
25
+ fontSize: theme.tokens.fontSize.lg,
26
+ fontWeight: theme.tokens.fontWeight.semibold,
27
+ color: theme.colors.textLight,
28
+ marginTop: theme.tokens.spacing.md - 2,
29
+ textAlign: 'center',
30
+ },
31
+ description: {
32
+ fontSize: theme.tokens.fontSize.md,
33
+ color: theme.colors.textLight,
34
+ marginTop: theme.tokens.spacing.xs + 1,
35
+ textAlign: 'center',
36
+ },
37
+ }));
38
+
39
+ export const EmptyState = memo(
40
+ ({ title, description, iconName = 'search', icon, style }: EmptyStateProps) => {
41
+ const colors = useColors();
42
+ const styles = useStyles();
43
+ const labels = useLabels();
44
+ const allowFontScaling = useAllowFontScaling();
45
+ const resolvedTitle = title ?? labels.noResultsTitle;
46
+ const resolvedDescription = description ?? labels.noResultsDescription;
47
+ return (
48
+ <View style={[styles.container, style]} accessible accessibilityRole="text">
49
+ {icon ?? <Icon name={iconName} size={48} color={colors.textLight} />}
50
+ <Text allowFontScaling={allowFontScaling} style={styles.title}>
51
+ {resolvedTitle}
52
+ </Text>
53
+ <Text allowFontScaling={allowFontScaling} style={styles.description}>
54
+ {resolvedDescription}
55
+ </Text>
56
+ </View>
57
+ );
58
+ }
59
+ );
60
+ EmptyState.displayName = 'EmptyState';
@@ -0,0 +1,63 @@
1
+ import React, { memo } from 'react';
2
+ import { type GestureResponderEvent, type StyleProp, type ViewStyle } from 'react-native';
3
+ import { PressableScale } from './PressableScale';
4
+ import { Icon } from '../icons/Icon';
5
+ import type { IconName } from '../icons/types';
6
+ import { createStyles } from '../theme/createStyles';
7
+ import { useColors } from '../theme/UIProvider';
8
+
9
+ export interface FabProps {
10
+ onPress: (e: GestureResponderEvent) => void;
11
+ iconName?: IconName;
12
+ icon?: React.ReactNode;
13
+ size?: number;
14
+ bottom?: number;
15
+ right?: number;
16
+ color?: string;
17
+ style?: StyleProp<ViewStyle>;
18
+ accessibilityLabel: string;
19
+ }
20
+
21
+ const useStyles = createStyles((theme) => ({
22
+ fab: {
23
+ position: 'absolute',
24
+ justifyContent: 'center',
25
+ alignItems: 'center',
26
+ ...theme.tokens.shadow.lg,
27
+ },
28
+ }));
29
+
30
+ /**
31
+ * Fab
32
+ * Botón flotante circular. `bottom`/`right` en vez del `tabBarHeight`
33
+ * obligatorio de la versión original — el consumidor decide su propio
34
+ * offset (con o sin tab bar).
35
+ */
36
+ export const Fab = memo(
37
+ ({ onPress, iconName = 'plus', icon, size = 60, bottom = 20, right = 20, color, style, accessibilityLabel }: FabProps) => {
38
+ const styles = useStyles();
39
+ const colors = useColors();
40
+ return (
41
+ <PressableScale
42
+ onPress={onPress}
43
+ style={[
44
+ styles.fab,
45
+ {
46
+ width: size,
47
+ height: size,
48
+ borderRadius: size / 2,
49
+ bottom,
50
+ right,
51
+ backgroundColor: color ?? colors.primary,
52
+ },
53
+ style,
54
+ ]}
55
+ accessibilityRole="button"
56
+ accessibilityLabel={accessibilityLabel}
57
+ >
58
+ {icon ?? <Icon name={iconName} size={size * 0.5} color={colors.white} />}
59
+ </PressableScale>
60
+ );
61
+ }
62
+ );
63
+ Fab.displayName = 'Fab';
@@ -0,0 +1,53 @@
1
+ import React, { memo } from 'react';
2
+ import { type GestureResponderEvent, type StyleProp, type ViewStyle } from 'react-native';
3
+ import { PressableScale } from './PressableScale';
4
+ import { Icon } from '../icons/Icon';
5
+ import type { IconName } from '../icons/types';
6
+ import { createStyles } from '../theme/createStyles';
7
+
8
+ export interface IconButtonProps {
9
+ iconName?: IconName;
10
+ icon?: React.ReactNode;
11
+ onPress?: (e: GestureResponderEvent) => void;
12
+ size?: number;
13
+ color?: string;
14
+ disabled?: boolean;
15
+ style?: StyleProp<ViewStyle>;
16
+ accessibilityLabel: string;
17
+ testID?: string;
18
+ }
19
+
20
+ const useStyles = createStyles((theme) => ({
21
+ hitArea: {
22
+ padding: theme.tokens.spacing.sm,
23
+ alignItems: 'center',
24
+ justifyContent: 'center',
25
+ },
26
+ }));
27
+
28
+ /**
29
+ * IconButton
30
+ * Botón de solo ícono con área táctil mínima de 44x44 garantizada por el
31
+ * padding (icon 24 + padding 8*2 = 40; sube a 44 con size>=24 default RN).
32
+ * `accessibilityLabel` es obligatorio: sin texto visible, es la única forma
33
+ * de que un lector de pantalla anuncie qué hace el botón.
34
+ */
35
+ export const IconButton = memo(
36
+ ({ iconName, icon, onPress, size = 24, color, disabled, style, accessibilityLabel, testID }: IconButtonProps) => {
37
+ const styles = useStyles();
38
+ return (
39
+ <PressableScale
40
+ onPress={onPress}
41
+ disabled={disabled}
42
+ style={[styles.hitArea, { opacity: disabled ? 0.5 : 1 }, style]}
43
+ accessibilityRole="button"
44
+ accessibilityLabel={accessibilityLabel}
45
+ accessibilityState={{ disabled: !!disabled }}
46
+ testID={testID}
47
+ >
48
+ {icon ?? (iconName && <Icon name={iconName} size={size} color={color} />)}
49
+ </PressableScale>
50
+ );
51
+ }
52
+ );
53
+ IconButton.displayName = 'IconButton';