@symbiote-native/navigation 2.1.0 → 3.0.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/README.md +89 -14
- package/build/angular/drawer/index.js +23 -9
- package/build/angular/index.d.ts +4 -4
- package/build/angular/index.js +1 -1
- package/build/angular/injectors/inject-navigation.js +1 -1
- package/build/angular/navigation-scope.directive.js +1 -1
- package/build/angular/search-bar-ref.directive.js +1 -1
- package/build/angular/stack/index.js +20 -6
- package/build/angular/tabs/index.js +21 -7
- package/build/core/drawer-options/index.js +19 -5
- package/build/core/drawer-router-state/index.js +8 -2
- package/build/core/guards.js +3 -1
- package/build/core/header-bar-buttons.js +14 -4
- package/build/core/index.d.ts +4 -4
- package/build/core/index.js +2 -2
- package/build/core/navigator-state/index.js +12 -4
- package/build/core/render-drawer.js +4 -1
- package/build/core/render-stack.js +13 -4
- package/build/core/screen-options.js +1 -1
- package/build/core/search-bar-commands.js +1 -1
- package/build/core/state-persistence/index.js +6 -2
- package/build/core/tab-router-state/index.js +8 -2
- package/build/react/drawer/index.js +36 -13
- package/build/react/index.d.ts +7 -7
- package/build/react/index.js +3 -3
- package/build/react/stack/index.js +34 -8
- package/build/react/tabs/index.js +14 -4
- package/build/solid/drawer/index.d.ts +22 -0
- package/build/solid/drawer/index.js +394 -0
- package/build/solid/drawer-screen-props.d.ts +13 -0
- package/build/solid/drawer-screen-props.js +2 -0
- package/build/solid/host.d.ts +2 -0
- package/build/solid/host.js +23 -0
- package/build/solid/index.d.ts +19 -0
- package/build/solid/index.js +24 -0
- package/build/solid/linking.d.ts +3 -0
- package/build/solid/linking.js +55 -0
- package/build/solid/navigation-context.d.ts +13 -0
- package/build/solid/navigation-context.js +37 -0
- package/build/solid/primitives/create-focus-effect.d.ts +1 -0
- package/build/solid/primitives/create-focus-effect.js +30 -0
- package/build/solid/primitives/create-is-focused.d.ts +2 -0
- package/build/solid/primitives/create-is-focused.js +31 -0
- package/build/solid/primitives/create-navigation-state.d.ts +3 -0
- package/build/solid/primitives/create-navigation-state.js +30 -0
- package/build/solid/primitives/index.d.ts +12 -0
- package/build/solid/primitives/index.js +8 -0
- package/build/solid/primitives/use-drawer-navigation.d.ts +5 -0
- package/build/solid/primitives/use-drawer-navigation.js +15 -0
- package/build/solid/primitives/use-navigation.d.ts +8 -0
- package/build/solid/primitives/use-navigation.js +23 -0
- package/build/solid/primitives/use-route.d.ts +3 -0
- package/build/solid/primitives/use-route.js +14 -0
- package/build/solid/primitives/use-stack-navigation.d.ts +5 -0
- package/build/solid/primitives/use-stack-navigation.js +19 -0
- package/build/solid/primitives/use-tab-navigation.d.ts +5 -0
- package/build/solid/primitives/use-tab-navigation.js +15 -0
- package/build/solid/screen-props.d.ts +19 -0
- package/build/solid/screen-props.js +9 -0
- package/build/solid/screen-registry.d.ts +31 -0
- package/build/solid/screen-registry.js +86 -0
- package/build/solid/screen.d.ts +6 -0
- package/build/solid/screen.js +69 -0
- package/build/solid/stack/index.d.ts +15 -0
- package/build/solid/stack/index.js +348 -0
- package/build/solid/tab-screen-props.d.ts +13 -0
- package/build/solid/tab-screen-props.js +3 -0
- package/build/solid/tabs/index.d.ts +14 -0
- package/build/solid/tabs/index.js +228 -0
- package/build/svelte/descriptor-subtree.js +3 -1
- package/build/svelte/drawer/index.svelte +163 -25
- package/build/svelte/drawer-screen.svelte +2 -1
- package/build/svelte/index.d.ts +3 -3
- package/build/svelte/index.js +1 -1
- package/build/svelte/navigation-scope.svelte +4 -1
- package/build/svelte/runes/use-navigation.svelte.js +5 -1
- package/build/svelte/stack/index.svelte +43 -6
- package/build/svelte/stack/stack-screen.svelte +66 -9
- package/build/svelte/tabs/index.svelte +42 -20
- package/build/vue/composables/use-navigation.js +5 -1
- package/build/vue/drawer/index.js +30 -8
- package/build/vue/index.d.ts +3 -3
- package/build/vue/index.js +1 -1
- package/build/vue/navigation-context.js +1 -1
- package/build/vue/stack/index.js +17 -6
- package/build/vue/tabs/index.js +18 -5
- package/build-ngc/angular/drawer/index.js +31 -11
- package/build-ngc/angular/drawer/index.js.map +1 -1
- package/build-ngc/angular/index.d.ts +4 -4
- package/build-ngc/angular/index.js.map +1 -1
- package/build-ngc/angular/injectors/inject-focus-effect.js.map +1 -1
- package/build-ngc/angular/injectors/inject-is-focused.js.map +1 -1
- package/build-ngc/angular/injectors/inject-navigation-state.js.map +1 -1
- package/build-ngc/angular/injectors/inject-navigation.js +1 -1
- package/build-ngc/angular/injectors/inject-navigation.js.map +1 -1
- package/build-ngc/angular/linking.js.map +1 -1
- package/build-ngc/angular/navigation-context.service.js.map +1 -1
- package/build-ngc/angular/navigation-scope.directive.js +1 -1
- package/build-ngc/angular/navigation-scope.directive.js.map +1 -1
- package/build-ngc/angular/screen.directive.js.map +1 -1
- package/build-ngc/angular/search-bar-ref.directive.js +1 -1
- package/build-ngc/angular/search-bar-ref.directive.js.map +1 -1
- package/build-ngc/angular/stack/index.js +26 -8
- package/build-ngc/angular/stack/index.js.map +1 -1
- package/build-ngc/angular/tab-screen.directive.js.map +1 -1
- package/build-ngc/angular/tabs/index.js +21 -7
- package/build-ngc/angular/tabs/index.js.map +1 -1
- package/build-ngc/core/constants.js.map +1 -1
- package/build-ngc/core/drawer-options/index.js +19 -5
- package/build-ngc/core/drawer-options/index.js.map +1 -1
- package/build-ngc/core/drawer-router-state/index.js +8 -2
- package/build-ngc/core/drawer-router-state/index.js.map +1 -1
- package/build-ngc/core/guards.js +3 -1
- package/build-ngc/core/guards.js.map +1 -1
- package/build-ngc/core/header-bar-buttons.js +14 -4
- package/build-ngc/core/header-bar-buttons.js.map +1 -1
- package/build-ngc/core/index.d.ts +4 -4
- package/build-ngc/core/index.js +2 -2
- package/build-ngc/core/index.js.map +1 -1
- package/build-ngc/core/linking-config/index.js.map +1 -1
- package/build-ngc/core/navigation-events/index.js.map +1 -1
- package/build-ngc/core/navigator-handles/index.js.map +1 -1
- package/build-ngc/core/navigator-state/index.js +12 -4
- package/build-ngc/core/navigator-state/index.js.map +1 -1
- package/build-ngc/core/render-drawer.js +4 -1
- package/build-ngc/core/render-drawer.js.map +1 -1
- package/build-ngc/core/render-stack.js +13 -4
- package/build-ngc/core/render-stack.js.map +1 -1
- package/build-ngc/core/render-tabs.js.map +1 -1
- package/build-ngc/core/screen-options.js +1 -1
- package/build-ngc/core/screen-options.js.map +1 -1
- package/build-ngc/core/search-bar-commands.js +1 -1
- package/build-ngc/core/search-bar-commands.js.map +1 -1
- package/build-ngc/core/stack-router-state/index.js.map +1 -1
- package/build-ngc/core/state-persistence/index.js +6 -2
- package/build-ngc/core/state-persistence/index.js.map +1 -1
- package/build-ngc/core/tab-router-state/index.js +8 -2
- package/build-ngc/core/tab-router-state/index.js.map +1 -1
- package/package.json +40 -15
- package/src/angular/drawer/index.ts +69 -25
- package/src/angular/index.ts +20 -4
- package/src/angular/injectors/inject-focus-effect.ts +8 -2
- package/src/angular/injectors/inject-is-focused.ts +6 -4
- package/src/angular/injectors/inject-navigation-state.ts +7 -4
- package/src/angular/injectors/inject-navigation.ts +12 -3
- package/src/angular/linking.ts +4 -1
- package/src/angular/navigation-context.service.ts +5 -2
- package/src/angular/navigation-scope.directive.ts +6 -2
- package/src/angular/screen.directive.ts +13 -3
- package/src/angular/search-bar-ref.directive.ts +10 -2
- package/src/angular/stack/index.ts +68 -22
- package/src/angular/tab-screen.directive.ts +3 -1
- package/src/angular/tabs/index.ts +36 -12
- package/src/core/constants.ts +6 -3
- package/src/core/drawer-options/index.ts +40 -13
- package/src/core/drawer-router-state/index.ts +11 -3
- package/src/core/guards.ts +3 -1
- package/src/core/header-bar-buttons.ts +41 -12
- package/src/core/index.ts +18 -4
- package/src/core/linking-config/index.ts +31 -10
- package/src/core/navigation-events/index.ts +8 -2
- package/src/core/navigator-handles/index.ts +8 -3
- package/src/core/navigator-props.ts +13 -4
- package/src/core/navigator-state/index.ts +24 -7
- package/src/core/render-drawer.ts +24 -9
- package/src/core/render-stack.ts +52 -13
- package/src/core/render-tabs.ts +24 -6
- package/src/core/screen-options.ts +15 -5
- package/src/core/search-bar-commands.ts +12 -3
- package/src/core/stack-router-state/index.ts +5 -2
- package/src/core/state-persistence/index.ts +13 -3
- package/src/core/tab-router-state/index.ts +10 -3
- package/src/react/drawer/index.ts +408 -344
- package/src/react/drawer-screen.ts +8 -2
- package/src/react/hooks/use-focus-effect.ts +8 -2
- package/src/react/hooks/use-is-focused.ts +6 -2
- package/src/react/hooks/use-navigation.ts +8 -2
- package/src/react/index.ts +37 -7
- package/src/react/linking.ts +4 -1
- package/src/react/navigation-context.ts +6 -2
- package/src/react/screen.ts +12 -3
- package/src/react/stack/index.ts +294 -231
- package/src/react/tab-screen.ts +3 -1
- package/src/react/tabs/index.ts +188 -158
- package/src/solid/drawer/index.ts +570 -0
- package/src/solid/drawer-screen-props.ts +24 -0
- package/src/solid/host.ts +29 -0
- package/src/solid/index.ts +87 -0
- package/src/solid/linking.ts +74 -0
- package/src/solid/navigation-context.ts +56 -0
- package/src/solid/primitives/create-focus-effect.ts +41 -0
- package/src/solid/primitives/create-is-focused.ts +41 -0
- package/src/solid/primitives/create-navigation-state.ts +44 -0
- package/src/solid/primitives/index.ts +12 -0
- package/src/solid/primitives/use-drawer-navigation.ts +25 -0
- package/src/solid/primitives/use-navigation.ts +44 -0
- package/src/solid/primitives/use-route.ts +18 -0
- package/src/solid/primitives/use-stack-navigation.ts +29 -0
- package/src/solid/primitives/use-tab-navigation.ts +25 -0
- package/src/solid/screen-props.ts +51 -0
- package/src/solid/screen-registry.ts +159 -0
- package/src/solid/screen.ts +80 -0
- package/src/solid/stack/index.ts +528 -0
- package/src/solid/tab-screen-props.ts +21 -0
- package/src/solid/tabs/index.ts +327 -0
- package/src/svelte/attachments.ts +3 -1
- package/src/svelte/descriptor-subtree.ts +3 -1
- package/src/svelte/drawer/index.svelte +163 -25
- package/src/svelte/drawer-screen-props.ts +8 -2
- package/src/svelte/drawer-screen.svelte +2 -1
- package/src/svelte/fabric-tree.test-helper.ts +37 -3
- package/src/svelte/index.ts +15 -3
- package/src/svelte/linking.svelte.ts +11 -5
- package/src/svelte/navigation-scope.svelte +4 -1
- package/src/svelte/runes/use-drawer-navigation.svelte.ts +3 -1
- package/src/svelte/runes/use-focus-effect.svelte.ts +8 -2
- package/src/svelte/runes/use-navigation-state.svelte.ts +3 -1
- package/src/svelte/runes/use-navigation.svelte.ts +13 -3
- package/src/svelte/runes/use-stack-navigation.svelte.ts +3 -1
- package/src/svelte/screen-props.ts +7 -2
- package/src/svelte/screen-registry.ts +9 -3
- package/src/svelte/stack/index.svelte +43 -6
- package/src/svelte/stack/stack-screen.svelte +66 -9
- package/src/svelte/svelte-compile.test-helper.ts +16 -12
- package/src/svelte/tab-screen-props.ts +3 -1
- package/src/svelte/tabs/index.svelte +42 -20
- package/src/vue/composables/use-navigation-state.ts +7 -4
- package/src/vue/composables/use-navigation.ts +13 -3
- package/src/vue/drawer/index.ts +122 -36
- package/src/vue/drawer-screen.ts +12 -5
- package/src/vue/index.ts +15 -3
- package/src/vue/linking.ts +7 -2
- package/src/vue/navigation-context.ts +17 -5
- package/src/vue/screen.ts +7 -2
- package/src/vue/stack/index.ts +82 -34
- package/src/vue/tab-screen.ts +3 -1
- package/src/vue/tabs/index.ts +51 -13
package/src/react/tabs/index.ts
CHANGED
|
@@ -55,7 +55,9 @@ export type ITabProps = {
|
|
|
55
55
|
|
|
56
56
|
type ITabRegistryEntry = Omit<ITabScreenProps, 'name'>;
|
|
57
57
|
|
|
58
|
-
function isTabScreenElement(
|
|
58
|
+
function isTabScreenElement(
|
|
59
|
+
child: ReactNode,
|
|
60
|
+
): child is ReactElement<ITabScreenProps> {
|
|
59
61
|
return isValidElement(child) && child.type === TabScreen;
|
|
60
62
|
}
|
|
61
63
|
|
|
@@ -64,174 +66,202 @@ function resolveTabOptions(
|
|
|
64
66
|
optionsArgs: ITabScreenOptionsArgs,
|
|
65
67
|
screenOptions: ITabOptions | undefined,
|
|
66
68
|
): ITabOptions {
|
|
67
|
-
const own =
|
|
69
|
+
const own =
|
|
70
|
+
typeof entry.options === 'function'
|
|
71
|
+
? entry.options(optionsArgs)
|
|
72
|
+
: entry.options;
|
|
68
73
|
return { ...screenOptions, ...own };
|
|
69
74
|
}
|
|
70
75
|
|
|
71
76
|
const TAB_CONTENT_STYLE = { flex: 1 };
|
|
72
77
|
const TAB_ROOT_STYLE = { flex: 1 };
|
|
73
78
|
|
|
74
|
-
const TabImpl = forwardRef<ITabNavigatorHandle, ITabProps>(
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
(
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
const
|
|
144
|
-
|
|
145
|
-
|
|
79
|
+
const TabImpl = forwardRef<ITabNavigatorHandle, ITabProps>(
|
|
80
|
+
(props, forwardedRef) => {
|
|
81
|
+
// Read BEFORE establishing this Tab's own Context value below - becomes the `parent` link a
|
|
82
|
+
// nested screen's useNavigation().getParent() walks (e.g. this Tab rendered as a Stack screen's
|
|
83
|
+
// content reaches that Stack via this value). undefined when this Tab is the nesting root.
|
|
84
|
+
const ambientContext = useContext(NavigationContext);
|
|
85
|
+
const registry = useMemo(
|
|
86
|
+
() => collectRegistry(props.children, isTabScreenElement),
|
|
87
|
+
[props.children],
|
|
88
|
+
);
|
|
89
|
+
const routeIdPrefix = useId();
|
|
90
|
+
|
|
91
|
+
const routes = useMemo<IRoute<unknown>[]>(
|
|
92
|
+
() =>
|
|
93
|
+
Array.from(registry.entries()).map(([name, entry]) => ({
|
|
94
|
+
key: `${routeIdPrefix}-${name}`,
|
|
95
|
+
name,
|
|
96
|
+
params: entry.initialParams,
|
|
97
|
+
})),
|
|
98
|
+
[registry, routeIdPrefix],
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
// A <Tab.Screen> can appear or disappear after mount (a marker behind a conditional, a
|
|
102
|
+
// data-driven screen list), so the route list is re-derived from `children` on every render and
|
|
103
|
+
// the STORED state is reconciled against it (reconcileTabRoutes, core - it preserves each
|
|
104
|
+
// surviving route's key and accumulated params and keeps focus on the same route NAME) rather
|
|
105
|
+
// than staying frozen at whatever the first render saw. `null` means no marker had registered
|
|
106
|
+
// yet: seeding an empty list would resolve initialRouteName against nothing and lose it, so the
|
|
107
|
+
// state stays derived until either a marker arrives or something dispatches.
|
|
108
|
+
const [storedState, setStoredState] = useState<ITabRouterState | null>(
|
|
109
|
+
() =>
|
|
110
|
+
routes.length === 0
|
|
111
|
+
? null
|
|
112
|
+
: createInitialTabState(routes, props.initialRouteName),
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
const resolveState = useCallback(
|
|
116
|
+
(stored: ITabRouterState | null): ITabRouterState =>
|
|
117
|
+
stored === null
|
|
118
|
+
? createInitialTabState(routes, props.initialRouteName)
|
|
119
|
+
: reconcileTabRoutes(stored, routes),
|
|
120
|
+
[routes, props.initialRouteName],
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
const state = resolveState(storedState);
|
|
124
|
+
|
|
125
|
+
if (routes.length === 0) dlog('Tab: no <Tab.Screen> children registered');
|
|
126
|
+
|
|
127
|
+
// The updater re-resolves rather than closing over `state` so that two dispatches batched into
|
|
128
|
+
// one render still compose: the second reduces over the first's result, not a stale snapshot.
|
|
129
|
+
const dispatch = useCallback(
|
|
130
|
+
(action: ITabRouterAction) =>
|
|
131
|
+
setStoredState(current =>
|
|
132
|
+
tabRouterReducer(resolveState(current), action),
|
|
133
|
+
),
|
|
134
|
+
[resolveState],
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
const jumpTo = useCallback(
|
|
138
|
+
(name: string, params?: unknown) =>
|
|
139
|
+
dispatch({ type: 'jumpTo', name, params }),
|
|
140
|
+
[dispatch],
|
|
141
|
+
);
|
|
142
|
+
const setParams = useCallback(
|
|
143
|
+
(params: unknown, key: string) =>
|
|
144
|
+
dispatch({ type: 'setParams', key, params }),
|
|
145
|
+
[dispatch],
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
const handle = useMemo<ITabNavigatorHandle>(
|
|
149
|
+
() => ({ jumpTo, setParams }),
|
|
150
|
+
[jumpTo, setParams],
|
|
151
|
+
);
|
|
152
|
+
|
|
153
|
+
useImperativeHandle(forwardedRef, () => handle, [handle]);
|
|
154
|
+
|
|
155
|
+
const focusedRoute: IRoute<unknown> | undefined = state.routes[state.index];
|
|
156
|
+
|
|
157
|
+
const items: ITabBarItemView[] = state.routes.map((route, index) => {
|
|
158
|
+
const entry = registry.get(route.name);
|
|
159
|
+
const focused = isFocusedRoute(index, state.index);
|
|
160
|
+
if (!entry) {
|
|
161
|
+
dlog(`Tab: no screen registered for route name "${route.name}"`);
|
|
162
|
+
return {
|
|
163
|
+
key: route.key,
|
|
164
|
+
focused,
|
|
165
|
+
label: route.name,
|
|
166
|
+
passthrough: {},
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const options = resolveTabOptions(
|
|
171
|
+
entry,
|
|
172
|
+
{ route, navigation: handle },
|
|
173
|
+
props.screenOptions,
|
|
174
|
+
);
|
|
175
|
+
|
|
146
176
|
return {
|
|
147
177
|
key: route.key,
|
|
148
178
|
focused,
|
|
149
|
-
label: route.name,
|
|
179
|
+
label: options.tabBarLabel ?? options.title ?? route.name,
|
|
180
|
+
icon: options.tabBarIcon,
|
|
181
|
+
badge: options.tabBarBadge,
|
|
182
|
+
activeTintColor: options.tabBarActiveTintColor,
|
|
183
|
+
inactiveTintColor: options.tabBarInactiveTintColor,
|
|
184
|
+
passthrough: {
|
|
185
|
+
onPress: () => jumpTo(route.name),
|
|
186
|
+
accessibilityRole: 'tab',
|
|
187
|
+
accessibilityState: { selected: focused },
|
|
188
|
+
},
|
|
189
|
+
};
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
const focusedEntry = focusedRoute
|
|
193
|
+
? registry.get(focusedRoute.name)
|
|
194
|
+
: undefined;
|
|
195
|
+
const focusedOptions: ITabOptions | undefined =
|
|
196
|
+
focusedEntry && focusedRoute
|
|
197
|
+
? resolveTabOptions(
|
|
198
|
+
focusedEntry,
|
|
199
|
+
{ route: focusedRoute, navigation: handle },
|
|
200
|
+
props.screenOptions,
|
|
201
|
+
)
|
|
202
|
+
: props.screenOptions;
|
|
203
|
+
|
|
204
|
+
const tabBar = descriptorToReact(
|
|
205
|
+
renderTabBar({
|
|
206
|
+
items,
|
|
207
|
+
style: focusedOptions?.tabBarStyle,
|
|
150
208
|
passthrough: {},
|
|
209
|
+
}),
|
|
210
|
+
);
|
|
211
|
+
|
|
212
|
+
// Only the focused route's screen is ever mounted (unlike Stack, which keeps every pushed route
|
|
213
|
+
// alive), so a fresh emitter per focus change is sufficient - no per-route emitter map to prune,
|
|
214
|
+
// because the previous screen's whole subtree (and any listeners it registered) is torn down by
|
|
215
|
+
// an ordinary React unmount when focus moves on. Keyed on the route KEY rather than the route
|
|
216
|
+
// object so a setParams-only change (new route object, same key) doesn't spuriously re-fire
|
|
217
|
+
// focus/blur.
|
|
218
|
+
const focusedRouteKey = focusedRoute?.key;
|
|
219
|
+
const routeEmitter = useMemo(
|
|
220
|
+
() => createNavigationEmitter(),
|
|
221
|
+
[focusedRouteKey],
|
|
222
|
+
);
|
|
223
|
+
|
|
224
|
+
// Tab paints its own bar in pure JS - there is no native onAppear/onDisappear to hook (unlike
|
|
225
|
+
// Stack's RNSScreen), so focus/blur is synthesized here: mount = focus, cleanup = blur, exactly
|
|
226
|
+
// what an effect keyed on focusedRouteKey already encodes - no diffFocusedRoute indirection
|
|
227
|
+
// needed (unlike Vue/Angular, which diff real prev/next keys inside an imperative watch/CD
|
|
228
|
+
// callback that has no mount/cleanup pairing of its own).
|
|
229
|
+
useEffect(() => {
|
|
230
|
+
if (focusedRouteKey === undefined) return undefined;
|
|
231
|
+
dlog(`Tab: route "${focusedRoute?.name}" focused`);
|
|
232
|
+
routeEmitter.emit(NAVIGATION_EVENT_FOCUS);
|
|
233
|
+
return () => {
|
|
234
|
+
dlog(`Tab: route "${focusedRoute?.name}" blurred`);
|
|
235
|
+
routeEmitter.emit(NAVIGATION_EVENT_BLUR);
|
|
151
236
|
};
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
},
|
|
169
|
-
};
|
|
170
|
-
});
|
|
171
|
-
|
|
172
|
-
const focusedEntry = focusedRoute ? registry.get(focusedRoute.name) : undefined;
|
|
173
|
-
const focusedOptions: ITabOptions | undefined =
|
|
174
|
-
focusedEntry && focusedRoute
|
|
175
|
-
? resolveTabOptions(
|
|
176
|
-
focusedEntry,
|
|
177
|
-
{ route: focusedRoute, navigation: handle },
|
|
178
|
-
props.screenOptions,
|
|
179
|
-
)
|
|
180
|
-
: props.screenOptions;
|
|
181
|
-
|
|
182
|
-
const tabBar = descriptorToReact(
|
|
183
|
-
renderTabBar({ items, style: focusedOptions?.tabBarStyle, passthrough: {} }),
|
|
184
|
-
);
|
|
185
|
-
|
|
186
|
-
// Only the focused route's screen is ever mounted (unlike Stack, which keeps every pushed route
|
|
187
|
-
// alive), so a fresh emitter per focus change is sufficient - no per-route emitter map to prune,
|
|
188
|
-
// because the previous screen's whole subtree (and any listeners it registered) is torn down by
|
|
189
|
-
// an ordinary React unmount when focus moves on. Keyed on the route KEY rather than the route
|
|
190
|
-
// object so a setParams-only change (new route object, same key) doesn't spuriously re-fire
|
|
191
|
-
// focus/blur.
|
|
192
|
-
const focusedRouteKey = focusedRoute?.key;
|
|
193
|
-
const routeEmitter = useMemo(() => createNavigationEmitter(), [focusedRouteKey]);
|
|
194
|
-
|
|
195
|
-
// Tab paints its own bar in pure JS - there is no native onAppear/onDisappear to hook (unlike
|
|
196
|
-
// Stack's RNSScreen), so focus/blur is synthesized here: mount = focus, cleanup = blur, exactly
|
|
197
|
-
// what an effect keyed on focusedRouteKey already encodes - no diffFocusedRoute indirection
|
|
198
|
-
// needed (unlike Vue/Angular, which diff real prev/next keys inside an imperative watch/CD
|
|
199
|
-
// callback that has no mount/cleanup pairing of its own).
|
|
200
|
-
useEffect(() => {
|
|
201
|
-
if (focusedRouteKey === undefined) return undefined;
|
|
202
|
-
dlog(`Tab: route "${focusedRoute?.name}" focused`);
|
|
203
|
-
routeEmitter.emit(NAVIGATION_EVENT_FOCUS);
|
|
204
|
-
return () => {
|
|
205
|
-
dlog(`Tab: route "${focusedRoute?.name}" blurred`);
|
|
206
|
-
routeEmitter.emit(NAVIGATION_EVENT_BLUR);
|
|
207
|
-
};
|
|
208
|
-
// focusedRoute omitted deliberately: only its .key (tracked via focusedRouteKey) should
|
|
209
|
-
// re-run this effect - see the comment above on focusedRouteKey.
|
|
210
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
211
|
-
}, [routeEmitter, focusedRouteKey]);
|
|
212
|
-
|
|
213
|
-
const content =
|
|
214
|
-
focusedEntry && focusedRoute
|
|
215
|
-
? createElement(
|
|
216
|
-
NavigationContext.Provider,
|
|
217
|
-
{
|
|
218
|
-
value: {
|
|
219
|
-
route: focusedRoute,
|
|
220
|
-
navigation: handle,
|
|
221
|
-
emitter: routeEmitter,
|
|
222
|
-
parent: ambientContext,
|
|
237
|
+
// focusedRoute omitted deliberately: only its .key (tracked via focusedRouteKey) should
|
|
238
|
+
// re-run this effect - see the comment above on focusedRouteKey.
|
|
239
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
240
|
+
}, [routeEmitter, focusedRouteKey]);
|
|
241
|
+
|
|
242
|
+
const content =
|
|
243
|
+
focusedEntry && focusedRoute
|
|
244
|
+
? createElement(
|
|
245
|
+
NavigationContext.Provider,
|
|
246
|
+
{
|
|
247
|
+
value: {
|
|
248
|
+
route: focusedRoute,
|
|
249
|
+
navigation: handle,
|
|
250
|
+
emitter: routeEmitter,
|
|
251
|
+
parent: ambientContext,
|
|
252
|
+
},
|
|
223
253
|
},
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
254
|
+
createElement(focusedEntry.component),
|
|
255
|
+
)
|
|
256
|
+
: null;
|
|
257
|
+
|
|
258
|
+
return createElement(
|
|
259
|
+
'symbiote-view',
|
|
260
|
+
{ style: TAB_ROOT_STYLE },
|
|
261
|
+
createElement('symbiote-view', { style: TAB_CONTENT_STYLE }, content),
|
|
262
|
+
tabBar,
|
|
263
|
+
);
|
|
264
|
+
},
|
|
265
|
+
);
|
|
236
266
|
|
|
237
267
|
export const Tab = Object.assign(TabImpl, { Screen: TabScreen });
|