@terreno/ui 0.11.5 → 0.11.6

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/dist/Common.d.ts CHANGED
@@ -2533,5 +2533,18 @@ export interface SidebarNavigationPanelProps {
2533
2533
  * Additional styles applied to each navigation item.
2534
2534
  */
2535
2535
  itemStyle?: StyleProp<ViewStyle>;
2536
+ /**
2537
+ * Controlled open state. When provided, the panel hides its internal hamburger
2538
+ * button and defers open/close to the caller.
2539
+ *
2540
+ * @platform mobile — the web sidebar is always visible; this prop is ignored on web.
2541
+ */
2542
+ isOpen?: boolean;
2543
+ /**
2544
+ * Called when the panel requests an open or close (controlled mode only).
2545
+ *
2546
+ * @platform mobile — the web sidebar is always visible; this prop is ignored on web.
2547
+ */
2548
+ onOpenChange?: (isOpen: boolean) => void;
2536
2549
  }
2537
2550
  export {};
@@ -1,22 +1,14 @@
1
+ import { Screen } from "expo-router/build/views/Screen";
1
2
  import { type FC } from "react";
2
3
  import type { SidebarNavigationPanelProps, SidebarNavigationProps } from "./Common";
3
4
  /**
4
- * Renders the hamburger button, drawer overlay, and children. Works without expo-router Navigator context.
5
- */
6
- export declare const SidebarNavigationPanel: FC<SidebarNavigationPanelProps>;
7
- /**
8
- * Custom expo-router navigator with a hamburger-triggered slide-in drawer.
9
- * Use in _layout.tsx files:
5
+ * Renders the bottom sheet overlay and children. Works without expo-router Navigator context.
10
6
  *
11
- * ```tsx
12
- * export default function SidebarLayout() {
13
- * return (
14
- * <SidebarNavigation
15
- * topItems={[{label: "Home", route: "index", iconName: "house"}]}
16
- * bottomItems={[{label: "Settings", route: "settings", iconName: "gear"}]}
17
- * />
18
- * );
19
- * }
20
- * ```
7
+ * Supports two modes:
8
+ * - Uncontrolled (default): manages open state internally and shows a floating hamburger button.
9
+ * - Controlled: caller provides isOpen + onOpenChange and owns the trigger (e.g. a header button).
21
10
  */
22
- export declare const SidebarNavigation: FC<SidebarNavigationProps>;
11
+ export declare const SidebarNavigationPanel: FC<SidebarNavigationPanelProps>;
12
+ export declare const SidebarNavigation: FC<SidebarNavigationProps> & {
13
+ Screen: typeof Screen;
14
+ };
@@ -1,18 +1,51 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { TabRouter } from "@react-navigation/native";
3
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";
4
+ // Screen is not exported from expo-router's public API (exports.d.ts only exposes ScreenProps).
5
+ // Stack.Screen and Tabs.Screen use this same internal path. If expo-router upgrades break this,
6
+ // update the import path here this is the only place in the codebase that references it.
7
+ // eslint-disable-next-line import/no-internal-modules
8
+ import { Screen } from "expo-router/build/views/Screen";
9
+ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
10
+ import { Animated, Dimensions, PanResponder, Pressable, Text as RNText, View, } from "react-native";
11
+ import { useSafeAreaInsets } from "react-native-safe-area-context";
8
12
  import { Icon } from "./Icon";
9
13
  import { Text } from "./Text";
10
14
  import { useTheme } from "./Theme";
11
- const DRAWER_WIDTH = 280;
12
- const ITEM_HEIGHT = 48;
13
- const ICON_SIZE = 20;
15
+ const ITEM_HEIGHT = 44;
14
16
  const BACKDROP_OPACITY = 0.5;
15
- const ANIMATION_DURATION = 250;
17
+ const SIDEBAR_BADGE_SURFACE = {
18
+ error: "error",
19
+ info: "secondaryDark",
20
+ neutral: "neutralDark",
21
+ success: "success",
22
+ warning: "warning",
23
+ };
24
+ const SidebarBadge = ({ badge, status }) => {
25
+ const { theme } = useTheme();
26
+ const backgroundColor = theme.surface[SIDEBAR_BADGE_SURFACE[status]];
27
+ const isDot = badge === true;
28
+ if (isDot) {
29
+ return _jsx(View, { style: { backgroundColor, borderRadius: 999, height: 18, width: 18 } });
30
+ }
31
+ const value = Number(badge) > 9 ? "9+" : String(badge);
32
+ return (_jsx(View, { style: {
33
+ alignItems: "center",
34
+ backgroundColor,
35
+ borderRadius: 999,
36
+ height: 18,
37
+ justifyContent: "center",
38
+ minWidth: 18,
39
+ paddingHorizontal: 4,
40
+ }, children: _jsx(RNText, { style: {
41
+ color: "#FFFFFF",
42
+ fontSize: 11,
43
+ fontWeight: "700",
44
+ lineHeight: 12,
45
+ }, children: value }) }));
46
+ };
47
+ const ANIMATION_DURATION = 300;
48
+ const DISMISS_THRESHOLD = 0.3;
16
49
  const SidebarItem = ({ item, isActive, onPress, itemStyle }) => {
17
50
  var _a;
18
51
  const { theme } = useTheme();
@@ -22,136 +55,195 @@ const SidebarItem = ({ item, isActive, onPress, itemStyle }) => {
22
55
  return (_jsxs(Pressable, { accessibilityLabel: item.label, accessibilityRole: "button", onPress: handlePress, style: [
23
56
  {
24
57
  alignItems: "center",
25
- backgroundColor: isActive ? theme.surface.secondaryLight : "transparent",
58
+ backgroundColor: isActive ? theme.surface.neutralLight : "transparent",
26
59
  borderRadius: theme.radius.default,
27
60
  flexDirection: "row",
28
- gap: 14,
61
+ gap: 12,
29
62
  height: ITEM_HEIGHT,
30
- marginHorizontal: 12,
31
- paddingHorizontal: 14,
63
+ marginHorizontal: 8,
64
+ paddingHorizontal: 12,
32
65
  },
33
66
  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 })] }));
67
+ ], children: [_jsxs(View, { style: { alignItems: "center", height: 40, justifyContent: "center", width: 40 }, children: [_jsx(Icon, { color: isActive ? "primary" : "secondaryLight", iconName: item.iconName, size: "xl" }), item.badge !== undefined && item.badge !== false && (_jsx(View, { style: { bottom: 0, position: "absolute", right: 0 }, children: _jsx(SidebarBadge, { badge: item.badge, status: (_a = item.badgeStatus) !== null && _a !== void 0 ? _a : "success" }) }))] }), _jsx(Text, { bold: isActive, color: isActive ? "primary" : "secondaryLight", size: "lg", children: item.label })] }));
40
68
  };
69
+ const SidebarHamburger = ({ onOpen }) => (_jsx(Pressable, { accessibilityLabel: "Open navigation menu", accessibilityRole: "button", onPress: onOpen, style: { alignItems: "center", height: 40, justifyContent: "center", width: 40 }, children: _jsx(Icon, { color: "primary", iconName: "bars", size: "md" }) }));
41
70
  /**
42
- * Renders the hamburger button, drawer overlay, and children. Works without expo-router Navigator context.
71
+ * Renders the bottom sheet overlay and children. Works without expo-router Navigator context.
72
+ *
73
+ * Supports two modes:
74
+ * - Uncontrolled (default): manages open state internally and shows a floating hamburger button.
75
+ * - Controlled: caller provides isOpen + onOpenChange and owns the trigger (e.g. a header button).
43
76
  */
44
- export const SidebarNavigationPanel = ({ topItems, bottomItems, activeRoute, onNavigate, children, panelStyle, itemStyle, }) => {
77
+ export const SidebarNavigationPanel = ({ topItems, bottomItems, activeRoute, onNavigate, children, panelStyle, itemStyle, isOpen: isOpenProp, onOpenChange, }) => {
45
78
  const { theme } = useTheme();
46
- const [isOpen, setIsOpen] = useState(false);
47
- const slideAnim = useRef(new Animated.Value(-DRAWER_WIDTH)).current;
79
+ const insets = useSafeAreaInsets();
80
+ const isControlled = isOpenProp !== undefined;
81
+ const [isOpenInternal, setIsOpenInternal] = useState(false);
82
+ const isOpen = isControlled ? isOpenProp : isOpenInternal;
83
+ const sheetHeight = useMemo(() => Dimensions.get("window").height * 0.65, []);
84
+ const slideAnim = useRef(new Animated.Value(sheetHeight)).current;
48
85
  const backdropAnim = useRef(new Animated.Value(0)).current;
49
- // Animate drawer open/close
86
+ const capturedSlideValue = useRef(0);
87
+ // Play open animation whenever isOpen becomes true
50
88
  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();
89
+ if (!isOpen) {
90
+ return;
91
+ }
92
+ slideAnim.setValue(sheetHeight);
93
+ backdropAnim.setValue(0);
94
+ Animated.parallel([
95
+ Animated.timing(slideAnim, {
96
+ duration: ANIMATION_DURATION,
97
+ toValue: 0,
98
+ useNativeDriver: true,
99
+ }),
100
+ Animated.timing(backdropAnim, {
101
+ duration: ANIMATION_DURATION,
102
+ toValue: BACKDROP_OPACITY,
103
+ useNativeDriver: true,
104
+ }),
105
+ ]).start();
106
+ }, [isOpen, slideAnim, backdropAnim, sheetHeight]);
107
+ // Play close animation then update state
108
+ const handleClose = useCallback(() => {
109
+ Animated.parallel([
110
+ Animated.timing(slideAnim, {
111
+ duration: ANIMATION_DURATION,
112
+ toValue: sheetHeight,
113
+ useNativeDriver: true,
114
+ }),
115
+ Animated.timing(backdropAnim, {
116
+ duration: ANIMATION_DURATION,
117
+ toValue: 0,
118
+ useNativeDriver: true,
119
+ }),
120
+ ]).start(() => {
121
+ if (isControlled) {
122
+ onOpenChange === null || onOpenChange === void 0 ? void 0 : onOpenChange(false);
123
+ }
124
+ else {
125
+ setIsOpenInternal(false);
126
+ }
127
+ });
128
+ }, [isControlled, onOpenChange, slideAnim, backdropAnim, sheetHeight]);
129
+ const handleOpen = useCallback(() => {
130
+ if (isControlled) {
131
+ onOpenChange === null || onOpenChange === void 0 ? void 0 : onOpenChange(true);
64
132
  }
65
133
  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();
134
+ setIsOpenInternal(true);
78
135
  }
79
- }, [isOpen, slideAnim, backdropAnim]);
80
- const handleOpen = useCallback(() => setIsOpen(true), []);
81
- const handleClose = useCallback(() => setIsOpen(false), []);
136
+ }, [isControlled, onOpenChange]);
82
137
  const handleNavigate = useCallback((route) => {
83
- setIsOpen(false);
138
+ handleClose();
84
139
  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: {
140
+ }, [handleClose, onNavigate]);
141
+ const panResponder = useMemo(() => PanResponder.create({
142
+ onMoveShouldSetPanResponder: (_, { dx, dy }) => Math.abs(dy) > Math.abs(dx) && dy > 4,
143
+ onPanResponderGrant: () => {
144
+ slideAnim.stopAnimation((value) => {
145
+ capturedSlideValue.current = value;
146
+ });
147
+ backdropAnim.stopAnimation();
148
+ },
149
+ onPanResponderMove: (_, { dy }) => {
150
+ const next = capturedSlideValue.current + dy;
151
+ if (next < 0) {
152
+ return;
153
+ }
154
+ slideAnim.setValue(next);
155
+ backdropAnim.setValue(BACKDROP_OPACITY * Math.max(0, 1 - next / sheetHeight));
156
+ },
157
+ onPanResponderRelease: (_, { dy, vy }) => {
158
+ if (dy > sheetHeight * DISMISS_THRESHOLD || vy > 0.5) {
159
+ handleClose();
160
+ }
161
+ else {
162
+ Animated.parallel([
163
+ Animated.timing(slideAnim, {
164
+ duration: 200,
165
+ toValue: 0,
166
+ useNativeDriver: true,
167
+ }),
168
+ Animated.timing(backdropAnim, {
169
+ duration: 200,
170
+ toValue: BACKDROP_OPACITY,
171
+ useNativeDriver: true,
172
+ }),
173
+ ]).start();
174
+ }
175
+ },
176
+ }), [slideAnim, backdropAnim, sheetHeight, handleClose]);
177
+ return (_jsxs(View, { style: { flex: 1 }, children: [children, !isControlled && (_jsx(Pressable, { accessibilityLabel: "Open navigation menu", accessibilityRole: "button", onPress: handleOpen, style: {
88
178
  alignItems: "center",
89
- backgroundColor: theme.surface.primary,
90
- borderRadius: theme.radius.full,
91
- elevation: 4,
92
179
  height: 44,
93
180
  justifyContent: "center",
94
181
  left: 16,
95
182
  position: "absolute",
96
- shadowColor: "#000",
97
- shadowOffset: { height: 2, width: 0 },
98
- shadowOpacity: 0.25,
99
- shadowRadius: 4,
100
- top: 16,
183
+ top: insets.top + 16,
101
184
  width: 44,
102
185
  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",
186
+ }, children: _jsx(Icon, { color: "primary", iconName: "bars", size: "md" }) })), isOpen && (_jsxs(_Fragment, { children: [_jsx(Pressable, { accessibilityElementsHidden: true, onPress: handleClose, style: { bottom: 0, left: 0, position: "absolute", right: 0, top: 0, zIndex: 100 }, children: _jsx(Animated.View, { style: { backgroundColor: "#000", flex: 1, opacity: backdropAnim } }) }), _jsxs(Animated.View, { style: [
187
+ {
188
+ backgroundColor: theme.surface.base,
189
+ borderTopLeftRadius: 16,
190
+ borderTopRightRadius: 16,
191
+ bottom: 0,
192
+ height: sheetHeight,
193
+ left: 0,
194
+ position: "absolute",
195
+ right: 0,
196
+ transform: [{ translateY: slideAnim }],
197
+ zIndex: 200,
198
+ },
199
+ panelStyle,
200
+ ], children: [_jsx(View, Object.assign({}, panResponder.panHandlers, { accessibilityHint: "Drag down to close", accessibilityLabel: "Navigation menu drag handle", accessibilityRole: "adjustable", style: { alignItems: "center", paddingBottom: 8, paddingTop: 12 }, children: _jsx(View, { style: {
201
+ backgroundColor: theme.border.default,
202
+ borderRadius: 2,
203
+ height: 4,
204
+ width: 36,
205
+ } }) })), _jsx(View, { style: { gap: 4, paddingBottom: insets.bottom + 8 }, children: [...topItems, ...bottomItems].map((item) => (_jsx(SidebarItem, { isActive: activeRoute === item.route, item: item, itemStyle: itemStyle, onPress: handleNavigate }, item.label))) })] })] }))] }));
206
+ };
207
+ const SidebarHeader = ({ onOpen }) => {
208
+ var _a, _b;
209
+ const { theme } = useTheme();
210
+ const insets = useSafeAreaInsets();
211
+ const { state, descriptors } = Navigator.useContext();
212
+ const activeRoute = state.routes[state.index];
213
+ const { headerLeft, headerRight, title } = ((_b = (_a = descriptors[activeRoute === null || activeRoute === void 0 ? void 0 : activeRoute.key]) === null || _a === void 0 ? void 0 : _a.options) !== null && _b !== void 0 ? _b : {});
214
+ return (_jsx(View, { style: {
215
+ backgroundColor: theme.surface.base,
216
+ borderBottomColor: theme.border.default,
217
+ borderBottomWidth: 1,
218
+ paddingTop: insets.top,
219
+ }, children: _jsxs(View, { style: {
220
+ alignItems: "center",
221
+ flexDirection: "row",
222
+ height: 44,
223
+ justifyContent: "space-between",
224
+ paddingHorizontal: 16,
225
+ }, children: [_jsxs(View, { style: { alignItems: "center", flexDirection: "row", gap: 12 }, children: [_jsx(SidebarHamburger, { onOpen: onOpen }), headerLeft === null || headerLeft === void 0 ? void 0 : headerLeft({})] }), Boolean(title) && (_jsx(View, { pointerEvents: "none", style: {
226
+ alignItems: "center",
227
+ bottom: 0,
228
+ justifyContent: "center",
121
229
  left: 0,
122
- paddingBottom: 32,
123
- paddingTop: 20,
124
230
  position: "absolute",
231
+ right: 0,
125
232
  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))) })] })] }));
233
+ }, children: _jsx(Text, { bold: true, size: "lg", children: title }) })), Boolean(headerRight) && _jsx(View, { style: { alignItems: "flex-end" }, children: headerRight === null || headerRight === void 0 ? void 0 : headerRight({}) })] }) }));
139
234
  };
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;
235
+ /** Renders the content panel and bottom sheet for the active screen. */
236
+ const SidebarNavigatorContent = ({ topItems, bottomItems, isOpen, onOpenChange, onNavigate, panelStyle, itemStyle }) => {
145
237
  const { state, navigation } = Navigator.useContext();
146
- const activeRoute = (_a = state.routes[state.index]) === null || _a === void 0 ? void 0 : _a.name;
238
+ const activeRoute = state.routes[state.index];
147
239
  const handleNavigate = useCallback((route) => {
148
240
  navigation.navigate(route);
149
241
  onNavigate === null || onNavigate === void 0 ? void 0 : onNavigate(route);
150
242
  }, [navigation, onNavigate]);
151
- return (_jsx(SidebarNavigationPanel, { activeRoute: activeRoute, bottomItems: bottomItems, itemStyle: itemStyle, onNavigate: handleNavigate, panelStyle: panelStyle, topItems: topItems, children: _jsx(Slot, {}) }));
243
+ return (_jsx(SidebarNavigationPanel, { activeRoute: activeRoute === null || activeRoute === void 0 ? void 0 : activeRoute.name, bottomItems: bottomItems, isOpen: isOpen, itemStyle: itemStyle, onNavigate: handleNavigate, onOpenChange: onOpenChange, panelStyle: panelStyle, topItems: topItems, children: _jsx(Slot, {}) }));
152
244
  };
153
245
  /**
154
- * Custom expo-router navigator with a hamburger-triggered slide-in drawer.
246
+ * Custom expo-router navigator with a header bar and hamburger-triggered bottom sheet.
155
247
  * Use in _layout.tsx files:
156
248
  *
157
249
  * ```tsx
@@ -165,7 +257,9 @@ const SidebarNavigatorContent = ({ topItems, bottomItems, onNavigate, panelStyle
165
257
  * }
166
258
  * ```
167
259
  */
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 }) }));
260
+ const SidebarNavigationBase = ({ topItems, bottomItems, onNavigate, initialRouteName, screenOptions, panelStyle, itemStyle, children, }) => {
261
+ const [isSheetOpen, setIsSheetOpen] = useState(false);
262
+ return (_jsxs(Navigator, { initialRouteName: initialRouteName, router: TabRouter, screenOptions: screenOptions, children: [_jsxs(View, { style: { flex: 1 }, children: [_jsx(SidebarHeader, { onOpen: () => setIsSheetOpen(true) }), _jsx(SidebarNavigatorContent, { bottomItems: bottomItems, isOpen: isSheetOpen, itemStyle: itemStyle, onNavigate: onNavigate, onOpenChange: setIsSheetOpen, panelStyle: panelStyle, topItems: topItems })] }), children] }));
170
263
  };
264
+ export const SidebarNavigation = Object.assign(SidebarNavigationBase, { Screen });
171
265
  //# sourceMappingURL=SidebarNavigation.native.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"SidebarNavigation.native.js","sourceRoot":"","sources":["../src/SidebarNavigation.native.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"SidebarNavigation.native.js","sourceRoot":"","sources":["../src/SidebarNavigation.native.tsx"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -137,5 +137,5 @@
137
137
  "test:coverage": "TZ=America/New_York bun run ../scripts/check-coverage.ts",
138
138
  "types": "bun typedoc"
139
139
  },
140
- "version": "0.11.5"
140
+ "version": "0.11.6"
141
141
  }
package/src/Common.ts CHANGED
@@ -3044,4 +3044,17 @@ export interface SidebarNavigationPanelProps {
3044
3044
  * Additional styles applied to each navigation item.
3045
3045
  */
3046
3046
  itemStyle?: StyleProp<ViewStyle>;
3047
+ /**
3048
+ * Controlled open state. When provided, the panel hides its internal hamburger
3049
+ * button and defers open/close to the caller.
3050
+ *
3051
+ * @platform mobile — the web sidebar is always visible; this prop is ignored on web.
3052
+ */
3053
+ isOpen?: boolean;
3054
+ /**
3055
+ * Called when the panel requests an open or close (controlled mode only).
3056
+ *
3057
+ * @platform mobile — the web sidebar is always visible; this prop is ignored on web.
3058
+ */
3059
+ onOpenChange?: (isOpen: boolean) => void;
3047
3060
  }
@@ -1,24 +1,84 @@
1
1
  import {TabRouter} from "@react-navigation/native";
2
2
  import {Navigator, Slot} from "expo-router";
3
- import {type FC, useCallback, useEffect, useRef, useState} from "react";
4
- import {Animated, Dimensions, Pressable, type StyleProp, View, type ViewStyle} from "react-native";
3
+ // Screen is not exported from expo-router's public API (exports.d.ts only exposes ScreenProps).
4
+ // Stack.Screen and Tabs.Screen use this same internal path. If expo-router upgrades break this,
5
+ // update the import path here — this is the only place in the codebase that references it.
6
+ // eslint-disable-next-line import/no-internal-modules
7
+ import {Screen} from "expo-router/build/views/Screen";
8
+ import {type FC, useCallback, useEffect, useMemo, useRef, useState} from "react";
9
+ import {
10
+ Animated,
11
+ Dimensions,
12
+ PanResponder,
13
+ Pressable,
14
+ Text as RNText,
15
+ type StyleProp,
16
+ View,
17
+ type ViewStyle,
18
+ } from "react-native";
19
+ import {useSafeAreaInsets} from "react-native-safe-area-context";
5
20
 
6
- import {Badge} from "./Badge";
7
21
  import type {
22
+ SidebarBadgeStatus,
8
23
  SidebarNavigationItem,
9
24
  SidebarNavigationPanelProps,
10
25
  SidebarNavigationProps,
26
+ SurfaceTheme,
11
27
  } from "./Common";
12
- import {SIDEBAR_BADGE_STATUS_MAP} from "./Common";
13
28
  import {Icon} from "./Icon";
14
29
  import {Text} from "./Text";
15
30
  import {useTheme} from "./Theme";
16
31
 
17
- const DRAWER_WIDTH = 280;
18
- const ITEM_HEIGHT = 48;
19
- const ICON_SIZE = 20;
32
+ const ITEM_HEIGHT = 44;
20
33
  const BACKDROP_OPACITY = 0.5;
21
- const ANIMATION_DURATION = 250;
34
+
35
+ const SIDEBAR_BADGE_SURFACE: Record<SidebarBadgeStatus, keyof SurfaceTheme> = {
36
+ error: "error",
37
+ info: "secondaryDark",
38
+ neutral: "neutralDark",
39
+ success: "success",
40
+ warning: "warning",
41
+ };
42
+
43
+ const SidebarBadge: FC<{badge: number | true; status: SidebarBadgeStatus}> = ({badge, status}) => {
44
+ const {theme} = useTheme();
45
+ const backgroundColor = theme.surface[SIDEBAR_BADGE_SURFACE[status]];
46
+ const isDot = badge === true;
47
+
48
+ if (isDot) {
49
+ return <View style={{backgroundColor, borderRadius: 999, height: 18, width: 18}} />;
50
+ }
51
+
52
+ const value = Number(badge) > 9 ? "9+" : String(badge);
53
+
54
+ return (
55
+ <View
56
+ style={{
57
+ alignItems: "center",
58
+ backgroundColor,
59
+ borderRadius: 999,
60
+ height: 18,
61
+ justifyContent: "center",
62
+ minWidth: 18,
63
+ paddingHorizontal: 4,
64
+ }}
65
+ >
66
+ <RNText
67
+ style={{
68
+ color: "#FFFFFF",
69
+ fontSize: 11,
70
+ fontWeight: "700",
71
+ lineHeight: 12,
72
+ }}
73
+ >
74
+ {value}
75
+ </RNText>
76
+ </View>
77
+ );
78
+ };
79
+
80
+ const ANIMATION_DURATION = 300;
81
+ const DISMISS_THRESHOLD = 0.3;
22
82
 
23
83
  const SidebarItem: FC<{
24
84
  item: SidebarNavigationItem;
@@ -40,46 +100,49 @@ const SidebarItem: FC<{
40
100
  style={[
41
101
  {
42
102
  alignItems: "center",
43
- backgroundColor: isActive ? theme.surface.secondaryLight : "transparent",
103
+ backgroundColor: isActive ? theme.surface.neutralLight : "transparent",
44
104
  borderRadius: theme.radius.default,
45
105
  flexDirection: "row",
46
- gap: 14,
106
+ gap: 12,
47
107
  height: ITEM_HEIGHT,
48
- marginHorizontal: 12,
49
- paddingHorizontal: 14,
108
+ marginHorizontal: 8,
109
+ paddingHorizontal: 12,
50
110
  },
51
111
  itemStyle,
52
112
  ]}
53
113
  >
54
- <View style={{alignItems: "center", justifyContent: "center", width: ICON_SIZE}}>
55
- <Icon color={isActive ? "primary" : "secondaryDark"} iconName={item.iconName} size="md" />
56
- {Boolean(item.badge) && (
57
- <View
58
- style={{
59
- bottom: item.badge === true ? -4 : undefined,
60
- position: "absolute",
61
- right: -6,
62
- top: item.badge === true ? undefined : -4,
63
- }}
64
- >
65
- <Badge
66
- maxValue={99}
67
- status={SIDEBAR_BADGE_STATUS_MAP[item.badgeStatus ?? "error"]}
68
- value={item.badge === true ? undefined : String(item.badge)}
69
- variant={item.badge === true ? "iconOnly" : "numberOnly"}
70
- />
114
+ <View style={{alignItems: "center", height: 40, justifyContent: "center", width: 40}}>
115
+ <Icon color={isActive ? "primary" : "secondaryLight"} iconName={item.iconName} size="xl" />
116
+ {item.badge !== undefined && item.badge !== false && (
117
+ <View style={{bottom: 0, position: "absolute", right: 0}}>
118
+ <SidebarBadge badge={item.badge} status={item.badgeStatus ?? "success"} />
71
119
  </View>
72
120
  )}
73
121
  </View>
74
- <Text bold={isActive} color={isActive ? "primary" : "secondaryDark"} size="md">
122
+ <Text bold={isActive} color={isActive ? "primary" : "secondaryLight"} size="lg">
75
123
  {item.label}
76
124
  </Text>
77
125
  </Pressable>
78
126
  );
79
127
  };
80
128
 
129
+ const SidebarHamburger: FC<{onOpen: () => void}> = ({onOpen}) => (
130
+ <Pressable
131
+ accessibilityLabel="Open navigation menu"
132
+ accessibilityRole="button"
133
+ onPress={onOpen}
134
+ style={{alignItems: "center", height: 40, justifyContent: "center", width: 40}}
135
+ >
136
+ <Icon color="primary" iconName="bars" size="md" />
137
+ </Pressable>
138
+ );
139
+
81
140
  /**
82
- * Renders the hamburger button, drawer overlay, and children. Works without expo-router Navigator context.
141
+ * Renders the bottom sheet overlay and children. Works without expo-router Navigator context.
142
+ *
143
+ * Supports two modes:
144
+ * - Uncontrolled (default): manages open state internally and shows a floating hamburger button.
145
+ * - Controlled: caller provides isOpen + onOpenChange and owns the trigger (e.g. a header button).
83
146
  */
84
147
  export const SidebarNavigationPanel: FC<SidebarNavigationPanelProps> = ({
85
148
  topItems,
@@ -89,187 +152,275 @@ export const SidebarNavigationPanel: FC<SidebarNavigationPanelProps> = ({
89
152
  children,
90
153
  panelStyle,
91
154
  itemStyle,
155
+ isOpen: isOpenProp,
156
+ onOpenChange,
92
157
  }) => {
93
158
  const {theme} = useTheme();
94
- const [isOpen, setIsOpen] = useState(false);
95
- const slideAnim = useRef(new Animated.Value(-DRAWER_WIDTH)).current;
159
+ const insets = useSafeAreaInsets();
160
+ const isControlled = isOpenProp !== undefined;
161
+ const [isOpenInternal, setIsOpenInternal] = useState(false);
162
+ const isOpen = isControlled ? isOpenProp : isOpenInternal;
163
+
164
+ const sheetHeight = useMemo(() => Dimensions.get("window").height * 0.65, []);
165
+ const slideAnim = useRef(new Animated.Value(sheetHeight)).current;
96
166
  const backdropAnim = useRef(new Animated.Value(0)).current;
167
+ const capturedSlideValue = useRef(0);
97
168
 
98
- // Animate drawer open/close
169
+ // Play open animation whenever isOpen becomes true
99
170
  useEffect(() => {
100
- if (isOpen) {
101
- Animated.parallel([
102
- Animated.timing(slideAnim, {
103
- duration: ANIMATION_DURATION,
104
- toValue: 0,
105
- useNativeDriver: true,
106
- }),
107
- Animated.timing(backdropAnim, {
108
- duration: ANIMATION_DURATION,
109
- toValue: BACKDROP_OPACITY,
110
- useNativeDriver: true,
111
- }),
112
- ]).start();
113
- } else {
114
- Animated.parallel([
115
- Animated.timing(slideAnim, {
116
- duration: ANIMATION_DURATION,
117
- toValue: -DRAWER_WIDTH,
118
- useNativeDriver: true,
119
- }),
120
- Animated.timing(backdropAnim, {
121
- duration: ANIMATION_DURATION,
122
- toValue: 0,
123
- useNativeDriver: true,
124
- }),
125
- ]).start();
171
+ if (!isOpen) {
172
+ return;
126
173
  }
127
- }, [isOpen, slideAnim, backdropAnim]);
174
+ slideAnim.setValue(sheetHeight);
175
+ backdropAnim.setValue(0);
176
+ Animated.parallel([
177
+ Animated.timing(slideAnim, {
178
+ duration: ANIMATION_DURATION,
179
+ toValue: 0,
180
+ useNativeDriver: true,
181
+ }),
182
+ Animated.timing(backdropAnim, {
183
+ duration: ANIMATION_DURATION,
184
+ toValue: BACKDROP_OPACITY,
185
+ useNativeDriver: true,
186
+ }),
187
+ ]).start();
188
+ }, [isOpen, slideAnim, backdropAnim, sheetHeight]);
128
189
 
129
- const handleOpen = useCallback(() => setIsOpen(true), []);
130
- const handleClose = useCallback(() => setIsOpen(false), []);
190
+ // Play close animation then update state
191
+ const handleClose = useCallback(() => {
192
+ Animated.parallel([
193
+ Animated.timing(slideAnim, {
194
+ duration: ANIMATION_DURATION,
195
+ toValue: sheetHeight,
196
+ useNativeDriver: true,
197
+ }),
198
+ Animated.timing(backdropAnim, {
199
+ duration: ANIMATION_DURATION,
200
+ toValue: 0,
201
+ useNativeDriver: true,
202
+ }),
203
+ ]).start(() => {
204
+ if (isControlled) {
205
+ onOpenChange?.(false);
206
+ } else {
207
+ setIsOpenInternal(false);
208
+ }
209
+ });
210
+ }, [isControlled, onOpenChange, slideAnim, backdropAnim, sheetHeight]);
211
+
212
+ const handleOpen = useCallback(() => {
213
+ if (isControlled) {
214
+ onOpenChange?.(true);
215
+ } else {
216
+ setIsOpenInternal(true);
217
+ }
218
+ }, [isControlled, onOpenChange]);
131
219
 
132
220
  const handleNavigate = useCallback(
133
221
  (route: string) => {
134
- setIsOpen(false);
222
+ handleClose();
135
223
  onNavigate(route);
136
224
  },
137
- [onNavigate]
225
+ [handleClose, onNavigate]
138
226
  );
139
227
 
140
- const screenHeight = Dimensions.get("window").height;
228
+ const panResponder = useMemo(
229
+ () =>
230
+ PanResponder.create({
231
+ onMoveShouldSetPanResponder: (_, {dx, dy}) => Math.abs(dy) > Math.abs(dx) && dy > 4,
232
+ onPanResponderGrant: () => {
233
+ slideAnim.stopAnimation((value) => {
234
+ capturedSlideValue.current = value;
235
+ });
236
+ backdropAnim.stopAnimation();
237
+ },
238
+ onPanResponderMove: (_, {dy}) => {
239
+ const next = capturedSlideValue.current + dy;
240
+ if (next < 0) {
241
+ return;
242
+ }
243
+ slideAnim.setValue(next);
244
+ backdropAnim.setValue(BACKDROP_OPACITY * Math.max(0, 1 - next / sheetHeight));
245
+ },
246
+ onPanResponderRelease: (_, {dy, vy}) => {
247
+ if (dy > sheetHeight * DISMISS_THRESHOLD || vy > 0.5) {
248
+ handleClose();
249
+ } else {
250
+ Animated.parallel([
251
+ Animated.timing(slideAnim, {
252
+ duration: 200,
253
+ toValue: 0,
254
+ useNativeDriver: true,
255
+ }),
256
+ Animated.timing(backdropAnim, {
257
+ duration: 200,
258
+ toValue: BACKDROP_OPACITY,
259
+ useNativeDriver: true,
260
+ }),
261
+ ]).start();
262
+ }
263
+ },
264
+ }),
265
+ [slideAnim, backdropAnim, sheetHeight, handleClose]
266
+ );
141
267
 
142
268
  return (
143
269
  <View style={{flex: 1}}>
144
270
  {children}
145
271
 
146
- {/* Hamburger button */}
147
- <Pressable
148
- accessibilityLabel="Open navigation menu"
149
- accessibilityRole="button"
150
- onPress={handleOpen}
151
- style={{
152
- alignItems: "center",
153
- backgroundColor: theme.surface.primary,
154
- borderRadius: theme.radius.full,
155
- elevation: 4,
156
- height: 44,
157
- justifyContent: "center",
158
- left: 16,
159
- position: "absolute",
160
- shadowColor: "#000",
161
- shadowOffset: {height: 2, width: 0},
162
- shadowOpacity: 0.25,
163
- shadowRadius: 4,
164
- top: 16,
165
- width: 44,
166
- zIndex: 10,
167
- }}
168
- >
169
- <Icon color="inverted" iconName="bars" size="md" />
170
- </Pressable>
171
-
172
- {/* Backdrop */}
173
- {isOpen && (
272
+ {/* Floating hamburger — only shown in uncontrolled (standalone) mode */}
273
+ {!isControlled && (
174
274
  <Pressable
175
- onPress={handleClose}
275
+ accessibilityLabel="Open navigation menu"
276
+ accessibilityRole="button"
277
+ onPress={handleOpen}
176
278
  style={{
177
- bottom: 0,
178
- left: 0,
279
+ alignItems: "center",
280
+ height: 44,
281
+ justifyContent: "center",
282
+ left: 16,
179
283
  position: "absolute",
180
- right: 0,
181
- top: 0,
182
- zIndex: 100,
284
+ top: insets.top + 16,
285
+ width: 44,
286
+ zIndex: 10,
183
287
  }}
184
288
  >
185
- <Animated.View
186
- style={{
187
- backgroundColor: "#000",
188
- flex: 1,
189
- opacity: backdropAnim,
190
- }}
191
- />
289
+ <Icon color="primary" iconName="bars" size="md" />
192
290
  </Pressable>
193
291
  )}
194
292
 
195
- {/* Drawer */}
196
- <Animated.View
197
- style={[
198
- {
199
- backgroundColor: theme.surface.base,
200
- borderColor: theme.border.default,
201
- borderRightWidth: 1,
202
- height: screenHeight,
203
- justifyContent: "space-between",
204
- left: 0,
205
- paddingBottom: 32,
206
- paddingTop: 20,
207
- position: "absolute",
208
- top: 0,
209
- transform: [{translateX: slideAnim}],
210
- width: DRAWER_WIDTH,
211
- zIndex: 200,
212
- },
213
- panelStyle,
214
- ]}
215
- >
216
- {/* Close button */}
217
- <View>
293
+ {isOpen && (
294
+ <>
295
+ {/* Backdrop */}
218
296
  <Pressable
219
- accessibilityLabel="Close navigation menu"
220
- accessibilityRole="button"
297
+ accessibilityElementsHidden
221
298
  onPress={handleClose}
299
+ style={{bottom: 0, left: 0, position: "absolute", right: 0, top: 0, zIndex: 100}}
300
+ >
301
+ <Animated.View style={{backgroundColor: "#000", flex: 1, opacity: backdropAnim}} />
302
+ </Pressable>
303
+
304
+ {/* Bottom sheet */}
305
+ <Animated.View
306
+ style={[
307
+ {
308
+ backgroundColor: theme.surface.base,
309
+ borderTopLeftRadius: 16,
310
+ borderTopRightRadius: 16,
311
+ bottom: 0,
312
+ height: sheetHeight,
313
+ left: 0,
314
+ position: "absolute",
315
+ right: 0,
316
+ transform: [{translateY: slideAnim}],
317
+ zIndex: 200,
318
+ },
319
+ panelStyle,
320
+ ]}
321
+ >
322
+ {/* Drag bar */}
323
+ <View
324
+ {...panResponder.panHandlers}
325
+ accessibilityHint="Drag down to close"
326
+ accessibilityLabel="Navigation menu drag handle"
327
+ accessibilityRole="adjustable"
328
+ style={{alignItems: "center", paddingBottom: 8, paddingTop: 12}}
329
+ >
330
+ <View
331
+ style={{
332
+ backgroundColor: theme.border.default,
333
+ borderRadius: 2,
334
+ height: 4,
335
+ width: 36,
336
+ }}
337
+ />
338
+ </View>
339
+
340
+ {/* Nav items */}
341
+ <View style={{gap: 4, paddingBottom: insets.bottom + 8}}>
342
+ {[...topItems, ...bottomItems].map((item) => (
343
+ <SidebarItem
344
+ isActive={activeRoute === item.route}
345
+ item={item}
346
+ itemStyle={itemStyle}
347
+ key={item.label}
348
+ onPress={handleNavigate}
349
+ />
350
+ ))}
351
+ </View>
352
+ </Animated.View>
353
+ </>
354
+ )}
355
+ </View>
356
+ );
357
+ };
358
+
359
+ const SidebarHeader: FC<{onOpen: () => void}> = ({onOpen}) => {
360
+ const {theme} = useTheme();
361
+ const insets = useSafeAreaInsets();
362
+ const {state, descriptors} = Navigator.useContext();
363
+ const activeRoute = state.routes[state.index];
364
+ const {headerLeft, headerRight, title} = (descriptors[activeRoute?.key]?.options ?? {}) as any;
365
+
366
+ return (
367
+ <View
368
+ style={{
369
+ backgroundColor: theme.surface.base,
370
+ borderBottomColor: theme.border.default,
371
+ borderBottomWidth: 1,
372
+ paddingTop: insets.top,
373
+ }}
374
+ >
375
+ <View
376
+ style={{
377
+ alignItems: "center",
378
+ flexDirection: "row",
379
+ height: 44,
380
+ justifyContent: "space-between",
381
+ paddingHorizontal: 16,
382
+ }}
383
+ >
384
+ <View style={{alignItems: "center", flexDirection: "row", gap: 12}}>
385
+ <SidebarHamburger onOpen={onOpen} />
386
+ {headerLeft?.({})}
387
+ </View>
388
+ {Boolean(title) && (
389
+ <View
390
+ pointerEvents="none"
222
391
  style={{
223
392
  alignItems: "center",
224
- alignSelf: "flex-end",
225
- height: 40,
393
+ bottom: 0,
226
394
  justifyContent: "center",
227
- marginRight: 12,
228
- width: 40,
395
+ left: 0,
396
+ position: "absolute",
397
+ right: 0,
398
+ top: 0,
229
399
  }}
230
400
  >
231
- <Icon color="secondaryDark" iconName="xmark" size="md" />
232
- </Pressable>
233
- <View style={{gap: 4, marginTop: 8}}>
234
- {topItems.map((item) => (
235
- <SidebarItem
236
- isActive={activeRoute === item.route}
237
- item={item}
238
- itemStyle={itemStyle}
239
- key={item.route}
240
- onPress={handleNavigate}
241
- />
242
- ))}
401
+ <Text bold size="lg">
402
+ {title}
403
+ </Text>
243
404
  </View>
244
- </View>
245
-
246
- <View style={{gap: 4}}>
247
- {bottomItems.map((item) => (
248
- <SidebarItem
249
- isActive={activeRoute === item.route}
250
- item={item}
251
- key={item.route}
252
- onPress={handleNavigate}
253
- />
254
- ))}
255
- </View>
256
- </Animated.View>
405
+ )}
406
+ {Boolean(headerRight) && <View style={{alignItems: "flex-end"}}>{headerRight?.({})}</View>}
407
+ </View>
257
408
  </View>
258
409
  );
259
410
  };
260
411
 
261
- /**
262
- * Reads active route from Navigator context and renders the drawer + Slot.
263
- */
412
+ /** Renders the content panel and bottom sheet for the active screen. */
264
413
  const SidebarNavigatorContent: FC<{
265
414
  topItems: SidebarNavigationItem[];
266
415
  bottomItems: SidebarNavigationItem[];
416
+ isOpen: boolean;
417
+ onOpenChange: (isOpen: boolean) => void;
267
418
  onNavigate?: (route: string) => void;
268
419
  panelStyle?: StyleProp<ViewStyle>;
269
420
  itemStyle?: StyleProp<ViewStyle>;
270
- }> = ({topItems, bottomItems, onNavigate, panelStyle, itemStyle}) => {
421
+ }> = ({topItems, bottomItems, isOpen, onOpenChange, onNavigate, panelStyle, itemStyle}) => {
271
422
  const {state, navigation} = Navigator.useContext();
272
- const activeRoute = state.routes[state.index]?.name;
423
+ const activeRoute = state.routes[state.index];
273
424
 
274
425
  const handleNavigate = useCallback(
275
426
  (route: string) => {
@@ -281,10 +432,12 @@ const SidebarNavigatorContent: FC<{
281
432
 
282
433
  return (
283
434
  <SidebarNavigationPanel
284
- activeRoute={activeRoute}
435
+ activeRoute={activeRoute?.name}
285
436
  bottomItems={bottomItems}
437
+ isOpen={isOpen}
286
438
  itemStyle={itemStyle}
287
439
  onNavigate={handleNavigate}
440
+ onOpenChange={onOpenChange}
288
441
  panelStyle={panelStyle}
289
442
  topItems={topItems}
290
443
  >
@@ -294,7 +447,7 @@ const SidebarNavigatorContent: FC<{
294
447
  };
295
448
 
296
449
  /**
297
- * Custom expo-router navigator with a hamburger-triggered slide-in drawer.
450
+ * Custom expo-router navigator with a header bar and hamburger-triggered bottom sheet.
298
451
  * Use in _layout.tsx files:
299
452
  *
300
453
  * ```tsx
@@ -308,7 +461,7 @@ const SidebarNavigatorContent: FC<{
308
461
  * }
309
462
  * ```
310
463
  */
311
- export const SidebarNavigation: FC<SidebarNavigationProps> = ({
464
+ const SidebarNavigationBase: FC<SidebarNavigationProps> = ({
312
465
  topItems,
313
466
  bottomItems,
314
467
  onNavigate,
@@ -316,16 +469,27 @@ export const SidebarNavigation: FC<SidebarNavigationProps> = ({
316
469
  screenOptions,
317
470
  panelStyle,
318
471
  itemStyle,
472
+ children,
319
473
  }) => {
474
+ const [isSheetOpen, setIsSheetOpen] = useState(false);
475
+
320
476
  return (
321
477
  <Navigator initialRouteName={initialRouteName} router={TabRouter} screenOptions={screenOptions}>
322
- <SidebarNavigatorContent
323
- bottomItems={bottomItems}
324
- itemStyle={itemStyle}
325
- onNavigate={onNavigate}
326
- panelStyle={panelStyle}
327
- topItems={topItems}
328
- />
478
+ <View style={{flex: 1}}>
479
+ <SidebarHeader onOpen={() => setIsSheetOpen(true)} />
480
+ <SidebarNavigatorContent
481
+ bottomItems={bottomItems}
482
+ isOpen={isSheetOpen}
483
+ itemStyle={itemStyle}
484
+ onNavigate={onNavigate}
485
+ onOpenChange={setIsSheetOpen}
486
+ panelStyle={panelStyle}
487
+ topItems={topItems}
488
+ />
489
+ </View>
490
+ {children}
329
491
  </Navigator>
330
492
  );
331
493
  };
494
+
495
+ export const SidebarNavigation = Object.assign(SidebarNavigationBase, {Screen});