@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
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export declare const Stack: import("svelte").Component<import("./stack-props").IStackProps, {
|
|
2
|
+
push: (name: string, params?: unknown) => void;
|
|
3
|
+
pop: (count?: number) => void;
|
|
4
|
+
popToTop: () => void;
|
|
5
|
+
popTo: (key: string) => void;
|
|
6
|
+
replace: (name: string, params?: unknown) => void;
|
|
7
|
+
setParams: (params: unknown, key?: string) => void;
|
|
8
|
+
reset: (nextState: import("../../core").INavigatorState) => void;
|
|
9
|
+
canGoBack: () => boolean;
|
|
10
|
+
}, ""> & {
|
|
11
|
+
Screen: import("svelte").Component<import("..").IScreenProps, {}, "">;
|
|
12
|
+
};
|
|
13
|
+
export type { INavigatorHandle } from '../../core';
|
|
14
|
+
export type { IStackProps } from './stack-props';
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
// `Stack.Screen` alongside the standalone `Screen` export, matching React's and Vue's barrels.
|
|
2
|
+
// A compiled Svelte component is an ordinary function object, so attaching the marker to it is
|
|
3
|
+
// the same Object.assign the other entries do - and `<Stack.Screen />` resolves through Svelte's
|
|
4
|
+
// member-expression component tags (verified against the real compiler).
|
|
5
|
+
import StackImpl from './index.svelte';
|
|
6
|
+
import Screen from '../screen.svelte';
|
|
7
|
+
export const Stack = Object.assign(StackImpl, { Screen });
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
// Stack, the Svelte lifecycle half. The route-stack transitions (navigator-state) and the
|
|
3
|
+
// options/props folds (screen-options, render-stack) live in @symbiote-native/navigation core,
|
|
4
|
+
// shared verbatim with the React/Vue/Angular entries; here Svelte supplies the lifecycle -
|
|
5
|
+
// `$state.raw` for the pushed-route stack (its twin of useReducer: reassign from the same pure
|
|
6
|
+
// reducer), a module counter for route-key generation, `export function`s for the push/pop/
|
|
7
|
+
// replace navigator handle (Svelte's twin of Vue's expose(), reachable via `bind:this`) - plus
|
|
8
|
+
// the react-native-screens chrome per route (stack-screen.svelte). Pushing/popping a route is
|
|
9
|
+
// an ordinary child mount/unmount: RNSScreenStack diffs its RNSScreen children natively, so no
|
|
10
|
+
// imperative native command is needed here at all. Neither this nor the Screen marker imports
|
|
11
|
+
// react-native-screens' own React components (ScreenStack.tsx et al - hooks, crashes a
|
|
12
|
+
// non-React adapter); the native views are driven directly through the ViewConfig ../../register
|
|
13
|
+
// registers. See CLAUDE.md <third_party_rn_packages_are_react_only>.
|
|
14
|
+
//
|
|
15
|
+
// HOW SCREENS ARE DISCOVERED: not by reading `children`, which Svelte hands over as an opaque
|
|
16
|
+
// Snippet - the markers register themselves through the context collector (../screen-registry.ts)
|
|
17
|
+
// while the snippet renders, and the route list derives from that. The snippet is rendered
|
|
18
|
+
// inside a collapsed `symbiote-text` (../registry-host.ts) so whitespace between two markers
|
|
19
|
+
// can never become an illegal raw-text child of a native view.
|
|
20
|
+
|
|
21
|
+
// Route keys must be unique per Stack INSTANCE (Svelte has no useId); a module counter is the
|
|
22
|
+
// same mechanism the Angular entry uses.
|
|
23
|
+
let stackInstanceCounter = 0;
|
|
24
|
+
|
|
25
|
+
const STACK_ROOT_PROPS: Record<string, unknown> = { style: { flex: 1 } };
|
|
26
|
+
</script>
|
|
27
|
+
|
|
28
|
+
<script lang="ts">
|
|
29
|
+
import type { Component } from 'svelte';
|
|
30
|
+
import { dlog } from '@symbiote-native/engine';
|
|
31
|
+
import {
|
|
32
|
+
NAVIGATION_EVENT_STATE,
|
|
33
|
+
RNS_SCREEN_STACK_VIEW_NAME,
|
|
34
|
+
STACK_ON_FINISH_TRANSITIONING,
|
|
35
|
+
createInitialNavigatorState,
|
|
36
|
+
createNavigationEmitter,
|
|
37
|
+
navigatorReducer,
|
|
38
|
+
reconcileStackRoutes,
|
|
39
|
+
resolveStackProps,
|
|
40
|
+
} from '../../core';
|
|
41
|
+
import type {
|
|
42
|
+
INavigationEmitter,
|
|
43
|
+
INavigatorAction,
|
|
44
|
+
INavigatorHandle,
|
|
45
|
+
INavigatorState,
|
|
46
|
+
IRoute,
|
|
47
|
+
} from '../../core';
|
|
48
|
+
import { hostProps } from '../attachments';
|
|
49
|
+
import { getNavigationScope } from '../navigation-context';
|
|
50
|
+
import { SCREEN_REGISTRY_HOST_PROPS } from '../registry-host';
|
|
51
|
+
import {
|
|
52
|
+
setScreenCollector,
|
|
53
|
+
toRegistry,
|
|
54
|
+
withoutScreen,
|
|
55
|
+
} from '../screen-registry';
|
|
56
|
+
import type { IRegisteredScreen } from '../screen-registry';
|
|
57
|
+
import type { IScreenProps, ISvelteScreenOptions } from '../screen-props';
|
|
58
|
+
import StackScreen from './stack-screen.svelte';
|
|
59
|
+
import type { IStackProps } from './stack-props';
|
|
60
|
+
|
|
61
|
+
let { initialRouteName, screenOptions, children }: IStackProps = $props();
|
|
62
|
+
|
|
63
|
+
// Read BEFORE this Stack establishes its own per-screen NavigationScope - becomes the `parent`
|
|
64
|
+
// link a nested screen's useNavigation().getParent() walks (e.g. this Stack rendered as a Tab
|
|
65
|
+
// screen's content reaches that Tab via this value). undefined when this Stack is the nesting
|
|
66
|
+
// root. Kept as the BOX, not its unwrapped value, so every read below sees the current one.
|
|
67
|
+
const parentScope = getNavigationScope();
|
|
68
|
+
|
|
69
|
+
const routeIdPrefix = `stack-${(stackInstanceCounter += 1)}`;
|
|
70
|
+
|
|
71
|
+
// `$state.raw` + reassignment, never a mutated array: an entry holds a component reference (a
|
|
72
|
+
// plain function) behind live getters, and a deep reactive proxy would wrap both.
|
|
73
|
+
let screens = $state.raw<IRegisteredScreen<IScreenProps['options']>[]>([]);
|
|
74
|
+
setScreenCollector<IScreenProps['options']>({
|
|
75
|
+
kind: 'stack',
|
|
76
|
+
register: screen => {
|
|
77
|
+
screens = [...screens, screen];
|
|
78
|
+
},
|
|
79
|
+
unregister: screen => {
|
|
80
|
+
screens = withoutScreen(screens, screen);
|
|
81
|
+
},
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
const registry = $derived(toRegistry(screens));
|
|
85
|
+
|
|
86
|
+
let routeSequence = 0;
|
|
87
|
+
function createRoute(name: string, params: unknown): IRoute<unknown> {
|
|
88
|
+
routeSequence += 1;
|
|
89
|
+
return { key: `${routeIdPrefix}-${name}-${routeSequence}`, name, params };
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// The pushed stack only exists once something has dispatched; until then the state derives from
|
|
93
|
+
// whatever the markers registered. `seededState` is a PLAIN local (not `$state`), so memoizing
|
|
94
|
+
// it inside the derivation below is not a state write during a derivation - it just makes the
|
|
95
|
+
// one route-key-allocating call happen exactly once, no matter how often the derivation re-runs
|
|
96
|
+
// as markers register.
|
|
97
|
+
let pushedState = $state.raw<INavigatorState | null>(null);
|
|
98
|
+
let seededState: INavigatorState | undefined;
|
|
99
|
+
|
|
100
|
+
function seedState(): INavigatorState {
|
|
101
|
+
if (seededState !== undefined) return seededState;
|
|
102
|
+
const startRouteName = initialRouteName ?? registry.keys().next().value;
|
|
103
|
+
if (startRouteName === undefined) {
|
|
104
|
+
dlog('Stack: no <Stack.Screen> children registered');
|
|
105
|
+
// Deliberately NOT memoized: markers may still be registering, and the next derivation run
|
|
106
|
+
// should get a real initial route rather than being stuck on this placeholder.
|
|
107
|
+
return createInitialNavigatorState({
|
|
108
|
+
key: routeIdPrefix,
|
|
109
|
+
name: '',
|
|
110
|
+
params: undefined,
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
seededState = createInitialNavigatorState(
|
|
114
|
+
createRoute(startRouteName, registry.get(startRouteName)?.initialParams),
|
|
115
|
+
);
|
|
116
|
+
return seededState;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// A `<Stack.Screen>` marker can unregister while its route is still in the pushed history (a
|
|
120
|
+
// marker behind an `{#if}`, a data-driven screen list), which would leave that entry with nothing
|
|
121
|
+
// for componentFor() to mount (reconcileStackRoutes' header). The reconciliation belongs in the
|
|
122
|
+
// derivation because it is PURE - it hands back the same state reference when nothing changed, so
|
|
123
|
+
// it writes no state and cannot re-trigger itself; `dispatch` below then persists the pruned
|
|
124
|
+
// history, since it reduces over this value rather than over `pushedState`.
|
|
125
|
+
const state = $derived(
|
|
126
|
+
reconcileStackRoutes(pushedState ?? seedState(), [...registry.keys()]),
|
|
127
|
+
);
|
|
128
|
+
|
|
129
|
+
function dispatch(action: INavigatorAction): void {
|
|
130
|
+
pushedState = navigatorReducer(state, action);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// One emitter per route.key, created lazily the first time a route is rendered and pruned once
|
|
134
|
+
// it is popped off the stack (the broadcast effect below).
|
|
135
|
+
const emitters = new Map<string, INavigationEmitter>();
|
|
136
|
+
function emitterFor(routeKey: string): INavigationEmitter {
|
|
137
|
+
let emitter = emitters.get(routeKey);
|
|
138
|
+
if (emitter === undefined) {
|
|
139
|
+
emitter = createNavigationEmitter();
|
|
140
|
+
emitters.set(routeKey, emitter);
|
|
141
|
+
}
|
|
142
|
+
return emitter;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export function push(name: string, params?: unknown): void {
|
|
146
|
+
dispatch({ type: 'push', route: createRoute(name, params) });
|
|
147
|
+
}
|
|
148
|
+
export function pop(count?: number): void {
|
|
149
|
+
dispatch({ type: 'pop', count });
|
|
150
|
+
}
|
|
151
|
+
export function popToTop(): void {
|
|
152
|
+
dispatch({ type: 'popToTop' });
|
|
153
|
+
}
|
|
154
|
+
export function popTo(key: string): void {
|
|
155
|
+
dispatch({ type: 'popTo', key });
|
|
156
|
+
}
|
|
157
|
+
export function replace(name: string, params?: unknown): void {
|
|
158
|
+
dispatch({ type: 'replace', route: createRoute(name, params) });
|
|
159
|
+
}
|
|
160
|
+
export function setParams(params: unknown, key?: string): void {
|
|
161
|
+
dispatch({ type: 'setParams', key, params });
|
|
162
|
+
}
|
|
163
|
+
export function reset(nextState: INavigatorState): void {
|
|
164
|
+
dispatch({ type: 'reset', state: nextState });
|
|
165
|
+
}
|
|
166
|
+
export function canGoBack(): boolean {
|
|
167
|
+
return state.routes.length > 1;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// The same handle, as a value, for everything that needs it INSIDE this component (the options
|
|
171
|
+
// fold's `navigation` argument, every route's NavigationScope). `bind:this` exposes the
|
|
172
|
+
// `export function`s above; this object is the internal twin of that surface.
|
|
173
|
+
const handle: INavigatorHandle = {
|
|
174
|
+
push,
|
|
175
|
+
pop,
|
|
176
|
+
popToTop,
|
|
177
|
+
popTo,
|
|
178
|
+
replace,
|
|
179
|
+
setParams,
|
|
180
|
+
reset,
|
|
181
|
+
canGoBack,
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
function popOne(): void {
|
|
185
|
+
dispatch({ type: 'pop', count: 1 });
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Broadcasts the router state to every still-live route's emitter (useNavigationState's source)
|
|
189
|
+
// after each commit, and prunes emitters for routes popped off the stack. Runs as an $effect
|
|
190
|
+
// rather than inline in the render path (Vue's shape), so the first broadcast lands AFTER the
|
|
191
|
+
// initial route's own screen has mounted and subscribed.
|
|
192
|
+
$effect(() => {
|
|
193
|
+
const current = state;
|
|
194
|
+
for (const route of current.routes) {
|
|
195
|
+
emitterFor(route.key).emit(NAVIGATION_EVENT_STATE, current);
|
|
196
|
+
}
|
|
197
|
+
for (const routeKey of [...emitters.keys()]) {
|
|
198
|
+
if (!current.routes.some(route => route.key === routeKey))
|
|
199
|
+
emitters.delete(routeKey);
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
// Investigation instrumentation (flicker-on-focus bug): STACK_ON_FINISH_TRANSITIONING is the
|
|
204
|
+
// native signal that the WHOLE push/pop animation has finished (as opposed to onAppear/
|
|
205
|
+
// onDisappear, which are per-screen) - logging it lets the per-screen appear/disappear
|
|
206
|
+
// timestamps be checked against the actual transition-complete moment. Kept behind DEBUG,
|
|
207
|
+
// never removed.
|
|
208
|
+
const stackProps = resolveStackProps({
|
|
209
|
+
passthrough: {
|
|
210
|
+
[STACK_ON_FINISH_TRANSITIONING]: () =>
|
|
211
|
+
dlog(`Stack: onFinishTransitioning at t=${Date.now()}`),
|
|
212
|
+
},
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
function optionsFor(route: IRoute<unknown>): ISvelteScreenOptions {
|
|
216
|
+
const entry = registry.get(route.name);
|
|
217
|
+
const own =
|
|
218
|
+
entry === undefined
|
|
219
|
+
? undefined
|
|
220
|
+
: typeof entry.options === 'function'
|
|
221
|
+
? entry.options({ route, navigation: handle })
|
|
222
|
+
: entry.options;
|
|
223
|
+
return { ...screenOptions, ...own };
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
function componentFor(route: IRoute<unknown>): Component | undefined {
|
|
227
|
+
const entry = registry.get(route.name);
|
|
228
|
+
if (entry === undefined) {
|
|
229
|
+
dlog(`Stack: no screen registered for route name "${route.name}"`);
|
|
230
|
+
return undefined;
|
|
231
|
+
}
|
|
232
|
+
return entry.component;
|
|
233
|
+
}
|
|
234
|
+
</script>
|
|
235
|
+
|
|
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>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Component } from 'svelte';
|
|
2
|
+
import type { INavigatorState } from '../../core';
|
|
3
|
+
import type { IStackProps } from './stack-props';
|
|
4
|
+
declare const Index: Component<IStackProps, {
|
|
5
|
+
push: (name: string, params?: unknown) => void;
|
|
6
|
+
pop: (count?: number) => void;
|
|
7
|
+
popToTop: () => void;
|
|
8
|
+
popTo: (key: string) => void;
|
|
9
|
+
replace: (name: string, params?: unknown) => void;
|
|
10
|
+
setParams: (params: unknown, key?: string) => void;
|
|
11
|
+
reset: (nextState: INavigatorState) => void;
|
|
12
|
+
canGoBack: () => boolean;
|
|
13
|
+
}, "">;
|
|
14
|
+
type Index = ReturnType<typeof Index>;
|
|
15
|
+
export default Index;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { Component, Snippet } from 'svelte';
|
|
2
|
+
import type { INavigationEmitter, INavigatorHandle, IRoute } from '../../core';
|
|
3
|
+
import type { INavigationScopeValue } from '../navigation-context';
|
|
4
|
+
import type { ISvelteScreenOptions } from '../screen-props';
|
|
5
|
+
export type IStackProps = {
|
|
6
|
+
initialRouteName?: string;
|
|
7
|
+
screenOptions?: ISvelteScreenOptions;
|
|
8
|
+
children?: Snippet;
|
|
9
|
+
};
|
|
10
|
+
export type IStackScreenProps = {
|
|
11
|
+
route: IRoute<unknown>;
|
|
12
|
+
index: number;
|
|
13
|
+
routeCount: number;
|
|
14
|
+
options: ISvelteScreenOptions;
|
|
15
|
+
navigation: INavigatorHandle;
|
|
16
|
+
emitter: INavigationEmitter;
|
|
17
|
+
parentScope: INavigationScopeValue | undefined;
|
|
18
|
+
component: Component;
|
|
19
|
+
onPopRequested: () => void;
|
|
20
|
+
};
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
import type { INavigatorPlatform } from '../../core';
|
|
3
|
+
|
|
4
|
+
// backTitleVisible defaults to `true` on both platforms per the codegen spec's own default
|
|
5
|
+
// (CT.WithDefault<boolean, 'true'>) - no ios/android divergence in v1 scope, so a single
|
|
6
|
+
// constant stands in for the per-platform injection point ISliderPlatform-style adapters use
|
|
7
|
+
// elsewhere.
|
|
8
|
+
const NAVIGATOR_PLATFORM: INavigatorPlatform = {
|
|
9
|
+
defaultHeaderBackTitleVisible: true,
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
// react-native-screens' RNSScreenStackHeaderConfig.mm requires every header child to be an
|
|
13
|
+
// RNSScreenStackHeaderSubview; `type: 'searchBar'` is how it knows which slot this one fills.
|
|
14
|
+
const HEADER_SUBVIEW_PROPS: Record<string, unknown> = { type: 'searchBar' };
|
|
15
|
+
</script>
|
|
16
|
+
|
|
17
|
+
<script lang="ts">
|
|
18
|
+
// One mounted route's native chrome. Split out of index.svelte so each route owns its own plan
|
|
19
|
+
// derivation and its own attachments, torn down by an ordinary component unmount when the route
|
|
20
|
+
// is popped - the Svelte equivalent of Vue's per-route render-loop closure.
|
|
21
|
+
//
|
|
22
|
+
// Every react-native-screens view here goes through `<svelte:element this={'RNSScreen'}>`
|
|
23
|
+
// rather than a literal tag: their Fabric names are capitalized and un-hyphenated, so a literal
|
|
24
|
+
// tag would parse as a COMPONENT reference in a Svelte template. Their props ride an
|
|
25
|
+
// `{@attach hostProps(...)}` attachment rather than an attribute, because a dynamic tag
|
|
26
|
+
// compiles through Svelte's generic setAttribute path and never the custom-element property-SET
|
|
27
|
+
// path the object bag depends on - see ../attachments.ts for the full reasoning.
|
|
28
|
+
//
|
|
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).
|
|
32
|
+
import { Platform, dlog } from '@symbiote-native/engine';
|
|
33
|
+
import {
|
|
34
|
+
NAVIGATION_EVENT_BLUR,
|
|
35
|
+
NAVIGATION_EVENT_FOCUS,
|
|
36
|
+
RNS_SCREEN_CONTENT_WRAPPER_VIEW_NAME,
|
|
37
|
+
RNS_SCREEN_STACK_HEADER_CONFIG_VIEW_NAME,
|
|
38
|
+
RNS_SCREEN_STACK_HEADER_SUBVIEW_VIEW_NAME,
|
|
39
|
+
RNS_SCREEN_STACK_VIEW_NAME,
|
|
40
|
+
RNS_SCREEN_VIEW_NAME,
|
|
41
|
+
RNS_SEARCH_BAR_VIEW_NAME,
|
|
42
|
+
SCREEN_ON_APPEAR,
|
|
43
|
+
SCREEN_ON_DISAPPEAR,
|
|
44
|
+
SCREEN_ON_DISMISSED,
|
|
45
|
+
SCREEN_ON_HEADER_BACK_BUTTON_CLICKED,
|
|
46
|
+
SCREEN_ON_WILL_APPEAR,
|
|
47
|
+
SCREEN_ON_WILL_DISAPPEAR,
|
|
48
|
+
buildSearchBarPassthrough,
|
|
49
|
+
resolveScreenRenderPlan,
|
|
50
|
+
} from '../../core';
|
|
51
|
+
import type { IScreenRenderPlan } from '../../core';
|
|
52
|
+
import { hostProps, searchBarRef } from '../attachments';
|
|
53
|
+
import NavigationScope from '../navigation-scope.svelte';
|
|
54
|
+
import type { INavigationScopeValue } from '../navigation-context';
|
|
55
|
+
import type { IStackScreenProps } from './stack-props';
|
|
56
|
+
|
|
57
|
+
let {
|
|
58
|
+
route,
|
|
59
|
+
index,
|
|
60
|
+
routeCount,
|
|
61
|
+
options,
|
|
62
|
+
navigation,
|
|
63
|
+
emitter,
|
|
64
|
+
parentScope,
|
|
65
|
+
// Destructured under a capitalized name because a Svelte template resolves a component tag
|
|
66
|
+
// only from a capitalized identifier.
|
|
67
|
+
component: ScreenComponent,
|
|
68
|
+
onPopRequested,
|
|
69
|
+
}: IStackScreenProps = $props();
|
|
70
|
+
|
|
71
|
+
const searchBarOptions = $derived(options.headerSearchBarOptions);
|
|
72
|
+
|
|
73
|
+
const plan = $derived.by<IScreenRenderPlan>(() =>
|
|
74
|
+
resolveScreenRenderPlan({
|
|
75
|
+
screenId: route.key,
|
|
76
|
+
index,
|
|
77
|
+
routeCount,
|
|
78
|
+
options,
|
|
79
|
+
platform: NAVIGATOR_PLATFORM,
|
|
80
|
+
isAndroid: Platform.OS === 'android',
|
|
81
|
+
screenPassthrough: {
|
|
82
|
+
[SCREEN_ON_DISMISSED]: onPopRequested,
|
|
83
|
+
[SCREEN_ON_HEADER_BACK_BUTTON_CLICKED]: onPopRequested,
|
|
84
|
+
// onAppear/onDisappear are the definitive visibility boundary (post-transition-
|
|
85
|
+
// animation), so 'focus'/'blur' fire exactly once per transition; onWillAppear/
|
|
86
|
+
// onWillDisappear fire BEFORE the animation runs, so wiring them to emit() too would
|
|
87
|
+
// double-invoke useFocusEffect per transition - they only get a debug log here.
|
|
88
|
+
[SCREEN_ON_WILL_APPEAR]: () =>
|
|
89
|
+
dlog(`Stack: route "${route.name}" will appear at t=${Date.now()}`),
|
|
90
|
+
[SCREEN_ON_APPEAR]: () => {
|
|
91
|
+
dlog(
|
|
92
|
+
`Stack: route "${route.name}" appeared (focus) at t=${Date.now()}`,
|
|
93
|
+
);
|
|
94
|
+
emitter.emit(NAVIGATION_EVENT_FOCUS);
|
|
95
|
+
},
|
|
96
|
+
[SCREEN_ON_WILL_DISAPPEAR]: () =>
|
|
97
|
+
dlog(
|
|
98
|
+
`Stack: route "${route.name}" will disappear at t=${Date.now()}`,
|
|
99
|
+
),
|
|
100
|
+
[SCREEN_ON_DISAPPEAR]: () => {
|
|
101
|
+
dlog(
|
|
102
|
+
`Stack: route "${route.name}" disappeared (blur) at t=${Date.now()}`,
|
|
103
|
+
);
|
|
104
|
+
emitter.emit(NAVIGATION_EVENT_BLUR);
|
|
105
|
+
},
|
|
106
|
+
},
|
|
107
|
+
// The imperative SearchBarCommands ref rides its OWN attachment on the RNSSearchBar leaf
|
|
108
|
+
// below (../attachments.ts), never this passthrough map - same split Angular uses, so no
|
|
109
|
+
// `ref` key can leak through to Fabric as a real prop.
|
|
110
|
+
searchBarPassthrough: searchBarOptions
|
|
111
|
+
? buildSearchBarPassthrough(searchBarOptions, message =>
|
|
112
|
+
dlog(`Stack: route "${route.name}" ${message}`),
|
|
113
|
+
)
|
|
114
|
+
: undefined,
|
|
115
|
+
}),
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
// Investigation instrumentation (flicker-on-focus bug): the actual timing/z-order-relevant
|
|
119
|
+
// values resolved onto the native RNSScreen, once per mounted route - rules a stackAnimation/
|
|
120
|
+
// transitionDuration mismatch against react-native-screens' own native default in or out. Kept
|
|
121
|
+
// behind DEBUG, never removed.
|
|
122
|
+
let hasLoggedScreenProps = false;
|
|
123
|
+
$effect(() => {
|
|
124
|
+
const screenProps = plan.screenProps;
|
|
125
|
+
if (hasLoggedScreenProps) return;
|
|
126
|
+
hasLoggedScreenProps = true;
|
|
127
|
+
dlog(
|
|
128
|
+
`Stack: route "${route.name}" resolved screen props ` +
|
|
129
|
+
`stackAnimation=${String(screenProps.stackAnimation)} ` +
|
|
130
|
+
`stackPresentation=${String(screenProps.stackPresentation)} ` +
|
|
131
|
+
`transitionDuration=${String(screenProps.transitionDuration)} ` +
|
|
132
|
+
`gestureEnabled=${String(screenProps.gestureEnabled)} at t=${Date.now()}`,
|
|
133
|
+
);
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
// A modal/formSheet screen has no UINavigationController of its own on iOS - nest an inner
|
|
137
|
+
// RNSScreenStack/RNSScreen purely to host the native header bar (see isHeaderInModal's comment
|
|
138
|
+
// in core/render-stack.ts). Skipping this leaves RNSScreenStackHeaderConfig with no navigation
|
|
139
|
+
// controller to attach to, so the header silently never renders. activityState mirrors the
|
|
140
|
+
// outer screen's own value - RNSScreen.mm treats an unset/inactive nested screen as not yet
|
|
141
|
+
// pushed, leaving it parked at its pre-push transition position.
|
|
142
|
+
const innerStackProps = $derived<Record<string, unknown>>({
|
|
143
|
+
style: plan.innerStackStyle,
|
|
144
|
+
});
|
|
145
|
+
const innerScreenProps = $derived<Record<string, unknown>>({
|
|
146
|
+
style: plan.innerScreenStyle,
|
|
147
|
+
activityState: plan.activityState,
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
const scopeValue = $derived<INavigationScopeValue>({
|
|
151
|
+
route,
|
|
152
|
+
navigation,
|
|
153
|
+
emitter,
|
|
154
|
+
parent: parentScope,
|
|
155
|
+
});
|
|
156
|
+
</script>
|
|
157
|
+
|
|
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>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Component } from 'svelte';
|
|
2
|
+
import type { IRoute, ITabNavigatorHandle, ITabOptions } from '../core';
|
|
3
|
+
export type ITabScreenOptionsArgs = {
|
|
4
|
+
route: IRoute<unknown>;
|
|
5
|
+
navigation: ITabNavigatorHandle;
|
|
6
|
+
};
|
|
7
|
+
export type ITabScreenOptionsResolver = (args: ITabScreenOptionsArgs) => ITabOptions;
|
|
8
|
+
export type ITabScreenProps = {
|
|
9
|
+
name: string;
|
|
10
|
+
component: Component;
|
|
11
|
+
options?: ITabOptions | ITabScreenOptionsResolver;
|
|
12
|
+
initialParams?: unknown;
|
|
13
|
+
};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
// Tab.Screen's public prop surface - mirrors screen-props.ts minus the stack-only concepts (no
|
|
2
|
+
// push/pop lifecycle, no header search bar). Split out of `tab-screen.svelte` for the same
|
|
3
|
+
// tsc-cannot-see-a-`.svelte`-named-export reason screen-props.ts documents.
|
|
4
|
+
export {};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
// Tab.Screen: a declarative marker that paints nothing, registered through the same
|
|
3
|
+
// context-based collector Stack.Screen uses (screen-registry.ts). The `'tab'` kind tag is what
|
|
4
|
+
// stops it from silently registering with an ancestor Stack/Drawer when it is written in the
|
|
5
|
+
// wrong place. See CLAUDE.md <third_party_rn_packages_are_react_only> - this and Tab import
|
|
6
|
+
// nothing from react-native.
|
|
7
|
+
import { collectScreen } from './screen-registry';
|
|
8
|
+
import type { ITabScreenProps } from './tab-screen-props';
|
|
9
|
+
|
|
10
|
+
let { name, component, options, initialParams }: ITabScreenProps = $props();
|
|
11
|
+
|
|
12
|
+
collectScreen('tab', {
|
|
13
|
+
get name(): ITabScreenProps['name'] {
|
|
14
|
+
return name;
|
|
15
|
+
},
|
|
16
|
+
get component(): ITabScreenProps['component'] {
|
|
17
|
+
return component;
|
|
18
|
+
},
|
|
19
|
+
get options(): ITabScreenProps['options'] {
|
|
20
|
+
return options;
|
|
21
|
+
},
|
|
22
|
+
get initialParams(): unknown {
|
|
23
|
+
return initialParams;
|
|
24
|
+
},
|
|
25
|
+
});
|
|
26
|
+
</script>
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare const Tab: import("svelte").Component<import("./tab-props").ITabProps, {
|
|
2
|
+
jumpTo: (name: string, params?: unknown) => void;
|
|
3
|
+
setParams: (params: unknown, key: string) => void;
|
|
4
|
+
}, ""> & {
|
|
5
|
+
Screen: import("svelte").Component<import("..").ITabScreenProps, {}, "">;
|
|
6
|
+
};
|
|
7
|
+
export type { ITabNavigatorHandle } from '../../core';
|
|
8
|
+
export type { ITabProps } from './tab-props';
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// `Tab.Screen` alongside the standalone `TabScreen` export, matching React's and Vue's barrels -
|
|
2
|
+
// see ../stack/index.ts for why Object.assign is the right mechanism on a compiled Svelte
|
|
3
|
+
// component.
|
|
4
|
+
import TabImpl from './index.svelte';
|
|
5
|
+
import TabScreen from '../tab-screen.svelte';
|
|
6
|
+
export const Tab = Object.assign(TabImpl, { Screen: TabScreen });
|