@typed/navigation 0.5.3 → 0.6.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/LICENSE +21 -0
- package/README.md +5 -0
- package/dist/cjs/Blocking.js +53 -0
- package/dist/cjs/Blocking.js.map +1 -0
- package/dist/cjs/Layer.js +27 -0
- package/dist/cjs/Layer.js.map +1 -0
- package/dist/cjs/Navigation.js +184 -62
- package/dist/cjs/Navigation.js.map +1 -1
- package/dist/cjs/index.js +36 -17
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/internal/fromWindow.js +376 -0
- package/dist/cjs/internal/fromWindow.js.map +1 -0
- package/dist/cjs/internal/memory.js +67 -0
- package/dist/cjs/internal/memory.js.map +1 -0
- package/dist/cjs/internal/shared.js +403 -0
- package/dist/cjs/internal/shared.js.map +1 -0
- package/dist/dts/Blocking.d.ts +33 -0
- package/dist/dts/Blocking.d.ts.map +1 -0
- package/dist/dts/Layer.d.ts +44 -0
- package/dist/dts/Layer.d.ts.map +1 -0
- package/dist/dts/Navigation.d.ts +377 -0
- package/dist/dts/Navigation.d.ts.map +1 -0
- package/dist/dts/index.d.ts +17 -0
- package/dist/dts/index.d.ts.map +1 -0
- package/dist/dts/internal/fromWindow.d.ts +12 -0
- package/dist/dts/internal/fromWindow.d.ts.map +1 -0
- package/dist/dts/internal/memory.d.ts +6 -0
- package/dist/dts/internal/memory.d.ts.map +1 -0
- package/dist/dts/internal/shared.d.ts +139 -0
- package/dist/dts/internal/shared.d.ts.map +1 -0
- package/dist/esm/Blocking.js +39 -0
- package/dist/esm/Blocking.js.map +1 -0
- package/dist/esm/Layer.js +18 -0
- package/dist/esm/Layer.js.map +1 -0
- package/dist/esm/Navigation.js +166 -0
- package/dist/esm/Navigation.js.map +1 -0
- package/dist/esm/index.js +17 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/internal/fromWindow.js +273 -0
- package/dist/esm/internal/fromWindow.js.map +1 -0
- package/dist/esm/internal/memory.js +54 -0
- package/dist/esm/internal/memory.js.map +1 -0
- package/dist/esm/internal/shared.js +336 -0
- package/dist/esm/internal/shared.js.map +1 -0
- package/dist/esm/package.json +4 -0
- package/package.json +52 -32
- package/src/Blocking.ts +102 -0
- package/src/Layer.ts +53 -0
- package/src/Navigation.ts +342 -159
- package/src/index.ts +17 -3
- package/src/internal/fromWindow.ts +421 -0
- package/src/internal/memory.ts +79 -0
- package/src/internal/shared.ts +514 -0
- package/dist/DOM.d.ts +0 -12
- package/dist/DOM.d.ts.map +0 -1
- package/dist/DOM.js +0 -87
- package/dist/DOM.js.map +0 -1
- package/dist/Memory.d.ts +0 -10
- package/dist/Memory.d.ts.map +0 -1
- package/dist/Memory.js +0 -55
- package/dist/Memory.js.map +0 -1
- package/dist/Navigation.d.ts +0 -116
- package/dist/Navigation.d.ts.map +0 -1
- package/dist/Navigation.js +0 -36
- package/dist/Navigation.js.map +0 -1
- package/dist/_makeServerWindow.d.ts +0 -16
- package/dist/_makeServerWindow.d.ts.map +0 -1
- package/dist/_makeServerWindow.js +0 -9
- package/dist/_makeServerWindow.js.map +0 -1
- package/dist/cjs/DOM.d.ts +0 -12
- package/dist/cjs/DOM.d.ts.map +0 -1
- package/dist/cjs/DOM.js +0 -116
- package/dist/cjs/DOM.js.map +0 -1
- package/dist/cjs/Memory.d.ts +0 -10
- package/dist/cjs/Memory.d.ts.map +0 -1
- package/dist/cjs/Memory.js +0 -82
- package/dist/cjs/Memory.js.map +0 -1
- package/dist/cjs/Navigation.d.ts +0 -116
- package/dist/cjs/Navigation.d.ts.map +0 -1
- package/dist/cjs/_makeServerWindow.d.ts +0 -16
- package/dist/cjs/_makeServerWindow.d.ts.map +0 -1
- package/dist/cjs/_makeServerWindow.js +0 -36
- package/dist/cjs/_makeServerWindow.js.map +0 -1
- package/dist/cjs/constant.d.ts +0 -2
- package/dist/cjs/constant.d.ts.map +0 -1
- package/dist/cjs/constant.js +0 -30
- package/dist/cjs/constant.js.map +0 -1
- package/dist/cjs/dom-intent.d.ts +0 -28
- package/dist/cjs/dom-intent.d.ts.map +0 -1
- package/dist/cjs/dom-intent.js +0 -172
- package/dist/cjs/dom-intent.js.map +0 -1
- package/dist/cjs/history.d.ts +0 -31
- package/dist/cjs/history.d.ts.map +0 -1
- package/dist/cjs/history.js +0 -131
- package/dist/cjs/history.js.map +0 -1
- package/dist/cjs/index.d.ts +0 -4
- package/dist/cjs/index.d.ts.map +0 -1
- package/dist/cjs/json.d.ts +0 -13
- package/dist/cjs/json.d.ts.map +0 -1
- package/dist/cjs/json.js +0 -24
- package/dist/cjs/json.js.map +0 -1
- package/dist/cjs/memory-intent.d.ts +0 -27
- package/dist/cjs/memory-intent.d.ts.map +0 -1
- package/dist/cjs/memory-intent.js +0 -156
- package/dist/cjs/memory-intent.js.map +0 -1
- package/dist/cjs/model.d.ts +0 -22
- package/dist/cjs/model.d.ts.map +0 -1
- package/dist/cjs/model.js +0 -48
- package/dist/cjs/model.js.map +0 -1
- package/dist/cjs/shared-intent.d.ts +0 -14
- package/dist/cjs/shared-intent.d.ts.map +0 -1
- package/dist/cjs/shared-intent.js +0 -82
- package/dist/cjs/shared-intent.js.map +0 -1
- package/dist/cjs/storage.d.ts +0 -19
- package/dist/cjs/storage.d.ts.map +0 -1
- package/dist/cjs/storage.js +0 -101
- package/dist/cjs/storage.js.map +0 -1
- package/dist/cjs/util.d.ts +0 -5
- package/dist/cjs/util.d.ts.map +0 -1
- package/dist/cjs/util.js +0 -39
- package/dist/cjs/util.js.map +0 -1
- package/dist/constant.d.ts +0 -2
- package/dist/constant.d.ts.map +0 -1
- package/dist/constant.js +0 -4
- package/dist/constant.js.map +0 -1
- package/dist/dom-intent.d.ts +0 -28
- package/dist/dom-intent.d.ts.map +0 -1
- package/dist/dom-intent.js +0 -140
- package/dist/dom-intent.js.map +0 -1
- package/dist/history.d.ts +0 -31
- package/dist/history.d.ts.map +0 -1
- package/dist/history.js +0 -104
- package/dist/history.js.map +0 -1
- package/dist/index.d.ts +0 -4
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -4
- package/dist/index.js.map +0 -1
- package/dist/intent.d.ts +0 -31
- package/dist/intent.d.ts.map +0 -1
- package/dist/intent.js +0 -157
- package/dist/intent.js.map +0 -1
- package/dist/json.d.ts +0 -13
- package/dist/json.d.ts.map +0 -1
- package/dist/json.js +0 -17
- package/dist/json.js.map +0 -1
- package/dist/memory-intent.d.ts +0 -27
- package/dist/memory-intent.d.ts.map +0 -1
- package/dist/memory-intent.js +0 -124
- package/dist/memory-intent.js.map +0 -1
- package/dist/model.d.ts +0 -22
- package/dist/model.d.ts.map +0 -1
- package/dist/model.js +0 -21
- package/dist/model.js.map +0 -1
- package/dist/shared-intent.d.ts +0 -14
- package/dist/shared-intent.d.ts.map +0 -1
- package/dist/shared-intent.js +0 -51
- package/dist/shared-intent.js.map +0 -1
- package/dist/storage.d.ts +0 -19
- package/dist/storage.d.ts.map +0 -1
- package/dist/storage.js +0 -73
- package/dist/storage.js.map +0 -1
- package/dist/tsconfig.cjs.build.tsbuildinfo +0 -1
- package/dist/util.d.ts +0 -5
- package/dist/util.d.ts.map +0 -1
- package/dist/util.js +0 -12
- package/dist/util.js.map +0 -1
- package/eslintrc.json +0 -3
- package/project.json +0 -46
- package/src/DOM.test.ts +0 -699
- package/src/DOM.ts +0 -163
- package/src/Memory.test.ts +0 -464
- package/src/Memory.ts +0 -102
- package/src/_makeServerWindow.ts +0 -28
- package/src/dom-intent.ts +0 -268
- package/src/history.ts +0 -165
- package/src/json.ts +0 -31
- package/src/memory-intent.ts +0 -224
- package/src/model.ts +0 -54
- package/src/shared-intent.ts +0 -117
- package/src/storage.ts +0 -101
- package/src/util.ts +0 -20
- package/tsconfig.build.json +0 -4
- package/tsconfig.build.tsbuildinfo +0 -1
- package/tsconfig.cjs.build.json +0 -22
- package/tsconfig.json +0 -27
- package/vite.config.mjs +0 -3
package/src/memory-intent.ts
DELETED
|
@@ -1,224 +0,0 @@
|
|
|
1
|
-
import { Option } from '@effect/data/Option'
|
|
2
|
-
import * as Effect from '@effect/io/Effect'
|
|
3
|
-
|
|
4
|
-
import type { MemoryNavigationOptions } from './Memory.js'
|
|
5
|
-
import {
|
|
6
|
-
Destination,
|
|
7
|
-
NavigateOptions,
|
|
8
|
-
NavigationError,
|
|
9
|
-
NavigationEvent,
|
|
10
|
-
NavigationType,
|
|
11
|
-
} from './Navigation.js'
|
|
12
|
-
import { Model } from './model.js'
|
|
13
|
-
import {
|
|
14
|
-
Notify,
|
|
15
|
-
Save,
|
|
16
|
-
makeGoTo,
|
|
17
|
-
makeNotify,
|
|
18
|
-
makeOnNavigation,
|
|
19
|
-
makeOnNavigationEnd,
|
|
20
|
-
} from './shared-intent.js'
|
|
21
|
-
import { createKey, getUrl } from './util.js'
|
|
22
|
-
|
|
23
|
-
// Roughly the number of History entries in a browser anyways
|
|
24
|
-
const DEFAULT_MAX_ENTRIES = 50
|
|
25
|
-
|
|
26
|
-
export type MemoryIntent = {
|
|
27
|
-
readonly back: ReturnType<ReturnType<typeof makeGo>>
|
|
28
|
-
|
|
29
|
-
readonly forward: ReturnType<ReturnType<typeof makeGo>>
|
|
30
|
-
|
|
31
|
-
readonly push: ReturnType<typeof makePush>
|
|
32
|
-
|
|
33
|
-
readonly replace: ReturnType<typeof makeReplace>
|
|
34
|
-
|
|
35
|
-
readonly navigate: (
|
|
36
|
-
url: string,
|
|
37
|
-
options?: NavigateOptions,
|
|
38
|
-
) => ReturnType<ReturnType<typeof makePush | typeof makeReplace>>
|
|
39
|
-
|
|
40
|
-
readonly notify: Notify
|
|
41
|
-
|
|
42
|
-
readonly go: ReturnType<typeof makeGo>
|
|
43
|
-
|
|
44
|
-
readonly goTo: (key: string) => Effect.Effect<never, NavigationError, Option<Destination>>
|
|
45
|
-
|
|
46
|
-
readonly reload: ReturnType<typeof makeReload>
|
|
47
|
-
|
|
48
|
-
readonly onNavigation: ReturnType<typeof makeOnNavigation>
|
|
49
|
-
|
|
50
|
-
readonly onNavigationEnd: ReturnType<typeof makeOnNavigationEnd>
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
export function makeIntent(model: Model, options: MemoryNavigationOptions): MemoryIntent {
|
|
54
|
-
const { origin } = options.initialUrl
|
|
55
|
-
const base = options.base ?? '/'
|
|
56
|
-
const maxEntries = Math.abs(options.maxEntries ?? DEFAULT_MAX_ENTRIES)
|
|
57
|
-
const notify = makeNotify(model)
|
|
58
|
-
const save = makeSave(model)
|
|
59
|
-
const go = makeGo(model, notify, save)
|
|
60
|
-
const replace = makeReplace(model, notify, save, base, origin)
|
|
61
|
-
const push = makePush(model, notify, save, base, origin, maxEntries)
|
|
62
|
-
|
|
63
|
-
return {
|
|
64
|
-
back: go(-1),
|
|
65
|
-
forward: go(1),
|
|
66
|
-
push,
|
|
67
|
-
replace,
|
|
68
|
-
navigate: (url: string, options: NavigateOptions = {}) =>
|
|
69
|
-
options.history === 'replace' ? replace(url, options) : push(url, options),
|
|
70
|
-
go: go,
|
|
71
|
-
goTo: makeGoTo(model, go),
|
|
72
|
-
reload: makeReload(model, notify, save),
|
|
73
|
-
onNavigation: makeOnNavigation(model),
|
|
74
|
-
onNavigationEnd: makeOnNavigationEnd(model),
|
|
75
|
-
notify,
|
|
76
|
-
} as const
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
export type Intent = ReturnType<typeof makeIntent>
|
|
80
|
-
|
|
81
|
-
export const makeSave: (
|
|
82
|
-
model: Model,
|
|
83
|
-
) => (event: NavigationEvent) => Effect.Effect<never, never, void> =
|
|
84
|
-
(model: Model) => (event: NavigationEvent) =>
|
|
85
|
-
Effect.gen(function* ($) {
|
|
86
|
-
const events = yield* $(model.events)
|
|
87
|
-
const index = yield* $(model.index)
|
|
88
|
-
|
|
89
|
-
// Update current entry
|
|
90
|
-
yield* $(model.currentEntry.set(event.destination))
|
|
91
|
-
|
|
92
|
-
// Update canGoBack
|
|
93
|
-
yield* $(model.canGoBack.set(index > 0))
|
|
94
|
-
|
|
95
|
-
// Update canGoForward
|
|
96
|
-
yield* $(model.canGoForward.set(index < events.length - 1))
|
|
97
|
-
})
|
|
98
|
-
|
|
99
|
-
export const makeReload = (model: Model, notify: Notify, save: Save<never>) =>
|
|
100
|
-
Effect.gen(function* ($) {
|
|
101
|
-
const i = yield* $(model.index.get)
|
|
102
|
-
const e = yield* $(model.events)
|
|
103
|
-
const event = e[i]
|
|
104
|
-
const reloadEvent = { ...event, navigationType: NavigationType.Reload }
|
|
105
|
-
|
|
106
|
-
yield* $(notify(reloadEvent))
|
|
107
|
-
yield* $(save(reloadEvent))
|
|
108
|
-
|
|
109
|
-
return event.destination
|
|
110
|
-
})
|
|
111
|
-
|
|
112
|
-
export const makeReplace =
|
|
113
|
-
(model: Model, notify: Notify, save: Save<never>, base: string, origin: string) =>
|
|
114
|
-
(url: string, options: NavigateOptions = {}) =>
|
|
115
|
-
Effect.gen(function* ($) {
|
|
116
|
-
const entry = yield* $(model.currentEntry.get)
|
|
117
|
-
const destination: Destination = {
|
|
118
|
-
key: entry.key,
|
|
119
|
-
url: getUrl(url, base, origin),
|
|
120
|
-
state: options.state,
|
|
121
|
-
}
|
|
122
|
-
const event: NavigationEvent = {
|
|
123
|
-
destination,
|
|
124
|
-
hashChange: entry.url.hash !== destination.url.hash,
|
|
125
|
-
navigationType: NavigationType.Replace,
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
yield* $(notify(event))
|
|
129
|
-
|
|
130
|
-
const currentIndex = yield* $(model.index)
|
|
131
|
-
|
|
132
|
-
yield* $(
|
|
133
|
-
model.events.update((entries) => {
|
|
134
|
-
const updated = entries.slice(0)
|
|
135
|
-
updated[currentIndex] = event
|
|
136
|
-
return updated
|
|
137
|
-
}),
|
|
138
|
-
)
|
|
139
|
-
|
|
140
|
-
yield* $(save(event))
|
|
141
|
-
|
|
142
|
-
return destination
|
|
143
|
-
})
|
|
144
|
-
|
|
145
|
-
export const makePush =
|
|
146
|
-
(
|
|
147
|
-
model: Model,
|
|
148
|
-
notify: Notify,
|
|
149
|
-
save: Save<never>,
|
|
150
|
-
base: string,
|
|
151
|
-
origin: string,
|
|
152
|
-
maxEntries: number,
|
|
153
|
-
) =>
|
|
154
|
-
(url: string, options: NavigateOptions = {}) =>
|
|
155
|
-
Effect.gen(function* ($) {
|
|
156
|
-
const entry = yield* $(model.currentEntry.get)
|
|
157
|
-
const destination: Destination = {
|
|
158
|
-
key: yield* $(createKey),
|
|
159
|
-
url: getUrl(url, base, origin),
|
|
160
|
-
state: options.state,
|
|
161
|
-
}
|
|
162
|
-
const event: NavigationEvent = {
|
|
163
|
-
destination,
|
|
164
|
-
hashChange: entry.url.hash !== destination.url.hash,
|
|
165
|
-
navigationType: NavigationType.Push,
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
// Notify event handlers
|
|
169
|
-
yield* $(notify(event))
|
|
170
|
-
|
|
171
|
-
const currentIndex = yield* $(model.index)
|
|
172
|
-
|
|
173
|
-
// Remove all entries after the current index
|
|
174
|
-
// and add the new destination to the end
|
|
175
|
-
yield* $(
|
|
176
|
-
model.events.update((entries) => {
|
|
177
|
-
const updated = entries.slice(0, currentIndex + 1)
|
|
178
|
-
updated.push(event)
|
|
179
|
-
return updated.slice(-maxEntries)
|
|
180
|
-
}),
|
|
181
|
-
)
|
|
182
|
-
|
|
183
|
-
// Update the index to the new destination
|
|
184
|
-
yield* $(model.index.update((i) => i + 1))
|
|
185
|
-
|
|
186
|
-
yield* $(save(event))
|
|
187
|
-
|
|
188
|
-
return destination
|
|
189
|
-
})
|
|
190
|
-
|
|
191
|
-
export const makeGo = (model: Model, notify: Notify, save: Save<never>) => (delta: number) =>
|
|
192
|
-
Effect.gen(function* ($) {
|
|
193
|
-
const currentEntries = yield* $(model.events)
|
|
194
|
-
const totalEntries = currentEntries.length
|
|
195
|
-
const currentIndex = yield* $(model.index)
|
|
196
|
-
|
|
197
|
-
// Nothing to do here
|
|
198
|
-
if (delta === 0) return currentEntries[currentIndex].destination
|
|
199
|
-
|
|
200
|
-
const nextIndex =
|
|
201
|
-
delta > 0
|
|
202
|
-
? Math.min(currentIndex + delta, totalEntries - 1)
|
|
203
|
-
: Math.max(currentIndex + delta, 0)
|
|
204
|
-
const nextEntry = currentEntries[nextIndex]
|
|
205
|
-
|
|
206
|
-
yield* $(
|
|
207
|
-
notify({
|
|
208
|
-
...nextEntry,
|
|
209
|
-
navigationType: getNavigationType(currentIndex, nextIndex),
|
|
210
|
-
}),
|
|
211
|
-
)
|
|
212
|
-
|
|
213
|
-
yield* $(model.index.set(nextIndex))
|
|
214
|
-
|
|
215
|
-
yield* $(save(nextEntry))
|
|
216
|
-
|
|
217
|
-
return nextEntry.destination
|
|
218
|
-
})
|
|
219
|
-
|
|
220
|
-
function getNavigationType(currentIndex: number, nextIndex: number): NavigationType {
|
|
221
|
-
if (nextIndex > currentIndex) return NavigationType.Forward
|
|
222
|
-
if (nextIndex < currentIndex) return NavigationType.Back
|
|
223
|
-
return NavigationType.Reload
|
|
224
|
-
}
|
package/src/model.ts
DELETED
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import * as Effect from '@effect/io/Effect'
|
|
2
|
-
import * as Scope from '@effect/io/Scope'
|
|
3
|
-
import * as Fx from '@typed/fx'
|
|
4
|
-
|
|
5
|
-
import { Destination, NavigationError, NavigationEvent, OnNavigationOptions } from './Navigation.js'
|
|
6
|
-
|
|
7
|
-
export interface Model {
|
|
8
|
-
readonly onNavigationHandlers: Set<
|
|
9
|
-
readonly [
|
|
10
|
-
(event: NavigationEvent) => Effect.Effect<never, NavigationError, unknown>,
|
|
11
|
-
OnNavigationOptions?,
|
|
12
|
-
]
|
|
13
|
-
>
|
|
14
|
-
readonly onNavigationEndHandlers: Set<
|
|
15
|
-
readonly [
|
|
16
|
-
(event: NavigationEvent) => Effect.Effect<never, never, unknown>,
|
|
17
|
-
OnNavigationOptions?,
|
|
18
|
-
]
|
|
19
|
-
>
|
|
20
|
-
readonly events: Fx.RefSubject<never, readonly NavigationEvent[]>
|
|
21
|
-
readonly index: Fx.RefSubject<never, number>
|
|
22
|
-
readonly entries: Fx.Computed<never, never, Destination[]>
|
|
23
|
-
readonly currentEntry: Fx.RefSubject<never, Destination>
|
|
24
|
-
readonly canGoBack: Fx.RefSubject<never, boolean>
|
|
25
|
-
readonly canGoForward: Fx.RefSubject<never, boolean>
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export const makeModel = (
|
|
29
|
-
initialEntries: readonly NavigationEvent[],
|
|
30
|
-
initialIndex: number,
|
|
31
|
-
): Effect.Effect<Scope.Scope, never, Model> =>
|
|
32
|
-
Effect.gen(function* ($) {
|
|
33
|
-
const events = yield* $(Fx.makeRef(Effect.succeed(initialEntries)))
|
|
34
|
-
const index = yield* $(Fx.makeRef(Effect.succeed(initialIndex)))
|
|
35
|
-
const entries = events.map((es) => es.map((e) => e.destination))
|
|
36
|
-
const currentEntry = yield* $(
|
|
37
|
-
Fx.makeRef(Effect.succeed(initialEntries[initialIndex].destination)),
|
|
38
|
-
)
|
|
39
|
-
const canGoBack = yield* $(Fx.makeRef(Effect.succeed(initialIndex > 0)))
|
|
40
|
-
const canGoForward = yield* $(
|
|
41
|
-
Fx.makeRef(Effect.succeed(initialIndex < initialEntries.length - 1)),
|
|
42
|
-
)
|
|
43
|
-
|
|
44
|
-
return {
|
|
45
|
-
onNavigationHandlers: new Set(),
|
|
46
|
-
onNavigationEndHandlers: new Set(),
|
|
47
|
-
events,
|
|
48
|
-
index,
|
|
49
|
-
entries,
|
|
50
|
-
currentEntry,
|
|
51
|
-
canGoBack,
|
|
52
|
-
canGoForward,
|
|
53
|
-
}
|
|
54
|
-
})
|
package/src/shared-intent.ts
DELETED
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
import * as Option from '@effect/data/Option'
|
|
2
|
-
import * as Effect from '@effect/io/Effect'
|
|
3
|
-
import * as Scope from '@effect/io/Scope'
|
|
4
|
-
|
|
5
|
-
import { Destination, NavigationError, NavigationEvent, OnNavigationOptions } from './Navigation.js'
|
|
6
|
-
import { Model } from './model.js'
|
|
7
|
-
|
|
8
|
-
export type Notify = (event: NavigationEvent) => Effect.Effect<never, NavigationError, void>
|
|
9
|
-
export type NotifyEnd = (event: NavigationEvent) => Effect.Effect<never, never, void>
|
|
10
|
-
|
|
11
|
-
export type Save<R> = (event: NavigationEvent) => Effect.Effect<R, never, void>
|
|
12
|
-
|
|
13
|
-
// Anytime there are changes to the model, we need to notify all event handlers
|
|
14
|
-
export const makeNotify = (model: Model) => (event: NavigationEvent) =>
|
|
15
|
-
Effect.gen(function* ($) {
|
|
16
|
-
// Notify event handlers
|
|
17
|
-
if (model.onNavigationHandlers.size > 0)
|
|
18
|
-
yield* $(
|
|
19
|
-
Effect.forEach(
|
|
20
|
-
model.onNavigationHandlers,
|
|
21
|
-
([handler, options]) => (options?.passive ? Effect.fork(handler(event)) : handler(event)),
|
|
22
|
-
{ discard: true },
|
|
23
|
-
),
|
|
24
|
-
)
|
|
25
|
-
})
|
|
26
|
-
|
|
27
|
-
export const makeOnNavigation =
|
|
28
|
-
(model: Model) =>
|
|
29
|
-
<R>(
|
|
30
|
-
handler: (event: NavigationEvent) => Effect.Effect<R, NavigationError, unknown>,
|
|
31
|
-
options?: OnNavigationOptions,
|
|
32
|
-
): Effect.Effect<R | Scope.Scope, NavigationError, void> =>
|
|
33
|
-
Effect.uninterruptibleMask((restore) =>
|
|
34
|
-
Effect.gen(function* ($) {
|
|
35
|
-
const context = yield* $(Effect.context<R>())
|
|
36
|
-
const handler_ = (event: NavigationEvent) =>
|
|
37
|
-
restore(Effect.provideContext(handler(event), context))
|
|
38
|
-
const entry = [handler_, options] as const
|
|
39
|
-
|
|
40
|
-
model.onNavigationHandlers.add(entry)
|
|
41
|
-
|
|
42
|
-
yield* $(
|
|
43
|
-
Effect.addFinalizer(() => Effect.sync(() => model.onNavigationHandlers.delete(entry))),
|
|
44
|
-
)
|
|
45
|
-
|
|
46
|
-
// Send the latest navigation event on subscription
|
|
47
|
-
const events = yield* $(model.events)
|
|
48
|
-
const index = yield* $(model.index)
|
|
49
|
-
const event = events[index]
|
|
50
|
-
|
|
51
|
-
yield* $(restore(handler(event)))
|
|
52
|
-
}),
|
|
53
|
-
)
|
|
54
|
-
|
|
55
|
-
// Anytime there are changes to the model, we need to notify all event handlers
|
|
56
|
-
export const makeNotifyEnd = (model: Model) => (event: NavigationEvent) =>
|
|
57
|
-
Effect.gen(function* ($) {
|
|
58
|
-
// Notify event handlers
|
|
59
|
-
if (model.onNavigationEndHandlers.size > 0)
|
|
60
|
-
yield* $(
|
|
61
|
-
Effect.forEach(
|
|
62
|
-
model.onNavigationEndHandlers,
|
|
63
|
-
([handler, options]) => (options?.passive ? Effect.fork(handler(event)) : handler(event)),
|
|
64
|
-
{ discard: true },
|
|
65
|
-
),
|
|
66
|
-
)
|
|
67
|
-
})
|
|
68
|
-
|
|
69
|
-
export const makeOnNavigationEnd =
|
|
70
|
-
(model: Model) =>
|
|
71
|
-
<R>(
|
|
72
|
-
handler: (event: NavigationEvent) => Effect.Effect<R, never, unknown>,
|
|
73
|
-
options?: OnNavigationOptions,
|
|
74
|
-
): Effect.Effect<R | Scope.Scope, never, void> =>
|
|
75
|
-
Effect.uninterruptibleMask((restore) =>
|
|
76
|
-
Effect.gen(function* ($) {
|
|
77
|
-
const context = yield* $(Effect.context<R>())
|
|
78
|
-
const handler_ = (event: NavigationEvent) =>
|
|
79
|
-
restore(Effect.provideContext(handler(event), context))
|
|
80
|
-
const entry = [handler_, options] as const
|
|
81
|
-
|
|
82
|
-
model.onNavigationEndHandlers.add(entry)
|
|
83
|
-
|
|
84
|
-
yield* $(
|
|
85
|
-
Effect.addFinalizer(() => Effect.sync(() => model.onNavigationEndHandlers.delete(entry))),
|
|
86
|
-
)
|
|
87
|
-
|
|
88
|
-
// Send the latest navigation event on subscription
|
|
89
|
-
const events = yield* $(model.events)
|
|
90
|
-
const index = yield* $(model.index)
|
|
91
|
-
const event = events[index]
|
|
92
|
-
|
|
93
|
-
yield* $(restore(handler(event)))
|
|
94
|
-
}),
|
|
95
|
-
)
|
|
96
|
-
|
|
97
|
-
export const makeGoTo =
|
|
98
|
-
<R, E>(
|
|
99
|
-
model: Model,
|
|
100
|
-
go: (delta: number, skipHistory?: boolean) => Effect.Effect<R, E, Destination>,
|
|
101
|
-
) =>
|
|
102
|
-
(key: string): Effect.Effect<R, E, Option.Option<Destination>> =>
|
|
103
|
-
Effect.gen(function* ($) {
|
|
104
|
-
const entries = yield* $(model.entries)
|
|
105
|
-
const currentIndex = yield* $(model.index)
|
|
106
|
-
const nextIndex = entries.findIndex((destination) => destination.key === key)
|
|
107
|
-
|
|
108
|
-
if (nextIndex === -1) return Option.none()
|
|
109
|
-
|
|
110
|
-
const delta = nextIndex - currentIndex
|
|
111
|
-
|
|
112
|
-
if (delta !== 0) {
|
|
113
|
-
return Option.some(yield* $(go(delta)))
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
return Option.some(entries[nextIndex])
|
|
117
|
-
})
|
package/src/storage.ts
DELETED
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
import * as Option from '@effect/data/Option'
|
|
2
|
-
import * as Effect from '@effect/io/Effect'
|
|
3
|
-
import { History, Location, getItem, setItem } from '@typed/dom'
|
|
4
|
-
|
|
5
|
-
import type { DomNavigationOptions } from './DOM.js'
|
|
6
|
-
import { NavigationEvent, Destination, NavigationType } from './Navigation.js'
|
|
7
|
-
import { NavigationEventJson, decodeNavigationEvent } from './json.js'
|
|
8
|
-
import { createKey, getUrl } from './util.js'
|
|
9
|
-
|
|
10
|
-
const TYPED_NAVIGATION_ENTRIES_KEY = '@typed/navigation/entries'
|
|
11
|
-
const TYPED_NAVIGATION_INDEX_KEY = '@typed/navigation/index'
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* @internal
|
|
15
|
-
*/
|
|
16
|
-
export const getStoredEvents: Effect.Effect<Storage, never, readonly NavigationEvent[]> =
|
|
17
|
-
Effect.gen(function* ($) {
|
|
18
|
-
const option = yield* $(getItem(TYPED_NAVIGATION_ENTRIES_KEY))
|
|
19
|
-
|
|
20
|
-
if (Option.isNone(option)) {
|
|
21
|
-
return []
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
return (JSON.parse(option.value) as readonly NavigationEventJson[]).map(decodeNavigationEvent)
|
|
25
|
-
})
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* @internal
|
|
29
|
-
*/
|
|
30
|
-
export const getStoredIndex = Effect.gen(function* ($) {
|
|
31
|
-
const option = yield* $(getItem(TYPED_NAVIGATION_INDEX_KEY))
|
|
32
|
-
|
|
33
|
-
if (Option.isNone(option)) {
|
|
34
|
-
return Option.none()
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
const n = JSON.parse(option.value) as number
|
|
38
|
-
|
|
39
|
-
if (Number.isNaN(n)) {
|
|
40
|
-
return Option.none()
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
return Option.some(n)
|
|
44
|
-
})
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* @internal
|
|
48
|
-
*/
|
|
49
|
-
export const getInitialValues = (
|
|
50
|
-
base: string,
|
|
51
|
-
options: DomNavigationOptions,
|
|
52
|
-
): Effect.Effect<
|
|
53
|
-
Storage | History | Location,
|
|
54
|
-
never,
|
|
55
|
-
readonly [readonly NavigationEvent[], number]
|
|
56
|
-
> =>
|
|
57
|
-
Effect.gen(function* ($) {
|
|
58
|
-
// Get Resources
|
|
59
|
-
const history = yield* $(History)
|
|
60
|
-
const location = yield* $(Location)
|
|
61
|
-
|
|
62
|
-
// Read the stored entries and index
|
|
63
|
-
const storedEntries = yield* $(getStoredEvents)
|
|
64
|
-
const storedIndex = Option.getOrElse(yield* $(getStoredIndex), () => storedEntries.length - 1)
|
|
65
|
-
const storedEntry = storedEntries[storedIndex]
|
|
66
|
-
|
|
67
|
-
// Read the initial url from the location
|
|
68
|
-
const initialUrl = getUrl(location.href, base, location.origin)
|
|
69
|
-
const initial: Destination = {
|
|
70
|
-
key: options.initialKey ?? (yield* $(createKey)),
|
|
71
|
-
url: initialUrl,
|
|
72
|
-
state: history.state,
|
|
73
|
-
}
|
|
74
|
-
const initialEvent: NavigationEvent = {
|
|
75
|
-
destination: initial,
|
|
76
|
-
hashChange: false,
|
|
77
|
-
navigationType: NavigationType.Push,
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
// If there are no stored entries then we can just use the initial entry
|
|
81
|
-
if (!storedEntry) {
|
|
82
|
-
return [[initialEvent], 0] as const
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
// If we're starting on the same page as the initial entry
|
|
86
|
-
// then we can just use the initial entries
|
|
87
|
-
if (storedEntry.destination.url.href === initialUrl.href) {
|
|
88
|
-
return [storedEntries, storedIndex] as const
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
// Otherwise, we need to push the initial entry with the current page
|
|
92
|
-
const entries = [...storedEntries.slice(0, storedIndex + 1), initialEvent]
|
|
93
|
-
|
|
94
|
-
return [entries, entries.length - 1] as const
|
|
95
|
-
})
|
|
96
|
-
|
|
97
|
-
export const saveToStorage = (entries: readonly NavigationEvent[], index: number) =>
|
|
98
|
-
Effect.gen(function* ($) {
|
|
99
|
-
yield* $(setItem(TYPED_NAVIGATION_ENTRIES_KEY, JSON.stringify(entries)))
|
|
100
|
-
yield* $(setItem(TYPED_NAVIGATION_INDEX_KEY, JSON.stringify(index)))
|
|
101
|
-
})
|
package/src/util.ts
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import * as Effect from '@effect/io/Effect'
|
|
2
|
-
import { pathJoin } from '@typed/path'
|
|
3
|
-
|
|
4
|
-
import { DestinationKey } from './Navigation.js'
|
|
5
|
-
|
|
6
|
-
export function getUrl(href: string, base: string, origin: string) {
|
|
7
|
-
const url = new URL(href, origin)
|
|
8
|
-
|
|
9
|
-
if (!url.pathname.startsWith(base)) {
|
|
10
|
-
url.pathname = pathJoin(base, url.pathname)
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
return url
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export const createKey = Effect.randomWith((random) =>
|
|
17
|
-
Effect.map(random.nextIntBetween(0, Number.MAX_SAFE_INTEGER), (n) =>
|
|
18
|
-
DestinationKey(n.toString(36).slice(2, 10)),
|
|
19
|
-
),
|
|
20
|
-
)
|
package/tsconfig.build.json
DELETED