@react-navigation/native 7.1.26 → 8.0.0-alpha.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.
Files changed (78) hide show
  1. package/lib/module/Link.js.map +1 -1
  2. package/lib/module/NavigationContainer.js +73 -63
  3. package/lib/module/NavigationContainer.js.map +1 -1
  4. package/lib/module/__stubs__/createStackNavigator.js.map +1 -1
  5. package/lib/module/extractPathFromURL.js +12 -7
  6. package/lib/module/extractPathFromURL.js.map +1 -1
  7. package/lib/module/getStateFromHref.js +30 -0
  8. package/lib/module/getStateFromHref.js.map +1 -0
  9. package/lib/module/index.js +0 -1
  10. package/lib/module/index.js.map +1 -1
  11. package/lib/module/theming/DarkTheme.js.map +1 -1
  12. package/lib/module/theming/DefaultTheme.js +1 -1
  13. package/lib/module/theming/DefaultTheme.js.map +1 -1
  14. package/lib/module/theming/fonts.js.map +1 -1
  15. package/lib/module/useLinkBuilder.js +5 -8
  16. package/lib/module/useLinkBuilder.js.map +1 -1
  17. package/lib/module/useLinkProps.js +20 -0
  18. package/lib/module/useLinkProps.js.map +1 -1
  19. package/lib/module/useLinking.js +23 -24
  20. package/lib/module/useLinking.js.map +1 -1
  21. package/lib/module/useLinking.native.js +21 -36
  22. package/lib/module/useLinking.native.js.map +1 -1
  23. package/lib/module/useScrollToTop.js +1 -0
  24. package/lib/module/useScrollToTop.js.map +1 -1
  25. package/lib/module/useThenable.js +9 -6
  26. package/lib/module/useThenable.js.map +1 -1
  27. package/lib/typescript/src/Link.d.ts +3 -2
  28. package/lib/typescript/src/Link.d.ts.map +1 -1
  29. package/lib/typescript/src/NavigationContainer.d.ts +35 -7
  30. package/lib/typescript/src/NavigationContainer.d.ts.map +1 -1
  31. package/lib/typescript/src/__stubs__/createStackNavigator.d.ts +1 -2
  32. package/lib/typescript/src/__stubs__/createStackNavigator.d.ts.map +1 -1
  33. package/lib/typescript/src/extractPathFromURL.d.ts +2 -1
  34. package/lib/typescript/src/extractPathFromURL.d.ts.map +1 -1
  35. package/lib/typescript/src/getStateFromHref.d.ts +4 -0
  36. package/lib/typescript/src/getStateFromHref.d.ts.map +1 -0
  37. package/lib/typescript/src/index.d.ts +0 -1
  38. package/lib/typescript/src/index.d.ts.map +1 -1
  39. package/lib/typescript/src/theming/DarkTheme.d.ts +63 -2
  40. package/lib/typescript/src/theming/DarkTheme.d.ts.map +1 -1
  41. package/lib/typescript/src/theming/DefaultTheme.d.ts +63 -2
  42. package/lib/typescript/src/theming/DefaultTheme.d.ts.map +1 -1
  43. package/lib/typescript/src/theming/fonts.d.ts.map +1 -1
  44. package/lib/typescript/src/types.d.ts +49 -23
  45. package/lib/typescript/src/types.d.ts.map +1 -1
  46. package/lib/typescript/src/useLinkBuilder.d.ts +14 -6
  47. package/lib/typescript/src/useLinkBuilder.d.ts.map +1 -1
  48. package/lib/typescript/src/useLinkProps.d.ts +3 -3
  49. package/lib/typescript/src/useLinkProps.d.ts.map +1 -1
  50. package/lib/typescript/src/useLinking.d.ts +48 -4
  51. package/lib/typescript/src/useLinking.d.ts.map +1 -1
  52. package/lib/typescript/src/useLinking.native.d.ts +69 -4
  53. package/lib/typescript/src/useLinking.native.d.ts.map +1 -1
  54. package/lib/typescript/src/useScrollToTop.d.ts.map +1 -1
  55. package/lib/typescript/src/useThenable.d.ts +4 -1
  56. package/lib/typescript/src/useThenable.d.ts.map +1 -1
  57. package/package.json +8 -8
  58. package/src/Link.tsx +18 -15
  59. package/src/NavigationContainer.tsx +141 -84
  60. package/src/__stubs__/createStackNavigator.tsx +0 -2
  61. package/src/extractPathFromURL.tsx +28 -18
  62. package/src/getStateFromHref.tsx +46 -0
  63. package/src/index.tsx +0 -1
  64. package/src/theming/DarkTheme.tsx +4 -3
  65. package/src/theming/DefaultTheme.tsx +5 -4
  66. package/src/theming/fonts.tsx +1 -2
  67. package/src/types.tsx +60 -22
  68. package/src/useLinkBuilder.tsx +6 -9
  69. package/src/useLinkProps.tsx +36 -7
  70. package/src/useLinking.native.tsx +25 -48
  71. package/src/useLinking.tsx +32 -38
  72. package/src/useScrollToTop.tsx +3 -1
  73. package/src/useThenable.tsx +14 -6
  74. package/lib/module/UnhandledLinkingContext.js +0 -14
  75. package/lib/module/UnhandledLinkingContext.js.map +0 -1
  76. package/lib/typescript/src/UnhandledLinkingContext.d.ts +0 -6
  77. package/lib/typescript/src/UnhandledLinkingContext.d.ts.map +0 -1
  78. package/src/UnhandledLinkingContext.tsx +0 -18
@@ -3,16 +3,16 @@ import {
3
3
  getActionFromState,
4
4
  getPathFromState,
5
5
  getStateFromPath,
6
+ type InitialState,
6
7
  type NavigationContainerProps,
7
8
  type NavigationContainerRef,
8
- type NavigationState,
9
9
  type ParamListBase,
10
+ type RootParamList,
10
11
  ThemeProvider,
11
12
  validatePathConfig,
12
13
  } from '@react-navigation/core';
13
14
  import * as React from 'react';
14
- import { I18nManager } from 'react-native';
15
- import useLatestCallback from 'use-latest-callback';
15
+ import { I18nManager, Platform } from 'react-native';
16
16
 
17
17
  import { LinkingContext } from './LinkingContext';
18
18
  import { LocaleDirContext } from './LocaleDirContext';
@@ -21,12 +21,12 @@ import type {
21
21
  DocumentTitleOptions,
22
22
  LinkingOptions,
23
23
  LocaleDirection,
24
+ Persistor,
24
25
  } from './types';
25
- import { UnhandledLinkingContext } from './UnhandledLinkingContext';
26
26
  import { useBackButton } from './useBackButton';
27
27
  import { useDocumentTitle } from './useDocumentTitle';
28
28
  import { useLinking } from './useLinking';
29
- import { useThenable } from './useThenable';
29
+ import { type Thenable, useThenable } from './useThenable';
30
30
 
31
31
  declare global {
32
32
  var REACT_NAVIGATION_DEVTOOLS: WeakMap<
@@ -41,7 +41,9 @@ type Props<ParamList extends {}> = NavigationContainerProps & {
41
41
  /**
42
42
  * Initial state object for the navigation tree.
43
43
  *
44
- * If this is provided, deep link or URLs won't be handled on the initial render.
44
+ * If this is provided:
45
+ * - Deep link or URLs won't be handled on the initial render.
46
+ * - Persisted state won't be restored.
45
47
  */
46
48
  initialState?: NavigationContainerProps['initialState'];
47
49
  /**
@@ -56,11 +58,36 @@ type Props<ParamList extends {}> = NavigationContainerProps & {
56
58
  */
57
59
  linking?: LinkingOptions<ParamList>;
58
60
  /**
59
- * Fallback element to render until initial state is resolved from deep linking.
61
+ * Persistor object to persist and restore navigation state.
62
+ *
63
+ * State is not restored if a deep link is handled on the initial render
64
+ * Not supported on web when linking is enabled.
65
+ *
66
+ * Example:
67
+ *
68
+ * ```ts
69
+ * const persistor = {
70
+ * async persist(state) {
71
+ * await AsyncStorage.setItem('state-key-v1', JSON.stringify(state));
72
+ * },
73
+ * async restore() {
74
+ * const state = await AsyncStorage.getItem('state-key-v1');
75
+ *
76
+ * return state ? JSON.parse(state) : undefined;
77
+ * },
78
+ * };
79
+ *
80
+ * <NavigationContainer persistor={persistor}>...</NavigationContainer>
81
+ * ```
82
+ */
83
+ persistor?: Persistor;
84
+ /**
85
+ * Fallback element to render until initial state is resolved.
86
+ * Used when deep link or persisted state is being restored asynchronously.
60
87
  *
61
88
  * Defaults to `null`.
62
89
  */
63
- fallback?: React.ReactNode;
90
+ fallback?: React.ReactElement | null;
64
91
  /**
65
92
  * Options to configure the document title on Web.
66
93
  *
@@ -70,21 +97,22 @@ type Props<ParamList extends {}> = NavigationContainerProps & {
70
97
  documentTitle?: DocumentTitleOptions;
71
98
  };
72
99
 
100
+ const RESTORE_STATE_ERROR =
101
+ 'Failed to restore navigation state. The state will be initialized based on the navigation tree.';
102
+
73
103
  function NavigationContainerInner(
74
104
  {
75
105
  direction = I18nManager.getConstants().isRTL ? 'rtl' : 'ltr',
76
106
  theme = DefaultTheme,
77
107
  linking,
108
+ persistor,
78
109
  fallback = null,
79
110
  documentTitle,
80
- onReady,
81
111
  onStateChange,
82
112
  ...rest
83
113
  }: Props<ParamListBase>,
84
114
  ref?: React.Ref<NavigationContainerRef<ParamListBase> | null>
85
115
  ) {
86
- const isLinkingEnabled = linking ? linking.enabled !== false : false;
87
-
88
116
  if (linking?.config) {
89
117
  validatePathConfig(linking.config);
90
118
  }
@@ -95,82 +123,108 @@ function NavigationContainerInner(
95
123
  useBackButton(refContainer);
96
124
  useDocumentTitle(refContainer, documentTitle);
97
125
 
98
- const [lastUnhandledLink, setLastUnhandledLink] = React.useState<
99
- string | undefined
100
- >();
101
-
102
- const { getInitialState } = useLinking(
103
- refContainer,
104
- {
105
- enabled: isLinkingEnabled,
106
- prefixes: [],
107
- ...linking,
108
- },
109
- setLastUnhandledLink
110
- );
111
-
112
- const linkingContext = React.useMemo(() => ({ options: linking }), [linking]);
126
+ const linkingConfig = React.useMemo(() => {
127
+ if (linking == null) {
128
+ return {
129
+ options: {
130
+ enabled: false,
131
+ },
132
+ };
133
+ }
113
134
 
114
- const unhandledLinkingContext = React.useMemo(
115
- () => ({ lastUnhandledLink, setLastUnhandledLink }),
116
- [lastUnhandledLink, setLastUnhandledLink]
117
- );
135
+ return {
136
+ options: {
137
+ ...linking,
138
+ enabled: linking.enabled !== false,
139
+ prefixes: linking.prefixes ?? ['*'],
140
+ getStateFromPath: linking?.getStateFromPath ?? getStateFromPath,
141
+ getPathFromState: linking?.getPathFromState ?? getPathFromState,
142
+ getActionFromState: linking?.getActionFromState ?? getActionFromState,
143
+ },
144
+ };
145
+ }, [linking]);
118
146
 
119
- const onReadyForLinkingHandling = useLatestCallback(() => {
120
- // If the screen path matches lastUnhandledLink, we do not track it
121
- const path = refContainer.current?.getCurrentRoute()?.path;
122
- setLastUnhandledLink((previousLastUnhandledLink) => {
123
- if (previousLastUnhandledLink === path) {
124
- return undefined;
125
- }
126
- return previousLastUnhandledLink;
127
- });
128
- onReady?.();
129
- });
147
+ const { getInitialState } = useLinking(refContainer, linkingConfig.options);
130
148
 
131
- const onStateChangeForLinkingHandling = useLatestCallback(
132
- (state: Readonly<NavigationState> | undefined) => {
133
- // If the screen path matches lastUnhandledLink, we do not track it
134
- const path = refContainer.current?.getCurrentRoute()?.path;
135
- setLastUnhandledLink((previousLastUnhandledLink) => {
136
- if (previousLastUnhandledLink === path) {
137
- return undefined;
138
- }
139
- return previousLastUnhandledLink;
140
- });
141
- onStateChange?.(state);
142
- }
143
- );
144
149
  // Add additional linking related info to the ref
145
150
  // This will be used by the devtools
146
151
  React.useEffect(() => {
147
152
  if (refContainer.current) {
148
153
  REACT_NAVIGATION_DEVTOOLS.set(refContainer.current, {
149
154
  get linking() {
150
- return {
151
- ...linking,
152
- enabled: isLinkingEnabled,
153
- prefixes: linking?.prefixes ?? [],
154
- getStateFromPath: linking?.getStateFromPath ?? getStateFromPath,
155
- getPathFromState: linking?.getPathFromState ?? getPathFromState,
156
- getActionFromState:
157
- linking?.getActionFromState ?? getActionFromState,
158
- };
155
+ return linkingConfig.options;
159
156
  },
160
157
  });
161
158
  }
162
159
  });
163
160
 
164
- const [isResolved, initialState] = useThenable(getInitialState);
161
+ const [isLinkStateResolved, initialStateFromLink] = useThenable(() => {
162
+ if (rest.initialState != null || !linkingConfig.options.enabled) {
163
+ return undefined;
164
+ }
165
+
166
+ return getInitialState();
167
+ });
168
+
169
+ const isPersistenceSupported =
170
+ Platform.OS === 'web' ? !linkingConfig.options.enabled : true;
171
+
172
+ const [isPersistedStateResolved, initialStateFromPersisted] = useThenable(
173
+ () => {
174
+ if (
175
+ isPersistenceSupported === false ||
176
+ rest.initialState != null ||
177
+ persistor == null
178
+ ) {
179
+ return undefined;
180
+ }
181
+
182
+ let restoredState;
183
+
184
+ try {
185
+ restoredState = persistor.restore();
186
+ } catch (e) {
187
+ console.error(RESTORE_STATE_ERROR, e);
188
+
189
+ return undefined;
190
+ }
191
+
192
+ if (restoredState == null) {
193
+ return undefined;
194
+ }
195
+
196
+ if ('then' in restoredState) {
197
+ return restoredState.then(
198
+ (state) => state,
199
+ (error) => {
200
+ console.error(RESTORE_STATE_ERROR, error);
201
+
202
+ return undefined;
203
+ }
204
+ );
205
+ }
206
+
207
+ const thenable: Thenable<InitialState | undefined> = {
208
+ then(onfulfilled) {
209
+ return Promise.resolve(
210
+ onfulfilled ? onfulfilled(restoredState) : restoredState
211
+ );
212
+ },
213
+ };
214
+
215
+ return thenable;
216
+ }
217
+ );
165
218
 
166
219
  // FIXME
167
220
  // @ts-expect-error not sure why this is not working
168
221
  React.useImperativeHandle(ref, () => refContainer.current);
169
222
 
170
- const isLinkingReady =
171
- rest.initialState != null || !isLinkingEnabled || isResolved;
223
+ const isStateReady =
224
+ rest.initialState != null ||
225
+ (isLinkStateResolved && isPersistedStateResolved);
172
226
 
173
- if (!isLinkingReady) {
227
+ if (!isStateReady) {
174
228
  return (
175
229
  <LocaleDirContext.Provider value={direction}>
176
230
  <ThemeProvider value={theme}>{fallback}</ThemeProvider>
@@ -180,32 +234,35 @@ function NavigationContainerInner(
180
234
 
181
235
  return (
182
236
  <LocaleDirContext.Provider value={direction}>
183
- <UnhandledLinkingContext.Provider value={unhandledLinkingContext}>
184
- <LinkingContext.Provider value={linkingContext}>
185
- <BaseNavigationContainer
186
- {...rest}
187
- theme={theme}
188
- onReady={onReadyForLinkingHandling}
189
- onStateChange={onStateChangeForLinkingHandling}
190
- initialState={
191
- rest.initialState == null ? initialState : rest.initialState
192
- }
193
- ref={refContainer}
194
- />
195
- </LinkingContext.Provider>
196
- </UnhandledLinkingContext.Provider>
237
+ <LinkingContext.Provider value={linkingConfig}>
238
+ <BaseNavigationContainer
239
+ {...rest}
240
+ theme={theme}
241
+ initialState={
242
+ rest.initialState ??
243
+ initialStateFromLink ??
244
+ initialStateFromPersisted
245
+ }
246
+ onStateChange={(state) => {
247
+ onStateChange?.(state);
248
+ persistor?.persist(state);
249
+ }}
250
+ ref={refContainer}
251
+ />
252
+ </LinkingContext.Provider>
197
253
  </LocaleDirContext.Provider>
198
254
  );
199
255
  }
200
256
 
201
257
  /**
202
258
  * Container component that manages the navigation state.
259
+ *
203
260
  * This should be rendered at the root wrapping the whole app.
204
261
  */
205
262
  export const NavigationContainer = React.forwardRef(
206
263
  NavigationContainerInner
207
- ) as <RootParamList extends {} = ReactNavigation.RootParamList>(
208
- props: Props<RootParamList> & {
209
- ref?: React.Ref<NavigationContainerRef<RootParamList>>;
264
+ ) as <ParamList extends {} = RootParamList>(
265
+ props: Props<ParamList> & {
266
+ ref?: React.Ref<NavigationContainerRef<ParamList>>;
210
267
  }
211
268
  ) => React.ReactElement;
@@ -12,7 +12,6 @@ import {
12
12
  const StackNavigator = (
13
13
  props: DefaultNavigatorOptions<
14
14
  ParamListBase,
15
- string | undefined,
16
15
  StackNavigationState<ParamListBase>,
17
16
  {},
18
17
  {},
@@ -35,7 +34,6 @@ export function createStackNavigator<
35
34
  ParamList extends ParamListBase,
36
35
  >(): TypedNavigator<{
37
36
  ParamList: ParamList;
38
- NavigatorID: string | undefined;
39
37
  State: StackNavigationState<ParamList>;
40
38
  ScreenOptions: {};
41
39
  EventMap: {};
@@ -1,27 +1,37 @@
1
1
  import escapeStringRegexp from 'escape-string-regexp';
2
2
 
3
- export function extractPathFromURL(prefixes: string[], url: string) {
3
+ import type { LinkingPrefix } from './types';
4
+
5
+ export function extractPathFromURL(prefixes: LinkingPrefix[], url: string) {
4
6
  for (const prefix of prefixes) {
5
- const protocol = prefix.match(/^[^:]+:/)?.[0] ?? '';
6
- const host = prefix
7
- .replace(new RegExp(`^${escapeStringRegexp(protocol)}`), '')
8
- .replace(/\/+/g, '/') // Replace multiple slash (//) with single ones
9
- .replace(/^\//, ''); // Remove extra leading slash
10
-
11
- const prefixRegex = new RegExp(
12
- `^${escapeStringRegexp(protocol)}(/)*${host
13
- .split('.')
14
- .map((it) => (it === '*' ? '[^/]+' : escapeStringRegexp(it)))
15
- .join('\\.')}`
16
- );
7
+ let prefixRegex;
8
+
9
+ if (prefix === '*') {
10
+ prefixRegex = /^(((https?:\/\/)[^/]+)|([^/]+:(\/\/)?))/;
11
+ } else {
12
+ const protocol = prefix.match(/^[^:]+:/)?.[0] ?? '';
13
+ const host = prefix
14
+ .replace(new RegExp(`^${escapeStringRegexp(protocol)}`), '')
15
+ .replace(/\/+/g, '/') // Replace multiple slash (//) with single ones
16
+ .replace(/^\//, ''); // Remove extra leading slash
17
+
18
+ prefixRegex = new RegExp(
19
+ `^${escapeStringRegexp(protocol)}(/)*${host
20
+ .split('.')
21
+ .map((it) => (it === '*' ? '[^/]+' : escapeStringRegexp(it)))
22
+ .join('\\.')}`
23
+ );
24
+ }
17
25
 
18
26
  const [originAndPath, ...searchParams] = url.split('?');
19
- const normalizedURL = originAndPath
20
- .replace(/\/+/g, '/')
21
- .concat(searchParams.length ? `?${searchParams.join('?')}` : '');
22
27
 
23
- if (prefixRegex.test(normalizedURL)) {
24
- return normalizedURL.replace(prefixRegex, '');
28
+ if (prefixRegex.test(originAndPath)) {
29
+ const result = originAndPath
30
+ .replace(prefixRegex, '')
31
+ .replace(/\/+/g, '/')
32
+ .concat(searchParams.length ? `?${searchParams.join('?')}` : '');
33
+
34
+ return result.startsWith('/') ? result : `/${result}`;
25
35
  }
26
36
  }
27
37
 
@@ -0,0 +1,46 @@
1
+ import { getStateFromPath, type ParamListBase } from '@react-navigation/core';
2
+
3
+ import { extractPathFromURL } from './extractPathFromURL';
4
+ import type { LinkingOptions } from './types';
5
+
6
+ export function getStateFromHref(
7
+ href: string,
8
+ options: LinkingOptions<ParamListBase> | undefined
9
+ ): ReturnType<typeof getStateFromPath> {
10
+ const {
11
+ prefixes,
12
+ filter,
13
+ config,
14
+ getStateFromPath: getStateFromPathHelper = getStateFromPath,
15
+ } = options || {};
16
+
17
+ let path;
18
+
19
+ if (href.startsWith('/')) {
20
+ path = href;
21
+ } else if (href) {
22
+ if (filter && !filter(href)) {
23
+ throw new Error(
24
+ `Failed to parse href '${href}'. It doesn't match the filter specified in linking config.`
25
+ );
26
+ }
27
+
28
+ if (prefixes == null || prefixes.length === 0) {
29
+ throw new Error(
30
+ `Failed to parse href '${href}'. It doesn't start with '/' and no prefixes are defined in linking config.`
31
+ );
32
+ }
33
+
34
+ path = extractPathFromURL(prefixes, href);
35
+ }
36
+
37
+ if (path == null) {
38
+ throw new Error(
39
+ `Got invalid href '${href}'. It must start with '/' or match one of the prefixes: ${options?.prefixes?.map((prefix) => `'${prefix}'`).join(', ')}.`
40
+ );
41
+ }
42
+
43
+ const state = getStateFromPathHelper(path, config);
44
+
45
+ return state;
46
+ }
package/src/index.tsx CHANGED
@@ -7,7 +7,6 @@ export { ServerContainer } from './ServerContainer';
7
7
  export { DarkTheme } from './theming/DarkTheme';
8
8
  export { DefaultTheme } from './theming/DefaultTheme';
9
9
  export * from './types';
10
- export { UnhandledLinkingContext as UNSTABLE_UnhandledLinkingContext } from './UnhandledLinkingContext';
11
10
  export { useLinkBuilder } from './useLinkBuilder';
12
11
  export { type LinkProps, useLinkProps } from './useLinkProps';
13
12
  export { useLinkTo } from './useLinkTo';
@@ -1,7 +1,8 @@
1
- import type { Theme } from '../types';
1
+ import type { Theme } from '@react-navigation/core';
2
+
2
3
  import { fonts } from './fonts';
3
4
 
4
- export const DarkTheme: Theme = {
5
+ export const DarkTheme = {
5
6
  dark: true,
6
7
  colors: {
7
8
  primary: 'rgb(10, 132, 255)',
@@ -12,4 +13,4 @@ export const DarkTheme: Theme = {
12
13
  notification: 'rgb(255, 69, 58)',
13
14
  },
14
15
  fonts,
15
- };
16
+ } as const satisfies Theme;
@@ -1,15 +1,16 @@
1
- import type { Theme } from '../types';
1
+ import type { Theme } from '@react-navigation/core';
2
+
2
3
  import { fonts } from './fonts';
3
4
 
4
- export const DefaultTheme: Theme = {
5
+ export const DefaultTheme = {
5
6
  dark: false,
6
7
  colors: {
7
8
  primary: 'rgb(0, 122, 255)',
8
9
  background: 'rgb(242, 242, 242)',
9
10
  card: 'rgb(255, 255, 255)',
10
11
  text: 'rgb(28, 28, 30)',
11
- border: 'rgb(216, 216, 216)',
12
+ border: 'rgb(178, 178, 178)',
12
13
  notification: 'rgb(255, 59, 48)',
13
14
  },
14
15
  fonts,
15
- };
16
+ } as const satisfies Theme;
@@ -1,7 +1,6 @@
1
+ import type { Theme } from '@react-navigation/core';
1
2
  import { Platform } from 'react-native';
2
3
 
3
- import type { Theme } from '../types';
4
-
5
4
  const WEB_FONT_STACK =
6
5
  'system-ui, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"';
7
6
 
package/src/types.tsx CHANGED
@@ -2,16 +2,23 @@ import type {
2
2
  getActionFromState as getActionFromStateDefault,
3
3
  getPathFromState as getPathFromStateDefault,
4
4
  getStateFromPath as getStateFromPathDefault,
5
+ InitialState,
6
+ NavigationState,
5
7
  PathConfigMap,
6
8
  Route,
7
9
  } from '@react-navigation/core';
10
+ import type { ColorValue as ReactNativeColorValue } from 'react-native';
8
11
 
9
- declare global {
10
- // eslint-disable-next-line @typescript-eslint/no-namespace
11
- namespace ReactNavigation {
12
- interface Theme extends NativeTheme {}
13
- }
14
- }
12
+ type ColorValue =
13
+ | `#${string}`
14
+ | `rgb(${string})`
15
+ | `rgba(${string})`
16
+ | `hsl(${string})`
17
+ | `hsla(${string})`
18
+ | `hwb(${string})`
19
+ | `hwba(${string})`
20
+ | `var(--${string})`
21
+ | ReactNativeColorValue;
15
22
 
16
23
  type FontStyle = {
17
24
  fontFamily: string;
@@ -32,12 +39,12 @@ type FontStyle = {
32
39
  interface NativeTheme {
33
40
  dark: boolean;
34
41
  colors: {
35
- primary: string;
36
- background: string;
37
- card: string;
38
- text: string;
39
- border: string;
40
- notification: string;
42
+ primary: ColorValue;
43
+ background: ColorValue;
44
+ card: ColorValue;
45
+ text: ColorValue;
46
+ border: ColorValue;
47
+ notification: ColorValue;
41
48
  };
42
49
  fonts: {
43
50
  regular: FontStyle;
@@ -47,19 +54,34 @@ interface NativeTheme {
47
54
  };
48
55
  }
49
56
 
50
- export type Theme = NativeTheme;
57
+ declare module '@react-navigation/core' {
58
+ interface Theme extends NativeTheme {}
59
+ }
51
60
 
52
61
  export type LocaleDirection = 'ltr' | 'rtl';
53
62
 
63
+ export type LinkingPrefix = '*' | (string & {});
64
+
54
65
  export type LinkingOptions<ParamList extends {}> = {
55
66
  /**
56
67
  * Whether deep link handling should be enabled.
57
- * Defaults to true.
68
+ *
69
+ * Defaults to `true` when a linking config is specified.
58
70
  */
59
71
  enabled?: boolean;
60
72
  /**
61
- * The prefixes are stripped from the URL before parsing them.
62
- * Usually they are the `scheme` + `host` (e.g. `myapp://chat?user=jane`)
73
+ * The prefixes to match to determine whether to handle a URL.
74
+ *
75
+ * Supported prefix formats:
76
+ * - `${scheme}://` - App-specific scheme, e.g. `myapp://`
77
+ * - `${protocol}://${host}` - Universal links or app links, e.g. `https://example.com`, `https://subdomain.example.com`
78
+ * - `${protocol}://*.${domain}` - Any subdomain of given domain, e.g. `https://*.example.com`
79
+ * - `${protocol}://${host}/${path}` - Subpath of given host, e.g. `https://example.com/app`
80
+ * - `*` - Any domain or subdomain with `http://` and `https://` as well as any app-specific scheme
81
+ *
82
+ * The prefix will be stripped from the URL before it's parsed.
83
+ *
84
+ * Defaults to `[*]`.
63
85
  *
64
86
  * This is not supported on Web.
65
87
  *
@@ -67,14 +89,14 @@ export type LinkingOptions<ParamList extends {}> = {
67
89
  * ```js
68
90
  * {
69
91
  * prefixes: [
70
- * "myapp://", // App-specific scheme
71
- * "https://example.com", // Prefix for universal links
72
- * "https://*.example.com" // Prefix which matches any subdomain
92
+ * "myapp://",
93
+ * "https://example.com",
94
+ * "https://*.example.com"
73
95
  * ]
74
96
  * }
75
97
  * ```
76
98
  */
77
- prefixes: string[];
99
+ prefixes?: LinkingPrefix[];
78
100
  /**
79
101
  * Optional function which takes an incoming URL returns a boolean
80
102
  * indicating whether React Navigation should handle it.
@@ -153,9 +175,11 @@ export type LinkingOptions<ParamList extends {}> = {
153
175
  * subscribe: (listener) => {
154
176
  * const onReceiveURL = ({ url }) => listener(url);
155
177
  *
156
- * Linking.addEventListener('url', onReceiveURL);
178
+ * const subscription = Linking.addEventListener('url', onReceiveURL);
157
179
  *
158
- * return () => Linking.removeEventListener('url', onReceiveURL);
180
+ * return () => {
181
+ * subscription.remove();
182
+ * };
159
183
  * }
160
184
  * }
161
185
  * ```
@@ -186,6 +210,20 @@ export type DocumentTitleOptions = {
186
210
  ) => string;
187
211
  };
188
212
 
213
+ export type Persistor = {
214
+ /**
215
+ * Callback to persist the navigation state.
216
+ */
217
+ persist(state: NavigationState | undefined): void;
218
+ /**
219
+ * Callback to restore the navigation state.
220
+ * Should return the restored state or a Promise which resolves to the restored state.
221
+ *
222
+ * If a Promise is returned, providing a `fallback` component is recommended.
223
+ */
224
+ restore(): PromiseLike<InitialState | undefined> | InitialState | undefined;
225
+ };
226
+
189
227
  export type ServerContainerRef = {
190
228
  getCurrentOptions(): Record<string, any> | undefined;
191
229
  };