crelte 0.2.0 → 0.2.2
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/cookies/ServerCookies.d.ts +4 -0
- package/dist/cookies/ServerCookies.d.ts.map +1 -1
- package/dist/cookies/ServerCookies.js +4 -0
- package/dist/cookies/internal.d.ts +3 -0
- package/dist/cookies/internal.d.ts.map +1 -0
- package/dist/cookies/internal.js +2 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -0
- package/dist/init/client.d.ts.map +1 -1
- package/dist/init/client.js +6 -0
- package/dist/routing/InnerRouter.d.ts +21 -15
- package/dist/routing/InnerRouter.d.ts.map +1 -1
- package/dist/routing/InnerRouter.js +76 -49
- package/dist/routing/Request.d.ts +6 -0
- package/dist/routing/Request.d.ts.map +1 -1
- package/dist/routing/Request.js +6 -0
- package/dist/routing/Route.d.ts +40 -17
- package/dist/routing/Route.d.ts.map +1 -1
- package/dist/routing/Route.js +69 -41
- package/dist/routing/Router.d.ts +30 -8
- package/dist/routing/Router.d.ts.map +1 -1
- package/dist/routing/Router.js +81 -20
- package/package.json +5 -1
- package/src/cookies/ServerCookies.ts +4 -0
- package/src/cookies/internal.ts +3 -0
- package/src/index.ts +9 -0
- package/src/init/client.ts +9 -0
- package/src/routing/InnerRouter.ts +81 -57
- package/src/routing/Request.ts +8 -0
- package/src/routing/Route.ts +91 -52
- package/src/routing/Router.ts +100 -27
- package/LICENSE.md +0 -41
|
@@ -2,7 +2,7 @@ import Site, { SiteFromGraphQl } from './Site.js';
|
|
|
2
2
|
import History from './History.js';
|
|
3
3
|
import { ClientHistory, ServerHistory } from './History.js';
|
|
4
4
|
import Request, { isRequest, RequestOptions } from './Request.js';
|
|
5
|
-
import Route
|
|
5
|
+
import Route from './Route.js';
|
|
6
6
|
|
|
7
7
|
export type InnerRouterOpts = {
|
|
8
8
|
preloadOnMouseOver: boolean;
|
|
@@ -66,14 +66,9 @@ export default class InnerRouter {
|
|
|
66
66
|
req._fillFromState(window.history.state);
|
|
67
67
|
|
|
68
68
|
req.origin = 'init';
|
|
69
|
-
|
|
70
|
-
if (req.search.get('x-craft-live-preview')) {
|
|
71
|
-
req.origin = 'live-preview-init';
|
|
72
|
-
}
|
|
73
|
-
|
|
74
69
|
window.history.scrollRestoration = 'manual';
|
|
75
70
|
|
|
76
|
-
this.
|
|
71
|
+
this.setRoute(req);
|
|
77
72
|
}
|
|
78
73
|
|
|
79
74
|
/**
|
|
@@ -139,9 +134,10 @@ export default class InnerRouter {
|
|
|
139
134
|
}
|
|
140
135
|
|
|
141
136
|
/**
|
|
142
|
-
* Resolve a url or Route and convert it to a
|
|
137
|
+
* Resolve a url or Route and convert it to a Request
|
|
143
138
|
*
|
|
144
139
|
* @param target
|
|
140
|
+
* @param opts, any option present will override the value in target
|
|
145
141
|
* @return Returns null if the url does not match our host (the protocol get's ignored)
|
|
146
142
|
*/
|
|
147
143
|
targetToRequest(
|
|
@@ -221,12 +217,20 @@ export default class InnerRouter {
|
|
|
221
217
|
|
|
222
218
|
e.preventDefault();
|
|
223
219
|
|
|
224
|
-
const
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
route
|
|
220
|
+
const req = this.targetToRequest(link.href, { origin: 'click' });
|
|
221
|
+
const routeEq =
|
|
222
|
+
this.route && this.route.eqUrl(req) && this.route.eqSearch(req);
|
|
223
|
+
// the route is the same don't do anything
|
|
224
|
+
// or maybe scroll the page to the hash? todo
|
|
225
|
+
if (routeEq && this.route?.eqHash(req)) return;
|
|
226
|
+
|
|
227
|
+
// this means the hash did not match, so we wan't to just scroll but not load
|
|
228
|
+
// data
|
|
229
|
+
if (routeEq) {
|
|
230
|
+
req.disableLoadData = true;
|
|
231
|
+
}
|
|
228
232
|
|
|
229
|
-
this.open(
|
|
233
|
+
this.open(req);
|
|
230
234
|
});
|
|
231
235
|
|
|
232
236
|
if (this.preloadOnMouseOver) {
|
|
@@ -270,7 +274,7 @@ export default class InnerRouter {
|
|
|
270
274
|
// use the latest state
|
|
271
275
|
this.history.replaceState(this.route._toState());
|
|
272
276
|
|
|
273
|
-
if (current.
|
|
277
|
+
if (current.inLivePreview()) {
|
|
274
278
|
sessionStorage.setItem(
|
|
275
279
|
'live-preview-scroll',
|
|
276
280
|
// use the latest scrollY
|
|
@@ -286,31 +290,28 @@ export default class InnerRouter {
|
|
|
286
290
|
window.addEventListener('popstate', async e => {
|
|
287
291
|
if (!('route' in e.state)) return;
|
|
288
292
|
|
|
289
|
-
const
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
// since the pop event replaced our state we can't replace the state
|
|
294
|
-
// for the scrollY in our open call so we just clear the current
|
|
295
|
-
// route since it is now already the new route
|
|
296
|
-
this.route = null;
|
|
293
|
+
const req = this.targetToRequest(window.location.href);
|
|
294
|
+
req._fillFromState(e.state);
|
|
295
|
+
req.origin = 'pop';
|
|
297
296
|
|
|
298
|
-
this.
|
|
297
|
+
this.setRoute(req);
|
|
299
298
|
});
|
|
300
299
|
}
|
|
301
300
|
|
|
302
301
|
/**
|
|
303
|
-
* Open
|
|
302
|
+
* Open a new route
|
|
304
303
|
*
|
|
305
304
|
* @param route a route object or an url or uri, never input the same route object again
|
|
306
305
|
* @param pushState if true pushed the state to the window.history
|
|
306
|
+
*
|
|
307
|
+
* ## Important
|
|
308
|
+
* Make sure a req always has the correct origin,
|
|
309
|
+
* `push` and `replace` will cause this function to throw an error
|
|
307
310
|
*/
|
|
308
|
-
open(
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
) {
|
|
313
|
-
const req = this.targetToRequest(target, opts);
|
|
311
|
+
open(req: Request) {
|
|
312
|
+
if (['push', 'replace'].includes(req.origin)) {
|
|
313
|
+
throw new Error('Do not use open with push or replace');
|
|
314
|
+
}
|
|
314
315
|
|
|
315
316
|
const current = this.route;
|
|
316
317
|
if (current) {
|
|
@@ -340,14 +341,10 @@ export default class InnerRouter {
|
|
|
340
341
|
return;
|
|
341
342
|
}
|
|
342
343
|
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
this.
|
|
346
|
-
|
|
347
|
-
});
|
|
348
|
-
} else {
|
|
349
|
-
this.setRoute(req);
|
|
350
|
-
}
|
|
344
|
+
req.index = (current?.index ?? 0) + 1;
|
|
345
|
+
this.onRoute(req, () => {
|
|
346
|
+
this.push(req, true);
|
|
347
|
+
});
|
|
351
348
|
}
|
|
352
349
|
|
|
353
350
|
/**
|
|
@@ -358,47 +355,70 @@ export default class InnerRouter {
|
|
|
358
355
|
*
|
|
359
356
|
* @param req
|
|
360
357
|
*/
|
|
361
|
-
setRoute(req: Request) {
|
|
358
|
+
setRoute(req: Request, preventOnRoute = false) {
|
|
362
359
|
this.route = req.toRoute();
|
|
363
360
|
|
|
364
|
-
this.onRoute(req, () => {});
|
|
361
|
+
if (!preventOnRoute) this.onRoute(req, () => {});
|
|
365
362
|
}
|
|
366
363
|
|
|
367
364
|
/**
|
|
368
|
-
* This pushes
|
|
369
|
-
* or currentSiteId change
|
|
365
|
+
* This pushes a new route to the history
|
|
370
366
|
*
|
|
371
|
-
*
|
|
372
|
-
* (search argument) and then call pushState
|
|
367
|
+
* @param req, never input the same route object again
|
|
373
368
|
*
|
|
374
|
-
*
|
|
369
|
+
* ## Important
|
|
370
|
+
* Make sure the route has the correct origin
|
|
375
371
|
*/
|
|
376
|
-
|
|
377
|
-
const url =
|
|
372
|
+
push(req: Request, preventOnRoute = false) {
|
|
373
|
+
const url = req.url;
|
|
374
|
+
// todo a push should also store the previous scrollY
|
|
375
|
+
|
|
376
|
+
let nReq = req;
|
|
377
|
+
if (req.scrollY === null) {
|
|
378
|
+
// if there is no scrollY stored we store the current scrollY
|
|
379
|
+
// since a push does not cause a scroll top
|
|
380
|
+
// todo: probably should refactor something probably
|
|
381
|
+
// should not be here
|
|
382
|
+
nReq = req.clone();
|
|
383
|
+
nReq.scrollY = this.history.scrollY();
|
|
384
|
+
}
|
|
378
385
|
|
|
379
386
|
this.history.pushState(
|
|
380
|
-
|
|
387
|
+
nReq._toState(),
|
|
381
388
|
url.pathname + url.search + url.hash,
|
|
382
389
|
);
|
|
383
390
|
|
|
384
|
-
this.
|
|
391
|
+
this.setRoute(req, preventOnRoute);
|
|
385
392
|
}
|
|
386
393
|
|
|
387
394
|
/**
|
|
388
|
-
* This replaces the
|
|
389
|
-
* or currentSiteId change
|
|
395
|
+
* This replaces the current route
|
|
390
396
|
*
|
|
391
|
-
* @param
|
|
397
|
+
* @param req, never input the same route object again
|
|
398
|
+
*
|
|
399
|
+
* ## Important
|
|
400
|
+
* Make sure the route has the correct origin
|
|
392
401
|
*/
|
|
393
|
-
|
|
394
|
-
const url =
|
|
402
|
+
replace(req: Request) {
|
|
403
|
+
const url = req.url;
|
|
404
|
+
|
|
405
|
+
let nReq = req;
|
|
406
|
+
if (req.scrollY === null) {
|
|
407
|
+
// if there is no scrollY stored we store the current scrollY
|
|
408
|
+
// since a replace does not cause a scrollTo and we wan't
|
|
409
|
+
// history back to work as intended
|
|
410
|
+
// todo: probably should refactor something probably
|
|
411
|
+
// should not be here
|
|
412
|
+
nReq = req.clone();
|
|
413
|
+
nReq.scrollY = this.history.scrollY();
|
|
414
|
+
}
|
|
395
415
|
|
|
396
416
|
this.history.replaceState(
|
|
397
|
-
|
|
417
|
+
nReq._toState(),
|
|
398
418
|
url.pathname + url.search + url.hash,
|
|
399
419
|
);
|
|
400
420
|
|
|
401
|
-
this.
|
|
421
|
+
this.setRoute(req);
|
|
402
422
|
}
|
|
403
423
|
|
|
404
424
|
/**
|
|
@@ -430,7 +450,7 @@ export default class InnerRouter {
|
|
|
430
450
|
|
|
431
451
|
// if the route is a live preview init and we have a scrollY stored
|
|
432
452
|
// scroll to that
|
|
433
|
-
if (req.
|
|
453
|
+
if (req.inLivePreview()) {
|
|
434
454
|
const scrollY = sessionStorage.getItem('live-preview-scroll');
|
|
435
455
|
if (scrollY) {
|
|
436
456
|
scrollTo = {
|
|
@@ -465,6 +485,10 @@ export default class InnerRouter {
|
|
|
465
485
|
};
|
|
466
486
|
}
|
|
467
487
|
|
|
488
|
+
// make sure push and replace don't cause a scroll if it is not intended
|
|
489
|
+
if (!scrollTo && (req.origin === 'push' || req.origin === 'replace'))
|
|
490
|
+
return;
|
|
491
|
+
|
|
468
492
|
// scroll to the top if nothing else matches
|
|
469
493
|
if (!scrollTo) {
|
|
470
494
|
scrollTo = {
|
package/src/routing/Request.ts
CHANGED
|
@@ -10,6 +10,7 @@ export type RequestOptions = {
|
|
|
10
10
|
index?: number;
|
|
11
11
|
origin?: RouteOrigin;
|
|
12
12
|
disableScroll?: boolean;
|
|
13
|
+
disableLoadData?: boolean;
|
|
13
14
|
statusCode?: number;
|
|
14
15
|
};
|
|
15
16
|
|
|
@@ -25,6 +26,12 @@ export default class Request extends Route {
|
|
|
25
26
|
*/
|
|
26
27
|
disableScroll: boolean;
|
|
27
28
|
|
|
29
|
+
/**
|
|
30
|
+
* Disable loading data
|
|
31
|
+
* @default false
|
|
32
|
+
*/
|
|
33
|
+
disableLoadData: boolean;
|
|
34
|
+
|
|
28
35
|
/**
|
|
29
36
|
* The Status code that should be returned for a redirect
|
|
30
37
|
*/
|
|
@@ -40,6 +47,7 @@ export default class Request extends Route {
|
|
|
40
47
|
super(url, site, opts);
|
|
41
48
|
|
|
42
49
|
this.disableScroll = opts.disableScroll ?? false;
|
|
50
|
+
this.disableLoadData = opts.disableLoadData ?? false;
|
|
43
51
|
this.statusCode = opts.statusCode ?? null;
|
|
44
52
|
this._renderBarrier = new RenderBarrier();
|
|
45
53
|
}
|
package/src/routing/Route.ts
CHANGED
|
@@ -13,23 +13,25 @@ export type RouteOptions = {
|
|
|
13
13
|
*
|
|
14
14
|
* - `'init'`: is set on the first page load
|
|
15
15
|
* - `'manual'`: is set when a route is triggered manually via `Router.open`
|
|
16
|
-
* - `'live-preview-init'`: is set on the first page load in live preview mode
|
|
17
16
|
* - `'click'`: is set when a route is triggered by a click event
|
|
18
17
|
* - `'pop'`: is set when a route is triggered by a popstate event (back/forward)
|
|
18
|
+
* - `'replace'`: is set when a route is replaced via `Router.replaceState`
|
|
19
|
+
* - `'push'`: is set when a route is pushed via `Router.pushState`
|
|
20
|
+
*
|
|
21
|
+
* ## Note
|
|
22
|
+
* `replace` and `push` will not call loadData
|
|
19
23
|
*/
|
|
20
24
|
export type RouteOrigin =
|
|
21
25
|
| 'init'
|
|
22
|
-
| 'live-preview-init'
|
|
23
26
|
| 'manual'
|
|
24
27
|
| 'click'
|
|
25
|
-
| 'pop'
|
|
28
|
+
| 'pop'
|
|
29
|
+
| 'replace'
|
|
30
|
+
| 'push';
|
|
26
31
|
|
|
27
32
|
/**
|
|
28
33
|
* A Route contains information about the current page for example the url and
|
|
29
|
-
* the site
|
|
30
|
-
*
|
|
31
|
-
* ## Note
|
|
32
|
-
* Never update the route directly, clone it before
|
|
34
|
+
* the site
|
|
33
35
|
*/
|
|
34
36
|
export default class Route {
|
|
35
37
|
/**
|
|
@@ -51,6 +53,13 @@ export default class Route {
|
|
|
51
53
|
|
|
52
54
|
/**
|
|
53
55
|
* The scroll position of the current route
|
|
56
|
+
*
|
|
57
|
+
* ## Note
|
|
58
|
+
* This does not have to represent the current scroll position
|
|
59
|
+
* should more be used internally.
|
|
60
|
+
*
|
|
61
|
+
* It might be useful for a new request to specify the wanted
|
|
62
|
+
* scroll position
|
|
54
63
|
*/
|
|
55
64
|
scrollY: number | null;
|
|
56
65
|
|
|
@@ -154,6 +163,51 @@ export default class Route {
|
|
|
154
163
|
return this.url.hash;
|
|
155
164
|
}
|
|
156
165
|
|
|
166
|
+
/**
|
|
167
|
+
* Checks if there are previous routes which would allow it to go back
|
|
168
|
+
*/
|
|
169
|
+
canGoBack(): boolean {
|
|
170
|
+
return !!this.index;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Gets the search param
|
|
175
|
+
*
|
|
176
|
+
* ## Example
|
|
177
|
+
* ```
|
|
178
|
+
* const route = new Route('https://example.com/foo/bar/?a=1&b=2', null);
|
|
179
|
+
* console.log(route.getSearchParam('a')); // '1'
|
|
180
|
+
* ```
|
|
181
|
+
*/
|
|
182
|
+
getSearchParam(key: string): string | null {
|
|
183
|
+
return this.search.get(key);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Sets the search param or removes it if the value is null or undefined
|
|
188
|
+
*
|
|
189
|
+
* ## Example
|
|
190
|
+
* ```
|
|
191
|
+
* const route = new Route('https://example.com/foo/bar/?a=1&b=2', null);
|
|
192
|
+
* route.setSearchParam('a', '3');
|
|
193
|
+
* console.log(route.url.href); // 'https://example.com/foo/bar/?a=3&b=2'
|
|
194
|
+
*
|
|
195
|
+
* route.setSearchParam('a', null);
|
|
196
|
+
* console.log(route.url.href); // 'https://example.com/foo/bar/?b=2'
|
|
197
|
+
* ```
|
|
198
|
+
*/
|
|
199
|
+
setSearchParam(key: string, value?: string | number | null) {
|
|
200
|
+
if (typeof value !== 'undefined' && value !== null) {
|
|
201
|
+
this.search.set(key, value as string);
|
|
202
|
+
} else {
|
|
203
|
+
this.search.delete(key);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
inLivePreview(): boolean {
|
|
208
|
+
return !!this.search.get('x-craft-live-preview');
|
|
209
|
+
}
|
|
210
|
+
|
|
157
211
|
/**
|
|
158
212
|
* Returns if the site matches the url
|
|
159
213
|
*/
|
|
@@ -176,7 +230,32 @@ export default class Route {
|
|
|
176
230
|
* This checks all properties of the url but search params do not have to be
|
|
177
231
|
* in the same order
|
|
178
232
|
*/
|
|
179
|
-
eq(route: Route) {
|
|
233
|
+
eq(route: Route | null) {
|
|
234
|
+
return (
|
|
235
|
+
route &&
|
|
236
|
+
this.eqUrl(route) &&
|
|
237
|
+
this.eqSearch(route) &&
|
|
238
|
+
this.eqHash(route)
|
|
239
|
+
);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Checks if the route is equal to another route
|
|
244
|
+
*
|
|
245
|
+
* This does not check the search params or hash
|
|
246
|
+
*/
|
|
247
|
+
eqUrl(route: Route | null) {
|
|
248
|
+
return (
|
|
249
|
+
route &&
|
|
250
|
+
this.url.pathname === route.url.pathname &&
|
|
251
|
+
this.url.origin === route.url.origin
|
|
252
|
+
);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Checks if the search params are equal to another route
|
|
257
|
+
*/
|
|
258
|
+
eqSearch(route: Route | null) {
|
|
180
259
|
const searchEq = (a: URLSearchParams, b: URLSearchParams) => {
|
|
181
260
|
if (a.size !== b.size) return false;
|
|
182
261
|
|
|
@@ -191,54 +270,14 @@ export default class Route {
|
|
|
191
270
|
.every(([[ak, av], [bk, bv]]) => ak === bk && av === bv);
|
|
192
271
|
};
|
|
193
272
|
|
|
194
|
-
return (
|
|
195
|
-
route &&
|
|
196
|
-
this.url.pathname === route.url.pathname &&
|
|
197
|
-
this.url.origin === route.url.origin &&
|
|
198
|
-
searchEq(this.search, route.search) &&
|
|
199
|
-
this.hash === route.hash
|
|
200
|
-
);
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
/**
|
|
204
|
-
* Checks if there are previous routes which would allow it to go back
|
|
205
|
-
*/
|
|
206
|
-
canGoBack(): boolean {
|
|
207
|
-
return !!this.index;
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
/**
|
|
211
|
-
* Gets the search param
|
|
212
|
-
*
|
|
213
|
-
* ## Example
|
|
214
|
-
* ```
|
|
215
|
-
* const route = new Route('https://example.com/foo/bar/?a=1&b=2', null);
|
|
216
|
-
* console.log(route.getSearchParam('a')); // '1'
|
|
217
|
-
* ```
|
|
218
|
-
*/
|
|
219
|
-
getSearchParam(key: string): string | null {
|
|
220
|
-
return this.search.get(key);
|
|
273
|
+
return route && searchEq(this.search, route.search);
|
|
221
274
|
}
|
|
222
275
|
|
|
223
276
|
/**
|
|
224
|
-
*
|
|
225
|
-
*
|
|
226
|
-
* ## Example
|
|
227
|
-
* ```
|
|
228
|
-
* const route = new Route('https://example.com/foo/bar/?a=1&b=2', null);
|
|
229
|
-
* route.setSearchParam('a', '3');
|
|
230
|
-
* console.log(route.url.href); // 'https://example.com/foo/bar/?a=3&b=2'
|
|
231
|
-
*
|
|
232
|
-
* route.setSearchParam('a', null);
|
|
233
|
-
* console.log(route.url.href); // 'https://example.com/foo/bar/?b=2'
|
|
234
|
-
* ```
|
|
277
|
+
* Checks if the hash is equal to another route
|
|
235
278
|
*/
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
this.search.set(key, value as string);
|
|
239
|
-
} else {
|
|
240
|
-
this.search.delete(key);
|
|
241
|
-
}
|
|
279
|
+
eqHash(route: Route | null) {
|
|
280
|
+
return route && this.hash === route.hash;
|
|
242
281
|
}
|
|
243
282
|
|
|
244
283
|
/**
|
package/src/routing/Router.ts
CHANGED
|
@@ -14,7 +14,7 @@ export type RouterOptions = {
|
|
|
14
14
|
|
|
15
15
|
const defaultRouterOpts = {
|
|
16
16
|
preloadOnMouseOver: false,
|
|
17
|
-
|
|
17
|
+
debugTiming: false,
|
|
18
18
|
};
|
|
19
19
|
|
|
20
20
|
type LoadedMore = {
|
|
@@ -35,6 +35,17 @@ type Internal = {
|
|
|
35
35
|
ready: () => any,
|
|
36
36
|
) => void;
|
|
37
37
|
|
|
38
|
+
// onNothingLoaded get's called if the request did not load new Data
|
|
39
|
+
// since maybe a push or replace was called
|
|
40
|
+
onNothingLoaded: (
|
|
41
|
+
req: Request,
|
|
42
|
+
// call ready once your ready to update the dom
|
|
43
|
+
// this makes sure we trigger a route and site update
|
|
44
|
+
// almost at the same moment and probably the same tick
|
|
45
|
+
// to make sure we don't have any flickering
|
|
46
|
+
ready: () => void,
|
|
47
|
+
) => void;
|
|
48
|
+
|
|
38
49
|
onLoad: LoadFn;
|
|
39
50
|
|
|
40
51
|
domReady: (req: Request) => void;
|
|
@@ -78,8 +89,6 @@ export default class Router {
|
|
|
78
89
|
*/
|
|
79
90
|
private _loadingProgress: Writable<number>;
|
|
80
91
|
|
|
81
|
-
private _onRouteEv: Listeners<[Route]>;
|
|
82
|
-
|
|
83
92
|
private _onRequest: Listeners<[Request]>;
|
|
84
93
|
|
|
85
94
|
/** @hidden */
|
|
@@ -105,13 +114,12 @@ export default class Router {
|
|
|
105
114
|
this._loading = new Writable(false);
|
|
106
115
|
this._loadingProgress = new Writable(0);
|
|
107
116
|
|
|
108
|
-
this._onRouteEv = new Listeners();
|
|
109
|
-
|
|
110
117
|
this._onRequest = new Listeners();
|
|
111
118
|
|
|
112
119
|
this._internal = {
|
|
113
|
-
onLoaded: () => {},
|
|
114
|
-
|
|
120
|
+
onLoaded: () => { },
|
|
121
|
+
onNothingLoaded: () => { },
|
|
122
|
+
onLoad: () => { },
|
|
115
123
|
domReady: req => this.inner.domReady(req),
|
|
116
124
|
initClient: () => this._initClient(),
|
|
117
125
|
initServer: (url, acceptLang) => this._initServer(url, acceptLang),
|
|
@@ -167,10 +175,13 @@ export default class Router {
|
|
|
167
175
|
/**
|
|
168
176
|
* Open a new route
|
|
169
177
|
*
|
|
170
|
-
* @param target the target to open can be an url or a
|
|
178
|
+
* @param target the target to open can be an url, a route or a request
|
|
171
179
|
* the url needs to start with http or with a / which will be considered as
|
|
172
180
|
* the site baseUrl
|
|
173
181
|
*
|
|
182
|
+
* ## Note
|
|
183
|
+
* The origin will always be set to 'manual'
|
|
184
|
+
*
|
|
174
185
|
* ## Example
|
|
175
186
|
* ```
|
|
176
187
|
* import { getRouter } from 'crelte';
|
|
@@ -182,16 +193,25 @@ export default class Router {
|
|
|
182
193
|
* // the following page will be opened https://example.com/de/foo/bar
|
|
183
194
|
* ```
|
|
184
195
|
*/
|
|
185
|
-
open(target: string | URL | Route, opts: RequestOptions = {}) {
|
|
186
|
-
this.inner.
|
|
196
|
+
open(target: string | URL | Route | Request, opts: RequestOptions = {}) {
|
|
197
|
+
const req = this.inner.targetToRequest(target, {
|
|
198
|
+
...opts,
|
|
199
|
+
origin: 'manual',
|
|
200
|
+
});
|
|
201
|
+
this.inner.open(req);
|
|
187
202
|
}
|
|
188
203
|
|
|
189
204
|
/**
|
|
190
|
-
* This pushes the
|
|
205
|
+
* This pushes the new route without triggering a new pageload
|
|
191
206
|
*
|
|
192
207
|
* You can use this when using pagination for example change the route object
|
|
193
208
|
* (search argument) and then call pushState
|
|
194
209
|
*
|
|
210
|
+
* ## Note
|
|
211
|
+
* This will always set the origin to 'push'
|
|
212
|
+
* And will clear the scrollY value if you not provide a new one via the `opts`
|
|
213
|
+
* This will disableLoadData by default if you not provide an override via the `opts`
|
|
214
|
+
*
|
|
195
215
|
* ## Example
|
|
196
216
|
* ```
|
|
197
217
|
* import { getRouter } from 'crelte';
|
|
@@ -204,18 +224,37 @@ export default class Router {
|
|
|
204
224
|
* router.pushState(route);
|
|
205
225
|
* ```
|
|
206
226
|
*/
|
|
207
|
-
|
|
227
|
+
push(route: Route | Request, opts: RequestOptions = {}) {
|
|
228
|
+
// cancel previous request
|
|
208
229
|
this.pageLoader.discard();
|
|
209
|
-
this.inner.
|
|
230
|
+
const req = this.inner.targetToRequest(route, {
|
|
231
|
+
...opts,
|
|
232
|
+
origin: 'push',
|
|
233
|
+
scrollY: opts.scrollY ?? undefined,
|
|
234
|
+
disableLoadData: opts.disableLoadData ?? true,
|
|
235
|
+
});
|
|
236
|
+
this.inner.push(req);
|
|
210
237
|
this.destroyRequest();
|
|
211
238
|
this.setNewRoute(route);
|
|
212
239
|
}
|
|
213
240
|
|
|
241
|
+
/**
|
|
242
|
+
* @deprecated use push instead
|
|
243
|
+
*/
|
|
244
|
+
pushState(route: Route | Request) {
|
|
245
|
+
this.push(route);
|
|
246
|
+
}
|
|
247
|
+
|
|
214
248
|
/**
|
|
215
249
|
* This replaces the state of the route without triggering an event
|
|
216
250
|
*
|
|
217
251
|
* You can use this when using some filters for example a search filter
|
|
218
252
|
*
|
|
253
|
+
* ## Note
|
|
254
|
+
* This will always set the origin to 'replace'
|
|
255
|
+
* And will clear the scrollY value if you not provide a new one via the `opts`
|
|
256
|
+
* This will disableLoadData by default if you not provide an override via the `opts`
|
|
257
|
+
*
|
|
219
258
|
* ## Example
|
|
220
259
|
* ```
|
|
221
260
|
* import { getRouter } from 'crelte';
|
|
@@ -228,11 +267,24 @@ export default class Router {
|
|
|
228
267
|
* router.replaceState(route);
|
|
229
268
|
* ```
|
|
230
269
|
*/
|
|
231
|
-
|
|
270
|
+
replace(route: Route | Request, opts: RequestOptions = {}) {
|
|
271
|
+
// cancel previous request
|
|
232
272
|
this.pageLoader.discard();
|
|
233
|
-
this.inner.
|
|
273
|
+
const req = this.inner.targetToRequest(route, {
|
|
274
|
+
origin: 'replace',
|
|
275
|
+
scrollY: opts.scrollY ?? undefined,
|
|
276
|
+
disableLoadData: opts.disableLoadData ?? true,
|
|
277
|
+
});
|
|
278
|
+
this.inner.replace(req);
|
|
234
279
|
this.destroyRequest();
|
|
235
|
-
this.setNewRoute(
|
|
280
|
+
this.setNewRoute(req);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* @deprecated use replace instead
|
|
285
|
+
*/
|
|
286
|
+
replaceState(route: Route | Request) {
|
|
287
|
+
this.replace(route);
|
|
236
288
|
}
|
|
237
289
|
|
|
238
290
|
/**
|
|
@@ -259,13 +311,13 @@ export default class Router {
|
|
|
259
311
|
/**
|
|
260
312
|
* Add a listener for the onRoute event
|
|
261
313
|
*
|
|
262
|
-
* This
|
|
263
|
-
*
|
|
314
|
+
* This will trigger every time a new route is set
|
|
315
|
+
* and is equivalent to router.route.subscribe(fn)
|
|
264
316
|
*
|
|
265
317
|
* @returns a function to remove the listener
|
|
266
318
|
*/
|
|
267
319
|
onRoute(fn: (route: Route) => void): () => void {
|
|
268
|
-
return this.
|
|
320
|
+
return this.route.subscribe(fn);
|
|
269
321
|
}
|
|
270
322
|
|
|
271
323
|
/**
|
|
@@ -297,10 +349,14 @@ export default class Router {
|
|
|
297
349
|
): Promise<ServerInited> {
|
|
298
350
|
this.inner.initServer();
|
|
299
351
|
|
|
352
|
+
this._internal.onNothingLoaded = (_req, ready) => {
|
|
353
|
+
ready();
|
|
354
|
+
};
|
|
355
|
+
|
|
300
356
|
const prom: Promise<ServerInited> = new Promise(resolve => {
|
|
301
357
|
this._internal.onLoaded = (success, req, ready) => {
|
|
302
358
|
const props = ready();
|
|
303
|
-
this._internal.onLoaded = () => {};
|
|
359
|
+
this._internal.onLoaded = () => { };
|
|
304
360
|
|
|
305
361
|
resolve({
|
|
306
362
|
success,
|
|
@@ -360,7 +416,11 @@ export default class Router {
|
|
|
360
416
|
this._onRequest.trigger(req);
|
|
361
417
|
|
|
362
418
|
// route prepared
|
|
363
|
-
|
|
419
|
+
if (!req.disableLoadData) {
|
|
420
|
+
this.pageLoader.load(req, { changeHistory });
|
|
421
|
+
} else {
|
|
422
|
+
this._onNothingLoaded(req, { changeHistory });
|
|
423
|
+
}
|
|
364
424
|
}
|
|
365
425
|
|
|
366
426
|
private destroyRequest() {
|
|
@@ -389,15 +449,28 @@ export default class Router {
|
|
|
389
449
|
|
|
390
450
|
const route = req.toRoute();
|
|
391
451
|
|
|
392
|
-
|
|
452
|
+
// call the client or server saying we are ready for a new render
|
|
453
|
+
this._internal.onLoaded(resp.success, req, () => {
|
|
393
454
|
this.setNewRoute(route);
|
|
455
|
+
return resp.data;
|
|
456
|
+
});
|
|
457
|
+
}
|
|
394
458
|
|
|
395
|
-
|
|
396
|
-
|
|
459
|
+
private async _onNothingLoaded(req: Request, more: LoadedMore) {
|
|
460
|
+
// check if the render was cancelled
|
|
461
|
+
if (await req._renderBarrier.ready()) return;
|
|
397
462
|
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
463
|
+
// when the data is loaded let's update the route of the inner
|
|
464
|
+
// this is will only happen if no other route has been requested
|
|
465
|
+
// in the meantime
|
|
466
|
+
more.changeHistory();
|
|
467
|
+
|
|
468
|
+
const route = req.toRoute();
|
|
469
|
+
|
|
470
|
+
// call the client or server saying there was an update in the route
|
|
471
|
+
// but no new data was loaded so no render should happen
|
|
472
|
+
this._internal.onNothingLoaded(req, () => {
|
|
473
|
+
this.setNewRoute(route);
|
|
401
474
|
});
|
|
402
475
|
}
|
|
403
476
|
|