@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
|
@@ -3,36 +3,25 @@ package dev.apexnative.navigation.react
|
|
|
3
3
|
import android.app.Activity
|
|
4
4
|
import android.graphics.Rect
|
|
5
5
|
import androidx.core.view.WindowInsetsCompat
|
|
6
|
-
import
|
|
7
|
-
import
|
|
8
|
-
import com.facebook.react.bridge.WritableMap
|
|
9
|
-
import dev.apexnative.navigation.react.events.EventContract
|
|
6
|
+
import dev.apexnative.navigation.react.ScreenEnvironment.Region
|
|
7
|
+
import dev.apexnative.navigation.react.ScreenEnvironment.Regions
|
|
10
8
|
import dev.apexnative.navigation.react.events.EventEmitter
|
|
11
9
|
import dev.apexnative.navigation.utils.WindowFolds
|
|
12
|
-
import java.util.concurrent.ConcurrentHashMap
|
|
13
10
|
|
|
14
11
|
/**
|
|
15
|
-
*
|
|
16
|
-
* (a fold the window is divided at, `division`) and where hardware covers it (a fold that hides what is
|
|
17
|
-
* camera cutout: `occlusion`). Only those on the screen, cut to it: a fold at the edge of a screen of a
|
|
18
|
-
* it. Measured on the main thread as each screen lays out, and again after a change of the window's
|
|
19
|
-
* the window out);
|
|
20
|
-
* `ANN.ScreenRegionsChanged`.
|
|
12
|
+
* Measures the reserved regions of each screen (`useReservedRegions`), in dp from the top left of its content: where
|
|
13
|
+
* it divides (a fold the window is divided at, `division`) and where hardware covers it (a fold that hides what is
|
|
14
|
+
* under it, the camera cutout: `occlusion`). Only those on the screen, cut to it: a fold at the edge of a screen of a
|
|
15
|
+
* pair is not on it. Measured on the main thread as each screen lays out, and again after a change of the window's
|
|
16
|
+
* folds (which lays the window out); [ScreenEnvironment] keeps them, answers JavaScript's reads and tells it of every
|
|
17
|
+
* change as `ANN.ScreenRegionsChanged`.
|
|
21
18
|
*/
|
|
22
19
|
internal object ScreenRegions {
|
|
23
|
-
data class Region(val x: Double, val y: Double, val width: Double, val height: Double)
|
|
24
|
-
|
|
25
|
-
data class Regions(val division: List<Region>, val occlusion: List<Region>) {
|
|
26
|
-
val isEmpty: Boolean get() = division.isEmpty() && occlusion.isEmpty()
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
private val NONE = Regions(emptyList(), emptyList())
|
|
30
|
-
private val regions = ConcurrentHashMap<String, Regions>()
|
|
31
|
-
|
|
32
20
|
/**
|
|
33
21
|
* Measures the regions of the screen whose content the layout puts at [content] in the window (pixels), with the
|
|
34
|
-
* window's [insets] (the camera cutout), and
|
|
35
|
-
* JavaScript the screen was made for: silent once it went. Main thread
|
|
22
|
+
* window's [insets] (the camera cutout), and keeps them in [ScreenEnvironment], which tells JavaScript when they
|
|
23
|
+
* changed through [emitter], the one of the JavaScript the screen was made for: silent once it went. Main thread
|
|
24
|
+
* only.
|
|
36
25
|
*/
|
|
37
26
|
fun update(
|
|
38
27
|
activity: Activity,
|
|
@@ -49,17 +38,7 @@ internal object ScreenRegions {
|
|
|
49
38
|
occlusion = (folds.filter { it.occludes }.map { it.bounds } + cutouts)
|
|
50
39
|
.mapNotNull { onScreen(it, content, density) },
|
|
51
40
|
)
|
|
52
|
-
|
|
53
|
-
// None at first is what JavaScript reads before the first layout.
|
|
54
|
-
if (previous == value || (previous == null && value.isEmpty)) return
|
|
55
|
-
emitter.emitScreenRegionsChanged(screenId, toMap(value))
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/** `{ division, occlusion }`, empty lists for an unknown screen and before its first layout. Any thread. */
|
|
59
|
-
fun get(screenId: String): WritableMap = toMap(regions[screenId] ?: NONE)
|
|
60
|
-
|
|
61
|
-
fun remove(screenId: String) {
|
|
62
|
-
regions.remove(screenId)
|
|
41
|
+
ScreenEnvironment.setRegions(screenId, value, ScreenEnvironment.changesOf(emitter))
|
|
63
42
|
}
|
|
64
43
|
|
|
65
44
|
// The part of [bounds] (window pixels) on the screen, in dp from its top left; null off it. A fold without width
|
|
@@ -74,20 +53,4 @@ internal object ScreenRegions {
|
|
|
74
53
|
height = part.height() / density.toDouble(),
|
|
75
54
|
)
|
|
76
55
|
}
|
|
77
|
-
|
|
78
|
-
private fun toMap(value: Regions): WritableMap = Arguments.createMap().apply {
|
|
79
|
-
putArray(EventContract.ScreenRegionsChanged.DIVISION, toArray(value.division))
|
|
80
|
-
putArray(EventContract.ScreenRegionsChanged.OCCLUSION, toArray(value.occlusion))
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
private fun toArray(regions: List<Region>): WritableArray = Arguments.createArray().apply {
|
|
84
|
-
for (region in regions) {
|
|
85
|
-
pushMap(Arguments.createMap().apply {
|
|
86
|
-
putDouble(EventContract.ScreenRegionRect.X, region.x)
|
|
87
|
-
putDouble(EventContract.ScreenRegionRect.Y, region.y)
|
|
88
|
-
putDouble(EventContract.ScreenRegionRect.WIDTH, region.width)
|
|
89
|
-
putDouble(EventContract.ScreenRegionRect.HEIGHT, region.height)
|
|
90
|
-
})
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
56
|
}
|
|
@@ -1,25 +1,17 @@
|
|
|
1
1
|
package dev.apexnative.navigation.react
|
|
2
2
|
|
|
3
|
-
import android.
|
|
4
|
-
import android.
|
|
5
|
-
import com.facebook.react.
|
|
6
|
-
import com.facebook.react.uimanager.UIManagerHelper
|
|
7
|
-
import com.facebook.react.uimanager.events.Event
|
|
8
|
-
import com.facebook.react.uimanager.events.EventDispatcherListener
|
|
9
|
-
import com.facebook.react.views.scroll.ScrollEvent
|
|
3
|
+
import android.view.ViewGroup
|
|
4
|
+
import android.widget.HorizontalScrollView
|
|
5
|
+
import com.facebook.react.views.scroll.ReactScrollViewHelper
|
|
10
6
|
import com.facebook.react.views.scroll.ScrollEventType
|
|
11
|
-
import dev.apexnative.navigation.utils.NavigationLog
|
|
12
|
-
import dev.apexnative.navigation.utils.UiThread
|
|
13
|
-
import dev.apexnative.navigation.viewcontrollers.viewcontroller.ScrollEventListener
|
|
14
|
-
import java.lang.reflect.Field
|
|
15
|
-
import java.util.Collections
|
|
16
|
-
import java.util.WeakHashMap
|
|
17
7
|
|
|
18
8
|
/**
|
|
19
|
-
* The scroll events of React Native's
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
* the
|
|
9
|
+
* The scroll events of React Native's scroll views, as the screens' scroll listeners hear them: through React Native's
|
|
10
|
+
* public scroll listener ([ReactScrollViewHelper.addScrollListener]), which every `ReactScrollView`,
|
|
11
|
+
* `ReactHorizontalScrollView` and `ReactNestedScrollView` of the process calls on the main thread just before it
|
|
12
|
+
* dispatches the same event to JavaScript, after the same throttle. One listener serves every screen and every React
|
|
13
|
+
* context; it is added while a screen listens and removed when the last one stops. Horizontal scroll views are left
|
|
14
|
+
* out: a pager or a carousel doesn't move the bars. Main thread only.
|
|
23
15
|
*/
|
|
24
16
|
internal object ScrollEvents {
|
|
25
17
|
/** What a scroll event is. */
|
|
@@ -28,133 +20,62 @@ internal object ScrollEvents {
|
|
|
28
20
|
/** One scroll event: the list's tag (its view's id), its kind, its vertical offset and speed, in pixels. */
|
|
29
21
|
class Reading(val viewTag: Int, val kind: Kind, val offsetY: Float, val velocityY: Float)
|
|
30
22
|
|
|
31
|
-
|
|
23
|
+
// One screen's listening: its own object, so that a stop takes away that listening and no other.
|
|
24
|
+
private class Listening(val handler: (Reading) -> Unit)
|
|
32
25
|
|
|
33
|
-
|
|
34
|
-
private
|
|
26
|
+
// The screens' listenings, in the order they came.
|
|
27
|
+
private val listenings = mutableListOf<Listening>()
|
|
35
28
|
|
|
36
|
-
//
|
|
37
|
-
private val
|
|
29
|
+
// React Native holds its scroll listeners weakly: this object keeps the one it added for as long as it is added.
|
|
30
|
+
private val listener = object : ReactScrollViewHelper.ScrollListener {
|
|
31
|
+
override fun onScroll(
|
|
32
|
+
scrollView: ViewGroup?,
|
|
33
|
+
scrollEventType: ScrollEventType?,
|
|
34
|
+
xVelocity: Float,
|
|
35
|
+
yVelocity: Float,
|
|
36
|
+
) = hear(scrollView, scrollEventType, yVelocity)
|
|
38
37
|
|
|
39
|
-
|
|
40
|
-
fun waitForDispatcher(listener: ScrollEventListener) {
|
|
41
|
-
waiting += listener
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/** A React context is ready (`ReloadCoordinator`): the listeners waiting for an event dispatcher listen now. */
|
|
45
|
-
fun dispatcherReady() {
|
|
46
|
-
val listeners = waiting.toList()
|
|
47
|
-
waiting.clear()
|
|
48
|
-
listeners.forEach(ScrollEventListener::attachIfNeeded)
|
|
38
|
+
override fun onLayout(scrollView: ViewGroup?) = Unit
|
|
49
39
|
}
|
|
50
40
|
|
|
51
41
|
/**
|
|
52
|
-
* Calls [handler]
|
|
53
|
-
*
|
|
54
|
-
* its scroll events can't be read.
|
|
42
|
+
* Calls [handler] with each scroll event of a vertical scroll view of the app, until the function returned is
|
|
43
|
+
* called.
|
|
55
44
|
*/
|
|
56
|
-
fun listen(
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
val listener = EventDispatcherListener { event ->
|
|
66
|
-
// The event goes back to its pool after the dispatch: it is read now, on the thread that dispatches.
|
|
67
|
-
val reading = read(event) ?: return@EventDispatcherListener
|
|
68
|
-
if (UiThread.isMainThread()) handler(reading) else UiThread.post { handler(reading) }
|
|
45
|
+
fun listen(handler: (Reading) -> Unit): () -> Unit {
|
|
46
|
+
val listening = Listening(handler)
|
|
47
|
+
if (listenings.isEmpty()) ReactScrollViewHelper.addScrollListener(listener)
|
|
48
|
+
listenings += listening
|
|
49
|
+
return {
|
|
50
|
+
// By identity: the second call of a stop finds nothing to take away.
|
|
51
|
+
if (listenings.removeAll { it === listening } && listenings.isEmpty()) {
|
|
52
|
+
ReactScrollViewHelper.removeScrollListener(listener)
|
|
53
|
+
}
|
|
69
54
|
}
|
|
70
|
-
dispatcher.addListener(listener)
|
|
71
|
-
return { dispatcher.removeListener(listener) }
|
|
72
55
|
}
|
|
73
56
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
val
|
|
77
|
-
|
|
78
|
-
val type = fields.type.get(event) as? ScrollEventType ?: return null
|
|
79
|
-
val offsetY = readFloat(fields.offsetY, event) ?: return null
|
|
80
|
-
val velocityY = readFloat(fields.velocityY, event) ?: return null
|
|
81
|
-
Reading(event.viewTag, kindOf(type), offsetY, velocityY)
|
|
82
|
-
} catch (e: ReflectiveOperationException) {
|
|
83
|
-
null
|
|
84
|
-
} catch (e: IllegalArgumentException) {
|
|
85
|
-
null
|
|
86
|
-
}
|
|
57
|
+
/** One call of React Native's listener: the handlers listening now hear its reading, if it has one. */
|
|
58
|
+
fun hear(scrollView: ViewGroup?, type: ScrollEventType?, velocityY: Float) {
|
|
59
|
+
val reading = read(scrollView, type, velocityY) ?: return
|
|
60
|
+
listenings.toList().forEach { it.handler(reading) }
|
|
87
61
|
}
|
|
88
62
|
|
|
89
|
-
|
|
63
|
+
/**
|
|
64
|
+
* The reading of one call of React Native's listener: none for a horizontal scroll view, or when React Native gives
|
|
65
|
+
* no view or no kind. The offset is the view's `scrollY` and the speed React Native's own, in pixels, as the event
|
|
66
|
+
* sent to JavaScript carries them before it converts them to dp.
|
|
67
|
+
*/
|
|
68
|
+
fun read(scrollView: ViewGroup?, type: ScrollEventType?, velocityY: Float): Reading? {
|
|
69
|
+
if (scrollView == null || type == null || scrollView is HorizontalScrollView) return null
|
|
70
|
+
return Reading(scrollView.id, kindOf(type), scrollView.scrollY.toFloat(), velocityY)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** The kind of a reading for React Native's [type]. */
|
|
74
|
+
fun kindOf(type: ScrollEventType): Kind = when (type) {
|
|
90
75
|
ScrollEventType.BEGIN_DRAG -> Kind.BeginDrag
|
|
91
76
|
ScrollEventType.END_DRAG -> Kind.EndDrag
|
|
92
77
|
ScrollEventType.SCROLL -> Kind.Scroll
|
|
93
78
|
ScrollEventType.MOMENTUM_BEGIN -> Kind.MomentumBegin
|
|
94
79
|
ScrollEventType.MOMENTUM_END -> Kind.MomentumEnd
|
|
95
80
|
}
|
|
96
|
-
|
|
97
|
-
@Synchronized
|
|
98
|
-
private fun fields(): Fields? {
|
|
99
|
-
if (lookedUp) return fields
|
|
100
|
-
lookedUp = true
|
|
101
|
-
fields = try {
|
|
102
|
-
val owner = ScrollEvent::class.java
|
|
103
|
-
val type = declaredField(owner, TYPE_NAMES) { ScrollEventType::class.java.isAssignableFrom(it) }
|
|
104
|
-
val offsetY = declaredField(owner, OFFSET_NAMES, ::isNumeric)
|
|
105
|
-
val velocityY = declaredField(owner, VELOCITY_NAMES, ::isNumeric)
|
|
106
|
-
if (type != null && offsetY != null && velocityY != null) Fields(type, offsetY, velocityY) else null
|
|
107
|
-
} catch (e: ReflectiveOperationException) {
|
|
108
|
-
null
|
|
109
|
-
} catch (e: SecurityException) {
|
|
110
|
-
null
|
|
111
|
-
}
|
|
112
|
-
if (fields == null) {
|
|
113
|
-
Log.w(
|
|
114
|
-
NavigationLog.TAG,
|
|
115
|
-
"[ApexNavigation] React Native's scroll events changed: hideOnScroll and the large title " +
|
|
116
|
-
"don't follow the scroll",
|
|
117
|
-
)
|
|
118
|
-
}
|
|
119
|
-
return fields
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
// The names of the fields: React Native 0.87 first, then the earlier ones with the `m` prefix.
|
|
123
|
-
private val TYPE_NAMES = listOf("scrollEventType", "mScrollEventType")
|
|
124
|
-
private val OFFSET_NAMES = listOf("scrollY", "mScrollY")
|
|
125
|
-
private val VELOCITY_NAMES = listOf("yVelocity", "mYVelocity")
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* The first of the fields [names] that [owner] declares with a type [accepted] takes, made readable; none when no
|
|
129
|
-
* name fits. React Native renamed them and changed their types between versions.
|
|
130
|
-
*/
|
|
131
|
-
fun declaredField(owner: Class<*>, names: List<String>, accepted: (Class<*>) -> Boolean): Field? {
|
|
132
|
-
for (name in names) {
|
|
133
|
-
val field = try {
|
|
134
|
-
owner.getDeclaredField(name)
|
|
135
|
-
} catch (e: NoSuchFieldException) {
|
|
136
|
-
continue
|
|
137
|
-
}
|
|
138
|
-
if (!accepted(field.type)) continue
|
|
139
|
-
field.isAccessible = true
|
|
140
|
-
return field
|
|
141
|
-
}
|
|
142
|
-
return null
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
/** A number of any kind: a primitive one (`int`, `float`, `double`, …) or a boxed one. */
|
|
146
|
-
fun isNumeric(type: Class<*>): Boolean =
|
|
147
|
-
type in PRIMITIVE_NUMBERS || Number::class.java.isAssignableFrom(type)
|
|
148
|
-
|
|
149
|
-
/** [field] of [target] as a float, whatever numeric type it has; none when it holds no number. */
|
|
150
|
-
fun readFloat(field: Field, target: Any): Float? = (field.get(target) as? Number)?.toFloat()
|
|
151
|
-
|
|
152
|
-
private val PRIMITIVE_NUMBERS = setOf<Class<*>?>(
|
|
153
|
-
Byte::class.javaPrimitiveType,
|
|
154
|
-
Short::class.javaPrimitiveType,
|
|
155
|
-
Int::class.javaPrimitiveType,
|
|
156
|
-
Long::class.javaPrimitiveType,
|
|
157
|
-
Float::class.javaPrimitiveType,
|
|
158
|
-
Double::class.javaPrimitiveType,
|
|
159
|
-
)
|
|
160
81
|
}
|
|
@@ -10,7 +10,7 @@ import com.facebook.react.uimanager.StateWrapper
|
|
|
10
10
|
import com.facebook.react.uimanager.ThemedReactContext
|
|
11
11
|
import com.facebook.react.uimanager.UIManagerHelper
|
|
12
12
|
import com.facebook.react.views.view.ReactViewGroup
|
|
13
|
-
import dev.apexnative.navigation.react.
|
|
13
|
+
import dev.apexnative.navigation.react.ScreenEnvironment
|
|
14
14
|
import dev.apexnative.navigation.viewcontrollers.component.ScreenReadiness
|
|
15
15
|
|
|
16
16
|
/**
|
|
@@ -20,7 +20,7 @@ import dev.apexnative.navigation.viewcontrollers.component.ScreenReadiness
|
|
|
20
20
|
@SuppressLint("ViewConstructor")
|
|
21
21
|
class SafeAreaView(reactContext: ThemedReactContext) : ReactViewGroup(reactContext) {
|
|
22
22
|
private var stopListening: (() -> Unit)? = null
|
|
23
|
-
private var applied:
|
|
23
|
+
private var applied: ScreenEnvironment.Insets? = null
|
|
24
24
|
private var readySent = false
|
|
25
25
|
private var contentSignalled = false
|
|
26
26
|
|
|
@@ -48,7 +48,7 @@ class SafeAreaView(reactContext: ThemedReactContext) : ReactViewGroup(reactConte
|
|
|
48
48
|
stopListening?.invoke()
|
|
49
49
|
field = value
|
|
50
50
|
applied = null
|
|
51
|
-
stopListening = value?.let { id ->
|
|
51
|
+
stopListening = value?.let { id -> ScreenEnvironment.listenToPadding(id) { applyPadding() } }
|
|
52
52
|
// The screen has a safe area view: it counts as rendered once this view says so.
|
|
53
53
|
value?.let(ScreenReadiness::wrapped)
|
|
54
54
|
applyPadding()
|
|
@@ -56,7 +56,7 @@ class SafeAreaView(reactContext: ThemedReactContext) : ReactViewGroup(reactConte
|
|
|
56
56
|
|
|
57
57
|
private fun applyPadding() {
|
|
58
58
|
val state = stateWrapper ?: return
|
|
59
|
-
val padding = screenId?.let(
|
|
59
|
+
val padding = screenId?.let(ScreenEnvironment::padding) ?: return
|
|
60
60
|
if (padding == applied) return
|
|
61
61
|
applied = padding
|
|
62
62
|
state.updateState(
|
|
@@ -69,9 +69,9 @@ class SafeAreaView(reactContext: ThemedReactContext) : ReactViewGroup(reactConte
|
|
|
69
69
|
)
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
-
private fun paddingOf(data: ReadableMap):
|
|
72
|
+
private fun paddingOf(data: ReadableMap): ScreenEnvironment.Insets? {
|
|
73
73
|
if (!data.hasKey("top")) return null
|
|
74
|
-
return
|
|
74
|
+
return ScreenEnvironment.Insets(data.getDouble("top"), data.getDouble("right"), data.getDouble("bottom"),
|
|
75
75
|
data.getDouble("left"))
|
|
76
76
|
}
|
|
77
77
|
|
|
@@ -2,9 +2,13 @@ package dev.apexnative.navigation.slots
|
|
|
2
2
|
|
|
3
3
|
import android.annotation.SuppressLint
|
|
4
4
|
import android.content.Context
|
|
5
|
+
import android.util.Size
|
|
5
6
|
import android.view.MotionEvent
|
|
6
7
|
import android.view.ViewGroup
|
|
7
8
|
import android.widget.FrameLayout
|
|
9
|
+
import com.facebook.react.uimanager.PixelUtil.dpToPx
|
|
10
|
+
import dev.apexnative.navigation.react.ContentHeights
|
|
11
|
+
import dev.apexnative.navigation.react.ContentMounts
|
|
8
12
|
import dev.apexnative.navigation.react.events.AppearanceDispatcher
|
|
9
13
|
import dev.apexnative.navigation.react.events.ComponentType
|
|
10
14
|
import dev.apexnative.navigation.react.events.ScreenAppearance
|
|
@@ -16,9 +20,10 @@ import dev.apexnative.navigation.viewcontrollers.viewcontroller.ScrollEventListe
|
|
|
16
20
|
* What a slot screen (`slot:<Name>`) shows: the empty container its slot of the app's React tree moves its children
|
|
17
21
|
* into, under the screen's id ([SlotContainers]), told of each change of its size. It is the screen's content for the
|
|
18
22
|
* core: ready once the slot's content has mounted into it and, when the screen has a safe area view, the screen
|
|
19
|
-
* rendered into that view; its mounted handlers run at each mount
|
|
20
|
-
*
|
|
21
|
-
*
|
|
23
|
+
* rendered into that view; its mounted handlers run at each mount of Fabric and as the content mounts into it. Its
|
|
24
|
+
* content measures without a height limit in the slot's own layout ([contentSize]). Its appearance events go through
|
|
25
|
+
* its record to the core's dispatcher (`AppearanceDispatcher`), which holds them until it is ready or the hold limit.
|
|
26
|
+
* Destroyed while the id is still its own, it tells JavaScript to take the slot's content away (`ANN.SlotDestroyed`).
|
|
22
27
|
*/
|
|
23
28
|
@SuppressLint("ViewConstructor")
|
|
24
29
|
class SlotScreenView(
|
|
@@ -30,7 +35,7 @@ class SlotScreenView(
|
|
|
30
35
|
private val scrollEventListener = ScrollEventListener()
|
|
31
36
|
private val whenMounted = mutableListOf<Runnable>()
|
|
32
37
|
private val whenRendered = mutableListOf<Runnable>()
|
|
33
|
-
private val
|
|
38
|
+
private val mounts = ContentMounts { !destroyed && isAttachedToWindow }
|
|
34
39
|
private var listening = false
|
|
35
40
|
|
|
36
41
|
// The safe area view of a slot renders in the app's one React tree: every signal of the id is the screen's.
|
|
@@ -84,10 +89,11 @@ class SlotScreenView(
|
|
|
84
89
|
destroyed = true
|
|
85
90
|
whenMounted.clear()
|
|
86
91
|
whenRendered.clear()
|
|
87
|
-
|
|
92
|
+
mounts.clear()
|
|
88
93
|
stopListening()
|
|
89
94
|
// A later screen with the same id keeps the slot.
|
|
90
95
|
if (SlotContainers.get(componentId) !== this) return
|
|
96
|
+
ContentHeights.forgetSlot(componentId)
|
|
91
97
|
SlotContainers.remove(componentId)
|
|
92
98
|
if (!isPreparedPush) AppearanceDispatcher.slotDestroyed(componentId, appearance.generation)
|
|
93
99
|
}
|
|
@@ -120,7 +126,21 @@ class SlotScreenView(
|
|
|
120
126
|
}
|
|
121
127
|
|
|
122
128
|
override fun setOnContentMounted(key: String, handler: Runnable?) {
|
|
123
|
-
if (
|
|
129
|
+
if (!destroyed) mounts.set(key, handler)
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// The slot measures its content in its own layout: the first question asks for it and lays the slot out again; the
|
|
133
|
+
// height comes with that layout, before the mount the handlers hear. The width is the container's, at which the
|
|
134
|
+
// slot lays its content out.
|
|
135
|
+
override fun contentSize(maxWidth: Int): Size? {
|
|
136
|
+
if (destroyed) return null
|
|
137
|
+
if (!ContentHeights.isSlotRequested(componentId)) {
|
|
138
|
+
ContentHeights.requestSlot(componentId)
|
|
139
|
+
SlotContainers.changed(componentId)
|
|
140
|
+
return null
|
|
141
|
+
}
|
|
142
|
+
val height = ContentHeights.slotHeight(componentId)?.takeIf { it > 0f } ?: return null
|
|
143
|
+
return Size(if (width > 0) width else maxWidth, Math.round(height.dpToPx()))
|
|
124
144
|
}
|
|
125
145
|
|
|
126
146
|
// Only the container that holds the slot of the id: a destroyed one, or one a later screen replaced, takes no
|
|
@@ -136,7 +156,7 @@ class SlotScreenView(
|
|
|
136
156
|
val waiting = whenMounted.toList()
|
|
137
157
|
whenMounted.clear()
|
|
138
158
|
waiting.forEach { it.run() }
|
|
139
|
-
|
|
159
|
+
mounts.run()
|
|
140
160
|
checkReadiness()
|
|
141
161
|
}
|
|
142
162
|
|
|
@@ -15,6 +15,7 @@ import com.facebook.react.uimanager.ThemedReactContext
|
|
|
15
15
|
import com.facebook.react.uimanager.UIManagerHelper
|
|
16
16
|
import com.facebook.react.uimanager.events.EventDispatcher
|
|
17
17
|
import com.facebook.react.views.view.ReactViewGroup
|
|
18
|
+
import dev.apexnative.navigation.react.ContentHeights
|
|
18
19
|
|
|
19
20
|
/**
|
|
20
21
|
* The React children of a slot. They live outside the views of the host's surface, in the container of a screen, so
|
|
@@ -79,6 +80,8 @@ class SlotView(reactContext: ThemedReactContext) : ReactViewGroup(reactContext)
|
|
|
79
80
|
}
|
|
80
81
|
private var lastWidth = -1f
|
|
81
82
|
private var lastHeight = -1f
|
|
83
|
+
// The slot was laid out once more, at the same size, for the measure of its content (`ContentHeights`).
|
|
84
|
+
private var measureAsked = false
|
|
82
85
|
|
|
83
86
|
private val slotId: String?
|
|
84
87
|
get() = getTag(R.id.view_tag_native_id) as? String
|
|
@@ -116,7 +119,14 @@ class SlotView(reactContext: ThemedReactContext) : ReactViewGroup(reactContext)
|
|
|
116
119
|
if (container != null && state != null && container.width > 0 && container.height > 0) {
|
|
117
120
|
val width = container.width.toFloat().pxToDp()
|
|
118
121
|
val height = container.height.toFloat().pxToDp()
|
|
119
|
-
|
|
122
|
+
// A container that measures the content gets one layout more for it, with the same size, until the height
|
|
123
|
+
// came.
|
|
124
|
+
val requested = ContentHeights.isSlotRequested(id)
|
|
125
|
+
// A request that went (its screen did) and comes again gets its layout again.
|
|
126
|
+
if (!requested) measureAsked = false
|
|
127
|
+
val measures = !measureAsked && requested && ContentHeights.slotHeight(id) == null
|
|
128
|
+
if (width != lastWidth || height != lastHeight || measures) {
|
|
129
|
+
measureAsked = measureAsked || measures
|
|
120
130
|
lastWidth = width
|
|
121
131
|
lastHeight = height
|
|
122
132
|
state.updateState(
|
|
@@ -136,6 +146,7 @@ class SlotView(reactContext: ThemedReactContext) : ReactViewGroup(reactContext)
|
|
|
136
146
|
}
|
|
137
147
|
|
|
138
148
|
fun drop() {
|
|
149
|
+
measureAsked = false
|
|
139
150
|
SlotContainers.unregister(this)
|
|
140
151
|
(content.parent as? ViewGroup)?.removeView(content)
|
|
141
152
|
}
|
|
@@ -28,7 +28,7 @@ internal object MaterialTheme {
|
|
|
28
28
|
private val wrappers = WeakHashMap<Context, WeakReference<Context>>()
|
|
29
29
|
// Themes of the navigation's own views, each made once over the activity's with a style of its own.
|
|
30
30
|
private val followers = WeakHashMap<Context, Int>()
|
|
31
|
-
// The popup theme of the bar's
|
|
31
|
+
// The popup theme of the bar's "⋮" per activity: the app's theme doesn't change while the activity lives.
|
|
32
32
|
private val popupThemes = WeakHashMap<Activity, Int>()
|
|
33
33
|
|
|
34
34
|
fun context(context: Context): Context {
|
|
@@ -38,11 +38,10 @@ internal object MaterialTheme {
|
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
/**
|
|
41
|
-
* The popup theme of the top bar's
|
|
42
|
-
* theme follows light and dark: the menu takes the bar's own theme, which [rebase]
|
|
43
|
-
* the app's colors. With a Material 3 theme fixed to light or to dark, a day/night
|
|
44
|
-
* current mode's colors where the theme shows the other mode.
|
|
45
|
-
* first opening, in the mode of that moment, and no public API makes it again for a later switch.
|
|
41
|
+
* The popup theme of the top bar's "⋮", over the bar's theme at each opening of its popup, in the mode of that
|
|
42
|
+
* moment. None when the app's theme follows light and dark: the menu takes the bar's own theme, which [rebase]
|
|
43
|
+
* keeps in the current mode, with the app's colors. With a Material 3 theme fixed to light or to dark, a day/night
|
|
44
|
+
* overlay that gives the menu the current mode's colors where the theme shows the other mode.
|
|
46
45
|
*/
|
|
47
46
|
fun popupTheme(context: Context): Int {
|
|
48
47
|
val activity = activityOf(context) ?: return 0
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
package dev.apexnative.navigation.utils
|
|
2
|
+
|
|
3
|
+
import android.animation.ValueAnimator
|
|
4
|
+
import android.annotation.SuppressLint
|
|
5
|
+
import android.content.Context
|
|
6
|
+
import android.os.Build
|
|
7
|
+
import android.provider.Settings
|
|
8
|
+
import dev.apexnative.navigation.NavigationApplication
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Whether the system plays no animations: "Remove animations" in the accessibility settings, or the animator duration
|
|
12
|
+
* scale at 0 in the developer options. Then a transition doesn't wait for its elements' targets and images and moves no
|
|
13
|
+
* element, and the side menu opens and closes at once. Any thread.
|
|
14
|
+
*/
|
|
15
|
+
internal object SystemAnimations {
|
|
16
|
+
/**
|
|
17
|
+
* The animations are off now; [context] reads the scale below Android 8, else the application's. Lint can't see
|
|
18
|
+
* that the decision calls `areAnimatorsEnabled` (API 26) only from Android 8.
|
|
19
|
+
*/
|
|
20
|
+
@SuppressLint("NewApi")
|
|
21
|
+
fun areOff(context: Context? = null): Boolean =
|
|
22
|
+
areOff(Build.VERSION.SDK_INT, { ValueAnimator.areAnimatorsEnabled() }) { durationScaleOf(context) }
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* The decision: from Android 8 the animator's own answer ([animatorsEnabled]), which follows both settings;
|
|
26
|
+
* below it the duration scale ([durationScale]) at 0.
|
|
27
|
+
*/
|
|
28
|
+
fun areOff(sdk: Int, animatorsEnabled: () -> Boolean, durationScale: () -> Float): Boolean =
|
|
29
|
+
if (sdk >= Build.VERSION_CODES.O) !animatorsEnabled() else durationScale() == 0f
|
|
30
|
+
|
|
31
|
+
// Without a context and before the application started (the unit tests), the animations play.
|
|
32
|
+
private fun durationScaleOf(context: Context?): Float {
|
|
33
|
+
val resolver = (context ?: NavigationApplication.instanceOrNull)?.contentResolver ?: return 1f
|
|
34
|
+
return Settings.Global.getFloat(resolver, Settings.Global.ANIMATOR_DURATION_SCALE, 1f)
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -26,16 +26,3 @@ val View.borderRadius: Float
|
|
|
26
26
|
*/
|
|
27
27
|
internal fun resolveBorderRadius(radius: LengthPercentage, width: Int, density: Float): Float =
|
|
28
28
|
(radius.resolve(width / density) * density).coerceAtLeast(0f)
|
|
29
|
-
|
|
30
|
-
/** The scale the parents of [view] give it, along x and along y: a view in a scaled parent shows smaller. */
|
|
31
|
-
fun computeInheritedScale(view: View): Pair<Float, Float> {
|
|
32
|
-
var scaleX = 1f
|
|
33
|
-
var scaleY = 1f
|
|
34
|
-
var parent = view.parent as? View
|
|
35
|
-
while (parent != null) {
|
|
36
|
-
scaleX *= parent.scaleX
|
|
37
|
-
scaleY *= parent.scaleY
|
|
38
|
-
parent = parent.parent as? View
|
|
39
|
-
}
|
|
40
|
-
return scaleX to scaleY
|
|
41
|
-
}
|
|
@@ -9,7 +9,7 @@ import androidx.window.layout.WindowMetricsCalculator
|
|
|
9
9
|
import dev.apexnative.navigation.NavigationActivity
|
|
10
10
|
import dev.apexnative.navigation.options.DefaultOptions
|
|
11
11
|
import dev.apexnative.navigation.options.Options
|
|
12
|
-
import dev.apexnative.navigation.react.
|
|
12
|
+
import dev.apexnative.navigation.react.ScreenEnvironment
|
|
13
13
|
import dev.apexnative.navigation.react.events.EventEmitter
|
|
14
14
|
import dev.apexnative.navigation.utils.SystemUiUtils
|
|
15
15
|
import dev.apexnative.navigation.utils.WindowFolds
|
|
@@ -146,12 +146,14 @@ class ArrangementController internal constructor(
|
|
|
146
146
|
val overlay = plan.layout == ArrangementPlan.Layout.OVERLAY
|
|
147
147
|
val single = plan.layout == ArrangementPlan.Layout.SINGLE
|
|
148
148
|
if (primary is ComponentViewController) {
|
|
149
|
-
val state =
|
|
150
|
-
|
|
149
|
+
val state = ScreenEnvironment.Arrangement(PRIMARY, layout, front = overlay, hidden = false)
|
|
150
|
+
val changes = ScreenEnvironment.changesOf(EventEmitter.of(primary.jsGeneration))
|
|
151
|
+
ScreenEnvironment.setArrangement(primary.id, state, notify, changes)
|
|
151
152
|
}
|
|
152
153
|
if (secondary is ComponentViewController) {
|
|
153
|
-
val state =
|
|
154
|
-
|
|
154
|
+
val state = ScreenEnvironment.Arrangement(SECONDARY, layout, front = false, hidden = single)
|
|
155
|
+
val changes = ScreenEnvironment.changesOf(EventEmitter.of(secondary.jsGeneration))
|
|
156
|
+
ScreenEnvironment.setArrangement(secondary.id, state, notify, changes)
|
|
155
157
|
}
|
|
156
158
|
}
|
|
157
159
|
|
|
@@ -244,9 +246,9 @@ class ArrangementController internal constructor(
|
|
|
244
246
|
// The stack keeps the bar's title, buttons and search by the view of its screen on top: they go with the pair.
|
|
245
247
|
if (!isDestroyed && peekView() != null) performOnParentController { it.onChildDestroyed(this) }
|
|
246
248
|
folds?.removeListener(onFoldsChanged)
|
|
247
|
-
ArrangementStates.remove(primary.id)
|
|
248
|
-
ArrangementStates.remove(secondary.id)
|
|
249
249
|
shownChildren.containerDisappeared()
|
|
250
|
+
// The screens go with the pair: each clears its own environment, its arrangement too, unless its id is
|
|
251
|
+
// another's now.
|
|
250
252
|
super.destroy()
|
|
251
253
|
}
|
|
252
254
|
|
|
@@ -2,6 +2,7 @@ package dev.apexnative.navigation.viewcontrollers.bottomtabs
|
|
|
2
2
|
|
|
3
3
|
import android.view.View
|
|
4
4
|
import dev.apexnative.navigation.viewcontrollers.viewcontroller.ScrollEventListener
|
|
5
|
+
import dev.apexnative.navigation.views.bottomtabs.BottomTabsLayout
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* bottomTabs.hideOnScroll: the tabs hide after a drag that scrolls the screen's content down and come back after one
|
|
@@ -43,9 +44,10 @@ class BottomTabsCollapseBehavior(private val container: () -> View?) :
|
|
|
43
44
|
override fun onHide() {
|
|
44
45
|
val view = container() ?: return
|
|
45
46
|
hiddenByScroll = true
|
|
46
|
-
// Out of the screen with what is under it
|
|
47
|
-
|
|
48
|
-
|
|
47
|
+
// Out of the screen with what is under it (a compact bar floats over its bottom margin and the navigation bar)
|
|
48
|
+
// and the custom row that rises above it.
|
|
49
|
+
val distance = (view.parent as? BottomTabsLayout)?.hideDistance ?: view.height
|
|
50
|
+
view.animate().translationY(distance.toFloat()).setDuration(DURATION).start()
|
|
49
51
|
}
|
|
50
52
|
|
|
51
53
|
private companion object {
|