@sigmela/router 0.0.14 → 0.0.15
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/lib/module/Navigation.js +10 -16
- package/lib/module/Router.js +299 -52
- package/lib/module/ScreenStack/ScreenStack.native.js +3 -0
- package/lib/module/ScreenStack/ScreenStack.web.js +139 -0
- package/lib/module/ScreenStack/index.js +3 -0
- package/lib/module/{ScreenStackItem.js → ScreenStackItem/ScreenStackItem.js} +2 -2
- package/lib/module/ScreenStackItem/ScreenStackItem.types.js +3 -0
- package/lib/module/ScreenStackItem/ScreenStackItem.web.js +38 -0
- package/lib/module/ScreenStackItem/index.js +3 -0
- package/lib/module/StackRenderer.js +5 -5
- package/lib/module/TabBar/RenderTabBar.native.js +224 -0
- package/lib/module/TabBar/RenderTabBar.web.js +130 -0
- package/lib/module/TabBar/TabIcon.web.js +42 -0
- package/lib/module/TabBar/index.js +3 -0
- package/lib/module/styles.css +139 -0
- package/lib/module/web/TransitionStack.js +227 -0
- package/lib/typescript/src/Navigation.d.ts +2 -1
- package/lib/typescript/src/Router.d.ts +15 -1
- package/lib/typescript/src/ScreenStack/ScreenStack.native.d.ts +2 -0
- package/lib/typescript/src/ScreenStack/ScreenStack.web.d.ts +11 -0
- package/lib/typescript/src/ScreenStack/index.d.ts +2 -0
- package/lib/typescript/src/ScreenStackItem/ScreenStackItem.d.ts +3 -0
- package/lib/typescript/src/ScreenStackItem/ScreenStackItem.types.d.ts +10 -0
- package/lib/typescript/src/ScreenStackItem/ScreenStackItem.web.d.ts +3 -0
- package/lib/typescript/src/ScreenStackItem/index.d.ts +3 -0
- package/lib/typescript/src/TabBar/RenderTabBar.native.d.ts +8 -0
- package/lib/typescript/src/TabBar/{RenderTabBar.d.ts → RenderTabBar.web.d.ts} +1 -1
- package/lib/typescript/src/TabBar/TabBar.d.ts +11 -3
- package/lib/typescript/src/TabBar/TabIcon.web.d.ts +7 -0
- package/lib/typescript/src/TabBar/index.d.ts +2 -0
- package/lib/typescript/src/index.d.ts +1 -1
- package/lib/typescript/src/types.d.ts +24 -165
- package/lib/typescript/src/web/TransitionStack.d.ts +21 -0
- package/package.json +3 -2
- package/lib/module/TabBar/RenderTabBar.js +0 -123
- package/lib/typescript/src/ScreenStackItem.d.ts +0 -10
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
3
|
import { memo, useCallback, useSyncExternalStore } from 'react';
|
|
4
|
-
import { ScreenStackItem } from "./ScreenStackItem.js";
|
|
5
|
-
import { ScreenStack } from
|
|
4
|
+
import { ScreenStackItem } from "./ScreenStackItem/index.js";
|
|
5
|
+
import { ScreenStack } from "./ScreenStack/index.js";
|
|
6
6
|
import { useRouter } from "./RouterContext.js";
|
|
7
7
|
import { StyleSheet } from 'react-native';
|
|
8
8
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
@@ -16,13 +16,13 @@ export const StackRenderer = /*#__PURE__*/memo(({
|
|
|
16
16
|
const get = useCallback(() => router.getStackHistory(stackId), [router, stackId]);
|
|
17
17
|
const historyForThisStack = useSyncExternalStore(subscribe, get, get);
|
|
18
18
|
return /*#__PURE__*/_jsx(ScreenStack, {
|
|
19
|
-
style: [styles.flex, appearance?.
|
|
19
|
+
style: [styles.flex, appearance?.screen],
|
|
20
20
|
children: historyForThisStack.map(item => /*#__PURE__*/_jsx(ScreenStackItem, {
|
|
21
21
|
appearance: appearance,
|
|
22
22
|
stackId: stackId,
|
|
23
23
|
item: item
|
|
24
|
-
}, item.key))
|
|
25
|
-
});
|
|
24
|
+
}, `stack-renderer-${item.key}`))
|
|
25
|
+
}, `stack-${stackId}`);
|
|
26
26
|
});
|
|
27
27
|
const styles = StyleSheet.create({
|
|
28
28
|
flex: {
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import { StackRenderer } from "../StackRenderer.js";
|
|
4
|
+
import { TabBarContext } from "./TabBarContext.js";
|
|
5
|
+
import { useRouter } from "../RouterContext.js";
|
|
6
|
+
import { BottomTabsScreen, BottomTabs, ScreenStackItem } from 'react-native-screens';
|
|
7
|
+
import { Platform, StyleSheet, View } from 'react-native';
|
|
8
|
+
import { useCallback, useSyncExternalStore, memo, useEffect, useState } from 'react';
|
|
9
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
10
|
+
// Helpers outside render to avoid re-creation
|
|
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
|
+
if ('sfSymbolName' in v || 'imageSource' in v || 'templateSource' in v) return false;
|
|
20
|
+
}
|
|
21
|
+
return false;
|
|
22
|
+
};
|
|
23
|
+
const isRNSIcon = value => {
|
|
24
|
+
if (value == null || typeof value !== 'object') return false;
|
|
25
|
+
const v = value;
|
|
26
|
+
return 'sfSymbolName' in v || 'imageSource' in v || 'templateSource' in v;
|
|
27
|
+
};
|
|
28
|
+
const buildIOSIcon = value => {
|
|
29
|
+
if (!value) return undefined;
|
|
30
|
+
if (isRNSIcon(value)) return value;
|
|
31
|
+
return {
|
|
32
|
+
templateSource: value
|
|
33
|
+
};
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
// Map unified tab icon props to RNS BottomTabsScreen platform-specific props
|
|
37
|
+
const getTabIcon = tab => {
|
|
38
|
+
const {
|
|
39
|
+
icon,
|
|
40
|
+
selectedIcon
|
|
41
|
+
} = tab;
|
|
42
|
+
if (icon || selectedIcon) {
|
|
43
|
+
if (Platform.OS === 'android' && isImageSource(icon)) {
|
|
44
|
+
return {
|
|
45
|
+
iconResource: icon
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
return {
|
|
49
|
+
selectedIcon: buildIOSIcon(selectedIcon),
|
|
50
|
+
icon: buildIOSIcon(icon)
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
return undefined;
|
|
54
|
+
};
|
|
55
|
+
export const RenderTabBar = /*#__PURE__*/memo(({
|
|
56
|
+
tabBar,
|
|
57
|
+
appearance = {}
|
|
58
|
+
}) => {
|
|
59
|
+
const router = useRouter();
|
|
60
|
+
const subscribe = useCallback(cb => tabBar.subscribe(cb), [tabBar]);
|
|
61
|
+
const snapshot = useSyncExternalStore(subscribe, tabBar.getState, tabBar.getState);
|
|
62
|
+
const {
|
|
63
|
+
tabs,
|
|
64
|
+
index,
|
|
65
|
+
config
|
|
66
|
+
} = snapshot;
|
|
67
|
+
const {
|
|
68
|
+
iconColor,
|
|
69
|
+
iconColorActive,
|
|
70
|
+
androidActiveIndicatorEnabled,
|
|
71
|
+
androidActiveIndicatorColor,
|
|
72
|
+
androidRippleColor,
|
|
73
|
+
labelVisibilityMode,
|
|
74
|
+
title,
|
|
75
|
+
backgroundColor,
|
|
76
|
+
badgeBackgroundColor,
|
|
77
|
+
iOSShadowColor
|
|
78
|
+
} = appearance?.tabBar ?? {};
|
|
79
|
+
useEffect(() => {
|
|
80
|
+
router.ensureTabSeed(index);
|
|
81
|
+
}, [index, router]);
|
|
82
|
+
const onNativeFocusChange = useCallback(event => {
|
|
83
|
+
const tabKey = event.nativeEvent.tabKey;
|
|
84
|
+
const tabIndex = tabs.findIndex(route => route.tabKey === tabKey);
|
|
85
|
+
router.onTabIndexChange(tabIndex);
|
|
86
|
+
}, [tabs, router]);
|
|
87
|
+
const onTabPress = useCallback(nextIndex => {
|
|
88
|
+
router.onTabIndexChange(nextIndex);
|
|
89
|
+
}, [router]);
|
|
90
|
+
const containerProps = {
|
|
91
|
+
tabBarBackgroundColor: backgroundColor,
|
|
92
|
+
tabBarItemTitleFontFamily: title?.fontFamily,
|
|
93
|
+
tabBarItemTitleFontSize: title?.fontSize,
|
|
94
|
+
tabBarItemTitleFontWeight: title?.fontWeight,
|
|
95
|
+
tabBarItemTitleFontStyle: title?.fontStyle,
|
|
96
|
+
tabBarItemTitleFontColor: title?.color,
|
|
97
|
+
tabBarItemTitleFontColorActive: title?.activeColor,
|
|
98
|
+
tabBarItemIconColor: iconColor,
|
|
99
|
+
tabBarItemIconColorActive: iconColorActive,
|
|
100
|
+
tabBarItemActiveIndicatorColor: androidActiveIndicatorColor,
|
|
101
|
+
tabBarItemActiveIndicatorEnabled: androidActiveIndicatorEnabled,
|
|
102
|
+
tabBarItemRippleColor: androidRippleColor,
|
|
103
|
+
tabBarItemLabelVisibilityMode: labelVisibilityMode
|
|
104
|
+
};
|
|
105
|
+
const iosState = {
|
|
106
|
+
tabBarItemTitleFontFamily: title?.fontFamily,
|
|
107
|
+
tabBarItemTitleFontSize: title?.fontSize,
|
|
108
|
+
tabBarItemTitleFontWeight: title?.fontWeight,
|
|
109
|
+
tabBarItemTitleFontStyle: title?.fontStyle,
|
|
110
|
+
tabBarItemTitleFontColor: title?.color,
|
|
111
|
+
tabBarItemBadgeBackgroundColor: badgeBackgroundColor,
|
|
112
|
+
tabBarItemTitleFontColorActive: title?.color,
|
|
113
|
+
tabBarItemIconColorActive: iconColorActive,
|
|
114
|
+
tabBarItemIconColor: iconColor
|
|
115
|
+
};
|
|
116
|
+
const iosAppearance = Platform.select({
|
|
117
|
+
default: undefined,
|
|
118
|
+
ios: {
|
|
119
|
+
tabBarBackgroundColor: backgroundColor,
|
|
120
|
+
tabBarShadowColor: iOSShadowColor,
|
|
121
|
+
compactInline: {
|
|
122
|
+
normal: iosState
|
|
123
|
+
},
|
|
124
|
+
stacked: {
|
|
125
|
+
normal: iosState
|
|
126
|
+
},
|
|
127
|
+
inline: {
|
|
128
|
+
normal: iosState
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
// If a custom component is provided, render it instead of default native BottomTabs
|
|
134
|
+
const CustomTabBar = config.component;
|
|
135
|
+
|
|
136
|
+
// Track visited tabs to lazily mount on first visit and keep mounted afterwards
|
|
137
|
+
const [visited, setVisited] = useState({});
|
|
138
|
+
useEffect(() => {
|
|
139
|
+
const key = tabs[index]?.tabKey;
|
|
140
|
+
if (key) {
|
|
141
|
+
setVisited(prev => prev[key] ? prev : {
|
|
142
|
+
...prev,
|
|
143
|
+
[key]: true
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
}, [tabs, index]);
|
|
147
|
+
if (CustomTabBar) {
|
|
148
|
+
return /*#__PURE__*/_jsx(ScreenStackItem, {
|
|
149
|
+
screenId: "root-tabbar",
|
|
150
|
+
headerConfig: {
|
|
151
|
+
hidden: true
|
|
152
|
+
},
|
|
153
|
+
style: StyleSheet.absoluteFill,
|
|
154
|
+
stackAnimation: "slide_from_right",
|
|
155
|
+
children: /*#__PURE__*/_jsxs(TabBarContext.Provider, {
|
|
156
|
+
value: tabBar,
|
|
157
|
+
children: [/*#__PURE__*/_jsx(View, {
|
|
158
|
+
style: styles.flex,
|
|
159
|
+
children: tabs.filter(t => visited[t.tabKey]).map(tab => {
|
|
160
|
+
const isActive = tab.tabKey === tabs[index]?.tabKey;
|
|
161
|
+
const stackForTab = tabBar.stacks[tab.tabKey];
|
|
162
|
+
const ScreenForTab = tabBar.screens[tab.tabKey];
|
|
163
|
+
return /*#__PURE__*/_jsx(View, {
|
|
164
|
+
style: [styles.flex, !isActive && styles.hidden],
|
|
165
|
+
children: stackForTab ? /*#__PURE__*/_jsx(StackRenderer, {
|
|
166
|
+
appearance: appearance,
|
|
167
|
+
stack: stackForTab
|
|
168
|
+
}) : ScreenForTab ? /*#__PURE__*/_jsx(ScreenForTab, {}) : null
|
|
169
|
+
}, `tab-content-${tab.tabKey}`);
|
|
170
|
+
})
|
|
171
|
+
}), /*#__PURE__*/_jsx(CustomTabBar, {
|
|
172
|
+
onTabPress: onTabPress,
|
|
173
|
+
activeIndex: index,
|
|
174
|
+
tabs: tabs
|
|
175
|
+
})]
|
|
176
|
+
})
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
return /*#__PURE__*/_jsx(ScreenStackItem, {
|
|
180
|
+
screenId: "root-tabbar",
|
|
181
|
+
headerConfig: {
|
|
182
|
+
hidden: true
|
|
183
|
+
},
|
|
184
|
+
style: StyleSheet.absoluteFill,
|
|
185
|
+
stackAnimation: "slide_from_right",
|
|
186
|
+
children: /*#__PURE__*/_jsx(TabBarContext.Provider, {
|
|
187
|
+
value: tabBar,
|
|
188
|
+
children: /*#__PURE__*/_jsx(BottomTabs, {
|
|
189
|
+
onNativeFocusChange: onNativeFocusChange,
|
|
190
|
+
...containerProps,
|
|
191
|
+
children: tabs.map(tab => {
|
|
192
|
+
const isFocused = tab.tabKey === tabs[index]?.tabKey;
|
|
193
|
+
const stack = tabBar.stacks[tab.tabKey];
|
|
194
|
+
const Screen = tabBar.screens[tab.tabKey];
|
|
195
|
+
const icon = getTabIcon(tab);
|
|
196
|
+
return /*#__PURE__*/_jsx(BottomTabsScreen, {
|
|
197
|
+
scrollEdgeAppearance: iosAppearance,
|
|
198
|
+
standardAppearance: iosAppearance,
|
|
199
|
+
isFocused: isFocused,
|
|
200
|
+
tabKey: tab.tabKey,
|
|
201
|
+
title: tab.title,
|
|
202
|
+
badgeValue: tab.badgeValue,
|
|
203
|
+
specialEffects: tab.specialEffects,
|
|
204
|
+
selectedIcon: icon?.selectedIcon,
|
|
205
|
+
iconResource: icon?.iconResource,
|
|
206
|
+
icon: icon?.icon,
|
|
207
|
+
children: stack ? /*#__PURE__*/_jsx(StackRenderer, {
|
|
208
|
+
appearance: appearance,
|
|
209
|
+
stack: stack
|
|
210
|
+
}) : Screen ? /*#__PURE__*/_jsx(Screen, {}) : null
|
|
211
|
+
}, tab.tabKey);
|
|
212
|
+
})
|
|
213
|
+
})
|
|
214
|
+
})
|
|
215
|
+
});
|
|
216
|
+
});
|
|
217
|
+
const styles = StyleSheet.create({
|
|
218
|
+
flex: {
|
|
219
|
+
flex: 1
|
|
220
|
+
},
|
|
221
|
+
hidden: {
|
|
222
|
+
display: 'none'
|
|
223
|
+
}
|
|
224
|
+
});
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import { StackRenderer } from "../StackRenderer.js";
|
|
4
|
+
import { TabBarContext } from "./TabBarContext.js";
|
|
5
|
+
import { useRouter } from "../RouterContext.js";
|
|
6
|
+
import { TabIcon } from './TabIcon';
|
|
7
|
+
import { useCallback, useSyncExternalStore, memo, useEffect, useMemo } from 'react';
|
|
8
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
9
|
+
const isImageSource = value => {
|
|
10
|
+
if (value == null) return false;
|
|
11
|
+
const valueType = typeof value;
|
|
12
|
+
if (valueType === 'number') return true;
|
|
13
|
+
if (Array.isArray(value)) return value.length > 0;
|
|
14
|
+
if (valueType === 'object') {
|
|
15
|
+
const v = value;
|
|
16
|
+
if ('uri' in v || 'width' in v || 'height' in v) return true;
|
|
17
|
+
}
|
|
18
|
+
return false;
|
|
19
|
+
};
|
|
20
|
+
const toColorString = c => typeof c === 'string' ? c : undefined;
|
|
21
|
+
|
|
22
|
+
//
|
|
23
|
+
|
|
24
|
+
export const RenderTabBar = /*#__PURE__*/memo(({
|
|
25
|
+
tabBar,
|
|
26
|
+
appearance
|
|
27
|
+
}) => {
|
|
28
|
+
const router = useRouter();
|
|
29
|
+
const subscribe = useCallback(cb => tabBar.subscribe(cb), [tabBar]);
|
|
30
|
+
const snapshot = useSyncExternalStore(subscribe, tabBar.getState, tabBar.getState);
|
|
31
|
+
const {
|
|
32
|
+
tabs,
|
|
33
|
+
index,
|
|
34
|
+
config
|
|
35
|
+
} = snapshot;
|
|
36
|
+
useEffect(() => {
|
|
37
|
+
router.ensureTabSeed(index);
|
|
38
|
+
}, [index, router]);
|
|
39
|
+
const focusedTab = tabs[index];
|
|
40
|
+
const stack = focusedTab ? tabBar.stacks[focusedTab.tabKey] : undefined;
|
|
41
|
+
const Screen = focusedTab ? tabBar.screens[focusedTab.tabKey] : undefined;
|
|
42
|
+
const onTabClick = useCallback(nextIndex => {
|
|
43
|
+
const targetTab = tabs[nextIndex];
|
|
44
|
+
if (!targetTab) return;
|
|
45
|
+
const targetStack = tabBar.stacks[targetTab.tabKey];
|
|
46
|
+
if (targetStack) {
|
|
47
|
+
// Prefer last visited route in the target stack; otherwise first route
|
|
48
|
+
const stackId = targetStack.getId();
|
|
49
|
+
const history = router.getStackHistory(stackId);
|
|
50
|
+
const last = history.length ? history[history.length - 1] : undefined;
|
|
51
|
+
const toPath = last?.path ?? targetStack.getFirstRoute()?.path;
|
|
52
|
+
if (toPath) {
|
|
53
|
+
const currentPath = router.getVisibleRoute()?.path;
|
|
54
|
+
if (nextIndex === index && toPath === currentPath) return;
|
|
55
|
+
// Use replace to avoid duplicating history entries when switching tabs
|
|
56
|
+
router.replace(toPath, true);
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Fallback: just switch tab index (no history push if there is no path)
|
|
62
|
+
router.onTabIndexChange(nextIndex);
|
|
63
|
+
}, [router, tabBar, tabs, index]);
|
|
64
|
+
const tabBarStyle = useMemo(() => {
|
|
65
|
+
const tabBarBg = toColorString(appearance?.tabBar?.backgroundColor);
|
|
66
|
+
return tabBarBg ? {
|
|
67
|
+
backgroundColor: tabBarBg
|
|
68
|
+
} : undefined;
|
|
69
|
+
}, [appearance?.tabBar?.backgroundColor]);
|
|
70
|
+
const titleBaseStyle = useMemo(() => ({
|
|
71
|
+
fontFamily: appearance?.tabBar?.title?.fontFamily,
|
|
72
|
+
fontSize: appearance?.tabBar?.title?.fontSize,
|
|
73
|
+
fontWeight: appearance?.tabBar?.title?.fontWeight,
|
|
74
|
+
fontStyle: appearance?.tabBar?.title?.fontStyle
|
|
75
|
+
}), [appearance?.tabBar?.title?.fontFamily, appearance?.tabBar?.title?.fontSize, appearance?.tabBar?.title?.fontWeight, appearance?.tabBar?.title?.fontStyle]);
|
|
76
|
+
|
|
77
|
+
// If a custom component is provided, render it instead of the default web tab bar
|
|
78
|
+
const CustomTabBar = config.component;
|
|
79
|
+
return /*#__PURE__*/_jsx("div", {
|
|
80
|
+
className: "screen-stack-item",
|
|
81
|
+
"data-presentation": "push",
|
|
82
|
+
"data-phase": "active",
|
|
83
|
+
children: /*#__PURE__*/_jsx(TabBarContext.Provider, {
|
|
84
|
+
value: tabBar,
|
|
85
|
+
children: /*#__PURE__*/_jsxs("div", {
|
|
86
|
+
className: "tab-stacks-container",
|
|
87
|
+
children: [stack ? /*#__PURE__*/_jsx(StackRenderer, {
|
|
88
|
+
appearance: appearance,
|
|
89
|
+
stack: stack
|
|
90
|
+
}) : Screen ? /*#__PURE__*/_jsx(Screen, {}) : null, CustomTabBar ? /*#__PURE__*/_jsx(CustomTabBar, {
|
|
91
|
+
onTabPress: onTabClick,
|
|
92
|
+
activeIndex: index,
|
|
93
|
+
tabs: tabs
|
|
94
|
+
}) : /*#__PURE__*/_jsx("div", {
|
|
95
|
+
className: "tab-bar",
|
|
96
|
+
style: tabBarStyle,
|
|
97
|
+
children: tabs.map((tab, i) => {
|
|
98
|
+
const isActive = i === index;
|
|
99
|
+
const iconTint = toColorString(isActive ? appearance?.tabBar?.iconColorActive : appearance?.tabBar?.iconColor);
|
|
100
|
+
const title = appearance?.tabBar?.title;
|
|
101
|
+
const labelColor = isActive ? toColorString(title?.activeColor) ?? toColorString(title?.color) : toColorString(title?.color);
|
|
102
|
+
const labelStyle = {
|
|
103
|
+
...titleBaseStyle,
|
|
104
|
+
color: labelColor
|
|
105
|
+
};
|
|
106
|
+
return /*#__PURE__*/_jsxs("button", {
|
|
107
|
+
"data-index": i,
|
|
108
|
+
className: `tab-item${isActive ? ' active' : ''}`,
|
|
109
|
+
onClick: () => onTabClick(i),
|
|
110
|
+
children: [/*#__PURE__*/_jsx("div", {
|
|
111
|
+
className: "tab-item-icon",
|
|
112
|
+
children: isImageSource(tab.icon) ? /*#__PURE__*/_jsx(TabIcon, {
|
|
113
|
+
source: tab.icon,
|
|
114
|
+
tintColor: iconTint
|
|
115
|
+
}) : null
|
|
116
|
+
}), tab.badgeValue ? /*#__PURE__*/_jsx("span", {
|
|
117
|
+
className: "tab-item-label-badge",
|
|
118
|
+
children: tab.badgeValue
|
|
119
|
+
}) : null, /*#__PURE__*/_jsx("div", {
|
|
120
|
+
className: "tab-item-label",
|
|
121
|
+
style: labelStyle,
|
|
122
|
+
children: tab.title
|
|
123
|
+
})]
|
|
124
|
+
}, tab.tabKey);
|
|
125
|
+
})
|
|
126
|
+
})]
|
|
127
|
+
})
|
|
128
|
+
})
|
|
129
|
+
});
|
|
130
|
+
});
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import { memo } from 'react';
|
|
4
|
+
import { Image } from 'react-native';
|
|
5
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
6
|
+
const resolveImageUri = source => {
|
|
7
|
+
if (!source) return undefined;
|
|
8
|
+
const resolved = typeof Image.resolveAssetSource === 'function' ? Image.resolveAssetSource(source) : undefined;
|
|
9
|
+
if (resolved?.uri) return resolved.uri;
|
|
10
|
+
if (Array.isArray(source)) {
|
|
11
|
+
const first = source[0];
|
|
12
|
+
if (first && typeof first === 'object' && 'uri' in first) return first.uri;
|
|
13
|
+
}
|
|
14
|
+
if (typeof source === 'object' && source && 'uri' in source) return source.uri;
|
|
15
|
+
return undefined;
|
|
16
|
+
};
|
|
17
|
+
export const TabIcon = /*#__PURE__*/memo(({
|
|
18
|
+
source,
|
|
19
|
+
tintColor
|
|
20
|
+
}) => {
|
|
21
|
+
const iconUri = resolveImageUri(source);
|
|
22
|
+
const useMask = Boolean(tintColor && iconUri);
|
|
23
|
+
if (useMask && iconUri) {
|
|
24
|
+
const maskStyle = {
|
|
25
|
+
backgroundColor: tintColor,
|
|
26
|
+
WebkitMaskImage: `url(${iconUri})`,
|
|
27
|
+
maskImage: `url(${iconUri})`,
|
|
28
|
+
WebkitMaskSize: 'contain',
|
|
29
|
+
maskSize: 'contain',
|
|
30
|
+
WebkitMaskRepeat: 'no-repeat',
|
|
31
|
+
maskRepeat: 'no-repeat',
|
|
32
|
+
WebkitMaskPosition: 'center',
|
|
33
|
+
maskPosition: 'center'
|
|
34
|
+
};
|
|
35
|
+
return /*#__PURE__*/_jsx("div", {
|
|
36
|
+
style: maskStyle
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
return /*#__PURE__*/_jsx(Image, {
|
|
40
|
+
source: source
|
|
41
|
+
});
|
|
42
|
+
});
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
:root {
|
|
4
|
+
--tabs-transition: .2s ease-in-out;
|
|
5
|
+
--transition-standard-easing: cubic-bezier(.4, .0, .2, 1);
|
|
6
|
+
--transition-standard-in-time: .3s;
|
|
7
|
+
--transition-standard-out-time: .25s;
|
|
8
|
+
|
|
9
|
+
--transition-standard-in: var(--transition-standard-in-time) var(--transition-standard-easing);
|
|
10
|
+
--transition-standard-out: var(--transition-standard-out-time) var(--transition-standard-easing);
|
|
11
|
+
|
|
12
|
+
--background-color-true: #181818;
|
|
13
|
+
|
|
14
|
+
--background-color: var(--background-color-true);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
body {
|
|
19
|
+
margin: 0;
|
|
20
|
+
padding: 0;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.screen-stack {
|
|
24
|
+
min-width: 100%;
|
|
25
|
+
width: 100%;
|
|
26
|
+
display: flex;
|
|
27
|
+
position: relative;
|
|
28
|
+
height: 100dvh;
|
|
29
|
+
overflow: hidden;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.screen-stack > .screen-stack-item {
|
|
33
|
+
position: absolute;
|
|
34
|
+
inset: 0;
|
|
35
|
+
display: flex;
|
|
36
|
+
font-size: 24px;
|
|
37
|
+
display: none;
|
|
38
|
+
overflow: hidden;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.screen-stack > .screen-stack-item.active {
|
|
42
|
+
display: flex;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.screen-stack[data-animation="navigation"].animating > .screen-stack-item {
|
|
46
|
+
transition: transform var(--transition-standard-in), filter var(--transition-standard-in);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.screen-stack[data-animation="navigation"].animating.backwards > .screen-stack-item {
|
|
50
|
+
transition: transform var(--transition-standard-out), filter var(--transition-standard-out);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.screen-stack[data-animation="modal"].animating > .screen-stack-item {
|
|
54
|
+
transition: transform var(--transition-standard-in), filter var(--transition-standard-in);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.screen-stack[data-animation="modal"].animating.backwards > .screen-stack-item {
|
|
58
|
+
transition: transform var(--transition-standard-out), filter var(--transition-standard-out);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.tab-stacks-container {
|
|
62
|
+
display: flex;
|
|
63
|
+
flex-direction: column;
|
|
64
|
+
flex: 1 1 auto;
|
|
65
|
+
min-height: 100vhd;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.tab-bar {
|
|
69
|
+
display: flex;
|
|
70
|
+
flex-direction: row;
|
|
71
|
+
align-items: center;
|
|
72
|
+
justify-content: center;
|
|
73
|
+
flex-shrink: 0;
|
|
74
|
+
position: relative;
|
|
75
|
+
height: 49px;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.tab-item {
|
|
79
|
+
appearance: none;
|
|
80
|
+
background: transparent;
|
|
81
|
+
border: 0;
|
|
82
|
+
margin: 0;
|
|
83
|
+
padding: 4px 0 2px;
|
|
84
|
+
height: 100%;
|
|
85
|
+
color: inherit;
|
|
86
|
+
display: flex;
|
|
87
|
+
flex: 1 1 0%;
|
|
88
|
+
flex-direction: column;
|
|
89
|
+
align-items: center;
|
|
90
|
+
justify-content: center;
|
|
91
|
+
position: relative;
|
|
92
|
+
cursor: pointer;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
.tab-item:focus {
|
|
96
|
+
outline: none;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
.tab-item-icon {
|
|
100
|
+
width: 24px;
|
|
101
|
+
height: 24px;
|
|
102
|
+
display: flex;
|
|
103
|
+
align-items: center;
|
|
104
|
+
justify-content: center;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
.tab-item-icon > * {
|
|
108
|
+
width: 24px;
|
|
109
|
+
height: 24px;
|
|
110
|
+
display: block;
|
|
111
|
+
object-fit: contain;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
.tab-item-label {
|
|
115
|
+
font-size: 11px;
|
|
116
|
+
line-height: 12px;
|
|
117
|
+
text-align: center;
|
|
118
|
+
white-space: nowrap;
|
|
119
|
+
pointer-events: none;
|
|
120
|
+
margin-top: 2px;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
.tab-item-label-badge {
|
|
124
|
+
position: absolute;
|
|
125
|
+
top: 4px;
|
|
126
|
+
left: 50%;
|
|
127
|
+
transform: translateX(10px);
|
|
128
|
+
min-width: 16px;
|
|
129
|
+
height: 16px;
|
|
130
|
+
padding: 0 5px;
|
|
131
|
+
border-radius: 9999px;
|
|
132
|
+
background-color: #dc3545;
|
|
133
|
+
color: #fff;
|
|
134
|
+
font-size: 10px;
|
|
135
|
+
line-height: 16px;
|
|
136
|
+
display: inline-flex;
|
|
137
|
+
align-items: center;
|
|
138
|
+
justify-content: center;
|
|
139
|
+
}
|