@studio-piot/native-ui 0.2.4 → 0.16.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 (341) 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 +8 -0
  19. package/dist/components/Choice/Choice.d.ts.map +1 -0
  20. package/dist/components/Choice/Choice.js +60 -0
  21. package/dist/components/Choice/Choice.js.map +1 -0
  22. package/dist/components/Choice/choiceLayout.d.ts +7 -0
  23. package/dist/components/Choice/choiceLayout.d.ts.map +1 -0
  24. package/dist/components/Choice/choiceLayout.js +13 -0
  25. package/dist/components/Choice/choiceLayout.js.map +1 -0
  26. package/dist/components/Choice/choiceThemeRoles.d.ts +8 -0
  27. package/dist/components/Choice/choiceThemeRoles.d.ts.map +1 -0
  28. package/dist/components/Choice/choiceThemeRoles.js +10 -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 +26 -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/SegmentedControl/types.d.ts +0 -1
  176. package/dist/components/SegmentedControl/types.d.ts.map +1 -1
  177. package/dist/components/Select/Select.d.ts +1 -1
  178. package/dist/components/Select/Select.d.ts.map +1 -1
  179. package/dist/components/Select/Select.js +8 -9
  180. package/dist/components/Select/Select.js.map +1 -1
  181. package/dist/components/Select/selectThemeRoles.d.ts +2 -0
  182. package/dist/components/Select/selectThemeRoles.d.ts.map +1 -1
  183. package/dist/components/Select/selectThemeRoles.js +2 -0
  184. package/dist/components/Select/selectThemeRoles.js.map +1 -1
  185. package/dist/components/Select/types.d.ts +4 -0
  186. package/dist/components/Select/types.d.ts.map +1 -1
  187. package/dist/components/TabAccessoryButton/TabAccessoryButton.d.ts +8 -0
  188. package/dist/components/TabAccessoryButton/TabAccessoryButton.d.ts.map +1 -0
  189. package/dist/components/TabAccessoryButton/TabAccessoryButton.js +77 -0
  190. package/dist/components/TabAccessoryButton/TabAccessoryButton.js.map +1 -0
  191. package/dist/components/TabAccessoryButton/index.d.ts +5 -0
  192. package/dist/components/TabAccessoryButton/index.d.ts.map +1 -0
  193. package/dist/components/TabAccessoryButton/index.js +11 -0
  194. package/dist/components/TabAccessoryButton/index.js.map +1 -0
  195. package/dist/components/TabAccessoryButton/tabAccessoryLayout.d.ts +27 -0
  196. package/dist/components/TabAccessoryButton/tabAccessoryLayout.d.ts.map +1 -0
  197. package/dist/components/TabAccessoryButton/tabAccessoryLayout.js +38 -0
  198. package/dist/components/TabAccessoryButton/tabAccessoryLayout.js.map +1 -0
  199. package/dist/components/TabAccessoryButton/tabAccessoryThemeRoles.d.ts +14 -0
  200. package/dist/components/TabAccessoryButton/tabAccessoryThemeRoles.d.ts.map +1 -0
  201. package/dist/components/TabAccessoryButton/tabAccessoryThemeRoles.js +19 -0
  202. package/dist/components/TabAccessoryButton/tabAccessoryThemeRoles.js.map +1 -0
  203. package/dist/components/TabAccessoryButton/types.d.ts +27 -0
  204. package/dist/components/TabAccessoryButton/types.d.ts.map +1 -0
  205. package/dist/components/TabAccessoryButton/types.js +3 -0
  206. package/dist/components/TabAccessoryButton/types.js.map +1 -0
  207. package/dist/components/index.d.ts +9 -3
  208. package/dist/components/index.d.ts.map +1 -1
  209. package/dist/components/index.js +30 -1
  210. package/dist/components/index.js.map +1 -1
  211. package/dist/index.d.ts +11 -4
  212. package/dist/index.d.ts.map +1 -1
  213. package/dist/index.js +34 -2
  214. package/dist/index.js.map +1 -1
  215. package/dist/layout/HeaderInsetProbe.d.ts +15 -0
  216. package/dist/layout/HeaderInsetProbe.d.ts.map +1 -0
  217. package/dist/layout/HeaderInsetProbe.js +28 -0
  218. package/dist/layout/HeaderInsetProbe.js.map +1 -0
  219. package/dist/layout/headerInset.d.ts +26 -0
  220. package/dist/layout/headerInset.d.ts.map +1 -0
  221. package/dist/layout/headerInset.js +29 -0
  222. package/dist/layout/headerInset.js.map +1 -0
  223. package/dist/layout/index.d.ts +1 -0
  224. package/dist/layout/index.d.ts.map +1 -1
  225. package/dist/layout/index.js +3 -1
  226. package/dist/layout/index.js.map +1 -1
  227. package/dist/layout/sheet.d.ts +3 -0
  228. package/dist/layout/sheet.d.ts.map +1 -0
  229. package/dist/layout/sheet.js +6 -0
  230. package/dist/layout/sheet.js.map +1 -0
  231. package/dist/theme/ThemeProvider.d.ts +4 -1
  232. package/dist/theme/ThemeProvider.d.ts.map +1 -1
  233. package/dist/theme/ThemeProvider.js +5 -2
  234. package/dist/theme/ThemeProvider.js.map +1 -1
  235. package/dist/theme/index.d.ts +2 -0
  236. package/dist/theme/index.d.ts.map +1 -1
  237. package/dist/theme/index.js +6 -1
  238. package/dist/theme/index.js.map +1 -1
  239. package/dist/theme/resolveThemeSpacing.d.ts +12 -0
  240. package/dist/theme/resolveThemeSpacing.d.ts.map +1 -0
  241. package/dist/theme/resolveThemeSpacing.js +14 -0
  242. package/dist/theme/resolveThemeSpacing.js.map +1 -0
  243. package/dist/theme/types.d.ts +2 -0
  244. package/dist/theme/types.d.ts.map +1 -1
  245. package/dist/theme/useThemeSpacing.d.ts +4 -0
  246. package/dist/theme/useThemeSpacing.d.ts.map +1 -0
  247. package/dist/theme/useThemeSpacing.js +11 -0
  248. package/dist/theme/useThemeSpacing.js.map +1 -0
  249. package/docs/components/button.md +92 -38
  250. package/docs/components/choice.md +88 -0
  251. package/docs/components/error-banner.md +46 -3
  252. package/docs/components/form-sheet.md +140 -0
  253. package/docs/components/form.md +80 -45
  254. package/docs/components/header-button.md +310 -0
  255. package/docs/components/header-wash.md +40 -0
  256. package/docs/components/page.md +183 -0
  257. package/docs/components/select.md +2 -2
  258. package/docs/components/tab-accessory-button.md +146 -0
  259. package/docs/specifications/Liquid Glass header buttons in Expo Router.md +327 -0
  260. package/package.json +1 -1
  261. package/src/components/Button/Button.tsx +82 -38
  262. package/src/components/Button/buttonAppearance.test.ts +44 -0
  263. package/src/components/Button/buttonAppearance.ts +38 -0
  264. package/src/components/Button/buttonThemeRoles.test.ts +10 -0
  265. package/src/components/Button/buttonThemeRoles.ts +6 -0
  266. package/src/components/Button/index.ts +12 -1
  267. package/src/components/Choice/Choice.tsx +109 -0
  268. package/src/components/Choice/choiceLayout.test.ts +14 -0
  269. package/src/components/Choice/choiceLayout.ts +9 -0
  270. package/src/components/Choice/choiceThemeRoles.test.ts +14 -0
  271. package/src/components/Choice/choiceThemeRoles.ts +8 -0
  272. package/src/components/Choice/index.ts +4 -0
  273. package/src/components/Choice/types.ts +26 -0
  274. package/src/components/ErrorBanner/ErrorBanner.tsx +32 -22
  275. package/src/components/ErrorBanner/errorBannerContext.tsx +31 -6
  276. package/src/components/ErrorBanner/errorBannerLayout.test.ts +49 -0
  277. package/src/components/ErrorBanner/errorBannerLayout.ts +59 -0
  278. package/src/components/ErrorBanner/icons.tsx +25 -6
  279. package/src/components/ErrorBanner/index.ts +1 -0
  280. package/src/components/Form/Form.tsx +77 -173
  281. package/src/components/Form/FormCloseButton.tsx +12 -4
  282. package/src/components/Form/FormErrorBanner.tsx +57 -0
  283. package/src/components/Form/FormSubmitFooter.tsx +70 -0
  284. package/src/components/Form/formThemeRoles.test.ts +13 -0
  285. package/src/components/Form/formThemeRoles.ts +7 -0
  286. package/src/components/Form/index.ts +1 -0
  287. package/src/components/Form/types.ts +14 -10
  288. package/src/components/FormSheet/FormSheet.tsx +279 -0
  289. package/src/components/FormSheet/formSheetLayout.test.ts +66 -0
  290. package/src/components/FormSheet/formSheetLayout.ts +55 -0
  291. package/src/components/FormSheet/formSheetThemeRoles.test.ts +14 -0
  292. package/src/components/FormSheet/formSheetThemeRoles.ts +8 -0
  293. package/src/components/FormSheet/index.ts +7 -0
  294. package/src/components/FormSheet/types.ts +66 -0
  295. package/src/components/HeaderButton/HeaderCapsule.tsx +63 -0
  296. package/src/components/HeaderButton/HeaderGroup.tsx +36 -0
  297. package/src/components/HeaderButton/HeaderIcon.tsx +56 -0
  298. package/src/components/HeaderButton/headerButtonLayout.test.ts +47 -0
  299. package/src/components/HeaderButton/headerButtonLayout.ts +36 -0
  300. package/src/components/HeaderButton/headerButtonThemeRoles.test.ts +9 -0
  301. package/src/components/HeaderButton/headerButtonThemeRoles.ts +5 -0
  302. package/src/components/HeaderButton/index.ts +14 -0
  303. package/src/components/HeaderButton/types.ts +40 -0
  304. package/src/components/HeaderWash/HeaderWash.tsx +20 -0
  305. package/src/components/HeaderWash/headerWashGradient.test.ts +22 -0
  306. package/src/components/{Form/withAlpha.ts → HeaderWash/headerWashGradient.ts} +9 -0
  307. package/src/components/HeaderWash/index.ts +1 -0
  308. package/src/components/Page/Page.tsx +80 -0
  309. package/src/components/Page/PageBleed.tsx +14 -0
  310. package/src/components/Page/index.ts +4 -0
  311. package/src/components/Page/pageContext.ts +8 -0
  312. package/src/components/Page/pageInsets.test.ts +73 -0
  313. package/src/components/Page/pageInsets.ts +52 -0
  314. package/src/components/Page/pageThemeRoles.test.ts +9 -0
  315. package/src/components/Page/pageThemeRoles.ts +5 -0
  316. package/src/components/Page/types.ts +26 -0
  317. package/src/components/SegmentedControl/types.ts +0 -1
  318. package/src/components/Select/Select.tsx +11 -4
  319. package/src/components/Select/selectThemeRoles.test.ts +5 -0
  320. package/src/components/Select/selectThemeRoles.ts +2 -0
  321. package/src/components/Select/types.ts +4 -0
  322. package/src/components/TabAccessoryButton/TabAccessoryButton.tsx +150 -0
  323. package/src/components/TabAccessoryButton/index.ts +12 -0
  324. package/src/components/TabAccessoryButton/tabAccessoryLayout.test.ts +42 -0
  325. package/src/components/TabAccessoryButton/tabAccessoryLayout.ts +43 -0
  326. package/src/components/TabAccessoryButton/tabAccessoryThemeRoles.test.ts +28 -0
  327. package/src/components/TabAccessoryButton/tabAccessoryThemeRoles.ts +22 -0
  328. package/src/components/TabAccessoryButton/types.ts +27 -0
  329. package/src/components/index.ts +64 -2
  330. package/src/index.ts +61 -2
  331. package/src/layout/headerInset.test.ts +60 -0
  332. package/src/layout/headerInset.ts +41 -0
  333. package/src/layout/index.ts +1 -0
  334. package/src/layout/sheet.ts +2 -0
  335. package/src/theme/ThemeProvider.tsx +9 -1
  336. package/src/theme/index.ts +6 -0
  337. package/src/theme/resolveThemeSpacing.test.ts +29 -0
  338. package/src/theme/resolveThemeSpacing.ts +21 -0
  339. package/src/theme/types.ts +3 -0
  340. package/src/theme/useThemeSpacing.ts +8 -0
  341. package/src/components/Form/withAlpha.test.ts +0 -13
@@ -1,87 +1,122 @@
1
1
  # Form — usage
2
2
 
3
- Full-screen modal form shell: keyboard-aware scroll, floating title bar with Cancel, sticky submit footer. Colours default from `ThemeProvider`. Apps own form fields (e.g. `FieldText`) and can swap Cancel via a slot.
3
+ Full-screen form under the native stack header: keyboard-aware scroll, sticky submit footer, and the error banner. The title and close button come from the screen's stack options, like any other screen, so the header is native: the iOS 26 glass bar, the large title, back gestures. Colours default from `ThemeProvider`. Apps own the fields (e.g. `FieldText`).
4
4
 
5
- For `ThemeProvider` and `useThemeColor`, see [theme provider and Button usage](./button.md).
5
+ For `ThemeProvider` and `useThemeColor`, see [theme provider and Button usage](./button.md). For a form over a map, see [FormSheet](./form-sheet.md).
6
6
 
7
7
  ## Peer dependencies
8
8
 
9
- `Form` needs these installed in the app (same versions you already use for Expo):
10
-
11
9
  - `react-native-keyboard-controller`
12
10
  - `react-native-safe-area-context`
11
+ - `react-native-screens` (iOS overlay for the error banner)
13
12
 
14
13
  Wrap the app (or the form route) with `KeyboardProvider` from `react-native-keyboard-controller` and a safe-area provider.
15
14
 
16
- ## Theme roles
17
-
18
- | Usage | Theme role |
19
- |---|---|
20
- | Screen / fade ground | `background` |
21
- | Title | `text` |
22
- | Footer ground | `surface` |
23
- | Submit fill | `buttonBackground` |
24
- | Submit label | `buttonLabel` |
25
-
26
15
  ## Basic usage
27
16
 
28
17
  ```tsx
29
- import { Form, FieldText } from "@studio-piot/native-ui";
18
+ // app/(app)/create-activity/_layout.tsx
19
+ export default function Layout() {
20
+ return <Stack />;
21
+ }
22
+ ```
30
23
 
31
- export function NewItemScreen({ onClose, onSave }: Props) {
24
+ ```tsx
25
+ // app/(app)/create-activity/index.tsx
26
+ import { Form, FieldText, HeaderIcon } from "@studio-piot/native-ui";
27
+ import { router, Stack } from "expo-router";
28
+ import { SymbolView } from "expo-symbols";
29
+
30
+ export default function CreateActivity() {
32
31
  return (
33
- <Form title="New item" onClose={onClose} onSubmit={onSave}>
34
- <FieldText label="Name" value={name} onChangeText={setName} />
35
- </Form>
32
+ <>
33
+ <Stack.Screen
34
+ options={{
35
+ headerShown: true,
36
+ title: "New activity",
37
+ headerLeft: () => (
38
+ <HeaderIcon
39
+ label="Close"
40
+ icon={({ color, size }) => <SymbolView name="xmark" size={size} tintColor={color} />}
41
+ onPress={() => router.back()}
42
+ />
43
+ ),
44
+ }}
45
+ />
46
+ <Form onSubmit={save} errors={errors}>
47
+ <FieldText name="name" label="Name" value={name} onChangeText={setName} />
48
+ </Form>
49
+ </>
36
50
  );
37
51
  }
38
52
  ```
39
53
 
40
- ## Close button slot
54
+ - **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.
55
+ - **Make `Form` the screen's root element.** Its scroll view must be a direct child of the screen so iOS insets it for the header and content scrolls behind the bar. A `<Stack.Screen>` next to it in a fragment is fine. Don't wrap `Form` in a `View`.
56
+ - **Header buttons.** Use `HeaderIcon` / `HeaderCapsule` for glass buttons, or `FormCloseButton` for a plain text "Cancel": `headerLeft: () => <FormCloseButton onPress={close} />`.
57
+ - **Large title.** `headerLargeTitle: true` works. The content starts under the large title and follows it as it collapses.
58
+
59
+ ## Header fade
41
60
 
42
- Pass `closeButton` to replace the default plain Cancel (e.g. Expo liquid-glass Cancel in the app):
61
+ For a wash behind a transparent header, with fields fading as they scroll under it, use [`HeaderWash`](./header-wash.md) as the stack's `headerBackground`:
43
62
 
44
63
  ```tsx
45
- import { Form } from "@studio-piot/native-ui";
46
- import { FormCloseButton } from "@/components/FormCloseButton";
47
-
48
- <Form
49
- title="New activity"
50
- onClose={() => router.back()}
51
- onSubmit={save}
52
- closeButton={<FormCloseButton onPress={() => router.back()} />}
53
- >
54
- {children}
55
- </Form>
64
+ <Stack
65
+ screenOptions={{
66
+ headerTransparent: true,
67
+ headerBackground: () => <HeaderWash />,
68
+ scrollEdgeEffects: { top: "hidden" },
69
+ }}
70
+ />
56
71
  ```
57
72
 
58
- The default package `FormCloseButton` is a themed text Cancel. Export it if you want that control outside `Form`.
73
+ On iOS the content still starts below the header and scrolls behind it. On Android a transparent toolbar overlaps the content, so keep the Android header opaque for forms, or pad the first field yourself.
74
+
75
+ ## Error banner
76
+
77
+ Pass `errors` to show the banner (see [ErrorBanner](./error-banner.md)). A row scrolls its field clear of both the header and the banner. Pass `headerHeight={useHeaderHeight()}` (from `expo-router/react-navigation`) so that works with a large title or a custom header. Without it, `Form` assumes the status bar plus a standard bar.
78
+
79
+ - **iOS:** the banner covers the top of the screen, including the native header. It is drawn in a native overlay window (`FullWindowOverlay` from react-native-screens), because the header bar sits above all React content.
80
+ - **Android:** the native header doesn't overlap the screen, so the banner slides down directly below it.
81
+
82
+ ## Theme roles
83
+
84
+ | Usage | Theme role |
85
+ |---|---|
86
+ | Scroll ground | `formBackground` |
87
+ | Footer ground | `formFooterBackground` |
88
+ | `FormCloseButton` label | `formClose` |
89
+ | Submit fill | `buttonBackground` |
90
+ | Submit label | `buttonLabel` |
91
+
92
+ The header's colours come from your stack options (`headerTintColor`, `headerTitleStyle`, `headerStyle`).
59
93
 
60
94
  ## Props
61
95
 
62
96
  | Prop | Type | Default | Notes |
63
97
  |---|---|---|---|
64
- | `title` | `string` | — | Required. Header title |
65
98
  | `children` | `ReactNode` | — | Required. Scroll body |
66
- | `onClose` | `() => void` | — | Required. Wired to default Cancel |
67
- | `onSubmit` | `() => void` | — | Required. Sticky Save. Presents the error banner, then runs. |
68
- | `errors` | `FieldError[]` | `[]` | Field errors. The banner lists them and scrolls to each `name`. Editing a field dismisses the banner; the next submit shows it again. |
99
+ | `onSubmit` | `() => void` | — | Required. Sticky submit. Presents the error banner, then runs |
100
+ | `errors` | `FieldError[]` | `[]` | Field errors. The banner lists them and scrolls to each `name`. Editing a field dismisses the banner; the next submit shows it again |
69
101
  | `submitLabel` | `string` | `"Save"` | |
70
102
  | `submitting` | `boolean` | `false` | Loading on submit |
71
103
  | `submitDisabled` | `boolean` | `false` | |
72
- | `closeButton` | `ReactNode` | package Cancel | Slot for app Cancel |
73
104
  | `submitRadius` | `ButtonRadius` | `"lg"` | Submit button radius |
74
- | `paddingHorizontal` | `number` | `16` | Shared inset for header, content, and footer. Override per screen if needed. |
75
- | `backgroundColor` | `string` | theme `background` | Optional override |
76
- | `titleColor` | `string` | theme `text` | Optional override |
77
- | `footerBackgroundColor` | `string` | theme `surface` | Optional override |
105
+ | `headerHeight` | `number` | status bar + standard bar | `useHeaderHeight()`. For scrolling an error's field clear of the header |
106
+ | `paddingHorizontal` | `number` | theme `spacing.screen` | Side inset for the content and footer |
107
+ | `backgroundColor` | `string` | theme `formBackground` | Optional override |
108
+ | `footerBackgroundColor` | `string` | theme `formFooterBackground` | Optional override |
78
109
  | `buttonBackgroundColor` | `string` | theme `buttonBackground` | Optional override |
79
110
  | `buttonLabelColor` | `string` | theme `buttonLabel` | Optional override |
80
111
 
112
+ The content starts `spacing.pageTop` below the header, the same as `Page`.
113
+
114
+ ## Migrating from 0.12
115
+
116
+ `title`, `onClose`, `closeButton` and `titleColor` are gone, along with the `formTitle` role. Move them to the screen's stack options: `title`, `headerLeft`, and `headerTitleStyle`. Set `headerShown: true` if the route hid its header.
117
+
81
118
  ## Imports
82
119
 
83
120
  ```ts
84
- import { Form, FormCloseButton, type FormProps } from "@studio-piot/native-ui";
121
+ import { Form, FormCloseButton, FORM_THEME_ROLES, type FormProps } from "@studio-piot/native-ui";
85
122
  ```
86
-
87
- Header, scroll content, and footer share one horizontal inset (default `16`). Override with `paddingHorizontal` when a screen needs a different gutter; apps keep their own layout tokens and do not import the package default.
@@ -0,0 +1,310 @@
1
+ # HeaderIcon, HeaderCapsule, HeaderGroup — usage
2
+
3
+ Content for native stack header buttons (`headerLeft` / `headerRight` in Expo Router or React Navigation). On iOS 26 the native stack wraps whatever those functions return in a `UIBarButtonItem` and draws the Liquid Glass background itself. These components supply the content only: an icon, or an icon and a label.
4
+
5
+ - **No background or border of their own on iOS 26+.** A styled button inside the glass gives two shapes, one inside the other.
6
+ - **The shape follows the content.** `HeaderIcon` is 36×36, which gives a circle. `HeaderCapsule` is wider than tall, which gives a pill.
7
+ - **One function, one glass shape.** Everything returned by `headerRight` shares one capsule; `HeaderGroup` lays several buttons out inside it.
8
+ - **Fallback.** Android and iOS 18 and earlier have no glass. The same code shows plain icons there. Pass `fallbackBackgroundColor` to draw a circle or pill on those platforms only.
9
+
10
+ The package has no Expo dependency, so the icon is a render function. It receives the resolved `color` and `size`; return an expo-symbols `SymbolView` or any other icon component.
11
+
12
+ ## HeaderIcon (round)
13
+
14
+ ```tsx
15
+ import { HeaderIcon } from "@studio-piot/native-ui";
16
+ import { SymbolView } from "expo-symbols";
17
+ import { router, Stack } from "expo-router";
18
+
19
+ <Stack.Screen
20
+ name="index"
21
+ options={{
22
+ headerLeft: () => (
23
+ <HeaderIcon
24
+ label="Close"
25
+ size={18}
26
+ icon={({ color, size }) => (
27
+ <SymbolView name="xmark" size={size} weight="semibold" tintColor={color} />
28
+ )}
29
+ onPress={() => router.back()}
30
+ />
31
+ ),
32
+ }}
33
+ />
34
+ ```
35
+
36
+ `label` is required: it is the accessibility label, and VoiceOver has nothing else to read on an icon-only button.
37
+
38
+ ## HeaderCapsule (icon and label)
39
+
40
+ ```tsx
41
+ headerLeft: () => (
42
+ <HeaderCapsule
43
+ label="Close"
44
+ icon={({ color, size }) => (
45
+ <SymbolView name="xmark" size={size} weight="semibold" tintColor={color} />
46
+ )}
47
+ onPress={() => router.back()}
48
+ />
49
+ ),
50
+ ```
51
+
52
+ `icon` is optional; without it the capsule shows the label only.
53
+
54
+ ## HeaderGroup (bundled)
55
+
56
+ Several buttons in one `HeaderGroup` share a single glass capsule. Each keeps its own size and action.
57
+
58
+ ```tsx
59
+ headerRight: () => (
60
+ <HeaderGroup>
61
+ <HeaderIcon
62
+ label="Saved routes"
63
+ size={19}
64
+ icon={({ color, size }) => <SymbolView name="bookmark" size={size} tintColor={color} />}
65
+ onPress={() => router.push("/new-activity/saved-routes")}
66
+ />
67
+ <HeaderIcon
68
+ label="Settings"
69
+ icon={({ color, size }) => <SymbolView name="gearshape" size={size} tintColor={color} />}
70
+ onPress={() => router.push("/settings")}
71
+ />
72
+ </HeaderGroup>
73
+ ),
74
+ ```
75
+
76
+ To get two separate glass circles instead of one shared capsule, each button has to be its own native bar button item, which needs the native bar-button-items API in your Expo Router or react-native-screens version.
77
+
78
+ ## Full layout
79
+
80
+ ```tsx
81
+ // app/new-activity/_layout.tsx
82
+ import {
83
+ HeaderCapsule,
84
+ HeaderGroup,
85
+ HeaderIcon,
86
+ type HeaderButtonIcon,
87
+ useThemeColor,
88
+ } from "@studio-piot/native-ui";
89
+ import { router, Stack } from "expo-router";
90
+ import { SFSymbol, SymbolView } from "expo-symbols";
91
+
92
+ const symbol =
93
+ (name: SFSymbol): HeaderButtonIcon =>
94
+ ({ color, size }) => <SymbolView name={name} size={size} weight="semibold" tintColor={color} />;
95
+
96
+ export default function NewActivityLayout() {
97
+ const background = useThemeColor("background");
98
+ const text = useThemeColor("text");
99
+
100
+ return (
101
+ <Stack
102
+ screenOptions={{
103
+ headerTintColor: text,
104
+ headerTitleStyle: { color: text },
105
+ headerStyle: { backgroundColor: background },
106
+ headerShadowVisible: false,
107
+ contentStyle: { backgroundColor: background },
108
+ }}
109
+ >
110
+ <Stack.Screen
111
+ name="index"
112
+ options={{
113
+ title: "New activity",
114
+ headerBackVisible: false,
115
+ headerLeft: () => (
116
+ <HeaderCapsule label="Close" icon={symbol("xmark")} onPress={() => router.back()} />
117
+ ),
118
+ headerRight: () => (
119
+ <HeaderGroup>
120
+ <HeaderIcon
121
+ label="Saved routes"
122
+ size={19}
123
+ icon={symbol("bookmark")}
124
+ onPress={() => router.push("/new-activity/saved-routes")}
125
+ />
126
+ <HeaderIcon
127
+ label="Settings"
128
+ icon={symbol("gearshape")}
129
+ onPress={() => router.push("/settings")}
130
+ />
131
+ </HeaderGroup>
132
+ ),
133
+ }}
134
+ />
135
+ <Stack.Screen name="running" options={{ title: "Running" }} />
136
+ </Stack>
137
+ );
138
+ }
139
+ ```
140
+
141
+ A small `symbol()` helper like this keeps call sites short. Child screens keep the default back button.
142
+
143
+ **Buttons that need screen state.** Set the options in the screen file instead of the layout:
144
+
145
+ ```tsx
146
+ export default function RunningScreen() {
147
+ const [favourite, setFavourite] = useState(false);
148
+
149
+ return (
150
+ <>
151
+ <Stack.Screen
152
+ options={{
153
+ headerRight: () => (
154
+ <HeaderIcon
155
+ label={favourite ? "Remove favourite" : "Add favourite"}
156
+ icon={symbol(favourite ? "heart.fill" : "heart")}
157
+ onPress={() => setFavourite((f) => !f)}
158
+ />
159
+ ),
160
+ }}
161
+ />
162
+ {/* screen content */}
163
+ </>
164
+ );
165
+ }
166
+ ```
167
+
168
+ ## Android and older iOS
169
+
170
+ SF Symbols are iOS only. Pick the icon per platform inside the render function, or use an icon set that works everywhere:
171
+
172
+ ```tsx
173
+ icon={({ color, size }) =>
174
+ Platform.OS === "ios" ? (
175
+ <SymbolView name="xmark" size={size} tintColor={color} />
176
+ ) : (
177
+ <MaterialIcons name="close" size={size + 4} color={color} />
178
+ )
179
+ }
180
+ ```
181
+
182
+ Without glass the buttons are plain icons. For a visible shape on those platforms only:
183
+
184
+ ```tsx
185
+ <HeaderIcon label="Close" icon={symbol("xmark")} fallbackBackgroundColor="#0000000F" onPress={close} />
186
+ ```
187
+
188
+ On iOS 26+ `fallbackBackgroundColor` is ignored, so you never get a shape inside the glass. Inside a `HeaderGroup`, put `fallbackBackgroundColor` on the group, not on each icon. `supportsLiquidGlass(Platform.OS, Platform.Version)` is exported if you need the same check elsewhere.
189
+
190
+ ## Actions
191
+
192
+ Every button takes an `onPress`, so any function works.
193
+
194
+ | Goal | Call | Notes |
195
+ |---|---|---|
196
+ | Go back one screen | `router.back()` | From the first screen of a nested stack, this closes the modal |
197
+ | Close the modal | `router.dismiss()` | When the stack is presented with `presentation: "modal"` |
198
+ | Close the whole flow from a deep screen | `router.dismissAll()` | Then `router.back()` to leave the modal |
199
+ | Open another screen | `router.push("/path")` | Adds to the history |
200
+ | Swap the current screen | `router.replace("/path")` | No back step |
201
+ | Open a sheet | `router.push("/sheet-route")` | Set `presentation: "formSheet"` on that route |
202
+ | Share | `Share.share({ message })` | From `react-native` |
203
+ | Toggle state | `setState(...)` | Define the button in the screen file |
204
+
205
+ **Safe close** — closes the modal whether or not there is something to go back to:
206
+
207
+ ```tsx
208
+ export function closeFlow() {
209
+ if (router.canDismiss()) {
210
+ router.dismissAll();
211
+ }
212
+ router.back();
213
+ }
214
+ ```
215
+
216
+ **Confirm before discarding:**
217
+
218
+ ```tsx
219
+ function confirmClose(hasChanges: boolean) {
220
+ if (!hasChanges) return router.back();
221
+
222
+ Alert.alert("Discard activity?", "Your changes will be lost.", [
223
+ { text: "Keep editing", style: "cancel" },
224
+ { text: "Discard", style: "destructive", onPress: () => router.back() },
225
+ ]);
226
+ }
227
+ ```
228
+
229
+ **Haptic tap** — wrap `onPress`:
230
+
231
+ ```tsx
232
+ onPress={() => {
233
+ Haptics.selectionAsync();
234
+ router.back();
235
+ }}
236
+ ```
237
+
238
+ ## Theme roles
239
+
240
+ Apps must add this key to both light and dark maps when `color` is omitted.
241
+
242
+ | Usage | Role |
243
+ |---|---|
244
+ | Icon tint and capsule label | `headerButton` |
245
+
246
+ Usually the same value as the header's `headerTintColor`.
247
+
248
+ ## Props
249
+
250
+ ### HeaderIcon
251
+
252
+ | Prop | Type | Default | Notes |
253
+ |---|---|---|---|
254
+ | `icon` | `HeaderButtonIcon` | — | Required. `({ color, size }) => ReactNode` |
255
+ | `label` | `string` | — | Required. Accessibility label |
256
+ | `onPress` | `() => void` | — | Required |
257
+ | `size` | `number` | `17` | Icon box. Range 15–20; above ~24 the icon crowds the circle |
258
+ | `color` | `string` | theme `headerButton` | Optional override |
259
+ | `fallbackBackgroundColor` | `string` | — | Circle on non-glass platforms only |
260
+ | `disabled` | `boolean` | `false` | 40% opacity; not pressable |
261
+ | `hitSlop` | `number` | `6` | Bigger tap area without changing the glass size |
262
+ | `style` | view style | — | Button box |
263
+
264
+ ### HeaderCapsule
265
+
266
+ | Prop | Type | Default | Notes |
267
+ |---|---|---|---|
268
+ | `label` | `string` | — | Required. Shown text and accessibility label |
269
+ | `onPress` | `() => void` | — | Required |
270
+ | `icon` | `HeaderButtonIcon` | — | Optional, before the label |
271
+ | `iconSize` | `number` | `15` | Close to the height of the 16pt label |
272
+ | `fontSize` | `number` | `16` | Label weight is 600, matching system bar button text |
273
+ | `color` | `string` | theme `headerButton` | Optional override |
274
+ | `fallbackBackgroundColor` | `string` | — | Pill on non-glass platforms only |
275
+ | `disabled` | `boolean` | `false` | 40% opacity; not pressable |
276
+ | `hitSlop` | `number` | `6` | |
277
+ | `style` | view style | — | Button row |
278
+
279
+ ### HeaderGroup
280
+
281
+ | Prop | Type | Default | Notes |
282
+ |---|---|---|---|
283
+ | `children` | `ReactNode` | — | Usually `HeaderIcon`s |
284
+ | `fallbackBackgroundColor` | `string` | — | Shared pill on non-glass platforms only |
285
+ | `style` | view style | — | Row; gap between children is 4 |
286
+
287
+ ## Gotchas
288
+
289
+ - **Thin symbols look smaller.** `xmark` fills less of its box than `gearshape` at the same size; go about 1pt bigger on thin symbols.
290
+ - **`SymbolView` `size` is a box, not a font size.** Wide or tall symbols are shrunk to fit; check alignment on a device.
291
+ - **An empty `View` still shows glass.** `headerLeft: () => <View />` draws an empty circle. Hide the button with `headerLeft: undefined`.
292
+
293
+ ## Imports
294
+
295
+ ```ts
296
+ import {
297
+ HeaderCapsule,
298
+ HeaderGroup,
299
+ HeaderIcon,
300
+ HEADER_BUTTON_THEME_ROLES,
301
+ supportsLiquidGlass,
302
+ type HeaderButtonIcon,
303
+ type HeaderCapsuleProps,
304
+ type HeaderGroupProps,
305
+ type HeaderIconProps,
306
+ } from "@studio-piot/native-ui";
307
+
308
+ // Or components only:
309
+ import { HeaderIcon } from "@studio-piot/native-ui/components";
310
+ ```
@@ -0,0 +1,40 @@
1
+ # HeaderWash — usage
2
+
3
+ A background for a transparent native header: your screen colour, nearly opaque at the top, fading to clear at the bottom of the header. The native title and buttons sit on the wash, and content fades as it scrolls up under the bar.
4
+
5
+ Use it as the stack's `headerBackground`, together with `headerTransparent: true`:
6
+
7
+ ```tsx
8
+ // app/(app)/create-activity/_layout.tsx
9
+ import { HeaderWash } from "@studio-piot/native-ui";
10
+ import { Stack } from "expo-router";
11
+
12
+ export default function Layout() {
13
+ return (
14
+ <Stack
15
+ screenOptions={{
16
+ headerTransparent: true,
17
+ headerBackground: () => <HeaderWash />,
18
+ // iOS 26 adds its own soft edge under a transparent bar; hide it so the two don't stack.
19
+ scrollEdgeEffects: { top: "hidden" },
20
+ }}
21
+ />
22
+ );
23
+ }
24
+ ```
25
+
26
+ - **Sizing and stacking come from the native stack.** With `headerTransparent: true`, the stack draws `headerBackground` above the screen content and sizes it to the header's measured height, including a large title as it collapses.
27
+ - **Colour.** Defaults to the `formBackground` role, so it matches `Form`. Pass `color` to match another screen.
28
+ - **Content.** The screen's scroll view still starts below the header (`Form` and `Page` do this), so nothing sits under the wash until you scroll.
29
+
30
+ ## Props
31
+
32
+ | Prop | Type | Default | Notes |
33
+ |---|---|---|---|
34
+ | `color` | `string` | theme `formBackground` | The wash colour, opaque end |
35
+
36
+ ## Imports
37
+
38
+ ```ts
39
+ import { HeaderWash } from "@studio-piot/native-ui";
40
+ ```