@apexnative/navigation 1.0.0-alpha.4 → 1.0.0-alpha.6
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/CHANGELOG.md +38 -0
- package/README.md +1 -1
- package/android/CLAUDE.md +23 -14
- package/android/CORE.md +32 -19
- package/android/src/main/java/dev/apexnative/navigation/NavigationActivity.kt +60 -13
- package/android/src/main/java/dev/apexnative/navigation/options/AnimationsOptions.kt +2 -2
- package/android/src/main/java/dev/apexnative/navigation/options/BottomTabsOptions.kt +13 -8
- package/android/src/main/java/dev/apexnative/navigation/options/HardwareBackButtonOptions.kt +9 -1
- package/android/src/main/java/dev/apexnative/navigation/options/StackAnimationOptions.kt +40 -2
- package/android/src/main/java/dev/apexnative/navigation/react/CommandAnswer.kt +28 -13
- package/android/src/main/java/dev/apexnative/navigation/react/Hinge.kt +7 -4
- package/android/src/main/java/dev/apexnative/navigation/react/NavigationTurboModule.kt +3 -3
- package/android/src/main/java/dev/apexnative/navigation/react/ReactView.kt +39 -20
- package/android/src/main/java/dev/apexnative/navigation/react/ReloadCoordinator.kt +7 -6
- package/android/src/main/java/dev/apexnative/navigation/react/ScreenEnvironment.kt +177 -0
- package/android/src/main/java/dev/apexnative/navigation/react/ScreenRegions.kt +19 -51
- package/android/src/main/java/dev/apexnative/navigation/react/ScrollEvents.kt +49 -128
- package/android/src/main/java/dev/apexnative/navigation/react/events/AppearanceDispatcher.kt +30 -39
- package/android/src/main/java/dev/apexnative/navigation/react/events/EventEmitter.kt +34 -7
- package/android/src/main/java/dev/apexnative/navigation/react/events/NavigationState.kt +154 -0
- package/android/src/main/java/dev/apexnative/navigation/safearea/SafeAreaView.kt +12 -10
- package/android/src/main/java/dev/apexnative/navigation/slots/SlotScreenView.kt +12 -12
- 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 +10 -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 +22 -21
- 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 +12 -9
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/ContentReadiness.kt +1 -1
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/ScreenReadiness.kt +103 -56
- 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 +119 -8
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalViews.kt +7 -0
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/SheetBack.kt +42 -0
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/navigator/BackCallbackCheck.kt +50 -0
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/navigator/BackCallbackState.kt +45 -14
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/navigator/Navigator.kt +25 -5
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/navigator/RootCoordinator.kt +2 -2
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/parent/ParentController.kt +3 -6
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/splitview/SplitViewController.kt +3 -0
- 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 +13 -5
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackViews.kt +3 -0
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/search/ScreenSearch.kt +3 -5
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/search/StackSearchPresenter.kt +2 -5
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/topbar/BarComponents.kt +5 -51
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/topbar/TopBarPresenter.kt +4 -9
- 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/CustomBottomTabItemView.kt +0 -5
- 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 +9 -2
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ElementPictures.kt +11 -0
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/FlightGeometry.kt +318 -25
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ImageMatrixAnimator.kt +18 -20
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/PlainViewAnimator.kt +17 -25
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ReactImageAnimator.kt +15 -22
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/SharedAnimatorFactory.kt +12 -9
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/TextChangeAnimator.kt +84 -35
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ViewChangeAnimator.kt +49 -40
- package/cli/doctor/checks/splash.js +4 -22
- package/cli/splash/index.js +3 -1
- package/docs-md/architecture/transitions.md +4 -2
- package/docs-md/containers/modals.md +4 -2
- 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 +1 -1
- package/docs-md/fundamentals/starting-the-app.md +1 -1
- package/docs-md/guides/cli.md +1 -2
- package/docs-md/help/glossary.md +2 -2
- package/docs-md/help/troubleshooting.md +23 -2
- package/docs-md/index.json +12 -0
- 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 +36 -0
- package/docs-md/screen-ui/bar-buttons.md +4 -4
- 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 +2 -2
- package/docs-md/screen-ui/top-bar.md +8 -4
- package/docs-md/start/installation.md +1 -1
- package/ios/Animations/ANNFrameDriver.mm +2 -0
- package/ios/Animations/Elements/ANNSharedElementFlight.mm +70 -4
- 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 +6 -4
- package/ios/Animations/Interpolators/ANNViewTracks.mm +49 -9
- package/ios/CLAUDE.md +17 -15
- package/ios/CORE.md +30 -26
- package/ios/Contract/ANNCommandAnswer.h +9 -2
- package/ios/Contract/ANNCommandAnswer.mm +25 -12
- 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/ANNAppearanceDispatcher.h +5 -4
- package/ios/Core/ANNAppearanceDispatcher.mm +12 -1
- package/ios/Core/ANNJavaScriptCalls.h +11 -12
- package/ios/Core/ANNJavaScriptCalls.mm +7 -16
- package/ios/Core/ANNNavigationState.h +4 -6
- package/ios/Core/ANNNavigationState.mm +0 -4
- package/ios/Core/ANNReloadCoordinator.mm +4 -8
- package/ios/Core/ApexNavigation+Internal.h +1 -1
- 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/ANNSplashViewController.h +5 -3
- package/ios/Layouts/Root/ANNSplashViewController.mm +147 -0
- package/ios/Layouts/Screen/ANNComponentViewController.h +2 -2
- package/ios/Layouts/Screen/ANNComponentViewController.mm +9 -22
- 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 +142 -27
- 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/ANNBottomTabsOptions.h +2 -0
- package/ios/Options/ANNBottomTabsOptions.mm +1 -0
- package/ios/Options/ANNOptions.h +18 -0
- package/ios/Options/ANNOptions.mm +112 -1
- package/ios/Options/ANNOptionsReader.mm +110 -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/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 +6 -0
- package/ios/React/ANNSurfaceHostingView.mm +116 -0
- 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 +20 -16
- package/lib/module/ARCHITECTURE.md +8 -4
- package/lib/module/api/commands/commands.js +54 -6
- package/lib/module/api/commands/commands.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/options/OptionsChain.js +2 -2
- package/lib/module/engine/commands.js +21 -1
- package/lib/module/engine/commands.js.map +1 -1
- package/lib/module/engine/places.js +8 -0
- package/lib/module/engine/places.js.map +1 -1
- package/lib/module/engine/screens.js +29 -4
- package/lib/module/engine/screens.js.map +1 -1
- package/lib/typescript/api/app/createNavigation.d.ts +3 -3
- package/lib/typescript/api/commands/commands.d.ts.map +1 -1
- package/lib/typescript/api/commands/presentation.d.ts +4 -3
- package/lib/typescript/api/commands/presentation.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/options/OptionsChain.d.ts +2 -2
- package/lib/typescript/engine/commands.d.ts.map +1 -1
- package/lib/typescript/engine/places.d.ts +2 -0
- package/lib/typescript/engine/places.d.ts.map +1 -1
- package/lib/typescript/engine/screens.d.ts +20 -6
- package/lib/typescript/engine/screens.d.ts.map +1 -1
- package/lib/typescript/interfaces/Options.d.ts +1 -1
- package/lib/typescript/interfaces/Options.d.ts.map +1 -1
- package/lib/typescript/interfaces/options/animations.d.ts +30 -9
- package/lib/typescript/interfaces/options/animations.d.ts.map +1 -1
- package/lib/typescript/interfaces/options/bottomTabs.d.ts +18 -5
- package/lib/typescript/interfaces/options/bottomTabs.d.ts.map +1 -1
- package/lib/typescript/interfaces/options/buttons.d.ts +8 -8
- package/lib/typescript/interfaces/options/stack.d.ts +9 -0
- 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 +32 -29
- package/lib/typescript/interfaces/options/topBar.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/ARCHITECTURE.md +8 -4
- package/src/api/app/createNavigation.ts +3 -3
- package/src/api/commands/commands.ts +48 -7
- package/src/api/commands/presentation.ts +4 -3
- package/src/api/hooks/useSafeAreaInsets.ts +4 -3
- package/src/api/options/OptionsChain.ts +2 -2
- package/src/engine/commands.ts +30 -6
- package/src/engine/places.ts +9 -0
- package/src/engine/screens.tsx +29 -8
- package/src/interfaces/Options.ts +1 -0
- package/src/interfaces/options/animations.ts +31 -9
- package/src/interfaces/options/bottomTabs.ts +18 -5
- package/src/interfaces/options/buttons.ts +8 -8
- package/src/interfaces/options/stack.ts +9 -0
- package/src/interfaces/options/system.ts +8 -5
- package/src/interfaces/options/toolbar.ts +2 -2
- package/src/interfaces/options/topBar.ts +32 -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/react/events/NavigationStateReporter.kt +0 -62
- package/android/src/main/java/dev/apexnative/navigation/react/events/StateHold.kt +0 -62
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/CustomTabRowPlacer.kt +0 -223
- package/ios/Layouts/Screen/ANNScreenInsets.h +0 -30
- package/ios/Layouts/Screen/ANNScreenInsets.mm +0 -70
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,43 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.0.0-alpha.6](https://github.com/apexnative/navigation/compare/v1.0.0-alpha.5...v1.0.0-alpha.6) (2026-10-25)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### ⚠ BREAKING CHANGES
|
|
7
|
+
|
|
8
|
+
* leave the bottom edge of a screen free by default ([#49](https://github.com/apexnative/navigation/issues/49))
|
|
9
|
+
|
|
10
|
+
### Features
|
|
11
|
+
|
|
12
|
+
* a sheet's back preview on android, lighter tabs and top bar, the compatibility mode on ios 27 ([#46](https://github.com/apexnative/navigation/issues/46)) ([24d333d](https://github.com/apexnative/navigation/commit/24d333dd43fc5e056786f22dc8d7f1a0ff82a1ad))
|
|
13
|
+
* leave the bottom edge of a screen free by default ([#49](https://github.com/apexnative/navigation/issues/49)) ([b6bb165](https://github.com/apexnative/navigation/commit/b6bb165152114fb2886bd3c4ee55560c418f0cbe))
|
|
14
|
+
* **tabs:** choose whether a tab switch animates with bottomTabs.switchAnimation ([#52](https://github.com/apexnative/navigation/issues/52)) ([c6185b6](https://github.com/apexnative/navigation/commit/c6185b6b42574cff671def1ebdf8aa4f5e4be681))
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
### Bug Fixes
|
|
18
|
+
|
|
19
|
+
* an explicit top edge stays free under a large title ([#53](https://github.com/apexnative/navigation/issues/53)) ([188a89e](https://github.com/apexnative/navigation/commit/188a89e8d160c071f91cc010ed43ccc0683e12b3))
|
|
20
|
+
* shared elements fly turned and scaled views the same way on ios and android ([#50](https://github.com/apexnative/navigation/issues/50)) ([bf63ab6](https://github.com/apexnative/navigation/commit/bf63ab624d33ff9295af297ce6506206b1bfa0e6))
|
|
21
|
+
* the custom tab row moves with its tabs on android, and tab cells get selected and badge ([#51](https://github.com/apexnative/navigation/issues/51)) ([fd8f41d](https://github.com/apexnative/navigation/commit/fd8f41d2efd108741672220c54253a86704929a5))
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
### Performance Improvements
|
|
25
|
+
|
|
26
|
+
* **ios:** render a preview's screen when a press on its view starts ([#48](https://github.com/apexnative/navigation/issues/48)) ([4a57d1a](https://github.com/apexnative/navigation/commit/4a57d1aee1df7690dbde4c7d08dca2a70773d1f5))
|
|
27
|
+
|
|
28
|
+
## [1.0.0-alpha.5](https://github.com/apexnative/navigation/compare/v1.0.0-alpha.4...v1.0.0-alpha.5) (2026-10-23)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
### Features
|
|
32
|
+
|
|
33
|
+
* android back options, a launch screen copy from UILaunchScreen, modal and back fixes ([#43](https://github.com/apexnative/navigation/issues/43)) ([29f311e](https://github.com/apexnative/navigation/commit/29f311e318134f284a0c6279e6c86e15612c9343))
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
### Bug Fixes
|
|
37
|
+
|
|
38
|
+
* **android:** send the state of a tab switch from code at its start ([#44](https://github.com/apexnative/navigation/issues/44)) ([34d03d0](https://github.com/apexnative/navigation/commit/34d03d0214cf09e14329d0bae51e47d46063f054))
|
|
39
|
+
* **hooks:** measure agent folder scopes from the worktree a file is in ([#42](https://github.com/apexnative/navigation/issues/42)) ([d306137](https://github.com/apexnative/navigation/commit/d306137534d3ba551fab8a7346c7ad7300c0faf7))
|
|
40
|
+
|
|
3
41
|
## [1.0.0-alpha.4](https://github.com/apexnative/navigation/compare/v1.0.0-alpha.3...v1.0.0-alpha.4) (2026-10-23)
|
|
4
42
|
|
|
5
43
|
|
package/README.md
CHANGED
|
@@ -97,7 +97,7 @@ navigation.push('Details', { props: { title: 'Order 42' } });
|
|
|
97
97
|
each other: tabs of stacks, a stack in a modal, tabs in a side menu.
|
|
98
98
|
- **Screen UI as options:** the top bar, bar buttons, search, the toolbar and the floating action button, the status
|
|
99
99
|
bar and the system bars.
|
|
100
|
-
- **No safe area code:** every screen keeps its
|
|
100
|
+
- **No safe area code:** every screen keeps its top and sides clear of the bars and the cutout by itself; the bottom is free, and a list adds `useSafeAreaInsets().bottom`.
|
|
101
101
|
- **Native screens:** SwiftUI, UIKit and Compose screens are pushed, presented and put in tabs the same way as React
|
|
102
102
|
screens.
|
|
103
103
|
- **One React tree when you want it:** each screen is its own React root by default; with `appProvider` all screens
|
package/android/CLAUDE.md
CHANGED
|
@@ -22,10 +22,10 @@ top-level folder. Main thread only, unless a line says otherwise.
|
|
|
22
22
|
`Variant`, `ThemeColour`); `parsers/` reads them tolerantly from React Native data.
|
|
23
23
|
- `react/`: the way in and out: `NavigationTurboModule`, `CommandRunner`, `CommandAnswer`, `ErrorTexts`,
|
|
24
24
|
`OptionsCommands`, `PendingCalls` (a task behind the calls JavaScript made), `ReloadCoordinator`, `ReactGateway`,
|
|
25
|
-
`ReactView` (a React root), the stores (`
|
|
26
|
-
`LaunchLink`, `ScrollEvents`.
|
|
25
|
+
`ReactView` (a React root), the stores (`ScreenEnvironment`: a screen's insets, padding, regions and pair state;
|
|
26
|
+
`Hinge`, `StartInfo`), `ScreenRegions` (measures a screen's regions), `LaunchLink`, `ScrollEvents`.
|
|
27
27
|
- `react/events/`: `AppearanceDispatcher`, `ScreenAppearance`, `EventEmitter`, `EventContract` (made by
|
|
28
|
-
`scripts/contract.mjs`), `
|
|
28
|
+
`scripts/contract.mjs`), `NavigationState`, `ComponentType`.
|
|
29
29
|
- `viewcontrollers/viewcontroller/`: `ViewController` (the base of every node), `Presenter`, `IReactView` (what a
|
|
30
30
|
screen shows), `ScrollEventListener`.
|
|
31
31
|
- `viewcontrollers/child/`, `parent/`: `ChildController` and the registry; `ParentController` (the base of layouts),
|
|
@@ -44,7 +44,7 @@ top-level folder. Main thread only, unless a line says otherwise.
|
|
|
44
44
|
- `viewcontrollers/sidemenu/`: `SideMenuController`, `SideMenuMotion`, `SideMenuPresenter`, `SideMenuRules`.
|
|
45
45
|
- `viewcontrollers/splitview/`, `arrangement/`: the split view and the pair (modules, plus the core's
|
|
46
46
|
`SplitViewPresenter`).
|
|
47
|
-
- `viewcontrollers/modal/`, `overlay/`: `ModalCoordinator`, `ModalRecord`, `ModalViews`, `ModalSheet`;
|
|
47
|
+
- `viewcontrollers/modal/`, `overlay/`: `ModalCoordinator`, `ModalRecord`, `ModalViews`, `ModalSheet`, `SheetBack`;
|
|
48
48
|
`OverlayCoordinator`, `OverlayRecord`, `OverlayViews`, `OverlayTouches`.
|
|
49
49
|
- `views/`: Android views and animators, no rules: `component/`, `externalcomponent/`, `stack/` (`StackLayout`,
|
|
50
50
|
`topbar/`, `fab/`), `bottomtabs/`, `sidemenu/`, `animations/` (curves, `SideAnimator`, `AnimationRules`), `element/`
|
|
@@ -78,7 +78,8 @@ top-level folder. Main thread only, unless a line says otherwise.
|
|
|
78
78
|
`StackViews.kt`, `StackTransitions.kt`, `PreparedPushes.kt`; `views/stack/StackLayout.kt`, `ScreenEdge.kt` (the edge
|
|
79
79
|
a moving screen extends); back: `NavigationActivity.kt`, `viewcontrollers/navigator/BackCallbackState.kt`; the
|
|
80
80
|
preview of a pop during the back gesture (Android 14+): `StackController.BackGesture`, `BackPreview.kt`,
|
|
81
|
-
`stackPoppedByBack` of the layouts.
|
|
81
|
+
`stackPoppedByBack` of the layouts; of a sheet's dismissal: `viewcontrollers/modal/SheetBack.kt`,
|
|
82
|
+
`ModalCoordinator.backTargetSheet`, `ModalSheet.BackPreview`.
|
|
82
83
|
- Stack FAB: `viewcontrollers/stack/fab/FabPresenter.kt`, `FabPlan.kt`, `FabPlacement.kt`; `views/stack/fab/`.
|
|
83
84
|
- Top bar (title, buttons, menus, large title): `viewcontrollers/stack/topbar/TopBarPresenter.kt`, `TopBarRoom.kt`,
|
|
84
85
|
`TopBarVisibility.kt`, `BarComponents.kt`, `ButtonPlaces.kt`, `LargeTitleCollapse.kt`, `BarRowTransition.kt`;
|
|
@@ -87,7 +88,7 @@ top-level folder. Main thread only, unless a line says otherwise.
|
|
|
87
88
|
`views/stack/topbar/search/`.
|
|
88
89
|
- Tabs: `viewcontrollers/bottomtabs/BottomTabsController.kt`, `BottomTabsPresenter.kt`, `TabSelection.kt`,
|
|
89
90
|
`TabItems.kt`, `TabsRoom.kt`; `views/bottomtabs/`; `customrow/BottomTabsCustomRowOptions.kt`,
|
|
90
|
-
`viewcontrollers/bottomtabs/
|
|
91
|
+
`viewcontrollers/bottomtabs/CustomTabRowGeometry.kt`.
|
|
91
92
|
- Modals and sheets: `viewcontrollers/modal/ModalCoordinator.kt`, `ModalRecord.kt`, `ModalViews.kt`, `ModalSheet.kt`,
|
|
92
93
|
`ModalAnimations.kt`; `options/SheetOptions.kt`.
|
|
93
94
|
- Overlays: `viewcontrollers/overlay/OverlayCoordinator.kt`, `OverlayViews.kt`, `OverlayTouches.kt`.
|
|
@@ -96,36 +97,37 @@ top-level folder. Main thread only, unless a line says otherwise.
|
|
|
96
97
|
- Split view: `viewcontrollers/splitview/SplitViewController.kt`, `SplitViewLayout.kt`, `SplitPanes.kt`,
|
|
97
98
|
`SplitViewPresenter.kt`; `StackCommands.showInColumn`.
|
|
98
99
|
- Pair (arrangement): `viewcontrollers/arrangement/ArrangementController.kt`, `ArrangementPlanner.kt`,
|
|
99
|
-
`ArrangementLayout.kt`, `ArrangementDefinition.kt`; `react/
|
|
100
|
+
`ArrangementLayout.kt`, `ArrangementDefinition.kt`; the pair's state in `react/ScreenEnvironment.kt`.
|
|
100
101
|
- Preview, key commands: iOS only. The options are parsed and merged (`options/PreviewOptions.kt`,
|
|
101
102
|
`options/KeyCommandOptions.kt`) and not applied; `dismissPreview` does nothing.
|
|
102
103
|
- Animations and transitions: `views/animations/` (`AnimationRules`, `Curves`, `SideAnimator`, `ValueInterpolation`,
|
|
103
|
-
`FadeThrough`, `SystemTransition`); `views/element/`
|
|
104
|
-
`animators/`); `options/StackAnimationOptions.kt`,
|
|
104
|
+
`FadeThrough`, `SystemTransition`, `AnimationLayer`: the hardware layer of a fade); `views/element/`
|
|
105
|
+
(`ElementTransitions`, `TransitionLayer`, `TargetsWait`, `animators/`); `options/StackAnimationOptions.kt`,
|
|
106
|
+
`ViewAnimationOptions.kt`.
|
|
105
107
|
- System bars: `system/SystemUiOwner.kt`, `SystemUiLayers.kt`, `SystemUiRules.kt`, `SystemUiWindow.kt`,
|
|
106
108
|
`AppDirection.kt`; `viewcontrollers/parent/ContainerBackground.kt`.
|
|
107
109
|
- Events and state: `react/events/AppearanceDispatcher.kt`, `ScreenAppearance.kt`, `EventEmitter.kt`,
|
|
108
|
-
`
|
|
110
|
+
`NavigationState.kt`; `react/CommandAnswer.kt`.
|
|
109
111
|
- Options (parsing, merging): `options/Options.kt`, `OptionsReader.kt`, `OptionsCache.kt`, `DefaultOptions.kt`,
|
|
110
112
|
`params/`, `parsers/`; `viewcontrollers/viewcontroller/ViewController.kt` (resolution).
|
|
111
113
|
- Error texts and answers: `react/ErrorTexts.kt` and `react/CommandContract.kt`, made by `scripts/contract.mjs` from
|
|
112
114
|
`src/interfaces/contract/errors.json` and `commands.json`.
|
|
113
115
|
- Links: `react/LaunchLink.kt`, `NavigationActivity.kt` (`onNewIntent`); `react/StartInfo.kt`.
|
|
114
|
-
- Hinge and regions: `react/Hinge.kt`, `react/ScreenRegions.kt`, `react/
|
|
116
|
+
- Hinge and regions: `react/Hinge.kt`, `react/ScreenRegions.kt`, `react/ScreenEnvironment.kt`, `utils/WindowFolds.kt`.
|
|
115
117
|
- Native screens: `nativescreen/NativeScreenContext.kt`, `NativeScreenCalls.kt`, `NativeScreenCreator.kt`;
|
|
116
118
|
`viewcontrollers/externalcomponent/`.
|
|
117
119
|
- Slots: `slots/SlotHost.kt`, `SlotView.kt`, `SlotScreenView.kt`, `SlotContainers.kt` (containers and slot views by
|
|
118
120
|
slot id: a change goes straight to the views of its id, `SlotViews.kt`), `SlotViewManager.kt`.
|
|
119
121
|
- Safe area: `safearea/SafeAreaView.kt`, `SafeAreaViewManager.kt`; `viewcontrollers/component/ScreenReadiness.kt`;
|
|
120
|
-
`react/
|
|
122
|
+
`react/ScreenEnvironment.kt`.
|
|
121
123
|
- The C++ of `ANNSlot`, `ANNSafeAreaView`: `android/src/main/jni/CMakeLists.txt`, `ApexNavigationSpec.h`; sources in
|
|
122
124
|
`ios/cpp/`.
|
|
123
125
|
|
|
124
126
|
## Threads
|
|
125
127
|
|
|
126
128
|
- **JS thread.** The sync getters `getScreenInsets`, `getScreenRegions`, `getArrangementState`, `getHinge`,
|
|
127
|
-
`getStartInfo` read stores (`ConcurrentHashMap`
|
|
128
|
-
|
|
129
|
+
`getStartInfo` read stores (one `ConcurrentHashMap` of immutable records in `ScreenEnvironment`; `@Volatile` in
|
|
130
|
+
`Hinge`). They never touch a node.
|
|
129
131
|
- **Module thread.** The `check` of every command with a promise: `LayoutReader` and, through it, `OptionsReader` (also
|
|
130
132
|
for `setDefaultOptions`, `mergeOptions` and the options of `dismissModal`, in the runner's `prepare` step);
|
|
131
133
|
`ReadableJson` for native-screen props. The registry of native screens is read under its lock. Images are not loaded
|
|
@@ -172,6 +174,9 @@ top-level folder. Main thread only, unless a line says otherwise.
|
|
|
172
174
|
`cancel` it on every exit.
|
|
173
175
|
- A layer that waits to show must be a `WaitingLayer` and leave on every exit (show, supersede, destroy, root gone).
|
|
174
176
|
- Never resolve a promise yourself: only `CommandAnswer` knows whether its JavaScript went and sends the state first.
|
|
177
|
+
- An event of a screen goes to the JavaScript the screen was made for: `EventEmitter.of(node.jsGeneration)`, or an
|
|
178
|
+
emitter kept from then (it goes silent after a reload). React Native keeps one context across a reload, so a sender
|
|
179
|
+
taken from the context at event time tells the new JavaScript about a screen it never had.
|
|
175
180
|
- A signal that comes inside a layout pass or a view's attach must not move views there; the stack posts readiness
|
|
176
181
|
(`deliver`).
|
|
177
182
|
- A step that runs later (a timer, a readiness signal, an animator's end, a user callback) runs under no runner: it
|
|
@@ -206,6 +211,10 @@ top-level folder. Main thread only, unless a line says otherwise.
|
|
|
206
211
|
- The back gesture's preview shows the screen under the top outside any operation of the queue: it may only reveal
|
|
207
212
|
it and put it back, and its release goes through `StackQueue.cut(gesture = true)`. A command that comes meanwhile
|
|
208
213
|
stops it through `StackDisplay.stop`, like any transition it takes over.
|
|
214
|
+
- What back does (`Navigator.canHandleBack`, `keepsBackFromSystem`) is computed on events, not before each frame: a
|
|
215
|
+
new path that changes it without a command (a gesture, an animator's end, a layout's switch) calls
|
|
216
|
+
`BackCallbackState.update`, or the system's back leaves the app where it should go back. Debug builds log a missed
|
|
217
|
+
one (`BackCallbackCheck`).
|
|
209
218
|
- Without edge to edge the window resizes for the keyboard by the manifest's soft input mode, so
|
|
210
219
|
`layout.adjustResize: false` goes through the system UI owner's keyboard group (`SystemUiWindow.setKeyboardCovers`),
|
|
211
220
|
not through the screen's padding.
|
package/android/CORE.md
CHANGED
|
@@ -10,7 +10,7 @@ otherwise.
|
|
|
10
10
|
```
|
|
11
11
|
JS ─ ANNTurboModule (codegen NativeANNTurboModuleSpec)
|
|
12
12
|
│ sync getters answer on the JS thread from stores the main thread fills
|
|
13
|
-
│ (
|
|
13
|
+
│ (ScreenEnvironment, Hinge, StartInfo)
|
|
14
14
|
▼ module thread
|
|
15
15
|
react/NavigationTurboModule one override per method
|
|
16
16
|
│ layout/LayoutReader ► options/OptionsReader the whole layout read and checked, its options read
|
|
@@ -31,7 +31,7 @@ coordinators: RootCoordinator · StackQueue (one per stack) · ModalCoordinator
|
|
|
31
31
|
presenters: ComponentPresenter · StackPresenter + TopBarPresenter · BottomTabsPresenter · SideMenuPresenter ·
|
|
32
32
|
SplitViewPresenter · system/SystemUiOwner (status bar, navigation bar, orientation, keyboard)
|
|
33
33
|
▼
|
|
34
|
-
react/events/AppearanceDispatcher ─►
|
|
34
|
+
react/events/AppearanceDispatcher ─► NavigationState ─► EventEmitter ─► ANN.* events ─► JS
|
|
35
35
|
```
|
|
36
36
|
|
|
37
37
|
- **Way in.** `NavigationTurboModule` only turns a method into a `CommandRunner` call; it holds no navigation logic. It
|
|
@@ -84,11 +84,12 @@ react/events/AppearanceDispatcher ─► NavigationStateReporter ─► EventEmi
|
|
|
84
84
|
`AppearanceDispatcher.beginTransition`: **`ComponentWillAppear`** of Profile. The transition runs
|
|
85
85
|
(`StackTransitions`, `SystemTransition`, or the screens' own animation, `AnimationRules.stackIsOwn`).
|
|
86
86
|
8. **End.** `finishPush` calls `AppearanceDispatcher.endTransition`: **`ComponentDidDisappear`** of Home, then
|
|
87
|
-
**`ComponentDidAppear`** of Profile, then
|
|
88
|
-
transition's own events (none for a push), then
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
87
|
+
**`ComponentDidAppear`** of Profile, then the answer's one state step (`CommandAnswer.reportStateNow`:
|
|
88
|
+
**`NavigationStateChanged`**, only if it changed), then the transition's own events (none for a push), then
|
|
89
|
+
`CommandAnswer.resolve` with Profile's id.
|
|
90
|
+
9. **Answer.** `resolve` sends the state only when no transition sent it (`reportStateNow` runs once per answer: a
|
|
91
|
+
`setRoot` with layout modals sends it here), resolves the promise, then sends **`CommandCompleted`**
|
|
92
|
+
(`CommandContract.COMPLETED`). After the task, `BackCallbackState.update` recomputes what back does.
|
|
92
93
|
|
|
93
94
|
The events go from the main thread, synchronously, through `EventEmitter.current()`; the promise is answered once.
|
|
94
95
|
|
|
@@ -127,13 +128,16 @@ Each rule has one owner. Change the owner, not a caller; if a new feature needs
|
|
|
127
128
|
`beginTransition` starts the appearances; `endTransition` runs what left, then what came, then the state, then the
|
|
128
129
|
transition's events, then the answer. Events asked before a screen's content is ready are held in its
|
|
129
130
|
`ScreenAppearance` (until the content mounts or `HOLD_LIMIT_MILLIS`).
|
|
130
|
-
- **Navigation state.** `
|
|
131
|
-
|
|
132
|
-
`
|
|
133
|
-
|
|
131
|
+
- **Navigation state.** `NavigationState` owns it: the last state sent, the walk (`stateOf`), the posted update and its
|
|
132
|
+
holds (`NavigationState.Updates`). `AppearanceDispatcher.state` (`StateUpdates`) is the one way to ask for it:
|
|
133
|
+
`setNeedsUpdate` coalesces the asks to one posted update per pass of the main loop and keeps it while held; the
|
|
134
|
+
update calls `updateNow`. A command's answer sends it once, at once and never held (`CommandAnswer.reportStateNow`):
|
|
135
|
+
at the end of its transition, else right before its promise for the commands that change what shows; nothing for a
|
|
136
|
+
JavaScript that went. It reads the navigator through `Navigators.current()`, and `stateOf` finds the
|
|
134
137
|
`tab` as the nearest tabs of the visible screen (the last tabs met walking down from the visible layout). A `setRoot`
|
|
135
138
|
with layout modals or overlays takes `AppearanceDispatcher.StateUpdates.hold()` and releases it right before its
|
|
136
|
-
answer, so JavaScript hears one state; every hold has one `release()`.
|
|
139
|
+
answer, so JavaScript hears one state; every hold has one `release()`. The holds go when the JavaScript goes
|
|
140
|
+
(`NavigationState.javaScriptWent`), so one left by its `setRoot` never holds the next JavaScript's updates.
|
|
137
141
|
- **Answer.** `CommandAnswer`: resolves or rejects once, and nothing once the JavaScript went. The id a stack entry
|
|
138
142
|
answers with is one rule, `StackController.answerIdOf` (the queue asks it through `StackQueue.Host.answerIdOf` with
|
|
139
143
|
an `AnswerKind`): `push` answers the entry's own id (a pair's), every other command the screen it stands for (a
|
|
@@ -141,7 +145,11 @@ Each rule has one owner. Change the owner, not a caller; if a new feature needs
|
|
|
141
145
|
- **Stack queue.** `StackQueue`: every command, back and return of a container goes through it; it keeps `entries`
|
|
142
146
|
(what JavaScript sees) and `shown` (what the container shows) and answers once.
|
|
143
147
|
- **Screen readiness.** `ScreenReadiness` (by id; written by `SafeAreaView`) and `ContentReadiness` (the `Readiness`
|
|
144
|
-
the waits ask, through `IReactView.isReady`).
|
|
148
|
+
the waits ask, through `IReactView.isReady`). As on iOS, the signals are kept (a listener that comes later hears them
|
|
149
|
+
at once), an id has a listener per key (several roots of one bar component), and each signal carries the React
|
|
150
|
+
surface it came from: a `ReactView` takes only its own surface's, so an old root with the same id never makes a new
|
|
151
|
+
one ready. An id renders at once while a root marks it (`ReactView`'s `rendersAtOnce`); the last one takes the id's
|
|
152
|
+
signals with it. A screen's id is cleared as the screen is made and destroyed, everything on reload.
|
|
145
153
|
- **Render wait.** `RenderWait`: the one wait for screens to render, used by `RootCoordinator`, `ModalCoordinator`,
|
|
146
154
|
`OverlayCoordinator` and `StackQueue`; `RenderWait.waitedScreens` walks `ParentController.waitedChildren()` at every
|
|
147
155
|
level. What differs between users are values (limit, bar phase, unknown-content rule), never steps.
|
|
@@ -171,7 +179,8 @@ Each rule has one owner. Change the owner, not a caller; if a new feature needs
|
|
|
171
179
|
- **Back.** `NavigationActivity.passBack` → `ReactGateway.onBackPressed` (JavaScript's `BackHandler`) →
|
|
172
180
|
`invokeDefaultOnBackPressed` → `Navigator.handleBack` → `ModalCoordinator.handleBack` → the root's
|
|
173
181
|
`ParentController.handleBack` → `StackController.handleBack` → `StackQueue.cut`. `BackCallbackState` keeps the
|
|
174
|
-
system callback enabled only while back has something to do
|
|
182
|
+
system callback enabled only while back has something to do, computed again on the events that change it (its KDoc
|
|
183
|
+
lists them), never before each frame; debug builds check it (`BackCallbackCheck`).
|
|
175
184
|
- **Error texts.** `ErrorTexts`: one function per text, the same words as iOS, made with iOS's from
|
|
176
185
|
`src/interfaces/contract/errors.json` (`scripts/contract.mjs`). JavaScript adds `[navigation] <command>:`.
|
|
177
186
|
- **Event names and keys.** `EventContract`, made from the event types of `src/interfaces/`; `EventEmitter` and the
|
|
@@ -179,7 +188,7 @@ Each rule has one owner. Change the owner, not a caller; if a new feature needs
|
|
|
179
188
|
- **Room for the bars.** A screen's room at the top and bottom is `ScreenFrame` with `ScreenSafeArea` and the
|
|
180
189
|
`ParentController` room hooks (`getTopInset`, `keepsStatusBarRoomOf`, …). A screen in a pair forwards its room to
|
|
181
190
|
the pair (`ScreenFrame.applyRoom`), and the pair's `getTopInset` uses the screens' rule (`ScreenFrame.topRoomOf`)
|
|
182
|
-
with the options it shows with. A view's place in the window for the safe area (the React root, the stack's header
|
|
191
|
+
with the options it shows with. The bottom edge is not padded by default (only `'bottom'` in the edges pads it); `roomPadding` still shrinks the view for an opaque navigation bar and the keyboard. A view's place in the window for the safe area (the React root, the stack's header
|
|
183
192
|
bottom in `headerBottomOf`, the tabs' top in `tabsTopOf`) is `RestPlace`: from layout, without translations, a sheet
|
|
184
193
|
at rest.
|
|
185
194
|
- **A container's own move.** A container that moves children in or out of the window without a command (the split
|
|
@@ -200,9 +209,9 @@ Modules are the feature classes that plug into the core through its contract (th
|
|
|
200
209
|
(`SlotHost`, `SlotView`, `SlotContainers`), safe area (`SafeAreaView`), native screens (`nativescreen/`), splash
|
|
201
210
|
(`splash/`), animations (`FadeThrough`, `SystemTransition`, `TransitionTargets`, three element animators), utils
|
|
202
211
|
(`MaterialTheme`, `NavigationLog`, `awaitBarComponents`, `whenDrawn`).
|
|
203
|
-
- **Stores and singletons:**
|
|
204
|
-
|
|
205
|
-
(`BackCallbackState`), stack (`PreparedFrame`).
|
|
212
|
+
- **Stores and singletons:** a screen's environment (`ScreenEnvironment`: its insets and padding, its regions as
|
|
213
|
+
`ScreenRegions` measures them, its pair state), the window (`Hinge`, `WindowFolds`), links (`LaunchLink`,
|
|
214
|
+
`StartInfo`), events (`NavigationState`), commands (`BackCallbackState`), stack (`PreparedFrame`).
|
|
206
215
|
|
|
207
216
|
How they plug in:
|
|
208
217
|
|
|
@@ -226,4 +235,8 @@ How they plug in:
|
|
|
226
235
|
- **Core code in a module's folder:** `viewcontrollers/splitview/SplitViewPresenter.kt`, `slots/SlotScreenView.kt`,
|
|
227
236
|
and in `views/animations/` and `views/element/` everything except the files named above.
|
|
228
237
|
- **Stores** are written on the main thread by the core and read from any thread; they are cleared with their nodes and on
|
|
229
|
-
reload.
|
|
238
|
+
reload. A screen's environment is one immutable record per id in `ScreenEnvironment` (insets for JavaScript, padding,
|
|
239
|
+
regions, pair state), replaced at once; the screen's node clears it with one `remove` when it is destroyed, unless a
|
|
240
|
+
later node has its id; the padding listeners of its safe area views stay until the views stop. Their events, and
|
|
241
|
+
the search's, go through the emitter of the JavaScript the screen was made for
|
|
242
|
+
(`EventEmitter.of(node.jsGeneration)`); the hinge keeps the `EventEmitter.current()` of the call that turned it on.
|
|
@@ -35,6 +35,8 @@ import dev.apexnative.navigation.splash.SplashTheme
|
|
|
35
35
|
import dev.apexnative.navigation.splash.SplashView
|
|
36
36
|
import dev.apexnative.navigation.system.SystemUiWindow
|
|
37
37
|
import dev.apexnative.navigation.utils.MaterialTheme
|
|
38
|
+
import dev.apexnative.navigation.viewcontrollers.modal.SheetBack
|
|
39
|
+
import dev.apexnative.navigation.viewcontrollers.navigator.BackCallbackCheck
|
|
38
40
|
import dev.apexnative.navigation.viewcontrollers.navigator.BackCallbackState
|
|
39
41
|
import dev.apexnative.navigation.viewcontrollers.navigator.Navigator
|
|
40
42
|
import dev.apexnative.navigation.viewcontrollers.navigator.Navigators
|
|
@@ -71,38 +73,73 @@ open class NavigationActivity : AppCompatActivity(), DefaultHardwareBackBtnHandl
|
|
|
71
73
|
// Enabled while back has something to do in the app (BackCallbackState); otherwise the system does its own back.
|
|
72
74
|
// From Android 14, where the app hears the progress of the back gesture, a gesture that would pop a stack with the
|
|
73
75
|
// default transition previews the pop as the system previews back between activities (`StackController
|
|
74
|
-
// .BackGesture`)
|
|
76
|
+
// .BackGesture`), and one that would dismiss a sheet previews its hide as Material's sheets do (`SheetBack`), its
|
|
77
|
+
// release closing it as a drag down does; JavaScript's listeners hear back first, so with them there is no preview.
|
|
75
78
|
private val backCallback = object : OnBackPressedCallback(false) {
|
|
76
79
|
override fun handleOnBackStarted(backEvent: BackEventCompat) {
|
|
77
80
|
backGesture = null
|
|
81
|
+
sheetGesture = null
|
|
82
|
+
val navigator = navigator ?: return
|
|
83
|
+
backCheck?.atBack(navigator)
|
|
78
84
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.UPSIDE_DOWN_CAKE || BackCallbackState.hasJsListeners) return
|
|
79
|
-
val stack = navigator
|
|
80
|
-
|
|
85
|
+
val stack = navigator.stackPoppedByBack()
|
|
86
|
+
if (stack != null) {
|
|
87
|
+
backGesture = stack.startBackGesture(backEvent.swipeEdge == BackEventCompat.EDGE_LEFT, backEvent.touchY)
|
|
88
|
+
return
|
|
89
|
+
}
|
|
90
|
+
sheetGesture = navigator.sheetDismissedByBack()?.startBackPreview(backEvent)
|
|
81
91
|
}
|
|
82
92
|
|
|
83
93
|
override fun handleOnBackProgressed(backEvent: BackEventCompat) {
|
|
84
94
|
backGesture?.progress(backEvent.progress, backEvent.touchY)
|
|
95
|
+
sheetGesture?.progress(backEvent)
|
|
85
96
|
}
|
|
86
97
|
|
|
87
98
|
override fun handleOnBackCancelled() {
|
|
88
99
|
backGesture?.cancel()
|
|
89
100
|
backGesture = null
|
|
101
|
+
sheetGesture?.cancel()
|
|
102
|
+
sheetGesture = null
|
|
103
|
+
navigator?.let(BackCallbackState::update)
|
|
90
104
|
}
|
|
91
105
|
|
|
92
106
|
override fun handleOnBackPressed() {
|
|
107
|
+
navigator?.let { backCheck?.atBack(it) }
|
|
93
108
|
val gesture = backGesture
|
|
94
109
|
backGesture = null
|
|
95
|
-
|
|
110
|
+
val sheet = sheetGesture
|
|
111
|
+
sheetGesture = null
|
|
112
|
+
when {
|
|
113
|
+
gesture?.commit() == true -> BackCallbackState.update()
|
|
114
|
+
sheet != null && commitSheet(sheet) -> BackCallbackState.update()
|
|
115
|
+
else -> passBack()
|
|
116
|
+
}
|
|
96
117
|
}
|
|
97
118
|
}
|
|
98
119
|
|
|
99
120
|
// The back gesture that previews a pop now; none.
|
|
100
121
|
private var backGesture: StackController.BackGesture? = null
|
|
101
122
|
|
|
102
|
-
//
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
123
|
+
// The back gesture that previews a sheet's hide now; none.
|
|
124
|
+
private var sheetGesture: SheetBack.Gesture? = null
|
|
125
|
+
|
|
126
|
+
// A sheet's preview released: the sheet goes as the user's drag down closes it, and back doesn't go on (no
|
|
127
|
+
// `passBack`, which would dismiss it a second time). A command that changed what back does since the gesture
|
|
128
|
+
// started (a push in the sheet, a modal over it, a hold) leaves it to the usual back, the sheet back in place.
|
|
129
|
+
private fun commitSheet(gesture: SheetBack.Gesture): Boolean {
|
|
130
|
+
if (navigator?.sheetDismissedByBack() !== gesture.sheet) {
|
|
131
|
+
gesture.cancel()
|
|
132
|
+
return false
|
|
133
|
+
}
|
|
134
|
+
return gesture.commit()
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Debug builds only: the check that what back does never changes without its trigger (BackCallbackState), sampled
|
|
138
|
+
// before frames and at back events. It only logs; release builds have neither the check nor the listener.
|
|
139
|
+
private val backCheck = if (BuildConfig.DEBUG) BackCallbackCheck() else null
|
|
140
|
+
private val backCheckBeforeFrame = ViewTreeObserver.OnPreDrawListener {
|
|
141
|
+
val navigator = navigator
|
|
142
|
+
if (navigator != null) backCheck?.sample(navigator, SystemClock.uptimeMillis())
|
|
106
143
|
true
|
|
107
144
|
}
|
|
108
145
|
private var frameObserver: ViewTreeObserver? = null
|
|
@@ -143,7 +180,9 @@ open class NavigationActivity : AppCompatActivity(), DefaultHardwareBackBtnHandl
|
|
|
143
180
|
SlotHost.attachIfStarted(this)
|
|
144
181
|
reactGateway.onActivityCreated(this)
|
|
145
182
|
onBackPressedDispatcher.addCallback(this, backCallback)
|
|
146
|
-
|
|
183
|
+
if (backCheck != null) {
|
|
184
|
+
frameObserver = window.decorView.viewTreeObserver.also { it.addOnPreDrawListener(backCheckBeforeFrame) }
|
|
185
|
+
}
|
|
147
186
|
// The activity's system UI: its owner remembers the theme's colors and the manifest's orientation now, before
|
|
148
187
|
// anything changes them.
|
|
149
188
|
navigator.systemUi.start(SystemUiWindow.of(this))
|
|
@@ -218,6 +257,8 @@ open class NavigationActivity : AppCompatActivity(), DefaultHardwareBackBtnHandl
|
|
|
218
257
|
isNavigationResumed = true
|
|
219
258
|
// The root, the modals and the overlays lay out again; nothing is sent.
|
|
220
259
|
navigator?.onResume()
|
|
260
|
+
// A safety net: what back does changes on its own triggers, and the system asks the callback from here on.
|
|
261
|
+
navigator?.let(BackCallbackState::update)
|
|
221
262
|
if (developerSupport() != null) reloadBroadcast.register()
|
|
222
263
|
ReloadCoordinator.launchIfDue(this)
|
|
223
264
|
}
|
|
@@ -239,6 +280,8 @@ open class NavigationActivity : AppCompatActivity(), DefaultHardwareBackBtnHandl
|
|
|
239
280
|
override fun onWindowFocusChanged(hasFocus: Boolean) {
|
|
240
281
|
super.onWindowFocusChanged(hasFocus)
|
|
241
282
|
if (reactGateway.currentReactContext != null) reactGateway.host.onWindowFocusChange(hasFocus)
|
|
283
|
+
// A safety net, as at onResume: back after a dialog or another window over the activity.
|
|
284
|
+
if (hasFocus) navigator?.let(BackCallbackState::update)
|
|
242
285
|
}
|
|
243
286
|
|
|
244
287
|
override fun onUserLeaveHint() {
|
|
@@ -302,9 +345,11 @@ open class NavigationActivity : AppCompatActivity(), DefaultHardwareBackBtnHandl
|
|
|
302
345
|
splashHold = null
|
|
303
346
|
reloadBroadcast.release()
|
|
304
347
|
navigator?.let(BackCallbackState::detach)
|
|
305
|
-
// The window's observer now, and the one
|
|
306
|
-
|
|
307
|
-
|
|
348
|
+
// The debug check's listener: from the window's observer now, and the one it was added to, if they differ.
|
|
349
|
+
if (backCheck != null) {
|
|
350
|
+
val observers = listOfNotNull(frameObserver, window?.peekDecorView()?.viewTreeObserver).distinct()
|
|
351
|
+
observers.forEach { if (it.isAlive) it.removeOnPreDrawListener(backCheckBeforeFrame) }
|
|
352
|
+
}
|
|
308
353
|
frameObserver = null
|
|
309
354
|
SlotHost.detach(this)
|
|
310
355
|
navigator?.destroyWithActivity()
|
|
@@ -317,7 +362,9 @@ open class NavigationActivity : AppCompatActivity(), DefaultHardwareBackBtnHandl
|
|
|
317
362
|
/**
|
|
318
363
|
* Back that JavaScript's listeners didn't take: the navigation does its step first — a stack's top leaves or holds,
|
|
319
364
|
* a pane closes —; with nothing to do there, the activity's own back, as without the navigation. An app may
|
|
320
|
-
* override it (e.g. `moveTaskToBack(true)`).
|
|
365
|
+
* override it (e.g. `moveTaskToBack(true)`). The callback is off for that back only: the dispatcher's own back is
|
|
366
|
+
* the activity's, not [onBackPressed]; then it is computed again, and `hardwareBackButton.systemAnimation: false`
|
|
367
|
+
* turns it on again.
|
|
321
368
|
*/
|
|
322
369
|
override fun invokeDefaultOnBackPressed() {
|
|
323
370
|
val handled = navigator?.handleBack(CommandListenerAdapter()) == true
|
|
@@ -8,7 +8,7 @@ import com.facebook.react.bridge.ReadableMap
|
|
|
8
8
|
*/
|
|
9
9
|
class AnimationsOptions {
|
|
10
10
|
@JvmField var push = StackPushAnimationOptions()
|
|
11
|
-
@JvmField var pop =
|
|
11
|
+
@JvmField var pop = StackPopAnimationOptions()
|
|
12
12
|
@JvmField var setStackRoot = SetStackRootAnimationOptions()
|
|
13
13
|
@JvmField var setRoot = EnterExitAnimationOptions()
|
|
14
14
|
@JvmField var showModal = ModalAnimationOptions()
|
|
@@ -43,7 +43,7 @@ class AnimationsOptions {
|
|
|
43
43
|
val options = AnimationsOptions()
|
|
44
44
|
if (map == null) return options
|
|
45
45
|
options.push = map.group("push") { StackPushAnimationOptions.parse(it) }
|
|
46
|
-
options.pop = map.group("pop") {
|
|
46
|
+
options.pop = map.group("pop") { StackPopAnimationOptions.parse(it) }
|
|
47
47
|
options.setStackRoot = map.group("setStackRoot") { SetStackRootAnimationOptions.parse(it) }
|
|
48
48
|
options.setRoot = map.group("setRoot") { EnterExitAnimationOptions.parse(it) }
|
|
49
49
|
options.showModal = map.group("showModal") { ModalAnimationOptions.parse(it) }
|
|
@@ -95,6 +95,7 @@ class BottomTabsAccessoryOptions {
|
|
|
95
95
|
class BottomTabsOptions {
|
|
96
96
|
@JvmField var visible: Bool = NullBool()
|
|
97
97
|
@JvmField var animate: Bool = NullBool()
|
|
98
|
+
@JvmField var switchAnimation: Text = NullText()
|
|
98
99
|
/** Selects a tab once; a whole number, truncated where it is read. */
|
|
99
100
|
@JvmField var currentTabIndex: Fraction = NullFraction()
|
|
100
101
|
/** Selects a tab once. */
|
|
@@ -124,18 +125,19 @@ class BottomTabsOptions {
|
|
|
124
125
|
@JvmField var minimizeBehavior: Text = NullText()
|
|
125
126
|
@JvmField var bottomAccessory = BottomTabsAccessoryOptions()
|
|
126
127
|
|
|
127
|
-
fun hasValue(): Boolean = visible.hasValue() || animate.hasValue() ||
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
bottomAccessory.hasValue()
|
|
128
|
+
fun hasValue(): Boolean = visible.hasValue() || animate.hasValue() || switchAnimation.hasValue() ||
|
|
129
|
+
currentTabIndex.hasValue() || currentTabId.hasValue() || testID.hasValue() || accessibilityLabel.hasValue() ||
|
|
130
|
+
moreTestID.hasValue() || drawBehind.hasValue() || backgroundColor.hasValue() || tabsAttachMode.hasValue() ||
|
|
131
|
+
barStyle.hasValue() || layoutStyle.hasValue() || cornerRadius.hasValue() || bottomMargin.hasValue() ||
|
|
132
|
+
translucent.hasValue() || hideShadow.hasValue() || titleDisplayMode.hasValue() || elevation.hasValue() ||
|
|
133
|
+
hideOnScroll.hasValue() || borderColor.hasValue() || borderWidth.hasValue() || shadow.hasValue() ||
|
|
134
|
+
customRow.hasValue() || sidebar.hasValue() || sidebarLayout.hasValue() || customizationId.hasValue() ||
|
|
135
|
+
minimizeBehavior.hasValue() || bottomAccessory.hasValue()
|
|
135
136
|
|
|
136
137
|
fun mergeWith(other: BottomTabsOptions) {
|
|
137
138
|
if (other.visible.hasValue()) visible = other.visible
|
|
138
139
|
if (other.animate.hasValue()) animate = other.animate
|
|
140
|
+
if (other.switchAnimation.hasValue()) switchAnimation = other.switchAnimation
|
|
139
141
|
if (other.currentTabIndex.hasValue()) currentTabIndex = other.currentTabIndex
|
|
140
142
|
if (other.currentTabId.hasValue()) currentTabId = other.currentTabId
|
|
141
143
|
if (other.testID.hasValue()) testID = other.testID
|
|
@@ -167,6 +169,7 @@ class BottomTabsOptions {
|
|
|
167
169
|
fun mergeWithDefault(defaults: BottomTabsOptions) {
|
|
168
170
|
if (!visible.hasValue()) visible = defaults.visible
|
|
169
171
|
if (!animate.hasValue()) animate = defaults.animate
|
|
172
|
+
if (!switchAnimation.hasValue()) switchAnimation = defaults.switchAnimation
|
|
170
173
|
if (!currentTabIndex.hasValue()) currentTabIndex = defaults.currentTabIndex
|
|
171
174
|
if (!currentTabId.hasValue()) currentTabId = defaults.currentTabId
|
|
172
175
|
if (!testID.hasValue()) testID = defaults.testID
|
|
@@ -198,6 +201,7 @@ class BottomTabsOptions {
|
|
|
198
201
|
fun copy() = BottomTabsOptions().also { it.mergeWith(this) }
|
|
199
202
|
|
|
200
203
|
companion object {
|
|
204
|
+
private val SWITCH_ANIMATIONS = setOf("platform", "none")
|
|
201
205
|
private val TABS_ATTACH_MODES = setOf("together", "afterInitialTab", "onSwitchToTab")
|
|
202
206
|
private val BAR_STYLES = setOf("default", "black")
|
|
203
207
|
private val LAYOUT_STYLES = setOf("stretch", "compact")
|
|
@@ -212,6 +216,7 @@ class BottomTabsOptions {
|
|
|
212
216
|
if (map == null) return options
|
|
213
217
|
options.visible = BoolParser.parse(map, "visible")
|
|
214
218
|
options.animate = BoolParser.parse(map, "animate")
|
|
219
|
+
options.switchAnimation = TextParser.parse(map, "switchAnimation", SWITCH_ANIMATIONS)
|
|
215
220
|
options.currentTabIndex = FractionParser.parse(map, "currentTabIndex")
|
|
216
221
|
options.currentTabId = TextParser.parse(map, "currentTabId")
|
|
217
222
|
options.testID = TextParser.parse(map, "testID")
|
package/android/src/main/java/dev/apexnative/navigation/options/HardwareBackButtonOptions.kt
CHANGED
|
@@ -15,20 +15,27 @@ class HardwareBackButtonOptions {
|
|
|
15
15
|
@JvmField var popStackOnPress: Bool = NullBool()
|
|
16
16
|
/** Internal. */
|
|
17
17
|
@JvmField var dismissModalOnPress: Bool = NullBool()
|
|
18
|
+
/**
|
|
19
|
+
* `systemAnimation`: `false` keeps back in the app on the first screen too, so the system doesn't play its
|
|
20
|
+
* animation of going home; the press then does the activity's own back.
|
|
21
|
+
*/
|
|
22
|
+
@JvmField var systemAnimation: Bool = NullBool()
|
|
18
23
|
|
|
19
24
|
fun hasValue(): Boolean = bottomTabsOnPress.hasValue() || popStackOnPress.hasValue() ||
|
|
20
|
-
dismissModalOnPress.hasValue()
|
|
25
|
+
dismissModalOnPress.hasValue() || systemAnimation.hasValue()
|
|
21
26
|
|
|
22
27
|
fun mergeWith(other: HardwareBackButtonOptions) {
|
|
23
28
|
if (other.bottomTabsOnPress.hasValue()) bottomTabsOnPress = other.bottomTabsOnPress
|
|
24
29
|
if (other.popStackOnPress.hasValue()) popStackOnPress = other.popStackOnPress
|
|
25
30
|
if (other.dismissModalOnPress.hasValue()) dismissModalOnPress = other.dismissModalOnPress
|
|
31
|
+
if (other.systemAnimation.hasValue()) systemAnimation = other.systemAnimation
|
|
26
32
|
}
|
|
27
33
|
|
|
28
34
|
fun mergeWithDefault(defaults: HardwareBackButtonOptions) {
|
|
29
35
|
if (!bottomTabsOnPress.hasValue()) bottomTabsOnPress = defaults.bottomTabsOnPress
|
|
30
36
|
if (!popStackOnPress.hasValue()) popStackOnPress = defaults.popStackOnPress
|
|
31
37
|
if (!dismissModalOnPress.hasValue()) dismissModalOnPress = defaults.dismissModalOnPress
|
|
38
|
+
if (!systemAnimation.hasValue()) systemAnimation = defaults.systemAnimation
|
|
32
39
|
}
|
|
33
40
|
|
|
34
41
|
fun copy() = HardwareBackButtonOptions().also { it.mergeWith(this) }
|
|
@@ -43,6 +50,7 @@ class HardwareBackButtonOptions {
|
|
|
43
50
|
options.bottomTabsOnPress = TextParser.parse(map, "bottomTabsOnPress", BOTTOM_TABS_ON_PRESS)
|
|
44
51
|
options.popStackOnPress = BoolParser.parse(map, "popStackOnPress")
|
|
45
52
|
options.dismissModalOnPress = BoolParser.parse(map, "dismissModalOnPress")
|
|
53
|
+
options.systemAnimation = BoolParser.parse(map, "systemAnimation")
|
|
46
54
|
return options
|
|
47
55
|
}
|
|
48
56
|
}
|
|
@@ -99,8 +99,8 @@ class BottomTabsEnterExitAnimationOptions {
|
|
|
99
99
|
}
|
|
100
100
|
|
|
101
101
|
/**
|
|
102
|
-
* `StackAnimationOptions`: the
|
|
103
|
-
*
|
|
102
|
+
* `StackAnimationOptions`: the base of the animations of `push`, `pop` and `setStackRoot`; over the shared elements and
|
|
103
|
+
* element transitions of [LayoutAnimation].
|
|
104
104
|
*/
|
|
105
105
|
open class StackAnimationOptions : LayoutAnimation() {
|
|
106
106
|
@JvmField var enabled: Bool = NullBool()
|
|
@@ -188,6 +188,44 @@ class StackPushAnimationOptions : StackAnimationOptions() {
|
|
|
188
188
|
}
|
|
189
189
|
}
|
|
190
190
|
|
|
191
|
+
/**
|
|
192
|
+
* `StackPopAnimationOptions`: the animation of `pop`, and `preview`: whether a back gesture over the stack previews the
|
|
193
|
+
* pop of its top (Android 14+); `false` pops it when the gesture is released, as on Android 13.
|
|
194
|
+
*/
|
|
195
|
+
class StackPopAnimationOptions : StackAnimationOptions() {
|
|
196
|
+
@JvmField var preview: Bool = NullBool()
|
|
197
|
+
|
|
198
|
+
override fun hasValue(): Boolean = super.hasValue() || preview.hasValue()
|
|
199
|
+
|
|
200
|
+
fun mergeWith(other: StackPopAnimationOptions) {
|
|
201
|
+
super.mergeWith(other)
|
|
202
|
+
if (other.preview.hasValue()) preview = other.preview
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
fun mergeWithDefault(defaults: StackPopAnimationOptions) {
|
|
206
|
+
super.mergeWithDefault(defaults)
|
|
207
|
+
if (!preview.hasValue()) preview = defaults.preview
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
override fun copy(): StackPopAnimationOptions = StackPopAnimationOptions().also { it.mergeWith(this) }
|
|
211
|
+
|
|
212
|
+
// Reads the fields of this class from [map], after those of its parent.
|
|
213
|
+
private fun readStackPopAnimationOptions(map: ReadableMap) {
|
|
214
|
+
readStackAnimationOptions(map)
|
|
215
|
+
preview = BoolParser.parse(map, "preview")
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
companion object {
|
|
219
|
+
@JvmStatic
|
|
220
|
+
fun parse(map: ReadableMap?): StackPopAnimationOptions {
|
|
221
|
+
val options = StackPopAnimationOptions()
|
|
222
|
+
if (map == null) return options
|
|
223
|
+
options.readStackPopAnimationOptions(map)
|
|
224
|
+
return options
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
191
229
|
/** `SetStackRootAnimationOptions`: the animation of `setStackRoot`. */
|
|
192
230
|
class SetStackRootAnimationOptions : StackAnimationOptions() {
|
|
193
231
|
@JvmField var asPush: Bool = NullBool()
|