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.
- 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 +6 -5
- package/src/Swup.ts +43 -40
- package/src/helpers/Location.ts +1 -0
- package/src/helpers/getCurrentUrl.ts +1 -1
- 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 +98 -37
- 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 +83 -39
- package/src/modules/renderPage.ts +18 -18
- 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
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
|
|
package/src/modules/navigate.ts
CHANGED
|
@@ -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
|
|
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.
|
|
47
|
+
window.location.assign(url);
|
|
42
48
|
return;
|
|
43
49
|
}
|
|
44
50
|
|
|
45
51
|
const { url: to, hash } = Location.fromUrl(url);
|
|
46
|
-
|
|
47
|
-
this.
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
138
|
-
visit.to.html = html;
|
|
171
|
+
await page;
|
|
139
172
|
}
|
|
140
173
|
|
|
141
|
-
//
|
|
142
|
-
|
|
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
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
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
|
-
//
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
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
|
-
//
|
|
164
|
-
|
|
199
|
+
// Check if failed/aborted in the meantime
|
|
200
|
+
if (visit.done) return;
|
|
165
201
|
|
|
166
|
-
//
|
|
167
|
-
|
|
168
|
-
|
|
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
|
-
|
|
219
|
+
visit.state = VisitState.FAILED;
|
|
220
|
+
|
|
221
|
+
// Log to console
|
|
178
222
|
console.error(error);
|
|
179
223
|
|
|
180
|
-
//
|
|
224
|
+
// Remove current history entry, then load requested url in browser
|
|
181
225
|
this.options.skipPopStateHandling = () => {
|
|
182
|
-
window.location.
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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-
|
|
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
|
-
|
|
45
|
-
|
|
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
|
};
|
|
@@ -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 } =
|
|
10
|
-
const scrollTarget = target ??
|
|
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 +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
|
-
};
|