@terreno/ui 0.8.3 → 0.9.1

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.
@@ -0,0 +1,171 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { TabRouter } from "@react-navigation/native";
3
+ import { Navigator, Slot } from "expo-router";
4
+ import { useCallback, useEffect, useRef, useState } from "react";
5
+ import { Animated, Dimensions, Pressable, View } from "react-native";
6
+ import { Badge } from "./Badge";
7
+ import { SIDEBAR_BADGE_STATUS_MAP } from "./Common";
8
+ import { Icon } from "./Icon";
9
+ import { Text } from "./Text";
10
+ import { useTheme } from "./Theme";
11
+ const DRAWER_WIDTH = 280;
12
+ const ITEM_HEIGHT = 48;
13
+ const ICON_SIZE = 20;
14
+ const BACKDROP_OPACITY = 0.5;
15
+ const ANIMATION_DURATION = 250;
16
+ const SidebarItem = ({ item, isActive, onPress, itemStyle }) => {
17
+ var _a;
18
+ const { theme } = useTheme();
19
+ const handlePress = useCallback(() => {
20
+ onPress(item.route);
21
+ }, [onPress, item.route]);
22
+ return (_jsxs(Pressable, { accessibilityLabel: item.label, accessibilityRole: "button", onPress: handlePress, style: [
23
+ {
24
+ alignItems: "center",
25
+ backgroundColor: isActive ? theme.surface.secondaryLight : "transparent",
26
+ borderRadius: theme.radius.default,
27
+ flexDirection: "row",
28
+ gap: 14,
29
+ height: ITEM_HEIGHT,
30
+ marginHorizontal: 12,
31
+ paddingHorizontal: 14,
32
+ },
33
+ itemStyle,
34
+ ], children: [_jsxs(View, { style: { alignItems: "center", justifyContent: "center", width: ICON_SIZE }, children: [_jsx(Icon, { color: isActive ? "primary" : "secondaryDark", iconName: item.iconName, size: "md" }), Boolean(item.badge) && (_jsx(View, { style: {
35
+ bottom: item.badge === true ? -4 : undefined,
36
+ position: "absolute",
37
+ right: -6,
38
+ top: item.badge === true ? undefined : -4,
39
+ }, children: _jsx(Badge, { maxValue: 99, status: SIDEBAR_BADGE_STATUS_MAP[(_a = item.badgeStatus) !== null && _a !== void 0 ? _a : "error"], value: item.badge === true ? undefined : String(item.badge), variant: item.badge === true ? "iconOnly" : "numberOnly" }) }))] }), _jsx(Text, { bold: isActive, color: isActive ? "primary" : "secondaryDark", size: "md", children: item.label })] }));
40
+ };
41
+ /**
42
+ * Renders the hamburger button, drawer overlay, and children. Works without expo-router Navigator context.
43
+ */
44
+ export const SidebarNavigationPanel = ({ topItems, bottomItems, activeRoute, onNavigate, children, panelStyle, itemStyle, }) => {
45
+ const { theme } = useTheme();
46
+ const [isOpen, setIsOpen] = useState(false);
47
+ const slideAnim = useRef(new Animated.Value(-DRAWER_WIDTH)).current;
48
+ const backdropAnim = useRef(new Animated.Value(0)).current;
49
+ // Animate drawer open/close
50
+ useEffect(() => {
51
+ if (isOpen) {
52
+ Animated.parallel([
53
+ Animated.timing(slideAnim, {
54
+ duration: ANIMATION_DURATION,
55
+ toValue: 0,
56
+ useNativeDriver: true,
57
+ }),
58
+ Animated.timing(backdropAnim, {
59
+ duration: ANIMATION_DURATION,
60
+ toValue: BACKDROP_OPACITY,
61
+ useNativeDriver: true,
62
+ }),
63
+ ]).start();
64
+ }
65
+ else {
66
+ Animated.parallel([
67
+ Animated.timing(slideAnim, {
68
+ duration: ANIMATION_DURATION,
69
+ toValue: -DRAWER_WIDTH,
70
+ useNativeDriver: true,
71
+ }),
72
+ Animated.timing(backdropAnim, {
73
+ duration: ANIMATION_DURATION,
74
+ toValue: 0,
75
+ useNativeDriver: true,
76
+ }),
77
+ ]).start();
78
+ }
79
+ }, [isOpen, slideAnim, backdropAnim]);
80
+ const handleOpen = useCallback(() => setIsOpen(true), []);
81
+ const handleClose = useCallback(() => setIsOpen(false), []);
82
+ const handleNavigate = useCallback((route) => {
83
+ setIsOpen(false);
84
+ onNavigate(route);
85
+ }, [onNavigate]);
86
+ const screenHeight = Dimensions.get("window").height;
87
+ return (_jsxs(View, { style: { flex: 1 }, children: [children, _jsx(Pressable, { accessibilityLabel: "Open navigation menu", accessibilityRole: "button", onPress: handleOpen, style: {
88
+ alignItems: "center",
89
+ backgroundColor: theme.surface.primary,
90
+ borderRadius: theme.radius.full,
91
+ elevation: 4,
92
+ height: 44,
93
+ justifyContent: "center",
94
+ left: 16,
95
+ position: "absolute",
96
+ shadowColor: "#000",
97
+ shadowOffset: { height: 2, width: 0 },
98
+ shadowOpacity: 0.25,
99
+ shadowRadius: 4,
100
+ top: 16,
101
+ width: 44,
102
+ zIndex: 10,
103
+ }, children: _jsx(Icon, { color: "inverted", iconName: "bars", size: "md" }) }), isOpen && (_jsx(Pressable, { onPress: handleClose, style: {
104
+ bottom: 0,
105
+ left: 0,
106
+ position: "absolute",
107
+ right: 0,
108
+ top: 0,
109
+ zIndex: 100,
110
+ }, children: _jsx(Animated.View, { style: {
111
+ backgroundColor: "#000",
112
+ flex: 1,
113
+ opacity: backdropAnim,
114
+ } }) })), _jsxs(Animated.View, { style: [
115
+ {
116
+ backgroundColor: theme.surface.base,
117
+ borderColor: theme.border.default,
118
+ borderRightWidth: 1,
119
+ height: screenHeight,
120
+ justifyContent: "space-between",
121
+ left: 0,
122
+ paddingBottom: 32,
123
+ paddingTop: 20,
124
+ position: "absolute",
125
+ top: 0,
126
+ transform: [{ translateX: slideAnim }],
127
+ width: DRAWER_WIDTH,
128
+ zIndex: 200,
129
+ },
130
+ panelStyle,
131
+ ], children: [_jsxs(View, { children: [_jsx(Pressable, { accessibilityLabel: "Close navigation menu", accessibilityRole: "button", onPress: handleClose, style: {
132
+ alignItems: "center",
133
+ alignSelf: "flex-end",
134
+ height: 40,
135
+ justifyContent: "center",
136
+ marginRight: 12,
137
+ width: 40,
138
+ }, children: _jsx(Icon, { color: "secondaryDark", iconName: "xmark", size: "md" }) }), _jsx(View, { style: { gap: 4, marginTop: 8 }, children: topItems.map((item) => (_jsx(SidebarItem, { isActive: activeRoute === item.route, item: item, itemStyle: itemStyle, onPress: handleNavigate }, item.route))) })] }), _jsx(View, { style: { gap: 4 }, children: bottomItems.map((item) => (_jsx(SidebarItem, { isActive: activeRoute === item.route, item: item, onPress: handleNavigate }, item.route))) })] })] }));
139
+ };
140
+ /**
141
+ * Reads active route from Navigator context and renders the drawer + Slot.
142
+ */
143
+ const SidebarNavigatorContent = ({ topItems, bottomItems, onNavigate, panelStyle, itemStyle }) => {
144
+ var _a;
145
+ const { state, navigation } = Navigator.useContext();
146
+ const activeRoute = (_a = state.routes[state.index]) === null || _a === void 0 ? void 0 : _a.name;
147
+ const handleNavigate = useCallback((route) => {
148
+ navigation.navigate(route);
149
+ onNavigate === null || onNavigate === void 0 ? void 0 : onNavigate(route);
150
+ }, [navigation, onNavigate]);
151
+ return (_jsx(SidebarNavigationPanel, { activeRoute: activeRoute, bottomItems: bottomItems, itemStyle: itemStyle, onNavigate: handleNavigate, panelStyle: panelStyle, topItems: topItems, children: _jsx(Slot, {}) }));
152
+ };
153
+ /**
154
+ * Custom expo-router navigator with a hamburger-triggered slide-in drawer.
155
+ * Use in _layout.tsx files:
156
+ *
157
+ * ```tsx
158
+ * export default function SidebarLayout() {
159
+ * return (
160
+ * <SidebarNavigation
161
+ * topItems={[{label: "Home", route: "index", iconName: "house"}]}
162
+ * bottomItems={[{label: "Settings", route: "settings", iconName: "gear"}]}
163
+ * />
164
+ * );
165
+ * }
166
+ * ```
167
+ */
168
+ export const SidebarNavigation = ({ topItems, bottomItems, onNavigate, initialRouteName, screenOptions, panelStyle, itemStyle, }) => {
169
+ return (_jsx(Navigator, { initialRouteName: initialRouteName, router: TabRouter, screenOptions: screenOptions, children: _jsx(SidebarNavigatorContent, { bottomItems: bottomItems, itemStyle: itemStyle, onNavigate: onNavigate, panelStyle: panelStyle, topItems: topItems }) }));
170
+ };
171
+ //# sourceMappingURL=SidebarNavigation.native.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SidebarNavigation.native.js","sourceRoot":"","sources":["../src/SidebarNavigation.native.tsx"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -69,6 +69,7 @@ export * from "./SectionDivider";
69
69
  export * from "./SegmentedControl";
70
70
  export * from "./SelectBadge";
71
71
  export * from "./SelectField";
72
+ export * from "./SidebarNavigation";
72
73
  export * from "./SideDrawer";
73
74
  export * from "./Signature";
74
75
  export * from "./SignatureField";
package/dist/index.js CHANGED
@@ -68,6 +68,7 @@ export * from "./SectionDivider";
68
68
  export * from "./SegmentedControl";
69
69
  export * from "./SelectBadge";
70
70
  export * from "./SelectField";
71
+ export * from "./SidebarNavigation";
71
72
  export * from "./SideDrawer";
72
73
  export * from "./Signature";
73
74
  export * from "./SignatureField";
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.tsx"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -25,6 +25,7 @@
25
25
  "@react-native-community/datetimepicker": "8.6.0",
26
26
  "@react-native-community/slider": "5.0.1",
27
27
  "@react-native-picker/picker": "2.11.4",
28
+ "@react-navigation/native": "^7.1.8",
28
29
  "emoji-datasource": "^16.0.0",
29
30
  "expo-clipboard": "~8.0.8",
30
31
  "expo-document-picker": "~14.0.8",
@@ -132,5 +133,5 @@
132
133
  "test:ci": "TZ=America/New_York bun test",
133
134
  "types": "bun typedoc"
134
135
  },
135
- "version": "0.8.3"
136
+ "version": "0.9.1"
136
137
  }
package/src/Common.ts CHANGED
@@ -2909,3 +2909,114 @@ export interface UserInactivityProps {
2909
2909
  */
2910
2910
  timeForInactivity?: number;
2911
2911
  }
2912
+
2913
+ /**
2914
+ * Maps badge status keys to their semantic meaning for sidebar navigation items.
2915
+ */
2916
+ export const SIDEBAR_BADGE_STATUS_MAP = {
2917
+ error: "error",
2918
+ info: "info",
2919
+ neutral: "neutral",
2920
+ success: "success",
2921
+ warning: "warning",
2922
+ } as const;
2923
+
2924
+ export type SidebarBadgeStatus = keyof typeof SIDEBAR_BADGE_STATUS_MAP;
2925
+
2926
+ export interface SidebarNavigationItem {
2927
+ /**
2928
+ * Display text for the navigation item.
2929
+ */
2930
+ label: string;
2931
+ /**
2932
+ * Route name matching the expo-router file-based route (e.g. "index", "dashboard").
2933
+ */
2934
+ route: string;
2935
+ /**
2936
+ * FontAwesome 6 icon name rendered alongside the label.
2937
+ */
2938
+ iconName: IconName;
2939
+ /**
2940
+ * Badge displayed on the icon. A number shows a count (capped at 99+); true shows a dot indicator.
2941
+ */
2942
+ badge?: number | boolean;
2943
+ /**
2944
+ * Color status of the badge. Defaults to "error".
2945
+ */
2946
+ badgeStatus?: SidebarBadgeStatus;
2947
+ }
2948
+
2949
+ /**
2950
+ * Props for the SidebarNavigation custom expo-router navigator.
2951
+ * Used in _layout.tsx files to provide sidebar navigation.
2952
+ */
2953
+ export interface SidebarNavigationProps {
2954
+ /**
2955
+ * Navigation items displayed at the top of the sidebar.
2956
+ */
2957
+ topItems: SidebarNavigationItem[];
2958
+ /**
2959
+ * Navigation items displayed at the bottom of the sidebar.
2960
+ */
2961
+ bottomItems: SidebarNavigationItem[];
2962
+ /**
2963
+ * Optional callback fired after a navigation item is pressed.
2964
+ */
2965
+ onNavigate?: (route: string) => void;
2966
+ /**
2967
+ * The route to show when the navigator first renders.
2968
+ */
2969
+ initialRouteName?: string;
2970
+ /**
2971
+ * Screen options passed through to the underlying Navigator.
2972
+ */
2973
+ screenOptions?: Record<string, unknown>;
2974
+ /**
2975
+ * Additional styles applied to the sidebar panel container.
2976
+ */
2977
+ panelStyle?: StyleProp<ViewStyle>;
2978
+ /**
2979
+ * Additional styles applied to each navigation item.
2980
+ */
2981
+ itemStyle?: StyleProp<ViewStyle>;
2982
+ /**
2983
+ * Optional Screen definitions passed to the underlying Navigator,
2984
+ * e.g. <Screen name="index" options={{title: "Home"}} />.
2985
+ */
2986
+ children?: React.ReactNode;
2987
+ }
2988
+
2989
+ /**
2990
+ * Props for the standalone SidebarNavigationPanel (no expo-router dependency).
2991
+ * Useful for demos, testing, or non-expo-router apps.
2992
+ */
2993
+ export interface SidebarNavigationPanelProps {
2994
+ /**
2995
+ * Navigation items displayed at the top of the sidebar.
2996
+ */
2997
+ topItems: SidebarNavigationItem[];
2998
+ /**
2999
+ * Navigation items displayed at the bottom of the sidebar.
3000
+ */
3001
+ bottomItems: SidebarNavigationItem[];
3002
+ /**
3003
+ * The currently active route, used to highlight the matching item.
3004
+ */
3005
+ activeRoute?: string;
3006
+ /**
3007
+ * Called when a navigation item is pressed.
3008
+ */
3009
+ onNavigate: (route: string) => void;
3010
+ /**
3011
+ * Main content rendered beside (web) or behind (mobile) the sidebar.
3012
+ */
3013
+ children: React.ReactNode;
3014
+ /**
3015
+ * Additional styles applied to the sidebar panel container.
3016
+ */
3017
+ panelStyle?: StyleProp<ViewStyle>;
3018
+ /**
3019
+ * Additional styles applied to each navigation item.
3020
+ */
3021
+ itemStyle?: StyleProp<ViewStyle>;
3022
+ }
@@ -17,6 +17,7 @@ interface ConsentFormScreenProps {
17
17
  locale: string;
18
18
  onAgree: (data: {checkboxValues: Record<string, boolean>; signature?: string}) => void;
19
19
  onDecline?: () => void;
20
+ variables?: Record<string, string>;
20
21
  }
21
22
 
22
23
  export const ConsentFormScreen: React.FC<ConsentFormScreenProps> = ({
@@ -25,6 +26,7 @@ export const ConsentFormScreen: React.FC<ConsentFormScreenProps> = ({
25
26
  locale,
26
27
  onAgree,
27
28
  onDecline,
29
+ variables,
28
30
  }) => {
29
31
  const [checkboxValues, setCheckboxValues] = useState<Record<string, boolean>>({});
30
32
  const [hasScrolledToBottom, setHasScrolledToBottom] = useState(!form.requireScrollToBottom);
@@ -35,7 +37,10 @@ export const ConsentFormScreen: React.FC<ConsentFormScreenProps> = ({
35
37
  const [contentHeight, setContentHeight] = useState(0);
36
38
  const [layoutHeight, setLayoutHeight] = useState(0);
37
39
 
38
- const content = form.content[locale] ?? form.content[form.defaultLocale] ?? "";
40
+ const rawContent = form.content[locale] ?? form.content[form.defaultLocale] ?? "";
41
+ const content = variables
42
+ ? rawContent.replace(/\{\{(\w+)\}\}/g, (match, key) => variables[key] ?? match)
43
+ : rawContent;
39
44
 
40
45
  const allRequiredCheckboxesChecked = form.checkboxes.every((checkbox, index) => {
41
46
  if (!checkbox.required) {
@@ -1,5 +1,7 @@
1
1
  import {describe, expect, it, mock} from "bun:test";
2
2
  import React from "react";
3
+ import {Pressable} from "react-native";
4
+ import {Box} from "./Box";
3
5
  import {ConsentNavigator} from "./ConsentNavigator";
4
6
  import {Text} from "./Text";
5
7
  import {renderWithTheme} from "./test-utils";
@@ -71,6 +73,15 @@ const createLoadingMockApi = () => {
71
73
  };
72
74
  };
73
75
 
76
+ const ExtraScreen: React.FC<{onNext?: () => void}> = ({onNext}) => (
77
+ <Box testID="extra-screen">
78
+ <Text>Extra Screen Content</Text>
79
+ <Pressable onPress={onNext} testID="extra-screen-next">
80
+ <Text>Next</Text>
81
+ </Pressable>
82
+ </Box>
83
+ );
84
+
74
85
  describe("ConsentNavigator", () => {
75
86
  it("renders children when there are no pending consent forms", async () => {
76
87
  const api = createMockApi([]);
@@ -108,4 +119,49 @@ describe("ConsentNavigator", () => {
108
119
 
109
120
  expect(getByTestId("consent-navigator-loading")).toBeTruthy();
110
121
  });
122
+
123
+ it("shows extra screens after consent forms are completed", async () => {
124
+ const api = createMockApi([]);
125
+
126
+ const {getByTestId, queryByText} = renderWithTheme(
127
+ <ConsentNavigator api={api} extraScreens={[<ExtraScreen key="extra" />]}>
128
+ <Text>App Content</Text>
129
+ </ConsentNavigator>
130
+ );
131
+
132
+ expect(getByTestId("extra-screen")).toBeTruthy();
133
+ expect(queryByText("App Content")).toBeNull();
134
+ });
135
+
136
+ it("shows children after all extra screens are dismissed", async () => {
137
+ const api = createMockApi([]);
138
+ const {act, fireEvent, waitFor} = await import("@testing-library/react-native");
139
+
140
+ const {getByTestId, getByText} = renderWithTheme(
141
+ <ConsentNavigator api={api} extraScreens={[<ExtraScreen key="extra" />]}>
142
+ <Text>App Content</Text>
143
+ </ConsentNavigator>
144
+ );
145
+
146
+ expect(getByTestId("extra-screen")).toBeTruthy();
147
+ await act(async () => {
148
+ fireEvent.press(getByTestId("extra-screen-next"));
149
+ });
150
+ await waitFor(() => {
151
+ expect(getByText("App Content")).toBeTruthy();
152
+ });
153
+ });
154
+
155
+ it("injects onNext prop into extra screen elements", async () => {
156
+ const api = createMockApi([]);
157
+
158
+ const {getByTestId} = renderWithTheme(
159
+ <ConsentNavigator api={api} extraScreens={[<ExtraScreen key="extra" />]}>
160
+ <Text>App Content</Text>
161
+ </ConsentNavigator>
162
+ );
163
+
164
+ // The extra screen should have a working next button (onNext was injected)
165
+ expect(getByTestId("extra-screen-next")).toBeTruthy();
166
+ });
111
167
  });
@@ -13,19 +13,25 @@ interface ConsentNavigatorProps {
13
13
  api: any;
14
14
  baseUrl?: string;
15
15
  children: React.ReactNode;
16
+ extraScreens?: React.ReactNode[];
16
17
  onError?: (error: any) => void;
18
+ variables?: Record<string, string>;
17
19
  }
18
20
 
19
21
  export const ConsentNavigator: React.FC<ConsentNavigatorProps> = ({
20
22
  api,
21
23
  baseUrl,
22
24
  children,
25
+ extraScreens,
23
26
  onError,
27
+ variables,
24
28
  }) => {
25
29
  const [currentIndex, setCurrentIndex] = useState(0);
30
+ const [extraScreenIndex, setExtraScreenIndex] = useState(0);
26
31
  const {forms, isLoading, error, refetch} = useConsentForms(api, baseUrl);
27
32
  const {submit, isSubmitting} = useSubmitConsent(api, baseUrl);
28
33
  const locale = detectLocale();
34
+ const validExtraScreens = extraScreens ?? [];
29
35
 
30
36
  if (isLoading) {
31
37
  console.debug("[ConsentNavigator] Loading pending consents...");
@@ -68,6 +74,18 @@ export const ConsentNavigator: React.FC<ConsentNavigatorProps> = ({
68
74
  }
69
75
 
70
76
  if (forms.length === 0 || currentIndex >= forms.length) {
77
+ if (extraScreenIndex < validExtraScreens.length) {
78
+ const currentScreen = validExtraScreens[extraScreenIndex];
79
+ console.debug(
80
+ `[ConsentNavigator] Showing extra screen ${extraScreenIndex + 1}/${validExtraScreens.length}`
81
+ );
82
+ if (React.isValidElement(currentScreen)) {
83
+ return React.cloneElement(currentScreen as React.ReactElement<any>, {
84
+ onNext: () => setExtraScreenIndex((i) => i + 1),
85
+ });
86
+ }
87
+ return <>{currentScreen}</>;
88
+ }
71
89
  console.debug("[ConsentNavigator] No pending consents, showing app");
72
90
  return <>{children}</>;
73
91
  }
@@ -123,6 +141,7 @@ export const ConsentNavigator: React.FC<ConsentNavigatorProps> = ({
123
141
  locale={locale}
124
142
  onAgree={handleAgree}
125
143
  onDecline={currentForm.required ? undefined : handleDecline}
144
+ variables={variables}
126
145
  />
127
146
  );
128
147
  };
@@ -66,7 +66,7 @@ const DateTimeSegment: FC<DateTimeSegmentProps> = ({
66
66
  borderColor: error ? theme.border.error : "transparent",
67
67
  flexDirection: "row",
68
68
  flexShrink: 1,
69
- height: 50,
69
+ height: 40,
70
70
  overflow: "hidden",
71
71
  padding: 0,
72
72
  width: config.width,
@@ -999,9 +999,9 @@ export const DateTimeField: FC<DateTimeFieldProps> = ({
999
999
  borderWidth: 1,
1000
1000
  flexDirection: parentIsLessThanBreakpointOrIsMobile ? "column" : "row",
1001
1001
  maxWidth: isMobileDatetime ? 250 : maximumWidth,
1002
+ minHeight: 40,
1002
1003
  minWidth: isMobileDatetime ? 200 : minimumWidth,
1003
1004
  paddingHorizontal: 6,
1004
- paddingVertical: 2,
1005
1005
  }}
1006
1006
  >
1007
1007
  {showDateSection && (