crelte 0.2.0 → 0.2.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.
@@ -12,6 +12,7 @@ export type RouterOptions = {
12
12
  */
13
13
  type Internal = {
14
14
  onLoaded: (success: boolean, req: Request, ready: () => any) => void;
15
+ onNothingLoaded: (req: Request, ready: () => void) => void;
15
16
  onLoad: LoadFn;
16
17
  domReady: (req: Request) => void;
17
18
  initClient: () => void;
@@ -42,7 +43,6 @@ export default class Router {
42
43
  * The loading progress, the value is between 0 and 1
43
44
  */
44
45
  private _loadingProgress;
45
- private _onRouteEv;
46
46
  private _onRequest;
47
47
  /** @hidden */
48
48
  _internal: Internal;
@@ -72,10 +72,13 @@ export default class Router {
72
72
  /**
73
73
  * Open a new route
74
74
  *
75
- * @param target the target to open can be an url or a route
75
+ * @param target the target to open can be an url, a route or a request
76
76
  * the url needs to start with http or with a / which will be considered as
77
77
  * the site baseUrl
78
78
  *
79
+ * ## Note
80
+ * The origin will always be set to 'manual'
81
+ *
79
82
  * ## Example
80
83
  * ```
81
84
  * import { getRouter } from 'crelte';
@@ -87,13 +90,18 @@ export default class Router {
87
90
  * // the following page will be opened https://example.com/de/foo/bar
88
91
  * ```
89
92
  */
90
- open(target: string | URL | Route, opts?: RequestOptions): void;
93
+ open(target: string | URL | Route | Request, opts?: RequestOptions): void;
91
94
  /**
92
- * This pushes the state of the route without triggering an event
95
+ * This pushes the new route without triggering a new pageload
93
96
  *
94
97
  * You can use this when using pagination for example change the route object
95
98
  * (search argument) and then call pushState
96
99
  *
100
+ * ## Note
101
+ * This will always set the origin to 'push'
102
+ * And will clear the scrollY value if you not provide a new one via the `opts`
103
+ * This will disableLoadData by default if you not provide an override via the `opts`
104
+ *
97
105
  * ## Example
98
106
  * ```
99
107
  * import { getRouter } from 'crelte';
@@ -106,12 +114,21 @@ export default class Router {
106
114
  * router.pushState(route);
107
115
  * ```
108
116
  */
109
- pushState(route: Route): void;
117
+ push(route: Route | Request, opts?: RequestOptions): void;
118
+ /**
119
+ * @deprecated use push instead
120
+ */
121
+ pushState(route: Route | Request): void;
110
122
  /**
111
123
  * This replaces the state of the route without triggering an event
112
124
  *
113
125
  * You can use this when using some filters for example a search filter
114
126
  *
127
+ * ## Note
128
+ * This will always set the origin to 'replace'
129
+ * And will clear the scrollY value if you not provide a new one via the `opts`
130
+ * This will disableLoadData by default if you not provide an override via the `opts`
131
+ *
115
132
  * ## Example
116
133
  * ```
117
134
  * import { getRouter } from 'crelte';
@@ -124,7 +141,11 @@ export default class Router {
124
141
  * router.replaceState(route);
125
142
  * ```
126
143
  */
127
- replaceState(route: Route): void;
144
+ replace(route: Route | Request, opts?: RequestOptions): void;
145
+ /**
146
+ * @deprecated use replace instead
147
+ */
148
+ replaceState(route: Route | Request): void;
128
149
  /**
129
150
  * Checks if there are previous routes which would allow it to go back
130
151
  */
@@ -140,8 +161,8 @@ export default class Router {
140
161
  /**
141
162
  * Add a listener for the onRoute event
142
163
  *
143
- * This differs from router.route.subscribe in the way that
144
- * it will only trigger if a new render / load will occur
164
+ * This will trigger every time a new route is set
165
+ * and is equivalent to router.route.subscribe(fn)
145
166
  *
146
167
  * @returns a function to remove the listener
147
168
  */
@@ -161,6 +182,7 @@ export default class Router {
161
182
  private destroyRequest;
162
183
  private _onPreload;
163
184
  private _onLoaded;
185
+ private _onNothingLoaded;
164
186
  private _onProgress;
165
187
  }
166
188
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"Router.d.ts","sourceRoot":"","sources":["../../../../src/routing/Router.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"Router.d.ts","sourceRoot":"","sources":["../../../../src/routing/Router.ts"],"names":[],"mappings":"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"}
@@ -28,7 +28,6 @@ export default class Router {
28
28
  * The loading progress, the value is between 0 and 1
29
29
  */
30
30
  _loadingProgress;
31
- _onRouteEv;
32
31
  _onRequest;
33
32
  /** @hidden */
34
33
  _internal;
@@ -48,10 +47,10 @@ export default class Router {
48
47
  this._request = null;
49
48
  this._loading = new Writable(false);
50
49
  this._loadingProgress = new Writable(0);
51
- this._onRouteEv = new Listeners();
52
50
  this._onRequest = new Listeners();
53
51
  this._internal = {
54
52
  onLoaded: () => { },
53
+ onNothingLoaded: () => { },
55
54
  onLoad: () => { },
56
55
  domReady: req => this.inner.domReady(req),
57
56
  initClient: () => this._initClient(),
@@ -96,10 +95,13 @@ export default class Router {
96
95
  /**
97
96
  * Open a new route
98
97
  *
99
- * @param target the target to open can be an url or a route
98
+ * @param target the target to open can be an url, a route or a request
100
99
  * the url needs to start with http or with a / which will be considered as
101
100
  * the site baseUrl
102
101
  *
102
+ * ## Note
103
+ * The origin will always be set to 'manual'
104
+ *
103
105
  * ## Example
104
106
  * ```
105
107
  * import { getRouter } from 'crelte';
@@ -112,14 +114,23 @@ export default class Router {
112
114
  * ```
113
115
  */
114
116
  open(target, opts = {}) {
115
- this.inner.open(target, opts);
117
+ const req = this.inner.targetToRequest(target, {
118
+ ...opts,
119
+ origin: 'manual',
120
+ });
121
+ this.inner.open(req);
116
122
  }
117
123
  /**
118
- * This pushes the state of the route without triggering an event
124
+ * This pushes the new route without triggering a new pageload
119
125
  *
120
126
  * You can use this when using pagination for example change the route object
121
127
  * (search argument) and then call pushState
122
128
  *
129
+ * ## Note
130
+ * This will always set the origin to 'push'
131
+ * And will clear the scrollY value if you not provide a new one via the `opts`
132
+ * This will disableLoadData by default if you not provide an override via the `opts`
133
+ *
123
134
  * ## Example
124
135
  * ```
125
136
  * import { getRouter } from 'crelte';
@@ -132,17 +143,35 @@ export default class Router {
132
143
  * router.pushState(route);
133
144
  * ```
134
145
  */
135
- pushState(route) {
146
+ push(route, opts = {}) {
147
+ // cancel previous request
136
148
  this.pageLoader.discard();
137
- this.inner.pushState(route);
149
+ const req = this.inner.targetToRequest(route, {
150
+ ...opts,
151
+ origin: 'push',
152
+ scrollY: opts.scrollY ?? undefined,
153
+ disableLoadData: opts.disableLoadData ?? true,
154
+ });
155
+ this.inner.push(req);
138
156
  this.destroyRequest();
139
157
  this.setNewRoute(route);
140
158
  }
159
+ /**
160
+ * @deprecated use push instead
161
+ */
162
+ pushState(route) {
163
+ this.push(route);
164
+ }
141
165
  /**
142
166
  * This replaces the state of the route without triggering an event
143
167
  *
144
168
  * You can use this when using some filters for example a search filter
145
169
  *
170
+ * ## Note
171
+ * This will always set the origin to 'replace'
172
+ * And will clear the scrollY value if you not provide a new one via the `opts`
173
+ * This will disableLoadData by default if you not provide an override via the `opts`
174
+ *
146
175
  * ## Example
147
176
  * ```
148
177
  * import { getRouter } from 'crelte';
@@ -155,11 +184,23 @@ export default class Router {
155
184
  * router.replaceState(route);
156
185
  * ```
157
186
  */
158
- replaceState(route) {
187
+ replace(route, opts = {}) {
188
+ // cancel previous request
159
189
  this.pageLoader.discard();
160
- this.inner.replaceState(route);
190
+ const req = this.inner.targetToRequest(route, {
191
+ origin: 'replace',
192
+ scrollY: opts.scrollY ?? undefined,
193
+ disableLoadData: opts.disableLoadData ?? true,
194
+ });
195
+ this.inner.replace(req);
161
196
  this.destroyRequest();
162
- this.setNewRoute(route);
197
+ this.setNewRoute(req);
198
+ }
199
+ /**
200
+ * @deprecated use replace instead
201
+ */
202
+ replaceState(route) {
203
+ this.replace(route);
163
204
  }
164
205
  /**
165
206
  * Checks if there are previous routes which would allow it to go back
@@ -182,13 +223,13 @@ export default class Router {
182
223
  /**
183
224
  * Add a listener for the onRoute event
184
225
  *
185
- * This differs from router.route.subscribe in the way that
186
- * it will only trigger if a new render / load will occur
226
+ * This will trigger every time a new route is set
227
+ * and is equivalent to router.route.subscribe(fn)
187
228
  *
188
229
  * @returns a function to remove the listener
189
230
  */
190
231
  onRoute(fn) {
191
- return this._onRouteEv.add(fn);
232
+ return this.route.subscribe(fn);
192
233
  }
193
234
  /**
194
235
  * Add a listener for the onRequest event
@@ -213,6 +254,9 @@ export default class Router {
213
254
  }
214
255
  async _initServer(url, acceptLang) {
215
256
  this.inner.initServer();
257
+ this._internal.onNothingLoaded = (_req, ready) => {
258
+ ready();
259
+ };
216
260
  const prom = new Promise(resolve => {
217
261
  this._internal.onLoaded = (success, req, ready) => {
218
262
  const props = ready();
@@ -263,7 +307,12 @@ export default class Router {
263
307
  }
264
308
  this._onRequest.trigger(req);
265
309
  // route prepared
266
- this.pageLoader.load(req, { changeHistory });
310
+ if (!req.disableLoadData) {
311
+ this.pageLoader.load(req, { changeHistory });
312
+ }
313
+ else {
314
+ this._onNothingLoaded(req, { changeHistory });
315
+ }
267
316
  }
268
317
  destroyRequest() {
269
318
  if (!this._request)
@@ -283,15 +332,27 @@ export default class Router {
283
332
  // in the meantime
284
333
  more.changeHistory();
285
334
  const route = req.toRoute();
286
- const updateRoute = () => {
287
- this.setNewRoute(route);
288
- this._onRouteEv.trigger(route.clone());
289
- };
335
+ // call the client or server saying we are ready for a new render
290
336
  this._internal.onLoaded(resp.success, req, () => {
291
- updateRoute();
337
+ this.setNewRoute(route);
292
338
  return resp.data;
293
339
  });
294
340
  }
341
+ async _onNothingLoaded(req, more) {
342
+ // check if the render was cancelled
343
+ if (await req._renderBarrier.ready())
344
+ return;
345
+ // when the data is loaded let's update the route of the inner
346
+ // this is will only happen if no other route has been requested
347
+ // in the meantime
348
+ more.changeHistory();
349
+ const route = req.toRoute();
350
+ // call the client or server saying there was an update in the route
351
+ // but no new data was loaded so no render should happen
352
+ this._internal.onNothingLoaded(req, () => {
353
+ this.setNewRoute(route);
354
+ });
355
+ }
295
356
  _onProgress(loading, progress) {
296
357
  if (this._loading.get() !== loading)
297
358
  this._loading.set(loading);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "crelte",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "author": "Crelte <support@crelte.com>",
5
5
  "type": "module",
6
6
  "scripts": {
package/src/index.ts CHANGED
@@ -143,6 +143,15 @@ export function getCookies(): Cookies {
143
143
  return getCrelte().cookies;
144
144
  }
145
145
 
146
+ export function onRoute(fn: (route: Route, crelte: Crelte) => void) {
147
+ const crelte = getCrelte();
148
+ const rmListener = crelte.router.onRoute(route => {
149
+ fn(route, crelte);
150
+ });
151
+
152
+ onDestroy(rmListener);
153
+ }
154
+
146
155
  /**
147
156
  * Listen for requests
148
157
  *
@@ -204,6 +204,15 @@ export function main(data: MainData) {
204
204
  await render();
205
205
  };
206
206
 
207
+ crelte.router._internal.onNothingLoaded = async (req, ready) => {
208
+ crelte.globals._updateSiteId(req.site.id);
209
+ ready();
210
+
211
+ await tick();
212
+
213
+ crelte.router._internal.domReady(req);
214
+ };
215
+
207
216
  crelte.router._internal.initClient();
208
217
  }
209
218
 
@@ -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, { RouteOptions } from './Route.js';
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.open(req, {}, false);
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 Route
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 route = this.routeFromUrl(link.href);
225
- if (this.route?.eq(route)) return;
226
-
227
- route.origin = 'click';
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(route);
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.origin === 'live-preview-init') {
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 route = this.targetToRequest(window.location.href);
290
- route._fillFromState(e.state);
291
- route.origin = 'pop';
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.open(route, {}, false);
297
+ this.setRoute(req);
299
298
  });
300
299
  }
301
300
 
302
301
  /**
303
- * Open's a route
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
- target: string | URL | Route | Request,
310
- opts: RouteOptions = {},
311
- pushState: boolean = true,
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
- if (pushState) {
344
- req.index = (current?.index ?? 0) + 1;
345
- this.onRoute(req, () => {
346
- this.pushState(req.toRoute());
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 the state of the route without triggering a currentRoute
369
- * or currentSiteId change
365
+ * This pushes a new route to the history
370
366
  *
371
- * You can use when using pagination for example change the route object
372
- * (search argument) and then call pushState
367
+ * @param req, never input the same route object again
373
368
  *
374
- * @param route, never input the same route object again
369
+ * ## Important
370
+ * Make sure the route has the correct origin
375
371
  */
376
- pushState(route: Route) {
377
- const url = route.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
- route._toState(),
387
+ nReq._toState(),
381
388
  url.pathname + url.search + url.hash,
382
389
  );
383
390
 
384
- this.route = route;
391
+ this.setRoute(req, preventOnRoute);
385
392
  }
386
393
 
387
394
  /**
388
- * This replaces the state of the route without triggering a currentRoute
389
- * or currentSiteId change
395
+ * This replaces the current route
390
396
  *
391
- * @param route, never input the same route object again
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
- replaceState(route: Route) {
394
- const url = route.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
- route._toState(),
417
+ nReq._toState(),
398
418
  url.pathname + url.search + url.hash,
399
419
  );
400
420
 
401
- this.route = route;
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.origin === 'live-preview-init') {
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 = {
@@ -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
  }