@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
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DEFAULT_THEME_SPACING = void 0;
|
|
4
|
+
exports.resolveThemeSpacing = resolveThemeSpacing;
|
|
5
|
+
exports.DEFAULT_THEME_SPACING = {
|
|
6
|
+
screen: 16,
|
|
7
|
+
pageTop: 16,
|
|
8
|
+
pageBottom: 24,
|
|
9
|
+
};
|
|
10
|
+
/** App spacing over the package defaults. Pure so it can be unit-tested. */
|
|
11
|
+
function resolveThemeSpacing(spacing) {
|
|
12
|
+
return { ...exports.DEFAULT_THEME_SPACING, ...spacing };
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=resolveThemeSpacing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolveThemeSpacing.js","sourceRoot":"","sources":["../../src/theme/resolveThemeSpacing.ts"],"names":[],"mappings":";;;AAgBA,kDAIC;AAXY,QAAA,qBAAqB,GAAiB;IACjD,MAAM,EAAE,EAAE;IACV,OAAO,EAAE,EAAE;IACX,UAAU,EAAE,EAAE;CACf,CAAC;AAEF,4EAA4E;AAC5E,SAAgB,mBAAmB,CACjC,OAA0C;IAE1C,OAAO,EAAE,GAAG,6BAAqB,EAAE,GAAG,OAAO,EAAE,CAAC;AAClD,CAAC"}
|
package/dist/theme/types.d.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
import type { ThemeSpacing } from "./resolveThemeSpacing";
|
|
1
2
|
export type ThemeColors = Record<string, string>;
|
|
2
3
|
export type ThemeContextValue = {
|
|
3
4
|
colors: {
|
|
4
5
|
light: ThemeColors;
|
|
5
6
|
dark: ThemeColors;
|
|
6
7
|
};
|
|
8
|
+
spacing: ThemeSpacing;
|
|
7
9
|
};
|
|
8
10
|
//# sourceMappingURL=types.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/theme/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAEjD,MAAM,MAAM,iBAAiB,GAAG;IAC9B,MAAM,EAAE;QAAE,KAAK,EAAE,WAAW,CAAC;QAAC,IAAI,EAAE,WAAW,CAAA;KAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/theme/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAE1D,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAEjD,MAAM,MAAM,iBAAiB,GAAG;IAC9B,MAAM,EAAE;QAAE,KAAK,EAAE,WAAW,CAAC;QAAC,IAAI,EAAE,WAAW,CAAA;KAAE,CAAC;IAClD,OAAO,EAAE,YAAY,CAAC;CACvB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useThemeSpacing.d.ts","sourceRoot":"","sources":["../../src/theme/useThemeSpacing.ts"],"names":[],"mappings":"AACA,OAAO,EAAyB,KAAK,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAGjF,yEAAyE;AACzE,wBAAgB,eAAe,IAAI,YAAY,CAE9C"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useThemeSpacing = useThemeSpacing;
|
|
4
|
+
const react_1 = require("react");
|
|
5
|
+
const resolveThemeSpacing_1 = require("./resolveThemeSpacing");
|
|
6
|
+
const ThemeProvider_1 = require("./ThemeProvider");
|
|
7
|
+
/** Spacing from `ThemeProvider`, or the package defaults outside one. */
|
|
8
|
+
function useThemeSpacing() {
|
|
9
|
+
return (0, react_1.useContext)(ThemeProvider_1.ThemeContext)?.spacing ?? resolveThemeSpacing_1.DEFAULT_THEME_SPACING;
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=useThemeSpacing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useThemeSpacing.js","sourceRoot":"","sources":["../../src/theme/useThemeSpacing.ts"],"names":[],"mappings":";;AAKA,0CAEC;AAPD,iCAAmC;AACnC,+DAAiF;AACjF,mDAA+C;AAE/C,yEAAyE;AACzE,SAAgB,eAAe;IAC7B,OAAO,IAAA,kBAAU,EAAC,4BAAY,CAAC,EAAE,OAAO,IAAI,2CAAqB,CAAC;AACpE,CAAC"}
|
|
@@ -29,16 +29,36 @@ export function Root({ children }: { children: React.ReactNode }) {
|
|
|
29
29
|
// Example app colours — role names are yours
|
|
30
30
|
export const Colors = {
|
|
31
31
|
light: {
|
|
32
|
-
buttonBackground: "#
|
|
33
|
-
buttonLabel: "#
|
|
32
|
+
buttonBackground: "#2563EB",
|
|
33
|
+
buttonLabel: "#FFFFFF",
|
|
34
34
|
},
|
|
35
35
|
dark: {
|
|
36
|
-
buttonBackground: "#
|
|
37
|
-
buttonLabel: "#
|
|
36
|
+
buttonBackground: "#2563EB",
|
|
37
|
+
buttonLabel: "#FFFFFF",
|
|
38
38
|
},
|
|
39
39
|
} as const;
|
|
40
40
|
```
|
|
41
41
|
|
|
42
|
+
### Spacing
|
|
43
|
+
|
|
44
|
+
`ThemeProvider` also takes optional `spacing`. Omitted keys keep the package default, so most apps don't set it.
|
|
45
|
+
|
|
46
|
+
```tsx
|
|
47
|
+
<ThemeProvider colors={Colors} spacing={{ screen: 20 }}>
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
| Key | Default | Used by |
|
|
51
|
+
|---|---|---|
|
|
52
|
+
| `screen` | `16` | Side padding of `Page` and `Form` content, `ErrorBanner`, the `TabAccessoryButton` fallback pill |
|
|
53
|
+
| `pageTop` | `16` | Gap between the header and the first item of a `Page` |
|
|
54
|
+
| `pageBottom` | `24` | Gap after the last item of a `Page` |
|
|
55
|
+
|
|
56
|
+
Read it in your own components with `useThemeSpacing()`. Outside `ThemeProvider` it returns the defaults (`DEFAULT_THEME_SPACING`). A component's own padding prop, such as `Form`'s `paddingHorizontal`, still wins.
|
|
57
|
+
|
|
58
|
+
```tsx
|
|
59
|
+
const { screen } = useThemeSpacing();
|
|
60
|
+
```
|
|
61
|
+
|
|
42
62
|
### `useThemeColor`
|
|
43
63
|
|
|
44
64
|
Resolves a role for the current scheme (React Native `useColorScheme`; anything other than `"dark"` is light):
|
|
@@ -70,42 +90,82 @@ The package does not store preferences or call `Appearance.setColorScheme`. If y
|
|
|
70
90
|
|
|
71
91
|
## Button
|
|
72
92
|
|
|
73
|
-
`Button` is presentational.
|
|
93
|
+
`Button` is presentational. Colours default from `ThemeProvider`; pass props only to override. Full width of its parent by default; override with `style`.
|
|
74
94
|
|
|
75
95
|
```tsx
|
|
76
|
-
import { Button
|
|
96
|
+
import { Button } from "@studio-piot/native-ui";
|
|
77
97
|
|
|
78
98
|
export function SaveButton({ onPress }: { onPress: () => void }) {
|
|
79
|
-
return
|
|
80
|
-
<Button
|
|
81
|
-
label="Save"
|
|
82
|
-
onPress={onPress}
|
|
83
|
-
backgroundColor={useThemeColor("buttonBackground")}
|
|
84
|
-
labelColor={useThemeColor("buttonLabel")}
|
|
85
|
-
/>
|
|
86
|
-
);
|
|
99
|
+
return <Button label="Save" onPress={onPress} />;
|
|
87
100
|
}
|
|
88
101
|
```
|
|
89
102
|
|
|
103
|
+
### Theme roles
|
|
104
|
+
|
|
105
|
+
Add these keys to both light and dark maps when you omit the colour props. `Form` uses the same roles for its submit button.
|
|
106
|
+
|
|
107
|
+
| Prop | Default theme role | Filled | Outline |
|
|
108
|
+
|---|---|---|---|
|
|
109
|
+
| `backgroundColor` | `buttonBackground` | Fill | Border, label, icon, spinner |
|
|
110
|
+
| `labelColor` | `buttonLabel` | Label, icon, spinner | Unused |
|
|
111
|
+
|
|
112
|
+
Explicit props always win, so existing code that passes both colours keeps working:
|
|
113
|
+
|
|
114
|
+
```tsx
|
|
115
|
+
<Button
|
|
116
|
+
label="Delete"
|
|
117
|
+
onPress={onDelete}
|
|
118
|
+
backgroundColor={useThemeColor("danger")}
|
|
119
|
+
labelColor={useThemeColor("onDanger")}
|
|
120
|
+
/>
|
|
121
|
+
```
|
|
122
|
+
|
|
90
123
|
### Props
|
|
91
124
|
|
|
92
125
|
| Prop | Type | Default | Notes |
|
|
93
126
|
|---|---|---|---|
|
|
94
|
-
| `label` | `string` | — | Required |
|
|
127
|
+
| `label` | `string` | — | Required. Shown text and accessibility label |
|
|
95
128
|
| `onPress` | `() => void` | — | Required |
|
|
96
|
-
| `backgroundColor` | `string` |
|
|
97
|
-
| `labelColor` | `string` |
|
|
129
|
+
| `backgroundColor` | `string` | theme `buttonBackground` | Optional override |
|
|
130
|
+
| `labelColor` | `string` | theme `buttonLabel` | Optional override |
|
|
98
131
|
| `variant` | `"filled" \| "outline"` | `"filled"` | |
|
|
99
132
|
| `radius` | see below | `"xl"` | Tailwind-style steps |
|
|
100
|
-
| `
|
|
101
|
-
| `
|
|
133
|
+
| `icon` | `ButtonIcon` | — | `({ color, size }) => ReactNode` |
|
|
134
|
+
| `iconPosition` | `"start" \| "end" \| "only"` | `"start"` | `"only"` hides the label text |
|
|
135
|
+
| `iconSize` | `number` | `20` | |
|
|
136
|
+
| `disabled` | `boolean` | `false` | 40% opacity; not pressable |
|
|
137
|
+
| `loading` | `boolean` | `false` | Spinner over the content; not pressable; full opacity |
|
|
102
138
|
| `style` | view style | — | Merged after the shell |
|
|
103
139
|
| `labelStyle` | text style | — | Merged after the label |
|
|
104
140
|
|
|
105
141
|
### Variants
|
|
106
142
|
|
|
107
|
-
- **filled** — `backgroundColor` fill, `labelColor
|
|
108
|
-
- **outline** — transparent fill
|
|
143
|
+
- **filled** — `backgroundColor` fill; label, icon and spinner in `labelColor`.
|
|
144
|
+
- **outline** — transparent fill with a 1pt border; border, label, icon and spinner all in `backgroundColor`.
|
|
145
|
+
|
|
146
|
+
### Icons
|
|
147
|
+
|
|
148
|
+
The package has no Expo dependency, so the icon is a render function. It receives the content colour for the variant and `iconSize`; return an expo-symbols `SymbolView` or any other icon.
|
|
149
|
+
|
|
150
|
+
```tsx
|
|
151
|
+
import { SymbolView } from "expo-symbols";
|
|
152
|
+
|
|
153
|
+
<Button
|
|
154
|
+
label="Share"
|
|
155
|
+
onPress={onShare}
|
|
156
|
+
icon={({ color, size }) => (
|
|
157
|
+
<SymbolView name="square.and.arrow.up" size={size} tintColor={color} />
|
|
158
|
+
)}
|
|
159
|
+
/>
|
|
160
|
+
|
|
161
|
+
// Icon after the label
|
|
162
|
+
<Button label="Next" iconPosition="end" icon={arrow} onPress={onNext} />
|
|
163
|
+
|
|
164
|
+
// Icon only — label is still read by VoiceOver
|
|
165
|
+
<Button label="Add" iconPosition="only" icon={plus} radius="full" style={{ width: 52 }} onPress={onAdd} />
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
The icon and label are 8pt apart and centred together.
|
|
109
169
|
|
|
110
170
|
### Radius
|
|
111
171
|
|
|
@@ -121,39 +181,33 @@ export function SaveButton({ onPress }: { onPress: () => void }) {
|
|
|
121
181
|
| `full` | pill (`999`) |
|
|
122
182
|
|
|
123
183
|
```tsx
|
|
124
|
-
<Button
|
|
125
|
-
label="Continue"
|
|
126
|
-
onPress={onContinue}
|
|
127
|
-
backgroundColor={backgroundColor}
|
|
128
|
-
labelColor={labelColor}
|
|
129
|
-
radius="full"
|
|
130
|
-
variant="outline"
|
|
131
|
-
/>
|
|
184
|
+
<Button label="Continue" onPress={onContinue} radius="full" variant="outline" />
|
|
132
185
|
```
|
|
133
186
|
|
|
187
|
+
### Layout
|
|
188
|
+
|
|
189
|
+
Minimum height 52, 20pt horizontal and 12pt vertical padding. Long labels wrap and stay centred inside the padding. The label is 17pt, weight 600.
|
|
190
|
+
|
|
134
191
|
### Loading and disabled
|
|
135
192
|
|
|
136
193
|
```tsx
|
|
137
|
-
<Button
|
|
138
|
-
label="Saving…"
|
|
139
|
-
onPress={onSave}
|
|
140
|
-
backgroundColor={backgroundColor}
|
|
141
|
-
labelColor={labelColor}
|
|
142
|
-
loading={isSaving}
|
|
143
|
-
disabled={!canSave}
|
|
144
|
-
/>
|
|
194
|
+
<Button label="Save" onPress={onSave} loading={isSaving} disabled={!canSave} />
|
|
145
195
|
```
|
|
146
196
|
|
|
147
|
-
|
|
197
|
+
Neither state fires `onPress`. Disabled fades the button to 40%. Loading keeps full opacity and shows a spinner over the content. The label stays in the layout, invisible, so the button keeps its size. Screen readers get `busy` while loading.
|
|
148
198
|
|
|
149
199
|
## Imports
|
|
150
200
|
|
|
151
201
|
```ts
|
|
152
202
|
import {
|
|
153
203
|
Button,
|
|
204
|
+
BUTTON_THEME_ROLES,
|
|
154
205
|
ThemeProvider,
|
|
206
|
+
type ButtonIcon,
|
|
207
|
+
type ButtonProps,
|
|
155
208
|
useColorScheme,
|
|
156
209
|
useThemeColor,
|
|
210
|
+
useThemeSpacing,
|
|
157
211
|
version,
|
|
158
212
|
} from "@studio-piot/native-ui";
|
|
159
213
|
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# Choice — usage
|
|
2
|
+
|
|
3
|
+
One option in a list of next steps: an icon, a title and an optional description, in a filled row. Tapping it acts straight away, usually to open the next screen, as in "What are you doing? Running / HIIT".
|
|
4
|
+
|
|
5
|
+
Use `Choice` for a short list of destinations, or, with `selected`, for picking one of a few visual options such as candidate routes. To pick a value that stays on the screen, use [RadioField](./radio-field.md), [SegmentedControl](./segmented-control.md) or [Select](./select.md). For a settings list of links, use [LinkBlocks](./link-blocks.md).
|
|
6
|
+
|
|
7
|
+
## Basic usage
|
|
8
|
+
|
|
9
|
+
```tsx
|
|
10
|
+
import { Choice, Page } from "@studio-piot/native-ui";
|
|
11
|
+
import { router } from "expo-router";
|
|
12
|
+
import { SymbolView } from "expo-symbols";
|
|
13
|
+
import { View } from "react-native";
|
|
14
|
+
|
|
15
|
+
export default function NewActivity() {
|
|
16
|
+
return (
|
|
17
|
+
<Page>
|
|
18
|
+
<View style={{ gap: 16 }}>
|
|
19
|
+
<Choice
|
|
20
|
+
title="Running"
|
|
21
|
+
description="Outdoors, with GPS"
|
|
22
|
+
icon={({ color, size }) => (
|
|
23
|
+
<SymbolView name="figure.run" size={size} tintColor={color} />
|
|
24
|
+
)}
|
|
25
|
+
onPress={() => router.push("/new-activity/running")}
|
|
26
|
+
/>
|
|
27
|
+
<Choice
|
|
28
|
+
title="HIIT"
|
|
29
|
+
description="Intervals, anywhere"
|
|
30
|
+
icon={({ color, size }) => (
|
|
31
|
+
<SymbolView name="stopwatch" size={size} tintColor={color} />
|
|
32
|
+
)}
|
|
33
|
+
onPress={() => router.push("/new-activity/hiit")}
|
|
34
|
+
/>
|
|
35
|
+
</View>
|
|
36
|
+
</Page>
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
- **Icon.** A render function, like `Button` and `HeaderIcon`: it receives the icon colour and `iconSize` (default 28). Icons sit in a 32pt column so titles line up down the list; a larger icon widens its column instead of overflowing.
|
|
42
|
+
- **Title only.** `description` is optional.
|
|
43
|
+
- **Spacing.** Put choices in a stack with a 16pt gap. The row has no margin of its own.
|
|
44
|
+
|
|
45
|
+
## Selectable choices
|
|
46
|
+
|
|
47
|
+
Pass `selected` to make the choices a set where one is picked, for example candidate routes with a drawing of each in the icon slot. The selected row gets a highlight border (`choiceSelected`); the others keep a transparent border of the same width, so nothing shifts when the selection moves. Tapping still calls `onPress`, where you update the selection.
|
|
48
|
+
|
|
49
|
+
```tsx
|
|
50
|
+
{routes.map((route) => (
|
|
51
|
+
<Choice
|
|
52
|
+
key={route.id}
|
|
53
|
+
title={route.name}
|
|
54
|
+
description={`${route.km} km`}
|
|
55
|
+
icon={({ color, size }) => <RouteThumbnail route={route} color={color} size={size} />}
|
|
56
|
+
iconSize={48}
|
|
57
|
+
selected={route.id === selectedId}
|
|
58
|
+
onPress={() => setSelectedId(route.id)}
|
|
59
|
+
/>
|
|
60
|
+
))}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Pair it with one action that uses the selection, e.g. **Use this route**. Leave `selected` out when tapping should just act.
|
|
64
|
+
|
|
65
|
+
## Accessibility
|
|
66
|
+
|
|
67
|
+
The row is one button. VoiceOver reads its text together, for example "Running, Outdoors, with GPS, button". With `selected`, it is a radio option instead, read as selected or not. The icon is decorative and hidden from screen readers. `disabled` sets the disabled state, fades the row to 40% and blocks presses.
|
|
68
|
+
|
|
69
|
+
## Theme roles
|
|
70
|
+
|
|
71
|
+
Add these keys to both the light and dark maps.
|
|
72
|
+
|
|
73
|
+
| Usage | Role |
|
|
74
|
+
|---|---|
|
|
75
|
+
| Row fill | `choiceBackground` |
|
|
76
|
+
| Title | `choiceTitle` |
|
|
77
|
+
| Description | `choiceDescription` |
|
|
78
|
+
| Icon | `choiceIcon` |
|
|
79
|
+
| Selected border (with `selected`) | `choiceSelected` |
|
|
80
|
+
|
|
81
|
+
## Props
|
|
82
|
+
|
|
83
|
+
| Prop | Type | Default | Notes |
|
|
84
|
+
|---|---|---|---|
|
|
85
|
+
| `title` | `string` | — | Required |
|
|
86
|
+
| `onPress` | `() => void` | — | Required |
|
|
87
|
+
| `description` | `string` | — | Second line under the title; may wrap to more lines |
|
|
88
|
+
| `selected` | `boolean` | — | Makes it a selectable option: highlight border when true, radio semantics |
|
|
89
|
+
| `icon` | `ButtonIcon` | — | `({ color, size }) => ReactNode` |
|
|
90
|
+
| `iconSize` | `number` | `28` | Icon box |
|
|
91
|
+
| `disabled` | `boolean` | `false` | 40% opacity; not pressable |
|
|
92
|
+
| `radius` | `ButtonRadius` | `"xl"` | Same scale as `Button` (12) |
|
|
93
|
+
| `backgroundColor` | `string` | theme `choiceBackground` | Optional override |
|
|
94
|
+
| `titleColor` | `string` | theme `choiceTitle` | Optional override |
|
|
95
|
+
| `descriptionColor` | `string` | theme `choiceDescription` | Optional override |
|
|
96
|
+
| `iconColor` | `string` | theme `choiceIcon` | Optional override |
|
|
97
|
+
| `selectedColor` | `string` | theme `choiceSelected` | Optional override |
|
|
98
|
+
| `style` | view style | — | Merged after the row |
|
|
99
|
+
|
|
100
|
+
Layout: 72pt minimum height, 16pt horizontal and 14pt vertical padding, 12pt between the icon and the text. The title is 17pt semibold and the description 13pt, the same as the rest of the package.
|
|
101
|
+
|
|
102
|
+
## Imports
|
|
103
|
+
|
|
104
|
+
```ts
|
|
105
|
+
import {
|
|
106
|
+
Choice,
|
|
107
|
+
CHOICE_ICON_SIZE,
|
|
108
|
+
CHOICE_THEME_ROLES,
|
|
109
|
+
type ChoiceProps,
|
|
110
|
+
} from "@studio-piot/native-ui";
|
|
111
|
+
```
|
|
@@ -12,7 +12,7 @@ npx expo install react-native-svg
|
|
|
12
12
|
|
|
13
13
|
## Theme roles
|
|
14
14
|
|
|
15
|
-
Set these in both light and dark.
|
|
15
|
+
Set these in both the light and dark maps, with values that suit each scheme, so the banner follows the system appearance like the rest of the screen. A light banner in light mode and a dark one in dark mode both work. The status bar text switches to stay readable: dark text over a light fill, light text over a dark one.
|
|
16
16
|
|
|
17
17
|
| Usage | Role |
|
|
18
18
|
|---|---|
|
|
@@ -22,6 +22,39 @@ Set these in both light and dark. The Ribe banner stays charcoal with a flare di
|
|
|
22
22
|
| Row dividers | `errorBannerDivider` |
|
|
23
23
|
| Megaphone disc | `errorBannerAccent` |
|
|
24
24
|
|
|
25
|
+
```ts
|
|
26
|
+
const Colors = {
|
|
27
|
+
light: {
|
|
28
|
+
errorBannerBackground: "#FFFFFF",
|
|
29
|
+
errorBannerText: "#111827",
|
|
30
|
+
errorBannerSecondary: "#6B7280",
|
|
31
|
+
errorBannerDivider: "#E5E7EB",
|
|
32
|
+
errorBannerAccent: "#DC2626",
|
|
33
|
+
},
|
|
34
|
+
dark: {
|
|
35
|
+
errorBannerBackground: "#1F2937",
|
|
36
|
+
errorBannerText: "#F9FAFB",
|
|
37
|
+
errorBannerSecondary: "#9CA3AF",
|
|
38
|
+
errorBannerDivider: "#374151",
|
|
39
|
+
errorBannerAccent: "#DC2626",
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
The megaphone is drawn in `errorBannerBackground`, so pick an accent that contrasts with the fill in both schemes.
|
|
45
|
+
|
|
46
|
+
## Sizes
|
|
47
|
+
|
|
48
|
+
Text matches the 17pt body text of `Button`, `FieldText` and `Select`.
|
|
49
|
+
|
|
50
|
+
| Element | Value |
|
|
51
|
+
|---|---|
|
|
52
|
+
| Title and row text | 17pt, line height 22 (title weight 600) |
|
|
53
|
+
| Megaphone disc | 32×32, icon 18 |
|
|
54
|
+
| Dismiss cross | 18 |
|
|
55
|
+
| Row arrow | 20 |
|
|
56
|
+
| Row height | at least 44 (tap target) |
|
|
57
|
+
|
|
25
58
|
## With Form
|
|
26
59
|
|
|
27
60
|
Give each field a `name` that matches `FieldError.field`. On submit, set `errors`. `Form` opens the banner. Typing in a field, changing a choice, or pressing ✕ hides it. The next submit shows it again. While an error is listed, that field keeps the error border and does not repeat the message underneath.
|
|
@@ -56,7 +89,8 @@ export function NewActivity({ onClose }: { onClose: () => void }) {
|
|
|
56
89
|
};
|
|
57
90
|
|
|
58
91
|
return (
|
|
59
|
-
|
|
92
|
+
// Title and close button live in the screen's stack options; see Form.
|
|
93
|
+
<Form errors={errors} onSubmit={submit}>
|
|
60
94
|
<FieldText name="name" label="Name" value={name} onChangeText={setName} />
|
|
61
95
|
<Select
|
|
62
96
|
name="type"
|
|
@@ -74,7 +108,16 @@ List errors in the same order as the fields, top to bottom.
|
|
|
74
108
|
|
|
75
109
|
## Without Form
|
|
76
110
|
|
|
77
|
-
`ErrorBanner`, `useErrorBanner`, and `ErrorBannerProvider` work with any scroll view. Put the banner inside the provider and call `jump` from context so a row scrolls the matching `ErrorBannerField` (or a package field with `name`) into view. Call `present` on submit and `dismiss` when the runner edits the screen.
|
|
111
|
+
`ErrorBanner`, `useErrorBanner`, and `ErrorBannerProvider` work with any scroll view. `Form` and `FormSheet` wire them up for you. Put the banner inside the provider and call `jump` from context so a row scrolls the matching `ErrorBannerField` (or a package field with `name`) into view. Call `present` on submit and `dismiss` when the runner edits the screen.
|
|
112
|
+
|
|
113
|
+
`ErrorBannerProvider` props:
|
|
114
|
+
|
|
115
|
+
| Prop | Notes |
|
|
116
|
+
|---|---|
|
|
117
|
+
| `scrollRef` | Anything with `scrollTo({ y, animated })`: a `ScrollView`, `KeyboardAwareScrollView` or `BottomSheetScrollView` |
|
|
118
|
+
| `bannerHeightRef` | How far the banner covers the top of the scroll area |
|
|
119
|
+
| `measureRef` | Optional. A view at the top of the scroll content to measure fields against. Defaults to `scrollRef`, which works for a plain `ScrollView` |
|
|
120
|
+
| `onBeforeJump` | Optional. Runs before scrolling to a field, e.g. to expand a sheet |
|
|
78
121
|
|
|
79
122
|
## FieldError
|
|
80
123
|
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
# FormSheet — usage
|
|
2
|
+
|
|
3
|
+
A form in a draggable sheet over a full-screen background, usually a map. Use it when the screen is about the background and the form adjusts it: choose a start point on a map, set a radius, preview a route.
|
|
4
|
+
|
|
5
|
+
- **Title on the sheet.** `title` sits at the top of the sheet, under the drag indicator. The native header keeps only the close button, transparent so the background shows behind it.
|
|
6
|
+
- **Three stops.** `0` shows just the handle and the title. `1` puts the sheet top at half the screen. `2` takes it up to the bottom of the native header, never further.
|
|
7
|
+
- **Sticky submit.** The submit button sits in a footer that stays above the keyboard, the same footer as `Form`.
|
|
8
|
+
- **Errors.** Pass `errors` and the `ErrorBanner` works as it does in `Form`. A banner row expands the sheet and scrolls to that field.
|
|
9
|
+
|
|
10
|
+
Use `Form` instead when the screen is only the form.
|
|
11
|
+
|
|
12
|
+
## Peer dependencies
|
|
13
|
+
|
|
14
|
+
The same as `Form` and `Select`: `@gorhom/bottom-sheet`, `react-native-gesture-handler`, `react-native-reanimated`, `react-native-keyboard-controller` and `react-native-safe-area-context`, plus `react-native-screens` for the iOS banner overlay. Wrap the app in `GestureHandlerRootView` and `KeyboardProvider`.
|
|
15
|
+
|
|
16
|
+
## Basic usage
|
|
17
|
+
|
|
18
|
+
```tsx
|
|
19
|
+
// app/(app)/run/_layout.tsx
|
|
20
|
+
export default function Layout() {
|
|
21
|
+
return <Stack screenOptions={{ headerTransparent: true }} />;
|
|
22
|
+
}
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
```tsx
|
|
26
|
+
// app/(app)/run/index.tsx
|
|
27
|
+
import { FormSheet, HeaderIcon, Slider } from "@studio-piot/native-ui";
|
|
28
|
+
import { router, Stack } from "expo-router";
|
|
29
|
+
import { useHeaderHeight } from "expo-router/react-navigation";
|
|
30
|
+
|
|
31
|
+
export default function RunSetup() {
|
|
32
|
+
const [distance, setDistance] = useState(5);
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
<>
|
|
36
|
+
<Stack.Screen
|
|
37
|
+
options={{
|
|
38
|
+
headerShown: true,
|
|
39
|
+
headerTransparent: true,
|
|
40
|
+
title: "", // the title is on the sheet
|
|
41
|
+
headerLeft: () => (
|
|
42
|
+
<HeaderIcon label="Close" icon={closeIcon} onPress={() => router.back()} />
|
|
43
|
+
),
|
|
44
|
+
}}
|
|
45
|
+
/>
|
|
46
|
+
<FormSheet
|
|
47
|
+
title="Run"
|
|
48
|
+
headerHeight={useHeaderHeight()}
|
|
49
|
+
initialIndex={1}
|
|
50
|
+
background={(inset) => <RouteMap chromeInset={inset} radiusKm={distance / 2} />}
|
|
51
|
+
onSubmit={generate}
|
|
52
|
+
submitLabel="Generate route"
|
|
53
|
+
>
|
|
54
|
+
<Slider label="Distance" value={distance} onValueChange={setDistance} />
|
|
55
|
+
</FormSheet>
|
|
56
|
+
</>
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
- **Modal routes need their own stack.** On iOS a screen presented as a modal (`presentation: "modal"` or `"fullScreenModal"`) sits outside the navigation controller, so its header options are ignored and no header appears. Make the route a folder with its own `_layout.tsx` that returns a `<Stack />`, and put the screen in `index.tsx`. The parent still presents the folder as a modal, and the URL doesn't change.
|
|
62
|
+
- **Use `headerTransparent: true`.** Without it the header is opaque, the map starts below it, and the sheet's top stop still works, but the map doesn't show behind the bar.
|
|
63
|
+
- **Pass `headerHeight={useHeaderHeight()}`** (from `expo-router/react-navigation`). The top stop ends there. Without it, `FormSheet` assumes the status bar plus a standard bar (44pt on iOS, 56pt on Android), which is wrong for a large title or a custom header.
|
|
64
|
+
|
|
65
|
+
## The background and its insets
|
|
66
|
+
|
|
67
|
+
`background` is a function so the layer can keep its own controls and camera clear of the chrome. It receives a `FormSheetInset`:
|
|
68
|
+
|
|
69
|
+
| Field | Meaning |
|
|
70
|
+
|---|---|
|
|
71
|
+
| `top` | Bottom of the native header, from the screen top |
|
|
72
|
+
| `bottom` | Top of the sheet at its current stop, from the screen bottom (includes the footer) |
|
|
73
|
+
| `bottomAtHalf` | Top of the sheet at the middle stop. Doesn't change while dragging, so it suits map camera padding |
|
|
74
|
+
|
|
75
|
+
The background fills the whole screen behind the sheet. Its top corners are rounded (`SHEET_TOP_RADIUS`, 20), so a map that should show through those corners should continue a little past `bottom`, for example `bottom - SHEET_TOP_RADIUS - 8`.
|
|
76
|
+
|
|
77
|
+
## Errors
|
|
78
|
+
|
|
79
|
+
The same contract as `Form`: give each field a `name` matching `FieldError.field`, then set `errors` when submit fails.
|
|
80
|
+
|
|
81
|
+
```tsx
|
|
82
|
+
<FormSheet errors={errors} onSubmit={submit} …>
|
|
83
|
+
<FieldText name="name" label="Name" value={name} onChangeText={setName} />
|
|
84
|
+
</FormSheet>
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
On iOS the banner covers the top of the screen, including the native header (it is drawn in a native overlay window). On Android it slides down directly below the header. Tapping a row expands the sheet to stop `2`, scrolls the field clear of the banner and focuses it if it can take focus. Editing a field dismisses the banner, and the next submit shows it again.
|
|
88
|
+
|
|
89
|
+
## Theme roles
|
|
90
|
+
|
|
91
|
+
| Usage | Role |
|
|
92
|
+
|---|---|
|
|
93
|
+
| Sheet fill and handle | `formSheetBackground` |
|
|
94
|
+
| Sheet title | `formSheetTitle` |
|
|
95
|
+
| Footer ground | `formSheetFooterBackground` |
|
|
96
|
+
| Drag indicator | `formSheetHandle` |
|
|
97
|
+
| Submit fill | `buttonBackground` |
|
|
98
|
+
| Submit label | `buttonLabel` |
|
|
99
|
+
|
|
100
|
+
With `errors`, the banner also reads the `errorBanner*` roles.
|
|
101
|
+
|
|
102
|
+
## Props
|
|
103
|
+
|
|
104
|
+
| Prop | Type | Default | Notes |
|
|
105
|
+
|---|---|---|---|
|
|
106
|
+
| `title` | `string` | — | Top of the sheet, under the indicator. Always visible at the lowest stop |
|
|
107
|
+
| `children` | `ReactNode` | — | Required. Sheet content; scrolls inside the sheet |
|
|
108
|
+
| `background` | `(inset: FormSheetInset) => ReactNode` | — | Required. Full-screen layer behind the sheet |
|
|
109
|
+
| `onSubmit` | `() => void` | — | Required |
|
|
110
|
+
| `errors` | `FieldError[]` | `[]` | Screen banner. Empty hides it |
|
|
111
|
+
| `submitLabel` | `string` | `"Save"` | |
|
|
112
|
+
| `submitting` | `boolean` | `false` | Spinner on the submit button |
|
|
113
|
+
| `submitDisabled` | `boolean` | `false` | |
|
|
114
|
+
| `submitRadius` | `ButtonRadius` | `"lg"` | |
|
|
115
|
+
| `initialIndex` | `0 \| 1 \| 2` | `0` | Stop on first paint |
|
|
116
|
+
| `headerHeight` | `number` | status bar + standard bar | `useHeaderHeight()`. Where the top stop ends |
|
|
117
|
+
| `paddingHorizontal` | `number` | theme `spacing.screen` | Content and footer |
|
|
118
|
+
| `backgroundColor` | `string` | theme `formSheetBackground` | |
|
|
119
|
+
| `titleColor` | `string` | theme `formSheetTitle` | |
|
|
120
|
+
| `footerBackgroundColor` | `string` | theme `formSheetFooterBackground` | |
|
|
121
|
+
| `handleColor` | `string` | theme `formSheetHandle` | |
|
|
122
|
+
| `buttonBackgroundColor` | `string` | theme `buttonBackground` | |
|
|
123
|
+
| `buttonLabelColor` | `string` | theme `buttonLabel` | |
|
|
124
|
+
|
|
125
|
+
## Migrating from 0.12
|
|
126
|
+
|
|
127
|
+
`onClose` and `closeButton` are gone. Move the close button to the screen's stack options (`headerLeft`) with `headerShown: true` and `headerTransparent: true`. `title`, `titleColor` and the `formSheetTitle` role still put the title on the sheet. Leave the header's `title` empty so it isn't shown twice.
|
|
128
|
+
|
|
129
|
+
## Imports
|
|
130
|
+
|
|
131
|
+
```ts
|
|
132
|
+
import {
|
|
133
|
+
FormSheet,
|
|
134
|
+
FORM_SHEET_THEME_ROLES,
|
|
135
|
+
SHEET_TOP_RADIUS,
|
|
136
|
+
formSheetSnapPoints,
|
|
137
|
+
type FormSheetInset,
|
|
138
|
+
type FormSheetProps,
|
|
139
|
+
} from "@studio-piot/native-ui";
|
|
140
|
+
```
|