@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,145 @@
|
|
|
1
|
+
// Co-located React-driven test (ADR 0025) proving the Context "parent" chain: a navigator reads
|
|
2
|
+
// the ambient NavigationContext BEFORE establishing its own, so when one navigator's screen
|
|
3
|
+
// renders ANOTHER navigator (here, a Stack screen renders a Tab), a screen deep inside the nested
|
|
4
|
+
// Tab can call useNavigation().getParent() to reach the enclosing Stack's handle. Mirrors
|
|
5
|
+
// stack.test.tsx's fixture (an injected codegen-shaped RNSScreen ViewConfig) since a real Stack is
|
|
6
|
+
// part of this composition; Tab needs no ViewConfig of its own (tabs.test.tsx).
|
|
7
|
+
|
|
8
|
+
import { act, createElement, createRef } from 'react';
|
|
9
|
+
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
10
|
+
import { mount, unmount, setNativeViewConfigSource } from '@symbiote-native/react';
|
|
11
|
+
import type { INativeViewConfig } from '@symbiote-native/engine';
|
|
12
|
+
import { installFabric, type IFakeNode } from '@symbiote-native/test-utils';
|
|
13
|
+
import { Stack } from './stack';
|
|
14
|
+
import type { INavigatorHandle } from './stack';
|
|
15
|
+
import { Tab } from './tabs';
|
|
16
|
+
import { useNavigation } from './hooks';
|
|
17
|
+
import type { IAnyNavigatorHandle } from './navigation-context';
|
|
18
|
+
|
|
19
|
+
const ROOT_TAG = 768;
|
|
20
|
+
const SCREEN_VIEW = 'RNSScreen';
|
|
21
|
+
const STACK_VIEW = 'RNSScreenStack';
|
|
22
|
+
const HEADER_CONFIG_VIEW = 'RNSScreenStackHeaderConfig';
|
|
23
|
+
|
|
24
|
+
function directEvent(registrationName: string) {
|
|
25
|
+
return { registrationName };
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const VIEW_CONFIGS: Record<string, INativeViewConfig> = {
|
|
29
|
+
[SCREEN_VIEW]: {
|
|
30
|
+
directEventTypes: {
|
|
31
|
+
topAppear: directEvent('onAppear'),
|
|
32
|
+
topDisappear: directEvent('onDisappear'),
|
|
33
|
+
topWillAppear: directEvent('onWillAppear'),
|
|
34
|
+
topWillDisappear: directEvent('onWillDisappear'),
|
|
35
|
+
topDismissed: directEvent('onDismissed'),
|
|
36
|
+
topHeaderBackButtonClicked: directEvent('onHeaderBackButtonClicked'),
|
|
37
|
+
},
|
|
38
|
+
validAttributes: {
|
|
39
|
+
screenId: true,
|
|
40
|
+
activityState: true,
|
|
41
|
+
gestureEnabled: true,
|
|
42
|
+
stackAnimation: true,
|
|
43
|
+
stackPresentation: true,
|
|
44
|
+
transitionDuration: true,
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
[STACK_VIEW]: {
|
|
48
|
+
directEventTypes: { topFinishTransitioning: directEvent('onFinishTransitioning') },
|
|
49
|
+
validAttributes: {},
|
|
50
|
+
},
|
|
51
|
+
[HEADER_CONFIG_VIEW]: {
|
|
52
|
+
directEventTypes: { topPressHeaderBarButtonItem: directEvent('onPressHeaderBarButtonItem') },
|
|
53
|
+
validAttributes: { title: true, hidden: true, backTitle: true, backTitleVisible: true },
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const fabric = installFabric();
|
|
58
|
+
setNativeViewConfigSource(name => VIEW_CONFIGS[name]);
|
|
59
|
+
|
|
60
|
+
beforeEach(() => fabric.reset());
|
|
61
|
+
afterEach(() => unmount(ROOT_TAG));
|
|
62
|
+
|
|
63
|
+
function findAllText(nodes: readonly IFakeNode[]): string[] {
|
|
64
|
+
const found: string[] = [];
|
|
65
|
+
const collect = (list: readonly IFakeNode[]): void => {
|
|
66
|
+
for (const node of list) {
|
|
67
|
+
if (node.viewName === 'RCTRawText' && typeof node.props.text === 'string') {
|
|
68
|
+
found.push(node.props.text);
|
|
69
|
+
}
|
|
70
|
+
collect(node.children);
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
collect(nodes);
|
|
74
|
+
return found;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function StackDetailsScreen(): ReturnType<typeof createElement> {
|
|
78
|
+
return createElement('symbiote-text', {}, 'stack-details');
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
describe('nested navigators (Context parent chain)', () => {
|
|
82
|
+
it("a root Stack screen's useNavigation().getParent() is undefined (no ambient navigator above it)", () => {
|
|
83
|
+
let capturedParent: IAnyNavigatorHandle | undefined;
|
|
84
|
+
let getParentCalled = false;
|
|
85
|
+
function RootScreen(): ReturnType<typeof createElement> {
|
|
86
|
+
capturedParent = useNavigation().getParent();
|
|
87
|
+
getParentCalled = true;
|
|
88
|
+
return createElement('symbiote-text', {}, 'root');
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
mount(
|
|
92
|
+
ROOT_TAG,
|
|
93
|
+
createElement(
|
|
94
|
+
Stack,
|
|
95
|
+
{ initialRouteName: 'Root' },
|
|
96
|
+
createElement(Stack.Screen, { name: 'Root', component: RootScreen }),
|
|
97
|
+
),
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
expect(getParentCalled).toBe(true);
|
|
101
|
+
expect(capturedParent).toBeUndefined();
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it('useNavigation().getParent() from inside a Tab screen nested in a Stack screen reaches the enclosing Stack, and pushing through it adds a Stack route', () => {
|
|
105
|
+
let capturedParent: IAnyNavigatorHandle | undefined;
|
|
106
|
+
|
|
107
|
+
function NestedTabHomeScreen(): ReturnType<typeof createElement> {
|
|
108
|
+
capturedParent = useNavigation().getParent();
|
|
109
|
+
return createElement('symbiote-text', {}, 'tab-home');
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// The Stack screen's own component: a Tab navigator, nested exactly the way a real app
|
|
113
|
+
// composes navigators (a Stack screen's content IS another navigator).
|
|
114
|
+
function RootScreenRendersTab(): ReturnType<typeof createElement> {
|
|
115
|
+
return createElement(
|
|
116
|
+
Tab,
|
|
117
|
+
{ initialRouteName: 'TabHome' },
|
|
118
|
+
createElement(Tab.Screen, { name: 'TabHome', component: NestedTabHomeScreen }),
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const ref = createRef<INavigatorHandle>();
|
|
123
|
+
mount(
|
|
124
|
+
ROOT_TAG,
|
|
125
|
+
createElement(
|
|
126
|
+
Stack,
|
|
127
|
+
{ ref, initialRouteName: 'Root' },
|
|
128
|
+
createElement(Stack.Screen, { name: 'Root', component: RootScreenRendersTab }),
|
|
129
|
+
createElement(Stack.Screen, { name: 'Details', component: StackDetailsScreen }),
|
|
130
|
+
),
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
expect(findAllText(fabric.committed)).toContain('tab-home');
|
|
134
|
+
|
|
135
|
+
if (!capturedParent) throw new Error('getParent() returned undefined');
|
|
136
|
+
if (!('push' in capturedParent)) {
|
|
137
|
+
throw new Error('parent handle is not a Stack handle (missing push)');
|
|
138
|
+
}
|
|
139
|
+
// capturedParent is now narrowed to INavigatorHandle by the 'push in' guard above.
|
|
140
|
+
act(() => capturedParent.push('Details'));
|
|
141
|
+
|
|
142
|
+
expect(findAllText(fabric.committed)).toContain('stack-details');
|
|
143
|
+
expect(ref.current?.canGoBack()).toBe(true);
|
|
144
|
+
});
|
|
145
|
+
});
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
// Stack.Screen: a declarative marker, never rendered on its own - Stack reads its props via
|
|
2
|
+
// React.Children to build the static name -> {component, options} registry, then mounts the
|
|
3
|
+
// registered component itself for each pushed route. Mirrors @react-navigation's JSX-config
|
|
4
|
+
// screens, minus the linking/param-list generics (v1 scope).
|
|
5
|
+
|
|
6
|
+
import type { FC, ReactElement, RefObject } from 'react';
|
|
7
|
+
import type { IRoute } from '../core';
|
|
8
|
+
import type { ISearchBarCommands, ISearchBarOptions, IScreenOptions } from '../core';
|
|
9
|
+
import type { INavigatorHandle } from '../core';
|
|
10
|
+
|
|
11
|
+
// The imperative ref (focus/blur/clearText/setText/cancelSearch/toggleCancelButton) carries a
|
|
12
|
+
// React ref type, so - per CLAUDE.md's <prop_types_split_agnostic_vs_per_adapter> - it cannot
|
|
13
|
+
// live in the shared, agnostic ISearchBarOptions (core/navigator-props.ts). This adapter-only
|
|
14
|
+
// overlay adds it on top of the agnostic field base, mirroring react-native-screens' own
|
|
15
|
+
// SearchBarProps.ref: React.RefObject<SearchBarCommands | null>.
|
|
16
|
+
export type IReactSearchBarOptions = ISearchBarOptions & {
|
|
17
|
+
ref?: RefObject<ISearchBarCommands | null>;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export type IReactScreenOptions = Omit<IScreenOptions, 'headerSearchBarOptions'> & {
|
|
21
|
+
headerSearchBarOptions?: IReactSearchBarOptions;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
// The options resolver runs INSIDE the Stack while it computes a screen's options - its
|
|
25
|
+
// bar-button/menu onPress handlers close over the live navigator handle. This is NOT the mounted
|
|
26
|
+
// screen component (that one reads navigation/route via the hooks); it's a config callback with no
|
|
27
|
+
// lifecycle, so hooks aren't available to it and the handle arrives as an argument instead.
|
|
28
|
+
export type IScreenOptionsArgs = {
|
|
29
|
+
route: IRoute<unknown>;
|
|
30
|
+
navigation: INavigatorHandle;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export type IScreenOptionsResolver = (args: IScreenOptionsArgs) => IReactScreenOptions;
|
|
34
|
+
|
|
35
|
+
export type IScreenProps = {
|
|
36
|
+
name: string;
|
|
37
|
+
component: FC;
|
|
38
|
+
options?: IReactScreenOptions | IScreenOptionsResolver;
|
|
39
|
+
initialParams?: unknown;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export function Screen(_props: IScreenProps): ReactElement | null {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
// Stack, the React lifecycle half. The route-stack transitions (navigator-state) and the
|
|
2
|
+
// options/props folds (screen-options, render-stack) live in @symbiote-native/navigation core,
|
|
3
|
+
// shared verbatim with the Vue/Angular adapters; here React supplies the lifecycle - useReducer
|
|
4
|
+
// for the pushed-route stack, useId + a ref counter for route-key generation, useImperativeHandle
|
|
5
|
+
// for the push/pop/replace navigator handle - plus the descriptor bridge for the header config
|
|
6
|
+
// leaf. Pushing/popping a route is an ordinary child mount/unmount: RNSScreenStack diffs its
|
|
7
|
+
// RNSScreen children natively, so no imperative native command is needed here at all. Neither
|
|
8
|
+
// this nor the Screen marker imports react-native-screens' own React components (ScreenStack.tsx
|
|
9
|
+
// et al - hooks, crashes a non-React adapter); the native views are driven directly through the
|
|
10
|
+
// ViewConfig ../register registers. See CLAUDE.md <third_party_rn_packages_are_react_only>.
|
|
11
|
+
|
|
12
|
+
import {
|
|
13
|
+
createElement,
|
|
14
|
+
forwardRef,
|
|
15
|
+
isValidElement,
|
|
16
|
+
useCallback,
|
|
17
|
+
useContext,
|
|
18
|
+
useEffect,
|
|
19
|
+
useId,
|
|
20
|
+
useImperativeHandle,
|
|
21
|
+
useMemo,
|
|
22
|
+
useReducer,
|
|
23
|
+
useRef,
|
|
24
|
+
} from 'react';
|
|
25
|
+
import type { ReactElement, ReactNode } from 'react';
|
|
26
|
+
import { descriptorToReact } from '@symbiote-native/react';
|
|
27
|
+
import { Platform, debugNodeId, dlog } from '@symbiote-native/engine';
|
|
28
|
+
import type { ISymbioteNode } from '@symbiote-native/engine';
|
|
29
|
+
import {
|
|
30
|
+
NAVIGATION_EVENT_BLUR,
|
|
31
|
+
NAVIGATION_EVENT_FOCUS,
|
|
32
|
+
NAVIGATION_EVENT_STATE,
|
|
33
|
+
RNS_SCREEN_CONTENT_WRAPPER_VIEW_NAME,
|
|
34
|
+
RNS_SCREEN_STACK_VIEW_NAME,
|
|
35
|
+
RNS_SCREEN_VIEW_NAME,
|
|
36
|
+
SCREEN_ON_APPEAR,
|
|
37
|
+
SCREEN_ON_DISAPPEAR,
|
|
38
|
+
SCREEN_ON_DISMISSED,
|
|
39
|
+
SCREEN_ON_HEADER_BACK_BUTTON_CLICKED,
|
|
40
|
+
SCREEN_ON_WILL_APPEAR,
|
|
41
|
+
SCREEN_ON_WILL_DISAPPEAR,
|
|
42
|
+
STACK_ON_FINISH_TRANSITIONING,
|
|
43
|
+
buildSearchBarHandle,
|
|
44
|
+
buildSearchBarPassthrough,
|
|
45
|
+
computeActivityState,
|
|
46
|
+
createInitialNavigatorState,
|
|
47
|
+
createNavigationEmitter,
|
|
48
|
+
navigatorReducer,
|
|
49
|
+
resolveScreenRenderPlan,
|
|
50
|
+
resolveStackProps,
|
|
51
|
+
} from '../../core';
|
|
52
|
+
import type { INavigationEmitter, INavigatorHandle, INavigatorPlatform, IRoute } from '../../core';
|
|
53
|
+
import { collectRegistry } from '../collect-registry';
|
|
54
|
+
import { NavigationContext } from '../navigation-context';
|
|
55
|
+
import { Screen } from '../screen';
|
|
56
|
+
import type { IReactScreenOptions, IScreenOptionsArgs, IScreenProps } from '../screen';
|
|
57
|
+
|
|
58
|
+
export type { INavigatorHandle } from '../../core';
|
|
59
|
+
|
|
60
|
+
export type IStackProps = {
|
|
61
|
+
initialRouteName?: string;
|
|
62
|
+
screenOptions?: IReactScreenOptions;
|
|
63
|
+
children?: ReactNode;
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
// backTitleVisible defaults to `true` on both platforms per the codegen spec's own default
|
|
67
|
+
// (CT.WithDefault<boolean, 'true'>) - no ios/android divergence in v1 scope, so a single constant
|
|
68
|
+
// stands in for the per-platform injection point ISliderPlatform-style adapters use elsewhere.
|
|
69
|
+
const NAVIGATOR_PLATFORM: INavigatorPlatform = { defaultHeaderBackTitleVisible: true };
|
|
70
|
+
|
|
71
|
+
type IScreenRegistryEntry = Omit<IScreenProps, 'name'>;
|
|
72
|
+
|
|
73
|
+
function isScreenElement(child: ReactNode): child is ReactElement<IScreenProps> {
|
|
74
|
+
return isValidElement(child) && child.type === Screen;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function resolveScreenOptions(
|
|
78
|
+
entry: IScreenRegistryEntry,
|
|
79
|
+
optionsArgs: IScreenOptionsArgs,
|
|
80
|
+
screenOptions: IReactScreenOptions | undefined,
|
|
81
|
+
): IReactScreenOptions {
|
|
82
|
+
const own = typeof entry.options === 'function' ? entry.options(optionsArgs) : entry.options;
|
|
83
|
+
return { ...screenOptions, ...own };
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const StackImpl = forwardRef<INavigatorHandle, IStackProps>((props, forwardedRef) => {
|
|
87
|
+
// Read BEFORE establishing this Stack's own Context value below - becomes the `parent` link a
|
|
88
|
+
// nested screen's useNavigation().getParent() walks (e.g. this Stack rendered as a Tab screen's
|
|
89
|
+
// content reaches that Tab via this value). undefined when this Stack is the nesting root.
|
|
90
|
+
const ambientContext = useContext(NavigationContext);
|
|
91
|
+
const registry = useMemo(
|
|
92
|
+
() => collectRegistry(props.children, isScreenElement),
|
|
93
|
+
[props.children],
|
|
94
|
+
);
|
|
95
|
+
const routeIdPrefix = useId();
|
|
96
|
+
const routeSequence = useRef(0);
|
|
97
|
+
// One emitter per route.key, keyed exactly like routeSequence's counter is scoped - created
|
|
98
|
+
// lazily the first time a route is rendered, pruned once it's popped off the stack (below).
|
|
99
|
+
const emitters = useRef(new Map<string, INavigationEmitter>()).current;
|
|
100
|
+
// Investigation instrumentation (flicker-on-focus bug): route keys whose resolved
|
|
101
|
+
// transition/animation-timing props have already been logged, so the once-per-mount dump below
|
|
102
|
+
// doesn't spam on every re-render. Kept behind DEBUG, never removed.
|
|
103
|
+
const loggedScreenPropKeys = useRef(new Set<string>()).current;
|
|
104
|
+
|
|
105
|
+
const emitterFor = useCallback(
|
|
106
|
+
(routeKey: string): INavigationEmitter => {
|
|
107
|
+
let emitter = emitters.get(routeKey);
|
|
108
|
+
if (!emitter) {
|
|
109
|
+
emitter = createNavigationEmitter();
|
|
110
|
+
emitters.set(routeKey, emitter);
|
|
111
|
+
}
|
|
112
|
+
return emitter;
|
|
113
|
+
},
|
|
114
|
+
[emitters],
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
const createRoute = useCallback(
|
|
118
|
+
(name: string, params: unknown): IRoute<unknown> => {
|
|
119
|
+
routeSequence.current += 1;
|
|
120
|
+
return { key: `${routeIdPrefix}-${name}-${routeSequence.current}`, name, params };
|
|
121
|
+
},
|
|
122
|
+
[routeIdPrefix],
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
const [state, dispatch] = useReducer(navigatorReducer, undefined, () => {
|
|
126
|
+
const initialRouteName = props.initialRouteName ?? registry.keys().next().value;
|
|
127
|
+
if (initialRouteName === undefined) {
|
|
128
|
+
dlog('Stack: no <Stack.Screen> children registered');
|
|
129
|
+
return createInitialNavigatorState({ key: routeIdPrefix, name: '', params: undefined });
|
|
130
|
+
}
|
|
131
|
+
return createInitialNavigatorState(
|
|
132
|
+
createRoute(initialRouteName, registry.get(initialRouteName)?.initialParams),
|
|
133
|
+
);
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
const handle = useMemo<INavigatorHandle>(
|
|
137
|
+
() => ({
|
|
138
|
+
push: (name, params) => dispatch({ type: 'push', route: createRoute(name, params) }),
|
|
139
|
+
pop: count => dispatch({ type: 'pop', count }),
|
|
140
|
+
popToTop: () => dispatch({ type: 'popToTop' }),
|
|
141
|
+
popTo: key => dispatch({ type: 'popTo', key }),
|
|
142
|
+
replace: (name, params) => dispatch({ type: 'replace', route: createRoute(name, params) }),
|
|
143
|
+
setParams: (params, key) => dispatch({ type: 'setParams', key, params }),
|
|
144
|
+
reset: nextState => dispatch({ type: 'reset', state: nextState }),
|
|
145
|
+
canGoBack: () => state.routes.length > 1,
|
|
146
|
+
}),
|
|
147
|
+
[createRoute, state.routes.length],
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
useImperativeHandle(forwardedRef, () => handle, [handle]);
|
|
151
|
+
|
|
152
|
+
// Broadcasts the router state to every still-live route's emitter (useNavigationState's
|
|
153
|
+
// source) after each commit, and prunes emitters for routes popped off the stack - mirroring
|
|
154
|
+
// React's own subscribe-in-effect pattern rather than emitting mid-render, which would call a
|
|
155
|
+
// descendant's setState while this component is still rendering.
|
|
156
|
+
useEffect(() => {
|
|
157
|
+
for (const route of state.routes) {
|
|
158
|
+
emitterFor(route.key).emit(NAVIGATION_EVENT_STATE, state);
|
|
159
|
+
}
|
|
160
|
+
for (const routeKey of emitters.keys()) {
|
|
161
|
+
if (!state.routes.some(route => route.key === routeKey)) emitters.delete(routeKey);
|
|
162
|
+
}
|
|
163
|
+
}, [state, emitterFor, emitters]);
|
|
164
|
+
|
|
165
|
+
// Investigation instrumentation (flicker-on-focus bug): STACK_ON_FINISH_TRANSITIONING is the
|
|
166
|
+
// native signal that the WHOLE push/pop animation has finished (as opposed to onAppear/
|
|
167
|
+
// onDisappear, which are per-screen) - logging it lets the per-screen appear/disappear
|
|
168
|
+
// timestamps above be checked against the actual transition-complete moment. Kept behind
|
|
169
|
+
// DEBUG, never removed.
|
|
170
|
+
const stackProps = resolveStackProps({
|
|
171
|
+
passthrough: {
|
|
172
|
+
[STACK_ON_FINISH_TRANSITIONING]: () =>
|
|
173
|
+
dlog(`Stack: onFinishTransitioning at t=${Date.now()}`),
|
|
174
|
+
},
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
const children = state.routes.map((route, index) => {
|
|
178
|
+
const entry = registry.get(route.name);
|
|
179
|
+
if (!entry) {
|
|
180
|
+
dlog(`Stack: no screen registered for route name "${route.name}"`);
|
|
181
|
+
return null;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const screenOptionsArgs: IScreenOptionsArgs = { route, navigation: handle };
|
|
185
|
+
const mergedOptions = resolveScreenOptions(entry, screenOptionsArgs, props.screenOptions);
|
|
186
|
+
const activityState = computeActivityState(index, state.routes.length);
|
|
187
|
+
// Investigation instrumentation (flicker-on-focus bug): fires on EVERY Stack render, not just
|
|
188
|
+
// on transitions, so the log stream shows whether a route's activityState/index ever changes
|
|
189
|
+
// outside of a push/pop dispatch (e.g. from an unrelated re-render racing the native
|
|
190
|
+
// transition). Kept behind DEBUG per <keep_logs_gate_behind_DEBUG> - never remove.
|
|
191
|
+
dlog(
|
|
192
|
+
`Stack: render route "${route.name}" index=${index}/${state.routes.length - 1} ` +
|
|
193
|
+
`activityState=${activityState} at t=${Date.now()}`,
|
|
194
|
+
);
|
|
195
|
+
|
|
196
|
+
const routeEmitter = emitterFor(route.key);
|
|
197
|
+
|
|
198
|
+
const searchBarOptions = mergedOptions.headerSearchBarOptions;
|
|
199
|
+
const plan = resolveScreenRenderPlan({
|
|
200
|
+
screenId: route.key,
|
|
201
|
+
index,
|
|
202
|
+
routeCount: state.routes.length,
|
|
203
|
+
options: mergedOptions,
|
|
204
|
+
platform: NAVIGATOR_PLATFORM,
|
|
205
|
+
isAndroid: Platform.OS === 'android',
|
|
206
|
+
screenPassthrough: {
|
|
207
|
+
[SCREEN_ON_DISMISSED]: () => dispatch({ type: 'pop', count: 1 }),
|
|
208
|
+
[SCREEN_ON_HEADER_BACK_BUTTON_CLICKED]: () => dispatch({ type: 'pop', count: 1 }),
|
|
209
|
+
// onAppear/onDisappear are the definitive visibility boundary (post-transition-animation),
|
|
210
|
+
// so 'focus'/'blur' fire exactly once per transition; onWillAppear/onWillDisappear fire
|
|
211
|
+
// BEFORE the animation runs, so wiring them to emit() too would double-invoke
|
|
212
|
+
// useFocusEffect per transition - they only get a debug log here.
|
|
213
|
+
[SCREEN_ON_WILL_APPEAR]: () =>
|
|
214
|
+
dlog(`Stack: route "${route.name}" will appear at t=${Date.now()}`),
|
|
215
|
+
[SCREEN_ON_APPEAR]: () => {
|
|
216
|
+
dlog(`Stack: route "${route.name}" appeared (focus) at t=${Date.now()}`);
|
|
217
|
+
routeEmitter.emit(NAVIGATION_EVENT_FOCUS);
|
|
218
|
+
},
|
|
219
|
+
[SCREEN_ON_WILL_DISAPPEAR]: () =>
|
|
220
|
+
dlog(`Stack: route "${route.name}" will disappear at t=${Date.now()}`),
|
|
221
|
+
[SCREEN_ON_DISAPPEAR]: () => {
|
|
222
|
+
dlog(`Stack: route "${route.name}" disappeared (blur) at t=${Date.now()}`);
|
|
223
|
+
routeEmitter.emit(NAVIGATION_EVENT_BLUR);
|
|
224
|
+
},
|
|
225
|
+
},
|
|
226
|
+
searchBarPassthrough: searchBarOptions
|
|
227
|
+
? {
|
|
228
|
+
...buildSearchBarPassthrough(searchBarOptions, message =>
|
|
229
|
+
dlog(`Stack: route "${route.name}" ${message}`),
|
|
230
|
+
),
|
|
231
|
+
// The imperative ref (SearchBarCommands): a callback ref attached straight to the
|
|
232
|
+
// RNSSearchBar host element via `passthrough.ref` - createElement extracts `ref` from
|
|
233
|
+
// a props object same as ScrollView/TextInput's own ref binding (see
|
|
234
|
+
// descriptor-to-react's comment), so the host instance IS the ISymbioteNode. Built
|
|
235
|
+
// fresh per mount/unmount, never captured eagerly (buildSearchBarHandle's own
|
|
236
|
+
// lazy-getter contract is satisfied trivially here since `node` is already resolved).
|
|
237
|
+
ref: (node: ISymbioteNode | null) => {
|
|
238
|
+
// Investigation instrumentation (search-bar-ref "node not committed" bug): compare
|
|
239
|
+
// this debugNodeId against the mirror.set/dispatchViewCommand logs in commit.ts -
|
|
240
|
+
// same id on both sides proves the ref really does hold the committed node; a
|
|
241
|
+
// mismatch proves a stale closure instead. Kept behind DEBUG, never removed.
|
|
242
|
+
dlog(
|
|
243
|
+
`Stack: search bar ref callback, node=${node === null ? 'null' : debugNodeId(node)} at t=${Date.now()}`,
|
|
244
|
+
);
|
|
245
|
+
const appRef = searchBarOptions.ref;
|
|
246
|
+
if (!appRef) return;
|
|
247
|
+
appRef.current = node === null ? null : buildSearchBarHandle(() => node);
|
|
248
|
+
},
|
|
249
|
+
}
|
|
250
|
+
: undefined,
|
|
251
|
+
});
|
|
252
|
+
// Investigation instrumentation (flicker-on-focus bug): the actual timing/z-order-relevant
|
|
253
|
+
// values resolved onto the native RNSScreen, once per route.key (not every render) - rules a
|
|
254
|
+
// stackAnimation/transitionDuration mismatch against react-native-screens' own native default
|
|
255
|
+
// in or out. Kept behind DEBUG, never removed.
|
|
256
|
+
if (!loggedScreenPropKeys.has(route.key)) {
|
|
257
|
+
loggedScreenPropKeys.add(route.key);
|
|
258
|
+
dlog(
|
|
259
|
+
`Stack: route "${route.name}" resolved screen props ` +
|
|
260
|
+
`stackAnimation=${String(plan.screenProps.stackAnimation)} ` +
|
|
261
|
+
`stackPresentation=${String(plan.screenProps.stackPresentation)} ` +
|
|
262
|
+
`transitionDuration=${String(plan.screenProps.transitionDuration)} ` +
|
|
263
|
+
`gestureEnabled=${String(plan.screenProps.gestureEnabled)} at t=${Date.now()}`,
|
|
264
|
+
);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// Must not be flattened away (collapsable: false) - react-native-screens' native side
|
|
268
|
+
// finds THIS specific view type by class check to register a formSheet's content for
|
|
269
|
+
// sizing (see RNS_SCREEN_CONTENT_WRAPPER_VIEW_NAME's comment in core/constants.ts). A
|
|
270
|
+
// `push` screen doesn't need it, but a `formSheet` one is otherwise left with no content
|
|
271
|
+
// ever attached natively.
|
|
272
|
+
const content = createElement(
|
|
273
|
+
RNS_SCREEN_CONTENT_WRAPPER_VIEW_NAME,
|
|
274
|
+
plan.contentWrapperProps,
|
|
275
|
+
createElement(
|
|
276
|
+
NavigationContext.Provider,
|
|
277
|
+
{ value: { route, navigation: handle, emitter: routeEmitter, parent: ambientContext } },
|
|
278
|
+
createElement(entry.component),
|
|
279
|
+
),
|
|
280
|
+
);
|
|
281
|
+
|
|
282
|
+
// react-native-screens' own Screen.tsx swaps in a DIFFERENT Fabric component
|
|
283
|
+
// ('RNSModalScreen') for a modally-presented screen - its native updateLayoutMetrics: relies on
|
|
284
|
+
// this exact class to know it must NOT apply Yoga's computed frame (see resolveScreenViewName's
|
|
285
|
+
// comment in core/render-stack.ts). The nested Screen isHeaderInModal adds below is always
|
|
286
|
+
// plain 'RNSScreen': react-native-screens' own inner Screen never carries a presentation prop
|
|
287
|
+
// either, since it exists purely to host the header, not to be modally presented itself.
|
|
288
|
+
|
|
289
|
+
// A modal/formSheet screen has no UINavigationController of its own on iOS - nest an inner
|
|
290
|
+
// RNSScreenStack/RNSScreen purely to host the native header bar (see isHeaderInModal's
|
|
291
|
+
// comment in core/render-stack.ts). Skipping this leaves RNSScreenStackHeaderConfig with no
|
|
292
|
+
// navigation controller to attach to, so the header silently never renders.
|
|
293
|
+
return plan.inModal
|
|
294
|
+
? createElement(
|
|
295
|
+
plan.screenViewName,
|
|
296
|
+
{ key: route.key, ...plan.screenProps },
|
|
297
|
+
createElement(
|
|
298
|
+
RNS_SCREEN_STACK_VIEW_NAME,
|
|
299
|
+
{ style: plan.innerStackStyle },
|
|
300
|
+
createElement(
|
|
301
|
+
RNS_SCREEN_VIEW_NAME,
|
|
302
|
+
// activityState mirrors the outer Screen's own value - react-native-screens'
|
|
303
|
+
// RNSScreen.mm treats an unset/inactive nested screen as not yet pushed, leaving it
|
|
304
|
+
// parked at its pre-push transition position (off past the bottom edge) instead of
|
|
305
|
+
// its real, presented frame.
|
|
306
|
+
{ style: plan.innerScreenStyle, activityState: plan.activityState },
|
|
307
|
+
descriptorToReact(plan.headerConfig),
|
|
308
|
+
content,
|
|
309
|
+
),
|
|
310
|
+
),
|
|
311
|
+
)
|
|
312
|
+
: createElement(
|
|
313
|
+
plan.screenViewName,
|
|
314
|
+
{ key: route.key, ...plan.screenProps },
|
|
315
|
+
descriptorToReact(plan.headerConfig),
|
|
316
|
+
content,
|
|
317
|
+
);
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
return createElement(RNS_SCREEN_STACK_VIEW_NAME, stackProps, ...children);
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
export const Stack = Object.assign(StackImpl, { Screen });
|