@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
|
@@ -4,7 +4,10 @@
|
|
|
4
4
|
// (`key`) is generated by the CALLER (the adapter, via React's useId or an equivalent) - the
|
|
5
5
|
// reducer never invents identity itself, so it stays a pure (state, action) => state function.
|
|
6
6
|
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
SCREEN_ACTIVITY_STATE_FOCUSED,
|
|
9
|
+
SCREEN_ACTIVITY_STATE_INACTIVE,
|
|
10
|
+
} from '../constants';
|
|
8
11
|
import { mergeParams } from '../guards';
|
|
9
12
|
|
|
10
13
|
export type IRoute<TParams = undefined> = Readonly<{
|
|
@@ -28,7 +31,9 @@ export type INavigatorAction =
|
|
|
28
31
|
| { type: 'setParams'; key?: string; params: unknown }
|
|
29
32
|
| { type: 'reset'; state: INavigatorState };
|
|
30
33
|
|
|
31
|
-
export function createInitialNavigatorState(
|
|
34
|
+
export function createInitialNavigatorState(
|
|
35
|
+
initialRoute: IRouteEntry,
|
|
36
|
+
): INavigatorState {
|
|
32
37
|
return { routes: [initialRoute] };
|
|
33
38
|
}
|
|
34
39
|
|
|
@@ -50,7 +55,9 @@ export function navigatorReducer(
|
|
|
50
55
|
}
|
|
51
56
|
|
|
52
57
|
case 'popToTop':
|
|
53
|
-
return state.routes.length <= 1
|
|
58
|
+
return state.routes.length <= 1
|
|
59
|
+
? state
|
|
60
|
+
: { routes: state.routes.slice(0, 1) };
|
|
54
61
|
|
|
55
62
|
case 'popTo': {
|
|
56
63
|
const index = state.routes.findIndex(route => route.key === action.key);
|
|
@@ -68,7 +75,8 @@ export function navigatorReducer(
|
|
|
68
75
|
// `source`-defaults-to-focused-route behavior, minus the cross-navigator target routing this
|
|
69
76
|
// single-navigator reducer has no need for.
|
|
70
77
|
case 'setParams': {
|
|
71
|
-
const targetKey =
|
|
78
|
+
const targetKey =
|
|
79
|
+
action.key ?? state.routes[state.routes.length - 1]?.key;
|
|
72
80
|
if (targetKey === undefined) return state;
|
|
73
81
|
const index = state.routes.findIndex(route => route.key === targetKey);
|
|
74
82
|
if (index === -1) return state;
|
|
@@ -78,7 +86,11 @@ export function navigatorReducer(
|
|
|
78
86
|
params: mergeParams(target.params, action.params),
|
|
79
87
|
};
|
|
80
88
|
return {
|
|
81
|
-
routes: [
|
|
89
|
+
routes: [
|
|
90
|
+
...state.routes.slice(0, index),
|
|
91
|
+
nextRoute,
|
|
92
|
+
...state.routes.slice(index + 1),
|
|
93
|
+
],
|
|
82
94
|
};
|
|
83
95
|
}
|
|
84
96
|
|
|
@@ -102,9 +114,14 @@ export function navigatorReducer(
|
|
|
102
114
|
// an already-mounted NativeStack screen's activityState can never decrease via a plain prop
|
|
103
115
|
// update ("activityState can only progress in NativeStack") - since no mounted route's index ever
|
|
104
116
|
// exceeds routesLength - 1, this formula never produces a decrease across a push or pop.
|
|
105
|
-
export function computeActivityState(
|
|
117
|
+
export function computeActivityState(
|
|
118
|
+
index: number,
|
|
119
|
+
routesLength: number,
|
|
120
|
+
): number {
|
|
106
121
|
const focusedIndex = routesLength - 1;
|
|
107
|
-
return index > focusedIndex
|
|
122
|
+
return index > focusedIndex
|
|
123
|
+
? SCREEN_ACTIVITY_STATE_INACTIVE
|
|
124
|
+
: SCREEN_ACTIVITY_STATE_FOCUSED;
|
|
108
125
|
}
|
|
109
126
|
|
|
110
127
|
export function isTopRoute(index: number, routesLength: number): boolean {
|
|
@@ -32,7 +32,9 @@ export type IDrawerSlot = 'content' | 'overlay' | 'panel';
|
|
|
32
32
|
// slide: panel, overlay, content (both panel and content animate; content stays visually on top).
|
|
33
33
|
// permanent: NOT absolutely positioned (an ordinary flexDirection:'row' sidebar), so sibling
|
|
34
34
|
// order IS the left-to-right screen position - it must follow drawerPosition.
|
|
35
|
-
export function drawerChildOrder(
|
|
35
|
+
export function drawerChildOrder(
|
|
36
|
+
options: IDrawerOptions,
|
|
37
|
+
): readonly IDrawerSlot[] {
|
|
36
38
|
const type = resolveDrawerType(options);
|
|
37
39
|
switch (type) {
|
|
38
40
|
case 'back':
|
|
@@ -89,12 +91,22 @@ function panelBaseStyle(options: IDrawerOptions): IViewStyle {
|
|
|
89
91
|
};
|
|
90
92
|
}
|
|
91
93
|
|
|
92
|
-
export function renderDrawer(
|
|
94
|
+
export function renderDrawer(
|
|
95
|
+
view: IDrawerViewProps,
|
|
96
|
+
options: IDrawerOptions,
|
|
97
|
+
): IDescriptor {
|
|
93
98
|
const type = resolveDrawerType(options);
|
|
94
|
-
const content = el(
|
|
99
|
+
const content = el(
|
|
100
|
+
'symbiote-view',
|
|
101
|
+
{ ...view.contentPassthrough, style: CONTENT_STYLE },
|
|
102
|
+
[],
|
|
103
|
+
);
|
|
95
104
|
const panel = el(
|
|
96
105
|
'symbiote-view',
|
|
97
|
-
{
|
|
106
|
+
{
|
|
107
|
+
...view.panelPassthrough,
|
|
108
|
+
style: [panelBaseStyle(options), view.drawerStyle],
|
|
109
|
+
},
|
|
98
110
|
[],
|
|
99
111
|
);
|
|
100
112
|
|
|
@@ -110,11 +122,14 @@ export function renderDrawer(view: IDrawerViewProps, options: IDrawerOptions): I
|
|
|
110
122
|
);
|
|
111
123
|
}
|
|
112
124
|
|
|
113
|
-
const children = drawerChildOrder(options).reduce<IDescriptor[]>(
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
125
|
+
const children = drawerChildOrder(options).reduce<IDescriptor[]>(
|
|
126
|
+
(acc, slot) => {
|
|
127
|
+
const descriptor = slots[slot];
|
|
128
|
+
if (descriptor !== undefined) acc.push(descriptor);
|
|
129
|
+
return acc;
|
|
130
|
+
},
|
|
131
|
+
[],
|
|
132
|
+
);
|
|
118
133
|
|
|
119
134
|
const rootStyle: IViewStyle =
|
|
120
135
|
type === 'permanent' ? { ...ROOT_STYLE, flexDirection: 'row' } : ROOT_STYLE;
|
package/src/core/render-stack.ts
CHANGED
|
@@ -41,7 +41,11 @@ import type {
|
|
|
41
41
|
IStackPresentation,
|
|
42
42
|
IStackViewProps,
|
|
43
43
|
} from './navigator-props';
|
|
44
|
-
import {
|
|
44
|
+
import {
|
|
45
|
+
resolveHeaderConfigView,
|
|
46
|
+
resolveScreenView,
|
|
47
|
+
resolveSearchBarView,
|
|
48
|
+
} from './screen-options';
|
|
45
49
|
|
|
46
50
|
// Mirrors react-native-screens' ScreenStackItem.tsx (getPositioningStyle): a formSheet's content
|
|
47
51
|
// wrapper does not get `flex: 1`. RNSScreen.mm's updateBounds pushes the sheet's live native size
|
|
@@ -101,7 +105,9 @@ export function resolveScreenViewName(
|
|
|
101
105
|
stackPresentation === 'push' ||
|
|
102
106
|
stackPresentation === 'containedModal' ||
|
|
103
107
|
stackPresentation === 'containedTransparentModal';
|
|
104
|
-
return isPlainPresentation
|
|
108
|
+
return isPlainPresentation
|
|
109
|
+
? RNS_SCREEN_VIEW_NAME
|
|
110
|
+
: RNS_MODAL_SCREEN_VIEW_NAME;
|
|
105
111
|
}
|
|
106
112
|
|
|
107
113
|
export function resolveScreenContentWrapperStyle(
|
|
@@ -141,14 +147,23 @@ export function isHeaderInModal(
|
|
|
141
147
|
isAndroid: boolean,
|
|
142
148
|
): boolean {
|
|
143
149
|
if (isAndroid) return false;
|
|
144
|
-
return (
|
|
150
|
+
return (
|
|
151
|
+
(stackPresentation ?? STACK_DEFAULT_PRESENTATION) !== 'push' &&
|
|
152
|
+
!headerHidden
|
|
153
|
+
);
|
|
145
154
|
}
|
|
146
155
|
|
|
147
156
|
// Inner-modal wrapper styles for isHeaderInModal: the inner RNSScreenStack fills its outer
|
|
148
157
|
// RNSScreen, and the inner RNSScreen fills the stack absolutely on all four edges. This mirrors
|
|
149
158
|
// react-native-screens' own `styles.container` / `StyleSheet.absoluteFill` for this exact pair.
|
|
150
159
|
const HEADER_IN_MODAL_STACK_STYLE = { flex: 1 };
|
|
151
|
-
const HEADER_IN_MODAL_SCREEN_STYLE = {
|
|
160
|
+
const HEADER_IN_MODAL_SCREEN_STYLE = {
|
|
161
|
+
position: 'absolute',
|
|
162
|
+
top: 0,
|
|
163
|
+
start: 0,
|
|
164
|
+
end: 0,
|
|
165
|
+
bottom: 0,
|
|
166
|
+
};
|
|
152
167
|
|
|
153
168
|
export function resolveHeaderInModalStackStyle(): Record<string, unknown> {
|
|
154
169
|
return HEADER_IN_MODAL_STACK_STYLE;
|
|
@@ -160,7 +175,9 @@ export function resolveHeaderInModalScreenStyle(): Record<string, unknown> {
|
|
|
160
175
|
|
|
161
176
|
// The RNSScreen leaf's own props; the adapter spreads this record directly onto the element it
|
|
162
177
|
// builds and appends the header config child and real screen content itself, not here.
|
|
163
|
-
export function resolveScreenProps(
|
|
178
|
+
export function resolveScreenProps(
|
|
179
|
+
view: IScreenViewProps,
|
|
180
|
+
): Record<string, unknown> {
|
|
164
181
|
return {
|
|
165
182
|
...view.passthrough,
|
|
166
183
|
screenId: view.screenId,
|
|
@@ -175,7 +192,8 @@ export function resolveScreenProps(view: IScreenViewProps): Record<string, unkno
|
|
|
175
192
|
// coerceChildScrollViewComponentSizeToSize) is driven by that same state, so an async push
|
|
176
193
|
// likely lags one step behind a fast drag. Scoped to formSheet: push/plain-modal screens have
|
|
177
194
|
// no comparable live-resize need.
|
|
178
|
-
synchronousShadowStateUpdatesEnabled:
|
|
195
|
+
synchronousShadowStateUpdatesEnabled:
|
|
196
|
+
view.stackPresentation === 'formSheet',
|
|
179
197
|
transitionDuration: view.transitionDuration,
|
|
180
198
|
sheetAllowedDetents: view.sheetAllowedDetents,
|
|
181
199
|
// Native RNSScreen's real prop names carry an "Index" suffix (react-native-screens' own
|
|
@@ -199,7 +217,9 @@ export function resolveScreenProps(view: IScreenViewProps): Record<string, unkno
|
|
|
199
217
|
// `style: {flex: 1}` is ours, not react-native-screens': RNSScreenStackView sizes its nav
|
|
200
218
|
// controller to `self.bounds`, which Yoga would otherwise collapse to zero, and (unlike RN's
|
|
201
219
|
// own ScreenStack.tsx) we drive the native view directly (<third_party_rn_packages_are_react_only>).
|
|
202
|
-
export function resolveStackProps(
|
|
220
|
+
export function resolveStackProps(
|
|
221
|
+
view: IStackViewProps,
|
|
222
|
+
): Record<string, unknown> {
|
|
203
223
|
return { style: { flex: 1 }, ...view.passthrough };
|
|
204
224
|
}
|
|
205
225
|
|
|
@@ -257,7 +277,9 @@ export function renderHeaderConfig(
|
|
|
257
277
|
// The RNSSearchBar leaf's static config props; event handlers ride in `passthrough`.
|
|
258
278
|
// renderHeaderConfig wraps this into the RNSSearchBar Descriptor and nests it as the header
|
|
259
279
|
// config's child.
|
|
260
|
-
export function resolveSearchBarProps(
|
|
280
|
+
export function resolveSearchBarProps(
|
|
281
|
+
view: ISearchBarViewProps,
|
|
282
|
+
): Record<string, unknown> {
|
|
261
283
|
return {
|
|
262
284
|
...view.passthrough,
|
|
263
285
|
placeholder: view.placeholder,
|
|
@@ -368,19 +390,29 @@ export type IScreenRenderPlan = {
|
|
|
368
390
|
innerScreenStyle: Record<string, unknown>;
|
|
369
391
|
};
|
|
370
392
|
|
|
371
|
-
export function resolveScreenRenderPlan(
|
|
393
|
+
export function resolveScreenRenderPlan(
|
|
394
|
+
input: IScreenRenderPlanInput,
|
|
395
|
+
): IScreenRenderPlan {
|
|
372
396
|
const { options, isAndroid } = input;
|
|
373
397
|
const headerHidden = options?.headerShown === false;
|
|
374
398
|
const activityState = computeActivityState(input.index, input.routeCount);
|
|
375
399
|
|
|
376
400
|
const screenProps = resolveScreenProps(
|
|
377
|
-
resolveScreenView(
|
|
401
|
+
resolveScreenView(
|
|
402
|
+
input.screenId,
|
|
403
|
+
activityState,
|
|
404
|
+
options,
|
|
405
|
+
input.screenPassthrough,
|
|
406
|
+
),
|
|
378
407
|
);
|
|
379
408
|
|
|
380
409
|
const searchBarOptions = options?.headerSearchBarOptions;
|
|
381
410
|
const searchBarProps = searchBarOptions
|
|
382
411
|
? resolveSearchBarProps(
|
|
383
|
-
resolveSearchBarView(
|
|
412
|
+
resolveSearchBarView(
|
|
413
|
+
searchBarOptions,
|
|
414
|
+
input.searchBarPassthrough ?? {},
|
|
415
|
+
),
|
|
384
416
|
)
|
|
385
417
|
: undefined;
|
|
386
418
|
|
|
@@ -394,7 +426,10 @@ export function resolveScreenRenderPlan(input: IScreenRenderPlanInput): IScreenR
|
|
|
394
426
|
|
|
395
427
|
return {
|
|
396
428
|
activityState,
|
|
397
|
-
screenViewName: resolveScreenViewName(
|
|
429
|
+
screenViewName: resolveScreenViewName(
|
|
430
|
+
options?.stackPresentation,
|
|
431
|
+
isAndroid,
|
|
432
|
+
),
|
|
398
433
|
screenProps,
|
|
399
434
|
headerConfig,
|
|
400
435
|
searchBarProps,
|
|
@@ -407,7 +442,11 @@ export function resolveScreenRenderPlan(input: IScreenRenderPlanInput): IScreenR
|
|
|
407
442
|
),
|
|
408
443
|
collapsable: false,
|
|
409
444
|
},
|
|
410
|
-
inModal: isHeaderInModal(
|
|
445
|
+
inModal: isHeaderInModal(
|
|
446
|
+
options?.stackPresentation,
|
|
447
|
+
headerHidden,
|
|
448
|
+
isAndroid,
|
|
449
|
+
),
|
|
411
450
|
innerStackStyle: resolveHeaderInModalStackStyle(),
|
|
412
451
|
innerScreenStyle: resolveHeaderInModalScreenStyle(),
|
|
413
452
|
};
|
package/src/core/render-tabs.ts
CHANGED
|
@@ -9,7 +9,12 @@
|
|
|
9
9
|
|
|
10
10
|
import { el, txt } from '@symbiote-native/components';
|
|
11
11
|
import type { IDescriptor } from '@symbiote-native/components';
|
|
12
|
-
import type {
|
|
12
|
+
import type {
|
|
13
|
+
IColorValue,
|
|
14
|
+
IViewStyle,
|
|
15
|
+
ITextStyle,
|
|
16
|
+
IStyleProp,
|
|
17
|
+
} from '@symbiote-native/engine';
|
|
13
18
|
import type { ITabBarIcon } from './tab-options';
|
|
14
19
|
|
|
15
20
|
// react-navigation's own bottom-tabs default to the surrounding theme's primary/text colors;
|
|
@@ -96,14 +101,19 @@ function resolveTintColor(item: ITabBarItemView): IColorValue {
|
|
|
96
101
|
// for a vector-icon-font label); an IDescriptor icon (an <Image>/vector-icon element the adapter
|
|
97
102
|
// already resolved) is spliced in verbatim, exactly like renderHeaderConfig treats a pre-built
|
|
98
103
|
// Descriptor child.
|
|
99
|
-
function renderIcon(
|
|
104
|
+
function renderIcon(
|
|
105
|
+
icon: ITabBarIcon | undefined,
|
|
106
|
+
color: IColorValue,
|
|
107
|
+
): IDescriptor | undefined {
|
|
100
108
|
if (icon === undefined) return undefined;
|
|
101
109
|
if (typeof icon === 'string')
|
|
102
110
|
return txt({ style: { color, fontSize: TAB_ICON_FONT_SIZE } }, [icon]);
|
|
103
111
|
return icon;
|
|
104
112
|
}
|
|
105
113
|
|
|
106
|
-
function renderBadge(
|
|
114
|
+
function renderBadge(
|
|
115
|
+
badge: string | number | undefined,
|
|
116
|
+
): IDescriptor | undefined {
|
|
107
117
|
if (badge === undefined) return undefined;
|
|
108
118
|
return el('symbiote-view', { style: BADGE_STYLE }, [
|
|
109
119
|
txt({ style: BADGE_TEXT_STYLE }, [String(badge)]),
|
|
@@ -121,8 +131,12 @@ export function renderTabBarItem(item: ITabBarItemView): IDescriptor {
|
|
|
121
131
|
|
|
122
132
|
const children: IDescriptor[] = [];
|
|
123
133
|
if (iconChildren.length > 0)
|
|
124
|
-
children.push(
|
|
125
|
-
|
|
134
|
+
children.push(
|
|
135
|
+
el('symbiote-view', { style: ICON_WRAPPER_STYLE }, iconChildren),
|
|
136
|
+
);
|
|
137
|
+
children.push(
|
|
138
|
+
txt({ style: { color, fontSize: TAB_LABEL_FONT_SIZE } }, [item.label]),
|
|
139
|
+
);
|
|
126
140
|
|
|
127
141
|
return el(
|
|
128
142
|
'symbiote-view',
|
|
@@ -134,5 +148,9 @@ export function renderTabBarItem(item: ITabBarItemView): IDescriptor {
|
|
|
134
148
|
|
|
135
149
|
export function renderTabBar(view: ITabBarViewProps): IDescriptor {
|
|
136
150
|
const items = view.items.map(renderTabBarItem);
|
|
137
|
-
return el(
|
|
151
|
+
return el(
|
|
152
|
+
'symbiote-view',
|
|
153
|
+
{ ...view.passthrough, style: [TAB_BAR_STYLE, view.style] },
|
|
154
|
+
items,
|
|
155
|
+
);
|
|
138
156
|
}
|
|
@@ -20,7 +20,10 @@ import type {
|
|
|
20
20
|
ISheetInitialDetent,
|
|
21
21
|
ISheetLargestUndimmedDetent,
|
|
22
22
|
} from './navigator-props';
|
|
23
|
-
import {
|
|
23
|
+
import {
|
|
24
|
+
buildHeaderBarButtonDispatch,
|
|
25
|
+
prepareHeaderBarButtonItems,
|
|
26
|
+
} from './header-bar-buttons';
|
|
24
27
|
|
|
25
28
|
// These sentinels must be kept in sync with react-native-screens' native side - they are RNS's
|
|
26
29
|
// own compat values for the 'fitToContents'/'large'/'medium'/'all' legacy presets, replicated
|
|
@@ -31,7 +34,9 @@ const SHEET_COMPAT_MEDIUM: number[] = [0.5];
|
|
|
31
34
|
const SHEET_COMPAT_ALL: number[] = [0.5, 1.0];
|
|
32
35
|
const SHEET_DIMMED_ALWAYS = -1;
|
|
33
36
|
|
|
34
|
-
function resolveSheetAllowedDetents(
|
|
37
|
+
function resolveSheetAllowedDetents(
|
|
38
|
+
compat: ISheetAllowedDetents | undefined,
|
|
39
|
+
): number[] {
|
|
35
40
|
if (compat === undefined) return SHEET_COMPAT_LARGE;
|
|
36
41
|
if (Array.isArray(compat)) return compat;
|
|
37
42
|
switch (compat) {
|
|
@@ -74,7 +79,9 @@ function resolveSheetInitialDetent(
|
|
|
74
79
|
return compat;
|
|
75
80
|
}
|
|
76
81
|
|
|
77
|
-
function toOptionalBooleanNativeProp(
|
|
82
|
+
function toOptionalBooleanNativeProp(
|
|
83
|
+
value: boolean | undefined,
|
|
84
|
+
): IOptionalBooleanNativeProp {
|
|
78
85
|
if (value === undefined) return 'undefined';
|
|
79
86
|
return value ? 'true' : 'false';
|
|
80
87
|
}
|
|
@@ -119,7 +126,9 @@ export function resolveScreenView(
|
|
|
119
126
|
options: IScreenOptions | undefined,
|
|
120
127
|
passthrough: Record<string, unknown> = {},
|
|
121
128
|
): IScreenViewProps {
|
|
122
|
-
const sheetAllowedDetents = resolveSheetAllowedDetents(
|
|
129
|
+
const sheetAllowedDetents = resolveSheetAllowedDetents(
|
|
130
|
+
options?.sheetAllowedDetents,
|
|
131
|
+
);
|
|
123
132
|
const lastDetentIndex = sheetAllowedDetents.length - 1;
|
|
124
133
|
return {
|
|
125
134
|
screenId,
|
|
@@ -142,7 +151,8 @@ export function resolveScreenView(
|
|
|
142
151
|
sheetExpandsWhenScrolledToEdge: options?.sheetExpandsWhenScrolledToEdge,
|
|
143
152
|
sheetElevation: options?.sheetElevation,
|
|
144
153
|
sheetShouldOverflowTopInset: options?.sheetShouldOverflowTopInset,
|
|
145
|
-
sheetDefaultResizeAnimationEnabled:
|
|
154
|
+
sheetDefaultResizeAnimationEnabled:
|
|
155
|
+
options?.sheetDefaultResizeAnimationEnabled,
|
|
146
156
|
statusBarStyle: options?.statusBarStyle,
|
|
147
157
|
statusBarHidden: options?.statusBarHidden,
|
|
148
158
|
statusBarAnimation: options?.statusBarAnimation,
|
|
@@ -5,7 +5,11 @@
|
|
|
5
5
|
// buildScrollViewHandle exactly (same lazy-getter shape); the adapter supplies the node getter
|
|
6
6
|
// and its own framework-specific ref field (see react/screen.ts's IReactSearchBarOptions - the
|
|
7
7
|
// ref itself is per-adapter, same split as IPressableProps).
|
|
8
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
dispatchViewCommand,
|
|
10
|
+
dlog,
|
|
11
|
+
type ISymbioteNode,
|
|
12
|
+
} from '@symbiote-native/engine';
|
|
9
13
|
|
|
10
14
|
export interface ISearchBarCommands {
|
|
11
15
|
focus(): void;
|
|
@@ -21,7 +25,10 @@ export interface ISearchBarCommands {
|
|
|
21
25
|
// stale handle outlived its screen) was the one unlogged gap, silently indistinguishable from
|
|
22
26
|
// "the command fired but did nothing". Investigation instrumentation (HeaderOptionsScreen
|
|
23
27
|
// unresponsive-buttons bug); kept behind DEBUG per <keep_logs_gate_behind_DEBUG>, never removed.
|
|
24
|
-
function warnIfDetached(
|
|
28
|
+
function warnIfDetached(
|
|
29
|
+
command: string,
|
|
30
|
+
node: ISymbioteNode | null,
|
|
31
|
+
): node is ISymbioteNode {
|
|
25
32
|
if (node !== null) return true;
|
|
26
33
|
dlog(
|
|
27
34
|
`SearchBarCommands.${command}: skipped, node is null (ref never attached) at t=${Date.now()}`,
|
|
@@ -45,7 +52,9 @@ function makeCommand(
|
|
|
45
52
|
|
|
46
53
|
// `getNode` is a LAZY getter, read on every call - see buildScrollViewHandle's comment for why
|
|
47
54
|
// an eager capture would freeze `null`.
|
|
48
|
-
export function buildSearchBarHandle(
|
|
55
|
+
export function buildSearchBarHandle(
|
|
56
|
+
getNode: () => ISymbioteNode | null,
|
|
57
|
+
): ISearchBarCommands {
|
|
49
58
|
return {
|
|
50
59
|
focus: makeCommand(getNode, 'focus'),
|
|
51
60
|
blur: makeCommand(getNode, 'blur'),
|
|
@@ -42,8 +42,11 @@ export function reconcileStackRoutes(
|
|
|
42
42
|
state: INavigatorState,
|
|
43
43
|
registeredNames: readonly string[],
|
|
44
44
|
): INavigatorState {
|
|
45
|
-
const routes = state.routes.filter(route =>
|
|
45
|
+
const routes = state.routes.filter(route =>
|
|
46
|
+
registeredNames.includes(route.name),
|
|
47
|
+
);
|
|
46
48
|
// filter preserves order, so an unchanged length means nothing was dropped.
|
|
47
|
-
if (routes.length === state.routes.length || routes.length === 0)
|
|
49
|
+
if (routes.length === state.routes.length || routes.length === 0)
|
|
50
|
+
return state;
|
|
48
51
|
return { routes };
|
|
49
52
|
}
|
|
@@ -11,11 +11,19 @@ import { isRecord } from '../guards';
|
|
|
11
11
|
// params: undefined}`, and JSON.stringify drops undefined-valued keys - a JSON round trip of a
|
|
12
12
|
// perfectly valid route legitimately arrives here without a `params` key at all.
|
|
13
13
|
function isRoute(value: unknown): value is IRoute<unknown> {
|
|
14
|
-
return
|
|
14
|
+
return (
|
|
15
|
+
isRecord(value) &&
|
|
16
|
+
typeof value.key === 'string' &&
|
|
17
|
+
typeof value.name === 'string'
|
|
18
|
+
);
|
|
15
19
|
}
|
|
16
20
|
|
|
17
21
|
function isNavigatorState(value: unknown): value is INavigatorState {
|
|
18
|
-
return
|
|
22
|
+
return (
|
|
23
|
+
isRecord(value) &&
|
|
24
|
+
Array.isArray(value.routes) &&
|
|
25
|
+
value.routes.every(isRoute)
|
|
26
|
+
);
|
|
19
27
|
}
|
|
20
28
|
|
|
21
29
|
export function serializeNavigatorState(state: INavigatorState): unknown {
|
|
@@ -24,7 +32,9 @@ export function serializeNavigatorState(state: INavigatorState): unknown {
|
|
|
24
32
|
|
|
25
33
|
export function deserializeNavigatorState(raw: unknown): INavigatorState {
|
|
26
34
|
if (!isNavigatorState(raw)) {
|
|
27
|
-
throw new Error(
|
|
35
|
+
throw new Error(
|
|
36
|
+
'deserializeNavigatorState: persisted value is not a valid navigator state',
|
|
37
|
+
);
|
|
28
38
|
}
|
|
29
39
|
return raw;
|
|
30
40
|
}
|
|
@@ -29,7 +29,8 @@ export function createInitialTabState(
|
|
|
29
29
|
routes: readonly IRouteEntry[],
|
|
30
30
|
initialRouteName?: string,
|
|
31
31
|
): ITabRouterState {
|
|
32
|
-
if (initialRouteName === undefined)
|
|
32
|
+
if (initialRouteName === undefined)
|
|
33
|
+
return { routes, index: INITIAL_FOCUSED_INDEX };
|
|
33
34
|
const index = routes.findIndex(route => route.name === initialRouteName);
|
|
34
35
|
return { routes, index: index === -1 ? INITIAL_FOCUSED_INDEX : index };
|
|
35
36
|
}
|
|
@@ -77,7 +78,10 @@ export function tabRouterReducer(
|
|
|
77
78
|
}
|
|
78
79
|
const target = state.routes[index];
|
|
79
80
|
const routes = state.routes.slice();
|
|
80
|
-
routes[index] = {
|
|
81
|
+
routes[index] = {
|
|
82
|
+
...target,
|
|
83
|
+
params: mergeParams(target.params, action.params),
|
|
84
|
+
};
|
|
81
85
|
return { ...state, index, routes };
|
|
82
86
|
}
|
|
83
87
|
|
|
@@ -86,7 +90,10 @@ export function tabRouterReducer(
|
|
|
86
90
|
if (index === -1) return state;
|
|
87
91
|
const target = state.routes[index];
|
|
88
92
|
const routes = state.routes.slice();
|
|
89
|
-
routes[index] = {
|
|
93
|
+
routes[index] = {
|
|
94
|
+
...target,
|
|
95
|
+
params: mergeParams(target.params, action.params),
|
|
96
|
+
};
|
|
90
97
|
return { ...state, routes };
|
|
91
98
|
}
|
|
92
99
|
|