@react-navigation/core 7.17.2 → 7.17.4
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/StaticNavigation.js +0 -19
- package/lib/module/StaticNavigation.js.map +1 -1
- package/lib/module/checkSerializable.js +11 -4
- package/lib/module/checkSerializable.js.map +1 -1
- package/lib/module/getActionFromState.js +15 -2
- package/lib/module/getActionFromState.js.map +1 -1
- package/lib/module/getStateFromPath.js +98 -74
- package/lib/module/getStateFromPath.js.map +1 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/types.js +55 -0
- package/lib/module/types.js.map +1 -1
- package/lib/module/useEventEmitter.js +28 -29
- package/lib/module/useEventEmitter.js.map +1 -1
- package/lib/module/useNavigationBuilder.js +48 -47
- package/lib/module/useNavigationBuilder.js.map +1 -1
- package/lib/module/useRouteCache.js +2 -1
- package/lib/module/useRouteCache.js.map +1 -1
- package/lib/typescript/src/StaticNavigation.d.ts +2 -63
- package/lib/typescript/src/StaticNavigation.d.ts.map +1 -1
- package/lib/typescript/src/checkSerializable.d.ts +5 -3
- package/lib/typescript/src/checkSerializable.d.ts.map +1 -1
- package/lib/typescript/src/getActionFromState.d.ts.map +1 -1
- package/lib/typescript/src/getStateFromPath.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +1 -1
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/types.d.ts +102 -2
- package/lib/typescript/src/types.d.ts.map +1 -1
- package/lib/typescript/src/useEventEmitter.d.ts.map +1 -1
- package/lib/typescript/src/useNavigationBuilder.d.ts.map +1 -1
- package/lib/typescript/src/useRouteCache.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/StaticNavigation.tsx +1 -91
- package/src/checkSerializable.tsx +22 -12
- package/src/getActionFromState.tsx +20 -4
- package/src/getStateFromPath.tsx +147 -100
- package/src/index.tsx +0 -1
- package/src/types.tsx +138 -4
- package/src/useEventEmitter.tsx +33 -38
- package/src/useNavigationBuilder.tsx +77 -76
- package/src/useRouteCache.tsx +2 -1
package/src/getStateFromPath.tsx
CHANGED
|
@@ -29,6 +29,8 @@ type RouteConfig = {
|
|
|
29
29
|
params: { screen: string; name?: string; index: number }[];
|
|
30
30
|
routeNames: string[];
|
|
31
31
|
parse?: ParseConfig;
|
|
32
|
+
explicitParamNames?: Set<string>;
|
|
33
|
+
hasNestedScreens: boolean;
|
|
32
34
|
};
|
|
33
35
|
|
|
34
36
|
type InitialRouteConfig = {
|
|
@@ -49,6 +51,22 @@ type ParsedRoute = {
|
|
|
49
51
|
type ConfigResources = {
|
|
50
52
|
initialRoutes: InitialRouteConfig[];
|
|
51
53
|
configs: RouteConfig[];
|
|
54
|
+
configsByScreen: Record<string, RouteConfig[]>;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const NESTED_SCREEN_PARAM_NAMES = [
|
|
58
|
+
'screen',
|
|
59
|
+
'params',
|
|
60
|
+
'initial',
|
|
61
|
+
'path',
|
|
62
|
+
'merge',
|
|
63
|
+
'pop',
|
|
64
|
+
];
|
|
65
|
+
|
|
66
|
+
const getExplicitParamNames = (parse?: ParseConfig) => {
|
|
67
|
+
const names = Object.entries(parse ?? {}).map(([name]) => name);
|
|
68
|
+
|
|
69
|
+
return names.length ? new Set(names) : undefined;
|
|
52
70
|
};
|
|
53
71
|
|
|
54
72
|
/**
|
|
@@ -76,7 +94,8 @@ export function getStateFromPath<ParamList extends {}>(
|
|
|
76
94
|
path: string,
|
|
77
95
|
options?: Options<ParamList>
|
|
78
96
|
): ResultState | undefined {
|
|
79
|
-
const { initialRoutes, configs } =
|
|
97
|
+
const { initialRoutes, configs, configsByScreen } =
|
|
98
|
+
getConfigResources(options);
|
|
80
99
|
|
|
81
100
|
const screens = options?.screens;
|
|
82
101
|
|
|
@@ -103,15 +122,23 @@ export function getStateFromPath<ParamList extends {}>(
|
|
|
103
122
|
remaining = remaining.replace(normalizedPrefix, '');
|
|
104
123
|
}
|
|
105
124
|
|
|
125
|
+
const decodedSegments: string[] = [];
|
|
126
|
+
|
|
127
|
+
for (const segment of remaining.split('/')) {
|
|
128
|
+
if (!segment) {
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
try {
|
|
133
|
+
decodedSegments.push(decodeURIComponent(segment));
|
|
134
|
+
} catch {
|
|
135
|
+
return undefined;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
106
139
|
if (screens === undefined) {
|
|
107
140
|
// When no config is specified, use the path segments as route names
|
|
108
|
-
const routes =
|
|
109
|
-
.split('/')
|
|
110
|
-
.filter(Boolean)
|
|
111
|
-
.map((segment) => {
|
|
112
|
-
const name = decodeURIComponent(segment);
|
|
113
|
-
return { name };
|
|
114
|
-
});
|
|
141
|
+
const routes = decodedSegments.map((name) => ({ name }));
|
|
115
142
|
|
|
116
143
|
if (routes.length) {
|
|
117
144
|
return createNestedStateObject(path, routes, initialRoutes);
|
|
@@ -130,32 +157,26 @@ export function getStateFromPath<ParamList extends {}>(
|
|
|
130
157
|
path,
|
|
131
158
|
match.routeNames.map((name) => ({ name })),
|
|
132
159
|
initialRoutes,
|
|
133
|
-
|
|
160
|
+
match
|
|
134
161
|
);
|
|
135
162
|
}
|
|
136
163
|
|
|
137
164
|
return undefined;
|
|
138
165
|
}
|
|
139
166
|
|
|
140
|
-
let result: PartialState<NavigationState> | undefined;
|
|
141
|
-
let current: PartialState<NavigationState> | undefined;
|
|
142
|
-
|
|
143
167
|
// We match the whole path against the regex instead of segments
|
|
144
168
|
// This makes sure matches such as wildcard will catch any unmatched routes, even if nested
|
|
145
|
-
const { routes,
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
remaining = remainingPath;
|
|
151
|
-
result = current;
|
|
152
|
-
}
|
|
169
|
+
const { routes, config } = matchAgainstConfigs(
|
|
170
|
+
remaining,
|
|
171
|
+
configs,
|
|
172
|
+
configsByScreen
|
|
173
|
+
);
|
|
153
174
|
|
|
154
|
-
if (
|
|
175
|
+
if (routes === undefined || config === undefined) {
|
|
155
176
|
return undefined;
|
|
156
177
|
}
|
|
157
178
|
|
|
158
|
-
return
|
|
179
|
+
return createNestedStateObject(path, routes, initialRoutes, config);
|
|
159
180
|
}
|
|
160
181
|
|
|
161
182
|
/**
|
|
@@ -189,12 +210,16 @@ function prepareConfigResources(options?: Options<{}>) {
|
|
|
189
210
|
|
|
190
211
|
checkForDuplicatedConfigs(configs);
|
|
191
212
|
|
|
192
|
-
const
|
|
213
|
+
const configsByScreen: Record<string, RouteConfig[]> = {};
|
|
214
|
+
|
|
215
|
+
for (const c of configs) {
|
|
216
|
+
(configsByScreen[c.screen] ??= []).push(c);
|
|
217
|
+
}
|
|
193
218
|
|
|
194
219
|
return {
|
|
195
220
|
initialRoutes,
|
|
196
221
|
configs,
|
|
197
|
-
|
|
222
|
+
configsByScreen,
|
|
198
223
|
};
|
|
199
224
|
}
|
|
200
225
|
|
|
@@ -337,17 +362,14 @@ function checkForDuplicatedConfigs(configs: RouteConfig[]) {
|
|
|
337
362
|
}, {});
|
|
338
363
|
}
|
|
339
364
|
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
}));
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
const matchAgainstConfigs = (remaining: string, configs: RouteConfig[]) => {
|
|
365
|
+
const matchAgainstConfigs = (
|
|
366
|
+
remaining: string,
|
|
367
|
+
configs: RouteConfig[],
|
|
368
|
+
configsByScreen: Record<string, RouteConfig[]>
|
|
369
|
+
) => {
|
|
349
370
|
let routes: ParsedRoute[] | undefined;
|
|
350
371
|
let remainingPath = remaining;
|
|
372
|
+
let matchingConfig: RouteConfig | undefined;
|
|
351
373
|
|
|
352
374
|
// Go through all configs, and see if the next path segment matches our regex
|
|
353
375
|
for (const config of configs) {
|
|
@@ -359,53 +381,57 @@ const matchAgainstConfigs = (remaining: string, configs: RouteConfig[]) => {
|
|
|
359
381
|
|
|
360
382
|
// If our regex matches, we need to extract params from the path
|
|
361
383
|
if (match) {
|
|
362
|
-
routes =
|
|
363
|
-
|
|
364
|
-
// Check matching name AND pattern in case same screen is used at different levels in config
|
|
365
|
-
return (
|
|
366
|
-
c.screen === routeName &&
|
|
367
|
-
arrayStartsWith(config.segments, c.segments)
|
|
368
|
-
);
|
|
369
|
-
});
|
|
384
|
+
routes = [];
|
|
385
|
+
matchingConfig = config;
|
|
370
386
|
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
const index = Number(key.replace('param_', ''));
|
|
377
|
-
const param = routeConfig.params.find(
|
|
378
|
-
(it) => it.index === index
|
|
379
|
-
);
|
|
380
|
-
|
|
381
|
-
if (param?.screen === routeName && param?.name) {
|
|
382
|
-
return [param.name, value];
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
return null;
|
|
386
|
-
})
|
|
387
|
-
.filter((it) => it != null)
|
|
388
|
-
.map(([key, value]) => {
|
|
389
|
-
if (value == null) {
|
|
390
|
-
return [key, undefined];
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
const decoded = decodeURIComponent(value);
|
|
394
|
-
const parsed = routeConfig.parse?.[key]
|
|
395
|
-
? routeConfig.parse[key](decoded)
|
|
396
|
-
: decoded;
|
|
397
|
-
|
|
398
|
-
return [key, parsed];
|
|
399
|
-
})
|
|
400
|
-
)
|
|
401
|
-
: undefined;
|
|
387
|
+
for (const routeName of config.routeNames) {
|
|
388
|
+
// Check matching name AND pattern in case same screen is used at different levels in config
|
|
389
|
+
const routeConfig = configsByScreen[routeName]?.find((c) =>
|
|
390
|
+
arrayStartsWith(config.segments, c.segments)
|
|
391
|
+
);
|
|
402
392
|
|
|
403
|
-
|
|
404
|
-
|
|
393
|
+
let params: Record<string, unknown> | undefined;
|
|
394
|
+
|
|
395
|
+
if (routeConfig && match.groups) {
|
|
396
|
+
const paramEntries: [string, unknown][] = [];
|
|
397
|
+
|
|
398
|
+
for (const [key, value] of Object.entries(match.groups)) {
|
|
399
|
+
const index = Number(key.replace('param_', ''));
|
|
400
|
+
const param = routeConfig.params.find((it) => it.index === index);
|
|
401
|
+
|
|
402
|
+
if (param?.screen !== routeName || !param.name) {
|
|
403
|
+
continue;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
if (value == null) {
|
|
407
|
+
paramEntries.push([param.name, undefined]);
|
|
408
|
+
continue;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
let decoded: string;
|
|
412
|
+
|
|
413
|
+
try {
|
|
414
|
+
decoded = decodeURIComponent(value);
|
|
415
|
+
} catch {
|
|
416
|
+
return { routes: undefined, remainingPath };
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
const parser = routeConfig.parse?.[param.name];
|
|
420
|
+
|
|
421
|
+
paramEntries.push([param.name, parser ? parser(decoded) : decoded]);
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
if (paramEntries.length) {
|
|
425
|
+
params = Object.fromEntries(paramEntries);
|
|
426
|
+
}
|
|
405
427
|
}
|
|
406
428
|
|
|
407
|
-
|
|
408
|
-
|
|
429
|
+
if (params && Object.keys(params).length) {
|
|
430
|
+
routes.push({ name: routeName, params });
|
|
431
|
+
} else {
|
|
432
|
+
routes.push({ name: routeName });
|
|
433
|
+
}
|
|
434
|
+
}
|
|
409
435
|
|
|
410
436
|
remainingPath = remainingPath.replace(match[0], '');
|
|
411
437
|
|
|
@@ -413,7 +439,7 @@ const matchAgainstConfigs = (remaining: string, configs: RouteConfig[]) => {
|
|
|
413
439
|
}
|
|
414
440
|
}
|
|
415
441
|
|
|
416
|
-
return { routes, remainingPath };
|
|
442
|
+
return { routes, remainingPath, config: matchingConfig };
|
|
417
443
|
};
|
|
418
444
|
|
|
419
445
|
const createNormalizedConfigs = (
|
|
@@ -439,6 +465,9 @@ const createNormalizedConfigs = (
|
|
|
439
465
|
// if an object is specified as the value (e.g. Foo: { ... }),
|
|
440
466
|
// it can have `path` property and
|
|
441
467
|
// it could have `screens` prop which has nested configs
|
|
468
|
+
const nestedScreens = config.screens;
|
|
469
|
+
const hasNestedScreens = !!nestedScreens;
|
|
470
|
+
|
|
442
471
|
if (typeof config.path === 'string') {
|
|
443
472
|
if (config.exact && config.path == null) {
|
|
444
473
|
throw new Error(
|
|
@@ -458,7 +487,8 @@ const createNormalizedConfigs = (
|
|
|
458
487
|
screen,
|
|
459
488
|
[...routeNames],
|
|
460
489
|
[...paths, { screen, path: alias }],
|
|
461
|
-
config.parse
|
|
490
|
+
config.parse,
|
|
491
|
+
hasNestedScreens
|
|
462
492
|
)
|
|
463
493
|
);
|
|
464
494
|
} else if (typeof alias === 'object') {
|
|
@@ -469,7 +499,8 @@ const createNormalizedConfigs = (
|
|
|
469
499
|
alias.exact
|
|
470
500
|
? [{ screen, path: alias.path }]
|
|
471
501
|
: [...paths, { screen, path: alias.path }],
|
|
472
|
-
alias.parse
|
|
502
|
+
alias.parse,
|
|
503
|
+
hasNestedScreens
|
|
473
504
|
)
|
|
474
505
|
);
|
|
475
506
|
}
|
|
@@ -484,7 +515,13 @@ const createNormalizedConfigs = (
|
|
|
484
515
|
|
|
485
516
|
paths.push({ screen, path: config.path });
|
|
486
517
|
configs.push(
|
|
487
|
-
createConfigItem(
|
|
518
|
+
createConfigItem(
|
|
519
|
+
screen,
|
|
520
|
+
[...routeNames],
|
|
521
|
+
[...paths],
|
|
522
|
+
config.parse,
|
|
523
|
+
hasNestedScreens
|
|
524
|
+
)
|
|
488
525
|
);
|
|
489
526
|
|
|
490
527
|
configs.push(...aliasConfigs);
|
|
@@ -500,7 +537,7 @@ const createNormalizedConfigs = (
|
|
|
500
537
|
);
|
|
501
538
|
}
|
|
502
539
|
|
|
503
|
-
if (
|
|
540
|
+
if (nestedScreens) {
|
|
504
541
|
// property `initialRouteName` without `screens` has no purpose
|
|
505
542
|
if (config.initialRouteName) {
|
|
506
543
|
initials.push({
|
|
@@ -509,10 +546,10 @@ const createNormalizedConfigs = (
|
|
|
509
546
|
});
|
|
510
547
|
}
|
|
511
548
|
|
|
512
|
-
Object.keys(
|
|
549
|
+
Object.keys(nestedScreens).forEach((nestedConfig) => {
|
|
513
550
|
const result = createNormalizedConfigs(
|
|
514
551
|
nestedConfig,
|
|
515
|
-
|
|
552
|
+
nestedScreens as Record<string, string | PathConfig<ParamListBase>>,
|
|
516
553
|
initials,
|
|
517
554
|
[...paths],
|
|
518
555
|
[...parentScreens],
|
|
@@ -533,7 +570,8 @@ const createConfigItem = (
|
|
|
533
570
|
screen: string,
|
|
534
571
|
routeNames: string[],
|
|
535
572
|
paths: { screen: string; path: string }[],
|
|
536
|
-
parse?: ParseConfig
|
|
573
|
+
parse?: ParseConfig,
|
|
574
|
+
hasNestedScreens = false
|
|
537
575
|
): RouteConfig => {
|
|
538
576
|
const parts: (PatternPart & { screen: string })[] = [];
|
|
539
577
|
|
|
@@ -578,22 +616,11 @@ const createConfigItem = (
|
|
|
578
616
|
params,
|
|
579
617
|
routeNames,
|
|
580
618
|
parse,
|
|
619
|
+
explicitParamNames: getExplicitParamNames(parse),
|
|
620
|
+
hasNestedScreens,
|
|
581
621
|
};
|
|
582
622
|
};
|
|
583
623
|
|
|
584
|
-
const findParseConfigForRoute = (
|
|
585
|
-
routeName: string,
|
|
586
|
-
flatConfig: RouteConfig[]
|
|
587
|
-
): ParseConfig | undefined => {
|
|
588
|
-
for (const config of flatConfig) {
|
|
589
|
-
if (routeName === config.routeNames[config.routeNames.length - 1]) {
|
|
590
|
-
return config.parse;
|
|
591
|
-
}
|
|
592
|
-
}
|
|
593
|
-
|
|
594
|
-
return undefined;
|
|
595
|
-
};
|
|
596
|
-
|
|
597
624
|
// Try to find an initial route connected with the one passed
|
|
598
625
|
const findInitialRoute = (
|
|
599
626
|
routeName: string,
|
|
@@ -655,7 +682,7 @@ const createNestedStateObject = (
|
|
|
655
682
|
path: string,
|
|
656
683
|
routes: ParsedRoute[],
|
|
657
684
|
initialRoutes: InitialRouteConfig[],
|
|
658
|
-
|
|
685
|
+
routeConfig?: RouteConfig
|
|
659
686
|
) => {
|
|
660
687
|
let route = routes.shift() as ParsedRoute;
|
|
661
688
|
const parentScreens: string[] = [];
|
|
@@ -699,7 +726,10 @@ const createNestedStateObject = (
|
|
|
699
726
|
|
|
700
727
|
const params = parseQueryParams(
|
|
701
728
|
path,
|
|
702
|
-
|
|
729
|
+
routeConfig?.parse,
|
|
730
|
+
routeConfig?.explicitParamNames,
|
|
731
|
+
routeConfig?.hasNestedScreens,
|
|
732
|
+
route.params
|
|
703
733
|
);
|
|
704
734
|
|
|
705
735
|
if (params) {
|
|
@@ -711,7 +741,10 @@ const createNestedStateObject = (
|
|
|
711
741
|
|
|
712
742
|
const parseQueryParams = (
|
|
713
743
|
path: string,
|
|
714
|
-
parseConfig?:
|
|
744
|
+
parseConfig?: ParseConfig,
|
|
745
|
+
explicitParamNames?: Set<string>,
|
|
746
|
+
hasNestedScreens = false,
|
|
747
|
+
routeParams?: Record<string, unknown>
|
|
715
748
|
) => {
|
|
716
749
|
const query = path.split('?')[1];
|
|
717
750
|
const params: Record<string, unknown> = queryString.parse(query);
|
|
@@ -727,5 +760,19 @@ const parseQueryParams = (
|
|
|
727
760
|
});
|
|
728
761
|
}
|
|
729
762
|
|
|
763
|
+
if (
|
|
764
|
+
hasNestedScreens &&
|
|
765
|
+
!explicitParamNames?.has('screen') &&
|
|
766
|
+
(typeof params.screen === 'string' ||
|
|
767
|
+
typeof routeParams?.screen === 'string')
|
|
768
|
+
) {
|
|
769
|
+
for (const name of NESTED_SCREEN_PARAM_NAMES) {
|
|
770
|
+
if (!explicitParamNames?.has(name)) {
|
|
771
|
+
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
|
772
|
+
delete params[name];
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
|
|
730
777
|
return Object.keys(params).length ? params : undefined;
|
|
731
778
|
};
|
package/src/index.tsx
CHANGED
package/src/types.tsx
CHANGED
|
@@ -10,14 +10,148 @@ import type {
|
|
|
10
10
|
} from '@react-navigation/routers';
|
|
11
11
|
import type * as React from 'react';
|
|
12
12
|
|
|
13
|
+
/**
|
|
14
|
+
* Flatten a type to remove all type alias names, unions etc.
|
|
15
|
+
* This will show a plain object when hovering over the type.
|
|
16
|
+
*/
|
|
17
|
+
type FlatType<T> = { [K in keyof T]: T[K] } & {};
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* keyof T doesn't work for union types. We can use distributive conditional types instead.
|
|
21
|
+
* https://www.typescriptlang.org/docs/handbook/2/conditional-types.html#distributive-conditional-types
|
|
22
|
+
*/
|
|
23
|
+
type KeysOf<T> = T extends {} ? keyof T : never;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* We get a union type when using keyof, but we want an intersection instead.
|
|
27
|
+
* https://stackoverflow.com/a/50375286/1665026
|
|
28
|
+
*/
|
|
29
|
+
type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (
|
|
30
|
+
k: infer I
|
|
31
|
+
) => void
|
|
32
|
+
? I
|
|
33
|
+
: never;
|
|
34
|
+
|
|
35
|
+
type UnknownToUndefined<T> = unknown extends T ? undefined : T;
|
|
36
|
+
|
|
37
|
+
type ParamsForScreenComponent<T> = T extends {
|
|
38
|
+
screen: React.ComponentType<{ route: { params: infer P } }>;
|
|
39
|
+
}
|
|
40
|
+
? P
|
|
41
|
+
: T extends React.ComponentType<{ route: { params: infer P } }>
|
|
42
|
+
? P
|
|
43
|
+
: undefined;
|
|
44
|
+
|
|
45
|
+
type StaticNavigationConfig = {
|
|
46
|
+
readonly config: {
|
|
47
|
+
readonly screens?: Record<string, any>;
|
|
48
|
+
readonly groups?: {
|
|
49
|
+
[key: string]: {
|
|
50
|
+
screens: Record<string, any>;
|
|
51
|
+
};
|
|
52
|
+
};
|
|
53
|
+
};
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
type ParamsForScreen<T> = T extends {
|
|
57
|
+
screen: infer Screen;
|
|
58
|
+
}
|
|
59
|
+
? Screen extends StaticNavigationConfig
|
|
60
|
+
? NavigatorScreenParams<StaticParamList<Screen>> | undefined
|
|
61
|
+
: UnknownToUndefined<ParamsForScreenComponent<T>>
|
|
62
|
+
: T extends StaticNavigationConfig
|
|
63
|
+
? NavigatorScreenParams<StaticParamList<T>> | undefined
|
|
64
|
+
: UnknownToUndefined<ParamsForScreenComponent<T>>;
|
|
65
|
+
|
|
66
|
+
type ParamListForScreens<Screens> = {
|
|
67
|
+
[Key in KeysOf<Screens>]: ParamsForScreen<Screens[Key]>;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
type ParamListForGroups<
|
|
71
|
+
Groups extends
|
|
72
|
+
| Readonly<{
|
|
73
|
+
[key: string]: {
|
|
74
|
+
screens: Record<string, any>;
|
|
75
|
+
};
|
|
76
|
+
}>
|
|
77
|
+
| undefined,
|
|
78
|
+
> = Groups extends {
|
|
79
|
+
[key: string]: {
|
|
80
|
+
screens: Record<string, any>;
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
? ParamListForScreens<UnionToIntersection<Groups[keyof Groups]['screens']>>
|
|
84
|
+
: {};
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Infer the param list from the static navigation config.
|
|
88
|
+
*/
|
|
89
|
+
export type StaticParamList<T extends StaticNavigationConfig> = FlatType<
|
|
90
|
+
ParamListForScreens<T['config']['screens']> &
|
|
91
|
+
ParamListForGroups<T['config']['groups']>
|
|
92
|
+
>;
|
|
93
|
+
|
|
94
|
+
type ParamListForStaticNavigator<T> = T extends StaticNavigationConfig
|
|
95
|
+
? StaticParamList<T>
|
|
96
|
+
: {};
|
|
97
|
+
|
|
98
|
+
type ParamListForTypedNavigator<T> = T extends {
|
|
99
|
+
Screen: any;
|
|
100
|
+
} & PrivateValueStore<infer Value>
|
|
101
|
+
? Value[0]
|
|
102
|
+
: {};
|
|
103
|
+
|
|
104
|
+
type ParamListForRootNavigator<T> =
|
|
105
|
+
string extends keyof ParamListForTypedNavigator<T>
|
|
106
|
+
? ParamListForStaticNavigator<T>
|
|
107
|
+
: ParamListForTypedNavigator<T>;
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Root navigator used in the app.
|
|
111
|
+
* It's used for the global types in the app.
|
|
112
|
+
*
|
|
113
|
+
* Users need to use module augmentation to add their navigator type:
|
|
114
|
+
*
|
|
115
|
+
* ```ts
|
|
116
|
+
* // Navigator created with static or dynamic API
|
|
117
|
+
* const RootStack = createStackNavigator({
|
|
118
|
+
* // ...
|
|
119
|
+
* });
|
|
120
|
+
*
|
|
121
|
+
* type RootStackType = typeof RootStack;
|
|
122
|
+
*
|
|
123
|
+
* declare module '@react-navigation/core' {
|
|
124
|
+
* interface RootNavigator extends RootStackType {}
|
|
125
|
+
* }
|
|
126
|
+
* ```
|
|
127
|
+
*/
|
|
128
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
|
129
|
+
export interface RootNavigator {}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Theme object for the navigation components.
|
|
133
|
+
*
|
|
134
|
+
* Custom properties can be added using declaration merging:
|
|
135
|
+
*
|
|
136
|
+
* ```ts
|
|
137
|
+
* declare module '@react-navigation/core' {
|
|
138
|
+
* interface Theme extends NativeTheme {
|
|
139
|
+
* myCustomProperty: string;
|
|
140
|
+
* }
|
|
141
|
+
* }
|
|
142
|
+
* ```
|
|
143
|
+
*/
|
|
144
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
|
145
|
+
export interface Theme {}
|
|
146
|
+
|
|
147
|
+
type RootTheme = Theme;
|
|
148
|
+
|
|
13
149
|
declare global {
|
|
14
150
|
// eslint-disable-next-line @typescript-eslint/no-namespace
|
|
15
151
|
namespace ReactNavigation {
|
|
16
|
-
|
|
17
|
-
interface RootParamList {}
|
|
152
|
+
interface RootParamList extends ParamListForRootNavigator<RootNavigator> {}
|
|
18
153
|
|
|
19
|
-
|
|
20
|
-
interface Theme {}
|
|
154
|
+
interface Theme extends RootTheme {}
|
|
21
155
|
}
|
|
22
156
|
}
|
|
23
157
|
|
package/src/useEventEmitter.tsx
CHANGED
|
@@ -75,59 +75,54 @@ export function useEventEmitter<T extends Record<string, any>>(
|
|
|
75
75
|
target?: string;
|
|
76
76
|
canPreventDefault?: boolean;
|
|
77
77
|
}) => {
|
|
78
|
-
const items = listeners.current[type]
|
|
78
|
+
const items = listeners.current[type];
|
|
79
79
|
|
|
80
80
|
// Copy the current list of callbacks in case they are mutated during execution
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
81
|
+
let callbacks: Listeners | undefined;
|
|
82
|
+
|
|
83
|
+
if (items !== undefined) {
|
|
84
|
+
callbacks =
|
|
85
|
+
target !== undefined
|
|
86
|
+
? items[target]?.slice()
|
|
87
|
+
: ([] as Listeners)
|
|
88
|
+
.concat(...Object.keys(items).map((t) => items[t]))
|
|
89
|
+
.filter((cb, i, self) => self.lastIndexOf(cb) === i);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const descriptors: PropertyDescriptorMap = {
|
|
93
|
+
type: { enumerable: true, value: type },
|
|
92
94
|
};
|
|
93
95
|
|
|
94
96
|
if (target !== undefined) {
|
|
95
|
-
|
|
96
|
-
enumerable: true,
|
|
97
|
-
get() {
|
|
98
|
-
return target;
|
|
99
|
-
},
|
|
100
|
-
});
|
|
97
|
+
descriptors.target = { enumerable: true, value: target };
|
|
101
98
|
}
|
|
102
99
|
|
|
103
100
|
if (data !== undefined) {
|
|
104
|
-
|
|
105
|
-
enumerable: true,
|
|
106
|
-
get() {
|
|
107
|
-
return data;
|
|
108
|
-
},
|
|
109
|
-
});
|
|
101
|
+
descriptors.data = { enumerable: true, value: data };
|
|
110
102
|
}
|
|
111
103
|
|
|
104
|
+
let defaultPrevented = false;
|
|
105
|
+
|
|
112
106
|
if (canPreventDefault) {
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
enumerable: true,
|
|
118
|
-
get() {
|
|
119
|
-
return defaultPrevented;
|
|
120
|
-
},
|
|
107
|
+
descriptors.defaultPrevented = {
|
|
108
|
+
enumerable: true,
|
|
109
|
+
get() {
|
|
110
|
+
return defaultPrevented;
|
|
121
111
|
},
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
112
|
+
};
|
|
113
|
+
descriptors.preventDefault = {
|
|
114
|
+
enumerable: true,
|
|
115
|
+
value() {
|
|
116
|
+
defaultPrevented = true;
|
|
127
117
|
},
|
|
128
|
-
}
|
|
118
|
+
};
|
|
129
119
|
}
|
|
130
120
|
|
|
121
|
+
const event: EventArg<any, any, any> = Object.defineProperties(
|
|
122
|
+
{} as EventArg<any, any, any>,
|
|
123
|
+
descriptors
|
|
124
|
+
);
|
|
125
|
+
|
|
131
126
|
listenRef.current?.(event);
|
|
132
127
|
|
|
133
128
|
callbacks?.forEach((cb) => cb(event));
|