@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
@@ -0,0 +1,70 @@
1
+ import { StyleSheet, View, type LayoutChangeEvent } from "react-native";
2
+ import { KeyboardStickyView } from "react-native-keyboard-controller";
3
+ import { useSafeAreaInsets } from "react-native-safe-area-context";
4
+ import { Button } from "../Button";
5
+ import type { ButtonRadius } from "../Button/buttonRadius";
6
+
7
+ /** Sticky submit footer shared by `Form` and `FormSheet`. */
8
+ export function FormSubmitFooter({
9
+ label,
10
+ onPress,
11
+ submitting,
12
+ disabled,
13
+ radius,
14
+ paddingHorizontal,
15
+ backgroundColor,
16
+ buttonBackgroundColor,
17
+ buttonLabelColor,
18
+ onLayout,
19
+ }: {
20
+ label: string;
21
+ onPress: () => void;
22
+ submitting: boolean;
23
+ disabled: boolean;
24
+ radius: ButtonRadius;
25
+ paddingHorizontal: number;
26
+ backgroundColor: string;
27
+ buttonBackgroundColor?: string;
28
+ buttonLabelColor?: string;
29
+ /** Height of the bar above the home indicator, for scroll clearance. */
30
+ onLayout?: (event: LayoutChangeEvent) => void;
31
+ }) {
32
+ const insets = useSafeAreaInsets();
33
+
34
+ return (
35
+ // The spacer is the home-indicator clearance. `opened` equals that
36
+ // spacer, so once the keyboard is up the spacer sits inside the
37
+ // keyboard and the button rests on whatever height the keyboard is.
38
+ <KeyboardStickyView offset={{ closed: 0, opened: insets.bottom }}>
39
+ <View style={[styles.shell, { backgroundColor }]}>
40
+ <View onLayout={onLayout} style={[styles.bar, { paddingHorizontal }]}>
41
+ <Button
42
+ backgroundColor={buttonBackgroundColor}
43
+ disabled={disabled}
44
+ label={label}
45
+ labelColor={buttonLabelColor}
46
+ loading={submitting}
47
+ onPress={onPress}
48
+ radius={radius}
49
+ />
50
+ </View>
51
+ <View style={{ height: insets.bottom }} />
52
+ </View>
53
+ </KeyboardStickyView>
54
+ );
55
+ }
56
+
57
+ const styles = StyleSheet.create({
58
+ shell: {
59
+ zIndex: 2,
60
+ // Cast upward so the sticky footer separates from the scroll body.
61
+ shadowColor: "#000",
62
+ shadowOffset: { width: 0, height: -4 },
63
+ shadowOpacity: 0.08,
64
+ shadowRadius: 12,
65
+ elevation: 8,
66
+ },
67
+ bar: {
68
+ paddingVertical: 12,
69
+ },
70
+ });
@@ -0,0 +1,13 @@
1
+ import assert from "node:assert/strict";
2
+ import { describe, it } from "node:test";
3
+ import { FORM_THEME_ROLES } from "./formThemeRoles.ts";
4
+
5
+ describe("FORM_THEME_ROLES", () => {
6
+ it("maps colour props to form-prefixed roles, not the app's own names", () => {
7
+ assert.deepEqual(FORM_THEME_ROLES, {
8
+ backgroundColor: "formBackground",
9
+ footerBackgroundColor: "formFooterBackground",
10
+ closeColor: "formClose",
11
+ });
12
+ });
13
+ });
@@ -0,0 +1,7 @@
1
+ export const FORM_THEME_ROLES = {
2
+ backgroundColor: "formBackground",
3
+ footerBackgroundColor: "formFooterBackground",
4
+ closeColor: "formClose",
5
+ } as const;
6
+
7
+ export type FormThemeColorProp = keyof typeof FORM_THEME_ROLES;
@@ -1,3 +1,4 @@
1
1
  export { Form } from "./Form";
2
2
  export { FormCloseButton } from "./FormCloseButton";
3
+ export { FORM_THEME_ROLES, type FormThemeColorProp } from "./formThemeRoles";
3
4
  export type { FormProps } from "./types";
@@ -2,10 +2,12 @@ import type { ReactNode } from "react";
2
2
  import type { ButtonRadius } from "../Button/buttonRadius";
3
3
  import type { FieldError } from "../ErrorBanner/types";
4
4
 
5
+ /**
6
+ * Set the title and close button in the screen's stack options
7
+ * (`title`, `headerLeft`), not here.
8
+ */
5
9
  export type FormProps = {
6
- title: string;
7
10
  children: ReactNode;
8
- onClose: () => void;
9
11
  onSubmit: () => void;
10
12
  /**
11
13
  * Field errors for the screen banner. Empty hides it.
@@ -16,19 +18,21 @@ export type FormProps = {
16
18
  submitLabel?: string;
17
19
  submitting?: boolean;
18
20
  submitDisabled?: boolean;
19
- /** App-specific Cancel control. Defaults to a plain text Cancel. */
20
- closeButton?: ReactNode;
21
21
  submitRadius?: ButtonRadius;
22
22
  /**
23
- * Horizontal inset for header, scroll content, and footer.
24
- * Defaults to 16. Pass a value to override.
23
+ * Native header height, from `useHeaderHeight()` in
24
+ * `expo-router/react-navigation`. Used to scroll an error's field clear of
25
+ * the header. Defaults to the status bar plus a standard bar.
26
+ */
27
+ headerHeight?: number;
28
+ /**
29
+ * Horizontal inset for the scroll content and footer.
30
+ * Defaults to theme `spacing.screen`. Pass a value to override.
25
31
  */
26
32
  paddingHorizontal?: number;
27
- /** Defaults to theme role `background`. */
33
+ /** Defaults to theme role `formBackground`. */
28
34
  backgroundColor?: string;
29
- /** Defaults to theme role `text`. */
30
- titleColor?: string;
31
- /** Defaults to theme role `surface`. Footer ground. */
35
+ /** Defaults to theme role `formFooterBackground`. Footer ground. */
32
36
  footerBackgroundColor?: string;
33
37
  /** Defaults to theme role `buttonBackground`. */
34
38
  buttonBackgroundColor?: string;
@@ -0,0 +1,279 @@
1
+ import BottomSheet, {
2
+ BottomSheetHandle,
3
+ BottomSheetScrollView,
4
+ type BottomSheetHandleProps,
5
+ type BottomSheetScrollViewMethods,
6
+ } from "@gorhom/bottom-sheet";
7
+ import { useCallback, useMemo, useRef, useState } from "react";
8
+ import {
9
+ Platform,
10
+ StyleSheet,
11
+ Text,
12
+ useWindowDimensions,
13
+ View,
14
+ type LayoutChangeEvent,
15
+ } from "react-native";
16
+ import { useKeyboardState } from "react-native-keyboard-controller";
17
+ import { useSafeAreaInsets } from "react-native-safe-area-context";
18
+ import { estimateHeaderHeight } from "../../layout/headerInset";
19
+ import { SHEET_TOP_RADIUS } from "../../layout/sheet";
20
+ import { useThemeSpacing } from "../../theme/useThemeSpacing";
21
+ import {
22
+ ErrorBannerProvider,
23
+ useErrorBanner,
24
+ } from "../ErrorBanner";
25
+ import { useResolvedFieldColor } from "../Field/useResolvedFieldColor";
26
+ import { FormErrorBanner } from "../Form/FormErrorBanner";
27
+ import { FormSubmitFooter } from "../Form/FormSubmitFooter";
28
+ import {
29
+ formSheetHandleFallback,
30
+ formSheetSnapPoints,
31
+ sheetBannerOverlap,
32
+ } from "./formSheetLayout";
33
+ import { FORM_SHEET_THEME_ROLES } from "./formSheetThemeRoles";
34
+ import type { FormSheetInset, FormSheetProps, FormSheetSnapIndex } from "./types";
35
+
36
+ const EXPANDED: FormSheetSnapIndex = 2;
37
+
38
+ function useMeasuredHeight(initial = 0) {
39
+ const [height, setHeight] = useState(initial);
40
+ const onLayout = useCallback((event: LayoutChangeEvent) => {
41
+ const next = Math.round(event.nativeEvent.layout.height);
42
+ setHeight((current) => (current === next ? current : next));
43
+ }, []);
44
+ return [height, onLayout] as const;
45
+ }
46
+
47
+ /**
48
+ * A form in a draggable sheet over a full-screen background, usually a map.
49
+ * Three stops: the handle and title, half the screen, and up to the native
50
+ * header. The submit button stays above the keyboard. Set the close button
51
+ * in the screen's stack options, with `headerTransparent: true` so the
52
+ * background shows behind the header.
53
+ */
54
+ export function FormSheet({
55
+ title,
56
+ children,
57
+ background,
58
+ onSubmit,
59
+ errors = [],
60
+ submitLabel = "Save",
61
+ submitting = false,
62
+ submitDisabled = false,
63
+ submitRadius = "lg",
64
+ initialIndex = 0,
65
+ headerHeight: headerHeightProp,
66
+ paddingHorizontal: paddingHorizontalProp,
67
+ backgroundColor: backgroundColorProp,
68
+ titleColor: titleColorProp,
69
+ footerBackgroundColor: footerBackgroundColorProp,
70
+ handleColor: handleColorProp,
71
+ buttonBackgroundColor,
72
+ buttonLabelColor,
73
+ }: FormSheetProps) {
74
+ const insets = useSafeAreaInsets();
75
+ const spacing = useThemeSpacing();
76
+ const paddingHorizontal = paddingHorizontalProp ?? spacing.screen;
77
+ const { height: windowHeight } = useWindowDimensions();
78
+ const backgroundColor = useResolvedFieldColor(
79
+ backgroundColorProp,
80
+ FORM_SHEET_THEME_ROLES.backgroundColor,
81
+ );
82
+ const titleColor = useResolvedFieldColor(
83
+ titleColorProp,
84
+ FORM_SHEET_THEME_ROLES.titleColor,
85
+ );
86
+ const footerBackground = useResolvedFieldColor(
87
+ footerBackgroundColorProp,
88
+ FORM_SHEET_THEME_ROLES.footerBackgroundColor,
89
+ );
90
+ const handleColor = useResolvedFieldColor(
91
+ handleColorProp,
92
+ FORM_SHEET_THEME_ROLES.handleColor,
93
+ );
94
+
95
+ const keyboardHeight = useKeyboardState((state) => state.height);
96
+ const keyboardLift = Math.max(0, keyboardHeight - insets.bottom);
97
+
98
+ const [footerHeight, onFooterLayout] = useMeasuredHeight();
99
+ const [handleHeight, onHandleLayout] = useMeasuredHeight(
100
+ formSheetHandleFallback(Boolean(title)),
101
+ );
102
+ const [sheetAreaHeight, onSheetAreaLayout] = useMeasuredHeight();
103
+ const [snapIndex, setSnapIndex] = useState<FormSheetSnapIndex>(initialIndex);
104
+
105
+ // Estimates until the first layout pass reports real sizes.
106
+ const resolvedHeader =
107
+ headerHeightProp ?? estimateHeaderHeight(Platform.OS, insets.top);
108
+ const resolvedFooter = footerHeight || insets.bottom + 76;
109
+ const resolvedArea =
110
+ sheetAreaHeight || Math.max(0, windowHeight - resolvedFooter - keyboardLift);
111
+
112
+ const snapPoints = useMemo(
113
+ () =>
114
+ formSheetSnapPoints({
115
+ windowHeight,
116
+ sheetAreaHeight: resolvedArea,
117
+ headerHeight: resolvedHeader,
118
+ handleHeight,
119
+ }),
120
+ [handleHeight, resolvedArea, resolvedHeader, windowHeight],
121
+ );
122
+
123
+ const sheetRef = useRef<BottomSheet>(null);
124
+ const scrollRef = useRef<BottomSheetScrollViewMethods>(null);
125
+ const contentRef = useRef<View>(null);
126
+ const bannerRawHeightRef = useRef(0);
127
+ const bannerOverlapRef = useRef(0);
128
+ const scrollTopRef = useRef(0);
129
+ scrollTopRef.current = resolvedHeader + handleHeight;
130
+ const banner = useErrorBanner(errors);
131
+
132
+ // A banner row expands the sheet first, so the field has room to show.
133
+ const expandForJump = useCallback(() => {
134
+ sheetRef.current?.snapToIndex(EXPANDED);
135
+ bannerOverlapRef.current = sheetBannerOverlap(
136
+ bannerRawHeightRef.current,
137
+ scrollTopRef.current,
138
+ );
139
+ }, []);
140
+
141
+ const renderHandle = useCallback(
142
+ (props: BottomSheetHandleProps) => (
143
+ <View
144
+ onLayout={onHandleLayout}
145
+ style={[styles.handle, { backgroundColor, paddingHorizontal }]}
146
+ >
147
+ <BottomSheetHandle
148
+ {...props}
149
+ indicatorStyle={{ backgroundColor: handleColor }}
150
+ />
151
+ {title ? (
152
+ <Text
153
+ accessibilityRole="header"
154
+ numberOfLines={1}
155
+ style={[styles.title, { color: titleColor }]}
156
+ >
157
+ {title}
158
+ </Text>
159
+ ) : null}
160
+ </View>
161
+ ),
162
+ [backgroundColor, handleColor, onHandleLayout, paddingHorizontal, title, titleColor],
163
+ );
164
+
165
+ const inset: FormSheetInset = {
166
+ top: resolvedHeader,
167
+ bottom: resolvedFooter + snapPoints[snapIndex],
168
+ bottomAtHalf: resolvedFooter + snapPoints[1],
169
+ };
170
+
171
+ return (
172
+ <ErrorBannerProvider
173
+ bannerHeightRef={bannerOverlapRef}
174
+ errors={errors}
175
+ measureRef={contentRef}
176
+ onBeforeJump={expandForJump}
177
+ onDismiss={banner.dismiss}
178
+ scrollRef={scrollRef}
179
+ >
180
+ <View style={[styles.screen, { backgroundColor }]}>
181
+ <View pointerEvents="box-none" style={StyleSheet.absoluteFill}>
182
+ {background(inset)}
183
+ </View>
184
+
185
+ <View
186
+ onLayout={onSheetAreaLayout}
187
+ pointerEvents="box-none"
188
+ style={[styles.sheetArea, { marginBottom: keyboardLift }]}
189
+ >
190
+ <BottomSheet
191
+ ref={sheetRef}
192
+ animateOnMount={false}
193
+ index={initialIndex}
194
+ backgroundStyle={[styles.sheet, { backgroundColor }]}
195
+ enableContentPanningGesture
196
+ enableDynamicSizing={false}
197
+ keyboardBehavior="extend"
198
+ enablePanDownToClose={false}
199
+ handleComponent={renderHandle}
200
+ onChange={(next: number) => {
201
+ // -1 is closed; the sheet only has three stops.
202
+ if (next === 0 || next === 1 || next === 2) {
203
+ setSnapIndex(next);
204
+ }
205
+ }}
206
+ snapPoints={snapPoints}
207
+ >
208
+ <BottomSheetScrollView
209
+ ref={scrollRef}
210
+ contentContainerStyle={[styles.content, { paddingHorizontal }]}
211
+ keyboardShouldPersistTaps="handled"
212
+ >
213
+ {/* Field positions are measured from here: the top of the content. */}
214
+ <View ref={contentRef} collapsable={false} style={styles.contentTop}>
215
+ {children}
216
+ </View>
217
+ </BottomSheetScrollView>
218
+ </BottomSheet>
219
+ </View>
220
+
221
+ <FormSubmitFooter
222
+ backgroundColor={footerBackground}
223
+ buttonBackgroundColor={buttonBackgroundColor}
224
+ buttonLabelColor={buttonLabelColor}
225
+ disabled={submitDisabled}
226
+ label={submitLabel}
227
+ onLayout={onFooterLayout}
228
+ onPress={() => {
229
+ banner.present();
230
+ onSubmit();
231
+ }}
232
+ paddingHorizontal={paddingHorizontal}
233
+ radius={submitRadius}
234
+ submitting={submitting}
235
+ />
236
+
237
+ <FormErrorBanner
238
+ androidTop={resolvedHeader}
239
+ errors={errors}
240
+ onDismiss={banner.dismiss}
241
+ onHeightChange={(height) => {
242
+ bannerRawHeightRef.current = height;
243
+ }}
244
+ open={banner.open}
245
+ />
246
+ </View>
247
+ </ErrorBannerProvider>
248
+ );
249
+ }
250
+
251
+ const styles = StyleSheet.create({
252
+ screen: {
253
+ flex: 1,
254
+ },
255
+ sheetArea: {
256
+ flex: 1,
257
+ },
258
+ sheet: {
259
+ borderTopLeftRadius: SHEET_TOP_RADIUS,
260
+ borderTopRightRadius: SHEET_TOP_RADIUS,
261
+ },
262
+ handle: {
263
+ alignItems: "center",
264
+ paddingBottom: 12,
265
+ borderTopLeftRadius: SHEET_TOP_RADIUS,
266
+ borderTopRightRadius: SHEET_TOP_RADIUS,
267
+ },
268
+ title: {
269
+ fontSize: 17,
270
+ fontWeight: "600",
271
+ textAlign: "center",
272
+ },
273
+ content: {
274
+ paddingBottom: 24,
275
+ },
276
+ contentTop: {
277
+ paddingTop: 8,
278
+ },
279
+ });
@@ -0,0 +1,66 @@
1
+ import assert from "node:assert/strict";
2
+ import { describe, it } from "node:test";
3
+ import {
4
+ FORM_SHEET_HANDLE_FALLBACK,
5
+ formSheetHandleFallback,
6
+ formSheetSnapPoints,
7
+ sheetBannerOverlap,
8
+ } from "./formSheetLayout.ts";
9
+
10
+ describe("formSheetSnapPoints", () => {
11
+ it("returns the handle, half the screen, and up to the header", () => {
12
+ const [collapsed, half, full] = formSheetSnapPoints({
13
+ windowHeight: 800,
14
+ sheetAreaHeight: 720,
15
+ headerHeight: 100,
16
+ handleHeight: 72,
17
+ });
18
+ assert.equal(collapsed, 72);
19
+ assert.equal(half, 320);
20
+ assert.equal(full, 620);
21
+ });
22
+
23
+ it("keeps half between the handle and the header when space is tight", () => {
24
+ const [collapsed, half, full] = formSheetSnapPoints({
25
+ windowHeight: 800,
26
+ sheetAreaHeight: 420,
27
+ headerHeight: 100,
28
+ handleHeight: 72,
29
+ });
30
+ assert.equal(collapsed, 72);
31
+ assert.ok(half > collapsed);
32
+ assert.ok(half < full);
33
+ assert.equal(full, 320);
34
+ });
35
+
36
+ it("always returns three increasing heights, even with no room", () => {
37
+ const [collapsed, half, full] = formSheetSnapPoints({
38
+ windowHeight: 800,
39
+ sheetAreaHeight: 80,
40
+ headerHeight: 100,
41
+ handleHeight: 72,
42
+ });
43
+ assert.ok(collapsed < half && half < full);
44
+ });
45
+ });
46
+
47
+ describe("sheetBannerOverlap", () => {
48
+ it("is how far the banner reaches into the expanded sheet's scroll area", () => {
49
+ assert.equal(sheetBannerOverlap(180, 150), 30);
50
+ });
51
+
52
+ it("is zero when the banner ends above the scroll area", () => {
53
+ assert.equal(sheetBannerOverlap(120, 150), 0);
54
+ });
55
+ });
56
+
57
+ describe("formSheetHandleFallback", () => {
58
+ it("leaves room for the title line before the handle is measured", () => {
59
+ assert.equal(formSheetHandleFallback(true), FORM_SHEET_HANDLE_FALLBACK.withTitle);
60
+ assert.ok(FORM_SHEET_HANDLE_FALLBACK.withTitle > FORM_SHEET_HANDLE_FALLBACK.indicatorOnly);
61
+ });
62
+
63
+ it("is just the indicator without a title", () => {
64
+ assert.equal(formSheetHandleFallback(false), FORM_SHEET_HANDLE_FALLBACK.indicatorOnly);
65
+ });
66
+ });
@@ -0,0 +1,55 @@
1
+ export type FormSheetSnapInput = {
2
+ /** Full window height, used to place the middle stop at half the screen. */
3
+ windowHeight: number;
4
+ /** Height of the sheet's parent: the screen above the footer and keyboard. */
5
+ sheetAreaHeight: number;
6
+ /** Header block the expanded sheet stops beneath. */
7
+ headerHeight: number;
8
+ /** Collapsed sheet: drag handle plus title. */
9
+ handleHeight: number;
10
+ };
11
+
12
+ /**
13
+ * Three sheet heights, shortest first. Minimum is the handle. Middle puts the
14
+ * sheet top at half the screen. Maximum stops at the bottom of the header.
15
+ * Pure so it can be unit-tested.
16
+ */
17
+ export function formSheetSnapPoints({
18
+ windowHeight,
19
+ sheetAreaHeight,
20
+ headerHeight,
21
+ handleHeight,
22
+ }: FormSheetSnapInput): [number, number, number] {
23
+ const collapsed = Math.max(1, Math.round(handleHeight));
24
+ const full = Math.max(collapsed, Math.round(sheetAreaHeight - headerHeight));
25
+ const halfRaw = Math.round(sheetAreaHeight - windowHeight / 2);
26
+ // The sheet needs three distinct, increasing stops even when there is
27
+ // almost no room, so nudge each one above the last.
28
+ const half = Math.max(collapsed + 1, Math.min(full, halfRaw));
29
+ const top = Math.max(half + 1, full);
30
+ return [collapsed, half, top];
31
+ }
32
+
33
+ /**
34
+ * How far the error banner reaches into the expanded sheet's scroll area,
35
+ * whose top sits `scrollTop` below the screen top (header plus handle).
36
+ * A banner row scrolls its field this much further so it is not hidden.
37
+ */
38
+ export function sheetBannerOverlap(bannerHeight: number, scrollTop: number): number {
39
+ return Math.max(0, bannerHeight - scrollTop);
40
+ }
41
+
42
+ /** Handle heights before the first layout pass measures the real one. */
43
+ export const FORM_SHEET_HANDLE_FALLBACK = {
44
+ /** Drag indicator and padding. */
45
+ indicatorOnly: 36,
46
+ /** Indicator plus one title line. */
47
+ withTitle: 64,
48
+ } as const;
49
+
50
+ /** The collapsed stop is the handle, so it must fit the title when there is one. */
51
+ export function formSheetHandleFallback(hasTitle: boolean): number {
52
+ return hasTitle
53
+ ? FORM_SHEET_HANDLE_FALLBACK.withTitle
54
+ : FORM_SHEET_HANDLE_FALLBACK.indicatorOnly;
55
+ }
@@ -0,0 +1,14 @@
1
+ import assert from "node:assert/strict";
2
+ import { describe, it } from "node:test";
3
+ import { FORM_SHEET_THEME_ROLES } from "./formSheetThemeRoles.ts";
4
+
5
+ describe("FORM_SHEET_THEME_ROLES", () => {
6
+ it("maps colour props to formSheet-prefixed roles", () => {
7
+ assert.deepEqual(FORM_SHEET_THEME_ROLES, {
8
+ backgroundColor: "formSheetBackground",
9
+ titleColor: "formSheetTitle",
10
+ footerBackgroundColor: "formSheetFooterBackground",
11
+ handleColor: "formSheetHandle",
12
+ });
13
+ });
14
+ });
@@ -0,0 +1,8 @@
1
+ export const FORM_SHEET_THEME_ROLES = {
2
+ backgroundColor: "formSheetBackground",
3
+ titleColor: "formSheetTitle",
4
+ footerBackgroundColor: "formSheetFooterBackground",
5
+ handleColor: "formSheetHandle",
6
+ } as const;
7
+
8
+ export type FormSheetThemeColorProp = keyof typeof FORM_SHEET_THEME_ROLES;
@@ -0,0 +1,7 @@
1
+ export { FormSheet } from "./FormSheet";
2
+ export { formSheetSnapPoints, type FormSheetSnapInput } from "./formSheetLayout";
3
+ export {
4
+ FORM_SHEET_THEME_ROLES,
5
+ type FormSheetThemeColorProp,
6
+ } from "./formSheetThemeRoles";
7
+ export type { FormSheetInset, FormSheetProps, FormSheetSnapIndex } from "./types";
@@ -0,0 +1,66 @@
1
+ import type { ReactNode } from "react";
2
+ import type { ButtonRadius } from "../Button/buttonRadius";
3
+ import type { FieldError } from "../ErrorBanner/types";
4
+
5
+ /** Where the chrome sits over the background layer, in points from the screen edges. */
6
+ export type FormSheetInset = {
7
+ /** Bottom of the native header. */
8
+ top: number;
9
+ /** Top of the sheet at its current stop, measured from the screen bottom. */
10
+ bottom: number;
11
+ /** Top of the sheet at its middle stop. Steady while dragging, e.g. for map camera padding. */
12
+ bottomAtHalf: number;
13
+ };
14
+
15
+ export type FormSheetSnapIndex = 0 | 1 | 2;
16
+
17
+ /**
18
+ * Set the close button in the screen's stack options (`headerLeft`), with
19
+ * `headerTransparent: true`. The form's title lives on the sheet (`title`).
20
+ */
21
+ export type FormSheetProps = {
22
+ /**
23
+ * Shown at the top of the sheet, under the drag indicator. The lowest stop
24
+ * always shows it.
25
+ */
26
+ title?: string;
27
+ children: ReactNode;
28
+ /**
29
+ * Full-screen layer behind the sheet, usually a map. Receives the chrome
30
+ * insets so it can keep its own controls clear of the header and sheet.
31
+ */
32
+ background: (inset: FormSheetInset) => ReactNode;
33
+ onSubmit: () => void;
34
+ /**
35
+ * Field errors for the screen banner. Empty hides it. A banner row
36
+ * expands the sheet and scrolls to the field. Editing a field dismisses
37
+ * the banner; the next submit shows it again.
38
+ */
39
+ errors?: readonly FieldError[];
40
+ submitLabel?: string;
41
+ submitting?: boolean;
42
+ submitDisabled?: boolean;
43
+ submitRadius?: ButtonRadius;
44
+ /** Stop on first paint: `0` the handle and title, `1` half the screen, `2` up to the header. Default `0`. */
45
+ initialIndex?: FormSheetSnapIndex;
46
+ /**
47
+ * Native header height, from `useHeaderHeight()` in
48
+ * `expo-router/react-navigation`. The top stop ends here. Defaults to the
49
+ * status bar plus a standard bar.
50
+ */
51
+ headerHeight?: number;
52
+ /** Side inset for the sheet content and footer. Defaults to theme `spacing.screen`. */
53
+ paddingHorizontal?: number;
54
+ /** Sheet fill. Defaults to theme role `formSheetBackground`. */
55
+ backgroundColor?: string;
56
+ /** Sheet title. Defaults to theme role `formSheetTitle`. */
57
+ titleColor?: string;
58
+ /** Footer ground. Defaults to theme role `formSheetFooterBackground`. */
59
+ footerBackgroundColor?: string;
60
+ /** Drag indicator. Defaults to theme role `formSheetHandle`. */
61
+ handleColor?: string;
62
+ /** Defaults to theme role `buttonBackground`. */
63
+ buttonBackgroundColor?: string;
64
+ /** Defaults to theme role `buttonLabel`. */
65
+ buttonLabelColor?: string;
66
+ };