@react-navigation/native 7.0.0-alpha.1 → 7.0.0-alpha.10

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 (206) hide show
  1. package/lib/commonjs/Link.js +13 -5
  2. package/lib/commonjs/Link.js.map +1 -1
  3. package/lib/commonjs/LinkingContext.js +7 -5
  4. package/lib/commonjs/LinkingContext.js.map +1 -1
  5. package/lib/commonjs/LocaleDirContext.js +12 -0
  6. package/lib/commonjs/LocaleDirContext.js.map +1 -0
  7. package/lib/commonjs/NavigationContainer.js +57 -19
  8. package/lib/commonjs/NavigationContainer.js.map +1 -1
  9. package/lib/commonjs/ServerContainer.js +3 -4
  10. package/lib/commonjs/ServerContainer.js.map +1 -1
  11. package/lib/commonjs/ServerContext.js +3 -4
  12. package/lib/commonjs/ServerContext.js.map +1 -1
  13. package/lib/commonjs/UnhandledLinkingContext.js +20 -0
  14. package/lib/commonjs/UnhandledLinkingContext.js.map +1 -0
  15. package/lib/commonjs/__stubs__/createStackNavigator.js +19 -0
  16. package/lib/commonjs/__stubs__/createStackNavigator.js.map +1 -0
  17. package/lib/commonjs/__stubs__/window.js +79 -0
  18. package/lib/commonjs/__stubs__/window.js.map +1 -0
  19. package/lib/commonjs/createMemoryHistory.js +4 -7
  20. package/lib/commonjs/createMemoryHistory.js.map +1 -1
  21. package/lib/commonjs/createStaticNavigation.js +5 -4
  22. package/lib/commonjs/createStaticNavigation.js.map +1 -1
  23. package/lib/commonjs/extractPathFromURL.js +3 -3
  24. package/lib/commonjs/extractPathFromURL.js.map +1 -1
  25. package/lib/commonjs/index.js +17 -17
  26. package/lib/commonjs/index.js.map +1 -1
  27. package/lib/commonjs/theming/DarkTheme.js +1 -2
  28. package/lib/commonjs/theming/DarkTheme.js.map +1 -1
  29. package/lib/commonjs/theming/DefaultTheme.js +1 -2
  30. package/lib/commonjs/theming/DefaultTheme.js.map +1 -1
  31. package/lib/commonjs/theming/fonts.js +1 -2
  32. package/lib/commonjs/theming/fonts.js.map +1 -1
  33. package/lib/commonjs/types.js.map +1 -1
  34. package/lib/commonjs/useBackButton.js +3 -19
  35. package/lib/commonjs/useBackButton.js.map +1 -1
  36. package/lib/commonjs/useBackButton.native.js +27 -0
  37. package/lib/commonjs/useBackButton.native.js.map +1 -0
  38. package/lib/commonjs/useDocumentTitle.js +5 -5
  39. package/lib/commonjs/useDocumentTitle.js.map +1 -1
  40. package/lib/commonjs/useDocumentTitle.native.js.map +1 -1
  41. package/lib/commonjs/useLinkBuilder.js +80 -0
  42. package/lib/commonjs/useLinkBuilder.js.map +1 -0
  43. package/lib/commonjs/useLinkProps.js +20 -23
  44. package/lib/commonjs/useLinkProps.js.map +1 -1
  45. package/lib/commonjs/useLinkTo.js +30 -0
  46. package/lib/commonjs/useLinkTo.js.map +1 -0
  47. package/lib/commonjs/useLinking.js +34 -35
  48. package/lib/commonjs/useLinking.js.map +1 -1
  49. package/lib/commonjs/useLinking.native.js +30 -23
  50. package/lib/commonjs/useLinking.native.js.map +1 -1
  51. package/lib/commonjs/useLocale.js +23 -0
  52. package/lib/commonjs/useLocale.js.map +1 -0
  53. package/lib/commonjs/useScrollToTop.js +8 -6
  54. package/lib/commonjs/useScrollToTop.js.map +1 -1
  55. package/lib/commonjs/useThenable.js +3 -2
  56. package/lib/commonjs/useThenable.js.map +1 -1
  57. package/lib/commonjs/useUnhandledLinking.js +75 -0
  58. package/lib/commonjs/useUnhandledLinking.js.map +1 -0
  59. package/lib/module/Link.js +11 -3
  60. package/lib/module/Link.js.map +1 -1
  61. package/lib/module/LinkingContext.js +4 -1
  62. package/lib/module/LinkingContext.js.map +1 -1
  63. package/lib/module/LocaleDirContext.js +4 -0
  64. package/lib/module/LocaleDirContext.js.map +1 -0
  65. package/lib/module/NavigationContainer.js +53 -15
  66. package/lib/module/NavigationContainer.js.map +1 -1
  67. package/lib/module/ServerContainer.js.map +1 -1
  68. package/lib/module/ServerContext.js.map +1 -1
  69. package/lib/module/UnhandledLinkingContext.js +12 -0
  70. package/lib/module/UnhandledLinkingContext.js.map +1 -0
  71. package/lib/module/__stubs__/createStackNavigator.js +11 -0
  72. package/lib/module/__stubs__/createStackNavigator.js.map +1 -0
  73. package/lib/module/__stubs__/window.js +73 -0
  74. package/lib/module/__stubs__/window.js.map +1 -0
  75. package/lib/module/createMemoryHistory.js +4 -7
  76. package/lib/module/createMemoryHistory.js.map +1 -1
  77. package/lib/module/createStaticNavigation.js +3 -2
  78. package/lib/module/createStaticNavigation.js.map +1 -1
  79. package/lib/module/extractPathFromURL.js +3 -3
  80. package/lib/module/extractPathFromURL.js.map +1 -1
  81. package/lib/module/index.js +3 -3
  82. package/lib/module/index.js.map +1 -1
  83. package/lib/module/theming/DarkTheme.js.map +1 -1
  84. package/lib/module/theming/DefaultTheme.js.map +1 -1
  85. package/lib/module/theming/fonts.js.map +1 -1
  86. package/lib/module/types.js.map +1 -1
  87. package/lib/module/useBackButton.js +3 -17
  88. package/lib/module/useBackButton.js.map +1 -1
  89. package/lib/module/useBackButton.native.js +19 -0
  90. package/lib/module/useBackButton.native.js.map +1 -0
  91. package/lib/module/useDocumentTitle.js +3 -3
  92. package/lib/module/useDocumentTitle.js.map +1 -1
  93. package/lib/module/useDocumentTitle.native.js.map +1 -1
  94. package/lib/module/{useLinkTools.js → useLinkBuilder.js} +10 -10
  95. package/lib/module/useLinkBuilder.js.map +1 -0
  96. package/lib/module/useLinkProps.js +18 -21
  97. package/lib/module/useLinkProps.js.map +1 -1
  98. package/lib/module/useLinkTo.js +23 -0
  99. package/lib/module/useLinkTo.js.map +1 -0
  100. package/lib/module/useLinking.js +31 -34
  101. package/lib/module/useLinking.js.map +1 -1
  102. package/lib/module/useLinking.native.js +28 -21
  103. package/lib/module/useLinking.native.js.map +1 -1
  104. package/lib/module/useLocale.js +16 -0
  105. package/lib/module/useLocale.js.map +1 -0
  106. package/lib/module/useScrollToTop.js +7 -5
  107. package/lib/module/useScrollToTop.js.map +1 -1
  108. package/lib/module/useThenable.js +1 -0
  109. package/lib/module/useThenable.js.map +1 -1
  110. package/lib/module/useUnhandledLinking.js +69 -0
  111. package/lib/module/useUnhandledLinking.js.map +1 -0
  112. package/lib/typescript/src/Link.d.ts +3 -3
  113. package/lib/typescript/src/Link.d.ts.map +1 -1
  114. package/lib/typescript/src/LinkingContext.d.ts +1 -1
  115. package/lib/typescript/src/LinkingContext.d.ts.map +1 -1
  116. package/lib/typescript/src/LocaleDirContext.d.ts +4 -0
  117. package/lib/typescript/src/LocaleDirContext.d.ts.map +1 -0
  118. package/lib/typescript/src/NavigationContainer.d.ts +4 -4
  119. package/lib/typescript/src/NavigationContainer.d.ts.map +1 -1
  120. package/lib/typescript/src/ServerContainer.d.ts +1 -1
  121. package/lib/typescript/src/ServerContainer.d.ts.map +1 -1
  122. package/lib/typescript/src/UnhandledLinkingContext.d.ts +6 -0
  123. package/lib/typescript/src/UnhandledLinkingContext.d.ts.map +1 -0
  124. package/lib/typescript/src/__stubs__/createStackNavigator.d.ts +32 -0
  125. package/lib/typescript/src/__stubs__/createStackNavigator.d.ts.map +1 -0
  126. package/lib/typescript/src/{__mocks__ → __stubs__}/window.d.ts +1 -1
  127. package/lib/typescript/src/{__mocks__ → __stubs__}/window.d.ts.map +1 -1
  128. package/lib/typescript/src/createStaticNavigation.d.ts +2 -2
  129. package/lib/typescript/src/createStaticNavigation.d.ts.map +1 -1
  130. package/lib/typescript/src/extractPathFromURL.d.ts.map +1 -1
  131. package/lib/typescript/src/index.d.ts +3 -3
  132. package/lib/typescript/src/index.d.ts.map +1 -1
  133. package/lib/typescript/src/types.d.ts +37 -18
  134. package/lib/typescript/src/types.d.ts.map +1 -1
  135. package/lib/typescript/src/useBackButton.d.ts +2 -2
  136. package/lib/typescript/src/useBackButton.d.ts.map +1 -1
  137. package/lib/typescript/src/useBackButton.native.d.ts +4 -0
  138. package/lib/typescript/src/useBackButton.native.d.ts.map +1 -0
  139. package/lib/typescript/src/useLinkBuilder.d.ts +25 -0
  140. package/lib/typescript/src/useLinkBuilder.d.ts.map +1 -0
  141. package/lib/typescript/src/useLinkProps.d.ts +2 -2
  142. package/lib/typescript/src/useLinkProps.d.ts.map +1 -1
  143. package/lib/typescript/src/useLinkTo.d.ts +6 -0
  144. package/lib/typescript/src/useLinkTo.d.ts.map +1 -0
  145. package/lib/typescript/src/useLinking.d.ts +8 -20
  146. package/lib/typescript/src/useLinking.d.ts.map +1 -1
  147. package/lib/typescript/src/useLinking.native.d.ts +4 -20
  148. package/lib/typescript/src/useLinking.native.d.ts.map +1 -1
  149. package/lib/typescript/src/useLocale.d.ts +7 -0
  150. package/lib/typescript/src/useLocale.d.ts.map +1 -0
  151. package/lib/typescript/src/useScrollToTop.d.ts.map +1 -1
  152. package/lib/typescript/src/useThenable.d.ts.map +1 -1
  153. package/lib/typescript/src/useUnhandledLinking.d.ts +7 -0
  154. package/lib/typescript/src/useUnhandledLinking.d.ts.map +1 -0
  155. package/package.json +15 -15
  156. package/src/Link.tsx +11 -2
  157. package/src/LinkingContext.tsx +8 -2
  158. package/src/LocaleDirContext.tsx +7 -0
  159. package/src/NavigationContainer.tsx +80 -24
  160. package/src/ServerContainer.tsx +1 -1
  161. package/src/UnhandledLinkingContext.tsx +18 -0
  162. package/src/{__mocks__ → __stubs__}/window.tsx +7 -5
  163. package/src/createStaticNavigation.tsx +6 -4
  164. package/src/extractPathFromURL.tsx +4 -1
  165. package/src/index.tsx +3 -3
  166. package/src/types.tsx +40 -18
  167. package/src/useBackButton.native.tsx +33 -0
  168. package/src/useBackButton.tsx +3 -25
  169. package/src/{useLinkTools.tsx → useLinkBuilder.tsx} +8 -8
  170. package/src/useLinkProps.tsx +20 -19
  171. package/src/useLinkTo.tsx +30 -0
  172. package/src/useLinking.native.tsx +26 -20
  173. package/src/useLinking.tsx +43 -41
  174. package/src/useLocale.tsx +18 -0
  175. package/src/useScrollToTop.tsx +13 -7
  176. package/src/useThenable.tsx +1 -0
  177. package/src/useUnhandledLinking.tsx +91 -0
  178. package/lib/commonjs/theming/ThemeContext.js +0 -14
  179. package/lib/commonjs/theming/ThemeContext.js.map +0 -1
  180. package/lib/commonjs/theming/ThemeProvider.js +0 -20
  181. package/lib/commonjs/theming/ThemeProvider.js.map +0 -1
  182. package/lib/commonjs/theming/useTheme.js +0 -15
  183. package/lib/commonjs/theming/useTheme.js.map +0 -1
  184. package/lib/commonjs/useLinkTools.js +0 -80
  185. package/lib/commonjs/useLinkTools.js.map +0 -1
  186. package/lib/module/theming/ThemeContext.js +0 -5
  187. package/lib/module/theming/ThemeContext.js.map +0 -1
  188. package/lib/module/theming/ThemeProvider.js +0 -12
  189. package/lib/module/theming/ThemeProvider.js.map +0 -1
  190. package/lib/module/theming/useTheme.js +0 -7
  191. package/lib/module/theming/useTheme.js.map +0 -1
  192. package/lib/module/useLinkTools.js.map +0 -1
  193. package/lib/typescript/src/__mocks__/createStackNavigator.d.ts +0 -56
  194. package/lib/typescript/src/__mocks__/createStackNavigator.d.ts.map +0 -1
  195. package/lib/typescript/src/theming/ThemeContext.d.ts +0 -4
  196. package/lib/typescript/src/theming/ThemeContext.d.ts.map +0 -1
  197. package/lib/typescript/src/theming/ThemeProvider.d.ts +0 -9
  198. package/lib/typescript/src/theming/ThemeProvider.d.ts.map +0 -1
  199. package/lib/typescript/src/theming/useTheme.d.ts +0 -2
  200. package/lib/typescript/src/theming/useTheme.d.ts.map +0 -1
  201. package/lib/typescript/src/useLinkTools.d.ts +0 -33
  202. package/lib/typescript/src/useLinkTools.d.ts.map +0 -1
  203. package/src/theming/ThemeContext.tsx +0 -8
  204. package/src/theming/ThemeProvider.tsx +0 -15
  205. package/src/theming/useTheme.tsx +0 -9
  206. /package/src/{__mocks__ → __stubs__}/createStackNavigator.tsx +0 -0
@@ -0,0 +1,33 @@
1
+ import type {
2
+ NavigationContainerRef,
3
+ ParamListBase,
4
+ } from '@react-navigation/core';
5
+ import * as React from 'react';
6
+ import { BackHandler } from 'react-native';
7
+
8
+ export function useBackButton(
9
+ ref: React.RefObject<NavigationContainerRef<ParamListBase>>
10
+ ) {
11
+ React.useEffect(() => {
12
+ const subscription = BackHandler.addEventListener(
13
+ 'hardwareBackPress',
14
+ () => {
15
+ const navigation = ref.current;
16
+
17
+ if (navigation == null) {
18
+ return false;
19
+ }
20
+
21
+ if (navigation.canGoBack()) {
22
+ navigation.goBack();
23
+
24
+ return true;
25
+ }
26
+
27
+ return false;
28
+ }
29
+ );
30
+
31
+ return () => subscription.remove();
32
+ }, [ref]);
33
+ }
@@ -2,32 +2,10 @@ import type {
2
2
  NavigationContainerRef,
3
3
  ParamListBase,
4
4
  } from '@react-navigation/core';
5
- import * as React from 'react';
6
- import { BackHandler } from 'react-native';
7
5
 
8
6
  export function useBackButton(
9
- ref: React.RefObject<NavigationContainerRef<ParamListBase>>
7
+ _: React.RefObject<NavigationContainerRef<ParamListBase>>
10
8
  ) {
11
- React.useEffect(() => {
12
- const subscription = BackHandler.addEventListener(
13
- 'hardwareBackPress',
14
- () => {
15
- const navigation = ref.current;
16
-
17
- if (navigation == null) {
18
- return false;
19
- }
20
-
21
- if (navigation.canGoBack()) {
22
- navigation.goBack();
23
-
24
- return true;
25
- }
26
-
27
- return false;
28
- }
29
- );
30
-
31
- return () => subscription.remove();
32
- }, [ref]);
9
+ // No-op
10
+ // BackHandler is not available on web
33
11
  }
@@ -3,10 +3,10 @@ import {
3
3
  getActionFromState,
4
4
  getPathFromState,
5
5
  getStateFromPath,
6
- NavigationHelpers,
6
+ type NavigationHelpers,
7
7
  NavigationHelpersContext,
8
- NavigationProp,
9
- ParamListBase,
8
+ type NavigationProp,
9
+ type ParamListBase,
10
10
  } from '@react-navigation/core';
11
11
  import * as React from 'react';
12
12
 
@@ -45,10 +45,10 @@ const getRootStateForNavigate = (
45
45
  };
46
46
 
47
47
  /**
48
- * Build destination link for a navigate action.
49
- * Useful for showing anchor tags on the web for buttons that perform navigation.
48
+ * Helpers to build href or action based on the linking options.
49
+ * @returns `buildHref` to build an `href` for screen and `buildAction` to build an action from an `href`.
50
50
  */
51
- export function useLinkTools() {
51
+ export function useLinkBuilder() {
52
52
  const navigation = React.useContext(NavigationHelpersContext);
53
53
  const linking = React.useContext(LinkingContext);
54
54
 
@@ -84,7 +84,7 @@ export function useLinkTools() {
84
84
  const buildAction = React.useCallback(
85
85
  (href: string) => {
86
86
  if (!href.startsWith('/')) {
87
- throw new Error(`The path must start with '/' (${href}).`);
87
+ throw new Error(`The href must start with '/' (${href}).`);
88
88
  }
89
89
 
90
90
  const { options } = linking;
@@ -98,7 +98,7 @@ export function useLinkTools() {
98
98
 
99
99
  return action ?? CommonActions.reset(state);
100
100
  } else {
101
- throw new Error('Failed to parse the path to a navigation state.');
101
+ throw new Error('Failed to parse the href to a navigation state.');
102
102
  }
103
103
  },
104
104
  [linking]
@@ -1,20 +1,20 @@
1
1
  import {
2
2
  getPathFromState,
3
- NavigationAction,
3
+ type NavigationAction,
4
4
  NavigationContainerRefContext,
5
5
  NavigationHelpersContext,
6
- NavigatorScreenParams,
7
- ParamListBase,
6
+ type NavigatorScreenParams,
7
+ type ParamListBase,
8
8
  } from '@react-navigation/core';
9
9
  import type { NavigationState, PartialState } from '@react-navigation/routers';
10
10
  import * as React from 'react';
11
- import { GestureResponderEvent, Platform } from 'react-native';
11
+ import { type GestureResponderEvent, Platform } from 'react-native';
12
12
 
13
13
  import { LinkingContext } from './LinkingContext';
14
14
 
15
15
  export type Props<
16
16
  ParamList extends ReactNavigation.RootParamList,
17
- RouteName extends keyof ParamList = keyof ParamList
17
+ RouteName extends keyof ParamList = keyof ParamList,
18
18
  > =
19
19
  | ({
20
20
  screen: Extract<RouteName, string>;
@@ -43,7 +43,7 @@ const getStateFromParams = (
43
43
  {
44
44
  name: params.screen,
45
45
  params: params.params,
46
- // @ts-expect-error
46
+ // @ts-expect-error this is fine 🔥
47
47
  state: params.screen
48
48
  ? getStateFromParams(
49
49
  params.params as
@@ -80,19 +80,20 @@ export function useLinkProps<ParamList extends ReactNavigation.RootParamList>({
80
80
  const onPress = (
81
81
  e?: React.MouseEvent<HTMLAnchorElement, MouseEvent> | GestureResponderEvent
82
82
  ) => {
83
+ // @ts-expect-error: these properties exist on web, but not in React Native
84
+ const hasModifierKey = e.metaKey || e.altKey || e.ctrlKey || e.shiftKey; // ignore clicks with modifier keys
85
+ // @ts-expect-error: these properties exist on web, but not in React Native
86
+ const isLeftClick = e.button == null || e.button === 0; // only handle left clicks
87
+ const isSelfTarget = [undefined, null, '', 'self'].includes(
88
+ // @ts-expect-error: these properties exist on web, but not in React Native
89
+ e.currentTarget?.target
90
+ ); // let browser handle "target=_blank" etc.
91
+
83
92
  let shouldHandle = false;
84
93
 
85
94
  if (Platform.OS !== 'web' || !e) {
86
- shouldHandle = e ? !e.defaultPrevented : true;
87
- } else if (
88
- !e.defaultPrevented && // onPress prevented default
89
- // @ts-expect-error: these properties exist on web, but not in React Native
90
- !(e.metaKey || e.altKey || e.ctrlKey || e.shiftKey) && // ignore clicks with modifier keys
91
- // @ts-expect-error: these properties exist on web, but not in React Native
92
- (e.button == null || e.button === 0) && // ignore everything but left clicks
93
- // @ts-expect-error: these properties exist on web, but not in React Native
94
- [undefined, null, '', 'self'].includes(e.currentTarget?.target) // let browser handle "target=_blank" etc.
95
- ) {
95
+ shouldHandle = true;
96
+ } else if (!hasModifierKey && isLeftClick && isSelfTarget) {
96
97
  e.preventDefault();
97
98
  shouldHandle = true;
98
99
  }
@@ -109,7 +110,7 @@ export function useLinkProps<ParamList extends ReactNavigation.RootParamList>({
109
110
  );
110
111
  }
111
112
  } else {
112
- // @ts-expect-error: This is already type-checked by the prop types
113
+ // @ts-expect-error This is already type-checked by the prop types
113
114
  navigation?.navigate(screen, params);
114
115
  }
115
116
  }
@@ -126,9 +127,9 @@ export function useLinkProps<ParamList extends ReactNavigation.RootParamList>({
126
127
  routes: [
127
128
  {
128
129
  name: screen,
129
- // @ts-expect-error
130
+ // @ts-expect-error this is fine 🔥
130
131
  params: params,
131
- // @ts-expect-error
132
+ // @ts-expect-error this is fine 🔥
132
133
  state: getStateFromParams(params),
133
134
  },
134
135
  ],
@@ -0,0 +1,30 @@
1
+ import { NavigationContainerRefContext } from '@react-navigation/core';
2
+ import * as React from 'react';
3
+
4
+ import { useLinkBuilder } from './useLinkBuilder';
5
+
6
+ /**
7
+ * Helper to navigate to a screen using a href based on the linking options.
8
+ * @returns function that receives the href to navigate to.
9
+ */
10
+ export function useLinkTo() {
11
+ const navigation = React.useContext(NavigationContainerRefContext);
12
+ const { buildAction } = useLinkBuilder();
13
+
14
+ const linkTo = React.useCallback(
15
+ (href: string) => {
16
+ if (navigation === undefined) {
17
+ throw new Error(
18
+ "Couldn't find a navigation object. Is your component inside NavigationContainer?"
19
+ );
20
+ }
21
+
22
+ const action = buildAction(href);
23
+
24
+ navigation.dispatch(action);
25
+ },
26
+ [buildAction, navigation]
27
+ );
28
+
29
+ return linkTo;
30
+ }
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  getActionFromState as getActionFromStateDefault,
3
3
  getStateFromPath as getStateFromPathDefault,
4
- NavigationContainerRef,
5
- ParamListBase,
4
+ type NavigationContainerRef,
5
+ type ParamListBase,
6
6
  useNavigationIndependentTree,
7
7
  } from '@react-navigation/core';
8
8
  import * as React from 'react';
@@ -15,7 +15,7 @@ type ResultState = ReturnType<typeof getStateFromPathDefault>;
15
15
 
16
16
  type Options = LinkingOptions<ParamListBase>;
17
17
 
18
- let linkingHandlers: Symbol[] = [];
18
+ const linkingHandlers: symbol[] = [];
19
19
 
20
20
  export function useLinking(
21
21
  ref: React.RefObject<NavigationContainerRef<ParamListBase>>,
@@ -27,11 +27,11 @@ export function useLinking(
27
27
  getInitialURL = () =>
28
28
  Promise.race([
29
29
  Linking.getInitialURL(),
30
- new Promise<undefined>((resolve) =>
30
+ new Promise<undefined>((resolve) => {
31
31
  // Timeout in 150ms if `getInitialState` doesn't resolve
32
32
  // Workaround for https://github.com/facebook/react-native/issues/25675
33
- setTimeout(resolve, 150)
34
- ),
33
+ setTimeout(resolve, 150);
34
+ }),
35
35
  ]),
36
36
  subscribe = (listener) => {
37
37
  const callback = ({ url }: { url: string }) => listener(url);
@@ -55,7 +55,8 @@ export function useLinking(
55
55
  },
56
56
  getStateFromPath = getStateFromPathDefault,
57
57
  getActionFromState = getActionFromStateDefault,
58
- }: Options
58
+ }: Options,
59
+ onUnhandledLinking: (lastUnhandledLining: string | undefined) => void
59
60
  ) {
60
61
  const independent = useNavigationIndependentTree();
61
62
 
@@ -140,12 +141,21 @@ export function useLinking(
140
141
  if (enabledRef.current) {
141
142
  const url = getInitialURLRef.current();
142
143
 
143
- if (url != null && typeof url !== 'string') {
144
- return url.then((url) => {
145
- const state = getStateFromURL(url);
144
+ if (url != null) {
145
+ if (typeof url !== 'string') {
146
+ return url.then((url) => {
147
+ const state = getStateFromURL(url);
146
148
 
147
- return state;
148
- });
149
+ if (typeof url === 'string') {
150
+ // If the link were handled, it gets cleared in NavigationContainer
151
+ onUnhandledLinking(extractPathFromURL(prefixes, url));
152
+ }
153
+
154
+ return state;
155
+ });
156
+ } else {
157
+ onUnhandledLinking(extractPathFromURL(prefixes, url));
158
+ }
149
159
  }
150
160
 
151
161
  state = getStateFromURL(url);
@@ -161,7 +171,7 @@ export function useLinking(
161
171
  };
162
172
 
163
173
  return thenable as PromiseLike<ResultState | undefined>;
164
- }, [getStateFromURL]);
174
+ }, [getStateFromURL, onUnhandledLinking, prefixes]);
165
175
 
166
176
  React.useEffect(() => {
167
177
  const listener = (url: string) => {
@@ -173,14 +183,10 @@ export function useLinking(
173
183
  const state = navigation ? getStateFromURL(url) : undefined;
174
184
 
175
185
  if (navigation && state) {
176
- // Make sure that the routes in the state exist in the root navigator
177
- // Otherwise there's an error in the linking configuration
186
+ // If the link were handled, it gets cleared in NavigationContainer
187
+ onUnhandledLinking(extractPathFromURL(prefixes, url));
178
188
  const rootState = navigation.getRootState();
179
-
180
189
  if (state.routes.some((r) => !rootState?.routeNames.includes(r.name))) {
181
- console.warn(
182
- "The navigation state parsed from the URL contains routes not present in the root navigator. This usually means that the linking configuration doesn't match the navigation structure. See https://reactnavigation.org/docs/configuring-links for more details on how to specify a linking configuration."
183
- );
184
190
  return;
185
191
  }
186
192
 
@@ -207,7 +213,7 @@ export function useLinking(
207
213
  };
208
214
 
209
215
  return subscribe(listener);
210
- }, [enabled, getStateFromURL, ref, subscribe]);
216
+ }, [enabled, getStateFromURL, onUnhandledLinking, prefixes, ref, subscribe]);
211
217
 
212
218
  return {
213
219
  getInitialState,
@@ -3,9 +3,9 @@ import {
3
3
  getActionFromState as getActionFromStateDefault,
4
4
  getPathFromState as getPathFromStateDefault,
5
5
  getStateFromPath as getStateFromPathDefault,
6
- NavigationContainerRef,
7
- NavigationState,
8
- ParamListBase,
6
+ type NavigationContainerRef,
7
+ type NavigationState,
8
+ type ParamListBase,
9
9
  useNavigationIndependentTree,
10
10
  } from '@react-navigation/core';
11
11
  import isEqual from 'fast-deep-equal';
@@ -60,39 +60,16 @@ const findMatchingState = <T extends NavigationState>(
60
60
  /**
61
61
  * Run async function in series as it's called.
62
62
  */
63
- const series = (cb: () => Promise<void>) => {
64
- // Whether we're currently handling a callback
65
- let handling = false;
66
- let queue: (() => Promise<void>)[] = [];
67
-
68
- const callback = async () => {
69
- try {
70
- if (handling) {
71
- // If we're currently handling a previous event, wait before handling this one
72
- // Add the callback to the beginning of the queue
73
- queue.unshift(callback);
74
- return;
75
- }
76
-
77
- handling = true;
78
-
79
- await cb();
80
- } finally {
81
- handling = false;
82
-
83
- if (queue.length) {
84
- // If we have queued items, handle the last one
85
- const last = queue.pop();
86
-
87
- last?.();
88
- }
89
- }
63
+ export const series = (cb: () => Promise<void>) => {
64
+ let queue = Promise.resolve();
65
+ const callback = () => {
66
+ // eslint-disable-next-line promise/no-callback-in-promise
67
+ queue = queue.then(cb);
90
68
  };
91
-
92
69
  return callback;
93
70
  };
94
71
 
95
- let linkingHandlers: Symbol[] = [];
72
+ const linkingHandlers: symbol[] = [];
96
73
 
97
74
  type Options = LinkingOptions<ParamListBase>;
98
75
 
@@ -104,7 +81,8 @@ export function useLinking(
104
81
  getStateFromPath = getStateFromPathDefault,
105
82
  getPathFromState = getPathFromStateDefault,
106
83
  getActionFromState = getActionFromStateDefault,
107
- }: Options
84
+ }: Options,
85
+ onUnhandledLinking: (lastUnhandledLining: string | undefined) => void
108
86
  ) {
109
87
  const independent = useNavigationIndependentTree();
110
88
 
@@ -163,6 +141,17 @@ export function useLinking(
163
141
  getActionFromStateRef.current = getActionFromState;
164
142
  });
165
143
 
144
+ const validateRoutesNotExistInRootState = React.useCallback(
145
+ (state: ResultState) => {
146
+ const navigation = ref.current;
147
+ const rootState = navigation?.getRootState();
148
+ // Make sure that the routes in the state exist in the root navigator
149
+ // Otherwise there's an error in the linking configuration
150
+ return state?.routes.some((r) => !rootState?.routeNames.includes(r.name));
151
+ },
152
+ [ref]
153
+ );
154
+
166
155
  const server = React.useContext(ServerContext);
167
156
 
168
157
  const getInitialState = React.useCallback(() => {
@@ -178,6 +167,9 @@ export function useLinking(
178
167
  if (path) {
179
168
  value = getStateFromPathRef.current(path, configRef.current);
180
169
  }
170
+
171
+ // If the link were handled, it gets cleared in NavigationContainer
172
+ onUnhandledLinking(path);
181
173
  }
182
174
 
183
175
  const thenable = {
@@ -207,6 +199,8 @@ export function useLinking(
207
199
  return;
208
200
  }
209
201
 
202
+ const { location } = window;
203
+
210
204
  const path = location.pathname + location.search;
211
205
  const index = history.index;
212
206
 
@@ -230,14 +224,11 @@ export function useLinking(
230
224
  // We should only dispatch an action when going forward
231
225
  // Otherwise the action will likely add items to history, which would mess things up
232
226
  if (state) {
227
+ // If the link were handled, it gets cleared in NavigationContainer
228
+ onUnhandledLinking(path);
233
229
  // Make sure that the routes in the state exist in the root navigator
234
230
  // Otherwise there's an error in the linking configuration
235
- const rootState = navigation.getRootState();
236
-
237
- if (state.routes.some((r) => !rootState?.routeNames.includes(r.name))) {
238
- console.warn(
239
- "The navigation state parsed from the URL contains routes not present in the root navigator. This usually means that the linking configuration doesn't match the navigation structure. See https://reactnavigation.org/docs/configuring-links for more details on how to specify a linking configuration."
240
- );
231
+ if (validateRoutesNotExistInRootState(state)) {
241
232
  return;
242
233
  }
243
234
 
@@ -272,7 +263,13 @@ export function useLinking(
272
263
  navigation.resetRoot(state);
273
264
  }
274
265
  });
275
- }, [enabled, history, ref]);
266
+ }, [
267
+ enabled,
268
+ history,
269
+ onUnhandledLinking,
270
+ ref,
271
+ validateRoutesNotExistInRootState,
272
+ ]);
276
273
 
277
274
  React.useEffect(() => {
278
275
  if (!enabled) {
@@ -381,7 +378,12 @@ export function useLinking(
381
378
  const currentIndex = history.index;
382
379
 
383
380
  try {
384
- if (nextIndex !== -1 && nextIndex < currentIndex) {
381
+ if (
382
+ nextIndex !== -1 &&
383
+ nextIndex < currentIndex &&
384
+ // We should only go back if the entry exists and it's less than current index
385
+ history.get(nextIndex - currentIndex)
386
+ ) {
385
387
  // An existing entry for this path exists and it's less than current index, go back to that
386
388
  await history.go(nextIndex - currentIndex);
387
389
  } else {
@@ -0,0 +1,18 @@
1
+ import * as React from 'react';
2
+
3
+ import { LocaleDirContext } from './LocaleDirContext';
4
+
5
+ /**
6
+ * Hook to access the text direction specified in the `NavigationContainer`.
7
+ */
8
+ export function useLocale() {
9
+ const direction = React.useContext(LocaleDirContext);
10
+
11
+ if (direction === undefined) {
12
+ throw new Error(
13
+ "Couldn't determine the text direction. Is your component inside NavigationContainer?"
14
+ );
15
+ }
16
+
17
+ return { direction };
18
+ }
@@ -1,7 +1,8 @@
1
1
  import {
2
- EventArg,
3
- NavigationProp,
4
- useNavigation,
2
+ type EventArg,
3
+ NavigationContext,
4
+ type NavigationProp,
5
+ type ParamListBase,
5
6
  useRoute,
6
7
  } from '@react-navigation/core';
7
8
  import * as React from 'react';
@@ -49,13 +50,18 @@ function getScrollableNode(ref: React.RefObject<ScrollableWrapper>) {
49
50
  }
50
51
 
51
52
  export function useScrollToTop(ref: React.RefObject<ScrollableWrapper>) {
52
- const navigation = useNavigation();
53
+ const navigation = React.useContext(NavigationContext);
53
54
  const route = useRoute();
54
55
 
56
+ if (navigation === undefined) {
57
+ throw new Error(
58
+ "Couldn't find a navigation object. Is your component inside NavigationContainer?"
59
+ );
60
+ }
61
+
55
62
  React.useEffect(() => {
56
- let tabNavigations: NavigationProp<ReactNavigation.RootParamList>[] = [];
63
+ const tabNavigations: NavigationProp<ParamListBase>[] = [];
57
64
  let currentNavigation = navigation;
58
-
59
65
  // If the screen is nested inside multiple tab navigators, we should scroll to top for any of them
60
66
  // So we need to find all the parent tab navigators and add the listeners there
61
67
  while (currentNavigation) {
@@ -74,7 +80,7 @@ export function useScrollToTop(ref: React.RefObject<ScrollableWrapper>) {
74
80
  return tab.addListener(
75
81
  // We don't wanna import tab types here to avoid extra deps
76
82
  // in addition, there are multiple tab implementations
77
- // @ts-expect-error
83
+ // @ts-expect-error the `tabPress` event is only available when navigation type is tab
78
84
  'tabPress',
79
85
  (e: EventArg<'tabPress', true>) => {
80
86
  // We should scroll to top only when the screen is focused
@@ -6,6 +6,7 @@ export function useThenable<T>(create: () => PromiseLike<T>) {
6
6
  let initialState: [boolean, T | undefined] = [false, undefined];
7
7
 
8
8
  // Check if our thenable is synchronous
9
+ // eslint-disable-next-line promise/catch-or-return, promise/always-return
9
10
  promise.then((result) => {
10
11
  initialState = [true, result];
11
12
  });
@@ -0,0 +1,91 @@
1
+ import {
2
+ getStateFromPath,
3
+ NavigationContext,
4
+ type NavigationState,
5
+ type PartialState,
6
+ } from '@react-navigation/core';
7
+ import React from 'react';
8
+ import useLatestCallback from 'use-latest-callback';
9
+
10
+ import { LinkingContext } from './LinkingContext';
11
+ import { UnhandledLinkingContext } from './UnhandledLinkingContext';
12
+
13
+ // FIXME: don't rely on depth only to get the navigator state
14
+ function extractNavigatorSpecificState(
15
+ _: NavigationState,
16
+ pathState: PartialState<NavigationState>,
17
+ depth: number
18
+ ) {
19
+ let partialPathState: PartialState<NavigationState> | undefined = pathState;
20
+
21
+ let currentDepth = depth;
22
+ while (currentDepth) {
23
+ if (!partialPathState) {
24
+ return undefined;
25
+ }
26
+ partialPathState =
27
+ partialPathState.routes[partialPathState.routes.length - 1].state;
28
+ currentDepth--;
29
+ }
30
+ return partialPathState;
31
+ }
32
+
33
+ export function UNSTABLE_useUnhandledLinking() {
34
+ const navigation = React.useContext(NavigationContext);
35
+ const linking = React.useContext(LinkingContext);
36
+ const { setLastUnhandledLink, lastUnhandledLink } = React.useContext(
37
+ UnhandledLinkingContext
38
+ );
39
+
40
+ const { options } = linking;
41
+
42
+ const getStateForRouteNamesChange = (
43
+ currentState: NavigationState
44
+ ): PartialState<NavigationState> | undefined => {
45
+ if (lastUnhandledLink == null) {
46
+ // noop, nothing to handle
47
+ return;
48
+ }
49
+
50
+ // at web, the path is already extracted
51
+ const path = lastUnhandledLink;
52
+ if (!lastUnhandledLink) {
53
+ return;
54
+ }
55
+
56
+ // First, we parse the URL to get the desired state
57
+ const getStateFromPathHelper =
58
+ options?.getStateFromPath ?? getStateFromPath;
59
+
60
+ const pathState = getStateFromPathHelper(path, options?.config);
61
+
62
+ if (!pathState) {
63
+ return;
64
+ }
65
+
66
+ let depth = 0;
67
+ let parent = navigation;
68
+ while (parent) {
69
+ depth++;
70
+ parent = parent.getParent();
71
+ }
72
+
73
+ const state = extractNavigatorSpecificState(currentState, pathState, depth);
74
+
75
+ if (!state) {
76
+ return;
77
+ }
78
+
79
+ return state;
80
+ };
81
+
82
+ const clearUnhandledLink = useLatestCallback(() => {
83
+ setLastUnhandledLink(undefined);
84
+ });
85
+
86
+ return {
87
+ lastUnhandledLink,
88
+ getStateForRouteNamesChange,
89
+ clearUnhandledLink,
90
+ };
91
+ }
@@ -1,14 +0,0 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.ThemeContext = void 0;
7
- var React = _interopRequireWildcard(require("react"));
8
- var _DefaultTheme = require("./DefaultTheme");
9
- function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
10
- function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
11
- const ThemeContext = /*#__PURE__*/React.createContext(_DefaultTheme.DefaultTheme);
12
- exports.ThemeContext = ThemeContext;
13
- ThemeContext.displayName = 'ThemeContext';
14
- //# sourceMappingURL=ThemeContext.js.map
@@ -1 +0,0 @@
1
- {"version":3,"names":["ThemeContext","React","createContext","DefaultTheme","displayName"],"sourceRoot":"../../../src","sources":["theming/ThemeContext.tsx"],"mappings":";;;;;;AAAA;AAGA;AAA8C;AAAA;AAEvC,MAAMA,YAAY,gBAAGC,KAAK,CAACC,aAAa,CAAQC,0BAAY,CAAC;AAAC;AAErEH,YAAY,CAACI,WAAW,GAAG,cAAc"}