@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.
- package/README.md +22 -18
- package/dist/components/Button/Button.d.ts +20 -4
- package/dist/components/Button/Button.d.ts.map +1 -1
- package/dist/components/Button/Button.js +34 -21
- package/dist/components/Button/Button.js.map +1 -1
- package/dist/components/Button/buttonAppearance.d.ts +20 -0
- package/dist/components/Button/buttonAppearance.d.ts.map +1 -0
- package/dist/components/Button/buttonAppearance.js +28 -0
- package/dist/components/Button/buttonAppearance.js.map +1 -0
- package/dist/components/Button/buttonThemeRoles.d.ts +6 -0
- package/dist/components/Button/buttonThemeRoles.d.ts.map +1 -0
- package/dist/components/Button/buttonThemeRoles.js +8 -0
- package/dist/components/Button/buttonThemeRoles.js.map +1 -0
- package/dist/components/Button/index.d.ts +3 -1
- package/dist/components/Button/index.d.ts.map +1 -1
- package/dist/components/Button/index.js +4 -1
- package/dist/components/Button/index.js.map +1 -1
- package/dist/components/Choice/Choice.d.ts +10 -0
- package/dist/components/Choice/Choice.d.ts.map +1 -0
- package/dist/components/Choice/Choice.js +66 -0
- package/dist/components/Choice/Choice.js.map +1 -0
- package/dist/components/Choice/choiceLayout.d.ts +23 -0
- package/dist/components/Choice/choiceLayout.d.ts.map +1 -0
- package/dist/components/Choice/choiceLayout.js +32 -0
- package/dist/components/Choice/choiceLayout.js.map +1 -0
- package/dist/components/Choice/choiceThemeRoles.d.ts +9 -0
- package/dist/components/Choice/choiceThemeRoles.d.ts.map +1 -0
- package/dist/components/Choice/choiceThemeRoles.js +11 -0
- package/dist/components/Choice/choiceThemeRoles.js.map +1 -0
- package/dist/components/Choice/index.d.ts +5 -0
- package/dist/components/Choice/index.d.ts.map +1 -0
- package/dist/components/Choice/index.js +10 -0
- package/dist/components/Choice/index.js.map +1 -0
- package/dist/components/Choice/types.d.ts +33 -0
- package/dist/components/Choice/types.d.ts.map +1 -0
- package/dist/components/Choice/types.js +3 -0
- package/dist/components/Choice/types.js.map +1 -0
- package/dist/components/ErrorBanner/ErrorBanner.d.ts +1 -1
- package/dist/components/ErrorBanner/ErrorBanner.d.ts.map +1 -1
- package/dist/components/ErrorBanner/ErrorBanner.js +19 -19
- package/dist/components/ErrorBanner/ErrorBanner.js.map +1 -1
- package/dist/components/ErrorBanner/errorBannerContext.d.ts +23 -3
- package/dist/components/ErrorBanner/errorBannerContext.d.ts.map +1 -1
- package/dist/components/ErrorBanner/errorBannerContext.js +9 -5
- package/dist/components/ErrorBanner/errorBannerContext.js.map +1 -1
- package/dist/components/ErrorBanner/errorBannerLayout.d.ts +18 -0
- package/dist/components/ErrorBanner/errorBannerLayout.d.ts.map +1 -0
- package/dist/components/ErrorBanner/errorBannerLayout.js +54 -0
- package/dist/components/ErrorBanner/errorBannerLayout.js.map +1 -0
- package/dist/components/ErrorBanner/icons.d.ts +7 -5
- package/dist/components/ErrorBanner/icons.d.ts.map +1 -1
- package/dist/components/ErrorBanner/icons.js +8 -4
- package/dist/components/ErrorBanner/icons.js.map +1 -1
- package/dist/components/ErrorBanner/index.d.ts +1 -1
- package/dist/components/ErrorBanner/index.d.ts.map +1 -1
- package/dist/components/ErrorBanner/index.js.map +1 -1
- package/dist/components/Form/Form.d.ts +9 -1
- package/dist/components/Form/Form.d.ts.map +1 -1
- package/dist/components/Form/Form.js +56 -78
- package/dist/components/Form/Form.js.map +1 -1
- package/dist/components/Form/FormCloseButton.d.ts +3 -1
- package/dist/components/Form/FormCloseButton.d.ts.map +1 -1
- package/dist/components/Form/FormCloseButton.js +5 -4
- package/dist/components/Form/FormCloseButton.js.map +1 -1
- package/dist/components/Form/FormErrorBanner.d.ts +15 -0
- package/dist/components/Form/FormErrorBanner.d.ts.map +1 -0
- package/dist/components/Form/FormErrorBanner.js +30 -0
- package/dist/components/Form/FormErrorBanner.js.map +1 -0
- package/dist/components/Form/FormSubmitFooter.d.ts +17 -0
- package/dist/components/Form/FormSubmitFooter.d.ts.map +1 -0
- package/dist/components/Form/FormSubmitFooter.js +32 -0
- package/dist/components/Form/FormSubmitFooter.js.map +1 -0
- package/dist/components/Form/formThemeRoles.d.ts +7 -0
- package/dist/components/Form/formThemeRoles.d.ts.map +1 -0
- package/dist/components/Form/formThemeRoles.js +9 -0
- package/dist/components/Form/formThemeRoles.js.map +1 -0
- package/dist/components/Form/index.d.ts +1 -0
- package/dist/components/Form/index.d.ts.map +1 -1
- package/dist/components/Form/index.js +3 -1
- package/dist/components/Form/index.js.map +1 -1
- package/dist/components/Form/types.d.ts +14 -10
- package/dist/components/Form/types.d.ts.map +1 -1
- package/dist/components/FormSheet/FormSheet.d.ts +10 -0
- package/dist/components/FormSheet/FormSheet.d.ts.map +1 -0
- package/dist/components/FormSheet/FormSheet.js +153 -0
- package/dist/components/FormSheet/FormSheet.js.map +1 -0
- package/dist/components/FormSheet/formSheetLayout.d.ts +32 -0
- package/dist/components/FormSheet/formSheetLayout.d.ts.map +1 -0
- package/dist/components/FormSheet/formSheetLayout.js +43 -0
- package/dist/components/FormSheet/formSheetLayout.js.map +1 -0
- package/dist/components/FormSheet/formSheetThemeRoles.d.ts +8 -0
- package/dist/components/FormSheet/formSheetThemeRoles.d.ts.map +1 -0
- package/dist/components/FormSheet/formSheetThemeRoles.js +10 -0
- package/dist/components/FormSheet/formSheetThemeRoles.js.map +1 -0
- package/dist/components/FormSheet/index.d.ts +5 -0
- package/dist/components/FormSheet/index.d.ts.map +1 -0
- package/dist/components/FormSheet/index.js +10 -0
- package/dist/components/FormSheet/index.js.map +1 -0
- package/dist/components/FormSheet/types.d.ts +64 -0
- package/dist/components/FormSheet/types.d.ts.map +1 -0
- package/dist/components/FormSheet/types.js +3 -0
- package/dist/components/FormSheet/types.js.map +1 -0
- package/dist/components/HeaderButton/HeaderCapsule.d.ts +4 -0
- package/dist/components/HeaderButton/HeaderCapsule.d.ts.map +1 -0
- package/dist/components/HeaderButton/HeaderCapsule.js +32 -0
- package/dist/components/HeaderButton/HeaderCapsule.js.map +1 -0
- package/dist/components/HeaderButton/HeaderGroup.d.ts +4 -0
- package/dist/components/HeaderButton/HeaderGroup.d.ts.map +1 -0
- package/dist/components/HeaderButton/HeaderGroup.js +23 -0
- package/dist/components/HeaderButton/HeaderGroup.js.map +1 -0
- package/dist/components/HeaderButton/HeaderIcon.d.ts +4 -0
- package/dist/components/HeaderButton/HeaderIcon.d.ts.map +1 -0
- package/dist/components/HeaderButton/HeaderIcon.js +28 -0
- package/dist/components/HeaderButton/HeaderIcon.js.map +1 -0
- package/dist/components/HeaderButton/headerButtonLayout.d.ts +21 -0
- package/dist/components/HeaderButton/headerButtonLayout.d.ts.map +1 -0
- package/dist/components/HeaderButton/headerButtonLayout.js +34 -0
- package/dist/components/HeaderButton/headerButtonLayout.js.map +1 -0
- package/dist/components/HeaderButton/headerButtonThemeRoles.d.ts +5 -0
- package/dist/components/HeaderButton/headerButtonThemeRoles.d.ts.map +1 -0
- package/dist/components/HeaderButton/headerButtonThemeRoles.js +7 -0
- package/dist/components/HeaderButton/headerButtonThemeRoles.js.map +1 -0
- package/dist/components/HeaderButton/index.d.ts +7 -0
- package/dist/components/HeaderButton/index.d.ts.map +1 -0
- package/dist/components/HeaderButton/index.js +14 -0
- package/dist/components/HeaderButton/index.js.map +1 -0
- package/dist/components/HeaderButton/types.d.ts +40 -0
- package/dist/components/HeaderButton/types.d.ts.map +1 -0
- package/dist/components/HeaderButton/types.js +3 -0
- package/dist/components/HeaderButton/types.js.map +1 -0
- package/dist/components/HeaderWash/HeaderWash.d.ts +10 -0
- package/dist/components/HeaderWash/HeaderWash.d.ts.map +1 -0
- package/dist/components/HeaderWash/HeaderWash.js +19 -0
- package/dist/components/HeaderWash/HeaderWash.js.map +1 -0
- package/dist/components/HeaderWash/headerWashGradient.d.ts +8 -0
- package/dist/components/HeaderWash/headerWashGradient.d.ts.map +1 -0
- package/dist/components/HeaderWash/headerWashGradient.js +21 -0
- package/dist/components/HeaderWash/headerWashGradient.js.map +1 -0
- package/dist/components/HeaderWash/index.d.ts +2 -0
- package/dist/components/HeaderWash/index.d.ts.map +1 -0
- package/dist/components/HeaderWash/index.js +6 -0
- package/dist/components/HeaderWash/index.js.map +1 -0
- package/dist/components/Page/Page.d.ts +13 -0
- package/dist/components/Page/Page.d.ts.map +1 -0
- package/dist/components/Page/Page.js +49 -0
- package/dist/components/Page/Page.js.map +1 -0
- package/dist/components/Page/PageBleed.d.ts +9 -0
- package/dist/components/Page/PageBleed.d.ts.map +1 -0
- package/dist/components/Page/PageBleed.js +12 -0
- package/dist/components/Page/PageBleed.js.map +1 -0
- package/dist/components/Page/index.d.ts +5 -0
- package/dist/components/Page/index.d.ts.map +1 -0
- package/dist/components/Page/index.js +10 -0
- package/dist/components/Page/index.js.map +1 -0
- package/dist/components/Page/pageContext.d.ts +4 -0
- package/dist/components/Page/pageContext.d.ts.map +1 -0
- package/dist/components/Page/pageContext.js +11 -0
- package/dist/components/Page/pageContext.js.map +1 -0
- package/dist/components/Page/pageInsets.d.ts +26 -0
- package/dist/components/Page/pageInsets.d.ts.map +1 -0
- package/dist/components/Page/pageInsets.js +37 -0
- package/dist/components/Page/pageInsets.js.map +1 -0
- package/dist/components/Page/pageLayout.d.ts +5 -0
- package/dist/components/Page/pageLayout.d.ts.map +1 -0
- package/dist/components/Page/pageLayout.js +8 -0
- package/dist/components/Page/pageLayout.js.map +1 -0
- package/dist/components/Page/pageThemeRoles.d.ts +5 -0
- package/dist/components/Page/pageThemeRoles.d.ts.map +1 -0
- package/dist/components/Page/pageThemeRoles.js +7 -0
- package/dist/components/Page/pageThemeRoles.js.map +1 -0
- package/dist/components/Page/types.d.ts +22 -0
- package/dist/components/Page/types.d.ts.map +1 -0
- package/dist/components/Page/types.js +3 -0
- package/dist/components/Page/types.js.map +1 -0
- package/dist/components/Select/Select.d.ts +1 -1
- package/dist/components/Select/Select.d.ts.map +1 -1
- package/dist/components/Select/Select.js +8 -9
- package/dist/components/Select/Select.js.map +1 -1
- package/dist/components/Select/selectThemeRoles.d.ts +2 -0
- package/dist/components/Select/selectThemeRoles.d.ts.map +1 -1
- package/dist/components/Select/selectThemeRoles.js +2 -0
- package/dist/components/Select/selectThemeRoles.js.map +1 -1
- package/dist/components/Select/types.d.ts +4 -0
- package/dist/components/Select/types.d.ts.map +1 -1
- package/dist/components/TabAccessoryButton/TabAccessoryButton.d.ts +8 -0
- package/dist/components/TabAccessoryButton/TabAccessoryButton.d.ts.map +1 -0
- package/dist/components/TabAccessoryButton/TabAccessoryButton.js +77 -0
- package/dist/components/TabAccessoryButton/TabAccessoryButton.js.map +1 -0
- package/dist/components/TabAccessoryButton/index.d.ts +5 -0
- package/dist/components/TabAccessoryButton/index.d.ts.map +1 -0
- package/dist/components/TabAccessoryButton/index.js +11 -0
- package/dist/components/TabAccessoryButton/index.js.map +1 -0
- package/dist/components/TabAccessoryButton/tabAccessoryLayout.d.ts +27 -0
- package/dist/components/TabAccessoryButton/tabAccessoryLayout.d.ts.map +1 -0
- package/dist/components/TabAccessoryButton/tabAccessoryLayout.js +38 -0
- package/dist/components/TabAccessoryButton/tabAccessoryLayout.js.map +1 -0
- package/dist/components/TabAccessoryButton/tabAccessoryThemeRoles.d.ts +14 -0
- package/dist/components/TabAccessoryButton/tabAccessoryThemeRoles.d.ts.map +1 -0
- package/dist/components/TabAccessoryButton/tabAccessoryThemeRoles.js +19 -0
- package/dist/components/TabAccessoryButton/tabAccessoryThemeRoles.js.map +1 -0
- package/dist/components/TabAccessoryButton/types.d.ts +27 -0
- package/dist/components/TabAccessoryButton/types.d.ts.map +1 -0
- package/dist/components/TabAccessoryButton/types.js +3 -0
- package/dist/components/TabAccessoryButton/types.js.map +1 -0
- package/dist/components/index.d.ts +9 -3
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/index.js +30 -1
- package/dist/components/index.js.map +1 -1
- package/dist/index.d.ts +11 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +34 -2
- package/dist/index.js.map +1 -1
- package/dist/layout/HeaderInsetProbe.d.ts +15 -0
- package/dist/layout/HeaderInsetProbe.d.ts.map +1 -0
- package/dist/layout/HeaderInsetProbe.js +28 -0
- package/dist/layout/HeaderInsetProbe.js.map +1 -0
- package/dist/layout/headerInset.d.ts +26 -0
- package/dist/layout/headerInset.d.ts.map +1 -0
- package/dist/layout/headerInset.js +29 -0
- package/dist/layout/headerInset.js.map +1 -0
- package/dist/layout/index.d.ts +1 -0
- package/dist/layout/index.d.ts.map +1 -1
- package/dist/layout/index.js +3 -1
- package/dist/layout/index.js.map +1 -1
- package/dist/layout/sheet.d.ts +3 -0
- package/dist/layout/sheet.d.ts.map +1 -0
- package/dist/layout/sheet.js +6 -0
- package/dist/layout/sheet.js.map +1 -0
- package/dist/theme/ThemeProvider.d.ts +4 -1
- package/dist/theme/ThemeProvider.d.ts.map +1 -1
- package/dist/theme/ThemeProvider.js +5 -2
- package/dist/theme/ThemeProvider.js.map +1 -1
- package/dist/theme/index.d.ts +2 -0
- package/dist/theme/index.d.ts.map +1 -1
- package/dist/theme/index.js +6 -1
- package/dist/theme/index.js.map +1 -1
- package/dist/theme/resolveThemeSpacing.d.ts +12 -0
- package/dist/theme/resolveThemeSpacing.d.ts.map +1 -0
- package/dist/theme/resolveThemeSpacing.js +14 -0
- package/dist/theme/resolveThemeSpacing.js.map +1 -0
- package/dist/theme/types.d.ts +2 -0
- package/dist/theme/types.d.ts.map +1 -1
- package/dist/theme/useThemeSpacing.d.ts +4 -0
- package/dist/theme/useThemeSpacing.d.ts.map +1 -0
- package/dist/theme/useThemeSpacing.js +11 -0
- package/dist/theme/useThemeSpacing.js.map +1 -0
- package/docs/components/button.md +92 -38
- package/docs/components/choice.md +111 -0
- package/docs/components/error-banner.md +46 -3
- package/docs/components/form-sheet.md +140 -0
- package/docs/components/form.md +80 -45
- package/docs/components/header-button.md +310 -0
- package/docs/components/header-wash.md +40 -0
- package/docs/components/page.md +183 -0
- package/docs/components/select.md +2 -2
- package/docs/components/tab-accessory-button.md +146 -0
- package/docs/specifications/Liquid Glass header buttons in Expo Router.md +327 -0
- package/package.json +1 -1
- package/src/components/Button/Button.tsx +82 -38
- package/src/components/Button/buttonAppearance.test.ts +44 -0
- package/src/components/Button/buttonAppearance.ts +38 -0
- package/src/components/Button/buttonThemeRoles.test.ts +10 -0
- package/src/components/Button/buttonThemeRoles.ts +6 -0
- package/src/components/Button/index.ts +12 -1
- package/src/components/Choice/Choice.tsx +120 -0
- package/src/components/Choice/choiceLayout.test.ts +49 -0
- package/src/components/Choice/choiceLayout.ts +34 -0
- package/src/components/Choice/choiceThemeRoles.test.ts +15 -0
- package/src/components/Choice/choiceThemeRoles.ts +9 -0
- package/src/components/Choice/index.ts +4 -0
- package/src/components/Choice/types.ts +33 -0
- package/src/components/ErrorBanner/ErrorBanner.tsx +32 -22
- package/src/components/ErrorBanner/errorBannerContext.tsx +31 -6
- package/src/components/ErrorBanner/errorBannerLayout.test.ts +49 -0
- package/src/components/ErrorBanner/errorBannerLayout.ts +59 -0
- package/src/components/ErrorBanner/icons.tsx +25 -6
- package/src/components/ErrorBanner/index.ts +1 -0
- package/src/components/Form/Form.tsx +77 -173
- package/src/components/Form/FormCloseButton.tsx +12 -4
- package/src/components/Form/FormErrorBanner.tsx +57 -0
- package/src/components/Form/FormSubmitFooter.tsx +70 -0
- package/src/components/Form/formThemeRoles.test.ts +13 -0
- package/src/components/Form/formThemeRoles.ts +7 -0
- package/src/components/Form/index.ts +1 -0
- package/src/components/Form/types.ts +14 -10
- package/src/components/FormSheet/FormSheet.tsx +279 -0
- package/src/components/FormSheet/formSheetLayout.test.ts +66 -0
- package/src/components/FormSheet/formSheetLayout.ts +55 -0
- package/src/components/FormSheet/formSheetThemeRoles.test.ts +14 -0
- package/src/components/FormSheet/formSheetThemeRoles.ts +8 -0
- package/src/components/FormSheet/index.ts +7 -0
- package/src/components/FormSheet/types.ts +66 -0
- package/src/components/HeaderButton/HeaderCapsule.tsx +63 -0
- package/src/components/HeaderButton/HeaderGroup.tsx +36 -0
- package/src/components/HeaderButton/HeaderIcon.tsx +56 -0
- package/src/components/HeaderButton/headerButtonLayout.test.ts +47 -0
- package/src/components/HeaderButton/headerButtonLayout.ts +36 -0
- package/src/components/HeaderButton/headerButtonThemeRoles.test.ts +9 -0
- package/src/components/HeaderButton/headerButtonThemeRoles.ts +5 -0
- package/src/components/HeaderButton/index.ts +14 -0
- package/src/components/HeaderButton/types.ts +40 -0
- package/src/components/HeaderWash/HeaderWash.tsx +20 -0
- package/src/components/HeaderWash/headerWashGradient.test.ts +22 -0
- package/src/components/{Form/withAlpha.ts → HeaderWash/headerWashGradient.ts} +9 -0
- package/src/components/HeaderWash/index.ts +1 -0
- package/src/components/Page/Page.tsx +80 -0
- package/src/components/Page/PageBleed.tsx +14 -0
- package/src/components/Page/index.ts +4 -0
- package/src/components/Page/pageContext.ts +8 -0
- package/src/components/Page/pageInsets.test.ts +73 -0
- package/src/components/Page/pageInsets.ts +52 -0
- package/src/components/Page/pageThemeRoles.test.ts +9 -0
- package/src/components/Page/pageThemeRoles.ts +5 -0
- package/src/components/Page/types.ts +26 -0
- package/src/components/Select/Select.tsx +11 -4
- package/src/components/Select/selectThemeRoles.test.ts +5 -0
- package/src/components/Select/selectThemeRoles.ts +2 -0
- package/src/components/Select/types.ts +4 -0
- package/src/components/TabAccessoryButton/TabAccessoryButton.tsx +150 -0
- package/src/components/TabAccessoryButton/index.ts +12 -0
- package/src/components/TabAccessoryButton/tabAccessoryLayout.test.ts +42 -0
- package/src/components/TabAccessoryButton/tabAccessoryLayout.ts +43 -0
- package/src/components/TabAccessoryButton/tabAccessoryThemeRoles.test.ts +28 -0
- package/src/components/TabAccessoryButton/tabAccessoryThemeRoles.ts +22 -0
- package/src/components/TabAccessoryButton/types.ts +27 -0
- package/src/components/index.ts +64 -2
- package/src/index.ts +61 -2
- package/src/layout/headerInset.test.ts +60 -0
- package/src/layout/headerInset.ts +41 -0
- package/src/layout/index.ts +1 -0
- package/src/layout/sheet.ts +2 -0
- package/src/theme/ThemeProvider.tsx +9 -1
- package/src/theme/index.ts +6 -0
- package/src/theme/resolveThemeSpacing.test.ts +29 -0
- package/src/theme/resolveThemeSpacing.ts +21 -0
- package/src/theme/types.ts +3 -0
- package/src/theme/useThemeSpacing.ts +8 -0
- 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 {
|
|
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
|
|
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=
|
|
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
|
-
<
|
|
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: "#
|
|
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:
|
|
209
|
+
gap: ERROR_BANNER_HEADER_GAP,
|
|
197
210
|
},
|
|
198
211
|
iconDisc: {
|
|
199
|
-
width:
|
|
200
|
-
height:
|
|
201
|
-
borderRadius:
|
|
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:
|
|
208
|
-
lineHeight:
|
|
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:
|
|
221
|
-
marginLeft:
|
|
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:
|
|
230
|
-
lineHeight:
|
|
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
|
|
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<
|
|
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
|
-
|
|
120
|
+
const relativeTo = measureRef?.current ?? scroll;
|
|
121
|
+
if (!anchor || !scroll || !relativeTo) {
|
|
100
122
|
return;
|
|
101
123
|
}
|
|
124
|
+
onBeforeJump?.();
|
|
102
125
|
anchor.view.measureLayout(
|
|
103
|
-
|
|
126
|
+
relativeTo as unknown as ReactNativeElement,
|
|
104
127
|
(_x, y) => {
|
|
105
128
|
scroll.scrollTo({
|
|
106
|
-
y:
|
|
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
|
-
|
|
3
|
+
type IconProps = { color: string; size: number };
|
|
4
|
+
|
|
5
|
+
export function Megaphone({ color, size }: IconProps) {
|
|
4
6
|
return (
|
|
5
7
|
<Svg
|
|
6
|
-
width={
|
|
7
|
-
height={
|
|
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 }:
|
|
24
|
+
export function ArrowRight({ color, size }: IconProps) {
|
|
23
25
|
return (
|
|
24
26
|
<Svg
|
|
25
|
-
width={
|
|
26
|
-
height={
|
|
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
|
+
}
|