@react-navigation/core 8.0.0-alpha.32 → 8.0.0-alpha.34
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/lib/module/NavigationBuilderContext.js +8 -13
- package/lib/module/NavigationBuilderContext.js.map +1 -1
- package/lib/module/NavigationIndependentTree.js +42 -15
- package/lib/module/NavigationIndependentTree.js.map +1 -1
- package/lib/module/getPathFromState.js +41 -16
- package/lib/module/getPathFromState.js.map +1 -1
- package/lib/module/getPatternParts.js +43 -12
- package/lib/module/getPatternParts.js.map +1 -1
- package/lib/module/getStateFromPath.js +176 -110
- package/lib/module/getStateFromPath.js.map +1 -1
- package/lib/module/index.js +1 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/useDescriptors.js +2 -2
- package/lib/module/useDescriptors.js.map +1 -1
- package/lib/module/useFocusedListenersChildrenAdapter.js +2 -2
- package/lib/module/useFocusedListenersChildrenAdapter.js.map +1 -1
- package/lib/module/useNavigationBuilder.js +2 -2
- package/lib/module/useNavigationBuilder.js.map +1 -1
- package/lib/module/useNavigationCache.js +3 -3
- package/lib/module/useNavigationCache.js.map +1 -1
- package/lib/module/useNavigationHelpers.js +2 -2
- package/lib/module/useNavigationHelpers.js.map +1 -1
- package/lib/module/useOnAction.js +6 -4
- package/lib/module/useOnAction.js.map +1 -1
- package/lib/module/useOnGetNavigation.js +2 -2
- package/lib/module/useOnGetNavigation.js.map +1 -1
- package/lib/module/useOnGetState.js +2 -2
- package/lib/module/useOnGetState.js.map +1 -1
- package/lib/module/useOnPreventRemove.js +2 -2
- package/lib/module/useOnPreventRemove.js.map +1 -1
- package/lib/module/useOnRouteFocus.js +2 -2
- package/lib/module/useOnRouteFocus.js.map +1 -1
- package/lib/module/useOptionsGetters.js +2 -4
- package/lib/module/useOptionsGetters.js.map +1 -1
- package/lib/module/useScheduleUpdate.js +2 -2
- package/lib/module/useScheduleUpdate.js.map +1 -1
- package/lib/typescript/src/NavigationBuilderContext.d.ts +8 -5
- package/lib/typescript/src/NavigationBuilderContext.d.ts.map +1 -1
- package/lib/typescript/src/NavigationIndependentTree.d.ts.map +1 -1
- package/lib/typescript/src/getPathFromState.d.ts.map +1 -1
- package/lib/typescript/src/getPatternParts.d.ts +4 -2
- package/lib/typescript/src/getPatternParts.d.ts.map +1 -1
- package/lib/typescript/src/getStateFromPath.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +1 -0
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/types.d.ts +1 -1
- package/lib/typescript/src/types.d.ts.map +1 -1
- package/lib/typescript/src/useDescriptors.d.ts.map +1 -1
- package/lib/typescript/src/useNavigationBuilder.d.ts.map +1 -1
- package/lib/typescript/src/useOnAction.d.ts.map +1 -1
- package/lib/typescript/src/useOnGetNavigation.d.ts.map +1 -1
- package/lib/typescript/src/useOnGetState.d.ts.map +1 -1
- package/lib/typescript/src/useOnPreventRemove.d.ts.map +1 -1
- package/lib/typescript/src/useOnRouteFocus.d.ts.map +1 -1
- package/lib/typescript/src/useOptionsGetters.d.ts.map +1 -1
- package/lib/typescript/src/utilities.d.ts +3 -0
- package/lib/typescript/src/utilities.d.ts.map +1 -1
- package/package.json +2 -3
- package/src/NavigationBuilderContext.tsx +21 -17
- package/src/NavigationIndependentTree.tsx +48 -22
- package/src/getPathFromState.tsx +63 -15
- package/src/getPatternParts.tsx +75 -14
- package/src/getStateFromPath.tsx +251 -114
- package/src/index.tsx +1 -0
- package/src/types.tsx +1 -1
- package/src/useDescriptors.tsx +2 -1
- package/src/useFocusedListenersChildrenAdapter.tsx +2 -2
- package/src/useNavigationBuilder.tsx +2 -4
- package/src/useNavigationCache.tsx +3 -3
- package/src/useNavigationHelpers.tsx +2 -2
- package/src/useOnAction.tsx +6 -5
- package/src/useOnGetNavigation.tsx +3 -2
- package/src/useOnGetState.tsx +4 -2
- package/src/useOnPreventRemove.tsx +4 -2
- package/src/useOnRouteFocus.tsx +2 -4
- package/src/useOptionsGetters.tsx +3 -2
- package/src/useScheduleUpdate.tsx +2 -2
- package/src/utilities.tsx +5 -2
package/src/getPathFromState.tsx
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import type { NavigationState, PartialState } from '@react-navigation/routers';
|
|
2
2
|
import queryString from 'query-string';
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
combinePatternParts,
|
|
6
|
+
getPatternParts,
|
|
7
|
+
type PatternPart,
|
|
8
|
+
} from './getPatternParts';
|
|
5
9
|
import { getStateFromRouteParams } from './getStateFromRouteParams';
|
|
6
10
|
import type { PathConfig, PathConfigMap } from './types';
|
|
7
11
|
import { validatePathConfig } from './validatePathConfig';
|
|
@@ -228,16 +232,16 @@ export function getPathFromState<ParamList extends {}>(
|
|
|
228
232
|
const value = params[key];
|
|
229
233
|
|
|
230
234
|
if (value === undefined) {
|
|
231
|
-
let
|
|
235
|
+
let canBeOmitted = false;
|
|
232
236
|
|
|
233
237
|
for (const part of ownParts) {
|
|
234
|
-
if (part.
|
|
235
|
-
|
|
238
|
+
if (part.name === key) {
|
|
239
|
+
canBeOmitted = part.optional === true || part.repeat != null;
|
|
236
240
|
break;
|
|
237
241
|
}
|
|
238
242
|
}
|
|
239
243
|
|
|
240
|
-
if (
|
|
244
|
+
if (canBeOmitted) {
|
|
241
245
|
continue;
|
|
242
246
|
}
|
|
243
247
|
}
|
|
@@ -252,12 +256,12 @@ export function getPathFromState<ParamList extends {}>(
|
|
|
252
256
|
const claimedParams = new Set<string>();
|
|
253
257
|
|
|
254
258
|
for (const part of ownParts) {
|
|
255
|
-
if (part.
|
|
256
|
-
const value = currentParams[part.
|
|
259
|
+
if (part.name && part.name in currentParams) {
|
|
260
|
+
const value = currentParams[part.name];
|
|
257
261
|
|
|
258
262
|
if (value !== undefined) {
|
|
259
263
|
partValues.set(part, value);
|
|
260
|
-
claimedParams.add(part.
|
|
264
|
+
claimedParams.add(part.name);
|
|
261
265
|
}
|
|
262
266
|
}
|
|
263
267
|
}
|
|
@@ -283,7 +287,7 @@ export function getPathFromState<ParamList extends {}>(
|
|
|
283
287
|
}
|
|
284
288
|
|
|
285
289
|
for (const part of ownParts) {
|
|
286
|
-
if (part.
|
|
290
|
+
if (part.name && !partValues.has(part)) {
|
|
287
291
|
partValues.set(part, undefined);
|
|
288
292
|
}
|
|
289
293
|
}
|
|
@@ -326,7 +330,7 @@ export function getPathFromState<ParamList extends {}>(
|
|
|
326
330
|
let index = 0;
|
|
327
331
|
|
|
328
332
|
for (const part of parts) {
|
|
329
|
-
const { segment,
|
|
333
|
+
const { segment, name, optional, repeat } = part;
|
|
330
334
|
|
|
331
335
|
if (index > 0) {
|
|
332
336
|
path += '/';
|
|
@@ -343,7 +347,7 @@ export function getPathFromState<ParamList extends {}>(
|
|
|
343
347
|
}
|
|
344
348
|
|
|
345
349
|
// If the path has a pattern for a param, put the param in the path
|
|
346
|
-
if (
|
|
350
|
+
if (name) {
|
|
347
351
|
const value = partValues.get(part);
|
|
348
352
|
|
|
349
353
|
if (value === undefined && optional) {
|
|
@@ -351,6 +355,42 @@ export function getPathFromState<ParamList extends {}>(
|
|
|
351
355
|
continue;
|
|
352
356
|
}
|
|
353
357
|
|
|
358
|
+
if (repeat) {
|
|
359
|
+
if (value === null) {
|
|
360
|
+
throw new Error(
|
|
361
|
+
`The path pattern '${segment}' does not allow null for param '${name}'.`
|
|
362
|
+
);
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
if (Array.isArray(value)) {
|
|
366
|
+
throw new Error(
|
|
367
|
+
`The path pattern '${segment}' requires a string for param '${name}'.`
|
|
368
|
+
);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
const values =
|
|
372
|
+
value === undefined || value === '' ? [] : value.split('/');
|
|
373
|
+
|
|
374
|
+
if (repeat === 'one-or-more' && values.length === 0) {
|
|
375
|
+
throw new Error(
|
|
376
|
+
`The path pattern '${segment}' requires at least one value for param '${name}'.`
|
|
377
|
+
);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
if (values.some((item) => item.length === 0)) {
|
|
381
|
+
throw new Error(
|
|
382
|
+
`The path pattern '${segment}' does not allow empty values for param '${name}'.`
|
|
383
|
+
);
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
if (values.length === 0) {
|
|
387
|
+
path = path.replace(/\/$/, '');
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
path += values.map(encodePathParam).join('/');
|
|
391
|
+
continue;
|
|
392
|
+
}
|
|
393
|
+
|
|
354
394
|
// Valid characters according to
|
|
355
395
|
// https://datatracker.ietf.org/doc/html/rfc3986#section-3.3 (see pchar definition)
|
|
356
396
|
path += encodePathParam(String(value));
|
|
@@ -397,8 +437,13 @@ export function getPathFromState<ParamList extends {}>(
|
|
|
397
437
|
}
|
|
398
438
|
|
|
399
439
|
// Remove multiple as well as trailing slashes
|
|
400
|
-
|
|
401
|
-
|
|
440
|
+
if (path.includes('//')) {
|
|
441
|
+
path = path.replace(/\/+/g, '/');
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
if (path.length > 1 && path.endsWith('/')) {
|
|
445
|
+
path = path.slice(0, -1);
|
|
446
|
+
}
|
|
402
447
|
|
|
403
448
|
// If path doesn't start with a slash, add it
|
|
404
449
|
// This makes sure that history.pushState will update the path correctly instead of appending
|
|
@@ -417,7 +462,10 @@ const createConfigItem = (
|
|
|
417
462
|
const ownParts = getPatternParts(config);
|
|
418
463
|
|
|
419
464
|
if (parentParts) {
|
|
420
|
-
return {
|
|
465
|
+
return {
|
|
466
|
+
parts: combinePatternParts(parentParts, ownParts),
|
|
467
|
+
ownParts,
|
|
468
|
+
};
|
|
421
469
|
}
|
|
422
470
|
|
|
423
471
|
return { parts: ownParts, ownParts };
|
|
@@ -434,7 +482,7 @@ const createConfigItem = (
|
|
|
434
482
|
const ownParts = config.path ? getPatternParts(config.path) : [];
|
|
435
483
|
const parts =
|
|
436
484
|
config.exact !== true
|
|
437
|
-
?
|
|
485
|
+
? combinePatternParts(parentParts || [], ownParts)
|
|
438
486
|
: ownParts.length
|
|
439
487
|
? ownParts
|
|
440
488
|
: undefined;
|
package/src/getPatternParts.tsx
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
|
+
export type PatternPartRepeat = 'zero-or-more' | 'one-or-more';
|
|
2
|
+
|
|
1
3
|
export type PatternPart = {
|
|
2
4
|
segment: string;
|
|
3
|
-
|
|
4
|
-
|
|
5
|
+
name?: string;
|
|
6
|
+
pattern?: string;
|
|
5
7
|
optional?: boolean;
|
|
8
|
+
repeat?: PatternPartRepeat;
|
|
6
9
|
};
|
|
7
10
|
|
|
8
11
|
/**
|
|
@@ -15,6 +18,7 @@ export function getPatternParts(path: string): PatternPart[] {
|
|
|
15
18
|
|
|
16
19
|
let isRegex = false;
|
|
17
20
|
let isParam = false;
|
|
21
|
+
let hasRepeat = false;
|
|
18
22
|
let isEscaped = false;
|
|
19
23
|
let isInCharClass = false;
|
|
20
24
|
let regexInnerParens = 0;
|
|
@@ -55,7 +59,7 @@ export function getPatternParts(path: string): PatternPart[] {
|
|
|
55
59
|
// The ')' is part of the regex if we're already inside one
|
|
56
60
|
regexInnerParens--;
|
|
57
61
|
} else {
|
|
58
|
-
current.
|
|
62
|
+
current.pattern += char;
|
|
59
63
|
isRegex = false;
|
|
60
64
|
isParam = false;
|
|
61
65
|
}
|
|
@@ -65,7 +69,7 @@ export function getPatternParts(path: string): PatternPart[] {
|
|
|
65
69
|
);
|
|
66
70
|
}
|
|
67
71
|
} else if (char === '?' && !isRegex) {
|
|
68
|
-
if (current.
|
|
72
|
+
if (current.name) {
|
|
69
73
|
isParam = false;
|
|
70
74
|
|
|
71
75
|
current.optional = true;
|
|
@@ -74,6 +78,27 @@ export function getPatternParts(path: string): PatternPart[] {
|
|
|
74
78
|
`Encountered '?' without preceding ':' in path: ${path}`
|
|
75
79
|
);
|
|
76
80
|
}
|
|
81
|
+
} else if (
|
|
82
|
+
current.name &&
|
|
83
|
+
!isRegex &&
|
|
84
|
+
(char === '*' || char === '+') &&
|
|
85
|
+
(path[i + 1] == null ||
|
|
86
|
+
path[i + 1] === '/' ||
|
|
87
|
+
path[i + 1] === '?' ||
|
|
88
|
+
path[i + 1] === '*' ||
|
|
89
|
+
path[i + 1] === '+')
|
|
90
|
+
) {
|
|
91
|
+
if (
|
|
92
|
+
current.optional ||
|
|
93
|
+
(path[i + 1] !== undefined && path[i + 1] !== '/')
|
|
94
|
+
) {
|
|
95
|
+
throw new Error(`Cannot combine path param modifiers in path: ${path}`);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
isParam = false;
|
|
99
|
+
|
|
100
|
+
current.repeat = char === '*' ? 'zero-or-more' : 'one-or-more';
|
|
101
|
+
hasRepeat = true;
|
|
77
102
|
} else if (char == null || (char === '/' && !isRegex)) {
|
|
78
103
|
isParam = false;
|
|
79
104
|
|
|
@@ -84,12 +109,12 @@ export function getPatternParts(path: string): PatternPart[] {
|
|
|
84
109
|
continue;
|
|
85
110
|
}
|
|
86
111
|
|
|
87
|
-
if (current.
|
|
88
|
-
current.
|
|
112
|
+
if (current.name) {
|
|
113
|
+
current.name = current.name.replace(/^:/, '');
|
|
89
114
|
}
|
|
90
115
|
|
|
91
|
-
if (current.
|
|
92
|
-
current.
|
|
116
|
+
if (current.pattern) {
|
|
117
|
+
current.pattern = current.pattern.replace(/^\(/, '').replace(/\)$/, '');
|
|
93
118
|
}
|
|
94
119
|
|
|
95
120
|
parts.push(current);
|
|
@@ -102,8 +127,8 @@ export function getPatternParts(path: string): PatternPart[] {
|
|
|
102
127
|
}
|
|
103
128
|
|
|
104
129
|
if (isRegex) {
|
|
105
|
-
current.
|
|
106
|
-
current.
|
|
130
|
+
current.pattern = current.pattern || '';
|
|
131
|
+
current.pattern += char;
|
|
107
132
|
|
|
108
133
|
// Track escapes and character classes so parens inside them are not special
|
|
109
134
|
if (isEscaped) {
|
|
@@ -118,8 +143,8 @@ export function getPatternParts(path: string): PatternPart[] {
|
|
|
118
143
|
}
|
|
119
144
|
|
|
120
145
|
if (isParam && !isRegex) {
|
|
121
|
-
current.
|
|
122
|
-
current.
|
|
146
|
+
current.name = current.name || '';
|
|
147
|
+
current.name += char;
|
|
123
148
|
}
|
|
124
149
|
}
|
|
125
150
|
|
|
@@ -127,7 +152,7 @@ export function getPatternParts(path: string): PatternPart[] {
|
|
|
127
152
|
throw new Error(`Could not find closing ')' in path: ${path}`);
|
|
128
153
|
}
|
|
129
154
|
|
|
130
|
-
const params = parts.map((part) => part.
|
|
155
|
+
const params = parts.map((part) => part.name).filter(Boolean);
|
|
131
156
|
|
|
132
157
|
for (const [index, param] of params.entries()) {
|
|
133
158
|
if (params.indexOf(param) !== index) {
|
|
@@ -135,13 +160,49 @@ export function getPatternParts(path: string): PatternPart[] {
|
|
|
135
160
|
}
|
|
136
161
|
}
|
|
137
162
|
|
|
163
|
+
if (hasRepeat) {
|
|
164
|
+
validateRepeatedParts(parts, path);
|
|
165
|
+
}
|
|
166
|
+
|
|
138
167
|
return parts;
|
|
139
168
|
}
|
|
140
169
|
|
|
170
|
+
function validateRepeatedParts(parts: PatternPart[], path: string) {
|
|
171
|
+
let variablePart: PatternPart | undefined;
|
|
172
|
+
|
|
173
|
+
for (const part of parts) {
|
|
174
|
+
if (!part.name && part.segment !== '*') {
|
|
175
|
+
variablePart = undefined;
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (!part.repeat && !part.optional && part.segment !== '*') {
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (variablePart && (variablePart.repeat || part.repeat)) {
|
|
184
|
+
throw new Error(
|
|
185
|
+
`A repeated param must be separated from optional, repeated, or wildcard segments by a static segment: ${path}`
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
variablePart = part;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
141
193
|
export function combinePatternParts<T extends PatternPart>(
|
|
142
194
|
parentParts: T[],
|
|
143
195
|
parts: T[],
|
|
144
196
|
exact = false
|
|
145
197
|
): T[] {
|
|
146
|
-
|
|
198
|
+
const combined = exact ? parts : [...parentParts, ...parts];
|
|
199
|
+
|
|
200
|
+
if (combined.some((part) => part.repeat)) {
|
|
201
|
+
validateRepeatedParts(
|
|
202
|
+
combined,
|
|
203
|
+
combined.map((part) => part.segment).join('/')
|
|
204
|
+
);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
return combined;
|
|
147
208
|
}
|