@apexnative/navigation 1.0.0-alpha.5 → 1.0.0-alpha.7
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/ApexNavigation.podspec +2 -1
- package/CHANGELOG.md +32 -0
- package/README.md +1 -1
- package/android/CLAUDE.md +31 -20
- package/android/CORE.md +12 -8
- package/android/consumer-rules.pro +6 -0
- package/android/src/main/java/dev/apexnative/navigation/NavigationActivity.kt +36 -5
- package/android/src/main/java/dev/apexnative/navigation/options/BottomTabOptions.kt +1 -1
- package/android/src/main/java/dev/apexnative/navigation/options/BottomTabsOptions.kt +13 -8
- package/android/src/main/java/dev/apexnative/navigation/options/ButtonOptions.kt +1 -1
- package/android/src/main/java/dev/apexnative/navigation/options/LargeTitleOptions.kt +1 -1
- package/android/src/main/java/dev/apexnative/navigation/options/LayoutAnimation.kt +1 -1
- package/android/src/main/java/dev/apexnative/navigation/options/OptionKey.kt +18 -0
- package/android/src/main/java/dev/apexnative/navigation/options/OptionPaths.kt +91 -0
- package/android/src/main/java/dev/apexnative/navigation/options/SheetOptions.kt +7 -4
- package/android/src/main/java/dev/apexnative/navigation/options/TitleOptions.kt +2 -2
- package/android/src/main/java/dev/apexnative/navigation/options/TopBarAppearanceOptions.kt +1 -1
- package/android/src/main/java/dev/apexnative/navigation/options/TopBarOptions.kt +16 -7
- package/android/src/main/java/dev/apexnative/navigation/react/ContentHeights.kt +95 -0
- package/android/src/main/java/dev/apexnative/navigation/react/ContentMounts.kt +66 -0
- package/android/src/main/java/dev/apexnative/navigation/react/NavigationTurboModule.kt +7 -3
- package/android/src/main/java/dev/apexnative/navigation/react/OptionsCommands.kt +41 -3
- package/android/src/main/java/dev/apexnative/navigation/react/ReactView.kt +16 -1
- package/android/src/main/java/dev/apexnative/navigation/react/ReloadCoordinator.kt +0 -2
- package/android/src/main/java/dev/apexnative/navigation/react/ScreenEnvironment.kt +177 -0
- package/android/src/main/java/dev/apexnative/navigation/react/ScreenRegions.kt +12 -49
- package/android/src/main/java/dev/apexnative/navigation/react/ScrollEvents.kt +49 -128
- package/android/src/main/java/dev/apexnative/navigation/safearea/SafeAreaView.kt +6 -6
- package/android/src/main/java/dev/apexnative/navigation/slots/SlotScreenView.kt +27 -7
- package/android/src/main/java/dev/apexnative/navigation/slots/SlotView.kt +12 -1
- package/android/src/main/java/dev/apexnative/navigation/utils/MaterialTheme.kt +5 -6
- package/android/src/main/java/dev/apexnative/navigation/utils/SystemAnimations.kt +36 -0
- package/android/src/main/java/dev/apexnative/navigation/utils/ViewExtensions.kt +0 -13
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/arrangement/ArrangementController.kt +9 -7
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/BottomTabsCollapseBehavior.kt +5 -3
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/BottomTabsController.kt +13 -10
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/CustomTabRowGeometry.kt +22 -0
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/TabsRoom.kt +0 -5
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/ComponentViewController.kt +7 -8
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/ScreenSafeArea.kt +29 -11
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/externalcomponent/ExternalComponentViewController.kt +2 -6
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalCoordinator.kt +20 -2
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalSheet.kt +443 -58
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalViews.kt +33 -5
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/SheetBack.kt +42 -0
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/SheetStops.kt +60 -2
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/navigator/Navigator.kt +4 -0
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/parent/ParentController.kt +3 -6
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/sidemenu/SideMenuMotion.kt +8 -2
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/BackPreview.kt +32 -4
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/PreparedFrame.kt +3 -3
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackCommands.kt +2 -2
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackController.kt +50 -7
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackQueue.kt +15 -3
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackViews.kt +10 -4
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/fab/FabMotion.kt +142 -0
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/fab/FabPresenter.kt +215 -43
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/search/StackSearchPresenter.kt +25 -26
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/topbar/BarRowTransition.kt +3 -2
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/topbar/ButtonPlaces.kt +11 -5
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/topbar/TopBarPresenter.kt +36 -38
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/viewcontroller/ScrollEventListener.kt +6 -9
- package/android/src/main/java/dev/apexnative/navigation/views/animations/AnimationLayer.kt +73 -0
- package/android/src/main/java/dev/apexnative/navigation/views/animations/ValueInterpolation.kt +14 -0
- package/android/src/main/java/dev/apexnative/navigation/views/bottomtabs/BottomTabsLayout.kt +61 -2
- package/android/src/main/java/dev/apexnative/navigation/views/bottomtabs/CustomTabRow.kt +72 -10
- package/android/src/main/java/dev/apexnative/navigation/views/element/ElementTransitions.kt +129 -179
- package/android/src/main/java/dev/apexnative/navigation/views/element/TargetsWait.kt +8 -1
- package/android/src/main/java/dev/apexnative/navigation/views/element/TransitionLayer.kt +37 -112
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ElementPictures.kt +67 -3
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/FlightGeometry.kt +346 -36
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ImageMorph.kt +199 -0
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/SharedAnimatorFactory.kt +55 -42
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/TextMorph.kt +176 -0
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ViewMorph.kt +108 -0
- package/android/src/main/java/dev/apexnative/navigation/views/stack/fab/FabCopy.kt +85 -0
- package/android/src/main/java/dev/apexnative/navigation/views/stack/fab/FabMenu.kt +13 -0
- package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/TitleBar.kt +5 -24
- package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/TopBar.kt +51 -34
- package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/titlebar/BarButtonComponentCell.kt +6 -6
- package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/titlebar/BarButtonView.kt +113 -0
- package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/titlebar/BarMenu.kt +219 -0
- package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/titlebar/ButtonBar.kt +121 -175
- package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/titlebar/ButtonMenus.kt +34 -64
- package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/titlebar/ButtonRow.kt +216 -0
- package/android/src/main/jni/ANNContentHeights.cpp +82 -0
- package/android/src/main/res/drawable/apex_navigation_ic_arrow_right.xml +14 -0
- package/android/src/main/res/layout/apex_navigation_menu_header.xml +19 -0
- package/android/src/main/res/layout/apex_navigation_menu_item.xml +68 -0
- package/android/src/main/res/values/styles.xml +1 -1
- package/android/src/main/res/values-night/styles.xml +1 -1
- package/cli/doctor/checks/app.js +16 -1
- package/cli/project.js +4 -0
- package/cli/setup/index.js +5 -0
- package/cli/setup/steps/metro.js +46 -0
- package/docs-md/architecture/native-screens.md +4 -0
- package/docs-md/architecture/theme-and-context.md +131 -12
- package/docs-md/architecture/transitions.md +46 -5
- package/docs-md/containers/modals.md +2 -0
- package/docs-md/containers/overlays.md +1 -1
- package/docs-md/containers/side-menu.md +1 -1
- package/docs-md/containers/tabs.md +2 -0
- package/docs-md/flows/bottom-sheets.md +4 -2
- package/docs-md/fundamentals/screens-and-props.md +9 -1
- package/docs-md/fundamentals/starting-the-app.md +1 -1
- package/docs-md/guides/cli.md +3 -1
- package/docs-md/help/glossary.md +2 -2
- package/docs-md/help/troubleshooting.md +22 -1
- package/docs-md/index.json +41 -1
- package/docs-md/migration/from-expo-router.md +1 -1
- package/docs-md/migration/from-react-native-navigation.md +1 -1
- package/docs-md/migration/from-react-navigation.md +2 -2
- package/docs-md/screen-ui/back-and-gestures.md +17 -1
- package/docs-md/screen-ui/bar-buttons.md +5 -5
- package/docs-md/screen-ui/search.md +1 -1
- package/docs-md/screen-ui/system-ui.md +16 -11
- package/docs-md/screen-ui/toolbar-and-fab.md +4 -2
- package/docs-md/screen-ui/top-bar.md +8 -4
- package/docs-md/start/installation.md +16 -1
- package/ios/Animations/ANNFrameDriver.h +55 -15
- package/ios/Animations/ANNFrameDriver.mm +216 -60
- package/ios/Animations/ANNTransitionAnimator.h +26 -5
- package/ios/Animations/ANNTransitionAnimator.mm +49 -3
- package/ios/Animations/ANNTransitionPlan.h +2 -2
- package/ios/Animations/ANNTransitionPlan.mm +10 -0
- package/ios/Animations/Elements/ANNElementFinder.h +2 -2
- package/ios/Animations/Elements/ANNElementFinder.mm +2 -2
- package/ios/Animations/Elements/ANNElementTransition.h +3 -3
- package/ios/Animations/Elements/ANNElementTransition.mm +2 -2
- package/ios/Animations/Elements/ANNSharedElementFlight.h +3 -3
- package/ios/Animations/Elements/ANNSharedElementFlight.mm +249 -45
- package/ios/Animations/Interpolators/ANNInterpolate.h +2 -30
- package/ios/Animations/Interpolators/ANNTransformParts.h +48 -0
- package/ios/Animations/Interpolators/ANNTransformParts.mm +507 -0
- package/ios/Animations/Interpolators/ANNViewTracks.h +9 -6
- package/ios/Animations/Interpolators/ANNViewTracks.mm +100 -30
- package/ios/CLAUDE.md +32 -22
- package/ios/CORE.md +25 -21
- package/ios/Contract/ANNCommandRunner.h +8 -7
- package/ios/Contract/ANNCommandRunner.mm +21 -27
- package/ios/Contract/ANNLayoutNode.h +3 -3
- package/ios/Contract/ANNLayoutReader.h +3 -3
- package/ios/Core/ANNAppDelegate.mm +0 -2
- package/ios/Core/ANNDeepLinkQueue.h +6 -5
- package/ios/Core/ANNDeepLinkQueue.mm +22 -10
- package/ios/Core/ANNJavaScriptCalls.h +11 -12
- package/ios/Core/ANNJavaScriptCalls.mm +7 -16
- package/ios/Core/ANNReloadCoordinator.mm +4 -8
- package/ios/Core/ANNSceneDelegate.mm +2 -2
- package/ios/Core/ApexNavigation+Internal.h +1 -1
- package/ios/Core/ApexNavigation.mm +6 -2
- package/ios/Layouts/Arrangement/ANNArrangementState.h +2 -17
- package/ios/Layouts/Arrangement/ANNArrangementState.mm +0 -38
- package/ios/Layouts/Arrangement/ANNArrangementViewController.mm +4 -4
- package/ios/Layouts/Root/ANNRootCoordinator.mm +45 -15
- package/ios/Layouts/Screen/ANNComponentViewController.h +2 -2
- package/ios/Layouts/Screen/ANNComponentViewController.mm +13 -23
- package/ios/Layouts/Screen/ANNPreviewInteraction.h +7 -4
- package/ios/Layouts/Screen/ANNPreviewInteraction.mm +72 -10
- package/ios/Layouts/Screen/ANNScreenEnvironment.h +63 -0
- package/ios/Layouts/Screen/ANNScreenEnvironment.mm +181 -0
- package/ios/Layouts/Screen/ANNScreenRegions.h +3 -18
- package/ios/Layouts/Screen/ANNScreenRegions.mm +0 -38
- package/ios/Layouts/Screen/ANNScreenSafeArea.h +4 -9
- package/ios/Layouts/Screen/ANNScreenSafeArea.mm +14 -16
- package/ios/Layouts/Stack/ANNPreviewPush.h +7 -6
- package/ios/Layouts/Stack/ANNPreviewPush.mm +57 -5
- package/ios/Layouts/Stack/ANNStackAnimatingDelegate.h +11 -2
- package/ios/Layouts/Stack/ANNStackAnimatingDelegate.mm +14 -7
- package/ios/Layouts/Stack/ANNStackController.h +8 -0
- package/ios/Layouts/Stack/ANNStackController.mm +153 -11
- package/ios/Layouts/Stack/ANNSwipePopInteraction.h +26 -0
- package/ios/Layouts/Stack/ANNSwipePopInteraction.mm +185 -0
- package/ios/Layouts/Stack/InteractivePopGestureDelegate.mm +28 -4
- package/ios/Layouts/Tabs/ANNBottomTabsAccessoryView.h +12 -1
- package/ios/Layouts/Tabs/ANNBottomTabsAccessoryView.mm +23 -0
- package/ios/Layouts/Tabs/ANNBottomTabsController.mm +2 -1
- package/ios/Layouts/Tabs/ANNBottomTabsCustomRow.mm +8 -3
- package/ios/Layouts/Tabs/ANNBottomTabsPresenter.h +4 -0
- package/ios/Layouts/Tabs/ANNBottomTabsPresenter.mm +123 -36
- package/ios/Layouts/Tabs/ANNTabsDesign.h +6 -5
- package/ios/Layouts/Tabs/ANNTabsDesign.mm +9 -1
- package/ios/Layouts/Tabs/UITabBar+utils.h +5 -5
- package/ios/Layouts/Tabs/UITabBar+utils.mm +32 -13
- package/ios/Options/ANNAnimationOptions.h +2 -1
- package/ios/Options/ANNAnimationOptions.mm +1 -1
- package/ios/Options/ANNBottomTabsOptions.h +2 -0
- package/ios/Options/ANNBottomTabsOptions.mm +1 -0
- package/ios/Options/ANNOptions.h +25 -0
- package/ios/Options/ANNOptions.mm +157 -1
- package/ios/Options/ANNOptionsReader.mm +108 -0
- package/ios/Options/ANNStackPopAnimationOptions.h +13 -0
- package/ios/Options/ANNStackPopAnimationOptions.mm +11 -0
- package/ios/Options/Params/ANNParam.h +15 -0
- package/ios/Options/Params/ANNParam.mm +61 -0
- package/ios/Options/Params/Color.h +3 -0
- package/ios/Options/Params/Color.mm +21 -0
- package/ios/Options/Params/ColorParser.h +4 -3
- package/ios/Options/Params/ColorParser.mm +22 -4
- package/ios/Options/Params/Image.h +4 -0
- package/ios/Options/Params/Image.mm +20 -0
- package/ios/React/ANNReactView.mm +6 -25
- package/ios/React/ANNSafeAreaViewComponentView.mm +26 -15
- package/ios/React/ANNSurfaceHostingView.h +14 -0
- package/ios/React/ANNSurfaceHostingView.mm +144 -1
- package/ios/React/Slots/ANNSlotScreenView.mm +8 -2
- package/ios/TopBar/ANNBackButtonPresenter.mm +6 -5
- package/ios/TopBar/ANNBarAppearance.h +2 -1
- package/ios/TopBar/ANNBarAppearance.mm +111 -30
- package/ios/TopBar/ANNBarBackgroundComponent.mm +16 -0
- package/ios/TopBar/ANNBarButtonComponentView.h +4 -4
- package/ios/TopBar/ANNBarButtonComponentView.mm +1 -1
- package/ios/TopBar/ANNBarButtons.h +5 -3
- package/ios/TopBar/ANNBarButtons.mm +186 -17
- package/ios/TopBar/ANNLargeTitlePresenter.mm +24 -4
- package/ios/TopBar/ANNMainList.h +3 -0
- package/ios/TopBar/ANNMainList.mm +4 -0
- package/ios/TopBar/ANNScrollEdgeEffects.h +22 -6
- package/ios/TopBar/ANNScrollEdgeEffects.mm +123 -16
- package/ios/TopBar/ANNSearchPresenter.mm +2 -2
- package/ios/TopBar/ANNSearchResultsViewController.mm +5 -5
- package/ios/TopBar/ANNTopBarPresenter.h +3 -2
- package/ios/TopBar/ANNTopBarPresenter.mm +95 -22
- package/ios/TurboModules/ANNTurboModule.mm +66 -16
- package/ios/cpp/react/renderer/components/ApexNavigationSpec/ANNSlotState.cpp +20 -0
- package/ios/cpp/react/renderer/components/ApexNavigationSpec/ANNSlotState.h +5 -14
- package/lib/module/ARCHITECTURE.md +17 -7
- package/lib/module/adapters/NativeANNTurboModule.js.map +1 -1
- package/lib/module/api/app/AppRegistry.js +14 -2
- package/lib/module/api/app/AppRegistry.js.map +1 -1
- package/lib/module/api/app/createNavigation.js +188 -66
- package/lib/module/api/app/createNavigation.js.map +1 -1
- package/lib/module/api/events/navigationEvents.js +6 -2
- package/lib/module/api/events/navigationEvents.js.map +1 -1
- package/lib/module/api/hooks/useSafeAreaInsets.js +4 -3
- package/lib/module/api/hooks/useSafeAreaInsets.js.map +1 -1
- package/lib/module/api/linking/linking.js +11 -0
- package/lib/module/api/linking/linking.js.map +1 -1
- package/lib/module/api/options/OptionsChain.js +2 -2
- package/lib/module/api/screens/defineScreen.js +45 -3
- package/lib/module/api/screens/defineScreen.js.map +1 -1
- package/lib/module/api/screens/nativeScreens.js +31 -6
- package/lib/module/api/screens/nativeScreens.js.map +1 -1
- package/lib/module/api/screens/registerScreens.js +172 -35
- package/lib/module/api/screens/registerScreens.js.map +1 -1
- package/lib/module/api/theme/theme.js +5 -0
- package/lib/module/api/theme/theme.js.map +1 -1
- package/lib/module/engine/commands.js +42 -2
- package/lib/module/engine/commands.js.map +1 -1
- package/lib/module/engine/layout.js +7 -2
- package/lib/module/engine/layout.js.map +1 -1
- package/lib/module/engine/nativeScreens.js +9 -0
- package/lib/module/engine/nativeScreens.js.map +1 -1
- package/lib/module/engine/options.js +5 -4
- package/lib/module/engine/options.js.map +1 -1
- package/lib/module/engine/optionsChange.js +102 -0
- package/lib/module/engine/optionsChange.js.map +1 -0
- package/lib/module/engine/refresh.js +85 -0
- package/lib/module/engine/refresh.js.map +1 -0
- package/lib/module/engine/rememberedOptions.js +60 -0
- package/lib/module/engine/rememberedOptions.js.map +1 -1
- package/lib/module/engine/screens.js +55 -4
- package/lib/module/engine/screens.js.map +1 -1
- package/lib/typescript/adapters/NativeANNTurboModule.d.ts +7 -0
- package/lib/typescript/adapters/NativeANNTurboModule.d.ts.map +1 -1
- package/lib/typescript/api/app/AppRegistry.d.ts.map +1 -1
- package/lib/typescript/api/app/createNavigation.d.ts +8 -3
- package/lib/typescript/api/app/createNavigation.d.ts.map +1 -1
- package/lib/typescript/api/events/navigationEvents.d.ts +2 -2
- package/lib/typescript/api/events/navigationEvents.d.ts.map +1 -1
- package/lib/typescript/api/hooks/useSafeAreaInsets.d.ts +4 -3
- package/lib/typescript/api/hooks/useSafeAreaInsets.d.ts.map +1 -1
- package/lib/typescript/api/index.d.ts +1 -1
- package/lib/typescript/api/index.d.ts.map +1 -1
- package/lib/typescript/api/linking/linking.d.ts +8 -0
- package/lib/typescript/api/linking/linking.d.ts.map +1 -1
- package/lib/typescript/api/options/OptionsChain.d.ts +2 -2
- package/lib/typescript/api/screens/defineScreen.d.ts +23 -4
- package/lib/typescript/api/screens/defineScreen.d.ts.map +1 -1
- package/lib/typescript/api/screens/nativeScreens.d.ts +8 -1
- package/lib/typescript/api/screens/nativeScreens.d.ts.map +1 -1
- package/lib/typescript/api/screens/registerScreens.d.ts.map +1 -1
- package/lib/typescript/api/theme/theme.d.ts +2 -0
- package/lib/typescript/api/theme/theme.d.ts.map +1 -1
- package/lib/typescript/engine/commands.d.ts +5 -0
- package/lib/typescript/engine/commands.d.ts.map +1 -1
- package/lib/typescript/engine/layout.d.ts.map +1 -1
- package/lib/typescript/engine/nativeScreens.d.ts +5 -0
- package/lib/typescript/engine/nativeScreens.d.ts.map +1 -1
- package/lib/typescript/engine/optionsChange.d.ts +28 -0
- package/lib/typescript/engine/optionsChange.d.ts.map +1 -0
- package/lib/typescript/engine/refresh.d.ts +35 -0
- package/lib/typescript/engine/refresh.d.ts.map +1 -0
- package/lib/typescript/engine/rememberedOptions.d.ts +13 -0
- package/lib/typescript/engine/rememberedOptions.d.ts.map +1 -1
- package/lib/typescript/engine/screens.d.ts +27 -6
- package/lib/typescript/engine/screens.d.ts.map +1 -1
- package/lib/typescript/interfaces/ComponentEvents.d.ts +2 -2
- package/lib/typescript/interfaces/options/animations.d.ts +44 -14
- package/lib/typescript/interfaces/options/animations.d.ts.map +1 -1
- package/lib/typescript/interfaces/options/bottomTabs.d.ts +19 -6
- package/lib/typescript/interfaces/options/bottomTabs.d.ts.map +1 -1
- package/lib/typescript/interfaces/options/buttons.d.ts +10 -10
- package/lib/typescript/interfaces/options/modals.d.ts +6 -7
- package/lib/typescript/interfaces/options/modals.d.ts.map +1 -1
- package/lib/typescript/interfaces/options/stack.d.ts +8 -3
- package/lib/typescript/interfaces/options/stack.d.ts.map +1 -1
- package/lib/typescript/interfaces/options/system.d.ts +8 -5
- package/lib/typescript/interfaces/options/system.d.ts.map +1 -1
- package/lib/typescript/interfaces/options/toolbar.d.ts +2 -2
- package/lib/typescript/interfaces/options/topBar.d.ts +46 -29
- package/lib/typescript/interfaces/options/topBar.d.ts.map +1 -1
- package/metro/acceptPlugin.js +57 -0
- package/metro/babelTransformer.js +45 -0
- package/metro/index.js +36 -0
- package/package.json +3 -1
- package/src/ARCHITECTURE.md +17 -7
- package/src/adapters/NativeANNTurboModule.ts +7 -0
- package/src/api/app/AppRegistry.ts +16 -2
- package/src/api/app/createNavigation.ts +201 -69
- package/src/api/events/navigationEvents.ts +9 -3
- package/src/api/hooks/useSafeAreaInsets.ts +4 -3
- package/src/api/index.ts +1 -0
- package/src/api/linking/linking.ts +22 -1
- package/src/api/options/OptionsChain.ts +2 -2
- package/src/api/screens/defineScreen.ts +49 -5
- package/src/api/screens/nativeScreens.ts +40 -4
- package/src/api/screens/registerScreens.tsx +170 -42
- package/src/api/theme/theme.ts +5 -0
- package/src/engine/commands.ts +51 -6
- package/src/engine/layout.ts +11 -2
- package/src/engine/nativeScreens.ts +10 -1
- package/src/engine/options.ts +6 -8
- package/src/engine/optionsChange.ts +107 -0
- package/src/engine/refresh.ts +97 -0
- package/src/engine/rememberedOptions.ts +70 -0
- package/src/engine/screens.tsx +53 -8
- package/src/interfaces/ComponentEvents.ts +2 -2
- package/src/interfaces/options/animations.ts +44 -14
- package/src/interfaces/options/bottomTabs.ts +19 -6
- package/src/interfaces/options/buttons.ts +10 -10
- package/src/interfaces/options/modals.ts +6 -7
- package/src/interfaces/options/stack.ts +8 -3
- package/src/interfaces/options/system.ts +8 -5
- package/src/interfaces/options/toolbar.ts +2 -2
- package/src/interfaces/options/topBar.ts +46 -29
- package/android/src/main/java/dev/apexnative/navigation/react/ArrangementStates.kt +0 -42
- package/android/src/main/java/dev/apexnative/navigation/react/ScreenInsets.kt +0 -70
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/CustomTabRowPlacer.kt +0 -223
- package/android/src/main/java/dev/apexnative/navigation/views/element/ElementStub.kt +0 -31
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ImageMatrixAnimator.kt +0 -136
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/PlainViewAnimator.kt +0 -86
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/PropertyAnimatorCreator.kt +0 -33
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ReactImageAnimator.kt +0 -64
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/TextChangeAnimator.kt +0 -177
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ViewChangeAnimator.kt +0 -137
- package/ios/Layouts/Screen/ANNScreenInsets.h +0 -30
- package/ios/Layouts/Screen/ANNScreenInsets.mm +0 -70
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
// Fast Refresh runs an edited module again, and every component it exports is a new function. React Refresh keeps
|
|
2
|
+
// the versions of one component in a family: Metro registers each export under its module and name, and React renders
|
|
3
|
+
// the newest version of the family in place of the old one. What the library keeps of a screen (its options, its key,
|
|
4
|
+
// its pair) is read through the same family, so an edit reaches it without a reload.
|
|
5
|
+
|
|
6
|
+
/** The versions of one component React Refresh keeps, `current` the newest. */
|
|
7
|
+
interface Family {
|
|
8
|
+
current: unknown;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
interface ReactRefresh {
|
|
12
|
+
getFamilyByType?: (type: unknown) => Family | undefined;
|
|
13
|
+
register?: (type: unknown, id: string) => void;
|
|
14
|
+
createSignatureFunctionForTransform?: () => (
|
|
15
|
+
type: unknown,
|
|
16
|
+
key: string,
|
|
17
|
+
forceReset?: boolean
|
|
18
|
+
) => unknown;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// React Native puts React Refresh where Metro's module system finds it, in development only.
|
|
22
|
+
function reactRefresh(): ReactRefresh | undefined {
|
|
23
|
+
if (!__DEV__) return undefined;
|
|
24
|
+
const global = globalThis as { __METRO_GLOBAL_PREFIX__?: string } & Record<string, unknown>;
|
|
25
|
+
return global[`${global.__METRO_GLOBAL_PREFIX__ ?? ''}__ReactRefresh`] as
|
|
26
|
+
ReactRefresh | undefined;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** Whether React Refresh runs: development with Metro. */
|
|
30
|
+
export function refreshRuns(): boolean {
|
|
31
|
+
return reactRefresh()?.getFamilyByType !== undefined;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** The family a component's versions are kept in; undefined in a release build or for a value Metro didn't export. */
|
|
35
|
+
export function familyOf(type: unknown): Family | undefined {
|
|
36
|
+
return reactRefresh()?.getFamilyByType?.(type);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* The newest version of a component after Fast Refresh ran its module again; the component itself when there is none
|
|
41
|
+
* newer, and in a release build.
|
|
42
|
+
*/
|
|
43
|
+
export function newestType<T extends object>(type: T): T {
|
|
44
|
+
return (familyOf(type)?.current as T | undefined) ?? type;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
let redefinedListener: ((screen: object) => void) | undefined;
|
|
48
|
+
|
|
49
|
+
/** Sets what runs when a module makes a screen again: Fast Refresh ran it after an edit. */
|
|
50
|
+
export function onRedefined(listener: (screen: object) => void): void {
|
|
51
|
+
redefinedListener = listener;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Tells about a screen a module made, once the module ran: Metro registers its exports after it, so the family of the
|
|
56
|
+
* screen is known by then, and a new version of a screen that shows is found by it. Development only.
|
|
57
|
+
*/
|
|
58
|
+
export function redefined(screen: object): void {
|
|
59
|
+
if (!__DEV__ || !redefinedListener) return;
|
|
60
|
+
const listener = redefinedListener;
|
|
61
|
+
Promise.resolve().then(() => listener(screen));
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// The id each family of screens gives the components they render, so their versions join one family too.
|
|
65
|
+
const innerIds = new WeakMap<Family, string>();
|
|
66
|
+
let innerCount = 0;
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Puts a component a screen or the app renders (`owner`: the screen, or the app made by createNavigation; `role`: which
|
|
70
|
+
* of its components) in a family of its own, once the owner's module ran and Metro has given the owner its family: the
|
|
71
|
+
* next version of the owner registers its component under the same id, so React Refresh keeps the component's state
|
|
72
|
+
* when the module runs again, also when the module doesn't export it. A component React Refresh knows already (its
|
|
73
|
+
* module exports it, or Babel registered it) keeps its own family. Development only.
|
|
74
|
+
*/
|
|
75
|
+
export function keepStateOf(owner: object, component: unknown, role = 'component'): void {
|
|
76
|
+
const refresh = reactRefresh();
|
|
77
|
+
if (!refresh?.register || component === undefined) return;
|
|
78
|
+
const register = refresh.register;
|
|
79
|
+
Promise.resolve().then(() => {
|
|
80
|
+
const family = familyOf(owner);
|
|
81
|
+
if (!family) return;
|
|
82
|
+
let id = innerIds.get(family);
|
|
83
|
+
if (id === undefined) {
|
|
84
|
+
id = `apexnative.navigation#${++innerCount}`;
|
|
85
|
+
innerIds.set(family, id);
|
|
86
|
+
}
|
|
87
|
+
register(component, `${id}.${role}`);
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Makes React Refresh mount a component of the library again, not keep its state, when a new version of it comes: its
|
|
93
|
+
* hooks are the app's, which may have changed, and the library's code has no signature of them. Development only.
|
|
94
|
+
*/
|
|
95
|
+
export function resetOnEdit(type: object): void {
|
|
96
|
+
reactRefresh()?.createSignatureFunctionForTransform?.()(type, 'apexnative.navigation', true);
|
|
97
|
+
}
|
|
@@ -29,9 +29,77 @@ export function rememberMergedOptions(id: string, options: Options): void {
|
|
|
29
29
|
remembered.set(id, before ? mergeOptions(before, kept) : kept);
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
+
/** Takes the options at the paths, such as `['topBar', 'subtitle']`, out of those remembered for the node with the id. */
|
|
33
|
+
export function forgetMergedPaths(id: string, paths: readonly string[][]): void {
|
|
34
|
+
const own = ownOptions.get(id);
|
|
35
|
+
if (own) own.options = withoutPaths(own.options, paths);
|
|
36
|
+
const options = remembered.get(id);
|
|
37
|
+
if (options) remembered.set(id, withoutPaths(options, paths));
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// A copy of the options without the values at the paths.
|
|
41
|
+
function withoutPaths(options: Options, paths: readonly string[][]): Options {
|
|
42
|
+
const kept = structuredCopy(options);
|
|
43
|
+
for (const path of paths) {
|
|
44
|
+
const parent = path
|
|
45
|
+
.slice(0, -1)
|
|
46
|
+
.reduce<Record<string, unknown> | undefined>(
|
|
47
|
+
(group, key) => group?.[key] as Record<string, unknown> | undefined,
|
|
48
|
+
kept
|
|
49
|
+
);
|
|
50
|
+
if (parent) delete parent[path[path.length - 1]!];
|
|
51
|
+
}
|
|
52
|
+
return kept as Options;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// A copy of the plain objects of options, the other values shared.
|
|
56
|
+
function structuredCopy(options: Options): Record<string, unknown> {
|
|
57
|
+
return Object.fromEntries(
|
|
58
|
+
Object.entries(options).map(([key, value]) => [
|
|
59
|
+
key,
|
|
60
|
+
isGroup(value) ? structuredCopy(value as Options) : value,
|
|
61
|
+
])
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function isGroup(value: unknown): boolean {
|
|
66
|
+
return (
|
|
67
|
+
typeof value === 'object' && value !== null && Object.getPrototypeOf(value) === Object.prototype
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
|
|
32
71
|
/** Forgets the merged options of a node that went; a node of the last root keeps them until another root goes out. */
|
|
33
72
|
export function forgetMergedOptions(id: string): void {
|
|
34
73
|
if (!lastRoot?.ids.has(id)) remembered.delete(id);
|
|
74
|
+
ownOptions.delete(id);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// The own options of each screen as JavaScript sent them, in development: those it showed with, and every merge and
|
|
78
|
+
// clear since. An edit of a screen's options (Fast Refresh) changes only what still has the old definition's value.
|
|
79
|
+
const ownOptions = new Map<string, { name: string; options: Options }>();
|
|
80
|
+
|
|
81
|
+
/** Notes the own options a screen of the name shows with (development). */
|
|
82
|
+
export function noteOwnOptions(id: string, name: string, options: Options): void {
|
|
83
|
+
if (__DEV__) ownOptions.set(id, { name, options });
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** Notes a merge into a screen's own options (development). */
|
|
87
|
+
export function noteMergedOwnOptions(id: string, options: Options): void {
|
|
88
|
+
const noted = __DEV__ ? ownOptions.get(id) : undefined;
|
|
89
|
+
if (noted) noted.options = mergeOptions(noted.options, options);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** The own options of a screen as JavaScript sent them (development); undefined for a screen it didn't note. */
|
|
93
|
+
export function ownOptionsOf(id: string): Options | undefined {
|
|
94
|
+
return ownOptions.get(id)?.options;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* The ids of the screens of the name the native side has, mounted in React or not yet (a tab not selected yet), as
|
|
99
|
+
* development notes them.
|
|
100
|
+
*/
|
|
101
|
+
export function notedScreens(name: string): string[] {
|
|
102
|
+
return [...ownOptions].filter(([, noted]) => noted.name === name).map(([id]) => id);
|
|
35
103
|
}
|
|
36
104
|
|
|
37
105
|
/** The options merged into the node with the id, as remembered; undefined without any. */
|
|
@@ -55,6 +123,8 @@ export function rootGoesOut(nodes: LayoutNode[], again: SentRoot | undefined): v
|
|
|
55
123
|
const before = lastRoot;
|
|
56
124
|
lastRoot = { nodes, ids: new Set(nodes.flatMap(idsOf)) };
|
|
57
125
|
if (!before) return;
|
|
126
|
+
// The screens of the root before go with it, the tabs React never mounted too.
|
|
127
|
+
for (const id of before.ids) if (!lastRoot.ids.has(id)) ownOptions.delete(id);
|
|
58
128
|
const moved = new Map<string, Options>();
|
|
59
129
|
if (again === before) {
|
|
60
130
|
pairNodes(before.nodes, nodes, (old, node) => {
|
package/src/engine/screens.tsx
CHANGED
|
@@ -52,8 +52,15 @@ export type EngineScreen =
|
|
|
52
52
|
/** What the root of a React screen renders: the screen's id, and the props the command gave it. */
|
|
53
53
|
export type ScreenRootProps = { componentId: string } & Record<string, unknown>;
|
|
54
54
|
|
|
55
|
+
/**
|
|
56
|
+
* What React Native gives the root of a React screen: the screen's id, its root tag (none in a slot of the app's one
|
|
57
|
+
* tree), and the props the native side gives that root, a tab cell's `tabIndex`, `selected`, `badge`, `dot` and
|
|
58
|
+
* `dotColor`.
|
|
59
|
+
*/
|
|
60
|
+
export type NativeRootProps = { componentId: string; rootTag?: number } & Record<string, unknown>;
|
|
61
|
+
|
|
55
62
|
const screens = new Map<string, EngineScreen>();
|
|
56
|
-
const roots = new Map<string, ComponentType<
|
|
63
|
+
const roots = new Map<string, ComponentType<NativeRootProps>>();
|
|
57
64
|
let unmounted: ((screenId: string) => void) | undefined;
|
|
58
65
|
// The React roots mounted under each id, by their root tags. One id can have several: after a JavaScript reload React
|
|
59
66
|
// Native starts the roots of the old screens again in the new runtime, under the ids the new screens get too, and
|
|
@@ -61,6 +68,34 @@ let unmounted: ((screenId: string) => void) | undefined;
|
|
|
61
68
|
// bar of every screen that shows it. The id lets its props go only with the last of them. A slot of the app's one tree
|
|
62
69
|
// has no tag: while the one tree shows a screen with the id, no root of that id lets it go.
|
|
63
70
|
const owners = new Map<string, Set<number>>();
|
|
71
|
+
// The screens that show by their ids: the name, and how many roots or slots of the id are mounted (a component of the
|
|
72
|
+
// options has a root in the bar of every screen that shows it).
|
|
73
|
+
const shown = new Map<string, { name: string; mounts: number }>();
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Notes a screen as shown, a React root or slot of it or the controller of a native screen, until the function it
|
|
77
|
+
* gives is called.
|
|
78
|
+
*/
|
|
79
|
+
export function noteShown(componentId: string, name: string): () => void {
|
|
80
|
+
const entry = shown.get(componentId);
|
|
81
|
+
if (entry?.name === name) entry.mounts++;
|
|
82
|
+
else shown.set(componentId, { name, mounts: 1 });
|
|
83
|
+
return () => {
|
|
84
|
+
const current = shown.get(componentId);
|
|
85
|
+
if (current?.name !== name) return;
|
|
86
|
+
if (--current.mounts === 0) shown.delete(componentId);
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** The ids of the screens with the name that show now. */
|
|
91
|
+
export function shownScreens(name: string): string[] {
|
|
92
|
+
return [...shown].filter(([, entry]) => entry.name === name).map(([id]) => id);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** Whether a React root of the screen with the id is mounted now (a slot of the app's one tree doesn't count). */
|
|
96
|
+
export function hasRoot(componentId: string): boolean {
|
|
97
|
+
return owners.has(componentId);
|
|
98
|
+
}
|
|
64
99
|
|
|
65
100
|
/** What runs when the root of a screen unmounts: the screen is gone. */
|
|
66
101
|
export function onScreenUnmounted(listener: (screenId: string) => void): void {
|
|
@@ -193,20 +228,29 @@ export function isReactScreen(name: string): boolean {
|
|
|
193
228
|
return screen !== undefined && 'component' in screen;
|
|
194
229
|
}
|
|
195
230
|
|
|
231
|
+
/**
|
|
232
|
+
* The props a screen's component renders with: those a command left under its id ([stored]), the props the native side
|
|
233
|
+
* gave its root over them ([root]: a tab cell's state, which the docs promise arrives on top of `passProps`), and its
|
|
234
|
+
* id. The root tag stays with the root.
|
|
235
|
+
*/
|
|
236
|
+
export function screenProps(stored: object, root: NativeRootProps): ScreenRootProps {
|
|
237
|
+
const props: ScreenRootProps = { ...stored, ...root, componentId: root.componentId };
|
|
238
|
+
delete props.rootTag;
|
|
239
|
+
return props;
|
|
240
|
+
}
|
|
241
|
+
|
|
196
242
|
/**
|
|
197
243
|
* The root a React screen runs in: the screen with the props a command left for it under its id, and the new ones when
|
|
198
|
-
* options give its id new props
|
|
199
|
-
*
|
|
244
|
+
* options give its id new props, under the props the native side gives the root ([screenProps]). The slot of the app's
|
|
245
|
+
* one tree renders the same root.
|
|
200
246
|
*/
|
|
201
|
-
export function screenRoot(
|
|
202
|
-
name: string
|
|
203
|
-
): ComponentType<{ componentId: string; rootTag?: number }> | undefined {
|
|
247
|
+
export function screenRoot(name: string): ComponentType<NativeRootProps> | undefined {
|
|
204
248
|
const screen = screens.get(name);
|
|
205
249
|
if (!screen || !('component' in screen)) return undefined;
|
|
206
250
|
let root = roots.get(name);
|
|
207
251
|
if (!root) {
|
|
208
252
|
const Screen = screen.component();
|
|
209
|
-
root = function ScreenRoot(rootProps:
|
|
253
|
+
root = function ScreenRoot(rootProps: NativeRootProps) {
|
|
210
254
|
// In the app's one tree the slot renders the root, without a tag of its own.
|
|
211
255
|
const { componentId, rootTag } = rootProps;
|
|
212
256
|
const watch = useCallback(
|
|
@@ -214,6 +258,7 @@ export function screenRoot(
|
|
|
214
258
|
[componentId]
|
|
215
259
|
);
|
|
216
260
|
const props = useSyncExternalStore(watch, () => propsOf(componentId));
|
|
261
|
+
useEffect(() => noteShown(componentId, name), [componentId]);
|
|
217
262
|
useEffect(() => {
|
|
218
263
|
if (rootTag !== undefined) {
|
|
219
264
|
const tags = owners.get(componentId) ?? new Set<number>();
|
|
@@ -240,7 +285,7 @@ export function screenRoot(
|
|
|
240
285
|
unmounted?.(componentId);
|
|
241
286
|
};
|
|
242
287
|
}, [componentId, rootTag]);
|
|
243
|
-
return <Screen {...props
|
|
288
|
+
return <Screen {...screenProps(props, rootProps)} />;
|
|
244
289
|
};
|
|
245
290
|
root.displayName = `ScreenRoot(${name})`;
|
|
246
291
|
roots.set(name, root);
|
|
@@ -95,8 +95,8 @@ export interface SheetDetentChangedEvent {
|
|
|
95
95
|
/** The id of the visible screen of the modal. */
|
|
96
96
|
componentId: string;
|
|
97
97
|
/**
|
|
98
|
-
* The detent the sheet stopped at: `'medium'`, `'large'`,
|
|
99
|
-
*
|
|
98
|
+
* The detent the sheet stopped at: `'medium'`, `'large'`, `'content'` (the detent that fits the content), or a
|
|
99
|
+
* height (points on iOS, dp on Android).
|
|
100
100
|
*/
|
|
101
101
|
detent: 'medium' | 'large' | 'content' | number;
|
|
102
102
|
}
|
|
@@ -258,10 +258,12 @@ export type ElementAnimation = AppearingElementAnimation | DisappearingElementAn
|
|
|
258
258
|
* the way (an error in its animation) is skipped too, with a warning in the log: it shows and hides with its screen,
|
|
259
259
|
* and the transition and its command go on. Without an animation of the screens of its own, the screen that comes fades
|
|
260
260
|
* in (or the one that goes fades out) in 300 ms around the elements. When the transition ends or is cancelled, the
|
|
261
|
-
* element gets its own values back.
|
|
262
|
-
*
|
|
263
|
-
*
|
|
264
|
-
*
|
|
261
|
+
* element gets its own values back. The element's own `transform` counts: a property that doesn't animate keeps the
|
|
262
|
+
* element's value, and a missing end is that value (a turn read from −180° to 180°, as React Native draws it). `x`
|
|
263
|
+
* and `y` are the position in the parent of the element on both platforms. As on a side of a screen, don't set `x`
|
|
264
|
+
* with `translationX` or `y` with `translationY` on one element: the translation plays and the position doesn't.
|
|
265
|
+
* The element plays where it is, in its screen: it moves with its screen, its parents clip it, and the views drawn
|
|
266
|
+
* after it in its screen stay over it.
|
|
265
267
|
*/
|
|
266
268
|
export interface ElementTransition {
|
|
267
269
|
/** The `nativeID` of the view to animate. */
|
|
@@ -292,12 +294,20 @@ export interface ElementTransition {
|
|
|
292
294
|
* A view that flies from the screen that goes to the screen that comes, changing its place, size, corners and look on
|
|
293
295
|
* the way. A pair whose view is not found on either screen is skipped while the rest plays, and the transition waits
|
|
294
296
|
* until the views are found. A pair that fails on the way (an error in its start or its flight) is skipped too, with a
|
|
295
|
-
* warning in the log: its views show and hide with their screens, and the transition and its command go on.
|
|
296
|
-
*
|
|
297
|
-
* own, the screen that comes fades in (or the one that goes fades out) in 300 ms around the pair.
|
|
298
|
-
*
|
|
299
|
-
*
|
|
300
|
-
*
|
|
297
|
+
* warning in the log: its views show and hide with their screens, and the transition and its command go on. So is a
|
|
298
|
+
* pair with a view without a size, or one whose picture can't be taken. Without an animation of the screens of its
|
|
299
|
+
* own, the screen that comes fades in (or the one that goes fades out) in 300 ms around the pair. What flies is a copy:
|
|
300
|
+
* pictures of the two views taken as the transition starts, while the views stay hidden in their places; what changes
|
|
301
|
+
* in them meanwhile shows when they are back. A view its parents cut (`overflow: hidden`, a scroll view, the edge of
|
|
302
|
+
* its screen) shows only the part they leave visible, and that part goes to the one of the other view on the way.
|
|
303
|
+
* Text keeps its proportions, flies whole and changes its picture within a quarter of the flight. A view turned or
|
|
304
|
+
* scaled by its own `transform` or by a parent's flies from where it shows, and its angle goes to the other view's
|
|
305
|
+
* along the shorter arc. The angle is the one React Native draws, from −180° to 180°:
|
|
306
|
+
* `rotate: '270deg'` turns back a quarter, and no pair spins a full turn. (Android) An image without a background or a
|
|
307
|
+
* border flies as the image of the view that comes, from the part and place the fit (`resizeMode`) shows in one view
|
|
308
|
+
* to those in the other, and an animated image plays on; images of other proportions fly as pictures. An image that
|
|
309
|
+
* doesn't load within a second drops out of the transition. A skew and a tilt (`skewX`, `rotateX`, …) don't fly: the
|
|
310
|
+
* view flies without them and has them again at the end.
|
|
301
311
|
*/
|
|
302
312
|
export interface SharedElementTransition {
|
|
303
313
|
/** The `nativeID` of the view on the screen that goes. */
|
|
@@ -406,14 +416,27 @@ export interface StackPushAnimationOptions extends StackAnimationOptions {
|
|
|
406
416
|
}
|
|
407
417
|
|
|
408
418
|
/**
|
|
409
|
-
* The animation of a pop, read from the screen that goes. As {@link StackAnimationOptions}, with the
|
|
410
|
-
* back gesture on Android.
|
|
419
|
+
* The animation of a pop, read from the screen that goes. As {@link StackAnimationOptions}, with the swipe back of iOS
|
|
420
|
+
* and the preview of the back gesture on Android.
|
|
411
421
|
*/
|
|
412
422
|
export interface StackPopAnimationOptions extends StackAnimationOptions {
|
|
423
|
+
/**
|
|
424
|
+
* (iOS) Whether the swipe back plays this pop under the finger. Without it, the swipe back plays UIKit's slide, and
|
|
425
|
+
* the pop's own animation plays only for the back button and `pop`. With `true`, the swipe from the edge plays the
|
|
426
|
+
* pop's own animation as far as the finger has gone across the screen: the user can stop halfway, and letting go
|
|
427
|
+
* finishes the pop or puts the screen back, the rest playing faster after a quick flick. It applies only when the pop
|
|
428
|
+
* has an animation of its own (`content`, `elementTransitions`, `sharedElementTransitions`). The top bar shows the
|
|
429
|
+
* screen under from the start of the swipe, as UIKit does in a transition of its own, and comes back when the swipe
|
|
430
|
+
* is given up. On iOS 26 the swipe from the content stays off for a screen with an own pop.
|
|
431
|
+
* Android ignores it: a pop with an animation of its own plays when the back gesture ends.
|
|
432
|
+
* @default false
|
|
433
|
+
*/
|
|
434
|
+
interactive?: boolean;
|
|
413
435
|
/**
|
|
414
436
|
* (Android 14+) Whether the back gesture on a pushed screen previews its pop as the user swipes: the screen shrinks
|
|
415
437
|
* and follows the finger, the screen under it shows behind. `false` pops the screen when the gesture is released,
|
|
416
|
-
* as on Android 13 and older; back still pops it. iOS ignores it: its swipe back is UIKit's
|
|
438
|
+
* as on Android 13 and older; back still pops it. iOS ignores it: its swipe back is UIKit's, or the pop's own
|
|
439
|
+
* animation with `interactive`.
|
|
417
440
|
* @default true
|
|
418
441
|
*/
|
|
419
442
|
preview?: boolean;
|
|
@@ -469,6 +492,12 @@ export interface ModalAnimationOptions extends EnterExitAnimationOptions {
|
|
|
469
492
|
* options of the command. A change by `mergeOptions` is for the next command and animates nothing itself. A theme
|
|
470
493
|
* switch or a rotation does not change an animation. Without a key, the transition is the platform's own.
|
|
471
494
|
*
|
|
495
|
+
* The system's setting to reduce motion is respected, with no option to turn that off. (iOS) With Reduce Motion on, a
|
|
496
|
+
* transition of these keys is a fade of the same length: the screen that comes fades in (a push, `setStackRoot`, a
|
|
497
|
+
* modal's show, `setRoot`), the screen that goes fades out (a pop, a dismissal); no element moves. (Android) With
|
|
498
|
+
* Remove animations on (or the animator duration scale at 0), the transitions end at once, no element moves and none
|
|
499
|
+
* is waited for, a side menu opens and closes without its slide, and a sheet shows and closes without it.
|
|
500
|
+
*
|
|
472
501
|
* ```ts
|
|
473
502
|
* const options: Options = {
|
|
474
503
|
* animations: {
|
|
@@ -484,7 +513,8 @@ export interface AnimationOptions {
|
|
|
484
513
|
push?: StackPushAnimationOptions;
|
|
485
514
|
/**
|
|
486
515
|
* The pop of a screen, also `popTo` and `popToRoot`, read from the screen that goes. The swipe back of iOS is
|
|
487
|
-
* UIKit's; the back gesture of Android previews the pop
|
|
516
|
+
* UIKit's, or the pop's own animation under the finger (`interactive`); the back gesture of Android previews the pop
|
|
517
|
+
* (`preview`).
|
|
488
518
|
*/
|
|
489
519
|
pop?: StackPopAnimationOptions;
|
|
490
520
|
/** The replacing of the screens of a stack, read from the new top screen. */
|
|
@@ -73,8 +73,10 @@ export interface OptionsBottomTabsAccessoryComponent {
|
|
|
73
73
|
/**
|
|
74
74
|
* (iOS 26+) The accessory above the tab bar, like the mini player of a music app. The system sizes it (48 points high)
|
|
75
75
|
* and the component fills that size. It is placed above the bar, or beside the bar when the bar is minimized; on an
|
|
76
|
-
* iPad with the bar at the top it sits at the bottom over the full width, always above the bar.
|
|
77
|
-
*
|
|
76
|
+
* iPad with the bar at the top it sits at the bottom over the full width, always above the bar. A hidden accessory
|
|
77
|
+
* (`visible: false`, or no name) keeps its React root and its state, and shows again with them. When another accessory
|
|
78
|
+
* (another `componentId`) shows, the replaced one stops once it has left the window and its state is gone. The last
|
|
79
|
+
* accessory stops together with the tabs. Android has no accessory.
|
|
78
80
|
*/
|
|
79
81
|
export interface OptionsBottomTabsAccessory {
|
|
80
82
|
/**
|
|
@@ -155,6 +157,16 @@ export interface OptionsBottomTabs {
|
|
|
155
157
|
* @default true
|
|
156
158
|
*/
|
|
157
159
|
animate?: boolean;
|
|
160
|
+
/**
|
|
161
|
+
* How the content changes when another tab is selected on screen, by a press, by back or from code. `'platform'`
|
|
162
|
+
* does what the platform does: on Android the tab that goes fades out and the new one fades in and grows (Material's
|
|
163
|
+
* fade through, 300 ms), on iOS the content changes at once. `'none'` changes it at once on both platforms. The
|
|
164
|
+
* appear events of a switch and `ANN.BottomTabSelected` come at its end, so with `'none'` right away. The tab that is
|
|
165
|
+
* selected decides: the value of the screen it shows, over its own, the tabs' and the default options. A
|
|
166
|
+
* `mergeOptions` applies from the next switch. Off screen the content always changes at once.
|
|
167
|
+
* @default 'platform'
|
|
168
|
+
*/
|
|
169
|
+
switchAnimation?: 'platform' | 'none';
|
|
158
170
|
/**
|
|
159
171
|
* The tab to select, as an index from 0. It selects once, when the options arrive, at creation (the first tab to
|
|
160
172
|
* show) or in `mergeOptions`; it is not kept. An index out of range selects nothing (or the first tab at creation)
|
|
@@ -187,9 +199,10 @@ export interface OptionsBottomTabs {
|
|
|
187
199
|
*/
|
|
188
200
|
drawBehind?: boolean;
|
|
189
201
|
/**
|
|
190
|
-
* The background color of the bar; `null` gives the default one. On Android the default is the surface color
|
|
191
|
-
* Material 3 theme. Before iOS 26
|
|
192
|
-
*
|
|
202
|
+
* The background color of the bar; `null` gives the default one. On Android the default is the surface container color
|
|
203
|
+
* (`colorSurfaceContainer`) of the Material 3 theme. Before iOS 26, and in the compatibility mode (`UIDesignRequiresCompatibility` in Info.plist: iOS 26, and iOS 27
|
|
204
|
+
* only for an app built with an SDK before iOS 27), the bar is opaque with the color, or system opaque without it,
|
|
205
|
+
* and a transparent color makes it transparent. On iOS 26 and later with glass the color is ignored
|
|
193
206
|
* with one warning, since the system has no color for glass, while a transparent color gives a transparent bar. The
|
|
194
207
|
* theme color `bar` is its default option.
|
|
195
208
|
*/
|
|
@@ -396,7 +409,7 @@ export interface OptionsBottomTab {
|
|
|
396
409
|
selectedFontSize?: number;
|
|
397
410
|
/**
|
|
398
411
|
* (iOS) The insets of the icon. Each side is independent. They apply before iOS 26 only; iOS 26 and the compatibility
|
|
399
|
-
* mode ignore them, and so does a system role before iOS 18.
|
|
412
|
+
* mode (see `backgroundColor`) ignore them, and so does a system role before iOS 18.
|
|
400
413
|
*/
|
|
401
414
|
iconInsets?: IconInsets;
|
|
402
415
|
/**
|
|
@@ -60,7 +60,7 @@ export interface IconInsets {
|
|
|
60
60
|
* a color the other fields are ignored.
|
|
61
61
|
*
|
|
62
62
|
* On iOS the icon is drawn onto the shape and the button becomes a button of its own (the back button keeps its
|
|
63
|
-
* history menu), without the glass capsule
|
|
63
|
+
* history menu), without the glass capsule (glass design, iOS 26+) by default. On Android the icon sits on the shape, mirrored in a
|
|
64
64
|
* right-to-left layout. Layers merge differently: on iOS an object merges field by field like any other, on Android
|
|
65
65
|
* an `iconBackground` that has a `color` replaces the earlier one as a whole.
|
|
66
66
|
*/
|
|
@@ -159,7 +159,7 @@ export interface OptionsTopBarButtonMenu {
|
|
|
159
159
|
* A button of the top bar (`leftButtons`, `rightButtons`, `centerButtons`) or of the toolbar (`toolbar.buttons`). The
|
|
160
160
|
* kinds are picked by the fields that are set. On iOS, the first that fits wins: a `component`, then an icon with text
|
|
161
161
|
* and `showText`, then an icon with `iconBackground` or `iconInsets`, then an SF Symbol, an image `icon`, a `text`,
|
|
162
|
-
* a `systemItem`; a button with none of them is not shown. On Android a button is a
|
|
162
|
+
* a `systemItem`; a button with none of them is not shown. On Android a button is a button of the bar's row: text, icon
|
|
163
163
|
* or a component. Lists of buttons are replaced as a whole and take nothing from the lists of lower layers.
|
|
164
164
|
*
|
|
165
165
|
* Text, icon, color and similar fields are the same in all four places; where a place or a side differs, the field
|
|
@@ -189,7 +189,7 @@ export interface OptionsTopBarButton {
|
|
|
189
189
|
*/
|
|
190
190
|
icon?: ImageResource;
|
|
191
191
|
/**
|
|
192
|
-
* (iOS) The space around the image of `icon`; the button becomes a button of its own, without a capsule
|
|
192
|
+
* (iOS) The space around the image of `icon`; the button becomes a button of its own, without a capsule with the glass design (iOS 26+, see `UIDesignRequiresCompatibility`).
|
|
193
193
|
*/
|
|
194
194
|
iconInsets?: IconInsets;
|
|
195
195
|
/**
|
|
@@ -204,7 +204,7 @@ export interface OptionsTopBarButton {
|
|
|
204
204
|
systemItem?: SystemItemIcon;
|
|
205
205
|
/**
|
|
206
206
|
* The text of the button. A button with an icon keeps it as a label for the overflow menu, the customization panel
|
|
207
|
-
* and VoiceOver (iOS) or
|
|
207
|
+
* and VoiceOver (iOS), or for TalkBack, the tooltip of a long press and the overflow menu (Android).
|
|
208
208
|
*/
|
|
209
209
|
text?: string;
|
|
210
210
|
/** The text a screen reader says for the button. */
|
|
@@ -238,14 +238,14 @@ export interface OptionsTopBarButton {
|
|
|
238
238
|
disabledColor?: Color;
|
|
239
239
|
/**
|
|
240
240
|
* A shape behind the icon. It works with an `icon` image and a `color` of the shape; on iOS the button becomes a
|
|
241
|
-
* button of its own without menu, toggle or style, and without a capsule
|
|
241
|
+
* button of its own without menu, toggle or style, and without a capsule with the glass design by default. The color of a whole
|
|
242
242
|
* side is `leftButtonBackgroundColor` / `rightButtonBackgroundColor`.
|
|
243
243
|
*/
|
|
244
244
|
iconBackground?: IconBackgroundOptions;
|
|
245
245
|
/**
|
|
246
|
-
* (iOS) `'plain'` or `'prominent'`, the main action of the bar (Done, Send).
|
|
246
|
+
* (iOS) `'plain'` or `'prominent'`, the main action of the bar (Done, Send). With the glass design (iOS 26+, see `UIDesignRequiresCompatibility`) a prominent button is a
|
|
247
247
|
* capsule in the button's tint with bold text or icon on it (the tint is `color`, the bar's tint without it);
|
|
248
|
-
*
|
|
248
|
+
* without it, it is the system's done style with bold text. Not for component buttons, buttons with
|
|
249
249
|
* `iconBackground`, and `showText` buttons.
|
|
250
250
|
* @default 'plain'
|
|
251
251
|
*/
|
|
@@ -257,7 +257,7 @@ export interface OptionsTopBarButton {
|
|
|
257
257
|
*/
|
|
258
258
|
testID?: string;
|
|
259
259
|
/**
|
|
260
|
-
* (iOS 26
|
|
260
|
+
* (iOS 26+, glass design) Whether the button has no glass capsule under it; neighbors keep theirs. By default `true` for
|
|
261
261
|
* component buttons and for icon buttons with a background or insets, `false` for the rest. In a `group` of several
|
|
262
262
|
* buttons the capsule is shared and this has no effect. A change by `mergeOptions` builds the buttons anew.
|
|
263
263
|
*/
|
|
@@ -270,8 +270,8 @@ export interface OptionsTopBarButton {
|
|
|
270
270
|
*/
|
|
271
271
|
showText?: boolean;
|
|
272
272
|
/**
|
|
273
|
-
* (iOS 26
|
|
274
|
-
* own.
|
|
273
|
+
* (iOS 26+, glass design) Buttons next to each other with the same group share one capsule; a button without a group has its
|
|
274
|
+
* own. Without the glass design it does nothing, on iOS 15 there are no groups. A side that has a group of several buttons
|
|
275
275
|
* doesn't animate button changes. The toolbar has no groups: only icons share a capsule there. For center buttons
|
|
276
276
|
* the group is a section of the overflow menu, and its name is the id the customization is saved under.
|
|
277
277
|
*/
|
|
@@ -51,8 +51,7 @@ export type OptionsModalTransitionStyle =
|
|
|
51
51
|
*
|
|
52
52
|
* On iOS `'medium'` and `'large'` are the system stops; a number and `'content'` need iOS 16 and are left out below it
|
|
53
53
|
* with a warning in the log. On Android `'medium'` is half of the window, `'large'` the full height under the status
|
|
54
|
-
* bar, a number is capped at `'large'`, and `'content'`
|
|
55
|
-
* height by content.
|
|
54
|
+
* bar, a number is capped at `'large'`, and `'content'` is the height of the content, measured as on iOS.
|
|
56
55
|
*/
|
|
57
56
|
export type OptionsModalSheetDetent = 'medium' | 'large' | 'content' | number;
|
|
58
57
|
|
|
@@ -67,11 +66,11 @@ export type OptionsModalSheetDetent = 'medium' | 'large' | 'content' | number;
|
|
|
67
66
|
export interface OptionsModalSheet {
|
|
68
67
|
/**
|
|
69
68
|
* The stops the sheet can rest on, and the user drags it between, in any order: on both platforms they are sorted by
|
|
70
|
-
* height and repeats dropped. On iOS `'medium'` counts as half of `'large'` there, and `'content'` takes its
|
|
71
|
-
* the height of the content, again whenever that changes; a list left empty by unsupported values
|
|
72
|
-
* `['large']`. On Android the Material sheet has three resting places at most: the lowest stop of the list, the
|
|
73
|
-
* highest, and
|
|
74
|
-
* dropped.
|
|
69
|
+
* height and repeats dropped. On iOS `'medium'` counts as half of `'large'` there, and on both `'content'` takes its
|
|
70
|
+
* place by the height of the content, again whenever that changes; on iOS a list left empty by unsupported values
|
|
71
|
+
* gives `['large']`. On Android the Material sheet has three resting places at most: the lowest stop of the list, the
|
|
72
|
+
* highest, and one between them, `selectedDetent` when it stands there, else the lowest of the others; the rest are
|
|
73
|
+
* dropped. Stops of the same height there are one.
|
|
75
74
|
* @default ['large']
|
|
76
75
|
*/
|
|
77
76
|
detents?: OptionsModalSheetDetent[];
|
|
@@ -39,13 +39,18 @@ export interface HardwareBackButtonOptions {
|
|
|
39
39
|
|
|
40
40
|
/**
|
|
41
41
|
* (Android) A floating action button over the screen, or an entry of its menu. The button shows while its screen is
|
|
42
|
-
* on top of the stack; without the option there is none.
|
|
43
|
-
* the
|
|
42
|
+
* on top of the stack; without the option there is none. It changes as a push or a pop starts and moves with its
|
|
43
|
+
* screen, as the tab bar does: the new screen's button comes in with it, the old one goes out with the screen it
|
|
44
|
+
* belongs to, and an open menu closes. With an animation of the screens' own the old button shrinks away and the new
|
|
45
|
+
* one grows as it starts; during the back gesture the button of the screen under the top shows with that screen.
|
|
46
|
+
* `mergeOptions` and the default options of the app apply, and the colors of the theme are read again when the theme
|
|
47
|
+
* changes.
|
|
44
48
|
*/
|
|
45
49
|
export interface OptionsFab {
|
|
46
50
|
/**
|
|
47
51
|
* The identifier of the button. A press reports it, and the app finds the `onPress` of the button by it. A button
|
|
48
|
-
* without an `id` doesn't show. The same `id` on a later show or merge updates the button
|
|
52
|
+
* without an `id` doesn't show. The same `id` on a later show or merge updates the button, which stays in place
|
|
53
|
+
* while the screens move; another `id` replaces it.
|
|
49
54
|
* For an entry of `actions` it is the id of that entry's own press.
|
|
50
55
|
*/
|
|
51
56
|
id: string;
|
|
@@ -34,12 +34,15 @@ export interface OptionsLayout {
|
|
|
34
34
|
* indicator or navigation bar, the camera cutout. `[]` lets the content reach every edge. The array replaces the
|
|
35
35
|
* one from a lower layer. The screen adds the padding itself; `useSafeAreaInsets()` returns what is still covered,
|
|
36
36
|
* which is 0 on an edge the screen keeps and the whole covered size on an edge where the content goes under a bar.
|
|
37
|
-
* Edges listed here
|
|
37
|
+
* Edges listed here win, but the top under a large title: there the list keeps its first rows clear of the bar
|
|
38
|
+
* itself and scrolls under it, which collapses the title, so a `'top'` listed here counts for nothing.
|
|
38
39
|
*
|
|
39
|
-
* Without it
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
40
|
+
* Without it the screen keeps the top and the sides, and its content goes down to the bottom: under the home
|
|
41
|
+
* indicator or the navigation bar, and under a tab bar or toolbar it goes under. A list keeps its last rows clear
|
|
42
|
+
* with `useSafeAreaInsets().bottom`. The top is free where the content goes under a top bar: on iOS under a bar
|
|
43
|
+
* that shows, on Android under a bar that hides on scroll; on both under a large title, so the list scrolls under
|
|
44
|
+
* the bar. On iOS the side of a vertical bar is free too. To have the screen keep the bottom, list every edge it
|
|
45
|
+
* keeps: listed edges replace the default.
|
|
43
46
|
*/
|
|
44
47
|
safeAreaEdges?: SafeAreaEdge[];
|
|
45
48
|
/**
|
|
@@ -4,7 +4,7 @@ import type { OptionsTopBarButton } from './buttons';
|
|
|
4
4
|
export interface OptionsToolbarSpace {
|
|
5
5
|
/**
|
|
6
6
|
* `'flexibleSpace'` takes the free width and pushes the next buttons to the far edge; `'fixedSpace'` is a gap of zero
|
|
7
|
-
* width that starts a new capsule
|
|
7
|
+
* width that starts a new capsule with the glass design (iOS 26+).
|
|
8
8
|
*/
|
|
9
9
|
systemItem: 'flexibleSpace' | 'fixedSpace';
|
|
10
10
|
}
|
|
@@ -36,7 +36,7 @@ export interface OptionsToolbar {
|
|
|
36
36
|
* The buttons from the leading edge, in the format of the bar buttons: text, icon, SF Symbol, `systemItem`, a
|
|
37
37
|
* component (drawn before the screen shows), `enabled`, `color`, `style`, `menu` (it opens upward), `toggle`,
|
|
38
38
|
* `showText`, `group`; also spaces and the search field. A press sends a button press. The color of a side doesn't
|
|
39
|
-
* apply to them, and badges are not drawn.
|
|
39
|
+
* apply to them, and badges are not drawn. With the glass design (iOS 26+, see `UIDesignRequiresCompatibility`) each button has its own capsule, neighbor icons with the same
|
|
40
40
|
* `group` share one (the toolbar has no real groups, so text buttons never share), and a zero-width space is put
|
|
41
41
|
* between neighbors of different groups unless an explicit space is there. The list is replaced as a whole;
|
|
42
42
|
* `mergeOptions` animates it.
|