swup 4.4.4 → 4.5.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.
Files changed (55) 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 +6 -5
  34. package/src/Swup.ts +43 -40
  35. package/src/helpers/Location.ts +1 -0
  36. package/src/helpers/getCurrentUrl.ts +1 -1
  37. package/src/helpers/history.ts +37 -0
  38. package/src/helpers.ts +1 -2
  39. package/src/modules/Cache.ts +2 -2
  40. package/src/modules/Classes.ts +8 -1
  41. package/src/modules/Hooks.ts +98 -37
  42. package/src/modules/Visit.ts +86 -47
  43. package/src/modules/animatePageIn.ts +9 -8
  44. package/src/modules/animatePageOut.ts +7 -18
  45. package/src/modules/fetchPage.ts +9 -11
  46. package/src/modules/navigate.ts +83 -39
  47. package/src/modules/renderPage.ts +18 -18
  48. package/src/modules/replaceContent.ts +1 -0
  49. package/src/modules/scrollToContent.ts +6 -4
  50. package/dist/types/helpers/createHistoryRecord.d.ts +0 -10
  51. package/dist/types/helpers/createHistoryRecord.d.ts.map +0 -1
  52. package/dist/types/helpers/updateHistoryRecord.d.ts +0 -3
  53. package/dist/types/helpers/updateHistoryRecord.d.ts.map +0 -1
  54. package/src/helpers/createHistoryRecord.ts +0 -24
  55. package/src/helpers/updateHistoryRecord.ts +0 -19
@@ -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
 
@@ -1,7 +1,13 @@
1
1
  import type Swup from '../Swup.js';
2
- import { createHistoryRecord, updateHistoryRecord, getCurrentUrl, Location } from '../helpers.js';
3
2
  import { FetchError, type FetchOptions, type PageData } from './fetchPage.js';
4
- import type { VisitInitOptions } from './Visit.js';
3
+ import { type VisitInitOptions, type Visit, VisitState } from './Visit.js';
4
+ import {
5
+ createHistoryRecord,
6
+ updateHistoryRecord,
7
+ getCurrentUrl,
8
+ Location,
9
+ classify
10
+ } from '../helpers.js';
5
11
 
6
12
  export type HistoryAction = 'push' | 'replace';
7
13
  export type HistoryDirection = 'forwards' | 'backwards';
@@ -38,13 +44,14 @@ export function navigate(
38
44
 
39
45
  // Check if the visit should be ignored
40
46
  if (this.shouldIgnoreVisit(url, { el: init.el, event: init.event })) {
41
- window.location.href = url;
47
+ window.location.assign(url);
42
48
  return;
43
49
  }
44
50
 
45
51
  const { url: to, hash } = Location.fromUrl(url);
46
- this.visit = this.createVisit({ ...init, to, hash });
47
- this.performNavigation(options);
52
+
53
+ const visit = this.createVisit({ ...init, to, hash });
54
+ this.performNavigation(visit, options);
48
55
  }
49
56
 
50
57
  /**
@@ -60,12 +67,24 @@ export function navigate(
60
67
  */
61
68
  export async function performNavigation(
62
69
  this: Swup,
70
+ visit: Visit,
63
71
  options: NavigationOptions & FetchOptions = {}
64
72
  ): Promise<void> {
73
+ if (this.navigating) {
74
+ if (this.visit.state >= VisitState.ENTERING) {
75
+ // Currently navigating and content already loaded? Finish and queue
76
+ visit.state = VisitState.QUEUED;
77
+ this.onVisitEnd = () => this.performNavigation(visit, options);
78
+ return;
79
+ } else {
80
+ // Currently navigating and content not loaded? Abort running visit
81
+ await this.hooks.call('visit:abort', this.visit, undefined);
82
+ this.visit.state = VisitState.ABORTED;
83
+ }
84
+ }
85
+
65
86
  this.navigating = true;
66
- // Save this localy to a) allow ignoring the visit if a new one was started in the meantime
67
- // and b) avoid unintended modifications to any newer visits
68
- const visit = this.visit;
87
+ this.visit = visit;
69
88
 
70
89
  const { el } = visit.trigger;
71
90
  options.referrer = options.referrer || this.currentPageUrl;
@@ -102,10 +121,11 @@ export async function performNavigation(
102
121
  delete options.cache;
103
122
 
104
123
  try {
105
- await this.hooks.call('visit:start', undefined);
124
+ await this.hooks.call('visit:start', visit, undefined);
125
+ visit.state = VisitState.STARTED;
106
126
 
107
127
  // Begin loading page
108
- const pagePromise = this.hooks.call('page:load', { options }, async (visit, args) => {
128
+ const page = this.hooks.call('page:load', visit, { options }, async (visit, args) => {
109
129
  // Read from cache
110
130
  let cachedPage: PageData | undefined;
111
131
  if (visit.cache.read) {
@@ -118,6 +138,12 @@ export async function performNavigation(
118
138
  return args.page;
119
139
  });
120
140
 
141
+ // When page loaded: mark visit as loaded, save html into visit object
142
+ page.then(({ html }) => {
143
+ visit.advance(VisitState.LOADED);
144
+ visit.to.html = html;
145
+ });
146
+
121
147
  // Create/update history record if this is not a popstate call or leads to the same URL
122
148
  if (!visit.history.popstate) {
123
149
  // Add the hash directly from the trigger element
@@ -132,58 +158,76 @@ export async function performNavigation(
132
158
 
133
159
  this.currentPageUrl = getCurrentUrl();
134
160
 
161
+ // Mark visit type with classes
162
+ if (visit.history.popstate) {
163
+ this.classes.add('is-popstate');
164
+ }
165
+ if (visit.animation.name) {
166
+ this.classes.add(`to-${classify(visit.animation.name)}`);
167
+ }
168
+
135
169
  // Wait for page before starting to animate out?
136
170
  if (visit.animation.wait) {
137
- const { html } = await pagePromise;
138
- visit.to.html = html;
171
+ await page;
139
172
  }
140
173
 
141
- // perform the actual transition: animate and replace content
142
- await this.hooks.call('visit:transition', undefined, async (visit) => {
143
- // Start leave animation
144
- const animationPromise = this.animatePageOut();
174
+ // Check if failed/aborted in the meantime
175
+ if (visit.done) return;
145
176
 
146
- // Wait for page to load and leave animation to finish
147
- const [page] = await Promise.all([pagePromise, animationPromise]);
148
-
149
- // Abort if another visit was started in the meantime
150
- if (visit.id !== this.visit.id) {
151
- return false;
177
+ // Perform the actual transition: animate and replace content
178
+ await this.hooks.call('visit:transition', visit, undefined, async () => {
179
+ // No animation? Just await page and render
180
+ if (!visit.animation.animate) {
181
+ await this.hooks.call('animation:skip', undefined);
182
+ await this.renderPage(visit, await page);
183
+ return;
152
184
  }
153
185
 
154
- // Render page: replace content and scroll to top/fragment
155
- await this.renderPage(page);
156
-
157
- // Wait for enter animation
158
- await this.animatePageIn();
159
-
160
- return true;
186
+ // Animate page out, render page, animate page in
187
+ visit.advance(VisitState.LEAVING);
188
+ await this.animatePageOut(visit);
189
+ if (visit.animation.native && document.startViewTransition) {
190
+ await document.startViewTransition(
191
+ async () => await this.renderPage(visit, await page)
192
+ ).finished;
193
+ } else {
194
+ await this.renderPage(visit, await page);
195
+ }
196
+ await this.animatePageIn(visit);
161
197
  });
162
198
 
163
- // Finalize visit
164
- await this.hooks.call('visit:end', undefined, () => this.classes.clear());
199
+ // Check if failed/aborted in the meantime
200
+ if (visit.done) return;
165
201
 
166
- // Reset visit info after finish?
167
- // if (visit.to && this.isSameResolvedUrl(visit.to.url, requestedUrl)) {
168
- // this.visit = this.createVisit({ to: undefined });
169
- // }
202
+ // Finalize visit
203
+ await this.hooks.call('visit:end', visit, undefined, () => this.classes.clear());
204
+ visit.state = VisitState.COMPLETED;
170
205
  this.navigating = false;
206
+
207
+ /** Run eventually queued function */
208
+ if (this.onVisitEnd) {
209
+ this.onVisitEnd();
210
+ this.onVisitEnd = undefined;
211
+ }
171
212
  } catch (error) {
172
213
  // Return early if error is undefined or signals an aborted request
173
214
  if (!error || (error as FetchError)?.aborted) {
215
+ visit.state = VisitState.ABORTED;
174
216
  return;
175
217
  }
176
218
 
177
- // Log to console as we swallow almost all hook errors
219
+ visit.state = VisitState.FAILED;
220
+
221
+ // Log to console
178
222
  console.error(error);
179
223
 
180
- // Rewrite `skipPopStateHandling` to redirect manually when `history.go` is processed
224
+ // Remove current history entry, then load requested url in browser
181
225
  this.options.skipPopStateHandling = () => {
182
- window.location.href = visit.to.url + visit.to.hash;
226
+ window.location.assign(visit.to.url + visit.to.hash);
183
227
  return true;
184
228
  };
185
229
 
186
230
  // Go back to the actual page we're still at
187
- window.history.go(-1);
231
+ window.history.back();
188
232
  }
189
233
  }
@@ -1,39 +1,40 @@
1
1
  import { updateHistoryRecord, getCurrentUrl, classify } from '../helpers.js';
2
2
  import type Swup from '../Swup.js';
3
3
  import type { PageData } from './fetchPage.js';
4
+ import { VisitState, type Visit } from './Visit.js';
4
5
 
5
6
  /**
6
7
  * Render the next page: replace the content and update scroll position.
7
8
  */
8
- export const renderPage = async function (this: Swup, page: PageData): Promise<void> {
9
- const { url, html } = page;
9
+ export const renderPage = async function (this: Swup, visit: Visit, page: PageData): Promise<void> {
10
+ // Check if failed/aborted in the meantime
11
+ if (visit.done) return;
10
12
 
11
- this.classes.remove('is-leaving');
13
+ visit.advance(VisitState.ENTERING);
14
+
15
+ const { url } = page;
12
16
 
13
17
  // update state if the url was redirected
14
18
  if (!this.isSameResolvedUrl(getCurrentUrl(), url)) {
15
19
  updateHistoryRecord(url);
16
20
  this.currentPageUrl = getCurrentUrl();
17
- this.visit.to.url = this.currentPageUrl;
18
- }
19
-
20
- // only add for animated page loads
21
- if (this.visit.animation.animate) {
22
- this.classes.add('is-rendering');
21
+ visit.to.url = this.currentPageUrl;
23
22
  }
24
23
 
25
- // save html into visit context for easier retrieval
26
- this.visit.to.html = html;
27
-
28
24
  // replace content: allow handlers and plugins to overwrite paga data and containers
29
- await this.hooks.call('content:replace', { page }, (visit, { page }) => {
25
+ await this.hooks.call('content:replace', visit, { page }, (visit, { page }) => {
26
+ this.classes.remove('is-leaving');
27
+ // only add for animated page loads
28
+ if (visit.animation.animate) {
29
+ this.classes.add('is-rendering');
30
+ }
30
31
  const success = this.replaceContent(page, { containers: visit.containers });
31
32
  if (!success) {
32
33
  throw new Error('[swup] Container mismatch, aborting');
33
34
  }
34
35
  if (visit.animation.animate) {
35
36
  // Make sure to add these classes to new containers as well
36
- this.classes.add('is-animating', 'is-changing', 'is-rendering');
37
+ this.classes.add('is-changing', 'is-animating', 'is-rendering');
37
38
  if (visit.animation.name) {
38
39
  this.classes.add(`to-${classify(visit.animation.name)}`);
39
40
  }
@@ -41,10 +42,9 @@ export const renderPage = async function (this: Swup, page: PageData): Promise<v
41
42
  });
42
43
 
43
44
  // scroll into view: either anchor or top of page
44
- // @ts-ignore: not returning a promise is intentional to allow users to pause in handler
45
- await this.hooks.call('content:scroll', undefined, () => {
46
- return this.scrollToContent();
45
+ await this.hooks.call('content:scroll', visit, undefined, () => {
46
+ return this.scrollToContent(visit);
47
47
  });
48
48
 
49
- await this.hooks.call('page:view', { url: this.currentPageUrl, title: document.title });
49
+ await this.hooks.call('page:view', visit, { url: this.currentPageUrl, title: document.title });
50
50
  };
@@ -53,5 +53,6 @@ export const replaceContent = function (
53
53
  }
54
54
  });
55
55
 
56
+ // Return true if all containers were replaced
56
57
  return replaced.length === containers.length;
57
58
  };
@@ -1,19 +1,21 @@
1
1
  import type Swup from '../Swup.js';
2
+ import type { Visit } from './Visit.js';
2
3
 
3
4
  /**
4
5
  * Update the scroll position after page render.
5
6
  * @returns Promise<boolean>
6
7
  */
7
- export const scrollToContent = function (this: Swup): boolean {
8
+ export const scrollToContent = function (this: Swup, visit: Visit): boolean {
8
9
  const options: ScrollIntoViewOptions = { behavior: 'auto' };
9
- const { target, reset } = this.visit.scroll;
10
- const scrollTarget = target ?? this.visit.to.hash;
10
+ const { target, reset } = visit.scroll;
11
+ const scrollTarget = target ?? visit.to.hash;
11
12
 
12
13
  let scrolled = false;
13
14
 
14
15
  if (scrollTarget) {
15
16
  scrolled = this.hooks.callSync(
16
17
  'scroll:anchor',
18
+ visit,
17
19
  { hash: scrollTarget, options },
18
20
  (visit, { hash, options }) => {
19
21
  const anchor = this.getAnchorElement(hash);
@@ -26,7 +28,7 @@ export const scrollToContent = function (this: Swup): boolean {
26
28
  }
27
29
 
28
30
  if (reset && !scrolled) {
29
- scrolled = this.hooks.callSync('scroll:top', { options }, (visit, { options }) => {
31
+ scrolled = this.hooks.callSync('scroll:top', visit, { options }, (visit, { options }) => {
30
32
  window.scrollTo({ top: 0, left: 0, ...options });
31
33
  return true;
32
34
  });
@@ -1,10 +0,0 @@
1
- export interface HistoryState {
2
- url: string;
3
- source: 'swup';
4
- random: number;
5
- index?: number;
6
- [key: string]: unknown;
7
- }
8
- /** Create a new history record with a custom swup identifier. */
9
- export declare const createHistoryRecord: (url: string, customData?: Record<string, unknown>) => void;
10
- //# sourceMappingURL=createHistoryRecord.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"createHistoryRecord.d.ts","sourceRoot":"","sources":["../../../src/helpers/createHistoryRecord.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,YAAY;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACvB;AAED,iEAAiE;AACjE,eAAO,MAAM,mBAAmB,QAC1B,MAAM,eACC,OAAO,MAAM,EAAE,OAAO,CAAC,KACjC,IASF,CAAC"}
@@ -1,3 +0,0 @@
1
- /** Update the current history record with a custom swup identifier. */
2
- export declare const updateHistoryRecord: (url?: string | null, customData?: Record<string, unknown>) => void;
3
- //# sourceMappingURL=updateHistoryRecord.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"updateHistoryRecord.d.ts","sourceRoot":"","sources":["../../../src/helpers/updateHistoryRecord.ts"],"names":[],"mappings":"AAGA,uEAAuE;AACvE,eAAO,MAAM,mBAAmB,SAC1B,MAAM,GAAG,IAAI,eACN,OAAO,MAAM,EAAE,OAAO,CAAC,KACjC,IAWF,CAAC"}
@@ -1,24 +0,0 @@
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
- /** Create a new history record with a custom swup identifier. */
12
- export const createHistoryRecord = (
13
- url: string,
14
- customData: Record<string, unknown> = {}
15
- ): void => {
16
- url = url || getCurrentUrl({ hash: true });
17
- const data: HistoryState = {
18
- url,
19
- random: Math.random(),
20
- source: 'swup',
21
- ...customData
22
- };
23
- history.pushState(data, '', url);
24
- };