@onekeyfe/react-native-bottom-tabs 6.7.0-19 → 6.7.0-21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. package/android/src/main/java/com/onekey/bottomtabs/BottomBarBlurRenderer.kt +50 -30
  2. package/android/src/main/java/com/onekey/bottomtabs/BottomTabsDrawerManager.kt +55 -0
  3. package/android/src/main/java/com/onekey/bottomtabs/BottomTabsDrawerView.kt +496 -0
  4. package/android/src/main/java/com/onekey/bottomtabs/BottomTabsPackage.kt +1 -1
  5. package/android/src/main/java/com/onekey/bottomtabs/BottomTabsView.kt +24 -4
  6. package/android/src/main/java/com/onekey/bottomtabs/BottomTabsViewManager.kt +3 -0
  7. package/docs/SPEC.md +184 -20
  8. package/ios/RCTBottomTabsComponentView.mm +37 -0
  9. package/ios/RCTBottomTabsContainerView.swift +192 -7
  10. package/ios/RCTBottomTabsDrawerComponentView.h +8 -0
  11. package/ios/RCTBottomTabsDrawerComponentView.mm +79 -0
  12. package/ios/RCTBottomTabsDrawerContainerView.swift +327 -0
  13. package/lib/module/BottomTabs.js +144 -87
  14. package/lib/module/BottomTabsDrawerNativeComponent.ts +19 -0
  15. package/lib/module/BottomTabsDrawerNativeComponent.web.js +13 -0
  16. package/lib/module/BottomTabsNativeComponent.ts +13 -0
  17. package/lib/typescript/src/BottomTabs.d.ts +36 -10
  18. package/lib/typescript/src/BottomTabsDrawerNativeComponent.d.ts +15 -0
  19. package/lib/typescript/src/BottomTabsDrawerNativeComponent.web.d.ts +4 -0
  20. package/lib/typescript/src/BottomTabsNativeComponent.d.ts +8 -0
  21. package/lib/typescript/src/index.d.ts +1 -1
  22. package/lib/typescript/src/types.d.ts +1 -0
  23. package/package.json +3 -2
  24. package/react-native.config.js +4 -1
  25. package/src/BottomTabs.tsx +235 -115
  26. package/src/BottomTabsDrawerNativeComponent.ts +19 -0
  27. package/src/BottomTabsDrawerNativeComponent.web.tsx +9 -0
  28. package/src/BottomTabsNativeComponent.ts +13 -0
  29. package/src/index.tsx +1 -1
  30. package/src/types.ts +3 -0
@@ -1,14 +1,15 @@
1
1
  "use strict";
2
2
 
3
- import React, { useLayoutEffect, useRef } from 'react';
4
- import { Image, Platform, StyleSheet, View, processColor } from 'react-native';
3
+ import React, { useLayoutEffect, useRef } from "react";
4
+ import { Image, Platform, StyleSheet, View, processColor } from "react-native";
5
5
  import { BottomTabBarHeightContext } from "./utils/BottomTabBarHeightContext.js";
6
6
 
7
7
  // eslint-disable-next-line @react-native/no-deep-imports
8
8
 
9
- import NativeBottomTabs from './BottomTabsNativeComponent';
10
- import useLatestCallback from 'use-latest-callback';
9
+ import NativeBottomTabs from "./BottomTabsNativeComponent";
10
+ import useLatestCallback from "use-latest-callback";
11
11
  import DelayedFreeze from "./DelayedFreeze.js";
12
+ import NativeBottomTabsDrawer from "./BottomTabsDrawerNativeComponent";
12
13
  import { BottomAccessoryView } from "./BottomAccessoryView.js";
13
14
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
14
15
  const isAppleSymbol = icon => icon?.sfSymbol;
@@ -19,6 +20,10 @@ const BottomTabs = ({
19
20
  renderLazyPlaceholder,
20
21
  onIndexChange,
21
22
  onTabDoublePress,
23
+ renderDrawerContent,
24
+ drawerOpen = false,
25
+ drawerSwipeEnabled = false,
26
+ onDrawerOpenChange,
22
27
  rippleColor,
23
28
  tabBarActiveTintColor: activeTintColor,
24
29
  tabBarInactiveTintColor: inactiveTintColor,
@@ -60,7 +65,7 @@ const BottomTabs = ({
60
65
  route
61
66
  }) => route.preventsDefault,
62
67
  hapticFeedbackEnabled = false,
63
- labeled = Platform.OS !== 'android' ? true : undefined,
68
+ labeled = Platform.OS !== "android" ? true : undefined,
64
69
  getFreezeOnBlur = ({
65
70
  route
66
71
  }) => route.freezeOnBlur,
@@ -69,21 +74,27 @@ const BottomTabs = ({
69
74
  tabLabelStyle,
70
75
  renderBottomAccessoryView,
71
76
  activeIndicatorColor,
72
- androidBottomBlurEnabled = Platform.OS === 'android' && Number(Platform.Version) >= 36,
77
+ androidBottomBlurEnabled = Platform.OS === "android" && Number(Platform.Version) >= 36,
73
78
  delayedFreeze = false,
79
+ onTabBarPlacementChange,
80
+ sideAccessoryIcon,
81
+ sideAccessoryLabel,
82
+ onSideAccessoryPress,
74
83
  ...props
75
84
  }) => {
76
85
  // @ts-ignore
77
86
  const focusedKey = navigationState.routes[navigationState.index].key;
78
87
  const customTabBarWrapperRef = useRef(null);
79
88
  const nativeBottomTabsRef = useRef(null);
80
- const [tabBarHeight, setTabBarHeight] = React.useState(0);
89
+ const [nativeTabBarHeight, setNativeTabBarHeight] = React.useState(0);
90
+ const [customTabBarHeight, setCustomTabBarHeight] = React.useState(0);
91
+ const tabBarHeight = renderCustomTabBar ? customTabBarHeight : props.tabBarHidden ? 0 : nativeTabBarHeight;
81
92
  const [measuredDimensions, setMeasuredDimensions] = React.useState({
82
- width: '100%',
83
- height: '100%'
93
+ width: "100%",
94
+ height: "100%"
84
95
  });
85
96
  const trimmedRoutes = React.useMemo(() => {
86
- if (Platform.OS === 'android' && navigationState.routes.length > ANDROID_MAX_TABS) {
97
+ if (Platform.OS === "android" && navigationState.routes.length > ANDROID_MAX_TABS) {
87
98
  console.warn(`BottomTabs only supports up to ${ANDROID_MAX_TABS} tabs on Android`);
88
99
  return navigationState.routes.slice(0, ANDROID_MAX_TABS);
89
100
  }
@@ -93,7 +104,7 @@ const BottomTabs = ({
93
104
  if (!loaded.includes(focusedKey)) {
94
105
  setLoaded(loaded => [...loaded, focusedKey]);
95
106
  }
96
- const isIOS = Platform.OS === 'ios';
107
+ const isIOS = Platform.OS === "ios";
97
108
 
98
109
  // Unfocused (outline) icons — independent of focusedKey so the array stays
99
110
  // stable across tab switches and the native side never reloads on switch.
@@ -120,8 +131,8 @@ const BottomTabs = ({
120
131
  const items = React.useMemo(() => trimmedRoutes.map((route, index) => {
121
132
  const icon = icons[index];
122
133
  const isSfSymbol = isAppleSymbol(icon);
123
- if (Platform.OS === 'android' && isSfSymbol) {
124
- console.warn('SF Symbols are not supported on Android. Use require() or pass uri to load an image instead.');
134
+ if (Platform.OS === "android" && isSfSymbol) {
135
+ console.warn("SF Symbols are not supported on Android. Use require() or pass uri to load an image instead.");
125
136
  }
126
137
  const color = processColor(getActiveTintColor({
127
138
  route
@@ -141,9 +152,9 @@ const BottomTabs = ({
141
152
  badge: getBadge?.({
142
153
  route
143
154
  }),
144
- badgeBackgroundColor: typeof badgeBgColor === 'number' ? badgeBgColor : undefined,
145
- badgeTextColor: typeof badgeTxtColor === 'number' ? badgeTxtColor : undefined,
146
- activeTintColor: typeof color === 'number' ? color : undefined,
155
+ badgeBackgroundColor: typeof badgeBgColor === "number" ? badgeBgColor : undefined,
156
+ badgeTextColor: typeof badgeTxtColor === "number" ? badgeTxtColor : undefined,
157
+ activeTintColor: typeof color === "number" ? color : undefined,
147
158
  hidden: getHidden?.({
148
159
  route
149
160
  }),
@@ -163,14 +174,14 @@ const BottomTabs = ({
163
174
  // @ts-ignore - resolveAssetSource accepts ImageSourcePropType
164
175
  const resolved = Image.resolveAssetSource(icon);
165
176
  return {
166
- uri: resolved?.uri ?? '',
177
+ uri: resolved?.uri ?? "",
167
178
  width: resolved?.width ?? 0,
168
179
  height: resolved?.height ?? 0,
169
180
  scale: resolved?.scale ?? 1
170
181
  };
171
182
  }
172
183
  return {
173
- uri: '',
184
+ uri: "",
174
185
  width: 0,
175
186
  height: 0,
176
187
  scale: 1
@@ -184,14 +195,14 @@ const BottomTabs = ({
184
195
  // @ts-ignore - resolveAssetSource accepts ImageSourcePropType
185
196
  const resolved = Image.resolveAssetSource(icon);
186
197
  return {
187
- uri: resolved?.uri ?? '',
198
+ uri: resolved?.uri ?? "",
188
199
  width: resolved?.width ?? 0,
189
200
  height: resolved?.height ?? 0,
190
201
  scale: resolved?.scale ?? 1
191
202
  };
192
203
  }
193
204
  return {
194
- uri: '',
205
+ uri: "",
195
206
  width: 0,
196
207
  height: 0,
197
208
  scale: 1
@@ -225,8 +236,8 @@ const BottomTabs = ({
225
236
  }
226
237
  }, [focusedKey, jumpTo]);
227
238
  const handleTabBarMeasured = React.useCallback(event => {
228
- setTabBarHeight(event.nativeEvent.height);
229
- }, [setTabBarHeight]);
239
+ setNativeTabBarHeight(event.nativeEvent.height);
240
+ }, [setNativeTabBarHeight]);
230
241
  const handleNativeLayout = React.useCallback(event => {
231
242
  const {
232
243
  width,
@@ -237,88 +248,134 @@ const BottomTabs = ({
237
248
  height
238
249
  });
239
250
  }, [setMeasuredDimensions]);
251
+ const handleTabBarPlacementChange = React.useCallback(event => {
252
+ onTabBarPlacementChange?.(event.nativeEvent.placement);
253
+ }, [onTabBarPlacementChange]);
254
+ const handleSideAccessoryPress = React.useCallback(() => {
255
+ onSideAccessoryPress?.();
256
+ }, [onSideAccessoryPress]);
257
+ const resolvedSideAccessoryIcon = React.useMemo(() => {
258
+ if (!isIOS || !sideAccessoryIcon) {
259
+ return undefined;
260
+ }
261
+ // @ts-ignore - resolveAssetSource accepts ImageSourcePropType
262
+ const resolved = Image.resolveAssetSource(sideAccessoryIcon);
263
+ return resolved?.uri ? {
264
+ uri: resolved.uri,
265
+ width: resolved.width ?? 0,
266
+ height: resolved.height ?? 0,
267
+ scale: resolved.scale ?? 1
268
+ } : undefined;
269
+ }, [sideAccessoryIcon]);
240
270
  useLayoutEffect(() => {
241
271
  if (renderCustomTabBar && customTabBarWrapperRef.current) {
242
272
  customTabBarWrapperRef.current.measure((_x, _y, _width, height) => {
243
- setTabBarHeight(height);
273
+ setCustomTabBarHeight(height);
244
274
  });
245
275
  }
246
276
  }, [renderCustomTabBar]);
247
- return /*#__PURE__*/_jsxs(BottomTabBarHeightContext.Provider, {
277
+ return /*#__PURE__*/_jsx(BottomTabBarHeightContext.Provider, {
248
278
  value: tabBarHeight,
249
- children: [/*#__PURE__*/_jsxs(NativeBottomTabs, {
250
- ref: nativeBottomTabsRef,
251
- ...props,
252
- ...tabLabelStyle,
279
+ children: /*#__PURE__*/_jsxs(NativeBottomTabsDrawer, {
253
280
  style: styles.fullWidth,
254
- items: items,
255
- icons: renderCustomTabBar ? undefined : resolvedIconAssets,
256
- selectedIcons: isIOS && !renderCustomTabBar ? resolvedSelectedIconAssets : undefined,
257
- selectedPage: focusedKey,
258
- tabBarHidden: props.tabBarHidden ?? !!renderCustomTabBar,
259
- onTabDoublePress: handleTabDoublePress,
260
- onPageSelected: handlePageSelected,
261
- onTabBarMeasured: handleTabBarMeasured,
262
- onNativeLayout: handleNativeLayout,
281
+ enabled: !!renderDrawerContent,
282
+ open: drawerOpen,
283
+ swipeEnabled: drawerSwipeEnabled,
263
284
  hapticFeedbackEnabled: hapticFeedbackEnabled,
264
- activeTintColor: activeTintColor,
265
- inactiveTintColor: inactiveTintColor,
266
- barTintColor: tabBarStyle?.backgroundColor,
267
- rippleColor: rippleColor,
268
- activeIndicatorColor: activeIndicatorColor,
269
- androidBottomBlurEnabled: Platform.OS === 'android' && !renderCustomTabBar && androidBottomBlurEnabled,
270
- labeled: labeled,
271
- children: [trimmedRoutes.map(route => {
272
- if (getLazy({
273
- route
274
- }) !== false && !loaded.includes(route.key)) {
275
- return /*#__PURE__*/_jsx(View, {
276
- collapsable: false,
277
- style: styles.fullWidth,
278
- children: renderLazyPlaceholder?.({
285
+ bottomBarHeight: tabBarHeight ?? 0,
286
+ onDrawerOpenChange: event => onDrawerOpenChange?.(event.nativeEvent.open),
287
+ children: [/*#__PURE__*/_jsx(View, {
288
+ collapsable: false,
289
+ style: styles.drawerMenu,
290
+ children: Platform.OS !== "web" ? renderDrawerContent?.() : null
291
+ }), /*#__PURE__*/_jsxs(View, {
292
+ collapsable: false,
293
+ style: styles.fullWidth,
294
+ children: [/*#__PURE__*/_jsxs(NativeBottomTabs, {
295
+ ref: nativeBottomTabsRef,
296
+ ...props,
297
+ ...tabLabelStyle,
298
+ style: styles.fullWidth,
299
+ items: items,
300
+ icons: renderCustomTabBar ? undefined : resolvedIconAssets,
301
+ selectedIcons: isIOS && !renderCustomTabBar ? resolvedSelectedIconAssets : undefined,
302
+ selectedPage: focusedKey,
303
+ tabBarHidden: props.tabBarHidden ?? !!renderCustomTabBar,
304
+ onTabDoublePress: handleTabDoublePress,
305
+ onPageSelected: handlePageSelected,
306
+ onTabBarMeasured: handleTabBarMeasured,
307
+ onNativeLayout: handleNativeLayout,
308
+ onTabBarPlacementChange: handleTabBarPlacementChange,
309
+ sideAccessoryIcon: renderCustomTabBar ? undefined : resolvedSideAccessoryIcon,
310
+ sideAccessoryLabel: sideAccessoryLabel,
311
+ onSideAccessoryPress: handleSideAccessoryPress,
312
+ hapticFeedbackEnabled: hapticFeedbackEnabled,
313
+ activeTintColor: activeTintColor,
314
+ inactiveTintColor: inactiveTintColor,
315
+ barTintColor: tabBarStyle?.backgroundColor,
316
+ rippleColor: rippleColor,
317
+ activeIndicatorColor: activeIndicatorColor,
318
+ androidBottomBlurEnabled: Platform.OS === "android" && !renderCustomTabBar && androidBottomBlurEnabled,
319
+ labeled: labeled,
320
+ children: [trimmedRoutes.map(route => {
321
+ if (getLazy({
322
+ route
323
+ }) !== false && !loaded.includes(route.key)) {
324
+ return /*#__PURE__*/_jsx(View, {
325
+ collapsable: false,
326
+ style: styles.fullWidth,
327
+ children: renderLazyPlaceholder?.({
328
+ route
329
+ })
330
+ }, route.key);
331
+ }
332
+ const focused = route.key === focusedKey;
333
+ const freeze = !focused ? getFreezeOnBlur({
279
334
  route
280
- })
281
- }, route.key);
282
- }
283
- const focused = route.key === focusedKey;
284
- const freeze = !focused ? getFreezeOnBlur({
285
- route
286
- }) : false;
287
- const customStyle = getSceneStyle({
288
- route
289
- });
290
- return /*#__PURE__*/_jsx(View, {
291
- style: [styles.screen, renderCustomTabBar ? styles.fullWidth : measuredDimensions, customStyle],
292
- collapsable: false,
293
- pointerEvents: focused ? 'auto' : 'none',
294
- accessibilityElementsHidden: !focused,
295
- importantForAccessibility: focused ? 'auto' : 'no-hide-descendants',
296
- children: /*#__PURE__*/_jsx(DelayedFreeze, {
297
- freeze: !!freeze,
298
- delayedFreeze: delayedFreeze,
299
- children: renderScene({
300
- route,
301
- jumpTo
302
- })
303
- })
304
- }, route.key);
305
- }), Platform.OS === 'ios' && parseFloat(Platform.Version) >= 26 && renderBottomAccessoryView && !renderCustomTabBar ? /*#__PURE__*/_jsx(BottomAccessoryView, {
306
- renderBottomAccessoryView: renderBottomAccessoryView
307
- }) : null]
308
- }), renderCustomTabBar ? /*#__PURE__*/_jsx(View, {
309
- ref: customTabBarWrapperRef,
310
- children: renderCustomTabBar()
311
- }) : null]
335
+ }) : false;
336
+ const customStyle = getSceneStyle({
337
+ route
338
+ });
339
+ return /*#__PURE__*/_jsx(View, {
340
+ style: [styles.screen, renderCustomTabBar ? styles.fullWidth : measuredDimensions, customStyle],
341
+ collapsable: false,
342
+ pointerEvents: focused ? "auto" : "none",
343
+ accessibilityElementsHidden: !focused,
344
+ importantForAccessibility: focused ? "auto" : "no-hide-descendants",
345
+ children: /*#__PURE__*/_jsx(DelayedFreeze, {
346
+ freeze: !!freeze,
347
+ delayedFreeze: delayedFreeze,
348
+ children: renderScene({
349
+ route,
350
+ jumpTo
351
+ })
352
+ })
353
+ }, route.key);
354
+ }), Platform.OS === "ios" && parseFloat(Platform.Version) >= 26 && renderBottomAccessoryView && !renderCustomTabBar ? /*#__PURE__*/_jsx(BottomAccessoryView, {
355
+ renderBottomAccessoryView: renderBottomAccessoryView
356
+ }) : null]
357
+ }), renderCustomTabBar ? /*#__PURE__*/_jsx(View, {
358
+ ref: customTabBarWrapperRef,
359
+ onLayout: event => setCustomTabBarHeight(event.nativeEvent.layout.height),
360
+ children: renderCustomTabBar()
361
+ }) : null]
362
+ })]
363
+ })
312
364
  });
313
365
  };
314
366
  const styles = StyleSheet.create({
315
367
  fullWidth: {
316
- width: '100%',
317
- height: '100%',
368
+ width: "100%",
369
+ height: "100%",
318
370
  flex: 1
319
371
  },
372
+ drawerMenu: {
373
+ position: "absolute",
374
+ width: "78%",
375
+ height: "100%"
376
+ },
320
377
  screen: {
321
- position: 'absolute'
378
+ position: "absolute"
322
379
  }
323
380
  });
324
381
  export default BottomTabs;
@@ -0,0 +1,19 @@
1
+ import { codegenNativeComponent, type ViewProps } from "react-native";
2
+ import type {
3
+ DirectEventHandler,
4
+ Double,
5
+ WithDefault,
6
+ } from "react-native/Libraries/Types/CodegenTypes";
7
+
8
+ export interface BottomTabsDrawerNativeProps extends ViewProps {
9
+ enabled?: WithDefault<boolean, false>;
10
+ open?: WithDefault<boolean, false>;
11
+ swipeEnabled?: WithDefault<boolean, false>;
12
+ hapticFeedbackEnabled?: WithDefault<boolean, false>;
13
+ bottomBarHeight?: Double;
14
+ onDrawerOpenChange?: DirectEventHandler<Readonly<{ open: boolean }>>;
15
+ }
16
+
17
+ export default codegenNativeComponent<BottomTabsDrawerNativeProps>(
18
+ "RNBottomTabsDrawer"
19
+ );
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+
3
+ import React from "react";
4
+ import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
5
+ // The fixed first slot is the menu; Web only renders the foreground slot.
6
+ export default function BottomTabsDrawerWeb({
7
+ children
8
+ }) {
9
+ return /*#__PURE__*/_jsx(_Fragment, {
10
+ children: React.Children.toArray(children)[1]
11
+ });
12
+ }
13
+ //# sourceMappingURL=BottomTabsDrawerNativeComponent.web.js.map
@@ -22,6 +22,14 @@ export type OnNativeLayout = Readonly<{
22
22
  height: Double;
23
23
  }>;
24
24
 
25
+ // Where the system draws the tab bar: 'bottom', or 'leading' / 'trailing' for
26
+ // a vertical bar (the iPhone Duo side strip, iOS 27.1+).
27
+ export type OnTabBarPlacementChange = Readonly<{
28
+ placement: string;
29
+ }>;
30
+
31
+ export type OnSideAccessoryPress = Readonly<{}>;
32
+
25
33
  export type BottomTabsItems = ReadonlyArray<{
26
34
  key: string;
27
35
  title: string;
@@ -65,6 +73,11 @@ export interface BottomTabsNativeProps extends ViewProps {
65
73
  ignoreBottomInsets?: boolean;
66
74
  androidTabBarStyle?: WithDefault<'default' | 'floating', 'default'>;
67
75
  ignoreKeyboardInsets?: WithDefault<boolean, true>;
76
+ onTabBarPlacementChange?: DirectEventHandler<OnTabBarPlacementChange>;
77
+ // A round button drawn above a vertical tab bar; iOS only.
78
+ sideAccessoryIcon?: ImageSource;
79
+ sideAccessoryLabel?: string;
80
+ onSideAccessoryPress?: DirectEventHandler<OnSideAccessoryPress>;
68
81
  }
69
82
 
70
83
  export default codegenNativeComponent<BottomTabsNativeProps>('RNBottomTabsView');
@@ -1,10 +1,18 @@
1
- import React from 'react';
2
- import { type ColorValue, type StyleProp, type ViewStyle } from 'react-native';
3
- import type { ImageSource } from 'react-native/Libraries/Image/ImageSource';
4
- import type { AppleIcon, BaseRoute, NavigationState, TabRole } from './types';
5
- import { type RNBottomTabsAccessoryViewProps } from './BottomAccessoryView';
1
+ import React from "react";
2
+ import { type ColorValue, type ImageSourcePropType, type StyleProp, type ViewStyle } from "react-native";
3
+ import type { ImageSource } from "react-native/Libraries/Image/ImageSource";
4
+ import type { AppleIcon, BaseRoute, NavigationState, TabBarPlacement, TabRole } from "./types";
5
+ import { type RNBottomTabsAccessoryViewProps } from "./BottomAccessoryView";
6
6
  interface Props<Route extends BaseRoute> {
7
7
  labeled?: boolean;
8
+ /** Optional physical-left menu; all motion and input are owned by native. */
9
+ renderDrawerContent?: () => React.ReactNode;
10
+ /** External drawer target. Mirror onDrawerOpenChange for user transitions. */
11
+ drawerOpen?: boolean;
12
+ /** Enables root-screen content swipes after inner horizontal views reach their boundary. */
13
+ drawerSwipeEnabled?: boolean;
14
+ /** Reports a completed user target so the external open state can follow it. */
15
+ onDrawerOpenChange?: (open: boolean) => void;
8
16
  /**
9
17
  * A tab bar style that adapts to each platform.
10
18
  *
@@ -27,11 +35,11 @@ interface Props<Route extends BaseRoute> {
27
35
  /**
28
36
  * Describes the appearance attributes for the tabBar to use when an observable scroll view is scrolled to the bottom. (iOS only)
29
37
  */
30
- scrollEdgeAppearance?: 'default' | 'opaque' | 'transparent';
38
+ scrollEdgeAppearance?: "default" | "opaque" | "transparent";
31
39
  /**
32
40
  * Behavior for minimizing the tab bar. (iOS 26+)
33
41
  */
34
- minimizeBehavior?: 'automatic' | 'onScrollDown' | 'onScrollUp' | 'never';
42
+ minimizeBehavior?: "automatic" | "onScrollDown" | "onScrollUp" | "never";
35
43
  /**
36
44
  * Active tab color.
37
45
  */
@@ -178,7 +186,7 @@ interface Props<Route extends BaseRoute> {
178
186
  * A function that returns a React element to display as bottom accessory view.
179
187
  * iOS 26+ only.
180
188
  */
181
- renderBottomAccessoryView?: RNBottomTabsAccessoryViewProps['renderBottomAccessoryView'];
189
+ renderBottomAccessoryView?: RNBottomTabsAccessoryViewProps["renderBottomAccessoryView"];
182
190
  /**
183
191
  * Whether the tab bar is hidden.
184
192
  */
@@ -190,7 +198,7 @@ interface Props<Route extends BaseRoute> {
190
198
  /**
191
199
  * Visual style of the Android tab bar. Android only.
192
200
  */
193
- androidTabBarStyle?: 'default' | 'floating';
201
+ androidTabBarStyle?: "default" | "floating";
194
202
  /**
195
203
  * Whether the tab bar ignores the soft keyboard. Defaults to true, so the
196
204
  * keyboard covers the tab bar as on iOS. Set to false to pad the tab bar by
@@ -203,7 +211,25 @@ interface Props<Route extends BaseRoute> {
203
211
  * original delayed behavior.
204
212
  */
205
213
  delayedFreeze?: boolean;
214
+ /**
215
+ * Called when the system moves the tab bar between the bottom edge and a
216
+ * vertical bar on the leading or trailing edge (iPhone Duo). iOS only.
217
+ */
218
+ onTabBarPlacementChange?: (placement: TabBarPlacement) => void;
219
+ /**
220
+ * Icon of a round button shown above a vertical tab bar. The button only
221
+ * appears while the tab bar is vertical and visible. iOS only.
222
+ */
223
+ sideAccessoryIcon?: ImageSourcePropType;
224
+ /**
225
+ * Accessibility label of the side accessory button.
226
+ */
227
+ sideAccessoryLabel?: string;
228
+ /**
229
+ * Called when the side accessory button is pressed.
230
+ */
231
+ onSideAccessoryPress?: () => void;
206
232
  }
207
- declare const BottomTabs: <Route extends BaseRoute>({ navigationState, renderScene, renderLazyPlaceholder, onIndexChange, onTabDoublePress, rippleColor, tabBarActiveTintColor: activeTintColor, tabBarInactiveTintColor: inactiveTintColor, getBadge, getBadgeBackgroundColor, getBadgeTextColor, getLazy, getLabelText, getIcon, getHidden, getActiveTintColor, getTestID, getRole, getSceneStyle, getPreventsDefault, hapticFeedbackEnabled, labeled, getFreezeOnBlur, tabBar: renderCustomTabBar, tabBarStyle, tabLabelStyle, renderBottomAccessoryView, activeIndicatorColor, androidBottomBlurEnabled, delayedFreeze, ...props }: Props<Route>) => import("react/jsx-runtime").JSX.Element;
233
+ declare const BottomTabs: <Route extends BaseRoute>({ navigationState, renderScene, renderLazyPlaceholder, onIndexChange, onTabDoublePress, renderDrawerContent, drawerOpen, drawerSwipeEnabled, onDrawerOpenChange, rippleColor, tabBarActiveTintColor: activeTintColor, tabBarInactiveTintColor: inactiveTintColor, getBadge, getBadgeBackgroundColor, getBadgeTextColor, getLazy, getLabelText, getIcon, getHidden, getActiveTintColor, getTestID, getRole, getSceneStyle, getPreventsDefault, hapticFeedbackEnabled, labeled, getFreezeOnBlur, tabBar: renderCustomTabBar, tabBarStyle, tabLabelStyle, renderBottomAccessoryView, activeIndicatorColor, androidBottomBlurEnabled, delayedFreeze, onTabBarPlacementChange, sideAccessoryIcon, sideAccessoryLabel, onSideAccessoryPress, ...props }: Props<Route>) => import("react/jsx-runtime").JSX.Element;
208
234
  export default BottomTabs;
209
235
  //# sourceMappingURL=BottomTabs.d.ts.map
@@ -0,0 +1,15 @@
1
+ import { type ViewProps } from "react-native";
2
+ import type { DirectEventHandler, Double, WithDefault } from "react-native/Libraries/Types/CodegenTypes";
3
+ export interface BottomTabsDrawerNativeProps extends ViewProps {
4
+ enabled?: WithDefault<boolean, false>;
5
+ open?: WithDefault<boolean, false>;
6
+ swipeEnabled?: WithDefault<boolean, false>;
7
+ hapticFeedbackEnabled?: WithDefault<boolean, false>;
8
+ bottomBarHeight?: Double;
9
+ onDrawerOpenChange?: DirectEventHandler<Readonly<{
10
+ open: boolean;
11
+ }>>;
12
+ }
13
+ declare const _default: import("react-native/types_generated/Libraries/Utilities/codegenNativeComponent").NativeComponentType<BottomTabsDrawerNativeProps>;
14
+ export default _default;
15
+ //# sourceMappingURL=BottomTabsDrawerNativeComponent.d.ts.map
@@ -0,0 +1,4 @@
1
+ import React from "react";
2
+ import type { BottomTabsDrawerNativeProps } from "./BottomTabsDrawerNativeComponent";
3
+ export default function BottomTabsDrawerWeb({ children, }: React.PropsWithChildren<BottomTabsDrawerNativeProps>): import("react/jsx-runtime").JSX.Element;
4
+ //# sourceMappingURL=BottomTabsDrawerNativeComponent.web.d.ts.map
@@ -11,6 +11,10 @@ export type OnNativeLayout = Readonly<{
11
11
  width: Double;
12
12
  height: Double;
13
13
  }>;
14
+ export type OnTabBarPlacementChange = Readonly<{
15
+ placement: string;
16
+ }>;
17
+ export type OnSideAccessoryPress = Readonly<{}>;
14
18
  export type BottomTabsItems = ReadonlyArray<{
15
19
  key: string;
16
20
  title: string;
@@ -53,6 +57,10 @@ export interface BottomTabsNativeProps extends ViewProps {
53
57
  ignoreBottomInsets?: boolean;
54
58
  androidTabBarStyle?: WithDefault<'default' | 'floating', 'default'>;
55
59
  ignoreKeyboardInsets?: WithDefault<boolean, true>;
60
+ onTabBarPlacementChange?: DirectEventHandler<OnTabBarPlacementChange>;
61
+ sideAccessoryIcon?: ImageSource;
62
+ sideAccessoryLabel?: string;
63
+ onSideAccessoryPress?: DirectEventHandler<OnSideAccessoryPress>;
56
64
  }
57
65
  declare const _default: import("react-native").HostComponent<BottomTabsNativeProps>;
58
66
  export default _default;
@@ -12,5 +12,5 @@ export { BottomTabBarHeightContext } from './utils/BottomTabBarHeightContext';
12
12
  /**
13
13
  * Types
14
14
  */
15
- export type { AppleIcon, TabRole } from './types';
15
+ export type { AppleIcon, TabBarPlacement, TabRole } from './types';
16
16
  //# sourceMappingURL=index.d.ts.map
@@ -5,6 +5,7 @@ export type AppleIcon = {
5
5
  sfSymbol: SFSymbol;
6
6
  };
7
7
  export type TabRole = 'search';
8
+ export type TabBarPlacement = 'bottom' | 'leading' | 'trailing';
8
9
  export type BaseRoute = {
9
10
  key: string;
10
11
  title?: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onekeyfe/react-native-bottom-tabs",
3
- "version": "6.7.0-19",
3
+ "version": "6.7.0-21",
4
4
  "description": "Native bottom tabs (tab bar + scene container) for React Native on iOS and Android",
5
5
  "source": "./src/index.tsx",
6
6
  "main": "./lib/module/index.js",
@@ -107,7 +107,8 @@
107
107
  "ios": {
108
108
  "componentProvider": {
109
109
  "RNBottomTabsView": "RCTBottomTabsComponentView",
110
- "RNBottomTabsAccessoryView": "RCTBottomAccessoryComponentView"
110
+ "RNBottomTabsAccessoryView": "RCTBottomAccessoryComponentView",
111
+ "RNBottomTabsDrawer": "RCTBottomTabsDrawerComponentView"
111
112
  },
112
113
  "modulesConformingToProtocol": {
113
114
  "RCTImageDataDecoder": [
@@ -2,7 +2,10 @@ module.exports = {
2
2
  dependency: {
3
3
  platforms: {
4
4
  android: {
5
- componentDescriptors: ['RNBottomTabsViewComponentDescriptor'],
5
+ componentDescriptors: [
6
+ 'RNBottomTabsViewComponentDescriptor',
7
+ 'RNBottomTabsDrawerComponentDescriptor',
8
+ ],
6
9
  cmakeListsPath: undefined,
7
10
  packageImportPath:
8
11
  'import com.onekey.bottomtabs.BottomTabsPackage;',