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.
Files changed (54) hide show
  1. package/dist/Swup.cjs +1 -1
  2. package/dist/Swup.cjs.map +1 -1
  3. package/dist/Swup.modern.js +1 -1
  4. package/dist/Swup.modern.js.map +1 -1
  5. package/dist/Swup.module.js +1 -1
  6. package/dist/Swup.module.js.map +1 -1
  7. package/dist/Swup.umd.js +1 -1
  8. package/dist/Swup.umd.js.map +1 -1
  9. package/dist/types/Swup.d.ts +8 -4
  10. package/dist/types/Swup.d.ts.map +1 -1
  11. package/dist/types/helpers/Location.d.ts.map +1 -1
  12. package/dist/types/helpers/history.d.ts +14 -0
  13. package/dist/types/helpers/history.d.ts.map +1 -0
  14. package/dist/types/helpers.d.ts +1 -2
  15. package/dist/types/helpers.d.ts.map +1 -1
  16. package/dist/types/modules/Classes.d.ts.map +1 -1
  17. package/dist/types/modules/Hooks.d.ts +16 -8
  18. package/dist/types/modules/Hooks.d.ts.map +1 -1
  19. package/dist/types/modules/Visit.d.ts +28 -22
  20. package/dist/types/modules/Visit.d.ts.map +1 -1
  21. package/dist/types/modules/animatePageIn.d.ts +2 -1
  22. package/dist/types/modules/animatePageIn.d.ts.map +1 -1
  23. package/dist/types/modules/animatePageOut.d.ts +2 -1
  24. package/dist/types/modules/animatePageOut.d.ts.map +1 -1
  25. package/dist/types/modules/fetchPage.d.ts.map +1 -1
  26. package/dist/types/modules/navigate.d.ts +2 -2
  27. package/dist/types/modules/navigate.d.ts.map +1 -1
  28. package/dist/types/modules/renderPage.d.ts +2 -1
  29. package/dist/types/modules/renderPage.d.ts.map +1 -1
  30. package/dist/types/modules/replaceContent.d.ts.map +1 -1
  31. package/dist/types/modules/scrollToContent.d.ts +2 -1
  32. package/dist/types/modules/scrollToContent.d.ts.map +1 -1
  33. package/package.json +2 -1
  34. package/src/Swup.ts +40 -37
  35. package/src/helpers/Location.ts +1 -0
  36. package/src/helpers/history.ts +37 -0
  37. package/src/helpers.ts +1 -2
  38. package/src/modules/Cache.ts +2 -2
  39. package/src/modules/Classes.ts +8 -1
  40. package/src/modules/Hooks.ts +77 -29
  41. package/src/modules/Visit.ts +86 -47
  42. package/src/modules/animatePageIn.ts +9 -8
  43. package/src/modules/animatePageOut.ts +7 -18
  44. package/src/modules/fetchPage.ts +9 -11
  45. package/src/modules/navigate.ts +78 -34
  46. package/src/modules/renderPage.ts +15 -13
  47. package/src/modules/replaceContent.ts +1 -0
  48. package/src/modules/scrollToContent.ts +6 -4
  49. package/dist/types/helpers/createHistoryRecord.d.ts +0 -10
  50. package/dist/types/helpers/createHistoryRecord.d.ts.map +0 -1
  51. package/dist/types/helpers/updateHistoryRecord.d.ts +0 -3
  52. package/dist/types/helpers/updateHistoryRecord.d.ts.map +0 -1
  53. package/src/helpers/createHistoryRecord.ts +0 -24
  54. 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/createHistoryRecord.js';
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';
@@ -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. */
@@ -3,7 +3,14 @@ import { queryAll } from '../utils.js';
3
3
 
4
4
  export class Classes {
5
5
  protected swup: Swup;
6
- protected swupClasses = ['to-', 'is-changing', 'is-rendering', 'is-popstate', 'is-animating'];
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;
@@ -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 type { Visit } from './Visit.js';
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
- args: HookArguments<T>,
343
- defaultHandler?: HookDefaultHandler<T>
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
- args: HookArguments<T>,
364
- defaultHandler?: HookDefaultHandler<T>
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
- const result = await runAsPromise(handler, [this.swup.visit, args, defaultHandler]);
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
- const result = (handler as HookDefaultHandler<T>)(this.swup.visit, args, defaultHandler); // prettier-ignore
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>(hook: T, args?: HookArguments<T>): void {
504
- const detail: HookEventDetail = { hook, args, visit: this.swup.visit };
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
  );
@@ -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
- /** An object holding details about the current visit. */
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
- /** Create a new visit object. */
93
- export function createVisit(
94
- this: Swup,
95
- { to, from = this.currentPageUrl, hash, el, event }: VisitInitOptions
96
- ): Visit {
97
- return {
98
- id: Math.random(),
99
- from: { url: from },
100
- to: { url: to, hash },
101
- containers: this.options.containers,
102
- animation: {
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
- scope: this.options.animationScope,
107
- selector: this.options.animationSelector
108
- },
109
- trigger: {
110
- el,
111
- event
112
- },
113
- cache: {
114
- read: this.options.cache,
115
- write: this.options.cache
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 (!this.visit.animation.animate) {
10
- return;
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
- async (visit, { skip }) => {
17
+ (visit, { skip }) => {
17
18
  if (skip) return;
18
- await this.awaitAnimations({ selector: visit.animation.selector });
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 { classify } from '../helpers.js';
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
- if (!this.visit.animation.animate) {
10
- await this.hooks.call('animation:skip', undefined);
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 }, async (visit, { skip }) => {
13
+ await this.hooks.call('animation:out:await', visit, { skip: false }, (visit, { skip }) => {
25
14
  if (skip) return;
26
- await this.awaitAnimations({ selector: visit.animation.selector });
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
  };
@@ -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