@typed/router 0.17.7 → 0.18.1
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/Http/Router.d.ts +2 -0
- package/dist/Http/Router.d.ts.map +1 -0
- package/dist/Http/Router.js +2 -0
- package/dist/Http/Router.js.map +1 -0
- package/dist/Link.d.ts +12 -11
- package/dist/Link.d.ts.map +1 -1
- package/dist/Link.js +9 -7
- package/dist/Link.js.map +1 -1
- package/dist/Match.d.ts +3 -3
- package/dist/Match.d.ts.map +1 -1
- package/dist/Matcher.d.ts +30 -4
- package/dist/Matcher.d.ts.map +1 -1
- package/dist/Matcher.js +27 -4
- package/dist/Matcher.js.map +1 -1
- package/dist/cjs/Link.d.ts +12 -11
- package/dist/cjs/Link.d.ts.map +1 -1
- package/dist/cjs/Link.js +8 -6
- package/dist/cjs/Link.js.map +1 -1
- package/dist/cjs/Match.d.ts +3 -3
- package/dist/cjs/Match.d.ts.map +1 -1
- package/dist/cjs/Matcher.d.ts +30 -4
- package/dist/cjs/Matcher.d.ts.map +1 -1
- package/dist/cjs/Matcher.js +28 -5
- package/dist/cjs/Matcher.js.map +1 -1
- package/dist/cjs/Redirect.d.ts +1 -1
- package/dist/cjs/matchRoutes.js +1 -1
- package/dist/cjs/matchRoutes.js.map +1 -1
- package/dist/cjs/router.d.ts +14 -4
- package/dist/cjs/router.d.ts.map +1 -1
- package/dist/cjs/router.js +18 -2
- package/dist/cjs/router.js.map +1 -1
- package/dist/matchRoutes.js +1 -1
- package/dist/matchRoutes.js.map +1 -1
- package/dist/router.d.ts +14 -4
- package/dist/router.d.ts.map +1 -1
- package/dist/router.js +14 -1
- package/dist/router.js.map +1 -1
- package/dist/tsconfig.cjs.build.tsbuildinfo +1 -1
- package/package.json +12 -12
- package/src/Link.ts +36 -31
- package/src/Match.ts +3 -3
- package/src/Matcher.ts +95 -11
- package/src/matchRoutes.ts +1 -1
- package/src/router.ts +59 -6
- package/tsconfig.build.tsbuildinfo +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@typed/router",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.18.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.js",
|
|
@@ -19,18 +19,18 @@
|
|
|
19
19
|
}
|
|
20
20
|
},
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"@effect/data": "^0.17.
|
|
23
|
-
"@effect/io": "^0.38.
|
|
24
|
-
"@typed/context": "0.
|
|
25
|
-
"@typed/dom": "8.
|
|
26
|
-
"@typed/error": "0.
|
|
27
|
-
"@typed/fx": "1.
|
|
28
|
-
"@typed/html": "3.
|
|
29
|
-
"@typed/navigation": "0.
|
|
30
|
-
"@typed/path": "0.6.
|
|
31
|
-
"@typed/route": "0.
|
|
22
|
+
"@effect/data": "^0.17.6",
|
|
23
|
+
"@effect/io": "^0.38.2",
|
|
24
|
+
"@typed/context": "0.18.1",
|
|
25
|
+
"@typed/dom": "8.18.1",
|
|
26
|
+
"@typed/error": "0.17.1",
|
|
27
|
+
"@typed/fx": "1.17.1",
|
|
28
|
+
"@typed/html": "3.18.1",
|
|
29
|
+
"@typed/navigation": "0.5.1",
|
|
30
|
+
"@typed/path": "0.6.3",
|
|
31
|
+
"@typed/route": "0.14.1"
|
|
32
32
|
},
|
|
33
|
-
"gitHead": "
|
|
33
|
+
"gitHead": "8cba441523534aa1bd327bd84eb0e71334bb914e",
|
|
34
34
|
"publishConfig": {
|
|
35
35
|
"access": "public"
|
|
36
36
|
},
|
package/src/Link.ts
CHANGED
|
@@ -1,39 +1,42 @@
|
|
|
1
|
+
import { not } from '@effect/data/Predicate'
|
|
1
2
|
import * as Effect from '@effect/io/Effect'
|
|
2
3
|
import * as Scope from '@effect/io/Scope'
|
|
4
|
+
import { getIsUsingKeyModifier } from '@typed/dom'
|
|
3
5
|
import * as Fx from '@typed/fx'
|
|
4
|
-
import { Placeholder } from '@typed/html'
|
|
6
|
+
import { EventHandler, Placeholder } from '@typed/html'
|
|
5
7
|
import * as Navigation from '@typed/navigation'
|
|
6
8
|
import { pathJoin } from '@typed/path'
|
|
7
9
|
|
|
8
10
|
import { Router, getCurrentPathFromUrl } from './router.js'
|
|
9
11
|
|
|
10
12
|
export interface UseLinkParams<
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
E3 = never,
|
|
17
|
-
R4 = never,
|
|
18
|
-
E4 = never,
|
|
19
|
-
R5 = never,
|
|
20
|
-
E5 = never,
|
|
13
|
+
To extends Placeholder.AnyOf<string>,
|
|
14
|
+
Replace extends Placeholder.AnyOf<boolean>,
|
|
15
|
+
State,
|
|
16
|
+
Relative extends Placeholder.AnyOf<boolean>,
|
|
17
|
+
Key extends Placeholder.AnyOf<string>,
|
|
21
18
|
> {
|
|
22
|
-
readonly to:
|
|
23
|
-
readonly replace?:
|
|
24
|
-
readonly state?:
|
|
25
|
-
readonly relative?:
|
|
26
|
-
readonly key?:
|
|
19
|
+
readonly to: To
|
|
20
|
+
readonly replace?: Replace
|
|
21
|
+
readonly state?: State
|
|
22
|
+
readonly relative?: Relative
|
|
23
|
+
readonly key?: Key
|
|
27
24
|
}
|
|
28
25
|
|
|
29
26
|
export namespace UseLinkParams {
|
|
30
|
-
export type Any = UseLinkParams<
|
|
27
|
+
export type Any = UseLinkParams<
|
|
28
|
+
Placeholder.AnyOf<string>,
|
|
29
|
+
Placeholder.AnyOf<boolean>,
|
|
30
|
+
unknown,
|
|
31
|
+
Placeholder.AnyOf<boolean>,
|
|
32
|
+
Placeholder.AnyOf<string>
|
|
33
|
+
>
|
|
31
34
|
|
|
32
|
-
export type Context<T extends Any> = Placeholder.
|
|
35
|
+
export type Context<T extends Any> = Placeholder.Context<
|
|
33
36
|
T['to'] | T['replace'] | T['state'] | T['relative'] | T['key']
|
|
34
37
|
>
|
|
35
38
|
|
|
36
|
-
export type Error<T extends Any> = Placeholder.
|
|
39
|
+
export type Error<T extends Any> = Placeholder.Error<
|
|
37
40
|
T['to'] | T['replace'] | T['state'] | T['relative'] | T['key']
|
|
38
41
|
>
|
|
39
42
|
}
|
|
@@ -42,7 +45,8 @@ export interface UseLink<E> {
|
|
|
42
45
|
readonly url: Fx.Computed<Router, E, string>
|
|
43
46
|
readonly options: Fx.Computed<never, E, Navigation.NavigateOptions>
|
|
44
47
|
readonly navigate: Effect.Effect<Router, E, Navigation.Destination>
|
|
45
|
-
readonly active: Fx.
|
|
48
|
+
readonly active: Fx.Fx<Router, E, boolean>
|
|
49
|
+
readonly onClick: EventHandler<KeyboardEvent | MouseEvent, Router, E>
|
|
46
50
|
}
|
|
47
51
|
|
|
48
52
|
export namespace UseLink {
|
|
@@ -63,15 +67,16 @@ export function useLink<Params extends UseLinkParams.Any>(
|
|
|
63
67
|
Placeholder.asRef(params.state ?? null),
|
|
64
68
|
Placeholder.asRef(params.key),
|
|
65
69
|
Placeholder.asRef(params.relative ?? true),
|
|
70
|
+
Router,
|
|
66
71
|
] as const),
|
|
67
|
-
([to, replace, state, key, relative]) => {
|
|
72
|
+
([to, replace, state, key, relative, router]) => {
|
|
68
73
|
const url = Fx.RefSubject.tuple(to, relative).mapEffect(([to, relative]) =>
|
|
69
74
|
Effect.gen(function* ($) {
|
|
70
75
|
let url = to
|
|
71
76
|
|
|
72
77
|
// Check if we should make the URL relative to the current route
|
|
73
78
|
if (relative) {
|
|
74
|
-
const { route, params } =
|
|
79
|
+
const { route, params } = router
|
|
75
80
|
const matched = yield* $(params)
|
|
76
81
|
const basePath = route.make(matched)
|
|
77
82
|
|
|
@@ -89,15 +94,8 @@ export function useLink<Params extends UseLinkParams.Any>(
|
|
|
89
94
|
}),
|
|
90
95
|
)
|
|
91
96
|
|
|
92
|
-
const active
|
|
93
|
-
(url)
|
|
94
|
-
Effect.gen(function* ($) {
|
|
95
|
-
const {
|
|
96
|
-
navigation: { currentEntry },
|
|
97
|
-
} = yield* $(Router)
|
|
98
|
-
|
|
99
|
-
return isActive(url, (yield* $(currentEntry)).url)
|
|
100
|
-
}),
|
|
97
|
+
const active = Fx.map(Fx.combine(url, router.navigation.currentEntry), ([url, destination]) =>
|
|
98
|
+
isActive(url, destination.url),
|
|
101
99
|
)
|
|
102
100
|
|
|
103
101
|
const navigate: Effect.Effect<
|
|
@@ -108,11 +106,18 @@ export function useLink<Params extends UseLinkParams.Any>(
|
|
|
108
106
|
Router.withEffect((r) => r.navigation.navigate(url, options)),
|
|
109
107
|
)
|
|
110
108
|
|
|
109
|
+
const onClick = EventHandler.preventDefault.if<
|
|
110
|
+
KeyboardEvent | MouseEvent,
|
|
111
|
+
Router,
|
|
112
|
+
UseLinkParams.Error<Params>
|
|
113
|
+
>(not(getIsUsingKeyModifier), () => navigate)
|
|
114
|
+
|
|
111
115
|
return {
|
|
112
116
|
url,
|
|
113
117
|
options,
|
|
114
118
|
navigate,
|
|
115
119
|
active,
|
|
120
|
+
onClick,
|
|
116
121
|
} satisfies UseLink.FromParams<Params>
|
|
117
122
|
},
|
|
118
123
|
)
|
package/src/Match.ts
CHANGED
|
@@ -54,16 +54,16 @@ export namespace Match {
|
|
|
54
54
|
: never
|
|
55
55
|
|
|
56
56
|
export type Context<M extends Any> =
|
|
57
|
-
| Fx.Fx.
|
|
57
|
+
| Fx.Fx.Context<Rendered<M>>
|
|
58
58
|
| ([Guard<M>] extends [never] ? never : Effect.Effect.Context<Guard<M>>)
|
|
59
59
|
| ([Matched<M>] extends [never] ? never : Effect.Effect.Context<Matched<M>>)
|
|
60
60
|
|
|
61
61
|
export type Error<M extends Any> =
|
|
62
|
-
| Fx.Fx.
|
|
62
|
+
| Fx.Fx.Error<Rendered<M>>
|
|
63
63
|
| ([Guard<M>] extends [never] ? never : Exclude<Effect.Effect.Error<Guard<M>>, NavigationError>)
|
|
64
64
|
| ([Matched<M>] extends [never] ? never : Effect.Effect.Error<Matched<M>>)
|
|
65
65
|
|
|
66
|
-
export type Success<M extends Any> = Fx.Fx.
|
|
66
|
+
export type Success<M extends Any> = Fx.Fx.Success<Rendered<M>>
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
export function Match<P extends string, Rendered extends Fx.Fx<any, any, any>>(
|
package/src/Matcher.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import * as Chunk from '@effect/data/Chunk'
|
|
2
2
|
import * as Debug from '@effect/data/Function'
|
|
3
|
+
import * as Pipeable from '@effect/data/Pipeable'
|
|
4
|
+
import * as Cause from '@effect/io/Cause'
|
|
3
5
|
import * as Effect from '@effect/io/Effect'
|
|
4
6
|
import * as Fx from '@typed/fx'
|
|
5
7
|
import { RenderContext } from '@typed/html'
|
|
@@ -11,16 +13,31 @@ import { Redirect } from './Redirect.js'
|
|
|
11
13
|
import { matchRoutes } from './matchRoutes.js'
|
|
12
14
|
import { Router } from './router.js'
|
|
13
15
|
|
|
14
|
-
export interface Matcher<Matches extends ReadonlyArray<Match.Any>> {
|
|
16
|
+
export interface Matcher<Matches extends ReadonlyArray<Match.Any>> extends Pipeable.Pipeable {
|
|
15
17
|
/** @internal */
|
|
16
18
|
readonly matches: Chunk.Chunk<Match.Any>
|
|
17
19
|
|
|
20
|
+
/**
|
|
21
|
+
* Matches a route to an Fx
|
|
22
|
+
*/
|
|
18
23
|
readonly match: <P extends string, R, E, A, Opts extends MatchOptions.Any<P> = MatchOptions<P>>(
|
|
19
24
|
route: Route<P>,
|
|
20
25
|
render: (params: Fx.Filtered<never, never, ParamsOf<P>>) => Fx.Fx<R, E, A>,
|
|
21
26
|
options?: Opts,
|
|
22
27
|
) => Matcher<readonly [...Matches, Match<P, Fx.Fx<R, E, A>, Opts>]>
|
|
23
28
|
|
|
29
|
+
/**
|
|
30
|
+
* Matches a route to a value
|
|
31
|
+
*/
|
|
32
|
+
readonly matchTo: <P extends string, A, Opts extends MatchOptions.Any<P> = MatchOptions<P>>(
|
|
33
|
+
route: Route<P>,
|
|
34
|
+
to: (params: ParamsOf<P>) => A,
|
|
35
|
+
options?: Opts,
|
|
36
|
+
) => Matcher<readonly [...Matches, Match<P, Fx.Fx<never, never, A>, Opts>]>
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Matches a route to a lazy Fx
|
|
40
|
+
*/
|
|
24
41
|
readonly matchLazy: <
|
|
25
42
|
P extends string,
|
|
26
43
|
R,
|
|
@@ -33,6 +50,10 @@ export interface Matcher<Matches extends ReadonlyArray<Match.Any>> {
|
|
|
33
50
|
options?: Opts,
|
|
34
51
|
) => Matcher<readonly [...Matches, Match<P, Fx.Fx<R, E, A>, Opts>]>
|
|
35
52
|
|
|
53
|
+
/**
|
|
54
|
+
* The default way to turn a Matcher into an Fx is by providing an Fx to run when
|
|
55
|
+
* no routes match.
|
|
56
|
+
*/
|
|
36
57
|
readonly notFound: <R, E, A>(
|
|
37
58
|
render: (params: Fx.Filtered<never, never, Readonly<Record<string, string>>>) => Fx.Fx<R, E, A>,
|
|
38
59
|
) => Fx.Fx<
|
|
@@ -41,6 +62,32 @@ export interface Matcher<Matches extends ReadonlyArray<Match.Any>> {
|
|
|
41
62
|
A | Match.Success<Matches[number]>
|
|
42
63
|
>
|
|
43
64
|
|
|
65
|
+
/**
|
|
66
|
+
* Redirect to a route when no routes match. This variant will only utilize
|
|
67
|
+
* the route provided to it.
|
|
68
|
+
*/
|
|
69
|
+
readonly redirect: <P extends string>(
|
|
70
|
+
route: Route<P>,
|
|
71
|
+
...params: [keyof ParamsOf<P>] extends [never] ? [] : [ParamsOf<P>]
|
|
72
|
+
) => Fx.Fx<
|
|
73
|
+
Router | RenderContext | Match.Context<Matches[number]>,
|
|
74
|
+
Exclude<Match.Error<Matches[number]>, Redirect>,
|
|
75
|
+
Match.Success<Matches[number]>
|
|
76
|
+
>
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Redirect to a route when no routes match. This variant will utilize the
|
|
80
|
+
* parent Router to redirect a route relative to it.
|
|
81
|
+
*/
|
|
82
|
+
readonly relativeRedirect: <P extends string>(
|
|
83
|
+
route: Route<P>,
|
|
84
|
+
...params: [keyof ParamsOf<P>] extends [never] ? [] : [ParamsOf<P>]
|
|
85
|
+
) => Fx.Fx<
|
|
86
|
+
Router | RenderContext | Match.Context<Matches[number]>,
|
|
87
|
+
Cause.NoSuchElementException | Exclude<Match.Error<Matches[number]>, Redirect>,
|
|
88
|
+
Match.Success<Matches[number]>
|
|
89
|
+
>
|
|
90
|
+
|
|
44
91
|
readonly concat: <OtherMatches extends ReadonlyArray<Match.Any>>(
|
|
45
92
|
other: Matcher<OtherMatches>,
|
|
46
93
|
) => Matcher<readonly [...Matches, ...OtherMatches]>
|
|
@@ -49,17 +96,24 @@ export interface Matcher<Matches extends ReadonlyArray<Match.Any>> {
|
|
|
49
96
|
export function Matcher<const Matches extends ReadonlyArray<Match.Any>>(
|
|
50
97
|
matches: Chunk.Chunk<Match.Any>,
|
|
51
98
|
): Matcher<Matches> {
|
|
52
|
-
|
|
53
|
-
|
|
99
|
+
function match<P extends string, R, E, A, Opts extends MatchOptions.Any<P> = never>(
|
|
100
|
+
route: Route<P>,
|
|
101
|
+
render: (params: Fx.Filtered<never, never, ParamsOf<P>>) => Fx.Fx<R, E, A>,
|
|
102
|
+
options?: Opts,
|
|
103
|
+
): Matcher<readonly [...Matches, Match<P, Fx.Fx<R, E, A>, Opts>]> {
|
|
104
|
+
return Matcher(Chunk.append(matches, Match(route, render, options || {})))
|
|
105
|
+
}
|
|
54
106
|
|
|
55
|
-
|
|
107
|
+
const self: Matcher<Matches> = {
|
|
108
|
+
matches,
|
|
109
|
+
match,
|
|
110
|
+
matchTo<P extends string, A, Opts extends MatchOptions.Any<P> = never>(
|
|
56
111
|
route: Route<P>,
|
|
57
|
-
|
|
112
|
+
to: (params: ParamsOf<P>) => A,
|
|
58
113
|
options?: Opts,
|
|
59
|
-
): Matcher<readonly [...Matches, Match<P, Fx.Fx<
|
|
60
|
-
return
|
|
114
|
+
): Matcher<readonly [...Matches, Match<P, Fx.Fx<never, never, A>, Opts>]> {
|
|
115
|
+
return match(route, (params) => params.map(to), options)
|
|
61
116
|
},
|
|
62
|
-
|
|
63
117
|
matchLazy<P extends string, R, E, A, Opts extends MatchOptions.Any<P> = never>(
|
|
64
118
|
route: Route<P>,
|
|
65
119
|
render: () => Promise<(params: Fx.Filtered<never, never, ParamsOf<P>>) => Fx.Fx<R, E, A>>,
|
|
@@ -74,15 +128,45 @@ export function Matcher<const Matches extends ReadonlyArray<Match.Any>>(
|
|
|
74
128
|
) => Fx.Fx<R, E, A>,
|
|
75
129
|
) => Fx.scoped(matchRoutes(Chunk.toReadonlyArray(matches) as Matches, render)),
|
|
76
130
|
|
|
131
|
+
redirect: (route, params?) =>
|
|
132
|
+
redirectEffect(
|
|
133
|
+
self,
|
|
134
|
+
Redirect.redirect(route.make(params || ({} as any)), {
|
|
135
|
+
history: 'replace',
|
|
136
|
+
}),
|
|
137
|
+
),
|
|
138
|
+
|
|
139
|
+
relativeRedirect: (route, params?) =>
|
|
140
|
+
redirectEffect(
|
|
141
|
+
self,
|
|
142
|
+
Router.withEffect((router: Router) =>
|
|
143
|
+
Effect.gen(function* ($) {
|
|
144
|
+
const parentParams: ParamsOf<string> = yield* $(router.params)
|
|
145
|
+
const allParams = { ...parentParams, ...params }
|
|
146
|
+
const nested = router.route.concat(route)
|
|
147
|
+
const url = nested.make(allParams as any)
|
|
148
|
+
|
|
149
|
+
return yield* $(Redirect.redirect(url, { history: 'replace' }))
|
|
150
|
+
}),
|
|
151
|
+
),
|
|
152
|
+
),
|
|
153
|
+
|
|
77
154
|
concat: <OtherMatches extends ReadonlyArray<Match.Any>>(
|
|
78
155
|
other: Matcher<OtherMatches>,
|
|
79
156
|
): Matcher<readonly [...Matches, ...OtherMatches]> => {
|
|
80
157
|
return Matcher(Chunk.appendAll(matches, other.matches))
|
|
81
158
|
},
|
|
159
|
+
|
|
160
|
+
pipe() {
|
|
161
|
+
// eslint-disable-next-line prefer-rest-params
|
|
162
|
+
return Pipeable.pipeArguments(this, arguments)
|
|
163
|
+
},
|
|
82
164
|
}
|
|
165
|
+
|
|
166
|
+
return self
|
|
83
167
|
}
|
|
84
168
|
|
|
85
|
-
export const { match, matchLazy } = Matcher<readonly []>(Chunk.empty())
|
|
169
|
+
export const { match, matchTo, matchLazy } = Matcher<readonly []>(Chunk.empty())
|
|
86
170
|
|
|
87
171
|
export const notFound: {
|
|
88
172
|
<R, E, A>(
|
|
@@ -92,7 +176,7 @@ export const notFound: {
|
|
|
92
176
|
) => Fx.Fx<
|
|
93
177
|
Router | R | Match.Context<Matches[number]>,
|
|
94
178
|
Exclude<E | Match.Error<Matches[number]>, Redirect>,
|
|
95
|
-
A | Fx.Fx.
|
|
179
|
+
A | Fx.Fx.Success<Match.Rendered<Matches[number]>>
|
|
96
180
|
>
|
|
97
181
|
|
|
98
182
|
<Matches extends readonly Match.Any[], R, E, A>(
|
|
@@ -101,7 +185,7 @@ export const notFound: {
|
|
|
101
185
|
): Fx.Fx<
|
|
102
186
|
Router | R | Match.Context<Matches[number]>,
|
|
103
187
|
Exclude<E | Match.Error<Matches[number]>, Redirect>,
|
|
104
|
-
A | Fx.Fx.
|
|
188
|
+
A | Fx.Fx.Success<Match.Rendered<Matches[number]>>
|
|
105
189
|
>
|
|
106
190
|
} = Debug.dual(
|
|
107
191
|
2,
|
package/src/matchRoutes.ts
CHANGED
|
@@ -48,7 +48,7 @@ export function matchRoutes<
|
|
|
48
48
|
|
|
49
49
|
return [
|
|
50
50
|
// If the route is the a single slash, use the route directly to keep any parsing options.
|
|
51
|
-
|
|
51
|
+
nestedRouter.route,
|
|
52
52
|
render,
|
|
53
53
|
match.options?.guard as
|
|
54
54
|
| ((params: ParamsOf<string>) => Effect.Effect<_R, NavigationError, boolean>)
|
package/src/router.ts
CHANGED
|
@@ -3,10 +3,10 @@ import * as Effect from '@effect/io/Effect'
|
|
|
3
3
|
import * as Layer from '@effect/io/Layer'
|
|
4
4
|
import { Tag } from '@typed/context'
|
|
5
5
|
import { DomServicesElementParams } from '@typed/dom'
|
|
6
|
-
import
|
|
6
|
+
import * as Fx from '@typed/fx'
|
|
7
7
|
import * as Navigation from '@typed/navigation'
|
|
8
8
|
import { ParamsOf, PathJoin } from '@typed/path'
|
|
9
|
-
import { Route } from '@typed/route'
|
|
9
|
+
import { Route, RouteOptions } from '@typed/route'
|
|
10
10
|
|
|
11
11
|
export interface Router<in out P extends string = string> {
|
|
12
12
|
/**
|
|
@@ -17,13 +17,16 @@ export interface Router<in out P extends string = string> {
|
|
|
17
17
|
/**
|
|
18
18
|
* The current params for the current path.
|
|
19
19
|
*/
|
|
20
|
-
readonly params: Filtered<never, never, ParamsOf<P>>
|
|
20
|
+
readonly params: Fx.Filtered<never, never, ParamsOf<P>>
|
|
21
21
|
|
|
22
22
|
/**
|
|
23
23
|
* Construct a new Router instance by defining a new Route which is concatenated
|
|
24
24
|
* to the current Route.
|
|
25
25
|
*/
|
|
26
|
-
readonly define: <P2 extends string>(
|
|
26
|
+
readonly define: <P2 extends string>(
|
|
27
|
+
route: Route<P2>,
|
|
28
|
+
overrides?: RouteOptions,
|
|
29
|
+
) => Router<PathJoin<[P, P2]>>
|
|
27
30
|
|
|
28
31
|
/**
|
|
29
32
|
* The parent Router instance if one exists.
|
|
@@ -53,8 +56,11 @@ export const navigation: Layer.Layer<Navigation.Navigation, never, Router> = Rou
|
|
|
53
56
|
route,
|
|
54
57
|
navigation,
|
|
55
58
|
params: currentPath.filterMap(route.match),
|
|
56
|
-
define: <P2 extends string>(
|
|
57
|
-
|
|
59
|
+
define: <P2 extends string>(
|
|
60
|
+
other: Route<P2>,
|
|
61
|
+
overrides?: RouteOptions,
|
|
62
|
+
): Router<PathJoin<[P, P2]>> =>
|
|
63
|
+
makeRouter(route.concat(other, overrides), Option.some(router)),
|
|
58
64
|
parent,
|
|
59
65
|
}
|
|
60
66
|
|
|
@@ -78,3 +84,50 @@ export const memory = (
|
|
|
78
84
|
options: Navigation.MemoryNavigationOptions,
|
|
79
85
|
): Layer.Layer<never, never, Navigation.Navigation | Router> =>
|
|
80
86
|
Layer.provideMerge(Navigation.memory(options), navigation)
|
|
87
|
+
|
|
88
|
+
const routerNavigation: Layer.Layer<Router, never, Navigation.Navigation> = Layer.effectContext(
|
|
89
|
+
Effect.map(Router, (router) => Navigation.Navigation.build(router.navigation).context),
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
const provideRouterNavigationEffect = Effect.provideSomeLayer(routerNavigation)
|
|
93
|
+
const provideRouterNavigationFx = Fx.provideSomeLayer(routerNavigation)
|
|
94
|
+
|
|
95
|
+
export const back: Effect.Effect<Router, never, Navigation.Destination> =
|
|
96
|
+
provideRouterNavigationEffect(Navigation.back)
|
|
97
|
+
|
|
98
|
+
export const canGoBack: Effect.Effect<Router, never, boolean> & Fx.Fx<Router, never, boolean> =
|
|
99
|
+
Object.assign(
|
|
100
|
+
provideRouterNavigationEffect(Navigation.canGoBack),
|
|
101
|
+
provideRouterNavigationFx(Navigation.canGoBack),
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
export const canGoForward: Effect.Effect<Router, never, boolean> & Fx.Fx<Router, never, boolean> =
|
|
105
|
+
Object.assign(
|
|
106
|
+
provideRouterNavigationEffect(Navigation.canGoForward),
|
|
107
|
+
provideRouterNavigationFx(Navigation.canGoForward),
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
export const cancelNavigation = Navigation.cancelNavigation
|
|
111
|
+
|
|
112
|
+
export const forward = provideRouterNavigationEffect(Navigation.forward)
|
|
113
|
+
|
|
114
|
+
export const navigate: (
|
|
115
|
+
url: string,
|
|
116
|
+
options?: Navigation.NavigateOptions,
|
|
117
|
+
) => Effect.Effect<Router, never, Navigation.Destination> = (url, options) =>
|
|
118
|
+
provideRouterNavigationEffect(Navigation.navigate(url, options))
|
|
119
|
+
|
|
120
|
+
export const push: (
|
|
121
|
+
url: string,
|
|
122
|
+
options?: Omit<Navigation.NavigateOptions, 'history'>,
|
|
123
|
+
) => Effect.Effect<Router, never, Navigation.Destination> = (url, options) =>
|
|
124
|
+
navigate(url, { ...options, history: 'push' })
|
|
125
|
+
|
|
126
|
+
export const replace: (
|
|
127
|
+
url: string,
|
|
128
|
+
options?: Omit<Navigation.NavigateOptions, 'history'>,
|
|
129
|
+
) => Effect.Effect<Router, never, Navigation.Destination> = (url, options) =>
|
|
130
|
+
navigate(url, { ...options, history: 'replace' })
|
|
131
|
+
|
|
132
|
+
export const reload: Effect.Effect<Router, never, Navigation.Destination> =
|
|
133
|
+
provideRouterNavigationEffect(Navigation.reload)
|