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,135 @@
1
+ import React, { memo, useEffect, useRef } from 'react';
2
+ import { Animated, Pressable, Text, View, type StyleProp, type ViewStyle } from 'react-native';
3
+ import { createStyles } from '../theme/createStyles';
4
+ import { useAllowFontScaling, useColors } from '../theme/UIProvider';
5
+ import { Icon } from '../icons/Icon';
6
+ import type { IconName } from '../icons/types';
7
+
8
+ export type ToastVariant = 'success' | 'error' | 'warning' | 'info';
9
+
10
+ export interface ToastProps {
11
+ title: string;
12
+ message?: string;
13
+ variant?: ToastVariant;
14
+ /** Se dispara al tocar el toast (además de cerrarlo). */
15
+ onPress?: () => void;
16
+ onDismiss?: () => void;
17
+ /** Oculta la X de cerrar. El toast igual se auto-descarta por tiempo. */
18
+ hideCloseButton?: boolean;
19
+ style?: StyleProp<ViewStyle>;
20
+ testID?: string;
21
+ }
22
+
23
+ const ICON_BY_VARIANT: Record<ToastVariant, IconName> = {
24
+ success: 'check',
25
+ error: 'alert',
26
+ warning: 'warning',
27
+ info: 'info',
28
+ };
29
+
30
+ const useStyles = createStyles((theme) => ({
31
+ container: {
32
+ flexDirection: 'row',
33
+ alignItems: 'center',
34
+ gap: theme.tokens.spacing.md,
35
+ backgroundColor: theme.colors.surface,
36
+ borderRadius: theme.tokens.radius.lg,
37
+ borderWidth: 1,
38
+ borderColor: theme.colors.border,
39
+ paddingVertical: theme.tokens.spacing.md,
40
+ paddingHorizontal: theme.tokens.spacing.lg,
41
+ ...theme.tokens.shadow.lg,
42
+ },
43
+ accent: {
44
+ position: 'absolute',
45
+ left: 0,
46
+ top: 0,
47
+ bottom: 0,
48
+ width: 4,
49
+ borderTopLeftRadius: theme.tokens.radius.lg,
50
+ borderBottomLeftRadius: theme.tokens.radius.lg,
51
+ },
52
+ text: { flex: 1, gap: 2 },
53
+ title: {
54
+ fontSize: theme.tokens.fontSize.md,
55
+ fontWeight: theme.tokens.fontWeight.semibold,
56
+ color: theme.colors.text,
57
+ },
58
+ message: { fontSize: theme.tokens.fontSize.sm, color: theme.colors.textLight, lineHeight: theme.tokens.lineHeight.sm },
59
+ }));
60
+
61
+ /**
62
+ * Toast
63
+ * Notificación efímera. Normalmente no se usa directo: `ToastProvider` +
64
+ * `useToast()` la montan y la descartan por vos. Se expone igual por si
65
+ * querés colocarla vos mismo dentro de una pantalla concreta.
66
+ */
67
+ export const Toast = memo(
68
+ ({ title, message, variant = 'info', onPress, onDismiss, hideCloseButton, style, testID }: ToastProps) => {
69
+ const colors = useColors();
70
+ const styles = useStyles();
71
+ const allowFontScaling = useAllowFontScaling();
72
+
73
+ const accentColor: Record<ToastVariant, string> = {
74
+ success: colors.success,
75
+ error: colors.error,
76
+ warning: colors.warning,
77
+ info: colors.secondary,
78
+ };
79
+
80
+ const body = (
81
+ <View style={[styles.container, style]} testID={testID} accessibilityRole="alert">
82
+ <View style={[styles.accent, { backgroundColor: accentColor[variant] }]} />
83
+ <Icon name={ICON_BY_VARIANT[variant]} size={22} color={accentColor[variant]} />
84
+ <View style={styles.text}>
85
+ <Text allowFontScaling={allowFontScaling} style={styles.title}>
86
+ {title}
87
+ </Text>
88
+ {!!message && (
89
+ <Text allowFontScaling={allowFontScaling} style={styles.message}>
90
+ {message}
91
+ </Text>
92
+ )}
93
+ </View>
94
+ {!hideCloseButton && !!onDismiss && (
95
+ <Pressable onPress={onDismiss} accessibilityRole="button" accessibilityLabel="Dismiss" hitSlop={8}>
96
+ <Icon name="close" size={18} color={colors.textLight} />
97
+ </Pressable>
98
+ )}
99
+ </View>
100
+ );
101
+
102
+ if (!onPress) return body;
103
+
104
+ return (
105
+ <Pressable onPress={onPress} accessibilityRole="button" accessibilityLabel={title}>
106
+ {body}
107
+ </Pressable>
108
+ );
109
+ }
110
+ );
111
+ Toast.displayName = 'Toast';
112
+
113
+ /** Envuelve un Toast con su animación de entrada/salida. Uso interno del provider. */
114
+ export const AnimatedToast = memo(
115
+ ({ children, onHeight }: { children: React.ReactNode; onHeight?: (h: number) => void }) => {
116
+ const progress = useRef(new Animated.Value(0)).current;
117
+
118
+ useEffect(() => {
119
+ Animated.spring(progress, { toValue: 1, useNativeDriver: true, speed: 14, bounciness: 4 }).start();
120
+ }, [progress]);
121
+
122
+ return (
123
+ <Animated.View
124
+ onLayout={(e) => onHeight?.(e.nativeEvent.layout.height)}
125
+ style={{
126
+ opacity: progress,
127
+ transform: [{ translateY: progress.interpolate({ inputRange: [0, 1], outputRange: [-16, 0] }) }],
128
+ }}
129
+ >
130
+ {children}
131
+ </Animated.View>
132
+ );
133
+ }
134
+ );
135
+ AnimatedToast.displayName = 'AnimatedToast';
@@ -0,0 +1,166 @@
1
+ import React, { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react';
2
+ import { View } from 'react-native';
3
+ import { AnimatedToast, Toast, type ToastVariant } from './Toast';
4
+ import { useInsets } from '../theme/UIProvider';
5
+ import { createStyles } from '../theme/createStyles';
6
+
7
+ export interface ToastOptions {
8
+ title: string;
9
+ message?: string;
10
+ variant?: ToastVariant;
11
+ /** ms antes de auto-descartar. 0 = no se descarta solo. @default 4000 */
12
+ duration?: number;
13
+ onPress?: () => void;
14
+ hideCloseButton?: boolean;
15
+ }
16
+
17
+ interface ToastEntry extends ToastOptions {
18
+ id: string;
19
+ }
20
+
21
+ interface ToastContextValue {
22
+ /** Muestra un toast y devuelve su id (por si querés cerrarlo antes de tiempo). */
23
+ show: (options: ToastOptions) => string;
24
+ hide: (id: string) => void;
25
+ hideAll: () => void;
26
+ }
27
+
28
+ const ToastContext = createContext<ToastContextValue | undefined>(undefined);
29
+
30
+ const useStyles = createStyles((theme) => ({
31
+ host: { position: 'absolute', left: 0, right: 0, paddingHorizontal: theme.tokens.spacing.lg, gap: theme.tokens.spacing.sm },
32
+ }));
33
+
34
+ export interface ToastProviderProps {
35
+ children: React.ReactNode;
36
+ /** @default 'top' */
37
+ position?: 'top' | 'bottom';
38
+ /** Máximo de toasts simultáneos; los más viejos se descartan. @default 3 */
39
+ maxVisible?: number;
40
+ /** Duración por defecto en ms. @default 4000 */
41
+ defaultDuration?: number;
42
+ }
43
+
44
+ /**
45
+ * ToastProvider
46
+ * Monta la capa de toasts sobre el resto de la app. Colocar una vez cerca de
47
+ * la raíz, dentro de UIProvider. Los timers de auto-dismiss se limpian al
48
+ * desmontar y al cerrar cada toast, así que no quedan callbacks colgados.
49
+ */
50
+ export function ToastProvider({
51
+ children,
52
+ position = 'top',
53
+ maxVisible = 3,
54
+ defaultDuration = 4000,
55
+ }: ToastProviderProps) {
56
+ const [toasts, setToasts] = useState<ToastEntry[]>([]);
57
+ const timers = useRef(new Map<string, ReturnType<typeof setTimeout>>());
58
+ const counter = useRef(0);
59
+ const insets = useInsets();
60
+ const styles = useStyles();
61
+
62
+ const clearTimer = useCallback((id: string) => {
63
+ const t = timers.current.get(id);
64
+ if (t) {
65
+ clearTimeout(t);
66
+ timers.current.delete(id);
67
+ }
68
+ }, []);
69
+
70
+ const hide = useCallback(
71
+ (id: string) => {
72
+ clearTimer(id);
73
+ setToasts((prev) => prev.filter((t) => t.id !== id));
74
+ },
75
+ [clearTimer]
76
+ );
77
+
78
+ const hideAll = useCallback(() => {
79
+ timers.current.forEach((t) => clearTimeout(t));
80
+ timers.current.clear();
81
+ setToasts([]);
82
+ }, []);
83
+
84
+ const show = useCallback(
85
+ (options: ToastOptions) => {
86
+ counter.current += 1;
87
+ const id = `toast-${counter.current}`;
88
+ const duration = options.duration ?? defaultDuration;
89
+
90
+ setToasts((prev) => {
91
+ const next = [...prev, { ...options, id }];
92
+ // Se descartan los más viejos, no los recién llegados: un toast nuevo
93
+ // siempre se ve.
94
+ const overflow = next.length - maxVisible;
95
+ if (overflow > 0) {
96
+ next.slice(0, overflow).forEach((t) => clearTimer(t.id));
97
+ return next.slice(overflow);
98
+ }
99
+ return next;
100
+ });
101
+
102
+ if (duration > 0) {
103
+ timers.current.set(
104
+ id,
105
+ setTimeout(() => hide(id), duration)
106
+ );
107
+ }
108
+ return id;
109
+ },
110
+ [defaultDuration, maxVisible, hide, clearTimer]
111
+ );
112
+
113
+ // Al desmontar el provider no debe quedar ningún setTimeout apuntando a un
114
+ // setState de un árbol ya desmontado.
115
+ useEffect(() => {
116
+ const pending = timers.current;
117
+ return () => {
118
+ pending.forEach((t) => clearTimeout(t));
119
+ pending.clear();
120
+ };
121
+ }, []);
122
+
123
+ const value = useMemo<ToastContextValue>(() => ({ show, hide, hideAll }), [show, hide, hideAll]);
124
+
125
+ const hostPosition =
126
+ position === 'top'
127
+ ? { top: insets.top + 8 }
128
+ : { bottom: insets.bottom + 8, flexDirection: 'column-reverse' as const };
129
+
130
+ return (
131
+ <ToastContext.Provider value={value}>
132
+ {children}
133
+ {toasts.length > 0 && (
134
+ <View style={[styles.host, hostPosition]} pointerEvents="box-none" testID="toast-host">
135
+ {toasts.map((t) => (
136
+ <AnimatedToast key={t.id}>
137
+ <Toast
138
+ title={t.title}
139
+ message={t.message}
140
+ variant={t.variant}
141
+ hideCloseButton={t.hideCloseButton}
142
+ onDismiss={() => hide(t.id)}
143
+ onPress={
144
+ t.onPress
145
+ ? () => {
146
+ t.onPress?.();
147
+ hide(t.id);
148
+ }
149
+ : undefined
150
+ }
151
+ />
152
+ </AnimatedToast>
153
+ ))}
154
+ </View>
155
+ )}
156
+ </ToastContext.Provider>
157
+ );
158
+ }
159
+
160
+ export function useToast(): ToastContextValue {
161
+ const ctx = useContext(ToastContext);
162
+ if (!ctx) {
163
+ throw new Error('orn-ui: useToast must be used within a <ToastProvider>.');
164
+ }
165
+ return ctx;
166
+ }
@@ -0,0 +1,137 @@
1
+ import React, { useCallback, useMemo, useState } from 'react';
2
+ import { ScrollView, View, type StyleProp, type ViewStyle } from 'react-native';
3
+ import { createStyles } from '../theme/createStyles';
4
+ import { Steps, type StepItem } from '../molecules/Steps';
5
+ import { Button } from '../atoms/Button';
6
+
7
+ export interface WizardStep extends StepItem {
8
+ content: React.ReactNode;
9
+ /** false bloquea el avance desde este paso (validación). @default true */
10
+ canGoNext?: boolean;
11
+ }
12
+
13
+ export interface WizardProps {
14
+ steps: WizardStep[];
15
+ /** Paso actual (controlado). Si se omite, el Wizard maneja su propio estado. */
16
+ current?: number;
17
+ onStepChange?: (index: number) => void;
18
+ onFinish?: () => void;
19
+ /** @default 'horizontal' */
20
+ orientation?: 'horizontal' | 'vertical';
21
+ completedIndicator?: 'check' | 'number';
22
+ /** Permite volver a un paso ya completado tocándolo en el indicador. @default true */
23
+ allowStepNavigation?: boolean;
24
+ backLabel?: string;
25
+ nextLabel?: string;
26
+ finishLabel?: string;
27
+ /**
28
+ * El contenido del paso scrollea cuando no entra. Ponelo en false sólo si
29
+ * un paso renderiza su propia lista virtualizada (anidar una
30
+ * VirtualizedList dentro de un ScrollView rompe la virtualización).
31
+ * @default true
32
+ */
33
+ scrollableContent?: boolean;
34
+ style?: StyleProp<ViewStyle>;
35
+ }
36
+
37
+ const useStyles = createStyles((theme) => ({
38
+ container: { flex: 1, gap: theme.tokens.spacing.xl },
39
+ content: { flex: 1 },
40
+ contentInner: { flexGrow: 1, paddingBottom: theme.tokens.spacing.sm },
41
+ footer: { flexDirection: 'row', gap: theme.tokens.spacing.md },
42
+ footerButton: { flex: 1 },
43
+ }));
44
+
45
+ /**
46
+ * Wizard
47
+ * Flujo multi-paso: indicador (Steps) + contenido del paso actual + barra de
48
+ * navegación. Funciona controlado (`current` + `onStepChange`) o por su
49
+ * cuenta. `canGoNext: false` en un paso deshabilita el avance, que es donde
50
+ * se engancha la validación del formulario de ese paso.
51
+ */
52
+ export function Wizard({
53
+ steps,
54
+ current: controlledCurrent,
55
+ onStepChange,
56
+ onFinish,
57
+ orientation = 'horizontal',
58
+ completedIndicator = 'check',
59
+ allowStepNavigation = true,
60
+ backLabel = 'Back',
61
+ nextLabel = 'Next',
62
+ finishLabel = 'Finish',
63
+ scrollableContent = true,
64
+ style,
65
+ }: WizardProps) {
66
+ const styles = useStyles();
67
+ const [uncontrolled, setUncontrolled] = useState(0);
68
+ const isControlled = controlledCurrent !== undefined;
69
+ const current = isControlled ? controlledCurrent : uncontrolled;
70
+
71
+ const goTo = useCallback(
72
+ (index: number) => {
73
+ const clamped = Math.max(0, Math.min(index, steps.length - 1));
74
+ if (!isControlled) setUncontrolled(clamped);
75
+ onStepChange?.(clamped);
76
+ },
77
+ [isControlled, onStepChange, steps.length]
78
+ );
79
+
80
+ const stepItems = useMemo(
81
+ () => steps.map(({ label, description }) => ({ label, description })),
82
+ [steps]
83
+ );
84
+
85
+ const activeStep = steps[current];
86
+ const isFirst = current === 0;
87
+ const isLast = current === steps.length - 1;
88
+ const canGoNext = activeStep?.canGoNext !== false;
89
+
90
+ const handleNext = () => {
91
+ if (isLast) onFinish?.();
92
+ else goTo(current + 1);
93
+ };
94
+
95
+ return (
96
+ <View style={[styles.container, style]}>
97
+ <Steps
98
+ steps={stepItems}
99
+ current={current}
100
+ orientation={orientation}
101
+ completedIndicator={completedIndicator}
102
+ // Sólo se puede volver atrás tocando el indicador; saltar hacia
103
+ // adelante se saltearía la validación de los pasos intermedios.
104
+ onStepPress={allowStepNavigation ? (index) => index < current && goTo(index) : undefined}
105
+ />
106
+
107
+ {/* El contenido scrollea en vez de desbordar: con el teclado abierto un
108
+ form largo se comía el footer y los botones quedaban pintados encima
109
+ de los campos. */}
110
+ {scrollableContent ? (
111
+ <ScrollView
112
+ style={styles.content}
113
+ contentContainerStyle={styles.contentInner}
114
+ keyboardShouldPersistTaps="handled"
115
+ automaticallyAdjustKeyboardInsets
116
+ showsVerticalScrollIndicator={false}
117
+ >
118
+ {activeStep?.content}
119
+ </ScrollView>
120
+ ) : (
121
+ <View style={styles.content}>{activeStep?.content}</View>
122
+ )}
123
+
124
+ <View style={styles.footer}>
125
+ {!isFirst && (
126
+ <Button title={backLabel} variant="outline" onPress={() => goTo(current - 1)} style={styles.footerButton} />
127
+ )}
128
+ <Button
129
+ title={isLast ? finishLabel : nextLabel}
130
+ onPress={handleNext}
131
+ disabled={!canGoNext}
132
+ style={styles.footerButton}
133
+ />
134
+ </View>
135
+ </View>
136
+ );
137
+ }
@@ -0,0 +1,26 @@
1
+ export { Modal } from './Modal';
2
+ export type { ModalProps, ModalVariant } from './Modal';
3
+ export { BottomSheet } from './BottomSheet';
4
+ export type { BottomSheetProps } from './BottomSheet';
5
+ export { Select } from './Select';
6
+ export type { SelectProps, SelectOption } from './Select';
7
+ export { Alert } from './Alert';
8
+ export type { AlertProps, AlertButton, AlertType } from './Alert';
9
+ export { AlertProvider, useAlert } from './AlertProvider';
10
+ export type { AlertOptions, ConfirmOptions } from './AlertProvider';
11
+ export { Screen } from './Screen';
12
+ export type { ScreenProps, ScreenEdge } from './Screen';
13
+ export { List } from './List';
14
+ export type { ListProps } from './List';
15
+ export { SearchList } from './SearchList';
16
+ export type { SearchListProps } from './SearchList';
17
+ export { Toast } from './Toast';
18
+ export type { ToastProps, ToastVariant } from './Toast';
19
+ export { ToastProvider, useToast } from './ToastProvider';
20
+ export type { ToastProviderProps, ToastOptions } from './ToastProvider';
21
+ export { DatePicker, buildMonthGrid, DEFAULT_MONTH_NAMES, DEFAULT_WEEKDAY_NAMES } from './DatePicker';
22
+ export type { DatePickerProps, DateRange } from './DatePicker';
23
+ export { DateField } from './DateField';
24
+ export type { DateFieldProps } from './DateField';
25
+ export { Wizard } from './Wizard';
26
+ export type { WizardProps, WizardStep } from './Wizard';
@@ -0,0 +1,129 @@
1
+ import React, { createContext, useContext, useMemo, useState, useCallback } from 'react';
2
+ import { Appearance, useColorScheme } from 'react-native';
3
+ import { defaultTheme } from './createTheme';
4
+ import { defaultLabels, type Labels } from './labels';
5
+ import { zeroInsets, type EdgeInsets } from './insets';
6
+ import type { Theme, ThemePair } from './types';
7
+ import { renderDefaultIcon } from '../icons/defaultIcons';
8
+ import type { IconRenderer } from '../icons/types';
9
+
10
+ export type ThemeMode = 'system' | 'light' | 'dark';
11
+
12
+ interface UIContextValue {
13
+ theme: Theme;
14
+ mode: ThemeMode;
15
+ setMode: (mode: ThemeMode) => void;
16
+ icons: IconRenderer;
17
+ insets: EdgeInsets;
18
+ labels: Labels;
19
+ allowFontScaling: boolean;
20
+ }
21
+
22
+ const UIContext = createContext<UIContextValue | undefined>(undefined);
23
+
24
+ export interface UIProviderProps {
25
+ children: React.ReactNode;
26
+ /** Par light/dark. Por defecto el theme de marca de la librería (créalo con createTheme()). */
27
+ theme?: ThemePair;
28
+ /** Modo inicial (no controlado) si no se pasa `mode`. @default 'system' */
29
+ defaultMode?: ThemeMode;
30
+ /** Modo controlado. Si se pasa junto a onModeChange, el provider no gestiona estado propio. */
31
+ mode?: ThemeMode;
32
+ onModeChange?: (mode: ThemeMode) => void;
33
+ /** Renderer de íconos. Por defecto usa los glifos zero-dep incluidos. */
34
+ icons?: IconRenderer;
35
+ /** Insets de safe area (de useSafeAreaInsets()). Por defecto {0,0,0,0}. */
36
+ insets?: EdgeInsets;
37
+ labels?: Partial<Labels>;
38
+ /** @default false, para paridad con el resto de la librería (accesibilidad primero). */
39
+ allowFontScaling?: boolean;
40
+ }
41
+
42
+ /**
43
+ * UIProvider
44
+ * Único punto de entrada de la librería: resuelve el theme activo (sistema u
45
+ * override manual), e inyecta íconos, insets y textos por defecto a todo el
46
+ * árbol. No persiste el modo — si el consumidor quiere recordar la preferencia
47
+ * entre sesiones, controla `mode`/`onModeChange` y persiste donde prefiera.
48
+ */
49
+ export function UIProvider({
50
+ children,
51
+ theme = defaultTheme,
52
+ defaultMode = 'system',
53
+ mode: controlledMode,
54
+ onModeChange,
55
+ icons = renderDefaultIcon,
56
+ insets = zeroInsets,
57
+ labels: labelOverrides,
58
+ allowFontScaling = false,
59
+ }: UIProviderProps) {
60
+ const systemScheme = useColorScheme();
61
+ const [uncontrolledMode, setUncontrolledMode] = useState<ThemeMode>(defaultMode);
62
+ const isControlled = controlledMode !== undefined;
63
+ const mode = isControlled ? controlledMode : uncontrolledMode;
64
+
65
+ const setMode = useCallback(
66
+ (next: ThemeMode) => {
67
+ if (!isControlled) setUncontrolledMode(next);
68
+ onModeChange?.(next);
69
+ },
70
+ [isControlled, onModeChange]
71
+ );
72
+
73
+ const scheme: 'light' | 'dark' =
74
+ mode === 'system' ? (systemScheme === 'dark' ? 'dark' : 'light') : mode;
75
+
76
+ const resolvedTheme = theme[scheme];
77
+
78
+ const labels = useMemo(
79
+ () => (labelOverrides ? { ...defaultLabels, ...labelOverrides } : defaultLabels),
80
+ [labelOverrides]
81
+ );
82
+
83
+ const value = useMemo<UIContextValue>(
84
+ () => ({ theme: resolvedTheme, mode, setMode, icons, insets, labels, allowFontScaling }),
85
+ [resolvedTheme, mode, setMode, icons, insets, labels, allowFontScaling]
86
+ );
87
+
88
+ return <UIContext.Provider value={value}>{children}</UIContext.Provider>;
89
+ }
90
+
91
+ function useUIContext(): UIContextValue {
92
+ const ctx = useContext(UIContext);
93
+ if (!ctx) {
94
+ throw new Error('orn-ui: this hook must be used within a <UIProvider>.');
95
+ }
96
+ return ctx;
97
+ }
98
+
99
+ export function useTheme(): Theme {
100
+ return useUIContext().theme;
101
+ }
102
+
103
+ export function useColors(): Theme['colors'] {
104
+ return useUIContext().theme.colors;
105
+ }
106
+
107
+ export function useThemeMode() {
108
+ const { mode, setMode } = useUIContext();
109
+ return { mode, setMode };
110
+ }
111
+
112
+ export function useIcons(): IconRenderer {
113
+ return useUIContext().icons;
114
+ }
115
+
116
+ export function useInsets(): EdgeInsets {
117
+ return useUIContext().insets;
118
+ }
119
+
120
+ export function useLabels(): Labels {
121
+ return useUIContext().labels;
122
+ }
123
+
124
+ export function useAllowFontScaling(): boolean {
125
+ return useUIContext().allowFontScaling;
126
+ }
127
+
128
+ /** Re-exportado para que tests/consumidores puedan simular cambios de sistema sin RN real. */
129
+ export const __testUtils = { Appearance };
@@ -0,0 +1,40 @@
1
+ import { StyleSheet } from 'react-native';
2
+ import { useTheme } from './UIProvider';
3
+ import type { Theme } from './types';
4
+
5
+ /**
6
+ * createStyles(factory)
7
+ * Devuelve un hook useStyles() que memoiza el StyleSheet resultante por
8
+ * theme (WeakMap<Theme, Styles>), no por instancia de componente. Todas las
9
+ * filas de una lista comparten el mismo objeto de estilos: sin esto, N filas
10
+ * ⇒ N StyleSheet.create() por render, que es lo que hacía cada componente
11
+ * de la app original con `useMemo(() => createStyles(colors), [colors])`.
12
+ *
13
+ * La cache vive en el closure del factory (una WeakMap por llamada a
14
+ * createStyles, es decir, una por componente), así que temas de distintos
15
+ * <UIProvider> en el mismo árbol no colisionan entre sí.
16
+ *
17
+ * El tipo de `factory` sigue el patrón oficial de RN para StyleSheet
18
+ * tipados (`T extends StyleSheet.NamedStyles<T> | StyleSheet.NamedStyles<any>`):
19
+ * es lo que le da contexto a TS para inferir literales ('row', 'center', ...)
20
+ * en vez de ensancharlos a `string` dentro del objeto devuelto.
21
+ */
22
+ export function createStyles<T extends StyleSheet.NamedStyles<T> | StyleSheet.NamedStyles<any>>(
23
+ factory: (theme: Theme) => T | StyleSheet.NamedStyles<T>
24
+ ): () => T {
25
+ const cache = new WeakMap<Theme, T>();
26
+
27
+ return function useStyles(): T {
28
+ const theme = useTheme();
29
+ let styles = cache.get(theme);
30
+ if (!styles) {
31
+ // El cast es seguro: StyleSheet.create<T>(...) con el mismo T del
32
+ // factory siempre devuelve T; TS no puede probarlo por sí solo dentro
33
+ // de un cuerpo genérico (T podría "instanciarse" con un subtipo no
34
+ // relacionado desde su punto de vista, aunque en runtime es el mismo).
35
+ styles = StyleSheet.create(factory(theme)) as T;
36
+ cache.set(theme, styles);
37
+ }
38
+ return styles;
39
+ };
40
+ }
@@ -0,0 +1,49 @@
1
+ import { lightPalette, darkPalette } from './palettes';
2
+ import { tokens as defaultTokens } from './tokens';
3
+ import type { Theme, ThemeOverrides, ThemePair } from './types';
4
+
5
+ function mergeDeep<T extends Record<string, any>>(base: T, patch?: Partial<T>): T {
6
+ if (!patch) return base;
7
+ const out: Record<string, any> = { ...base };
8
+ for (const key of Object.keys(patch)) {
9
+ const patchVal = (patch as Record<string, any>)[key];
10
+ const baseVal = (base as Record<string, any>)[key];
11
+ if (
12
+ patchVal &&
13
+ baseVal &&
14
+ typeof patchVal === 'object' &&
15
+ typeof baseVal === 'object' &&
16
+ !Array.isArray(patchVal)
17
+ ) {
18
+ out[key] = mergeDeep(baseVal, patchVal);
19
+ } else if (patchVal !== undefined) {
20
+ out[key] = patchVal;
21
+ }
22
+ }
23
+ return out as T;
24
+ }
25
+
26
+ /**
27
+ * Construye el par de themes light/dark que consume <UIProvider theme={...}>.
28
+ * Nunca muta los defaults: cada llamada produce objetos nuevos, seguros de
29
+ * compartir entre providers distintos (tests, multi-brand en el mismo árbol).
30
+ */
31
+ export function createTheme(overrides?: ThemeOverrides): ThemePair {
32
+ const tokens = mergeDeep(defaultTokens, overrides?.tokens as any);
33
+
34
+ const light: Theme = {
35
+ colors: mergeDeep(lightPalette, overrides?.colors?.light),
36
+ tokens,
37
+ scheme: 'light',
38
+ };
39
+
40
+ const dark: Theme = {
41
+ colors: mergeDeep(darkPalette, overrides?.colors?.dark),
42
+ tokens,
43
+ scheme: 'dark',
44
+ };
45
+
46
+ return { light, dark };
47
+ }
48
+
49
+ export const defaultTheme: ThemePair = createTheme();