@apexnative/navigation 1.0.0-alpha.1 → 1.0.0-alpha.2
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 +16 -0
- package/README.md +147 -51
- package/android/CLAUDE.md +10 -4
- package/android/CORE.md +4 -1
- package/android/RECIPES.md +4 -3
- package/android/src/main/java/dev/apexnative/navigation/react/ArrangementStates.kt +5 -4
- package/android/src/main/java/dev/apexnative/navigation/react/ErrorTexts.kt +75 -60
- package/android/src/main/java/dev/apexnative/navigation/react/Hinge.kt +3 -2
- package/android/src/main/java/dev/apexnative/navigation/react/OptionsCommands.kt +1 -1
- package/android/src/main/java/dev/apexnative/navigation/react/ScreenInsets.kt +5 -4
- package/android/src/main/java/dev/apexnative/navigation/react/ScreenRegions.kt +7 -6
- package/android/src/main/java/dev/apexnative/navigation/react/events/AppearanceDispatcher.kt +3 -35
- package/android/src/main/java/dev/apexnative/navigation/react/events/EventContract.kt +226 -0
- package/android/src/main/java/dev/apexnative/navigation/react/events/EventEmitter.kt +101 -87
- package/android/src/main/java/dev/apexnative/navigation/react/events/ScreenAppearance.kt +0 -6
- package/android/src/main/java/dev/apexnative/navigation/utils/UiUtils.kt +3 -3
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/TabItems.kt +3 -3
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalCoordinator.kt +12 -8
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalSheet.kt +12 -11
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalViews.kt +2 -2
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/SheetStops.kt +25 -0
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/navigator/Navigator.kt +1 -1
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/sidemenu/SideMenuController.kt +1 -3
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/sidemenu/SideMenuMotion.kt +4 -7
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/sidemenu/SideMenuPresenter.kt +16 -7
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/sidemenu/SideMenuRules.kt +8 -1
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/splitview/SplitViewController.kt +2 -1
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/topbar/TopBarPresenter.kt +2 -1
- package/android/src/main/java/dev/apexnative/navigation/views/bottomtabs/BottomTabs.kt +5 -5
- package/android/src/main/java/dev/apexnative/navigation/views/element/ElementStep.kt +31 -0
- package/android/src/main/java/dev/apexnative/navigation/views/element/ElementTransitions.kt +85 -27
- package/android/src/main/java/dev/apexnative/navigation/views/element/TargetsWait.kt +5 -4
- package/android/src/main/java/dev/apexnative/navigation/views/element/TransitionTargets.kt +9 -6
- package/android/src/main/java/dev/apexnative/navigation/views/element/TransitionViews.kt +100 -0
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ElementPictures.kt +45 -1
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ImageMatrixAnimator.kt +3 -5
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/PlainViewAnimator.kt +3 -5
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ReactImageAnimator.kt +3 -5
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/SharedAnimatorFactory.kt +2 -0
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/TextChangeAnimator.kt +4 -6
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ViewChangeAnimator.kt +4 -6
- package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/titlebar/ButtonBar.kt +4 -3
- package/cli/app.js +1 -1
- package/cli/project.js +6 -2
- package/cli/setup/index.js +13 -0
- package/cli/setup/steps/appDelegate.js +66 -11
- package/cli/setup/steps/sceneDelegate.js +108 -0
- package/docs-md/architecture/transitions.md +1 -1
- package/docs-md/guides/deep-links.md +15 -33
- package/docs-md/start/installation.md +1 -0
- package/ios/Animations/ANNTransitionAnimator.mm +13 -11
- package/ios/Animations/ANNTransitionWait.mm +6 -7
- package/ios/Animations/Elements/ANNElementFinder.h +57 -0
- package/ios/Animations/Elements/ANNElementFinder.mm +174 -2
- package/ios/Animations/Elements/ANNElementTransition.h +7 -5
- package/ios/Animations/Elements/ANNElementTransition.mm +17 -19
- package/ios/Animations/Elements/ANNSharedElementFlight.h +13 -8
- package/ios/Animations/Elements/ANNSharedElementFlight.mm +59 -24
- package/ios/CLAUDE.md +10 -2
- package/ios/CORE.md +3 -2
- package/ios/Contract/ANNCommandAnswer.h +4 -3
- package/ios/Contract/ANNCommandAnswer.mm +12 -2
- package/ios/Contract/ANNCommandRunner.h +2 -2
- package/ios/Contract/ANNCommandRunner.mm +1 -1
- package/ios/Contract/ANNErrorTexts.h +6 -2
- package/ios/Contract/ANNErrorTexts.mm +18 -10
- package/ios/Core/ANNDeepLinkQueue.h +20 -1
- package/ios/Core/ANNDeepLinkQueue.mm +107 -12
- package/ios/Core/ANNErrorHandler.h +8 -3
- package/ios/Core/ANNErrorHandler.mm +17 -1
- package/ios/Core/ANNEventContract.h +246 -0
- package/ios/Core/ANNEventContract.mm +225 -0
- package/ios/Core/ANNEventEmitter.h +5 -1
- package/ios/Core/ANNEventEmitter.mm +117 -80
- package/ios/Core/ANNHinge.mm +6 -4
- package/ios/Core/ANNSceneDelegate.mm +25 -15
- package/ios/Core/ANNSystemUI.h +3 -2
- package/ios/Core/ANNSystemUI.mm +7 -0
- package/ios/Layouts/Arrangement/ANNArrangementViewController.mm +6 -4
- package/ios/Layouts/Modal/ANNModalCoordinator.mm +1 -1
- package/ios/Layouts/Modal/ANNModalPresentation.h +8 -5
- package/ios/Layouts/Modal/ANNModalPresentation.mm +12 -12
- package/ios/Layouts/Modal/ANNSheetPresenter.mm +83 -1
- package/ios/Layouts/Root/ANNRootCoordinator.mm +12 -3
- package/ios/Layouts/Screen/ANNComponentPresenter.h +11 -3
- package/ios/Layouts/Screen/ANNComponentPresenter.mm +34 -6
- package/ios/Layouts/Screen/ANNComponentViewController.mm +28 -7
- package/ios/Layouts/Screen/ANNNativeScreenContext.mm +7 -5
- package/ios/Layouts/Screen/ANNScreenInsets.mm +6 -4
- package/ios/Layouts/Screen/ANNScreenRegions.mm +9 -6
- package/ios/Layouts/SplitView/ANNSplitViewController.mm +4 -7
- package/ios/Options/ANNSheetOptions.h +4 -1
- package/ios/Public/ApexNavigation/ANNSceneDelegate.h +3 -0
- package/ios/RECIPES.md +2 -1
- package/ios/TurboModules/ANNTurboEventEmitter.mm +4 -35
- package/ios/TurboModules/ANNTurboModule.mm +5 -2
- package/lib/module/ARCHITECTURE.md +7 -4
- package/lib/module/api/app/createNavigation.js +2 -0
- package/lib/module/api/app/createNavigation.js.map +1 -1
- package/lib/module/api/linking/dispatchedLinks.js +23 -0
- package/lib/module/api/linking/dispatchedLinks.js.map +1 -0
- package/lib/module/interfaces/contract/errors.json +275 -0
- package/lib/typescript/api/app/createNavigation.d.ts.map +1 -1
- package/lib/typescript/api/linking/dispatchedLinks.d.ts +7 -0
- package/lib/typescript/api/linking/dispatchedLinks.d.ts.map +1 -0
- package/lib/typescript/interfaces/Events.d.ts +9 -0
- package/lib/typescript/interfaces/Events.d.ts.map +1 -1
- package/lib/typescript/interfaces/options/animations.d.ts +17 -13
- package/lib/typescript/interfaces/options/animations.d.ts.map +1 -1
- package/lib/typescript/interfaces/options/modals.d.ts +15 -11
- package/lib/typescript/interfaces/options/modals.d.ts.map +1 -1
- package/lib/typescript/interfaces/options/sideMenu.d.ts +7 -6
- package/lib/typescript/interfaces/options/sideMenu.d.ts.map +1 -1
- package/lib/typescript/interfaces/options/system.d.ts +4 -3
- package/lib/typescript/interfaces/options/system.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/ARCHITECTURE.md +7 -4
- package/src/api/app/createNavigation.ts +2 -0
- package/src/api/linking/dispatchedLinks.ts +18 -0
- package/src/interfaces/Events.ts +10 -0
- package/src/interfaces/contract/errors.json +275 -0
- package/src/interfaces/options/animations.ts +17 -13
- package/src/interfaces/options/modals.ts +15 -11
- package/src/interfaces/options/sideMenu.ts +7 -6
- package/src/interfaces/options/system.ts +4 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.0.0-alpha.2](https://github.com/apexnative/navigation/compare/v1.0.0-alpha.1...v1.0.0-alpha.2) (2026-10-22)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
* **android:** fractional option sizes, sheet stops, live side menu size and menu appear events ([#18](https://github.com/apexnative/navigation/issues/18)) ([3ed8913](https://github.com/apexnative/navigation/commit/3ed8913126d83f5d75a265d22e3f80e581d329b8))
|
|
9
|
+
* **cli:** set up and make apps on React Native 0.88's template ([#23](https://github.com/apexnative/navigation/issues/23)) ([3c24f04](https://github.com/apexnative/navigation/commit/3c24f042f4d6a6a2660768a7443cc5eeba004235))
|
|
10
|
+
* **ios:** hand sheet detents to UIKit sorted by height and without repeats ([#15](https://github.com/apexnative/navigation/issues/15)) ([d1c1004](https://github.com/apexnative/navigation/commit/d1c10043353f45b452e7200d2c179515b6989737))
|
|
11
|
+
* **ios:** hide the dismissal of the old root's modals when a new root shows without animation ([#14](https://github.com/apexnative/navigation/issues/14)) ([b639232](https://github.com/apexnative/navigation/commit/b639232e8b73c38bf8599b7bac24ecc5c7104ca6))
|
|
12
|
+
* **ios:** keep deep links working on React Native 0.88 in apps with a scene delegate ([#24](https://github.com/apexnative/navigation/issues/24)) ([7f690cd](https://github.com/apexnative/navigation/commit/7f690cdac41be774b55cf7186105faa605249bc2))
|
|
13
|
+
* **ios:** keep the status bar blur in the status bar's frame as it changes ([#17](https://github.com/apexnative/navigation/issues/17)) ([d5051e9](https://github.com/apexnative/navigation/commit/d5051e9c30fc0040e726d12ef352da7cc43f2a8c))
|
|
14
|
+
* **ios:** log instead of calling a command's nil promise block ([#22](https://github.com/apexnative/navigation/issues/22)) ([b2ed4ca](https://github.com/apexnative/navigation/commit/b2ed4ca2cb76207b24747e78bf702ef1ab02146b))
|
|
15
|
+
* **ios:** reject a command whose answer goes away unanswered ([#12](https://github.com/apexnative/navigation/issues/12)) ([0914c03](https://github.com/apexnative/navigation/commit/0914c03ec465ae8273d736d1b12e5faef2ba4a52))
|
|
16
|
+
* **ios:** show a partialCurl modal as coverVertical when it isn't full screen ([#16](https://github.com/apexnative/navigation/issues/16)) ([404b3be](https://github.com/apexnative/navigation/commit/404b3be98beac73069bfc16bb3903f583aa9b844))
|
|
17
|
+
* keep a transition running when one element fails, and find its elements in one walk ([#21](https://github.com/apexnative/navigation/issues/21)) ([5dd1179](https://github.com/apexnative/navigation/commit/5dd1179834211abc985ffffbd2bd9622530be1dc))
|
|
18
|
+
|
|
3
19
|
## [1.0.0-alpha.1](https://github.com/apexnative/navigation/compare/v1.0.0-alpha.0...v1.0.0-alpha.1) (2026-10-21)
|
|
4
20
|
|
|
5
21
|
|
package/README.md
CHANGED
|
@@ -1,34 +1,131 @@
|
|
|
1
1
|
<picture>
|
|
2
|
-
<source media="(prefers-color-scheme: dark)" srcset=".github/assets/navigation-banner-dark.png">
|
|
3
|
-
<img alt="@apexnative/navigation: native navigation for React Native on iOS, iPadOS and Android" src=".github/assets/navigation-banner-light.png">
|
|
2
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/apexnative/navigation/main/.github/assets/navigation-banner-dark.png">
|
|
3
|
+
<img alt="@apexnative/navigation: native navigation for React Native on iOS, iPadOS and Android" src="https://raw.githubusercontent.com/apexnative/navigation/main/.github/assets/navigation-banner-light.png">
|
|
4
4
|
</picture>
|
|
5
5
|
|
|
6
|
+
<p align="center">
|
|
7
|
+
<a href="https://www.npmjs.com/package/@apexnative/navigation"><img alt="npm version" src="https://img.shields.io/npm/v/@apexnative/navigation?label=npm&color=cb3837"></a>
|
|
8
|
+
<a href="https://www.npmjs.com/package/@apexnative/navigation"><img alt="npm downloads" src="https://img.shields.io/npm/dm/@apexnative/navigation"></a>
|
|
9
|
+
<a href="https://github.com/apexnative/navigation/actions/workflows/ci.yml"><img alt="CI" src="https://img.shields.io/github/actions/workflow/status/apexnative/navigation/ci.yml?branch=main&label=CI"></a>
|
|
10
|
+
<img alt="Platforms: iOS, iPadOS, Android" src="https://img.shields.io/badge/platforms-iOS%20%7C%20iPadOS%20%7C%20Android-informational">
|
|
11
|
+
<img alt="React Native 0.85 or newer, New Architecture" src="https://img.shields.io/badge/React%20Native-0.85%2B%20%C2%B7%20New%20Architecture-61dafb">
|
|
12
|
+
<img alt="TypeScript types included" src="https://img.shields.io/npm/types/@apexnative/navigation">
|
|
13
|
+
<a href="https://github.com/apexnative/navigation/blob/main/LICENSE"><img alt="License: MIT" src="https://img.shields.io/npm/l/@apexnative/navigation"></a>
|
|
14
|
+
</p>
|
|
15
|
+
|
|
16
|
+
<p align="center">
|
|
17
|
+
<a href="https://navigation.apexnative.dev/docs/start/introduction"><b>Documentation</b></a> ·
|
|
18
|
+
<a href="https://navigation.apexnative.dev/docs/start/quick-start">Quick start</a> ·
|
|
19
|
+
<a href="https://navigation.apexnative.dev/api/starting-the-app/createNavigation">API reference</a> ·
|
|
20
|
+
<a href="https://github.com/apexnative/navigation/releases">Releases</a>
|
|
21
|
+
</p>
|
|
22
|
+
|
|
6
23
|
# @apexnative/navigation
|
|
7
24
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
`
|
|
25
|
+
**The platform owns the navigation. React describes it.**
|
|
26
|
+
|
|
27
|
+
`@apexnative/navigation` gives a React Native app the navigation of the platform itself. Stacks, tabs, modals, side
|
|
28
|
+
menus, split views and overlays are the real containers of iOS and Android: `UINavigationController`,
|
|
29
|
+
`UITabBarController`, sheets with detents, `UISplitViewController`, the Android back stack and the Material components.
|
|
30
|
+
You describe the screens and how they are arranged in TypeScript; the native side shows them and reports back.
|
|
11
31
|
|
|
12
32
|
> **Status:** a pre-release on npm; the API can still change before 1.0. The current version is on the
|
|
13
33
|
> [releases page](https://github.com/apexnative/navigation/releases).
|
|
14
34
|
|
|
15
|
-
##
|
|
35
|
+
## Why
|
|
36
|
+
|
|
37
|
+
**The platform already knows how to navigate.** iOS and Android hold years of decisions about gestures,
|
|
38
|
+
accessibility, every screen size, memory and multitasking. A router that keeps its own model of the navigation has to
|
|
39
|
+
mirror all of that in JavaScript and catch up with every OS release. This library gives React the platform's own
|
|
40
|
+
containers instead of replacing them, so a new tab bar or toolbar of the OS arrives with its beta, not a year later.
|
|
41
|
+
|
|
42
|
+
**Native behavior, not only a native look.** The swipe back, the back button and dragging a sheet are the system's
|
|
43
|
+
own, with the same transitions, accessibility and memory use as in an app written in Swift or Kotlin. Nothing is
|
|
44
|
+
replayed into a copy of the state in React: JavaScript reads the state of the platform through events and hooks.
|
|
45
|
+
|
|
46
|
+
**Describe, don't command.** Native navigation usually speaks in JSON layouts and string ids, so a wrong screen name
|
|
47
|
+
or a missing prop shows up at run time. Here screens, props and roots are typed, and the same mistake is an error in
|
|
48
|
+
the editor.
|
|
49
|
+
|
|
50
|
+
| | A router in the React tree | Native navigation by commands | This library |
|
|
51
|
+
| ---------------------- | --------------------------------------------- | ----------------------------- | ------------------------------------------------- |
|
|
52
|
+
| **Who owns the state** | an object in the React tree | the OS | the OS |
|
|
53
|
+
| **Gestures** | native or drawn in JS, synced back into React | the system's own | the system's own |
|
|
54
|
+
| **How you navigate** | navigator components or files as URLs | JSON layouts and screen ids | `Stack`, `Tabs`, `SideMenu`; commands without ids |
|
|
55
|
+
| **Types** | typed route params | strings, props unchecked | names, props and roots checked |
|
|
56
|
+
|
|
57
|
+
## A quick look
|
|
58
|
+
|
|
59
|
+
A screen is a component with its options:
|
|
60
|
+
|
|
61
|
+
```tsx
|
|
62
|
+
import { Text } from 'react-native';
|
|
63
|
+
import { defineScreen } from '@apexnative/navigation';
|
|
64
|
+
|
|
65
|
+
function Details({ title }: { title?: string }) {
|
|
66
|
+
return <Text>{title ?? 'Details'}</Text>;
|
|
67
|
+
}
|
|
16
68
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
critical path, and we measure it in CI.
|
|
69
|
+
export const DetailsScreen = defineScreen({
|
|
70
|
+
component: Details,
|
|
71
|
+
options: (s, props) => s.title(props.title ?? 'Details'),
|
|
72
|
+
});
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
The navigation names the screens and the root:
|
|
76
|
+
|
|
77
|
+
```ts
|
|
78
|
+
import { createNavigation } from '@apexnative/navigation';
|
|
28
79
|
|
|
29
|
-
|
|
80
|
+
export const Navigation = createNavigation({
|
|
81
|
+
screens: { Home: HomeScreen, Details: DetailsScreen },
|
|
82
|
+
root: 'Home',
|
|
83
|
+
});
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Any screen opens another one with the platform's own transition:
|
|
30
87
|
|
|
31
|
-
|
|
88
|
+
```ts
|
|
89
|
+
const navigation = useNavigation();
|
|
90
|
+
navigation.push('Details', { props: { title: 'Order 42' } });
|
|
91
|
+
// navigation.push('Detail') or a prop Details doesn't take: a TypeScript error
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## What's in it
|
|
95
|
+
|
|
96
|
+
- **Containers:** native stacks, tabs, modals and sheets, side menus, split views on iPad, and overlays, nested in
|
|
97
|
+
each other: tabs of stacks, a stack in a modal, tabs in a side menu.
|
|
98
|
+
- **Screen UI as options:** the top bar, bar buttons, search, the toolbar and the floating action button, the status
|
|
99
|
+
bar and the system bars.
|
|
100
|
+
- **No safe area code:** every screen keeps its content clear of the bars, the cutout and the home indicator by itself.
|
|
101
|
+
- **Native screens:** SwiftUI, UIKit and Compose screens are pushed, presented and put in tabs the same way as React
|
|
102
|
+
screens.
|
|
103
|
+
- **One React tree when you want it:** each screen is its own React root by default; with `appProvider` all screens
|
|
104
|
+
share one tree.
|
|
105
|
+
- **The app's lifecycle:** cold start, the splash screen, deep links, notifications and switching roots on sign-in
|
|
106
|
+
and sign-out.
|
|
107
|
+
- **A CLI**, `apex-navigation`: sets up an app, checks it (`doctor`), adds screens, deep links, icons and the splash
|
|
108
|
+
screen, upgrades the navigation, migrates an app from another navigation library, and serves the commands and the
|
|
109
|
+
docs to AI coding agents (`mcp`).
|
|
110
|
+
|
|
111
|
+
## When it fits
|
|
112
|
+
|
|
113
|
+
- Apps that should look and behave like the platform, on iPhone, iPad and Android.
|
|
114
|
+
- Apps with native screens, or a team that also writes Swift or Kotlin.
|
|
115
|
+
- Apps where startup, transitions and memory matter.
|
|
116
|
+
|
|
117
|
+
It doesn't fit an app that also ships to the web, or one that draws the same fully custom UI on every platform in
|
|
118
|
+
JavaScript.
|
|
119
|
+
|
|
120
|
+
## Requirements
|
|
121
|
+
|
|
122
|
+
| | |
|
|
123
|
+
| -------------- | ------------------------------------------------------------- |
|
|
124
|
+
| React Native | 0.85 or newer, with the New Architecture (bridgeless, Fabric) |
|
|
125
|
+
| iOS and iPadOS | 15.1 or newer |
|
|
126
|
+
| Android | 7.0 or newer (minSdk 24) |
|
|
127
|
+
| Expo | not supported by `setup` yet |
|
|
128
|
+
| Web | not a target |
|
|
32
129
|
|
|
33
130
|
## Installation
|
|
34
131
|
|
|
@@ -37,41 +134,40 @@ npm install @apexnative/navigation
|
|
|
37
134
|
npx apex-navigation setup
|
|
38
135
|
```
|
|
39
136
|
|
|
40
|
-
`setup` shows what it changes in the
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
Edge-to-edge on Android follows the platform: always on Android 16+ for apps targeting SDK 36, and on
|
|
68
|
-
Android 15 for apps targeting SDK 35. With `edgeToEdgeEnabled=true` in `android/gradle.properties` (the
|
|
69
|
-
default of new React Native apps) it's on for every Android version.
|
|
137
|
+
`setup` shows what it changes in the native files and in `index.js`, asks before writing, and installs the pods on a
|
|
138
|
+
Mac. A new app starts with `npx @apexnative/navigation init MyApp`, and `npx apex-navigation doctor` checks an app
|
|
139
|
+
and the machine and fixes what it can. The [installation guide](https://navigation.apexnative.dev/docs/start/installation)
|
|
140
|
+
has the details, and the [quick start](https://navigation.apexnative.dev/docs/start/quick-start) builds a first app
|
|
141
|
+
with two screens.
|
|
142
|
+
|
|
143
|
+
Coming from another library: [React Navigation](https://navigation.apexnative.dev/docs/migration/from-react-navigation),
|
|
144
|
+
[Expo Router](https://navigation.apexnative.dev/docs/migration/from-expo-router),
|
|
145
|
+
[react-native-navigation](https://navigation.apexnative.dev/docs/migration/from-react-native-navigation).
|
|
146
|
+
|
|
147
|
+
## Principles
|
|
148
|
+
|
|
149
|
+
1. **The platform leads.** Native stacks, tabs, sheets and gestures, not a web-style router. A URL is one entry point
|
|
150
|
+
among push notifications, widgets and shortcuts.
|
|
151
|
+
2. **Native is the source of truth.** Navigation state, screen lifecycle, safe areas, keyboard and theme come from the
|
|
152
|
+
OS.
|
|
153
|
+
3. **App lifecycle is first-class.** Cold start, splash, session restore, background and process death are explicit
|
|
154
|
+
steps.
|
|
155
|
+
4. **New OS features, fast.** Support for new iOS and Android releases lands with the betas.
|
|
156
|
+
5. **Customization without losing native behavior.** Options of system components first, your own native components
|
|
157
|
+
inside native containers next, custom JS UI last.
|
|
158
|
+
6. **Performance is a requirement.** Transitions and gestures run natively; JavaScript stays off the critical path.
|
|
159
|
+
7. **Rendering and logic apart.** React renders screens. The logic of the app (a socket, a session, services) lives
|
|
160
|
+
outside the render tree: `navigation` works outside React, and `willMount` prepares the app before the first
|
|
161
|
+
screen.
|
|
162
|
+
|
|
163
|
+
When two of them pull apart, the order decides.
|
|
70
164
|
|
|
71
165
|
## Contributing
|
|
72
166
|
|
|
73
|
-
|
|
167
|
+
The [Showcase app](https://github.com/apexnative/navigation/tree/main/example) runs every feature as a scenario.
|
|
168
|
+
See [CONTRIBUTING.md](https://github.com/apexnative/navigation/blob/main/CONTRIBUTING.md) and the
|
|
169
|
+
[code of conduct](https://github.com/apexnative/navigation/blob/main/CODE_OF_CONDUCT.md).
|
|
74
170
|
|
|
75
171
|
## License
|
|
76
172
|
|
|
77
|
-
[MIT](LICENSE)
|
|
173
|
+
[MIT](https://github.com/apexnative/navigation/blob/main/LICENSE)
|
package/android/CLAUDE.md
CHANGED
|
@@ -23,8 +23,8 @@ top-level folder. Main thread only, unless a line says otherwise.
|
|
|
23
23
|
- `react/`: the way in and out: `NavigationTurboModule`, `CommandRunner`, `CommandAnswer`, `ErrorTexts`,
|
|
24
24
|
`OptionsCommands`, `ReloadCoordinator`, `ReactGateway`, `ReactView` (a React root), the stores (`ScreenInsets`,
|
|
25
25
|
`ScreenRegions`, `ArrangementStates`, `Hinge`, `StartInfo`), `LaunchLink`, `ScrollEvents`.
|
|
26
|
-
- `react/events/`: `AppearanceDispatcher`, `ScreenAppearance`, `EventEmitter`, `
|
|
27
|
-
`ComponentType`.
|
|
26
|
+
- `react/events/`: `AppearanceDispatcher`, `ScreenAppearance`, `EventEmitter`, `EventContract` (made by
|
|
27
|
+
`scripts/contract.mjs`), `NavigationStateReporter`, `ComponentType`.
|
|
28
28
|
- `viewcontrollers/viewcontroller/`: `ViewController` (the base of every node), `Presenter`, `IReactView` (what a
|
|
29
29
|
screen shows), `ScrollEventListener`.
|
|
30
30
|
- `viewcontrollers/child/`, `parent/`: `ChildController` and the registry; `ParentController` (the base of layouts),
|
|
@@ -104,7 +104,7 @@ top-level folder. Main thread only, unless a line says otherwise.
|
|
|
104
104
|
`NavigationStateReporter.kt`; `react/CommandAnswer.kt`.
|
|
105
105
|
- Options (parsing, merging): `options/Options.kt`, `OptionsReader.kt`, `OptionsCache.kt`, `DefaultOptions.kt`,
|
|
106
106
|
`params/`, `parsers/`; `viewcontrollers/viewcontroller/ViewController.kt` (resolution).
|
|
107
|
-
- Error texts: `react/ErrorTexts.kt`.
|
|
107
|
+
- Error texts: `react/ErrorTexts.kt`, made by `scripts/contract.mjs` from `src/interfaces/contract/errors.json`.
|
|
108
108
|
- Links: `react/LaunchLink.kt`, `NavigationActivity.kt` (`onNewIntent`); `react/StartInfo.kt`.
|
|
109
109
|
- Hinge and regions: `react/Hinge.kt`, `react/ScreenRegions.kt`, `react/ScreenInsets.kt`, `utils/WindowFolds.kt`.
|
|
110
110
|
- Native screens: `nativescreen/NativeScreenContext.kt`, `NativeScreenCalls.kt`, `NativeScreenCreator.kt`;
|
|
@@ -144,7 +144,11 @@ top-level folder. Main thread only, unless a line says otherwise.
|
|
|
144
144
|
## Pitfalls
|
|
145
145
|
|
|
146
146
|
- Native code never touches React Native's views (their children, their layout): Fabric owns and recycles them. Move
|
|
147
|
-
content through `SlotContainers` and `SlotView`, and find views by `nativeID` only through React Native's own search
|
|
147
|
+
content through `SlotContainers` and `SlotView`, and find views by `nativeID` only through React Native's own search
|
|
148
|
+
or its tag (`ReactR.id.view_tag_native_id`); a transition's elements in one walk per screen (`TransitionViews`).
|
|
149
|
+
- Each step of a transition's element (its move into the layer, its animator, a flight's start and frames in
|
|
150
|
+
`MoveAnimator`) runs in `elementStep`: an exception skips that element only. A new step of an element goes there
|
|
151
|
+
too, or one bad element takes the whole transition down.
|
|
148
152
|
- Every change of a stack's children goes through its `StackQueue`; a direct change of `shown` or of the container
|
|
149
153
|
skips the table that decides interrupts and answers.
|
|
150
154
|
- Every event of an appearance goes through `AppearanceDispatcher`. A container with its own loop of
|
|
@@ -166,6 +170,8 @@ top-level folder. Main thread only, unless a line says otherwise.
|
|
|
166
170
|
- Only `SystemUiWindow` writes to the window. A presenter that changes a bar option calls `SystemUiOwner.invalidate`.
|
|
167
171
|
- The core names a module container only in `LayoutFactory`; elsewhere use a capability or `visibleChildren()`.
|
|
168
172
|
- A hidden stack keeps a `mergeOptions` per entry; applying it for the wrong entry paints the wrong screen's bar.
|
|
173
|
+
- A `mergeOptions` moves or resizes only by what the merge names: the resolved options still hold the values of the
|
|
174
|
+
show, and acting on them undoes what the user did since (a dragged sheet snapped back to `selectedDetent`).
|
|
169
175
|
- A sync getter reads a store, never a node; the store is written on main and cleared with its node.
|
|
170
176
|
- A re-apply of the large title (a pop back, a light/dark switch, `mergeOptions`) collapses it to the list's last
|
|
171
177
|
offset that `ScrollEventListener.lastOffset` keeps, never to 0, or it comes back expanded over a scrolled list.
|
package/android/CORE.md
CHANGED
|
@@ -165,7 +165,10 @@ Each rule has one owner. Change the owner, not a caller; if a new feature needs
|
|
|
165
165
|
`invokeDefaultOnBackPressed` → `Navigator.handleBack` → `ModalCoordinator.handleBack` → the root's
|
|
166
166
|
`ParentController.handleBack` → `StackController.handleBack` → `StackQueue.cut`. `BackCallbackState` keeps the
|
|
167
167
|
system callback enabled only while back has something to do.
|
|
168
|
-
- **Error texts.** `ErrorTexts`: one function per text, the same words as iOS
|
|
168
|
+
- **Error texts.** `ErrorTexts`: one function per text, the same words as iOS, made with iOS's from
|
|
169
|
+
`src/interfaces/contract/errors.json` (`scripts/contract.mjs`). JavaScript adds `[navigation] <command>:`.
|
|
170
|
+
- **Event names and keys.** `EventContract`, made from the event types of `src/interfaces/`; `EventEmitter` and the
|
|
171
|
+
builders of payloads use it, never a string.
|
|
169
172
|
- **Room for the bars.** A screen's room at the top and bottom is `ScreenFrame` with `ScreenSafeArea` and the
|
|
170
173
|
`ParentController` room hooks (`getTopInset`, `keepsStatusBarRoomOf`, …). A screen in a pair forwards its room to
|
|
171
174
|
the pair (`ScreenFrame.applyRoom`), and the pair's `getTopInset` uses the screens' rule (`ScreenFrame.topRoomOf`)
|
package/android/RECIPES.md
CHANGED
|
@@ -29,11 +29,12 @@ The steps for adding an option and for adding a command to the Android core; rea
|
|
|
29
29
|
3. The body goes to the owner of the area: `Navigator` (root, modals, overlays), `StackCommands` (stacks), a
|
|
30
30
|
coordinator. A stack operation is a `StackQueue` operation.
|
|
31
31
|
4. New layout fields are read and checked in `LayoutReader`; checks against what shows are in `LayoutChecks` or in the
|
|
32
|
-
command, before any node is made. Every rejection text is a function in `ErrorTexts` (same words as iOS)
|
|
32
|
+
command, before any node is made. Every rejection text is a function in `ErrorTexts` (same words as iOS),
|
|
33
|
+
made by `node scripts/contract.mjs` from `src/interfaces/contract/errors.json`: a new text goes there.
|
|
33
34
|
5. The answer goes through `CommandAnswer`: add the name to `COMPLETED_COMMANDS` when the command changes what shows
|
|
34
35
|
(then the state, the promise and `CommandCompleted` follow in that order). A transition ends through
|
|
35
36
|
`AppearanceDispatcher.endTransition`.
|
|
36
37
|
6. Tests: `CommandRunnerTest`, `CommandAnswerTest`, `ErrorTextsTest`, `LayoutReaderTest`; a stack operation also needs a
|
|
37
38
|
case in the queue tests (`StackQueueHarness`).
|
|
38
|
-
7. A new event:
|
|
39
|
-
`AppearanceDispatcher.Sender`.
|
|
39
|
+
7. A new event: the payload type in `src/interfaces`, then `node scripts/contract.mjs` for its name and keys in
|
|
40
|
+
`EventContract`; `EventEmitter` (`emit…`) with those, and for an appearance event the `AppearanceDispatcher.Sender`.
|
|
@@ -4,6 +4,7 @@ import android.content.Context
|
|
|
4
4
|
import com.facebook.react.ReactApplication
|
|
5
5
|
import com.facebook.react.bridge.Arguments
|
|
6
6
|
import com.facebook.react.bridge.WritableMap
|
|
7
|
+
import dev.apexnative.navigation.react.events.EventContract
|
|
7
8
|
import dev.apexnative.navigation.react.events.EventEmitter
|
|
8
9
|
import java.util.concurrent.ConcurrentHashMap
|
|
9
10
|
|
|
@@ -33,9 +34,9 @@ internal object ArrangementStates {
|
|
|
33
34
|
}
|
|
34
35
|
|
|
35
36
|
private fun toMap(state: State): WritableMap = Arguments.createMap().apply {
|
|
36
|
-
putString(
|
|
37
|
-
putString(
|
|
38
|
-
putBoolean(
|
|
39
|
-
putBoolean(
|
|
37
|
+
putString(EventContract.ArrangementChanged.PLACEMENT, state.placement)
|
|
38
|
+
putString(EventContract.ArrangementChanged.LAYOUT, state.layout)
|
|
39
|
+
putBoolean(EventContract.ArrangementChanged.FRONT, state.front)
|
|
40
|
+
putBoolean(EventContract.ArrangementChanged.HIDDEN, state.hidden)
|
|
40
41
|
}
|
|
41
42
|
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
// Made by scripts/contract.mjs from src/interfaces/contract/errors.json: change that file and run the script again.
|
|
2
|
+
|
|
1
3
|
package dev.apexnative.navigation.react
|
|
2
4
|
|
|
3
5
|
/**
|
|
@@ -6,116 +8,129 @@ package dev.apexnative.navigation.react
|
|
|
6
8
|
* <command>:`.
|
|
7
9
|
*/
|
|
8
10
|
internal object ErrorTexts {
|
|
9
|
-
|
|
11
|
+
// Where the screen is.
|
|
12
|
+
|
|
13
|
+
fun screenNotShown(componentId: String) = "the screen '$componentId' is not shown"
|
|
10
14
|
|
|
11
|
-
fun notInStack(
|
|
15
|
+
fun notInStack(componentId: String) = "the screen '$componentId' is not in a stack"
|
|
12
16
|
|
|
13
|
-
fun firstScreenOfStack(
|
|
17
|
+
fun firstScreenOfStack(componentId: String) = "the screen '$componentId' is the first screen of its stack"
|
|
14
18
|
|
|
15
|
-
fun notInThisStack(
|
|
19
|
+
fun notInThisStack(componentId: String) = "the screen '$componentId' is not in this stack"
|
|
16
20
|
|
|
17
|
-
fun notInModal(
|
|
21
|
+
fun notInModal(componentId: String) = "the screen '$componentId' is not in a modal"
|
|
18
22
|
|
|
19
|
-
fun notInOverlay(
|
|
23
|
+
fun notInOverlay(componentId: String) = "the screen '$componentId' is not in an overlay"
|
|
20
24
|
|
|
21
|
-
fun notInSplitView(
|
|
25
|
+
fun notInSplitView(componentId: String) = "the screen '$componentId' is not in a split view"
|
|
22
26
|
|
|
23
|
-
fun neitherSplitViewNorStack(
|
|
27
|
+
fun neitherSplitViewNorStack(componentId: String) =
|
|
28
|
+
"the screen '$componentId' is neither in a split view nor in a stack"
|
|
24
29
|
|
|
25
|
-
fun noSuchColumn(
|
|
30
|
+
fun noSuchColumn(componentId: String, column: String) =
|
|
31
|
+
"the split view of the screen '$componentId' has no '$column' column"
|
|
26
32
|
|
|
27
|
-
fun columnIsStack(
|
|
28
|
-
"the '$column' column of the split view of the screen '$
|
|
33
|
+
fun columnIsStack(componentId: String, column: String) =
|
|
34
|
+
"the '$column' column of the split view of the screen '$componentId' is a stack: it takes a screen, not a stack"
|
|
29
35
|
|
|
30
|
-
fun columnNotStack(
|
|
31
|
-
"the '$column' column of the split view of the screen '$
|
|
36
|
+
fun columnNotStack(componentId: String, column: String) =
|
|
37
|
+
"the '$column' column of the split view of the screen '$componentId' is not a stack"
|
|
32
38
|
|
|
33
|
-
fun noScreensForStack(
|
|
39
|
+
fun noScreensForStack(componentId: String) = "there are no screens to put in the stack of the screen '$componentId'"
|
|
34
40
|
|
|
35
|
-
fun alreadyShown(
|
|
41
|
+
fun alreadyShown(componentId: String) = "the screen '$componentId' shows already: an id goes to one screen"
|
|
36
42
|
|
|
37
|
-
|
|
38
|
-
"setRoot was replaced by a newer setRoot before the root '$id' finished showing"
|
|
43
|
+
// Root, modals, the app.
|
|
39
44
|
|
|
40
|
-
fun
|
|
45
|
+
fun setRootReplaced(rootId: String) =
|
|
46
|
+
"setRoot was replaced by a newer setRoot before the root '$rootId' finished showing"
|
|
41
47
|
|
|
42
|
-
fun
|
|
48
|
+
fun rootDestroyed(rootId: String) = "the root '$rootId' was destroyed before it showed"
|
|
49
|
+
|
|
50
|
+
fun modalWithoutRoot(componentId: String) = "the modal '$componentId' can't be dismissed: there is no root under it"
|
|
43
51
|
|
|
44
52
|
fun nowhereToShow() = "the navigation has nowhere to show screens: the app has no activity or window"
|
|
45
53
|
|
|
54
|
+
// Native screens.
|
|
55
|
+
|
|
46
56
|
fun nativeScreenNotRegistered(name: String) = "no native screen is registered as '$name'"
|
|
47
57
|
|
|
48
58
|
fun nativeScreenNotCreated(name: String) =
|
|
49
59
|
"the native screen '$name' could not be created: its creator returned nothing"
|
|
50
60
|
|
|
51
|
-
|
|
61
|
+
/** The error of a call of a native screen whose JavaScript reloaded before it answered. */
|
|
62
|
+
fun nativeCallReloaded(name: String, componentId: String) =
|
|
63
|
+
"JavaScript reloaded before the call '$name' of the screen '$componentId' was answered"
|
|
52
64
|
|
|
53
|
-
|
|
65
|
+
/** The error of a call of a native screen while no JavaScript can answer it. */
|
|
66
|
+
fun nativeCallNotRunning(name: String, componentId: String) =
|
|
67
|
+
"the navigation is not running: the call '$name' of the screen '$componentId' can't be answered"
|
|
54
68
|
|
|
55
|
-
|
|
56
|
-
"the side menu '$id' has a child of type '$type': it takes SideMenuLeft, SideMenuCenter and SideMenuRight"
|
|
69
|
+
// The layout.
|
|
57
70
|
|
|
58
|
-
fun
|
|
71
|
+
fun unknownNodeType(type: String) = "the layout has a node of unknown type '$type'"
|
|
59
72
|
|
|
60
|
-
fun
|
|
73
|
+
fun nodeWithoutId(type: String) = "the layout has a node of type '$type' without an id"
|
|
61
74
|
|
|
62
|
-
fun
|
|
75
|
+
fun sideMenuChild(menuId: String, type: String) =
|
|
76
|
+
"the side menu '$menuId' has a child of type '$type': it takes SideMenuLeft, SideMenuCenter and SideMenuRight"
|
|
63
77
|
|
|
64
|
-
fun
|
|
78
|
+
fun stackTakesScreens(stackId: String, type: String) = "the stack '$stackId' takes screens, not a '$type'"
|
|
65
79
|
|
|
66
|
-
fun
|
|
80
|
+
fun emptyStack(stackId: String) = "the stack '$stackId' has no screens"
|
|
67
81
|
|
|
68
|
-
fun
|
|
82
|
+
fun emptyTabs(tabsId: String) = "the tabs '$tabsId' have no tabs"
|
|
69
83
|
|
|
70
|
-
|
|
84
|
+
/** Only Android checks it: its tab bar shows at most 5 tabs. */
|
|
85
|
+
fun tooManyTabs(tabsId: String, count: Int) = "the tabs '$tabsId' have $count tabs: Android shows at most 5"
|
|
71
86
|
|
|
72
|
-
fun
|
|
73
|
-
"the
|
|
87
|
+
fun splitViewColumns(splitViewId: String) =
|
|
88
|
+
"the split view '$splitViewId' needs its primary and its secondary column"
|
|
74
89
|
|
|
75
|
-
/**
|
|
76
|
-
fun
|
|
77
|
-
"the option '$path' of the default options has a value the navigation can't read"
|
|
90
|
+
/** Only iOS checks it: Android has pairs on every version. */
|
|
91
|
+
fun pairUnsupported(pairId: String) = "the pair '$pairId' needs iOS 27.1 and an app built with its SDK"
|
|
78
92
|
|
|
79
|
-
fun
|
|
93
|
+
fun pairNeedsTwoScreens(pairId: String) = "the pair '$pairId' needs two screens: its primary and its secondary"
|
|
80
94
|
|
|
81
|
-
|
|
82
|
-
fun couldNot(what: String, reason: String) = "could not $what: $reason"
|
|
95
|
+
fun nodeWithoutName(type: String, nodeId: String) = "the layout has a node '$nodeId' of type '$type' without a name"
|
|
83
96
|
|
|
84
|
-
fun
|
|
85
|
-
"JavaScript reloaded before the call '$name' of the screen '$id' was answered"
|
|
97
|
+
fun rootMissing() = "the layout of setRoot has no root"
|
|
86
98
|
|
|
87
|
-
fun
|
|
88
|
-
"the navigation is not running: the call '$name' of the screen '$id' can't be answered"
|
|
99
|
+
fun tabsInTabs(tabsId: String) = "the tabs '$tabsId' have a tab of type 'BottomTabs': tabs don't go inside tabs"
|
|
89
100
|
|
|
90
|
-
|
|
91
|
-
|
|
101
|
+
fun sideMenuSides(menuId: String) =
|
|
102
|
+
"the side menu '$menuId' takes one SideMenuCenter and at most one SideMenuLeft and one SideMenuRight"
|
|
92
103
|
|
|
93
|
-
|
|
94
|
-
|
|
104
|
+
fun sideChildCount(sideId: String, menuId: String) =
|
|
105
|
+
"the side '$sideId' of the side menu '$menuId' needs exactly one child"
|
|
95
106
|
|
|
96
|
-
|
|
107
|
+
fun sideMenuInSide(menuId: String) =
|
|
108
|
+
"the side menu '$menuId' has a side menu in a side: a side menu doesn't go inside a side menu"
|
|
97
109
|
|
|
98
|
-
fun
|
|
110
|
+
fun sideOutsideMenu(nodeId: String, type: String) =
|
|
111
|
+
"the layout has a node '$nodeId' of type '$type' outside a side menu"
|
|
99
112
|
|
|
100
|
-
fun
|
|
113
|
+
fun splitViewInColumn(splitViewId: String) =
|
|
114
|
+
"the split view '$splitViewId' has a column of type 'SplitView': a split view doesn't go inside a split view"
|
|
101
115
|
|
|
102
|
-
fun
|
|
116
|
+
fun idGivenTwice(nodeId: String) = "the layout gives the id '$nodeId' to two nodes"
|
|
103
117
|
|
|
104
|
-
|
|
105
|
-
"the side menu '$id' takes one SideMenuCenter and at most one SideMenuLeft and one SideMenuRight"
|
|
118
|
+
// Options, previews, any other failure.
|
|
106
119
|
|
|
107
|
-
fun
|
|
108
|
-
"the
|
|
120
|
+
fun unreadableOption(path: String, componentId: String) =
|
|
121
|
+
"the option '$path' of the screen '$componentId' has a value the navigation can't read"
|
|
109
122
|
|
|
110
|
-
|
|
111
|
-
|
|
123
|
+
/** An unreadable value of `setDefaultOptions`: the default options belong to no screen. */
|
|
124
|
+
fun unreadableDefaultOption(path: String) =
|
|
125
|
+
"the option '$path' of the default options has a value the navigation can't read"
|
|
112
126
|
|
|
113
|
-
fun
|
|
127
|
+
fun previewGone(previewId: String) = "the preview '$previewId' went without being opened"
|
|
114
128
|
|
|
115
|
-
|
|
116
|
-
|
|
129
|
+
/** Any other failure: what could not be done and why, `could not <what>: <reason>`. */
|
|
130
|
+
fun couldNot(what: String, reason: String) = "could not $what: $reason"
|
|
117
131
|
|
|
118
|
-
|
|
132
|
+
/** A command whose part of the native core is not written yet. */
|
|
133
|
+
fun notDoneYet(method: String) = "could not run '$method': the native core doesn't do it yet"
|
|
119
134
|
|
|
120
135
|
/** The reason of [couldNot] for an exception: its message, else the name of its class. */
|
|
121
136
|
fun reasonOf(e: Throwable): String =
|
|
@@ -12,6 +12,7 @@ import androidx.lifecycle.LifecycleOwner
|
|
|
12
12
|
import com.facebook.react.ReactApplication
|
|
13
13
|
import com.facebook.react.bridge.Arguments
|
|
14
14
|
import com.facebook.react.bridge.WritableMap
|
|
15
|
+
import dev.apexnative.navigation.react.events.EventContract
|
|
15
16
|
import dev.apexnative.navigation.react.events.EventEmitter
|
|
16
17
|
import dev.apexnative.navigation.utils.WindowFolds
|
|
17
18
|
import java.lang.ref.WeakReference
|
|
@@ -148,7 +149,7 @@ internal object Hinge {
|
|
|
148
149
|
|
|
149
150
|
private fun toMap(value: Value?): WritableMap = Arguments.createMap().apply {
|
|
150
151
|
if (value == null) return@apply
|
|
151
|
-
putString(
|
|
152
|
-
value.angle?.let { putDouble(
|
|
152
|
+
putString(EventContract.HingeChanged.STATE, value.state)
|
|
153
|
+
value.angle?.let { putDouble(EventContract.HingeChanged.ANGLE, it.toDouble()) }
|
|
153
154
|
}
|
|
154
155
|
}
|
|
@@ -73,7 +73,7 @@ internal class OptionsCommands(
|
|
|
73
73
|
prepare = { OptionsReader.read(options, context()) },
|
|
74
74
|
) { read ->
|
|
75
75
|
if (!runner.hasActivity()) {
|
|
76
|
-
runner.logFailure(MERGE_OPTIONS, ErrorTexts.
|
|
76
|
+
runner.logFailure(MERGE_OPTIONS, ErrorTexts.nowhereToShow())
|
|
77
77
|
return@runVoid
|
|
78
78
|
}
|
|
79
79
|
val node = findNode(componentId) ?: return@runVoid
|
|
@@ -4,6 +4,7 @@ import android.content.Context
|
|
|
4
4
|
import com.facebook.react.ReactApplication
|
|
5
5
|
import com.facebook.react.bridge.Arguments
|
|
6
6
|
import com.facebook.react.bridge.WritableMap
|
|
7
|
+
import dev.apexnative.navigation.react.events.EventContract
|
|
7
8
|
import dev.apexnative.navigation.react.events.EventEmitter
|
|
8
9
|
import java.util.concurrent.ConcurrentHashMap
|
|
9
10
|
|
|
@@ -61,9 +62,9 @@ object ScreenInsets {
|
|
|
61
62
|
}
|
|
62
63
|
|
|
63
64
|
private fun toMap(value: Insets): WritableMap = Arguments.createMap().apply {
|
|
64
|
-
putDouble(
|
|
65
|
-
putDouble(
|
|
66
|
-
putDouble(
|
|
67
|
-
putDouble(
|
|
65
|
+
putDouble(EventContract.ScreenInsetsChanged.TOP, value.top)
|
|
66
|
+
putDouble(EventContract.ScreenInsetsChanged.RIGHT, value.right)
|
|
67
|
+
putDouble(EventContract.ScreenInsetsChanged.BOTTOM, value.bottom)
|
|
68
|
+
putDouble(EventContract.ScreenInsetsChanged.LEFT, value.left)
|
|
68
69
|
}
|
|
69
70
|
}
|