@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,37 @@
|
|
|
1
|
+
// Angular injection function: runs `effect` while the route is focused and runs its own returned
|
|
2
|
+
// cleanup on blur - exactly a plain effect's contract, just re-armed on every focus/blur pair
|
|
3
|
+
// instead of once on construction - mirrors @react-navigation's useFocusEffect. Named `effect` for
|
|
4
|
+
// API parity with the React/Vue twins, NOT Angular's own `effect()` reactive primitive (this
|
|
5
|
+
// callback re-runs on focus/blur events, not on a signal dependency change) - callers should keep
|
|
6
|
+
// `effect` referentially stable across calls they don't intend to change (this function is called
|
|
7
|
+
// once per component construction, nothing here re-subscribes automatically otherwise).
|
|
8
|
+
|
|
9
|
+
import { DestroyRef, inject } from '@angular/core';
|
|
10
|
+
import { NAVIGATION_EVENT_BLUR, NAVIGATION_EVENT_FOCUS } from '../../core';
|
|
11
|
+
import { requireNavigationContext } from '../navigation-context.service';
|
|
12
|
+
|
|
13
|
+
export type IFocusEffectCallback = () => (() => void) | void;
|
|
14
|
+
|
|
15
|
+
export function injectFocusEffect(effect: IFocusEffectCallback): void {
|
|
16
|
+
const context = requireNavigationContext('injectFocusEffect');
|
|
17
|
+
const destroyRef = inject(DestroyRef);
|
|
18
|
+
|
|
19
|
+
let cleanup: ReturnType<IFocusEffectCallback>;
|
|
20
|
+
|
|
21
|
+
const runEffect = (): void => {
|
|
22
|
+
cleanup = effect();
|
|
23
|
+
};
|
|
24
|
+
const runCleanup = (): void => {
|
|
25
|
+
cleanup?.();
|
|
26
|
+
cleanup = undefined;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const unsubscribeFocus = context.emitter.addListener(NAVIGATION_EVENT_FOCUS, runEffect);
|
|
30
|
+
const unsubscribeBlur = context.emitter.addListener(NAVIGATION_EVENT_BLUR, runCleanup);
|
|
31
|
+
|
|
32
|
+
destroyRef.onDestroy(() => {
|
|
33
|
+
unsubscribeFocus();
|
|
34
|
+
unsubscribeBlur();
|
|
35
|
+
runCleanup();
|
|
36
|
+
});
|
|
37
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// Angular injection function: subscribes to the route's focus/blur pair and returns a Signal of
|
|
2
|
+
// whether it's currently focused - mirrors @react-navigation's useIsFocused. Starts `false` rather
|
|
3
|
+
// than guessing from stack position: a route's emitter only fires 'focus' once the navigator's own
|
|
4
|
+
// appear/mount signal lands (Stack: RNSScreen's native onAppear; Tab/Drawer: the synthesized
|
|
5
|
+
// mount-time emit), so a screen genuinely isn't focused yet at the instant it's constructed.
|
|
6
|
+
|
|
7
|
+
import { DestroyRef, inject, signal, type Signal } from '@angular/core';
|
|
8
|
+
import { NAVIGATION_EVENT_BLUR, NAVIGATION_EVENT_FOCUS } from '../../core';
|
|
9
|
+
import { requireNavigationContext } from '../navigation-context.service';
|
|
10
|
+
|
|
11
|
+
export function injectIsFocused(): Signal<boolean> {
|
|
12
|
+
const context = requireNavigationContext('injectIsFocused');
|
|
13
|
+
const destroyRef = inject(DestroyRef);
|
|
14
|
+
const isFocused = signal(false);
|
|
15
|
+
|
|
16
|
+
const unsubscribeFocus = context.emitter.addListener(NAVIGATION_EVENT_FOCUS, () =>
|
|
17
|
+
isFocused.set(true),
|
|
18
|
+
);
|
|
19
|
+
const unsubscribeBlur = context.emitter.addListener(NAVIGATION_EVENT_BLUR, () =>
|
|
20
|
+
isFocused.set(false),
|
|
21
|
+
);
|
|
22
|
+
destroyRef.onDestroy(() => {
|
|
23
|
+
unsubscribeFocus();
|
|
24
|
+
unsubscribeBlur();
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
return isFocused.asReadonly();
|
|
28
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
// Angular injection function: subscribes to the router's `state` broadcast (stack.ts's `dispatch`
|
|
2
|
+
// re-emits the full INavigatorState to every route's emitter after each change) and returns a
|
|
3
|
+
// Signal of `selector(state)`, updating only when the emitted state changes - mirrors
|
|
4
|
+
// @react-navigation's useNavigationState. The reducer/dispatch machinery it selects over lives in
|
|
5
|
+
// core/navigator-state.ts; this function only wires the subscription.
|
|
6
|
+
//
|
|
7
|
+
// Seeded from a single-route snapshot ({ routes: [route] }) rather than left undefined: the real
|
|
8
|
+
// broadcast lands after the FIRST dispatch, so a selector reading e.g. `state.routes.at(-1)?.name`
|
|
9
|
+
// still resolves correctly before any navigation action for the common single-route case, closing
|
|
10
|
+
// the same async gap injectIsFocused documents.
|
|
11
|
+
|
|
12
|
+
import { DestroyRef, inject, signal, type Signal } from '@angular/core';
|
|
13
|
+
import { NAVIGATION_EVENT_STATE } from '../../core';
|
|
14
|
+
import type { INavigatorState } from '../../core';
|
|
15
|
+
import { requireNavigationContext } from '../navigation-context.service';
|
|
16
|
+
|
|
17
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
18
|
+
return typeof value === 'object' && value !== null;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function isNavigatorState(value: unknown): value is INavigatorState {
|
|
22
|
+
return isRecord(value) && Array.isArray(value.routes);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function injectNavigationState<TResult>(
|
|
26
|
+
selector: (state: INavigatorState) => TResult,
|
|
27
|
+
): Signal<TResult> {
|
|
28
|
+
const context = requireNavigationContext('injectNavigationState');
|
|
29
|
+
const destroyRef = inject(DestroyRef);
|
|
30
|
+
const route = context.route();
|
|
31
|
+
if (route === undefined) {
|
|
32
|
+
throw new Error(
|
|
33
|
+
"injectNavigationState: no route has been assigned to this screen's navigation scope yet",
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const result = signal(selector({ routes: [route] }));
|
|
38
|
+
|
|
39
|
+
const unsubscribe = context.emitter.addListener(NAVIGATION_EVENT_STATE, (state: unknown) => {
|
|
40
|
+
if (!isNavigatorState(state)) return;
|
|
41
|
+
result.set(selector(state));
|
|
42
|
+
});
|
|
43
|
+
destroyRef.onDestroy(unsubscribe);
|
|
44
|
+
|
|
45
|
+
return result.asReadonly();
|
|
46
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// Angular injection function (calls `inject()`, so it must run in an injection context - a
|
|
2
|
+
// component/directive constructor or field initializer, same requirement `inject()` itself has).
|
|
3
|
+
// Returns the current screen's navigator handle (push/pop/replace/... for a Stack screen,
|
|
4
|
+
// jumpTo/setParams for a Tab screen, openDrawer/... for a Drawer screen) plus addListener bound to
|
|
5
|
+
// that route's own emitter - mirrors @react-navigation's `navigation.addListener('focus', cb)`
|
|
6
|
+
// surface. All pub/sub logic lives in ../../core/navigation-events; this function only reads
|
|
7
|
+
// NavigationContextService and binds identity.
|
|
8
|
+
|
|
9
|
+
import type { INavigationEventListener, INavigationEventName } from '../../core';
|
|
10
|
+
import { requireNavigationContext, type IAnyNavigatorHandle } from '../navigation-context.service';
|
|
11
|
+
|
|
12
|
+
export type INavigationHandle = IAnyNavigatorHandle & {
|
|
13
|
+
addListener: (event: INavigationEventName, listener: INavigationEventListener) => () => void;
|
|
14
|
+
// Walks exactly ONE hop up NavigationContextService's `parent` chain to the enclosing
|
|
15
|
+
// navigator's handle (plain immediate-parent walking, no named/targeted ancestor lookup).
|
|
16
|
+
getParent: () => IAnyNavigatorHandle | undefined;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export function injectNavigation(): INavigationHandle {
|
|
20
|
+
const context = requireNavigationContext('injectNavigation');
|
|
21
|
+
return {
|
|
22
|
+
...context.navigation,
|
|
23
|
+
addListener: context.emitter.addListener,
|
|
24
|
+
getParent: () => context.parent?.navigation,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// Angular injection function: returns the current screen's route as a live Signal - mirrors
|
|
2
|
+
// @react-navigation's useRoute, reactive rather than a one-shot read because a setParams call
|
|
3
|
+
// swaps in a new route object (same key) while the screen stays mounted (see
|
|
4
|
+
// navigation-context.service.ts's comment on why `route` is a signal there). Zero logic of its
|
|
5
|
+
// own beyond that signal read.
|
|
6
|
+
|
|
7
|
+
import { computed, type Signal } from '@angular/core';
|
|
8
|
+
import type { IRoute } from '../../core';
|
|
9
|
+
import { requireNavigationContext } from '../navigation-context.service';
|
|
10
|
+
|
|
11
|
+
export function injectRoute(): Signal<IRoute<unknown>> {
|
|
12
|
+
const context = requireNavigationContext('injectRoute');
|
|
13
|
+
// A `computed` (not the raw context.route signal) so the return type narrows to
|
|
14
|
+
// `IRoute<unknown>` structurally - NavigationScopeDirective always assigns a route before any
|
|
15
|
+
// content (and therefore any injectRoute() caller) exists, so this only ever throws on misuse.
|
|
16
|
+
return computed(() => {
|
|
17
|
+
const current = context.route();
|
|
18
|
+
if (current === undefined) {
|
|
19
|
+
throw new Error(
|
|
20
|
+
"injectRoute: no route has been assigned to this screen's navigation scope yet",
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
return current;
|
|
24
|
+
});
|
|
25
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// injectNavigation() 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 injector narrows that union ONCE, here, so call sites never write their own
|
|
4
|
+
// 'push' in navigation check. Angular twin of react/hooks/use-stack-navigation.ts.
|
|
5
|
+
|
|
6
|
+
import type { INavigatorHandle } from '../../core';
|
|
7
|
+
import { isStackNavigatorHandle } from '../../core';
|
|
8
|
+
import { injectNavigation } from './inject-navigation';
|
|
9
|
+
import type { INavigationHandle } from './inject-navigation';
|
|
10
|
+
|
|
11
|
+
export type IStackNavigationHandle = INavigatorHandle &
|
|
12
|
+
Pick<INavigationHandle, 'addListener' | 'getParent'>;
|
|
13
|
+
|
|
14
|
+
export function injectStackNavigation(): IStackNavigationHandle {
|
|
15
|
+
const navigation = injectNavigation();
|
|
16
|
+
if (!isStackNavigatorHandle(navigation)) {
|
|
17
|
+
throw new Error(
|
|
18
|
+
'injectStackNavigation() was called from a component whose nearest navigator is not a ' +
|
|
19
|
+
'Stack. Use injectNavigation() instead if this component can render under more than one ' +
|
|
20
|
+
'navigator kind.',
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
return navigation;
|
|
24
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// Twin of inject-stack-navigation.ts, narrowing to a Tab handle instead.
|
|
2
|
+
|
|
3
|
+
import type { ITabNavigatorHandle } from '../../core';
|
|
4
|
+
import { isTabNavigatorHandle } from '../../core';
|
|
5
|
+
import { injectNavigation } from './inject-navigation';
|
|
6
|
+
import type { INavigationHandle } from './inject-navigation';
|
|
7
|
+
|
|
8
|
+
export type ITabNavigationHandle = ITabNavigatorHandle &
|
|
9
|
+
Pick<INavigationHandle, 'addListener' | 'getParent'>;
|
|
10
|
+
|
|
11
|
+
export function injectTabNavigation(): ITabNavigationHandle {
|
|
12
|
+
const navigation = injectNavigation();
|
|
13
|
+
if (!isTabNavigatorHandle(navigation)) {
|
|
14
|
+
throw new Error(
|
|
15
|
+
'injectTabNavigation() was called from a component whose nearest navigator is not a Tab. ' +
|
|
16
|
+
'Use injectNavigation() instead if this component can render under more than one navigator kind.',
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
return navigation;
|
|
20
|
+
}
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
// Co-located Angular-driven test for the navigation injectors layer (./index). Mirrors
|
|
2
|
+
// ../stack.test.ts's fixture (an injected codegen-shaped RNSScreen ViewConfig exposing
|
|
3
|
+
// onAppear/onDisappear) and drives the same native events stack.ts wires to emit 'focus'/'blur' -
|
|
4
|
+
// proving the injectors react to the real RNS lifecycle, not to a synthetic shortcut. Each
|
|
5
|
+
// injector calls `inject()`, so it is called once per screen component's own constructor (the
|
|
6
|
+
// natural Angular injection-context call site).
|
|
7
|
+
|
|
8
|
+
import '@angular/compiler';
|
|
9
|
+
import {
|
|
10
|
+
Component,
|
|
11
|
+
CUSTOM_ELEMENTS_SCHEMA,
|
|
12
|
+
Input,
|
|
13
|
+
ViewChild,
|
|
14
|
+
type Signal,
|
|
15
|
+
type Type,
|
|
16
|
+
} from '@angular/core';
|
|
17
|
+
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
18
|
+
import { mount, unmount, setNativeViewConfigSource } from '@symbiote-native/angular';
|
|
19
|
+
import type { INativeViewConfig } from '@symbiote-native/engine';
|
|
20
|
+
import { installFabric, type IFakeNode } from '@symbiote-native/test-utils';
|
|
21
|
+
import { Stack } from '../stack';
|
|
22
|
+
import { ScreenDirective } from '../screen.directive';
|
|
23
|
+
import type { IRoute, INavigatorState } from '../../core';
|
|
24
|
+
import {
|
|
25
|
+
injectFocusEffect,
|
|
26
|
+
injectIsFocused,
|
|
27
|
+
injectNavigation,
|
|
28
|
+
injectNavigationState,
|
|
29
|
+
injectRoute,
|
|
30
|
+
} from './index';
|
|
31
|
+
import type { INavigationHandle } from './index';
|
|
32
|
+
|
|
33
|
+
const ROOT_TAG = 5130;
|
|
34
|
+
const SCREEN_VIEW = 'RNSScreen';
|
|
35
|
+
const STACK_VIEW = 'RNSScreenStack';
|
|
36
|
+
const HEADER_CONFIG_VIEW = 'RNSScreenStackHeaderConfig';
|
|
37
|
+
|
|
38
|
+
function directEvent(registrationName: string): { registrationName: string } {
|
|
39
|
+
return { registrationName };
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const VIEW_CONFIGS: Record<string, INativeViewConfig> = {
|
|
43
|
+
[SCREEN_VIEW]: {
|
|
44
|
+
directEventTypes: {
|
|
45
|
+
topAppear: directEvent('onAppear'),
|
|
46
|
+
topDisappear: directEvent('onDisappear'),
|
|
47
|
+
},
|
|
48
|
+
validAttributes: { screenId: true, activityState: true },
|
|
49
|
+
},
|
|
50
|
+
[STACK_VIEW]: { directEventTypes: {}, validAttributes: {} },
|
|
51
|
+
[HEADER_CONFIG_VIEW]: { directEventTypes: {}, validAttributes: {} },
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const fabric = installFabric();
|
|
55
|
+
setNativeViewConfigSource(name => VIEW_CONFIGS[name]);
|
|
56
|
+
|
|
57
|
+
const tick = (): Promise<void> => new Promise(resolve => setTimeout(resolve, 0));
|
|
58
|
+
|
|
59
|
+
beforeEach(() => fabric.reset());
|
|
60
|
+
afterEach(() => unmount(ROOT_TAG));
|
|
61
|
+
|
|
62
|
+
function screenNodes(): IFakeNode[] {
|
|
63
|
+
const found: IFakeNode[] = [];
|
|
64
|
+
const collect = (nodes: readonly IFakeNode[]): void => {
|
|
65
|
+
for (const node of nodes) {
|
|
66
|
+
if (node.viewName === SCREEN_VIEW) found.push(node);
|
|
67
|
+
collect(node.children);
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
collect(fabric.committed);
|
|
71
|
+
return found;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
@Component({
|
|
75
|
+
selector: 'plain-screen',
|
|
76
|
+
standalone: true,
|
|
77
|
+
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
|
78
|
+
template: `<symbiote-text>plain</symbiote-text>`,
|
|
79
|
+
})
|
|
80
|
+
class PlainScreenComponent {}
|
|
81
|
+
|
|
82
|
+
let capturedIsFocused: Signal<boolean> | undefined;
|
|
83
|
+
|
|
84
|
+
@Component({
|
|
85
|
+
selector: 'is-focused-screen',
|
|
86
|
+
standalone: true,
|
|
87
|
+
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
|
88
|
+
template: `<symbiote-text>home</symbiote-text>`,
|
|
89
|
+
})
|
|
90
|
+
class IsFocusedScreenComponent {
|
|
91
|
+
constructor() {
|
|
92
|
+
capturedIsFocused = injectIsFocused();
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const focusEffectEvents: string[] = [];
|
|
97
|
+
|
|
98
|
+
@Component({
|
|
99
|
+
selector: 'focus-effect-screen',
|
|
100
|
+
standalone: true,
|
|
101
|
+
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
|
102
|
+
template: `<symbiote-text>home</symbiote-text>`,
|
|
103
|
+
})
|
|
104
|
+
class FocusEffectScreenComponent {
|
|
105
|
+
constructor() {
|
|
106
|
+
injectFocusEffect(() => {
|
|
107
|
+
focusEffectEvents.push('effect');
|
|
108
|
+
return () => focusEffectEvents.push('cleanup');
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
let capturedNavigation: INavigationHandle | undefined;
|
|
114
|
+
let capturedRoute: Signal<IRoute<unknown>> | undefined;
|
|
115
|
+
const focusListenerEvents: string[] = [];
|
|
116
|
+
|
|
117
|
+
@Component({
|
|
118
|
+
selector: 'navigation-route-screen',
|
|
119
|
+
standalone: true,
|
|
120
|
+
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
|
121
|
+
template: `<symbiote-text>details</symbiote-text>`,
|
|
122
|
+
})
|
|
123
|
+
class NavigationRouteScreenComponent {
|
|
124
|
+
constructor() {
|
|
125
|
+
capturedNavigation = injectNavigation();
|
|
126
|
+
capturedRoute = injectRoute();
|
|
127
|
+
capturedNavigation.addListener('focus', () => focusListenerEvents.push('focus'));
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
let capturedRouteCount: Signal<number> | undefined;
|
|
132
|
+
|
|
133
|
+
@Component({
|
|
134
|
+
selector: 'navigation-state-screen',
|
|
135
|
+
standalone: true,
|
|
136
|
+
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
|
137
|
+
template: `<symbiote-text>home</symbiote-text>`,
|
|
138
|
+
})
|
|
139
|
+
class NavigationStateScreenComponent {
|
|
140
|
+
constructor() {
|
|
141
|
+
capturedRouteCount = injectNavigationState((state: INavigatorState) => state.routes.length);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
let capturedHost: InjectorsTestHost | undefined;
|
|
146
|
+
|
|
147
|
+
@Component({
|
|
148
|
+
selector: 'injectors-test-host',
|
|
149
|
+
standalone: true,
|
|
150
|
+
imports: [Stack, ScreenDirective],
|
|
151
|
+
template: `
|
|
152
|
+
<Stack #nav initialRouteName="Home">
|
|
153
|
+
<ng-template symbioteScreen name="Home" [component]="homeComponent"></ng-template>
|
|
154
|
+
<ng-template symbioteScreen name="Details" [component]="detailsComponent"></ng-template>
|
|
155
|
+
</Stack>
|
|
156
|
+
`,
|
|
157
|
+
})
|
|
158
|
+
class InjectorsTestHost {
|
|
159
|
+
@ViewChild('nav') nav!: Stack;
|
|
160
|
+
|
|
161
|
+
@Input() homeComponent: Type<unknown> = PlainScreenComponent;
|
|
162
|
+
@Input() detailsComponent: Type<unknown> = PlainScreenComponent;
|
|
163
|
+
|
|
164
|
+
constructor() {
|
|
165
|
+
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
166
|
+
capturedHost = this;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
describe('Angular navigation injectors', () => {
|
|
171
|
+
it("injectIsFocused reflects the route's native appear/disappear events", async () => {
|
|
172
|
+
capturedIsFocused = undefined;
|
|
173
|
+
capturedHost = undefined;
|
|
174
|
+
mount(ROOT_TAG, InjectorsTestHost, {
|
|
175
|
+
initialProps: { homeComponent: IsFocusedScreenComponent },
|
|
176
|
+
});
|
|
177
|
+
await tick();
|
|
178
|
+
expect(capturedIsFocused?.()).toBe(false);
|
|
179
|
+
|
|
180
|
+
const home = screenNodes()[0];
|
|
181
|
+
fabric.fireEvent(home.instanceHandle, 'topAppear', {});
|
|
182
|
+
await tick();
|
|
183
|
+
expect(capturedIsFocused?.()).toBe(true);
|
|
184
|
+
|
|
185
|
+
fabric.fireEvent(home.instanceHandle, 'topDisappear', {});
|
|
186
|
+
await tick();
|
|
187
|
+
expect(capturedIsFocused?.()).toBe(false);
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
it('injectFocusEffect runs its effect on focus and its cleanup on blur', async () => {
|
|
191
|
+
focusEffectEvents.length = 0;
|
|
192
|
+
capturedHost = undefined;
|
|
193
|
+
mount(ROOT_TAG, InjectorsTestHost, {
|
|
194
|
+
initialProps: { homeComponent: FocusEffectScreenComponent },
|
|
195
|
+
});
|
|
196
|
+
await tick();
|
|
197
|
+
expect(focusEffectEvents).toEqual([]);
|
|
198
|
+
|
|
199
|
+
const home = screenNodes()[0];
|
|
200
|
+
fabric.fireEvent(home.instanceHandle, 'topAppear', {});
|
|
201
|
+
await tick();
|
|
202
|
+
expect(focusEffectEvents).toEqual(['effect']);
|
|
203
|
+
|
|
204
|
+
fabric.fireEvent(home.instanceHandle, 'topDisappear', {});
|
|
205
|
+
await tick();
|
|
206
|
+
expect(focusEffectEvents).toEqual(['effect', 'cleanup']);
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
it('injectNavigation().addListener fires on focus and injectRoute exposes name/params', async () => {
|
|
210
|
+
capturedNavigation = undefined;
|
|
211
|
+
capturedRoute = undefined;
|
|
212
|
+
focusListenerEvents.length = 0;
|
|
213
|
+
capturedHost = undefined;
|
|
214
|
+
mount(ROOT_TAG, InjectorsTestHost, {
|
|
215
|
+
initialProps: { detailsComponent: NavigationRouteScreenComponent },
|
|
216
|
+
});
|
|
217
|
+
await tick();
|
|
218
|
+
|
|
219
|
+
capturedHost!.nav.push('Details', { id: 7 });
|
|
220
|
+
await tick();
|
|
221
|
+
expect(capturedRoute?.().name).toBe('Details');
|
|
222
|
+
expect(capturedRoute?.().params).toEqual({ id: 7 });
|
|
223
|
+
|
|
224
|
+
const details = screenNodes()[1];
|
|
225
|
+
fabric.fireEvent(details.instanceHandle, 'topAppear', {});
|
|
226
|
+
await tick();
|
|
227
|
+
expect(focusListenerEvents).toEqual(['focus']);
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
it('injectNavigationState reflects the route stack growing/shrinking across push/pop', async () => {
|
|
231
|
+
capturedRouteCount = undefined;
|
|
232
|
+
capturedHost = undefined;
|
|
233
|
+
mount(ROOT_TAG, InjectorsTestHost, {
|
|
234
|
+
initialProps: { homeComponent: NavigationStateScreenComponent },
|
|
235
|
+
});
|
|
236
|
+
await tick();
|
|
237
|
+
expect(capturedRouteCount?.()).toBe(1);
|
|
238
|
+
|
|
239
|
+
capturedHost!.nav.push('Details');
|
|
240
|
+
await tick();
|
|
241
|
+
expect(capturedRouteCount?.()).toBe(2);
|
|
242
|
+
|
|
243
|
+
capturedHost!.nav.pop();
|
|
244
|
+
await tick();
|
|
245
|
+
expect(capturedRouteCount?.()).toBe(1);
|
|
246
|
+
});
|
|
247
|
+
});
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
// Co-located Angular-driven test for injectStackNavigation/injectTabNavigation/
|
|
2
|
+
// injectDrawerNavigation - the narrowed twins of injectNavigation() that hide the union guard.
|
|
3
|
+
// Angular twin of react/hooks/use-typed-navigation.test.tsx.
|
|
4
|
+
|
|
5
|
+
import '@angular/compiler';
|
|
6
|
+
import { Component, CUSTOM_ELEMENTS_SCHEMA, Input } from '@angular/core';
|
|
7
|
+
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
8
|
+
import { mount, unmount, setNativeViewConfigSource, Dimensions } from '@symbiote-native/angular';
|
|
9
|
+
import type { INativeViewConfig } from '@symbiote-native/engine';
|
|
10
|
+
import { installFabric } from '@symbiote-native/test-utils';
|
|
11
|
+
import { Stack } from '../stack';
|
|
12
|
+
import { ScreenDirective } from '../screen.directive';
|
|
13
|
+
import { Tab } from '../tabs';
|
|
14
|
+
import { TabScreenDirective } from '../tab-screen.directive';
|
|
15
|
+
import { Drawer } from '../drawer';
|
|
16
|
+
import { DrawerScreenDirective } from '../drawer-screen.directive';
|
|
17
|
+
import { injectDrawerNavigation, injectStackNavigation, injectTabNavigation } from './index';
|
|
18
|
+
|
|
19
|
+
const ROOT_TAG = 5613;
|
|
20
|
+
const SCREEN_VIEW = 'RNSScreen';
|
|
21
|
+
const STACK_VIEW = 'RNSScreenStack';
|
|
22
|
+
|
|
23
|
+
const VIEW_CONFIGS: Record<string, INativeViewConfig> = {
|
|
24
|
+
[SCREEN_VIEW]: {
|
|
25
|
+
directEventTypes: {
|
|
26
|
+
topAppear: { registrationName: 'onAppear' },
|
|
27
|
+
topDisappear: { registrationName: 'onDisappear' },
|
|
28
|
+
},
|
|
29
|
+
validAttributes: { screenId: true, activityState: true },
|
|
30
|
+
},
|
|
31
|
+
[STACK_VIEW]: { directEventTypes: {}, validAttributes: {} },
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
// Drawer reads the screen width off WindowDimensionsService; headless has no DeviceInfo native
|
|
35
|
+
// module, so seed a concrete width once - same fixture as drawer.test.ts.
|
|
36
|
+
Dimensions.set({ window: { width: 375, height: 812, scale: 1, fontScale: 1 } });
|
|
37
|
+
|
|
38
|
+
const fabric = installFabric();
|
|
39
|
+
setNativeViewConfigSource(name => VIEW_CONFIGS[name]);
|
|
40
|
+
const tick = (): Promise<void> => new Promise(resolve => setTimeout(resolve, 0));
|
|
41
|
+
|
|
42
|
+
beforeEach(() => fabric.reset());
|
|
43
|
+
afterEach(() => unmount(ROOT_TAG));
|
|
44
|
+
|
|
45
|
+
@Component({
|
|
46
|
+
selector: 'plain-screen',
|
|
47
|
+
standalone: true,
|
|
48
|
+
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
|
49
|
+
template: `<symbiote-text>plain</symbiote-text>`,
|
|
50
|
+
})
|
|
51
|
+
class PlainScreenComponent {}
|
|
52
|
+
|
|
53
|
+
let canPush = false;
|
|
54
|
+
|
|
55
|
+
@Component({
|
|
56
|
+
selector: 'stack-tracked-screen',
|
|
57
|
+
standalone: true,
|
|
58
|
+
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
|
59
|
+
template: `<symbiote-text>home</symbiote-text>`,
|
|
60
|
+
})
|
|
61
|
+
class StackTrackedScreenComponent {
|
|
62
|
+
constructor() {
|
|
63
|
+
const navigation = injectStackNavigation();
|
|
64
|
+
canPush = typeof navigation.push === 'function';
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
@Component({
|
|
69
|
+
selector: 'stack-throwing-screen',
|
|
70
|
+
standalone: true,
|
|
71
|
+
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
|
72
|
+
template: `<symbiote-text>home</symbiote-text>`,
|
|
73
|
+
})
|
|
74
|
+
class StackThrowingScreenComponent {
|
|
75
|
+
constructor() {
|
|
76
|
+
injectStackNavigation();
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
let canJumpTo = false;
|
|
81
|
+
|
|
82
|
+
@Component({
|
|
83
|
+
selector: 'tab-tracked-screen',
|
|
84
|
+
standalone: true,
|
|
85
|
+
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
|
86
|
+
template: `<symbiote-text>home</symbiote-text>`,
|
|
87
|
+
})
|
|
88
|
+
class TabTrackedScreenComponent {
|
|
89
|
+
constructor() {
|
|
90
|
+
const navigation = injectTabNavigation();
|
|
91
|
+
canJumpTo = typeof navigation.jumpTo === 'function';
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
@Component({
|
|
96
|
+
selector: 'tab-throwing-screen',
|
|
97
|
+
standalone: true,
|
|
98
|
+
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
|
99
|
+
template: `<symbiote-text>home</symbiote-text>`,
|
|
100
|
+
})
|
|
101
|
+
class TabThrowingScreenComponent {
|
|
102
|
+
constructor() {
|
|
103
|
+
injectTabNavigation();
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
let canOpenDrawer = false;
|
|
108
|
+
|
|
109
|
+
@Component({
|
|
110
|
+
selector: 'drawer-tracked-screen',
|
|
111
|
+
standalone: true,
|
|
112
|
+
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
|
113
|
+
template: `<symbiote-text>home</symbiote-text>`,
|
|
114
|
+
})
|
|
115
|
+
class DrawerTrackedScreenComponent {
|
|
116
|
+
constructor() {
|
|
117
|
+
const navigation = injectDrawerNavigation();
|
|
118
|
+
canOpenDrawer = typeof navigation.openDrawer === 'function';
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
@Component({
|
|
123
|
+
selector: 'drawer-throwing-screen',
|
|
124
|
+
standalone: true,
|
|
125
|
+
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
|
126
|
+
template: `<symbiote-text>home</symbiote-text>`,
|
|
127
|
+
})
|
|
128
|
+
class DrawerThrowingScreenComponent {
|
|
129
|
+
constructor() {
|
|
130
|
+
injectDrawerNavigation();
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
@Component({
|
|
135
|
+
selector: 'stack-host',
|
|
136
|
+
standalone: true,
|
|
137
|
+
imports: [Stack, ScreenDirective],
|
|
138
|
+
template: `
|
|
139
|
+
<Stack initialRouteName="Home">
|
|
140
|
+
<ng-template symbioteScreen name="Home" [component]="homeComponent"></ng-template>
|
|
141
|
+
<ng-template symbioteScreen name="Details" [component]="plainComponent"></ng-template>
|
|
142
|
+
</Stack>
|
|
143
|
+
`,
|
|
144
|
+
})
|
|
145
|
+
class StackHost {
|
|
146
|
+
@Input() homeComponent: unknown = PlainScreenComponent;
|
|
147
|
+
readonly plainComponent = PlainScreenComponent;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
@Component({
|
|
151
|
+
selector: 'tab-host',
|
|
152
|
+
standalone: true,
|
|
153
|
+
imports: [Tab, TabScreenDirective],
|
|
154
|
+
template: `
|
|
155
|
+
<Tab initialRouteName="Home">
|
|
156
|
+
<ng-template symbioteTabScreen name="Home" [component]="homeComponent"></ng-template>
|
|
157
|
+
<ng-template symbioteTabScreen name="Search" [component]="plainComponent"></ng-template>
|
|
158
|
+
</Tab>
|
|
159
|
+
`,
|
|
160
|
+
})
|
|
161
|
+
class TabHost {
|
|
162
|
+
@Input() homeComponent: unknown = PlainScreenComponent;
|
|
163
|
+
readonly plainComponent = PlainScreenComponent;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
@Component({
|
|
167
|
+
selector: 'drawer-host',
|
|
168
|
+
standalone: true,
|
|
169
|
+
imports: [Drawer, DrawerScreenDirective],
|
|
170
|
+
template: `
|
|
171
|
+
<Drawer initialRouteName="Home">
|
|
172
|
+
<ng-template symbioteDrawerScreen name="Home" [component]="homeComponent"></ng-template>
|
|
173
|
+
<ng-template symbioteDrawerScreen name="Profile" [component]="plainComponent"></ng-template>
|
|
174
|
+
</Drawer>
|
|
175
|
+
`,
|
|
176
|
+
})
|
|
177
|
+
class DrawerHost {
|
|
178
|
+
@Input() homeComponent: unknown = PlainScreenComponent;
|
|
179
|
+
readonly plainComponent = PlainScreenComponent;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
describe('injectStackNavigation', () => {
|
|
183
|
+
it('returns a concretely-typed Stack handle with push, no narrowing needed', async () => {
|
|
184
|
+
canPush = false;
|
|
185
|
+
mount(ROOT_TAG, StackHost, { initialProps: { homeComponent: StackTrackedScreenComponent } });
|
|
186
|
+
await tick();
|
|
187
|
+
expect(canPush).toBe(true);
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
it('throws when the nearest navigator is a Tab, not a Stack', () => {
|
|
191
|
+
expect(() =>
|
|
192
|
+
mount(ROOT_TAG, TabHost, { initialProps: { homeComponent: StackThrowingScreenComponent } }),
|
|
193
|
+
).toThrow(/nearest navigator is not a Stack/);
|
|
194
|
+
});
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
describe('injectTabNavigation', () => {
|
|
198
|
+
it('returns a concretely-typed Tab handle with jumpTo, no narrowing needed', async () => {
|
|
199
|
+
canJumpTo = false;
|
|
200
|
+
mount(ROOT_TAG, TabHost, { initialProps: { homeComponent: TabTrackedScreenComponent } });
|
|
201
|
+
await tick();
|
|
202
|
+
expect(canJumpTo).toBe(true);
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
it('throws when the nearest navigator is a Stack, not a Tab', () => {
|
|
206
|
+
expect(() =>
|
|
207
|
+
mount(ROOT_TAG, StackHost, { initialProps: { homeComponent: TabThrowingScreenComponent } }),
|
|
208
|
+
).toThrow(/nearest navigator is not a Tab/);
|
|
209
|
+
});
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
describe('injectDrawerNavigation', () => {
|
|
213
|
+
it('returns a concretely-typed Drawer handle with openDrawer, no narrowing needed', async () => {
|
|
214
|
+
canOpenDrawer = false;
|
|
215
|
+
mount(ROOT_TAG, DrawerHost, {
|
|
216
|
+
initialProps: { homeComponent: DrawerTrackedScreenComponent },
|
|
217
|
+
});
|
|
218
|
+
await tick();
|
|
219
|
+
expect(canOpenDrawer).toBe(true);
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
it('throws when the nearest navigator is a Stack, not a Drawer', () => {
|
|
223
|
+
expect(() =>
|
|
224
|
+
mount(ROOT_TAG, StackHost, {
|
|
225
|
+
initialProps: { homeComponent: DrawerThrowingScreenComponent },
|
|
226
|
+
}),
|
|
227
|
+
).toThrow(/nearest navigator is not a Drawer/);
|
|
228
|
+
});
|
|
229
|
+
});
|