@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
|
@@ -7,14 +7,15 @@ import dev.apexnative.navigation.viewcontrollers.stack.StackDisplay.BarMotion
|
|
|
7
7
|
* row and the large title lies over them and the real row turns transparent; then the picture goes with the old screen
|
|
8
8
|
* and the row comes with the new one ([RowTimeline]). Each change of the stack's current child owns the bar: a newer
|
|
9
9
|
* one takes it over (the older picture goes, its animator stops), and only the end of the transition that owns it puts
|
|
10
|
-
* the row back at rest. Without a picture (a `HARDWARE` bitmap in the bar) the old row just hides. Main
|
|
10
|
+
* the row back at rest. Without a picture (a `HARDWARE` bitmap in the bar on Android 8) the old row just hides. Main
|
|
11
|
+
* thread only.
|
|
11
12
|
*/
|
|
12
13
|
internal class BarRowTransition(private val target: Target) {
|
|
13
14
|
/** What the transition moves: the bar's views, or the unit tests' fake. */
|
|
14
15
|
interface Target {
|
|
15
16
|
/**
|
|
16
17
|
* Lays a picture of the row and the large title over them, laid out at once and taking the touches; false when
|
|
17
|
-
* the bar can't be
|
|
18
|
+
* the bar can't be recorded (a `HARDWARE` bitmap on Android 8): no picture then.
|
|
18
19
|
*/
|
|
19
20
|
fun takeSnapshot(): Boolean
|
|
20
21
|
|
package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/topbar/ButtonPlaces.kt
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
package dev.apexnative.navigation.viewcontrollers.stack.topbar
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* Where the
|
|
5
|
-
* ([maxActionButtons]), one less for "⋮" when it shows; the pinned buttons take theirs first, then the
|
|
6
|
-
* highest `priority` (ties by the list's order); the rest go into "⋮".
|
|
7
|
-
*
|
|
8
|
-
*
|
|
4
|
+
* Where the buttons of the bar go. The right ones: the bar has as many places as AppCompat gives a toolbar for the
|
|
5
|
+
* window now ([maxActionButtons]), one less for "⋮" when it shows; the pinned buttons take theirs first, then the
|
|
6
|
+
* buttons with the highest `priority` (ties by the list's order); the rest go into "⋮". In the line ([line]) the left
|
|
7
|
+
* buttons stand from its start in the list's order, and the right ones toward its end, the first one last. A pure
|
|
8
|
+
* function.
|
|
9
9
|
*/
|
|
10
10
|
internal object ButtonPlaces {
|
|
11
11
|
/** A right button as the count sees it: its `priority` ([HIGH], [STANDARD], [LOW]) and `pinned`. */
|
|
@@ -36,6 +36,12 @@ internal object ButtonPlaces {
|
|
|
36
36
|
else -> 2
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
+
/**
|
|
40
|
+
* The buttons of a side in the bar, [inBar] in the list's order, in the line's order from its start: the left ones
|
|
41
|
+
* as they are, the right ones the other way round, so the first right button stands at the end of the line.
|
|
42
|
+
*/
|
|
43
|
+
fun <T> line(inBar: List<T>, left: Boolean): List<T> = if (left) inBar else inBar.reversed()
|
|
44
|
+
|
|
39
45
|
/**
|
|
40
46
|
* The plan for [buttons] with [maxPlaces] places, [taken] of them already used by what the core always puts in the
|
|
41
47
|
* bar (the search's magnifier), and [overflowItems] items of `overflowMenu`, which put "⋮" in the bar whatever
|
|
@@ -7,17 +7,13 @@ import android.app.Activity
|
|
|
7
7
|
import android.content.Context
|
|
8
8
|
import android.content.pm.ApplicationInfo
|
|
9
9
|
import android.graphics.Color
|
|
10
|
-
import android.graphics.drawable.BitmapDrawable
|
|
11
10
|
import android.graphics.drawable.Drawable
|
|
12
|
-
import android.view.Menu
|
|
13
|
-
import android.view.MenuItem
|
|
14
11
|
import android.view.MotionEvent
|
|
15
12
|
import android.view.View
|
|
16
13
|
import android.view.ViewGroup
|
|
17
14
|
import android.view.animation.LinearInterpolator
|
|
18
15
|
import android.view.animation.PathInterpolator
|
|
19
16
|
import android.widget.FrameLayout
|
|
20
|
-
import android.widget.ImageView
|
|
21
17
|
import androidx.appcompat.content.res.AppCompatResources
|
|
22
18
|
import androidx.core.graphics.drawable.DrawableCompat
|
|
23
19
|
import androidx.core.view.WindowInsetsCompat
|
|
@@ -59,6 +55,7 @@ import dev.apexnative.navigation.views.stack.topbar.TitleBar
|
|
|
59
55
|
import dev.apexnative.navigation.views.stack.topbar.TopBar
|
|
60
56
|
import dev.apexnative.navigation.views.stack.topbar.TopBarDefaults
|
|
61
57
|
import dev.apexnative.navigation.views.stack.topbar.titlebar.BarButtonComponentCell
|
|
58
|
+
import dev.apexnative.navigation.views.stack.topbar.titlebar.BarMenu
|
|
62
59
|
import dev.apexnative.navigation.views.stack.topbar.titlebar.ButtonBar
|
|
63
60
|
import dev.apexnative.navigation.views.stack.topbar.titlebar.IconBackgroundDrawable
|
|
64
61
|
import kotlin.math.roundToInt
|
|
@@ -142,8 +139,8 @@ internal class TopBarPresenter(private val activity: Activity, private val host:
|
|
|
142
139
|
this.layout = layout
|
|
143
140
|
val bar = layout.topBar
|
|
144
141
|
this.bar = bar
|
|
145
|
-
leftButtons = ButtonBar(bar.leftButtonBar,
|
|
146
|
-
rightButtons = ButtonBar(bar.rightButtonBar,
|
|
142
|
+
leftButtons = ButtonBar(bar.leftButtonBar, ::loadIcon, Buttons())
|
|
143
|
+
rightButtons = ButtonBar(bar.rightButtonBar, ::loadIcon, Buttons())
|
|
147
144
|
visibility = TopBarVisibility(VisibilityTarget(bar) { collapse?.reset() })
|
|
148
145
|
rowTransition = BarRowTransition(RowTarget(bar).also { rowTarget = it })
|
|
149
146
|
collapse = TopBarCollapseBehavior(bar)
|
|
@@ -212,8 +209,8 @@ internal class TopBarPresenter(private val activity: Activity, private val host:
|
|
|
212
209
|
* bar as it is goes with [leaving], and the bar, which takes [incoming]'s options next, is drawn flat as [incoming]
|
|
213
210
|
* moves — under [leaving] on a pop ([StackDisplay.BarTravel.Back]) (`StackLayout.barTravels`). No picture when the
|
|
214
211
|
* bar doesn't show. Nothing travels when the new top hides the bar by a side of its own
|
|
215
|
-
* (`animations.*.topBar.exit`), which plays instead, or when the bar can't be
|
|
216
|
-
*
|
|
212
|
+
* (`animations.*.topBar.exit`), which plays instead, or when the bar can't be recorded (a `HARDWARE` bitmap in it
|
|
213
|
+
* on Android 8): the bar takes the options in its place.
|
|
217
214
|
*/
|
|
218
215
|
private fun beginTravel(
|
|
219
216
|
bar: TopBar,
|
|
@@ -238,10 +235,10 @@ internal class TopBarPresenter(private val activity: Activity, private val host:
|
|
|
238
235
|
|
|
239
236
|
// The bar's picture where the bar is in the stack, its moves counted.
|
|
240
237
|
private fun pictureOfBar(bar: TopBar): Drawable? {
|
|
241
|
-
val
|
|
238
|
+
val picture = bar.picture() ?: return null
|
|
242
239
|
val left = (bar.left + bar.translationX).roundToInt()
|
|
243
240
|
val top = (bar.top + bar.translationY).roundToInt()
|
|
244
|
-
return
|
|
241
|
+
return picture.apply { setBounds(left, top, left + intrinsicWidth, top + intrinsicHeight) }
|
|
245
242
|
}
|
|
246
243
|
|
|
247
244
|
// The bar's travel ends: the picture goes, the bar is drawn in its place again with its shadow.
|
|
@@ -284,6 +281,8 @@ internal class TopBarPresenter(private val activity: Activity, private val host:
|
|
|
284
281
|
val bar = bar ?: return
|
|
285
282
|
val screen = host.optionsScreen(entry)
|
|
286
283
|
if (screen !== shownScreen) return apply(entry)
|
|
284
|
+
// An open menu has the colors of before.
|
|
285
|
+
if (delta.topBar.menuBackgroundColor.hasValue() || delta.topBar.menuTextColor.hasValue()) closeMenus()
|
|
287
286
|
val options = host.resolvedOf(entry)
|
|
288
287
|
applyAll(bar, entry, screen, options, merge = true, delta = delta)
|
|
289
288
|
}
|
|
@@ -293,6 +292,8 @@ internal class TopBarPresenter(private val activity: Activity, private val host:
|
|
|
293
292
|
* now.
|
|
294
293
|
*/
|
|
295
294
|
fun repaint(entry: ViewController<*>?) {
|
|
295
|
+
// An open menu has the old colors: it closes.
|
|
296
|
+
closeMenus()
|
|
296
297
|
keys.clear()
|
|
297
298
|
apply(entry)
|
|
298
299
|
val options = entry?.let(host::resolvedOf) ?: return
|
|
@@ -582,16 +583,15 @@ internal class TopBarPresenter(private val activity: Activity, private val host:
|
|
|
582
583
|
keys[Part.Left] = leftKey
|
|
583
584
|
reportWithoutId(topBar.buttons.left, "leftButtons")
|
|
584
585
|
leftButtons?.set(
|
|
585
|
-
left.map { button(screen, it, roots) },
|
|
586
|
+
ButtonPlaces.line(left, left = true).map { button(screen, it, roots) },
|
|
586
587
|
emptyList(),
|
|
587
588
|
null,
|
|
588
589
|
merge && topBar.animateLeftButtons.isTrue,
|
|
589
590
|
)
|
|
590
591
|
}
|
|
591
592
|
val overflow = topBar.overflowMenu?.takeIf { it.items.isNotEmpty() }
|
|
592
|
-
val menu = bar.rightButtonBar.menu
|
|
593
593
|
search.applyMagnifier()
|
|
594
|
-
val taken =
|
|
594
|
+
val taken = if (bar.rightButtonBar.trailingView != null) 1 else 0
|
|
595
595
|
val configuration = context.resources.configuration
|
|
596
596
|
val plan = ButtonPlaces.plan(
|
|
597
597
|
right.map { ButtonPlaces.Button(ButtonPlaces.priorityOf(it.priority.get(null)), it.pinned.isTrue) },
|
|
@@ -612,12 +612,11 @@ internal class TopBarPresenter(private val activity: Activity, private val host:
|
|
|
612
612
|
keys[Part.Right] = rightKey
|
|
613
613
|
reportWithoutId(topBar.buttons.right, "rightButtons")
|
|
614
614
|
rightButtons?.set(
|
|
615
|
-
plan.inBar.map { button(screen, right[it], roots) },
|
|
615
|
+
ButtonPlaces.line(plan.inBar, left = false).map { button(screen, right[it], roots) },
|
|
616
616
|
plan.inOverflow.map { button(screen, right[it], roots) },
|
|
617
617
|
overflow,
|
|
618
618
|
merge && topBar.animateRightButtons.isTrue,
|
|
619
619
|
)
|
|
620
|
-
search.applyMagnifier()
|
|
621
620
|
rightButtons?.setOverflowColor(overflowColor)
|
|
622
621
|
}
|
|
623
622
|
}
|
|
@@ -639,9 +638,9 @@ internal class TopBarPresenter(private val activity: Activity, private val host:
|
|
|
639
638
|
).joinToString()
|
|
640
639
|
if (keys[Part.Back] == key) return
|
|
641
640
|
keys[Part.Back] = key
|
|
642
|
-
val
|
|
641
|
+
val row = bar.leftButtonBar
|
|
643
642
|
if (!shown) {
|
|
644
|
-
|
|
643
|
+
row.navigationIcon = null
|
|
645
644
|
return
|
|
646
645
|
}
|
|
647
646
|
val own = back.icon.get(null)?.let(::loadIcon)?.mutate()
|
|
@@ -668,19 +667,13 @@ internal class TopBarPresenter(private val activity: Activity, private val host:
|
|
|
668
667
|
dp(background.height.get(0.0)),
|
|
669
668
|
)
|
|
670
669
|
} ?: icon
|
|
671
|
-
|
|
672
|
-
// Taking the icon, the
|
|
673
|
-
//
|
|
674
|
-
// gets the bar's direction at once
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
toolbar.navigationContentDescription = label
|
|
679
|
-
// The toolbar uses its navigation button again from screen to screen: its id is set every time.
|
|
680
|
-
for (index in 0 until toolbar.childCount) {
|
|
681
|
-
val child = toolbar.getChildAt(index)
|
|
682
|
-
if ((child as? ImageView)?.drawable === held) TestIds.apply(child, testId)
|
|
683
|
-
}
|
|
670
|
+
row.navigationIcon = drawable
|
|
671
|
+
// Taking the icon, the back button gives it its own direction, which is stale while the button is new or the
|
|
672
|
+
// bar's direction has just changed; the button corrects it only once its direction is resolved again. So the
|
|
673
|
+
// arrow gets the bar's direction at once; a background passes it to the arrow.
|
|
674
|
+
DrawableCompat.setLayoutDirection(drawable, arrowDirection)
|
|
675
|
+
row.navigationContentDescription = label
|
|
676
|
+
row.navigationButton?.let { TestIds.apply(it, testId) }
|
|
684
677
|
}
|
|
685
678
|
|
|
686
679
|
// The background (`topBar.background.component`).
|
|
@@ -719,15 +712,14 @@ internal class TopBarPresenter(private val activity: Activity, private val host:
|
|
|
719
712
|
|
|
720
713
|
private fun pictureUnderRow(bar: TopBar, leaving: ViewController<*>) {
|
|
721
714
|
val view = leaving.peekView() ?: return
|
|
722
|
-
val (
|
|
715
|
+
val (picture, top) = bar.pictureUnderRow() ?: return
|
|
723
716
|
val barPlace = IntArray(2)
|
|
724
717
|
val viewPlace = IntArray(2)
|
|
725
718
|
bar.getLocationInWindow(barPlace)
|
|
726
719
|
view.getLocationInWindow(viewPlace)
|
|
727
|
-
val picture = BitmapDrawable(bar.resources, bitmap)
|
|
728
720
|
val left = barPlace[0] - viewPlace[0]
|
|
729
721
|
val y = barPlace[1] + top - viewPlace[1]
|
|
730
|
-
picture.setBounds(left, y, left +
|
|
722
|
+
picture.setBounds(left, y, left + picture.intrinsicWidth, y + picture.intrinsicHeight)
|
|
731
723
|
view.overlay.add(picture)
|
|
732
724
|
underRow = leaving to picture
|
|
733
725
|
}
|
|
@@ -1037,11 +1029,16 @@ internal class TopBarPresenter(private val activity: Activity, private val host:
|
|
|
1037
1029
|
return (buttons.left.orEmpty() + buttons.right.orEmpty()).firstOrNull { it.id.get(null) == buttonId }?.menu
|
|
1038
1030
|
}
|
|
1039
1031
|
|
|
1040
|
-
//
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1032
|
+
// Read as a menu opens, so they follow light and dark mode: `menuBackgroundColor`, else the bar's color as it
|
|
1033
|
+
// shows (none when it lets something through), and then `menuTextColor` or else the title's color, the pair
|
|
1034
|
+
// the bar shows; with neither, the theme's.
|
|
1035
|
+
override fun menuColors(): BarMenu.Colors {
|
|
1036
|
+
val topBar = shownEntry?.let(host::resolvedOf)?.topBar ?: return BarMenu.Colors(null, null)
|
|
1037
|
+
val context = bar?.context ?: activity
|
|
1038
|
+
val text = topBar.menuTextColor.get(context, null)
|
|
1039
|
+
topBar.menuBackgroundColor.get(context, null)?.let { return BarMenu.Colors(it, text) }
|
|
1040
|
+
val barColor = bar?.barColor?.takeIf { Color.alpha(it) == OPAQUE } ?: return BarMenu.Colors(null, text)
|
|
1041
|
+
return BarMenu.Colors(barColor, text ?: topBar.title.color.get(context, null))
|
|
1045
1042
|
}
|
|
1046
1043
|
}
|
|
1047
1044
|
|
|
@@ -1183,6 +1180,7 @@ internal class TopBarPresenter(private val activity: Activity, private val host:
|
|
|
1183
1180
|
|
|
1184
1181
|
private companion object {
|
|
1185
1182
|
const val DEFAULT_HEIGHT_DP = 64.0
|
|
1183
|
+
const val OPAQUE = 255
|
|
1186
1184
|
const val DEFAULT_TITLE_SP = 22.0
|
|
1187
1185
|
const val DEFAULT_SUBTITLE_SP = 14.0
|
|
1188
1186
|
const val CENTER = "center"
|
|
@@ -13,8 +13,8 @@ import java.lang.ref.WeakReference
|
|
|
13
13
|
* tag) is in the screen, found once per tag —, not the horizontal ones, nor those of a modal, an overlay or another
|
|
14
14
|
* screen; a slot screen's lists are in its container too. While a list is dragged, each follower gets the drag as a
|
|
15
15
|
* translation to follow; when the drag ends, the way to settle; at every scroll event, the drags' and the inertia's,
|
|
16
|
-
* the offset. It listens to React Native's
|
|
17
|
-
*
|
|
16
|
+
* the offset. It listens to React Native's scroll views ([ScrollEvents]) only while it has followers and its screen's
|
|
17
|
+
* view. Released with the screen. Main thread only.
|
|
18
18
|
*/
|
|
19
19
|
class ScrollEventListener() {
|
|
20
20
|
/** Follows the content while it is dragged. */
|
|
@@ -105,15 +105,12 @@ class ScrollEventListener() {
|
|
|
105
105
|
}
|
|
106
106
|
|
|
107
107
|
/**
|
|
108
|
-
* Listens to React Native's scroll
|
|
109
|
-
*
|
|
110
|
-
* window.
|
|
108
|
+
* Listens to React Native's scroll views when followers wait for them and it doesn't yet, once it has the screen's
|
|
109
|
+
* view to tell its own lists by; asked again as the screen joins a window.
|
|
111
110
|
*/
|
|
112
111
|
internal fun attachIfNeeded() {
|
|
113
|
-
if (released || stopListening != null || registrations.isEmpty()) return
|
|
114
|
-
|
|
115
|
-
stopListening = ScrollEvents.listen(context, ::onScrollEvent)
|
|
116
|
-
if (stopListening == null) ScrollEvents.waitForDispatcher(this)
|
|
112
|
+
if (released || stopListening != null || registrations.isEmpty() || scope?.get() == null) return
|
|
113
|
+
stopListening = ScrollEvents.listen(::onScrollEvent)
|
|
117
114
|
}
|
|
118
115
|
|
|
119
116
|
/** The screen is destroyed: no follower hears it any more. */
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
package dev.apexnative.navigation.views.animations
|
|
2
|
+
|
|
3
|
+
import android.animation.Animator
|
|
4
|
+
import android.animation.AnimatorListenerAdapter
|
|
5
|
+
import android.animation.AnimatorSet
|
|
6
|
+
import android.animation.ObjectAnimator
|
|
7
|
+
import android.view.View
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* A hardware layer on a view for the time of an animator that fades it. A view whose `alpha` is below 1 and whose
|
|
11
|
+
* rendering overlaps (any `ViewGroup` by default) is drawn into an offscreen buffer at every frame; through a layer it
|
|
12
|
+
* is drawn once and the GPU only composites it. The layer is taken as the animator starts and only from
|
|
13
|
+
* `LAYER_TYPE_NONE` (a layer someone else set stays theirs), and given back as it ends or is cancelled. A pure
|
|
14
|
+
* translation or scale needs none: RenderThread draws it. The stack's default transition has its own
|
|
15
|
+
* (`StackViews.dress`). Main thread only.
|
|
16
|
+
*/
|
|
17
|
+
internal object AnimationLayer {
|
|
18
|
+
private const val ALPHA = "alpha"
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* [view] draws through a hardware layer while [animator] plays, when [animator] changes the `alpha` of [view] and
|
|
22
|
+
* the view's rendering overlaps; [animator] itself, for chaining.
|
|
23
|
+
*/
|
|
24
|
+
fun during(view: View, animator: Animator): Animator {
|
|
25
|
+
if (!changesAlpha(animator, view)) return animator
|
|
26
|
+
val hold = Hold({ view.layerType }, { view.setLayerType(it, null) })
|
|
27
|
+
animator.addListener(object : AnimatorListenerAdapter() {
|
|
28
|
+
override fun onAnimationStart(animation: Animator) {
|
|
29
|
+
hold.take(changesAlpha = true, overlapping = view.hasOverlappingRendering())
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
override fun onAnimationCancel(animation: Animator) = hold.release()
|
|
33
|
+
|
|
34
|
+
override fun onAnimationEnd(animation: Animator) = hold.release()
|
|
35
|
+
})
|
|
36
|
+
return animator
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Whether an animation takes a layer: it fades, its view's rendering overlaps, and the view has no layer. */
|
|
40
|
+
fun takes(changesAlpha: Boolean, overlapping: Boolean, layerType: Int): Boolean =
|
|
41
|
+
changesAlpha && overlapping && layerType == View.LAYER_TYPE_NONE
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* The layer one animation took on one view, through [layerType] and [setLayerType]: taken at most once at a time,
|
|
45
|
+
* given back once, and only while it is still the hardware layer it set.
|
|
46
|
+
*/
|
|
47
|
+
class Hold(private val layerType: () -> Int, private val setLayerType: (Int) -> Unit) {
|
|
48
|
+
/** Whether the animation holds the view's layer now. */
|
|
49
|
+
var isTaken = false
|
|
50
|
+
private set
|
|
51
|
+
|
|
52
|
+
/** The animation starts: the layer, when [takes] says so. */
|
|
53
|
+
fun take(changesAlpha: Boolean, overlapping: Boolean) {
|
|
54
|
+
if (isTaken || !takes(changesAlpha, overlapping, layerType())) return
|
|
55
|
+
setLayerType(View.LAYER_TYPE_HARDWARE)
|
|
56
|
+
isTaken = true
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** The animation ended or stopped: the layer goes, unless someone changed it meanwhile. */
|
|
60
|
+
fun release() {
|
|
61
|
+
if (!isTaken) return
|
|
62
|
+
isTaken = false
|
|
63
|
+
if (layerType() == View.LAYER_TYPE_HARDWARE) setLayerType(View.LAYER_TYPE_NONE)
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Whether [animator], or an animator in it, writes the alpha of [view].
|
|
68
|
+
private fun changesAlpha(animator: Animator, view: View): Boolean = when (animator) {
|
|
69
|
+
is AnimatorSet -> animator.childAnimations.any { changesAlpha(it, view) }
|
|
70
|
+
is ObjectAnimator -> animator.target === view && animator.values.orEmpty().any { it.propertyName == ALPHA }
|
|
71
|
+
else -> false
|
|
72
|
+
}
|
|
73
|
+
}
|
package/android/src/main/java/dev/apexnative/navigation/views/animations/ValueInterpolation.kt
CHANGED
|
@@ -18,6 +18,18 @@ internal object ValueInterpolation {
|
|
|
18
18
|
else -> from + fraction * (to - from)
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
+
/**
|
|
22
|
+
* An angle in degrees at [fraction] of the way along the shorter arc, as iOS turns a shared element: 0 → 270 turns
|
|
23
|
+
* back by 90, 170 → −170 turns on by 20; half a turn goes on. At the end of the time exactly [to].
|
|
24
|
+
*/
|
|
25
|
+
fun angle(from: Float, to: Float, fraction: Float, time: Float = fraction): Float {
|
|
26
|
+
if (time <= 0f) return from
|
|
27
|
+
if (time >= 1f) return to
|
|
28
|
+
val forward = ((to - from) % FULL_TURN + FULL_TURN) % FULL_TURN
|
|
29
|
+
val turn = if (forward > HALF_TURN) forward - FULL_TURN else forward
|
|
30
|
+
return from + fraction * turn
|
|
31
|
+
}
|
|
32
|
+
|
|
21
33
|
/**
|
|
22
34
|
* A color (an sRGB color int, as React Native keeps a background; none is transparent) at [fraction] of the way
|
|
23
35
|
* from [from] to [to]: L, a and b in Lab (D65), the alpha linear, the result rounded; at the end of the time
|
|
@@ -90,4 +102,6 @@ internal object ValueInterpolation {
|
|
|
90
102
|
private const val WHITE_Z = 108.883
|
|
91
103
|
private const val EPSILON = 0.008856
|
|
92
104
|
private const val KAPPA = 903.3
|
|
105
|
+
private const val FULL_TURN = 360f
|
|
106
|
+
private const val HALF_TURN = 180f
|
|
93
107
|
}
|
package/android/src/main/java/dev/apexnative/navigation/views/bottomtabs/BottomTabsLayout.kt
CHANGED
|
@@ -10,13 +10,15 @@ import androidx.annotation.RestrictTo
|
|
|
10
10
|
import androidx.core.view.ViewCompat
|
|
11
11
|
import androidx.core.view.WindowInsetsCompat
|
|
12
12
|
import dev.apexnative.navigation.utils.MaterialTheme
|
|
13
|
+
import dev.apexnative.navigation.viewcontrollers.bottomtabs.CustomTabRowGeometry
|
|
13
14
|
|
|
14
15
|
/**
|
|
15
16
|
* The view of bottom tabs: the view of each tab made so far fills it, the selected one visible and the others invisible
|
|
16
17
|
* but laid out, so a switch shows a tab at once and its screens render; the bar's [container] lies over them at the
|
|
17
18
|
* bottom, the module's bar ([bar], `R.id.bottomTabs`) in it. The container's distance from the bottom ([barOffset]:
|
|
18
19
|
* `bottomMargin`, and the navigation bar under a compact bar) is this view's own inset, not a layout parameter a root
|
|
19
|
-
* may ignore. The
|
|
20
|
+
* may ignore. The custom [row], when the tabs have one, lies right after the container and over the bar. The window
|
|
21
|
+
* insets go on to every child as they came. Main thread only.
|
|
20
22
|
*/
|
|
21
23
|
@SuppressLint("ViewConstructor")
|
|
22
24
|
@RestrictTo(RestrictTo.Scope.LIBRARY)
|
|
@@ -34,8 +36,25 @@ class BottomTabsLayout(context: Context) : FrameLayout(context) {
|
|
|
34
36
|
/** The window insets reached this view: the bar's room and the screens' room may have changed. */
|
|
35
37
|
internal var onInsets: (() -> Unit)? = null
|
|
36
38
|
|
|
37
|
-
/**
|
|
39
|
+
/**
|
|
40
|
+
* The custom row of the tabs, when every tab draws its cell (`bottomTab.component`): a child right after the
|
|
41
|
+
* container, laid out over the bar, going with the container.
|
|
42
|
+
*/
|
|
38
43
|
internal var row: CustomTabRow? = null
|
|
44
|
+
set(value) {
|
|
45
|
+
if (field === value) return
|
|
46
|
+
field?.let {
|
|
47
|
+
removeView(it)
|
|
48
|
+
it.container = null
|
|
49
|
+
}
|
|
50
|
+
field = value
|
|
51
|
+
if (value == null) return
|
|
52
|
+
(value.parent as? ViewGroup)?.removeView(value)
|
|
53
|
+
value.container = container
|
|
54
|
+
value.place = null
|
|
55
|
+
// Its frame is absolute: the layout below puts it in its parameters.
|
|
56
|
+
addView(value, indexOfChild(container) + 1, LayoutParams(0, 0, Gravity.TOP or Gravity.LEFT))
|
|
57
|
+
}
|
|
39
58
|
|
|
40
59
|
/** The container's distance from the bottom, in pixels. */
|
|
41
60
|
internal var barOffset = 0
|
|
@@ -59,6 +78,16 @@ class BottomTabsLayout(context: Context) : FrameLayout(context) {
|
|
|
59
78
|
}
|
|
60
79
|
}
|
|
61
80
|
|
|
81
|
+
/**
|
|
82
|
+
* How far the container goes down to leave the screen whole: from the top of what the bar shows — the container, or
|
|
83
|
+
* the custom row that rises above it — to this view's bottom, in pixels.
|
|
84
|
+
*/
|
|
85
|
+
internal val hideDistance: Int
|
|
86
|
+
get() {
|
|
87
|
+
val rowTop = row?.takeIf { it.visibility != View.GONE }?.top ?: container.top
|
|
88
|
+
return (height - minOf(container.top, rowTop)).coerceAtLeast(0)
|
|
89
|
+
}
|
|
90
|
+
|
|
62
91
|
/** [view] of a tab joins under the bar, invisible until its tab shows. */
|
|
63
92
|
internal fun addTab(view: View) {
|
|
64
93
|
if (view.parent === this) return
|
|
@@ -85,5 +114,35 @@ class BottomTabsLayout(context: Context) : FrameLayout(context) {
|
|
|
85
114
|
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
|
|
86
115
|
super.onLayout(changed, left, top, right, bottom)
|
|
87
116
|
if (barOffset != 0) container.offsetTopAndBottom(-barOffset)
|
|
117
|
+
row?.let(::placeRow)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// The row over the bar where the bar rests (its container's motion the row takes before each frame), when its place
|
|
121
|
+
// changed: laid out there at once, and kept in its parameters, so the next passes measure it and put it there the
|
|
122
|
+
// same way. A hidden bar keeps the row where it was.
|
|
123
|
+
private fun placeRow(row: CustomTabRow) {
|
|
124
|
+
if (container.visibility == View.GONE || bar.width == 0) return
|
|
125
|
+
val barLeft = container.left + bar.left
|
|
126
|
+
val barTop = container.top + bar.top
|
|
127
|
+
val place = row.placeOver(
|
|
128
|
+
barLeft = barLeft,
|
|
129
|
+
barRight = barLeft + bar.width,
|
|
130
|
+
barBottom = barTop + bar.height,
|
|
131
|
+
barHeight = bar.height,
|
|
132
|
+
tabsHeight = height,
|
|
133
|
+
navigationBar = navigationBarInset,
|
|
134
|
+
)
|
|
135
|
+
if (!CustomTabRowGeometry.needsPlacing(row.place, place)) return
|
|
136
|
+
row.place = place
|
|
137
|
+
val params = row.layoutParams as LayoutParams
|
|
138
|
+
params.width = place.right - place.left
|
|
139
|
+
params.height = place.bottom - place.top
|
|
140
|
+
params.leftMargin = place.left
|
|
141
|
+
params.topMargin = place.top
|
|
142
|
+
row.measure(
|
|
143
|
+
MeasureSpec.makeMeasureSpec(params.width, MeasureSpec.EXACTLY),
|
|
144
|
+
MeasureSpec.makeMeasureSpec(params.height, MeasureSpec.EXACTLY),
|
|
145
|
+
)
|
|
146
|
+
row.layout(place.left, place.top, place.right, place.bottom)
|
|
88
147
|
}
|
|
89
148
|
}
|
|
@@ -11,18 +11,21 @@ import android.view.MotionEvent
|
|
|
11
11
|
import android.view.View
|
|
12
12
|
import android.view.ViewGroup
|
|
13
13
|
import android.view.ViewOutlineProvider
|
|
14
|
+
import android.view.ViewTreeObserver
|
|
14
15
|
import android.widget.FrameLayout
|
|
15
16
|
import android.widget.LinearLayout
|
|
16
17
|
import dev.apexnative.navigation.customrow.BottomTabsCustomRowOptions
|
|
17
18
|
import dev.apexnative.navigation.utils.TestIds
|
|
19
|
+
import dev.apexnative.navigation.utils.UiUtils
|
|
18
20
|
import dev.apexnative.navigation.viewcontrollers.bottomtabs.CustomTabRowGeometry
|
|
19
21
|
|
|
20
22
|
/**
|
|
21
|
-
* The custom row of bottom tabs on Android (`bottomTabs.customRow`): a container over the bar,
|
|
22
|
-
* that holds the tabs' cells in one row of equal widths in tab order. It owns its
|
|
23
|
-
* the touches: each cell's place takes them, the React elements in the cell don't, and a tap
|
|
24
|
-
* goes to [onPress] or [onLongPress] with the tab's index.
|
|
25
|
-
*
|
|
23
|
+
* The custom row of bottom tabs on Android (`bottomTabs.customRow`): a container over the bar, a child of the tabs'
|
|
24
|
+
* view right after the bar's container, that holds the tabs' cells in one row of equal widths in tab order. It owns its
|
|
25
|
+
* look, its visibility and the touches: each cell's place takes them, the React elements in the cell don't, and a tap
|
|
26
|
+
* or a long press of a place goes to [onPress] or [onLongPress] with the tab's index. The tabs' view lays it out over
|
|
27
|
+
* the bar ([placeOver]); before each frame it goes with the bar's [container]. What it looks like is its tabs'. Main
|
|
28
|
+
* thread only.
|
|
26
29
|
*/
|
|
27
30
|
@SuppressLint("ViewConstructor")
|
|
28
31
|
internal class CustomTabRow(context: Context) : FrameLayout(context) {
|
|
@@ -42,11 +45,23 @@ internal class CustomTabRow(context: Context) : FrameLayout(context) {
|
|
|
42
45
|
/** A cell's place was long-pressed. */
|
|
43
46
|
var onLongPress: ((Int) -> Unit)? = null
|
|
44
47
|
|
|
45
|
-
/** `bottomTabs.customRow` of the tabs, resolved:
|
|
48
|
+
/** `bottomTabs.customRow` of the tabs, resolved: its height and margins say where the row lies. */
|
|
46
49
|
var options = BottomTabsCustomRowOptions()
|
|
47
50
|
|
|
48
51
|
/**
|
|
49
|
-
*
|
|
52
|
+
* The bar's container the row goes with: its motion (a ride, a slide, the hide on scroll), its alpha, its height
|
|
53
|
+
* over the screens and whether it shows. The tabs' view sets it as the row joins it.
|
|
54
|
+
*/
|
|
55
|
+
var container: View? = null
|
|
56
|
+
|
|
57
|
+
// Before each frame of the window the row is in: it goes with the container.
|
|
58
|
+
private val following = ViewTreeObserver.OnPreDrawListener {
|
|
59
|
+
container?.let(::follow)
|
|
60
|
+
true
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Where the tabs' view put the row last, in it; none before it did. The frame holds `bottomMargin` below the
|
|
50
65
|
* content: the row draws, clips, casts its shadow, lays its cells and takes touches only over the content, and the
|
|
51
66
|
* margin below lets the touches through to what is under it.
|
|
52
67
|
*/
|
|
@@ -117,10 +132,57 @@ internal class CustomTabRow(context: Context) : FrameLayout(context) {
|
|
|
117
132
|
return super.dispatchTouchEvent(event)
|
|
118
133
|
}
|
|
119
134
|
|
|
120
|
-
/**
|
|
121
|
-
|
|
122
|
-
|
|
135
|
+
/**
|
|
136
|
+
* Where the row lies over the bar whose edges are [barLeft], [barRight] and [barBottom] and whose height is
|
|
137
|
+
* [barHeight], in the tabs' view of [tabsHeight] with the navigation bar's [navigationBar] at its bottom: its
|
|
138
|
+
* height and margins from [options].
|
|
139
|
+
*/
|
|
140
|
+
fun placeOver(barLeft: Int, barRight: Int, barBottom: Int, barHeight: Int, tabsHeight: Int, navigationBar: Int) =
|
|
141
|
+
CustomTabRowGeometry.inTabs(
|
|
142
|
+
barLeft = barLeft,
|
|
143
|
+
barRight = barRight,
|
|
144
|
+
barBottom = barBottom,
|
|
145
|
+
tabsHeight = tabsHeight,
|
|
146
|
+
navigationBar = navigationBar,
|
|
147
|
+
margin = UiUtils.dpToPixels(
|
|
148
|
+
context,
|
|
149
|
+
options.horizontalMargin.get(CustomTabRowGeometry.HORIZONTAL_MARGIN_DP.toDouble()),
|
|
150
|
+
),
|
|
151
|
+
content = CustomTabRowGeometry.content(
|
|
152
|
+
barHeight = barHeight,
|
|
153
|
+
height = options.height.get(null)?.let { UiUtils.dpToPixels(context, it) },
|
|
154
|
+
extra = UiUtils.dpToPixels(context, CustomTabRowGeometry.EXTRA_HEIGHT_DP.toDouble()),
|
|
155
|
+
),
|
|
156
|
+
bottomMargin = UiUtils.dpToPixels(context, options.bottomMargin.get(0.0)),
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
override fun onAttachedToWindow() {
|
|
160
|
+
super.onAttachedToWindow()
|
|
161
|
+
viewTreeObserver.addOnPreDrawListener(following)
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
override fun onDetachedFromWindow() {
|
|
165
|
+
viewTreeObserver.removeOnPreDrawListener(following)
|
|
166
|
+
super.onDetachedFromWindow()
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// The row hides with the bar and takes its motion and its alpha (the tabs' own alpha and motion, and their
|
|
170
|
+
// parents', the row has as their child); it stands as high as the container, and leaves accessibility while its
|
|
171
|
+
// tabs wait to show.
|
|
172
|
+
private fun follow(container: View) {
|
|
173
|
+
val wanted = if (container.visibility != View.GONE) View.VISIBLE else View.GONE
|
|
123
174
|
if (visibility != wanted) visibility = wanted
|
|
175
|
+
if (alpha != container.alpha) alpha = container.alpha
|
|
176
|
+
if (translationX != container.translationX) translationX = container.translationX
|
|
177
|
+
if (translationY != container.translationY) translationY = container.translationY
|
|
178
|
+
val lift = CustomTabRowGeometry.lift(container.z, elevation)
|
|
179
|
+
if (translationZ != lift) translationZ = lift
|
|
180
|
+
val mode = if (hiddenFromAccessibility()) {
|
|
181
|
+
View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
|
|
182
|
+
} else {
|
|
183
|
+
View.IMPORTANT_FOR_ACCESSIBILITY_AUTO
|
|
184
|
+
}
|
|
185
|
+
if (importantForAccessibility != mode) importantForAccessibility = mode
|
|
124
186
|
}
|
|
125
187
|
|
|
126
188
|
/** Draws [look]; nothing changes when it is the one drawn. */
|