@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,150 @@
1
+ import { Platform, Pressable, StyleSheet, Text, View } from "react-native";
2
+ import { useThemeSpacing } from "../../theme/useThemeSpacing";
3
+ import { Button } from "../Button";
4
+ import { useResolvedFieldColor } from "../Field/useResolvedFieldColor";
5
+ import { supportsLiquidGlass } from "../HeaderButton/headerButtonLayout";
6
+ import {
7
+ TAB_ACCESSORY_FONT_SIZE,
8
+ TAB_ACCESSORY_ICON_SIZE,
9
+ tabAccessoryVisible,
10
+ } from "./tabAccessoryLayout";
11
+ import { tabAccessoryRoles } from "./tabAccessoryThemeRoles";
12
+ import type { TabAccessoryButtonProps } from "./types";
13
+
14
+ const GLASS = supportsLiquidGlass(Platform.OS, Platform.Version);
15
+
16
+ /**
17
+ * Action above the bottom tabs. On iOS 26+ it is content for
18
+ * `NativeTabs.BottomAccessory`, which draws the glass. Elsewhere it floats
19
+ * a filled pill at the bottom of the screen.
20
+ */
21
+ export function TabAccessoryButton(props: TabAccessoryButtonProps) {
22
+ if (!tabAccessoryVisible(props.placement, GLASS)) {
23
+ return null;
24
+ }
25
+ if (props.placement === "screen") {
26
+ return <FloatingPill {...props} />;
27
+ }
28
+ return props.variant === "filled" ? (
29
+ <FilledContent {...props} />
30
+ ) : (
31
+ <GlassContent {...props} />
32
+ );
33
+ }
34
+
35
+ function GlassContent({
36
+ label,
37
+ onPress,
38
+ icon,
39
+ disabled = false,
40
+ color: colorProp,
41
+ }: TabAccessoryButtonProps) {
42
+ const color = useResolvedFieldColor(colorProp, tabAccessoryRoles("glass").content);
43
+
44
+ return (
45
+ <Pressable
46
+ accessibilityLabel={label}
47
+ accessibilityRole="button"
48
+ accessibilityState={{ disabled }}
49
+ disabled={disabled}
50
+ onPress={onPress}
51
+ style={({ pressed }) => [
52
+ styles.content,
53
+ { opacity: disabled ? 0.4 : pressed ? 0.5 : 1 },
54
+ ]}
55
+ >
56
+ {icon?.({ color, size: TAB_ACCESSORY_ICON_SIZE })}
57
+ <Text style={[styles.label, { color }]}>{label}</Text>
58
+ </Pressable>
59
+ );
60
+ }
61
+
62
+ /** Covers the accessory glass edge to edge, so there is no shape inside the glass. */
63
+ function FilledContent({
64
+ label,
65
+ onPress,
66
+ icon,
67
+ disabled = false,
68
+ backgroundColor: backgroundColorProp,
69
+ labelColor: labelColorProp,
70
+ }: TabAccessoryButtonProps) {
71
+ const roles = tabAccessoryRoles("filled");
72
+ const fill = useResolvedFieldColor(backgroundColorProp, roles.fill as string);
73
+ const color = useResolvedFieldColor(labelColorProp, roles.content);
74
+
75
+ return (
76
+ <Pressable
77
+ accessibilityLabel={label}
78
+ accessibilityRole="button"
79
+ accessibilityState={{ disabled }}
80
+ disabled={disabled}
81
+ onPress={onPress}
82
+ style={({ pressed }) => [
83
+ styles.content,
84
+ styles.filled,
85
+ { backgroundColor: fill, opacity: disabled ? 0.4 : pressed ? 0.75 : 1 },
86
+ ]}
87
+ >
88
+ {icon?.({ color, size: TAB_ACCESSORY_ICON_SIZE })}
89
+ <Text style={[styles.label, { color }]}>{label}</Text>
90
+ </Pressable>
91
+ );
92
+ }
93
+
94
+ function FloatingPill({
95
+ label,
96
+ onPress,
97
+ icon,
98
+ disabled,
99
+ backgroundColor,
100
+ labelColor,
101
+ }: TabAccessoryButtonProps) {
102
+ const { screen } = useThemeSpacing();
103
+
104
+ return (
105
+ <View
106
+ pointerEvents="box-none"
107
+ style={[styles.floating, { left: screen, right: screen, bottom: screen }]}
108
+ >
109
+ <Button
110
+ backgroundColor={backgroundColor}
111
+ disabled={disabled}
112
+ icon={icon}
113
+ label={label}
114
+ labelColor={labelColor}
115
+ onPress={onPress}
116
+ radius="full"
117
+ style={styles.pillShadow}
118
+ />
119
+ </View>
120
+ );
121
+ }
122
+
123
+ const styles = StyleSheet.create({
124
+ content: {
125
+ flex: 1,
126
+ alignSelf: "stretch",
127
+ flexDirection: "row",
128
+ alignItems: "center",
129
+ justifyContent: "center",
130
+ gap: 8,
131
+ paddingHorizontal: 16,
132
+ },
133
+ filled: {
134
+ borderRadius: 999,
135
+ },
136
+ label: {
137
+ fontSize: TAB_ACCESSORY_FONT_SIZE,
138
+ fontWeight: "600",
139
+ },
140
+ floating: {
141
+ position: "absolute",
142
+ },
143
+ pillShadow: {
144
+ shadowColor: "#000000",
145
+ shadowOpacity: 0.18,
146
+ shadowRadius: 12,
147
+ shadowOffset: { width: 0, height: 6 },
148
+ elevation: 6,
149
+ },
150
+ });
@@ -0,0 +1,12 @@
1
+ export { TabAccessoryButton } from "./TabAccessoryButton";
2
+ export {
3
+ TAB_ACCESSORY_SCREEN_INSET,
4
+ tabAccessoryScreenInset,
5
+ type TabAccessoryPlacement,
6
+ } from "./tabAccessoryLayout";
7
+ export {
8
+ TAB_ACCESSORY_THEME_ROLES,
9
+ type TabAccessoryThemeColorProp,
10
+ type TabAccessoryVariant,
11
+ } from "./tabAccessoryThemeRoles";
12
+ export type { TabAccessoryButtonProps } from "./types";
@@ -0,0 +1,42 @@
1
+ import assert from "node:assert/strict";
2
+ import { describe, it } from "node:test";
3
+ import {
4
+ TAB_ACCESSORY_FALLBACK_HEIGHT,
5
+ TAB_ACCESSORY_SCREEN_INSET,
6
+ tabAccessoryScreenClearance,
7
+ tabAccessoryScreenInset,
8
+ tabAccessoryVisible,
9
+ } from "./tabAccessoryLayout.ts";
10
+
11
+ describe("tabAccessoryVisible", () => {
12
+ it("shows the accessory copy only where iOS draws glass", () => {
13
+ assert.equal(tabAccessoryVisible("accessory", true), true);
14
+ assert.equal(tabAccessoryVisible("accessory", false), false);
15
+ });
16
+
17
+ it("shows the in-screen copy only where there is no glass", () => {
18
+ assert.equal(tabAccessoryVisible("screen", false), true);
19
+ assert.equal(tabAccessoryVisible("screen", true), false);
20
+ });
21
+ });
22
+
23
+ describe("tabAccessoryScreenInset", () => {
24
+ it("leaves the pill height plus an even gap above and below it", () => {
25
+ assert.equal(tabAccessoryScreenInset(16), TAB_ACCESSORY_FALLBACK_HEIGHT + 32);
26
+ assert.equal(tabAccessoryScreenInset(20), TAB_ACCESSORY_FALLBACK_HEIGHT + 40);
27
+ });
28
+
29
+ it("matches TAB_ACCESSORY_SCREEN_INSET with the default 16 spacing", () => {
30
+ assert.equal(tabAccessoryScreenInset(16), TAB_ACCESSORY_SCREEN_INSET);
31
+ });
32
+ });
33
+
34
+ describe("tabAccessoryScreenClearance", () => {
35
+ it("clears the floating fallback pill where there is no glass", () => {
36
+ assert.equal(tabAccessoryScreenClearance(false, 20), tabAccessoryScreenInset(20));
37
+ });
38
+
39
+ it("adds nothing on iOS 26, where the accessory is in the native tab bar", () => {
40
+ assert.equal(tabAccessoryScreenClearance(true, 20), 0);
41
+ });
42
+ });
@@ -0,0 +1,43 @@
1
+ export type TabAccessoryPlacement = "accessory" | "screen";
2
+
3
+ export const TAB_ACCESSORY_ICON_SIZE = 20;
4
+ export const TAB_ACCESSORY_FONT_SIZE = 17;
5
+ /** Same minimum height as `Button`. */
6
+ export const TAB_ACCESSORY_FALLBACK_HEIGHT = 52;
7
+
8
+ /**
9
+ * Bottom padding for a screen's scroll content so the fallback pill never
10
+ * hides the last item. The pill sits `screen` above the tabs (the same gap
11
+ * as its sides), so the content needs that gap below and above it.
12
+ * Pass `useThemeSpacing().screen`. Pure so it can be unit-tested.
13
+ */
14
+ export function tabAccessoryScreenInset(screen: number): number {
15
+ return TAB_ACCESSORY_FALLBACK_HEIGHT + screen * 2;
16
+ }
17
+
18
+ /** `tabAccessoryScreenInset` with the default 16 spacing. */
19
+ export const TAB_ACCESSORY_SCREEN_INSET = tabAccessoryScreenInset(16);
20
+
21
+ /**
22
+ * Mount one copy in `NativeTabs.BottomAccessory` and one in the screen;
23
+ * each renders only where it belongs. iOS 26+ has the glass accessory,
24
+ * other platforms float a pill inside the screen. Pure so it can be unit-tested.
25
+ */
26
+ export function tabAccessoryVisible(
27
+ placement: TabAccessoryPlacement,
28
+ glass: boolean,
29
+ ): boolean {
30
+ return placement === "accessory" ? glass : !glass;
31
+ }
32
+
33
+ /**
34
+ * Extra bottom room a scrolling screen needs for its in-screen copy.
35
+ * Only the fallback pill floats over the screen; on iOS 26+ the accessory
36
+ * is part of the native tab bar. Pure so it can be unit-tested.
37
+ */
38
+ export function tabAccessoryScreenClearance(
39
+ glass: boolean,
40
+ screen: number,
41
+ ): number {
42
+ return glass ? 0 : tabAccessoryScreenInset(screen);
43
+ }
@@ -0,0 +1,28 @@
1
+ import assert from "node:assert/strict";
2
+ import { describe, it } from "node:test";
3
+ import {
4
+ TAB_ACCESSORY_THEME_ROLES,
5
+ tabAccessoryRoles,
6
+ } from "./tabAccessoryThemeRoles.ts";
7
+
8
+ describe("TAB_ACCESSORY_THEME_ROLES", () => {
9
+ it("maps the glass content colour to a dedicated role", () => {
10
+ assert.equal(TAB_ACCESSORY_THEME_ROLES.color, "tabAccessoryLabel");
11
+ });
12
+ });
13
+
14
+ describe("tabAccessoryRoles", () => {
15
+ it("draws glass content in the accessory label colour with no fill", () => {
16
+ assert.deepEqual(tabAccessoryRoles("glass"), {
17
+ fill: undefined,
18
+ content: "tabAccessoryLabel",
19
+ });
20
+ });
21
+
22
+ it("fills with the button colours so it matches the fallback pill", () => {
23
+ assert.deepEqual(tabAccessoryRoles("filled"), {
24
+ fill: "buttonBackground",
25
+ content: "buttonLabel",
26
+ });
27
+ });
28
+ });
@@ -0,0 +1,22 @@
1
+ export const TAB_ACCESSORY_THEME_ROLES = {
2
+ color: "tabAccessoryLabel",
3
+ } as const;
4
+
5
+ export type TabAccessoryThemeColorProp = keyof typeof TAB_ACCESSORY_THEME_ROLES;
6
+
7
+ export type TabAccessoryVariant = "glass" | "filled";
8
+
9
+ /**
10
+ * Theme roles for the accessory content. Glass draws only an icon and label
11
+ * on the system glass; filled covers the glass with the button colours.
12
+ */
13
+ export function tabAccessoryRoles(variant: TabAccessoryVariant): {
14
+ fill: string | undefined;
15
+ content: string;
16
+ } {
17
+ if (variant === "filled") {
18
+ // Same roles as Button (BUTTON_THEME_ROLES), so it matches the fallback pill.
19
+ return { fill: "buttonBackground", content: "buttonLabel" };
20
+ }
21
+ return { fill: undefined, content: TAB_ACCESSORY_THEME_ROLES.color };
22
+ }
@@ -0,0 +1,27 @@
1
+ import type { ButtonIcon } from "../Button";
2
+ import type { TabAccessoryPlacement } from "./tabAccessoryLayout";
3
+ import type { TabAccessoryVariant } from "./tabAccessoryThemeRoles";
4
+
5
+ export type TabAccessoryButtonProps = {
6
+ /**
7
+ * `"accessory"` inside `NativeTabs.BottomAccessory` (renders on iOS 26+ only).
8
+ * `"screen"` inside the tab screen (renders on Android and iOS 18 and earlier only).
9
+ */
10
+ placement: TabAccessoryPlacement;
11
+ label: string;
12
+ onPress: () => void;
13
+ icon?: ButtonIcon;
14
+ disabled?: boolean;
15
+ /**
16
+ * iOS 26+ look. `"glass"` (default): icon and label on the system glass.
17
+ * `"filled"`: a pill in the button colours covers the glass. The fallback
18
+ * pill on other platforms is always filled.
19
+ */
20
+ variant?: TabAccessoryVariant;
21
+ /** Glass variant content colour. Defaults to theme role `tabAccessoryLabel`. */
22
+ color?: string;
23
+ /** Filled pill fill (filled variant and fallback). Defaults to theme role `buttonBackground`. */
24
+ backgroundColor?: string;
25
+ /** Filled pill label and icon (filled variant and fallback). Defaults to theme role `buttonLabel`. */
26
+ labelColor?: string;
27
+ };
@@ -1,9 +1,22 @@
1
1
  export {
2
2
  Button,
3
- type ButtonProps,
3
+ BUTTON_ICON_SIZE,
4
+ BUTTON_THEME_ROLES,
4
5
  buttonRadiusValue,
6
+ type ButtonIcon,
7
+ type ButtonIconPosition,
8
+ type ButtonProps,
5
9
  type ButtonRadius,
10
+ type ButtonThemeColorProp,
11
+ type ButtonVariant,
6
12
  } from "./Button";
13
+ export {
14
+ Choice,
15
+ CHOICE_ICON_SIZE,
16
+ CHOICE_THEME_ROLES,
17
+ type ChoiceProps,
18
+ type ChoiceThemeColorProp,
19
+ } from "./Choice";
7
20
  export {
8
21
  LinkBlocks,
9
22
  filterLinkBlockGroups,
@@ -20,7 +33,23 @@ export {
20
33
  type FieldTextProps,
21
34
  type FieldTextareaProps,
22
35
  } from "./Field";
23
- export { Form, FormCloseButton, type FormProps } from "./Form";
36
+ export {
37
+ Form,
38
+ FormCloseButton,
39
+ FORM_THEME_ROLES,
40
+ type FormProps,
41
+ type FormThemeColorProp,
42
+ } from "./Form";
43
+ export {
44
+ FormSheet,
45
+ FORM_SHEET_THEME_ROLES,
46
+ formSheetSnapPoints,
47
+ type FormSheetInset,
48
+ type FormSheetProps,
49
+ type FormSheetSnapIndex,
50
+ type FormSheetSnapInput,
51
+ type FormSheetThemeColorProp,
52
+ } from "./FormSheet";
24
53
  export {
25
54
  ErrorBanner,
26
55
  ErrorBannerField,
@@ -32,6 +61,7 @@ export {
32
61
  useErrorBannerField,
33
62
  type ErrorBannerThemeColorProp,
34
63
  type ErrorBannerProps,
64
+ type ErrorBannerScrollTarget,
35
65
  type FieldError,
36
66
  } from "./ErrorBanner";
37
67
  export {
@@ -67,3 +97,35 @@ export {
67
97
  type SliderThemeColorProp,
68
98
  type SliderProps,
69
99
  } from "./Slider";
100
+ export {
101
+ HeaderCapsule,
102
+ HeaderGroup,
103
+ HeaderIcon,
104
+ HEADER_BUTTON_THEME_ROLES,
105
+ supportsLiquidGlass,
106
+ type HeaderButtonThemeColorProp,
107
+ type HeaderButtonIcon,
108
+ type HeaderCapsuleProps,
109
+ type HeaderGroupProps,
110
+ type HeaderIconProps,
111
+ } from "./HeaderButton";
112
+ export { HeaderWash } from "./HeaderWash";
113
+ export {
114
+ TabAccessoryButton,
115
+ TAB_ACCESSORY_SCREEN_INSET,
116
+ TAB_ACCESSORY_THEME_ROLES,
117
+ tabAccessoryScreenInset,
118
+ type TabAccessoryButtonProps,
119
+ type TabAccessoryPlacement,
120
+ type TabAccessoryThemeColorProp,
121
+ type TabAccessoryVariant,
122
+ } from "./TabAccessoryButton";
123
+ export {
124
+ Page,
125
+ PageBleed,
126
+ PAGE_THEME_ROLES,
127
+ type PageBleedProps,
128
+ type PageProps,
129
+ type PageTabAccessory,
130
+ type PageThemeColorProp,
131
+ } from "./Page";
package/src/index.ts CHANGED
@@ -1,12 +1,26 @@
1
- export const version = "0.2.5";
1
+ export const version = "0.17.0";
2
2
 
3
3
  export * from "./theme";
4
+ export { SHEET_TOP_RADIUS, screenPaddingHorizontal } from "./layout";
4
5
  export {
5
6
  Button,
6
- type ButtonProps,
7
+ BUTTON_ICON_SIZE,
8
+ BUTTON_THEME_ROLES,
7
9
  buttonRadiusValue,
10
+ type ButtonIcon,
11
+ type ButtonIconPosition,
12
+ type ButtonProps,
8
13
  type ButtonRadius,
14
+ type ButtonThemeColorProp,
15
+ type ButtonVariant,
9
16
  } from "./components/Button";
17
+ export {
18
+ Choice,
19
+ CHOICE_ICON_SIZE,
20
+ CHOICE_THEME_ROLES,
21
+ type ChoiceProps,
22
+ type ChoiceThemeColorProp,
23
+ } from "./components/Choice";
10
24
  export {
11
25
  LinkBlocks,
12
26
  filterLinkBlockGroups,
@@ -26,8 +40,20 @@ export {
26
40
  export {
27
41
  Form,
28
42
  FormCloseButton,
43
+ FORM_THEME_ROLES,
44
+ type FormThemeColorProp,
29
45
  type FormProps,
30
46
  } from "./components/Form";
47
+ export {
48
+ FormSheet,
49
+ FORM_SHEET_THEME_ROLES,
50
+ formSheetSnapPoints,
51
+ type FormSheetInset,
52
+ type FormSheetProps,
53
+ type FormSheetSnapIndex,
54
+ type FormSheetSnapInput,
55
+ type FormSheetThemeColorProp,
56
+ } from "./components/FormSheet";
31
57
  export {
32
58
  ErrorBanner,
33
59
  ErrorBannerField,
@@ -39,6 +65,7 @@ export {
39
65
  useErrorBannerField,
40
66
  type ErrorBannerThemeColorProp,
41
67
  type ErrorBannerProps,
68
+ type ErrorBannerScrollTarget,
42
69
  type FieldError,
43
70
  } from "./components/ErrorBanner";
44
71
  export {
@@ -74,4 +101,36 @@ export {
74
101
  type SliderThemeColorProp,
75
102
  type SliderProps,
76
103
  } from "./components/Slider";
104
+ export {
105
+ HeaderCapsule,
106
+ HeaderGroup,
107
+ HeaderIcon,
108
+ HEADER_BUTTON_THEME_ROLES,
109
+ supportsLiquidGlass,
110
+ type HeaderButtonThemeColorProp,
111
+ type HeaderButtonIcon,
112
+ type HeaderCapsuleProps,
113
+ type HeaderGroupProps,
114
+ type HeaderIconProps,
115
+ } from "./components/HeaderButton";
116
+ export { HeaderWash } from "./components/HeaderWash";
117
+ export {
118
+ TabAccessoryButton,
119
+ TAB_ACCESSORY_SCREEN_INSET,
120
+ TAB_ACCESSORY_THEME_ROLES,
121
+ tabAccessoryScreenInset,
122
+ type TabAccessoryButtonProps,
123
+ type TabAccessoryPlacement,
124
+ type TabAccessoryThemeColorProp,
125
+ type TabAccessoryVariant,
126
+ } from "./components/TabAccessoryButton";
127
+ export {
128
+ Page,
129
+ PageBleed,
130
+ PAGE_THEME_ROLES,
131
+ type PageBleedProps,
132
+ type PageProps,
133
+ type PageTabAccessory,
134
+ type PageThemeColorProp,
135
+ } from "./components/Page";
77
136
  export * as components from "./components";
@@ -0,0 +1,60 @@
1
+ import assert from "node:assert/strict";
2
+ import { describe, it } from "node:test";
3
+ import { estimateHeaderHeight, formFieldScrollY } from "./headerInset.ts";
4
+
5
+ describe("formFieldScrollY", () => {
6
+ // iOS, transparent header ending at 100, banner in a window overlay.
7
+ const ios = { scrollTop: 0, visibleTop: 100, bannerBottom: 0, minScrollY: -100, gap: 12 };
8
+
9
+ it("puts the field just under the header when there is no banner", () => {
10
+ assert.equal(formFieldScrollY({ ...ios, fieldY: 400 }), 400 - 100 - 12);
11
+ });
12
+
13
+ it("puts the field under the banner when the banner reaches lower than the header", () => {
14
+ assert.equal(formFieldScrollY({ ...ios, fieldY: 400, bannerBottom: 180 }), 400 - 180 - 12);
15
+ });
16
+
17
+ it("never scrolls above the top of the content", () => {
18
+ assert.equal(formFieldScrollY({ ...ios, fieldY: 20, bannerBottom: 180 }), -100);
19
+ });
20
+
21
+ it("accounts for a scroll view that starts below an opaque header", () => {
22
+ // Scroll view starts at window y 100, no inset; overlay banner to 180.
23
+ assert.equal(
24
+ formFieldScrollY({
25
+ fieldY: 400,
26
+ scrollTop: 100,
27
+ visibleTop: 100,
28
+ bannerBottom: 180,
29
+ minScrollY: 0,
30
+ gap: 12,
31
+ }),
32
+ 400 + 100 - 180 - 12,
33
+ );
34
+ });
35
+
36
+ it("clears a transparent Android header and the banner below it", () => {
37
+ // Content starts at window 0 under an 80pt header; banner from 80 to 200.
38
+ assert.equal(
39
+ formFieldScrollY({
40
+ fieldY: 500,
41
+ scrollTop: 0,
42
+ visibleTop: 80,
43
+ bannerBottom: 200,
44
+ minScrollY: 0,
45
+ gap: 12,
46
+ }),
47
+ 500 - 200 - 12,
48
+ );
49
+ });
50
+ });
51
+
52
+ describe("estimateHeaderHeight", () => {
53
+ it("adds the standard iOS navigation bar to the status bar", () => {
54
+ assert.equal(estimateHeaderHeight("ios", 59), 59 + 44);
55
+ });
56
+
57
+ it("adds the standard Android toolbar to the status bar", () => {
58
+ assert.equal(estimateHeaderHeight("android", 24), 24 + 56);
59
+ });
60
+ });
@@ -0,0 +1,41 @@
1
+ /** Material toolbar height, which the Android native stack header uses. */
2
+ export const ANDROID_TOOLBAR_HEIGHT = 56;
3
+
4
+ /** Standard iOS navigation bar height, below the status bar. */
5
+ export const IOS_NAVIGATION_BAR_HEIGHT = 44;
6
+
7
+ /**
8
+ * Header height when the app does not pass `useHeaderHeight()`: the status
9
+ * bar plus a standard bar. A large title or a custom header is taller.
10
+ * Pure so it can be unit-tested.
11
+ */
12
+ export function estimateHeaderHeight(os: string, statusBar: number): number {
13
+ return statusBar + (os === "android" ? ANDROID_TOOLBAR_HEIGHT : IOS_NAVIGATION_BAR_HEIGHT);
14
+ }
15
+
16
+ /**
17
+ * Scroll offset that shows a field just below whichever ends lower: the
18
+ * native header or the error banner. `fieldY` is the field's content y;
19
+ * `scrollTop`, `visibleTop` and `bannerBottom` are window y values;
20
+ * `minScrollY` is the resting offset at the content top (negative when iOS
21
+ * insets the content). Pure so it can be unit-tested.
22
+ */
23
+ export function formFieldScrollY({
24
+ fieldY,
25
+ scrollTop,
26
+ visibleTop,
27
+ bannerBottom,
28
+ minScrollY,
29
+ gap,
30
+ }: {
31
+ fieldY: number;
32
+ scrollTop: number;
33
+ visibleTop: number;
34
+ bannerBottom: number;
35
+ minScrollY: number;
36
+ gap: number;
37
+ }): number {
38
+ const clearOf = Math.max(visibleTop, bannerBottom);
39
+ return Math.max(minScrollY, fieldY + scrollTop - clearOf - gap);
40
+ }
41
+
@@ -1 +1,2 @@
1
1
  export { screenPaddingHorizontal } from "./screenPadding";
2
+ export { SHEET_TOP_RADIUS } from "./sheet";
@@ -0,0 +1,2 @@
1
+ /** Top corner radius shared by the Select and FormSheet sheets. */
2
+ export const SHEET_TOP_RADIUS = 20;
@@ -4,17 +4,25 @@ import {
4
4
  useMemo,
5
5
  type ReactNode,
6
6
  } from "react";
7
+ import { resolveThemeSpacing, type ThemeSpacing } from "./resolveThemeSpacing";
7
8
  import type { ThemeColors, ThemeContextValue } from "./types";
8
9
 
9
10
  export const ThemeContext = createContext<ThemeContextValue | null>(null);
10
11
 
11
12
  export function ThemeProvider({
12
13
  colors,
14
+ spacing,
13
15
  children,
14
16
  }: {
15
17
  colors: { light: ThemeColors; dark: ThemeColors };
18
+ /** Overrides the package spacing defaults. Omitted keys keep the default. */
19
+ spacing?: Partial<ThemeSpacing>;
16
20
  children: ReactNode;
17
21
  }) {
18
- const value = useMemo(() => ({ colors }), [colors]);
22
+ const value = useMemo(
23
+ () => ({ colors, spacing: resolveThemeSpacing(spacing) }),
24
+ // Spacing is usually an inline literal; compare its values, not identity.
25
+ [colors, spacing?.screen, spacing?.pageTop, spacing?.pageBottom],
26
+ );
19
27
  return createElement(ThemeContext.Provider, { value }, children);
20
28
  }
@@ -2,4 +2,10 @@ export type { ThemeColors, ThemeContextValue } from "./types";
2
2
  export { ThemeProvider } from "./ThemeProvider";
3
3
  export { useColorScheme } from "./useColorScheme";
4
4
  export { useThemeColor } from "./useThemeColor";
5
+ export { useThemeSpacing } from "./useThemeSpacing";
5
6
  export { resolveColorScheme } from "./resolveColorScheme";
7
+ export {
8
+ DEFAULT_THEME_SPACING,
9
+ resolveThemeSpacing,
10
+ type ThemeSpacing,
11
+ } from "./resolveThemeSpacing";