@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
|
@@ -66,7 +66,12 @@ let tabInstanceCounter = 0;
|
|
|
66
66
|
@Component({
|
|
67
67
|
selector: 'Tab',
|
|
68
68
|
standalone: true,
|
|
69
|
-
imports: [
|
|
69
|
+
imports: [
|
|
70
|
+
NgComponentOutlet,
|
|
71
|
+
NavigationScopeDirective,
|
|
72
|
+
DescriptorOutlet,
|
|
73
|
+
View,
|
|
74
|
+
],
|
|
70
75
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
71
76
|
template: `
|
|
72
77
|
<View [style]="rootStyle">
|
|
@@ -101,7 +106,8 @@ export class Tab implements AfterContentInit, OnDestroy, ITabNavigatorHandle {
|
|
|
101
106
|
// Keyed by name -> the LIVE TabScreenDirective instance - see stack.ts's matching comment for
|
|
102
107
|
// why a snapshot copy would go stale on an in-place `[options]`/`[component]` change.
|
|
103
108
|
private readonly registry = new Map<string, TabScreenDirective>();
|
|
104
|
-
private tabScreenChildrenSubscription:
|
|
109
|
+
private tabScreenChildrenSubscription:
|
|
110
|
+
{ unsubscribe: () => void } | undefined;
|
|
105
111
|
|
|
106
112
|
private readonly stateSignal = signal<ITabRouterState | undefined>(undefined);
|
|
107
113
|
readonly state = this.stateSignal.asReadonly();
|
|
@@ -116,9 +122,10 @@ export class Tab implements AfterContentInit, OnDestroy, ITabNavigatorHandle {
|
|
|
116
122
|
|
|
117
123
|
ngAfterContentInit(): void {
|
|
118
124
|
this.syncRegistry();
|
|
119
|
-
this.tabScreenChildrenSubscription =
|
|
120
|
-
this.
|
|
121
|
-
|
|
125
|
+
this.tabScreenChildrenSubscription =
|
|
126
|
+
this.tabScreenChildren.changes.subscribe(() => {
|
|
127
|
+
this.syncRegistry();
|
|
128
|
+
});
|
|
122
129
|
}
|
|
123
130
|
|
|
124
131
|
ngOnDestroy(): void {
|
|
@@ -150,7 +157,8 @@ export class Tab implements AfterContentInit, OnDestroy, ITabNavigatorHandle {
|
|
|
150
157
|
private syncRegistry(): void {
|
|
151
158
|
this.rebuildRegistry();
|
|
152
159
|
const routes = this.routesFromRegistry();
|
|
153
|
-
if (routes.length === 0)
|
|
160
|
+
if (routes.length === 0)
|
|
161
|
+
dlog('Tab: no <ng-template symbioteTabScreen> children registered');
|
|
154
162
|
const current = this.stateSignal();
|
|
155
163
|
if (current !== undefined) {
|
|
156
164
|
this.stateSignal.set(reconcileTabRoutes(current, routes));
|
|
@@ -168,9 +176,15 @@ export class Tab implements AfterContentInit, OnDestroy, ITabNavigatorHandle {
|
|
|
168
176
|
this.stateSignal.set(tabRouterReducer(current, action));
|
|
169
177
|
}
|
|
170
178
|
|
|
171
|
-
private resolveTabOptions(
|
|
179
|
+
private resolveTabOptions(
|
|
180
|
+
entry: TabScreenDirective,
|
|
181
|
+
route: IRoute<unknown>,
|
|
182
|
+
): ITabOptions {
|
|
172
183
|
const props: ITabScreenOptionsArgs = { route, navigation: this };
|
|
173
|
-
const own =
|
|
184
|
+
const own =
|
|
185
|
+
typeof entry.options === 'function'
|
|
186
|
+
? entry.options(props)
|
|
187
|
+
: entry.options;
|
|
174
188
|
return { ...this.screenOptions, ...own };
|
|
175
189
|
}
|
|
176
190
|
|
|
@@ -207,8 +221,12 @@ export class Tab implements AfterContentInit, OnDestroy, ITabNavigatorHandle {
|
|
|
207
221
|
focusedRouteEmitter(): INavigationEmitter {
|
|
208
222
|
const key = this.focusedRoute()?.key;
|
|
209
223
|
return untracked(() => {
|
|
210
|
-
if (key === this.currentEmitterKey && this.currentEmitter)
|
|
211
|
-
|
|
224
|
+
if (key === this.currentEmitterKey && this.currentEmitter)
|
|
225
|
+
return this.currentEmitter;
|
|
226
|
+
const { blurKey, focusKey } = diffFocusedRoute(
|
|
227
|
+
this.currentEmitterKey,
|
|
228
|
+
key,
|
|
229
|
+
);
|
|
212
230
|
if (blurKey !== undefined && this.currentEmitter) {
|
|
213
231
|
dlog('Tab: previous route blurred');
|
|
214
232
|
this.currentEmitter.emit(NAVIGATION_EVENT_BLUR);
|
|
@@ -257,12 +275,18 @@ export class Tab implements AfterContentInit, OnDestroy, ITabNavigatorHandle {
|
|
|
257
275
|
});
|
|
258
276
|
|
|
259
277
|
const focusedRoute = this.focusedRoute();
|
|
260
|
-
const focusedEntry = focusedRoute
|
|
278
|
+
const focusedEntry = focusedRoute
|
|
279
|
+
? this.registry.get(focusedRoute.name)
|
|
280
|
+
: undefined;
|
|
261
281
|
const focusedOptions =
|
|
262
282
|
focusedEntry && focusedRoute
|
|
263
283
|
? this.resolveTabOptions(focusedEntry, focusedRoute)
|
|
264
284
|
: this.screenOptions;
|
|
265
285
|
|
|
266
|
-
return renderTabBar({
|
|
286
|
+
return renderTabBar({
|
|
287
|
+
items,
|
|
288
|
+
style: focusedOptions?.tabBarStyle,
|
|
289
|
+
passthrough: {},
|
|
290
|
+
});
|
|
267
291
|
}
|
|
268
292
|
}
|
package/src/core/constants.ts
CHANGED
|
@@ -11,12 +11,14 @@ export const RNS_SCREEN_VIEW_NAME = 'RNSScreen';
|
|
|
11
11
|
// render-stack.ts for why a formSheet/modal screen MUST use this different Fabric name.
|
|
12
12
|
export const RNS_MODAL_SCREEN_VIEW_NAME = 'RNSModalScreen';
|
|
13
13
|
export const RNS_SCREEN_STACK_VIEW_NAME = 'RNSScreenStack';
|
|
14
|
-
export const RNS_SCREEN_STACK_HEADER_CONFIG_VIEW_NAME =
|
|
14
|
+
export const RNS_SCREEN_STACK_HEADER_CONFIG_VIEW_NAME =
|
|
15
|
+
'RNSScreenStackHeaderConfig';
|
|
15
16
|
// The header-subview leaf react-native-screens itself uses to host arbitrary framework children
|
|
16
17
|
// (left/right/back/title/center/searchBar) inside the header. Registered here for ground-truth
|
|
17
18
|
// parity, but unconsumed by any fold in v1 scope: renderHeaderConfig's Descriptor never takes
|
|
18
19
|
// framework children (see render-stack.ts), so mounting a subview child is left to the adapter.
|
|
19
|
-
export const RNS_SCREEN_STACK_HEADER_SUBVIEW_VIEW_NAME =
|
|
20
|
+
export const RNS_SCREEN_STACK_HEADER_SUBVIEW_VIEW_NAME =
|
|
21
|
+
'RNSScreenStackHeaderSubview';
|
|
20
22
|
export const RNS_SEARCH_BAR_VIEW_NAME = 'RNSSearchBar';
|
|
21
23
|
// react-native-screens' own JS layer (ScreenStackItem -> DebugContainer -> ScreenContentWrapper)
|
|
22
24
|
// ALWAYS wraps a screen's content in this native view, `collapsable: false` so Fabric can never
|
|
@@ -43,7 +45,8 @@ export const STACK_ON_FINISH_TRANSITIONING = 'onFinishTransitioning';
|
|
|
43
45
|
// plain bar-button-item press, and a press on a (possibly nested) menu action of a 'menu'-type
|
|
44
46
|
// bar-button item.
|
|
45
47
|
export const HEADER_ON_PRESS_BAR_BUTTON_ITEM = 'onPressHeaderBarButtonItem';
|
|
46
|
-
export const HEADER_ON_PRESS_BAR_BUTTON_MENU_ITEM =
|
|
48
|
+
export const HEADER_ON_PRESS_BAR_BUTTON_MENU_ITEM =
|
|
49
|
+
'onPressHeaderBarButtonMenuItem';
|
|
47
50
|
|
|
48
51
|
// RNSSearchBar event prop keys (all DirectEventHandler in the codegen spec). onClose/onOpen are
|
|
49
52
|
// Android-only per the codegen spec's own comment.
|
|
@@ -7,7 +7,10 @@
|
|
|
7
7
|
// PanResponder equivalent (see packages/navigation's README / the drawer feasibility note for the
|
|
8
8
|
// full gap list).
|
|
9
9
|
|
|
10
|
-
import type {
|
|
10
|
+
import type {
|
|
11
|
+
IPanResponderGestureState,
|
|
12
|
+
ISymbioteEvent,
|
|
13
|
+
} from '@symbiote-native/engine';
|
|
11
14
|
import { isRecord } from '../guards';
|
|
12
15
|
import type { IDrawerSlot } from '../render-drawer';
|
|
13
16
|
|
|
@@ -59,7 +62,9 @@ export function resolveDrawerType(options: IDrawerOptions): IDrawerType {
|
|
|
59
62
|
return options.drawerType ?? DRAWER_DEFAULT_TYPE;
|
|
60
63
|
}
|
|
61
64
|
|
|
62
|
-
export function resolveDrawerPosition(
|
|
65
|
+
export function resolveDrawerPosition(
|
|
66
|
+
options: IDrawerOptions,
|
|
67
|
+
): IDrawerPosition {
|
|
63
68
|
return options.drawerPosition ?? DRAWER_DEFAULT_POSITION;
|
|
64
69
|
}
|
|
65
70
|
|
|
@@ -90,7 +95,9 @@ export function isDrawerOverlayVisible(options: IDrawerOptions): boolean {
|
|
|
90
95
|
// slide: panel AND content move together by the same delta, overlay fades in (content still
|
|
91
96
|
// covers the panel partially as they slide in tandem, same as front visually at rest).
|
|
92
97
|
// permanent: static, all zero (isDrawerAnimated() gates callers off this path already).
|
|
93
|
-
export function resolveDrawerGeometry(
|
|
98
|
+
export function resolveDrawerGeometry(
|
|
99
|
+
options: IDrawerOptions,
|
|
100
|
+
): IDrawerGeometry {
|
|
94
101
|
const width = resolveDrawerWidth(options);
|
|
95
102
|
const type = resolveDrawerType(options);
|
|
96
103
|
const sign = resolveDrawerPosition(options) === 'left' ? 1 : -1;
|
|
@@ -188,25 +195,37 @@ export function resolveDrawerSlotInterpolation(
|
|
|
188
195
|
return {
|
|
189
196
|
translateX: {
|
|
190
197
|
inputRange: DRAWER_PROGRESS_INPUT_RANGE,
|
|
191
|
-
outputRange: [
|
|
198
|
+
outputRange: [
|
|
199
|
+
geometry.contentTranslateXClosed,
|
|
200
|
+
geometry.contentTranslateXOpen,
|
|
201
|
+
],
|
|
192
202
|
},
|
|
193
203
|
};
|
|
194
204
|
case 'overlay':
|
|
195
205
|
return {
|
|
196
206
|
opacity: {
|
|
197
207
|
inputRange: DRAWER_PROGRESS_INPUT_RANGE,
|
|
198
|
-
outputRange: [
|
|
208
|
+
outputRange: [
|
|
209
|
+
geometry.overlayOpacityClosed,
|
|
210
|
+
geometry.overlayOpacityOpen,
|
|
211
|
+
],
|
|
199
212
|
},
|
|
200
213
|
translateX: {
|
|
201
214
|
inputRange: DRAWER_PROGRESS_INPUT_RANGE,
|
|
202
|
-
outputRange: [
|
|
215
|
+
outputRange: [
|
|
216
|
+
geometry.contentTranslateXClosed,
|
|
217
|
+
geometry.contentTranslateXOpen,
|
|
218
|
+
],
|
|
203
219
|
},
|
|
204
220
|
};
|
|
205
221
|
case 'panel':
|
|
206
222
|
return {
|
|
207
223
|
translateX: {
|
|
208
224
|
inputRange: DRAWER_PROGRESS_INPUT_RANGE,
|
|
209
|
-
outputRange: [
|
|
225
|
+
outputRange: [
|
|
226
|
+
geometry.panelTranslateXClosed,
|
|
227
|
+
geometry.panelTranslateXOpen,
|
|
228
|
+
],
|
|
210
229
|
},
|
|
211
230
|
};
|
|
212
231
|
}
|
|
@@ -235,7 +254,9 @@ export function isSwipeStartInEdge(
|
|
|
235
254
|
// ScrollView's way).
|
|
236
255
|
const MOVE_CLAIM_THRESHOLD = 5;
|
|
237
256
|
|
|
238
|
-
export function isHorizontalDrag(
|
|
257
|
+
export function isHorizontalDrag(
|
|
258
|
+
gestureState: IPanResponderGestureState,
|
|
259
|
+
): boolean {
|
|
239
260
|
return (
|
|
240
261
|
Math.abs(gestureState.dx) > MOVE_CLAIM_THRESHOLD &&
|
|
241
262
|
Math.abs(gestureState.dx) > Math.abs(gestureState.dy)
|
|
@@ -258,8 +279,10 @@ export function resolveSwipeIntent(
|
|
|
258
279
|
// symmetrically for a right drawer's leftward drag).
|
|
259
280
|
const signedDx = sign * gestureState.dx;
|
|
260
281
|
const signedVx = sign * gestureState.vx;
|
|
261
|
-
const minDistance =
|
|
262
|
-
|
|
282
|
+
const minDistance =
|
|
283
|
+
options.swipeMinDistance ?? DRAWER_DEFAULT_SWIPE_MIN_DISTANCE;
|
|
284
|
+
const minVelocity =
|
|
285
|
+
options.swipeMinVelocity ?? DRAWER_DEFAULT_SWIPE_MIN_VELOCITY;
|
|
263
286
|
|
|
264
287
|
const pastDistance = Math.abs(signedDx) >= minDistance;
|
|
265
288
|
const pastVelocity = Math.abs(signedVx) >= minVelocity;
|
|
@@ -276,7 +299,9 @@ export function clamp01(value: number): number {
|
|
|
276
299
|
}
|
|
277
300
|
|
|
278
301
|
function toFiniteNumber(value: unknown): number | undefined {
|
|
279
|
-
return typeof value === 'number' && Number.isFinite(value)
|
|
302
|
+
return typeof value === 'number' && Number.isFinite(value)
|
|
303
|
+
? value
|
|
304
|
+
: undefined;
|
|
280
305
|
}
|
|
281
306
|
|
|
282
307
|
// The touch's real starting page-X, read off the raw event rather than gestureState.x0: the
|
|
@@ -293,7 +318,8 @@ export function startPageXOf(event: ISymbioteEvent): number | undefined {
|
|
|
293
318
|
const direct = toFiniteNumber(nativeEvent.pageX);
|
|
294
319
|
if (direct !== undefined) return direct;
|
|
295
320
|
const touches = nativeEvent.touches;
|
|
296
|
-
if (Array.isArray(touches) && isRecord(touches[0]))
|
|
321
|
+
if (Array.isArray(touches) && isRecord(touches[0]))
|
|
322
|
+
return toFiniteNumber(touches[0].pageX);
|
|
297
323
|
return undefined;
|
|
298
324
|
}
|
|
299
325
|
|
|
@@ -323,7 +349,8 @@ export function shouldClaimDrawerSwipe(
|
|
|
323
349
|
options: IDrawerOptions,
|
|
324
350
|
phase: 'start' | 'move',
|
|
325
351
|
): boolean {
|
|
326
|
-
if ((options.swipeEnabled ?? DRAWER_DEFAULT_SWIPE_ENABLED) === false)
|
|
352
|
+
if ((options.swipeEnabled ?? DRAWER_DEFAULT_SWIPE_ENABLED) === false)
|
|
353
|
+
return false;
|
|
327
354
|
if (!isDrawerAnimated(options)) return false;
|
|
328
355
|
const startX = startPageXOf(event) ?? gestureState.x0;
|
|
329
356
|
if (!isSwipeStartInEdge(startX, screenWidth, isOpen, options)) return false;
|
|
@@ -34,7 +34,11 @@ export function createInitialDrawerRouterState(
|
|
|
34
34
|
return { routes, index: INITIAL_FOCUSED_INDEX, isOpen: false };
|
|
35
35
|
}
|
|
36
36
|
const index = routes.findIndex(route => route.name === initialRouteName);
|
|
37
|
-
return {
|
|
37
|
+
return {
|
|
38
|
+
routes,
|
|
39
|
+
index: index === -1 ? INITIAL_FOCUSED_INDEX : index,
|
|
40
|
+
isOpen: false,
|
|
41
|
+
};
|
|
38
42
|
}
|
|
39
43
|
|
|
40
44
|
// Mirrors @react-navigation/drawer's DrawerActions: jumpTo focuses a route by NAME (TabRouter's
|
|
@@ -49,7 +53,9 @@ export function drawerRouterReducer(
|
|
|
49
53
|
case 'jumpTo': {
|
|
50
54
|
const index = state.routes.findIndex(route => route.name === action.name);
|
|
51
55
|
if (index === -1) return state;
|
|
52
|
-
return index === state.index && !state.isOpen
|
|
56
|
+
return index === state.index && !state.isOpen
|
|
57
|
+
? state
|
|
58
|
+
: { ...state, index, isOpen: false };
|
|
53
59
|
}
|
|
54
60
|
|
|
55
61
|
case 'openDrawer':
|
|
@@ -66,6 +72,8 @@ export function drawerRouterReducer(
|
|
|
66
72
|
}
|
|
67
73
|
}
|
|
68
74
|
|
|
69
|
-
export function focusedDrawerRoute(
|
|
75
|
+
export function focusedDrawerRoute(
|
|
76
|
+
state: IDrawerRouterState,
|
|
77
|
+
): IRouteEntry | undefined {
|
|
70
78
|
return state.routes[state.index];
|
|
71
79
|
}
|
package/src/core/guards.ts
CHANGED
|
@@ -19,5 +19,7 @@ export function isRecord(value: unknown): value is Record<string, unknown> {
|
|
|
19
19
|
// navigator-state.ts (Stack) and tab-router-state.ts (Tabs) so the two reducers can't drift on
|
|
20
20
|
// this rule independently.
|
|
21
21
|
export function mergeParams(current: unknown, incoming: unknown): unknown {
|
|
22
|
-
return isRecord(current) && isRecord(incoming)
|
|
22
|
+
return isRecord(current) && isRecord(incoming)
|
|
23
|
+
? { ...current, ...incoming }
|
|
24
|
+
: incoming;
|
|
23
25
|
}
|
|
@@ -21,7 +21,9 @@ import type {
|
|
|
21
21
|
// time (both the structured `icon` field AND the flat native keys reach native - RNS's own
|
|
22
22
|
// prepareHeaderBarButtonItems keeps both), and resolves image assets through the same resolver
|
|
23
23
|
// the Image component uses.
|
|
24
|
-
function prepareIcon(
|
|
24
|
+
function prepareIcon(
|
|
25
|
+
icon: IHeaderBarButtonIcon | undefined,
|
|
26
|
+
): Record<string, unknown> {
|
|
25
27
|
if (!icon) return {};
|
|
26
28
|
switch (icon.type) {
|
|
27
29
|
case 'sfSymbol':
|
|
@@ -31,7 +33,9 @@ function prepareIcon(icon: IHeaderBarButtonIcon | undefined): Record<string, unk
|
|
|
31
33
|
case 'imageSource':
|
|
32
34
|
return { imageSource: imageStatics.resolveAssetSource(icon.imageSource) };
|
|
33
35
|
case 'templateSource':
|
|
34
|
-
return {
|
|
36
|
+
return {
|
|
37
|
+
templateSource: imageStatics.resolveAssetSource(icon.templateSource),
|
|
38
|
+
};
|
|
35
39
|
}
|
|
36
40
|
}
|
|
37
41
|
|
|
@@ -40,7 +44,11 @@ type ISharedItem = IHeaderBarButtonItemAction | IHeaderBarButtonItemMenu;
|
|
|
40
44
|
// The one id algorithm both the payload tagger (prepareMenuItems/prepareHeaderBarButtonItems)
|
|
41
45
|
// and the dispatch-lookup builder (collectMenuHandlers/collectBarButtonHandlers) call - so a
|
|
42
46
|
// change to the id scheme genuinely can't desync payload tagging from dispatch lookup.
|
|
43
|
-
function buildMenuId(
|
|
47
|
+
function buildMenuId(
|
|
48
|
+
path: string,
|
|
49
|
+
index: number,
|
|
50
|
+
side: 'left' | 'right',
|
|
51
|
+
): string {
|
|
44
52
|
return `${path}-${index}-${side}`;
|
|
45
53
|
}
|
|
46
54
|
|
|
@@ -54,18 +62,26 @@ function prepareTitleStyle(
|
|
|
54
62
|
if (!titleStyle) return undefined;
|
|
55
63
|
return {
|
|
56
64
|
...titleStyle,
|
|
57
|
-
color:
|
|
65
|
+
color:
|
|
66
|
+
titleStyle.color === undefined
|
|
67
|
+
? undefined
|
|
68
|
+
: processColor(titleStyle.color),
|
|
58
69
|
};
|
|
59
70
|
}
|
|
60
71
|
|
|
61
|
-
function prepareBadge(
|
|
72
|
+
function prepareBadge(
|
|
73
|
+
badge: ISharedItem['badge'],
|
|
74
|
+
): Record<string, unknown> | undefined {
|
|
62
75
|
if (!badge) return undefined;
|
|
63
76
|
return {
|
|
64
77
|
...badge,
|
|
65
78
|
style: badge.style
|
|
66
79
|
? {
|
|
67
80
|
...badge.style,
|
|
68
|
-
color:
|
|
81
|
+
color:
|
|
82
|
+
badge.style.color === undefined
|
|
83
|
+
? undefined
|
|
84
|
+
: processColor(badge.style.color),
|
|
69
85
|
backgroundColor:
|
|
70
86
|
badge.style.backgroundColor === undefined
|
|
71
87
|
? undefined
|
|
@@ -81,7 +97,9 @@ function prepareBadge(badge: ISharedItem['badge']): Record<string, unknown> | un
|
|
|
81
97
|
// headerRightBarButtonItems array it's nested in - not just the one bad field - so bar buttons and
|
|
82
98
|
// menu actions never reached native at all, despite the buttonId/menuId dispatch wiring itself
|
|
83
99
|
// being entirely correct.
|
|
84
|
-
function excludeOnPress<T extends { onPress: unknown }>(
|
|
100
|
+
function excludeOnPress<T extends { onPress: unknown }>(
|
|
101
|
+
item: T,
|
|
102
|
+
): Omit<T, 'onPress'> {
|
|
85
103
|
const { onPress: _onPress, ...rest } = item;
|
|
86
104
|
return rest;
|
|
87
105
|
}
|
|
@@ -95,12 +113,15 @@ function prepareSharedFields(
|
|
|
95
113
|
...(item.type === 'button' ? excludeOnPress(item) : item),
|
|
96
114
|
...prepareIcon(item.icon),
|
|
97
115
|
titleStyle: prepareTitleStyle(item.titleStyle),
|
|
98
|
-
tintColor:
|
|
116
|
+
tintColor:
|
|
117
|
+
item.tintColor === undefined ? undefined : processColor(item.tintColor),
|
|
99
118
|
badge: prepareBadge(item.badge),
|
|
100
119
|
};
|
|
101
120
|
}
|
|
102
121
|
|
|
103
|
-
type IMenuLike = {
|
|
122
|
+
type IMenuLike = {
|
|
123
|
+
items: (IHeaderBarButtonMenuAction | IHeaderBarButtonSubmenu)[];
|
|
124
|
+
};
|
|
104
125
|
|
|
105
126
|
// Recursively tags every menu action with a `menuId` derived from its tree position
|
|
106
127
|
// (`${path}-${index}-${side}`), mirroring RNS's own prepareMenu exactly - the id must match what
|
|
@@ -117,7 +138,11 @@ function prepareMenuItems<T extends IMenuLike>(
|
|
|
117
138
|
const currentPath = path ? `${path}.${menuIndex}` : `${menuIndex}`;
|
|
118
139
|
const icon = prepareIcon(menuItem.icon);
|
|
119
140
|
if (menuItem.type === 'submenu') {
|
|
120
|
-
return {
|
|
141
|
+
return {
|
|
142
|
+
...menuItem,
|
|
143
|
+
...icon,
|
|
144
|
+
...prepareMenuItems(menuItem, index, side, currentPath),
|
|
145
|
+
};
|
|
121
146
|
}
|
|
122
147
|
return {
|
|
123
148
|
...excludeOnPress(menuItem),
|
|
@@ -139,7 +164,8 @@ export function prepareHeaderBarButtonItems(
|
|
|
139
164
|
return items.map((item, index) => {
|
|
140
165
|
if (item.type === 'spacing') return item;
|
|
141
166
|
const prepared = prepareSharedFields(item);
|
|
142
|
-
if (item.type === 'button')
|
|
167
|
+
if (item.type === 'button')
|
|
168
|
+
return { ...prepared, buttonId: buildButtonId(index, side) };
|
|
143
169
|
return { ...prepared, menu: prepareMenuItems(item.menu, index, side) };
|
|
144
170
|
});
|
|
145
171
|
}
|
|
@@ -176,7 +202,10 @@ function collectBarButtonHandlers(
|
|
|
176
202
|
});
|
|
177
203
|
}
|
|
178
204
|
|
|
179
|
-
function readStringField(
|
|
205
|
+
function readStringField(
|
|
206
|
+
record: Record<string, unknown>,
|
|
207
|
+
key: string,
|
|
208
|
+
): string | undefined {
|
|
180
209
|
const value = record[key];
|
|
181
210
|
return typeof value === 'string' ? value : undefined;
|
|
182
211
|
}
|
package/src/core/index.ts
CHANGED
|
@@ -14,7 +14,11 @@ export {
|
|
|
14
14
|
computeActivityState,
|
|
15
15
|
isTopRoute,
|
|
16
16
|
} from './navigator-state';
|
|
17
|
-
export type {
|
|
17
|
+
export type {
|
|
18
|
+
IRoute,
|
|
19
|
+
INavigatorState,
|
|
20
|
+
INavigatorAction,
|
|
21
|
+
} from './navigator-state';
|
|
18
22
|
|
|
19
23
|
// Drops history entries whose `<Stack.Screen>` marker has unregistered - see
|
|
20
24
|
// stack-router-state.ts's header for why the Stack's history needs this and the Tab's projection
|
|
@@ -50,7 +54,11 @@ export type {
|
|
|
50
54
|
ISearchBarViewProps,
|
|
51
55
|
} from './navigator-props';
|
|
52
56
|
|
|
53
|
-
export {
|
|
57
|
+
export {
|
|
58
|
+
resolveHeaderConfigView,
|
|
59
|
+
resolveScreenView,
|
|
60
|
+
resolveSearchBarView,
|
|
61
|
+
} from './screen-options';
|
|
54
62
|
|
|
55
63
|
export { buildSearchBarHandle } from './search-bar-commands';
|
|
56
64
|
export type { ISearchBarCommands } from './search-bar-commands';
|
|
@@ -70,7 +78,10 @@ export {
|
|
|
70
78
|
} from './render-stack';
|
|
71
79
|
export type { IScreenRenderPlanInput, IScreenRenderPlan } from './render-stack';
|
|
72
80
|
|
|
73
|
-
export {
|
|
81
|
+
export {
|
|
82
|
+
serializeNavigatorState,
|
|
83
|
+
deserializeNavigatorState,
|
|
84
|
+
} from './state-persistence';
|
|
74
85
|
|
|
75
86
|
export {
|
|
76
87
|
createNavigationEmitter,
|
|
@@ -120,7 +131,10 @@ export {
|
|
|
120
131
|
drawerRouterReducer,
|
|
121
132
|
focusedDrawerRoute,
|
|
122
133
|
} from './drawer-router-state';
|
|
123
|
-
export type {
|
|
134
|
+
export type {
|
|
135
|
+
IDrawerRouterState,
|
|
136
|
+
IDrawerRouterAction,
|
|
137
|
+
} from './drawer-router-state';
|
|
124
138
|
|
|
125
139
|
export type {
|
|
126
140
|
IDrawerType,
|
|
@@ -49,7 +49,9 @@ function normalizeSegment(segment: string): string {
|
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
function joinPath(parent: string, child: string): string {
|
|
52
|
-
const parts = [normalizeSegment(parent), normalizeSegment(child)].filter(
|
|
52
|
+
const parts = [normalizeSegment(parent), normalizeSegment(child)].filter(
|
|
53
|
+
part => part.length > 0,
|
|
54
|
+
);
|
|
53
55
|
return parts.join('/');
|
|
54
56
|
}
|
|
55
57
|
|
|
@@ -68,14 +70,17 @@ function flattenScreens(
|
|
|
68
70
|
continue;
|
|
69
71
|
}
|
|
70
72
|
|
|
71
|
-
const nextParent =
|
|
73
|
+
const nextParent =
|
|
74
|
+
entry.path !== undefined ? joinPath(parentPath, entry.path) : parentPath;
|
|
72
75
|
|
|
73
76
|
if (entry.screens !== undefined) {
|
|
74
77
|
flattenScreens(entry.screens, nextParent, out);
|
|
75
78
|
} else if (entry.path !== undefined) {
|
|
76
79
|
out.push({ name, pattern: nextParent });
|
|
77
80
|
} else {
|
|
78
|
-
dlog(
|
|
81
|
+
dlog(
|
|
82
|
+
`linking-config: screen "${name}" has neither a path nor nested screens, skipping`,
|
|
83
|
+
);
|
|
79
84
|
}
|
|
80
85
|
}
|
|
81
86
|
return out;
|
|
@@ -93,12 +98,16 @@ function segmentCount(pattern: string): number {
|
|
|
93
98
|
// More static segments should win over a param that would also match (e.g. a literal
|
|
94
99
|
// '/user/me' pattern over '/user/:id'), mirroring how react-navigation prefers exact matches.
|
|
95
100
|
function bySpecificity(a: IFlatRoutePattern, b: IFlatRoutePattern): number {
|
|
96
|
-
const dynamicDiff =
|
|
101
|
+
const dynamicDiff =
|
|
102
|
+
dynamicSegmentCount(a.pattern) - dynamicSegmentCount(b.pattern);
|
|
97
103
|
if (dynamicDiff !== 0) return dynamicDiff;
|
|
98
104
|
return segmentCount(b.pattern) - segmentCount(a.pattern);
|
|
99
105
|
}
|
|
100
106
|
|
|
101
|
-
function matchPattern(
|
|
107
|
+
function matchPattern(
|
|
108
|
+
pattern: string,
|
|
109
|
+
pathname: string,
|
|
110
|
+
): Record<string, string> | null {
|
|
102
111
|
const patternSegments = pattern.length === 0 ? [] : pattern.split('/');
|
|
103
112
|
const pathSegments = pathname.length === 0 ? [] : pathname.split('/');
|
|
104
113
|
if (patternSegments.length !== pathSegments.length) return null;
|
|
@@ -127,7 +136,9 @@ function stripQueryAndHash(url: string): string {
|
|
|
127
136
|
// neither applies - an unrecognized scheme/host, not "no route matched".
|
|
128
137
|
function extractPathname(config: ILinkingConfig, url: string): string | null {
|
|
129
138
|
const withoutQuery = stripQueryAndHash(url);
|
|
130
|
-
const sortedPrefixes = [...config.prefixes].sort(
|
|
139
|
+
const sortedPrefixes = [...config.prefixes].sort(
|
|
140
|
+
(a, b) => b.length - a.length,
|
|
141
|
+
);
|
|
131
142
|
for (const prefix of sortedPrefixes) {
|
|
132
143
|
if (withoutQuery.startsWith(prefix)) {
|
|
133
144
|
return normalizeSegment(withoutQuery.slice(prefix.length));
|
|
@@ -137,14 +148,19 @@ function extractPathname(config: ILinkingConfig, url: string): string | null {
|
|
|
137
148
|
return null;
|
|
138
149
|
}
|
|
139
150
|
|
|
140
|
-
export function resolveRouteFromUrl(
|
|
151
|
+
export function resolveRouteFromUrl(
|
|
152
|
+
config: ILinkingConfig,
|
|
153
|
+
url: string,
|
|
154
|
+
): IRoute<unknown> | null {
|
|
141
155
|
const pathname = extractPathname(config, url);
|
|
142
156
|
if (pathname === null) {
|
|
143
157
|
dlog(`linking-config: url "${url}" matched no configured prefix`);
|
|
144
158
|
return null;
|
|
145
159
|
}
|
|
146
160
|
|
|
147
|
-
const candidates = flattenScreens(config.config.screens, '', []).sort(
|
|
161
|
+
const candidates = flattenScreens(config.config.screens, '', []).sort(
|
|
162
|
+
bySpecificity,
|
|
163
|
+
);
|
|
148
164
|
for (const candidate of candidates) {
|
|
149
165
|
const params = matchPattern(candidate.pattern, pathname);
|
|
150
166
|
if (params !== null) {
|
|
@@ -159,7 +175,9 @@ export function resolveRouteFromUrl(config: ILinkingConfig, url: string): IRoute
|
|
|
159
175
|
}
|
|
160
176
|
}
|
|
161
177
|
|
|
162
|
-
dlog(
|
|
178
|
+
dlog(
|
|
179
|
+
`linking-config: url "${url}" (path "${pathname}") matched no configured screen`,
|
|
180
|
+
);
|
|
163
181
|
return null;
|
|
164
182
|
}
|
|
165
183
|
|
|
@@ -182,7 +200,10 @@ function fillPattern(pattern: string, params: unknown): string | null {
|
|
|
182
200
|
return segments.join('/');
|
|
183
201
|
}
|
|
184
202
|
|
|
185
|
-
export function resolveUrlFromRoute(
|
|
203
|
+
export function resolveUrlFromRoute(
|
|
204
|
+
config: ILinkingConfig,
|
|
205
|
+
route: IRoute<unknown>,
|
|
206
|
+
): string | null {
|
|
186
207
|
const candidate = flattenScreens(config.config.screens, '', []).find(
|
|
187
208
|
entry => entry.name === route.name,
|
|
188
209
|
);
|
|
@@ -32,11 +32,17 @@ export type INavigationEventListener<TData = unknown> = (data: TData) => void;
|
|
|
32
32
|
|
|
33
33
|
export type INavigationEmitter = {
|
|
34
34
|
emit: (event: INavigationEventName, data?: unknown) => void;
|
|
35
|
-
addListener: (
|
|
35
|
+
addListener: (
|
|
36
|
+
event: INavigationEventName,
|
|
37
|
+
listener: INavigationEventListener,
|
|
38
|
+
) => () => void;
|
|
36
39
|
};
|
|
37
40
|
|
|
38
41
|
export function createNavigationEmitter(): INavigationEmitter {
|
|
39
|
-
const listenersByEvent = new Map<
|
|
42
|
+
const listenersByEvent = new Map<
|
|
43
|
+
INavigationEventName,
|
|
44
|
+
Set<INavigationEventListener>
|
|
45
|
+
>();
|
|
40
46
|
|
|
41
47
|
function addListener(
|
|
42
48
|
event: INavigationEventName,
|
|
@@ -44,9 +44,12 @@ export type IDrawerDescriptorMap = Record<
|
|
|
44
44
|
// return this union since they don't know which navigator mounted the calling component; the
|
|
45
45
|
// three guards below narrow it (used once, inside useStackNavigation/useTabNavigation/
|
|
46
46
|
// useDrawerNavigation and their Vue/Angular equivalents) so app code never writes the guard itself.
|
|
47
|
-
export type IAnyNavigatorHandle =
|
|
47
|
+
export type IAnyNavigatorHandle =
|
|
48
|
+
INavigatorHandle | ITabNavigatorHandle | IDrawerNavigatorHandle;
|
|
48
49
|
|
|
49
|
-
export function isStackNavigatorHandle(
|
|
50
|
+
export function isStackNavigatorHandle(
|
|
51
|
+
handle: IAnyNavigatorHandle,
|
|
52
|
+
): handle is INavigatorHandle {
|
|
50
53
|
return 'push' in handle;
|
|
51
54
|
}
|
|
52
55
|
|
|
@@ -58,6 +61,8 @@ export function isDrawerNavigatorHandle(
|
|
|
58
61
|
|
|
59
62
|
// jumpTo exists on both Tab and Drawer handles, so a Tab handle is a jumpTo-having handle that
|
|
60
63
|
// isn't a Drawer handle (Drawer's own openDrawer/closeDrawer/toggleDrawer rule it out).
|
|
61
|
-
export function isTabNavigatorHandle(
|
|
64
|
+
export function isTabNavigatorHandle(
|
|
65
|
+
handle: IAnyNavigatorHandle,
|
|
66
|
+
): handle is ITabNavigatorHandle {
|
|
62
67
|
return 'jumpTo' in handle && !isDrawerNavigatorHandle(handle);
|
|
63
68
|
}
|
|
@@ -51,8 +51,10 @@ export type IOptionalBooleanNativeProp = 'undefined' | 'false' | 'true';
|
|
|
51
51
|
// mirrors react-native-screens' own ScreenProps['sheetAllowedDetents'] - a plain fraction array,
|
|
52
52
|
// sorted ascending, or one of RNS's named presets; the fold to the native `sheetAllowedDetents:
|
|
53
53
|
// number[]` array happens in screen-options.ts, replicating RNS's own resolveSheetAllowedDetents.
|
|
54
|
-
export type ISheetAllowedDetents =
|
|
55
|
-
|
|
54
|
+
export type ISheetAllowedDetents =
|
|
55
|
+
number[] | 'fitToContents' | 'large' | 'medium' | 'all';
|
|
56
|
+
export type ISheetLargestUndimmedDetent =
|
|
57
|
+
number | 'last' | 'none' | 'all' | 'large' | 'medium';
|
|
56
58
|
export type ISheetInitialDetent = number | 'last';
|
|
57
59
|
|
|
58
60
|
// A bar-button item's icon: SF Symbol / iOS asset-catalog name, or an image asset. Mirrors RNS's
|
|
@@ -143,13 +145,20 @@ export type IHeaderBarButtonItemMenu = ISharedHeaderBarButtonItem & {
|
|
|
143
145
|
export type IHeaderBarButtonItemSpacing = { type: 'spacing'; spacing: number };
|
|
144
146
|
|
|
145
147
|
export type IHeaderBarButtonItem =
|
|
146
|
-
|
|
148
|
+
| IHeaderBarButtonItemAction
|
|
149
|
+
| IHeaderBarButtonItemMenu
|
|
150
|
+
| IHeaderBarButtonItemSpacing;
|
|
147
151
|
|
|
148
152
|
export type ISearchBarAutoCapitalize =
|
|
149
153
|
'systemDefault' | 'none' | 'words' | 'sentences' | 'characters';
|
|
150
154
|
|
|
151
155
|
export type ISearchBarPlacement =
|
|
152
|
-
|
|
156
|
+
| 'automatic'
|
|
157
|
+
| 'inline'
|
|
158
|
+
| 'stacked'
|
|
159
|
+
| 'integrated'
|
|
160
|
+
| 'integratedButton'
|
|
161
|
+
| 'integratedCentered';
|
|
153
162
|
|
|
154
163
|
// RNSSearchBar's full static config surface plus every event callback react-native-screens'
|
|
155
164
|
// own SearchBarProps exposes (types.tsx): onChangeText/onSearchButtonPress carry the current
|