@studio-piot/native-ui 0.2.5 → 0.17.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 (338) hide show
  1. package/README.md +22 -18
  2. package/dist/components/Button/Button.d.ts +20 -4
  3. package/dist/components/Button/Button.d.ts.map +1 -1
  4. package/dist/components/Button/Button.js +34 -21
  5. package/dist/components/Button/Button.js.map +1 -1
  6. package/dist/components/Button/buttonAppearance.d.ts +20 -0
  7. package/dist/components/Button/buttonAppearance.d.ts.map +1 -0
  8. package/dist/components/Button/buttonAppearance.js +28 -0
  9. package/dist/components/Button/buttonAppearance.js.map +1 -0
  10. package/dist/components/Button/buttonThemeRoles.d.ts +6 -0
  11. package/dist/components/Button/buttonThemeRoles.d.ts.map +1 -0
  12. package/dist/components/Button/buttonThemeRoles.js +8 -0
  13. package/dist/components/Button/buttonThemeRoles.js.map +1 -0
  14. package/dist/components/Button/index.d.ts +3 -1
  15. package/dist/components/Button/index.d.ts.map +1 -1
  16. package/dist/components/Button/index.js +4 -1
  17. package/dist/components/Button/index.js.map +1 -1
  18. package/dist/components/Choice/Choice.d.ts +10 -0
  19. package/dist/components/Choice/Choice.d.ts.map +1 -0
  20. package/dist/components/Choice/Choice.js +66 -0
  21. package/dist/components/Choice/Choice.js.map +1 -0
  22. package/dist/components/Choice/choiceLayout.d.ts +23 -0
  23. package/dist/components/Choice/choiceLayout.d.ts.map +1 -0
  24. package/dist/components/Choice/choiceLayout.js +32 -0
  25. package/dist/components/Choice/choiceLayout.js.map +1 -0
  26. package/dist/components/Choice/choiceThemeRoles.d.ts +9 -0
  27. package/dist/components/Choice/choiceThemeRoles.d.ts.map +1 -0
  28. package/dist/components/Choice/choiceThemeRoles.js +11 -0
  29. package/dist/components/Choice/choiceThemeRoles.js.map +1 -0
  30. package/dist/components/Choice/index.d.ts +5 -0
  31. package/dist/components/Choice/index.d.ts.map +1 -0
  32. package/dist/components/Choice/index.js +10 -0
  33. package/dist/components/Choice/index.js.map +1 -0
  34. package/dist/components/Choice/types.d.ts +33 -0
  35. package/dist/components/Choice/types.d.ts.map +1 -0
  36. package/dist/components/Choice/types.js +3 -0
  37. package/dist/components/Choice/types.js.map +1 -0
  38. package/dist/components/ErrorBanner/ErrorBanner.d.ts +1 -1
  39. package/dist/components/ErrorBanner/ErrorBanner.d.ts.map +1 -1
  40. package/dist/components/ErrorBanner/ErrorBanner.js +19 -19
  41. package/dist/components/ErrorBanner/ErrorBanner.js.map +1 -1
  42. package/dist/components/ErrorBanner/errorBannerContext.d.ts +23 -3
  43. package/dist/components/ErrorBanner/errorBannerContext.d.ts.map +1 -1
  44. package/dist/components/ErrorBanner/errorBannerContext.js +9 -5
  45. package/dist/components/ErrorBanner/errorBannerContext.js.map +1 -1
  46. package/dist/components/ErrorBanner/errorBannerLayout.d.ts +18 -0
  47. package/dist/components/ErrorBanner/errorBannerLayout.d.ts.map +1 -0
  48. package/dist/components/ErrorBanner/errorBannerLayout.js +54 -0
  49. package/dist/components/ErrorBanner/errorBannerLayout.js.map +1 -0
  50. package/dist/components/ErrorBanner/icons.d.ts +7 -5
  51. package/dist/components/ErrorBanner/icons.d.ts.map +1 -1
  52. package/dist/components/ErrorBanner/icons.js +8 -4
  53. package/dist/components/ErrorBanner/icons.js.map +1 -1
  54. package/dist/components/ErrorBanner/index.d.ts +1 -1
  55. package/dist/components/ErrorBanner/index.d.ts.map +1 -1
  56. package/dist/components/ErrorBanner/index.js.map +1 -1
  57. package/dist/components/Form/Form.d.ts +9 -1
  58. package/dist/components/Form/Form.d.ts.map +1 -1
  59. package/dist/components/Form/Form.js +56 -78
  60. package/dist/components/Form/Form.js.map +1 -1
  61. package/dist/components/Form/FormCloseButton.d.ts +3 -1
  62. package/dist/components/Form/FormCloseButton.d.ts.map +1 -1
  63. package/dist/components/Form/FormCloseButton.js +5 -4
  64. package/dist/components/Form/FormCloseButton.js.map +1 -1
  65. package/dist/components/Form/FormErrorBanner.d.ts +15 -0
  66. package/dist/components/Form/FormErrorBanner.d.ts.map +1 -0
  67. package/dist/components/Form/FormErrorBanner.js +30 -0
  68. package/dist/components/Form/FormErrorBanner.js.map +1 -0
  69. package/dist/components/Form/FormSubmitFooter.d.ts +17 -0
  70. package/dist/components/Form/FormSubmitFooter.d.ts.map +1 -0
  71. package/dist/components/Form/FormSubmitFooter.js +32 -0
  72. package/dist/components/Form/FormSubmitFooter.js.map +1 -0
  73. package/dist/components/Form/formThemeRoles.d.ts +7 -0
  74. package/dist/components/Form/formThemeRoles.d.ts.map +1 -0
  75. package/dist/components/Form/formThemeRoles.js +9 -0
  76. package/dist/components/Form/formThemeRoles.js.map +1 -0
  77. package/dist/components/Form/index.d.ts +1 -0
  78. package/dist/components/Form/index.d.ts.map +1 -1
  79. package/dist/components/Form/index.js +3 -1
  80. package/dist/components/Form/index.js.map +1 -1
  81. package/dist/components/Form/types.d.ts +14 -10
  82. package/dist/components/Form/types.d.ts.map +1 -1
  83. package/dist/components/FormSheet/FormSheet.d.ts +10 -0
  84. package/dist/components/FormSheet/FormSheet.d.ts.map +1 -0
  85. package/dist/components/FormSheet/FormSheet.js +153 -0
  86. package/dist/components/FormSheet/FormSheet.js.map +1 -0
  87. package/dist/components/FormSheet/formSheetLayout.d.ts +32 -0
  88. package/dist/components/FormSheet/formSheetLayout.d.ts.map +1 -0
  89. package/dist/components/FormSheet/formSheetLayout.js +43 -0
  90. package/dist/components/FormSheet/formSheetLayout.js.map +1 -0
  91. package/dist/components/FormSheet/formSheetThemeRoles.d.ts +8 -0
  92. package/dist/components/FormSheet/formSheetThemeRoles.d.ts.map +1 -0
  93. package/dist/components/FormSheet/formSheetThemeRoles.js +10 -0
  94. package/dist/components/FormSheet/formSheetThemeRoles.js.map +1 -0
  95. package/dist/components/FormSheet/index.d.ts +5 -0
  96. package/dist/components/FormSheet/index.d.ts.map +1 -0
  97. package/dist/components/FormSheet/index.js +10 -0
  98. package/dist/components/FormSheet/index.js.map +1 -0
  99. package/dist/components/FormSheet/types.d.ts +64 -0
  100. package/dist/components/FormSheet/types.d.ts.map +1 -0
  101. package/dist/components/FormSheet/types.js +3 -0
  102. package/dist/components/FormSheet/types.js.map +1 -0
  103. package/dist/components/HeaderButton/HeaderCapsule.d.ts +4 -0
  104. package/dist/components/HeaderButton/HeaderCapsule.d.ts.map +1 -0
  105. package/dist/components/HeaderButton/HeaderCapsule.js +32 -0
  106. package/dist/components/HeaderButton/HeaderCapsule.js.map +1 -0
  107. package/dist/components/HeaderButton/HeaderGroup.d.ts +4 -0
  108. package/dist/components/HeaderButton/HeaderGroup.d.ts.map +1 -0
  109. package/dist/components/HeaderButton/HeaderGroup.js +23 -0
  110. package/dist/components/HeaderButton/HeaderGroup.js.map +1 -0
  111. package/dist/components/HeaderButton/HeaderIcon.d.ts +4 -0
  112. package/dist/components/HeaderButton/HeaderIcon.d.ts.map +1 -0
  113. package/dist/components/HeaderButton/HeaderIcon.js +28 -0
  114. package/dist/components/HeaderButton/HeaderIcon.js.map +1 -0
  115. package/dist/components/HeaderButton/headerButtonLayout.d.ts +21 -0
  116. package/dist/components/HeaderButton/headerButtonLayout.d.ts.map +1 -0
  117. package/dist/components/HeaderButton/headerButtonLayout.js +34 -0
  118. package/dist/components/HeaderButton/headerButtonLayout.js.map +1 -0
  119. package/dist/components/HeaderButton/headerButtonThemeRoles.d.ts +5 -0
  120. package/dist/components/HeaderButton/headerButtonThemeRoles.d.ts.map +1 -0
  121. package/dist/components/HeaderButton/headerButtonThemeRoles.js +7 -0
  122. package/dist/components/HeaderButton/headerButtonThemeRoles.js.map +1 -0
  123. package/dist/components/HeaderButton/index.d.ts +7 -0
  124. package/dist/components/HeaderButton/index.d.ts.map +1 -0
  125. package/dist/components/HeaderButton/index.js +14 -0
  126. package/dist/components/HeaderButton/index.js.map +1 -0
  127. package/dist/components/HeaderButton/types.d.ts +40 -0
  128. package/dist/components/HeaderButton/types.d.ts.map +1 -0
  129. package/dist/components/HeaderButton/types.js +3 -0
  130. package/dist/components/HeaderButton/types.js.map +1 -0
  131. package/dist/components/HeaderWash/HeaderWash.d.ts +10 -0
  132. package/dist/components/HeaderWash/HeaderWash.d.ts.map +1 -0
  133. package/dist/components/HeaderWash/HeaderWash.js +19 -0
  134. package/dist/components/HeaderWash/HeaderWash.js.map +1 -0
  135. package/dist/components/HeaderWash/headerWashGradient.d.ts +8 -0
  136. package/dist/components/HeaderWash/headerWashGradient.d.ts.map +1 -0
  137. package/dist/components/HeaderWash/headerWashGradient.js +21 -0
  138. package/dist/components/HeaderWash/headerWashGradient.js.map +1 -0
  139. package/dist/components/HeaderWash/index.d.ts +2 -0
  140. package/dist/components/HeaderWash/index.d.ts.map +1 -0
  141. package/dist/components/HeaderWash/index.js +6 -0
  142. package/dist/components/HeaderWash/index.js.map +1 -0
  143. package/dist/components/Page/Page.d.ts +13 -0
  144. package/dist/components/Page/Page.d.ts.map +1 -0
  145. package/dist/components/Page/Page.js +49 -0
  146. package/dist/components/Page/Page.js.map +1 -0
  147. package/dist/components/Page/PageBleed.d.ts +9 -0
  148. package/dist/components/Page/PageBleed.d.ts.map +1 -0
  149. package/dist/components/Page/PageBleed.js +12 -0
  150. package/dist/components/Page/PageBleed.js.map +1 -0
  151. package/dist/components/Page/index.d.ts +5 -0
  152. package/dist/components/Page/index.d.ts.map +1 -0
  153. package/dist/components/Page/index.js +10 -0
  154. package/dist/components/Page/index.js.map +1 -0
  155. package/dist/components/Page/pageContext.d.ts +4 -0
  156. package/dist/components/Page/pageContext.d.ts.map +1 -0
  157. package/dist/components/Page/pageContext.js +11 -0
  158. package/dist/components/Page/pageContext.js.map +1 -0
  159. package/dist/components/Page/pageInsets.d.ts +26 -0
  160. package/dist/components/Page/pageInsets.d.ts.map +1 -0
  161. package/dist/components/Page/pageInsets.js +37 -0
  162. package/dist/components/Page/pageInsets.js.map +1 -0
  163. package/dist/components/Page/pageLayout.d.ts +5 -0
  164. package/dist/components/Page/pageLayout.d.ts.map +1 -0
  165. package/dist/components/Page/pageLayout.js +8 -0
  166. package/dist/components/Page/pageLayout.js.map +1 -0
  167. package/dist/components/Page/pageThemeRoles.d.ts +5 -0
  168. package/dist/components/Page/pageThemeRoles.d.ts.map +1 -0
  169. package/dist/components/Page/pageThemeRoles.js +7 -0
  170. package/dist/components/Page/pageThemeRoles.js.map +1 -0
  171. package/dist/components/Page/types.d.ts +22 -0
  172. package/dist/components/Page/types.d.ts.map +1 -0
  173. package/dist/components/Page/types.js +3 -0
  174. package/dist/components/Page/types.js.map +1 -0
  175. package/dist/components/Select/Select.d.ts +1 -1
  176. package/dist/components/Select/Select.d.ts.map +1 -1
  177. package/dist/components/Select/Select.js +8 -9
  178. package/dist/components/Select/Select.js.map +1 -1
  179. package/dist/components/Select/selectThemeRoles.d.ts +2 -0
  180. package/dist/components/Select/selectThemeRoles.d.ts.map +1 -1
  181. package/dist/components/Select/selectThemeRoles.js +2 -0
  182. package/dist/components/Select/selectThemeRoles.js.map +1 -1
  183. package/dist/components/Select/types.d.ts +4 -0
  184. package/dist/components/Select/types.d.ts.map +1 -1
  185. package/dist/components/TabAccessoryButton/TabAccessoryButton.d.ts +8 -0
  186. package/dist/components/TabAccessoryButton/TabAccessoryButton.d.ts.map +1 -0
  187. package/dist/components/TabAccessoryButton/TabAccessoryButton.js +77 -0
  188. package/dist/components/TabAccessoryButton/TabAccessoryButton.js.map +1 -0
  189. package/dist/components/TabAccessoryButton/index.d.ts +5 -0
  190. package/dist/components/TabAccessoryButton/index.d.ts.map +1 -0
  191. package/dist/components/TabAccessoryButton/index.js +11 -0
  192. package/dist/components/TabAccessoryButton/index.js.map +1 -0
  193. package/dist/components/TabAccessoryButton/tabAccessoryLayout.d.ts +27 -0
  194. package/dist/components/TabAccessoryButton/tabAccessoryLayout.d.ts.map +1 -0
  195. package/dist/components/TabAccessoryButton/tabAccessoryLayout.js +38 -0
  196. package/dist/components/TabAccessoryButton/tabAccessoryLayout.js.map +1 -0
  197. package/dist/components/TabAccessoryButton/tabAccessoryThemeRoles.d.ts +14 -0
  198. package/dist/components/TabAccessoryButton/tabAccessoryThemeRoles.d.ts.map +1 -0
  199. package/dist/components/TabAccessoryButton/tabAccessoryThemeRoles.js +19 -0
  200. package/dist/components/TabAccessoryButton/tabAccessoryThemeRoles.js.map +1 -0
  201. package/dist/components/TabAccessoryButton/types.d.ts +27 -0
  202. package/dist/components/TabAccessoryButton/types.d.ts.map +1 -0
  203. package/dist/components/TabAccessoryButton/types.js +3 -0
  204. package/dist/components/TabAccessoryButton/types.js.map +1 -0
  205. package/dist/components/index.d.ts +9 -3
  206. package/dist/components/index.d.ts.map +1 -1
  207. package/dist/components/index.js +30 -1
  208. package/dist/components/index.js.map +1 -1
  209. package/dist/index.d.ts +11 -4
  210. package/dist/index.d.ts.map +1 -1
  211. package/dist/index.js +34 -2
  212. package/dist/index.js.map +1 -1
  213. package/dist/layout/HeaderInsetProbe.d.ts +15 -0
  214. package/dist/layout/HeaderInsetProbe.d.ts.map +1 -0
  215. package/dist/layout/HeaderInsetProbe.js +28 -0
  216. package/dist/layout/HeaderInsetProbe.js.map +1 -0
  217. package/dist/layout/headerInset.d.ts +26 -0
  218. package/dist/layout/headerInset.d.ts.map +1 -0
  219. package/dist/layout/headerInset.js +29 -0
  220. package/dist/layout/headerInset.js.map +1 -0
  221. package/dist/layout/index.d.ts +1 -0
  222. package/dist/layout/index.d.ts.map +1 -1
  223. package/dist/layout/index.js +3 -1
  224. package/dist/layout/index.js.map +1 -1
  225. package/dist/layout/sheet.d.ts +3 -0
  226. package/dist/layout/sheet.d.ts.map +1 -0
  227. package/dist/layout/sheet.js +6 -0
  228. package/dist/layout/sheet.js.map +1 -0
  229. package/dist/theme/ThemeProvider.d.ts +4 -1
  230. package/dist/theme/ThemeProvider.d.ts.map +1 -1
  231. package/dist/theme/ThemeProvider.js +5 -2
  232. package/dist/theme/ThemeProvider.js.map +1 -1
  233. package/dist/theme/index.d.ts +2 -0
  234. package/dist/theme/index.d.ts.map +1 -1
  235. package/dist/theme/index.js +6 -1
  236. package/dist/theme/index.js.map +1 -1
  237. package/dist/theme/resolveThemeSpacing.d.ts +12 -0
  238. package/dist/theme/resolveThemeSpacing.d.ts.map +1 -0
  239. package/dist/theme/resolveThemeSpacing.js +14 -0
  240. package/dist/theme/resolveThemeSpacing.js.map +1 -0
  241. package/dist/theme/types.d.ts +2 -0
  242. package/dist/theme/types.d.ts.map +1 -1
  243. package/dist/theme/useThemeSpacing.d.ts +4 -0
  244. package/dist/theme/useThemeSpacing.d.ts.map +1 -0
  245. package/dist/theme/useThemeSpacing.js +11 -0
  246. package/dist/theme/useThemeSpacing.js.map +1 -0
  247. package/docs/components/button.md +92 -38
  248. package/docs/components/choice.md +111 -0
  249. package/docs/components/error-banner.md +46 -3
  250. package/docs/components/form-sheet.md +140 -0
  251. package/docs/components/form.md +80 -45
  252. package/docs/components/header-button.md +310 -0
  253. package/docs/components/header-wash.md +40 -0
  254. package/docs/components/page.md +183 -0
  255. package/docs/components/select.md +2 -2
  256. package/docs/components/tab-accessory-button.md +146 -0
  257. package/docs/specifications/Liquid Glass header buttons in Expo Router.md +327 -0
  258. package/package.json +1 -1
  259. package/src/components/Button/Button.tsx +82 -38
  260. package/src/components/Button/buttonAppearance.test.ts +44 -0
  261. package/src/components/Button/buttonAppearance.ts +38 -0
  262. package/src/components/Button/buttonThemeRoles.test.ts +10 -0
  263. package/src/components/Button/buttonThemeRoles.ts +6 -0
  264. package/src/components/Button/index.ts +12 -1
  265. package/src/components/Choice/Choice.tsx +120 -0
  266. package/src/components/Choice/choiceLayout.test.ts +49 -0
  267. package/src/components/Choice/choiceLayout.ts +34 -0
  268. package/src/components/Choice/choiceThemeRoles.test.ts +15 -0
  269. package/src/components/Choice/choiceThemeRoles.ts +9 -0
  270. package/src/components/Choice/index.ts +4 -0
  271. package/src/components/Choice/types.ts +33 -0
  272. package/src/components/ErrorBanner/ErrorBanner.tsx +32 -22
  273. package/src/components/ErrorBanner/errorBannerContext.tsx +31 -6
  274. package/src/components/ErrorBanner/errorBannerLayout.test.ts +49 -0
  275. package/src/components/ErrorBanner/errorBannerLayout.ts +59 -0
  276. package/src/components/ErrorBanner/icons.tsx +25 -6
  277. package/src/components/ErrorBanner/index.ts +1 -0
  278. package/src/components/Form/Form.tsx +77 -173
  279. package/src/components/Form/FormCloseButton.tsx +12 -4
  280. package/src/components/Form/FormErrorBanner.tsx +57 -0
  281. package/src/components/Form/FormSubmitFooter.tsx +70 -0
  282. package/src/components/Form/formThemeRoles.test.ts +13 -0
  283. package/src/components/Form/formThemeRoles.ts +7 -0
  284. package/src/components/Form/index.ts +1 -0
  285. package/src/components/Form/types.ts +14 -10
  286. package/src/components/FormSheet/FormSheet.tsx +279 -0
  287. package/src/components/FormSheet/formSheetLayout.test.ts +66 -0
  288. package/src/components/FormSheet/formSheetLayout.ts +55 -0
  289. package/src/components/FormSheet/formSheetThemeRoles.test.ts +14 -0
  290. package/src/components/FormSheet/formSheetThemeRoles.ts +8 -0
  291. package/src/components/FormSheet/index.ts +7 -0
  292. package/src/components/FormSheet/types.ts +66 -0
  293. package/src/components/HeaderButton/HeaderCapsule.tsx +63 -0
  294. package/src/components/HeaderButton/HeaderGroup.tsx +36 -0
  295. package/src/components/HeaderButton/HeaderIcon.tsx +56 -0
  296. package/src/components/HeaderButton/headerButtonLayout.test.ts +47 -0
  297. package/src/components/HeaderButton/headerButtonLayout.ts +36 -0
  298. package/src/components/HeaderButton/headerButtonThemeRoles.test.ts +9 -0
  299. package/src/components/HeaderButton/headerButtonThemeRoles.ts +5 -0
  300. package/src/components/HeaderButton/index.ts +14 -0
  301. package/src/components/HeaderButton/types.ts +40 -0
  302. package/src/components/HeaderWash/HeaderWash.tsx +20 -0
  303. package/src/components/HeaderWash/headerWashGradient.test.ts +22 -0
  304. package/src/components/{Form/withAlpha.ts → HeaderWash/headerWashGradient.ts} +9 -0
  305. package/src/components/HeaderWash/index.ts +1 -0
  306. package/src/components/Page/Page.tsx +80 -0
  307. package/src/components/Page/PageBleed.tsx +14 -0
  308. package/src/components/Page/index.ts +4 -0
  309. package/src/components/Page/pageContext.ts +8 -0
  310. package/src/components/Page/pageInsets.test.ts +73 -0
  311. package/src/components/Page/pageInsets.ts +52 -0
  312. package/src/components/Page/pageThemeRoles.test.ts +9 -0
  313. package/src/components/Page/pageThemeRoles.ts +5 -0
  314. package/src/components/Page/types.ts +26 -0
  315. package/src/components/Select/Select.tsx +11 -4
  316. package/src/components/Select/selectThemeRoles.test.ts +5 -0
  317. package/src/components/Select/selectThemeRoles.ts +2 -0
  318. package/src/components/Select/types.ts +4 -0
  319. package/src/components/TabAccessoryButton/TabAccessoryButton.tsx +150 -0
  320. package/src/components/TabAccessoryButton/index.ts +12 -0
  321. package/src/components/TabAccessoryButton/tabAccessoryLayout.test.ts +42 -0
  322. package/src/components/TabAccessoryButton/tabAccessoryLayout.ts +43 -0
  323. package/src/components/TabAccessoryButton/tabAccessoryThemeRoles.test.ts +28 -0
  324. package/src/components/TabAccessoryButton/tabAccessoryThemeRoles.ts +22 -0
  325. package/src/components/TabAccessoryButton/types.ts +27 -0
  326. package/src/components/index.ts +64 -2
  327. package/src/index.ts +61 -2
  328. package/src/layout/headerInset.test.ts +60 -0
  329. package/src/layout/headerInset.ts +41 -0
  330. package/src/layout/index.ts +1 -0
  331. package/src/layout/sheet.ts +2 -0
  332. package/src/theme/ThemeProvider.tsx +9 -1
  333. package/src/theme/index.ts +6 -0
  334. package/src/theme/resolveThemeSpacing.test.ts +29 -0
  335. package/src/theme/resolveThemeSpacing.ts +21 -0
  336. package/src/theme/types.ts +3 -0
  337. package/src/theme/useThemeSpacing.ts +8 -0
  338. package/src/components/Form/withAlpha.test.ts +0 -13
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DEFAULT_THEME_SPACING = void 0;
4
+ exports.resolveThemeSpacing = resolveThemeSpacing;
5
+ exports.DEFAULT_THEME_SPACING = {
6
+ screen: 16,
7
+ pageTop: 16,
8
+ pageBottom: 24,
9
+ };
10
+ /** App spacing over the package defaults. Pure so it can be unit-tested. */
11
+ function resolveThemeSpacing(spacing) {
12
+ return { ...exports.DEFAULT_THEME_SPACING, ...spacing };
13
+ }
14
+ //# sourceMappingURL=resolveThemeSpacing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolveThemeSpacing.js","sourceRoot":"","sources":["../../src/theme/resolveThemeSpacing.ts"],"names":[],"mappings":";;;AAgBA,kDAIC;AAXY,QAAA,qBAAqB,GAAiB;IACjD,MAAM,EAAE,EAAE;IACV,OAAO,EAAE,EAAE;IACX,UAAU,EAAE,EAAE;CACf,CAAC;AAEF,4EAA4E;AAC5E,SAAgB,mBAAmB,CACjC,OAA0C;IAE1C,OAAO,EAAE,GAAG,6BAAqB,EAAE,GAAG,OAAO,EAAE,CAAC;AAClD,CAAC"}
@@ -1,8 +1,10 @@
1
+ import type { ThemeSpacing } from "./resolveThemeSpacing";
1
2
  export type ThemeColors = Record<string, string>;
2
3
  export type ThemeContextValue = {
3
4
  colors: {
4
5
  light: ThemeColors;
5
6
  dark: ThemeColors;
6
7
  };
8
+ spacing: ThemeSpacing;
7
9
  };
8
10
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/theme/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAEjD,MAAM,MAAM,iBAAiB,GAAG;IAC9B,MAAM,EAAE;QAAE,KAAK,EAAE,WAAW,CAAC;QAAC,IAAI,EAAE,WAAW,CAAA;KAAE,CAAC;CACnD,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/theme/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAE1D,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAEjD,MAAM,MAAM,iBAAiB,GAAG;IAC9B,MAAM,EAAE;QAAE,KAAK,EAAE,WAAW,CAAC;QAAC,IAAI,EAAE,WAAW,CAAA;KAAE,CAAC;IAClD,OAAO,EAAE,YAAY,CAAC;CACvB,CAAC"}
@@ -0,0 +1,4 @@
1
+ import { type ThemeSpacing } from "./resolveThemeSpacing";
2
+ /** Spacing from `ThemeProvider`, or the package defaults outside one. */
3
+ export declare function useThemeSpacing(): ThemeSpacing;
4
+ //# sourceMappingURL=useThemeSpacing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useThemeSpacing.d.ts","sourceRoot":"","sources":["../../src/theme/useThemeSpacing.ts"],"names":[],"mappings":"AACA,OAAO,EAAyB,KAAK,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAGjF,yEAAyE;AACzE,wBAAgB,eAAe,IAAI,YAAY,CAE9C"}
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.useThemeSpacing = useThemeSpacing;
4
+ const react_1 = require("react");
5
+ const resolveThemeSpacing_1 = require("./resolveThemeSpacing");
6
+ const ThemeProvider_1 = require("./ThemeProvider");
7
+ /** Spacing from `ThemeProvider`, or the package defaults outside one. */
8
+ function useThemeSpacing() {
9
+ return (0, react_1.useContext)(ThemeProvider_1.ThemeContext)?.spacing ?? resolveThemeSpacing_1.DEFAULT_THEME_SPACING;
10
+ }
11
+ //# sourceMappingURL=useThemeSpacing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useThemeSpacing.js","sourceRoot":"","sources":["../../src/theme/useThemeSpacing.ts"],"names":[],"mappings":";;AAKA,0CAEC;AAPD,iCAAmC;AACnC,+DAAiF;AACjF,mDAA+C;AAE/C,yEAAyE;AACzE,SAAgB,eAAe;IAC7B,OAAO,IAAA,kBAAU,EAAC,4BAAY,CAAC,EAAE,OAAO,IAAI,2CAAqB,CAAC;AACpE,CAAC"}
@@ -29,16 +29,36 @@ export function Root({ children }: { children: React.ReactNode }) {
29
29
  // Example app colours — role names are yours
30
30
  export const Colors = {
31
31
  light: {
32
- buttonBackground: "#FF4F16",
33
- buttonLabel: "#22201D",
32
+ buttonBackground: "#2563EB",
33
+ buttonLabel: "#FFFFFF",
34
34
  },
35
35
  dark: {
36
- buttonBackground: "#FF4F16",
37
- buttonLabel: "#22201D",
36
+ buttonBackground: "#2563EB",
37
+ buttonLabel: "#FFFFFF",
38
38
  },
39
39
  } as const;
40
40
  ```
41
41
 
42
+ ### Spacing
43
+
44
+ `ThemeProvider` also takes optional `spacing`. Omitted keys keep the package default, so most apps don't set it.
45
+
46
+ ```tsx
47
+ <ThemeProvider colors={Colors} spacing={{ screen: 20 }}>
48
+ ```
49
+
50
+ | Key | Default | Used by |
51
+ |---|---|---|
52
+ | `screen` | `16` | Side padding of `Page` and `Form` content, `ErrorBanner`, the `TabAccessoryButton` fallback pill |
53
+ | `pageTop` | `16` | Gap between the header and the first item of a `Page` |
54
+ | `pageBottom` | `24` | Gap after the last item of a `Page` |
55
+
56
+ Read it in your own components with `useThemeSpacing()`. Outside `ThemeProvider` it returns the defaults (`DEFAULT_THEME_SPACING`). A component's own padding prop, such as `Form`'s `paddingHorizontal`, still wins.
57
+
58
+ ```tsx
59
+ const { screen } = useThemeSpacing();
60
+ ```
61
+
42
62
  ### `useThemeColor`
43
63
 
44
64
  Resolves a role for the current scheme (React Native `useColorScheme`; anything other than `"dark"` is light):
@@ -70,42 +90,82 @@ The package does not store preferences or call `Appearance.setColorScheme`. If y
70
90
 
71
91
  ## Button
72
92
 
73
- `Button` is presentational. Pass the colours you want. Full width of its parent is fixed.
93
+ `Button` is presentational. Colours default from `ThemeProvider`; pass props only to override. Full width of its parent by default; override with `style`.
74
94
 
75
95
  ```tsx
76
- import { Button, useThemeColor } from "@studio-piot/native-ui";
96
+ import { Button } from "@studio-piot/native-ui";
77
97
 
78
98
  export function SaveButton({ onPress }: { onPress: () => void }) {
79
- return (
80
- <Button
81
- label="Save"
82
- onPress={onPress}
83
- backgroundColor={useThemeColor("buttonBackground")}
84
- labelColor={useThemeColor("buttonLabel")}
85
- />
86
- );
99
+ return <Button label="Save" onPress={onPress} />;
87
100
  }
88
101
  ```
89
102
 
103
+ ### Theme roles
104
+
105
+ Add these keys to both light and dark maps when you omit the colour props. `Form` uses the same roles for its submit button.
106
+
107
+ | Prop | Default theme role | Filled | Outline |
108
+ |---|---|---|---|
109
+ | `backgroundColor` | `buttonBackground` | Fill | Border, label, icon, spinner |
110
+ | `labelColor` | `buttonLabel` | Label, icon, spinner | Unused |
111
+
112
+ Explicit props always win, so existing code that passes both colours keeps working:
113
+
114
+ ```tsx
115
+ <Button
116
+ label="Delete"
117
+ onPress={onDelete}
118
+ backgroundColor={useThemeColor("danger")}
119
+ labelColor={useThemeColor("onDanger")}
120
+ />
121
+ ```
122
+
90
123
  ### Props
91
124
 
92
125
  | Prop | Type | Default | Notes |
93
126
  |---|---|---|---|
94
- | `label` | `string` | — | Required |
127
+ | `label` | `string` | — | Required. Shown text and accessibility label |
95
128
  | `onPress` | `() => void` | — | Required |
96
- | `backgroundColor` | `string` | — | Required. Filled: fill. Outline: border + label |
97
- | `labelColor` | `string` | — | Required. Filled: label + spinner |
129
+ | `backgroundColor` | `string` | theme `buttonBackground` | Optional override |
130
+ | `labelColor` | `string` | theme `buttonLabel` | Optional override |
98
131
  | `variant` | `"filled" \| "outline"` | `"filled"` | |
99
132
  | `radius` | see below | `"xl"` | Tailwind-style steps |
100
- | `disabled` | `boolean` | `false` | Opacity treatment; same colours |
101
- | `loading` | `boolean` | `false` | Spinner instead of label; non-interactive |
133
+ | `icon` | `ButtonIcon` | — | `({ color, size }) => ReactNode` |
134
+ | `iconPosition` | `"start" \| "end" \| "only"` | `"start"` | `"only"` hides the label text |
135
+ | `iconSize` | `number` | `20` | |
136
+ | `disabled` | `boolean` | `false` | 40% opacity; not pressable |
137
+ | `loading` | `boolean` | `false` | Spinner over the content; not pressable; full opacity |
102
138
  | `style` | view style | — | Merged after the shell |
103
139
  | `labelStyle` | text style | — | Merged after the label |
104
140
 
105
141
  ### Variants
106
142
 
107
- - **filled** — `backgroundColor` fill, `labelColor` text/spinner
108
- - **outline** — transparent fill, border and label from `backgroundColor`. `labelColor` is still required but unused for the outline label; the outline spinner uses `backgroundColor`.
143
+ - **filled** — `backgroundColor` fill; label, icon and spinner in `labelColor`.
144
+ - **outline** — transparent fill with a 1pt border; border, label, icon and spinner all in `backgroundColor`.
145
+
146
+ ### Icons
147
+
148
+ The package has no Expo dependency, so the icon is a render function. It receives the content colour for the variant and `iconSize`; return an expo-symbols `SymbolView` or any other icon.
149
+
150
+ ```tsx
151
+ import { SymbolView } from "expo-symbols";
152
+
153
+ <Button
154
+ label="Share"
155
+ onPress={onShare}
156
+ icon={({ color, size }) => (
157
+ <SymbolView name="square.and.arrow.up" size={size} tintColor={color} />
158
+ )}
159
+ />
160
+
161
+ // Icon after the label
162
+ <Button label="Next" iconPosition="end" icon={arrow} onPress={onNext} />
163
+
164
+ // Icon only — label is still read by VoiceOver
165
+ <Button label="Add" iconPosition="only" icon={plus} radius="full" style={{ width: 52 }} onPress={onAdd} />
166
+ ```
167
+
168
+ The icon and label are 8pt apart and centred together.
109
169
 
110
170
  ### Radius
111
171
 
@@ -121,39 +181,33 @@ export function SaveButton({ onPress }: { onPress: () => void }) {
121
181
  | `full` | pill (`999`) |
122
182
 
123
183
  ```tsx
124
- <Button
125
- label="Continue"
126
- onPress={onContinue}
127
- backgroundColor={backgroundColor}
128
- labelColor={labelColor}
129
- radius="full"
130
- variant="outline"
131
- />
184
+ <Button label="Continue" onPress={onContinue} radius="full" variant="outline" />
132
185
  ```
133
186
 
187
+ ### Layout
188
+
189
+ Minimum height 52, 20pt horizontal and 12pt vertical padding. Long labels wrap and stay centred inside the padding. The label is 17pt, weight 600.
190
+
134
191
  ### Loading and disabled
135
192
 
136
193
  ```tsx
137
- <Button
138
- label="Saving…"
139
- onPress={onSave}
140
- backgroundColor={backgroundColor}
141
- labelColor={labelColor}
142
- loading={isSaving}
143
- disabled={!canSave}
144
- />
194
+ <Button label="Save" onPress={onSave} loading={isSaving} disabled={!canSave} />
145
195
  ```
146
196
 
147
- When `loading` or `disabled` is true, the control does not fire `onPress` and uses reduced opacity.
197
+ Neither state fires `onPress`. Disabled fades the button to 40%. Loading keeps full opacity and shows a spinner over the content. The label stays in the layout, invisible, so the button keeps its size. Screen readers get `busy` while loading.
148
198
 
149
199
  ## Imports
150
200
 
151
201
  ```ts
152
202
  import {
153
203
  Button,
204
+ BUTTON_THEME_ROLES,
154
205
  ThemeProvider,
206
+ type ButtonIcon,
207
+ type ButtonProps,
155
208
  useColorScheme,
156
209
  useThemeColor,
210
+ useThemeSpacing,
157
211
  version,
158
212
  } from "@studio-piot/native-ui";
159
213
 
@@ -0,0 +1,111 @@
1
+ # Choice — usage
2
+
3
+ One option in a list of next steps: an icon, a title and an optional description, in a filled row. Tapping it acts straight away, usually to open the next screen, as in "What are you doing? Running / HIIT".
4
+
5
+ Use `Choice` for a short list of destinations, or, with `selected`, for picking one of a few visual options such as candidate routes. To pick a value that stays on the screen, use [RadioField](./radio-field.md), [SegmentedControl](./segmented-control.md) or [Select](./select.md). For a settings list of links, use [LinkBlocks](./link-blocks.md).
6
+
7
+ ## Basic usage
8
+
9
+ ```tsx
10
+ import { Choice, Page } from "@studio-piot/native-ui";
11
+ import { router } from "expo-router";
12
+ import { SymbolView } from "expo-symbols";
13
+ import { View } from "react-native";
14
+
15
+ export default function NewActivity() {
16
+ return (
17
+ <Page>
18
+ <View style={{ gap: 16 }}>
19
+ <Choice
20
+ title="Running"
21
+ description="Outdoors, with GPS"
22
+ icon={({ color, size }) => (
23
+ <SymbolView name="figure.run" size={size} tintColor={color} />
24
+ )}
25
+ onPress={() => router.push("/new-activity/running")}
26
+ />
27
+ <Choice
28
+ title="HIIT"
29
+ description="Intervals, anywhere"
30
+ icon={({ color, size }) => (
31
+ <SymbolView name="stopwatch" size={size} tintColor={color} />
32
+ )}
33
+ onPress={() => router.push("/new-activity/hiit")}
34
+ />
35
+ </View>
36
+ </Page>
37
+ );
38
+ }
39
+ ```
40
+
41
+ - **Icon.** A render function, like `Button` and `HeaderIcon`: it receives the icon colour and `iconSize` (default 28). Icons sit in a 32pt column so titles line up down the list; a larger icon widens its column instead of overflowing.
42
+ - **Title only.** `description` is optional.
43
+ - **Spacing.** Put choices in a stack with a 16pt gap. The row has no margin of its own.
44
+
45
+ ## Selectable choices
46
+
47
+ Pass `selected` to make the choices a set where one is picked, for example candidate routes with a drawing of each in the icon slot. The selected row gets a highlight border (`choiceSelected`); the others keep a transparent border of the same width, so nothing shifts when the selection moves. Tapping still calls `onPress`, where you update the selection.
48
+
49
+ ```tsx
50
+ {routes.map((route) => (
51
+ <Choice
52
+ key={route.id}
53
+ title={route.name}
54
+ description={`${route.km} km`}
55
+ icon={({ color, size }) => <RouteThumbnail route={route} color={color} size={size} />}
56
+ iconSize={48}
57
+ selected={route.id === selectedId}
58
+ onPress={() => setSelectedId(route.id)}
59
+ />
60
+ ))}
61
+ ```
62
+
63
+ Pair it with one action that uses the selection, e.g. **Use this route**. Leave `selected` out when tapping should just act.
64
+
65
+ ## Accessibility
66
+
67
+ The row is one button. VoiceOver reads its text together, for example "Running, Outdoors, with GPS, button". With `selected`, it is a radio option instead, read as selected or not. The icon is decorative and hidden from screen readers. `disabled` sets the disabled state, fades the row to 40% and blocks presses.
68
+
69
+ ## Theme roles
70
+
71
+ Add these keys to both the light and dark maps.
72
+
73
+ | Usage | Role |
74
+ |---|---|
75
+ | Row fill | `choiceBackground` |
76
+ | Title | `choiceTitle` |
77
+ | Description | `choiceDescription` |
78
+ | Icon | `choiceIcon` |
79
+ | Selected border (with `selected`) | `choiceSelected` |
80
+
81
+ ## Props
82
+
83
+ | Prop | Type | Default | Notes |
84
+ |---|---|---|---|
85
+ | `title` | `string` | — | Required |
86
+ | `onPress` | `() => void` | — | Required |
87
+ | `description` | `string` | — | Second line under the title; may wrap to more lines |
88
+ | `selected` | `boolean` | — | Makes it a selectable option: highlight border when true, radio semantics |
89
+ | `icon` | `ButtonIcon` | — | `({ color, size }) => ReactNode` |
90
+ | `iconSize` | `number` | `28` | Icon box |
91
+ | `disabled` | `boolean` | `false` | 40% opacity; not pressable |
92
+ | `radius` | `ButtonRadius` | `"xl"` | Same scale as `Button` (12) |
93
+ | `backgroundColor` | `string` | theme `choiceBackground` | Optional override |
94
+ | `titleColor` | `string` | theme `choiceTitle` | Optional override |
95
+ | `descriptionColor` | `string` | theme `choiceDescription` | Optional override |
96
+ | `iconColor` | `string` | theme `choiceIcon` | Optional override |
97
+ | `selectedColor` | `string` | theme `choiceSelected` | Optional override |
98
+ | `style` | view style | — | Merged after the row |
99
+
100
+ Layout: 72pt minimum height, 16pt horizontal and 14pt vertical padding, 12pt between the icon and the text. The title is 17pt semibold and the description 13pt, the same as the rest of the package.
101
+
102
+ ## Imports
103
+
104
+ ```ts
105
+ import {
106
+ Choice,
107
+ CHOICE_ICON_SIZE,
108
+ CHOICE_THEME_ROLES,
109
+ type ChoiceProps,
110
+ } from "@studio-piot/native-ui";
111
+ ```
@@ -12,7 +12,7 @@ npx expo install react-native-svg
12
12
 
13
13
  ## Theme roles
14
14
 
15
- Set these in both light and dark. The Ribe banner stays charcoal with a flare disc in both schemes.
15
+ Set these in both the light and dark maps, with values that suit each scheme, so the banner follows the system appearance like the rest of the screen. A light banner in light mode and a dark one in dark mode both work. The status bar text switches to stay readable: dark text over a light fill, light text over a dark one.
16
16
 
17
17
  | Usage | Role |
18
18
  |---|---|
@@ -22,6 +22,39 @@ Set these in both light and dark. The Ribe banner stays charcoal with a flare di
22
22
  | Row dividers | `errorBannerDivider` |
23
23
  | Megaphone disc | `errorBannerAccent` |
24
24
 
25
+ ```ts
26
+ const Colors = {
27
+ light: {
28
+ errorBannerBackground: "#FFFFFF",
29
+ errorBannerText: "#111827",
30
+ errorBannerSecondary: "#6B7280",
31
+ errorBannerDivider: "#E5E7EB",
32
+ errorBannerAccent: "#DC2626",
33
+ },
34
+ dark: {
35
+ errorBannerBackground: "#1F2937",
36
+ errorBannerText: "#F9FAFB",
37
+ errorBannerSecondary: "#9CA3AF",
38
+ errorBannerDivider: "#374151",
39
+ errorBannerAccent: "#DC2626",
40
+ },
41
+ };
42
+ ```
43
+
44
+ The megaphone is drawn in `errorBannerBackground`, so pick an accent that contrasts with the fill in both schemes.
45
+
46
+ ## Sizes
47
+
48
+ Text matches the 17pt body text of `Button`, `FieldText` and `Select`.
49
+
50
+ | Element | Value |
51
+ |---|---|
52
+ | Title and row text | 17pt, line height 22 (title weight 600) |
53
+ | Megaphone disc | 32×32, icon 18 |
54
+ | Dismiss cross | 18 |
55
+ | Row arrow | 20 |
56
+ | Row height | at least 44 (tap target) |
57
+
25
58
  ## With Form
26
59
 
27
60
  Give each field a `name` that matches `FieldError.field`. On submit, set `errors`. `Form` opens the banner. Typing in a field, changing a choice, or pressing ✕ hides it. The next submit shows it again. While an error is listed, that field keeps the error border and does not repeat the message underneath.
@@ -56,7 +89,8 @@ export function NewActivity({ onClose }: { onClose: () => void }) {
56
89
  };
57
90
 
58
91
  return (
59
- <Form title="New activity" errors={errors} onClose={onClose} onSubmit={submit}>
92
+ // Title and close button live in the screen's stack options; see Form.
93
+ <Form errors={errors} onSubmit={submit}>
60
94
  <FieldText name="name" label="Name" value={name} onChangeText={setName} />
61
95
  <Select
62
96
  name="type"
@@ -74,7 +108,16 @@ List errors in the same order as the fields, top to bottom.
74
108
 
75
109
  ## Without Form
76
110
 
77
- `ErrorBanner`, `useErrorBanner`, and `ErrorBannerProvider` work with any scroll view. Put the banner inside the provider and call `jump` from context so a row scrolls the matching `ErrorBannerField` (or a package field with `name`) into view. Call `present` on submit and `dismiss` when the runner edits the screen.
111
+ `ErrorBanner`, `useErrorBanner`, and `ErrorBannerProvider` work with any scroll view. `Form` and `FormSheet` wire them up for you. Put the banner inside the provider and call `jump` from context so a row scrolls the matching `ErrorBannerField` (or a package field with `name`) into view. Call `present` on submit and `dismiss` when the runner edits the screen.
112
+
113
+ `ErrorBannerProvider` props:
114
+
115
+ | Prop | Notes |
116
+ |---|---|
117
+ | `scrollRef` | Anything with `scrollTo({ y, animated })`: a `ScrollView`, `KeyboardAwareScrollView` or `BottomSheetScrollView` |
118
+ | `bannerHeightRef` | How far the banner covers the top of the scroll area |
119
+ | `measureRef` | Optional. A view at the top of the scroll content to measure fields against. Defaults to `scrollRef`, which works for a plain `ScrollView` |
120
+ | `onBeforeJump` | Optional. Runs before scrolling to a field, e.g. to expand a sheet |
78
121
 
79
122
  ## FieldError
80
123
 
@@ -0,0 +1,140 @@
1
+ # FormSheet — usage
2
+
3
+ A form in a draggable sheet over a full-screen background, usually a map. Use it when the screen is about the background and the form adjusts it: choose a start point on a map, set a radius, preview a route.
4
+
5
+ - **Title on the sheet.** `title` sits at the top of the sheet, under the drag indicator. The native header keeps only the close button, transparent so the background shows behind it.
6
+ - **Three stops.** `0` shows just the handle and the title. `1` puts the sheet top at half the screen. `2` takes it up to the bottom of the native header, never further.
7
+ - **Sticky submit.** The submit button sits in a footer that stays above the keyboard, the same footer as `Form`.
8
+ - **Errors.** Pass `errors` and the `ErrorBanner` works as it does in `Form`. A banner row expands the sheet and scrolls to that field.
9
+
10
+ Use `Form` instead when the screen is only the form.
11
+
12
+ ## Peer dependencies
13
+
14
+ The same as `Form` and `Select`: `@gorhom/bottom-sheet`, `react-native-gesture-handler`, `react-native-reanimated`, `react-native-keyboard-controller` and `react-native-safe-area-context`, plus `react-native-screens` for the iOS banner overlay. Wrap the app in `GestureHandlerRootView` and `KeyboardProvider`.
15
+
16
+ ## Basic usage
17
+
18
+ ```tsx
19
+ // app/(app)/run/_layout.tsx
20
+ export default function Layout() {
21
+ return <Stack screenOptions={{ headerTransparent: true }} />;
22
+ }
23
+ ```
24
+
25
+ ```tsx
26
+ // app/(app)/run/index.tsx
27
+ import { FormSheet, HeaderIcon, Slider } from "@studio-piot/native-ui";
28
+ import { router, Stack } from "expo-router";
29
+ import { useHeaderHeight } from "expo-router/react-navigation";
30
+
31
+ export default function RunSetup() {
32
+ const [distance, setDistance] = useState(5);
33
+
34
+ return (
35
+ <>
36
+ <Stack.Screen
37
+ options={{
38
+ headerShown: true,
39
+ headerTransparent: true,
40
+ title: "", // the title is on the sheet
41
+ headerLeft: () => (
42
+ <HeaderIcon label="Close" icon={closeIcon} onPress={() => router.back()} />
43
+ ),
44
+ }}
45
+ />
46
+ <FormSheet
47
+ title="Run"
48
+ headerHeight={useHeaderHeight()}
49
+ initialIndex={1}
50
+ background={(inset) => <RouteMap chromeInset={inset} radiusKm={distance / 2} />}
51
+ onSubmit={generate}
52
+ submitLabel="Generate route"
53
+ >
54
+ <Slider label="Distance" value={distance} onValueChange={setDistance} />
55
+ </FormSheet>
56
+ </>
57
+ );
58
+ }
59
+ ```
60
+
61
+ - **Modal routes need their own stack.** On iOS a screen presented as a modal (`presentation: "modal"` or `"fullScreenModal"`) sits outside the navigation controller, so its header options are ignored and no header appears. Make the route a folder with its own `_layout.tsx` that returns a `<Stack />`, and put the screen in `index.tsx`. The parent still presents the folder as a modal, and the URL doesn't change.
62
+ - **Use `headerTransparent: true`.** Without it the header is opaque, the map starts below it, and the sheet's top stop still works, but the map doesn't show behind the bar.
63
+ - **Pass `headerHeight={useHeaderHeight()}`** (from `expo-router/react-navigation`). The top stop ends there. Without it, `FormSheet` assumes the status bar plus a standard bar (44pt on iOS, 56pt on Android), which is wrong for a large title or a custom header.
64
+
65
+ ## The background and its insets
66
+
67
+ `background` is a function so the layer can keep its own controls and camera clear of the chrome. It receives a `FormSheetInset`:
68
+
69
+ | Field | Meaning |
70
+ |---|---|
71
+ | `top` | Bottom of the native header, from the screen top |
72
+ | `bottom` | Top of the sheet at its current stop, from the screen bottom (includes the footer) |
73
+ | `bottomAtHalf` | Top of the sheet at the middle stop. Doesn't change while dragging, so it suits map camera padding |
74
+
75
+ The background fills the whole screen behind the sheet. Its top corners are rounded (`SHEET_TOP_RADIUS`, 20), so a map that should show through those corners should continue a little past `bottom`, for example `bottom - SHEET_TOP_RADIUS - 8`.
76
+
77
+ ## Errors
78
+
79
+ The same contract as `Form`: give each field a `name` matching `FieldError.field`, then set `errors` when submit fails.
80
+
81
+ ```tsx
82
+ <FormSheet errors={errors} onSubmit={submit} …>
83
+ <FieldText name="name" label="Name" value={name} onChangeText={setName} />
84
+ </FormSheet>
85
+ ```
86
+
87
+ On iOS the banner covers the top of the screen, including the native header (it is drawn in a native overlay window). On Android it slides down directly below the header. Tapping a row expands the sheet to stop `2`, scrolls the field clear of the banner and focuses it if it can take focus. Editing a field dismisses the banner, and the next submit shows it again.
88
+
89
+ ## Theme roles
90
+
91
+ | Usage | Role |
92
+ |---|---|
93
+ | Sheet fill and handle | `formSheetBackground` |
94
+ | Sheet title | `formSheetTitle` |
95
+ | Footer ground | `formSheetFooterBackground` |
96
+ | Drag indicator | `formSheetHandle` |
97
+ | Submit fill | `buttonBackground` |
98
+ | Submit label | `buttonLabel` |
99
+
100
+ With `errors`, the banner also reads the `errorBanner*` roles.
101
+
102
+ ## Props
103
+
104
+ | Prop | Type | Default | Notes |
105
+ |---|---|---|---|
106
+ | `title` | `string` | — | Top of the sheet, under the indicator. Always visible at the lowest stop |
107
+ | `children` | `ReactNode` | — | Required. Sheet content; scrolls inside the sheet |
108
+ | `background` | `(inset: FormSheetInset) => ReactNode` | — | Required. Full-screen layer behind the sheet |
109
+ | `onSubmit` | `() => void` | — | Required |
110
+ | `errors` | `FieldError[]` | `[]` | Screen banner. Empty hides it |
111
+ | `submitLabel` | `string` | `"Save"` | |
112
+ | `submitting` | `boolean` | `false` | Spinner on the submit button |
113
+ | `submitDisabled` | `boolean` | `false` | |
114
+ | `submitRadius` | `ButtonRadius` | `"lg"` | |
115
+ | `initialIndex` | `0 \| 1 \| 2` | `0` | Stop on first paint |
116
+ | `headerHeight` | `number` | status bar + standard bar | `useHeaderHeight()`. Where the top stop ends |
117
+ | `paddingHorizontal` | `number` | theme `spacing.screen` | Content and footer |
118
+ | `backgroundColor` | `string` | theme `formSheetBackground` | |
119
+ | `titleColor` | `string` | theme `formSheetTitle` | |
120
+ | `footerBackgroundColor` | `string` | theme `formSheetFooterBackground` | |
121
+ | `handleColor` | `string` | theme `formSheetHandle` | |
122
+ | `buttonBackgroundColor` | `string` | theme `buttonBackground` | |
123
+ | `buttonLabelColor` | `string` | theme `buttonLabel` | |
124
+
125
+ ## Migrating from 0.12
126
+
127
+ `onClose` and `closeButton` are gone. Move the close button to the screen's stack options (`headerLeft`) with `headerShown: true` and `headerTransparent: true`. `title`, `titleColor` and the `formSheetTitle` role still put the title on the sheet. Leave the header's `title` empty so it isn't shown twice.
128
+
129
+ ## Imports
130
+
131
+ ```ts
132
+ import {
133
+ FormSheet,
134
+ FORM_SHEET_THEME_ROLES,
135
+ SHEET_TOP_RADIUS,
136
+ formSheetSnapPoints,
137
+ type FormSheetInset,
138
+ type FormSheetProps,
139
+ } from "@studio-piot/native-ui";
140
+ ```