swup 4.0.0-rc.21 → 4.0.0-rc.23

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.
@@ -58,12 +58,12 @@ export default class Swup {
58
58
  private clickDelegate?;
59
59
  visit: typeof visit;
60
60
  performVisit: typeof performVisit;
61
- leavePage: (this: Swup) => Promise<void>;
61
+ animatePageOut: (this: Swup) => Promise<void>;
62
62
  renderPage: (this: Swup, requestedUrl: string, page: import("./modules/fetchPage.js").PageData) => Promise<void>;
63
63
  replaceContent: (this: Swup, { html }: import("./modules/fetchPage.js").PageData, { containers }?: {
64
64
  containers: string[];
65
65
  }) => boolean;
66
- enterPage: (this: Swup) => Promise<void>;
66
+ animatePageIn: (this: Swup) => Promise<void>;
67
67
  delegateEvent: <Selector extends string, TEvent extends keyof GlobalEventHandlersEventMap>(selector: Selector, type: TEvent, callback: import("delegate-it").DelegateEventHandler<GlobalEventHandlersEventMap[TEvent], Element>, options?: import("delegate-it").DelegateOptions | undefined) => DelegateEventUnsubscribe;
68
68
  fetchPage: typeof fetchPage;
69
69
  awaitAnimations: typeof awaitAnimations;
@@ -2,18 +2,33 @@ import Swup from '../Swup.js';
2
2
  import { PageData } from './fetchPage.js';
3
3
  export interface CacheData extends PageData {
4
4
  }
5
+ /**
6
+ * In-memory page cache.
7
+ */
5
8
  export declare class Cache {
9
+ /** Swup instance this cache belongs to */
6
10
  private swup;
11
+ /** Cached pages, indexed by URL */
7
12
  private pages;
8
13
  constructor(swup: Swup);
14
+ /** Number of cached pages in memory. */
9
15
  get size(): number;
16
+ /** All cached pages. */
10
17
  get all(): Map<string, CacheData>;
18
+ /** Check if the given URL has been cached. */
11
19
  has(url: string): boolean;
20
+ /** Return the cached page object if cached. */
12
21
  get(url: string): CacheData | undefined;
22
+ /** Create a cache record for the specified URL. */
13
23
  set(url: string, page: CacheData): void;
24
+ /** Update a cache record, overwriting or adding custom data. */
14
25
  update(url: string, page: CacheData): void;
26
+ /** Delete a cache record. */
15
27
  delete(url: string): void;
28
+ /** Empty the cache. */
16
29
  clear(): void;
30
+ /** Remove all cache entries that return true for a given predicate function. */
17
31
  prune(predicate: (url: string, page: CacheData) => boolean): void;
32
+ /** Resolve URLs by making them local and letting swup resolve them. */
18
33
  private resolve;
19
34
  }
@@ -1,6 +1,6 @@
1
1
  import Swup, { Options } from '../Swup.js';
2
2
  import { HistoryAction, HistoryDirection } from './visit.js';
3
- export interface Context<TEvent = Event> {
3
+ export interface Context {
4
4
  /** The previous page, about to leave */
5
5
  from: FromContext;
6
6
  /** The next page, about to enter */
@@ -10,7 +10,7 @@ export interface Context<TEvent = Event> {
10
10
  /** Information about animated page transitions */
11
11
  animation: AnimationContext;
12
12
  /** What triggered this visit */
13
- trigger: TriggerContext<TEvent>;
13
+ trigger: TriggerContext;
14
14
  /** Browser history behavior on this visit */
15
15
  history: HistoryContext;
16
16
  /** Scroll behavior on this visit */
@@ -44,11 +44,11 @@ export interface ScrollContext {
44
44
  /** Anchor element to scroll to on the next page. */
45
45
  target?: string;
46
46
  }
47
- export interface TriggerContext<TEvent = Event> {
47
+ export interface TriggerContext {
48
48
  /** DOM element that triggered this visit. */
49
49
  el?: Element;
50
50
  /** DOM event that triggered this visit. */
51
- event?: TEvent;
51
+ event?: Event;
52
52
  }
53
53
  export interface HistoryContext {
54
54
  /** History action to perform: `push` for creating a new history entry, `replace` for replacing the current entry. Default: `push` */
@@ -3,4 +3,4 @@ import Swup from '../Swup.js';
3
3
  * Perform the in/enter animation of the next page.
4
4
  * @returns Promise<void>
5
5
  */
6
- export declare const enterPage: (this: Swup) => Promise<void>;
6
+ export declare const animatePageIn: (this: Swup) => Promise<void>;
@@ -3,4 +3,4 @@ import Swup from '../Swup.js';
3
3
  * Perform the out/leave animation of the current page.
4
4
  * @returns Promise<void>
5
5
  */
6
- export declare const leavePage: (this: Swup) => Promise<void>;
6
+ export declare const animatePageOut: (this: Swup) => Promise<void>;
@@ -3,5 +3,11 @@ export declare const queryAll: (selector: string, context?: Document | Element)
3
3
  export declare const nextTick: () => Promise<void>;
4
4
  export declare function isPromise<T>(obj: any): obj is PromiseLike<T>;
5
5
  export declare function runAsPromise(func: Function, args?: any[], ctx?: any): Promise<any>;
6
+ /**
7
+ * Force a layout reflow, e.g. after adding classnames
8
+ * @returns The offset height, just here so it doesn't get optimized away by the JS engine
9
+ * @see https://stackoverflow.com/a/21665117/3759615
10
+ */
11
+ export declare function forceReflow(element?: HTMLElement): number;
6
12
  export declare const escapeCssIdentifier: (ident: string) => string;
7
13
  export declare const toMs: (s: string) => number;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "swup",
3
3
  "amdName": "Swup",
4
- "version": "4.0.0-rc.21",
4
+ "version": "4.0.0-rc.23",
5
5
  "description": "Versatile and extensible page transition library for server-rendered websites",
6
6
  "type": "module",
7
7
  "source": "./src/Swup.ts",
package/src/Swup.ts CHANGED
@@ -13,9 +13,9 @@ import { getAnchorElement } from './modules/getAnchorElement.js';
13
13
  import { awaitAnimations } from './modules/awaitAnimations.js';
14
14
  import { visit, performVisit, HistoryAction } from './modules/visit.js';
15
15
  import { fetchPage } from './modules/fetchPage.js';
16
- import { leavePage } from './modules/leavePage.js';
16
+ import { animatePageOut } from './modules/animatePageOut.js';
17
17
  import { replaceContent } from './modules/replaceContent.js';
18
- import { enterPage } from './modules/enterPage.js';
18
+ import { animatePageIn } from './modules/animatePageIn.js';
19
19
  import { renderPage } from './modules/renderPage.js';
20
20
  import { use, unuse, findPlugin, Plugin } from './modules/plugins.js';
21
21
  import { nextTick } from './utils.js';
@@ -69,10 +69,10 @@ export default class Swup {
69
69
 
70
70
  visit = visit;
71
71
  performVisit = performVisit;
72
- leavePage = leavePage;
72
+ animatePageOut = animatePageOut;
73
73
  renderPage = renderPage;
74
74
  replaceContent = replaceContent;
75
- enterPage = enterPage;
75
+ animatePageIn = animatePageIn;
76
76
  delegateEvent = delegateEvent;
77
77
  fetchPage = fetchPage;
78
78
  awaitAnimations = awaitAnimations;
@@ -148,14 +148,15 @@ export default class Swup {
148
148
  // Modify initial history record
149
149
  updateHistoryRecord(null, { index: 1 });
150
150
 
151
+ // Give consumers a chance to hook into enable and page:view
152
+ await nextTick();
153
+
151
154
  // Trigger enable hook
152
155
  await this.hooks.trigger('enable', undefined, () => {
153
156
  // Add swup-enabled class to html tag
154
157
  document.documentElement.classList.add('swup-enabled');
155
158
  });
156
159
 
157
- await nextTick();
158
-
159
160
  // Trigger page view hook
160
161
  await this.hooks.trigger('page:view', { url: this.currentPageUrl, title: document.title });
161
162
  }
@@ -209,29 +210,12 @@ export default class Swup {
209
210
  const el = event.delegateTarget as HTMLAnchorElement;
210
211
  const { href, url, hash } = Location.fromElement(el);
211
212
 
212
- // Get the animation name, if specified
213
- const animation = el.getAttribute('data-swup-animation') || undefined;
214
-
215
- // Get the history action, if specified
216
- let historyAction: HistoryAction | undefined;
217
- const historyAttr = el.getAttribute('data-swup-history');
218
- if (historyAttr && ['push', 'replace'].includes(historyAttr)) {
219
- historyAction = historyAttr as HistoryAction;
220
- }
221
-
222
213
  // Exit early if the link should be ignored
223
214
  if (this.shouldIgnoreVisit(href, { el, event })) {
224
215
  return;
225
216
  }
226
217
 
227
- this.context = this.createContext({
228
- to: url,
229
- hash,
230
- animation,
231
- el,
232
- event,
233
- action: historyAction
234
- });
218
+ this.context = this.createContext({ to: url, hash, el, event });
235
219
 
236
220
  // Exit early if control key pressed
237
221
  if (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) {
@@ -4,30 +4,41 @@ import { PageData } from './fetchPage.js';
4
4
 
5
5
  export interface CacheData extends PageData {}
6
6
 
7
+ /**
8
+ * In-memory page cache.
9
+ */
7
10
  export class Cache {
11
+ /** Swup instance this cache belongs to */
8
12
  private swup: Swup;
13
+
14
+ /** Cached pages, indexed by URL */
9
15
  private pages: Map<string, CacheData> = new Map();
10
16
 
11
17
  constructor(swup: Swup) {
12
18
  this.swup = swup;
13
19
  }
14
20
 
15
- get size() {
21
+ /** Number of cached pages in memory. */
22
+ get size(): number {
16
23
  return this.pages.size;
17
24
  }
18
25
 
26
+ /** All cached pages. */
19
27
  get all() {
20
28
  return this.pages;
21
29
  }
22
30
 
31
+ /** Check if the given URL has been cached. */
23
32
  public has(url: string): boolean {
24
33
  return this.pages.has(this.resolve(url));
25
34
  }
26
35
 
36
+ /** Return the cached page object if cached. */
27
37
  public get(url: string): CacheData | undefined {
28
38
  return this.pages.get(this.resolve(url));
29
39
  }
30
40
 
41
+ /** Create a cache record for the specified URL. */
31
42
  public set(url: string, page: CacheData) {
32
43
  url = this.resolve(url);
33
44
  page = { ...page, url };
@@ -35,21 +46,25 @@ export class Cache {
35
46
  this.swup.hooks.triggerSync('cache:set', { page });
36
47
  }
37
48
 
49
+ /** Update a cache record, overwriting or adding custom data. */
38
50
  public update(url: string, page: CacheData) {
39
51
  url = this.resolve(url);
40
52
  page = { ...this.get(url), ...page, url };
41
53
  this.pages.set(url, page);
42
54
  }
43
55
 
56
+ /** Delete a cache record. */
44
57
  public delete(url: string): void {
45
58
  this.pages.delete(this.resolve(url));
46
59
  }
47
60
 
61
+ /** Empty the cache. */
48
62
  public clear(): void {
49
63
  this.pages.clear();
50
64
  this.swup.hooks.triggerSync('cache:clear');
51
65
  }
52
66
 
67
+ /** Remove all cache entries that return true for a given predicate function. */
53
68
  public prune(predicate: (url: string, page: CacheData) => boolean): void {
54
69
  this.pages.forEach((page, url) => {
55
70
  if (predicate(url, page)) {
@@ -58,6 +73,7 @@ export class Cache {
58
73
  });
59
74
  }
60
75
 
76
+ /** Resolve URLs by making them local and letting swup resolve them. */
61
77
  private resolve(urlToResolve: string): string {
62
78
  const { url } = Location.fromUrl(urlToResolve);
63
79
  return this.swup.resolveUrl(url);
@@ -1,7 +1,7 @@
1
1
  import Swup, { Options } from '../Swup.js';
2
2
  import { HistoryAction, HistoryDirection } from './visit.js';
3
3
 
4
- export interface Context<TEvent = Event> {
4
+ export interface Context {
5
5
  /** The previous page, about to leave */
6
6
  from: FromContext;
7
7
  /** The next page, about to enter */
@@ -11,7 +11,7 @@ export interface Context<TEvent = Event> {
11
11
  /** Information about animated page transitions */
12
12
  animation: AnimationContext;
13
13
  /** What triggered this visit */
14
- trigger: TriggerContext<TEvent>;
14
+ trigger: TriggerContext;
15
15
  /** Browser history behavior on this visit */
16
16
  history: HistoryContext;
17
17
  /** Scroll behavior on this visit */
@@ -50,11 +50,11 @@ export interface ScrollContext {
50
50
  target?: string;
51
51
  }
52
52
 
53
- export interface TriggerContext<TEvent = Event> {
53
+ export interface TriggerContext {
54
54
  /** DOM element that triggered this visit. */
55
55
  el?: Element;
56
56
  /** DOM event that triggered this visit. */
57
- event?: TEvent;
57
+ event?: Event;
58
58
  }
59
59
 
60
60
  export interface HistoryContext {
@@ -1,6 +1,6 @@
1
1
  import { DelegateEvent } from 'delegate-it';
2
2
 
3
- import Swup, { Options } from '../Swup.js';
3
+ import Swup from '../Swup.js';
4
4
  import { isPromise, runAsPromise } from '../utils.js';
5
5
  import { Context } from './Context.js';
6
6
  import { FetchOptions, PageData } from './fetchPage.js';
@@ -5,7 +5,7 @@ import { nextTick } from '../utils.js';
5
5
  * Perform the in/enter animation of the next page.
6
6
  * @returns Promise<void>
7
7
  */
8
- export const enterPage = async function (this: Swup) {
8
+ export const animatePageIn = async function (this: Swup) {
9
9
  if (!this.context.animation.animate) {
10
10
  return;
11
11
  }
@@ -5,7 +5,7 @@ import { classify } from '../helpers.js';
5
5
  * Perform the out/leave animation of the current page.
6
6
  * @returns Promise<void>
7
7
  */
8
- export const leavePage = async function (this: Swup) {
8
+ export const animatePageOut = async function (this: Swup) {
9
9
  if (!this.context.animation.animate) {
10
10
  await this.hooks.trigger('animation:skip');
11
11
  return;
@@ -58,21 +58,28 @@ export async function performVisit(
58
58
  throw new Error(`swup.visit() requires a URL parameter`);
59
59
  }
60
60
 
61
+ const { el } = this.context.trigger;
61
62
  this.context.to.url = Location.fromUrl(url).url;
62
- const { animation, animate, history: historyAction } = options;
63
63
  options.referrer = options.referrer || this.currentPageUrl;
64
64
 
65
- if (animate === false) {
65
+ if (options.animate === false) {
66
66
  this.context.animation.animate = false;
67
67
  }
68
- if (historyAction) {
69
- this.context.history.action = historyAction;
70
- }
71
68
 
72
- // Clean up old animation classes and set custom animation name
69
+ // Clean up old animation classes
73
70
  if (!this.context.animation.animate) {
74
71
  this.classes.clear();
75
- } else if (animation) {
72
+ }
73
+
74
+ // Get history action from option or attribute on trigger element
75
+ const history = options.history || el?.getAttribute('data-swup-history') || undefined;
76
+ if (history && ['push', 'replace'].includes(history)) {
77
+ this.context.history.action = history as HistoryAction;
78
+ }
79
+
80
+ // Get custom animation name from option or attribute on trigger element
81
+ const animation = options.animation || el?.getAttribute('data-swup-animation') || undefined;
82
+ if (animation) {
76
83
  this.context.animation.name = animation;
77
84
  }
78
85
 
@@ -106,14 +113,14 @@ export async function performVisit(
106
113
  }
107
114
 
108
115
  // Wait for page to load and leave animation to finish
109
- const animationPromise = this.leavePage();
116
+ const animationPromise = this.animatePageOut();
110
117
  const [page] = await Promise.all([pagePromise, animationPromise]);
111
118
 
112
119
  // Render page: replace content and scroll to top/fragment
113
120
  await this.renderPage(this.context.to.url, page);
114
121
 
115
122
  // Wait for enter animation
116
- await this.enterPage();
123
+ await this.animatePageIn();
117
124
 
118
125
  // Finalize visit
119
126
  await this.hooks.trigger('visit:end', undefined, () => this.classes.clear());
@@ -138,6 +145,6 @@ export async function performVisit(
138
145
  };
139
146
 
140
147
  // Go back to the actual page we're still at
141
- history.go(-1);
148
+ window.history.go(-1);
142
149
  }
143
150
  }
@@ -38,6 +38,16 @@ export function runAsPromise(func: Function, args: any[] = [], ctx: any = {}): P
38
38
  });
39
39
  }
40
40
 
41
+ /**
42
+ * Force a layout reflow, e.g. after adding classnames
43
+ * @returns The offset height, just here so it doesn't get optimized away by the JS engine
44
+ * @see https://stackoverflow.com/a/21665117/3759615
45
+ */
46
+ export function forceReflow(element?: HTMLElement) {
47
+ element = element || document.body;
48
+ return element?.offsetHeight;
49
+ }
50
+
41
51
  export const escapeCssIdentifier = (ident: string) => {
42
52
  // @ts-ignore this is for support check, so it's correct that TS complains
43
53
  if (window.CSS && window.CSS.escape) {