@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
|
@@ -17,7 +17,10 @@ function isFabricNode(value: unknown): value is IFabricNode {
|
|
|
17
17
|
return typeof value === 'object' && value !== null;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
export function walkLive(
|
|
20
|
+
export function walkLive(
|
|
21
|
+
root: unknown,
|
|
22
|
+
visit: (node: IFabricNode) => void,
|
|
23
|
+
): void {
|
|
21
24
|
if (!isFabricNode(root)) return;
|
|
22
25
|
visit(root);
|
|
23
26
|
for (const child of root.children ?? []) walkLive(child, visit);
|
|
@@ -31,7 +34,10 @@ export function findAllLive(root: unknown, viewName: string): IFabricNode[] {
|
|
|
31
34
|
return found;
|
|
32
35
|
}
|
|
33
36
|
|
|
34
|
-
export function findLive(
|
|
37
|
+
export function findLive(
|
|
38
|
+
root: unknown,
|
|
39
|
+
viewName: string,
|
|
40
|
+
): IFabricNode | undefined {
|
|
35
41
|
return findAllLive(root, viewName)[0];
|
|
36
42
|
}
|
|
37
43
|
|
|
@@ -39,7 +45,10 @@ export function countLive(root: unknown, viewName: string): number {
|
|
|
39
45
|
return findAllLive(root, viewName).length;
|
|
40
46
|
}
|
|
41
47
|
|
|
42
|
-
export function findLiveByTestId(
|
|
48
|
+
export function findLiveByTestId(
|
|
49
|
+
root: unknown,
|
|
50
|
+
testID: string,
|
|
51
|
+
): IFabricNode | undefined {
|
|
43
52
|
let found: IFabricNode | undefined;
|
|
44
53
|
walkLive(root, node => {
|
|
45
54
|
if (found === undefined && node.props?.testID === testID) found = node;
|
|
@@ -47,6 +56,31 @@ export function findLiveByTestId(root: unknown, testID: string): IFabricNode | u
|
|
|
47
56
|
return found;
|
|
48
57
|
}
|
|
49
58
|
|
|
59
|
+
const TEXT_CONTAINER_VIEW_NAMES = new Set(['RCTText', 'RCTVirtualText']);
|
|
60
|
+
|
|
61
|
+
// Every raw text committed under a parent that cannot hold one - an invalid Fabric child.
|
|
62
|
+
// The gap Svelte leaves between two sibling tags compiles to a ' ' text node, and the shim
|
|
63
|
+
// drops it exactly when the parent takes no raw text (dom-shim/text.ts, svelte-adapter-dom-shim
|
|
64
|
+
// §16b); inside an RCTText the same string is a real word separator and must survive. So the
|
|
65
|
+
// parent, not the string, is what makes a raw text legal, and this returns the ones that are
|
|
66
|
+
// not. Formatted `viewName > "text"` so a failure names the offender.
|
|
67
|
+
export function rawTextsOutsideTextContainer(root: unknown): string[] {
|
|
68
|
+
const found: string[] = [];
|
|
69
|
+
const visit = (node: unknown, parent: IFabricNode | undefined): void => {
|
|
70
|
+
if (!isFabricNode(node)) return;
|
|
71
|
+
const parentName = parent?.viewName;
|
|
72
|
+
if (
|
|
73
|
+
node.viewName === 'RCTRawText' &&
|
|
74
|
+
(parentName === undefined || !TEXT_CONTAINER_VIEW_NAMES.has(parentName))
|
|
75
|
+
) {
|
|
76
|
+
found.push(`${String(parentName)} > ${JSON.stringify(node.props?.text)}`);
|
|
77
|
+
}
|
|
78
|
+
for (const child of node.children ?? []) visit(child, node);
|
|
79
|
+
};
|
|
80
|
+
visit(root, undefined);
|
|
81
|
+
return found;
|
|
82
|
+
}
|
|
83
|
+
|
|
50
84
|
// A depth-indented `viewName` outline, for asserting the exact committed SHAPE (which is what
|
|
51
85
|
// catches a stray whitespace text node, an extra wrapper, or a missing header).
|
|
52
86
|
export function outline(root: unknown): string[] {
|
package/src/svelte/index.ts
CHANGED
|
@@ -16,7 +16,11 @@ export type {
|
|
|
16
16
|
ISvelteScreenOptions,
|
|
17
17
|
ISvelteSearchBarOptions,
|
|
18
18
|
} from './screen-props';
|
|
19
|
-
export type {
|
|
19
|
+
export type {
|
|
20
|
+
IScreenOptions,
|
|
21
|
+
IStackAnimation,
|
|
22
|
+
IStackPresentation,
|
|
23
|
+
} from '../core';
|
|
20
24
|
|
|
21
25
|
export {
|
|
22
26
|
useNavigation,
|
|
@@ -34,7 +38,10 @@ export type {
|
|
|
34
38
|
ITabNavigationHandle,
|
|
35
39
|
IDrawerNavigationHandle,
|
|
36
40
|
} from './runes';
|
|
37
|
-
export type {
|
|
41
|
+
export type {
|
|
42
|
+
INavigationScopeValue,
|
|
43
|
+
IAnyNavigatorHandle,
|
|
44
|
+
} from './navigation-context';
|
|
38
45
|
|
|
39
46
|
export { useLinkingIntegration } from './linking.svelte';
|
|
40
47
|
export type { ILinkingConfig, IScreenLinkingConfig } from '../core';
|
|
@@ -68,4 +75,9 @@ export type {
|
|
|
68
75
|
IDrawerScreenOptionsResolver,
|
|
69
76
|
IDrawerScreenProps,
|
|
70
77
|
} from './drawer-screen-props';
|
|
71
|
-
export type {
|
|
78
|
+
export type {
|
|
79
|
+
IDrawerOptions,
|
|
80
|
+
IDrawerScreenOptions,
|
|
81
|
+
IDrawerType,
|
|
82
|
+
IDrawerPosition,
|
|
83
|
+
} from '../core';
|
|
@@ -34,7 +34,10 @@ export function useLinkingIntegration(
|
|
|
34
34
|
|
|
35
35
|
let cancelled = false;
|
|
36
36
|
|
|
37
|
-
const applyRoute = (
|
|
37
|
+
const applyRoute = (
|
|
38
|
+
url: string,
|
|
39
|
+
dispatch: (name: string, params?: unknown) => void,
|
|
40
|
+
): void => {
|
|
38
41
|
const route = resolveRouteFromUrl(config, url);
|
|
39
42
|
if (route === null) {
|
|
40
43
|
dlog(`useLinkingIntegration: no route resolved for "${url}"`);
|
|
@@ -53,10 +56,13 @@ export function useLinkingIntegration(
|
|
|
53
56
|
dlog(`useLinkingIntegration: getInitialURL failed: ${String(error)}`);
|
|
54
57
|
});
|
|
55
58
|
|
|
56
|
-
const subscription: IEventSubscription = Linking.addEventListener(
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
59
|
+
const subscription: IEventSubscription = Linking.addEventListener(
|
|
60
|
+
'url',
|
|
61
|
+
(event: IUrlEvent) => {
|
|
62
|
+
dlog(`useLinkingIntegration: url event "${event.url}"`);
|
|
63
|
+
applyRoute(event.url, handle.push);
|
|
64
|
+
},
|
|
65
|
+
);
|
|
60
66
|
|
|
61
67
|
return () => {
|
|
62
68
|
cancelled = true;
|
|
@@ -12,7 +12,10 @@
|
|
|
12
12
|
import { setNavigationScope } from './navigation-context';
|
|
13
13
|
import type { INavigationScopeValue } from './navigation-context';
|
|
14
14
|
|
|
15
|
-
let {
|
|
15
|
+
let {
|
|
16
|
+
value,
|
|
17
|
+
children,
|
|
18
|
+
}: { value: INavigationScopeValue; children?: Snippet } = $props();
|
|
16
19
|
|
|
17
20
|
setNavigationScope({
|
|
18
21
|
get current(): INavigationScopeValue {
|
|
@@ -8,7 +8,9 @@ import type { INavigationHandle } from './use-navigation.svelte';
|
|
|
8
8
|
export type IDrawerNavigationHandle = IDrawerNavigatorHandle &
|
|
9
9
|
Pick<INavigationHandle, 'addListener' | 'getParent'>;
|
|
10
10
|
|
|
11
|
-
export function useDrawerNavigation(): {
|
|
11
|
+
export function useDrawerNavigation(): {
|
|
12
|
+
readonly current: IDrawerNavigationHandle;
|
|
13
|
+
} {
|
|
12
14
|
const navigation = useNavigation();
|
|
13
15
|
const narrowed = $derived.by<IDrawerNavigationHandle>(() => {
|
|
14
16
|
const value = navigation.current;
|
|
@@ -24,8 +24,14 @@ export function useFocusEffect(effect: () => (() => void) | void): void {
|
|
|
24
24
|
// once. The teardown also runs any still-pending cleanup, matching Vue's onUnmounted.
|
|
25
25
|
$effect(() => {
|
|
26
26
|
const { emitter } = untrack(() => scope.current);
|
|
27
|
-
const unsubscribeFocus = emitter.addListener(
|
|
28
|
-
|
|
27
|
+
const unsubscribeFocus = emitter.addListener(
|
|
28
|
+
NAVIGATION_EVENT_FOCUS,
|
|
29
|
+
runEffect,
|
|
30
|
+
);
|
|
31
|
+
const unsubscribeBlur = emitter.addListener(
|
|
32
|
+
NAVIGATION_EVENT_BLUR,
|
|
33
|
+
runCleanup,
|
|
34
|
+
);
|
|
29
35
|
return () => {
|
|
30
36
|
unsubscribeFocus();
|
|
31
37
|
unsubscribeBlur();
|
|
@@ -23,7 +23,9 @@ function isNavigatorState(value: unknown): value is INavigatorState {
|
|
|
23
23
|
return isRecord(value) && Array.isArray(value.routes);
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
export function useNavigationState<TResult>(
|
|
26
|
+
export function useNavigationState<TResult>(
|
|
27
|
+
selector: (state: INavigatorState) => TResult,
|
|
28
|
+
): {
|
|
27
29
|
readonly current: TResult;
|
|
28
30
|
} {
|
|
29
31
|
const scope = requireNavigationScope('useNavigationState');
|
|
@@ -8,12 +8,18 @@
|
|
|
8
8
|
// §21). A caller reads `useNavigation().current` inside a `$derived`/template/`$effect`, exactly
|
|
9
9
|
// like unwrapping Vue's `ComputedRef` via `.value`.
|
|
10
10
|
|
|
11
|
-
import type {
|
|
11
|
+
import type {
|
|
12
|
+
INavigationEventListener,
|
|
13
|
+
INavigationEventName,
|
|
14
|
+
} from '../../core';
|
|
12
15
|
import { requireNavigationScope } from '../navigation-context';
|
|
13
16
|
import type { IAnyNavigatorHandle } from '../navigation-context';
|
|
14
17
|
|
|
15
18
|
export type INavigationHandle = IAnyNavigatorHandle & {
|
|
16
|
-
addListener: (
|
|
19
|
+
addListener: (
|
|
20
|
+
event: INavigationEventName,
|
|
21
|
+
listener: INavigationEventListener,
|
|
22
|
+
) => () => void;
|
|
17
23
|
// Walks exactly ONE hop up the scope's `parent` chain to the enclosing navigator's handle -
|
|
18
24
|
// e.g. a Tab screen nested inside a Stack screen calling getParent() to push a new Stack route.
|
|
19
25
|
// Callers narrow the union themselves ('push' in parent, etc.). Deliberately NOT
|
|
@@ -28,7 +34,11 @@ export function useNavigation(): { readonly current: INavigationHandle } {
|
|
|
28
34
|
const scope = requireNavigationScope('useNavigation');
|
|
29
35
|
const handle = $derived.by<INavigationHandle>(() => {
|
|
30
36
|
const { navigation, emitter, parent } = scope.current;
|
|
31
|
-
return {
|
|
37
|
+
return {
|
|
38
|
+
...navigation,
|
|
39
|
+
addListener: emitter.addListener,
|
|
40
|
+
getParent: () => parent?.navigation,
|
|
41
|
+
};
|
|
32
42
|
});
|
|
33
43
|
|
|
34
44
|
return {
|
|
@@ -11,7 +11,9 @@ import type { INavigationHandle } from './use-navigation.svelte';
|
|
|
11
11
|
export type IStackNavigationHandle = INavigatorHandle &
|
|
12
12
|
Pick<INavigationHandle, 'addListener' | 'getParent'>;
|
|
13
13
|
|
|
14
|
-
export function useStackNavigation(): {
|
|
14
|
+
export function useStackNavigation(): {
|
|
15
|
+
readonly current: IStackNavigationHandle;
|
|
16
|
+
} {
|
|
15
17
|
const navigation = useNavigation();
|
|
16
18
|
const narrowed = $derived.by<IStackNavigationHandle>(() => {
|
|
17
19
|
const value = navigation.current;
|
|
@@ -23,7 +23,10 @@ export type ISvelteSearchBarOptions = ISearchBarOptions & {
|
|
|
23
23
|
ref?: { current: ISearchBarCommands | null };
|
|
24
24
|
};
|
|
25
25
|
|
|
26
|
-
export type ISvelteScreenOptions = Omit<
|
|
26
|
+
export type ISvelteScreenOptions = Omit<
|
|
27
|
+
IScreenOptions,
|
|
28
|
+
'headerSearchBarOptions'
|
|
29
|
+
> & {
|
|
27
30
|
headerSearchBarOptions?: ISvelteSearchBarOptions;
|
|
28
31
|
};
|
|
29
32
|
|
|
@@ -35,7 +38,9 @@ export type IScreenOptionsArgs = {
|
|
|
35
38
|
navigation: INavigatorHandle;
|
|
36
39
|
};
|
|
37
40
|
|
|
38
|
-
export type IScreenOptionsResolver = (
|
|
41
|
+
export type IScreenOptionsResolver = (
|
|
42
|
+
args: IScreenOptionsArgs,
|
|
43
|
+
) => ISvelteScreenOptions;
|
|
39
44
|
|
|
40
45
|
export type IScreenProps = {
|
|
41
46
|
name: string;
|
|
@@ -45,7 +45,9 @@ export type IScreenCollector<TOptions> = {
|
|
|
45
45
|
const SCREEN_COLLECTOR_KEY = Symbol('symbiote-navigation-screen-collector');
|
|
46
46
|
|
|
47
47
|
// Called once from a navigator's own init, before it renders the children snippet.
|
|
48
|
-
export function setScreenCollector<TOptions>(
|
|
48
|
+
export function setScreenCollector<TOptions>(
|
|
49
|
+
collector: IScreenCollector<TOptions>,
|
|
50
|
+
): void {
|
|
49
51
|
setContext(SCREEN_COLLECTOR_KEY, collector);
|
|
50
52
|
}
|
|
51
53
|
|
|
@@ -55,9 +57,13 @@ export function collectScreen<TOptions>(
|
|
|
55
57
|
kind: INavigatorKind,
|
|
56
58
|
screen: IRegisteredScreen<TOptions>,
|
|
57
59
|
): void {
|
|
58
|
-
const collector = getContext<IScreenCollector<TOptions> | undefined>(
|
|
60
|
+
const collector = getContext<IScreenCollector<TOptions> | undefined>(
|
|
61
|
+
SCREEN_COLLECTOR_KEY,
|
|
62
|
+
);
|
|
59
63
|
if (collector === undefined) {
|
|
60
|
-
dlog(
|
|
64
|
+
dlog(
|
|
65
|
+
`navigation: <${kind} screen "${screen.name}"> rendered outside any navigator, ignored`,
|
|
66
|
+
);
|
|
61
67
|
return;
|
|
62
68
|
}
|
|
63
69
|
if (collector.kind !== kind) {
|
|
@@ -48,7 +48,11 @@
|
|
|
48
48
|
import { hostProps } from '../attachments';
|
|
49
49
|
import { getNavigationScope } from '../navigation-context';
|
|
50
50
|
import { SCREEN_REGISTRY_HOST_PROPS } from '../registry-host';
|
|
51
|
-
import {
|
|
51
|
+
import {
|
|
52
|
+
setScreenCollector,
|
|
53
|
+
toRegistry,
|
|
54
|
+
withoutScreen,
|
|
55
|
+
} from '../screen-registry';
|
|
52
56
|
import type { IRegisteredScreen } from '../screen-registry';
|
|
53
57
|
import type { IScreenProps, ISvelteScreenOptions } from '../screen-props';
|
|
54
58
|
import StackScreen from './stack-screen.svelte';
|
|
@@ -100,7 +104,11 @@
|
|
|
100
104
|
dlog('Stack: no <Stack.Screen> children registered');
|
|
101
105
|
// Deliberately NOT memoized: markers may still be registering, and the next derivation run
|
|
102
106
|
// should get a real initial route rather than being stuck on this placeholder.
|
|
103
|
-
return createInitialNavigatorState({
|
|
107
|
+
return createInitialNavigatorState({
|
|
108
|
+
key: routeIdPrefix,
|
|
109
|
+
name: '',
|
|
110
|
+
params: undefined,
|
|
111
|
+
});
|
|
104
112
|
}
|
|
105
113
|
seededState = createInitialNavigatorState(
|
|
106
114
|
createRoute(startRouteName, registry.get(startRouteName)?.initialParams),
|
|
@@ -114,7 +122,9 @@
|
|
|
114
122
|
// derivation because it is PURE - it hands back the same state reference when nothing changed, so
|
|
115
123
|
// it writes no state and cannot re-trigger itself; `dispatch` below then persists the pruned
|
|
116
124
|
// history, since it reduces over this value rather than over `pushedState`.
|
|
117
|
-
const state = $derived(
|
|
125
|
+
const state = $derived(
|
|
126
|
+
reconcileStackRoutes(pushedState ?? seedState(), [...registry.keys()]),
|
|
127
|
+
);
|
|
118
128
|
|
|
119
129
|
function dispatch(action: INavigatorAction): void {
|
|
120
130
|
pushedState = navigatorReducer(state, action);
|
|
@@ -185,7 +195,8 @@
|
|
|
185
195
|
emitterFor(route.key).emit(NAVIGATION_EVENT_STATE, current);
|
|
186
196
|
}
|
|
187
197
|
for (const routeKey of [...emitters.keys()]) {
|
|
188
|
-
if (!current.routes.some(route => route.key === routeKey))
|
|
198
|
+
if (!current.routes.some(route => route.key === routeKey))
|
|
199
|
+
emitters.delete(routeKey);
|
|
189
200
|
}
|
|
190
201
|
});
|
|
191
202
|
|
|
@@ -196,7 +207,8 @@
|
|
|
196
207
|
// never removed.
|
|
197
208
|
const stackProps = resolveStackProps({
|
|
198
209
|
passthrough: {
|
|
199
|
-
[STACK_ON_FINISH_TRANSITIONING]: () =>
|
|
210
|
+
[STACK_ON_FINISH_TRANSITIONING]: () =>
|
|
211
|
+
dlog(`Stack: onFinishTransitioning at t=${Date.now()}`),
|
|
200
212
|
},
|
|
201
213
|
});
|
|
202
214
|
|
|
@@ -221,4 +233,29 @@
|
|
|
221
233
|
}
|
|
222
234
|
</script>
|
|
223
235
|
|
|
224
|
-
<symbiote-view p={STACK_ROOT_PROPS}
|
|
236
|
+
<symbiote-view p={STACK_ROOT_PROPS}>
|
|
237
|
+
<symbiote-text p={SCREEN_REGISTRY_HOST_PROPS}>
|
|
238
|
+
{@render children?.()}
|
|
239
|
+
</symbiote-text>
|
|
240
|
+
<svelte:element
|
|
241
|
+
this={RNS_SCREEN_STACK_VIEW_NAME}
|
|
242
|
+
{@attach hostProps(stackProps)}
|
|
243
|
+
>
|
|
244
|
+
{#each state.routes as route, index (route.key)}
|
|
245
|
+
{@const screenComponent = componentFor(route)}
|
|
246
|
+
{#if screenComponent !== undefined}
|
|
247
|
+
<StackScreen
|
|
248
|
+
{route}
|
|
249
|
+
{index}
|
|
250
|
+
routeCount={state.routes.length}
|
|
251
|
+
options={optionsFor(route)}
|
|
252
|
+
navigation={handle}
|
|
253
|
+
emitter={emitterFor(route.key)}
|
|
254
|
+
parentScope={parentScope?.current}
|
|
255
|
+
component={screenComponent}
|
|
256
|
+
onPopRequested={popOne}
|
|
257
|
+
/>
|
|
258
|
+
{/if}
|
|
259
|
+
{/each}
|
|
260
|
+
</svelte:element>
|
|
261
|
+
</symbiote-view>
|
|
@@ -5,7 +5,9 @@
|
|
|
5
5
|
// (CT.WithDefault<boolean, 'true'>) - no ios/android divergence in v1 scope, so a single
|
|
6
6
|
// constant stands in for the per-platform injection point ISliderPlatform-style adapters use
|
|
7
7
|
// elsewhere.
|
|
8
|
-
const NAVIGATOR_PLATFORM: INavigatorPlatform = {
|
|
8
|
+
const NAVIGATOR_PLATFORM: INavigatorPlatform = {
|
|
9
|
+
defaultHeaderBackTitleVisible: true,
|
|
10
|
+
};
|
|
9
11
|
|
|
10
12
|
// react-native-screens' RNSScreenStackHeaderConfig.mm requires every header child to be an
|
|
11
13
|
// RNSScreenStackHeaderSubview; `type: 'searchBar'` is how it knows which slot this one fills.
|
|
@@ -24,9 +26,9 @@
|
|
|
24
26
|
// compiles through Svelte's generic setAttribute path and never the custom-element property-SET
|
|
25
27
|
// path the object bag depends on - see ../attachments.ts for the full reasoning.
|
|
26
28
|
//
|
|
27
|
-
//
|
|
28
|
-
//
|
|
29
|
-
// of a react-native-screens view.
|
|
29
|
+
// Whitespace between sibling tags in the per-route tree is inert: the shim drops a
|
|
30
|
+
// whitespace-only text node whose parent takes no raw text, so it never becomes an RCTRawText
|
|
31
|
+
// child of a react-native-screens view (dom-shim/text.ts, svelte-adapter-dom-shim skill §16b).
|
|
30
32
|
import { Platform, dlog } from '@symbiote-native/engine';
|
|
31
33
|
import {
|
|
32
34
|
NAVIGATION_EVENT_BLUR,
|
|
@@ -86,13 +88,19 @@
|
|
|
86
88
|
[SCREEN_ON_WILL_APPEAR]: () =>
|
|
87
89
|
dlog(`Stack: route "${route.name}" will appear at t=${Date.now()}`),
|
|
88
90
|
[SCREEN_ON_APPEAR]: () => {
|
|
89
|
-
dlog(
|
|
91
|
+
dlog(
|
|
92
|
+
`Stack: route "${route.name}" appeared (focus) at t=${Date.now()}`,
|
|
93
|
+
);
|
|
90
94
|
emitter.emit(NAVIGATION_EVENT_FOCUS);
|
|
91
95
|
},
|
|
92
96
|
[SCREEN_ON_WILL_DISAPPEAR]: () =>
|
|
93
|
-
dlog(
|
|
97
|
+
dlog(
|
|
98
|
+
`Stack: route "${route.name}" will disappear at t=${Date.now()}`,
|
|
99
|
+
),
|
|
94
100
|
[SCREEN_ON_DISAPPEAR]: () => {
|
|
95
|
-
dlog(
|
|
101
|
+
dlog(
|
|
102
|
+
`Stack: route "${route.name}" disappeared (blur) at t=${Date.now()}`,
|
|
103
|
+
);
|
|
96
104
|
emitter.emit(NAVIGATION_EVENT_BLUR);
|
|
97
105
|
},
|
|
98
106
|
},
|
|
@@ -131,7 +139,9 @@
|
|
|
131
139
|
// controller to attach to, so the header silently never renders. activityState mirrors the
|
|
132
140
|
// outer screen's own value - RNSScreen.mm treats an unset/inactive nested screen as not yet
|
|
133
141
|
// pushed, leaving it parked at its pre-push transition position.
|
|
134
|
-
const innerStackProps = $derived<Record<string, unknown>>({
|
|
142
|
+
const innerStackProps = $derived<Record<string, unknown>>({
|
|
143
|
+
style: plan.innerStackStyle,
|
|
144
|
+
});
|
|
135
145
|
const innerScreenProps = $derived<Record<string, unknown>>({
|
|
136
146
|
style: plan.innerScreenStyle,
|
|
137
147
|
activityState: plan.activityState,
|
|
@@ -145,4 +155,51 @@
|
|
|
145
155
|
});
|
|
146
156
|
</script>
|
|
147
157
|
|
|
148
|
-
{#snippet chrome()}
|
|
158
|
+
{#snippet chrome()}
|
|
159
|
+
<svelte:element
|
|
160
|
+
this={RNS_SCREEN_STACK_HEADER_CONFIG_VIEW_NAME}
|
|
161
|
+
{@attach hostProps(plan.headerConfig.props)}
|
|
162
|
+
>
|
|
163
|
+
{#if plan.searchBarProps !== undefined}
|
|
164
|
+
<svelte:element
|
|
165
|
+
this={RNS_SCREEN_STACK_HEADER_SUBVIEW_VIEW_NAME}
|
|
166
|
+
{@attach hostProps(HEADER_SUBVIEW_PROPS)}
|
|
167
|
+
>
|
|
168
|
+
<svelte:element
|
|
169
|
+
this={RNS_SEARCH_BAR_VIEW_NAME}
|
|
170
|
+
{@attach hostProps(plan.searchBarProps)}
|
|
171
|
+
{@attach searchBarRef(searchBarOptions?.ref)}
|
|
172
|
+
/>
|
|
173
|
+
</svelte:element>
|
|
174
|
+
{/if}
|
|
175
|
+
</svelte:element>
|
|
176
|
+
<svelte:element
|
|
177
|
+
this={RNS_SCREEN_CONTENT_WRAPPER_VIEW_NAME}
|
|
178
|
+
{@attach hostProps(plan.contentWrapperProps)}
|
|
179
|
+
>
|
|
180
|
+
<NavigationScope value={scopeValue}>
|
|
181
|
+
<ScreenComponent />
|
|
182
|
+
</NavigationScope>
|
|
183
|
+
</svelte:element>
|
|
184
|
+
{/snippet}
|
|
185
|
+
|
|
186
|
+
<svelte:element
|
|
187
|
+
this={plan.screenViewName}
|
|
188
|
+
{@attach hostProps(plan.screenProps)}
|
|
189
|
+
>
|
|
190
|
+
{#if plan.inModal}
|
|
191
|
+
<svelte:element
|
|
192
|
+
this={RNS_SCREEN_STACK_VIEW_NAME}
|
|
193
|
+
{@attach hostProps(innerStackProps)}
|
|
194
|
+
>
|
|
195
|
+
<svelte:element
|
|
196
|
+
this={RNS_SCREEN_VIEW_NAME}
|
|
197
|
+
{@attach hostProps(innerScreenProps)}
|
|
198
|
+
>
|
|
199
|
+
{@render chrome()}
|
|
200
|
+
</svelte:element>
|
|
201
|
+
</svelte:element>
|
|
202
|
+
{:else}
|
|
203
|
+
{@render chrome()}
|
|
204
|
+
{/if}
|
|
205
|
+
</svelte:element>
|
|
@@ -36,10 +36,11 @@ const {
|
|
|
36
36
|
compileSvelteModuleFile: (source: string, filename: string) => string;
|
|
37
37
|
} = metroSvelteTransformer;
|
|
38
38
|
|
|
39
|
-
const COMPILE_OPTIONS = {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
39
|
+
const COMPILE_OPTIONS = {
|
|
40
|
+
generate: 'client',
|
|
41
|
+
fragments: 'tree',
|
|
42
|
+
css: 'external',
|
|
43
|
+
} as const;
|
|
43
44
|
const SVELTE_SPECIFIER_PATTERN = /(['"])(\.{1,2}\/[^'"]*\.svelte)\1/g;
|
|
44
45
|
|
|
45
46
|
export type ISvelteHarness = {
|
|
@@ -47,8 +48,6 @@ export type ISvelteHarness = {
|
|
|
47
48
|
compileFile(sveltePath: string): string;
|
|
48
49
|
// Compile an inline fixture written into `dir`, so its own relative imports resolve from there.
|
|
49
50
|
compileSource(dir: string, name: string, source: string): string;
|
|
50
|
-
// The number of stray single-space text entries across everything compiled so far - must be 0.
|
|
51
|
-
strayWhitespaceCount(): number;
|
|
52
51
|
cleanup(): void;
|
|
53
52
|
};
|
|
54
53
|
|
|
@@ -58,7 +57,6 @@ export function createSvelteHarness(
|
|
|
58
57
|
): ISvelteHarness {
|
|
59
58
|
const written: string[] = [];
|
|
60
59
|
const compiledBySource = new Map<string, string>();
|
|
61
|
-
let strayWhitespace = 0;
|
|
62
60
|
|
|
63
61
|
function outPathFor(sveltePath: string): string {
|
|
64
62
|
const stem = basename(sveltePath).replace(/\./g, '-');
|
|
@@ -83,13 +81,21 @@ export function createSvelteHarness(
|
|
|
83
81
|
},
|
|
84
82
|
);
|
|
85
83
|
for (const [specifier, target] of Object.entries(aliases)) {
|
|
86
|
-
rewritten = rewritten
|
|
87
|
-
|
|
84
|
+
rewritten = rewritten
|
|
85
|
+
.split(`'${specifier}'`)
|
|
86
|
+
.join(`'${relativeSpecifier(outPath, target)}'`);
|
|
87
|
+
rewritten = rewritten
|
|
88
|
+
.split(`"${specifier}"`)
|
|
89
|
+
.join(`"${relativeSpecifier(outPath, target)}"`);
|
|
88
90
|
}
|
|
89
91
|
return rewritten;
|
|
90
92
|
}
|
|
91
93
|
|
|
92
|
-
function emit(
|
|
94
|
+
function emit(
|
|
95
|
+
sveltePath: string,
|
|
96
|
+
source: string,
|
|
97
|
+
isRuneModule: boolean,
|
|
98
|
+
): string {
|
|
93
99
|
const outPath = outPathFor(sveltePath);
|
|
94
100
|
// Registered BEFORE compiling so a cycle between two components terminates.
|
|
95
101
|
compiledBySource.set(sveltePath, outPath);
|
|
@@ -98,7 +104,6 @@ export function createSvelteHarness(
|
|
|
98
104
|
const code = isRuneModule
|
|
99
105
|
? compileSvelteModuleFile(source, `${filename}.ts`)
|
|
100
106
|
: compile(source, { ...COMPILE_OPTIONS, filename }).js.code;
|
|
101
|
-
strayWhitespace += (code.match(STRAY_WHITESPACE_PATTERN) ?? []).length;
|
|
102
107
|
writeFileSync(outPath, rewrite(code, outPath));
|
|
103
108
|
return outPath;
|
|
104
109
|
}
|
|
@@ -121,7 +126,6 @@ export function createSvelteHarness(
|
|
|
121
126
|
compileSource(dir, name, source) {
|
|
122
127
|
return emit(join(dir, `${name}.svelte`), source, false);
|
|
123
128
|
},
|
|
124
|
-
strayWhitespaceCount: () => strayWhitespace,
|
|
125
129
|
cleanup() {
|
|
126
130
|
for (const file of written) rmSync(file, { force: true });
|
|
127
131
|
written.length = 0;
|
|
@@ -10,7 +10,9 @@ export type ITabScreenOptionsArgs = {
|
|
|
10
10
|
navigation: ITabNavigatorHandle;
|
|
11
11
|
};
|
|
12
12
|
|
|
13
|
-
export type ITabScreenOptionsResolver = (
|
|
13
|
+
export type ITabScreenOptionsResolver = (
|
|
14
|
+
args: ITabScreenOptionsArgs,
|
|
15
|
+
) => ITabOptions;
|
|
14
16
|
|
|
15
17
|
export type ITabScreenProps = {
|
|
16
18
|
name: string;
|
|
@@ -48,7 +48,11 @@
|
|
|
48
48
|
import type { INavigationScopeValue } from '../navigation-context';
|
|
49
49
|
import NavigationScope from '../navigation-scope.svelte';
|
|
50
50
|
import { SCREEN_REGISTRY_HOST_PROPS } from '../registry-host';
|
|
51
|
-
import {
|
|
51
|
+
import {
|
|
52
|
+
setScreenCollector,
|
|
53
|
+
toRegistry,
|
|
54
|
+
withoutScreen,
|
|
55
|
+
} from '../screen-registry';
|
|
52
56
|
import type { IRegisteredScreen } from '../screen-registry';
|
|
53
57
|
import type { ITabScreenProps } from '../tab-screen-props';
|
|
54
58
|
import type { ITabProps } from './tab-props';
|
|
@@ -94,7 +98,8 @@
|
|
|
94
98
|
let dispatchedState = $state.raw<ITabRouterState | null>(null);
|
|
95
99
|
|
|
96
100
|
const state = $derived.by<ITabRouterState>(() => {
|
|
97
|
-
if (registeredRoutes.length === 0)
|
|
101
|
+
if (registeredRoutes.length === 0)
|
|
102
|
+
dlog('Tab: no <Tab.Screen> children registered');
|
|
98
103
|
const dispatched = dispatchedState;
|
|
99
104
|
return dispatched === null
|
|
100
105
|
? createInitialTabState(registeredRoutes, initialRouteName)
|
|
@@ -207,29 +212,33 @@
|
|
|
207
212
|
);
|
|
208
213
|
|
|
209
214
|
const tabBar = $derived.by<IDescriptor>(() =>
|
|
210
|
-
renderTabBar({
|
|
215
|
+
renderTabBar({
|
|
216
|
+
items,
|
|
217
|
+
style: focusedOptions?.tabBarStyle,
|
|
218
|
+
passthrough: {},
|
|
219
|
+
}),
|
|
211
220
|
);
|
|
212
221
|
|
|
213
222
|
// Only the focused route's screen is ever mounted (unlike Stack, which keeps every pushed route
|
|
214
223
|
// alive), so a fresh NavigationScope per focus change is sufficient - the previous screen's
|
|
215
224
|
// whole subtree (and any listeners it registered) is torn down by an ordinary unmount when
|
|
216
225
|
// focus moves on.
|
|
217
|
-
const focusedScreen = $derived.by<
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
);
|
|
226
|
+
const focusedScreen = $derived.by<
|
|
227
|
+
{ scope: INavigationScopeValue; component: Component } | undefined
|
|
228
|
+
>(() => {
|
|
229
|
+
if (focusedRoute === undefined) return undefined;
|
|
230
|
+
const entry = registry.get(focusedRoute.name);
|
|
231
|
+
if (entry === undefined) return undefined;
|
|
232
|
+
return {
|
|
233
|
+
component: entry.component,
|
|
234
|
+
scope: {
|
|
235
|
+
route: focusedRoute,
|
|
236
|
+
navigation: handle,
|
|
237
|
+
emitter: emitterFor(focusedRoute.key),
|
|
238
|
+
parent: parentScope?.current,
|
|
239
|
+
},
|
|
240
|
+
};
|
|
241
|
+
});
|
|
233
242
|
|
|
234
243
|
// The bar's ROOT stays a literal template tag (so `bind:this` has a statically known tag) and
|
|
235
244
|
// only its children go through the Descriptor bridge - the uniform shape every Svelte component
|
|
@@ -244,4 +253,17 @@
|
|
|
244
253
|
});
|
|
245
254
|
</script>
|
|
246
255
|
|
|
247
|
-
<symbiote-view p={TAB_ROOT_PROPS}
|
|
256
|
+
<symbiote-view p={TAB_ROOT_PROPS}>
|
|
257
|
+
<symbiote-text p={SCREEN_REGISTRY_HOST_PROPS}>
|
|
258
|
+
{@render children?.()}
|
|
259
|
+
</symbiote-text>
|
|
260
|
+
<symbiote-view p={TAB_CONTENT_PROPS}>
|
|
261
|
+
{#if focusedScreen !== undefined}
|
|
262
|
+
{@const FocusedComponent = focusedScreen.component}
|
|
263
|
+
<NavigationScope value={focusedScreen.scope}>
|
|
264
|
+
<FocusedComponent />
|
|
265
|
+
</NavigationScope>
|
|
266
|
+
{/if}
|
|
267
|
+
</symbiote-view>
|
|
268
|
+
<symbiote-view p={tabBar.props} bind:this={tabBarHost} />
|
|
269
|
+
</symbiote-view>
|