@symbiote-native/navigation 1.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/LICENSE +21 -0
- package/build/angular/drawer/index.d.ts +76 -0
- package/build/angular/drawer/index.js +508 -0
- package/build/angular/drawer-screen.directive.d.ts +14 -0
- package/build/angular/drawer-screen.directive.js +87 -0
- package/build/angular/index.d.ts +23 -0
- package/build/angular/index.js +22 -0
- package/build/angular/injectors/index.d.ts +13 -0
- package/build/angular/injectors/index.js +8 -0
- package/build/angular/injectors/inject-drawer-navigation.d.ts +4 -0
- package/build/angular/injectors/inject-drawer-navigation.js +12 -0
- package/build/angular/injectors/inject-focus-effect.d.ts +2 -0
- package/build/angular/injectors/inject-focus-effect.js +29 -0
- package/build/angular/injectors/inject-is-focused.d.ts +2 -0
- package/build/angular/injectors/inject-is-focused.js +20 -0
- package/build/angular/injectors/inject-navigation-state.d.ts +3 -0
- package/build/angular/injectors/inject-navigation-state.js +35 -0
- package/build/angular/injectors/inject-navigation.d.ts +7 -0
- package/build/angular/injectors/inject-navigation.js +16 -0
- package/build/angular/injectors/inject-route.d.ts +3 -0
- package/build/angular/injectors/inject-route.js +20 -0
- package/build/angular/injectors/inject-stack-navigation.d.ts +4 -0
- package/build/angular/injectors/inject-stack-navigation.js +15 -0
- package/build/angular/injectors/inject-tab-navigation.d.ts +4 -0
- package/build/angular/injectors/inject-tab-navigation.js +11 -0
- package/build/angular/linking.d.ts +3 -0
- package/build/angular/linking.js +43 -0
- package/build/angular/navigation-context.service.d.ts +13 -0
- package/build/angular/navigation-context.service.js +105 -0
- package/build/angular/navigation-scope.directive.d.ts +10 -0
- package/build/angular/navigation-scope.directive.js +90 -0
- package/build/angular/screen.directive.d.ts +22 -0
- package/build/angular/screen.directive.js +90 -0
- package/build/angular/search-bar-ref.directive.d.ts +10 -0
- package/build/angular/search-bar-ref.directive.js +88 -0
- package/build/angular/stack/index.d.ts +55 -0
- package/build/angular/stack/index.js +386 -0
- package/build/angular/tab-screen.directive.d.ts +14 -0
- package/build/angular/tab-screen.directive.js +87 -0
- package/build/angular/tabs/index.d.ts +42 -0
- package/build/angular/tabs/index.js +263 -0
- package/build/core/constants.d.ts +27 -0
- package/build/core/constants.js +61 -0
- package/build/core/drawer-options/index.d.ts +64 -0
- package/build/core/drawer-options/index.js +200 -0
- package/build/core/drawer-router-state/index.d.ts +21 -0
- package/build/core/drawer-router-state/index.js +40 -0
- package/build/core/guards.d.ts +2 -0
- package/build/core/guards.js +22 -0
- package/build/core/header-bar-buttons.d.ts +9 -0
- package/build/core/header-bar-buttons.js +163 -0
- package/build/core/index.d.ts +29 -0
- package/build/core/index.js +32 -0
- package/build/core/linking-config/index.d.ts +13 -0
- package/build/core/linking-config/index.js +171 -0
- package/build/core/navigation-events/index.d.ts +16 -0
- package/build/core/navigation-events/index.js +57 -0
- package/build/core/navigator-handles/index.d.ts +30 -0
- package/build/core/navigator-handles/index.js +15 -0
- package/build/core/navigator-props.d.ts +216 -0
- package/build/core/navigator-props.js +7 -0
- package/build/core/navigator-state/index.d.ts +36 -0
- package/build/core/navigator-state/index.js +81 -0
- package/build/core/render-drawer.d.ts +13 -0
- package/build/core/render-drawer.js +88 -0
- package/build/core/render-stack.d.ts +35 -0
- package/build/core/render-stack.js +294 -0
- package/build/core/render-tabs.d.ts +20 -0
- package/build/core/render-tabs.js +97 -0
- package/build/core/screen-options.d.ts +4 -0
- package/build/core/screen-options.js +133 -0
- package/build/core/search-bar-commands.d.ts +10 -0
- package/build/core/search-bar-commands.js +42 -0
- package/build/core/state-persistence/index.d.ts +3 -0
- package/build/core/state-persistence/index.js +23 -0
- package/build/core/tab-options.d.ts +13 -0
- package/build/core/tab-options.js +10 -0
- package/build/core/tab-router-state/index.d.ts +19 -0
- package/build/core/tab-router-state/index.js +51 -0
- package/build/react/collect-registry.d.ts +4 -0
- package/build/react/collect-registry.js +15 -0
- package/build/react/drawer/index.d.ts +29 -0
- package/build/react/drawer/index.js +277 -0
- package/build/react/drawer-screen.d.ts +14 -0
- package/build/react/drawer-screen.js +8 -0
- package/build/react/hooks/index.d.ts +12 -0
- package/build/react/hooks/index.js +8 -0
- package/build/react/hooks/use-drawer-navigation.d.ts +4 -0
- package/build/react/hooks/use-drawer-navigation.js +12 -0
- package/build/react/hooks/use-focus-effect.d.ts +2 -0
- package/build/react/hooks/use-focus-effect.js +29 -0
- package/build/react/hooks/use-is-focused.d.ts +1 -0
- package/build/react/hooks/use-is-focused.js +22 -0
- package/build/react/hooks/use-navigation-state/index.d.ts +2 -0
- package/build/react/hooks/use-navigation-state/index.js +39 -0
- package/build/react/hooks/use-navigation.d.ts +7 -0
- package/build/react/hooks/use-navigation.js +16 -0
- package/build/react/hooks/use-route.d.ts +2 -0
- package/build/react/hooks/use-route.js +8 -0
- package/build/react/hooks/use-stack-navigation.d.ts +4 -0
- package/build/react/hooks/use-stack-navigation.js +14 -0
- package/build/react/hooks/use-tab-navigation.d.ts +4 -0
- package/build/react/hooks/use-tab-navigation.js +12 -0
- package/build/react/index.d.ts +24 -0
- package/build/react/index.js +21 -0
- package/build/react/linking.d.ts +3 -0
- package/build/react/linking.js +47 -0
- package/build/react/navigation-context.d.ts +10 -0
- package/build/react/navigation-context.js +22 -0
- package/build/react/screen.d.ts +22 -0
- package/build/react/screen.js +7 -0
- package/build/react/stack/index.d.ts +13 -0
- package/build/react/stack/index.js +202 -0
- package/build/react/tab-screen.d.ts +15 -0
- package/build/react/tab-screen.js +8 -0
- package/build/react/tabs/index.d.ts +12 -0
- package/build/react/tabs/index.js +116 -0
- package/build/register.d.ts +7 -0
- package/build/register.js +58 -0
- package/build/vue/composables/index.d.ts +12 -0
- package/build/vue/composables/index.js +8 -0
- package/build/vue/composables/use-drawer-navigation.d.ts +5 -0
- package/build/vue/composables/use-drawer-navigation.js +15 -0
- package/build/vue/composables/use-focus-effect.d.ts +1 -0
- package/build/vue/composables/use-focus-effect.js +31 -0
- package/build/vue/composables/use-is-focused.d.ts +2 -0
- package/build/vue/composables/use-is-focused.js +28 -0
- package/build/vue/composables/use-navigation-state.d.ts +3 -0
- package/build/vue/composables/use-navigation-state.js +29 -0
- package/build/vue/composables/use-navigation.d.ts +8 -0
- package/build/vue/composables/use-navigation.js +13 -0
- package/build/vue/composables/use-route.d.ts +3 -0
- package/build/vue/composables/use-route.js +9 -0
- package/build/vue/composables/use-stack-navigation.d.ts +5 -0
- package/build/vue/composables/use-stack-navigation.js +18 -0
- package/build/vue/composables/use-tab-navigation.d.ts +5 -0
- package/build/vue/composables/use-tab-navigation.js +15 -0
- package/build/vue/drawer/index.d.ts +19 -0
- package/build/vue/drawer/index.js +390 -0
- package/build/vue/drawer-screen.d.ts +14 -0
- package/build/vue/drawer-screen.js +12 -0
- package/build/vue/index.d.ts +21 -0
- package/build/vue/index.js +19 -0
- package/build/vue/linking.d.ts +3 -0
- package/build/vue/linking.js +57 -0
- package/build/vue/navigation-context.d.ts +16 -0
- package/build/vue/navigation-context.js +65 -0
- package/build/vue/screen.d.ts +21 -0
- package/build/vue/screen.js +12 -0
- package/build/vue/stack/index.d.ts +9 -0
- package/build/vue/stack/index.js +279 -0
- package/build/vue/tab-screen.d.ts +14 -0
- package/build/vue/tab-screen.js +11 -0
- package/build/vue/tabs/index.d.ts +10 -0
- package/build/vue/tabs/index.js +209 -0
- package/build-ngc/angular/drawer/index.d.ts +79 -0
- package/build-ngc/angular/drawer/index.js +479 -0
- package/build-ngc/angular/drawer/index.js.map +1 -0
- package/build-ngc/angular/drawer-screen.directive.d.ts +17 -0
- package/build-ngc/angular/drawer-screen.directive.js +32 -0
- package/build-ngc/angular/drawer-screen.directive.js.map +1 -0
- package/build-ngc/angular/drawer.d.ts +79 -0
- package/build-ngc/angular/drawer.js +475 -0
- package/build-ngc/angular/drawer.js.map +1 -0
- package/build-ngc/angular/index.d.ts +23 -0
- package/build-ngc/angular/index.js +23 -0
- package/build-ngc/angular/index.js.map +1 -0
- package/build-ngc/angular/injectors/index.d.ts +13 -0
- package/build-ngc/angular/injectors/index.js +9 -0
- package/build-ngc/angular/injectors/index.js.map +1 -0
- package/build-ngc/angular/injectors/inject-drawer-navigation.d.ts +4 -0
- package/build-ngc/angular/injectors/inject-drawer-navigation.js +13 -0
- package/build-ngc/angular/injectors/inject-drawer-navigation.js.map +1 -0
- package/build-ngc/angular/injectors/inject-focus-effect.d.ts +2 -0
- package/build-ngc/angular/injectors/inject-focus-effect.js +30 -0
- package/build-ngc/angular/injectors/inject-focus-effect.js.map +1 -0
- package/build-ngc/angular/injectors/inject-is-focused.d.ts +2 -0
- package/build-ngc/angular/injectors/inject-is-focused.js +22 -0
- package/build-ngc/angular/injectors/inject-is-focused.js.map +1 -0
- package/build-ngc/angular/injectors/inject-navigation-state.d.ts +3 -0
- package/build-ngc/angular/injectors/inject-navigation-state.js +37 -0
- package/build-ngc/angular/injectors/inject-navigation-state.js.map +1 -0
- package/build-ngc/angular/injectors/inject-navigation.d.ts +7 -0
- package/build-ngc/angular/injectors/inject-navigation.js +17 -0
- package/build-ngc/angular/injectors/inject-navigation.js.map +1 -0
- package/build-ngc/angular/injectors/inject-route.d.ts +3 -0
- package/build-ngc/angular/injectors/inject-route.js +21 -0
- package/build-ngc/angular/injectors/inject-route.js.map +1 -0
- package/build-ngc/angular/injectors/inject-stack-navigation.d.ts +4 -0
- package/build-ngc/angular/injectors/inject-stack-navigation.js +16 -0
- package/build-ngc/angular/injectors/inject-stack-navigation.js.map +1 -0
- package/build-ngc/angular/injectors/inject-tab-navigation.d.ts +4 -0
- package/build-ngc/angular/injectors/inject-tab-navigation.js +12 -0
- package/build-ngc/angular/injectors/inject-tab-navigation.js.map +1 -0
- package/build-ngc/angular/linking.d.ts +3 -0
- package/build-ngc/angular/linking.js +44 -0
- package/build-ngc/angular/linking.js.map +1 -0
- package/build-ngc/angular/navigation-context.service.d.ts +16 -0
- package/build-ngc/angular/navigation-context.service.js +63 -0
- package/build-ngc/angular/navigation-context.service.js.map +1 -0
- package/build-ngc/angular/navigation-scope.directive.d.ts +13 -0
- package/build-ngc/angular/navigation-scope.directive.js +42 -0
- package/build-ngc/angular/navigation-scope.directive.js.map +1 -0
- package/build-ngc/angular/screen.directive.d.ts +25 -0
- package/build-ngc/angular/screen.directive.js +35 -0
- package/build-ngc/angular/screen.directive.js.map +1 -0
- package/build-ngc/angular/search-bar-ref.directive.d.ts +13 -0
- package/build-ngc/angular/search-bar-ref.directive.js +44 -0
- package/build-ngc/angular/search-bar-ref.directive.js.map +1 -0
- package/build-ngc/angular/stack/index.d.ts +58 -0
- package/build-ngc/angular/stack/index.js +406 -0
- package/build-ngc/angular/stack/index.js.map +1 -0
- package/build-ngc/angular/stack.d.ts +58 -0
- package/build-ngc/angular/stack.js +402 -0
- package/build-ngc/angular/stack.js.map +1 -0
- package/build-ngc/angular/tab-screen.directive.d.ts +17 -0
- package/build-ngc/angular/tab-screen.directive.js +32 -0
- package/build-ngc/angular/tab-screen.directive.js.map +1 -0
- package/build-ngc/angular/tabs/index.d.ts +45 -0
- package/build-ngc/angular/tabs/index.js +231 -0
- package/build-ngc/angular/tabs/index.js.map +1 -0
- package/build-ngc/angular/tabs.d.ts +45 -0
- package/build-ngc/angular/tabs.js +231 -0
- package/build-ngc/angular/tabs.js.map +1 -0
- package/build-ngc/core/constants.d.ts +27 -0
- package/build-ngc/core/constants.js +62 -0
- package/build-ngc/core/constants.js.map +1 -0
- package/build-ngc/core/drawer-options/index.d.ts +64 -0
- package/build-ngc/core/drawer-options/index.js +201 -0
- package/build-ngc/core/drawer-options/index.js.map +1 -0
- package/build-ngc/core/drawer-options.d.ts +64 -0
- package/build-ngc/core/drawer-options.js +201 -0
- package/build-ngc/core/drawer-options.js.map +1 -0
- package/build-ngc/core/drawer-router-state/index.d.ts +21 -0
- package/build-ngc/core/drawer-router-state/index.js +41 -0
- package/build-ngc/core/drawer-router-state/index.js.map +1 -0
- package/build-ngc/core/drawer-router-state.d.ts +21 -0
- package/build-ngc/core/drawer-router-state.js +41 -0
- package/build-ngc/core/drawer-router-state.js.map +1 -0
- package/build-ngc/core/guards.d.ts +2 -0
- package/build-ngc/core/guards.js +23 -0
- package/build-ngc/core/guards.js.map +1 -0
- package/build-ngc/core/header-bar-buttons.d.ts +9 -0
- package/build-ngc/core/header-bar-buttons.js +164 -0
- package/build-ngc/core/header-bar-buttons.js.map +1 -0
- package/build-ngc/core/href.d.ts +16 -0
- package/build-ngc/core/href.js +55 -0
- package/build-ngc/core/href.js.map +1 -0
- package/build-ngc/core/index.d.ts +29 -0
- package/build-ngc/core/index.js +33 -0
- package/build-ngc/core/index.js.map +1 -0
- package/build-ngc/core/linking-config/index.d.ts +13 -0
- package/build-ngc/core/linking-config/index.js +172 -0
- package/build-ngc/core/linking-config/index.js.map +1 -0
- package/build-ngc/core/linking-config.d.ts +13 -0
- package/build-ngc/core/linking-config.js +172 -0
- package/build-ngc/core/linking-config.js.map +1 -0
- package/build-ngc/core/navigation-events/index.d.ts +16 -0
- package/build-ngc/core/navigation-events/index.js +58 -0
- package/build-ngc/core/navigation-events/index.js.map +1 -0
- package/build-ngc/core/navigation-events.d.ts +16 -0
- package/build-ngc/core/navigation-events.js +58 -0
- package/build-ngc/core/navigation-events.js.map +1 -0
- package/build-ngc/core/navigator-handles/index.d.ts +30 -0
- package/build-ngc/core/navigator-handles/index.js +16 -0
- package/build-ngc/core/navigator-handles/index.js.map +1 -0
- package/build-ngc/core/navigator-handles.d.ts +30 -0
- package/build-ngc/core/navigator-handles.js +16 -0
- package/build-ngc/core/navigator-handles.js.map +1 -0
- package/build-ngc/core/navigator-props.d.ts +216 -0
- package/build-ngc/core/navigator-props.js +8 -0
- package/build-ngc/core/navigator-props.js.map +1 -0
- package/build-ngc/core/navigator-state/index.d.ts +36 -0
- package/build-ngc/core/navigator-state/index.js +82 -0
- package/build-ngc/core/navigator-state/index.js.map +1 -0
- package/build-ngc/core/navigator-state.d.ts +36 -0
- package/build-ngc/core/navigator-state.js +82 -0
- package/build-ngc/core/navigator-state.js.map +1 -0
- package/build-ngc/core/render-drawer.d.ts +13 -0
- package/build-ngc/core/render-drawer.js +89 -0
- package/build-ngc/core/render-drawer.js.map +1 -0
- package/build-ngc/core/render-stack.d.ts +35 -0
- package/build-ngc/core/render-stack.js +295 -0
- package/build-ngc/core/render-stack.js.map +1 -0
- package/build-ngc/core/render-tabs.d.ts +20 -0
- package/build-ngc/core/render-tabs.js +98 -0
- package/build-ngc/core/render-tabs.js.map +1 -0
- package/build-ngc/core/screen-options.d.ts +4 -0
- package/build-ngc/core/screen-options.js +134 -0
- package/build-ngc/core/screen-options.js.map +1 -0
- package/build-ngc/core/search-bar-commands.d.ts +10 -0
- package/build-ngc/core/search-bar-commands.js +43 -0
- package/build-ngc/core/search-bar-commands.js.map +1 -0
- package/build-ngc/core/search-params.d.ts +9 -0
- package/build-ngc/core/search-params.js +60 -0
- package/build-ngc/core/search-params.js.map +1 -0
- package/build-ngc/core/state-persistence/index.d.ts +3 -0
- package/build-ngc/core/state-persistence/index.js +24 -0
- package/build-ngc/core/state-persistence/index.js.map +1 -0
- package/build-ngc/core/state-persistence.d.ts +3 -0
- package/build-ngc/core/state-persistence.js +24 -0
- package/build-ngc/core/state-persistence.js.map +1 -0
- package/build-ngc/core/tab-options.d.ts +13 -0
- package/build-ngc/core/tab-options.js +11 -0
- package/build-ngc/core/tab-options.js.map +1 -0
- package/build-ngc/core/tab-router-state/index.d.ts +19 -0
- package/build-ngc/core/tab-router-state/index.js +52 -0
- package/build-ngc/core/tab-router-state/index.js.map +1 -0
- package/build-ngc/core/tab-router-state.d.ts +19 -0
- package/build-ngc/core/tab-router-state.js +52 -0
- package/build-ngc/core/tab-router-state.js.map +1 -0
- package/build-ngc/register.d.ts +7 -0
- package/build-ngc/register.js +59 -0
- package/build-ngc/register.js.map +1 -0
- package/codegen-specs/FullWindowOverlayNativeComponent.ts +13 -0
- package/codegen-specs/ModalScreenNativeComponent.ts +129 -0
- package/codegen-specs/NativeScreensModule.ts +8 -0
- package/codegen-specs/NativeScreensModule.web.ts +1 -0
- package/codegen-specs/ScreenContainerNativeComponent.ts +8 -0
- package/codegen-specs/ScreenContentWrapperNativeComponent.ts +9 -0
- package/codegen-specs/ScreenFooterNativeComponent.ts +6 -0
- package/codegen-specs/ScreenNativeComponent.ts +133 -0
- package/codegen-specs/ScreenNavigationContainerNativeComponent.ts +11 -0
- package/codegen-specs/ScreenStackHeaderConfigNativeComponent.ts +99 -0
- package/codegen-specs/ScreenStackHeaderSubviewNativeComponent.ts +25 -0
- package/codegen-specs/ScreenStackNativeComponent.ts +17 -0
- package/codegen-specs/SearchBarNativeComponent.ts +101 -0
- package/codegen-specs/codegenUtils.ts +6 -0
- package/codegen-specs/gamma/ScrollViewMarkerNativeComponent.ts +15 -0
- package/codegen-specs/gamma/modals/form-sheet/FormSheetContentWrapperNativeComponent.ts +10 -0
- package/codegen-specs/gamma/modals/form-sheet/FormSheetHostNativeComponent.ts +47 -0
- package/codegen-specs/gamma/split/SplitHostNativeComponent.ts +124 -0
- package/codegen-specs/gamma/split/SplitScreenNativeComponent.ts +24 -0
- package/codegen-specs/gamma/stack/StackHeaderConfigAndroidNativeComponent.ts +144 -0
- package/codegen-specs/gamma/stack/StackHeaderConfigIOSNativeComponent.ts +34 -0
- package/codegen-specs/gamma/stack/StackHeaderItemIOSNativeComponent.ts +47 -0
- package/codegen-specs/gamma/stack/StackHeaderItemSpacerIOSNativeComponent.ts +20 -0
- package/codegen-specs/gamma/stack/StackHeaderSubviewAndroidNativeComponent.ts +27 -0
- package/codegen-specs/gamma/stack/StackHostNativeComponent.ts +8 -0
- package/codegen-specs/gamma/stack/StackScreenNativeComponent.ts +42 -0
- package/codegen-specs/safe-area/SafeAreaViewNativeComponent.ts +24 -0
- package/codegen-specs/tabs/TabsBottomAccessoryContentNativeComponent.ts +15 -0
- package/codegen-specs/tabs/TabsBottomAccessoryNativeComponent.ts +19 -0
- package/codegen-specs/tabs/TabsHostAndroidNativeComponent.ts +69 -0
- package/codegen-specs/tabs/TabsHostIOSNativeComponent.ts +92 -0
- package/codegen-specs/tabs/TabsScreenAndroidNativeComponent.ts +112 -0
- package/codegen-specs/tabs/TabsScreenIOSNativeComponent.ts +171 -0
- package/package.json +201 -0
- package/react-native.config.cjs +58 -0
- package/src/angular/drawer/drawer.test.ts +299 -0
- package/src/angular/drawer/index.ts +546 -0
- package/src/angular/drawer-screen.directive.ts +28 -0
- package/src/angular/index.ts +63 -0
- package/src/angular/injectors/index.ts +13 -0
- package/src/angular/injectors/inject-drawer-navigation.ts +21 -0
- package/src/angular/injectors/inject-focus-effect.ts +37 -0
- package/src/angular/injectors/inject-is-focused.ts +28 -0
- package/src/angular/injectors/inject-navigation-state.ts +46 -0
- package/src/angular/injectors/inject-navigation.ts +26 -0
- package/src/angular/injectors/inject-route.ts +25 -0
- package/src/angular/injectors/inject-stack-navigation.ts +24 -0
- package/src/angular/injectors/inject-tab-navigation.ts +20 -0
- package/src/angular/injectors/injectors.test.ts +247 -0
- package/src/angular/injectors/use-typed-navigation.test.ts +229 -0
- package/src/angular/linking.ts +55 -0
- package/src/angular/navigation-context.service.ts +68 -0
- package/src/angular/navigation-scope.directive.ts +31 -0
- package/src/angular/nested-navigation.test.ts +189 -0
- package/src/angular/screen.directive.ts +48 -0
- package/src/angular/search-bar-ref.directive.ts +38 -0
- package/src/angular/stack/index.ts +401 -0
- package/src/angular/stack/stack.test.ts +419 -0
- package/src/angular/tab-screen.directive.ts +26 -0
- package/src/angular/tabs/index.ts +254 -0
- package/src/angular/tabs/tabs.test.ts +249 -0
- package/src/core/constants.ts +68 -0
- package/src/core/drawer-options/drawer-options.test.ts +415 -0
- package/src/core/drawer-options/index.ts +331 -0
- package/src/core/drawer-router-state/drawer-router-state.test.ts +116 -0
- package/src/core/drawer-router-state/index.ts +71 -0
- package/src/core/guards.ts +23 -0
- package/src/core/header-bar-buttons.ts +212 -0
- package/src/core/index.ts +170 -0
- package/src/core/linking-config/index.ts +209 -0
- package/src/core/linking-config/linking-config.test.ts +109 -0
- package/src/core/navigation-events/index.ts +94 -0
- package/src/core/navigation-events/navigation-events.test.ts +56 -0
- package/src/core/navigator-handles/index.ts +63 -0
- package/src/core/navigator-handles/navigator-handles.test.ts +46 -0
- package/src/core/navigator-props.ts +323 -0
- package/src/core/navigator-state/index.ts +112 -0
- package/src/core/navigator-state/navigator-state.test.ts +80 -0
- package/src/core/render-drawer.ts +123 -0
- package/src/core/render-stack.ts +414 -0
- package/src/core/render-tabs.ts +138 -0
- package/src/core/screen-options.ts +181 -0
- package/src/core/search-bar-commands.ts +57 -0
- package/src/core/state-persistence/index.ts +30 -0
- package/src/core/state-persistence/state-persistence.test.ts +42 -0
- package/src/core/tab-options.ts +25 -0
- package/src/core/tab-router-state/index.ts +75 -0
- package/src/core/tab-router-state/tab-router-state.test.ts +34 -0
- package/src/globals.d.ts +4 -0
- package/src/react/collect-registry.ts +19 -0
- package/src/react/drawer/drawer.test.tsx +472 -0
- package/src/react/drawer/index.ts +451 -0
- package/src/react/drawer-screen.ts +29 -0
- package/src/react/hooks/hooks.test.tsx +199 -0
- package/src/react/hooks/index.ts +12 -0
- package/src/react/hooks/use-drawer-navigation.ts +21 -0
- package/src/react/hooks/use-focus-effect.ts +36 -0
- package/src/react/hooks/use-is-focused.ts +26 -0
- package/src/react/hooks/use-navigation-state/index.ts +47 -0
- package/src/react/hooks/use-navigation-state/use-navigation-state.test.ts +85 -0
- package/src/react/hooks/use-navigation.ts +36 -0
- package/src/react/hooks/use-route.ts +11 -0
- package/src/react/hooks/use-stack-navigation.ts +23 -0
- package/src/react/hooks/use-tab-navigation.ts +21 -0
- package/src/react/hooks/use-typed-navigation.test.tsx +187 -0
- package/src/react/index.ts +52 -0
- package/src/react/linking.ts +59 -0
- package/src/react/navigation-context.ts +40 -0
- package/src/react/nested-navigation.test.tsx +145 -0
- package/src/react/screen.ts +44 -0
- package/src/react/stack/index.ts +323 -0
- package/src/react/stack/stack.test.tsx +514 -0
- package/src/react/tab-screen.ts +30 -0
- package/src/react/tabs/index.ts +204 -0
- package/src/react/tabs/tabs.test.tsx +256 -0
- package/src/register.ts +59 -0
- package/src/vue/composables/composables.test.ts +221 -0
- package/src/vue/composables/index.ts +12 -0
- package/src/vue/composables/use-drawer-navigation.ts +25 -0
- package/src/vue/composables/use-focus-effect.ts +37 -0
- package/src/vue/composables/use-is-focused.ts +34 -0
- package/src/vue/composables/use-navigation-state.ts +38 -0
- package/src/vue/composables/use-navigation.ts +30 -0
- package/src/vue/composables/use-route.ts +13 -0
- package/src/vue/composables/use-stack-navigation.ts +28 -0
- package/src/vue/composables/use-tab-navigation.ts +25 -0
- package/src/vue/composables/use-typed-navigation.test.ts +205 -0
- package/src/vue/drawer/drawer.test.ts +451 -0
- package/src/vue/drawer/index.ts +550 -0
- package/src/vue/drawer-screen.ts +32 -0
- package/src/vue/index.ts +70 -0
- package/src/vue/linking.ts +69 -0
- package/src/vue/navigation-context.ts +87 -0
- package/src/vue/nested-navigation.test.ts +155 -0
- package/src/vue/screen.ts +52 -0
- package/src/vue/stack/index.ts +376 -0
- package/src/vue/stack/stack.test.ts +501 -0
- package/src/vue/tab-screen.ts +29 -0
- package/src/vue/tabs/index.ts +302 -0
- package/src/vue/tabs/tabs.test.ts +278 -0
- package/symbiote-navigation.podspec +84 -0
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
// Co-located Vue-driven test, the Vue twin of react/hooks/hooks.test.tsx. Mirrors
|
|
2
|
+
// ../stack.test.ts's fixture (an injected codegen-shaped RNSScreen ViewConfig exposing
|
|
3
|
+
// onAppear/onDisappear/onWillAppear/onWillDisappear) and drives the same native events stack.ts
|
|
4
|
+
// wires to emit 'focus'/'blur', proving the composables react to the real RNS lifecycle, not to a
|
|
5
|
+
// synthetic shortcut.
|
|
6
|
+
|
|
7
|
+
import { defineComponent, h, ref } from '@vue/runtime-core';
|
|
8
|
+
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
9
|
+
import { mount, unmount, setNativeViewConfigSource } from '@symbiote-native/vue';
|
|
10
|
+
import type { INativeViewConfig } from '@symbiote-native/engine';
|
|
11
|
+
import { installFabric, type IFakeNode } from '@symbiote-native/test-utils';
|
|
12
|
+
import { Stack } from '../stack';
|
|
13
|
+
import type { INavigatorHandle } from '../stack';
|
|
14
|
+
import { useFocusEffect, useIsFocused, useNavigation, useNavigationState, useRoute } from './index';
|
|
15
|
+
|
|
16
|
+
const ROOT_TAG = 4513;
|
|
17
|
+
const SCREEN_VIEW = 'RNSScreen';
|
|
18
|
+
const STACK_VIEW = 'RNSScreenStack';
|
|
19
|
+
const HEADER_CONFIG_VIEW = 'RNSScreenStackHeaderConfig';
|
|
20
|
+
|
|
21
|
+
function directEvent(registrationName: string) {
|
|
22
|
+
return { registrationName };
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const VIEW_CONFIGS: Record<string, INativeViewConfig> = {
|
|
26
|
+
[SCREEN_VIEW]: {
|
|
27
|
+
directEventTypes: {
|
|
28
|
+
topAppear: directEvent('onAppear'),
|
|
29
|
+
topDisappear: directEvent('onDisappear'),
|
|
30
|
+
topWillAppear: directEvent('onWillAppear'),
|
|
31
|
+
topWillDisappear: directEvent('onWillDisappear'),
|
|
32
|
+
topDismissed: directEvent('onDismissed'),
|
|
33
|
+
topHeaderBackButtonClicked: directEvent('onHeaderBackButtonClicked'),
|
|
34
|
+
},
|
|
35
|
+
validAttributes: {
|
|
36
|
+
screenId: true,
|
|
37
|
+
activityState: true,
|
|
38
|
+
gestureEnabled: true,
|
|
39
|
+
stackAnimation: true,
|
|
40
|
+
stackPresentation: true,
|
|
41
|
+
transitionDuration: true,
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
[STACK_VIEW]: {
|
|
45
|
+
directEventTypes: { topFinishTransitioning: directEvent('onFinishTransitioning') },
|
|
46
|
+
validAttributes: {},
|
|
47
|
+
},
|
|
48
|
+
[HEADER_CONFIG_VIEW]: {
|
|
49
|
+
directEventTypes: { topPressHeaderBarButtonItem: directEvent('onPressHeaderBarButtonItem') },
|
|
50
|
+
validAttributes: { title: true, hidden: true, backTitle: true, backTitleVisible: true },
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const fabric = installFabric();
|
|
55
|
+
setNativeViewConfigSource(name => VIEW_CONFIGS[name]);
|
|
56
|
+
const tick = (): Promise<void> => new Promise(resolve => setTimeout(resolve, 0));
|
|
57
|
+
|
|
58
|
+
beforeEach(() => fabric.reset());
|
|
59
|
+
afterEach(() => unmount(ROOT_TAG));
|
|
60
|
+
|
|
61
|
+
function screenNodes(): IFakeNode[] {
|
|
62
|
+
const found: IFakeNode[] = [];
|
|
63
|
+
const collect = (nodes: readonly IFakeNode[]): void => {
|
|
64
|
+
for (const node of nodes) {
|
|
65
|
+
if (node.viewName === SCREEN_VIEW) found.push(node);
|
|
66
|
+
collect(node.children);
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
collect(fabric.committed);
|
|
70
|
+
return found;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function HomeScreen() {
|
|
74
|
+
return h('symbiote-text', {}, 'home');
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function DetailsScreen() {
|
|
78
|
+
return h('symbiote-text', {}, 'details');
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
describe('navigation composables', () => {
|
|
82
|
+
it("useIsFocused reflects the route's native appear/disappear events", async () => {
|
|
83
|
+
let latestIsFocused: boolean | undefined;
|
|
84
|
+
// Plain functions used as `component:` are stateless functional components - Vue calls them
|
|
85
|
+
// fresh on every render and treats their return value as vnodes directly, NOT as a "setup
|
|
86
|
+
// returns a render fn" component. A screen calling a composable needs a real setup-based
|
|
87
|
+
// component, hence defineComponent here instead of a bare function.
|
|
88
|
+
const TrackedHomeScreen = defineComponent(() => {
|
|
89
|
+
const isFocused = useIsFocused();
|
|
90
|
+
return () => {
|
|
91
|
+
latestIsFocused = isFocused.value;
|
|
92
|
+
return h('symbiote-text', {}, 'home');
|
|
93
|
+
};
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
mount(
|
|
97
|
+
ROOT_TAG,
|
|
98
|
+
defineComponent({
|
|
99
|
+
setup: () => () =>
|
|
100
|
+
h(Stack, { initialRouteName: 'Home' }, () => [
|
|
101
|
+
h(Stack.Screen, { name: 'Home', component: TrackedHomeScreen }),
|
|
102
|
+
h(Stack.Screen, { name: 'Details', component: DetailsScreen }),
|
|
103
|
+
]),
|
|
104
|
+
}),
|
|
105
|
+
);
|
|
106
|
+
await tick();
|
|
107
|
+
expect(latestIsFocused).toBe(false);
|
|
108
|
+
|
|
109
|
+
const home = screenNodes()[0];
|
|
110
|
+
fabric.fireEvent(home.instanceHandle, 'topAppear', {});
|
|
111
|
+
await tick();
|
|
112
|
+
expect(latestIsFocused).toBe(true);
|
|
113
|
+
|
|
114
|
+
fabric.fireEvent(home.instanceHandle, 'topDisappear', {});
|
|
115
|
+
await tick();
|
|
116
|
+
expect(latestIsFocused).toBe(false);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it('useFocusEffect runs its effect on focus and its cleanup on blur', async () => {
|
|
120
|
+
const events: string[] = [];
|
|
121
|
+
const TrackedHomeScreen = defineComponent(() => {
|
|
122
|
+
useFocusEffect(() => {
|
|
123
|
+
events.push('effect');
|
|
124
|
+
return () => events.push('cleanup');
|
|
125
|
+
});
|
|
126
|
+
return () => h('symbiote-text', {}, 'home');
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
mount(
|
|
130
|
+
ROOT_TAG,
|
|
131
|
+
defineComponent({
|
|
132
|
+
setup: () => () =>
|
|
133
|
+
h(Stack, { initialRouteName: 'Home' }, () => [
|
|
134
|
+
h(Stack.Screen, { name: 'Home', component: TrackedHomeScreen }),
|
|
135
|
+
h(Stack.Screen, { name: 'Details', component: DetailsScreen }),
|
|
136
|
+
]),
|
|
137
|
+
}),
|
|
138
|
+
);
|
|
139
|
+
await tick();
|
|
140
|
+
expect(events).toEqual([]);
|
|
141
|
+
|
|
142
|
+
const home = screenNodes()[0];
|
|
143
|
+
fabric.fireEvent(home.instanceHandle, 'topAppear', {});
|
|
144
|
+
await tick();
|
|
145
|
+
expect(events).toEqual(['effect']);
|
|
146
|
+
|
|
147
|
+
fabric.fireEvent(home.instanceHandle, 'topDisappear', {});
|
|
148
|
+
await tick();
|
|
149
|
+
expect(events).toEqual(['effect', 'cleanup']);
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
it('useNavigation().addListener fires on focus and useRoute exposes name/params', async () => {
|
|
153
|
+
let capturedName: string | undefined;
|
|
154
|
+
let capturedParams: unknown;
|
|
155
|
+
const focusEvents: string[] = [];
|
|
156
|
+
|
|
157
|
+
const TrackedDetailsScreen = defineComponent(() => {
|
|
158
|
+
const navigation = useNavigation();
|
|
159
|
+
const route = useRoute();
|
|
160
|
+
capturedName = route.value.name;
|
|
161
|
+
capturedParams = route.value.params;
|
|
162
|
+
navigation.value.addListener('focus', () => focusEvents.push('focus'));
|
|
163
|
+
return () => h('symbiote-text', {}, 'details');
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
const handleRef = ref<INavigatorHandle | null>(null);
|
|
167
|
+
mount(
|
|
168
|
+
ROOT_TAG,
|
|
169
|
+
defineComponent({
|
|
170
|
+
setup: () => () =>
|
|
171
|
+
h(Stack, { ref: handleRef, initialRouteName: 'Home' }, () => [
|
|
172
|
+
h(Stack.Screen, { name: 'Home', component: HomeScreen }),
|
|
173
|
+
h(Stack.Screen, { name: 'Details', component: TrackedDetailsScreen }),
|
|
174
|
+
]),
|
|
175
|
+
}),
|
|
176
|
+
);
|
|
177
|
+
await tick();
|
|
178
|
+
handleRef.value?.push('Details', { id: 7 });
|
|
179
|
+
await tick();
|
|
180
|
+
expect(capturedName).toBe('Details');
|
|
181
|
+
expect(capturedParams).toEqual({ id: 7 });
|
|
182
|
+
|
|
183
|
+
const details = screenNodes()[1];
|
|
184
|
+
fabric.fireEvent(details.instanceHandle, 'topAppear', {});
|
|
185
|
+
await tick();
|
|
186
|
+
expect(focusEvents).toEqual(['focus']);
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
it('useNavigationState reflects the route stack growing across a push', async () => {
|
|
190
|
+
let routeCount: number | undefined;
|
|
191
|
+
const TrackedHomeScreen = defineComponent(() => {
|
|
192
|
+
const count = useNavigationState(state => state.routes.length);
|
|
193
|
+
return () => {
|
|
194
|
+
routeCount = count.value;
|
|
195
|
+
return h('symbiote-text', {}, 'home');
|
|
196
|
+
};
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
const handleRef = ref<INavigatorHandle | null>(null);
|
|
200
|
+
mount(
|
|
201
|
+
ROOT_TAG,
|
|
202
|
+
defineComponent({
|
|
203
|
+
setup: () => () =>
|
|
204
|
+
h(Stack, { ref: handleRef, initialRouteName: 'Home' }, () => [
|
|
205
|
+
h(Stack.Screen, { name: 'Home', component: TrackedHomeScreen }),
|
|
206
|
+
h(Stack.Screen, { name: 'Details', component: DetailsScreen }),
|
|
207
|
+
]),
|
|
208
|
+
}),
|
|
209
|
+
);
|
|
210
|
+
await tick();
|
|
211
|
+
expect(routeCount).toBe(1);
|
|
212
|
+
|
|
213
|
+
handleRef.value?.push('Details');
|
|
214
|
+
await tick();
|
|
215
|
+
expect(routeCount).toBe(2);
|
|
216
|
+
|
|
217
|
+
handleRef.value?.pop();
|
|
218
|
+
await tick();
|
|
219
|
+
expect(routeCount).toBe(1);
|
|
220
|
+
});
|
|
221
|
+
});
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export { useNavigation } from './use-navigation';
|
|
2
|
+
export type { INavigationHandle } from './use-navigation';
|
|
3
|
+
export { useStackNavigation } from './use-stack-navigation';
|
|
4
|
+
export type { IStackNavigationHandle } from './use-stack-navigation';
|
|
5
|
+
export { useTabNavigation } from './use-tab-navigation';
|
|
6
|
+
export type { ITabNavigationHandle } from './use-tab-navigation';
|
|
7
|
+
export { useDrawerNavigation } from './use-drawer-navigation';
|
|
8
|
+
export type { IDrawerNavigationHandle } from './use-drawer-navigation';
|
|
9
|
+
export { useRoute } from './use-route';
|
|
10
|
+
export { useIsFocused } from './use-is-focused';
|
|
11
|
+
export { useFocusEffect } from './use-focus-effect';
|
|
12
|
+
export { useNavigationState } from './use-navigation-state';
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// Twin of use-stack-navigation.ts, narrowing to a Drawer handle instead.
|
|
2
|
+
|
|
3
|
+
import { computed } from '@vue/runtime-core';
|
|
4
|
+
import type { ComputedRef } from '@vue/runtime-core';
|
|
5
|
+
import type { IDrawerNavigatorHandle } from '../../core';
|
|
6
|
+
import { isDrawerNavigatorHandle } from '../../core';
|
|
7
|
+
import { useNavigation } from './use-navigation';
|
|
8
|
+
import type { INavigationHandle } from './use-navigation';
|
|
9
|
+
|
|
10
|
+
export type IDrawerNavigationHandle = IDrawerNavigatorHandle &
|
|
11
|
+
Pick<INavigationHandle, 'addListener' | 'getParent'>;
|
|
12
|
+
|
|
13
|
+
export function useDrawerNavigation(): ComputedRef<IDrawerNavigationHandle> {
|
|
14
|
+
const navigation = useNavigation();
|
|
15
|
+
return computed(() => {
|
|
16
|
+
const value = navigation.value;
|
|
17
|
+
if (!isDrawerNavigatorHandle(value)) {
|
|
18
|
+
throw new Error(
|
|
19
|
+
'useDrawerNavigation() was called from a component whose nearest navigator is not a Drawer. ' +
|
|
20
|
+
'Use useNavigation() instead if this component can render under more than one navigator kind.',
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
return value;
|
|
24
|
+
});
|
|
25
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// Mirrors @react-navigation's useFocusEffect; Vue twin of react/hooks/use-focus-effect.ts.
|
|
2
|
+
// React's version requires memoizing `effect` (useCallback) since a new identity re-subscribes
|
|
3
|
+
// it like any other useEffect dependency. That doesn't apply here: Vue's setup() runs once, so
|
|
4
|
+
// `effect` is read once at composable-call time and closed over directly by onMounted/onUnmounted -
|
|
5
|
+
// there's no dependency array to go stale.
|
|
6
|
+
|
|
7
|
+
import { onMounted, onUnmounted } from '@vue/runtime-core';
|
|
8
|
+
import { NAVIGATION_EVENT_BLUR, NAVIGATION_EVENT_FOCUS } from '../../core';
|
|
9
|
+
import { requireNavigationScope } from '../navigation-context';
|
|
10
|
+
|
|
11
|
+
export function useFocusEffect(effect: () => (() => void) | void): void {
|
|
12
|
+
const scope = requireNavigationScope('useFocusEffect');
|
|
13
|
+
|
|
14
|
+
let cleanup: (() => void) | void;
|
|
15
|
+
let unsubscribeFocus: (() => void) | undefined;
|
|
16
|
+
let unsubscribeBlur: (() => void) | undefined;
|
|
17
|
+
|
|
18
|
+
const runEffect = (): void => {
|
|
19
|
+
cleanup = effect();
|
|
20
|
+
};
|
|
21
|
+
const runCleanup = (): void => {
|
|
22
|
+
cleanup?.();
|
|
23
|
+
cleanup = undefined;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
onMounted(() => {
|
|
27
|
+
const { emitter } = scope.value;
|
|
28
|
+
unsubscribeFocus = emitter.addListener(NAVIGATION_EVENT_FOCUS, runEffect);
|
|
29
|
+
unsubscribeBlur = emitter.addListener(NAVIGATION_EVENT_BLUR, runCleanup);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
onUnmounted(() => {
|
|
33
|
+
unsubscribeFocus?.();
|
|
34
|
+
unsubscribeBlur?.();
|
|
35
|
+
runCleanup();
|
|
36
|
+
});
|
|
37
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// Mirrors @react-navigation's useIsFocused; Vue twin of react/hooks/use-is-focused.ts.
|
|
2
|
+
// Starts `false` rather than guessing from stack position: the route's emitter only fires 'focus'
|
|
3
|
+
// once RNSScreen's native onAppear lands (stack.ts) or the screen mounts focused (tabs.ts/drawer.ts) -
|
|
4
|
+
// a screen genuinely isn't focused at the instant it mounts, the same async gap real native
|
|
5
|
+
// transitions have.
|
|
6
|
+
|
|
7
|
+
import { onMounted, onUnmounted, ref } from '@vue/runtime-core';
|
|
8
|
+
import type { Ref } from '@vue/runtime-core';
|
|
9
|
+
import { NAVIGATION_EVENT_BLUR, NAVIGATION_EVENT_FOCUS } from '../../core';
|
|
10
|
+
import { requireNavigationScope } from '../navigation-context';
|
|
11
|
+
|
|
12
|
+
export function useIsFocused(): Ref<boolean> {
|
|
13
|
+
const scope = requireNavigationScope('useIsFocused');
|
|
14
|
+
const isFocused = ref(false);
|
|
15
|
+
let unsubscribeFocus: (() => void) | undefined;
|
|
16
|
+
let unsubscribeBlur: (() => void) | undefined;
|
|
17
|
+
|
|
18
|
+
onMounted(() => {
|
|
19
|
+
const { emitter } = scope.value;
|
|
20
|
+
unsubscribeFocus = emitter.addListener(NAVIGATION_EVENT_FOCUS, () => {
|
|
21
|
+
isFocused.value = true;
|
|
22
|
+
});
|
|
23
|
+
unsubscribeBlur = emitter.addListener(NAVIGATION_EVENT_BLUR, () => {
|
|
24
|
+
isFocused.value = false;
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
onUnmounted(() => {
|
|
29
|
+
unsubscribeFocus?.();
|
|
30
|
+
unsubscribeBlur?.();
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
return isFocused;
|
|
34
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// Mirrors @react-navigation's useNavigationState; Vue twin of react/hooks/use-navigation-state.ts.
|
|
2
|
+
// The reducer/dispatch machinery lives in core/navigator-state.ts (tab-router-state.ts /
|
|
3
|
+
// drawer-router-state.ts for the other two navigators) - this composable only wires the
|
|
4
|
+
// subscription to the `state` broadcast each render loop re-emits after commit.
|
|
5
|
+
//
|
|
6
|
+
// Seeded from a single-route snapshot ({ routes: [route] }) rather than left undefined: the real
|
|
7
|
+
// broadcast lands after mount (Stack's render loop; tabs.ts/drawer.ts emit on focus), so a
|
|
8
|
+
// selector reading e.g. `state.routes.at(-1)?.name` still resolves correctly on first paint for
|
|
9
|
+
// the common single-route case - the same async gap useIsFocused documents.
|
|
10
|
+
|
|
11
|
+
import { onMounted, onUnmounted, shallowRef } from '@vue/runtime-core';
|
|
12
|
+
import type { ShallowRef } from '@vue/runtime-core';
|
|
13
|
+
import type { INavigatorState } from '../../core';
|
|
14
|
+
import { NAVIGATION_EVENT_STATE, isRecord } from '../../core';
|
|
15
|
+
import { requireNavigationScope } from '../navigation-context';
|
|
16
|
+
|
|
17
|
+
function isNavigatorState(value: unknown): value is INavigatorState {
|
|
18
|
+
return isRecord(value) && Array.isArray(value.routes);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function useNavigationState<TResult>(
|
|
22
|
+
selector: (state: INavigatorState) => TResult,
|
|
23
|
+
): ShallowRef<TResult> {
|
|
24
|
+
const scope = requireNavigationScope('useNavigationState');
|
|
25
|
+
const result = shallowRef<TResult>(selector({ routes: [scope.value.route] }));
|
|
26
|
+
let unsubscribe: (() => void) | undefined;
|
|
27
|
+
|
|
28
|
+
onMounted(() => {
|
|
29
|
+
unsubscribe = scope.value.emitter.addListener(NAVIGATION_EVENT_STATE, (state: unknown) => {
|
|
30
|
+
if (!isNavigatorState(state)) return;
|
|
31
|
+
result.value = selector(state);
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
onUnmounted(() => unsubscribe?.());
|
|
36
|
+
|
|
37
|
+
return result;
|
|
38
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// Mirrors @react-navigation's `navigation.addListener('focus', cb)` surface; Vue twin of
|
|
2
|
+
// react/hooks/use-navigation.ts (returns a ComputedRef, Vue's reactive-primitive convention,
|
|
3
|
+
// instead of a plain value). All pub/sub logic lives in ../../core/navigation-events - this
|
|
4
|
+
// composable only reads the injected navigation scope and binds identity.
|
|
5
|
+
|
|
6
|
+
import { computed } from '@vue/runtime-core';
|
|
7
|
+
import type { ComputedRef } from '@vue/runtime-core';
|
|
8
|
+
import type { INavigationEventListener, INavigationEventName } from '../../core';
|
|
9
|
+
import { requireNavigationScope } from '../navigation-context';
|
|
10
|
+
import type { IAnyNavigatorHandle } from '../navigation-context';
|
|
11
|
+
|
|
12
|
+
export type INavigationHandle = IAnyNavigatorHandle & {
|
|
13
|
+
addListener: (event: INavigationEventName, listener: INavigationEventListener) => () => void;
|
|
14
|
+
// Walks exactly ONE hop up the scope's `parent` chain to the enclosing navigator's handle -
|
|
15
|
+
// e.g. a Tab screen nested inside a Stack screen calling getParent() to push a new Stack route.
|
|
16
|
+
// Callers narrow the union themselves ('push' in parent, etc.). Deliberately NOT
|
|
17
|
+
// react-navigation's getParent(id) (named/targeted ancestor lookup) or a target-based dispatch
|
|
18
|
+
// to a specific nested navigator by name - plain immediate-parent walking is v1 scope; multi-hop
|
|
19
|
+
// ancestry would need each returned handle to carry its own getParent, which the plain
|
|
20
|
+
// per-navigator handle types (INavigatorHandle/ITabNavigatorHandle/IDrawerNavigatorHandle) don't.
|
|
21
|
+
getParent: () => IAnyNavigatorHandle | undefined;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export function useNavigation(): ComputedRef<INavigationHandle> {
|
|
25
|
+
const scope = requireNavigationScope('useNavigation');
|
|
26
|
+
return computed(() => {
|
|
27
|
+
const { navigation, emitter, parent } = scope.value;
|
|
28
|
+
return { ...navigation, addListener: emitter.addListener, getParent: () => parent?.navigation };
|
|
29
|
+
});
|
|
30
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// Mirrors @react-navigation's useRoute; Vue twin of react/hooks/use-route.ts (returns a
|
|
2
|
+
// ComputedRef instead of a plain value). Zero logic of its own - the route object itself is built
|
|
3
|
+
// by stack.ts's/tabs.ts's/drawer.ts's render loop from the shared core reducer's state.
|
|
4
|
+
|
|
5
|
+
import { computed } from '@vue/runtime-core';
|
|
6
|
+
import type { ComputedRef } from '@vue/runtime-core';
|
|
7
|
+
import type { IRoute } from '../../core';
|
|
8
|
+
import { requireNavigationScope } from '../navigation-context';
|
|
9
|
+
|
|
10
|
+
export function useRoute(): ComputedRef<IRoute<unknown>> {
|
|
11
|
+
const scope = requireNavigationScope('useRoute');
|
|
12
|
+
return computed(() => scope.value.route);
|
|
13
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// useNavigation() returns a union (IAnyNavigatorHandle) since it doesn't know which navigator
|
|
2
|
+
// mounted the calling component. When a component genuinely knows it only ever renders under a
|
|
3
|
+
// Stack, this composable narrows that union ONCE, here, so call sites never write their own
|
|
4
|
+
// 'push' in navigation check. Vue twin of react/hooks/use-stack-navigation.ts.
|
|
5
|
+
|
|
6
|
+
import { computed } from '@vue/runtime-core';
|
|
7
|
+
import type { ComputedRef } from '@vue/runtime-core';
|
|
8
|
+
import type { INavigatorHandle } from '../../core';
|
|
9
|
+
import { isStackNavigatorHandle } from '../../core';
|
|
10
|
+
import { useNavigation } from './use-navigation';
|
|
11
|
+
import type { INavigationHandle } from './use-navigation';
|
|
12
|
+
|
|
13
|
+
export type IStackNavigationHandle = INavigatorHandle &
|
|
14
|
+
Pick<INavigationHandle, 'addListener' | 'getParent'>;
|
|
15
|
+
|
|
16
|
+
export function useStackNavigation(): ComputedRef<IStackNavigationHandle> {
|
|
17
|
+
const navigation = useNavigation();
|
|
18
|
+
return computed(() => {
|
|
19
|
+
const value = navigation.value;
|
|
20
|
+
if (!isStackNavigatorHandle(value)) {
|
|
21
|
+
throw new Error(
|
|
22
|
+
'useStackNavigation() was called from a component whose nearest navigator is not a Stack. ' +
|
|
23
|
+
'Use useNavigation() instead if this component can render under more than one navigator kind.',
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
return value;
|
|
27
|
+
});
|
|
28
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// Twin of use-stack-navigation.ts, narrowing to a Tab handle instead.
|
|
2
|
+
|
|
3
|
+
import { computed } from '@vue/runtime-core';
|
|
4
|
+
import type { ComputedRef } from '@vue/runtime-core';
|
|
5
|
+
import type { ITabNavigatorHandle } from '../../core';
|
|
6
|
+
import { isTabNavigatorHandle } from '../../core';
|
|
7
|
+
import { useNavigation } from './use-navigation';
|
|
8
|
+
import type { INavigationHandle } from './use-navigation';
|
|
9
|
+
|
|
10
|
+
export type ITabNavigationHandle = ITabNavigatorHandle &
|
|
11
|
+
Pick<INavigationHandle, 'addListener' | 'getParent'>;
|
|
12
|
+
|
|
13
|
+
export function useTabNavigation(): ComputedRef<ITabNavigationHandle> {
|
|
14
|
+
const navigation = useNavigation();
|
|
15
|
+
return computed(() => {
|
|
16
|
+
const value = navigation.value;
|
|
17
|
+
if (!isTabNavigatorHandle(value)) {
|
|
18
|
+
throw new Error(
|
|
19
|
+
'useTabNavigation() was called from a component whose nearest navigator is not a Tab. ' +
|
|
20
|
+
'Use useNavigation() instead if this component can render under more than one navigator kind.',
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
return value;
|
|
24
|
+
});
|
|
25
|
+
}
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
// Co-located Vue-driven test for useStackNavigation/useTabNavigation/useDrawerNavigation - the
|
|
2
|
+
// narrowed twins of useNavigation() that hide the union guard. Vue twin of
|
|
3
|
+
// react/hooks/use-typed-navigation.test.tsx.
|
|
4
|
+
|
|
5
|
+
import { defineComponent, h } from '@vue/runtime-core';
|
|
6
|
+
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
7
|
+
import { mount, unmount, setNativeViewConfigSource, Dimensions } from '@symbiote-native/vue';
|
|
8
|
+
import type { INativeViewConfig } from '@symbiote-native/engine';
|
|
9
|
+
import { installFabric } from '@symbiote-native/test-utils';
|
|
10
|
+
import { Stack } from '../stack';
|
|
11
|
+
import { Tab } from '../tabs';
|
|
12
|
+
import { Drawer } from '../drawer';
|
|
13
|
+
import { useDrawerNavigation, useStackNavigation, useTabNavigation } from './index';
|
|
14
|
+
|
|
15
|
+
const ROOT_TAG = 4613;
|
|
16
|
+
const SCREEN_VIEW = 'RNSScreen';
|
|
17
|
+
const STACK_VIEW = 'RNSScreenStack';
|
|
18
|
+
|
|
19
|
+
const VIEW_CONFIGS: Record<string, INativeViewConfig> = {
|
|
20
|
+
[SCREEN_VIEW]: {
|
|
21
|
+
directEventTypes: {
|
|
22
|
+
topAppear: { registrationName: 'onAppear' },
|
|
23
|
+
topDisappear: { registrationName: 'onDisappear' },
|
|
24
|
+
topWillAppear: { registrationName: 'onWillAppear' },
|
|
25
|
+
topWillDisappear: { registrationName: 'onWillDisappear' },
|
|
26
|
+
topDismissed: { registrationName: 'onDismissed' },
|
|
27
|
+
topHeaderBackButtonClicked: { registrationName: 'onHeaderBackButtonClicked' },
|
|
28
|
+
},
|
|
29
|
+
validAttributes: {
|
|
30
|
+
screenId: true,
|
|
31
|
+
activityState: true,
|
|
32
|
+
gestureEnabled: true,
|
|
33
|
+
stackAnimation: true,
|
|
34
|
+
stackPresentation: true,
|
|
35
|
+
transitionDuration: true,
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
[STACK_VIEW]: {
|
|
39
|
+
directEventTypes: { topFinishTransitioning: { registrationName: 'onFinishTransitioning' } },
|
|
40
|
+
validAttributes: {},
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
// Drawer reads the screen width off useWindowDimensions() to resolve its swipe edge zone;
|
|
45
|
+
// headless has no DeviceInfo native module, so seed a concrete width once - same fixture as
|
|
46
|
+
// drawer.test.ts.
|
|
47
|
+
Dimensions.set({ window: { width: 375, height: 812, scale: 1, fontScale: 1 } });
|
|
48
|
+
|
|
49
|
+
const fabric = installFabric();
|
|
50
|
+
setNativeViewConfigSource(name => VIEW_CONFIGS[name]);
|
|
51
|
+
const tick = (): Promise<void> => new Promise(resolve => setTimeout(resolve, 0));
|
|
52
|
+
|
|
53
|
+
beforeEach(() => fabric.reset());
|
|
54
|
+
afterEach(() => unmount(ROOT_TAG));
|
|
55
|
+
|
|
56
|
+
function textScreen(label: string) {
|
|
57
|
+
return () => h('symbiote-text', {}, label);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
describe('useStackNavigation', () => {
|
|
61
|
+
it('returns a concretely-typed Stack handle with push, no narrowing needed', async () => {
|
|
62
|
+
let canPush = false;
|
|
63
|
+
const TrackedHomeScreen = defineComponent(() => {
|
|
64
|
+
const navigation = useStackNavigation();
|
|
65
|
+
return () => {
|
|
66
|
+
canPush = typeof navigation.value.push === 'function';
|
|
67
|
+
return h('symbiote-text', {}, 'home');
|
|
68
|
+
};
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
mount(
|
|
72
|
+
ROOT_TAG,
|
|
73
|
+
defineComponent({
|
|
74
|
+
setup: () => () =>
|
|
75
|
+
h(Stack, { initialRouteName: 'Home' }, () => [
|
|
76
|
+
h(Stack.Screen, { name: 'Home', component: TrackedHomeScreen }),
|
|
77
|
+
h(Stack.Screen, { name: 'Details', component: textScreen('details') }),
|
|
78
|
+
]),
|
|
79
|
+
}),
|
|
80
|
+
);
|
|
81
|
+
await tick();
|
|
82
|
+
expect(canPush).toBe(true);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it('throws when the nearest navigator is a Tab, not a Stack', () => {
|
|
86
|
+
const TrackedHomeTab = defineComponent(() => {
|
|
87
|
+
const navigation = useStackNavigation();
|
|
88
|
+
return () => {
|
|
89
|
+
void navigation.value;
|
|
90
|
+
return h('symbiote-text', {}, 'home');
|
|
91
|
+
};
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
expect(() =>
|
|
95
|
+
mount(
|
|
96
|
+
ROOT_TAG,
|
|
97
|
+
defineComponent({
|
|
98
|
+
setup: () => () =>
|
|
99
|
+
h(Tab, { initialRouteName: 'Home' }, () => [
|
|
100
|
+
h(Tab.Screen, { name: 'Home', component: TrackedHomeTab }),
|
|
101
|
+
h(Tab.Screen, { name: 'Search', component: textScreen('search') }),
|
|
102
|
+
]),
|
|
103
|
+
}),
|
|
104
|
+
),
|
|
105
|
+
).toThrow(/nearest navigator is not a Stack/);
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
describe('useTabNavigation', () => {
|
|
110
|
+
it('returns a concretely-typed Tab handle with jumpTo, no narrowing needed', async () => {
|
|
111
|
+
let canJumpTo = false;
|
|
112
|
+
const TrackedHomeTab = defineComponent(() => {
|
|
113
|
+
const navigation = useTabNavigation();
|
|
114
|
+
return () => {
|
|
115
|
+
canJumpTo = typeof navigation.value.jumpTo === 'function';
|
|
116
|
+
return h('symbiote-text', {}, 'home');
|
|
117
|
+
};
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
mount(
|
|
121
|
+
ROOT_TAG,
|
|
122
|
+
defineComponent({
|
|
123
|
+
setup: () => () =>
|
|
124
|
+
h(Tab, { initialRouteName: 'Home' }, () => [
|
|
125
|
+
h(Tab.Screen, { name: 'Home', component: TrackedHomeTab }),
|
|
126
|
+
h(Tab.Screen, { name: 'Search', component: textScreen('search') }),
|
|
127
|
+
]),
|
|
128
|
+
}),
|
|
129
|
+
);
|
|
130
|
+
await tick();
|
|
131
|
+
expect(canJumpTo).toBe(true);
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
it('throws when the nearest navigator is a Stack, not a Tab', () => {
|
|
135
|
+
const TrackedHomeScreen = defineComponent(() => {
|
|
136
|
+
const navigation = useTabNavigation();
|
|
137
|
+
return () => {
|
|
138
|
+
void navigation.value;
|
|
139
|
+
return h('symbiote-text', {}, 'home');
|
|
140
|
+
};
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
expect(() =>
|
|
144
|
+
mount(
|
|
145
|
+
ROOT_TAG,
|
|
146
|
+
defineComponent({
|
|
147
|
+
setup: () => () =>
|
|
148
|
+
h(Stack, { initialRouteName: 'Home' }, () => [
|
|
149
|
+
h(Stack.Screen, { name: 'Home', component: TrackedHomeScreen }),
|
|
150
|
+
h(Stack.Screen, { name: 'Details', component: textScreen('details') }),
|
|
151
|
+
]),
|
|
152
|
+
}),
|
|
153
|
+
),
|
|
154
|
+
).toThrow(/nearest navigator is not a Tab/);
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
describe('useDrawerNavigation', () => {
|
|
159
|
+
it('returns a concretely-typed Drawer handle with openDrawer, no narrowing needed', async () => {
|
|
160
|
+
let canOpenDrawer = false;
|
|
161
|
+
const TrackedHomeScreen = defineComponent(() => {
|
|
162
|
+
const navigation = useDrawerNavigation();
|
|
163
|
+
return () => {
|
|
164
|
+
canOpenDrawer = typeof navigation.value.openDrawer === 'function';
|
|
165
|
+
return h('symbiote-text', {}, 'home');
|
|
166
|
+
};
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
mount(
|
|
170
|
+
ROOT_TAG,
|
|
171
|
+
defineComponent({
|
|
172
|
+
setup: () => () =>
|
|
173
|
+
h(Drawer, { initialRouteName: 'Home' }, () => [
|
|
174
|
+
h(Drawer.Screen, { name: 'Home', component: TrackedHomeScreen }),
|
|
175
|
+
h(Drawer.Screen, { name: 'Profile', component: textScreen('profile') }),
|
|
176
|
+
]),
|
|
177
|
+
}),
|
|
178
|
+
);
|
|
179
|
+
await tick();
|
|
180
|
+
expect(canOpenDrawer).toBe(true);
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
it('throws when the nearest navigator is a Stack, not a Drawer', () => {
|
|
184
|
+
const TrackedHomeScreen = defineComponent(() => {
|
|
185
|
+
const navigation = useDrawerNavigation();
|
|
186
|
+
return () => {
|
|
187
|
+
void navigation.value;
|
|
188
|
+
return h('symbiote-text', {}, 'home');
|
|
189
|
+
};
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
expect(() =>
|
|
193
|
+
mount(
|
|
194
|
+
ROOT_TAG,
|
|
195
|
+
defineComponent({
|
|
196
|
+
setup: () => () =>
|
|
197
|
+
h(Stack, { initialRouteName: 'Home' }, () => [
|
|
198
|
+
h(Stack.Screen, { name: 'Home', component: TrackedHomeScreen }),
|
|
199
|
+
h(Stack.Screen, { name: 'Details', component: textScreen('details') }),
|
|
200
|
+
]),
|
|
201
|
+
}),
|
|
202
|
+
),
|
|
203
|
+
).toThrow(/nearest navigator is not a Drawer/);
|
|
204
|
+
});
|
|
205
|
+
});
|