@symbiote-native/navigation 2.0.3 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +42 -12
- package/build/angular/drawer/index.js +29 -13
- package/build/angular/index.d.ts +4 -4
- package/build/angular/injectors/inject-navigation.js +1 -1
- package/build/angular/navigation-scope.directive.js +1 -1
- package/build/angular/search-bar-ref.directive.js +1 -1
- package/build/angular/stack/index.d.ts +2 -0
- package/build/angular/stack/index.js +43 -9
- package/build/angular/tabs/index.d.ts +1 -1
- package/build/angular/tabs/index.js +40 -13
- package/build/core/drawer-options/index.js +19 -5
- package/build/core/drawer-router-state/index.js +8 -2
- package/build/core/guards.js +3 -1
- package/build/core/header-bar-buttons.js +14 -4
- package/build/core/index.d.ts +6 -5
- package/build/core/index.js +7 -3
- package/build/core/navigator-state/index.js +12 -4
- package/build/core/render-drawer.js +4 -1
- package/build/core/render-stack.js +13 -4
- package/build/core/screen-options.js +1 -1
- package/build/core/search-bar-commands.js +1 -1
- package/build/core/stack-router-state/index.d.ts +2 -0
- package/build/core/stack-router-state/index.js +44 -0
- package/build/core/state-persistence/index.js +6 -2
- package/build/core/tab-router-state/index.d.ts +1 -0
- package/build/core/tab-router-state/index.js +27 -2
- package/build/react/drawer/index.js +59 -26
- package/build/react/index.d.ts +7 -7
- package/build/react/index.js +2 -2
- package/build/react/stack/index.js +47 -10
- package/build/react/tabs/index.js +36 -12
- package/build/solid/drawer/index.d.ts +22 -0
- package/build/solid/drawer/index.js +394 -0
- package/build/solid/drawer-screen-props.d.ts +13 -0
- package/build/solid/drawer-screen-props.js +2 -0
- package/build/solid/host.d.ts +2 -0
- package/build/solid/host.js +23 -0
- package/build/solid/index.d.ts +19 -0
- package/build/solid/index.js +24 -0
- package/build/solid/linking.d.ts +3 -0
- package/build/solid/linking.js +55 -0
- package/build/solid/navigation-context.d.ts +13 -0
- package/build/solid/navigation-context.js +37 -0
- package/build/solid/primitives/create-focus-effect.d.ts +1 -0
- package/build/solid/primitives/create-focus-effect.js +30 -0
- package/build/solid/primitives/create-is-focused.d.ts +2 -0
- package/build/solid/primitives/create-is-focused.js +31 -0
- package/build/solid/primitives/create-navigation-state.d.ts +3 -0
- package/build/solid/primitives/create-navigation-state.js +30 -0
- package/build/solid/primitives/index.d.ts +12 -0
- package/build/solid/primitives/index.js +8 -0
- package/build/solid/primitives/use-drawer-navigation.d.ts +5 -0
- package/build/solid/primitives/use-drawer-navigation.js +15 -0
- package/build/solid/primitives/use-navigation.d.ts +8 -0
- package/build/solid/primitives/use-navigation.js +23 -0
- package/build/solid/primitives/use-route.d.ts +3 -0
- package/build/solid/primitives/use-route.js +14 -0
- package/build/solid/primitives/use-stack-navigation.d.ts +5 -0
- package/build/solid/primitives/use-stack-navigation.js +19 -0
- package/build/solid/primitives/use-tab-navigation.d.ts +5 -0
- package/build/solid/primitives/use-tab-navigation.js +15 -0
- package/build/solid/screen-props.d.ts +19 -0
- package/build/solid/screen-props.js +9 -0
- package/build/solid/screen-registry.d.ts +31 -0
- package/build/solid/screen-registry.js +86 -0
- package/build/solid/screen.d.ts +6 -0
- package/build/solid/screen.js +69 -0
- package/build/solid/stack/index.d.ts +15 -0
- package/build/solid/stack/index.js +348 -0
- package/build/solid/tab-screen-props.d.ts +13 -0
- package/build/solid/tab-screen-props.js +3 -0
- package/build/solid/tabs/index.d.ts +14 -0
- package/build/solid/tabs/index.js +228 -0
- package/build/svelte/attachments.d.ts +5 -0
- package/build/svelte/attachments.js +61 -0
- package/build/svelte/descriptor-subtree.d.ts +3 -0
- package/build/svelte/descriptor-subtree.js +39 -0
- package/build/svelte/drawer/drawer-props.d.ts +14 -0
- package/build/svelte/drawer/drawer-props.js +3 -0
- package/build/svelte/drawer/index.d.ts +10 -0
- package/build/svelte/drawer/index.js +6 -0
- package/build/svelte/drawer/index.svelte +559 -0
- package/build/svelte/drawer/index.svelte.d.ts +10 -0
- package/build/svelte/drawer-screen-props.d.ts +13 -0
- package/build/svelte/drawer-screen-props.js +4 -0
- package/build/svelte/drawer-screen.svelte +25 -0
- package/build/svelte/drawer-screen.svelte.d.ts +4 -0
- package/build/svelte/index.d.ts +21 -0
- package/build/svelte/index.js +20 -0
- package/build/svelte/linking.svelte.d.ts +2 -0
- package/build/svelte/linking.svelte.js +55 -0
- package/build/svelte/navigation-context.d.ts +14 -0
- package/build/svelte/navigation-context.js +44 -0
- package/build/svelte/navigation-scope.svelte +27 -0
- package/build/svelte/navigation-scope.svelte.d.ts +9 -0
- package/build/svelte/registry-host.d.ts +1 -0
- package/build/svelte/registry-host.js +26 -0
- package/build/svelte/runes/index.d.ts +12 -0
- package/build/svelte/runes/index.js +12 -0
- package/build/svelte/runes/use-drawer-navigation.svelte.d.ts +6 -0
- package/build/svelte/runes/use-drawer-navigation.svelte.js +19 -0
- package/build/svelte/runes/use-focus-effect.svelte.d.ts +1 -0
- package/build/svelte/runes/use-focus-effect.svelte.js +31 -0
- package/build/svelte/runes/use-is-focused.svelte.d.ts +3 -0
- package/build/svelte/runes/use-is-focused.svelte.js +34 -0
- package/build/svelte/runes/use-navigation-state.svelte.d.ts +4 -0
- package/build/svelte/runes/use-navigation-state.svelte.js +39 -0
- package/build/svelte/runes/use-navigation.svelte.d.ts +9 -0
- package/build/svelte/runes/use-navigation.svelte.js +26 -0
- package/build/svelte/runes/use-route.svelte.d.ts +4 -0
- package/build/svelte/runes/use-route.svelte.js +12 -0
- package/build/svelte/runes/use-stack-navigation.svelte.d.ts +6 -0
- package/build/svelte/runes/use-stack-navigation.svelte.js +22 -0
- package/build/svelte/runes/use-tab-navigation.svelte.d.ts +6 -0
- package/build/svelte/runes/use-tab-navigation.svelte.js +19 -0
- package/build/svelte/screen-props.d.ts +21 -0
- package/build/svelte/screen-props.js +6 -0
- package/build/svelte/screen-registry.d.ts +17 -0
- package/build/svelte/screen-registry.js +57 -0
- package/build/svelte/screen.svelte +28 -0
- package/build/svelte/screen.svelte.d.ts +4 -0
- package/build/svelte/stack/index.d.ts +14 -0
- package/build/svelte/stack/index.js +7 -0
- package/build/svelte/stack/index.svelte +261 -0
- package/build/svelte/stack/index.svelte.d.ts +15 -0
- package/build/svelte/stack/stack-props.d.ts +20 -0
- package/build/svelte/stack/stack-props.js +4 -0
- package/build/svelte/stack/stack-screen.svelte +205 -0
- package/build/svelte/stack/stack-screen.svelte.d.ts +4 -0
- package/build/svelte/tab-screen-props.d.ts +13 -0
- package/build/svelte/tab-screen-props.js +4 -0
- package/build/svelte/tab-screen.svelte +26 -0
- package/build/svelte/tab-screen.svelte.d.ts +4 -0
- package/build/svelte/tabs/index.d.ts +8 -0
- package/build/svelte/tabs/index.js +6 -0
- package/build/svelte/tabs/index.svelte +269 -0
- package/build/svelte/tabs/index.svelte.d.ts +8 -0
- package/build/svelte/tabs/tab-props.d.ts +7 -0
- package/build/svelte/tabs/tab-props.js +3 -0
- package/build/vue/composables/use-navigation.js +5 -1
- package/build/vue/drawer/index.js +36 -13
- package/build/vue/index.d.ts +3 -3
- package/build/vue/navigation-context.js +1 -1
- package/build/vue/stack/index.js +43 -18
- package/build/vue/tabs/index.js +65 -39
- package/build-ngc/angular/drawer/index.js +40 -18
- package/build-ngc/angular/drawer/index.js.map +1 -1
- package/build-ngc/angular/drawer-screen.directive.js +3 -3
- package/build-ngc/angular/index.d.ts +4 -4
- package/build-ngc/angular/index.js.map +1 -1
- package/build-ngc/angular/injectors/inject-focus-effect.js.map +1 -1
- package/build-ngc/angular/injectors/inject-is-focused.js.map +1 -1
- package/build-ngc/angular/injectors/inject-navigation-state.js.map +1 -1
- package/build-ngc/angular/injectors/inject-navigation.js +1 -1
- package/build-ngc/angular/injectors/inject-navigation.js.map +1 -1
- package/build-ngc/angular/linking.js.map +1 -1
- package/build-ngc/angular/navigation-context.service.js +3 -3
- package/build-ngc/angular/navigation-context.service.js.map +1 -1
- package/build-ngc/angular/navigation-scope.directive.js +4 -4
- package/build-ngc/angular/navigation-scope.directive.js.map +1 -1
- package/build-ngc/angular/screen.directive.js +3 -3
- package/build-ngc/angular/screen.directive.js.map +1 -1
- package/build-ngc/angular/search-bar-ref.directive.js +4 -4
- package/build-ngc/angular/search-bar-ref.directive.js.map +1 -1
- package/build-ngc/angular/stack/index.d.ts +2 -0
- package/build-ngc/angular/stack/index.js +52 -14
- package/build-ngc/angular/stack/index.js.map +1 -1
- package/build-ngc/angular/tab-screen.directive.js +3 -3
- package/build-ngc/angular/tab-screen.directive.js.map +1 -1
- package/build-ngc/angular/tabs/index.d.ts +1 -1
- package/build-ngc/angular/tabs/index.js +43 -16
- package/build-ngc/angular/tabs/index.js.map +1 -1
- package/build-ngc/core/constants.js.map +1 -1
- package/build-ngc/core/drawer-options/index.js +19 -5
- package/build-ngc/core/drawer-options/index.js.map +1 -1
- package/build-ngc/core/drawer-router-state/index.js +8 -2
- package/build-ngc/core/drawer-router-state/index.js.map +1 -1
- package/build-ngc/core/guards.js +3 -1
- package/build-ngc/core/guards.js.map +1 -1
- package/build-ngc/core/header-bar-buttons.js +14 -4
- package/build-ngc/core/header-bar-buttons.js.map +1 -1
- package/build-ngc/core/index.d.ts +6 -5
- package/build-ngc/core/index.js +7 -3
- package/build-ngc/core/index.js.map +1 -1
- package/build-ngc/core/linking-config/index.js.map +1 -1
- package/build-ngc/core/navigation-events/index.js.map +1 -1
- package/build-ngc/core/navigator-handles/index.js.map +1 -1
- package/build-ngc/core/navigator-state/index.js +12 -4
- package/build-ngc/core/navigator-state/index.js.map +1 -1
- package/build-ngc/core/render-drawer.js +4 -1
- package/build-ngc/core/render-drawer.js.map +1 -1
- package/build-ngc/core/render-stack.js +13 -4
- package/build-ngc/core/render-stack.js.map +1 -1
- package/build-ngc/core/render-tabs.js.map +1 -1
- package/build-ngc/core/screen-options.js +1 -1
- package/build-ngc/core/screen-options.js.map +1 -1
- package/build-ngc/core/search-bar-commands.js +1 -1
- package/build-ngc/core/search-bar-commands.js.map +1 -1
- package/build-ngc/core/stack-router-state/index.d.ts +2 -0
- package/build-ngc/core/stack-router-state/index.js +45 -0
- package/build-ngc/core/stack-router-state/index.js.map +1 -0
- package/build-ngc/core/state-persistence/index.js +6 -2
- package/build-ngc/core/state-persistence/index.js.map +1 -1
- package/build-ngc/core/tab-router-state/index.d.ts +1 -0
- package/build-ngc/core/tab-router-state/index.js +27 -2
- package/build-ngc/core/tab-router-state/index.js.map +1 -1
- package/package.json +44 -16
- package/src/angular/drawer/index.ts +75 -29
- package/src/angular/index.ts +20 -4
- package/src/angular/injectors/inject-focus-effect.ts +8 -2
- package/src/angular/injectors/inject-is-focused.ts +6 -4
- package/src/angular/injectors/inject-navigation-state.ts +7 -4
- package/src/angular/injectors/inject-navigation.ts +12 -3
- package/src/angular/linking.ts +4 -1
- package/src/angular/navigation-context.service.ts +5 -2
- package/src/angular/navigation-scope.directive.ts +6 -2
- package/src/angular/screen.directive.ts +13 -3
- package/src/angular/search-bar-ref.directive.ts +10 -2
- package/src/angular/stack/index.ts +91 -24
- package/src/angular/tab-screen.directive.ts +3 -1
- package/src/angular/tabs/index.ts +54 -16
- package/src/core/constants.ts +6 -3
- package/src/core/drawer-options/index.ts +40 -13
- package/src/core/drawer-router-state/index.ts +11 -3
- package/src/core/guards.ts +3 -1
- package/src/core/header-bar-buttons.ts +41 -12
- package/src/core/index.ts +29 -5
- package/src/core/linking-config/index.ts +31 -10
- package/src/core/navigation-events/index.ts +8 -2
- package/src/core/navigator-handles/index.ts +8 -3
- package/src/core/navigator-props.ts +13 -4
- package/src/core/navigator-state/index.ts +24 -7
- package/src/core/render-drawer.ts +24 -9
- package/src/core/render-stack.ts +52 -13
- package/src/core/render-tabs.ts +24 -6
- package/src/core/screen-options.ts +15 -5
- package/src/core/search-bar-commands.ts +12 -3
- package/src/core/stack-router-state/index.ts +52 -0
- package/src/core/state-persistence/index.ts +13 -3
- package/src/core/tab-router-state/index.ts +35 -3
- package/src/react/drawer/index.ts +409 -330
- package/src/react/drawer-screen.ts +8 -2
- package/src/react/hooks/use-focus-effect.ts +8 -2
- package/src/react/hooks/use-is-focused.ts +6 -2
- package/src/react/hooks/use-navigation.ts +8 -2
- package/src/react/index.ts +37 -7
- package/src/react/linking.ts +4 -1
- package/src/react/navigation-context.ts +6 -2
- package/src/react/screen.ts +12 -3
- package/src/react/stack/index.ts +297 -220
- package/src/react/tab-screen.ts +3 -1
- package/src/react/tabs/index.ts +202 -139
- package/src/solid/drawer/index.ts +570 -0
- package/src/solid/drawer-screen-props.ts +24 -0
- package/src/solid/host.ts +29 -0
- package/src/solid/index.ts +87 -0
- package/src/solid/linking.ts +74 -0
- package/src/solid/navigation-context.ts +56 -0
- package/src/solid/primitives/create-focus-effect.ts +41 -0
- package/src/solid/primitives/create-is-focused.ts +41 -0
- package/src/solid/primitives/create-navigation-state.ts +44 -0
- package/src/solid/primitives/index.ts +12 -0
- package/src/solid/primitives/use-drawer-navigation.ts +25 -0
- package/src/solid/primitives/use-navigation.ts +44 -0
- package/src/solid/primitives/use-route.ts +18 -0
- package/src/solid/primitives/use-stack-navigation.ts +29 -0
- package/src/solid/primitives/use-tab-navigation.ts +25 -0
- package/src/solid/screen-props.ts +51 -0
- package/src/solid/screen-registry.ts +159 -0
- package/src/solid/screen.ts +80 -0
- package/src/solid/stack/index.ts +528 -0
- package/src/solid/tab-screen-props.ts +21 -0
- package/src/solid/tabs/index.ts +327 -0
- package/src/svelte/attachments.ts +70 -0
- package/src/svelte/descriptor-subtree.ts +51 -0
- package/src/svelte/drawer/drawer-props.ts +28 -0
- package/src/svelte/drawer/index.svelte +559 -0
- package/src/svelte/drawer/index.ts +10 -0
- package/src/svelte/drawer-screen-props.ts +26 -0
- package/src/svelte/drawer-screen.svelte +25 -0
- package/src/svelte/fabric-tree.test-helper.ts +95 -0
- package/src/svelte/index.ts +83 -0
- package/src/svelte/linking.svelte.ts +72 -0
- package/src/svelte/navigation-context.ts +62 -0
- package/src/svelte/navigation-scope.svelte +27 -0
- package/src/svelte/registry-host.ts +26 -0
- package/src/svelte/runes/index.ts +17 -0
- package/src/svelte/runes/use-drawer-navigation.svelte.ts +31 -0
- package/src/svelte/runes/use-focus-effect.svelte.ts +41 -0
- package/src/svelte/runes/use-is-focused.svelte.ts +38 -0
- package/src/svelte/runes/use-navigation-state.svelte.ts +48 -0
- package/src/svelte/runes/use-navigation.svelte.ts +49 -0
- package/src/svelte/runes/use-route.svelte.ts +16 -0
- package/src/svelte/runes/use-stack-navigation.svelte.ts +34 -0
- package/src/svelte/runes/use-tab-navigation.svelte.ts +29 -0
- package/src/svelte/screen-props.ts +50 -0
- package/src/svelte/screen-registry.ts +98 -0
- package/src/svelte/screen.svelte +28 -0
- package/src/svelte/stack/index.svelte +261 -0
- package/src/svelte/stack/index.ts +11 -0
- package/src/svelte/stack/stack-props.ts +31 -0
- package/src/svelte/stack/stack-screen.svelte +205 -0
- package/src/svelte/svelte-ambient.d.ts +6 -0
- package/src/svelte/svelte-compile.test-helper.ts +147 -0
- package/src/svelte/tab-screen-props.ts +22 -0
- package/src/svelte/tab-screen.svelte +26 -0
- package/src/svelte/tabs/index.svelte +269 -0
- package/src/svelte/tabs/index.ts +10 -0
- package/src/svelte/tabs/tab-props.ts +13 -0
- package/src/vue/composables/use-navigation-state.ts +7 -4
- package/src/vue/composables/use-navigation.ts +13 -3
- package/src/vue/drawer/index.ts +128 -41
- package/src/vue/drawer-screen.ts +12 -5
- package/src/vue/index.ts +15 -3
- package/src/vue/linking.ts +7 -2
- package/src/vue/navigation-context.ts +17 -5
- package/src/vue/screen.ts +7 -2
- package/src/vue/stack/index.ts +108 -43
- package/src/vue/tab-screen.ts +3 -1
- package/src/vue/tabs/index.ts +109 -68
|
@@ -0,0 +1,559 @@
|
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
// Drawer, the Svelte lifecycle half. The open/closed + focused-route router
|
|
3
|
+
// (drawer-router-state) and the pure swipe/geometry math (drawer-options) live in
|
|
4
|
+
// @symbiote-native/navigation core, shared verbatim with the React/Vue/Angular entries; here
|
|
5
|
+
// Svelte supplies the lifecycle - `$state.raw` for the router (its twin of useReducer), a
|
|
6
|
+
// PanResponder for the swipe gesture (built ONCE, its callbacks reading options and open-state
|
|
7
|
+
// LIVE off `$props()`/`$state` at gesture time), an AnimatedValue driving the slide/opacity
|
|
8
|
+
// transforms, and `export function`s for the open/close/toggle/jumpTo handle - mirroring
|
|
9
|
+
// tabs/index.svelte's shape (Tab is the closer sibling: both are fixed-route-list,
|
|
10
|
+
// no-react-native-screens navigators; Stack's push/pop + native-screen bridging don't apply).
|
|
11
|
+
//
|
|
12
|
+
// FEASIBILITY NOTE (mirrored from the React/Vue twins): the REAL @react-navigation/drawer is
|
|
13
|
+
// built on react-native-gesture-handler + react-native-reanimated, neither of which this
|
|
14
|
+
// codebase depends on. What's built here reaches the same swipe-to-open/close +
|
|
15
|
+
// front/back/slide/permanent behavior using only PanResponder + Animated (both already in
|
|
16
|
+
// @symbiote-native/engine), which is sufficient for a solid drawer but NOT byte-for-byte parity
|
|
17
|
+
// - see the explicit gap list at the bottom of this file (verbatim from the React twin - the
|
|
18
|
+
// gaps are architectural, not adapter-specific).
|
|
19
|
+
let drawerInstanceCounter = 0;
|
|
20
|
+
|
|
21
|
+
const DRAWER_SNAP_DURATION = 250;
|
|
22
|
+
</script>
|
|
23
|
+
|
|
24
|
+
<script lang="ts">
|
|
25
|
+
import type { Component } from 'svelte';
|
|
26
|
+
import { onDestroy, tick } from 'svelte';
|
|
27
|
+
import {
|
|
28
|
+
AnimatedValue,
|
|
29
|
+
Dimensions,
|
|
30
|
+
PanResponder,
|
|
31
|
+
dlog,
|
|
32
|
+
timing,
|
|
33
|
+
} from '@symbiote-native/engine';
|
|
34
|
+
import type {
|
|
35
|
+
IPanResponderGestureState,
|
|
36
|
+
ISymbioteEvent,
|
|
37
|
+
} from '@symbiote-native/engine';
|
|
38
|
+
// The ONLY import from the adapter's main barrel: `Animated.View` is a compiled `.svelte`
|
|
39
|
+
// component, so unlike PanResponder/Dimensions/AnimatedValue (pure engine re-exports, taken
|
|
40
|
+
// from the engine directly here) it has no `.svelte`-free home to import from.
|
|
41
|
+
import { Animated } from '@symbiote-native/svelte';
|
|
42
|
+
import {
|
|
43
|
+
DRAWER_DEFAULT_OVERLAY_COLOR,
|
|
44
|
+
NAVIGATION_EVENT_BLUR,
|
|
45
|
+
NAVIGATION_EVENT_FOCUS,
|
|
46
|
+
createInitialDrawerRouterState,
|
|
47
|
+
createNavigationEmitter,
|
|
48
|
+
diffFocusedRoute,
|
|
49
|
+
drawerChildOrder,
|
|
50
|
+
drawerRouterReducer,
|
|
51
|
+
isDrawerAnimated,
|
|
52
|
+
renderDrawer,
|
|
53
|
+
resolveDragProgress,
|
|
54
|
+
resolveDrawerGeometry,
|
|
55
|
+
resolveDrawerSlotInterpolation,
|
|
56
|
+
resolveSwipeIntent,
|
|
57
|
+
shouldClaimDrawerSwipe,
|
|
58
|
+
} from '../../core';
|
|
59
|
+
import type {
|
|
60
|
+
IDescriptor,
|
|
61
|
+
IDrawerDescriptorMap,
|
|
62
|
+
IDrawerNavigatorHandle,
|
|
63
|
+
IDrawerOptions,
|
|
64
|
+
IDrawerRouterAction,
|
|
65
|
+
IDrawerRouterState,
|
|
66
|
+
IDrawerScreenOptions,
|
|
67
|
+
IDrawerSlot,
|
|
68
|
+
INavigationEmitter,
|
|
69
|
+
IRoute,
|
|
70
|
+
} from '../../core';
|
|
71
|
+
import { getNavigationScope } from '../navigation-context';
|
|
72
|
+
import type { INavigationScopeValue } from '../navigation-context';
|
|
73
|
+
import NavigationScope from '../navigation-scope.svelte';
|
|
74
|
+
import { SCREEN_REGISTRY_HOST_PROPS } from '../registry-host';
|
|
75
|
+
import {
|
|
76
|
+
setScreenCollector,
|
|
77
|
+
toRegistry,
|
|
78
|
+
withoutScreen,
|
|
79
|
+
} from '../screen-registry';
|
|
80
|
+
import type { IRegisteredScreen } from '../screen-registry';
|
|
81
|
+
import type { IDrawerScreenProps } from '../drawer-screen-props';
|
|
82
|
+
import type { IDrawerProps } from './drawer-props';
|
|
83
|
+
|
|
84
|
+
let {
|
|
85
|
+
initialRouteName,
|
|
86
|
+
drawerStyle,
|
|
87
|
+
drawerType,
|
|
88
|
+
drawerPosition,
|
|
89
|
+
drawerWidth,
|
|
90
|
+
overlayColor,
|
|
91
|
+
swipeEnabled,
|
|
92
|
+
swipeEdgeWidth,
|
|
93
|
+
swipeMinDistance,
|
|
94
|
+
swipeMinVelocity,
|
|
95
|
+
children,
|
|
96
|
+
drawerContent,
|
|
97
|
+
}: IDrawerProps = $props();
|
|
98
|
+
|
|
99
|
+
// Read BEFORE this Drawer establishes its own per-screen NavigationScope - becomes the `parent`
|
|
100
|
+
// link a nested screen's useNavigation().getParent() walks. undefined at the nesting root.
|
|
101
|
+
const parentScope = getNavigationScope();
|
|
102
|
+
|
|
103
|
+
const routeIdPrefix = `drawer-${(drawerInstanceCounter += 1)}`;
|
|
104
|
+
|
|
105
|
+
// Unlike Vue (which reads everything off untyped `attrs` and needs a per-field guard to get
|
|
106
|
+
// back into the typed core surface), Svelte's `$props()` is already typed by IDrawerProps, so
|
|
107
|
+
// this is a plain re-assembly with no narrowing at all.
|
|
108
|
+
const options = $derived<IDrawerOptions>({
|
|
109
|
+
drawerType,
|
|
110
|
+
drawerPosition,
|
|
111
|
+
drawerWidth,
|
|
112
|
+
overlayColor,
|
|
113
|
+
swipeEnabled,
|
|
114
|
+
swipeEdgeWidth,
|
|
115
|
+
swipeMinDistance,
|
|
116
|
+
swipeMinVelocity,
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
let screens = $state.raw<IRegisteredScreen<IDrawerScreenProps['options']>[]>(
|
|
120
|
+
[],
|
|
121
|
+
);
|
|
122
|
+
setScreenCollector<IDrawerScreenProps['options']>({
|
|
123
|
+
kind: 'drawer',
|
|
124
|
+
register: screen => {
|
|
125
|
+
screens = [...screens, screen];
|
|
126
|
+
},
|
|
127
|
+
unregister: screen => {
|
|
128
|
+
screens = withoutScreen(screens, screen);
|
|
129
|
+
},
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
const registry = $derived(toRegistry(screens));
|
|
133
|
+
|
|
134
|
+
// Same seed-once-then-dispatch shape Stack and Tab use.
|
|
135
|
+
let dispatchedState = $state.raw<IDrawerRouterState | null>(null);
|
|
136
|
+
let seededState: IDrawerRouterState | undefined;
|
|
137
|
+
let routeSequence = 0;
|
|
138
|
+
|
|
139
|
+
function seedState(): IDrawerRouterState {
|
|
140
|
+
if (seededState !== undefined) return seededState;
|
|
141
|
+
const routes: IRoute<unknown>[] = [...registry.entries()].map(
|
|
142
|
+
([name, entry]) => {
|
|
143
|
+
routeSequence += 1;
|
|
144
|
+
return {
|
|
145
|
+
key: `${routeIdPrefix}-${name}-${routeSequence}`,
|
|
146
|
+
name,
|
|
147
|
+
params: entry.initialParams,
|
|
148
|
+
};
|
|
149
|
+
},
|
|
150
|
+
);
|
|
151
|
+
if (routes.length === 0) {
|
|
152
|
+
dlog('Drawer: no <Drawer.Screen> children registered');
|
|
153
|
+
// Deliberately not memoized - markers may still be registering.
|
|
154
|
+
return createInitialDrawerRouterState(routes, initialRouteName);
|
|
155
|
+
}
|
|
156
|
+
seededState = createInitialDrawerRouterState(routes, initialRouteName);
|
|
157
|
+
return seededState;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const state = $derived(dispatchedState ?? seedState());
|
|
161
|
+
|
|
162
|
+
function dispatch(action: IDrawerRouterAction): void {
|
|
163
|
+
dispatchedState = drawerRouterReducer(state, action);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// progress: 0 closed -> 1 open, the single AnimatedValue every slide/opacity transform below
|
|
167
|
+
// interpolates from. A plain `const`: a Svelte component's script runs once, so this needs no
|
|
168
|
+
// re-creation guard the way React's useRef(new Animated.Value(...)).current does.
|
|
169
|
+
const progress = new AnimatedValue(state.isOpen ? 1 : 0);
|
|
170
|
+
// Where a drag STARTS from, in progress units. Always exactly 0 or 1: a gesture only ever
|
|
171
|
+
// begins at rest, since terminate/release always snap the value back to a resting state before
|
|
172
|
+
// another grant can fire.
|
|
173
|
+
let dragStartProgress = 0;
|
|
174
|
+
|
|
175
|
+
function animateProgressTo(open: boolean): void {
|
|
176
|
+
dlog(`Drawer: animateProgressTo(open=${open}) starting at t=${Date.now()}`);
|
|
177
|
+
timing(progress, {
|
|
178
|
+
toValue: open ? 1 : 0,
|
|
179
|
+
duration: DRAWER_SNAP_DURATION,
|
|
180
|
+
// Native-driver wiring is deferred for v1 - see this file's header feasibility note. The
|
|
181
|
+
// JS timing loop still drives every frame, same as any other non-native-driven timing in
|
|
182
|
+
// this codebase.
|
|
183
|
+
useNativeDriver: false,
|
|
184
|
+
}).start();
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
export function openDrawer(): void {
|
|
188
|
+
dlog(
|
|
189
|
+
`Drawer: openDrawer() called, isOpen=${state.isOpen} at t=${Date.now()}`,
|
|
190
|
+
);
|
|
191
|
+
animateProgressTo(true);
|
|
192
|
+
dispatch({ type: 'openDrawer' });
|
|
193
|
+
}
|
|
194
|
+
export function closeDrawer(): void {
|
|
195
|
+
dlog(
|
|
196
|
+
`Drawer: closeDrawer() called, isOpen=${state.isOpen} at t=${Date.now()}`,
|
|
197
|
+
);
|
|
198
|
+
animateProgressTo(false);
|
|
199
|
+
dispatch({ type: 'closeDrawer' });
|
|
200
|
+
}
|
|
201
|
+
export function toggleDrawer(): void {
|
|
202
|
+
dlog(
|
|
203
|
+
`Drawer: toggleDrawer() called, isOpen=${state.isOpen} at t=${Date.now()}`,
|
|
204
|
+
);
|
|
205
|
+
animateProgressTo(!state.isOpen);
|
|
206
|
+
dispatch({ type: 'toggleDrawer' });
|
|
207
|
+
}
|
|
208
|
+
export function jumpTo(name: string): void {
|
|
209
|
+
// Both sides of the dispatch, because an unregistered name is a documented reducer no-op that
|
|
210
|
+
// hands the SAME state back: animating off the pre-dispatch snapshot alone would slide the
|
|
211
|
+
// panel shut while the router still says isOpen. `state` is a $derived recomputed
|
|
212
|
+
// synchronously off the reassigned `dispatchedState`, so the second read is already the
|
|
213
|
+
// reducer's own answer.
|
|
214
|
+
const wasOpen = state.isOpen;
|
|
215
|
+
dispatch({ type: 'jumpTo', name });
|
|
216
|
+
if (wasOpen && !state.isOpen) animateProgressTo(false);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
const handle: IDrawerNavigatorHandle = {
|
|
220
|
+
openDrawer,
|
|
221
|
+
closeDrawer,
|
|
222
|
+
toggleDrawer,
|
|
223
|
+
jumpTo,
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
// The window width is read fresh AT GESTURE TIME rather than through a reactive
|
|
227
|
+
// window-dimensions rune: it is only ever consumed inside these PanResponder callbacks (never
|
|
228
|
+
// rendered), so a live `Dimensions.get` is both simpler and strictly more current than a
|
|
229
|
+
// subscription that has to round-trip a re-render first.
|
|
230
|
+
const panResponder = PanResponder.create({
|
|
231
|
+
onStartShouldSetPanResponder: (
|
|
232
|
+
event: ISymbioteEvent,
|
|
233
|
+
gestureState: IPanResponderGestureState,
|
|
234
|
+
): boolean =>
|
|
235
|
+
shouldClaimDrawerSwipe(
|
|
236
|
+
event,
|
|
237
|
+
gestureState,
|
|
238
|
+
Dimensions.get('window').width,
|
|
239
|
+
state.isOpen,
|
|
240
|
+
options,
|
|
241
|
+
'start',
|
|
242
|
+
),
|
|
243
|
+
onMoveShouldSetPanResponder: (
|
|
244
|
+
event: ISymbioteEvent,
|
|
245
|
+
gestureState: IPanResponderGestureState,
|
|
246
|
+
): boolean =>
|
|
247
|
+
shouldClaimDrawerSwipe(
|
|
248
|
+
event,
|
|
249
|
+
gestureState,
|
|
250
|
+
Dimensions.get('window').width,
|
|
251
|
+
state.isOpen,
|
|
252
|
+
options,
|
|
253
|
+
'move',
|
|
254
|
+
),
|
|
255
|
+
onPanResponderGrant: (): void => {
|
|
256
|
+
dlog('Drawer: gesture grant');
|
|
257
|
+
dragStartProgress = state.isOpen ? 1 : 0;
|
|
258
|
+
},
|
|
259
|
+
onPanResponderMove: (
|
|
260
|
+
_event: ISymbioteEvent,
|
|
261
|
+
gestureState: IPanResponderGestureState,
|
|
262
|
+
): void => {
|
|
263
|
+
progress.setValue(
|
|
264
|
+
resolveDragProgress(gestureState, dragStartProgress, options),
|
|
265
|
+
);
|
|
266
|
+
},
|
|
267
|
+
onPanResponderRelease: (
|
|
268
|
+
_event: ISymbioteEvent,
|
|
269
|
+
gestureState: IPanResponderGestureState,
|
|
270
|
+
): void => {
|
|
271
|
+
const intent = resolveSwipeIntent(gestureState, state.isOpen, options);
|
|
272
|
+
const open = intent === 'open';
|
|
273
|
+
dlog(`Drawer: gesture release -> ${open ? 'open' : 'close'}`);
|
|
274
|
+
animateProgressTo(open);
|
|
275
|
+
dispatch(open ? { type: 'openDrawer' } : { type: 'closeDrawer' });
|
|
276
|
+
},
|
|
277
|
+
onPanResponderTerminate: (): void => {
|
|
278
|
+
dlog('Drawer: gesture terminated, snapping back');
|
|
279
|
+
animateProgressTo(state.isOpen);
|
|
280
|
+
},
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
// Only the focused route's screen is ever mounted (like Tab, unlike Stack which keeps every
|
|
284
|
+
// pushed route alive). One emitter per route.key, created lazily and cached for the navigator's
|
|
285
|
+
// whole lifetime.
|
|
286
|
+
const emitters = new Map<string, INavigationEmitter>();
|
|
287
|
+
function emitterFor(routeKey: string): INavigationEmitter {
|
|
288
|
+
let emitter = emitters.get(routeKey);
|
|
289
|
+
if (emitter === undefined) {
|
|
290
|
+
emitter = createNavigationEmitter();
|
|
291
|
+
emitters.set(routeKey, emitter);
|
|
292
|
+
}
|
|
293
|
+
return emitter;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// Focus/blur synthesis, deferred to `tick()` for the same reason tabs/index.svelte documents:
|
|
297
|
+
// the newly-focused screen's own subscriptions are created during THIS flush, after this
|
|
298
|
+
// effect was.
|
|
299
|
+
let previousFocusedKey: string | undefined;
|
|
300
|
+
$effect(() => {
|
|
301
|
+
const nextKey = state.routes[state.index]?.key;
|
|
302
|
+
const { blurKey, focusKey } = diffFocusedRoute(previousFocusedKey, nextKey);
|
|
303
|
+
if (blurKey === undefined && focusKey === undefined) return;
|
|
304
|
+
previousFocusedKey = nextKey;
|
|
305
|
+
void tick().then(() => {
|
|
306
|
+
if (blurKey !== undefined) {
|
|
307
|
+
dlog(`Drawer: route "${blurKey}" blurred at t=${Date.now()}`);
|
|
308
|
+
emitterFor(blurKey).emit(NAVIGATION_EVENT_BLUR);
|
|
309
|
+
}
|
|
310
|
+
if (focusKey !== undefined) {
|
|
311
|
+
dlog(`Drawer: route "${focusKey}" focused at t=${Date.now()}`);
|
|
312
|
+
emitterFor(focusKey).emit(NAVIGATION_EVENT_FOCUS);
|
|
313
|
+
}
|
|
314
|
+
});
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
onDestroy(() => {
|
|
318
|
+
if (previousFocusedKey !== undefined) {
|
|
319
|
+
emitterFor(previousFocusedKey).emit(NAVIGATION_EVENT_BLUR);
|
|
320
|
+
}
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
const animated = $derived(isDrawerAnimated(options));
|
|
324
|
+
const geometry = $derived(resolveDrawerGeometry(options));
|
|
325
|
+
|
|
326
|
+
const panelTranslateX = $derived(
|
|
327
|
+
animated
|
|
328
|
+
? progress.interpolate(
|
|
329
|
+
resolveDrawerSlotInterpolation(geometry, 'panel').translateX,
|
|
330
|
+
)
|
|
331
|
+
: undefined,
|
|
332
|
+
);
|
|
333
|
+
const contentTranslateX = $derived(
|
|
334
|
+
animated
|
|
335
|
+
? progress.interpolate(
|
|
336
|
+
resolveDrawerSlotInterpolation(geometry, 'content').translateX,
|
|
337
|
+
)
|
|
338
|
+
: undefined,
|
|
339
|
+
);
|
|
340
|
+
// The overlay is a full-screen absolutely-positioned sibling BELOW content in paint order (see
|
|
341
|
+
// render-drawer.ts's drawerChildOrder) - for 'front' that's fine since content never moves, but
|
|
342
|
+
// for 'slide' content itself translates away by contentTranslateX, and without following it the
|
|
343
|
+
// overlay stays pinned full-screen. resolveDrawerSlotInterpolation's 'overlay' branch ties its
|
|
344
|
+
// translateX to the SAME range as content's for exactly this reason, so overlayOpacity and
|
|
345
|
+
// overlayTranslateX below share one resolved config.
|
|
346
|
+
const overlayInterpolation = $derived(
|
|
347
|
+
animated ? resolveDrawerSlotInterpolation(geometry, 'overlay') : undefined,
|
|
348
|
+
);
|
|
349
|
+
const overlayStyle = $derived(
|
|
350
|
+
overlayInterpolation === undefined
|
|
351
|
+
? undefined
|
|
352
|
+
: {
|
|
353
|
+
opacity: progress.interpolate(overlayInterpolation.opacity),
|
|
354
|
+
transform: [
|
|
355
|
+
{
|
|
356
|
+
translateX: progress.interpolate(overlayInterpolation.translateX),
|
|
357
|
+
},
|
|
358
|
+
],
|
|
359
|
+
},
|
|
360
|
+
);
|
|
361
|
+
const contentStyle = $derived(
|
|
362
|
+
contentTranslateX === undefined
|
|
363
|
+
? undefined
|
|
364
|
+
: { transform: [{ translateX: contentTranslateX }] },
|
|
365
|
+
);
|
|
366
|
+
const panelStyle = $derived(
|
|
367
|
+
panelTranslateX === undefined
|
|
368
|
+
? undefined
|
|
369
|
+
: { transform: [{ translateX: panelTranslateX }] },
|
|
370
|
+
);
|
|
371
|
+
|
|
372
|
+
const focusedRoute = $derived(state.routes[state.index]);
|
|
373
|
+
const focusedScreen = $derived.by<
|
|
374
|
+
{ scope: INavigationScopeValue; component: Component } | undefined
|
|
375
|
+
>(() => {
|
|
376
|
+
if (focusedRoute === undefined) return undefined;
|
|
377
|
+
const entry = registry.get(focusedRoute.name);
|
|
378
|
+
if (entry === undefined) {
|
|
379
|
+
dlog(
|
|
380
|
+
`Drawer: no screen registered for route name "${focusedRoute.name}"`,
|
|
381
|
+
);
|
|
382
|
+
return undefined;
|
|
383
|
+
}
|
|
384
|
+
return {
|
|
385
|
+
component: entry.component,
|
|
386
|
+
scope: {
|
|
387
|
+
route: focusedRoute,
|
|
388
|
+
navigation: handle,
|
|
389
|
+
emitter: emitterFor(focusedRoute.key),
|
|
390
|
+
parent: parentScope?.current,
|
|
391
|
+
},
|
|
392
|
+
};
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
function resolveDrawerScreenOptions(
|
|
396
|
+
entry: IRegisteredScreen<IDrawerScreenProps['options']>,
|
|
397
|
+
route: IRoute<unknown>,
|
|
398
|
+
): IDrawerScreenOptions {
|
|
399
|
+
if (typeof entry.options === 'function')
|
|
400
|
+
return entry.options({ route, navigation: handle });
|
|
401
|
+
return entry.options ?? {};
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
const descriptors = $derived.by<IDrawerDescriptorMap>(() => {
|
|
405
|
+
const map: IDrawerDescriptorMap = {};
|
|
406
|
+
for (const route of state.routes) {
|
|
407
|
+
const entry = registry.get(route.name);
|
|
408
|
+
if (entry === undefined) continue;
|
|
409
|
+
map[route.key] = {
|
|
410
|
+
options: resolveDrawerScreenOptions(entry, route),
|
|
411
|
+
navigation: handle,
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
return map;
|
|
415
|
+
});
|
|
416
|
+
|
|
417
|
+
const root = $derived.by<IDescriptor>(() =>
|
|
418
|
+
renderDrawer(
|
|
419
|
+
{
|
|
420
|
+
overlayColor: overlayColor ?? DRAWER_DEFAULT_OVERLAY_COLOR,
|
|
421
|
+
drawerStyle,
|
|
422
|
+
contentPassthrough: {},
|
|
423
|
+
overlayPassthrough: animated
|
|
424
|
+
? {
|
|
425
|
+
pointerEvents: state.isOpen ? 'auto' : 'none',
|
|
426
|
+
onStartShouldSetResponder: () => true,
|
|
427
|
+
onResponderRelease: () => {
|
|
428
|
+
animateProgressTo(false);
|
|
429
|
+
dispatch({ type: 'closeDrawer' });
|
|
430
|
+
},
|
|
431
|
+
}
|
|
432
|
+
: {},
|
|
433
|
+
panelPassthrough: {},
|
|
434
|
+
},
|
|
435
|
+
options,
|
|
436
|
+
),
|
|
437
|
+
);
|
|
438
|
+
|
|
439
|
+
const order = $derived(drawerChildOrder(options));
|
|
440
|
+
|
|
441
|
+
// renderDrawer emits its slots as root children IN `drawerChildOrder`'s order, so the two zip
|
|
442
|
+
// together - the same index-pairing Vue's own render loop does.
|
|
443
|
+
const slotDescriptors = $derived.by<
|
|
444
|
+
Partial<Record<IDrawerSlot, IDescriptor>>
|
|
445
|
+
>(() => {
|
|
446
|
+
const map: Partial<Record<IDrawerSlot, IDescriptor>> = {};
|
|
447
|
+
order.forEach((slot, index) => {
|
|
448
|
+
const descriptor = root.children[index];
|
|
449
|
+
if (typeof descriptor !== 'string' && descriptor !== undefined)
|
|
450
|
+
map[slot] = descriptor;
|
|
451
|
+
});
|
|
452
|
+
return map;
|
|
453
|
+
});
|
|
454
|
+
|
|
455
|
+
const rootProps = $derived<Record<string, unknown>>({
|
|
456
|
+
style: root.props.style,
|
|
457
|
+
...panResponder.panHandlers,
|
|
458
|
+
});
|
|
459
|
+
</script>
|
|
460
|
+
|
|
461
|
+
{#snippet screenContent()}
|
|
462
|
+
{#if focusedScreen !== undefined}
|
|
463
|
+
{@const FocusedComponent = focusedScreen.component}
|
|
464
|
+
<NavigationScope value={focusedScreen.scope}>
|
|
465
|
+
<FocusedComponent />
|
|
466
|
+
</NavigationScope>
|
|
467
|
+
{/if}
|
|
468
|
+
{/snippet}
|
|
469
|
+
|
|
470
|
+
{#snippet contentSlot()}
|
|
471
|
+
{@const descriptor = slotDescriptors.content}
|
|
472
|
+
{#if descriptor !== undefined}
|
|
473
|
+
{#if contentStyle !== undefined}
|
|
474
|
+
<Animated.View
|
|
475
|
+
{...descriptor.props}
|
|
476
|
+
style={[descriptor.props.style, contentStyle]}
|
|
477
|
+
>
|
|
478
|
+
{@render screenContent()}
|
|
479
|
+
</Animated.View>
|
|
480
|
+
{:else}
|
|
481
|
+
<symbiote-view p={descriptor.props}>
|
|
482
|
+
{@render screenContent()}
|
|
483
|
+
</symbiote-view>
|
|
484
|
+
{/if}
|
|
485
|
+
{/if}
|
|
486
|
+
{/snippet}
|
|
487
|
+
|
|
488
|
+
{#snippet overlaySlot()}
|
|
489
|
+
{@const descriptor = slotDescriptors.overlay}
|
|
490
|
+
{#if descriptor !== undefined}
|
|
491
|
+
{#if overlayStyle !== undefined}
|
|
492
|
+
<Animated.View
|
|
493
|
+
{...descriptor.props}
|
|
494
|
+
style={[descriptor.props.style, overlayStyle]}
|
|
495
|
+
/>
|
|
496
|
+
{:else}
|
|
497
|
+
<symbiote-view p={descriptor.props} />
|
|
498
|
+
{/if}
|
|
499
|
+
{/if}
|
|
500
|
+
{/snippet}
|
|
501
|
+
|
|
502
|
+
{#snippet panelSlot()}
|
|
503
|
+
{@const descriptor = slotDescriptors.panel}
|
|
504
|
+
{#if descriptor !== undefined}
|
|
505
|
+
{#if panelStyle !== undefined}
|
|
506
|
+
<Animated.View
|
|
507
|
+
{...descriptor.props}
|
|
508
|
+
style={[descriptor.props.style, panelStyle]}
|
|
509
|
+
>
|
|
510
|
+
{@render drawerContent?.({
|
|
511
|
+
state,
|
|
512
|
+
descriptors,
|
|
513
|
+
navigation: handle,
|
|
514
|
+
})}
|
|
515
|
+
</Animated.View>
|
|
516
|
+
{:else}
|
|
517
|
+
<symbiote-view p={descriptor.props}>
|
|
518
|
+
{@render drawerContent?.({
|
|
519
|
+
state,
|
|
520
|
+
descriptors,
|
|
521
|
+
navigation: handle,
|
|
522
|
+
})}
|
|
523
|
+
</symbiote-view>
|
|
524
|
+
{/if}
|
|
525
|
+
{/if}
|
|
526
|
+
{/snippet}
|
|
527
|
+
|
|
528
|
+
<symbiote-view p={rootProps}>
|
|
529
|
+
<symbiote-text p={SCREEN_REGISTRY_HOST_PROPS}>
|
|
530
|
+
{@render children?.()}
|
|
531
|
+
</symbiote-text>
|
|
532
|
+
{#each order as slot (slot)}
|
|
533
|
+
{#if slot === 'content'}
|
|
534
|
+
{@render contentSlot()}
|
|
535
|
+
{:else if slot === 'overlay'}
|
|
536
|
+
{@render overlaySlot()}
|
|
537
|
+
{:else}
|
|
538
|
+
{@render panelSlot()}
|
|
539
|
+
{/if}
|
|
540
|
+
{/each}
|
|
541
|
+
</symbiote-view>
|
|
542
|
+
|
|
543
|
+
<!-- --- Explicit gap list vs the real react-native-gesture-handler + react-native-reanimated
|
|
544
|
+
@react-navigation/drawer (confirmed against its current docs, mirrored verbatim from the
|
|
545
|
+
React twin - the gaps are architectural, not adapter-specific) ---
|
|
546
|
+
1. `configureGestureHandler` - a raw react-native-gesture-handler `Gesture` object escape
|
|
547
|
+
hatch. No PanResponder equivalent exists; not ported.
|
|
548
|
+
2. Simultaneous/failure gesture RELATIONSHIPS (gesture-handler's declarative composition vs
|
|
549
|
+
a nested ScrollView, another PanResponder, etc.) - PanResponder only offers negotiation
|
|
550
|
+
via the should-set boolean gates used here (edge-start + dominant-axis), which is more
|
|
551
|
+
prone to an accidental hijack of a nested horizontal ScrollView/Swiper than
|
|
552
|
+
gesture-handler's system.
|
|
553
|
+
3. `useDrawerProgress` - a Reanimated SharedValue read on the UI thread. `progress` here is a
|
|
554
|
+
JS-thread AnimatedValue; interpolating it for consumer-facing content animation works, but
|
|
555
|
+
without native-driver wiring (gap noted above) it does not carry the same synchronous
|
|
556
|
+
UI-thread guarantee under JS-thread load.
|
|
557
|
+
4. `hideStatusBarOnOpen` / `keyboardDismissMode` / `statusBarAnimation` / `overlayStyle` -
|
|
558
|
+
not wired in this pass; straightforward additions once StatusBar/Keyboard module wiring is
|
|
559
|
+
needed here (not a PanResponder/Animated limitation, just unscoped for v1). -->
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// `Drawer.Screen` alongside the standalone `DrawerScreen` export, matching React's and Vue's
|
|
2
|
+
// barrels - see ../stack/index.ts for why Object.assign is the right mechanism on a compiled
|
|
3
|
+
// Svelte component.
|
|
4
|
+
import DrawerImpl from './index.svelte';
|
|
5
|
+
import DrawerScreen from '../drawer-screen.svelte';
|
|
6
|
+
|
|
7
|
+
export const Drawer = Object.assign(DrawerImpl, { Screen: DrawerScreen });
|
|
8
|
+
|
|
9
|
+
export type { IDrawerNavigatorHandle, IDrawerDescriptorMap } from '../../core';
|
|
10
|
+
export type { IDrawerContentSlotProps, IDrawerProps } from './drawer-props';
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// Drawer.Screen's public prop surface - mirrors tab-screen-props.ts (Tab is the closer sibling:
|
|
2
|
+
// both are fixed-route-list, no-push navigators). Split out of `drawer-screen.svelte` for the
|
|
3
|
+
// same tsc-cannot-see-a-`.svelte`-named-export reason screen-props.ts documents.
|
|
4
|
+
|
|
5
|
+
import type { Component } from 'svelte';
|
|
6
|
+
import type {
|
|
7
|
+
IDrawerNavigatorHandle,
|
|
8
|
+
IDrawerScreenOptions,
|
|
9
|
+
IRoute,
|
|
10
|
+
} from '../core';
|
|
11
|
+
|
|
12
|
+
export type IDrawerScreenOptionsArgs = {
|
|
13
|
+
route: IRoute<unknown>;
|
|
14
|
+
navigation: IDrawerNavigatorHandle;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export type IDrawerScreenOptionsResolver = (
|
|
18
|
+
args: IDrawerScreenOptionsArgs,
|
|
19
|
+
) => IDrawerScreenOptions;
|
|
20
|
+
|
|
21
|
+
export type IDrawerScreenProps = {
|
|
22
|
+
name: string;
|
|
23
|
+
component: Component;
|
|
24
|
+
options?: IDrawerScreenOptions | IDrawerScreenOptionsResolver;
|
|
25
|
+
initialParams?: unknown;
|
|
26
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
// Drawer.Screen: a declarative marker that paints nothing, registered through the same
|
|
3
|
+
// context-based collector Stack.Screen/Tab.Screen use (screen-registry.ts). See CLAUDE.md
|
|
4
|
+
// <third_party_rn_packages_are_react_only> - this and Drawer import nothing from react-native.
|
|
5
|
+
import { collectScreen } from './screen-registry';
|
|
6
|
+
import type { IDrawerScreenProps } from './drawer-screen-props';
|
|
7
|
+
|
|
8
|
+
let { name, component, options, initialParams }: IDrawerScreenProps =
|
|
9
|
+
$props();
|
|
10
|
+
|
|
11
|
+
collectScreen('drawer', {
|
|
12
|
+
get name(): IDrawerScreenProps['name'] {
|
|
13
|
+
return name;
|
|
14
|
+
},
|
|
15
|
+
get component(): IDrawerScreenProps['component'] {
|
|
16
|
+
return component;
|
|
17
|
+
},
|
|
18
|
+
get options(): IDrawerScreenProps['options'] {
|
|
19
|
+
return options;
|
|
20
|
+
},
|
|
21
|
+
get initialParams(): unknown {
|
|
22
|
+
return initialParams;
|
|
23
|
+
},
|
|
24
|
+
});
|
|
25
|
+
</script>
|