swup 3.1.0 → 4.0.0-rc.14
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 +20 -20
- package/dist/types/helpers/delegateEvent.d.ts +4 -2
- package/dist/types/helpers/fetch.d.ts +2 -2
- package/dist/types/helpers.d.ts +10 -10
- package/dist/types/index.d.ts +5 -5
- package/dist/types/modules/Cache.d.ts +2 -2
- package/dist/types/modules/__test__/fetchPage.test.d.ts +1 -0
- package/dist/types/modules/destroy.d.ts +2 -0
- package/dist/types/modules/enable.d.ts +2 -0
- package/dist/types/modules/enterPage.d.ts +2 -2
- package/dist/types/modules/events.d.ts +6 -6
- package/dist/types/modules/fetchPage.d.ts +3 -3
- package/dist/types/modules/getAnchorElement.d.ts +0 -7
- package/dist/types/modules/getAnimationPromises.d.ts +1 -1
- package/dist/types/modules/getPageData.d.ts +2 -2
- package/dist/types/modules/handleLinkToSamePage.d.ts +2 -0
- package/dist/types/modules/isSameResolvedUrl.d.ts +8 -0
- package/dist/types/modules/leavePage.d.ts +2 -2
- package/dist/types/modules/linkClickHandler.d.ts +3 -0
- package/dist/types/modules/loadPage.d.ts +2 -5
- package/dist/types/modules/off.d.ts +3 -0
- package/dist/types/modules/on.d.ts +5 -0
- package/dist/types/modules/plugins.d.ts +1 -1
- package/dist/types/modules/popStateHandler.d.ts +2 -0
- package/dist/types/modules/renderPage.d.ts +2 -2
- package/dist/types/modules/resolveUrl.d.ts +7 -0
- package/dist/types/modules/shouldIgnoreVisit.d.ts +4 -0
- package/dist/types/modules/transitions.d.ts +1 -1
- package/dist/types/modules/triggerEvent.d.ts +3 -0
- package/dist/types/modules/triggerWillOpenNewWindow.d.ts +2 -0
- package/dist/types/modules/updateTransition.d.ts +2 -0
- package/dist/types/utils.d.ts +1 -1
- package/package.json +8 -5
- package/readme.md +30 -12
- package/src/Swup.ts +115 -143
- package/src/__test__/index.test.ts +6 -1
- package/src/helpers/Location.ts +10 -7
- package/src/helpers/__test__/matchPath.test.ts +54 -0
- package/src/helpers/delegateEvent.ts +1 -0
- package/src/helpers/matchPath.ts +22 -0
- package/src/helpers.ts +2 -4
- package/src/index.ts +7 -4
- package/src/modules/Cache.ts +43 -33
- package/src/modules/Context.ts +91 -0
- package/src/modules/Hooks.ts +393 -0
- package/src/modules/__test__/cache.test.ts +142 -0
- package/src/modules/__test__/hooks.test.ts +192 -0
- package/src/modules/enterPage.ts +19 -17
- package/src/modules/fetchPage.ts +74 -29
- package/src/modules/getAnchorElement.ts +8 -4
- package/src/modules/getAnimationPromises.ts +66 -75
- package/src/modules/leavePage.ts +22 -20
- package/src/modules/loadPage.ts +72 -54
- package/src/modules/renderPage.ts +41 -32
- package/src/modules/replaceContent.ts +26 -17
- package/src/utils/index.ts +24 -3
- package/src/helpers/fetch.ts +0 -33
- package/src/helpers/getDataFromHtml.ts +0 -39
- package/src/helpers/markSwupElements.ts +0 -16
- package/src/modules/__test__/events.test.ts +0 -72
- package/src/modules/events.ts +0 -92
- package/src/modules/getPageData.ts +0 -24
- package/src/modules/transitions.ts +0 -10
package/src/modules/loadPage.ts
CHANGED
|
@@ -1,81 +1,99 @@
|
|
|
1
|
-
import { classify, createHistoryRecord, updateHistoryRecord, getCurrentUrl } from '../helpers.js';
|
|
2
1
|
import Swup from '../Swup.js';
|
|
3
|
-
import {
|
|
2
|
+
import { createHistoryRecord, updateHistoryRecord, getCurrentUrl, Location } from '../helpers.js';
|
|
3
|
+
import { FetchOptions } from '../modules/fetchPage.js';
|
|
4
|
+
import { ContextInitOptions } from './Context.js';
|
|
4
5
|
|
|
5
6
|
export type HistoryAction = 'push' | 'replace';
|
|
6
7
|
|
|
7
|
-
export type TransitionOptions = {
|
|
8
|
-
url: string;
|
|
9
|
-
customTransition?: string;
|
|
10
|
-
history?: HistoryAction;
|
|
11
|
-
};
|
|
12
|
-
|
|
13
8
|
export type PageLoadOptions = {
|
|
14
|
-
|
|
15
|
-
|
|
9
|
+
animate?: boolean;
|
|
10
|
+
transition?: string;
|
|
16
11
|
history?: HistoryAction;
|
|
17
|
-
event?: PopStateEvent;
|
|
18
12
|
};
|
|
19
13
|
|
|
20
|
-
export function loadPage(
|
|
21
|
-
|
|
22
|
-
|
|
14
|
+
export function loadPage(
|
|
15
|
+
this: Swup,
|
|
16
|
+
url: string,
|
|
17
|
+
options: PageLoadOptions & FetchOptions = {},
|
|
18
|
+
context: Omit<ContextInitOptions, 'to'> = {}
|
|
19
|
+
) {
|
|
23
20
|
// Check if the visit should be ignored
|
|
24
21
|
if (this.shouldIgnoreVisit(url)) {
|
|
25
22
|
window.location.href = url;
|
|
26
23
|
} else {
|
|
27
|
-
|
|
24
|
+
const { url: to, hash } = Location.fromUrl(url);
|
|
25
|
+
this.context = this.createContext({ ...context, to, hash });
|
|
26
|
+
this.performPageLoad(to, options);
|
|
28
27
|
}
|
|
29
28
|
}
|
|
30
29
|
|
|
31
|
-
export function performPageLoad(
|
|
32
|
-
|
|
30
|
+
export async function performPageLoad(
|
|
31
|
+
this: Swup,
|
|
32
|
+
url: string,
|
|
33
|
+
options: PageLoadOptions & FetchOptions = {}
|
|
34
|
+
) {
|
|
35
|
+
if (typeof url !== 'string') {
|
|
36
|
+
throw new Error(`loadPage requires a URL parameter`);
|
|
37
|
+
}
|
|
33
38
|
|
|
34
|
-
const
|
|
35
|
-
const
|
|
39
|
+
const { url: requestedUrl } = Location.fromUrl(url);
|
|
40
|
+
const { transition, animate, history: historyAction } = options;
|
|
41
|
+
options.referrer = options.referrer || this.currentPageUrl;
|
|
36
42
|
|
|
37
|
-
|
|
43
|
+
if (animate === false) {
|
|
44
|
+
this.context.transition.animate = false;
|
|
45
|
+
}
|
|
46
|
+
if (historyAction) {
|
|
47
|
+
this.context.history.action = historyAction;
|
|
48
|
+
}
|
|
38
49
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
50
|
+
if (!this.context.transition.animate) {
|
|
51
|
+
document.documentElement.classList.remove('is-animating');
|
|
52
|
+
this.cleanupAnimationClasses();
|
|
53
|
+
} else if (transition) {
|
|
54
|
+
this.context.transition.name = transition;
|
|
43
55
|
}
|
|
44
56
|
|
|
45
|
-
|
|
46
|
-
|
|
57
|
+
try {
|
|
58
|
+
await this.hooks.trigger('transitionStart');
|
|
59
|
+
const animationPromise = this.leavePage();
|
|
60
|
+
const pagePromise = this.hooks.trigger(
|
|
61
|
+
'loadPage',
|
|
62
|
+
{ url, options },
|
|
63
|
+
async (context, { url, options, page }) => await (page || this.fetchPage(url, options))
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
// create history record if this is not a popstate call (with or without anchor)
|
|
67
|
+
if (!this.context.history.popstate) {
|
|
68
|
+
const newUrl = url + (this.context.scroll.target || '');
|
|
69
|
+
if (this.context.history.action === 'replace') {
|
|
70
|
+
updateHistoryRecord(newUrl);
|
|
71
|
+
} else {
|
|
72
|
+
createHistoryRecord(newUrl);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
47
75
|
|
|
48
|
-
|
|
49
|
-
const fetchPromise = this.fetchPage(data);
|
|
76
|
+
this.currentPageUrl = getCurrentUrl();
|
|
50
77
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
78
|
+
// when everything is ready, render the page
|
|
79
|
+
const [page] = await Promise.all([pagePromise, animationPromise]);
|
|
80
|
+
this.renderPage(requestedUrl, page);
|
|
81
|
+
} catch (error: unknown) {
|
|
82
|
+
// Return early if error is undefined (probably aborted preload request)
|
|
83
|
+
if (!error) {
|
|
84
|
+
return;
|
|
58
85
|
}
|
|
59
|
-
}
|
|
60
86
|
|
|
61
|
-
|
|
87
|
+
// Log to console as we swallow almost all hook errors
|
|
88
|
+
console.error(error);
|
|
62
89
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
}
|
|
68
|
-
.catch((errorUrl) => {
|
|
69
|
-
// Return early if errorUrl is not defined (probably aborted preload request)
|
|
70
|
-
if (errorUrl === undefined) return;
|
|
90
|
+
// Rewrite `skipPopStateHandling` to redirect manually when `history.go` is processed
|
|
91
|
+
this.options.skipPopStateHandling = () => {
|
|
92
|
+
window.location.href = requestedUrl;
|
|
93
|
+
return true;
|
|
94
|
+
};
|
|
71
95
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
return true;
|
|
76
|
-
};
|
|
77
|
-
|
|
78
|
-
// Go back to the actual page we're still at
|
|
79
|
-
history.go(-1);
|
|
80
|
-
});
|
|
96
|
+
// Go back to the actual page we're still at
|
|
97
|
+
history.go(-1);
|
|
98
|
+
}
|
|
81
99
|
}
|
|
@@ -1,53 +1,62 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { updateHistoryRecord, getCurrentUrl } from '../helpers.js';
|
|
2
2
|
import Swup from '../Swup.js';
|
|
3
|
-
import {
|
|
3
|
+
import { PageData } from './fetchPage.js';
|
|
4
4
|
|
|
5
|
-
export
|
|
6
|
-
|
|
7
|
-
skipTransition?: boolean;
|
|
8
|
-
};
|
|
5
|
+
export const renderPage = async function (this: Swup, requestedUrl: string, page: PageData) {
|
|
6
|
+
const { url } = page;
|
|
9
7
|
|
|
10
|
-
export const renderPage = function (
|
|
11
|
-
this: Swup,
|
|
12
|
-
page: PageRecord,
|
|
13
|
-
{ event, skipTransition }: PageRenderOptions = {}
|
|
14
|
-
) {
|
|
15
8
|
document.documentElement.classList.remove('is-leaving');
|
|
16
9
|
|
|
17
10
|
// do nothing if another page was requested in the meantime
|
|
18
|
-
if (!this.isSameResolvedUrl(getCurrentUrl(),
|
|
11
|
+
if (!this.isSameResolvedUrl(getCurrentUrl(), requestedUrl)) {
|
|
19
12
|
return;
|
|
20
13
|
}
|
|
21
14
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
// update cache and state if the url was redirected
|
|
15
|
+
// update state if the url was redirected
|
|
25
16
|
if (!this.isSameResolvedUrl(getCurrentUrl(), url)) {
|
|
26
|
-
this.cache.cacheUrl({ ...page, url });
|
|
27
|
-
this.currentPageUrl = getCurrentUrl();
|
|
28
17
|
updateHistoryRecord(url);
|
|
18
|
+
this.currentPageUrl = getCurrentUrl();
|
|
19
|
+
this.context.to!.url = this.currentPageUrl;
|
|
29
20
|
}
|
|
30
21
|
|
|
31
22
|
// only add for page loads with transitions
|
|
32
|
-
if (
|
|
23
|
+
if (this.context.transition.animate) {
|
|
33
24
|
document.documentElement.classList.add('is-rendering');
|
|
34
25
|
}
|
|
35
26
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
this.
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
27
|
+
// replace content: allow handlers and plugins to overwrite paga data and containers
|
|
28
|
+
await this.hooks.trigger(
|
|
29
|
+
'replaceContent',
|
|
30
|
+
{ page, containers: this.context.containers },
|
|
31
|
+
(context, { page, containers }) => {
|
|
32
|
+
this.replaceContent(page, { containers });
|
|
33
|
+
}
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
await this.hooks.trigger(
|
|
37
|
+
'scrollToContent',
|
|
38
|
+
{ options: { behavior: 'auto' } },
|
|
39
|
+
(context, { options }) => {
|
|
40
|
+
if (this.context.scroll.target) {
|
|
41
|
+
const target = this.getAnchorElement(this.context.scroll.target);
|
|
42
|
+
if (target) {
|
|
43
|
+
target.scrollIntoView(options);
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
if (this.context.scroll.reset) {
|
|
48
|
+
window.scrollTo(0, 0);
|
|
49
|
+
}
|
|
45
50
|
}
|
|
51
|
+
);
|
|
46
52
|
|
|
47
|
-
|
|
48
|
-
|
|
53
|
+
await this.hooks.trigger('pageView', { url: this.currentPageUrl, title: document.title });
|
|
54
|
+
|
|
55
|
+
// empty cache if it's disabled (in case preload plugin filled it)
|
|
56
|
+
if (!this.options.cache) {
|
|
57
|
+
this.cache.clear();
|
|
58
|
+
}
|
|
49
59
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
});
|
|
60
|
+
// Perform in transition
|
|
61
|
+
this.enterPage();
|
|
53
62
|
};
|
|
@@ -1,26 +1,35 @@
|
|
|
1
|
+
import Swup, { Options } from '../Swup.js';
|
|
2
|
+
import { PageData } from './fetchPage.js';
|
|
3
|
+
|
|
1
4
|
/**
|
|
2
5
|
* Perform the replacement of content after loading a page.
|
|
3
6
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* It takes an object with the page data as return from `getPageData` and has to
|
|
7
|
-
* return a Promise that resolves once all content has been replaced and the
|
|
8
|
-
* site is ready to start animating in the new page.
|
|
9
|
-
*
|
|
10
|
-
* @param {object} page The page object
|
|
11
|
-
* @returns Promise
|
|
7
|
+
* It takes an object with the page data as returned from `fetchPage` and a list
|
|
8
|
+
* of container selectors to replace.
|
|
12
9
|
*/
|
|
13
|
-
export const replaceContent = function (
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
});
|
|
10
|
+
export const replaceContent = function (
|
|
11
|
+
this: Swup,
|
|
12
|
+
{ html }: PageData,
|
|
13
|
+
{ containers }: { containers: Options['containers'] } = this.options
|
|
14
|
+
): void {
|
|
15
|
+
const doc = new DOMParser().parseFromString(html, 'text/html');
|
|
20
16
|
|
|
21
17
|
// Update browser title
|
|
18
|
+
const title = doc.querySelector('title')?.innerText || '';
|
|
22
19
|
document.title = title;
|
|
23
20
|
|
|
24
|
-
//
|
|
25
|
-
|
|
21
|
+
// Update content containers
|
|
22
|
+
containers.forEach((selector) => {
|
|
23
|
+
const currentEl = document.querySelector(selector);
|
|
24
|
+
const incomingEl = doc.querySelector(selector);
|
|
25
|
+
if (!currentEl) {
|
|
26
|
+
console.warn(`[swup] Container missing in current document: ${selector}`);
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
if (!incomingEl) {
|
|
30
|
+
console.warn(`[swup] Container missing in incoming document: ${selector}`);
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
currentEl.replaceWith(incomingEl);
|
|
34
|
+
});
|
|
26
35
|
};
|
package/src/utils/index.ts
CHANGED
|
@@ -9,14 +9,35 @@ export const queryAll = (
|
|
|
9
9
|
return Array.from(context.querySelectorAll(selector));
|
|
10
10
|
};
|
|
11
11
|
|
|
12
|
-
export const nextTick = (
|
|
13
|
-
|
|
12
|
+
export const nextTick = (): Promise<void> => {
|
|
13
|
+
return new Promise((resolve) => {
|
|
14
14
|
requestAnimationFrame(() => {
|
|
15
|
-
|
|
15
|
+
requestAnimationFrame(() => {
|
|
16
|
+
resolve();
|
|
17
|
+
});
|
|
16
18
|
});
|
|
17
19
|
});
|
|
18
20
|
};
|
|
19
21
|
|
|
22
|
+
export function isPromise<T>(obj: any): obj is PromiseLike<T> {
|
|
23
|
+
return (
|
|
24
|
+
!!obj &&
|
|
25
|
+
(typeof obj === 'object' || typeof obj === 'function') &&
|
|
26
|
+
typeof obj.then === 'function'
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function runAsPromise(func: Function, args: any[] = [], ctx: any = {}): Promise<any> {
|
|
31
|
+
return new Promise((resolve, reject) => {
|
|
32
|
+
const result = func.apply(ctx, args);
|
|
33
|
+
if (isPromise(result)) {
|
|
34
|
+
result.then(resolve, reject);
|
|
35
|
+
} else {
|
|
36
|
+
resolve(result);
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
|
|
20
41
|
export const escapeCssIdentifier = (ident: string) => {
|
|
21
42
|
// @ts-ignore this is for support check, so it's correct that TS complains
|
|
22
43
|
if (window.CSS && window.CSS.escape) {
|
package/src/helpers/fetch.ts
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import { TransitionOptions } from '../modules/loadPage.js';
|
|
2
|
-
import { Options } from '../Swup.js';
|
|
3
|
-
|
|
4
|
-
export const fetch = (
|
|
5
|
-
options: TransitionOptions & { headers: Options['requestHeaders'] },
|
|
6
|
-
callback: (request: XMLHttpRequest) => void
|
|
7
|
-
): XMLHttpRequest => {
|
|
8
|
-
const defaults = {
|
|
9
|
-
url: window.location.pathname + window.location.search,
|
|
10
|
-
method: 'GET',
|
|
11
|
-
data: null,
|
|
12
|
-
headers: {}
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
const { url, method, headers, data } = { ...defaults, ...options };
|
|
16
|
-
|
|
17
|
-
const request = new XMLHttpRequest();
|
|
18
|
-
|
|
19
|
-
request.onreadystatechange = function () {
|
|
20
|
-
if (request.readyState === 4) {
|
|
21
|
-
// if (request.status === 500) {} else {}
|
|
22
|
-
callback(request);
|
|
23
|
-
}
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
request.open(method, url, true);
|
|
27
|
-
Object.entries(headers).forEach(([key, header]) => {
|
|
28
|
-
request.setRequestHeader(key, header);
|
|
29
|
-
});
|
|
30
|
-
request.send(data);
|
|
31
|
-
|
|
32
|
-
return request;
|
|
33
|
-
};
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import { query, queryAll } from '../utils.js';
|
|
2
|
-
|
|
3
|
-
export type PageHtmlData = {
|
|
4
|
-
title: string;
|
|
5
|
-
originalContent: string;
|
|
6
|
-
blocks: string[];
|
|
7
|
-
pageClass?: string;
|
|
8
|
-
};
|
|
9
|
-
|
|
10
|
-
export const getDataFromHtml = (html: string, containers: string[]): PageHtmlData => {
|
|
11
|
-
let fakeDom = document.createElement('html');
|
|
12
|
-
fakeDom.innerHTML = html;
|
|
13
|
-
let blocks: string[] = [];
|
|
14
|
-
|
|
15
|
-
containers.forEach((selector) => {
|
|
16
|
-
if (query(selector, fakeDom) == null) {
|
|
17
|
-
console.warn(`[swup] Container ${selector} not found on page.`);
|
|
18
|
-
return null;
|
|
19
|
-
} else {
|
|
20
|
-
if (queryAll(selector).length !== queryAll(selector, fakeDom).length) {
|
|
21
|
-
console.warn(`[swup] Mismatched number of containers found on new page.`);
|
|
22
|
-
}
|
|
23
|
-
queryAll(selector).forEach((item, index) => {
|
|
24
|
-
queryAll(selector, fakeDom)[index].setAttribute('data-swup', String(blocks.length));
|
|
25
|
-
blocks.push(queryAll(selector, fakeDom)[index].outerHTML);
|
|
26
|
-
});
|
|
27
|
-
}
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
const title = query('title', fakeDom)?.innerText || '';
|
|
31
|
-
const pageClass = query('body', fakeDom)?.className;
|
|
32
|
-
|
|
33
|
-
// to prevent memory leaks
|
|
34
|
-
fakeDom.innerHTML = '';
|
|
35
|
-
// @ts-ignore don't want to type it as possible null, since it's created at the top of the function always
|
|
36
|
-
fakeDom = null;
|
|
37
|
-
|
|
38
|
-
return { title, pageClass, blocks, originalContent: html };
|
|
39
|
-
};
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { query, queryAll } from '../utils.js';
|
|
2
|
-
|
|
3
|
-
export const markSwupElements = (element: Element, containers: string[]): void => {
|
|
4
|
-
let blocks = 0;
|
|
5
|
-
|
|
6
|
-
containers.forEach((selector) => {
|
|
7
|
-
if (query(selector, element) == null) {
|
|
8
|
-
console.warn(`[swup] Container ${selector} not found on page.`);
|
|
9
|
-
} else {
|
|
10
|
-
queryAll(selector).forEach((item: Element, index: number) => {
|
|
11
|
-
queryAll(selector, element)[index].setAttribute('data-swup', String(blocks));
|
|
12
|
-
blocks++;
|
|
13
|
-
});
|
|
14
|
-
}
|
|
15
|
-
});
|
|
16
|
-
};
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it, vi } from 'vitest';
|
|
2
|
-
import Swup from '../../Swup.js';
|
|
3
|
-
import { Handler } from '../events.js';
|
|
4
|
-
|
|
5
|
-
describe('events', () => {
|
|
6
|
-
it('should add event handlers to handlers array', () => {
|
|
7
|
-
const swup = new Swup();
|
|
8
|
-
const handler = vi.fn();
|
|
9
|
-
|
|
10
|
-
swup.on('enabled', handler);
|
|
11
|
-
|
|
12
|
-
expect(swup._handlers.enabled.indexOf(handler)).toBe(0);
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
it('should remove event handlers from handlers array', () => {
|
|
16
|
-
const swup = new Swup();
|
|
17
|
-
const handler = vi.fn();
|
|
18
|
-
|
|
19
|
-
swup.on('enabled', handler);
|
|
20
|
-
swup.on('animationInDone', handler);
|
|
21
|
-
swup.on('animationInStart', handler);
|
|
22
|
-
|
|
23
|
-
expect(swup._handlers.enabled.indexOf(handler)).toBe(0);
|
|
24
|
-
expect(swup._handlers.animationInDone.indexOf(handler)).toBe(0);
|
|
25
|
-
expect(swup._handlers.animationInStart.indexOf(handler)).toBe(0);
|
|
26
|
-
|
|
27
|
-
swup.off('enabled', handler);
|
|
28
|
-
expect(swup._handlers.enabled.indexOf(handler)).toBe(-1);
|
|
29
|
-
|
|
30
|
-
swup.off('animationInDone');
|
|
31
|
-
expect(swup._handlers.animationInDone.indexOf(handler)).toBe(-1);
|
|
32
|
-
|
|
33
|
-
swup.off();
|
|
34
|
-
expect(swup._handlers.animationInStart.indexOf(handler)).toBe(-1);
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
it('should trigger event handler', () => {
|
|
38
|
-
const swup = new Swup();
|
|
39
|
-
const handler = vi.fn();
|
|
40
|
-
|
|
41
|
-
swup.on('enabled', handler);
|
|
42
|
-
|
|
43
|
-
swup.triggerEvent('enabled');
|
|
44
|
-
|
|
45
|
-
expect(handler).toBeCalledTimes(1);
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
it('should trigger event handler with event', () => {
|
|
49
|
-
const swup = new Swup();
|
|
50
|
-
const handler: Handler<'popState'> = vi.fn();
|
|
51
|
-
const event = new PopStateEvent('');
|
|
52
|
-
|
|
53
|
-
swup.on('popState', handler);
|
|
54
|
-
swup.triggerEvent('popState', event);
|
|
55
|
-
|
|
56
|
-
expect(handler).toBeCalledWith(event);
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
it('types work and error when necessary', () => {
|
|
60
|
-
const swup = new Swup();
|
|
61
|
-
|
|
62
|
-
// @ts-expect-no-error
|
|
63
|
-
swup.on('popState', (event: PopStateEvent) => {});
|
|
64
|
-
// @ts-expect-no-error
|
|
65
|
-
swup.triggerEvent('popState', new PopStateEvent(''));
|
|
66
|
-
|
|
67
|
-
// @ts-expect-error
|
|
68
|
-
swup.on('popState', (event: MouseEvent) => {});
|
|
69
|
-
// @ts-expect-error
|
|
70
|
-
swup.triggerEvent('popState', new MouseEvent(''));
|
|
71
|
-
});
|
|
72
|
-
});
|
package/src/modules/events.ts
DELETED
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
import Swup from '../Swup.js';
|
|
2
|
-
import { DelegateEvent } from 'delegate-it';
|
|
3
|
-
|
|
4
|
-
type HandlersEventMap = {
|
|
5
|
-
animationInDone: undefined;
|
|
6
|
-
animationInStart: undefined;
|
|
7
|
-
animationOutDone: undefined;
|
|
8
|
-
animationOutStart: undefined;
|
|
9
|
-
animationSkipped: undefined;
|
|
10
|
-
clickLink: DelegateEvent<MouseEvent>;
|
|
11
|
-
contentReplaced: PopStateEvent | undefined;
|
|
12
|
-
disabled: undefined;
|
|
13
|
-
enabled: undefined;
|
|
14
|
-
openPageInNewTab: DelegateEvent<MouseEvent>;
|
|
15
|
-
pageLoaded: undefined;
|
|
16
|
-
pageRetrievedFromCache: undefined;
|
|
17
|
-
pageView: PopStateEvent | undefined;
|
|
18
|
-
popState: PopStateEvent;
|
|
19
|
-
samePage: DelegateEvent<MouseEvent>;
|
|
20
|
-
samePageWithHash: DelegateEvent<MouseEvent>;
|
|
21
|
-
serverError: undefined;
|
|
22
|
-
transitionStart: PopStateEvent | undefined;
|
|
23
|
-
transitionEnd: PopStateEvent | undefined;
|
|
24
|
-
willReplaceContent: PopStateEvent | undefined;
|
|
25
|
-
};
|
|
26
|
-
type AvailableEventNames = keyof HandlersEventMap;
|
|
27
|
-
|
|
28
|
-
export type Handler<T extends keyof HandlersEventMap> = (event: HandlersEventMap[T]) => void;
|
|
29
|
-
export type Handlers = {
|
|
30
|
-
[Key in keyof HandlersEventMap]: Handler<Key>[];
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
export function on<TEventType extends AvailableEventNames>(
|
|
34
|
-
this: Swup,
|
|
35
|
-
event: TEventType,
|
|
36
|
-
handler: Handler<TEventType>
|
|
37
|
-
): void {
|
|
38
|
-
const eventHandlers = this._handlers[event] as Handler<TEventType>[];
|
|
39
|
-
|
|
40
|
-
if (eventHandlers) {
|
|
41
|
-
eventHandlers.push(handler);
|
|
42
|
-
} else {
|
|
43
|
-
console.warn(`Unsupported event ${event}.`);
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
export function off<TEventType extends AvailableEventNames>(
|
|
48
|
-
this: Swup,
|
|
49
|
-
event?: TEventType,
|
|
50
|
-
handler?: Handler<TEventType>
|
|
51
|
-
) {
|
|
52
|
-
if (event && handler) {
|
|
53
|
-
const eventHandlers = this._handlers[event] as Handler<TEventType>[];
|
|
54
|
-
// Remove specific handler
|
|
55
|
-
if (eventHandlers.includes(handler)) {
|
|
56
|
-
(this._handlers[event] as Handler<TEventType>[]) = eventHandlers.filter(
|
|
57
|
-
(h) => h !== handler
|
|
58
|
-
);
|
|
59
|
-
} else {
|
|
60
|
-
console.warn(`Handler for event '${event}' not found.`);
|
|
61
|
-
}
|
|
62
|
-
} else if (event) {
|
|
63
|
-
// Remove all handlers for specific event
|
|
64
|
-
this._handlers[event] = [];
|
|
65
|
-
} else {
|
|
66
|
-
// Remove all handlers for all events
|
|
67
|
-
Object.keys(this._handlers).forEach((event) => {
|
|
68
|
-
this._handlers[event as keyof HandlersEventMap] = [];
|
|
69
|
-
});
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
export function triggerEvent<TEventType extends AvailableEventNames>(
|
|
74
|
-
this: Swup,
|
|
75
|
-
eventName: TEventType,
|
|
76
|
-
originalEvent?: HandlersEventMap[TEventType]
|
|
77
|
-
): void {
|
|
78
|
-
const eventHandlers = this._handlers[eventName] as Handler<TEventType>[];
|
|
79
|
-
|
|
80
|
-
// call saved handlers with "on" method and pass originalEvent object if available
|
|
81
|
-
eventHandlers.forEach((handler) => {
|
|
82
|
-
try {
|
|
83
|
-
handler(originalEvent as HandlersEventMap[TEventType]);
|
|
84
|
-
} catch (error) {
|
|
85
|
-
console.error(error);
|
|
86
|
-
}
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
// trigger event on document with prefix "swup:"
|
|
90
|
-
const event = new CustomEvent(`swup:${eventName}`, { detail: eventName });
|
|
91
|
-
document.dispatchEvent(event);
|
|
92
|
-
}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { getDataFromHtml } from '../helpers.js';
|
|
2
|
-
import Swup from '../Swup.js';
|
|
3
|
-
import { PageHtmlData } from '../helpers/getDataFromHtml.js';
|
|
4
|
-
|
|
5
|
-
export type PageData = PageHtmlData & {
|
|
6
|
-
responseURL: string;
|
|
7
|
-
};
|
|
8
|
-
export const getPageData = function (this: Swup, request: XMLHttpRequest): PageData | null {
|
|
9
|
-
// this method can be replaced in case other content than html is expected to be received from server
|
|
10
|
-
// this function should always return { title, pageClass, originalContent, blocks, responseURL }
|
|
11
|
-
// in case page has invalid structure - return null
|
|
12
|
-
const html = request.responseText;
|
|
13
|
-
const pageHtmlData = getDataFromHtml(html, this.options.containers);
|
|
14
|
-
|
|
15
|
-
if (!pageHtmlData) {
|
|
16
|
-
console.warn('[swup] Received page is invalid.');
|
|
17
|
-
return null;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
return {
|
|
21
|
-
...pageHtmlData,
|
|
22
|
-
responseURL: request.responseURL || window.location.href
|
|
23
|
-
};
|
|
24
|
-
};
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import Swup from '../Swup.js';
|
|
2
|
-
|
|
3
|
-
export function updateTransition(this: Swup, from: string, to: string, custom?: string): void {
|
|
4
|
-
this.transition = { from, to, custom };
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
export function shouldSkipTransition(this: Swup, { event }: { url?: string; event?: Event }) {
|
|
8
|
-
const isHistoryVisit = event instanceof PopStateEvent;
|
|
9
|
-
return !!(isHistoryVisit && !this.options.animateHistoryBrowsing);
|
|
10
|
-
}
|