@react-navigation/lynx 0.3.0-canary-20260907-96fbfef4 → 0.3.0
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/README.md +187 -0
- package/package.json +1 -1
package/README.md
ADDED
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
# `@react-navigation/lynx`
|
|
2
|
+
|
|
3
|
+
[React Navigation](https://reactnavigation.org) for [Lynx](https://lynxjs.org).
|
|
4
|
+
|
|
5
|
+
This is the platform layer, the same role `@react-navigation/native` plays for
|
|
6
|
+
React Native: it owns what is Lynx-specific, re-exports
|
|
7
|
+
`@react-navigation/core` so an app has one import surface, and navigators sit
|
|
8
|
+
on top of it.
|
|
9
|
+
|
|
10
|
+
## Installation
|
|
11
|
+
|
|
12
|
+
```sh
|
|
13
|
+
npm install @react-navigation/lynx lynx-screens
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
`@lynx-js/react` and `lynx-screens` are peer dependencies. The stack navigator
|
|
17
|
+
renders through `lynx-screens`, so its native side has to be linked into the
|
|
18
|
+
host app as well.
|
|
19
|
+
|
|
20
|
+
`react` has to resolve to a ReactLynx-compatible runtime, since
|
|
21
|
+
`@react-navigation/core` imports hooks React Native provides:
|
|
22
|
+
|
|
23
|
+
```js
|
|
24
|
+
// lynx.config.js
|
|
25
|
+
resolve: {
|
|
26
|
+
alias: {
|
|
27
|
+
react$: require.resolve('@react-navigation/lynx/react-compat'),
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Usage
|
|
33
|
+
|
|
34
|
+
```tsx
|
|
35
|
+
import { createStaticNavigation } from '@react-navigation/lynx';
|
|
36
|
+
import { createLynxStackNavigator } from '@react-navigation/lynx/stack';
|
|
37
|
+
|
|
38
|
+
const Stack = createLynxStackNavigator({
|
|
39
|
+
initialRouteName: 'Home',
|
|
40
|
+
screens: {
|
|
41
|
+
Home: HomeScreen,
|
|
42
|
+
Detail: DetailScreen,
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
const Navigation = createStaticNavigation(Stack);
|
|
47
|
+
|
|
48
|
+
export function App() {
|
|
49
|
+
return (
|
|
50
|
+
<page>
|
|
51
|
+
<Navigation />
|
|
52
|
+
</page>
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
`NavigationContainer` is exported too, for the dynamic API.
|
|
58
|
+
|
|
59
|
+
### Screen options
|
|
60
|
+
|
|
61
|
+
```tsx
|
|
62
|
+
const Stack = createLynxStackNavigator({
|
|
63
|
+
screens: {
|
|
64
|
+
Sheet: {
|
|
65
|
+
screen: SheetScreen,
|
|
66
|
+
options: {
|
|
67
|
+
presentation: 'formSheet',
|
|
68
|
+
contentStyle: { backgroundColor: '#fff' },
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
});
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
`presentation` is `'card'` or `'formSheet'`. The navigator also emits
|
|
76
|
+
`transitionStart` and `transitionEnd`, each carrying `{ closing }`.
|
|
77
|
+
|
|
78
|
+
To stop a screen from being dismissed, use core's `usePreventRemove`:
|
|
79
|
+
|
|
80
|
+
```tsx
|
|
81
|
+
usePreventRemove(hasUnsavedChanges, ({ data }) => {
|
|
82
|
+
confirm(() => navigation.dispatch(data.action));
|
|
83
|
+
});
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Deep linking
|
|
87
|
+
|
|
88
|
+
Path handling is `@react-navigation/core` unchanged: `config.screens` maps
|
|
89
|
+
paths onto route names and params, exactly as on React Native.
|
|
90
|
+
|
|
91
|
+
```tsx
|
|
92
|
+
import type { LinkingOptions } from '@react-navigation/lynx';
|
|
93
|
+
|
|
94
|
+
const linking: LinkingOptions<ParamList> = {
|
|
95
|
+
config: {
|
|
96
|
+
screens: {
|
|
97
|
+
Home: '',
|
|
98
|
+
Detail: 'detail/:id',
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
<Navigation linking={linking} />;
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
What is Lynx-specific is where the URL comes from. A card is not the process
|
|
107
|
+
that receives a link — the host app is — so the host has to hand it over.
|
|
108
|
+
|
|
109
|
+
**On a cold start**, the route rides in on `initData`, which the card can read
|
|
110
|
+
before any listener exists:
|
|
111
|
+
|
|
112
|
+
```kotlin
|
|
113
|
+
// Android
|
|
114
|
+
TemplateData.fromMap(mapOf(
|
|
115
|
+
"__navigation" to mapOf(
|
|
116
|
+
"route" to "/detail/42",
|
|
117
|
+
"nonce" to System.currentTimeMillis(),
|
|
118
|
+
),
|
|
119
|
+
))
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
```swift
|
|
123
|
+
// iOS
|
|
124
|
+
LynxTemplateData(dictionary: [
|
|
125
|
+
"__navigation": ["route": "/detail/42", "nonce": Date().timeIntervalSince1970],
|
|
126
|
+
])
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
`nonce` is required. `initData` is state, not an event: navigating to the same
|
|
130
|
+
route twice would leave the value untouched and the second one would be
|
|
131
|
+
dropped. Anything that changes per navigation works.
|
|
132
|
+
|
|
133
|
+
**Later routes** arrive as a global event carrying `{ url }`:
|
|
134
|
+
|
|
135
|
+
```kotlin
|
|
136
|
+
lynxView.sendGlobalEvent(
|
|
137
|
+
"reactnavigation.url",
|
|
138
|
+
JavaOnlyArray.of(JavaOnlyMap.from(mapOf("url" to "/detail/42"))),
|
|
139
|
+
)
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
```swift
|
|
143
|
+
lynxView.sendGlobalEvent("reactnavigation.url", withParams: [["url": "/detail/42"]])
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
The name is namespaced on purpose. React Native can afford the bare `url`
|
|
147
|
+
because `RCTLinkingManager` is its own emitter; Lynx's `GlobalEventEmitter` is
|
|
148
|
+
one namespace shared by the whole card and its host. A bare `url` event is
|
|
149
|
+
still accepted, so a host already emitting React Native's works unchanged.
|
|
150
|
+
|
|
151
|
+
The two channels are not interchangeable. A cold start cannot use the event —
|
|
152
|
+
no listener exists yet — and a warm one should not use `initData`, because
|
|
153
|
+
without a changing nonce a repeat route is invisible. A host that does both
|
|
154
|
+
will navigate twice.
|
|
155
|
+
|
|
156
|
+
The route may be a bare path (`/detail/42`), or a full URL if the app sets
|
|
157
|
+
`prefixes`. Prefix stripping matches React Native, wildcards included:
|
|
158
|
+
|
|
159
|
+
```tsx
|
|
160
|
+
const linking: LinkingOptions<ParamList> = {
|
|
161
|
+
prefixes: ['myapp://', 'https://*.example.com'],
|
|
162
|
+
config: { screens: { Detail: 'detail/:id' } },
|
|
163
|
+
};
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
`getInitialURL`, `subscribe`, `getStateFromPath`, `getActionFromState` and
|
|
167
|
+
`filter` are all replaceable, the same as on React Native.
|
|
168
|
+
|
|
169
|
+
### Reading it directly
|
|
170
|
+
|
|
171
|
+
`getInitialURL()` returns the launch route and is **synchronous** — React
|
|
172
|
+
Native has to await `Linking.getInitialURL()`, but on Lynx `initData` is
|
|
173
|
+
already in hand when the card starts, so the first render lands on the right
|
|
174
|
+
route instead of on the fallback.
|
|
175
|
+
|
|
176
|
+
```tsx
|
|
177
|
+
import { getInitialURL, subscribe } from '@react-navigation/lynx';
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
## Differences from `@react-navigation/native`
|
|
181
|
+
|
|
182
|
+
- No `Link`, `useLinkTo`, `useLinkBuilder` or `useRoutePath` yet: only the
|
|
183
|
+
URL-to-navigation direction is implemented.
|
|
184
|
+
- No state persistence, and no container-level back-button handling — the
|
|
185
|
+
native stack owns dismissal per screen, so a container handler would fight
|
|
186
|
+
with it.
|
|
187
|
+
- `getInitialURL` is synchronous, as above.
|