@ankhorage/contracts 11.0.0 → 12.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +15 -0
- package/README.md +1 -1
- package/dist/appManifest/navigator.js +1 -7
- package/dist/appManifest/navigator.js.map +1 -1
- package/dist/appManifest/navigatorOptions.js +18 -17
- package/dist/appManifest/navigatorOptions.js.map +1 -1
- package/dist/navigator/catalog.d.ts +42 -0
- package/dist/navigator/catalog.d.ts.map +1 -0
- package/dist/navigator/catalog.js +2 -0
- package/dist/navigator/catalog.js.map +1 -0
- package/dist/navigator/extensions.d.ts +23 -0
- package/dist/navigator/extensions.d.ts.map +1 -0
- package/dist/navigator/extensions.js +2 -0
- package/dist/navigator/extensions.js.map +1 -0
- package/dist/navigator/generation.d.ts +36 -0
- package/dist/navigator/generation.d.ts.map +1 -0
- package/dist/navigator/generation.js +2 -0
- package/dist/navigator/generation.js.map +1 -0
- package/dist/navigator/planning.d.ts +105 -0
- package/dist/navigator/planning.d.ts.map +1 -0
- package/dist/navigator/planning.js +2 -0
- package/dist/navigator/planning.js.map +1 -0
- package/dist/navigator.d.ts +21 -23
- package/dist/navigator.d.ts.map +1 -1
- package/dist/navigator.js +9 -9
- package/dist/navigator.js.map +1 -1
- package/package.json +4 -4
- package/src/appManifest/navigator.ts +1 -10
- package/src/appManifest/navigatorOptions.ts +19 -18
- package/src/navigator/catalog.ts +79 -0
- package/src/navigator/extensions.ts +26 -0
- package/src/navigator/generation.ts +46 -0
- package/src/navigator/planning.ts +147 -0
- package/src/navigator-runtime-contracts.test.ts +104 -0
- package/src/navigator-validation.test.ts +31 -4
- package/src/navigator.test.ts +37 -16
- package/src/navigator.ts +69 -29
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
CustomNavigatorNode,
|
|
3
|
+
DrawerNavigatorOptions,
|
|
4
|
+
NavigatorType,
|
|
5
|
+
RouteDefinition,
|
|
6
|
+
StackImplementation,
|
|
7
|
+
StackScreenOptions,
|
|
8
|
+
} from '../navigator';
|
|
9
|
+
import type { CustomNavigatorRegistry } from './extensions';
|
|
10
|
+
import type { NavigatorDependencyRequirement } from './generation';
|
|
11
|
+
|
|
12
|
+
export interface ResolvedHeadlessTabsPresentation {
|
|
13
|
+
presentation: ResolvedTabsPresentation;
|
|
14
|
+
customPresentationId?: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export type ResolvedTabsImplementation = 'headless' | 'javascript' | 'native';
|
|
18
|
+
|
|
19
|
+
export type ResolvedTabsPresentation = 'bottom' | 'top' | 'rail' | 'sidebar' | 'custom';
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Disposable adapter plan for a `tabs` topology.
|
|
23
|
+
*
|
|
24
|
+
* The implementation selects the Router runtime. Presentation only selects its visual chrome and
|
|
25
|
+
* never creates another route topology.
|
|
26
|
+
*/
|
|
27
|
+
export interface TabsNavigatorPlan {
|
|
28
|
+
implementation: ResolvedTabsImplementation;
|
|
29
|
+
module: ExpoRouterNavigatorModule;
|
|
30
|
+
exportName: string;
|
|
31
|
+
stability: NavigatorApiStability;
|
|
32
|
+
presentation?: ResolvedTabsPresentation;
|
|
33
|
+
presentations?: Readonly<Record<NavigatorResponsiveSize, ResolvedTabsPresentation>>;
|
|
34
|
+
customPresentationId?: string;
|
|
35
|
+
minimizeBehavior?: 'automatic' | 'never' | 'onScrollDown' | 'onScrollUp';
|
|
36
|
+
bottomAccessoryScreenId?: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface CreateNavigatorPlanOptions {
|
|
40
|
+
platform: NavigatorRuntimePlatform;
|
|
41
|
+
expoRouterVersion: string;
|
|
42
|
+
responsiveSize?: NavigatorResponsiveSize;
|
|
43
|
+
customNavigators?: CustomNavigatorRegistry;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export type ExpoRouterNavigatorModule =
|
|
47
|
+
| 'expo-router'
|
|
48
|
+
| 'expo-router/drawer'
|
|
49
|
+
| 'expo-router/js-stack'
|
|
50
|
+
| 'expo-router/js-tabs'
|
|
51
|
+
| 'expo-router/js-top-tabs'
|
|
52
|
+
| 'expo-router/ui'
|
|
53
|
+
| 'expo-router/unstable-split-view'
|
|
54
|
+
| 'expo-router/unstable-native-tabs';
|
|
55
|
+
|
|
56
|
+
export type NavigatorAdapterId =
|
|
57
|
+
| 'slot'
|
|
58
|
+
| 'stack.native'
|
|
59
|
+
| 'stack.javascript'
|
|
60
|
+
| 'stack.experimental'
|
|
61
|
+
| 'drawer'
|
|
62
|
+
| 'tabs.native'
|
|
63
|
+
| 'tabs.javascript'
|
|
64
|
+
| 'tabs.headless'
|
|
65
|
+
| 'split-view'
|
|
66
|
+
| 'custom';
|
|
67
|
+
|
|
68
|
+
export interface NavigatorAdapterPlan {
|
|
69
|
+
id: NavigatorAdapterId;
|
|
70
|
+
/** Built-in Expo Router entry point or an explicitly registered custom module. */
|
|
71
|
+
module?: string;
|
|
72
|
+
exportName?: string;
|
|
73
|
+
support: NavigatorSupportStatus;
|
|
74
|
+
stability: NavigatorApiStability;
|
|
75
|
+
limitations: readonly string[];
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export type NavigatorApiStability = 'stable' | 'alpha';
|
|
79
|
+
|
|
80
|
+
export type NavigatorCapabilityId = string;
|
|
81
|
+
|
|
82
|
+
export interface NavigatorDiagnostic {
|
|
83
|
+
code: string;
|
|
84
|
+
severity: 'error' | 'warning';
|
|
85
|
+
path: string;
|
|
86
|
+
message: string;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export interface NavigatorNodePlan {
|
|
90
|
+
type: NavigatorType;
|
|
91
|
+
pointer: string;
|
|
92
|
+
adapter: NavigatorAdapterPlan;
|
|
93
|
+
initialRouteName?: string;
|
|
94
|
+
routes: readonly NavigatorRoutePlan[];
|
|
95
|
+
stack?: {
|
|
96
|
+
implementation: StackImplementation;
|
|
97
|
+
options?: StackScreenOptions;
|
|
98
|
+
};
|
|
99
|
+
drawer?: {
|
|
100
|
+
options?: DrawerNavigatorOptions;
|
|
101
|
+
};
|
|
102
|
+
tabs?: TabsNavigatorPlan;
|
|
103
|
+
splitView?: {
|
|
104
|
+
columns: {
|
|
105
|
+
primary: string;
|
|
106
|
+
supplementary?: string;
|
|
107
|
+
};
|
|
108
|
+
inspector?: string;
|
|
109
|
+
topColumnForCollapsing?: 'primary' | 'secondary' | 'supplementary';
|
|
110
|
+
};
|
|
111
|
+
custom?: {
|
|
112
|
+
navigatorId: string;
|
|
113
|
+
config?: CustomNavigatorNode['config'];
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export interface NavigatorPlan {
|
|
118
|
+
context: NavigatorValidationContext;
|
|
119
|
+
root: NavigatorNodePlan;
|
|
120
|
+
diagnostics: readonly NavigatorDiagnostic[];
|
|
121
|
+
support: NavigatorSupportStatus;
|
|
122
|
+
capabilityIds: readonly NavigatorCapabilityId[];
|
|
123
|
+
dependencies: readonly NavigatorDependencyRequirement[];
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export type NavigatorResponsiveSize = 'compact' | 'medium' | 'expanded';
|
|
127
|
+
|
|
128
|
+
export interface NavigatorRoutePlan {
|
|
129
|
+
name: string;
|
|
130
|
+
path?: string;
|
|
131
|
+
label?: string;
|
|
132
|
+
icon?: RouteDefinition['icon'];
|
|
133
|
+
showInPrimaryNavigation?: boolean;
|
|
134
|
+
guards: readonly string[];
|
|
135
|
+
screenId?: string;
|
|
136
|
+
stackOptions?: StackScreenOptions;
|
|
137
|
+
navigator?: NavigatorNodePlan;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export type NavigatorRuntimePlatform = 'android' | 'ios' | 'web';
|
|
141
|
+
|
|
142
|
+
export type NavigatorSupportStatus = 'supported' | 'testing-only' | 'unsupported';
|
|
143
|
+
|
|
144
|
+
export interface NavigatorValidationContext {
|
|
145
|
+
platform: NavigatorRuntimePlatform;
|
|
146
|
+
expoRouterVersion: string;
|
|
147
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { expect, test } from 'bun:test';
|
|
2
|
+
|
|
3
|
+
import type {
|
|
4
|
+
CustomNavigatorRegistration,
|
|
5
|
+
NavigatorCatalog,
|
|
6
|
+
NavigatorGeneratedFile,
|
|
7
|
+
NavigatorGenerationBindings,
|
|
8
|
+
NavigatorGenerationResult,
|
|
9
|
+
NavigatorPlan,
|
|
10
|
+
} from './navigator';
|
|
11
|
+
|
|
12
|
+
test('keeps generation bindings and plan output portable across package boundaries', () => {
|
|
13
|
+
const bindings: NavigatorGenerationBindings = {
|
|
14
|
+
screens: { home: { module: '@/screens/Home', exportName: 'Home' } },
|
|
15
|
+
guards: {},
|
|
16
|
+
};
|
|
17
|
+
const file: NavigatorGeneratedFile = {
|
|
18
|
+
path: 'src/app/index.tsx',
|
|
19
|
+
contents: 'export default Home;',
|
|
20
|
+
};
|
|
21
|
+
const plan: NavigatorPlan = {
|
|
22
|
+
context: { platform: 'web', expoRouterVersion: '57.0.18' },
|
|
23
|
+
root: {
|
|
24
|
+
type: 'slot',
|
|
25
|
+
pointer: '',
|
|
26
|
+
adapter: {
|
|
27
|
+
id: 'slot',
|
|
28
|
+
module: 'expo-router',
|
|
29
|
+
exportName: 'Slot',
|
|
30
|
+
support: 'supported',
|
|
31
|
+
stability: 'stable',
|
|
32
|
+
limitations: [],
|
|
33
|
+
},
|
|
34
|
+
routes: [],
|
|
35
|
+
},
|
|
36
|
+
diagnostics: [],
|
|
37
|
+
support: 'supported',
|
|
38
|
+
capabilityIds: ['slot'],
|
|
39
|
+
dependencies: [],
|
|
40
|
+
};
|
|
41
|
+
const result: NavigatorGenerationResult = {
|
|
42
|
+
support: plan.support,
|
|
43
|
+
capabilityIds: plan.capabilityIds,
|
|
44
|
+
dependencies: plan.dependencies,
|
|
45
|
+
diagnostics: plan.diagnostics,
|
|
46
|
+
plan,
|
|
47
|
+
files: [file],
|
|
48
|
+
};
|
|
49
|
+
expect(JSON.parse(JSON.stringify({ bindings, result }))).toEqual({ bindings, result });
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test('separates capability taxonomy, target support, stability, and verification', () => {
|
|
53
|
+
const catalog: NavigatorCatalog = {
|
|
54
|
+
capabilities: [
|
|
55
|
+
{
|
|
56
|
+
id: 'tabs.headless.sidebar',
|
|
57
|
+
topology: 'tabs',
|
|
58
|
+
implementation: 'headless',
|
|
59
|
+
presentation: 'sidebar',
|
|
60
|
+
stability: 'stable',
|
|
61
|
+
targets: [
|
|
62
|
+
{
|
|
63
|
+
platform: 'web',
|
|
64
|
+
support: 'supported',
|
|
65
|
+
verification: [{ kind: 'browser', status: 'verified' }],
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
platform: 'ios',
|
|
69
|
+
support: 'testing-only',
|
|
70
|
+
verification: [{ kind: 'device', status: 'unverified' }],
|
|
71
|
+
},
|
|
72
|
+
],
|
|
73
|
+
dependencies: [],
|
|
74
|
+
requirements: [],
|
|
75
|
+
incompatibilities: [],
|
|
76
|
+
limitations: [],
|
|
77
|
+
},
|
|
78
|
+
],
|
|
79
|
+
presets: [
|
|
80
|
+
{
|
|
81
|
+
id: 'drawer',
|
|
82
|
+
description: 'Drawer as the root navigator with direct routes.',
|
|
83
|
+
topology: ['drawer'],
|
|
84
|
+
},
|
|
85
|
+
],
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
expect(catalog.capabilities[0]?.targets[1]?.support).toBe('testing-only');
|
|
89
|
+
expect(catalog.presets[0]?.topology).toEqual(['drawer']);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
test('describes a custom extension without importing Navigator or a UI runtime', () => {
|
|
93
|
+
const registration: CustomNavigatorRegistration = {
|
|
94
|
+
id: 'example',
|
|
95
|
+
platforms: ['web'],
|
|
96
|
+
stability: 'stable',
|
|
97
|
+
integration: 'expo-router-standard',
|
|
98
|
+
router: 'stack',
|
|
99
|
+
module: '@example/navigation',
|
|
100
|
+
exportName: 'ExampleNavigator',
|
|
101
|
+
validateConfig: () => [],
|
|
102
|
+
};
|
|
103
|
+
expect(registration.validateConfig({ mode: 'compact' })).toEqual([]);
|
|
104
|
+
});
|
|
@@ -6,7 +6,7 @@ import { describe, expect, it } from 'bun:test';
|
|
|
6
6
|
import { isAppNavigatorManifest } from './appManifest/screens';
|
|
7
7
|
import type {
|
|
8
8
|
CustomNavigatorNode,
|
|
9
|
-
|
|
9
|
+
HeadlessTabsConfig,
|
|
10
10
|
NavigatorNode,
|
|
11
11
|
StackImplementationConfig,
|
|
12
12
|
} from './navigator';
|
|
@@ -20,9 +20,9 @@ describe('app navigator manifest type discrimination', () => {
|
|
|
20
20
|
implementation: 'experimental',
|
|
21
21
|
options: { presentation: 'modal' },
|
|
22
22
|
};
|
|
23
|
-
// @ts-expect-error Fixed
|
|
24
|
-
const tabs:
|
|
25
|
-
implementation: '
|
|
23
|
+
// @ts-expect-error Fixed headless tabs do not accept a registered custom presentation id.
|
|
24
|
+
const tabs: HeadlessTabsConfig = {
|
|
25
|
+
implementation: 'headless',
|
|
26
26
|
presentation: 'sidebar',
|
|
27
27
|
customPresentationId: 'workspace-tabs',
|
|
28
28
|
};
|
|
@@ -70,6 +70,33 @@ describe('app navigator manifest stack validation', () => {
|
|
|
70
70
|
});
|
|
71
71
|
});
|
|
72
72
|
|
|
73
|
+
describe('app navigator manifest removed fields', () => {
|
|
74
|
+
it('rejects flow metadata and the superseded custom Tabs implementation', () => {
|
|
75
|
+
expect(
|
|
76
|
+
isAppNavigatorManifest({
|
|
77
|
+
type: 'stack',
|
|
78
|
+
flows: { onboarding: true },
|
|
79
|
+
routes: [],
|
|
80
|
+
}),
|
|
81
|
+
).toBe(false);
|
|
82
|
+
expect(
|
|
83
|
+
isAppNavigatorManifest({
|
|
84
|
+
type: 'tabs',
|
|
85
|
+
implementation: 'custom',
|
|
86
|
+
presentation: 'bottom',
|
|
87
|
+
routes: [],
|
|
88
|
+
}),
|
|
89
|
+
).toBe(false);
|
|
90
|
+
expect(
|
|
91
|
+
isAppNavigatorManifest({
|
|
92
|
+
type: 'stack',
|
|
93
|
+
preset: 'root-stack-tabs',
|
|
94
|
+
routes: [],
|
|
95
|
+
}),
|
|
96
|
+
).toBe(false);
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
|
|
73
100
|
describe('app navigator manifest sheet validation', () => {
|
|
74
101
|
it('validates native form-sheet detents and their presentation discriminant', () => {
|
|
75
102
|
for (const sheetAllowedDetents of [[], [0.8, 0.4], [0.4, 0.4], [0], [1.1], [Number.NaN]]) {
|
package/src/navigator.test.ts
CHANGED
|
@@ -23,7 +23,6 @@ function createAdaptiveTabs(): AppNavigatorManifest {
|
|
|
23
23
|
expanded: 'sidebar',
|
|
24
24
|
},
|
|
25
25
|
},
|
|
26
|
-
flows: { onboarding: true },
|
|
27
26
|
routes: [{ name: 'home', path: '/', screenId: 'home' }],
|
|
28
27
|
};
|
|
29
28
|
}
|
|
@@ -85,11 +84,24 @@ const VALID_NAVIGATOR_NODES = [
|
|
|
85
84
|
describe('app navigator manifest topology', () => {
|
|
86
85
|
it('keeps canonical topology presets finite and authorable', () => {
|
|
87
86
|
expect(NAVIGATOR_TYPES).toEqual(['slot', 'stack', 'tabs', 'drawer', 'split-view', 'custom']);
|
|
88
|
-
expect(NAVIGATOR_PRESETS).
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
87
|
+
expect(NAVIGATOR_PRESETS).toEqual([
|
|
88
|
+
'slot',
|
|
89
|
+
'stack',
|
|
90
|
+
'tabs',
|
|
91
|
+
'tabs-stack',
|
|
92
|
+
'stack-tabs',
|
|
93
|
+
'stack-tabs-stack',
|
|
94
|
+
'drawer',
|
|
95
|
+
'drawer-stack',
|
|
96
|
+
'stack-drawer',
|
|
97
|
+
'stack-drawer-stack',
|
|
98
|
+
'drawer-tabs',
|
|
99
|
+
'drawer-tabs-stack',
|
|
100
|
+
'stack-drawer-tabs',
|
|
101
|
+
'stack-drawer-tabs-stack',
|
|
102
|
+
'split-view',
|
|
103
|
+
'custom',
|
|
104
|
+
]);
|
|
93
105
|
});
|
|
94
106
|
|
|
95
107
|
it('accepts adaptive native/Web tabs with responsive custom presentation', () => {
|
|
@@ -152,12 +164,12 @@ describe('app navigator manifest node variants', () => {
|
|
|
152
164
|
});
|
|
153
165
|
});
|
|
154
166
|
|
|
155
|
-
describe('app navigator manifest
|
|
167
|
+
describe('app navigator manifest headless presentation', () => {
|
|
156
168
|
it('accepts fixed and registered custom Web presentations', () => {
|
|
157
169
|
expect(
|
|
158
170
|
isAppNavigatorManifest({
|
|
159
171
|
type: 'tabs',
|
|
160
|
-
implementation: '
|
|
172
|
+
implementation: 'headless',
|
|
161
173
|
presentation: 'sidebar',
|
|
162
174
|
routes: [],
|
|
163
175
|
}),
|
|
@@ -166,7 +178,7 @@ describe('app navigator manifest custom presentation', () => {
|
|
|
166
178
|
expect(
|
|
167
179
|
isAppNavigatorManifest({
|
|
168
180
|
type: 'tabs',
|
|
169
|
-
implementation: '
|
|
181
|
+
implementation: 'headless',
|
|
170
182
|
presentation: 'custom',
|
|
171
183
|
customPresentationId: 'workspace-tabs',
|
|
172
184
|
routes: [],
|
|
@@ -178,7 +190,7 @@ describe('app navigator manifest custom presentation', () => {
|
|
|
178
190
|
expect(
|
|
179
191
|
isAppNavigatorManifest({
|
|
180
192
|
type: 'tabs',
|
|
181
|
-
implementation: '
|
|
193
|
+
implementation: 'headless',
|
|
182
194
|
presentation: 'responsive',
|
|
183
195
|
routes: [],
|
|
184
196
|
}),
|
|
@@ -187,7 +199,7 @@ describe('app navigator manifest custom presentation', () => {
|
|
|
187
199
|
expect(
|
|
188
200
|
isAppNavigatorManifest({
|
|
189
201
|
type: 'tabs',
|
|
190
|
-
implementation: '
|
|
202
|
+
implementation: 'headless',
|
|
191
203
|
presentation: 'custom',
|
|
192
204
|
routes: [],
|
|
193
205
|
}),
|
|
@@ -219,7 +231,7 @@ describe('app navigator manifest tabs branch validation', () => {
|
|
|
219
231
|
expect(
|
|
220
232
|
isAppNavigatorManifest({
|
|
221
233
|
type: 'tabs',
|
|
222
|
-
implementation: '
|
|
234
|
+
implementation: 'headless',
|
|
223
235
|
presentation: 'sidebar',
|
|
224
236
|
customPresentationId: 'must-not-apply',
|
|
225
237
|
routes: [],
|
|
@@ -229,7 +241,7 @@ describe('app navigator manifest tabs branch validation', () => {
|
|
|
229
241
|
expect(
|
|
230
242
|
isAppNavigatorManifest({
|
|
231
243
|
type: 'tabs',
|
|
232
|
-
implementation: '
|
|
244
|
+
implementation: 'headless',
|
|
233
245
|
presentation: 'custom',
|
|
234
246
|
customPresentationId: 'workspace-tabs',
|
|
235
247
|
responsive: { compact: 'bottom', expanded: 'sidebar' },
|
|
@@ -240,14 +252,21 @@ describe('app navigator manifest tabs branch validation', () => {
|
|
|
240
252
|
});
|
|
241
253
|
|
|
242
254
|
describe('app navigator manifest composition', () => {
|
|
243
|
-
it('
|
|
255
|
+
it('expresses app-owned sequences as ordinary guarded navigator branches', () => {
|
|
244
256
|
expect(
|
|
245
257
|
isAppNavigatorManifest({
|
|
246
258
|
type: 'stack',
|
|
247
|
-
flows: { authentication: true },
|
|
248
259
|
routes: [
|
|
249
260
|
{
|
|
250
|
-
name: '
|
|
261
|
+
name: 'entry',
|
|
262
|
+
navigator: {
|
|
263
|
+
type: 'stack',
|
|
264
|
+
routes: [{ name: 'step', screenId: 'step' }],
|
|
265
|
+
},
|
|
266
|
+
},
|
|
267
|
+
{
|
|
268
|
+
name: 'main',
|
|
269
|
+
guards: ['entry-complete'],
|
|
251
270
|
navigator: {
|
|
252
271
|
type: 'tabs',
|
|
253
272
|
routes: [{ name: 'home', screenId: 'home' }],
|
|
@@ -257,7 +276,9 @@ describe('app navigator manifest composition', () => {
|
|
|
257
276
|
}),
|
|
258
277
|
).toBe(true);
|
|
259
278
|
});
|
|
279
|
+
});
|
|
260
280
|
|
|
281
|
+
describe('app navigator manifest route metadata', () => {
|
|
261
282
|
it('preserves authored route names, labels, paths, guards, and visibility without inference', () => {
|
|
262
283
|
const navigator = {
|
|
263
284
|
type: 'stack',
|
package/src/navigator.ts
CHANGED
|
@@ -8,16 +8,16 @@ export const NAVIGATOR_PRESETS = [
|
|
|
8
8
|
'stack',
|
|
9
9
|
'tabs',
|
|
10
10
|
'tabs-stack',
|
|
11
|
+
'stack-tabs',
|
|
12
|
+
'stack-tabs-stack',
|
|
11
13
|
'drawer',
|
|
12
14
|
'drawer-stack',
|
|
15
|
+
'stack-drawer',
|
|
16
|
+
'stack-drawer-stack',
|
|
13
17
|
'drawer-tabs',
|
|
14
18
|
'drawer-tabs-stack',
|
|
15
|
-
'
|
|
16
|
-
'
|
|
17
|
-
'root-stack-drawer',
|
|
18
|
-
'root-stack-drawer-stack',
|
|
19
|
-
'root-stack-drawer-tabs',
|
|
20
|
-
'root-stack-drawer-tabs-stack',
|
|
19
|
+
'stack-drawer-tabs',
|
|
20
|
+
'stack-drawer-tabs-stack',
|
|
21
21
|
'split-view',
|
|
22
22
|
'custom',
|
|
23
23
|
] as const;
|
|
@@ -94,15 +94,15 @@ export interface DrawerNavigatorOptions {
|
|
|
94
94
|
headerShown?: boolean;
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
-
export const
|
|
98
|
-
export type
|
|
97
|
+
export const FIXED_HEADLESS_TABS_PRESENTATIONS = ['bottom', 'top', 'rail', 'sidebar'] as const;
|
|
98
|
+
export type FixedHeadlessTabsPresentation = (typeof FIXED_HEADLESS_TABS_PRESENTATIONS)[number];
|
|
99
99
|
|
|
100
|
-
export const
|
|
101
|
-
...
|
|
100
|
+
export const HEADLESS_TABS_PRESENTATIONS = [
|
|
101
|
+
...FIXED_HEADLESS_TABS_PRESENTATIONS,
|
|
102
102
|
'responsive',
|
|
103
103
|
'custom',
|
|
104
104
|
] as const;
|
|
105
|
-
export type
|
|
105
|
+
export type HeadlessTabsPresentation = (typeof HEADLESS_TABS_PRESENTATIONS)[number];
|
|
106
106
|
|
|
107
107
|
export const JAVASCRIPT_TABS_PRESENTATIONS = ['bottom', 'top'] as const;
|
|
108
108
|
export type JavaScriptTabsPresentation = (typeof JAVASCRIPT_TABS_PRESENTATIONS)[number];
|
|
@@ -116,14 +116,14 @@ export const NATIVE_TABS_MINIMIZE_BEHAVIORS = [
|
|
|
116
116
|
export type NativeTabsMinimizeBehavior = (typeof NATIVE_TABS_MINIMIZE_BEHAVIORS)[number];
|
|
117
117
|
|
|
118
118
|
export interface ResponsiveTabsPresentation {
|
|
119
|
-
compact:
|
|
120
|
-
medium?:
|
|
121
|
-
expanded:
|
|
119
|
+
compact: FixedHeadlessTabsPresentation;
|
|
120
|
+
medium?: FixedHeadlessTabsPresentation;
|
|
121
|
+
expanded: FixedHeadlessTabsPresentation;
|
|
122
122
|
}
|
|
123
123
|
|
|
124
|
-
export type
|
|
124
|
+
export type HeadlessTabsPresentationConfig =
|
|
125
125
|
| {
|
|
126
|
-
presentation:
|
|
126
|
+
presentation: FixedHeadlessTabsPresentation;
|
|
127
127
|
responsive?: never;
|
|
128
128
|
customPresentationId?: never;
|
|
129
129
|
}
|
|
@@ -139,11 +139,11 @@ export type CustomTabsPresentationConfig =
|
|
|
139
139
|
customPresentationId: string;
|
|
140
140
|
};
|
|
141
141
|
|
|
142
|
-
export type
|
|
143
|
-
implementation: '
|
|
144
|
-
} &
|
|
142
|
+
export type HeadlessTabsConfig = {
|
|
143
|
+
implementation: 'headless';
|
|
144
|
+
} & HeadlessTabsPresentationConfig;
|
|
145
145
|
|
|
146
|
-
export type
|
|
146
|
+
export type HeadlessTabsWebConfig = HeadlessTabsPresentationConfig;
|
|
147
147
|
|
|
148
148
|
export interface NavigatorScreenReference {
|
|
149
149
|
screenId: string;
|
|
@@ -167,12 +167,12 @@ export interface AdaptiveTabsConfig {
|
|
|
167
167
|
implementation?: 'adaptive';
|
|
168
168
|
/** Android/iOS branch. Expo Router may expose this implementation as unstable. */
|
|
169
169
|
native?: NativeTabsConfig;
|
|
170
|
-
/** Web branch rendered through headless
|
|
171
|
-
web?:
|
|
170
|
+
/** Web branch rendered through headless tabs. */
|
|
171
|
+
web?: HeadlessTabsWebConfig;
|
|
172
172
|
}
|
|
173
173
|
|
|
174
174
|
export type TabsImplementationConfig =
|
|
175
|
-
AdaptiveTabsConfig |
|
|
175
|
+
AdaptiveTabsConfig | HeadlessTabsConfig | JavaScriptTabsConfig | NativeTabsConfig;
|
|
176
176
|
|
|
177
177
|
interface NavigatorNodeBase {
|
|
178
178
|
initialRouteName?: string;
|
|
@@ -237,11 +237,6 @@ export interface RouteDefinition {
|
|
|
237
237
|
navigator?: NavigatorNode;
|
|
238
238
|
}
|
|
239
239
|
|
|
240
|
-
export interface NavigatorFlows {
|
|
241
|
-
onboarding?: boolean;
|
|
242
|
-
authentication?: boolean;
|
|
243
|
-
}
|
|
244
|
-
|
|
245
240
|
export interface NavigatorDefaults {
|
|
246
241
|
tabs?: TabsImplementationConfig;
|
|
247
242
|
stack?: StackImplementationConfig;
|
|
@@ -266,7 +261,52 @@ export interface NavigatorPlatforms {
|
|
|
266
261
|
*/
|
|
267
262
|
export type AppNavigatorManifest = NavigatorNode & {
|
|
268
263
|
preset?: NavigatorPreset;
|
|
269
|
-
flows?: NavigatorFlows;
|
|
270
264
|
defaults?: NavigatorDefaults;
|
|
271
265
|
platforms?: NavigatorPlatforms;
|
|
272
266
|
};
|
|
267
|
+
|
|
268
|
+
export type {
|
|
269
|
+
NavigatorCapabilityDescriptor,
|
|
270
|
+
NavigatorCapabilityRequirement,
|
|
271
|
+
NavigatorCapabilityTarget,
|
|
272
|
+
NavigatorCapabilityVerification,
|
|
273
|
+
NavigatorCatalog,
|
|
274
|
+
NavigatorImplementation,
|
|
275
|
+
NavigatorPresentation,
|
|
276
|
+
NavigatorPresetDescriptor,
|
|
277
|
+
NavigatorVerificationKind,
|
|
278
|
+
NavigatorVerificationStatus,
|
|
279
|
+
} from './navigator/catalog';
|
|
280
|
+
export type {
|
|
281
|
+
CustomNavigatorConfigIssue,
|
|
282
|
+
CustomNavigatorRegistration,
|
|
283
|
+
CustomNavigatorRegistry,
|
|
284
|
+
} from './navigator/extensions';
|
|
285
|
+
export type {
|
|
286
|
+
NavigatorDependencyRequirement,
|
|
287
|
+
NavigatorGeneratedFile,
|
|
288
|
+
NavigatorGenerationBindings,
|
|
289
|
+
NavigatorGenerationOptions,
|
|
290
|
+
NavigatorGenerationResult,
|
|
291
|
+
NavigatorScreenModule,
|
|
292
|
+
} from './navigator/generation';
|
|
293
|
+
export type {
|
|
294
|
+
CreateNavigatorPlanOptions,
|
|
295
|
+
ExpoRouterNavigatorModule,
|
|
296
|
+
NavigatorAdapterId,
|
|
297
|
+
NavigatorAdapterPlan,
|
|
298
|
+
NavigatorApiStability,
|
|
299
|
+
NavigatorCapabilityId,
|
|
300
|
+
NavigatorDiagnostic,
|
|
301
|
+
NavigatorNodePlan,
|
|
302
|
+
NavigatorPlan,
|
|
303
|
+
NavigatorResponsiveSize,
|
|
304
|
+
NavigatorRoutePlan,
|
|
305
|
+
NavigatorRuntimePlatform,
|
|
306
|
+
NavigatorSupportStatus,
|
|
307
|
+
NavigatorValidationContext,
|
|
308
|
+
ResolvedHeadlessTabsPresentation,
|
|
309
|
+
ResolvedTabsImplementation,
|
|
310
|
+
ResolvedTabsPresentation,
|
|
311
|
+
TabsNavigatorPlan,
|
|
312
|
+
} from './navigator/planning';
|