@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
|
@@ -34,12 +34,14 @@ class TopBarOptions {
|
|
|
34
34
|
@JvmField var rightButtonDisabledColor: ThemeColour = NullThemeColour()
|
|
35
35
|
@JvmField var centerButtonColor: ThemeColour = NullThemeColour()
|
|
36
36
|
@JvmField var centerButtonDisabledColor: ThemeColour = NullThemeColour()
|
|
37
|
+
@JvmField var menuBackgroundColor: ThemeColour = NullThemeColour()
|
|
38
|
+
@JvmField var menuTextColor: ThemeColour = NullThemeColour()
|
|
37
39
|
@JvmField var drawBehind: Bool = NullBool()
|
|
38
40
|
@JvmField var testID: Text = NullText()
|
|
39
41
|
@JvmField var title = TitleOptions()
|
|
40
42
|
@JvmField var subtitle = SubtitleOptions()
|
|
41
43
|
/** `backButton`, `leftButtons`, `rightButtons`, `centerButtons`. */
|
|
42
|
-
@JvmField var buttons = TopBarButtons()
|
|
44
|
+
@ReadFromParent @JvmField var buttons = TopBarButtons()
|
|
43
45
|
@JvmField var customizationId: Text = NullText()
|
|
44
46
|
/** Shared between copies, as its module wants. */
|
|
45
47
|
@JvmField var overflowMenu: ButtonMenuOptions? = null
|
|
@@ -63,7 +65,8 @@ class TopBarOptions {
|
|
|
63
65
|
hideOnTap.hasValue() || hideOnKeyboard.hasValue() || leftButtonColor.hasValue() ||
|
|
64
66
|
rightButtonColor.hasValue() || leftButtonBackgroundColor.hasValue() || rightButtonBackgroundColor.hasValue() ||
|
|
65
67
|
leftButtonDisabledColor.hasValue() || rightButtonDisabledColor.hasValue() || centerButtonColor.hasValue() ||
|
|
66
|
-
centerButtonDisabledColor.hasValue() ||
|
|
68
|
+
centerButtonDisabledColor.hasValue() || menuBackgroundColor.hasValue() || menuTextColor.hasValue() ||
|
|
69
|
+
drawBehind.hasValue() || testID.hasValue() || title.hasValue() ||
|
|
67
70
|
subtitle.hasValue() || buttons.hasValue() || customizationId.hasValue() || overflowMenu != null ||
|
|
68
71
|
verticalBarKeeps.hasValue() || background.hasValue() || scrollEdgeAppearance.hasValue() ||
|
|
69
72
|
barStyle.hasValue() || style.hasValue() || noBorder.hasValue() || searchBar.hasAnyValue() ||
|
|
@@ -85,6 +88,8 @@ class TopBarOptions {
|
|
|
85
88
|
rightButtonDisabledColor = rightButtonDisabledColor.mergeWith(other.rightButtonDisabledColor)
|
|
86
89
|
centerButtonColor = centerButtonColor.mergeWith(other.centerButtonColor)
|
|
87
90
|
centerButtonDisabledColor = centerButtonDisabledColor.mergeWith(other.centerButtonDisabledColor)
|
|
91
|
+
menuBackgroundColor = menuBackgroundColor.mergeWith(other.menuBackgroundColor)
|
|
92
|
+
menuTextColor = menuTextColor.mergeWith(other.menuTextColor)
|
|
88
93
|
if (other.drawBehind.hasValue()) drawBehind = other.drawBehind
|
|
89
94
|
if (other.testID.hasValue()) testID = other.testID
|
|
90
95
|
title.mergeWith(other.title)
|
|
@@ -127,6 +132,8 @@ class TopBarOptions {
|
|
|
127
132
|
rightButtonDisabledColor = rightButtonDisabledColor.withDefault(defaults.rightButtonDisabledColor)
|
|
128
133
|
centerButtonColor = centerButtonColor.withDefault(defaults.centerButtonColor)
|
|
129
134
|
centerButtonDisabledColor = centerButtonDisabledColor.withDefault(defaults.centerButtonDisabledColor)
|
|
135
|
+
menuBackgroundColor = menuBackgroundColor.withDefault(defaults.menuBackgroundColor)
|
|
136
|
+
menuTextColor = menuTextColor.withDefault(defaults.menuTextColor)
|
|
130
137
|
if (!drawBehind.hasValue()) drawBehind = defaults.drawBehind
|
|
131
138
|
if (!testID.hasValue()) testID = defaults.testID
|
|
132
139
|
title.mergeWithDefault(defaults.title)
|
|
@@ -178,6 +185,8 @@ class TopBarOptions {
|
|
|
178
185
|
options.rightButtonDisabledColor = ThemeColour.parse(context, map, "rightButtonDisabledColor")
|
|
179
186
|
options.centerButtonColor = ThemeColour.parse(context, map, "centerButtonColor")
|
|
180
187
|
options.centerButtonDisabledColor = ThemeColour.parse(context, map, "centerButtonDisabledColor")
|
|
188
|
+
options.menuBackgroundColor = ThemeColour.parse(context, map, "menuBackgroundColor")
|
|
189
|
+
options.menuTextColor = ThemeColour.parse(context, map, "menuTextColor")
|
|
181
190
|
options.drawBehind = BoolParser.parse(map, "drawBehind")
|
|
182
191
|
options.testID = TextParser.parse(map, "testID")
|
|
183
192
|
options.title = map.group("title") { TitleOptions.parse(context, it) }
|
|
@@ -212,13 +221,13 @@ class TopBarOptions {
|
|
|
212
221
|
* don't give it; `[]` takes the buttons of the layers below away.
|
|
213
222
|
*/
|
|
214
223
|
class TopBarButtons {
|
|
215
|
-
@JvmField var back = BackButtonOptions()
|
|
224
|
+
@OptionKey("backButton") @JvmField var back = BackButtonOptions()
|
|
216
225
|
/** `topBar.leftButtons`; null when the options don't give them. */
|
|
217
|
-
@JvmField var left: List<ButtonOptions>? = null
|
|
226
|
+
@OptionKey("leftButtons") @JvmField var left: List<ButtonOptions>? = null
|
|
218
227
|
/** `topBar.rightButtons`; null when the options don't give them. */
|
|
219
|
-
@JvmField var right: List<ButtonOptions>? = null
|
|
228
|
+
@OptionKey("rightButtons") @JvmField var right: List<ButtonOptions>? = null
|
|
220
229
|
/** `topBar.centerButtons`; null when the options don't give them. */
|
|
221
|
-
@JvmField var center: List<ButtonOptions>? = null
|
|
230
|
+
@OptionKey("centerButtons") @JvmField var center: List<ButtonOptions>? = null
|
|
222
231
|
|
|
223
232
|
fun hasValue(): Boolean = back.hasValue() || left != null || right != null || center != null
|
|
224
233
|
|
|
@@ -263,7 +272,7 @@ class BackButtonOptions {
|
|
|
263
272
|
@JvmField var showTitle: Bool = NullBool()
|
|
264
273
|
@JvmField var color: ThemeColour = NullThemeColour()
|
|
265
274
|
@JvmField var fontSize: Fraction = NullFraction()
|
|
266
|
-
@JvmField var font = FontOptions()
|
|
275
|
+
@ReadFromParent @JvmField var font = FontOptions()
|
|
267
276
|
@JvmField var iconBackground = IconBackgroundOptions()
|
|
268
277
|
@JvmField var testID: Text = NullText()
|
|
269
278
|
@JvmField var enableMenu: Bool = NullBool()
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
package dev.apexnative.navigation.react
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import android.util.Log
|
|
5
|
+
import com.facebook.react.interfaces.fabric.ReactSurface
|
|
6
|
+
import com.facebook.react.internal.featureflags.ReactNativeFeatureFlags
|
|
7
|
+
import com.facebook.react.modules.i18nmanager.I18nUtil
|
|
8
|
+
import com.facebook.react.runtime.ReactSurfaceImpl
|
|
9
|
+
import com.facebook.soloader.SoLoader
|
|
10
|
+
import dev.apexnative.navigation.utils.NavigationLog
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* The height of React content laid out at a width without a height limit, as iOS measures a sheet's `'content'`
|
|
14
|
+
* detent: Fabric lays out a copy of the tree, so the shown layout stays as it is. A React root's surface measures
|
|
15
|
+
* through `SurfaceHandler::measure`; a slot's content in the slot's own layout, once asked for ([requestSlot]), which
|
|
16
|
+
* keeps the height for [slotHeight] (`ANNSlotContentHeights`, the C++ iOS shares). The native half is the library's
|
|
17
|
+
* own C++ (`android/src/main/jni`), in the codegen library of its components; when that library doesn't load, nothing
|
|
18
|
+
* is measured. Main thread.
|
|
19
|
+
*/
|
|
20
|
+
internal object ContentHeights {
|
|
21
|
+
private const val LIBRARY = "react_codegen_ApexNavigationSpec"
|
|
22
|
+
|
|
23
|
+
private val loaded: Boolean by lazy {
|
|
24
|
+
try {
|
|
25
|
+
SoLoader.loadLibrary(LIBRARY)
|
|
26
|
+
true
|
|
27
|
+
} catch (e: Throwable) {
|
|
28
|
+
Log.w(NavigationLog.TAG, "[ApexNavigation] $LIBRARY doesn't load: no content is measured", e)
|
|
29
|
+
false
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* The height of [surface]'s content at [width] pixels, in pixels, with the layout context the surface lays out
|
|
35
|
+
* with (`ReactSurfaceImpl.updateLayoutSpecs`); null before the surface runs or renders, or when it can't measure.
|
|
36
|
+
*/
|
|
37
|
+
fun surfaceHeight(context: Context, surface: ReactSurface, width: Int): Int? {
|
|
38
|
+
if (width <= 0 || surface !is ReactSurfaceImpl || !loaded) return null
|
|
39
|
+
val density = context.resources.displayMetrics.density
|
|
40
|
+
val scalesFont = ReactNativeFeatureFlags.enableFontScaleChangesUpdatingLayout()
|
|
41
|
+
val fontScale = if (scalesFont) context.resources.configuration.fontScale else 1f
|
|
42
|
+
val height = try {
|
|
43
|
+
nativeSurfaceHeight(
|
|
44
|
+
surface,
|
|
45
|
+
width / density,
|
|
46
|
+
density,
|
|
47
|
+
fontScale,
|
|
48
|
+
I18nUtil.instance.isRTL(context),
|
|
49
|
+
I18nUtil.instance.doLeftAndRightSwapInRTL(context),
|
|
50
|
+
)
|
|
51
|
+
} catch (e: Throwable) {
|
|
52
|
+
Log.w(NavigationLog.TAG, "[ApexNavigation] the content of a surface can't be measured", e)
|
|
53
|
+
return null
|
|
54
|
+
}
|
|
55
|
+
return if (height > 0f) Math.round(height * density) else null
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** The content of the slot [slotId] is measured from its next layout on. */
|
|
59
|
+
fun requestSlot(slotId: String) {
|
|
60
|
+
if (loaded) nativeRequestSlot(slotId)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** The content of the slot [slotId] is measured. */
|
|
64
|
+
fun isSlotRequested(slotId: String): Boolean = loaded && nativeIsSlotRequested(slotId)
|
|
65
|
+
|
|
66
|
+
/** The height of the content of the slot [slotId] at its last layout, in dp; null before it. */
|
|
67
|
+
fun slotHeight(slotId: String): Float? = if (loaded) nativeSlotHeight(slotId).takeIf { it >= 0f } else null
|
|
68
|
+
|
|
69
|
+
/** The screen of the slot [slotId] went: nothing more is measured for it. */
|
|
70
|
+
fun forgetSlot(slotId: String) {
|
|
71
|
+
if (loaded) nativeForgetSlot(slotId)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
@JvmStatic
|
|
75
|
+
private external fun nativeRequestSlot(slotId: String)
|
|
76
|
+
|
|
77
|
+
@JvmStatic
|
|
78
|
+
private external fun nativeIsSlotRequested(slotId: String): Boolean
|
|
79
|
+
|
|
80
|
+
@JvmStatic
|
|
81
|
+
private external fun nativeSlotHeight(slotId: String): Float
|
|
82
|
+
|
|
83
|
+
@JvmStatic
|
|
84
|
+
private external fun nativeForgetSlot(slotId: String)
|
|
85
|
+
|
|
86
|
+
@JvmStatic
|
|
87
|
+
private external fun nativeSurfaceHeight(
|
|
88
|
+
surface: ReactSurfaceImpl,
|
|
89
|
+
width: Float,
|
|
90
|
+
pointScale: Float,
|
|
91
|
+
fontScale: Float,
|
|
92
|
+
rtl: Boolean,
|
|
93
|
+
swapLeftAndRight: Boolean,
|
|
94
|
+
): Float
|
|
95
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
package dev.apexnative.navigation.react
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.bridge.UIManager
|
|
4
|
+
import com.facebook.react.bridge.UIManagerListener
|
|
5
|
+
import com.facebook.react.common.annotations.UnstableReactNativeAPI
|
|
6
|
+
import com.facebook.react.uimanager.UIManagerHelper
|
|
7
|
+
import com.facebook.react.uimanager.common.UIManagerType
|
|
8
|
+
import dev.apexnative.navigation.NavigationApplication
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* The handlers of a screen's content that hear each mount of Fabric (`IReactView.setOnContentMounted`), by key, while
|
|
12
|
+
* [hears] holds (the content is in a window and not destroyed). Fabric tells every mount of the app, on the main
|
|
13
|
+
* thread, not those of one surface: the handlers measure and compare, so a mount elsewhere costs only that. It listens
|
|
14
|
+
* only while a handler waits. Main thread only.
|
|
15
|
+
*/
|
|
16
|
+
internal class ContentMounts(private val hears: () -> Boolean) {
|
|
17
|
+
private val handlers = LinkedHashMap<String, Runnable>()
|
|
18
|
+
// The UI manager that tells the mounts; none while no handler waits.
|
|
19
|
+
private var uiManager: UIManager? = null
|
|
20
|
+
|
|
21
|
+
// React Native marks the listener unstable; it is the one place that tells a mount.
|
|
22
|
+
@OptIn(UnstableReactNativeAPI::class)
|
|
23
|
+
private val listener = object : UIManagerListener {
|
|
24
|
+
override fun willDispatchViewUpdates(uiManager: UIManager) = Unit
|
|
25
|
+
|
|
26
|
+
override fun willMountItems(uiManager: UIManager) = Unit
|
|
27
|
+
|
|
28
|
+
override fun didMountItems(uiManager: UIManager) {
|
|
29
|
+
if (hears()) run()
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
override fun didDispatchMountItems(uiManager: UIManager) = Unit
|
|
33
|
+
|
|
34
|
+
override fun didScheduleMountItems(uiManager: UIManager) = Unit
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** [handler] runs after each mount under [key]; null removes the handler of [key]. */
|
|
38
|
+
fun set(key: String, handler: Runnable?) {
|
|
39
|
+
if (handler == null) handlers.remove(key) else handlers[key] = handler
|
|
40
|
+
if (handlers.isEmpty()) stop() else start()
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** Runs the handlers now: a mount the content tells itself. */
|
|
44
|
+
fun run() = handlers.values.toList().forEach(Runnable::run)
|
|
45
|
+
|
|
46
|
+
/** The content goes: no handler runs again. */
|
|
47
|
+
fun clear() {
|
|
48
|
+
handlers.clear()
|
|
49
|
+
stop()
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
@OptIn(UnstableReactNativeAPI::class)
|
|
53
|
+
private fun start() {
|
|
54
|
+
if (uiManager != null) return
|
|
55
|
+
val reactContext = NavigationApplication.instanceOrNull?.reactGatewayOrNull?.currentReactContext ?: return
|
|
56
|
+
uiManager = UIManagerHelper.getUIManager(reactContext, UIManagerType.FABRIC)?.also {
|
|
57
|
+
it.addUIManagerEventListener(listener)
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
@OptIn(UnstableReactNativeAPI::class)
|
|
62
|
+
private fun stop() {
|
|
63
|
+
uiManager?.removeUIManagerEventListener(listener)
|
|
64
|
+
uiManager = null
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -55,6 +55,10 @@ class NavigationTurboModule(reactContext: ReactApplicationContext) : NativeANNTu
|
|
|
55
55
|
override fun mergeOptions(componentId: String, options: ReadableMap) =
|
|
56
56
|
optionsCommands.mergeOptions(componentId, options)
|
|
57
57
|
|
|
58
|
+
// Development (Fast Refresh): the options an edit took out of a screen that shows; as mergeOptions without one.
|
|
59
|
+
override fun clearOptions(componentId: String, paths: ReadableArray) =
|
|
60
|
+
optionsCommands.clearOptions(componentId, paths)
|
|
61
|
+
|
|
58
62
|
override fun push(commandId: String, componentId: String, layout: ReadableMap, promise: Promise) {
|
|
59
63
|
val what = "push the screen '${LayoutReader.idOf(layout)}'"
|
|
60
64
|
runner.run(
|
|
@@ -236,11 +240,11 @@ class NavigationTurboModule(reactContext: ReactApplicationContext) : NativeANNTu
|
|
|
236
240
|
check = {},
|
|
237
241
|
) { _, answer -> navigator().stackCommands.setColumnShown(componentId, column, shown, answer) }
|
|
238
242
|
|
|
239
|
-
override fun getArrangementState(componentId: String): WritableMap =
|
|
243
|
+
override fun getArrangementState(componentId: String): WritableMap = ScreenEnvironment.getArrangementState(componentId)
|
|
240
244
|
|
|
241
|
-
override fun getScreenInsets(componentId: String): WritableMap =
|
|
245
|
+
override fun getScreenInsets(componentId: String): WritableMap = ScreenEnvironment.getScreenInsets(componentId)
|
|
242
246
|
|
|
243
|
-
override fun getScreenRegions(componentId: String): WritableMap =
|
|
247
|
+
override fun getScreenRegions(componentId: String): WritableMap = ScreenEnvironment.getScreenRegions(componentId)
|
|
244
248
|
|
|
245
249
|
// With the activity the user sees, if there is one; the hinge remembers it and goes on with the next activity
|
|
246
250
|
// (`Hinge.attach`).
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
package dev.apexnative.navigation.react
|
|
2
2
|
|
|
3
3
|
import android.content.Context
|
|
4
|
+
import com.facebook.react.bridge.ReadableArray
|
|
4
5
|
import com.facebook.react.bridge.ReadableMap
|
|
6
|
+
import com.facebook.react.bridge.ReadableType
|
|
5
7
|
import dev.apexnative.navigation.options.DefaultOptions
|
|
8
|
+
import dev.apexnative.navigation.options.OptionPaths
|
|
6
9
|
import dev.apexnative.navigation.options.Options
|
|
7
10
|
import dev.apexnative.navigation.options.OptionsReader
|
|
8
11
|
import dev.apexnative.navigation.system.AppDirection
|
|
@@ -10,9 +13,9 @@ import dev.apexnative.navigation.viewcontrollers.navigator.Navigators
|
|
|
10
13
|
import dev.apexnative.navigation.viewcontrollers.viewcontroller.ViewController
|
|
11
14
|
|
|
12
15
|
/**
|
|
13
|
-
* The commands of the options, `setDefaultOptions` and `
|
|
14
|
-
* on the module thread, in the runner's step before the main task; the main task stores
|
|
15
|
-
* read is logged (`[ApexNavigation] <method>: <text>`); the app goes on.
|
|
16
|
+
* The commands of the options, `setDefaultOptions`, `mergeOptions` and `clearOptions`: all without a promise. Their
|
|
17
|
+
* options and paths are read on the module thread, in the runner's step before the main task; the main task stores
|
|
18
|
+
* and applies them. What can't be read is logged (`[ApexNavigation] <method>: <text>`); the app goes on.
|
|
16
19
|
*/
|
|
17
20
|
internal class OptionsCommands(
|
|
18
21
|
private val runner: CommandRunner,
|
|
@@ -30,6 +33,11 @@ internal class OptionsCommands(
|
|
|
30
33
|
* system UI follows (`Navigator.optionsMerged`, `mergeOptions` step 5). Main thread.
|
|
31
34
|
*/
|
|
32
35
|
private val merged: (ViewController<*>, Options) -> Unit = { _, _ -> },
|
|
36
|
+
/**
|
|
37
|
+
* Repaints what shows in the current navigation activity, as [repaint] does without new default options
|
|
38
|
+
* (`Navigator.repaint`), after `clearOptions`. Main thread.
|
|
39
|
+
*/
|
|
40
|
+
private val repaintCurrent: () -> Unit = {},
|
|
33
41
|
) {
|
|
34
42
|
constructor(runner: CommandRunner, context: () -> Context?) : this(
|
|
35
43
|
runner = runner,
|
|
@@ -40,6 +48,7 @@ internal class OptionsCommands(
|
|
|
40
48
|
Navigators.all().forEach { it.navigator?.defaultOptionsChanged(direction) }
|
|
41
49
|
},
|
|
42
50
|
merged = { node, options -> Navigators.current()?.navigator?.optionsMerged(node, options) },
|
|
51
|
+
repaintCurrent = { Navigators.current()?.navigator?.repaint() },
|
|
43
52
|
)
|
|
44
53
|
|
|
45
54
|
/**
|
|
@@ -82,8 +91,37 @@ internal class OptionsCommands(
|
|
|
82
91
|
merged(node, read.options)
|
|
83
92
|
}
|
|
84
93
|
|
|
94
|
+
/**
|
|
95
|
+
* `clearOptions` (development: Fast Refresh, after an edit took options out of a screen that shows): the values at
|
|
96
|
+
* [paths] (`topBar.subtitle`; a group's path for the whole group) leave the own options of the node [componentId]
|
|
97
|
+
* names, through `ViewController.changeOptions` ([OptionPaths.clear]), so it resolves to what the layouts around it
|
|
98
|
+
* and the defaults give there; the user layer stays. The stack it is in writes its back arrow's visibility into
|
|
99
|
+
* them again (`StackController.ownOptionsCleared`), then what shows is repainted as after `setDefaultOptions`. No
|
|
100
|
+
* node with that id, or no path that names an option: nothing, not even a log line. Without an activity: one log
|
|
101
|
+
* line, as `mergeOptions`.
|
|
102
|
+
*/
|
|
103
|
+
fun clearOptions(componentId: String, paths: ReadableArray) = runner.runVoid(
|
|
104
|
+
name = CLEAR_OPTIONS,
|
|
105
|
+
what = "clear the options of the screen '$componentId'",
|
|
106
|
+
prepare = {
|
|
107
|
+
(0 until paths.size()).filter { paths.getType(it) == ReadableType.String }.mapNotNull(paths::getString)
|
|
108
|
+
},
|
|
109
|
+
) { read ->
|
|
110
|
+
if (!runner.hasActivity()) {
|
|
111
|
+
runner.logFailure(CLEAR_OPTIONS, ErrorTexts.nowhereToShow())
|
|
112
|
+
return@runVoid
|
|
113
|
+
}
|
|
114
|
+
val node = findNode(componentId) ?: return@runVoid
|
|
115
|
+
val known = read.filter { OptionPaths.find(node.options, it) != null }
|
|
116
|
+
if (known.isEmpty()) return@runVoid
|
|
117
|
+
node.changeOptions { own -> known.forEach { OptionPaths.clear(own, it) } }
|
|
118
|
+
node.findParentStack()?.ownOptionsCleared(node)
|
|
119
|
+
repaintCurrent()
|
|
120
|
+
}
|
|
121
|
+
|
|
85
122
|
private companion object {
|
|
86
123
|
const val SET_DEFAULT_OPTIONS = "setDefaultOptions"
|
|
87
124
|
const val MERGE_OPTIONS = "mergeOptions"
|
|
125
|
+
const val CLEAR_OPTIONS = "clearOptions"
|
|
88
126
|
}
|
|
89
127
|
}
|
|
@@ -3,6 +3,7 @@ package dev.apexnative.navigation.react
|
|
|
3
3
|
import android.annotation.SuppressLint
|
|
4
4
|
import android.content.Context
|
|
5
5
|
import android.os.Bundle
|
|
6
|
+
import android.util.Size
|
|
6
7
|
import android.view.MotionEvent
|
|
7
8
|
import android.view.ViewGroup
|
|
8
9
|
import android.widget.FrameLayout
|
|
@@ -28,7 +29,9 @@ import dev.apexnative.navigation.viewcontrollers.viewcontroller.ScrollEventListe
|
|
|
28
29
|
* never makes this one ready. A screen without a safe area view (no view ever told anything of its id) is ready once
|
|
29
30
|
* the root view of its surface has children, read on this view's own layout passes. A root that renders at once
|
|
30
31
|
* ([rendersAtOnce]: a component of a bar, a tab's cell) marks its id so from before its surface starts until it stops.
|
|
31
|
-
* Ready once, ready for good; what waits for it ([whenReady]) runs then, once.
|
|
32
|
+
* Ready once, ready for good; what waits for it ([whenReady]) runs then, once. Its content measures without a height
|
|
33
|
+
* limit ([contentSize], `ContentHeights`), and the handlers of [setOnContentMounted] hear each mount of Fabric while
|
|
34
|
+
* the view is in a window.
|
|
32
35
|
* Without a signal or children, the limits of what waits decide: the view has no timer of its own. It owns the
|
|
33
36
|
* record of its appearance, which the dispatcher of the appearance events holds until the content is ready
|
|
34
37
|
* (`AppearanceDispatcher`).
|
|
@@ -51,6 +54,7 @@ class ReactView @JvmOverloads constructor(
|
|
|
51
54
|
private var readyTold = false
|
|
52
55
|
private val readyListeners = mutableListOf<Runnable>()
|
|
53
56
|
private var listening = false
|
|
57
|
+
private val mounts = ContentMounts { !destroyed && isAttachedToWindow }
|
|
54
58
|
|
|
55
59
|
// What this root hears of its id: only the signals of its own surface.
|
|
56
60
|
private val readiness = object : ScreenReadiness.Listener {
|
|
@@ -112,6 +116,16 @@ class ReactView @JvmOverloads constructor(
|
|
|
112
116
|
override val touchRoot: ViewGroup?
|
|
113
117
|
get() = surface?.view as? ViewGroup
|
|
114
118
|
|
|
119
|
+
override fun contentSize(maxWidth: Int): Size? {
|
|
120
|
+
val surface = surface ?: return null
|
|
121
|
+
val height = ContentHeights.surfaceHeight(context, surface, maxWidth) ?: return null
|
|
122
|
+
return Size(maxWidth, height)
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
override fun setOnContentMounted(key: String, handler: Runnable?) {
|
|
126
|
+
if (!destroyed) mounts.set(key, handler)
|
|
127
|
+
}
|
|
128
|
+
|
|
115
129
|
override fun isReady(): Boolean {
|
|
116
130
|
if (!ready && hasRendered()) ready = true
|
|
117
131
|
return ready
|
|
@@ -167,6 +181,7 @@ class ReactView @JvmOverloads constructor(
|
|
|
167
181
|
AppearanceDispatcher.didDisappear(appearance)
|
|
168
182
|
destroyed = true
|
|
169
183
|
readyListeners.clear()
|
|
184
|
+
mounts.clear()
|
|
170
185
|
stopListening()
|
|
171
186
|
if (rendersAtOnce) ScreenReadiness.setRendersAtOnce(componentId, this, false)
|
|
172
187
|
val surface = surface ?: return
|
|
@@ -82,8 +82,6 @@ internal object ReloadCoordinator {
|
|
|
82
82
|
UiThread.runOnMain {
|
|
83
83
|
launches.contextReady(context)
|
|
84
84
|
NativeScreenCalls.failAll { name, componentId -> ErrorTexts.nativeCallReloaded(name, componentId) }
|
|
85
|
-
// Scroll listeners that found no event dispatcher listen now.
|
|
86
|
-
ScrollEvents.dispatcherReady()
|
|
87
85
|
Navigators.all().filter { it.isNavigationResumed }.forEach(::launchIfDue)
|
|
88
86
|
}
|
|
89
87
|
}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
package dev.apexnative.navigation.react
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.bridge.Arguments
|
|
4
|
+
import com.facebook.react.bridge.WritableArray
|
|
5
|
+
import com.facebook.react.bridge.WritableMap
|
|
6
|
+
import dev.apexnative.navigation.react.events.EventContract
|
|
7
|
+
import dev.apexnative.navigation.react.events.EventEmitter
|
|
8
|
+
import java.util.concurrent.ConcurrentHashMap
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* What JavaScript knows of each screen's place, by the screen's id, in one record: its safe area (the insets JavaScript
|
|
12
|
+
* gets and the padding of its safe area view, `useSafeAreaInsets`), its reserved regions (`useReservedRegions`) and,
|
|
13
|
+
* in a pair, its arrangement (`useArrangement`). The core measures them (`ScreenSafeArea`, [ScreenRegions], the pair's
|
|
14
|
+
* plan) on the main thread and keeps them here; JavaScript reads them from its own thread for a screen's first render
|
|
15
|
+
* and gets every change as an event. The record is immutable and replaced at once, so a reader on another thread sees
|
|
16
|
+
* one whole record. When the screen goes, [remove] clears all of it. As on iOS (`ANNScreenEnvironment`).
|
|
17
|
+
*/
|
|
18
|
+
internal object ScreenEnvironment {
|
|
19
|
+
/** How much covers each edge of a screen's content, in dp. */
|
|
20
|
+
data class Insets(val top: Double, val right: Double, val bottom: Double, val left: Double)
|
|
21
|
+
|
|
22
|
+
/** A reserved region, in dp from the top left of the screen's content. */
|
|
23
|
+
data class Region(val x: Double, val y: Double, val width: Double, val height: Double)
|
|
24
|
+
|
|
25
|
+
/** Where a screen divides (`division`) and where hardware covers it (`occlusion`). */
|
|
26
|
+
data class Regions(val division: List<Region>, val occlusion: List<Region>) {
|
|
27
|
+
val isEmpty: Boolean get() = division.isEmpty() && occlusion.isEmpty()
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** Which screen of its pair a screen is, how the pair lies, whether it lies over the other one and is hidden. */
|
|
31
|
+
data class Arrangement(val placement: String, val layout: String, val front: Boolean, val hidden: Boolean)
|
|
32
|
+
|
|
33
|
+
/** Where the changes JavaScript hears go: the events of the screen's JavaScript, or a test's record. */
|
|
34
|
+
interface Changes {
|
|
35
|
+
fun insetsChanged(screenId: String, insets: Insets)
|
|
36
|
+
|
|
37
|
+
fun regionsChanged(screenId: String, regions: Regions)
|
|
38
|
+
|
|
39
|
+
fun arrangementChanged(screenId: String, arrangement: Arrangement)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// One screen's record; null for a fact not known yet (not laid out, outside a pair).
|
|
43
|
+
private data class Record(
|
|
44
|
+
val insets: Insets? = null,
|
|
45
|
+
val padding: Insets? = null,
|
|
46
|
+
val regions: Regions? = null,
|
|
47
|
+
val arrangement: Arrangement? = null,
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
private val NO_REGIONS = Regions(emptyList(), emptyList())
|
|
51
|
+
private val records = ConcurrentHashMap<String, Record>()
|
|
52
|
+
// Main thread only.
|
|
53
|
+
private val paddingListeners = HashMap<String, MutableSet<() -> Unit>>()
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Keeps the screen's [insets] for JavaScript (what still covers its content) and the [padding] of its safe area
|
|
57
|
+
* view. Calls the screen's padding listeners when the padding changed, then tells JavaScript when the insets did.
|
|
58
|
+
* Main thread only.
|
|
59
|
+
*/
|
|
60
|
+
fun setInsets(screenId: String, insets: Insets, padding: Insets, changes: Changes) {
|
|
61
|
+
val before = update(screenId) { it.copy(insets = insets, padding = padding) }
|
|
62
|
+
if (before?.padding != padding) paddingListeners[screenId]?.toList()?.forEach { it() }
|
|
63
|
+
if (before?.insets != insets) changes.insetsChanged(screenId, insets)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Keeps the screen's [regions], and tells JavaScript when they changed; none at first is what JavaScript reads
|
|
68
|
+
* before the first layout, so it isn't told. Main thread only.
|
|
69
|
+
*/
|
|
70
|
+
fun setRegions(screenId: String, regions: Regions, changes: Changes) {
|
|
71
|
+
val before = update(screenId) { it.copy(regions = regions) }?.regions
|
|
72
|
+
if (before == regions || (before == null && regions.isEmpty)) return
|
|
73
|
+
changes.regionsChanged(screenId, regions)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** Keeps the screen's [arrangement], and tells JavaScript when it changed and [notify] holds. Main thread only. */
|
|
77
|
+
fun setArrangement(screenId: String, arrangement: Arrangement, notify: Boolean, changes: Changes) {
|
|
78
|
+
val before = update(screenId) { it.copy(arrangement = arrangement) }?.arrangement
|
|
79
|
+
if (before == arrangement || !notify) return
|
|
80
|
+
changes.arrangementChanged(screenId, arrangement)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** The padding of the screen's safe area view; none before the screen was laid out. Any thread. */
|
|
84
|
+
fun padding(screenId: String): Insets? = records[screenId]?.padding
|
|
85
|
+
|
|
86
|
+
/** The insets JavaScript gets; none before the screen was laid out. Any thread. */
|
|
87
|
+
fun insets(screenId: String): Insets? = records[screenId]?.insets
|
|
88
|
+
|
|
89
|
+
/** The screen's reserved regions; none before its first layout. Any thread. */
|
|
90
|
+
fun regions(screenId: String): Regions? = records[screenId]?.regions
|
|
91
|
+
|
|
92
|
+
/** The screen's arrangement; none outside a pair. Any thread. */
|
|
93
|
+
fun arrangement(screenId: String): Arrangement? = records[screenId]?.arrangement
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Calls `listener` when the padding of the screen changes; returns the function that stops it. Main thread only.
|
|
97
|
+
*/
|
|
98
|
+
fun listenToPadding(screenId: String, listener: () -> Unit): () -> Unit {
|
|
99
|
+
paddingListeners.getOrPut(screenId) { LinkedHashSet() }.add(listener)
|
|
100
|
+
return {
|
|
101
|
+
paddingListeners[screenId]?.let {
|
|
102
|
+
it.remove(listener)
|
|
103
|
+
if (it.isEmpty()) paddingListeners.remove(screenId)
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Forgets everything kept for the screen. Its padding listeners stay: a live safe area view keeps listening while
|
|
110
|
+
* it keeps the id, and stops itself.
|
|
111
|
+
*/
|
|
112
|
+
fun remove(screenId: String) {
|
|
113
|
+
records.remove(screenId)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/** `getScreenInsets`: `{ top, right, bottom, left }`, empty before the screen was laid out. Any thread. */
|
|
117
|
+
fun getScreenInsets(screenId: String): WritableMap = insets(screenId)?.let(::toMap) ?: Arguments.createMap()
|
|
118
|
+
|
|
119
|
+
/** `getScreenRegions`: `{ division, occlusion }`, empty lists before the first layout. Any thread. */
|
|
120
|
+
fun getScreenRegions(screenId: String): WritableMap = toMap(regions(screenId) ?: NO_REGIONS)
|
|
121
|
+
|
|
122
|
+
/** `getArrangementState`: `{ placement, layout, front, hidden }`, empty outside a pair. Any thread. */
|
|
123
|
+
fun getArrangementState(screenId: String): WritableMap =
|
|
124
|
+
arrangement(screenId)?.let(::toMap) ?: Arguments.createMap()
|
|
125
|
+
|
|
126
|
+
/** The changes as events through [emitter], the one of the JavaScript the screen was made for. */
|
|
127
|
+
fun changesOf(emitter: EventEmitter): Changes = object : Changes {
|
|
128
|
+
override fun insetsChanged(screenId: String, insets: Insets) =
|
|
129
|
+
emitter.emitScreenInsetsChanged(screenId, toMap(insets))
|
|
130
|
+
|
|
131
|
+
override fun regionsChanged(screenId: String, regions: Regions) =
|
|
132
|
+
emitter.emitScreenRegionsChanged(screenId, toMap(regions))
|
|
133
|
+
|
|
134
|
+
override fun arrangementChanged(screenId: String, arrangement: Arrangement) =
|
|
135
|
+
emitter.emitArrangementChanged(screenId, toMap(arrangement))
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Replaces the screen's record at once with [change] of it; returns the record before, null for a new screen.
|
|
139
|
+
private fun update(screenId: String, change: (Record) -> Record): Record? {
|
|
140
|
+
var before: Record? = null
|
|
141
|
+
records.compute(screenId) { _, old ->
|
|
142
|
+
before = old
|
|
143
|
+
change(old ?: Record())
|
|
144
|
+
}
|
|
145
|
+
return before
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
private fun toMap(value: Insets): WritableMap = Arguments.createMap().apply {
|
|
149
|
+
putDouble(EventContract.ScreenInsetsChanged.TOP, value.top)
|
|
150
|
+
putDouble(EventContract.ScreenInsetsChanged.RIGHT, value.right)
|
|
151
|
+
putDouble(EventContract.ScreenInsetsChanged.BOTTOM, value.bottom)
|
|
152
|
+
putDouble(EventContract.ScreenInsetsChanged.LEFT, value.left)
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
private fun toMap(value: Regions): WritableMap = Arguments.createMap().apply {
|
|
156
|
+
putArray(EventContract.ScreenRegionsChanged.DIVISION, toArray(value.division))
|
|
157
|
+
putArray(EventContract.ScreenRegionsChanged.OCCLUSION, toArray(value.occlusion))
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
private fun toArray(regions: List<Region>): WritableArray = Arguments.createArray().apply {
|
|
161
|
+
for (region in regions) {
|
|
162
|
+
pushMap(Arguments.createMap().apply {
|
|
163
|
+
putDouble(EventContract.ScreenRegionRect.X, region.x)
|
|
164
|
+
putDouble(EventContract.ScreenRegionRect.Y, region.y)
|
|
165
|
+
putDouble(EventContract.ScreenRegionRect.WIDTH, region.width)
|
|
166
|
+
putDouble(EventContract.ScreenRegionRect.HEIGHT, region.height)
|
|
167
|
+
})
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
private fun toMap(value: Arrangement): WritableMap = Arguments.createMap().apply {
|
|
172
|
+
putString(EventContract.ArrangementChanged.PLACEMENT, value.placement)
|
|
173
|
+
putString(EventContract.ArrangementChanged.LAYOUT, value.layout)
|
|
174
|
+
putBoolean(EventContract.ArrangementChanged.FRONT, value.front)
|
|
175
|
+
putBoolean(EventContract.ArrangementChanged.HIDDEN, value.hidden)
|
|
176
|
+
}
|
|
177
|
+
}
|