@benjavicente/angular-router-experimental 1.142.11 → 1.142.13
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/dist/fesm2022/tanstack-angular-router-experimental-experimental.mjs +242 -204
- package/dist/fesm2022/tanstack-angular-router-experimental.mjs +1228 -1204
- package/dist/types/tanstack-angular-router-experimental-experimental.d.ts +38 -37
- package/dist/types/tanstack-angular-router-experimental.d.ts +59 -46
- package/package.json +4 -3
- package/src/Match.ts +90 -33
- package/src/Matches.ts +5 -2
- package/src/RouterProvider.ts +3 -0
- package/src/document/build-match-managed-document.ts +1 -1
- package/src/document/install-unified-document-sync.ts +6 -3
- package/src/document/provide-tanstack-body-managed-tags.ts +2 -2
- package/src/document/provide-tanstack-document-title.ts +4 -5
- package/src/document/provide-tanstack-head-managed-tags.ts +2 -2
- package/src/index.ts +6 -2
- package/src/injectCanGoBack.ts +2 -2
- package/src/injectIsShell.ts +7 -0
- package/src/injectLocation.ts +11 -8
- package/src/injectMatch.ts +45 -34
- package/src/injectMatchRoute.ts +4 -5
- package/src/injectMatches.ts +9 -9
- package/src/injectRouterState.ts +15 -10
- package/src/renderer/injectIsCatchingError.ts +20 -8
- package/src/renderer/injectRender.ts +4 -2
- package/src/route.ts +0 -3
- package/src/routerStores.ts +25 -52
- package/src/store/injectSelector.ts +62 -0
- package/src/store/injectStore.ts +33 -0
- package/src/transitioner.ts +12 -25
- package/src/injectStore.ts +0 -87
package/src/index.ts
CHANGED
|
@@ -54,10 +54,14 @@ export {
|
|
|
54
54
|
type InjectMatchesBaseOptions,
|
|
55
55
|
type InjectMatchesResult,
|
|
56
56
|
} from './injectMatches'
|
|
57
|
-
export {
|
|
57
|
+
export {
|
|
58
|
+
injectMatchRoute,
|
|
59
|
+
type InjectMatchRouteOptions,
|
|
60
|
+
} from './injectMatchRoute'
|
|
58
61
|
|
|
59
62
|
// Injection functions
|
|
60
63
|
export { injectRouter } from './injectRouter'
|
|
64
|
+
export { injectIsShell } from './injectIsShell'
|
|
61
65
|
|
|
62
66
|
export {
|
|
63
67
|
injectRouterState,
|
|
@@ -125,7 +129,7 @@ export { injectCanGoBack } from './injectCanGoBack'
|
|
|
125
129
|
|
|
126
130
|
export { injectErrorState } from './injectErrorState'
|
|
127
131
|
|
|
128
|
-
export { injectStore } from './injectStore'
|
|
132
|
+
export { injectStore } from './store/injectStore'
|
|
129
133
|
|
|
130
134
|
// Link
|
|
131
135
|
export { type LinkOptions as LinkInputOptions, Link } from './Link'
|
package/src/injectCanGoBack.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { injectRouter } from './injectRouter'
|
|
2
|
-
import { injectStore } from './injectStore'
|
|
2
|
+
import { injectStore } from './store/injectStore'
|
|
3
3
|
|
|
4
4
|
export function injectCanGoBack() {
|
|
5
5
|
const router = injectRouter()
|
|
6
6
|
|
|
7
7
|
return injectStore(
|
|
8
8
|
router.stores.location,
|
|
9
|
-
(location) => location.state
|
|
9
|
+
(location) => (location.state?.__TSR_index ?? 0) !== 0,
|
|
10
10
|
)
|
|
11
11
|
}
|
package/src/injectLocation.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { deepEqual } from '@benjavicente/router-core'
|
|
2
2
|
import { injectRouter } from './injectRouter'
|
|
3
|
-
import { injectStore } from './injectStore'
|
|
3
|
+
import { injectStore } from './store/injectStore'
|
|
4
|
+
import type * as Angular from '@angular/core'
|
|
4
5
|
import type {
|
|
5
6
|
AnyRouter,
|
|
6
7
|
RegisteredRouter,
|
|
7
8
|
RouterState,
|
|
8
9
|
} from '@benjavicente/router-core'
|
|
9
|
-
import type * as Angular from '@angular/core'
|
|
10
10
|
|
|
11
11
|
export interface InjectLocationOptions<TRouter extends AnyRouter, TSelected> {
|
|
12
12
|
select?: (
|
|
@@ -29,10 +29,13 @@ export function injectLocation<
|
|
|
29
29
|
): Angular.Signal<InjectLocationResult<TRouter, TSelected>> {
|
|
30
30
|
const router = injectRouter<TRouter>()
|
|
31
31
|
|
|
32
|
-
|
|
33
|
-
router.stores.location
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
32
|
+
if (!opts?.select) {
|
|
33
|
+
return injectStore(router.stores.location) as Angular.Signal<
|
|
34
|
+
InjectLocationResult<TRouter, TSelected>
|
|
35
|
+
>
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return injectStore(router.stores.location, opts.select, {
|
|
39
|
+
equal: deepEqual,
|
|
40
|
+
}) as Angular.Signal<InjectLocationResult<TRouter, TSelected>>
|
|
38
41
|
}
|
package/src/injectMatch.ts
CHANGED
|
@@ -2,7 +2,7 @@ import * as Angular from '@angular/core'
|
|
|
2
2
|
import { deepEqual, invariant } from '@benjavicente/router-core'
|
|
3
3
|
import { MATCH_CONTEXT_INJECTOR_TOKEN } from './matchInjectorToken'
|
|
4
4
|
import { injectRouter } from './injectRouter'
|
|
5
|
-
import { injectStore } from './injectStore'
|
|
5
|
+
import { injectStore } from './store/injectStore'
|
|
6
6
|
import type {
|
|
7
7
|
AnyRouter,
|
|
8
8
|
MakeRouteMatch,
|
|
@@ -13,6 +13,11 @@ import type {
|
|
|
13
13
|
ThrowOrOptional,
|
|
14
14
|
} from '@benjavicente/router-core'
|
|
15
15
|
|
|
16
|
+
const dummyStore = {
|
|
17
|
+
get: () => undefined,
|
|
18
|
+
subscribe: () => ({ unsubscribe: () => {} }),
|
|
19
|
+
} as any
|
|
20
|
+
|
|
16
21
|
export interface InjectMatchBaseOptions<
|
|
17
22
|
TRouter extends AnyRouter,
|
|
18
23
|
TFrom,
|
|
@@ -49,8 +54,8 @@ export type InjectMatchResult<
|
|
|
49
54
|
TSelected,
|
|
50
55
|
> = unknown extends TSelected
|
|
51
56
|
? TStrict extends true
|
|
52
|
-
|
|
53
|
-
|
|
57
|
+
? MakeRouteMatch<TRouter['routeTree'], TFrom, TStrict>
|
|
58
|
+
: MakeRouteMatchUnion<TRouter>
|
|
54
59
|
: TSelected
|
|
55
60
|
|
|
56
61
|
export function injectMatch<
|
|
@@ -75,48 +80,54 @@ export function injectMatch<
|
|
|
75
80
|
? undefined
|
|
76
81
|
: Angular.inject(MATCH_CONTEXT_INJECTOR_TOKEN)
|
|
77
82
|
|
|
83
|
+
const match = injectStore(
|
|
84
|
+
opts.from
|
|
85
|
+
? router.stores.getRouteMatchStore(opts.from)
|
|
86
|
+
: () => {
|
|
87
|
+
const matchId = nearestMatch?.matchId()
|
|
88
|
+
return matchId
|
|
89
|
+
? (router.stores.matchStores.get(matchId) ?? dummyStore)
|
|
90
|
+
: dummyStore
|
|
91
|
+
},
|
|
92
|
+
(d) => d,
|
|
93
|
+
)
|
|
78
94
|
const pendingRouteIds = injectStore(
|
|
79
95
|
router.stores.pendingRouteIds,
|
|
80
|
-
(
|
|
96
|
+
(ids) => ids,
|
|
81
97
|
)
|
|
82
98
|
const isTransitioning = injectStore(
|
|
83
99
|
router.stores.isTransitioning,
|
|
84
|
-
(
|
|
100
|
+
(value) => value,
|
|
85
101
|
)
|
|
86
102
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
return nearestMatch?.match()
|
|
93
|
-
}
|
|
103
|
+
return Angular.computed(
|
|
104
|
+
() => {
|
|
105
|
+
const selectedMatch = match()
|
|
94
106
|
|
|
95
|
-
|
|
96
|
-
|
|
107
|
+
if (selectedMatch !== undefined) {
|
|
108
|
+
return opts.select ? opts.select(selectedMatch as any) : selectedMatch
|
|
109
|
+
}
|
|
97
110
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
111
|
+
const hasPendingMatch = opts.from
|
|
112
|
+
? Boolean(pendingRouteIds()[opts.from!])
|
|
113
|
+
: (nearestMatch?.hasPending() ?? false)
|
|
101
114
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
115
|
+
if (
|
|
116
|
+
!hasPendingMatch &&
|
|
117
|
+
!isTransitioning() &&
|
|
118
|
+
(opts.shouldThrow ?? true)
|
|
119
|
+
) {
|
|
120
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
121
|
+
throw new Error(
|
|
122
|
+
`Invariant failed: Could not find ${opts.from ? `an active match from "${opts.from}"` : 'a nearest match!'}`,
|
|
123
|
+
)
|
|
124
|
+
}
|
|
105
125
|
|
|
106
|
-
|
|
107
|
-
!hasPendingMatch &&
|
|
108
|
-
!isTransitioning() &&
|
|
109
|
-
(opts.shouldThrow ?? true)
|
|
110
|
-
) {
|
|
111
|
-
if (process.env.NODE_ENV !== 'production') {
|
|
112
|
-
throw new Error(
|
|
113
|
-
`Invariant failed: Could not find ${opts.from ? `an active match from "${opts.from}"` : 'a nearest match!'}`,
|
|
114
|
-
)
|
|
126
|
+
invariant()
|
|
115
127
|
}
|
|
116
128
|
|
|
117
|
-
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
}, { equal: deepEqual }) as any
|
|
129
|
+
return undefined
|
|
130
|
+
},
|
|
131
|
+
{ equal: deepEqual },
|
|
132
|
+
) as any
|
|
122
133
|
}
|
package/src/injectMatchRoute.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as Angular from '@angular/core'
|
|
2
2
|
import { injectRouter } from './injectRouter'
|
|
3
|
-
import { injectStore } from './injectStore'
|
|
3
|
+
import { injectStore } from './store/injectStore'
|
|
4
4
|
import type {
|
|
5
5
|
AnyRouter,
|
|
6
6
|
DeepPartial,
|
|
@@ -9,7 +9,6 @@ import type {
|
|
|
9
9
|
MakeOptionalSearchParams,
|
|
10
10
|
MaskOptions,
|
|
11
11
|
MatchRouteOptions,
|
|
12
|
-
NoInfer,
|
|
13
12
|
RegisteredRouter,
|
|
14
13
|
ResolveRoute,
|
|
15
14
|
ToSubOptionsProps,
|
|
@@ -31,7 +30,7 @@ export function injectMatchRoute<
|
|
|
31
30
|
TRouter extends AnyRouter = RegisteredRouter,
|
|
32
31
|
>() {
|
|
33
32
|
const router = injectRouter<TRouter>()
|
|
34
|
-
const reactivity = injectStore(router.stores.
|
|
33
|
+
const reactivity = injectStore(router.stores.matchRouteDeps, (d) => d)
|
|
35
34
|
|
|
36
35
|
return <
|
|
37
36
|
const TFrom extends string = string,
|
|
@@ -41,12 +40,12 @@ export function injectMatchRoute<
|
|
|
41
40
|
>(
|
|
42
41
|
opts: InjectMatchRouteOptions<TRouter, TFrom, TTo, TMaskFrom, TMaskTo>,
|
|
43
42
|
): Angular.Signal<
|
|
44
|
-
false | Expand<ResolveRoute<TRouter, TFrom,
|
|
43
|
+
false | Expand<ResolveRoute<TRouter, TFrom, TTo>['types']['allParams']>
|
|
45
44
|
> => {
|
|
46
45
|
return Angular.computed(() => {
|
|
47
|
-
reactivity()
|
|
48
46
|
const { pending, caseSensitive, fuzzy, includeSearch, ...rest } = opts
|
|
49
47
|
|
|
48
|
+
reactivity()
|
|
50
49
|
return router.matchRoute(rest as any, {
|
|
51
50
|
pending,
|
|
52
51
|
caseSensitive,
|
package/src/injectMatches.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as Angular from '@angular/core'
|
|
2
2
|
import { deepEqual } from '@benjavicente/router-core'
|
|
3
3
|
import { injectRouter } from './injectRouter'
|
|
4
|
-
import { injectStore } from './injectStore'
|
|
4
|
+
import { injectStore } from './store/injectStore'
|
|
5
5
|
import { MATCH_CONTEXT_INJECTOR_TOKEN } from './matchInjectorToken'
|
|
6
6
|
import type {
|
|
7
7
|
AnyRouter,
|
|
@@ -28,15 +28,15 @@ export function injectMatches<
|
|
|
28
28
|
opts?: InjectMatchesBaseOptions<TRouter, TSelected>,
|
|
29
29
|
): Angular.Signal<InjectMatchesResult<TRouter, TSelected>> {
|
|
30
30
|
const router = injectRouter<TRouter>()
|
|
31
|
-
const matches = injectStore(router.stores.activeMatchesSnapshot, (value) => {
|
|
32
|
-
return value as Array<MakeRouteMatchUnion<TRouter>>
|
|
33
|
-
})
|
|
34
31
|
|
|
35
|
-
return
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
32
|
+
return injectStore(
|
|
33
|
+
router.stores.matches,
|
|
34
|
+
(currentMatches) => {
|
|
35
|
+
const matches = currentMatches as Array<MakeRouteMatchUnion<TRouter>>
|
|
36
|
+
return opts?.select ? opts.select(matches) : matches
|
|
37
|
+
},
|
|
38
|
+
{ equal: deepEqual },
|
|
39
|
+
) as Angular.Signal<InjectMatchesResult<TRouter, TSelected>>
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
export function injectParentMatches<
|
package/src/injectRouterState.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { isServer } from '@benjavicente/router-core/isServer'
|
|
|
2
2
|
import * as Angular from '@angular/core'
|
|
3
3
|
import { deepEqual } from '@benjavicente/router-core'
|
|
4
4
|
import { injectRouter } from './injectRouter'
|
|
5
|
-
import { injectStore } from './injectStore'
|
|
5
|
+
import { injectStore } from './store/injectStore'
|
|
6
6
|
import type {
|
|
7
7
|
AnyRouter,
|
|
8
8
|
RegisteredRouter,
|
|
@@ -29,25 +29,30 @@ export function injectRouterState<
|
|
|
29
29
|
warn: opts?.router === undefined,
|
|
30
30
|
})
|
|
31
31
|
const router = opts?.router ?? contextRouter
|
|
32
|
-
const state = injectStore(router.stores.__store, (state) => state)
|
|
33
32
|
|
|
34
33
|
// During SSR we render exactly once and do not need reactivity.
|
|
35
34
|
// Avoid subscribing to the store on the server since the server store
|
|
36
35
|
// implementation does not provide subscribe() semantics.
|
|
37
|
-
const _isServer =
|
|
38
|
-
typeof isServer === 'boolean' ? isServer : router.isServer
|
|
36
|
+
const _isServer = isServer ?? router.isServer
|
|
39
37
|
if (_isServer) {
|
|
40
|
-
const state = router.stores.__store.
|
|
38
|
+
const state = router.stores.__store.get() as RouterState<
|
|
41
39
|
TRouter['routeTree']
|
|
42
40
|
>
|
|
43
41
|
const selected = (
|
|
44
42
|
opts?.select ? opts.select(state) : state
|
|
45
43
|
) as InjectRouterStateResult<TRouter, TSelected>
|
|
46
|
-
return (() => selected) as Angular.Signal<
|
|
44
|
+
return (() => selected) as Angular.Signal<
|
|
45
|
+
InjectRouterStateResult<TRouter, TSelected>
|
|
46
|
+
>
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (!opts?.select) {
|
|
50
|
+
return injectStore(router.stores.__store) as Angular.Signal<
|
|
51
|
+
InjectRouterStateResult<TRouter, TSelected>
|
|
52
|
+
>
|
|
47
53
|
}
|
|
48
54
|
|
|
49
|
-
return
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
}, { equal: deepEqual }) as any;
|
|
55
|
+
return injectStore(router.stores.__store, opts.select, {
|
|
56
|
+
equal: deepEqual,
|
|
57
|
+
}) as Angular.Signal<InjectRouterStateResult<TRouter, TSelected>>
|
|
53
58
|
}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import * as Angular from '@angular/core'
|
|
2
2
|
import { injectRouter } from '../injectRouter'
|
|
3
3
|
import { injectRouterState } from '../injectRouterState'
|
|
4
|
-
import type { AnyRoute } from '@benjavicente/router-core'
|
|
4
|
+
import type { AnyRoute, AnyRouteMatch } from '@benjavicente/router-core'
|
|
5
5
|
|
|
6
|
-
export function
|
|
6
|
+
export function injectCatchingErrorMatch({
|
|
7
7
|
matchId,
|
|
8
8
|
}: {
|
|
9
9
|
matchId: Angular.Signal<string | undefined>
|
|
10
|
-
}): Angular.Signal<
|
|
10
|
+
}): Angular.Signal<AnyRouteMatch | undefined> {
|
|
11
11
|
const router = injectRouter()
|
|
12
12
|
|
|
13
13
|
const matches = injectRouterState({
|
|
@@ -20,21 +20,33 @@ export function injectIsCatchingError({
|
|
|
20
20
|
|
|
21
21
|
return Angular.computed(() => {
|
|
22
22
|
// The child route will handle the error with the default error component.
|
|
23
|
-
if (router.options.defaultErrorComponent != null) return
|
|
23
|
+
if (router.options.defaultErrorComponent != null) return undefined
|
|
24
24
|
|
|
25
25
|
const startingIndex = matchIndex()
|
|
26
|
-
if (startingIndex === -1) return
|
|
26
|
+
if (startingIndex === -1) return undefined
|
|
27
27
|
const matchesList = matches()
|
|
28
28
|
|
|
29
29
|
for (let i = startingIndex + 1; i < matchesList.length; i++) {
|
|
30
30
|
const descendant = matchesList[i]
|
|
31
31
|
const route = router.routesById[descendant?.routeId] as AnyRoute
|
|
32
32
|
// Is catched by a child route with an error component.
|
|
33
|
-
if (route.options.errorComponent != null) return
|
|
33
|
+
if (route.options.errorComponent != null) return undefined
|
|
34
34
|
|
|
35
35
|
// Found error status without error component in between.
|
|
36
|
-
if (descendant?.status ===
|
|
36
|
+
if (descendant?.status === 'error') return descendant
|
|
37
37
|
}
|
|
38
|
-
return
|
|
38
|
+
return undefined
|
|
39
|
+
})
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function injectIsCatchingError({
|
|
43
|
+
matchId,
|
|
44
|
+
}: {
|
|
45
|
+
matchId: Angular.Signal<string | undefined>
|
|
46
|
+
}): Angular.Signal<boolean> {
|
|
47
|
+
const catchingErrorMatch = injectCatchingErrorMatch({ matchId })
|
|
48
|
+
|
|
49
|
+
return Angular.computed(() => {
|
|
50
|
+
return catchingErrorMatch() !== undefined
|
|
39
51
|
})
|
|
40
52
|
}
|
|
@@ -10,7 +10,7 @@ import type { Provider, Type } from '@angular/core'
|
|
|
10
10
|
|
|
11
11
|
export type RenderValue =
|
|
12
12
|
| {
|
|
13
|
-
key?:
|
|
13
|
+
key?: unknown
|
|
14
14
|
component: Type<any> | null | undefined
|
|
15
15
|
inputs?: Record<string, () => unknown>
|
|
16
16
|
providers?: Array<Provider>
|
|
@@ -57,7 +57,9 @@ export function injectRender(renderValueFn: () => RenderValue): void {
|
|
|
57
57
|
function resolvedKey(value: RenderValue) {
|
|
58
58
|
const component = value?.component
|
|
59
59
|
if (!value || !component) return []
|
|
60
|
-
return
|
|
60
|
+
return Array.isArray(value.key)
|
|
61
|
+
? [component, ...value.key]
|
|
62
|
+
: [component, value.key]
|
|
61
63
|
}
|
|
62
64
|
|
|
63
65
|
function keysAreEqual(a: Array<any>, b: Array<any>) {
|
package/src/route.ts
CHANGED
package/src/routerStores.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { batch, createAtom } from '@tanstack/store'
|
|
2
2
|
import {
|
|
3
3
|
createNonReactiveMutableStore,
|
|
4
4
|
createNonReactiveReadonlyStore,
|
|
5
5
|
} from '@benjavicente/router-core'
|
|
6
6
|
import { isServer } from '@benjavicente/router-core/isServer'
|
|
7
|
+
import type { Readable } from '@tanstack/store'
|
|
7
8
|
import type {
|
|
8
9
|
AnyRoute,
|
|
9
10
|
GetStoreConfig,
|
|
@@ -13,9 +14,13 @@ import type {
|
|
|
13
14
|
} from '@benjavicente/router-core'
|
|
14
15
|
|
|
15
16
|
declare module '@benjavicente/router-core' {
|
|
17
|
+
export interface RouterReadableStore<TValue> extends Readable<TValue> {}
|
|
18
|
+
|
|
16
19
|
// eslint-disable-next-line unused-imports/no-unused-vars -- generic must match upstream `RouterStores<TRouteTree>` for augmentation
|
|
17
20
|
export interface RouterStores<in out TRouteTree extends AnyRoute> {
|
|
21
|
+
/** Maps each active routeId to the matchId of its child in the match tree. */
|
|
18
22
|
childMatchIdByRouteId: RouterReadableStore<Record<string, string>>
|
|
23
|
+
/** Maps each pending routeId to true for quick lookup. */
|
|
19
24
|
pendingRouteIds: RouterReadableStore<Record<string, boolean>>
|
|
20
25
|
}
|
|
21
26
|
}
|
|
@@ -27,68 +32,32 @@ function initRouterStores(
|
|
|
27
32
|
) => RouterReadableStore<TValue>,
|
|
28
33
|
) {
|
|
29
34
|
stores.childMatchIdByRouteId = createReadonlyStore(() => {
|
|
30
|
-
const ids = stores.matchesId.
|
|
31
|
-
const
|
|
32
|
-
|
|
35
|
+
const ids = stores.matchesId.get()
|
|
36
|
+
const obj: Record<string, string> = {}
|
|
33
37
|
for (let i = 0; i < ids.length - 1; i++) {
|
|
34
|
-
const
|
|
35
|
-
const childId = ids[i + 1]
|
|
36
|
-
if (matchId === undefined || childId === undefined) continue
|
|
37
|
-
const parentStore = stores.activeMatchStoresById.get(matchId)
|
|
38
|
+
const parentStore = stores.matchStores.get(ids[i]!)
|
|
38
39
|
if (parentStore?.routeId) {
|
|
39
|
-
|
|
40
|
+
obj[parentStore.routeId] = ids[i + 1]!
|
|
40
41
|
}
|
|
41
42
|
}
|
|
42
|
-
|
|
43
|
-
return result
|
|
43
|
+
return obj
|
|
44
44
|
})
|
|
45
45
|
|
|
46
46
|
stores.pendingRouteIds = createReadonlyStore(() => {
|
|
47
|
-
const ids = stores.
|
|
48
|
-
const
|
|
49
|
-
|
|
47
|
+
const ids = stores.pendingIds.get()
|
|
48
|
+
const obj: Record<string, boolean> = {}
|
|
50
49
|
for (const id of ids) {
|
|
51
|
-
const store = stores.
|
|
50
|
+
const store = stores.pendingMatchStores.get(id)
|
|
52
51
|
if (store?.routeId) {
|
|
53
|
-
|
|
52
|
+
obj[store.routeId] = true
|
|
54
53
|
}
|
|
55
54
|
}
|
|
56
|
-
|
|
57
|
-
return result
|
|
55
|
+
return obj
|
|
58
56
|
})
|
|
59
57
|
}
|
|
60
58
|
|
|
61
|
-
function createAngularMutableStore<TValue>(
|
|
62
|
-
initialValue: TValue,
|
|
63
|
-
): RouterWritableStore<TValue> {
|
|
64
|
-
const signal = Angular.signal(initialValue)
|
|
65
|
-
|
|
66
|
-
return {
|
|
67
|
-
get state() {
|
|
68
|
-
return signal()
|
|
69
|
-
},
|
|
70
|
-
setState(updater) {
|
|
71
|
-
signal.update(updater)
|
|
72
|
-
},
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
function createAngularReadonlyStore<TValue>(
|
|
77
|
-
read: () => TValue,
|
|
78
|
-
): RouterReadableStore<TValue> {
|
|
79
|
-
const computed = Angular.computed(read)
|
|
80
|
-
|
|
81
|
-
return {
|
|
82
|
-
get state() {
|
|
83
|
-
return computed()
|
|
84
|
-
},
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
59
|
export const getStoreFactory: GetStoreConfig = (opts) => {
|
|
89
|
-
|
|
90
|
-
typeof isServer === 'boolean' ? isServer : !!opts.isServer
|
|
91
|
-
if (useNonReactive) {
|
|
60
|
+
if (isServer ?? opts.isServer) {
|
|
92
61
|
return {
|
|
93
62
|
createMutableStore: createNonReactiveMutableStore,
|
|
94
63
|
createReadonlyStore: createNonReactiveReadonlyStore,
|
|
@@ -99,9 +68,13 @@ export const getStoreFactory: GetStoreConfig = (opts) => {
|
|
|
99
68
|
}
|
|
100
69
|
|
|
101
70
|
return {
|
|
102
|
-
createMutableStore:
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
71
|
+
createMutableStore: createAtom as <TValue>(
|
|
72
|
+
initialValue: TValue,
|
|
73
|
+
) => RouterWritableStore<TValue>,
|
|
74
|
+
createReadonlyStore: createAtom as <TValue>(
|
|
75
|
+
read: () => TValue,
|
|
76
|
+
) => RouterReadableStore<TValue>,
|
|
77
|
+
batch,
|
|
78
|
+
init: (stores) => initRouterStores(stores, createAtom),
|
|
106
79
|
}
|
|
107
80
|
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Injector,
|
|
3
|
+
assertInInjectionContext,
|
|
4
|
+
effect,
|
|
5
|
+
inject,
|
|
6
|
+
linkedSignal,
|
|
7
|
+
runInInjectionContext,
|
|
8
|
+
} from '@angular/core'
|
|
9
|
+
import type { CreateSignalOptions, Signal } from '@angular/core'
|
|
10
|
+
|
|
11
|
+
export interface InjectSelectorOptions<TSelected> extends Omit<
|
|
12
|
+
CreateSignalOptions<TSelected>,
|
|
13
|
+
'equal'
|
|
14
|
+
> {
|
|
15
|
+
compare?: (a: TSelected, b: TSelected) => boolean
|
|
16
|
+
injector?: Injector
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export type SelectionSource<T> = {
|
|
20
|
+
get: () => T
|
|
21
|
+
subscribe: (listener: (value: T) => void) => {
|
|
22
|
+
unsubscribe: () => void
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function resolveInjector(
|
|
27
|
+
fn: (...args: Array<never>) => unknown,
|
|
28
|
+
injector?: Injector,
|
|
29
|
+
) {
|
|
30
|
+
if (!injector) {
|
|
31
|
+
assertInInjectionContext(fn)
|
|
32
|
+
return inject(Injector)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return injector
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function injectSelector<TState, TSelected = NoInfer<TState>>(
|
|
39
|
+
source: SelectionSource<TState> | (() => SelectionSource<TState>),
|
|
40
|
+
selector: (state: NoInfer<TState>) => TSelected = (d) =>
|
|
41
|
+
d as unknown as TSelected,
|
|
42
|
+
options?: InjectSelectorOptions<TSelected>,
|
|
43
|
+
): Signal<TSelected> {
|
|
44
|
+
const injector = resolveInjector(injectSelector, options?.injector)
|
|
45
|
+
|
|
46
|
+
return runInInjectionContext(injector, () => {
|
|
47
|
+
const _source = typeof source === 'function' ? source : () => source
|
|
48
|
+
|
|
49
|
+
const slice = linkedSignal(() => selector(_source().get()), {
|
|
50
|
+
equal: options?.compare,
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
effect((onCleanup) => {
|
|
54
|
+
const { unsubscribe } = _source().subscribe((state) => {
|
|
55
|
+
slice.set(selector(state))
|
|
56
|
+
})
|
|
57
|
+
onCleanup(unsubscribe)
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
return slice.asReadonly()
|
|
61
|
+
})
|
|
62
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { injectSelector } from './injectSelector'
|
|
2
|
+
import type { CreateSignalOptions, Injector, Signal } from '@angular/core'
|
|
3
|
+
import type { SelectionSource } from './injectSelector'
|
|
4
|
+
|
|
5
|
+
type CompatibilityInjectStoreOptions<TSelected> =
|
|
6
|
+
CreateSignalOptions<TSelected> & {
|
|
7
|
+
injector?: Injector
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function injectStore<TState, TSelected = NoInfer<TState>>(
|
|
11
|
+
store: SelectionSource<TState>,
|
|
12
|
+
selector?: (state: NoInfer<TState>) => TSelected,
|
|
13
|
+
options?: CompatibilityInjectStoreOptions<TSelected>,
|
|
14
|
+
): Signal<TSelected>
|
|
15
|
+
export function injectStore<TState, TSelected = NoInfer<TState>>(
|
|
16
|
+
store: SelectionSource<TState> | (() => SelectionSource<TState>),
|
|
17
|
+
selector?: (state: NoInfer<TState>) => TSelected,
|
|
18
|
+
options?: CompatibilityInjectStoreOptions<TSelected>,
|
|
19
|
+
): Signal<TSelected>
|
|
20
|
+
export function injectStore<TState, TSelected = NoInfer<TState>>(
|
|
21
|
+
store: SelectionSource<TState> | (() => SelectionSource<TState>),
|
|
22
|
+
selector: (state: NoInfer<TState>) => TSelected = (d) =>
|
|
23
|
+
d as unknown as TSelected,
|
|
24
|
+
options?: CompatibilityInjectStoreOptions<TSelected>,
|
|
25
|
+
): Signal<TSelected> {
|
|
26
|
+
const { equal, injector, ...signalOptions } = options ?? {}
|
|
27
|
+
|
|
28
|
+
return injectSelector(store, selector, {
|
|
29
|
+
...signalOptions,
|
|
30
|
+
compare: equal,
|
|
31
|
+
injector,
|
|
32
|
+
})
|
|
33
|
+
}
|