@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,204 @@
|
|
|
1
|
+
// Tab, the React lifecycle half. The focused-index router (tab-router-state) and the tab-bar
|
|
2
|
+
// Descriptor builder (render-tabs) live in @symbiote-native/navigation core, shared verbatim
|
|
3
|
+
// with the Vue/Angular adapters; here React supplies the lifecycle - useReducer for the focused
|
|
4
|
+
// index, useId for route-key generation, useImperativeHandle for the jumpTo/setParams handle -
|
|
5
|
+
// plus the descriptor bridge for the tab-bar leaf, exactly like Stack bridges its header config
|
|
6
|
+
// (react/stack.ts). Unlike Stack, a bottom-tabs bar is a PURE-JS UI: it paints ordinary
|
|
7
|
+
// `symbiote-view`/`symbiote-text` primitives via the shared render fn, so there is no
|
|
8
|
+
// react-native-screens ViewConfig to register here - Tab needs no `../register` import.
|
|
9
|
+
|
|
10
|
+
import {
|
|
11
|
+
createElement,
|
|
12
|
+
forwardRef,
|
|
13
|
+
isValidElement,
|
|
14
|
+
useCallback,
|
|
15
|
+
useContext,
|
|
16
|
+
useEffect,
|
|
17
|
+
useId,
|
|
18
|
+
useImperativeHandle,
|
|
19
|
+
useMemo,
|
|
20
|
+
useReducer,
|
|
21
|
+
} from 'react';
|
|
22
|
+
import type { ReactElement, ReactNode } from 'react';
|
|
23
|
+
import { descriptorToReact } from '@symbiote-native/react';
|
|
24
|
+
import { dlog } from '@symbiote-native/engine';
|
|
25
|
+
import {
|
|
26
|
+
NAVIGATION_EVENT_BLUR,
|
|
27
|
+
NAVIGATION_EVENT_FOCUS,
|
|
28
|
+
createInitialTabState,
|
|
29
|
+
createNavigationEmitter,
|
|
30
|
+
isFocusedRoute,
|
|
31
|
+
renderTabBar,
|
|
32
|
+
tabRouterReducer,
|
|
33
|
+
} from '../../core';
|
|
34
|
+
import type { IRoute, ITabBarItemView, ITabNavigatorHandle, ITabOptions } from '../../core';
|
|
35
|
+
import { collectRegistry } from '../collect-registry';
|
|
36
|
+
import { NavigationContext } from '../navigation-context';
|
|
37
|
+
import { TabScreen } from '../tab-screen';
|
|
38
|
+
import type { ITabScreenOptionsArgs, ITabScreenProps } from '../tab-screen';
|
|
39
|
+
|
|
40
|
+
export type { ITabNavigatorHandle } from '../../core';
|
|
41
|
+
|
|
42
|
+
export type ITabProps = {
|
|
43
|
+
initialRouteName?: string;
|
|
44
|
+
screenOptions?: ITabOptions;
|
|
45
|
+
children?: ReactNode;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
type ITabRegistryEntry = Omit<ITabScreenProps, 'name'>;
|
|
49
|
+
|
|
50
|
+
function isTabScreenElement(child: ReactNode): child is ReactElement<ITabScreenProps> {
|
|
51
|
+
return isValidElement(child) && child.type === TabScreen;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function resolveTabOptions(
|
|
55
|
+
entry: ITabRegistryEntry,
|
|
56
|
+
optionsArgs: ITabScreenOptionsArgs,
|
|
57
|
+
screenOptions: ITabOptions | undefined,
|
|
58
|
+
): ITabOptions {
|
|
59
|
+
const own = typeof entry.options === 'function' ? entry.options(optionsArgs) : entry.options;
|
|
60
|
+
return { ...screenOptions, ...own };
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const TAB_CONTENT_STYLE = { flex: 1 };
|
|
64
|
+
const TAB_ROOT_STYLE = { flex: 1 };
|
|
65
|
+
|
|
66
|
+
const TabImpl = forwardRef<ITabNavigatorHandle, ITabProps>((props, forwardedRef) => {
|
|
67
|
+
// Read BEFORE establishing this Tab's own Context value below - becomes the `parent` link a
|
|
68
|
+
// nested screen's useNavigation().getParent() walks (e.g. this Tab rendered as a Stack screen's
|
|
69
|
+
// content reaches that Stack via this value). undefined when this Tab is the nesting root.
|
|
70
|
+
const ambientContext = useContext(NavigationContext);
|
|
71
|
+
const registry = useMemo(
|
|
72
|
+
() => collectRegistry(props.children, isTabScreenElement),
|
|
73
|
+
[props.children],
|
|
74
|
+
);
|
|
75
|
+
const routeIdPrefix = useId();
|
|
76
|
+
|
|
77
|
+
const routes = useMemo<IRoute<unknown>[]>(
|
|
78
|
+
() =>
|
|
79
|
+
Array.from(registry.entries()).map(([name, entry]) => ({
|
|
80
|
+
key: `${routeIdPrefix}-${name}`,
|
|
81
|
+
name,
|
|
82
|
+
params: entry.initialParams,
|
|
83
|
+
})),
|
|
84
|
+
[registry, routeIdPrefix],
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
const [state, dispatch] = useReducer(tabRouterReducer, undefined, () =>
|
|
88
|
+
createInitialTabState(routes, props.initialRouteName),
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
if (routes.length === 0) dlog('Tab: no <Tab.Screen> children registered');
|
|
92
|
+
|
|
93
|
+
const jumpTo = useCallback(
|
|
94
|
+
(name: string, params?: unknown) => dispatch({ type: 'jumpTo', name, params }),
|
|
95
|
+
[],
|
|
96
|
+
);
|
|
97
|
+
const setParams = useCallback(
|
|
98
|
+
(params: unknown, key: string) => dispatch({ type: 'setParams', key, params }),
|
|
99
|
+
[],
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
const handle = useMemo<ITabNavigatorHandle>(() => ({ jumpTo, setParams }), [jumpTo, setParams]);
|
|
103
|
+
|
|
104
|
+
useImperativeHandle(forwardedRef, () => handle, [handle]);
|
|
105
|
+
|
|
106
|
+
const focusedRoute: IRoute<unknown> | undefined = state.routes[state.index];
|
|
107
|
+
|
|
108
|
+
const items: ITabBarItemView[] = state.routes.map((route, index) => {
|
|
109
|
+
const entry = registry.get(route.name);
|
|
110
|
+
const focused = isFocusedRoute(index, state.index);
|
|
111
|
+
if (!entry) {
|
|
112
|
+
dlog(`Tab: no screen registered for route name "${route.name}"`);
|
|
113
|
+
return {
|
|
114
|
+
key: route.key,
|
|
115
|
+
focused,
|
|
116
|
+
label: route.name,
|
|
117
|
+
passthrough: {},
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const options = resolveTabOptions(entry, { route, navigation: handle }, props.screenOptions);
|
|
122
|
+
|
|
123
|
+
return {
|
|
124
|
+
key: route.key,
|
|
125
|
+
focused,
|
|
126
|
+
label: options.tabBarLabel ?? options.title ?? route.name,
|
|
127
|
+
icon: options.tabBarIcon,
|
|
128
|
+
badge: options.tabBarBadge,
|
|
129
|
+
activeTintColor: options.tabBarActiveTintColor,
|
|
130
|
+
inactiveTintColor: options.tabBarInactiveTintColor,
|
|
131
|
+
passthrough: {
|
|
132
|
+
onPress: () => jumpTo(route.name),
|
|
133
|
+
accessibilityRole: 'tab',
|
|
134
|
+
accessibilityState: { selected: focused },
|
|
135
|
+
},
|
|
136
|
+
};
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
const focusedEntry = focusedRoute ? registry.get(focusedRoute.name) : undefined;
|
|
140
|
+
const focusedOptions: ITabOptions | undefined =
|
|
141
|
+
focusedEntry && focusedRoute
|
|
142
|
+
? resolveTabOptions(
|
|
143
|
+
focusedEntry,
|
|
144
|
+
{ route: focusedRoute, navigation: handle },
|
|
145
|
+
props.screenOptions,
|
|
146
|
+
)
|
|
147
|
+
: props.screenOptions;
|
|
148
|
+
|
|
149
|
+
const tabBar = descriptorToReact(
|
|
150
|
+
renderTabBar({ items, style: focusedOptions?.tabBarStyle, passthrough: {} }),
|
|
151
|
+
);
|
|
152
|
+
|
|
153
|
+
// Only the focused route's screen is ever mounted (unlike Stack, which keeps every pushed route
|
|
154
|
+
// alive), so a fresh emitter per focus change is sufficient - no per-route emitter map to prune,
|
|
155
|
+
// because the previous screen's whole subtree (and any listeners it registered) is torn down by
|
|
156
|
+
// an ordinary React unmount when focus moves on. Keyed on the route KEY rather than the route
|
|
157
|
+
// object so a setParams-only change (new route object, same key) doesn't spuriously re-fire
|
|
158
|
+
// focus/blur.
|
|
159
|
+
const focusedRouteKey = focusedRoute?.key;
|
|
160
|
+
const routeEmitter = useMemo(() => createNavigationEmitter(), [focusedRouteKey]);
|
|
161
|
+
|
|
162
|
+
// Tab paints its own bar in pure JS - there is no native onAppear/onDisappear to hook (unlike
|
|
163
|
+
// Stack's RNSScreen), so focus/blur is synthesized here: mount = focus, cleanup = blur, exactly
|
|
164
|
+
// what an effect keyed on focusedRouteKey already encodes - no diffFocusedRoute indirection
|
|
165
|
+
// needed (unlike Vue/Angular, which diff real prev/next keys inside an imperative watch/CD
|
|
166
|
+
// callback that has no mount/cleanup pairing of its own).
|
|
167
|
+
useEffect(() => {
|
|
168
|
+
if (focusedRouteKey === undefined) return undefined;
|
|
169
|
+
dlog(`Tab: route "${focusedRoute?.name}" focused`);
|
|
170
|
+
routeEmitter.emit(NAVIGATION_EVENT_FOCUS);
|
|
171
|
+
return () => {
|
|
172
|
+
dlog(`Tab: route "${focusedRoute?.name}" blurred`);
|
|
173
|
+
routeEmitter.emit(NAVIGATION_EVENT_BLUR);
|
|
174
|
+
};
|
|
175
|
+
// focusedRoute omitted deliberately: only its .key (tracked via focusedRouteKey) should
|
|
176
|
+
// re-run this effect - see the comment above on focusedRouteKey.
|
|
177
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
178
|
+
}, [routeEmitter, focusedRouteKey]);
|
|
179
|
+
|
|
180
|
+
const content =
|
|
181
|
+
focusedEntry && focusedRoute
|
|
182
|
+
? createElement(
|
|
183
|
+
NavigationContext.Provider,
|
|
184
|
+
{
|
|
185
|
+
value: {
|
|
186
|
+
route: focusedRoute,
|
|
187
|
+
navigation: handle,
|
|
188
|
+
emitter: routeEmitter,
|
|
189
|
+
parent: ambientContext,
|
|
190
|
+
},
|
|
191
|
+
},
|
|
192
|
+
createElement(focusedEntry.component),
|
|
193
|
+
)
|
|
194
|
+
: null;
|
|
195
|
+
|
|
196
|
+
return createElement(
|
|
197
|
+
'symbiote-view',
|
|
198
|
+
{ style: TAB_ROOT_STYLE },
|
|
199
|
+
createElement('symbiote-view', { style: TAB_CONTENT_STYLE }, content),
|
|
200
|
+
tabBar,
|
|
201
|
+
);
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
export const Tab = Object.assign(TabImpl, { Screen: TabScreen });
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
// Co-located React-driven test (ADR 0025) for the @symbiote-native/navigation React Tab
|
|
2
|
+
// navigator. Unlike Stack (which drives real native RNSScreen views and needs an injected
|
|
3
|
+
// codegen-shaped ViewConfig - see stack.test.tsx), the tab bar is a PURE-JS UI painted from
|
|
4
|
+
// ordinary `symbiote-view`/`symbiote-text` primitives, so no ViewConfig source is needed here at
|
|
5
|
+
// all. Proves: only the focused route's screen mounts, jumpTo() moves focus, a tap (synthesized
|
|
6
|
+
// by the engine from a topTouchStart/topTouchEnd pair - core/engine/src/events/index.ts - on the
|
|
7
|
+
// tab button) drives the same jumpTo, and per-tab options (label/badge/tint) reach the tab bar.
|
|
8
|
+
|
|
9
|
+
import { act, createElement, createRef, useCallback } from 'react';
|
|
10
|
+
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
11
|
+
import { mount, unmount } from '@symbiote-native/react';
|
|
12
|
+
import { installFabric, type IFakeNode } from '@symbiote-native/test-utils';
|
|
13
|
+
import { Tab } from './index';
|
|
14
|
+
import type { ITabNavigatorHandle } from './index';
|
|
15
|
+
import { useFocusEffect, useIsFocused, useNavigation, useRoute } from '../hooks';
|
|
16
|
+
|
|
17
|
+
const ROOT_TAG = 640;
|
|
18
|
+
const TOUCH_START = 'topTouchStart';
|
|
19
|
+
const TOUCH_END = 'topTouchEnd';
|
|
20
|
+
|
|
21
|
+
const fabric = installFabric();
|
|
22
|
+
|
|
23
|
+
beforeEach(() => fabric.reset());
|
|
24
|
+
afterEach(() => unmount(ROOT_TAG));
|
|
25
|
+
|
|
26
|
+
function findAllText(nodes: readonly IFakeNode[]): string[] {
|
|
27
|
+
const found: string[] = [];
|
|
28
|
+
const collect = (list: readonly IFakeNode[]): void => {
|
|
29
|
+
for (const node of list) {
|
|
30
|
+
if (node.viewName === 'RCTRawText' && typeof node.props.text === 'string')
|
|
31
|
+
found.push(node.props.text);
|
|
32
|
+
collect(node.children);
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
collect(nodes);
|
|
36
|
+
return found;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// The tab bar row: the second child of Tab's root `symbiote-view` (content wrapper first, bar
|
|
40
|
+
// second - see react/tabs.ts's final createElement).
|
|
41
|
+
function tabBarRow(): IFakeNode {
|
|
42
|
+
const root = fabric.appRoot();
|
|
43
|
+
const tabRoot = root.children[0];
|
|
44
|
+
const bar = tabRoot?.children[1];
|
|
45
|
+
if (!bar) throw new Error('no tab bar row was committed');
|
|
46
|
+
return bar;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function tapItem(index: number): void {
|
|
50
|
+
const item = tabBarRow().children[index];
|
|
51
|
+
if (!item) throw new Error(`no tab item at index ${index}`);
|
|
52
|
+
act(() => {
|
|
53
|
+
fabric.fireEvent(item.instanceHandle, TOUCH_START, {
|
|
54
|
+
touches: [{ identifier: 1, pageX: 0, pageY: 0 }],
|
|
55
|
+
changedTouches: [{ identifier: 1, pageX: 0, pageY: 0 }],
|
|
56
|
+
});
|
|
57
|
+
fabric.fireEvent(item.instanceHandle, TOUCH_END, {
|
|
58
|
+
touches: [],
|
|
59
|
+
changedTouches: [{ identifier: 1, pageX: 0, pageY: 0 }],
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function HomeScreen(): ReturnType<typeof createElement> {
|
|
65
|
+
return createElement('symbiote-text', {}, 'home');
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function ProfileScreen(): ReturnType<typeof createElement> {
|
|
69
|
+
return createElement('symbiote-text', {}, 'profile');
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
describe('React Tab navigator', () => {
|
|
73
|
+
it('mounts only the initial focused route content, and the tab bar for every registered route', () => {
|
|
74
|
+
mount(
|
|
75
|
+
ROOT_TAG,
|
|
76
|
+
createElement(
|
|
77
|
+
Tab,
|
|
78
|
+
{ initialRouteName: 'Home' },
|
|
79
|
+
createElement(Tab.Screen, {
|
|
80
|
+
name: 'Home',
|
|
81
|
+
component: HomeScreen,
|
|
82
|
+
options: { tabBarLabel: 'Home' },
|
|
83
|
+
}),
|
|
84
|
+
createElement(Tab.Screen, {
|
|
85
|
+
name: 'Profile',
|
|
86
|
+
component: ProfileScreen,
|
|
87
|
+
options: { tabBarLabel: 'Profile' },
|
|
88
|
+
}),
|
|
89
|
+
),
|
|
90
|
+
);
|
|
91
|
+
expect(findAllText(fabric.committed)).toContain('home');
|
|
92
|
+
expect(findAllText(fabric.committed)).not.toContain('profile');
|
|
93
|
+
expect(tabBarRow().children).toHaveLength(2);
|
|
94
|
+
expect(findAllText(tabBarRow().children)).toEqual(['Home', 'Profile']);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it('jumpTo() switches the focused screen content', () => {
|
|
98
|
+
const ref = createRef<ITabNavigatorHandle>();
|
|
99
|
+
mount(
|
|
100
|
+
ROOT_TAG,
|
|
101
|
+
createElement(
|
|
102
|
+
Tab,
|
|
103
|
+
{ ref, initialRouteName: 'Home' },
|
|
104
|
+
createElement(Tab.Screen, { name: 'Home', component: HomeScreen }),
|
|
105
|
+
createElement(Tab.Screen, { name: 'Profile', component: ProfileScreen }),
|
|
106
|
+
),
|
|
107
|
+
);
|
|
108
|
+
act(() => ref.current?.jumpTo('Profile'));
|
|
109
|
+
expect(findAllText(fabric.committed)).toContain('profile');
|
|
110
|
+
expect(findAllText(fabric.committed)).not.toContain('home');
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it('a tap on a tab bar item drives the same jumpTo as the imperative handle', () => {
|
|
114
|
+
mount(
|
|
115
|
+
ROOT_TAG,
|
|
116
|
+
createElement(
|
|
117
|
+
Tab,
|
|
118
|
+
{ initialRouteName: 'Home' },
|
|
119
|
+
createElement(Tab.Screen, { name: 'Home', component: HomeScreen }),
|
|
120
|
+
createElement(Tab.Screen, { name: 'Profile', component: ProfileScreen }),
|
|
121
|
+
),
|
|
122
|
+
);
|
|
123
|
+
expect(findAllText(fabric.committed)).toContain('home');
|
|
124
|
+
tapItem(1);
|
|
125
|
+
expect(findAllText(fabric.committed)).toContain('profile');
|
|
126
|
+
expect(findAllText(fabric.committed)).not.toContain('home');
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it('jumpTo() to an unknown route name is a no-op', () => {
|
|
130
|
+
const ref = createRef<ITabNavigatorHandle>();
|
|
131
|
+
mount(
|
|
132
|
+
ROOT_TAG,
|
|
133
|
+
createElement(
|
|
134
|
+
Tab,
|
|
135
|
+
{ ref, initialRouteName: 'Home' },
|
|
136
|
+
createElement(Tab.Screen, { name: 'Home', component: HomeScreen }),
|
|
137
|
+
createElement(Tab.Screen, { name: 'Profile', component: ProfileScreen }),
|
|
138
|
+
),
|
|
139
|
+
);
|
|
140
|
+
act(() => ref.current?.jumpTo('Nowhere'));
|
|
141
|
+
expect(findAllText(fabric.committed)).toContain('home');
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
it('exposes route.params to the focused screen component via useRoute() after jumpTo', () => {
|
|
145
|
+
let receivedParams: unknown;
|
|
146
|
+
function ParamsScreen(): ReturnType<typeof createElement> {
|
|
147
|
+
receivedParams = useRoute().params;
|
|
148
|
+
return createElement('symbiote-text', {}, 'params');
|
|
149
|
+
}
|
|
150
|
+
const ref = createRef<ITabNavigatorHandle>();
|
|
151
|
+
mount(
|
|
152
|
+
ROOT_TAG,
|
|
153
|
+
createElement(
|
|
154
|
+
Tab,
|
|
155
|
+
{ ref, initialRouteName: 'Home' },
|
|
156
|
+
createElement(Tab.Screen, { name: 'Home', component: HomeScreen }),
|
|
157
|
+
createElement(Tab.Screen, { name: 'Profile', component: ParamsScreen }),
|
|
158
|
+
),
|
|
159
|
+
);
|
|
160
|
+
act(() => ref.current?.jumpTo('Profile', { id: 42 }));
|
|
161
|
+
expect(receivedParams).toEqual({ id: 42 });
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
it('resolves tabBarBadge onto the focused-agnostic tab item', () => {
|
|
165
|
+
mount(
|
|
166
|
+
ROOT_TAG,
|
|
167
|
+
createElement(
|
|
168
|
+
Tab,
|
|
169
|
+
{ initialRouteName: 'Home' },
|
|
170
|
+
createElement(Tab.Screen, {
|
|
171
|
+
name: 'Home',
|
|
172
|
+
component: HomeScreen,
|
|
173
|
+
options: { tabBarBadge: 3 },
|
|
174
|
+
}),
|
|
175
|
+
createElement(Tab.Screen, { name: 'Profile', component: ProfileScreen }),
|
|
176
|
+
),
|
|
177
|
+
);
|
|
178
|
+
expect(findAllText(tabBarRow().children)).toContain('3');
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
// Before this fix, Tab never wrapped its focused screen in NavigationContext.Provider at all,
|
|
182
|
+
// so every one of these hooks threw "must be used within a screen rendered by <Stack>" the
|
|
183
|
+
// moment a Tab screen called them - a real gap, not just a nesting concern (see this package's
|
|
184
|
+
// task notes). These cases prove the context is now provided and the focus semantics are wired.
|
|
185
|
+
it('useNavigation()/useRoute() are usable inside a Tab screen, and useIsFocused() reflects the focused tab', () => {
|
|
186
|
+
let homeIsFocused: boolean | undefined;
|
|
187
|
+
let homeRouteName: string | undefined;
|
|
188
|
+
let profileIsFocused: boolean | undefined;
|
|
189
|
+
|
|
190
|
+
function TrackedHomeScreen(): ReturnType<typeof createElement> {
|
|
191
|
+
const navigation = useNavigation();
|
|
192
|
+
homeIsFocused = useIsFocused();
|
|
193
|
+
homeRouteName = useRoute().name;
|
|
194
|
+
// Merely proving the handle is a real ITabNavigatorHandle (jumpTo/setParams), not the
|
|
195
|
+
// Stack-only shape this Context value was hard-typed to before the widened union.
|
|
196
|
+
expect(typeof navigation.jumpTo).toBe('function');
|
|
197
|
+
return createElement('symbiote-text', {}, 'home');
|
|
198
|
+
}
|
|
199
|
+
function TrackedProfileScreen(): ReturnType<typeof createElement> {
|
|
200
|
+
profileIsFocused = useIsFocused();
|
|
201
|
+
return createElement('symbiote-text', {}, 'profile');
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
const ref = createRef<ITabNavigatorHandle>();
|
|
205
|
+
// Tab's own focus-emitting effect runs in the same commit as the initial mount, but the
|
|
206
|
+
// setIsFocused(true) it triggers inside useIsFocused's listener lands in a follow-up render -
|
|
207
|
+
// act() is what drains that cascade synchronously (mirrors every other state-changing call in
|
|
208
|
+
// this file already being act()-wrapped).
|
|
209
|
+
act(() => {
|
|
210
|
+
mount(
|
|
211
|
+
ROOT_TAG,
|
|
212
|
+
createElement(
|
|
213
|
+
Tab,
|
|
214
|
+
{ ref, initialRouteName: 'Home' },
|
|
215
|
+
createElement(Tab.Screen, { name: 'Home', component: TrackedHomeScreen }),
|
|
216
|
+
createElement(Tab.Screen, { name: 'Profile', component: TrackedProfileScreen }),
|
|
217
|
+
),
|
|
218
|
+
);
|
|
219
|
+
});
|
|
220
|
+
expect(homeIsFocused).toBe(true);
|
|
221
|
+
expect(homeRouteName).toBe('Home');
|
|
222
|
+
|
|
223
|
+
act(() => ref.current?.jumpTo('Profile'));
|
|
224
|
+
expect(profileIsFocused).toBe(true);
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
it('useFocusEffect runs on Tab focus and its cleanup once jumpTo moves focus away', () => {
|
|
228
|
+
const events: string[] = [];
|
|
229
|
+
function TrackedHomeScreen(): ReturnType<typeof createElement> {
|
|
230
|
+
useFocusEffect(
|
|
231
|
+
useCallback(() => {
|
|
232
|
+
events.push('effect');
|
|
233
|
+
return () => events.push('cleanup');
|
|
234
|
+
}, []),
|
|
235
|
+
);
|
|
236
|
+
return createElement('symbiote-text', {}, 'home');
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const ref = createRef<ITabNavigatorHandle>();
|
|
240
|
+
mount(
|
|
241
|
+
ROOT_TAG,
|
|
242
|
+
createElement(
|
|
243
|
+
Tab,
|
|
244
|
+
{ ref, initialRouteName: 'Home' },
|
|
245
|
+
createElement(Tab.Screen, { name: 'Home', component: TrackedHomeScreen }),
|
|
246
|
+
createElement(Tab.Screen, { name: 'Profile', component: ProfileScreen }),
|
|
247
|
+
),
|
|
248
|
+
);
|
|
249
|
+
// Tab paints no native RNSScreen (unlike Stack), so there is no onAppear to wait for - the
|
|
250
|
+
// focused screen's useFocusEffect runs as soon as it mounts.
|
|
251
|
+
expect(events).toEqual(['effect']);
|
|
252
|
+
|
|
253
|
+
act(() => ref.current?.jumpTo('Profile'));
|
|
254
|
+
expect(events).toEqual(['effect', 'cleanup']);
|
|
255
|
+
});
|
|
256
|
+
});
|
package/src/register.ts
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
// Side-effect: register RNSScreen/RNSScreenStack/RNSScreenStackHeaderConfig/
|
|
2
|
+
// RNSScreenStackHeaderSubview/RNSSearchBar/RNSScreenContentWrapper's ViewConfigs in RN's
|
|
3
|
+
// ReactNativeViewConfigRegistry. Evaluating each codegen native-component spec
|
|
4
|
+
// (codegenNativeComponent('RNSScreen', ...) etc.) registers the getter the engine reads via
|
|
5
|
+
// setNativeViewConfigSource, so events + color processors derive on first commit. We import the
|
|
6
|
+
// codegen SPECS, never react-native-screens' React components (ScreenStack.tsx et al - hooks,
|
|
7
|
+
// crashes a non-React adapter) - these modules have no hooks, so they are framework-agnostic and
|
|
8
|
+
// shared by every adapter entry. Each adapter barrel imports this; the component modules and
|
|
9
|
+
// their headless tests stay free of it. See CLAUDE.md <third_party_rn_packages_are_react_only>.
|
|
10
|
+
//
|
|
11
|
+
// RNSSearchBar (headerSearchBarOptions' native leaf, nested by render-stack.ts's
|
|
12
|
+
// renderHeaderConfig) was missing from this list - its ViewConfig was never registered, so Fabric
|
|
13
|
+
// had no component descriptor for 'RNSSearchBar' and crashed on mount the moment a screen set
|
|
14
|
+
// headerSearchBarOptions. That is the root cause of the search-bar crash, not a deeper
|
|
15
|
+
// Fabric-interop gap.
|
|
16
|
+
//
|
|
17
|
+
// RNSScreenStackHeaderSubview (the wrapper renderHeaderConfig nests the search bar leaf inside,
|
|
18
|
+
// per RNSScreenStackHeaderConfig.mm's mountChildComponentView requiring every header child to be
|
|
19
|
+
// one) needs the same registration, or the same class of mount crash follows.
|
|
20
|
+
//
|
|
21
|
+
// RNSScreenContentWrapper (the RNS_SCREEN_CONTENT_WRAPPER_VIEW_NAME wrapper stack.ts wraps every
|
|
22
|
+
// screen's content in - see its comment in core/constants.ts) additionally needs a NATIVE-side
|
|
23
|
+
// registration: this file alone is not enough. The native iOS registry
|
|
24
|
+
// (RCTThirdPartyComponentsProvider.mm, generated by codegen from THIS package's own
|
|
25
|
+
// package.json#codegenConfig.ios.componentProvider map, NOT from react-native-screens') has no
|
|
26
|
+
// entry for 'RNSScreenContentWrapper' by default, so without it Fabric falls back to its
|
|
27
|
+
// "Unimplemented component" placeholder view regardless of this file's import. Both registries
|
|
28
|
+
// are independently required for a never-before-used react-native-screens view: this file (JS
|
|
29
|
+
// ViewConfig, read by @symbiote-native/engine's registry.ts) and package.json's componentProvider
|
|
30
|
+
// map (native ComponentDescriptor, read by codegen at `pod install` time).
|
|
31
|
+
//
|
|
32
|
+
// RNSModalScreen's gap on both registries isn't a mount crash but a silent mispositioning bug.
|
|
33
|
+
// react-native-screens' own Screen.tsx picks
|
|
34
|
+
// ModalScreenNativeComponent ('RNSModalScreen') instead of ScreenNativeComponent ('RNSScreen')
|
|
35
|
+
// whenever stackPresentation isn't push/containedModal/containedTransparentModal, because
|
|
36
|
+
// RNSScreen.mm's updateLayoutMetrics: checks `[self isKindOfClass:RNSModalScreen.class]` and, for
|
|
37
|
+
// that class, DELIBERATELY skips applying Yoga's computed frame (its own comment: "the available
|
|
38
|
+
// space is most likely restricted & differs from what Yoga resolves during first layout - we want
|
|
39
|
+
// to rely on native layout here"). Emitting a plain 'RNSScreen' for a formSheet/modal presentation
|
|
40
|
+
// (as resolveScreenViewName below used to do unconditionally) means that check never matches, so
|
|
41
|
+
// Yoga's frame - computed as if the screen were still a normal flexed sibling in its outer
|
|
42
|
+
// RNSScreenStack's push-oriented column layout - gets applied verbatim and never corrected,
|
|
43
|
+
// visually shifting the whole modal down by one full screen's height. Fixed in
|
|
44
|
+
// core/render-stack.ts's resolveScreenViewName, which now mirrors Screen.tsx's own
|
|
45
|
+
// shouldUseModalScreenComponent condition exactly.
|
|
46
|
+
//
|
|
47
|
+
// Unlike @symbiote-native/slider (which deep-imports @react-native-community/slider's compiled
|
|
48
|
+
// `dist/`), react-native-screens declares `"react-native": "src/index"` in its package.json -
|
|
49
|
+
// Metro's resolverMainFields prioritizes that field, so it resolves the library straight to raw
|
|
50
|
+
// TypeScript source and transforms it like first-party code. The deep import below targets that
|
|
51
|
+
// same `src/fabric/` tree rather than a compiled output.
|
|
52
|
+
|
|
53
|
+
import 'react-native-screens/src/fabric/ScreenNativeComponent';
|
|
54
|
+
import 'react-native-screens/src/fabric/ModalScreenNativeComponent';
|
|
55
|
+
import 'react-native-screens/src/fabric/ScreenStackNativeComponent';
|
|
56
|
+
import 'react-native-screens/src/fabric/ScreenStackHeaderConfigNativeComponent';
|
|
57
|
+
import 'react-native-screens/src/fabric/ScreenStackHeaderSubviewNativeComponent';
|
|
58
|
+
import 'react-native-screens/src/fabric/SearchBarNativeComponent';
|
|
59
|
+
import 'react-native-screens/src/fabric/ScreenContentWrapperNativeComponent';
|