@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
|
@@ -29,10 +29,21 @@ import {
|
|
|
29
29
|
useRef,
|
|
30
30
|
} from 'react';
|
|
31
31
|
import type { ReactElement, ReactNode } from 'react';
|
|
32
|
-
import {
|
|
33
|
-
|
|
32
|
+
import {
|
|
33
|
+
Animated,
|
|
34
|
+
PanResponder,
|
|
35
|
+
useWindowDimensions,
|
|
36
|
+
} from '@symbiote-native/react';
|
|
37
|
+
import type {
|
|
38
|
+
IPanResponderGestureState,
|
|
39
|
+
ISymbioteEvent,
|
|
40
|
+
} from '@symbiote-native/react';
|
|
34
41
|
import type { IDescriptor } from '@symbiote-native/components';
|
|
35
|
-
import {
|
|
42
|
+
import {
|
|
43
|
+
dlog,
|
|
44
|
+
type IStyleProp,
|
|
45
|
+
type IViewStyle,
|
|
46
|
+
} from '@symbiote-native/engine';
|
|
36
47
|
import {
|
|
37
48
|
DRAWER_DEFAULT_OVERLAY_COLOR,
|
|
38
49
|
NAVIGATION_EVENT_BLUR,
|
|
@@ -62,7 +73,10 @@ import type {
|
|
|
62
73
|
import { collectRegistry } from '../collect-registry';
|
|
63
74
|
import { NavigationContext } from '../navigation-context';
|
|
64
75
|
import { DrawerScreen } from '../drawer-screen';
|
|
65
|
-
import type {
|
|
76
|
+
import type {
|
|
77
|
+
IDrawerScreenOptionsArgs,
|
|
78
|
+
IDrawerScreenProps,
|
|
79
|
+
} from '../drawer-screen';
|
|
66
80
|
|
|
67
81
|
export type { IDrawerNavigatorHandle, IDrawerDescriptorMap } from '../../core';
|
|
68
82
|
|
|
@@ -80,7 +94,9 @@ export type IDrawerProps = IDrawerOptions & {
|
|
|
80
94
|
|
|
81
95
|
type IDrawerRegistryEntry = Omit<IDrawerScreenProps, 'name'>;
|
|
82
96
|
|
|
83
|
-
function isDrawerScreenElement(
|
|
97
|
+
function isDrawerScreenElement(
|
|
98
|
+
child: ReactNode,
|
|
99
|
+
): child is ReactElement<IDrawerScreenProps> {
|
|
84
100
|
return isValidElement(child) && child.type === DrawerScreen;
|
|
85
101
|
}
|
|
86
102
|
|
|
@@ -89,363 +105,411 @@ function resolveDrawerScreenOptions(
|
|
|
89
105
|
optionsArgs: IDrawerScreenOptionsArgs,
|
|
90
106
|
screenOptions: IDrawerScreenOptions | undefined,
|
|
91
107
|
): IDrawerScreenOptions {
|
|
92
|
-
const own =
|
|
108
|
+
const own =
|
|
109
|
+
typeof entry.options === 'function'
|
|
110
|
+
? entry.options(optionsArgs)
|
|
111
|
+
: entry.options;
|
|
93
112
|
return { ...screenOptions, ...own };
|
|
94
113
|
}
|
|
95
114
|
|
|
96
115
|
const DRAWER_SNAP_DURATION = 250;
|
|
97
116
|
|
|
98
|
-
const DrawerImpl = forwardRef<IDrawerNavigatorHandle, IDrawerProps>(
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
toValue
|
|
183
|
-
|
|
184
|
-
//
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
useNativeDriver: false,
|
|
188
|
-
}).start();
|
|
189
|
-
},
|
|
190
|
-
[progress],
|
|
191
|
-
);
|
|
192
|
-
|
|
193
|
-
const handle = useMemo<IDrawerNavigatorHandle>(
|
|
194
|
-
() => ({
|
|
195
|
-
openDrawer: () => {
|
|
196
|
-
dlog(`Drawer: openDrawer() called, isOpen=${stateRef.current.isOpen} at t=${Date.now()}`);
|
|
197
|
-
animateProgressTo(true);
|
|
198
|
-
dispatch({ type: 'openDrawer' });
|
|
199
|
-
},
|
|
200
|
-
closeDrawer: () => {
|
|
201
|
-
dlog(`Drawer: closeDrawer() called, isOpen=${stateRef.current.isOpen} at t=${Date.now()}`);
|
|
202
|
-
animateProgressTo(false);
|
|
203
|
-
dispatch({ type: 'closeDrawer' });
|
|
204
|
-
},
|
|
205
|
-
toggleDrawer: () => {
|
|
206
|
-
dlog(`Drawer: toggleDrawer() called, isOpen=${stateRef.current.isOpen} at t=${Date.now()}`);
|
|
207
|
-
animateProgressTo(!stateRef.current.isOpen);
|
|
208
|
-
dispatch({ type: 'toggleDrawer' });
|
|
209
|
-
},
|
|
210
|
-
jumpTo: (name: string) => {
|
|
211
|
-
// An unregistered name is a documented reducer no-op that hands the SAME state back, so
|
|
212
|
-
// animating off `isOpen` alone would slide the panel shut while the router still says
|
|
213
|
-
// open. useReducer's dispatch is async - stateRef only catches up on the next render - so
|
|
214
|
-
// the reducer (a pure function) is re-run here to learn what the dispatch produces, and
|
|
215
|
-
// the animation runs only on a genuine open -> closed transition.
|
|
216
|
-
const action: IDrawerRouterAction = { type: 'jumpTo', name };
|
|
217
|
-
const current = stateRef.current;
|
|
218
|
-
const next = drawerRouterReducer(current, action);
|
|
219
|
-
dispatch(action);
|
|
220
|
-
if (current.isOpen && !next.isOpen) animateProgressTo(false);
|
|
221
|
-
},
|
|
222
|
-
}),
|
|
223
|
-
[animateProgressTo],
|
|
224
|
-
);
|
|
225
|
-
|
|
226
|
-
useImperativeHandle(forwardedRef, () => handle, [handle]);
|
|
227
|
-
|
|
228
|
-
const panResponder = useRef(
|
|
229
|
-
PanResponder.create({
|
|
230
|
-
onStartShouldSetPanResponder: (
|
|
231
|
-
event: ISymbioteEvent,
|
|
232
|
-
gestureState: IPanResponderGestureState,
|
|
233
|
-
): boolean =>
|
|
234
|
-
shouldClaimDrawerSwipe(
|
|
235
|
-
event,
|
|
236
|
-
gestureState,
|
|
237
|
-
screenWidthRef.current,
|
|
238
|
-
stateRef.current.isOpen,
|
|
239
|
-
optionsRef.current,
|
|
240
|
-
'start',
|
|
241
|
-
),
|
|
242
|
-
onMoveShouldSetPanResponder: (
|
|
243
|
-
event: ISymbioteEvent,
|
|
244
|
-
gestureState: IPanResponderGestureState,
|
|
245
|
-
): boolean =>
|
|
246
|
-
shouldClaimDrawerSwipe(
|
|
247
|
-
event,
|
|
248
|
-
gestureState,
|
|
249
|
-
screenWidthRef.current,
|
|
250
|
-
stateRef.current.isOpen,
|
|
251
|
-
optionsRef.current,
|
|
252
|
-
'move',
|
|
253
|
-
),
|
|
254
|
-
onPanResponderGrant: (): void => {
|
|
255
|
-
dlog('Drawer: gesture grant');
|
|
256
|
-
dragStartProgress.current = stateRef.current.isOpen ? 1 : 0;
|
|
257
|
-
},
|
|
258
|
-
onPanResponderMove: (
|
|
259
|
-
_event: ISymbioteEvent,
|
|
260
|
-
gestureState: IPanResponderGestureState,
|
|
261
|
-
): void => {
|
|
262
|
-
const nextProgress = resolveDragProgress(
|
|
263
|
-
gestureState,
|
|
264
|
-
dragStartProgress.current,
|
|
265
|
-
optionsRef.current,
|
|
266
|
-
);
|
|
267
|
-
progress.setValue(nextProgress);
|
|
268
|
-
},
|
|
269
|
-
onPanResponderRelease: (
|
|
270
|
-
_event: ISymbioteEvent,
|
|
271
|
-
gestureState: IPanResponderGestureState,
|
|
272
|
-
): void => {
|
|
273
|
-
const intent = resolveSwipeIntent(
|
|
274
|
-
gestureState,
|
|
275
|
-
stateRef.current.isOpen,
|
|
276
|
-
optionsRef.current,
|
|
117
|
+
const DrawerImpl = forwardRef<IDrawerNavigatorHandle, IDrawerProps>(
|
|
118
|
+
(props, forwardedRef) => {
|
|
119
|
+
// Read BEFORE establishing this Drawer's own Context value below - becomes the `parent` link a
|
|
120
|
+
// nested screen's useNavigation().getParent() walks (e.g. this Drawer rendered as a Stack
|
|
121
|
+
// screen's content reaches that Stack via this value). undefined when this Drawer is the
|
|
122
|
+
// nesting root.
|
|
123
|
+
const ambientContext = useContext(NavigationContext);
|
|
124
|
+
const {
|
|
125
|
+
initialRouteName,
|
|
126
|
+
screenOptions,
|
|
127
|
+
renderDrawerContent,
|
|
128
|
+
children,
|
|
129
|
+
drawerStyle,
|
|
130
|
+
drawerType,
|
|
131
|
+
drawerPosition,
|
|
132
|
+
drawerWidth,
|
|
133
|
+
overlayColor,
|
|
134
|
+
swipeEnabled,
|
|
135
|
+
swipeEdgeWidth,
|
|
136
|
+
swipeMinDistance,
|
|
137
|
+
swipeMinVelocity,
|
|
138
|
+
} = props;
|
|
139
|
+
|
|
140
|
+
const options: IDrawerOptions = {
|
|
141
|
+
drawerType,
|
|
142
|
+
drawerPosition,
|
|
143
|
+
drawerWidth,
|
|
144
|
+
overlayColor,
|
|
145
|
+
swipeEnabled,
|
|
146
|
+
swipeEdgeWidth,
|
|
147
|
+
swipeMinDistance,
|
|
148
|
+
swipeMinVelocity,
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
const registry = useMemo(
|
|
152
|
+
() => collectRegistry(children, isDrawerScreenElement),
|
|
153
|
+
[children],
|
|
154
|
+
);
|
|
155
|
+
const routeIdPrefix = useId();
|
|
156
|
+
|
|
157
|
+
const routes = useMemo<IRoute<unknown>[]>(
|
|
158
|
+
() =>
|
|
159
|
+
Array.from(registry.entries()).map(([name, entry]) => ({
|
|
160
|
+
key: `${routeIdPrefix}-${name}`,
|
|
161
|
+
name,
|
|
162
|
+
params: entry.initialParams,
|
|
163
|
+
})),
|
|
164
|
+
[registry, routeIdPrefix],
|
|
165
|
+
);
|
|
166
|
+
|
|
167
|
+
const [state, dispatch] = useReducer(drawerRouterReducer, undefined, () =>
|
|
168
|
+
createInitialDrawerRouterState(routes, initialRouteName),
|
|
169
|
+
);
|
|
170
|
+
|
|
171
|
+
if (routes.length === 0)
|
|
172
|
+
dlog('Drawer: no <Drawer.Screen> children registered');
|
|
173
|
+
|
|
174
|
+
const { width: screenWidth } = useWindowDimensions();
|
|
175
|
+
|
|
176
|
+
// progress: 0 closed -> 1 open, the single Animated.Value every slide/opacity transform below
|
|
177
|
+
// interpolates from.
|
|
178
|
+
const progress = useRef(new Animated.Value(state.isOpen ? 1 : 0)).current;
|
|
179
|
+
// Where a drag STARTS from, in progress units. Always exactly 0 or 1: a gesture only ever
|
|
180
|
+
// begins at rest, since terminate/release always snap the value back to a resting state before
|
|
181
|
+
// another grant can fire.
|
|
182
|
+
const dragStartProgress = useRef(0);
|
|
183
|
+
|
|
184
|
+
// Refs so the PanResponder's callbacks - built ONCE below, RN's own idiom - always read the
|
|
185
|
+
// CURRENT render's values without forcing a new PanResponder identity on every state/prop
|
|
186
|
+
// change (recreating panHandlers mid-gesture would drop the in-flight touch).
|
|
187
|
+
const optionsRef = useRef(options);
|
|
188
|
+
optionsRef.current = options;
|
|
189
|
+
const screenWidthRef = useRef(screenWidth);
|
|
190
|
+
screenWidthRef.current = screenWidth;
|
|
191
|
+
// The whole router state, not just its isOpen bit: jumpTo below has to re-run the reducer to
|
|
192
|
+
// learn what the dispatch actually produced, and that needs the routes too.
|
|
193
|
+
const stateRef = useRef(state);
|
|
194
|
+
stateRef.current = state;
|
|
195
|
+
|
|
196
|
+
const animateProgressTo = useCallback(
|
|
197
|
+
(open: boolean): void => {
|
|
198
|
+
// Investigation instrumentation (Drawer openDrawer-no-op / toggleDrawer-no-animation bug):
|
|
199
|
+
// every imperative caller (openDrawer/closeDrawer/toggleDrawer/jumpTo) funnels through here,
|
|
200
|
+
// so this single seam proves whether Animated.timing is actually being started at all, and
|
|
201
|
+
// with what toValue - a live-only failure (e.g. a stale progress ref, or Animated.timing
|
|
202
|
+
// silently short-circuiting) would show up as this log firing with no visible motion. Kept
|
|
203
|
+
// behind DEBUG per <keep_logs_gate_behind_DEBUG>, never removed.
|
|
204
|
+
dlog(
|
|
205
|
+
`Drawer: animateProgressTo(open=${open}) starting at t=${Date.now()}`,
|
|
277
206
|
);
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
207
|
+
Animated.timing(progress, {
|
|
208
|
+
toValue: open ? 1 : 0,
|
|
209
|
+
duration: DRAWER_SNAP_DURATION,
|
|
210
|
+
// Native-driver wiring (the AnimatedComponent passthrough opt-in ADR 0017 defines) is
|
|
211
|
+
// deferred for v1 - see this file's header feasibility note. The JS timing loop still
|
|
212
|
+
// drives every frame, same as any other non-native-driven Animated.timing in this codebase.
|
|
213
|
+
useNativeDriver: false,
|
|
214
|
+
}).start();
|
|
282
215
|
},
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
216
|
+
[progress],
|
|
217
|
+
);
|
|
218
|
+
|
|
219
|
+
const handle = useMemo<IDrawerNavigatorHandle>(
|
|
220
|
+
() => ({
|
|
221
|
+
openDrawer: () => {
|
|
222
|
+
dlog(
|
|
223
|
+
`Drawer: openDrawer() called, isOpen=${stateRef.current.isOpen} at t=${Date.now()}`,
|
|
224
|
+
);
|
|
225
|
+
animateProgressTo(true);
|
|
226
|
+
dispatch({ type: 'openDrawer' });
|
|
227
|
+
},
|
|
228
|
+
closeDrawer: () => {
|
|
229
|
+
dlog(
|
|
230
|
+
`Drawer: closeDrawer() called, isOpen=${stateRef.current.isOpen} at t=${Date.now()}`,
|
|
231
|
+
);
|
|
232
|
+
animateProgressTo(false);
|
|
233
|
+
dispatch({ type: 'closeDrawer' });
|
|
234
|
+
},
|
|
235
|
+
toggleDrawer: () => {
|
|
236
|
+
dlog(
|
|
237
|
+
`Drawer: toggleDrawer() called, isOpen=${stateRef.current.isOpen} at t=${Date.now()}`,
|
|
238
|
+
);
|
|
239
|
+
animateProgressTo(!stateRef.current.isOpen);
|
|
240
|
+
dispatch({ type: 'toggleDrawer' });
|
|
241
|
+
},
|
|
242
|
+
jumpTo: (name: string) => {
|
|
243
|
+
// An unregistered name is a documented reducer no-op that hands the SAME state back, so
|
|
244
|
+
// animating off `isOpen` alone would slide the panel shut while the router still says
|
|
245
|
+
// open. useReducer's dispatch is async - stateRef only catches up on the next render - so
|
|
246
|
+
// the reducer (a pure function) is re-run here to learn what the dispatch produces, and
|
|
247
|
+
// the animation runs only on a genuine open -> closed transition.
|
|
248
|
+
const action: IDrawerRouterAction = { type: 'jumpTo', name };
|
|
249
|
+
const current = stateRef.current;
|
|
250
|
+
const next = drawerRouterReducer(current, action);
|
|
251
|
+
dispatch(action);
|
|
252
|
+
if (current.isOpen && !next.isOpen) animateProgressTo(false);
|
|
253
|
+
},
|
|
254
|
+
}),
|
|
255
|
+
[animateProgressTo],
|
|
256
|
+
);
|
|
257
|
+
|
|
258
|
+
useImperativeHandle(forwardedRef, () => handle, [handle]);
|
|
259
|
+
|
|
260
|
+
const panResponder = useRef(
|
|
261
|
+
PanResponder.create({
|
|
262
|
+
onStartShouldSetPanResponder: (
|
|
263
|
+
event: ISymbioteEvent,
|
|
264
|
+
gestureState: IPanResponderGestureState,
|
|
265
|
+
): boolean =>
|
|
266
|
+
shouldClaimDrawerSwipe(
|
|
267
|
+
event,
|
|
268
|
+
gestureState,
|
|
269
|
+
screenWidthRef.current,
|
|
270
|
+
stateRef.current.isOpen,
|
|
271
|
+
optionsRef.current,
|
|
272
|
+
'start',
|
|
273
|
+
),
|
|
274
|
+
onMoveShouldSetPanResponder: (
|
|
275
|
+
event: ISymbioteEvent,
|
|
276
|
+
gestureState: IPanResponderGestureState,
|
|
277
|
+
): boolean =>
|
|
278
|
+
shouldClaimDrawerSwipe(
|
|
279
|
+
event,
|
|
280
|
+
gestureState,
|
|
281
|
+
screenWidthRef.current,
|
|
282
|
+
stateRef.current.isOpen,
|
|
283
|
+
optionsRef.current,
|
|
284
|
+
'move',
|
|
285
|
+
),
|
|
286
|
+
onPanResponderGrant: (): void => {
|
|
287
|
+
dlog('Drawer: gesture grant');
|
|
288
|
+
dragStartProgress.current = stateRef.current.isOpen ? 1 : 0;
|
|
289
|
+
},
|
|
290
|
+
onPanResponderMove: (
|
|
291
|
+
_event: ISymbioteEvent,
|
|
292
|
+
gestureState: IPanResponderGestureState,
|
|
293
|
+
): void => {
|
|
294
|
+
const nextProgress = resolveDragProgress(
|
|
295
|
+
gestureState,
|
|
296
|
+
dragStartProgress.current,
|
|
297
|
+
optionsRef.current,
|
|
298
|
+
);
|
|
299
|
+
progress.setValue(nextProgress);
|
|
300
|
+
},
|
|
301
|
+
onPanResponderRelease: (
|
|
302
|
+
_event: ISymbioteEvent,
|
|
303
|
+
gestureState: IPanResponderGestureState,
|
|
304
|
+
): void => {
|
|
305
|
+
const intent = resolveSwipeIntent(
|
|
306
|
+
gestureState,
|
|
307
|
+
stateRef.current.isOpen,
|
|
308
|
+
optionsRef.current,
|
|
309
|
+
);
|
|
310
|
+
const open = intent === 'open';
|
|
311
|
+
dlog(`Drawer: gesture release -> ${open ? 'open' : 'close'}`);
|
|
312
|
+
animateProgressTo(open);
|
|
313
|
+
dispatch(open ? { type: 'openDrawer' } : { type: 'closeDrawer' });
|
|
314
|
+
},
|
|
315
|
+
onPanResponderTerminate: (): void => {
|
|
316
|
+
dlog('Drawer: gesture terminated, snapping back');
|
|
317
|
+
animateProgressTo(stateRef.current.isOpen);
|
|
318
|
+
},
|
|
319
|
+
}),
|
|
320
|
+
).current;
|
|
321
|
+
|
|
322
|
+
const animated = isDrawerAnimated(options);
|
|
323
|
+
const geometry = useMemo(
|
|
324
|
+
() => resolveDrawerGeometry(options),
|
|
325
|
+
[options.drawerType, options.drawerPosition, options.drawerWidth],
|
|
326
|
+
);
|
|
327
|
+
|
|
328
|
+
const panelSlot = animated
|
|
329
|
+
? resolveDrawerSlotInterpolation(geometry, 'panel')
|
|
330
|
+
: undefined;
|
|
331
|
+
const contentSlot = animated
|
|
332
|
+
? resolveDrawerSlotInterpolation(geometry, 'content')
|
|
333
|
+
: undefined;
|
|
334
|
+
const overlaySlot = animated
|
|
335
|
+
? resolveDrawerSlotInterpolation(geometry, 'overlay')
|
|
336
|
+
: undefined;
|
|
337
|
+
|
|
338
|
+
const panelTranslateX = panelSlot
|
|
339
|
+
? progress.interpolate(panelSlot.translateX)
|
|
340
|
+
: undefined;
|
|
341
|
+
const contentTranslateX = contentSlot
|
|
342
|
+
? progress.interpolate(contentSlot.translateX)
|
|
343
|
+
: undefined;
|
|
344
|
+
const overlayOpacity = overlaySlot
|
|
345
|
+
? progress.interpolate(overlaySlot.opacity)
|
|
346
|
+
: undefined;
|
|
347
|
+
// The overlay is a full-screen absolutely-positioned sibling BELOW content in paint order
|
|
348
|
+
// (see render-drawer.ts's drawerChildOrder) - for 'front' that's fine since content never moves,
|
|
349
|
+
// but for 'slide' content itself translates away by contentTranslateX, and without following it
|
|
350
|
+
// the overlay stays pinned full-screen, dimming (and touch-capturing) the now-revealed panel
|
|
351
|
+
// underneath instead of just the content sliver it's meant to dim. Tying overlay to the SAME
|
|
352
|
+
// translateX as content keeps it registered exactly under content, wherever content actually is
|
|
353
|
+
// (resolveDrawerSlotInterpolation's 'overlay' overload returns that same content delta).
|
|
354
|
+
const overlayTranslateX = overlaySlot
|
|
355
|
+
? progress.interpolate(overlaySlot.translateX)
|
|
356
|
+
: undefined;
|
|
357
|
+
|
|
358
|
+
const focusedRoute = state.routes[state.index];
|
|
359
|
+
const focusedEntry = focusedRoute
|
|
360
|
+
? registry.get(focusedRoute.name)
|
|
361
|
+
: undefined;
|
|
362
|
+
if (focusedRoute && !focusedEntry) {
|
|
363
|
+
dlog(
|
|
364
|
+
`Drawer: no screen registered for route name "${focusedRoute.name}"`,
|
|
365
|
+
);
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
// Only the focused route's screen is ever mounted (like Tab, unlike Stack which keeps every
|
|
369
|
+
// pushed route alive), so a fresh emitter per focus change is sufficient - see tabs.ts's
|
|
370
|
+
// matching comment for why no per-route emitter map is needed. Keyed on the route KEY, not the
|
|
371
|
+
// route object, so a jumpTo-with-no-params re-focus of the ALREADY-focused route (a no-op in
|
|
372
|
+
// drawerRouterReducer) and any future params merge don't spuriously re-fire focus/blur.
|
|
373
|
+
const focusedRouteKey = focusedRoute?.key;
|
|
374
|
+
const routeEmitter = useMemo(
|
|
375
|
+
() => createNavigationEmitter(),
|
|
376
|
+
[focusedRouteKey],
|
|
377
|
+
);
|
|
378
|
+
|
|
379
|
+
// Drawer paints its own panel in pure JS - there is no native onAppear/onDisappear to hook
|
|
380
|
+
// (unlike Stack's RNSScreen), so focus/blur is synthesized here: mount = focus, cleanup = blur,
|
|
381
|
+
// exactly what an effect keyed on focusedRouteKey already encodes - no diffFocusedRoute
|
|
382
|
+
// indirection needed (unlike Vue/Angular, which diff real prev/next keys inside an imperative
|
|
383
|
+
// watch/CD callback that has no mount/cleanup pairing of its own).
|
|
384
|
+
useEffect(() => {
|
|
385
|
+
if (focusedRouteKey === undefined) return undefined;
|
|
386
|
+
dlog(`Drawer: route "${focusedRoute?.name}" focused`);
|
|
387
|
+
routeEmitter.emit(NAVIGATION_EVENT_FOCUS);
|
|
388
|
+
return () => {
|
|
389
|
+
dlog(`Drawer: route "${focusedRoute?.name}" blurred`);
|
|
390
|
+
routeEmitter.emit(NAVIGATION_EVENT_BLUR);
|
|
391
|
+
};
|
|
392
|
+
// focusedRoute omitted deliberately: only its .key (tracked via focusedRouteKey) should
|
|
393
|
+
// re-run this effect - see the comment above on focusedRouteKey.
|
|
394
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
395
|
+
}, [routeEmitter, focusedRouteKey]);
|
|
396
|
+
|
|
397
|
+
const content =
|
|
398
|
+
focusedEntry && focusedRoute
|
|
399
|
+
? createElement(
|
|
400
|
+
NavigationContext.Provider,
|
|
401
|
+
{
|
|
402
|
+
value: {
|
|
403
|
+
route: focusedRoute,
|
|
404
|
+
navigation: handle,
|
|
405
|
+
emitter: routeEmitter,
|
|
406
|
+
parent: ambientContext,
|
|
407
|
+
},
|
|
408
|
+
},
|
|
409
|
+
createElement(focusedEntry.component),
|
|
410
|
+
)
|
|
411
|
+
: null;
|
|
412
|
+
|
|
413
|
+
const descriptors: IDrawerDescriptorMap = {};
|
|
414
|
+
for (const route of state.routes) {
|
|
415
|
+
const entry = registry.get(route.name);
|
|
416
|
+
if (entry === undefined) continue;
|
|
417
|
+
descriptors[route.key] = {
|
|
418
|
+
options: resolveDrawerScreenOptions(
|
|
419
|
+
entry,
|
|
420
|
+
{ route, navigation: handle },
|
|
421
|
+
screenOptions,
|
|
422
|
+
),
|
|
423
|
+
navigation: handle,
|
|
424
|
+
};
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
const drawerContent =
|
|
428
|
+
renderDrawerContent?.({ state, descriptors, navigation: handle }) ?? null;
|
|
429
|
+
|
|
430
|
+
const root = renderDrawer(
|
|
431
|
+
{
|
|
432
|
+
overlayColor: overlayColor ?? DRAWER_DEFAULT_OVERLAY_COLOR,
|
|
433
|
+
drawerStyle,
|
|
434
|
+
contentPassthrough: {},
|
|
435
|
+
overlayPassthrough: animated
|
|
436
|
+
? {
|
|
437
|
+
pointerEvents: state.isOpen ? 'auto' : 'none',
|
|
438
|
+
onStartShouldSetResponder: () => true,
|
|
439
|
+
onResponderRelease: () => {
|
|
440
|
+
animateProgressTo(false);
|
|
441
|
+
dispatch({ type: 'closeDrawer' });
|
|
442
|
+
},
|
|
443
|
+
}
|
|
444
|
+
: {},
|
|
445
|
+
panelPassthrough: {},
|
|
286
446
|
},
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
() =>
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
const contentTranslateX = contentSlot ? progress.interpolate(contentSlot.translateX) : undefined;
|
|
302
|
-
const overlayOpacity = overlaySlot ? progress.interpolate(overlaySlot.opacity) : undefined;
|
|
303
|
-
// The overlay is a full-screen absolutely-positioned sibling BELOW content in paint order
|
|
304
|
-
// (see render-drawer.ts's drawerChildOrder) - for 'front' that's fine since content never moves,
|
|
305
|
-
// but for 'slide' content itself translates away by contentTranslateX, and without following it
|
|
306
|
-
// the overlay stays pinned full-screen, dimming (and touch-capturing) the now-revealed panel
|
|
307
|
-
// underneath instead of just the content sliver it's meant to dim. Tying overlay to the SAME
|
|
308
|
-
// translateX as content keeps it registered exactly under content, wherever content actually is
|
|
309
|
-
// (resolveDrawerSlotInterpolation's 'overlay' overload returns that same content delta).
|
|
310
|
-
const overlayTranslateX = overlaySlot ? progress.interpolate(overlaySlot.translateX) : undefined;
|
|
311
|
-
|
|
312
|
-
const focusedRoute = state.routes[state.index];
|
|
313
|
-
const focusedEntry = focusedRoute ? registry.get(focusedRoute.name) : undefined;
|
|
314
|
-
if (focusedRoute && !focusedEntry) {
|
|
315
|
-
dlog(`Drawer: no screen registered for route name "${focusedRoute.name}"`);
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
// Only the focused route's screen is ever mounted (like Tab, unlike Stack which keeps every
|
|
319
|
-
// pushed route alive), so a fresh emitter per focus change is sufficient - see tabs.ts's
|
|
320
|
-
// matching comment for why no per-route emitter map is needed. Keyed on the route KEY, not the
|
|
321
|
-
// route object, so a jumpTo-with-no-params re-focus of the ALREADY-focused route (a no-op in
|
|
322
|
-
// drawerRouterReducer) and any future params merge don't spuriously re-fire focus/blur.
|
|
323
|
-
const focusedRouteKey = focusedRoute?.key;
|
|
324
|
-
const routeEmitter = useMemo(() => createNavigationEmitter(), [focusedRouteKey]);
|
|
325
|
-
|
|
326
|
-
// Drawer paints its own panel in pure JS - there is no native onAppear/onDisappear to hook
|
|
327
|
-
// (unlike Stack's RNSScreen), so focus/blur is synthesized here: mount = focus, cleanup = blur,
|
|
328
|
-
// exactly what an effect keyed on focusedRouteKey already encodes - no diffFocusedRoute
|
|
329
|
-
// indirection needed (unlike Vue/Angular, which diff real prev/next keys inside an imperative
|
|
330
|
-
// watch/CD callback that has no mount/cleanup pairing of its own).
|
|
331
|
-
useEffect(() => {
|
|
332
|
-
if (focusedRouteKey === undefined) return undefined;
|
|
333
|
-
dlog(`Drawer: route "${focusedRoute?.name}" focused`);
|
|
334
|
-
routeEmitter.emit(NAVIGATION_EVENT_FOCUS);
|
|
335
|
-
return () => {
|
|
336
|
-
dlog(`Drawer: route "${focusedRoute?.name}" blurred`);
|
|
337
|
-
routeEmitter.emit(NAVIGATION_EVENT_BLUR);
|
|
447
|
+
options,
|
|
448
|
+
);
|
|
449
|
+
|
|
450
|
+
const order = drawerChildOrder(options);
|
|
451
|
+
const slots = new Map<IDrawerSlot, IDescriptor>();
|
|
452
|
+
order.forEach((slot, index) => {
|
|
453
|
+
const descriptor = root.children[index];
|
|
454
|
+
if (typeof descriptor !== 'string') slots.set(slot, descriptor);
|
|
455
|
+
});
|
|
456
|
+
|
|
457
|
+
const slotChildren: Record<IDrawerSlot, ReactNode> = {
|
|
458
|
+
content,
|
|
459
|
+
overlay: null,
|
|
460
|
+
panel: drawerContent,
|
|
338
461
|
};
|
|
339
|
-
//
|
|
340
|
-
//
|
|
341
|
-
//
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
parent: ambientContext,
|
|
462
|
+
// Each animated style holds an AnimatedInterpolation node, not a plain number/color - it feeds
|
|
463
|
+
// only Animated.View's deliberately permissive `style?: unknown` (see create-animated-component.tsx),
|
|
464
|
+
// never the plain-IViewStyle branch below, so this stays untyped rather than widening IViewStyle.
|
|
465
|
+
const slotAnimatedStyle: Record<IDrawerSlot, unknown> = {
|
|
466
|
+
content:
|
|
467
|
+
contentTranslateX === undefined
|
|
468
|
+
? undefined
|
|
469
|
+
: { transform: [{ translateX: contentTranslateX }] },
|
|
470
|
+
overlay:
|
|
471
|
+
overlayOpacity === undefined
|
|
472
|
+
? undefined
|
|
473
|
+
: {
|
|
474
|
+
opacity: overlayOpacity,
|
|
475
|
+
transform: [{ translateX: overlayTranslateX }],
|
|
354
476
|
},
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
const descriptors: IDrawerDescriptorMap = {};
|
|
361
|
-
for (const route of state.routes) {
|
|
362
|
-
const entry = registry.get(route.name);
|
|
363
|
-
if (entry === undefined) continue;
|
|
364
|
-
descriptors[route.key] = {
|
|
365
|
-
options: resolveDrawerScreenOptions(entry, { route, navigation: handle }, screenOptions),
|
|
366
|
-
navigation: handle,
|
|
477
|
+
panel:
|
|
478
|
+
panelTranslateX === undefined
|
|
479
|
+
? undefined
|
|
480
|
+
: { transform: [{ translateX: panelTranslateX }] },
|
|
367
481
|
};
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
const drawerContent = renderDrawerContent?.({ state, descriptors, navigation: handle }) ?? null;
|
|
371
482
|
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
},
|
|
385
|
-
}
|
|
386
|
-
: {},
|
|
387
|
-
panelPassthrough: {},
|
|
388
|
-
},
|
|
389
|
-
options,
|
|
390
|
-
);
|
|
391
|
-
|
|
392
|
-
const order = drawerChildOrder(options);
|
|
393
|
-
const slots = new Map<IDrawerSlot, IDescriptor>();
|
|
394
|
-
order.forEach((slot, index) => {
|
|
395
|
-
const descriptor = root.children[index];
|
|
396
|
-
if (typeof descriptor !== 'string') slots.set(slot, descriptor);
|
|
397
|
-
});
|
|
398
|
-
|
|
399
|
-
const slotChildren: Record<IDrawerSlot, ReactNode> = {
|
|
400
|
-
content,
|
|
401
|
-
overlay: null,
|
|
402
|
-
panel: drawerContent,
|
|
403
|
-
};
|
|
404
|
-
// Each animated style holds an AnimatedInterpolation node, not a plain number/color - it feeds
|
|
405
|
-
// only Animated.View's deliberately permissive `style?: unknown` (see create-animated-component.tsx),
|
|
406
|
-
// never the plain-IViewStyle branch below, so this stays untyped rather than widening IViewStyle.
|
|
407
|
-
const slotAnimatedStyle: Record<IDrawerSlot, unknown> = {
|
|
408
|
-
content:
|
|
409
|
-
contentTranslateX === undefined
|
|
410
|
-
? undefined
|
|
411
|
-
: { transform: [{ translateX: contentTranslateX }] },
|
|
412
|
-
overlay:
|
|
413
|
-
overlayOpacity === undefined
|
|
414
|
-
? undefined
|
|
415
|
-
: { opacity: overlayOpacity, transform: [{ translateX: overlayTranslateX }] },
|
|
416
|
-
panel:
|
|
417
|
-
panelTranslateX === undefined ? undefined : { transform: [{ translateX: panelTranslateX }] },
|
|
418
|
-
};
|
|
419
|
-
|
|
420
|
-
function renderSlot(slot: IDrawerSlot): ReactElement | null {
|
|
421
|
-
const descriptor = slots.get(slot);
|
|
422
|
-
if (descriptor === undefined) return null;
|
|
423
|
-
const animatedStyle = slotAnimatedStyle[slot];
|
|
424
|
-
if (animatedStyle === undefined) {
|
|
483
|
+
function renderSlot(slot: IDrawerSlot): ReactElement | null {
|
|
484
|
+
const descriptor = slots.get(slot);
|
|
485
|
+
if (descriptor === undefined) return null;
|
|
486
|
+
const animatedStyle = slotAnimatedStyle[slot];
|
|
487
|
+
if (animatedStyle === undefined) {
|
|
488
|
+
return createElement(
|
|
489
|
+
descriptor.type,
|
|
490
|
+
{ key: descriptor.key, ...descriptor.props },
|
|
491
|
+
slotChildren[slot],
|
|
492
|
+
);
|
|
493
|
+
}
|
|
494
|
+
const style = [descriptor.props.style, animatedStyle];
|
|
425
495
|
return createElement(
|
|
426
|
-
|
|
427
|
-
{ key: descriptor.key, ...descriptor.props },
|
|
496
|
+
Animated.View,
|
|
497
|
+
{ key: descriptor.key, ...descriptor.props, style },
|
|
428
498
|
slotChildren[slot],
|
|
429
499
|
);
|
|
430
500
|
}
|
|
431
|
-
|
|
501
|
+
|
|
502
|
+
const drawerChildren = order
|
|
503
|
+
.map(renderSlot)
|
|
504
|
+
.filter((element): element is ReactElement => element !== null);
|
|
505
|
+
|
|
432
506
|
return createElement(
|
|
433
|
-
|
|
434
|
-
{
|
|
435
|
-
|
|
507
|
+
'symbiote-view',
|
|
508
|
+
{ style: root.props.style, ...panResponder.panHandlers },
|
|
509
|
+
...drawerChildren,
|
|
436
510
|
);
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
const drawerChildren = order
|
|
440
|
-
.map(renderSlot)
|
|
441
|
-
.filter((element): element is ReactElement => element !== null);
|
|
442
|
-
|
|
443
|
-
return createElement(
|
|
444
|
-
'symbiote-view',
|
|
445
|
-
{ style: root.props.style, ...panResponder.panHandlers },
|
|
446
|
-
...drawerChildren,
|
|
447
|
-
);
|
|
448
|
-
});
|
|
511
|
+
},
|
|
512
|
+
);
|
|
449
513
|
|
|
450
514
|
export const Drawer = Object.assign(DrawerImpl, { Screen: DrawerScreen });
|
|
451
515
|
|