@symbiote-native/navigation 2.0.3 → 2.2.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 +42 -12
- package/build/angular/drawer/index.js +29 -13
- package/build/angular/index.d.ts +4 -4
- 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.d.ts +2 -0
- package/build/angular/stack/index.js +43 -9
- package/build/angular/tabs/index.d.ts +1 -1
- package/build/angular/tabs/index.js +40 -13
- 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 +6 -5
- package/build/core/index.js +7 -3
- 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/stack-router-state/index.d.ts +2 -0
- package/build/core/stack-router-state/index.js +44 -0
- package/build/core/state-persistence/index.js +6 -2
- package/build/core/tab-router-state/index.d.ts +1 -0
- package/build/core/tab-router-state/index.js +27 -2
- package/build/react/drawer/index.js +59 -26
- package/build/react/index.d.ts +7 -7
- package/build/react/index.js +2 -2
- package/build/react/stack/index.js +47 -10
- package/build/react/tabs/index.js +36 -12
- 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/attachments.d.ts +5 -0
- package/build/svelte/attachments.js +61 -0
- package/build/svelte/descriptor-subtree.d.ts +3 -0
- package/build/svelte/descriptor-subtree.js +39 -0
- package/build/svelte/drawer/drawer-props.d.ts +14 -0
- package/build/svelte/drawer/drawer-props.js +3 -0
- package/build/svelte/drawer/index.d.ts +10 -0
- package/build/svelte/drawer/index.js +6 -0
- package/build/svelte/drawer/index.svelte +559 -0
- package/build/svelte/drawer/index.svelte.d.ts +10 -0
- package/build/svelte/drawer-screen-props.d.ts +13 -0
- package/build/svelte/drawer-screen-props.js +4 -0
- package/build/svelte/drawer-screen.svelte +25 -0
- package/build/svelte/drawer-screen.svelte.d.ts +4 -0
- package/build/svelte/index.d.ts +21 -0
- package/build/svelte/index.js +20 -0
- package/build/svelte/linking.svelte.d.ts +2 -0
- package/build/svelte/linking.svelte.js +55 -0
- package/build/svelte/navigation-context.d.ts +14 -0
- package/build/svelte/navigation-context.js +44 -0
- package/build/svelte/navigation-scope.svelte +27 -0
- package/build/svelte/navigation-scope.svelte.d.ts +9 -0
- package/build/svelte/registry-host.d.ts +1 -0
- package/build/svelte/registry-host.js +26 -0
- package/build/svelte/runes/index.d.ts +12 -0
- package/build/svelte/runes/index.js +12 -0
- package/build/svelte/runes/use-drawer-navigation.svelte.d.ts +6 -0
- package/build/svelte/runes/use-drawer-navigation.svelte.js +19 -0
- package/build/svelte/runes/use-focus-effect.svelte.d.ts +1 -0
- package/build/svelte/runes/use-focus-effect.svelte.js +31 -0
- package/build/svelte/runes/use-is-focused.svelte.d.ts +3 -0
- package/build/svelte/runes/use-is-focused.svelte.js +34 -0
- package/build/svelte/runes/use-navigation-state.svelte.d.ts +4 -0
- package/build/svelte/runes/use-navigation-state.svelte.js +39 -0
- package/build/svelte/runes/use-navigation.svelte.d.ts +9 -0
- package/build/svelte/runes/use-navigation.svelte.js +26 -0
- package/build/svelte/runes/use-route.svelte.d.ts +4 -0
- package/build/svelte/runes/use-route.svelte.js +12 -0
- package/build/svelte/runes/use-stack-navigation.svelte.d.ts +6 -0
- package/build/svelte/runes/use-stack-navigation.svelte.js +22 -0
- package/build/svelte/runes/use-tab-navigation.svelte.d.ts +6 -0
- package/build/svelte/runes/use-tab-navigation.svelte.js +19 -0
- package/build/svelte/screen-props.d.ts +21 -0
- package/build/svelte/screen-props.js +6 -0
- package/build/svelte/screen-registry.d.ts +17 -0
- package/build/svelte/screen-registry.js +57 -0
- package/build/svelte/screen.svelte +28 -0
- package/build/svelte/screen.svelte.d.ts +4 -0
- package/build/svelte/stack/index.d.ts +14 -0
- package/build/svelte/stack/index.js +7 -0
- package/build/svelte/stack/index.svelte +261 -0
- package/build/svelte/stack/index.svelte.d.ts +15 -0
- package/build/svelte/stack/stack-props.d.ts +20 -0
- package/build/svelte/stack/stack-props.js +4 -0
- package/build/svelte/stack/stack-screen.svelte +205 -0
- package/build/svelte/stack/stack-screen.svelte.d.ts +4 -0
- package/build/svelte/tab-screen-props.d.ts +13 -0
- package/build/svelte/tab-screen-props.js +4 -0
- package/build/svelte/tab-screen.svelte +26 -0
- package/build/svelte/tab-screen.svelte.d.ts +4 -0
- package/build/svelte/tabs/index.d.ts +8 -0
- package/build/svelte/tabs/index.js +6 -0
- package/build/svelte/tabs/index.svelte +269 -0
- package/build/svelte/tabs/index.svelte.d.ts +8 -0
- package/build/svelte/tabs/tab-props.d.ts +7 -0
- package/build/svelte/tabs/tab-props.js +3 -0
- package/build/vue/composables/use-navigation.js +5 -1
- package/build/vue/drawer/index.js +36 -13
- package/build/vue/index.d.ts +3 -3
- package/build/vue/navigation-context.js +1 -1
- package/build/vue/stack/index.js +43 -18
- package/build/vue/tabs/index.js +65 -39
- package/build-ngc/angular/drawer/index.js +40 -18
- package/build-ngc/angular/drawer/index.js.map +1 -1
- package/build-ngc/angular/drawer-screen.directive.js +3 -3
- 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 +3 -3
- package/build-ngc/angular/navigation-context.service.js.map +1 -1
- package/build-ngc/angular/navigation-scope.directive.js +4 -4
- package/build-ngc/angular/navigation-scope.directive.js.map +1 -1
- package/build-ngc/angular/screen.directive.js +3 -3
- package/build-ngc/angular/screen.directive.js.map +1 -1
- package/build-ngc/angular/search-bar-ref.directive.js +4 -4
- package/build-ngc/angular/search-bar-ref.directive.js.map +1 -1
- package/build-ngc/angular/stack/index.d.ts +2 -0
- package/build-ngc/angular/stack/index.js +52 -14
- package/build-ngc/angular/stack/index.js.map +1 -1
- package/build-ngc/angular/tab-screen.directive.js +3 -3
- package/build-ngc/angular/tab-screen.directive.js.map +1 -1
- package/build-ngc/angular/tabs/index.d.ts +1 -1
- package/build-ngc/angular/tabs/index.js +43 -16
- 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 +6 -5
- package/build-ngc/core/index.js +7 -3
- 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.d.ts +2 -0
- package/build-ngc/core/stack-router-state/index.js +45 -0
- package/build-ngc/core/stack-router-state/index.js.map +1 -0
- 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.d.ts +1 -0
- package/build-ngc/core/tab-router-state/index.js +27 -2
- package/build-ngc/core/tab-router-state/index.js.map +1 -1
- package/package.json +44 -16
- package/src/angular/drawer/index.ts +75 -29
- 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 +91 -24
- package/src/angular/tab-screen.directive.ts +3 -1
- package/src/angular/tabs/index.ts +54 -16
- 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 +29 -5
- 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 +52 -0
- package/src/core/state-persistence/index.ts +13 -3
- package/src/core/tab-router-state/index.ts +35 -3
- package/src/react/drawer/index.ts +409 -330
- 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 +297 -220
- package/src/react/tab-screen.ts +3 -1
- package/src/react/tabs/index.ts +202 -139
- 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 +70 -0
- package/src/svelte/descriptor-subtree.ts +51 -0
- package/src/svelte/drawer/drawer-props.ts +28 -0
- package/src/svelte/drawer/index.svelte +559 -0
- package/src/svelte/drawer/index.ts +10 -0
- package/src/svelte/drawer-screen-props.ts +26 -0
- package/src/svelte/drawer-screen.svelte +25 -0
- package/src/svelte/fabric-tree.test-helper.ts +95 -0
- package/src/svelte/index.ts +83 -0
- package/src/svelte/linking.svelte.ts +72 -0
- package/src/svelte/navigation-context.ts +62 -0
- package/src/svelte/navigation-scope.svelte +27 -0
- package/src/svelte/registry-host.ts +26 -0
- package/src/svelte/runes/index.ts +17 -0
- package/src/svelte/runes/use-drawer-navigation.svelte.ts +31 -0
- package/src/svelte/runes/use-focus-effect.svelte.ts +41 -0
- package/src/svelte/runes/use-is-focused.svelte.ts +38 -0
- package/src/svelte/runes/use-navigation-state.svelte.ts +48 -0
- package/src/svelte/runes/use-navigation.svelte.ts +49 -0
- package/src/svelte/runes/use-route.svelte.ts +16 -0
- package/src/svelte/runes/use-stack-navigation.svelte.ts +34 -0
- package/src/svelte/runes/use-tab-navigation.svelte.ts +29 -0
- package/src/svelte/screen-props.ts +50 -0
- package/src/svelte/screen-registry.ts +98 -0
- package/src/svelte/screen.svelte +28 -0
- package/src/svelte/stack/index.svelte +261 -0
- package/src/svelte/stack/index.ts +11 -0
- package/src/svelte/stack/stack-props.ts +31 -0
- package/src/svelte/stack/stack-screen.svelte +205 -0
- package/src/svelte/svelte-ambient.d.ts +6 -0
- package/src/svelte/svelte-compile.test-helper.ts +147 -0
- package/src/svelte/tab-screen-props.ts +22 -0
- package/src/svelte/tab-screen.svelte +26 -0
- package/src/svelte/tabs/index.svelte +269 -0
- package/src/svelte/tabs/index.ts +10 -0
- package/src/svelte/tabs/tab-props.ts +13 -0
- 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 +128 -41
- 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 +108 -43
- package/src/vue/tab-screen.ts +3 -1
- package/src/vue/tabs/index.ts +109 -68
package/src/vue/stack/index.ts
CHANGED
|
@@ -13,7 +13,12 @@
|
|
|
13
13
|
import { defineComponent, h, shallowRef, useId } from '@vue/runtime-core';
|
|
14
14
|
import type { VNode } from '@vue/runtime-core';
|
|
15
15
|
import { descriptorToVue, normalizeVueAttrs } from '@symbiote-native/vue';
|
|
16
|
-
import {
|
|
16
|
+
import {
|
|
17
|
+
Platform,
|
|
18
|
+
debugNodeId,
|
|
19
|
+
dlog,
|
|
20
|
+
isSymbioteNode,
|
|
21
|
+
} from '@symbiote-native/engine';
|
|
17
22
|
import {
|
|
18
23
|
NAVIGATION_EVENT_BLUR,
|
|
19
24
|
NAVIGATION_EVENT_FOCUS,
|
|
@@ -35,6 +40,7 @@ import {
|
|
|
35
40
|
createNavigationEmitter,
|
|
36
41
|
isRecord,
|
|
37
42
|
navigatorReducer,
|
|
43
|
+
reconcileStackRoutes,
|
|
38
44
|
resolveScreenRenderPlan,
|
|
39
45
|
resolveStackProps,
|
|
40
46
|
} from '../../core';
|
|
@@ -47,7 +53,11 @@ import type {
|
|
|
47
53
|
} from '../../core';
|
|
48
54
|
import { NavigationScope, injectNavigationScope } from '../navigation-context';
|
|
49
55
|
import { Screen } from '../screen';
|
|
50
|
-
import type {
|
|
56
|
+
import type {
|
|
57
|
+
IScreenOptionsArgs,
|
|
58
|
+
IScreenProps,
|
|
59
|
+
IVueScreenOptions,
|
|
60
|
+
} from '../screen';
|
|
51
61
|
|
|
52
62
|
export type { INavigatorHandle } from '../../core';
|
|
53
63
|
|
|
@@ -61,7 +71,9 @@ export type IStackProps = {
|
|
|
61
71
|
// backTitleVisible defaults to `true` on both platforms per the codegen spec's own default
|
|
62
72
|
// (CT.WithDefault<boolean, 'true'>) - no ios/android divergence in v1 scope, so a single constant
|
|
63
73
|
// stands in for the per-platform injection point ISliderPlatform-style adapters use elsewhere.
|
|
64
|
-
const NAVIGATOR_PLATFORM: INavigatorPlatform = {
|
|
74
|
+
const NAVIGATOR_PLATFORM: INavigatorPlatform = {
|
|
75
|
+
defaultHeaderBackTitleVisible: true,
|
|
76
|
+
};
|
|
65
77
|
|
|
66
78
|
type IScreenRegistryEntry = {
|
|
67
79
|
component: IScreenProps['component'];
|
|
@@ -92,7 +104,9 @@ function asScreenOptionsOrResolver(value: unknown): IScreenProps['options'] {
|
|
|
92
104
|
return asScreenOptions(value);
|
|
93
105
|
}
|
|
94
106
|
|
|
95
|
-
function collectRegistry(
|
|
107
|
+
function collectRegistry(
|
|
108
|
+
vnodes: readonly VNode[],
|
|
109
|
+
): Map<string, IScreenRegistryEntry> {
|
|
96
110
|
const registry = new Map<string, IScreenRegistryEntry>();
|
|
97
111
|
for (const vnode of vnodes) {
|
|
98
112
|
if (vnode.type !== Screen || !isRecord(vnode.props)) continue;
|
|
@@ -114,7 +128,9 @@ function resolveScreenOptions(
|
|
|
114
128
|
screenOptions: IVueScreenOptions | undefined,
|
|
115
129
|
): IVueScreenOptions {
|
|
116
130
|
const own =
|
|
117
|
-
typeof entry.options === 'function'
|
|
131
|
+
typeof entry.options === 'function'
|
|
132
|
+
? entry.options(screenOptionsArgs)
|
|
133
|
+
: entry.options;
|
|
118
134
|
return { ...screenOptions, ...own };
|
|
119
135
|
}
|
|
120
136
|
|
|
@@ -167,25 +183,44 @@ const StackImpl = defineComponent<IStackProps>(
|
|
|
167
183
|
});
|
|
168
184
|
} else {
|
|
169
185
|
initialState = createInitialNavigatorState(
|
|
170
|
-
createRoute(
|
|
186
|
+
createRoute(
|
|
187
|
+
initialRouteName,
|
|
188
|
+
initialRegistry.get(initialRouteName)?.initialParams,
|
|
189
|
+
),
|
|
171
190
|
);
|
|
172
191
|
}
|
|
173
192
|
|
|
174
193
|
const state = shallowRef<INavigatorState>(initialState);
|
|
175
194
|
|
|
195
|
+
// The screen names the render pass below last saw in the slot. A PLAIN local, not a ref: it is
|
|
196
|
+
// a cache of what was just rendered, never a reactive source, so refreshing it from inside the
|
|
197
|
+
// render closure is not a state write during render (which would re-trigger the very render
|
|
198
|
+
// effect that produced it).
|
|
199
|
+
let registeredNames: readonly string[] = [];
|
|
200
|
+
|
|
201
|
+
// A <Stack.Screen> marker can vanish from the slot while its route is still in the pushed
|
|
202
|
+
// history, which would leave that entry with nothing to render (reconcileStackRoutes' header).
|
|
203
|
+
// Reconciling on READ rather than writing the ref keeps the repair out of the render pass; the
|
|
204
|
+
// dispatch below then persists it, so the next push builds on the pruned history.
|
|
205
|
+
function currentState(): INavigatorState {
|
|
206
|
+
return reconcileStackRoutes(state.value, registeredNames);
|
|
207
|
+
}
|
|
208
|
+
|
|
176
209
|
function dispatch(action: Parameters<typeof navigatorReducer>[1]): void {
|
|
177
|
-
state.value = navigatorReducer(
|
|
210
|
+
state.value = navigatorReducer(currentState(), action);
|
|
178
211
|
}
|
|
179
212
|
|
|
180
213
|
const handle: INavigatorHandle = {
|
|
181
|
-
push: (name, params) =>
|
|
214
|
+
push: (name, params) =>
|
|
215
|
+
dispatch({ type: 'push', route: createRoute(name, params) }),
|
|
182
216
|
pop: count => dispatch({ type: 'pop', count }),
|
|
183
217
|
popToTop: () => dispatch({ type: 'popToTop' }),
|
|
184
218
|
popTo: key => dispatch({ type: 'popTo', key }),
|
|
185
|
-
replace: (name, params) =>
|
|
219
|
+
replace: (name, params) =>
|
|
220
|
+
dispatch({ type: 'replace', route: createRoute(name, params) }),
|
|
186
221
|
setParams: (params, key) => dispatch({ type: 'setParams', key, params }),
|
|
187
222
|
reset: nextState => dispatch({ type: 'reset', state: nextState }),
|
|
188
|
-
canGoBack: () =>
|
|
223
|
+
canGoBack: () => currentState().routes.length > 1,
|
|
189
224
|
};
|
|
190
225
|
expose(handle);
|
|
191
226
|
|
|
@@ -195,18 +230,21 @@ const StackImpl = defineComponent<IStackProps>(
|
|
|
195
230
|
// useEffect, which must be deferred past render to avoid a descendant setState-during-render):
|
|
196
231
|
// Vue's provide/inject + shallowRef.value writes are safe to perform directly inside the
|
|
197
232
|
// render closure below, so the broadcast happens right there, once per render.
|
|
198
|
-
function broadcastState(): void {
|
|
199
|
-
for (const route of
|
|
200
|
-
emitterFor(route.key).emit(NAVIGATION_EVENT_STATE,
|
|
233
|
+
function broadcastState(current: INavigatorState): void {
|
|
234
|
+
for (const route of current.routes) {
|
|
235
|
+
emitterFor(route.key).emit(NAVIGATION_EVENT_STATE, current);
|
|
201
236
|
}
|
|
202
237
|
for (const routeKey of emitters.keys()) {
|
|
203
|
-
if (!
|
|
238
|
+
if (!current.routes.some(route => route.key === routeKey))
|
|
239
|
+
emitters.delete(routeKey);
|
|
204
240
|
}
|
|
205
241
|
}
|
|
206
242
|
|
|
207
243
|
return () => {
|
|
208
|
-
broadcastState();
|
|
209
244
|
const registry = collectRegistry(slots.default?.() ?? []);
|
|
245
|
+
registeredNames = [...registry.keys()];
|
|
246
|
+
const current = currentState();
|
|
247
|
+
broadcastState(current);
|
|
210
248
|
const screenOptions = asScreenOptions(attrs.screenOptions);
|
|
211
249
|
|
|
212
250
|
// Investigation instrumentation (flicker-on-focus bug): STACK_ON_FINISH_TRANSITIONING is
|
|
@@ -221,21 +259,31 @@ const StackImpl = defineComponent<IStackProps>(
|
|
|
221
259
|
},
|
|
222
260
|
});
|
|
223
261
|
|
|
224
|
-
const children =
|
|
262
|
+
const children = current.routes.map((route, index) => {
|
|
225
263
|
const entry = registry.get(route.name);
|
|
226
264
|
if (entry === undefined) {
|
|
227
265
|
dlog(`Stack: no screen registered for route name "${route.name}"`);
|
|
228
266
|
return null;
|
|
229
267
|
}
|
|
230
268
|
|
|
231
|
-
const screenOptionsArgs: IScreenOptionsArgs = {
|
|
232
|
-
|
|
233
|
-
|
|
269
|
+
const screenOptionsArgs: IScreenOptionsArgs = {
|
|
270
|
+
route,
|
|
271
|
+
navigation: handle,
|
|
272
|
+
};
|
|
273
|
+
const mergedOptions = resolveScreenOptions(
|
|
274
|
+
entry,
|
|
275
|
+
screenOptionsArgs,
|
|
276
|
+
screenOptions,
|
|
277
|
+
);
|
|
278
|
+
const activityState = computeActivityState(
|
|
279
|
+
index,
|
|
280
|
+
current.routes.length,
|
|
281
|
+
);
|
|
234
282
|
// Investigation instrumentation (flicker-on-focus bug): fires on EVERY Stack render, not
|
|
235
283
|
// just on transitions, so the log stream shows whether a route's activityState/index ever
|
|
236
284
|
// changes outside of a push/pop dispatch. Kept behind DEBUG, never removed.
|
|
237
285
|
dlog(
|
|
238
|
-
`Stack: render route "${route.name}" index=${index}/${
|
|
286
|
+
`Stack: render route "${route.name}" index=${index}/${current.routes.length - 1} ` +
|
|
239
287
|
`activityState=${activityState} at t=${Date.now()}`,
|
|
240
288
|
);
|
|
241
289
|
|
|
@@ -245,27 +293,36 @@ const StackImpl = defineComponent<IStackProps>(
|
|
|
245
293
|
const plan = resolveScreenRenderPlan({
|
|
246
294
|
screenId: route.key,
|
|
247
295
|
index,
|
|
248
|
-
routeCount:
|
|
296
|
+
routeCount: current.routes.length,
|
|
249
297
|
options: mergedOptions,
|
|
250
298
|
platform: NAVIGATOR_PLATFORM,
|
|
251
299
|
isAndroid: Platform.OS === 'android',
|
|
252
300
|
screenPassthrough: {
|
|
253
301
|
[SCREEN_ON_DISMISSED]: () => dispatch({ type: 'pop', count: 1 }),
|
|
254
|
-
[SCREEN_ON_HEADER_BACK_BUTTON_CLICKED]: () =>
|
|
302
|
+
[SCREEN_ON_HEADER_BACK_BUTTON_CLICKED]: () =>
|
|
303
|
+
dispatch({ type: 'pop', count: 1 }),
|
|
255
304
|
// onAppear/onDisappear are the definitive visibility boundary (post-transition-
|
|
256
305
|
// animation), so 'focus'/'blur' fire exactly once per transition; onWillAppear/
|
|
257
306
|
// onWillDisappear fire BEFORE the animation runs, so wiring them to emit() too would
|
|
258
307
|
// double-invoke useFocusEffect per transition - they only get a debug log here.
|
|
259
308
|
[SCREEN_ON_WILL_APPEAR]: () =>
|
|
260
|
-
dlog(
|
|
309
|
+
dlog(
|
|
310
|
+
`Stack: route "${route.name}" will appear at t=${Date.now()}`,
|
|
311
|
+
),
|
|
261
312
|
[SCREEN_ON_APPEAR]: () => {
|
|
262
|
-
dlog(
|
|
313
|
+
dlog(
|
|
314
|
+
`Stack: route "${route.name}" appeared (focus) at t=${Date.now()}`,
|
|
315
|
+
);
|
|
263
316
|
routeEmitter.emit(NAVIGATION_EVENT_FOCUS);
|
|
264
317
|
},
|
|
265
318
|
[SCREEN_ON_WILL_DISAPPEAR]: () =>
|
|
266
|
-
dlog(
|
|
319
|
+
dlog(
|
|
320
|
+
`Stack: route "${route.name}" will disappear at t=${Date.now()}`,
|
|
321
|
+
),
|
|
267
322
|
[SCREEN_ON_DISAPPEAR]: () => {
|
|
268
|
-
dlog(
|
|
323
|
+
dlog(
|
|
324
|
+
`Stack: route "${route.name}" disappeared (blur) at t=${Date.now()}`,
|
|
325
|
+
);
|
|
269
326
|
routeEmitter.emit(NAVIGATION_EVENT_BLUR);
|
|
270
327
|
},
|
|
271
328
|
},
|
|
@@ -292,7 +349,8 @@ const StackImpl = defineComponent<IStackProps>(
|
|
|
292
349
|
);
|
|
293
350
|
const appRef = searchBarOptions.ref;
|
|
294
351
|
if (appRef === undefined) return;
|
|
295
|
-
appRef.value =
|
|
352
|
+
appRef.value =
|
|
353
|
+
node === null ? null : buildSearchBarHandle(() => node);
|
|
296
354
|
},
|
|
297
355
|
}
|
|
298
356
|
: undefined,
|
|
@@ -317,22 +375,26 @@ const StackImpl = defineComponent<IStackProps>(
|
|
|
317
375
|
// sizing (see RNS_SCREEN_CONTENT_WRAPPER_VIEW_NAME's comment in core/constants.ts). A
|
|
318
376
|
// `push` screen doesn't need it, but a `formSheet` one is otherwise left with no content
|
|
319
377
|
// ever attached natively.
|
|
320
|
-
const content = h(
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
378
|
+
const content = h(
|
|
379
|
+
RNS_SCREEN_CONTENT_WRAPPER_VIEW_NAME,
|
|
380
|
+
plan.contentWrapperProps,
|
|
381
|
+
[
|
|
382
|
+
h(
|
|
383
|
+
NavigationScope,
|
|
384
|
+
{
|
|
385
|
+
value: {
|
|
386
|
+
route,
|
|
387
|
+
navigation: handle,
|
|
388
|
+
emitter: routeEmitter,
|
|
389
|
+
parent: ambientScopeRef?.value,
|
|
390
|
+
},
|
|
329
391
|
},
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
392
|
+
// No route/navigation props: the screen reads both through composables off the
|
|
393
|
+
// NavigationScope provided just above (useRoute / useStackNavigation).
|
|
394
|
+
() => h(entry.component),
|
|
395
|
+
),
|
|
396
|
+
],
|
|
397
|
+
);
|
|
336
398
|
|
|
337
399
|
// react-native-screens' own Screen.tsx swaps in a DIFFERENT Fabric component
|
|
338
400
|
// ('RNSModalScreen') for a modally-presented screen - its native updateLayoutMetrics:
|
|
@@ -356,7 +418,10 @@ const StackImpl = defineComponent<IStackProps>(
|
|
|
356
418
|
// RNSScreen.mm treats an unset/inactive nested screen as not yet pushed,
|
|
357
419
|
// leaving it parked at its pre-push transition position (off past the bottom
|
|
358
420
|
// edge) instead of its real, presented frame.
|
|
359
|
-
{
|
|
421
|
+
{
|
|
422
|
+
style: plan.innerScreenStyle,
|
|
423
|
+
activityState: plan.activityState,
|
|
424
|
+
},
|
|
360
425
|
[descriptorToVue(plan.headerConfig), content],
|
|
361
426
|
),
|
|
362
427
|
]),
|
package/src/vue/tab-screen.ts
CHANGED
|
@@ -14,7 +14,9 @@ export type ITabScreenOptionsArgs = {
|
|
|
14
14
|
navigation: ITabNavigatorHandle;
|
|
15
15
|
};
|
|
16
16
|
|
|
17
|
-
export type ITabScreenOptionsResolver = (
|
|
17
|
+
export type ITabScreenOptionsResolver = (
|
|
18
|
+
props: ITabScreenOptionsArgs,
|
|
19
|
+
) => ITabOptions;
|
|
18
20
|
|
|
19
21
|
export type ITabScreenProps = {
|
|
20
22
|
name: string;
|
package/src/vue/tabs/index.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
// Tab, the Vue lifecycle half. The focused-index router (tab-router-state) and the tab-bar
|
|
2
2
|
// Descriptor builder (render-tabs) live in @symbiote-native/navigation core, shared verbatim with
|
|
3
|
-
// the React/Angular adapters; here Vue supplies the lifecycle - a
|
|
4
|
-
// (Vue's twin of useReducer), useId for route-key generation, expose() for the
|
|
5
|
-
// handle - plus the descriptor bridge for the tab-bar leaf,
|
|
3
|
+
// the React/Angular adapters; here Vue supplies the lifecycle - a shallowRef for the dispatched
|
|
4
|
+
// router state (Vue's twin of useReducer), useId for route-key generation, expose() for the
|
|
5
|
+
// jumpTo/setParams handle - plus the descriptor bridge for the tab-bar leaf, like Stack's header
|
|
6
6
|
// config (stack.ts). Unlike Stack, a bottom-tabs bar is a PURE-JS UI: it paints ordinary
|
|
7
7
|
// `symbiote-view`/`symbiote-text` primitives via the shared render fn, so there is no
|
|
8
8
|
// react-native-screens ViewConfig to register here - Tab needs no `../register` import.
|
|
@@ -11,11 +11,9 @@ import {
|
|
|
11
11
|
defineComponent,
|
|
12
12
|
h,
|
|
13
13
|
nextTick,
|
|
14
|
-
onMounted,
|
|
15
14
|
onUnmounted,
|
|
16
15
|
shallowRef,
|
|
17
16
|
useId,
|
|
18
|
-
watch,
|
|
19
17
|
} from '@vue/runtime-core';
|
|
20
18
|
import type { VNode } from '@vue/runtime-core';
|
|
21
19
|
import { descriptorToVue, normalizeVueAttrs } from '@symbiote-native/vue';
|
|
@@ -28,6 +26,7 @@ import {
|
|
|
28
26
|
diffFocusedRoute,
|
|
29
27
|
isFocusedRoute,
|
|
30
28
|
isRecord,
|
|
29
|
+
reconcileTabRoutes,
|
|
31
30
|
renderTabBar,
|
|
32
31
|
tabRouterReducer,
|
|
33
32
|
} from '../../core';
|
|
@@ -37,6 +36,8 @@ import type {
|
|
|
37
36
|
ITabBarItemView,
|
|
38
37
|
ITabNavigatorHandle,
|
|
39
38
|
ITabOptions,
|
|
39
|
+
ITabRouterAction,
|
|
40
|
+
ITabRouterState,
|
|
40
41
|
} from '../../core';
|
|
41
42
|
import { NavigationScope, injectNavigationScope } from '../navigation-context';
|
|
42
43
|
import { TabScreen } from '../tab-screen';
|
|
@@ -78,7 +79,9 @@ function asTabOptionsOrResolver(value: unknown): ITabScreenProps['options'] {
|
|
|
78
79
|
return asTabOptions(value);
|
|
79
80
|
}
|
|
80
81
|
|
|
81
|
-
function collectRegistry(
|
|
82
|
+
function collectRegistry(
|
|
83
|
+
vnodes: readonly VNode[],
|
|
84
|
+
): Map<string, ITabRegistryEntry> {
|
|
82
85
|
const registry = new Map<string, ITabRegistryEntry>();
|
|
83
86
|
for (const vnode of vnodes) {
|
|
84
87
|
if (vnode.type !== TabScreen || !isRecord(vnode.props)) continue;
|
|
@@ -100,7 +103,9 @@ function resolveTabOptions(
|
|
|
100
103
|
screenOptions: ITabOptions | undefined,
|
|
101
104
|
): ITabOptions {
|
|
102
105
|
const own =
|
|
103
|
-
typeof entry.options === 'function'
|
|
106
|
+
typeof entry.options === 'function'
|
|
107
|
+
? entry.options(screenComponentProps)
|
|
108
|
+
: entry.options;
|
|
104
109
|
return { ...screenOptions, ...own };
|
|
105
110
|
}
|
|
106
111
|
|
|
@@ -118,7 +123,9 @@ const TabImpl = defineComponent<ITabProps>(
|
|
|
118
123
|
const ambientScopeRef = injectNavigationScope();
|
|
119
124
|
const routeIdPrefix = useId();
|
|
120
125
|
|
|
121
|
-
function buildRoutes(
|
|
126
|
+
function buildRoutes(
|
|
127
|
+
registry: Map<string, ITabRegistryEntry>,
|
|
128
|
+
): IRoute<unknown>[] {
|
|
122
129
|
return Array.from(registry.entries()).map(([name, entry]) => ({
|
|
123
130
|
key: `${routeIdPrefix}-${name}`,
|
|
124
131
|
name,
|
|
@@ -128,14 +135,36 @@ const TabImpl = defineComponent<ITabProps>(
|
|
|
128
135
|
|
|
129
136
|
const initialRegistry = collectRegistry(slots.default?.() ?? []);
|
|
130
137
|
const initialRoutes = buildRoutes(initialRegistry);
|
|
131
|
-
if (initialRoutes.length === 0)
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
)
|
|
138
|
+
if (initialRoutes.length === 0)
|
|
139
|
+
dlog('Tab: no <Tab.Screen> children registered');
|
|
140
|
+
|
|
141
|
+
// A <Tab.Screen> can appear or disappear after mount (a marker behind a v-if, a data-driven
|
|
142
|
+
// screen list), so the route list is re-collected from the slot on every render and the
|
|
143
|
+
// DISPATCHED state is reconciled against it (reconcileTabRoutes, core - it preserves each
|
|
144
|
+
// surviving route's key and accumulated params and keeps focus on the same route NAME) rather
|
|
145
|
+
// than staying frozen at whatever setup() first saw. Slot vnodes are not reactive state, so
|
|
146
|
+
// the render fn is the only place that can observe the registry change: the reconciliation
|
|
147
|
+
// happens there, as a pure local, and never writes back - a state write inside a derivation is
|
|
148
|
+
// an error in Vue, not a style choice. `null` means nothing has dispatched yet, which is what
|
|
149
|
+
// keeps initialRouteName honored when the markers arrive after the first render.
|
|
150
|
+
const dispatchedState = shallowRef<ITabRouterState | null>(null);
|
|
151
|
+
// Non-reactive cache of the route list the last render derived: dispatch() reduces over the
|
|
152
|
+
// RECONCILED state, so it needs the same list that paint used. A ref here would be a reactive
|
|
153
|
+
// write from the render fn, exactly what the comment above rules out.
|
|
154
|
+
let latestRoutes: readonly IRoute<unknown>[] = initialRoutes;
|
|
155
|
+
|
|
156
|
+
function resolveState(routes: readonly IRoute<unknown>[]): ITabRouterState {
|
|
157
|
+
const dispatched = dispatchedState.value;
|
|
158
|
+
return dispatched === null
|
|
159
|
+
? createInitialTabState(routes, asString(attrs.initialRouteName))
|
|
160
|
+
: reconcileTabRoutes(dispatched, routes);
|
|
161
|
+
}
|
|
136
162
|
|
|
137
|
-
function dispatch(action:
|
|
138
|
-
|
|
163
|
+
function dispatch(action: ITabRouterAction): void {
|
|
164
|
+
dispatchedState.value = tabRouterReducer(
|
|
165
|
+
resolveState(latestRoutes),
|
|
166
|
+
action,
|
|
167
|
+
);
|
|
139
168
|
}
|
|
140
169
|
|
|
141
170
|
const jumpTo = (name: string, params?: unknown): void =>
|
|
@@ -146,16 +175,6 @@ const TabImpl = defineComponent<ITabProps>(
|
|
|
146
175
|
const handle: ITabNavigatorHandle = { jumpTo, setParams };
|
|
147
176
|
expose(handle);
|
|
148
177
|
|
|
149
|
-
// Tab paints its own bar in pure JS - there is no native onAppear/onDisappear to hook (unlike
|
|
150
|
-
// Stack's RNSScreen), so focus/blur is synthesized here: emit 'focus' once the newly-focused
|
|
151
|
-
// route's content has mounted, 'blur' when it's about to be replaced or Tab itself unmounts.
|
|
152
|
-
// Keyed on the route KEY (not the route object) so a setParams-only change (new route object,
|
|
153
|
-
// same key) doesn't spuriously re-fire focus/blur - mirrors tabs.ts's React twin exactly,
|
|
154
|
-
// just expressed as a `watch` over the focused key instead of a useEffect dependency array.
|
|
155
|
-
function focusedKeyOf(current: typeof state.value): string | undefined {
|
|
156
|
-
return current.routes[current.index]?.key;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
178
|
// One emitter per route.key, created lazily and cached for the navigator's whole lifetime -
|
|
160
179
|
// mirrors stack.ts's own `emitters` map. This decouples emitter IDENTITY (stable, looked up
|
|
161
180
|
// by key, read by the render closure below when it builds a route's NavigationScope) from
|
|
@@ -179,45 +198,42 @@ const TabImpl = defineComponent<ITabProps>(
|
|
|
179
198
|
// Stack's RNSScreen), so focus/blur is synthesized here: emit 'focus' once the newly-focused
|
|
180
199
|
// route's content has mounted, 'blur' when it's about to be replaced or Tab itself unmounts.
|
|
181
200
|
// Keyed on the route KEY (not the route object) so a setParams-only change (new route object,
|
|
182
|
-
// same key) doesn't spuriously re-fire focus/blur
|
|
183
|
-
//
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
emitterFor(focusedRouteKey).emit(NAVIGATION_EVENT_FOCUS);
|
|
190
|
-
}
|
|
191
|
-
});
|
|
192
|
-
|
|
201
|
+
// same key) doesn't spuriously re-fire focus/blur.
|
|
202
|
+
//
|
|
203
|
+
// Driven from the render pass rather than a `watch` because the focused key follows the
|
|
204
|
+
// SLOT-collected route list too - a marker disappearing can move focus, and slot vnodes are
|
|
205
|
+
// not reactive state, so no watcher source can see that; only a render can. The diff therefore
|
|
206
|
+
// lives where the state is derived, and stays a plain local, never a reactive write.
|
|
207
|
+
//
|
|
193
208
|
// The bookkeeping (which key is focused) updates immediately so the render closure below
|
|
194
209
|
// always reads the right route; the actual emit is deferred to nextTick(), which resolves
|
|
195
210
|
// only after the CURRENT flush cycle fully drains - including the newly-focused screen's own
|
|
196
|
-
// onMounted (subscribing its useIsFocused/useFocusEffect listeners).
|
|
197
|
-
//
|
|
198
|
-
// effect, not after an arbitrary sibling
|
|
199
|
-
//
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
nextKey
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
211
|
+
// onMounted (subscribing its useIsFocused/useFocusEffect listeners). Emitting inline would
|
|
212
|
+
// reach zero subscribers; flush:'post' would not have been enough either, since it only
|
|
213
|
+
// orders a callback after THIS component's own render effect, not after an arbitrary sibling
|
|
214
|
+
// post-flush job (a just-mounted child's onMounted) queued after it in the same batch.
|
|
215
|
+
let focusedRouteKey: string | undefined;
|
|
216
|
+
|
|
217
|
+
function syncFocus(current: ITabRouterState): void {
|
|
218
|
+
const nextKey = current.routes[current.index]?.key;
|
|
219
|
+
const { blurKey, focusKey } = diffFocusedRoute(focusedRouteKey, nextKey);
|
|
220
|
+
if (blurKey === undefined && focusKey === undefined) return;
|
|
221
|
+
focusedRouteKey = nextKey;
|
|
222
|
+
nextTick(() => {
|
|
223
|
+
if (blurKey !== undefined) {
|
|
224
|
+
dlog(`Tab: route "${blurKey}" blurred at t=${Date.now()}`);
|
|
225
|
+
emitterFor(blurKey).emit(NAVIGATION_EVENT_BLUR);
|
|
226
|
+
}
|
|
227
|
+
if (focusKey !== undefined) {
|
|
228
|
+
dlog(`Tab: route "${focusKey}" focused at t=${Date.now()}`);
|
|
229
|
+
emitterFor(focusKey).emit(NAVIGATION_EVENT_FOCUS);
|
|
230
|
+
}
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
|
|
219
234
|
onUnmounted(() => {
|
|
220
|
-
if (focusedRouteKey !== undefined)
|
|
235
|
+
if (focusedRouteKey !== undefined)
|
|
236
|
+
emitterFor(focusedRouteKey).emit(NAVIGATION_EVENT_BLUR);
|
|
221
237
|
});
|
|
222
238
|
|
|
223
239
|
return () => {
|
|
@@ -226,17 +242,32 @@ const TabImpl = defineComponent<ITabProps>(
|
|
|
226
242
|
|
|
227
243
|
if (registry.size === 0) dlog('Tab: no <Tab.Screen> children registered');
|
|
228
244
|
|
|
229
|
-
const
|
|
245
|
+
const routes = buildRoutes(registry);
|
|
246
|
+
latestRoutes = routes;
|
|
247
|
+
const state = resolveState(routes);
|
|
248
|
+
syncFocus(state);
|
|
249
|
+
|
|
250
|
+
const focusedRoute: IRoute<unknown> | undefined =
|
|
251
|
+
state.routes[state.index];
|
|
230
252
|
|
|
231
|
-
const items: ITabBarItemView[] = state.
|
|
253
|
+
const items: ITabBarItemView[] = state.routes.map((route, index) => {
|
|
232
254
|
const entry = registry.get(route.name);
|
|
233
|
-
const focused = isFocusedRoute(index, state.
|
|
255
|
+
const focused = isFocusedRoute(index, state.index);
|
|
234
256
|
if (entry === undefined) {
|
|
235
257
|
dlog(`Tab: no screen registered for route name "${route.name}"`);
|
|
236
|
-
return {
|
|
258
|
+
return {
|
|
259
|
+
key: route.key,
|
|
260
|
+
focused,
|
|
261
|
+
label: route.name,
|
|
262
|
+
passthrough: {},
|
|
263
|
+
};
|
|
237
264
|
}
|
|
238
265
|
|
|
239
|
-
const options = resolveTabOptions(
|
|
266
|
+
const options = resolveTabOptions(
|
|
267
|
+
entry,
|
|
268
|
+
{ route, navigation: handle },
|
|
269
|
+
screenOptions,
|
|
270
|
+
);
|
|
240
271
|
|
|
241
272
|
return {
|
|
242
273
|
key: route.key,
|
|
@@ -254,7 +285,9 @@ const TabImpl = defineComponent<ITabProps>(
|
|
|
254
285
|
};
|
|
255
286
|
});
|
|
256
287
|
|
|
257
|
-
const focusedEntry = focusedRoute
|
|
288
|
+
const focusedEntry = focusedRoute
|
|
289
|
+
? registry.get(focusedRoute.name)
|
|
290
|
+
: undefined;
|
|
258
291
|
const focusedOptions: ITabOptions | undefined =
|
|
259
292
|
focusedEntry && focusedRoute
|
|
260
293
|
? resolveTabOptions(
|
|
@@ -265,7 +298,11 @@ const TabImpl = defineComponent<ITabProps>(
|
|
|
265
298
|
: screenOptions;
|
|
266
299
|
|
|
267
300
|
const tabBar = descriptorToVue(
|
|
268
|
-
renderTabBar({
|
|
301
|
+
renderTabBar({
|
|
302
|
+
items,
|
|
303
|
+
style: focusedOptions?.tabBarStyle,
|
|
304
|
+
passthrough: {},
|
|
305
|
+
}),
|
|
269
306
|
);
|
|
270
307
|
|
|
271
308
|
// Only the focused route's screen is ever mounted (unlike Stack, which keeps every pushed
|
|
@@ -291,7 +328,11 @@ const TabImpl = defineComponent<ITabProps>(
|
|
|
291
328
|
: null;
|
|
292
329
|
|
|
293
330
|
return h('symbiote-view', { style: TAB_ROOT_STYLE }, [
|
|
294
|
-
h(
|
|
331
|
+
h(
|
|
332
|
+
'symbiote-view',
|
|
333
|
+
{ style: TAB_CONTENT_STYLE },
|
|
334
|
+
content === null ? [] : [content],
|
|
335
|
+
),
|
|
295
336
|
tabBar,
|
|
296
337
|
]);
|
|
297
338
|
};
|