@benjavicente/angular-router-experimental 1.142.11
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/LICENSE +21 -0
- package/README.md +15 -0
- package/dist/fesm2022/tanstack-angular-router-experimental-experimental.mjs +920 -0
- package/dist/fesm2022/tanstack-angular-router-experimental.mjs +4131 -0
- package/dist/types/tanstack-angular-router-experimental-experimental.d.ts +110 -0
- package/dist/types/tanstack-angular-router-experimental.d.ts +733 -0
- package/experimental/injectRouteErrorHandler.ts +51 -0
- package/experimental/public_api.ts +8 -0
- package/package.json +98 -0
- package/src/DefaultNotFound.ts +9 -0
- package/src/Link.ts +352 -0
- package/src/Match.ts +338 -0
- package/src/Matches.ts +37 -0
- package/src/RouterProvider.ts +162 -0
- package/src/document/build-match-managed-document.ts +308 -0
- package/src/document/document-dehydration.ts +27 -0
- package/src/document/document-equality.ts +29 -0
- package/src/document/document-router-token.ts +6 -0
- package/src/document/index.ts +33 -0
- package/src/document/install-unified-document-sync.ts +108 -0
- package/src/document/managed-document-types.ts +36 -0
- package/src/document/managed-dom.ts +307 -0
- package/src/document/provide-tanstack-body-managed-tags.ts +78 -0
- package/src/document/provide-tanstack-document-title.ts +59 -0
- package/src/document/provide-tanstack-document.ts +62 -0
- package/src/document/provide-tanstack-head-managed-tags.ts +63 -0
- package/src/fileRoute.ts +232 -0
- package/src/index.ts +173 -0
- package/src/injectBlocker.ts +196 -0
- package/src/injectCanGoBack.ts +11 -0
- package/src/injectErrorState.ts +21 -0
- package/src/injectIntersectionObserver.ts +28 -0
- package/src/injectLoaderData.ts +49 -0
- package/src/injectLoaderDeps.ts +45 -0
- package/src/injectLocation.ts +38 -0
- package/src/injectMatch.ts +122 -0
- package/src/injectMatchRoute.ts +58 -0
- package/src/injectMatches.ts +79 -0
- package/src/injectNavigate.ts +24 -0
- package/src/injectParams.ts +71 -0
- package/src/injectRouteContext.ts +31 -0
- package/src/injectRouter.ts +17 -0
- package/src/injectRouterState.ts +53 -0
- package/src/injectSearch.ts +71 -0
- package/src/injectStore.ts +87 -0
- package/src/matchInjectorToken.ts +23 -0
- package/src/renderer/injectIsCatchingError.ts +40 -0
- package/src/renderer/injectRender.ts +69 -0
- package/src/route.ts +641 -0
- package/src/router.ts +141 -0
- package/src/routerInjectionToken.ts +24 -0
- package/src/routerStores.ts +107 -0
- package/src/ssr-scroll-restoration.ts +48 -0
- package/src/transitioner.ts +255 -0
package/src/fileRoute.ts
ADDED
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
import { createRoute } from './route'
|
|
2
|
+
|
|
3
|
+
import { injectMatch } from './injectMatch'
|
|
4
|
+
import { injectLoaderDeps } from './injectLoaderDeps'
|
|
5
|
+
import { injectLoaderData } from './injectLoaderData'
|
|
6
|
+
import { injectSearch } from './injectSearch'
|
|
7
|
+
import { injectParams } from './injectParams'
|
|
8
|
+
import { injectNavigate } from './injectNavigate'
|
|
9
|
+
import { injectRouter } from './injectRouter'
|
|
10
|
+
import { injectRouteContext } from './injectRouteContext'
|
|
11
|
+
import type { InjectParamsRoute } from './injectParams'
|
|
12
|
+
import type { InjectMatchRoute } from './injectMatch'
|
|
13
|
+
import type { InjectSearchRoute } from './injectSearch'
|
|
14
|
+
import type {
|
|
15
|
+
AnyContext,
|
|
16
|
+
AnyRoute,
|
|
17
|
+
AnyRouter,
|
|
18
|
+
ConstrainLiteral,
|
|
19
|
+
FileBaseRouteOptions,
|
|
20
|
+
FileRoutesByPath,
|
|
21
|
+
LazyRouteOptions,
|
|
22
|
+
Register,
|
|
23
|
+
RegisteredRouter,
|
|
24
|
+
ResolveParams,
|
|
25
|
+
Route,
|
|
26
|
+
RouteById,
|
|
27
|
+
RouteConstraints,
|
|
28
|
+
RouteIds,
|
|
29
|
+
UpdatableRouteOptions,
|
|
30
|
+
UseNavigateResult,
|
|
31
|
+
} from '@benjavicente/router-core'
|
|
32
|
+
import type { InjectLoaderDepsRoute } from './injectLoaderDeps'
|
|
33
|
+
import type { InjectLoaderDataRoute } from './injectLoaderData'
|
|
34
|
+
import type { InjectRouteContextRoute } from './injectRouteContext'
|
|
35
|
+
|
|
36
|
+
export function createFileRoute<
|
|
37
|
+
TFilePath extends keyof FileRoutesByPath,
|
|
38
|
+
TParentRoute extends AnyRoute = FileRoutesByPath[TFilePath]['parentRoute'],
|
|
39
|
+
TId extends RouteConstraints['TId'] = FileRoutesByPath[TFilePath]['id'],
|
|
40
|
+
TPath extends RouteConstraints['TPath'] = FileRoutesByPath[TFilePath]['path'],
|
|
41
|
+
TFullPath extends
|
|
42
|
+
RouteConstraints['TFullPath'] = FileRoutesByPath[TFilePath]['fullPath'],
|
|
43
|
+
>(
|
|
44
|
+
path?: TFilePath,
|
|
45
|
+
): InternalFileRouteFactory<
|
|
46
|
+
TFilePath,
|
|
47
|
+
TParentRoute,
|
|
48
|
+
TId,
|
|
49
|
+
TPath,
|
|
50
|
+
TFullPath
|
|
51
|
+
>['createRoute'] {
|
|
52
|
+
if (typeof path === 'object') {
|
|
53
|
+
return new InternalFileRouteFactory<
|
|
54
|
+
TFilePath,
|
|
55
|
+
TParentRoute,
|
|
56
|
+
TId,
|
|
57
|
+
TPath,
|
|
58
|
+
TFullPath
|
|
59
|
+
>().createRoute(path) as any
|
|
60
|
+
}
|
|
61
|
+
return new InternalFileRouteFactory<
|
|
62
|
+
TFilePath,
|
|
63
|
+
TParentRoute,
|
|
64
|
+
TId,
|
|
65
|
+
TPath,
|
|
66
|
+
TFullPath
|
|
67
|
+
>().createRoute
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
class InternalFileRouteFactory<
|
|
71
|
+
TFilePath extends keyof FileRoutesByPath,
|
|
72
|
+
TParentRoute extends AnyRoute = FileRoutesByPath[TFilePath]['parentRoute'],
|
|
73
|
+
TId extends RouteConstraints['TId'] = FileRoutesByPath[TFilePath]['id'],
|
|
74
|
+
TPath extends RouteConstraints['TPath'] = FileRoutesByPath[TFilePath]['path'],
|
|
75
|
+
TFullPath extends
|
|
76
|
+
RouteConstraints['TFullPath'] = FileRoutesByPath[TFilePath]['fullPath'],
|
|
77
|
+
> {
|
|
78
|
+
createRoute = <
|
|
79
|
+
TRegister = Register,
|
|
80
|
+
TSearchValidator = undefined,
|
|
81
|
+
TParams = ResolveParams<TPath>,
|
|
82
|
+
TRouteContextFn = AnyContext,
|
|
83
|
+
TBeforeLoadFn = AnyContext,
|
|
84
|
+
TLoaderDeps extends Record<string, any> = {},
|
|
85
|
+
TLoaderFn = undefined,
|
|
86
|
+
TChildren = unknown,
|
|
87
|
+
TSSR = unknown,
|
|
88
|
+
TMiddlewares = unknown,
|
|
89
|
+
THandlers = undefined,
|
|
90
|
+
>(
|
|
91
|
+
options?: FileBaseRouteOptions<
|
|
92
|
+
TRegister,
|
|
93
|
+
TParentRoute,
|
|
94
|
+
TId,
|
|
95
|
+
TPath,
|
|
96
|
+
TSearchValidator,
|
|
97
|
+
TParams,
|
|
98
|
+
TLoaderDeps,
|
|
99
|
+
TLoaderFn,
|
|
100
|
+
AnyContext,
|
|
101
|
+
TRouteContextFn,
|
|
102
|
+
TBeforeLoadFn,
|
|
103
|
+
AnyContext,
|
|
104
|
+
TSSR,
|
|
105
|
+
TMiddlewares,
|
|
106
|
+
THandlers
|
|
107
|
+
> &
|
|
108
|
+
UpdatableRouteOptions<
|
|
109
|
+
TParentRoute,
|
|
110
|
+
TId,
|
|
111
|
+
TFullPath,
|
|
112
|
+
TParams,
|
|
113
|
+
TSearchValidator,
|
|
114
|
+
TLoaderFn,
|
|
115
|
+
TLoaderDeps,
|
|
116
|
+
AnyContext,
|
|
117
|
+
TRouteContextFn,
|
|
118
|
+
TBeforeLoadFn
|
|
119
|
+
>,
|
|
120
|
+
): Route<
|
|
121
|
+
TRegister,
|
|
122
|
+
TParentRoute,
|
|
123
|
+
TPath,
|
|
124
|
+
TFullPath,
|
|
125
|
+
TFilePath,
|
|
126
|
+
TId,
|
|
127
|
+
TSearchValidator,
|
|
128
|
+
TParams,
|
|
129
|
+
AnyContext,
|
|
130
|
+
TRouteContextFn,
|
|
131
|
+
TBeforeLoadFn,
|
|
132
|
+
TLoaderDeps,
|
|
133
|
+
TLoaderFn,
|
|
134
|
+
TChildren,
|
|
135
|
+
unknown,
|
|
136
|
+
TSSR,
|
|
137
|
+
TMiddlewares,
|
|
138
|
+
THandlers
|
|
139
|
+
> => {
|
|
140
|
+
const route = createRoute(options as any)
|
|
141
|
+
; (route as any).isRoot = false
|
|
142
|
+
return route as any
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
declare module '@benjavicente/router-core' {
|
|
147
|
+
export interface LazyRoute<in out TRoute extends AnyRoute> {
|
|
148
|
+
injectMatch: InjectMatchRoute<TRoute['id']>
|
|
149
|
+
injectRouteContext: InjectRouteContextRoute<TRoute['id']>
|
|
150
|
+
injectSearch: InjectSearchRoute<TRoute['id']>
|
|
151
|
+
injectParams: InjectParamsRoute<TRoute['id']>
|
|
152
|
+
injectLoaderDeps: InjectLoaderDepsRoute<TRoute['id']>
|
|
153
|
+
injectLoaderData: InjectLoaderDataRoute<TRoute['id']>
|
|
154
|
+
injectNavigate: () => UseNavigateResult<TRoute['fullPath']>
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export class LazyRoute<TRoute extends AnyRoute> {
|
|
159
|
+
options: {
|
|
160
|
+
id: string
|
|
161
|
+
} & LazyRouteOptions
|
|
162
|
+
|
|
163
|
+
constructor(
|
|
164
|
+
opts: {
|
|
165
|
+
id: string
|
|
166
|
+
} & LazyRouteOptions,
|
|
167
|
+
) {
|
|
168
|
+
this.options = opts
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
injectMatch: InjectMatchRoute<TRoute['id']> = (opts) => {
|
|
172
|
+
return injectMatch({
|
|
173
|
+
select: opts?.select,
|
|
174
|
+
from: this.options.id,
|
|
175
|
+
} as any) as any
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
injectRouteContext: InjectRouteContextRoute<TRoute['id']> = (opts) => {
|
|
179
|
+
return injectRouteContext({ ...(opts as any), from: this.options.id }) as any
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
injectSearch: InjectSearchRoute<TRoute['id']> = (opts) => {
|
|
183
|
+
return injectSearch({
|
|
184
|
+
select: opts?.select,
|
|
185
|
+
from: this.options.id,
|
|
186
|
+
} as any) as any
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
injectParams: InjectParamsRoute<TRoute['id']> = (opts) => {
|
|
190
|
+
return injectParams({
|
|
191
|
+
select: opts?.select,
|
|
192
|
+
from: this.options.id,
|
|
193
|
+
} as any) as any
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
injectLoaderDeps: InjectLoaderDepsRoute<TRoute['id']> = (opts) => {
|
|
197
|
+
return injectLoaderDeps({ ...opts, from: this.options.id } as any)
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
injectLoaderData: InjectLoaderDataRoute<TRoute['id']> = (opts) => {
|
|
201
|
+
return injectLoaderData({ ...opts, from: this.options.id } as any)
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
injectNavigate = (): UseNavigateResult<TRoute['fullPath']> => {
|
|
205
|
+
const router = injectRouter()
|
|
206
|
+
return injectNavigate({ from: router.routesById[this.options.id].fullPath })
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
export function createLazyRoute<
|
|
211
|
+
TRouter extends AnyRouter = RegisteredRouter,
|
|
212
|
+
TId extends string = string,
|
|
213
|
+
TRoute extends AnyRoute = RouteById<TRouter['routeTree'], TId>,
|
|
214
|
+
>(id: ConstrainLiteral<TId, RouteIds<TRouter['routeTree']>>) {
|
|
215
|
+
return (opts: LazyRouteOptions) => {
|
|
216
|
+
return new LazyRoute<TRoute>({
|
|
217
|
+
id: id,
|
|
218
|
+
...opts,
|
|
219
|
+
})
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
export function createLazyFileRoute<
|
|
224
|
+
TFilePath extends keyof FileRoutesByPath,
|
|
225
|
+
TRoute extends FileRoutesByPath[TFilePath]['preLoaderRoute'],
|
|
226
|
+
>(id: TFilePath): (opts: LazyRouteOptions) => LazyRoute<TRoute> {
|
|
227
|
+
if (typeof id === 'object') {
|
|
228
|
+
return new LazyRoute<TRoute>(id) as any
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
return (opts: LazyRouteOptions) => new LazyRoute<TRoute>({ id, ...opts })
|
|
232
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
// Router
|
|
2
|
+
export {
|
|
3
|
+
createRouter,
|
|
4
|
+
Router,
|
|
5
|
+
type AngularInjectFn,
|
|
6
|
+
type AngularRouterContext,
|
|
7
|
+
} from './router'
|
|
8
|
+
|
|
9
|
+
// Route creation
|
|
10
|
+
export {
|
|
11
|
+
createRoute,
|
|
12
|
+
createRootRoute,
|
|
13
|
+
createRootRouteWithContext,
|
|
14
|
+
createRouteMask,
|
|
15
|
+
getRouteApi,
|
|
16
|
+
Route,
|
|
17
|
+
RootRoute,
|
|
18
|
+
NotFoundRoute,
|
|
19
|
+
RouteApi,
|
|
20
|
+
type AnyRootRoute,
|
|
21
|
+
type RouteComponent,
|
|
22
|
+
type ErrorRouteComponent,
|
|
23
|
+
type NotFoundRouteComponent,
|
|
24
|
+
} from './route'
|
|
25
|
+
|
|
26
|
+
export {
|
|
27
|
+
createFileRoute,
|
|
28
|
+
LazyRoute,
|
|
29
|
+
createLazyRoute,
|
|
30
|
+
createLazyFileRoute,
|
|
31
|
+
} from './fileRoute'
|
|
32
|
+
|
|
33
|
+
// Router Provider
|
|
34
|
+
export { RouterProvider, provideTanstackRouter } from './RouterProvider'
|
|
35
|
+
export {
|
|
36
|
+
provideHeadContent,
|
|
37
|
+
provideTanstackDocument,
|
|
38
|
+
provideTanstackDocumentTitle,
|
|
39
|
+
provideTanstackHeadManagedTags,
|
|
40
|
+
provideTanstackBodyManagedTags,
|
|
41
|
+
type TanstackDocumentFeatures,
|
|
42
|
+
buildMatchManagedDocumentContent,
|
|
43
|
+
buildManagedDocumentContent,
|
|
44
|
+
} from './document'
|
|
45
|
+
|
|
46
|
+
// Components
|
|
47
|
+
export { Outlet, RouteMatch } from './Match'
|
|
48
|
+
export { Matches } from './Matches'
|
|
49
|
+
export { injectSsrScrollRestorationScript } from './ssr-scroll-restoration'
|
|
50
|
+
export {
|
|
51
|
+
injectMatches,
|
|
52
|
+
injectParentMatches,
|
|
53
|
+
injectChildMatches,
|
|
54
|
+
type InjectMatchesBaseOptions,
|
|
55
|
+
type InjectMatchesResult,
|
|
56
|
+
} from './injectMatches'
|
|
57
|
+
export { injectMatchRoute, type InjectMatchRouteOptions } from './injectMatchRoute'
|
|
58
|
+
|
|
59
|
+
// Injection functions
|
|
60
|
+
export { injectRouter } from './injectRouter'
|
|
61
|
+
|
|
62
|
+
export {
|
|
63
|
+
injectRouterState,
|
|
64
|
+
type InjectRouterStateOptions,
|
|
65
|
+
type InjectRouterStateResult,
|
|
66
|
+
} from './injectRouterState'
|
|
67
|
+
|
|
68
|
+
export { injectNavigate } from './injectNavigate'
|
|
69
|
+
|
|
70
|
+
export {
|
|
71
|
+
injectMatch,
|
|
72
|
+
type InjectMatchOptions,
|
|
73
|
+
type InjectMatchResult,
|
|
74
|
+
type InjectMatchRoute,
|
|
75
|
+
type InjectMatchBaseOptions,
|
|
76
|
+
} from './injectMatch'
|
|
77
|
+
|
|
78
|
+
export {
|
|
79
|
+
injectParams,
|
|
80
|
+
type InjectParamsOptions,
|
|
81
|
+
type InjectParamsRoute,
|
|
82
|
+
type InjectParamsBaseOptions,
|
|
83
|
+
} from './injectParams'
|
|
84
|
+
|
|
85
|
+
export {
|
|
86
|
+
injectSearch,
|
|
87
|
+
type InjectSearchOptions,
|
|
88
|
+
type InjectSearchRoute,
|
|
89
|
+
type InjectSearchBaseOptions,
|
|
90
|
+
} from './injectSearch'
|
|
91
|
+
|
|
92
|
+
export {
|
|
93
|
+
injectLoaderData,
|
|
94
|
+
type InjectLoaderDataOptions,
|
|
95
|
+
type InjectLoaderDataRoute,
|
|
96
|
+
type InjectLoaderDataBaseOptions,
|
|
97
|
+
} from './injectLoaderData'
|
|
98
|
+
|
|
99
|
+
export {
|
|
100
|
+
injectLoaderDeps,
|
|
101
|
+
type InjectLoaderDepsOptions,
|
|
102
|
+
type InjectLoaderDepsRoute,
|
|
103
|
+
type InjectLoaderDepsBaseOptions,
|
|
104
|
+
} from './injectLoaderDeps'
|
|
105
|
+
|
|
106
|
+
export {
|
|
107
|
+
injectRouteContext,
|
|
108
|
+
type InjectRouteContextRoute,
|
|
109
|
+
} from './injectRouteContext'
|
|
110
|
+
|
|
111
|
+
export {
|
|
112
|
+
injectLocation,
|
|
113
|
+
type InjectLocationOptions,
|
|
114
|
+
type InjectLocationResult,
|
|
115
|
+
} from './injectLocation'
|
|
116
|
+
|
|
117
|
+
export {
|
|
118
|
+
injectBlocker,
|
|
119
|
+
type InjectBlockerOpts,
|
|
120
|
+
type UseBlockerOpts,
|
|
121
|
+
type ShouldBlockFn,
|
|
122
|
+
} from './injectBlocker'
|
|
123
|
+
|
|
124
|
+
export { injectCanGoBack } from './injectCanGoBack'
|
|
125
|
+
|
|
126
|
+
export { injectErrorState } from './injectErrorState'
|
|
127
|
+
|
|
128
|
+
export { injectStore } from './injectStore'
|
|
129
|
+
|
|
130
|
+
// Link
|
|
131
|
+
export { type LinkOptions as LinkInputOptions, Link } from './Link'
|
|
132
|
+
|
|
133
|
+
// Core re-exports
|
|
134
|
+
export {
|
|
135
|
+
lazyFn,
|
|
136
|
+
notFound,
|
|
137
|
+
redirect,
|
|
138
|
+
isRedirect,
|
|
139
|
+
retainSearchParams,
|
|
140
|
+
createRouterConfig,
|
|
141
|
+
} from '@benjavicente/router-core'
|
|
142
|
+
|
|
143
|
+
// History utilities
|
|
144
|
+
export {
|
|
145
|
+
createHistory,
|
|
146
|
+
createBrowserHistory,
|
|
147
|
+
createHashHistory,
|
|
148
|
+
createMemoryHistory,
|
|
149
|
+
} from '@benjavicente/history'
|
|
150
|
+
|
|
151
|
+
export type {
|
|
152
|
+
BlockerFn,
|
|
153
|
+
HistoryLocation,
|
|
154
|
+
RouterHistory,
|
|
155
|
+
ParsedPath,
|
|
156
|
+
HistoryState,
|
|
157
|
+
} from '@benjavicente/history'
|
|
158
|
+
|
|
159
|
+
// Re-export types from router-core that are commonly used (FileRoutesByPath augmented by routeTree.gen.ts via declare module '@benjavicente/router-core')
|
|
160
|
+
export type {
|
|
161
|
+
AnyRouter,
|
|
162
|
+
RegisteredRouter,
|
|
163
|
+
RouterState,
|
|
164
|
+
LinkOptions,
|
|
165
|
+
NavigateOptions,
|
|
166
|
+
RouteOptions,
|
|
167
|
+
RootRouteOptions,
|
|
168
|
+
Register,
|
|
169
|
+
RouterContextOptions,
|
|
170
|
+
FileRoutesByPath,
|
|
171
|
+
CreateFileRoute,
|
|
172
|
+
CreateLazyFileRoute,
|
|
173
|
+
} from '@benjavicente/router-core'
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import * as Angular from '@angular/core'
|
|
2
|
+
import { injectRouter } from './injectRouter'
|
|
3
|
+
import type {
|
|
4
|
+
BlockerFnArgs,
|
|
5
|
+
HistoryAction,
|
|
6
|
+
HistoryLocation,
|
|
7
|
+
} from '@benjavicente/history'
|
|
8
|
+
import type {
|
|
9
|
+
AnyRoute,
|
|
10
|
+
AnyRouter,
|
|
11
|
+
ParseRoute,
|
|
12
|
+
RegisteredRouter,
|
|
13
|
+
} from '@benjavicente/router-core'
|
|
14
|
+
|
|
15
|
+
interface ShouldBlockFnLocation<
|
|
16
|
+
out TRouteId,
|
|
17
|
+
out TFullPath,
|
|
18
|
+
out TAllParams,
|
|
19
|
+
out TFullSearchSchema,
|
|
20
|
+
> {
|
|
21
|
+
routeId: TRouteId
|
|
22
|
+
fullPath: TFullPath
|
|
23
|
+
pathname: string
|
|
24
|
+
params: TAllParams
|
|
25
|
+
search: TFullSearchSchema
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
type AnyShouldBlockFnLocation = ShouldBlockFnLocation<any, any, any, any>
|
|
29
|
+
|
|
30
|
+
type MakeShouldBlockFnLocationUnion<
|
|
31
|
+
TRouter extends AnyRouter = RegisteredRouter,
|
|
32
|
+
TRoute extends AnyRoute = ParseRoute<TRouter['routeTree']>,
|
|
33
|
+
> = TRoute extends any
|
|
34
|
+
? ShouldBlockFnLocation<
|
|
35
|
+
TRoute['id'],
|
|
36
|
+
TRoute['fullPath'],
|
|
37
|
+
TRoute['types']['allParams'],
|
|
38
|
+
TRoute['types']['fullSearchSchema']
|
|
39
|
+
>
|
|
40
|
+
: never
|
|
41
|
+
|
|
42
|
+
type BlockerResolver<TRouter extends AnyRouter = RegisteredRouter> =
|
|
43
|
+
| {
|
|
44
|
+
status: 'blocked'
|
|
45
|
+
current: MakeShouldBlockFnLocationUnion<TRouter>
|
|
46
|
+
next: MakeShouldBlockFnLocationUnion<TRouter>
|
|
47
|
+
action: HistoryAction
|
|
48
|
+
proceed: () => void
|
|
49
|
+
reset: () => void
|
|
50
|
+
}
|
|
51
|
+
| {
|
|
52
|
+
status: 'idle'
|
|
53
|
+
current: undefined
|
|
54
|
+
next: undefined
|
|
55
|
+
action: undefined
|
|
56
|
+
proceed: undefined
|
|
57
|
+
reset: undefined
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
type ShouldBlockFnArgs<TRouter extends AnyRouter = RegisteredRouter> = {
|
|
61
|
+
current: MakeShouldBlockFnLocationUnion<TRouter>
|
|
62
|
+
next: MakeShouldBlockFnLocationUnion<TRouter>
|
|
63
|
+
action: HistoryAction
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export type ShouldBlockFn<TRouter extends AnyRouter = RegisteredRouter> = (
|
|
67
|
+
args: ShouldBlockFnArgs<TRouter>,
|
|
68
|
+
) => boolean | Promise<boolean>
|
|
69
|
+
|
|
70
|
+
export type UseBlockerOpts<
|
|
71
|
+
TRouter extends AnyRouter = RegisteredRouter,
|
|
72
|
+
TWithResolver extends boolean = boolean,
|
|
73
|
+
> = {
|
|
74
|
+
shouldBlockFn: ShouldBlockFn<TRouter>
|
|
75
|
+
enableBeforeUnload?: boolean | (() => boolean)
|
|
76
|
+
disabled?: boolean
|
|
77
|
+
withResolver?: TWithResolver
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export type InjectBlockerOpts<
|
|
81
|
+
TRouter extends AnyRouter = RegisteredRouter,
|
|
82
|
+
TWithResolver extends boolean = boolean,
|
|
83
|
+
> = {
|
|
84
|
+
shouldBlockFn: ShouldBlockFn<TRouter>
|
|
85
|
+
enableBeforeUnload?: boolean | (() => boolean)
|
|
86
|
+
disabled?: boolean | (() => boolean)
|
|
87
|
+
withResolver?: TWithResolver
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export function injectBlocker<
|
|
91
|
+
TRouter extends AnyRouter = RegisteredRouter,
|
|
92
|
+
TWithResolver extends boolean = boolean,
|
|
93
|
+
>(
|
|
94
|
+
opts: InjectBlockerOpts<TRouter, TWithResolver>,
|
|
95
|
+
): TWithResolver extends true
|
|
96
|
+
? Angular.Signal<BlockerResolver<TRouter>>
|
|
97
|
+
: void {
|
|
98
|
+
const {
|
|
99
|
+
shouldBlockFn,
|
|
100
|
+
enableBeforeUnload = true,
|
|
101
|
+
disabled = false,
|
|
102
|
+
withResolver = false,
|
|
103
|
+
} = opts
|
|
104
|
+
const router = injectRouter()
|
|
105
|
+
const { history } = router
|
|
106
|
+
|
|
107
|
+
const resolver = Angular.signal<BlockerResolver>({
|
|
108
|
+
status: 'idle',
|
|
109
|
+
current: undefined,
|
|
110
|
+
next: undefined,
|
|
111
|
+
action: undefined,
|
|
112
|
+
proceed: undefined,
|
|
113
|
+
reset: undefined,
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
Angular.effect((onCleanup) => {
|
|
117
|
+
const blockerFnComposed = async (blockerFnArgs: BlockerFnArgs) => {
|
|
118
|
+
function getLocation(
|
|
119
|
+
location: HistoryLocation,
|
|
120
|
+
): AnyShouldBlockFnLocation {
|
|
121
|
+
const parsedLocation = router.parseLocation(location)
|
|
122
|
+
const matchedRoutes = router.getMatchedRoutes(parsedLocation.pathname)
|
|
123
|
+
if (matchedRoutes.foundRoute === undefined) {
|
|
124
|
+
return {
|
|
125
|
+
routeId: '__notFound__',
|
|
126
|
+
fullPath: parsedLocation.pathname,
|
|
127
|
+
pathname: parsedLocation.pathname,
|
|
128
|
+
params: matchedRoutes.routeParams,
|
|
129
|
+
search: parsedLocation.search,
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return {
|
|
133
|
+
routeId: matchedRoutes.foundRoute.id,
|
|
134
|
+
fullPath: matchedRoutes.foundRoute.fullPath,
|
|
135
|
+
pathname: parsedLocation.pathname,
|
|
136
|
+
params: matchedRoutes.routeParams,
|
|
137
|
+
search: parsedLocation.search,
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const current = getLocation(blockerFnArgs.currentLocation)
|
|
142
|
+
const next = getLocation(blockerFnArgs.nextLocation)
|
|
143
|
+
|
|
144
|
+
if (
|
|
145
|
+
current.routeId === '__notFound__' &&
|
|
146
|
+
next.routeId !== '__notFound__'
|
|
147
|
+
) {
|
|
148
|
+
return false
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const shouldBlock = await shouldBlockFn({
|
|
152
|
+
action: blockerFnArgs.action,
|
|
153
|
+
current: current as MakeShouldBlockFnLocationUnion<TRouter>,
|
|
154
|
+
next: next as MakeShouldBlockFnLocationUnion<TRouter>,
|
|
155
|
+
})
|
|
156
|
+
if (!withResolver) {
|
|
157
|
+
return shouldBlock
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (!shouldBlock) {
|
|
161
|
+
return false
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const promise = new Promise<boolean>((resolve) => {
|
|
165
|
+
resolver.set({
|
|
166
|
+
status: 'blocked',
|
|
167
|
+
current: current as MakeShouldBlockFnLocationUnion<TRouter>,
|
|
168
|
+
next: next as MakeShouldBlockFnLocationUnion<TRouter>,
|
|
169
|
+
action: blockerFnArgs.action,
|
|
170
|
+
proceed: () => resolve(false),
|
|
171
|
+
reset: () => resolve(true),
|
|
172
|
+
})
|
|
173
|
+
})
|
|
174
|
+
|
|
175
|
+
const canNavigateAsync = await promise
|
|
176
|
+
resolver.set({
|
|
177
|
+
status: 'idle',
|
|
178
|
+
current: undefined,
|
|
179
|
+
next: undefined,
|
|
180
|
+
action: undefined,
|
|
181
|
+
proceed: undefined,
|
|
182
|
+
reset: undefined,
|
|
183
|
+
})
|
|
184
|
+
|
|
185
|
+
return canNavigateAsync
|
|
186
|
+
}
|
|
187
|
+
const isDisabled = typeof disabled === 'function' ? disabled() : disabled
|
|
188
|
+
const disposeBlock = isDisabled
|
|
189
|
+
? undefined
|
|
190
|
+
: history.block({ blockerFn: blockerFnComposed, enableBeforeUnload })
|
|
191
|
+
|
|
192
|
+
onCleanup(() => disposeBlock?.())
|
|
193
|
+
})
|
|
194
|
+
|
|
195
|
+
return resolver.asReadonly() as any
|
|
196
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { injectRouter } from './injectRouter'
|
|
2
|
+
import { injectStore } from './injectStore'
|
|
3
|
+
|
|
4
|
+
export function injectCanGoBack() {
|
|
5
|
+
const router = injectRouter()
|
|
6
|
+
|
|
7
|
+
return injectStore(
|
|
8
|
+
router.stores.location,
|
|
9
|
+
(location) => location.state.__TSR_index !== 0,
|
|
10
|
+
)
|
|
11
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import * as Angular from '@angular/core'
|
|
2
|
+
|
|
3
|
+
export const ERROR_STATE_INJECTOR_TOKEN = new Angular.InjectionToken<{
|
|
4
|
+
error: Error
|
|
5
|
+
reset: () => void
|
|
6
|
+
info: { componentStack: string }
|
|
7
|
+
}>('ERROR_STATE_INJECTOR_TOKEN')
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Injects the error state to the error componenet.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
export function injectErrorState() {
|
|
14
|
+
const errorState = Angular.inject(ERROR_STATE_INJECTOR_TOKEN, {
|
|
15
|
+
optional: true,
|
|
16
|
+
})
|
|
17
|
+
if (!errorState) {
|
|
18
|
+
throw new Error('injectErrorState was called outside of an error component')
|
|
19
|
+
}
|
|
20
|
+
return errorState
|
|
21
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import * as Angular from '@angular/core'
|
|
2
|
+
|
|
3
|
+
export function injectIntersectionObserver(
|
|
4
|
+
callback: (entry: IntersectionObserverEntry | undefined) => void,
|
|
5
|
+
intersectionObserverOptions: IntersectionObserverInit,
|
|
6
|
+
disabled: () => boolean,
|
|
7
|
+
) {
|
|
8
|
+
const elementRef = Angular.inject(Angular.ElementRef)
|
|
9
|
+
|
|
10
|
+
Angular.afterRenderEffect((onCleanup) => {
|
|
11
|
+
const isIntersectionObserverAvailable =
|
|
12
|
+
typeof IntersectionObserver === 'function'
|
|
13
|
+
|
|
14
|
+
const element = elementRef.nativeElement as HTMLElement | null
|
|
15
|
+
if (!element || !isIntersectionObserverAvailable || disabled()) return
|
|
16
|
+
|
|
17
|
+
const observer = new IntersectionObserver(
|
|
18
|
+
([entry]) => callback(entry),
|
|
19
|
+
intersectionObserverOptions,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
observer.observe(element)
|
|
23
|
+
|
|
24
|
+
onCleanup(() => {
|
|
25
|
+
observer.disconnect()
|
|
26
|
+
})
|
|
27
|
+
})
|
|
28
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { injectMatch } from './injectMatch'
|
|
2
|
+
import type * as Angular from '@angular/core'
|
|
3
|
+
import type {
|
|
4
|
+
AnyRouter,
|
|
5
|
+
RegisteredRouter,
|
|
6
|
+
ResolveUseLoaderData,
|
|
7
|
+
StrictOrFrom,
|
|
8
|
+
UseLoaderDataResult,
|
|
9
|
+
} from '@benjavicente/router-core'
|
|
10
|
+
|
|
11
|
+
export interface InjectLoaderDataBaseOptions<
|
|
12
|
+
TRouter extends AnyRouter,
|
|
13
|
+
TFrom,
|
|
14
|
+
TStrict extends boolean,
|
|
15
|
+
TSelected,
|
|
16
|
+
> {
|
|
17
|
+
select?: (match: ResolveUseLoaderData<TRouter, TFrom, TStrict>) => TSelected
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export type InjectLoaderDataOptions<
|
|
21
|
+
TRouter extends AnyRouter,
|
|
22
|
+
TFrom extends string | undefined,
|
|
23
|
+
TStrict extends boolean,
|
|
24
|
+
TSelected,
|
|
25
|
+
> = StrictOrFrom<TRouter, TFrom, TStrict> &
|
|
26
|
+
InjectLoaderDataBaseOptions<TRouter, TFrom, TStrict, TSelected>
|
|
27
|
+
|
|
28
|
+
export type InjectLoaderDataRoute<out TFrom> = <
|
|
29
|
+
TRouter extends AnyRouter = RegisteredRouter,
|
|
30
|
+
TSelected = unknown,
|
|
31
|
+
>(
|
|
32
|
+
opts?: InjectLoaderDataBaseOptions<TRouter, TFrom, true, TSelected>,
|
|
33
|
+
) => Angular.Signal<UseLoaderDataResult<TRouter, TFrom, true, TSelected>>
|
|
34
|
+
|
|
35
|
+
export function injectLoaderData<
|
|
36
|
+
TRouter extends AnyRouter = RegisteredRouter,
|
|
37
|
+
const TFrom extends string | undefined = undefined,
|
|
38
|
+
TStrict extends boolean = true,
|
|
39
|
+
TSelected = unknown,
|
|
40
|
+
>(
|
|
41
|
+
opts: InjectLoaderDataOptions<TRouter, TFrom, TStrict, TSelected>,
|
|
42
|
+
): Angular.Signal<UseLoaderDataResult<TRouter, TFrom, TStrict, TSelected>> {
|
|
43
|
+
return injectMatch({
|
|
44
|
+
from: opts.from!,
|
|
45
|
+
strict: opts.strict as true | undefined,
|
|
46
|
+
select: (s: any) =>
|
|
47
|
+
opts.select ? opts.select(s.loaderData) : s.loaderData,
|
|
48
|
+
} as any) as any
|
|
49
|
+
}
|