swup 4.4.4 → 4.5.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/dist/Swup.cjs +1 -1
- package/dist/Swup.cjs.map +1 -1
- package/dist/Swup.modern.js +1 -1
- package/dist/Swup.modern.js.map +1 -1
- package/dist/Swup.module.js +1 -1
- package/dist/Swup.module.js.map +1 -1
- package/dist/Swup.umd.js +1 -1
- package/dist/Swup.umd.js.map +1 -1
- package/dist/types/Swup.d.ts +8 -4
- package/dist/types/Swup.d.ts.map +1 -1
- package/dist/types/helpers/Location.d.ts.map +1 -1
- package/dist/types/helpers/history.d.ts +14 -0
- package/dist/types/helpers/history.d.ts.map +1 -0
- package/dist/types/helpers.d.ts +1 -2
- package/dist/types/helpers.d.ts.map +1 -1
- package/dist/types/modules/Classes.d.ts.map +1 -1
- package/dist/types/modules/Hooks.d.ts +16 -8
- package/dist/types/modules/Hooks.d.ts.map +1 -1
- package/dist/types/modules/Visit.d.ts +28 -22
- package/dist/types/modules/Visit.d.ts.map +1 -1
- package/dist/types/modules/animatePageIn.d.ts +2 -1
- package/dist/types/modules/animatePageIn.d.ts.map +1 -1
- package/dist/types/modules/animatePageOut.d.ts +2 -1
- package/dist/types/modules/animatePageOut.d.ts.map +1 -1
- package/dist/types/modules/fetchPage.d.ts.map +1 -1
- package/dist/types/modules/navigate.d.ts +2 -2
- package/dist/types/modules/navigate.d.ts.map +1 -1
- package/dist/types/modules/renderPage.d.ts +2 -1
- package/dist/types/modules/renderPage.d.ts.map +1 -1
- package/dist/types/modules/replaceContent.d.ts.map +1 -1
- package/dist/types/modules/scrollToContent.d.ts +2 -1
- package/dist/types/modules/scrollToContent.d.ts.map +1 -1
- package/package.json +2 -1
- package/src/Swup.ts +40 -37
- package/src/helpers/Location.ts +1 -0
- package/src/helpers/history.ts +37 -0
- package/src/helpers.ts +1 -2
- package/src/modules/Cache.ts +2 -2
- package/src/modules/Classes.ts +8 -1
- package/src/modules/Hooks.ts +77 -29
- package/src/modules/Visit.ts +86 -47
- package/src/modules/animatePageIn.ts +9 -8
- package/src/modules/animatePageOut.ts +7 -18
- package/src/modules/fetchPage.ts +9 -11
- package/src/modules/navigate.ts +78 -34
- package/src/modules/renderPage.ts +15 -13
- package/src/modules/replaceContent.ts +1 -0
- package/src/modules/scrollToContent.ts +6 -4
- package/dist/types/helpers/createHistoryRecord.d.ts +0 -10
- package/dist/types/helpers/createHistoryRecord.d.ts.map +0 -1
- package/dist/types/helpers/updateHistoryRecord.d.ts +0 -3
- package/dist/types/helpers/updateHistoryRecord.d.ts.map +0 -1
- package/src/helpers/createHistoryRecord.ts +0 -24
- package/src/helpers/updateHistoryRecord.ts +0 -19
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { getCurrentUrl } from './getCurrentUrl.js';
|
|
2
|
+
|
|
3
|
+
export interface HistoryState {
|
|
4
|
+
url: string;
|
|
5
|
+
source: 'swup';
|
|
6
|
+
random: number;
|
|
7
|
+
index?: number;
|
|
8
|
+
[key: string]: unknown;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
type HistoryData = Record<string, unknown>;
|
|
12
|
+
|
|
13
|
+
/** Create a new history record with a custom swup identifier. */
|
|
14
|
+
export const createHistoryRecord = (url: string, data: HistoryData = {}): void => {
|
|
15
|
+
url = url || getCurrentUrl({ hash: true });
|
|
16
|
+
const state: HistoryState = {
|
|
17
|
+
url,
|
|
18
|
+
random: Math.random(),
|
|
19
|
+
source: 'swup',
|
|
20
|
+
...data
|
|
21
|
+
};
|
|
22
|
+
history.pushState(state, '', url);
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/** Update the current history record with a custom swup identifier. */
|
|
26
|
+
export const updateHistoryRecord = (url: string | null = null, data: HistoryData = {}): void => {
|
|
27
|
+
url = url || getCurrentUrl({ hash: true });
|
|
28
|
+
const currentState = (history.state as HistoryState) || {};
|
|
29
|
+
const state: HistoryState = {
|
|
30
|
+
...currentState,
|
|
31
|
+
url,
|
|
32
|
+
random: Math.random(),
|
|
33
|
+
source: 'swup',
|
|
34
|
+
...data
|
|
35
|
+
};
|
|
36
|
+
history.replaceState(state, '', url);
|
|
37
|
+
};
|
package/src/helpers.ts
CHANGED
|
@@ -2,8 +2,7 @@
|
|
|
2
2
|
// e.g. import { updateHistoryRecord } from 'swup'
|
|
3
3
|
|
|
4
4
|
export { classify } from './helpers/classify.js';
|
|
5
|
-
export { createHistoryRecord } from './helpers/
|
|
6
|
-
export { updateHistoryRecord } from './helpers/updateHistoryRecord.js';
|
|
5
|
+
export { createHistoryRecord, updateHistoryRecord } from './helpers/history.js';
|
|
7
6
|
export { delegateEvent } from './helpers/delegateEvent.js';
|
|
8
7
|
export { getCurrentUrl } from './helpers/getCurrentUrl.js';
|
|
9
8
|
export { Location } from './helpers/Location.js';
|
package/src/modules/Cache.ts
CHANGED
|
@@ -49,7 +49,7 @@ export class Cache {
|
|
|
49
49
|
url = this.resolve(url);
|
|
50
50
|
page = { ...page, url };
|
|
51
51
|
this.pages.set(url, page);
|
|
52
|
-
this.swup.hooks.callSync('cache:set', { page });
|
|
52
|
+
this.swup.hooks.callSync('cache:set', undefined, { page });
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
/** Update a cache record, overwriting or adding custom data. */
|
|
@@ -67,7 +67,7 @@ export class Cache {
|
|
|
67
67
|
/** Empty the cache. */
|
|
68
68
|
clear(): void {
|
|
69
69
|
this.pages.clear();
|
|
70
|
-
this.swup.hooks.callSync('cache:clear', undefined);
|
|
70
|
+
this.swup.hooks.callSync('cache:clear', undefined, undefined);
|
|
71
71
|
}
|
|
72
72
|
|
|
73
73
|
/** Remove all cache entries that return true for a given predicate function. */
|
package/src/modules/Classes.ts
CHANGED
|
@@ -3,7 +3,14 @@ import { queryAll } from '../utils.js';
|
|
|
3
3
|
|
|
4
4
|
export class Classes {
|
|
5
5
|
protected swup: Swup;
|
|
6
|
-
protected swupClasses = [
|
|
6
|
+
protected swupClasses = [
|
|
7
|
+
'to-',
|
|
8
|
+
'is-changing',
|
|
9
|
+
'is-rendering',
|
|
10
|
+
'is-popstate',
|
|
11
|
+
'is-animating',
|
|
12
|
+
'is-leaving'
|
|
13
|
+
];
|
|
7
14
|
|
|
8
15
|
constructor(swup: Swup) {
|
|
9
16
|
this.swup = swup;
|
package/src/modules/Hooks.ts
CHANGED
|
@@ -2,7 +2,7 @@ import type { DelegateEvent } from 'delegate-it';
|
|
|
2
2
|
|
|
3
3
|
import type Swup from '../Swup.js';
|
|
4
4
|
import { isPromise, runAsPromise } from '../utils.js';
|
|
5
|
-
import
|
|
5
|
+
import { Visit } from './Visit.js';
|
|
6
6
|
import type { FetchOptions, PageData } from './fetchPage.js';
|
|
7
7
|
|
|
8
8
|
export interface HookDefinitions {
|
|
@@ -33,16 +33,16 @@ export interface HookDefinitions {
|
|
|
33
33
|
'scroll:anchor': { hash: string; options: ScrollIntoViewOptions };
|
|
34
34
|
'visit:start': undefined;
|
|
35
35
|
'visit:transition': undefined;
|
|
36
|
+
'visit:abort': undefined;
|
|
36
37
|
'visit:end': undefined;
|
|
37
38
|
}
|
|
38
39
|
|
|
39
40
|
export interface HookReturnValues {
|
|
40
|
-
'content:scroll': Promise<boolean
|
|
41
|
+
'content:scroll': Promise<boolean> | boolean;
|
|
41
42
|
'fetch:request': Promise<Response>;
|
|
42
43
|
'page:load': Promise<PageData>;
|
|
43
44
|
'scroll:top': boolean;
|
|
44
45
|
'scroll:anchor': boolean;
|
|
45
|
-
'visit:transition': Promise<boolean>;
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
export type HookArguments<T extends HookName> = HookDefinitions[T];
|
|
@@ -154,6 +154,7 @@ export class Hooks {
|
|
|
154
154
|
'scroll:anchor',
|
|
155
155
|
'visit:start',
|
|
156
156
|
'visit:transition',
|
|
157
|
+
'visit:abort',
|
|
157
158
|
'visit:end'
|
|
158
159
|
];
|
|
159
160
|
|
|
@@ -333,20 +334,29 @@ export class Hooks {
|
|
|
333
334
|
* Trigger a hook asynchronously, executing its default handler and all registered handlers.
|
|
334
335
|
* Will execute all handlers in order and `await` any `Promise`s they return.
|
|
335
336
|
* @param hook Name of the hook to trigger
|
|
337
|
+
* @param visit The visit object this hook belongs to
|
|
336
338
|
* @param args Arguments to pass to the handler
|
|
337
339
|
* @param defaultHandler A default implementation of this hook to execute
|
|
338
340
|
* @returns The resolved return value of the executed default handler
|
|
339
341
|
*/
|
|
342
|
+
// Overload: default order of arguments
|
|
343
|
+
async call<T extends HookName>(hook: T, visit: Visit | undefined, args: HookArguments<T>, defaultHandler?: HookDefaultHandler<T>): Promise<Awaited<ReturnType<HookDefaultHandler<T>>>>; // prettier-ignore
|
|
344
|
+
// Overload: legacy order of arguments, with visit missing
|
|
345
|
+
async call<T extends HookName>(hook: T, args: HookArguments<T>, defaultHandler?: HookDefaultHandler<T>): Promise<Awaited<ReturnType<HookDefaultHandler<T>>>>; // prettier-ignore
|
|
346
|
+
// Implementation
|
|
340
347
|
async call<T extends HookName>(
|
|
341
348
|
hook: T,
|
|
342
|
-
|
|
343
|
-
|
|
349
|
+
arg1: Visit | HookArguments<T>,
|
|
350
|
+
arg2: HookArguments<T> | HookDefaultHandler<T>,
|
|
351
|
+
arg3?: HookDefaultHandler<T>
|
|
344
352
|
): Promise<Awaited<ReturnType<HookDefaultHandler<T>>>> {
|
|
353
|
+
const [visit, args, defaultHandler] = this.parseCallArgs(hook, arg1, arg2, arg3);
|
|
354
|
+
|
|
345
355
|
const { before, handler, after } = this.getHandlers(hook, defaultHandler);
|
|
346
|
-
await this.run(before, args);
|
|
347
|
-
const [result] = await this.run(handler, args);
|
|
348
|
-
await this.run(after, args);
|
|
349
|
-
this.dispatchDomEvent(hook, args);
|
|
356
|
+
await this.run(before, visit, args);
|
|
357
|
+
const [result] = await this.run(handler, visit, args);
|
|
358
|
+
await this.run(after, visit, args);
|
|
359
|
+
this.dispatchDomEvent(hook, visit, args);
|
|
350
360
|
return result;
|
|
351
361
|
}
|
|
352
362
|
|
|
@@ -354,23 +364,51 @@ export class Hooks {
|
|
|
354
364
|
* Trigger a hook synchronously, executing its default handler and all registered handlers.
|
|
355
365
|
* Will execute all handlers in order, but will **not** `await` any `Promise`s they return.
|
|
356
366
|
* @param hook Name of the hook to trigger
|
|
367
|
+
* @param visit The visit object this hook belongs to
|
|
357
368
|
* @param args Arguments to pass to the handler
|
|
358
369
|
* @param defaultHandler A default implementation of this hook to execute
|
|
359
370
|
* @returns The (possibly unresolved) return value of the executed default handler
|
|
360
371
|
*/
|
|
372
|
+
// Overload: default order of arguments
|
|
373
|
+
callSync<T extends HookName>(hook: T, visit: Visit | undefined, args: HookArguments<T>, defaultHandler?: HookDefaultHandler<T>): ReturnType<HookDefaultHandler<T>>; // prettier-ignore
|
|
374
|
+
// Overload: legacy order of arguments, with visit missing
|
|
375
|
+
callSync<T extends HookName>(hook: T, args: HookArguments<T>, defaultHandler?: HookDefaultHandler<T>): ReturnType<HookDefaultHandler<T>>; // prettier-ignore
|
|
376
|
+
// Implementation
|
|
361
377
|
callSync<T extends HookName>(
|
|
362
378
|
hook: T,
|
|
363
|
-
|
|
364
|
-
|
|
379
|
+
arg1: Visit | HookArguments<T>,
|
|
380
|
+
arg2: HookArguments<T> | HookDefaultHandler<T>,
|
|
381
|
+
arg3?: HookDefaultHandler<T>
|
|
365
382
|
): ReturnType<HookDefaultHandler<T>> {
|
|
383
|
+
const [visit, args, defaultHandler] = this.parseCallArgs(hook, arg1, arg2, arg3);
|
|
366
384
|
const { before, handler, after } = this.getHandlers(hook, defaultHandler);
|
|
367
|
-
this.runSync(before, args);
|
|
368
|
-
const [result] = this.runSync(handler, args);
|
|
369
|
-
this.runSync(after, args);
|
|
370
|
-
this.dispatchDomEvent(hook, args);
|
|
385
|
+
this.runSync(before, visit, args);
|
|
386
|
+
const [result] = this.runSync(handler, visit, args);
|
|
387
|
+
this.runSync(after, visit, args);
|
|
388
|
+
this.dispatchDomEvent(hook, visit, args);
|
|
371
389
|
return result;
|
|
372
390
|
}
|
|
373
391
|
|
|
392
|
+
/**
|
|
393
|
+
* Parse the call arguments for call() and callSync() to allow legacy argument order.
|
|
394
|
+
*/
|
|
395
|
+
protected parseCallArgs<T extends HookName>(
|
|
396
|
+
hook: T,
|
|
397
|
+
arg1: Visit | HookArguments<T> | undefined,
|
|
398
|
+
arg2: HookArguments<T> | HookDefaultHandler<T>,
|
|
399
|
+
arg3?: HookDefaultHandler<T>
|
|
400
|
+
): [Visit | undefined, HookArguments<T>, HookDefaultHandler<T> | undefined] {
|
|
401
|
+
const isLegacyOrder =
|
|
402
|
+
!(arg1 instanceof Visit) && (typeof arg1 === 'object' || typeof arg2 === 'function');
|
|
403
|
+
if (isLegacyOrder) {
|
|
404
|
+
// Legacy positioning: arguments in second or handler passed in third place
|
|
405
|
+
return [undefined, arg1 as HookArguments<T>, arg2 as HookDefaultHandler<T>];
|
|
406
|
+
} else {
|
|
407
|
+
// Default positioning: visit passed in as first argument
|
|
408
|
+
return [arg1, arg2 as HookArguments<T>, arg3];
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
|
|
374
412
|
/**
|
|
375
413
|
* Execute the handlers for a hook, in order, as `Promise`s that will be `await`ed.
|
|
376
414
|
* @param registrations The registrations (handler + options) to execute
|
|
@@ -378,21 +416,25 @@ export class Hooks {
|
|
|
378
416
|
*/
|
|
379
417
|
|
|
380
418
|
// Overload: running HookDefaultHandler: expect HookDefaultHandler return type
|
|
381
|
-
protected async run<T extends HookName>(registrations: HookRegistration<T, HookDefaultHandler<T>>[], args: HookArguments<T>): Promise<Awaited<ReturnType<HookDefaultHandler<T>>>[]>; // prettier-ignore
|
|
419
|
+
protected async run<T extends HookName>(registrations: HookRegistration<T, HookDefaultHandler<T>>[], visit: Visit | undefined, args: HookArguments<T>): Promise<Awaited<ReturnType<HookDefaultHandler<T>>>[]>; // prettier-ignore
|
|
382
420
|
// Overload: running user handler: expect no specific type
|
|
383
|
-
protected async run<T extends HookName>(registrations: HookRegistration<T>[], args: HookArguments<T>): Promise<unknown[]>; // prettier-ignore
|
|
421
|
+
protected async run<T extends HookName>(registrations: HookRegistration<T>[], visit: Visit | undefined, args: HookArguments<T>): Promise<unknown[]>; // prettier-ignore
|
|
384
422
|
// Implementation
|
|
385
423
|
protected async run<T extends HookName, R extends HookRegistration<T>[]>(
|
|
386
424
|
registrations: R,
|
|
425
|
+
visit: Visit | undefined,
|
|
387
426
|
args: HookArguments<T>
|
|
388
427
|
): Promise<Awaited<ReturnType<HookDefaultHandler<T>>> | unknown[]> {
|
|
389
428
|
const results = [];
|
|
390
429
|
for (const { hook, handler, defaultHandler, once } of registrations) {
|
|
391
|
-
|
|
430
|
+
if (visit?.done) continue;
|
|
431
|
+
if (once) this.off(hook, handler);
|
|
432
|
+
const result = await runAsPromise(handler, [
|
|
433
|
+
visit || this.swup.visit,
|
|
434
|
+
args,
|
|
435
|
+
defaultHandler
|
|
436
|
+
]);
|
|
392
437
|
results.push(result);
|
|
393
|
-
if (once) {
|
|
394
|
-
this.off(hook, handler);
|
|
395
|
-
}
|
|
396
438
|
}
|
|
397
439
|
return results;
|
|
398
440
|
}
|
|
@@ -404,17 +446,20 @@ export class Hooks {
|
|
|
404
446
|
*/
|
|
405
447
|
|
|
406
448
|
// Overload: running HookDefaultHandler: expect HookDefaultHandler return type
|
|
407
|
-
protected runSync<T extends HookName>(registrations: HookRegistration<T, HookDefaultHandler<T>>[], args: HookArguments<T> ): ReturnType<HookDefaultHandler<T>>[]; // prettier-ignore
|
|
449
|
+
protected runSync<T extends HookName>(registrations: HookRegistration<T, HookDefaultHandler<T>>[], visit: Visit | undefined, args: HookArguments<T> ): ReturnType<HookDefaultHandler<T>>[]; // prettier-ignore
|
|
408
450
|
// Overload: running user handler: expect no specific type
|
|
409
|
-
protected runSync<T extends HookName>(registrations: HookRegistration<T>[], args: HookArguments<T>): unknown[]; // prettier-ignore
|
|
451
|
+
protected runSync<T extends HookName>(registrations: HookRegistration<T>[], visit: Visit | undefined, args: HookArguments<T>): unknown[]; // prettier-ignore
|
|
410
452
|
// Implementation
|
|
411
453
|
protected runSync<T extends HookName, R extends HookRegistration<T>[]>(
|
|
412
454
|
registrations: R,
|
|
455
|
+
visit: Visit | undefined,
|
|
413
456
|
args: HookArguments<T>
|
|
414
457
|
): (ReturnType<HookDefaultHandler<T>> | unknown)[] {
|
|
415
458
|
const results = [];
|
|
416
459
|
for (const { hook, handler, defaultHandler, once } of registrations) {
|
|
417
|
-
|
|
460
|
+
if (visit?.done) continue;
|
|
461
|
+
if (once) this.off(hook, handler);
|
|
462
|
+
const result = (handler as HookDefaultHandler<T>)(visit || this.swup.visit, args, defaultHandler); // prettier-ignore
|
|
418
463
|
results.push(result);
|
|
419
464
|
if (isPromise(result)) {
|
|
420
465
|
console.warn(
|
|
@@ -422,9 +467,6 @@ export class Hooks {
|
|
|
422
467
|
`Swup will not wait for it to resolve.`
|
|
423
468
|
);
|
|
424
469
|
}
|
|
425
|
-
if (once) {
|
|
426
|
-
this.off(hook, handler);
|
|
427
|
-
}
|
|
428
470
|
}
|
|
429
471
|
return results;
|
|
430
472
|
}
|
|
@@ -500,8 +542,14 @@ export class Hooks {
|
|
|
500
542
|
* Dispatch a custom event on the `document` for a hook. Prefixed with `swup:`
|
|
501
543
|
* @param hook Name of the hook.
|
|
502
544
|
*/
|
|
503
|
-
protected dispatchDomEvent<T extends HookName>(
|
|
504
|
-
|
|
545
|
+
protected dispatchDomEvent<T extends HookName>(
|
|
546
|
+
hook: T,
|
|
547
|
+
visit: Visit | undefined,
|
|
548
|
+
args?: HookArguments<T>
|
|
549
|
+
): void {
|
|
550
|
+
if (visit?.done) return;
|
|
551
|
+
|
|
552
|
+
const detail: HookEventDetail = { hook, args, visit: visit || this.swup.visit };
|
|
505
553
|
document.dispatchEvent(
|
|
506
554
|
new CustomEvent<HookEventDetail>(`swup:any`, { detail, bubbles: true })
|
|
507
555
|
);
|
package/src/modules/Visit.ts
CHANGED
|
@@ -2,27 +2,8 @@ import type Swup from '../Swup.js';
|
|
|
2
2
|
import type { Options } from '../Swup.js';
|
|
3
3
|
import type { HistoryAction, HistoryDirection } from './navigate.js';
|
|
4
4
|
|
|
5
|
-
/**
|
|
6
|
-
export interface Visit {
|
|
7
|
-
/** A unique ID to identify this visit */
|
|
8
|
-
id: number;
|
|
9
|
-
/** The previous page, about to leave */
|
|
10
|
-
from: VisitFrom;
|
|
11
|
-
/** The next page, about to enter */
|
|
12
|
-
to: VisitTo;
|
|
13
|
-
/** The content containers, about to be replaced */
|
|
14
|
-
containers: Options['containers'];
|
|
15
|
-
/** Information about animated page transitions */
|
|
16
|
-
animation: VisitAnimation;
|
|
17
|
-
/** What triggered this visit */
|
|
18
|
-
trigger: VisitTrigger;
|
|
19
|
-
/** Cache behavior for this visit */
|
|
20
|
-
cache: VisitCache;
|
|
21
|
-
/** Browser history behavior on this visit */
|
|
22
|
-
history: VisitHistory;
|
|
23
|
-
/** Scroll behavior on this visit */
|
|
24
|
-
scroll: VisitScroll;
|
|
25
|
-
}
|
|
5
|
+
/** See below for the class Visit {} definition */
|
|
6
|
+
// export interface Visit {}
|
|
26
7
|
|
|
27
8
|
export interface VisitFrom {
|
|
28
9
|
/** The URL of the previous page */
|
|
@@ -45,6 +26,8 @@ export interface VisitAnimation {
|
|
|
45
26
|
wait: boolean;
|
|
46
27
|
/** Name of a custom animation to run. */
|
|
47
28
|
name?: string;
|
|
29
|
+
/** Whether this animation uses the native browser ViewTransition API. Default: `false` */
|
|
30
|
+
native: boolean;
|
|
48
31
|
/** Elements on which to add animation classes. Default: `html` element */
|
|
49
32
|
scope: 'html' | 'containers' | string[];
|
|
50
33
|
/** Selector for detecting animation timing. Default: `[class*="transition-"]` */
|
|
@@ -89,39 +72,95 @@ export interface VisitInitOptions {
|
|
|
89
72
|
event?: Event;
|
|
90
73
|
}
|
|
91
74
|
|
|
92
|
-
/**
|
|
93
|
-
export
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
75
|
+
/** @internal */
|
|
76
|
+
export const VisitState = {
|
|
77
|
+
CREATED: 1,
|
|
78
|
+
QUEUED: 2,
|
|
79
|
+
STARTED: 3,
|
|
80
|
+
LEAVING: 4,
|
|
81
|
+
LOADED: 5,
|
|
82
|
+
ENTERING: 6,
|
|
83
|
+
COMPLETED: 7,
|
|
84
|
+
ABORTED: 8,
|
|
85
|
+
FAILED: 9
|
|
86
|
+
} as const;
|
|
87
|
+
|
|
88
|
+
/** @internal */
|
|
89
|
+
export type VisitState = (typeof VisitState)[keyof typeof VisitState];
|
|
90
|
+
|
|
91
|
+
/** An object holding details about the current visit. */
|
|
92
|
+
export class Visit {
|
|
93
|
+
/** A unique ID to identify this visit */
|
|
94
|
+
id: number;
|
|
95
|
+
/** The current state of this visit @internal */
|
|
96
|
+
state: VisitState;
|
|
97
|
+
/** The previous page, about to leave */
|
|
98
|
+
from: VisitFrom;
|
|
99
|
+
/** The next page, about to enter */
|
|
100
|
+
to: VisitTo;
|
|
101
|
+
/** The content containers, about to be replaced */
|
|
102
|
+
containers: Options['containers'];
|
|
103
|
+
/** Information about animated page transitions */
|
|
104
|
+
animation: VisitAnimation;
|
|
105
|
+
/** What triggered this visit */
|
|
106
|
+
trigger: VisitTrigger;
|
|
107
|
+
/** Cache behavior for this visit */
|
|
108
|
+
cache: VisitCache;
|
|
109
|
+
/** Browser history behavior on this visit */
|
|
110
|
+
history: VisitHistory;
|
|
111
|
+
/** Scroll behavior on this visit */
|
|
112
|
+
scroll: VisitScroll;
|
|
113
|
+
|
|
114
|
+
constructor(swup: Swup, options: VisitInitOptions) {
|
|
115
|
+
const { to, from = swup.currentPageUrl, hash, el, event } = options;
|
|
116
|
+
|
|
117
|
+
this.id = Math.random();
|
|
118
|
+
this.state = VisitState.CREATED;
|
|
119
|
+
this.from = { url: from };
|
|
120
|
+
this.to = { url: to, hash };
|
|
121
|
+
this.containers = swup.options.containers;
|
|
122
|
+
this.animation = {
|
|
103
123
|
animate: true,
|
|
104
124
|
wait: false,
|
|
105
125
|
name: undefined,
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
},
|
|
117
|
-
history: {
|
|
126
|
+
native: swup.options.native,
|
|
127
|
+
scope: swup.options.animationScope,
|
|
128
|
+
selector: swup.options.animationSelector
|
|
129
|
+
};
|
|
130
|
+
this.trigger = { el, event };
|
|
131
|
+
this.cache = {
|
|
132
|
+
read: swup.options.cache,
|
|
133
|
+
write: swup.options.cache
|
|
134
|
+
};
|
|
135
|
+
this.history = {
|
|
118
136
|
action: 'push',
|
|
119
137
|
popstate: false,
|
|
120
138
|
direction: undefined
|
|
121
|
-
}
|
|
122
|
-
scroll
|
|
139
|
+
};
|
|
140
|
+
this.scroll = {
|
|
123
141
|
reset: true,
|
|
124
142
|
target: undefined
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
/** @internal */
|
|
146
|
+
advance(state: VisitState) {
|
|
147
|
+
if (this.state < state) {
|
|
148
|
+
this.state = state;
|
|
125
149
|
}
|
|
126
|
-
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/** @internal */
|
|
153
|
+
abort() {
|
|
154
|
+
this.state = VisitState.ABORTED;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/** Is this visit done, i.e. completed, failed, or aborted? */
|
|
158
|
+
get done(): boolean {
|
|
159
|
+
return this.state >= VisitState.COMPLETED;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/** Create a new visit object. */
|
|
164
|
+
export function createVisit(this: Swup, options: VisitInitOptions): Visit {
|
|
165
|
+
return new Visit(this, options);
|
|
127
166
|
}
|
|
@@ -1,31 +1,32 @@
|
|
|
1
1
|
import type Swup from '../Swup.js';
|
|
2
2
|
import { nextTick } from '../utils.js';
|
|
3
|
+
import type { Visit } from './Visit.js';
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* Perform the in/enter animation of the next page.
|
|
6
7
|
* @returns Promise<void>
|
|
7
8
|
*/
|
|
8
|
-
export const animatePageIn = async function (this: Swup) {
|
|
9
|
-
if
|
|
10
|
-
|
|
11
|
-
}
|
|
9
|
+
export const animatePageIn = async function (this: Swup, visit: Visit) {
|
|
10
|
+
// Check if failed/aborted in the meantime
|
|
11
|
+
if (visit.done) return;
|
|
12
12
|
|
|
13
13
|
const animation = this.hooks.call(
|
|
14
14
|
'animation:in:await',
|
|
15
|
+
visit,
|
|
15
16
|
{ skip: false },
|
|
16
|
-
|
|
17
|
+
(visit, { skip }) => {
|
|
17
18
|
if (skip) return;
|
|
18
|
-
|
|
19
|
+
return this.awaitAnimations({ selector: visit.animation.selector });
|
|
19
20
|
}
|
|
20
21
|
);
|
|
21
22
|
|
|
22
23
|
await nextTick();
|
|
23
24
|
|
|
24
|
-
await this.hooks.call('animation:in:start', undefined, () => {
|
|
25
|
+
await this.hooks.call('animation:in:start', visit, undefined, () => {
|
|
25
26
|
this.classes.remove('is-animating');
|
|
26
27
|
});
|
|
27
28
|
|
|
28
29
|
await animation;
|
|
29
30
|
|
|
30
|
-
await this.hooks.call('animation:in:end', undefined);
|
|
31
|
+
await this.hooks.call('animation:in:end', visit, undefined);
|
|
31
32
|
};
|
|
@@ -1,30 +1,19 @@
|
|
|
1
1
|
import type Swup from '../Swup.js';
|
|
2
|
-
import {
|
|
2
|
+
import type { Visit } from './Visit.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Perform the out/leave animation of the current page.
|
|
6
6
|
* @returns Promise<void>
|
|
7
7
|
*/
|
|
8
|
-
export const animatePageOut = async function (this: Swup) {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
return;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
await this.hooks.call('animation:out:start', undefined, (visit) => {
|
|
15
|
-
this.classes.add('is-changing', 'is-leaving', 'is-animating');
|
|
16
|
-
if (visit.history.popstate) {
|
|
17
|
-
this.classes.add('is-popstate');
|
|
18
|
-
}
|
|
19
|
-
if (visit.animation.name) {
|
|
20
|
-
this.classes.add(`to-${classify(visit.animation.name)}`);
|
|
21
|
-
}
|
|
8
|
+
export const animatePageOut = async function (this: Swup, visit: Visit) {
|
|
9
|
+
await this.hooks.call('animation:out:start', visit, undefined, () => {
|
|
10
|
+
this.classes.add('is-changing', 'is-animating', 'is-leaving');
|
|
22
11
|
});
|
|
23
12
|
|
|
24
|
-
await this.hooks.call('animation:out:await', { skip: false },
|
|
13
|
+
await this.hooks.call('animation:out:await', visit, { skip: false }, (visit, { skip }) => {
|
|
25
14
|
if (skip) return;
|
|
26
|
-
|
|
15
|
+
return this.awaitAnimations({ selector: visit.animation.selector });
|
|
27
16
|
});
|
|
28
17
|
|
|
29
|
-
await this.hooks.call('animation:out:end', undefined);
|
|
18
|
+
await this.hooks.call('animation:out:end', visit, undefined);
|
|
30
19
|
};
|
package/src/modules/fetchPage.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type Swup from '../Swup.js';
|
|
2
2
|
import { Location } from '../helpers.js';
|
|
3
|
+
import type { Visit } from './Visit.js';
|
|
3
4
|
|
|
4
5
|
/** A page object as used by swup and its cache. */
|
|
5
6
|
export interface PageData {
|
|
@@ -17,6 +18,8 @@ export interface FetchOptions extends Omit<RequestInit, 'cache'> {
|
|
|
17
18
|
body?: string | FormData | URLSearchParams;
|
|
18
19
|
/** The request timeout in milliseconds. */
|
|
19
20
|
timeout?: number;
|
|
21
|
+
/** Optional visit object with additional context. @internal */
|
|
22
|
+
visit?: Visit;
|
|
20
23
|
}
|
|
21
24
|
|
|
22
25
|
export class FetchError extends Error {
|
|
@@ -47,6 +50,7 @@ export async function fetchPage(
|
|
|
47
50
|
): Promise<PageData> {
|
|
48
51
|
url = Location.fromUrl(url).url;
|
|
49
52
|
|
|
53
|
+
const { visit = this.visit } = options;
|
|
50
54
|
const headers = { ...this.options.requestHeaders, ...options.headers };
|
|
51
55
|
const timeout = options.timeout ?? this.options.timeout;
|
|
52
56
|
const controller = new AbortController();
|
|
@@ -67,6 +71,7 @@ export async function fetchPage(
|
|
|
67
71
|
try {
|
|
68
72
|
response = await this.hooks.call(
|
|
69
73
|
'fetch:request',
|
|
74
|
+
visit,
|
|
70
75
|
{ url, options },
|
|
71
76
|
(visit, { url, options }) => fetch(url, options)
|
|
72
77
|
);
|
|
@@ -75,14 +80,11 @@ export async function fetchPage(
|
|
|
75
80
|
}
|
|
76
81
|
} catch (error) {
|
|
77
82
|
if (timedOut) {
|
|
78
|
-
this.hooks.call('fetch:timeout', { url });
|
|
83
|
+
this.hooks.call('fetch:timeout', visit, { url });
|
|
79
84
|
throw new FetchError(`Request timed out: ${url}`, { url, timedOut });
|
|
80
85
|
}
|
|
81
86
|
if ((error as Error)?.name === 'AbortError' || signal.aborted) {
|
|
82
|
-
throw new FetchError(`Request aborted: ${url}`, {
|
|
83
|
-
url: url,
|
|
84
|
-
aborted: true
|
|
85
|
-
});
|
|
87
|
+
throw new FetchError(`Request aborted: ${url}`, { url, aborted: true });
|
|
86
88
|
}
|
|
87
89
|
throw error;
|
|
88
90
|
}
|
|
@@ -91,7 +93,7 @@ export async function fetchPage(
|
|
|
91
93
|
const html = await response.text();
|
|
92
94
|
|
|
93
95
|
if (status === 500) {
|
|
94
|
-
this.hooks.call('fetch:error', { status, response, url: responseUrl });
|
|
96
|
+
this.hooks.call('fetch:error', visit, { status, response, url: responseUrl });
|
|
95
97
|
throw new FetchError(`Server error: ${responseUrl}`, { status, url: responseUrl });
|
|
96
98
|
}
|
|
97
99
|
|
|
@@ -104,11 +106,7 @@ export async function fetchPage(
|
|
|
104
106
|
const page = { url: finalUrl, html };
|
|
105
107
|
|
|
106
108
|
// Write to cache for safe methods and non-redirects
|
|
107
|
-
if (
|
|
108
|
-
this.visit.cache.write &&
|
|
109
|
-
(!options.method || options.method === 'GET') &&
|
|
110
|
-
url === finalUrl
|
|
111
|
-
) {
|
|
109
|
+
if (visit.cache.write && (!options.method || options.method === 'GET') && url === finalUrl) {
|
|
112
110
|
this.cache.set(page.url, page);
|
|
113
111
|
}
|
|
114
112
|
|