@sigmela/router 0.3.7 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +23 -0
- package/LICENSE +2 -1
- package/README.md +108 -479
- package/dist/Router.d.ts +190 -0
- package/dist/Router.d.ts.map +1 -0
- package/dist/components/Modal.d.ts +56 -0
- package/dist/components/Modal.d.ts.map +1 -0
- package/dist/components/OverlayContentScope.d.ts +8 -0
- package/dist/components/OverlayContentScope.d.ts.map +1 -0
- package/dist/components/RouterCloseButton.d.ts +9 -0
- package/dist/components/RouterCloseButton.d.ts.map +1 -0
- package/dist/components/Sheet.d.ts +53 -0
- package/dist/components/Sheet.d.ts.map +1 -0
- package/dist/components/SheetSurface.d.ts +27 -0
- package/dist/components/SheetSurface.d.ts.map +1 -0
- package/dist/components/index.d.ts +4 -0
- package/dist/components/index.d.ts.map +1 -0
- package/dist/components/pageScrollLock.d.ts +3 -0
- package/dist/components/pageScrollLock.d.ts.map +1 -0
- package/dist/components/sheetDragEngine.d.ts +27 -0
- package/dist/components/sheetDragEngine.d.ts.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3649 -0
- package/dist/index.js.map +1 -0
- package/dist/navigationHistory.d.ts +17 -0
- package/dist/navigationHistory.d.ts.map +1 -0
- package/dist/router.css +971 -0
- package/dist/types.d.ts +3 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/utils/id.d.ts +2 -0
- package/dist/utils/id.d.ts.map +1 -0
- package/package.json +69 -174
- package/lib/module/Drawer/Drawer.js +0 -250
- package/lib/module/Drawer/DrawerContext.js +0 -4
- package/lib/module/Drawer/DrawerIcon.web.js +0 -47
- package/lib/module/Drawer/RenderDrawer.native.js +0 -248
- package/lib/module/Drawer/RenderDrawer.web.js +0 -197
- package/lib/module/Drawer/useDrawer.js +0 -11
- package/lib/module/Navigation.js +0 -80
- package/lib/module/NavigationStack.js +0 -254
- package/lib/module/Router.js +0 -2171
- package/lib/module/RouterContext.js +0 -33
- package/lib/module/ScreenStack/ScreenStack.native.js +0 -3
- package/lib/module/ScreenStack/ScreenStack.web.js +0 -561
- package/lib/module/ScreenStack/ScreenStackContext.js +0 -21
- package/lib/module/ScreenStack/animationHelpers.js +0 -87
- package/lib/module/ScreenStack/index.js +0 -3
- package/lib/module/ScreenStackItem/ScreenStackItem.js +0 -70
- package/lib/module/ScreenStackItem/ScreenStackItem.types.js +0 -3
- package/lib/module/ScreenStackItem/ScreenStackItem.web.js +0 -171
- package/lib/module/ScreenStackItem/index.js +0 -3
- package/lib/module/ScreenStackSheetItem/ScreenStackSheetItem.native.js +0 -70
- package/lib/module/ScreenStackSheetItem/ScreenStackSheetItem.web.js +0 -5
- package/lib/module/ScreenStackSheetItem/index.js +0 -3
- package/lib/module/SplitView/RenderSplitView.native.js +0 -101
- package/lib/module/SplitView/RenderSplitView.web.js +0 -107
- package/lib/module/SplitView/SplitView.js +0 -97
- package/lib/module/SplitView/SplitViewContext.js +0 -4
- package/lib/module/SplitView/index.js +0 -5
- package/lib/module/SplitView/useSplitView.js +0 -11
- package/lib/module/StackRenderer.js +0 -42
- package/lib/module/TabBar/RenderTabBar.native.js +0 -418
- package/lib/module/TabBar/RenderTabBar.web.js +0 -218
- package/lib/module/TabBar/TabBar.js +0 -208
- package/lib/module/TabBar/TabBarContext.js +0 -4
- package/lib/module/TabBar/TabIcon.web.js +0 -47
- package/lib/module/TabBar/index.js +0 -6
- package/lib/module/TabBar/useTabBar.js +0 -11
- package/lib/module/TabBar/useTabBarHeight.js +0 -22
- package/lib/module/createController.js +0 -5
- package/lib/module/index.js +0 -15
- package/lib/module/navigationNode.js +0 -3
- package/lib/module/package.json +0 -1
- package/lib/module/styles.css +0 -1111
- package/lib/module/types.js +0 -13
- package/lib/typescript/package.json +0 -1
- package/lib/typescript/src/Drawer/Drawer.d.ts +0 -100
- package/lib/typescript/src/Drawer/DrawerContext.d.ts +0 -3
- package/lib/typescript/src/Drawer/DrawerIcon.web.d.ts +0 -7
- package/lib/typescript/src/Drawer/RenderDrawer.native.d.ts +0 -8
- package/lib/typescript/src/Drawer/RenderDrawer.web.d.ts +0 -8
- package/lib/typescript/src/Drawer/useDrawer.d.ts +0 -2
- package/lib/typescript/src/Navigation.d.ts +0 -17
- package/lib/typescript/src/NavigationStack.d.ts +0 -46
- package/lib/typescript/src/Router.d.ts +0 -232
- package/lib/typescript/src/RouterContext.d.ts +0 -18
- package/lib/typescript/src/ScreenStack/ScreenStack.native.d.ts +0 -2
- package/lib/typescript/src/ScreenStack/ScreenStack.web.d.ts +0 -9
- package/lib/typescript/src/ScreenStack/ScreenStackContext.d.ts +0 -31
- package/lib/typescript/src/ScreenStack/animationHelpers.d.ts +0 -6
- package/lib/typescript/src/ScreenStack/index.d.ts +0 -2
- package/lib/typescript/src/ScreenStackItem/ScreenStackItem.d.ts +0 -3
- package/lib/typescript/src/ScreenStackItem/ScreenStackItem.types.d.ts +0 -14
- package/lib/typescript/src/ScreenStackItem/ScreenStackItem.web.d.ts +0 -3
- package/lib/typescript/src/ScreenStackItem/index.d.ts +0 -3
- package/lib/typescript/src/ScreenStackSheetItem/ScreenStackSheetItem.native.d.ts +0 -11
- package/lib/typescript/src/ScreenStackSheetItem/ScreenStackSheetItem.web.d.ts +0 -2
- package/lib/typescript/src/ScreenStackSheetItem/index.d.ts +0 -2
- package/lib/typescript/src/SplitView/RenderSplitView.native.d.ts +0 -19
- package/lib/typescript/src/SplitView/RenderSplitView.web.d.ts +0 -8
- package/lib/typescript/src/SplitView/SplitView.d.ts +0 -33
- package/lib/typescript/src/SplitView/SplitViewContext.d.ts +0 -3
- package/lib/typescript/src/SplitView/index.d.ts +0 -5
- package/lib/typescript/src/SplitView/useSplitView.d.ts +0 -2
- package/lib/typescript/src/StackRenderer.d.ts +0 -8
- package/lib/typescript/src/TabBar/RenderTabBar.native.d.ts +0 -8
- package/lib/typescript/src/TabBar/RenderTabBar.web.d.ts +0 -8
- package/lib/typescript/src/TabBar/TabBar.d.ts +0 -112
- package/lib/typescript/src/TabBar/TabBarContext.d.ts +0 -3
- package/lib/typescript/src/TabBar/TabIcon.web.d.ts +0 -7
- package/lib/typescript/src/TabBar/index.d.ts +0 -5
- package/lib/typescript/src/TabBar/useTabBar.d.ts +0 -2
- package/lib/typescript/src/TabBar/useTabBarHeight.d.ts +0 -18
- package/lib/typescript/src/createController.d.ts +0 -15
- package/lib/typescript/src/index.d.ts +0 -21
- package/lib/typescript/src/navigationNode.d.ts +0 -41
- package/lib/typescript/src/types.d.ts +0 -140
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
import React from 'react';
|
|
4
|
-
import { RenderSplitView } from './RenderSplitView';
|
|
5
|
-
class SecondaryStackWrapper {
|
|
6
|
-
constructor(stack) {
|
|
7
|
-
this.stack = stack;
|
|
8
|
-
}
|
|
9
|
-
getId() {
|
|
10
|
-
return this.stack.getId();
|
|
11
|
-
}
|
|
12
|
-
getNodeRoutes() {
|
|
13
|
-
// IMPORTANT:
|
|
14
|
-
// - do not mutate routes/options from the original NavigationStack
|
|
15
|
-
// - allowRootPop is used by Router.goBack so that a single-screen secondary can be dismissed
|
|
16
|
-
return this.stack.getNodeRoutes().map(r => ({
|
|
17
|
-
...r,
|
|
18
|
-
options: {
|
|
19
|
-
...(r.options ?? {}),
|
|
20
|
-
allowRootPop: true
|
|
21
|
-
}
|
|
22
|
-
}));
|
|
23
|
-
}
|
|
24
|
-
getNodeChildren() {
|
|
25
|
-
return this.stack.getNodeChildren();
|
|
26
|
-
}
|
|
27
|
-
getRenderer() {
|
|
28
|
-
return this.stack.getRenderer();
|
|
29
|
-
}
|
|
30
|
-
seed() {
|
|
31
|
-
return this.stack.seed?.() ?? null;
|
|
32
|
-
}
|
|
33
|
-
getDefaultOptions() {
|
|
34
|
-
return this.stack.getDefaultOptions?.();
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
export class SplitView {
|
|
38
|
-
_cachedRenderer = null;
|
|
39
|
-
constructor(options) {
|
|
40
|
-
this.splitViewId = `splitview-${Math.random().toString(36).slice(2)}`;
|
|
41
|
-
this.primary = options.primary;
|
|
42
|
-
this.secondary = options.secondary;
|
|
43
|
-
this.minWidth = options.minWidth;
|
|
44
|
-
this.primaryMaxWidth = options.primaryMaxWidth ?? 390;
|
|
45
|
-
this.secondaryWrapper = new SecondaryStackWrapper(this.secondary);
|
|
46
|
-
}
|
|
47
|
-
getId() {
|
|
48
|
-
return this.splitViewId;
|
|
49
|
-
}
|
|
50
|
-
getNodeRoutes() {
|
|
51
|
-
return [];
|
|
52
|
-
}
|
|
53
|
-
getNodeChildren() {
|
|
54
|
-
return [{
|
|
55
|
-
prefix: '',
|
|
56
|
-
node: this.primary
|
|
57
|
-
}, {
|
|
58
|
-
prefix: '',
|
|
59
|
-
node: this.secondaryWrapper
|
|
60
|
-
}];
|
|
61
|
-
}
|
|
62
|
-
getRenderer() {
|
|
63
|
-
if (this._cachedRenderer) {
|
|
64
|
-
return this._cachedRenderer;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
// eslint-disable-next-line consistent-this
|
|
68
|
-
const instance = this;
|
|
69
|
-
const renderer = function SplitViewScreen(props) {
|
|
70
|
-
return /*#__PURE__*/React.createElement(RenderSplitView, {
|
|
71
|
-
splitView: instance,
|
|
72
|
-
appearance: props?.appearance
|
|
73
|
-
});
|
|
74
|
-
};
|
|
75
|
-
this._cachedRenderer = renderer;
|
|
76
|
-
return renderer;
|
|
77
|
-
}
|
|
78
|
-
hasRoute(routeId) {
|
|
79
|
-
return this.primary.getRoutes().some(r => r.routeId === routeId) || this.secondary.getRoutes().some(r => r.routeId === routeId);
|
|
80
|
-
}
|
|
81
|
-
switchToRoute(_routeId) {
|
|
82
|
-
// SplitView does not have a single "active" child like TabBar.
|
|
83
|
-
// It renders both stacks (or overlays secondary on narrow via CSS).
|
|
84
|
-
}
|
|
85
|
-
setActiveChildByRoute(routeId) {
|
|
86
|
-
this.switchToRoute(routeId);
|
|
87
|
-
}
|
|
88
|
-
seed() {
|
|
89
|
-
const firstRoute = this.primary.getFirstRoute();
|
|
90
|
-
if (!firstRoute) return null;
|
|
91
|
-
return {
|
|
92
|
-
routeId: firstRoute.routeId,
|
|
93
|
-
path: firstRoute.path,
|
|
94
|
-
stackId: this.primary.getId()
|
|
95
|
-
};
|
|
96
|
-
}
|
|
97
|
-
}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
import { useContext } from 'react';
|
|
4
|
-
import { SplitViewContext } from "./SplitViewContext.js";
|
|
5
|
-
export const useSplitView = () => {
|
|
6
|
-
const splitView = useContext(SplitViewContext);
|
|
7
|
-
if (!splitView) {
|
|
8
|
-
throw new Error('useSplitView must be used within a SplitViewProvider');
|
|
9
|
-
}
|
|
10
|
-
return splitView;
|
|
11
|
-
};
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
import { memo, useEffect, useState, startTransition } from 'react';
|
|
4
|
-
import { ScreenStackItem } from "./ScreenStackItem/index.js";
|
|
5
|
-
import { ScreenStack } from "./ScreenStack/index.js";
|
|
6
|
-
import { useRouter } from "./RouterContext.js";
|
|
7
|
-
import { StyleSheet } from 'react-native';
|
|
8
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
9
|
-
export const StackRenderer = /*#__PURE__*/memo(({
|
|
10
|
-
stackId,
|
|
11
|
-
appearance,
|
|
12
|
-
history
|
|
13
|
-
}) => {
|
|
14
|
-
const router = useRouter();
|
|
15
|
-
const hasHistoryProp = history != null;
|
|
16
|
-
const [internalHistory, setInternalHistory] = useState(() => hasHistoryProp ? history : router.getStackHistory(stackId));
|
|
17
|
-
useEffect(() => {
|
|
18
|
-
if (hasHistoryProp) return;
|
|
19
|
-
const update = () => {
|
|
20
|
-
startTransition(() => {
|
|
21
|
-
setInternalHistory(router.getStackHistory(stackId));
|
|
22
|
-
});
|
|
23
|
-
};
|
|
24
|
-
const unsub = router.subscribeStack(stackId, update);
|
|
25
|
-
update();
|
|
26
|
-
return unsub;
|
|
27
|
-
}, [router, stackId, hasHistoryProp]);
|
|
28
|
-
const historyForThisStack = hasHistoryProp ? history : internalHistory;
|
|
29
|
-
return /*#__PURE__*/_jsx(ScreenStack, {
|
|
30
|
-
style: [styles.flex, appearance?.screen],
|
|
31
|
-
children: historyForThisStack.map(item => /*#__PURE__*/_jsx(ScreenStackItem, {
|
|
32
|
-
appearance: appearance,
|
|
33
|
-
stackId: stackId,
|
|
34
|
-
item: item
|
|
35
|
-
}, `stack-renderer-${item.key}`))
|
|
36
|
-
}, `stack-${stackId}`);
|
|
37
|
-
});
|
|
38
|
-
const styles = StyleSheet.create({
|
|
39
|
-
flex: {
|
|
40
|
-
flex: 1
|
|
41
|
-
}
|
|
42
|
-
});
|
|
@@ -1,418 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
import { StackRenderer } from "../StackRenderer.js";
|
|
4
|
-
import { TabBarContext } from "./TabBarContext.js";
|
|
5
|
-
import { useRouter } from "../RouterContext.js";
|
|
6
|
-
import { Tabs } from 'react-native-screens';
|
|
7
|
-
import { Platform, StyleSheet, View } from 'react-native';
|
|
8
|
-
import { useCallback, useSyncExternalStore, memo, useEffect, useState, useMemo, startTransition } from 'react';
|
|
9
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
10
|
-
const EMPTY_HISTORY = [];
|
|
11
|
-
const isImageSource = value => {
|
|
12
|
-
if (value == null) return false;
|
|
13
|
-
const valueType = typeof value;
|
|
14
|
-
if (valueType === 'number') return true;
|
|
15
|
-
if (Array.isArray(value)) return value.length > 0;
|
|
16
|
-
if (valueType === 'object') {
|
|
17
|
-
const v = value;
|
|
18
|
-
if ('uri' in v || 'width' in v || 'height' in v) return true;
|
|
19
|
-
// Check for new PlatformIcon format
|
|
20
|
-
if ('ios' in v || 'android' in v || 'shared' in v) return false;
|
|
21
|
-
// Check for legacy format
|
|
22
|
-
if ('sfSymbolName' in v || 'imageSource' in v || 'templateSource' in v) return false;
|
|
23
|
-
// Check for new type-based format
|
|
24
|
-
if ('type' in v) return false;
|
|
25
|
-
}
|
|
26
|
-
return false;
|
|
27
|
-
};
|
|
28
|
-
const isPlatformIcon = value => {
|
|
29
|
-
if (value == null || typeof value !== 'object') return false;
|
|
30
|
-
const v = value;
|
|
31
|
-
return 'ios' in v || 'android' in v || 'shared' in v;
|
|
32
|
-
};
|
|
33
|
-
const isLegacyIOSIcon = value => {
|
|
34
|
-
if (value == null || typeof value !== 'object') return false;
|
|
35
|
-
const v = value;
|
|
36
|
-
return 'sfSymbolName' in v || 'imageSource' in v || 'templateSource' in v;
|
|
37
|
-
};
|
|
38
|
-
const convertLegacyIOSIconToPlatformIconIOS = value => {
|
|
39
|
-
if (!value || typeof value !== 'object') return undefined;
|
|
40
|
-
const v = value;
|
|
41
|
-
if ('sfSymbolName' in v) {
|
|
42
|
-
return {
|
|
43
|
-
type: 'sfSymbol',
|
|
44
|
-
name: v.sfSymbolName
|
|
45
|
-
};
|
|
46
|
-
}
|
|
47
|
-
if ('templateSource' in v) {
|
|
48
|
-
return {
|
|
49
|
-
type: 'templateSource',
|
|
50
|
-
templateSource: v.templateSource
|
|
51
|
-
};
|
|
52
|
-
}
|
|
53
|
-
if ('imageSource' in v) {
|
|
54
|
-
return {
|
|
55
|
-
type: 'imageSource',
|
|
56
|
-
imageSource: v.imageSource
|
|
57
|
-
};
|
|
58
|
-
}
|
|
59
|
-
return undefined;
|
|
60
|
-
};
|
|
61
|
-
const buildIOSIcon = value => {
|
|
62
|
-
if (!value) return undefined;
|
|
63
|
-
|
|
64
|
-
// If it's already a PlatformIcon, extract ios
|
|
65
|
-
if (isPlatformIcon(value)) {
|
|
66
|
-
return value.ios;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
// If it's a legacy format, convert it
|
|
70
|
-
if (isLegacyIOSIcon(value)) {
|
|
71
|
-
return convertLegacyIOSIconToPlatformIconIOS(value);
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
// If it's an ImageSourcePropType, convert to templateSource
|
|
75
|
-
if (isImageSource(value)) {
|
|
76
|
-
return {
|
|
77
|
-
type: 'templateSource',
|
|
78
|
-
templateSource: value
|
|
79
|
-
};
|
|
80
|
-
}
|
|
81
|
-
return undefined;
|
|
82
|
-
};
|
|
83
|
-
const buildPlatformIcon = (icon, selectedIcon) => {
|
|
84
|
-
if (!icon && !selectedIcon) return undefined;
|
|
85
|
-
const iosIcon = buildIOSIcon(icon);
|
|
86
|
-
const iosSelectedIcon = buildIOSIcon(selectedIcon);
|
|
87
|
-
|
|
88
|
-
// If it's already a PlatformIcon, use it directly
|
|
89
|
-
if (isPlatformIcon(icon)) {
|
|
90
|
-
return {
|
|
91
|
-
...icon,
|
|
92
|
-
ios: iosSelectedIcon || icon.ios
|
|
93
|
-
};
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
// Build new PlatformIcon
|
|
97
|
-
const result = {};
|
|
98
|
-
if (iosIcon || iosSelectedIcon) {
|
|
99
|
-
result.ios = iosSelectedIcon || iosIcon;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
// For shared imageSource (works on both platforms)
|
|
103
|
-
if (isImageSource(icon) && !iosIcon) {
|
|
104
|
-
result.shared = {
|
|
105
|
-
type: 'imageSource',
|
|
106
|
-
imageSource: icon
|
|
107
|
-
};
|
|
108
|
-
}
|
|
109
|
-
return Object.keys(result).length > 0 ? result : undefined;
|
|
110
|
-
};
|
|
111
|
-
const getTabIcon = tab => {
|
|
112
|
-
const {
|
|
113
|
-
icon,
|
|
114
|
-
selectedIcon
|
|
115
|
-
} = tab;
|
|
116
|
-
if (!icon && !selectedIcon) return undefined;
|
|
117
|
-
|
|
118
|
-
// Build PlatformIcon for new API
|
|
119
|
-
const platformIcon = buildPlatformIcon(icon, selectedIcon);
|
|
120
|
-
if (!platformIcon) return undefined;
|
|
121
|
-
|
|
122
|
-
// For Android, if icon is a direct ImageSourcePropType, use shared
|
|
123
|
-
if (Platform.OS === 'android' && isImageSource(icon) && !platformIcon.shared) {
|
|
124
|
-
platformIcon.shared = {
|
|
125
|
-
type: 'imageSource',
|
|
126
|
-
imageSource: icon
|
|
127
|
-
};
|
|
128
|
-
}
|
|
129
|
-
return {
|
|
130
|
-
icon: platformIcon,
|
|
131
|
-
selectedIcon: platformIcon.ios
|
|
132
|
-
};
|
|
133
|
-
};
|
|
134
|
-
const TabStackRenderer = /*#__PURE__*/memo(({
|
|
135
|
-
stack,
|
|
136
|
-
appearance
|
|
137
|
-
}) => {
|
|
138
|
-
const router = useRouter();
|
|
139
|
-
const stackId = stack.getId();
|
|
140
|
-
|
|
141
|
-
// Start with empty history — useEffect populates via startTransition.
|
|
142
|
-
// This avoids mounting ScreenStackItems before parent Fragments attach.
|
|
143
|
-
const [history, setHistory] = useState(EMPTY_HISTORY);
|
|
144
|
-
useEffect(() => {
|
|
145
|
-
const update = () => {
|
|
146
|
-
startTransition(() => {
|
|
147
|
-
setHistory(router.getStackHistory(stackId));
|
|
148
|
-
});
|
|
149
|
-
};
|
|
150
|
-
const unsub = router.subscribeStack(stackId, update);
|
|
151
|
-
update();
|
|
152
|
-
return unsub;
|
|
153
|
-
}, [router, stackId]);
|
|
154
|
-
return /*#__PURE__*/_jsx(StackRenderer, {
|
|
155
|
-
appearance: appearance,
|
|
156
|
-
stackId: stackId,
|
|
157
|
-
history: history
|
|
158
|
-
});
|
|
159
|
-
});
|
|
160
|
-
TabStackRenderer.displayName = 'TabStackRenderer';
|
|
161
|
-
const TabNodeRenderer = /*#__PURE__*/memo(({
|
|
162
|
-
node,
|
|
163
|
-
appearance
|
|
164
|
-
}) => {
|
|
165
|
-
const Renderer = useMemo(() => node.getRenderer(), [node]);
|
|
166
|
-
return /*#__PURE__*/_jsx(Renderer, {
|
|
167
|
-
appearance: appearance
|
|
168
|
-
});
|
|
169
|
-
});
|
|
170
|
-
TabNodeRenderer.displayName = 'TabNodeRenderer';
|
|
171
|
-
export const RenderTabBar = /*#__PURE__*/memo(({
|
|
172
|
-
tabBar,
|
|
173
|
-
appearance = {}
|
|
174
|
-
}) => {
|
|
175
|
-
const router = useRouter();
|
|
176
|
-
const subscribe = useCallback(cb => tabBar.subscribe(cb), [tabBar]);
|
|
177
|
-
const snapshot = useSyncExternalStore(subscribe, tabBar.getState, tabBar.getState);
|
|
178
|
-
const {
|
|
179
|
-
tabs,
|
|
180
|
-
index,
|
|
181
|
-
config
|
|
182
|
-
} = snapshot;
|
|
183
|
-
|
|
184
|
-
// Local focus state driven by startTransition so that isFocused changes
|
|
185
|
-
// are deferred until the native Fragment lifecycle completes (Android Fabric).
|
|
186
|
-
const [focusedTabKey, setFocusedTabKey] = useState(() => tabs[index]?.tabKey);
|
|
187
|
-
|
|
188
|
-
// Sync focusedTabKey when index changes from external sources (programmatic navigation).
|
|
189
|
-
useEffect(() => {
|
|
190
|
-
const key = tabs[index]?.tabKey;
|
|
191
|
-
if (key && key !== focusedTabKey) {
|
|
192
|
-
startTransition(() => {
|
|
193
|
-
setFocusedTabKey(key);
|
|
194
|
-
});
|
|
195
|
-
}
|
|
196
|
-
}, [tabs, index, focusedTabKey]);
|
|
197
|
-
const {
|
|
198
|
-
iconColor,
|
|
199
|
-
iconColorActive,
|
|
200
|
-
androidActiveIndicatorEnabled,
|
|
201
|
-
androidActiveIndicatorColor,
|
|
202
|
-
androidRippleColor,
|
|
203
|
-
labelVisibilityMode,
|
|
204
|
-
title,
|
|
205
|
-
backgroundColor,
|
|
206
|
-
badgeBackgroundColor,
|
|
207
|
-
iOSShadowColor,
|
|
208
|
-
hidden,
|
|
209
|
-
tintColor,
|
|
210
|
-
controllerMode,
|
|
211
|
-
minimizeBehavior,
|
|
212
|
-
nativeContainerBackgroundColor,
|
|
213
|
-
iOSBlurEffect
|
|
214
|
-
} = appearance?.tabBar ?? {};
|
|
215
|
-
const onNativeFocusChange = useCallback(event => {
|
|
216
|
-
const tabKey = event.nativeEvent.tabKey;
|
|
217
|
-
const tabIndex = tabs.findIndex(route => route.tabKey === tabKey);
|
|
218
|
-
if (tabIndex === -1) return;
|
|
219
|
-
|
|
220
|
-
// Defer isFocused update so the native Fragment has time to attach.
|
|
221
|
-
startTransition(() => {
|
|
222
|
-
setFocusedTabKey(tabKey);
|
|
223
|
-
});
|
|
224
|
-
if (tabIndex !== index) {
|
|
225
|
-
tabBar.onIndexChange(tabIndex);
|
|
226
|
-
}
|
|
227
|
-
}, [tabs, tabBar, index]);
|
|
228
|
-
const seedTabIfNeeded = useCallback(targetTab => {
|
|
229
|
-
if (!targetTab) return;
|
|
230
|
-
const targetStack = tabBar.stacks[targetTab.tabKey];
|
|
231
|
-
const targetNode = tabBar.nodes[targetTab.tabKey];
|
|
232
|
-
if (targetStack) {
|
|
233
|
-
const stackId = targetStack.getId();
|
|
234
|
-
const stackHistory = router.getStackHistory(stackId);
|
|
235
|
-
if (stackHistory.length === 0) {
|
|
236
|
-
const firstRoute = targetStack.getFirstRoute();
|
|
237
|
-
if (firstRoute?.path) {
|
|
238
|
-
router.navigate(firstRoute.path);
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
return;
|
|
242
|
-
}
|
|
243
|
-
if (targetNode) {
|
|
244
|
-
const nodeId = targetNode.getId?.();
|
|
245
|
-
if (!nodeId) return;
|
|
246
|
-
const nodeHistory = router.getStackHistory(nodeId);
|
|
247
|
-
if (nodeHistory.length === 0) {
|
|
248
|
-
const seed = targetNode.seed?.();
|
|
249
|
-
if (seed?.path) {
|
|
250
|
-
const prefix = targetTab.tabPrefix ?? '';
|
|
251
|
-
const fullPath = prefix && !seed.path.startsWith(prefix) ? `${prefix}${seed.path.startsWith('/') ? '' : '/'}${seed.path}` : seed.path;
|
|
252
|
-
router.navigate(fullPath);
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
}, [tabBar.nodes, tabBar.stacks, router]);
|
|
257
|
-
useEffect(() => {
|
|
258
|
-
seedTabIfNeeded(tabs[index]);
|
|
259
|
-
}, [tabs, index, seedTabIfNeeded]);
|
|
260
|
-
const onTabPress = useCallback(nextIndex => {
|
|
261
|
-
const targetTab = tabs[nextIndex];
|
|
262
|
-
if (!targetTab) return;
|
|
263
|
-
|
|
264
|
-
// Update TabBar UI state
|
|
265
|
-
if (nextIndex !== index) {
|
|
266
|
-
tabBar.onIndexChange(nextIndex);
|
|
267
|
-
}
|
|
268
|
-
seedTabIfNeeded(targetTab);
|
|
269
|
-
}, [tabs, tabBar, index, seedTabIfNeeded]);
|
|
270
|
-
const containerProps = useMemo(() => ({
|
|
271
|
-
tabBarBackgroundColor: backgroundColor,
|
|
272
|
-
tabBarItemTitleFontFamily: title?.fontFamily,
|
|
273
|
-
tabBarItemTitleFontSize: title?.fontSize,
|
|
274
|
-
tabBarItemTitleFontSizeActive: title?.activeFontSize,
|
|
275
|
-
tabBarItemTitleFontWeight: title?.fontWeight,
|
|
276
|
-
tabBarItemTitleFontStyle: title?.fontStyle,
|
|
277
|
-
tabBarItemTitleFontColor: title?.color,
|
|
278
|
-
tabBarItemTitleFontColorActive: title?.activeColor,
|
|
279
|
-
tabBarItemIconColor: iconColor,
|
|
280
|
-
tabBarItemIconColorActive: iconColorActive,
|
|
281
|
-
tabBarItemActiveIndicatorColor: androidActiveIndicatorColor,
|
|
282
|
-
tabBarItemActiveIndicatorEnabled: androidActiveIndicatorEnabled,
|
|
283
|
-
tabBarItemRippleColor: androidRippleColor,
|
|
284
|
-
tabBarItemLabelVisibilityMode: labelVisibilityMode,
|
|
285
|
-
tabBarHidden: hidden,
|
|
286
|
-
tabBarTintColor: tintColor,
|
|
287
|
-
tabBarControllerMode: controllerMode,
|
|
288
|
-
tabBarMinimizeBehavior: minimizeBehavior,
|
|
289
|
-
nativeContainerStyle: nativeContainerBackgroundColor ? {
|
|
290
|
-
backgroundColor: nativeContainerBackgroundColor
|
|
291
|
-
} : undefined
|
|
292
|
-
}), [backgroundColor, title?.fontFamily, title?.fontSize, title?.activeFontSize, title?.fontWeight, title?.fontStyle, title?.color, title?.activeColor, iconColor, iconColorActive, androidActiveIndicatorColor, androidActiveIndicatorEnabled, androidRippleColor, labelVisibilityMode, hidden, tintColor, controllerMode, minimizeBehavior, nativeContainerBackgroundColor]);
|
|
293
|
-
const iosNormalState = useMemo(() => ({
|
|
294
|
-
tabBarItemTitleFontFamily: title?.fontFamily,
|
|
295
|
-
tabBarItemTitleFontSize: title?.fontSize,
|
|
296
|
-
tabBarItemTitleFontWeight: title?.fontWeight,
|
|
297
|
-
tabBarItemTitleFontStyle: title?.fontStyle,
|
|
298
|
-
tabBarItemTitleFontColor: title?.color,
|
|
299
|
-
tabBarItemBadgeBackgroundColor: badgeBackgroundColor,
|
|
300
|
-
tabBarItemIconColor: iconColor
|
|
301
|
-
}), [title?.fontFamily, title?.fontSize, title?.fontWeight, title?.fontStyle, title?.color, badgeBackgroundColor, iconColor]);
|
|
302
|
-
const iosSelectedState = useMemo(() => ({
|
|
303
|
-
tabBarItemTitleFontFamily: title?.fontFamily,
|
|
304
|
-
tabBarItemTitleFontSize: title?.fontSize,
|
|
305
|
-
tabBarItemTitleFontWeight: title?.fontWeight,
|
|
306
|
-
tabBarItemTitleFontStyle: title?.fontStyle,
|
|
307
|
-
tabBarItemTitleFontColor: title?.activeColor ?? title?.color,
|
|
308
|
-
tabBarItemBadgeBackgroundColor: badgeBackgroundColor,
|
|
309
|
-
tabBarItemIconColor: iconColorActive ?? iconColor
|
|
310
|
-
}), [title?.fontFamily, title?.fontSize, title?.fontWeight, title?.fontStyle, title?.color, title?.activeColor, badgeBackgroundColor, iconColorActive, iconColor]);
|
|
311
|
-
const iosAppearance = useMemo(() => Platform.select({
|
|
312
|
-
default: undefined,
|
|
313
|
-
ios: {
|
|
314
|
-
tabBarBackgroundColor: backgroundColor,
|
|
315
|
-
tabBarShadowColor: iOSShadowColor,
|
|
316
|
-
tabBarBlurEffect: iOSBlurEffect,
|
|
317
|
-
compactInline: {
|
|
318
|
-
normal: iosNormalState,
|
|
319
|
-
selected: iosSelectedState
|
|
320
|
-
},
|
|
321
|
-
stacked: {
|
|
322
|
-
normal: iosNormalState,
|
|
323
|
-
selected: iosSelectedState
|
|
324
|
-
},
|
|
325
|
-
inline: {
|
|
326
|
-
normal: iosNormalState,
|
|
327
|
-
selected: iosSelectedState
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
}), [backgroundColor, iOSShadowColor, iOSBlurEffect, iosNormalState, iosSelectedState]);
|
|
331
|
-
const CustomTabBar = config.component;
|
|
332
|
-
const tabIcons = useMemo(() => tabs.map(tab => getTabIcon(tab)), [tabs]);
|
|
333
|
-
const [visited, setVisited] = useState({});
|
|
334
|
-
useEffect(() => {
|
|
335
|
-
const key = tabs[index]?.tabKey;
|
|
336
|
-
if (key) {
|
|
337
|
-
startTransition(() => {
|
|
338
|
-
setVisited(prev => prev[key] ? prev : {
|
|
339
|
-
...prev,
|
|
340
|
-
[key]: true
|
|
341
|
-
});
|
|
342
|
-
});
|
|
343
|
-
}
|
|
344
|
-
}, [tabs, index]);
|
|
345
|
-
if (CustomTabBar) {
|
|
346
|
-
return /*#__PURE__*/_jsxs(TabBarContext.Provider, {
|
|
347
|
-
value: tabBar,
|
|
348
|
-
children: [/*#__PURE__*/_jsx(View, {
|
|
349
|
-
style: styles.flex,
|
|
350
|
-
children: tabs.filter(t => visited[t.tabKey]).map(tab => {
|
|
351
|
-
const isActive = tab.tabKey === tabs[index]?.tabKey;
|
|
352
|
-
const stackForTab = tabBar.stacks[tab.tabKey];
|
|
353
|
-
const nodeForTab = tabBar.nodes[tab.tabKey];
|
|
354
|
-
const ScreenForTab = tabBar.screens[tab.tabKey];
|
|
355
|
-
return /*#__PURE__*/_jsx(View, {
|
|
356
|
-
style: [styles.flex, !isActive && styles.hidden],
|
|
357
|
-
children: stackForTab ? /*#__PURE__*/_jsx(TabStackRenderer, {
|
|
358
|
-
appearance: appearance,
|
|
359
|
-
stack: stackForTab
|
|
360
|
-
}) : nodeForTab ? /*#__PURE__*/_jsx(TabNodeRenderer, {
|
|
361
|
-
appearance: appearance,
|
|
362
|
-
node: nodeForTab
|
|
363
|
-
}) : ScreenForTab ? /*#__PURE__*/_jsx(ScreenForTab, {}) : null
|
|
364
|
-
}, `tab-content-${tab.tabKey}`);
|
|
365
|
-
})
|
|
366
|
-
}), /*#__PURE__*/_jsx(CustomTabBar, {
|
|
367
|
-
onTabPress: onTabPress,
|
|
368
|
-
activeIndex: index,
|
|
369
|
-
tabs: tabs
|
|
370
|
-
})]
|
|
371
|
-
});
|
|
372
|
-
}
|
|
373
|
-
return /*#__PURE__*/_jsx(TabBarContext.Provider, {
|
|
374
|
-
value: tabBar,
|
|
375
|
-
children: /*#__PURE__*/_jsx(Tabs.Host, {
|
|
376
|
-
onNativeFocusChange: onNativeFocusChange,
|
|
377
|
-
bottomAccessory: config.bottomAccessory,
|
|
378
|
-
experimentalControlNavigationStateInJS: config.experimentalControlNavigationStateInJS,
|
|
379
|
-
...containerProps,
|
|
380
|
-
children: tabs.map((tab, i) => {
|
|
381
|
-
const isFocused = tab.tabKey === focusedTabKey;
|
|
382
|
-
const stack = tabBar.stacks[tab.tabKey];
|
|
383
|
-
const node = tabBar.nodes[tab.tabKey];
|
|
384
|
-
const Screen = tabBar.screens[tab.tabKey];
|
|
385
|
-
const convertedIcon = tabIcons[i];
|
|
386
|
-
const {
|
|
387
|
-
icon: _icon,
|
|
388
|
-
selectedIcon: _selectedIcon,
|
|
389
|
-
tabPrefix: _prefix,
|
|
390
|
-
...tabScreenProps
|
|
391
|
-
} = tab;
|
|
392
|
-
return /*#__PURE__*/_jsx(Tabs.Screen, {
|
|
393
|
-
...tabScreenProps,
|
|
394
|
-
scrollEdgeAppearance: iosAppearance,
|
|
395
|
-
standardAppearance: iosAppearance,
|
|
396
|
-
isFocused: isFocused,
|
|
397
|
-
icon: convertedIcon?.icon,
|
|
398
|
-
selectedIcon: convertedIcon?.selectedIcon,
|
|
399
|
-
children: stack ? /*#__PURE__*/_jsx(TabStackRenderer, {
|
|
400
|
-
appearance: appearance,
|
|
401
|
-
stack: stack
|
|
402
|
-
}) : node ? /*#__PURE__*/_jsx(TabNodeRenderer, {
|
|
403
|
-
appearance: appearance,
|
|
404
|
-
node: node
|
|
405
|
-
}) : Screen ? /*#__PURE__*/_jsx(Screen, {}) : null
|
|
406
|
-
}, tab.tabKey);
|
|
407
|
-
})
|
|
408
|
-
})
|
|
409
|
-
});
|
|
410
|
-
});
|
|
411
|
-
const styles = StyleSheet.create({
|
|
412
|
-
flex: {
|
|
413
|
-
flex: 1
|
|
414
|
-
},
|
|
415
|
-
hidden: {
|
|
416
|
-
display: 'none'
|
|
417
|
-
}
|
|
418
|
-
});
|