@onekeyfe/react-native-bottom-tabs 6.7.0-alpha.382 → 6.7.0-alpha.384
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/android/src/main/java/com/onekey/bottomtabs/BottomTabsDrawerManager.kt +55 -0
- package/android/src/main/java/com/onekey/bottomtabs/BottomTabsDrawerView.kt +496 -0
- package/android/src/main/java/com/onekey/bottomtabs/BottomTabsPackage.kt +1 -1
- package/docs/SPEC.md +121 -2
- package/ios/RCTBottomTabsContainerView.swift +2 -6
- package/ios/RCTBottomTabsDrawerComponentView.h +8 -0
- package/ios/RCTBottomTabsDrawerComponentView.mm +79 -0
- package/ios/RCTBottomTabsDrawerContainerView.swift +327 -0
- package/lib/module/BottomTabs.js +95 -65
- package/lib/module/BottomTabsDrawerNativeComponent.ts +19 -0
- package/lib/module/BottomTabsDrawerNativeComponent.web.js +13 -0
- package/lib/typescript/src/BottomTabs.d.ts +9 -1
- package/lib/typescript/src/BottomTabsDrawerNativeComponent.d.ts +15 -0
- package/lib/typescript/src/BottomTabsDrawerNativeComponent.web.d.ts +4 -0
- package/package.json +3 -2
- package/react-native.config.js +4 -1
- package/src/BottomTabs.tsx +124 -80
- package/src/BottomTabsDrawerNativeComponent.ts +19 -0
- package/src/BottomTabsDrawerNativeComponent.web.tsx +9 -0
package/lib/module/BottomTabs.js
CHANGED
|
@@ -9,6 +9,7 @@ import { BottomTabBarHeightContext } from "./utils/BottomTabBarHeightContext.js"
|
|
|
9
9
|
import NativeBottomTabs from './BottomTabsNativeComponent';
|
|
10
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,
|
|
@@ -77,7 +82,9 @@ const BottomTabs = ({
|
|
|
77
82
|
const focusedKey = navigationState.routes[navigationState.index].key;
|
|
78
83
|
const customTabBarWrapperRef = useRef(null);
|
|
79
84
|
const nativeBottomTabsRef = useRef(null);
|
|
80
|
-
const [
|
|
85
|
+
const [nativeTabBarHeight, setNativeTabBarHeight] = React.useState(0);
|
|
86
|
+
const [customTabBarHeight, setCustomTabBarHeight] = React.useState(0);
|
|
87
|
+
const tabBarHeight = renderCustomTabBar ? customTabBarHeight : props.tabBarHidden ? 0 : nativeTabBarHeight;
|
|
81
88
|
const [measuredDimensions, setMeasuredDimensions] = React.useState({
|
|
82
89
|
width: '100%',
|
|
83
90
|
height: '100%'
|
|
@@ -225,8 +232,8 @@ const BottomTabs = ({
|
|
|
225
232
|
}
|
|
226
233
|
}, [focusedKey, jumpTo]);
|
|
227
234
|
const handleTabBarMeasured = React.useCallback(event => {
|
|
228
|
-
|
|
229
|
-
}, [
|
|
235
|
+
setNativeTabBarHeight(event.nativeEvent.height);
|
|
236
|
+
}, [setNativeTabBarHeight]);
|
|
230
237
|
const handleNativeLayout = React.useCallback(event => {
|
|
231
238
|
const {
|
|
232
239
|
width,
|
|
@@ -240,75 +247,93 @@ const BottomTabs = ({
|
|
|
240
247
|
useLayoutEffect(() => {
|
|
241
248
|
if (renderCustomTabBar && customTabBarWrapperRef.current) {
|
|
242
249
|
customTabBarWrapperRef.current.measure((_x, _y, _width, height) => {
|
|
243
|
-
|
|
250
|
+
setCustomTabBarHeight(height);
|
|
244
251
|
});
|
|
245
252
|
}
|
|
246
253
|
}, [renderCustomTabBar]);
|
|
247
|
-
return /*#__PURE__*/
|
|
254
|
+
return /*#__PURE__*/_jsx(BottomTabBarHeightContext.Provider, {
|
|
248
255
|
value: tabBarHeight,
|
|
249
|
-
children:
|
|
250
|
-
ref: nativeBottomTabsRef,
|
|
251
|
-
...props,
|
|
252
|
-
...tabLabelStyle,
|
|
256
|
+
children: /*#__PURE__*/_jsxs(NativeBottomTabsDrawer, {
|
|
253
257
|
style: styles.fullWidth,
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
selectedPage: focusedKey,
|
|
258
|
-
tabBarHidden: props.tabBarHidden ?? !!renderCustomTabBar,
|
|
259
|
-
onTabDoublePress: handleTabDoublePress,
|
|
260
|
-
onPageSelected: handlePageSelected,
|
|
261
|
-
onTabBarMeasured: handleTabBarMeasured,
|
|
262
|
-
onNativeLayout: handleNativeLayout,
|
|
258
|
+
enabled: !!renderDrawerContent,
|
|
259
|
+
open: drawerOpen,
|
|
260
|
+
swipeEnabled: drawerSwipeEnabled,
|
|
263
261
|
hapticFeedbackEnabled: hapticFeedbackEnabled,
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
262
|
+
bottomBarHeight: tabBarHeight ?? 0,
|
|
263
|
+
onDrawerOpenChange: event => onDrawerOpenChange?.(event.nativeEvent.open),
|
|
264
|
+
children: [/*#__PURE__*/_jsx(View, {
|
|
265
|
+
collapsable: false,
|
|
266
|
+
style: styles.drawerMenu,
|
|
267
|
+
children: Platform.OS !== 'web' ? renderDrawerContent?.() : null
|
|
268
|
+
}), /*#__PURE__*/_jsxs(View, {
|
|
269
|
+
collapsable: false,
|
|
270
|
+
style: styles.fullWidth,
|
|
271
|
+
children: [/*#__PURE__*/_jsxs(NativeBottomTabs, {
|
|
272
|
+
ref: nativeBottomTabsRef,
|
|
273
|
+
...props,
|
|
274
|
+
...tabLabelStyle,
|
|
275
|
+
style: styles.fullWidth,
|
|
276
|
+
items: items,
|
|
277
|
+
icons: renderCustomTabBar ? undefined : resolvedIconAssets,
|
|
278
|
+
selectedIcons: isIOS && !renderCustomTabBar ? resolvedSelectedIconAssets : undefined,
|
|
279
|
+
selectedPage: focusedKey,
|
|
280
|
+
tabBarHidden: props.tabBarHidden ?? !!renderCustomTabBar,
|
|
281
|
+
onTabDoublePress: handleTabDoublePress,
|
|
282
|
+
onPageSelected: handlePageSelected,
|
|
283
|
+
onTabBarMeasured: handleTabBarMeasured,
|
|
284
|
+
onNativeLayout: handleNativeLayout,
|
|
285
|
+
hapticFeedbackEnabled: hapticFeedbackEnabled,
|
|
286
|
+
activeTintColor: activeTintColor,
|
|
287
|
+
inactiveTintColor: inactiveTintColor,
|
|
288
|
+
barTintColor: tabBarStyle?.backgroundColor,
|
|
289
|
+
rippleColor: rippleColor,
|
|
290
|
+
activeIndicatorColor: activeIndicatorColor,
|
|
291
|
+
androidBottomBlurEnabled: Platform.OS === 'android' && !renderCustomTabBar && androidBottomBlurEnabled,
|
|
292
|
+
labeled: labeled,
|
|
293
|
+
children: [trimmedRoutes.map(route => {
|
|
294
|
+
if (getLazy({
|
|
279
295
|
route
|
|
280
|
-
})
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
296
|
+
}) !== false && !loaded.includes(route.key)) {
|
|
297
|
+
return /*#__PURE__*/_jsx(View, {
|
|
298
|
+
collapsable: false,
|
|
299
|
+
style: styles.fullWidth,
|
|
300
|
+
children: renderLazyPlaceholder?.({
|
|
301
|
+
route
|
|
302
|
+
})
|
|
303
|
+
}, route.key);
|
|
304
|
+
}
|
|
305
|
+
const focused = route.key === focusedKey;
|
|
306
|
+
const freeze = !focused ? getFreezeOnBlur({
|
|
307
|
+
route
|
|
308
|
+
}) : false;
|
|
309
|
+
const customStyle = getSceneStyle({
|
|
310
|
+
route
|
|
311
|
+
});
|
|
312
|
+
return /*#__PURE__*/_jsx(View, {
|
|
313
|
+
style: [styles.screen, renderCustomTabBar ? styles.fullWidth : measuredDimensions, customStyle],
|
|
314
|
+
collapsable: false,
|
|
315
|
+
pointerEvents: focused ? 'auto' : 'none',
|
|
316
|
+
accessibilityElementsHidden: !focused,
|
|
317
|
+
importantForAccessibility: focused ? 'auto' : 'no-hide-descendants',
|
|
318
|
+
children: /*#__PURE__*/_jsx(DelayedFreeze, {
|
|
319
|
+
freeze: !!freeze,
|
|
320
|
+
delayedFreeze: delayedFreeze,
|
|
321
|
+
children: renderScene({
|
|
322
|
+
route,
|
|
323
|
+
jumpTo
|
|
324
|
+
})
|
|
325
|
+
})
|
|
326
|
+
}, route.key);
|
|
327
|
+
}), Platform.OS === 'ios' && parseFloat(Platform.Version) >= 26 && renderBottomAccessoryView && !renderCustomTabBar ? /*#__PURE__*/_jsx(BottomAccessoryView, {
|
|
328
|
+
renderBottomAccessoryView: renderBottomAccessoryView
|
|
329
|
+
}) : null]
|
|
330
|
+
}), renderCustomTabBar ? /*#__PURE__*/_jsx(View, {
|
|
331
|
+
ref: customTabBarWrapperRef,
|
|
332
|
+
onLayout: event => setCustomTabBarHeight(event.nativeEvent.layout.height),
|
|
333
|
+
children: renderCustomTabBar()
|
|
334
|
+
}) : null]
|
|
335
|
+
})]
|
|
336
|
+
})
|
|
312
337
|
});
|
|
313
338
|
};
|
|
314
339
|
const styles = StyleSheet.create({
|
|
@@ -317,6 +342,11 @@ const styles = StyleSheet.create({
|
|
|
317
342
|
height: '100%',
|
|
318
343
|
flex: 1
|
|
319
344
|
},
|
|
345
|
+
drawerMenu: {
|
|
346
|
+
position: 'absolute',
|
|
347
|
+
width: '78%',
|
|
348
|
+
height: '100%'
|
|
349
|
+
},
|
|
320
350
|
screen: {
|
|
321
351
|
position: 'absolute'
|
|
322
352
|
}
|
|
@@ -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
|
|
@@ -5,6 +5,14 @@ import type { AppleIcon, BaseRoute, NavigationState, TabRole } from './types';
|
|
|
5
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
|
*
|
|
@@ -204,6 +212,6 @@ interface Props<Route extends BaseRoute> {
|
|
|
204
212
|
*/
|
|
205
213
|
delayedFreeze?: boolean;
|
|
206
214
|
}
|
|
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;
|
|
215
|
+
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, ...props }: Props<Route>) => import("react/jsx-runtime").JSX.Element;
|
|
208
216
|
export default BottomTabs;
|
|
209
217
|
//# 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
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onekeyfe/react-native-bottom-tabs",
|
|
3
|
-
"version": "6.7.0-alpha.
|
|
3
|
+
"version": "6.7.0-alpha.384",
|
|
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": [
|
package/react-native.config.js
CHANGED
|
@@ -2,7 +2,10 @@ module.exports = {
|
|
|
2
2
|
dependency: {
|
|
3
3
|
platforms: {
|
|
4
4
|
android: {
|
|
5
|
-
componentDescriptors: [
|
|
5
|
+
componentDescriptors: [
|
|
6
|
+
'RNBottomTabsViewComponentDescriptor',
|
|
7
|
+
'RNBottomTabsDrawerComponentDescriptor',
|
|
8
|
+
],
|
|
6
9
|
cmakeListsPath: undefined,
|
|
7
10
|
packageImportPath:
|
|
8
11
|
'import com.onekey.bottomtabs.BottomTabsPackage;',
|
package/src/BottomTabs.tsx
CHANGED
|
@@ -21,6 +21,7 @@ import NativeBottomTabs, {
|
|
|
21
21
|
import useLatestCallback from 'use-latest-callback';
|
|
22
22
|
import type { AppleIcon, BaseRoute, NavigationState, TabRole } from './types';
|
|
23
23
|
import DelayedFreeze from './DelayedFreeze';
|
|
24
|
+
import NativeBottomTabsDrawer from './BottomTabsDrawerNativeComponent';
|
|
24
25
|
import {
|
|
25
26
|
BottomAccessoryView,
|
|
26
27
|
type RNBottomTabsAccessoryViewProps,
|
|
@@ -34,6 +35,14 @@ interface Props<Route extends BaseRoute> {
|
|
|
34
35
|
* Whether to show labels in tabs. When false, only icons will be displayed.
|
|
35
36
|
*/
|
|
36
37
|
labeled?: boolean;
|
|
38
|
+
/** Optional physical-left menu; all motion and input are owned by native. */
|
|
39
|
+
renderDrawerContent?: () => React.ReactNode;
|
|
40
|
+
/** External drawer target. Mirror onDrawerOpenChange for user transitions. */
|
|
41
|
+
drawerOpen?: boolean;
|
|
42
|
+
/** Enables root-screen content swipes after inner horizontal views reach their boundary. */
|
|
43
|
+
drawerSwipeEnabled?: boolean;
|
|
44
|
+
/** Reports a completed user target so the external open state can follow it. */
|
|
45
|
+
onDrawerOpenChange?: (open: boolean) => void;
|
|
37
46
|
/**
|
|
38
47
|
* A tab bar style that adapts to each platform.
|
|
39
48
|
*
|
|
@@ -225,6 +234,10 @@ const BottomTabs = <Route extends BaseRoute>({
|
|
|
225
234
|
renderLazyPlaceholder,
|
|
226
235
|
onIndexChange,
|
|
227
236
|
onTabDoublePress,
|
|
237
|
+
renderDrawerContent,
|
|
238
|
+
drawerOpen = false,
|
|
239
|
+
drawerSwipeEnabled = false,
|
|
240
|
+
onDrawerOpenChange,
|
|
228
241
|
rippleColor,
|
|
229
242
|
tabBarActiveTintColor: activeTintColor,
|
|
230
243
|
tabBarInactiveTintColor: inactiveTintColor,
|
|
@@ -263,7 +276,11 @@ const BottomTabs = <Route extends BaseRoute>({
|
|
|
263
276
|
const focusedKey = navigationState.routes[navigationState.index].key;
|
|
264
277
|
const customTabBarWrapperRef = useRef<View>(null) as React.RefObject<any>;
|
|
265
278
|
const nativeBottomTabsRef = useRef<React.ElementRef<typeof NativeBottomTabs>>(null);
|
|
266
|
-
const [
|
|
279
|
+
const [nativeTabBarHeight, setNativeTabBarHeight] = React.useState(0);
|
|
280
|
+
const [customTabBarHeight, setCustomTabBarHeight] = React.useState(0);
|
|
281
|
+
const tabBarHeight = renderCustomTabBar
|
|
282
|
+
? customTabBarHeight
|
|
283
|
+
: props.tabBarHidden ? 0 : nativeTabBarHeight;
|
|
267
284
|
const [measuredDimensions, setMeasuredDimensions] = React.useState<
|
|
268
285
|
{ width: DimensionValue; height: DimensionValue } | undefined
|
|
269
286
|
>({ width: '100%', height: '100%' });
|
|
@@ -435,9 +452,9 @@ const BottomTabs = <Route extends BaseRoute>({
|
|
|
435
452
|
|
|
436
453
|
const handleTabBarMeasured = React.useCallback(
|
|
437
454
|
(event: NativeSyntheticEvent<{ height: number }>) => {
|
|
438
|
-
|
|
455
|
+
setNativeTabBarHeight(event.nativeEvent.height);
|
|
439
456
|
},
|
|
440
|
-
[
|
|
457
|
+
[setNativeTabBarHeight]
|
|
441
458
|
);
|
|
442
459
|
|
|
443
460
|
const handleNativeLayout = React.useCallback(
|
|
@@ -451,96 +468,118 @@ const BottomTabs = <Route extends BaseRoute>({
|
|
|
451
468
|
useLayoutEffect(() => {
|
|
452
469
|
if (renderCustomTabBar && customTabBarWrapperRef.current) {
|
|
453
470
|
customTabBarWrapperRef.current.measure((_x: number, _y: number, _width: number, height: number) => {
|
|
454
|
-
|
|
471
|
+
setCustomTabBarHeight(height);
|
|
455
472
|
});
|
|
456
473
|
}
|
|
457
474
|
}, [renderCustomTabBar]);
|
|
458
475
|
|
|
459
476
|
return (
|
|
460
477
|
<BottomTabBarHeightContext.Provider value={tabBarHeight}>
|
|
461
|
-
<
|
|
462
|
-
ref={nativeBottomTabsRef}
|
|
463
|
-
{...props}
|
|
464
|
-
{...tabLabelStyle}
|
|
478
|
+
<NativeBottomTabsDrawer
|
|
465
479
|
style={styles.fullWidth}
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
isIOS && !renderCustomTabBar ? resolvedSelectedIconAssets : undefined
|
|
470
|
-
}
|
|
471
|
-
selectedPage={focusedKey}
|
|
472
|
-
tabBarHidden={props.tabBarHidden ?? !!renderCustomTabBar}
|
|
473
|
-
onTabDoublePress={handleTabDoublePress}
|
|
474
|
-
onPageSelected={handlePageSelected}
|
|
475
|
-
onTabBarMeasured={handleTabBarMeasured}
|
|
476
|
-
onNativeLayout={handleNativeLayout}
|
|
480
|
+
enabled={!!renderDrawerContent}
|
|
481
|
+
open={drawerOpen}
|
|
482
|
+
swipeEnabled={drawerSwipeEnabled}
|
|
477
483
|
hapticFeedbackEnabled={hapticFeedbackEnabled}
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
rippleColor={rippleColor}
|
|
482
|
-
activeIndicatorColor={activeIndicatorColor}
|
|
483
|
-
androidBottomBlurEnabled={
|
|
484
|
-
Platform.OS === 'android' &&
|
|
485
|
-
!renderCustomTabBar &&
|
|
486
|
-
androidBottomBlurEnabled
|
|
484
|
+
bottomBarHeight={tabBarHeight ?? 0}
|
|
485
|
+
onDrawerOpenChange={(event) =>
|
|
486
|
+
onDrawerOpenChange?.(event.nativeEvent.open)
|
|
487
487
|
}
|
|
488
|
-
labeled={labeled}
|
|
489
488
|
>
|
|
490
|
-
{
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
489
|
+
<View collapsable={false} style={styles.drawerMenu}>
|
|
490
|
+
{Platform.OS !== 'web' ? renderDrawerContent?.() : null}
|
|
491
|
+
</View>
|
|
492
|
+
<View collapsable={false} style={styles.fullWidth}>
|
|
493
|
+
<NativeBottomTabs
|
|
494
|
+
ref={nativeBottomTabsRef}
|
|
495
|
+
{...props}
|
|
496
|
+
{...tabLabelStyle}
|
|
497
|
+
style={styles.fullWidth}
|
|
498
|
+
items={items}
|
|
499
|
+
icons={renderCustomTabBar ? undefined : resolvedIconAssets}
|
|
500
|
+
selectedIcons={
|
|
501
|
+
isIOS && !renderCustomTabBar ? resolvedSelectedIconAssets : undefined
|
|
502
|
+
}
|
|
503
|
+
selectedPage={focusedKey}
|
|
504
|
+
tabBarHidden={props.tabBarHidden ?? !!renderCustomTabBar}
|
|
505
|
+
onTabDoublePress={handleTabDoublePress}
|
|
506
|
+
onPageSelected={handlePageSelected}
|
|
507
|
+
onTabBarMeasured={handleTabBarMeasured}
|
|
508
|
+
onNativeLayout={handleNativeLayout}
|
|
509
|
+
hapticFeedbackEnabled={hapticFeedbackEnabled}
|
|
510
|
+
activeTintColor={activeTintColor}
|
|
511
|
+
inactiveTintColor={inactiveTintColor}
|
|
512
|
+
barTintColor={tabBarStyle?.backgroundColor}
|
|
513
|
+
rippleColor={rippleColor}
|
|
514
|
+
activeIndicatorColor={activeIndicatorColor}
|
|
515
|
+
androidBottomBlurEnabled={
|
|
516
|
+
Platform.OS === 'android' &&
|
|
517
|
+
!renderCustomTabBar &&
|
|
518
|
+
androidBottomBlurEnabled
|
|
519
|
+
}
|
|
520
|
+
labeled={labeled}
|
|
521
|
+
>
|
|
522
|
+
{trimmedRoutes.map((route) => {
|
|
523
|
+
if (getLazy({ route }) !== false && !loaded.includes(route.key)) {
|
|
524
|
+
return (
|
|
525
|
+
<View
|
|
526
|
+
key={route.key}
|
|
527
|
+
collapsable={false}
|
|
528
|
+
style={styles.fullWidth}
|
|
529
|
+
>
|
|
530
|
+
{renderLazyPlaceholder?.({ route })}
|
|
531
|
+
</View>
|
|
532
|
+
);
|
|
521
533
|
}
|
|
534
|
+
|
|
535
|
+
const focused = route.key === focusedKey;
|
|
536
|
+
const freeze = !focused ? getFreezeOnBlur({ route }) : false;
|
|
537
|
+
|
|
538
|
+
const customStyle = getSceneStyle({ route });
|
|
539
|
+
|
|
540
|
+
return (
|
|
541
|
+
<View
|
|
542
|
+
key={route.key}
|
|
543
|
+
style={[
|
|
544
|
+
styles.screen,
|
|
545
|
+
renderCustomTabBar ? styles.fullWidth : measuredDimensions,
|
|
546
|
+
customStyle,
|
|
547
|
+
]}
|
|
548
|
+
collapsable={false}
|
|
549
|
+
pointerEvents={focused ? 'auto' : 'none'}
|
|
550
|
+
accessibilityElementsHidden={!focused}
|
|
551
|
+
importantForAccessibility={
|
|
552
|
+
focused ? 'auto' : 'no-hide-descendants'
|
|
553
|
+
}
|
|
554
|
+
>
|
|
555
|
+
<DelayedFreeze freeze={!!freeze} delayedFreeze={delayedFreeze}>
|
|
556
|
+
{renderScene({
|
|
557
|
+
route,
|
|
558
|
+
jumpTo,
|
|
559
|
+
})}
|
|
560
|
+
</DelayedFreeze>
|
|
561
|
+
</View>
|
|
562
|
+
);
|
|
563
|
+
})}
|
|
564
|
+
{Platform.OS === 'ios' &&
|
|
565
|
+
parseFloat(Platform.Version) >= 26 &&
|
|
566
|
+
renderBottomAccessoryView &&
|
|
567
|
+
!renderCustomTabBar ? (
|
|
568
|
+
<BottomAccessoryView
|
|
569
|
+
renderBottomAccessoryView={renderBottomAccessoryView}
|
|
570
|
+
/>
|
|
571
|
+
) : null}
|
|
572
|
+
</NativeBottomTabs>
|
|
573
|
+
{renderCustomTabBar ? (
|
|
574
|
+
<View
|
|
575
|
+
ref={customTabBarWrapperRef}
|
|
576
|
+
onLayout={(event) => setCustomTabBarHeight(event.nativeEvent.layout.height)}
|
|
522
577
|
>
|
|
523
|
-
|
|
524
|
-
{renderScene({
|
|
525
|
-
route,
|
|
526
|
-
jumpTo,
|
|
527
|
-
})}
|
|
528
|
-
</DelayedFreeze>
|
|
578
|
+
{renderCustomTabBar()}
|
|
529
579
|
</View>
|
|
530
|
-
)
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
parseFloat(Platform.Version) >= 26 &&
|
|
534
|
-
renderBottomAccessoryView &&
|
|
535
|
-
!renderCustomTabBar ? (
|
|
536
|
-
<BottomAccessoryView
|
|
537
|
-
renderBottomAccessoryView={renderBottomAccessoryView}
|
|
538
|
-
/>
|
|
539
|
-
) : null}
|
|
540
|
-
</NativeBottomTabs>
|
|
541
|
-
{renderCustomTabBar ? (
|
|
542
|
-
<View ref={customTabBarWrapperRef}>{renderCustomTabBar()}</View>
|
|
543
|
-
) : null}
|
|
580
|
+
) : null}
|
|
581
|
+
</View>
|
|
582
|
+
</NativeBottomTabsDrawer>
|
|
544
583
|
</BottomTabBarHeightContext.Provider>
|
|
545
584
|
);
|
|
546
585
|
};
|
|
@@ -551,6 +590,11 @@ const styles = StyleSheet.create({
|
|
|
551
590
|
height: '100%',
|
|
552
591
|
flex: 1,
|
|
553
592
|
},
|
|
593
|
+
drawerMenu: {
|
|
594
|
+
position: 'absolute',
|
|
595
|
+
width: '78%',
|
|
596
|
+
height: '100%',
|
|
597
|
+
},
|
|
554
598
|
screen: {
|
|
555
599
|
position: 'absolute',
|
|
556
600
|
},
|
|
@@ -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,9 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import type { BottomTabsDrawerNativeProps } from "./BottomTabsDrawerNativeComponent";
|
|
3
|
+
|
|
4
|
+
// The fixed first slot is the menu; Web only renders the foreground slot.
|
|
5
|
+
export default function BottomTabsDrawerWeb({
|
|
6
|
+
children,
|
|
7
|
+
}: React.PropsWithChildren<BottomTabsDrawerNativeProps>) {
|
|
8
|
+
return <>{React.Children.toArray(children)[1]}</>;
|
|
9
|
+
}
|