1 | "use strict";
|
2 |
|
3 | Object.defineProperty(exports, "__esModule", {
|
4 | value: true
|
5 | });
|
6 | exports.default = useNavigationBuilder;
|
7 |
|
8 | var _routers = require("@react-navigation/routers");
|
9 |
|
10 | var React = _interopRequireWildcard(require("react"));
|
11 |
|
12 | var _reactIs = require("react-is");
|
13 |
|
14 | var _Group = _interopRequireDefault(require("./Group"));
|
15 |
|
16 | var _isArrayEqual = _interopRequireDefault(require("./isArrayEqual"));
|
17 |
|
18 | var _isRecordEqual = _interopRequireDefault(require("./isRecordEqual"));
|
19 |
|
20 | var _NavigationHelpersContext = _interopRequireDefault(require("./NavigationHelpersContext"));
|
21 |
|
22 | var _NavigationRouteContext = _interopRequireDefault(require("./NavigationRouteContext"));
|
23 |
|
24 | var _NavigationStateContext = _interopRequireDefault(require("./NavigationStateContext"));
|
25 |
|
26 | var _PreventRemoveProvider = _interopRequireDefault(require("./PreventRemoveProvider"));
|
27 |
|
28 | var _Screen = _interopRequireDefault(require("./Screen"));
|
29 |
|
30 | var _types = require("./types");
|
31 |
|
32 | var _useChildListeners = _interopRequireDefault(require("./useChildListeners"));
|
33 |
|
34 | var _useComponent = _interopRequireDefault(require("./useComponent"));
|
35 |
|
36 | var _useCurrentRender = _interopRequireDefault(require("./useCurrentRender"));
|
37 |
|
38 | var _useDescriptors = _interopRequireDefault(require("./useDescriptors"));
|
39 |
|
40 | var _useEventEmitter = _interopRequireDefault(require("./useEventEmitter"));
|
41 |
|
42 | var _useFocusedListenersChildrenAdapter = _interopRequireDefault(require("./useFocusedListenersChildrenAdapter"));
|
43 |
|
44 | var _useFocusEvents = _interopRequireDefault(require("./useFocusEvents"));
|
45 |
|
46 | var _useKeyedChildListeners = _interopRequireDefault(require("./useKeyedChildListeners"));
|
47 |
|
48 | var _useNavigationHelpers = _interopRequireDefault(require("./useNavigationHelpers"));
|
49 |
|
50 | var _useOnAction = _interopRequireDefault(require("./useOnAction"));
|
51 |
|
52 | var _useOnGetState = _interopRequireDefault(require("./useOnGetState"));
|
53 |
|
54 | var _useOnRouteFocus = _interopRequireDefault(require("./useOnRouteFocus"));
|
55 |
|
56 | var _useRegisterNavigator = _interopRequireDefault(require("./useRegisterNavigator"));
|
57 |
|
58 | var _useScheduleUpdate = _interopRequireDefault(require("./useScheduleUpdate"));
|
59 |
|
60 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
61 |
|
62 | 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); }
|
63 |
|
64 | 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; }
|
65 |
|
66 |
|
67 |
|
68 | _types.PrivateValueStore;
|
69 |
|
70 | const isValidKey = key => key === undefined || typeof key === 'string' && key !== '';
|
71 |
|
72 |
|
73 |
|
74 |
|
75 |
|
76 |
|
77 |
|
78 | const getRouteConfigsFromChildren = (children, groupKey, groupOptions) => {
|
79 | const configs = React.Children.toArray(children).reduce((acc, child) => {
|
80 | var _child$type, _child$props;
|
81 |
|
82 | if ( React.isValidElement(child)) {
|
83 | if (child.type === _Screen.default) {
|
84 |
|
85 |
|
86 | if (!isValidKey(child.props.navigationKey)) {
|
87 | throw new Error(`Got an invalid 'navigationKey' prop (${JSON.stringify(child.props.navigationKey)}) for the screen '${child.props.name}'. It must be a non-empty string or 'undefined'.`);
|
88 | }
|
89 |
|
90 | acc.push({
|
91 | keys: [groupKey, child.props.navigationKey],
|
92 | options: groupOptions,
|
93 | props: child.props
|
94 | });
|
95 | return acc;
|
96 | }
|
97 |
|
98 | if (child.type === React.Fragment || child.type === _Group.default) {
|
99 | if (!isValidKey(child.props.navigationKey)) {
|
100 | throw new Error(`Got an invalid 'navigationKey' prop (${JSON.stringify(child.props.navigationKey)}) for the group. It must be a non-empty string or 'undefined'.`);
|
101 | }
|
102 |
|
103 |
|
104 |
|
105 | acc.push(...getRouteConfigsFromChildren(child.props.children, child.props.navigationKey, child.type !== _Group.default ? groupOptions : groupOptions != null ? [...groupOptions, child.props.screenOptions] : [child.props.screenOptions]));
|
106 | return acc;
|
107 | }
|
108 | }
|
109 |
|
110 | throw new Error(`A navigator can only contain 'Screen', 'Group' or 'React.Fragment' as its direct children (found ${/*#__PURE__*/React.isValidElement(child) ? `'${typeof child.type === 'string' ? child.type : (_child$type = child.type) === null || _child$type === void 0 ? void 0 : _child$type.name}'${(_child$props = child.props) !== null && _child$props !== void 0 && _child$props.name ? ` for the screen '${child.props.name}'` : ''}` : typeof child === 'object' ? JSON.stringify(child) : `'${String(child)}'`}). To render this component in the navigator, pass it in the 'component' prop to 'Screen'.`);
|
111 | }, []);
|
112 |
|
113 | if (process.env.NODE_ENV !== 'production') {
|
114 | configs.forEach(config => {
|
115 | const {
|
116 | name,
|
117 | children,
|
118 | component,
|
119 | getComponent
|
120 | } = config.props;
|
121 |
|
122 | if (typeof name !== 'string' || !name) {
|
123 | throw new Error(`Got an invalid name (${JSON.stringify(name)}) for the screen. It must be a non-empty string.`);
|
124 | }
|
125 |
|
126 | if (children != null || component !== undefined || getComponent !== undefined) {
|
127 | if (children != null && component !== undefined) {
|
128 | throw new Error(`Got both 'component' and 'children' props for the screen '${name}'. You must pass only one of them.`);
|
129 | }
|
130 |
|
131 | if (children != null && getComponent !== undefined) {
|
132 | throw new Error(`Got both 'getComponent' and 'children' props for the screen '${name}'. You must pass only one of them.`);
|
133 | }
|
134 |
|
135 | if (component !== undefined && getComponent !== undefined) {
|
136 | throw new Error(`Got both 'component' and 'getComponent' props for the screen '${name}'. You must pass only one of them.`);
|
137 | }
|
138 |
|
139 | if (children != null && typeof children !== 'function') {
|
140 | throw new Error(`Got an invalid value for 'children' prop for the screen '${name}'. It must be a function returning a React Element.`);
|
141 | }
|
142 |
|
143 | if (component !== undefined && !(0, _reactIs.isValidElementType)(component)) {
|
144 | throw new Error(`Got an invalid value for 'component' prop for the screen '${name}'. It must be a valid React Component.`);
|
145 | }
|
146 |
|
147 | if (getComponent !== undefined && typeof getComponent !== 'function') {
|
148 | throw new Error(`Got an invalid value for 'getComponent' prop for the screen '${name}'. It must be a function returning a React Component.`);
|
149 | }
|
150 |
|
151 | if (typeof component === 'function') {
|
152 | if (component.name === 'component') {
|
153 |
|
154 |
|
155 |
|
156 | console.warn(`Looks like you're passing an inline function for 'component' prop for the screen '${name}' (e.g. component={() => <SomeComponent />}). Passing an inline function will cause the component state to be lost on re-render and cause perf issues since it's re-created every render. You can pass the function as children to 'Screen' instead to achieve the desired behaviour.`);
|
157 | } else if (/^[a-z]/.test(component.name)) {
|
158 | console.warn(`Got a component with the name '${component.name}' for the screen '${name}'. React Components must start with an uppercase letter. If you're passing a regular function and not a component, pass it as children to 'Screen' instead. Otherwise capitalize your component's name.`);
|
159 | }
|
160 | }
|
161 | } else {
|
162 | throw new Error(`Couldn't find a 'component', 'getComponent' or 'children' prop for the screen '${name}'. This can happen if you passed 'undefined'. You likely forgot to export your component from the file it's defined in, or mixed up default import and named import when importing.`);
|
163 | }
|
164 | });
|
165 | }
|
166 |
|
167 | return configs;
|
168 | };
|
169 |
|
170 |
|
171 |
|
172 |
|
173 |
|
174 |
|
175 |
|
176 |
|
177 |
|
178 | function useNavigationBuilder(createRouter, options) {
|
179 | const navigatorKey = (0, _useRegisterNavigator.default)();
|
180 | const route = React.useContext(_NavigationRouteContext.default);
|
181 | const {
|
182 | children,
|
183 | screenListeners,
|
184 | ...rest
|
185 | } = options;
|
186 | const {
|
187 | current: router
|
188 | } = React.useRef(createRouter({ ...rest,
|
189 | ...(route !== null && route !== void 0 && route.params && route.params.state == null && route.params.initial !== false && typeof route.params.screen === 'string' ? {
|
190 | initialRouteName: route.params.screen
|
191 | } : null)
|
192 | }));
|
193 | const routeConfigs = getRouteConfigsFromChildren(children);
|
194 | const screens = routeConfigs.reduce((acc, config) => {
|
195 | if (config.props.name in acc) {
|
196 | throw new Error(`A navigator cannot contain multiple 'Screen' components with the same name (found duplicate screen named '${config.props.name}')`);
|
197 | }
|
198 |
|
199 | acc[config.props.name] = config;
|
200 | return acc;
|
201 | }, {});
|
202 | const routeNames = routeConfigs.map(config => config.props.name);
|
203 | const routeKeyList = routeNames.reduce((acc, curr) => {
|
204 | acc[curr] = screens[curr].keys.map(key => key !== null && key !== void 0 ? key : '').join(':');
|
205 | return acc;
|
206 | }, {});
|
207 | const routeParamList = routeNames.reduce((acc, curr) => {
|
208 | const {
|
209 | initialParams
|
210 | } = screens[curr].props;
|
211 | acc[curr] = initialParams;
|
212 | return acc;
|
213 | }, {});
|
214 | const routeGetIdList = routeNames.reduce((acc, curr) => Object.assign(acc, {
|
215 | [curr]: screens[curr].props.getId
|
216 | }), {});
|
217 |
|
218 | if (!routeNames.length) {
|
219 | throw new Error("Couldn't find any screens for the navigator. Have you defined any screens as its children?");
|
220 | }
|
221 |
|
222 | const isStateValid = React.useCallback(state => state.type === undefined || state.type === router.type, [router.type]);
|
223 | const isStateInitialized = React.useCallback(state => state !== undefined && state.stale === false && isStateValid(state), [isStateValid]);
|
224 | const {
|
225 | state: currentState,
|
226 | getState: getCurrentState,
|
227 | setState: setCurrentState,
|
228 | setKey,
|
229 | getKey,
|
230 | getIsInitial
|
231 | } = React.useContext(_NavigationStateContext.default);
|
232 | const stateCleanedUp = React.useRef(false);
|
233 | const cleanUpState = React.useCallback(() => {
|
234 | setCurrentState(undefined);
|
235 | stateCleanedUp.current = true;
|
236 | }, [setCurrentState]);
|
237 | const setState = React.useCallback(state => {
|
238 | if (stateCleanedUp.current) {
|
239 |
|
240 |
|
241 |
|
242 | return;
|
243 | }
|
244 |
|
245 | setCurrentState(state);
|
246 | }, [setCurrentState]);
|
247 | const [initializedState, isFirstStateInitialization] = React.useMemo(() => {
|
248 | var _route$params4;
|
249 |
|
250 | const initialRouteParamList = routeNames.reduce((acc, curr) => {
|
251 | var _route$params, _route$params2, _route$params3;
|
252 |
|
253 | const {
|
254 | initialParams
|
255 | } = screens[curr].props;
|
256 | const initialParamsFromParams = (route === null || route === void 0 ? void 0 : (_route$params = route.params) === null || _route$params === void 0 ? void 0 : _route$params.state) == null && (route === null || route === void 0 ? void 0 : (_route$params2 = route.params) === null || _route$params2 === void 0 ? void 0 : _route$params2.initial) !== false && (route === null || route === void 0 ? void 0 : (_route$params3 = route.params) === null || _route$params3 === void 0 ? void 0 : _route$params3.screen) === curr ? route.params.params : undefined;
|
257 | acc[curr] = initialParams !== undefined || initialParamsFromParams !== undefined ? { ...initialParams,
|
258 | ...initialParamsFromParams
|
259 | } : undefined;
|
260 | return acc;
|
261 | }, {});
|
262 |
|
263 |
|
264 |
|
265 |
|
266 | if ((currentState === undefined || !isStateValid(currentState)) && (route === null || route === void 0 ? void 0 : (_route$params4 = route.params) === null || _route$params4 === void 0 ? void 0 : _route$params4.state) == null) {
|
267 | return [router.getInitialState({
|
268 | routeNames,
|
269 | routeParamList: initialRouteParamList,
|
270 | routeGetIdList
|
271 | }), true];
|
272 | } else {
|
273 | var _route$params$state, _route$params5;
|
274 |
|
275 | return [router.getRehydratedState((_route$params$state = route === null || route === void 0 ? void 0 : (_route$params5 = route.params) === null || _route$params5 === void 0 ? void 0 : _route$params5.state) !== null && _route$params$state !== void 0 ? _route$params$state : currentState, {
|
276 | routeNames,
|
277 | routeParamList: initialRouteParamList,
|
278 | routeGetIdList
|
279 | }), false];
|
280 | }
|
281 |
|
282 |
|
283 |
|
284 |
|
285 |
|
286 |
|
287 | }, [currentState, router, isStateValid]);
|
288 | const previousRouteKeyListRef = React.useRef(routeKeyList);
|
289 | React.useEffect(() => {
|
290 | previousRouteKeyListRef.current = routeKeyList;
|
291 | });
|
292 | const previousRouteKeyList = previousRouteKeyListRef.current;
|
293 | let state =
|
294 |
|
295 |
|
296 | isStateInitialized(currentState) ? currentState : initializedState;
|
297 | let nextState = state;
|
298 |
|
299 | if (!(0, _isArrayEqual.default)(state.routeNames, routeNames) || !(0, _isRecordEqual.default)(routeKeyList, previousRouteKeyList)) {
|
300 |
|
301 | nextState = router.getStateForRouteNamesChange(state, {
|
302 | routeNames,
|
303 | routeParamList,
|
304 | routeGetIdList,
|
305 | routeKeyChanges: Object.keys(routeKeyList).filter(name => previousRouteKeyList.hasOwnProperty(name) && routeKeyList[name] !== previousRouteKeyList[name])
|
306 | });
|
307 | }
|
308 |
|
309 | const previousNestedParamsRef = React.useRef(route === null || route === void 0 ? void 0 : route.params);
|
310 | React.useEffect(() => {
|
311 | previousNestedParamsRef.current = route === null || route === void 0 ? void 0 : route.params;
|
312 | }, [route === null || route === void 0 ? void 0 : route.params]);
|
313 |
|
314 | if (route !== null && route !== void 0 && route.params) {
|
315 | const previousParams = previousNestedParamsRef.current;
|
316 | let action;
|
317 |
|
318 | if (typeof route.params.state === 'object' && route.params.state != null && route.params !== previousParams) {
|
319 |
|
320 | action = _routers.CommonActions.reset(route.params.state);
|
321 | } else if (typeof route.params.screen === 'string' && (route.params.initial === false && isFirstStateInitialization || route.params !== previousParams)) {
|
322 |
|
323 | action = _routers.CommonActions.navigate({
|
324 | name: route.params.screen,
|
325 | params: route.params.params,
|
326 | path: route.params.path
|
327 | });
|
328 | }
|
329 |
|
330 |
|
331 | const updatedState = action ? router.getStateForAction(nextState, action, {
|
332 | routeNames,
|
333 | routeParamList,
|
334 | routeGetIdList
|
335 | }) : null;
|
336 | nextState = updatedState !== null ? router.getRehydratedState(updatedState, {
|
337 | routeNames,
|
338 | routeParamList,
|
339 | routeGetIdList
|
340 | }) : nextState;
|
341 | }
|
342 |
|
343 | const shouldUpdate = state !== nextState;
|
344 | (0, _useScheduleUpdate.default)(() => {
|
345 | if (shouldUpdate) {
|
346 |
|
347 | setState(nextState);
|
348 | }
|
349 | });
|
350 |
|
351 |
|
352 |
|
353 | state = nextState;
|
354 | React.useEffect(() => {
|
355 | setKey(navigatorKey);
|
356 |
|
357 | if (!getIsInitial()) {
|
358 |
|
359 |
|
360 |
|
361 | setState(nextState);
|
362 | }
|
363 |
|
364 | return () => {
|
365 |
|
366 |
|
367 |
|
368 |
|
369 | setTimeout(() => {
|
370 | if (getCurrentState() !== undefined && getKey() === navigatorKey) {
|
371 | cleanUpState();
|
372 | }
|
373 | }, 0);
|
374 | };
|
375 | }, []);
|
376 |
|
377 |
|
378 |
|
379 | const initializedStateRef = React.useRef();
|
380 | initializedStateRef.current = initializedState;
|
381 | const getState = React.useCallback(() => {
|
382 | const currentState = getCurrentState();
|
383 | return isStateInitialized(currentState) ? currentState : initializedStateRef.current;
|
384 | }, [getCurrentState, isStateInitialized]);
|
385 | const emitter = (0, _useEventEmitter.default)(e => {
|
386 | let routeNames = [];
|
387 | let route;
|
388 |
|
389 | if (e.target) {
|
390 | var _route;
|
391 |
|
392 | route = state.routes.find(route => route.key === e.target);
|
393 |
|
394 | if ((_route = route) !== null && _route !== void 0 && _route.name) {
|
395 | routeNames.push(route.name);
|
396 | }
|
397 | } else {
|
398 | route = state.routes[state.index];
|
399 | routeNames.push(...Object.keys(screens).filter(name => {
|
400 | var _route2;
|
401 |
|
402 | return ((_route2 = route) === null || _route2 === void 0 ? void 0 : _route2.name) === name;
|
403 | }));
|
404 | }
|
405 |
|
406 | if (route == null) {
|
407 | return;
|
408 | }
|
409 |
|
410 | const navigation = descriptors[route.key].navigation;
|
411 | const listeners = [].concat(
|
412 | ...[screenListeners, ...routeNames.map(name => {
|
413 | const {
|
414 | listeners
|
415 | } = screens[name].props;
|
416 | return listeners;
|
417 | })].map(listeners => {
|
418 | const map = typeof listeners === 'function' ? listeners({
|
419 | route: route,
|
420 | navigation
|
421 | }) : listeners;
|
422 | return map ? Object.keys(map).filter(type => type === e.type).map(type => map === null || map === void 0 ? void 0 : map[type]) : undefined;
|
423 | }))
|
424 |
|
425 | .filter((cb, i, self) => cb && self.lastIndexOf(cb) === i);
|
426 | listeners.forEach(listener => listener === null || listener === void 0 ? void 0 : listener(e));
|
427 | });
|
428 | (0, _useFocusEvents.default)({
|
429 | state,
|
430 | emitter
|
431 | });
|
432 | React.useEffect(() => {
|
433 | emitter.emit({
|
434 | type: 'state',
|
435 | data: {
|
436 | state
|
437 | }
|
438 | });
|
439 | }, [emitter, state]);
|
440 | const {
|
441 | listeners: childListeners,
|
442 | addListener
|
443 | } = (0, _useChildListeners.default)();
|
444 | const {
|
445 | keyedListeners,
|
446 | addKeyedListener
|
447 | } = (0, _useKeyedChildListeners.default)();
|
448 | const onAction = (0, _useOnAction.default)({
|
449 | router,
|
450 | getState,
|
451 | setState,
|
452 | key: route === null || route === void 0 ? void 0 : route.key,
|
453 | actionListeners: childListeners.action,
|
454 | beforeRemoveListeners: keyedListeners.beforeRemove,
|
455 | routerConfigOptions: {
|
456 | routeNames,
|
457 | routeParamList,
|
458 | routeGetIdList
|
459 | },
|
460 | emitter
|
461 | });
|
462 | const onRouteFocus = (0, _useOnRouteFocus.default)({
|
463 | router,
|
464 | key: route === null || route === void 0 ? void 0 : route.key,
|
465 | getState,
|
466 | setState
|
467 | });
|
468 | const navigation = (0, _useNavigationHelpers.default)({
|
469 | id: options.id,
|
470 | onAction,
|
471 | getState,
|
472 | emitter,
|
473 | router
|
474 | });
|
475 | (0, _useFocusedListenersChildrenAdapter.default)({
|
476 | navigation,
|
477 | focusedListeners: childListeners.focus
|
478 | });
|
479 | (0, _useOnGetState.default)({
|
480 | getState,
|
481 | getStateListeners: keyedListeners.getState
|
482 | });
|
483 | const descriptors = (0, _useDescriptors.default)({
|
484 | state,
|
485 | screens,
|
486 | navigation,
|
487 | screenOptions: options.screenOptions,
|
488 | defaultScreenOptions: options.defaultScreenOptions,
|
489 | onAction,
|
490 | getState,
|
491 | setState,
|
492 | onRouteFocus,
|
493 | addListener,
|
494 | addKeyedListener,
|
495 | router,
|
496 |
|
497 | emitter
|
498 | });
|
499 | (0, _useCurrentRender.default)({
|
500 | state,
|
501 | navigation,
|
502 | descriptors
|
503 | });
|
504 | const NavigationContent = (0, _useComponent.default)(children => React.createElement(_NavigationHelpersContext.default.Provider, {
|
505 | value: navigation
|
506 | }, React.createElement(_PreventRemoveProvider.default, null, children)));
|
507 | return {
|
508 | state,
|
509 | navigation,
|
510 | descriptors,
|
511 | NavigationContent
|
512 | };
|
513 | }
|
514 |
|
\ | No newline at end of file |