@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
@@ -13,10 +13,22 @@ import {
13
13
  import { useSafeAreaInsets } from "react-native-safe-area-context";
14
14
  import { buttonRadiusValue } from "../Button/buttonRadius";
15
15
  import { useResolvedFieldColor } from "../Field/useResolvedFieldColor";
16
- import { screenPaddingHorizontal } from "../../layout/screenPadding";
16
+ import { useThemeSpacing } from "../../theme/useThemeSpacing";
17
17
  import { errorBannerTitle } from "./errorBannerCopy";
18
+ import {
19
+ ERROR_BANNER_ARROW_SIZE,
20
+ ERROR_BANNER_DISC_SIZE,
21
+ ERROR_BANNER_DISMISS_SIZE,
22
+ ERROR_BANNER_FONT_SIZE,
23
+ ERROR_BANNER_HEADER_GAP,
24
+ ERROR_BANNER_LINE_HEIGHT,
25
+ ERROR_BANNER_MEGAPHONE_SIZE,
26
+ ERROR_BANNER_ROW_INSET,
27
+ ERROR_BANNER_ROW_MIN_HEIGHT,
28
+ errorBannerBarStyle,
29
+ } from "./errorBannerLayout";
18
30
  import { ERROR_BANNER_THEME_ROLES } from "./errorBannerThemeRoles";
19
- import { ArrowRight, Megaphone } from "./icons";
31
+ import { ArrowRight, Cross, Megaphone } from "./icons";
20
32
  import type { ErrorBannerProps } from "./types";
21
33
 
22
34
  const BOTTOM_RADIUS = buttonRadiusValue("xl");
@@ -33,13 +45,15 @@ export function ErrorBanner({
33
45
  onDismiss,
34
46
  onHeightChange,
35
47
  safeAreaTop,
36
- paddingHorizontal = screenPaddingHorizontal,
48
+ paddingHorizontal: paddingHorizontalProp,
37
49
  backgroundColor: backgroundColorProp,
38
50
  textColor: textColorProp,
39
51
  secondaryColor: secondaryColorProp,
40
52
  dividerColor: dividerColorProp,
41
53
  accentColor: accentColorProp,
42
54
  }: ErrorBannerProps) {
55
+ const spacing = useThemeSpacing();
56
+ const paddingHorizontal = paddingHorizontalProp ?? spacing.screen;
43
57
  const insets = useSafeAreaInsets();
44
58
  const topInset = safeAreaTop ?? insets.top;
45
59
  const backgroundColor = useResolvedFieldColor(
@@ -108,7 +122,7 @@ export function ErrorBanner({
108
122
 
109
123
  return (
110
124
  <>
111
- {open ? <StatusBar animated barStyle="light-content" /> : null}
125
+ {open ? <StatusBar animated barStyle={errorBannerBarStyle(backgroundColor)} /> : null}
112
126
  <Animated.View
113
127
  accessibilityRole="alert"
114
128
  accessibilityElementsHidden={!open}
@@ -133,7 +147,7 @@ export function ErrorBanner({
133
147
  importantForAccessibility="no-hide-descendants"
134
148
  style={[styles.iconDisc, { backgroundColor: accentColor }]}
135
149
  >
136
- <Megaphone color={backgroundColor} />
150
+ <Megaphone color={backgroundColor} size={ERROR_BANNER_MEGAPHONE_SIZE} />
137
151
  </View>
138
152
  <Text style={[styles.title, { color: textColor }]}>{title}</Text>
139
153
  <Pressable
@@ -142,7 +156,7 @@ export function ErrorBanner({
142
156
  hitSlop={12}
143
157
  onPress={onDismiss}
144
158
  >
145
- <Text style={[styles.dismiss, { color: secondaryColor }]}>✕</Text>
159
+ <Cross color={secondaryColor} size={ERROR_BANNER_DISMISS_SIZE} />
146
160
  </Pressable>
147
161
  </View>
148
162
  <View style={styles.rows}>
@@ -151,7 +165,6 @@ export function ErrorBanner({
151
165
  key={error.field}
152
166
  accessibilityLabel={`${error.short}. Go to field.`}
153
167
  accessibilityRole="button"
154
- hitSlop={{ top: 4, bottom: 4 }}
155
168
  onPress={() => onJump(error.field)}
156
169
  style={({ pressed }) => [
157
170
  styles.row,
@@ -166,7 +179,7 @@ export function ErrorBanner({
166
179
  accessibilityElementsHidden
167
180
  importantForAccessibility="no-hide-descendants"
168
181
  >
169
- <ArrowRight color={secondaryColor} />
182
+ <ArrowRight color={secondaryColor} size={ERROR_BANNER_ARROW_SIZE} />
170
183
  </View>
171
184
  </Pressable>
172
185
  ))}
@@ -184,7 +197,7 @@ const styles = StyleSheet.create({
184
197
  right: 0,
185
198
  zIndex: 100,
186
199
  paddingBottom: 10,
187
- shadowColor: "#22201D",
200
+ shadowColor: "#000000",
188
201
  shadowOpacity: 0.25,
189
202
  shadowRadius: 12,
190
203
  shadowOffset: { width: 0, height: 8 },
@@ -193,32 +206,29 @@ const styles = StyleSheet.create({
193
206
  header: {
194
207
  flexDirection: "row",
195
208
  alignItems: "center",
196
- gap: 10,
209
+ gap: ERROR_BANNER_HEADER_GAP,
197
210
  },
198
211
  iconDisc: {
199
- width: 28,
200
- height: 28,
201
- borderRadius: 14,
212
+ width: ERROR_BANNER_DISC_SIZE,
213
+ height: ERROR_BANNER_DISC_SIZE,
214
+ borderRadius: ERROR_BANNER_DISC_SIZE / 2,
202
215
  alignItems: "center",
203
216
  justifyContent: "center",
204
217
  },
205
218
  title: {
206
219
  flex: 1,
207
- fontSize: 15,
208
- lineHeight: 20,
220
+ fontSize: ERROR_BANNER_FONT_SIZE,
221
+ lineHeight: ERROR_BANNER_LINE_HEIGHT,
209
222
  fontWeight: "600",
210
223
  },
211
- dismiss: {
212
- fontSize: 15,
213
- },
214
224
  rows: {
215
225
  marginTop: 6,
216
226
  },
217
227
  row: {
218
228
  flexDirection: "row",
219
229
  alignItems: "center",
220
- minHeight: 36,
221
- marginLeft: 38,
230
+ minHeight: ERROR_BANNER_ROW_MIN_HEIGHT,
231
+ marginLeft: ERROR_BANNER_ROW_INSET,
222
232
  borderTopWidth: 1,
223
233
  },
224
234
  pressed: {
@@ -226,7 +236,7 @@ const styles = StyleSheet.create({
226
236
  },
227
237
  rowLabel: {
228
238
  flex: 1,
229
- fontSize: 15,
230
- lineHeight: 20,
239
+ fontSize: ERROR_BANNER_FONT_SIZE,
240
+ lineHeight: ERROR_BANNER_LINE_HEIGHT,
231
241
  },
232
242
  });
@@ -8,10 +8,15 @@ import {
8
8
  type ReactNode,
9
9
  type RefObject,
10
10
  } from "react";
11
- import { View, type ReactNativeElement, type ScrollView } from "react-native";
11
+ import { View, type ReactNativeElement } from "react-native";
12
12
  import { fieldScrollY } from "./errorBannerCopy";
13
13
  import type { FieldError } from "./types";
14
14
 
15
+ /** Anything that scrolls: a ScrollView, a KeyboardAwareScrollView, a BottomSheetScrollView. */
16
+ export type ErrorBannerScrollTarget = {
17
+ scrollTo(options: { y: number; animated?: boolean }): void;
18
+ };
19
+
15
20
  export type ErrorBannerAnchor = {
16
21
  view: View;
17
22
  focus?: () => void;
@@ -76,12 +81,28 @@ export function ErrorBannerProvider({
76
81
  errors,
77
82
  onDismiss,
78
83
  bannerHeightRef,
84
+ measureRef,
85
+ onBeforeJump,
86
+ scrollYForField,
79
87
  }: {
80
88
  children: ReactNode;
81
- scrollRef: RefObject<ScrollView | null>;
89
+ scrollRef: RefObject<ErrorBannerScrollTarget | null>;
82
90
  errors: readonly FieldError[];
83
91
  onDismiss: () => void;
92
+ /** How far the banner covers the top of the scroll area. */
84
93
  bannerHeightRef: RefObject<number>;
94
+ /**
95
+ * View that field positions are measured against: the scroll content's
96
+ * top. Defaults to `scrollRef`, which works for a plain ScrollView.
97
+ */
98
+ measureRef?: RefObject<View | null>;
99
+ /** Runs before scrolling to a field, e.g. to expand a sheet. */
100
+ onBeforeJump?: () => void;
101
+ /**
102
+ * Scroll offset for a field at content y `fieldY`. Defaults to clearing
103
+ * `bannerHeightRef` from the top of the scroll area.
104
+ */
105
+ scrollYForField?: (fieldY: number) => number;
85
106
  }) {
86
107
  const anchors = useRef(new Map<string, ErrorBannerAnchor>());
87
108
 
@@ -96,21 +117,25 @@ export function ErrorBannerProvider({
96
117
  const jump = useCallback((field: string) => {
97
118
  const anchor = anchors.current.get(field);
98
119
  const scroll = scrollRef.current;
99
- if (!anchor || !scroll) {
120
+ const relativeTo = measureRef?.current ?? scroll;
121
+ if (!anchor || !scroll || !relativeTo) {
100
122
  return;
101
123
  }
124
+ onBeforeJump?.();
102
125
  anchor.view.measureLayout(
103
- scroll as unknown as ReactNativeElement,
126
+ relativeTo as unknown as ReactNativeElement,
104
127
  (_x, y) => {
105
128
  scroll.scrollTo({
106
- y: fieldScrollY(y, bannerHeightRef.current ?? 0),
129
+ y: scrollYForField
130
+ ? scrollYForField(y)
131
+ : fieldScrollY(y, bannerHeightRef.current ?? 0),
107
132
  animated: true,
108
133
  });
109
134
  anchor.focus?.();
110
135
  },
111
136
  () => undefined,
112
137
  );
113
- }, [bannerHeightRef, scrollRef]);
138
+ }, [bannerHeightRef, measureRef, onBeforeJump, scrollRef, scrollYForField]);
114
139
 
115
140
  const value = useMemo(
116
141
  () => ({ errors, register, dismiss: onDismiss, jump }),
@@ -0,0 +1,49 @@
1
+ import assert from "node:assert/strict";
2
+ import { describe, it } from "node:test";
3
+ import {
4
+ ERROR_BANNER_DISC_SIZE,
5
+ ERROR_BANNER_FONT_SIZE,
6
+ ERROR_BANNER_HEADER_GAP,
7
+ ERROR_BANNER_ROW_INSET,
8
+ ERROR_BANNER_ROW_MIN_HEIGHT,
9
+ errorBannerBarStyle,
10
+ } from "./errorBannerLayout.ts";
11
+
12
+ describe("errorBannerBarStyle", () => {
13
+ it("uses light status bar text on a dark banner", () => {
14
+ assert.equal(errorBannerBarStyle("#1F2937"), "light-content");
15
+ assert.equal(errorBannerBarStyle("#000"), "light-content");
16
+ });
17
+
18
+ it("uses dark status bar text on a light banner", () => {
19
+ assert.equal(errorBannerBarStyle("#F9FAFB"), "dark-content");
20
+ assert.equal(errorBannerBarStyle("#fff"), "dark-content");
21
+ });
22
+
23
+ it("reads rgb() and 8-digit hex colours", () => {
24
+ assert.equal(errorBannerBarStyle("rgb(255, 255, 255)"), "dark-content");
25
+ assert.equal(errorBannerBarStyle("rgba(31, 41, 55, 0.9)"), "light-content");
26
+ assert.equal(errorBannerBarStyle("#FFFFFFCC"), "dark-content");
27
+ });
28
+
29
+ it("falls back to the system default when the colour cannot be read", () => {
30
+ assert.equal(errorBannerBarStyle("red"), "default");
31
+ });
32
+ });
33
+
34
+ describe("error banner sizes", () => {
35
+ it("matches the 17pt body text used by the other components", () => {
36
+ assert.equal(ERROR_BANNER_FONT_SIZE, 17);
37
+ });
38
+
39
+ it("gives each row the 44pt minimum tap target", () => {
40
+ assert.ok(ERROR_BANNER_ROW_MIN_HEIGHT >= 44);
41
+ });
42
+
43
+ it("lines rows up with the title, after the disc and gap", () => {
44
+ assert.equal(
45
+ ERROR_BANNER_ROW_INSET,
46
+ ERROR_BANNER_DISC_SIZE + ERROR_BANNER_HEADER_GAP,
47
+ );
48
+ });
49
+ });
@@ -0,0 +1,59 @@
1
+ /** Title and rows, matching the 17pt body text of Button, Field and Select. */
2
+ export const ERROR_BANNER_FONT_SIZE = 17;
3
+ export const ERROR_BANNER_LINE_HEIGHT = 22;
4
+ export const ERROR_BANNER_DISC_SIZE = 32;
5
+ export const ERROR_BANNER_MEGAPHONE_SIZE = 18;
6
+ export const ERROR_BANNER_ARROW_SIZE = 20;
7
+ export const ERROR_BANNER_DISMISS_SIZE = 18;
8
+ export const ERROR_BANNER_HEADER_GAP = 12;
9
+ export const ERROR_BANNER_ROW_MIN_HEIGHT = 44;
10
+ /** Rows start under the title, past the disc and gap. */
11
+ export const ERROR_BANNER_ROW_INSET =
12
+ ERROR_BANNER_DISC_SIZE + ERROR_BANNER_HEADER_GAP;
13
+
14
+ export type ErrorBannerBarStyle = "light-content" | "dark-content" | "default";
15
+
16
+ function parseRgb(color: string): [number, number, number] | null {
17
+ const value = color.trim();
18
+ const hex = /^#([0-9a-f]{3,8})$/i.exec(value)?.[1];
19
+ if (hex) {
20
+ const full =
21
+ hex.length === 3 || hex.length === 4
22
+ ? hex
23
+ .slice(0, 3)
24
+ .split("")
25
+ .map((c) => c + c)
26
+ .join("")
27
+ : hex.slice(0, 6);
28
+ if (full.length !== 6) {
29
+ return null;
30
+ }
31
+ return [0, 2, 4].map((i) => parseInt(full.slice(i, i + 2), 16)) as [
32
+ number,
33
+ number,
34
+ number,
35
+ ];
36
+ }
37
+ const rgb = /^rgba?\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)/i.exec(value);
38
+ if (rgb) {
39
+ return [Number(rgb[1]), Number(rgb[2]), Number(rgb[3])];
40
+ }
41
+ return null;
42
+ }
43
+
44
+ /**
45
+ * Status bar text that stays readable over the banner fill: light text on a
46
+ * dark banner, dark text on a light one. Pure so it can be unit-tested.
47
+ */
48
+ export function errorBannerBarStyle(backgroundColor: string): ErrorBannerBarStyle {
49
+ const rgb = parseRgb(backgroundColor);
50
+ if (!rgb) {
51
+ return "default";
52
+ }
53
+ const [r, g, b] = rgb.map((c) => {
54
+ const s = c / 255;
55
+ return s <= 0.03928 ? s / 12.92 : ((s + 0.055) / 1.055) ** 2.4;
56
+ });
57
+ const luminance = 0.2126 * r + 0.7152 * g + 0.0722 * b;
58
+ return luminance > 0.179 ? "dark-content" : "light-content";
59
+ }
@@ -1,10 +1,12 @@
1
1
  import Svg, { Line, Path, Polyline } from "react-native-svg";
2
2
 
3
- export function Megaphone({ color }: { color: string }) {
3
+ type IconProps = { color: string; size: number };
4
+
5
+ export function Megaphone({ color, size }: IconProps) {
4
6
  return (
5
7
  <Svg
6
- width={16}
7
- height={16}
8
+ width={size}
9
+ height={size}
8
10
  viewBox="0 0 24 24"
9
11
  fill="none"
10
12
  stroke={color}
@@ -19,11 +21,11 @@ export function Megaphone({ color }: { color: string }) {
19
21
  );
20
22
  }
21
23
 
22
- export function ArrowRight({ color }: { color: string }) {
24
+ export function ArrowRight({ color, size }: IconProps) {
23
25
  return (
24
26
  <Svg
25
- width={18}
26
- height={18}
27
+ width={size}
28
+ height={size}
27
29
  viewBox="0 0 24 24"
28
30
  fill="none"
29
31
  stroke={color}
@@ -36,3 +38,20 @@ export function ArrowRight({ color }: { color: string }) {
36
38
  </Svg>
37
39
  );
38
40
  }
41
+
42
+ export function Cross({ color, size }: IconProps) {
43
+ return (
44
+ <Svg
45
+ width={size}
46
+ height={size}
47
+ viewBox="0 0 24 24"
48
+ fill="none"
49
+ stroke={color}
50
+ strokeWidth={2.25}
51
+ strokeLinecap="round"
52
+ >
53
+ <Line x1={6} y1={6} x2={18} y2={18} />
54
+ <Line x1={18} y1={6} x2={6} y2={18} />
55
+ </Svg>
56
+ );
57
+ }
@@ -5,6 +5,7 @@ export {
5
5
  useErrorBannerContext,
6
6
  useErrorBannerDismiss,
7
7
  useErrorBannerField,
8
+ type ErrorBannerScrollTarget,
8
9
  } from "./errorBannerContext";
9
10
  export {
10
11
  ERROR_BANNER_THEME_ROLES,