swup 4.6.1 → 4.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,13 +1,8 @@
1
1
  import type Swup from '../Swup.js';
2
2
  import { FetchError, type FetchOptions, type PageData } from './fetchPage.js';
3
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';
4
+ import { createHistoryRecord, updateHistoryRecord, Location, classify } from '../helpers.js';
5
+ import { getContextualAttr } from '../utils.js';
11
6
 
12
7
  export type HistoryAction = 'push' | 'replace';
13
8
  export type HistoryDirection = 'forwards' | 'backwards';
@@ -24,6 +19,8 @@ type NavigationOptions = {
24
19
  history?: HistoryAction;
25
20
  /** Whether this visit should read from or write to the cache. */
26
21
  cache?: CacheControl;
22
+ /** Custom metadata associated with this visit. */
23
+ meta?: Record<string, unknown>;
27
24
  };
28
25
 
29
26
  /**
@@ -88,7 +85,7 @@ export async function performNavigation(
88
85
  this.visit = visit;
89
86
 
90
87
  const { el } = visit.trigger;
91
- options.referrer = options.referrer || this.currentPageUrl;
88
+ options.referrer = options.referrer || this.location.url;
92
89
 
93
90
  if (options.animate === false) {
94
91
  visit.animation.animate = false;
@@ -100,17 +97,20 @@ export async function performNavigation(
100
97
  }
101
98
 
102
99
  // Get history action from option or attribute on trigger element
103
- const history = options.history || el?.getAttribute('data-swup-history') || undefined;
104
- if (history && ['push', 'replace'].includes(history)) {
100
+ const history = options.history || getContextualAttr(el, 'data-swup-history');
101
+ if (typeof history === 'string' && ['push', 'replace'].includes(history)) {
105
102
  visit.history.action = history as HistoryAction;
106
103
  }
107
104
 
108
105
  // Get custom animation name from option or attribute on trigger element
109
- const animation = options.animation || el?.getAttribute('data-swup-animation') || undefined;
110
- if (animation) {
106
+ const animation = options.animation || getContextualAttr(el, 'data-swup-animation');
107
+ if (typeof animation === 'string') {
111
108
  visit.animation.name = animation;
112
109
  }
113
110
 
111
+ // Get custom metadata from option
112
+ visit.meta = options.meta || {};
113
+
114
114
  // Sanitize cache option
115
115
  if (typeof options.cache === 'object') {
116
116
  visit.cache.read = options.cache.read ?? visit.cache.read;
@@ -123,6 +123,7 @@ export async function performNavigation(
123
123
 
124
124
  try {
125
125
  await this.hooks.call('visit:start', visit, undefined);
126
+
126
127
  visit.state = VisitState.STARTED;
127
128
 
128
129
  // Begin loading page
@@ -150,18 +151,16 @@ export async function performNavigation(
150
151
  });
151
152
 
152
153
  // Create/update history record if this is not a popstate call or leads to the same URL
154
+ const newUrl = visit.to.url + visit.to.hash;
153
155
  if (!visit.history.popstate) {
154
- // Add the hash directly from the trigger element
155
- const newUrl = visit.to.url + visit.to.hash;
156
- if (visit.history.action === 'replace' || visit.to.url === this.currentPageUrl) {
156
+ if (visit.history.action === 'replace' || visit.to.url === this.location.url) {
157
157
  updateHistoryRecord(newUrl);
158
158
  } else {
159
159
  this.currentHistoryIndex++;
160
160
  createHistoryRecord(newUrl, { index: this.currentHistoryIndex });
161
161
  }
162
162
  }
163
-
164
- this.currentPageUrl = getCurrentUrl();
163
+ this.location = Location.fromUrl(newUrl);
165
164
 
166
165
  // Mark visit type with classes
167
166
  if (visit.history.popstate) {
@@ -1,4 +1,4 @@
1
- import { updateHistoryRecord, getCurrentUrl, classify } from '../helpers.js';
1
+ import { updateHistoryRecord, getCurrentUrl, classify, Location } from '../helpers.js';
2
2
  import type Swup from '../Swup.js';
3
3
  import type { PageData } from './fetchPage.js';
4
4
  import { VisitState, type Visit } from './Visit.js';
@@ -17,8 +17,9 @@ export const renderPage = async function (this: Swup, visit: Visit, page: PageDa
17
17
  // update state if the url was redirected
18
18
  if (!this.isSameResolvedUrl(getCurrentUrl(), url)) {
19
19
  updateHistoryRecord(url);
20
- this.currentPageUrl = getCurrentUrl();
21
- visit.to.url = this.currentPageUrl;
20
+ this.location = Location.fromUrl(url);
21
+ visit.to.url = this.location.url;
22
+ visit.to.hash = this.location.hash;
22
23
  }
23
24
 
24
25
  // replace content: allow handlers and plugins to overwrite paga data and containers
@@ -46,5 +47,5 @@ export const renderPage = async function (this: Swup, visit: Visit, page: PageDa
46
47
  return this.scrollToContent(visit);
47
48
  });
48
49
 
49
- await this.hooks.call('page:view', visit, { url: this.currentPageUrl, title: document.title });
50
+ await this.hooks.call('page:view', visit, { url: this.location.url, title: document.title });
50
51
  };
@@ -52,3 +52,17 @@ export function forceReflow(element?: HTMLElement): void {
52
52
  element = element || document.body;
53
53
  element?.getBoundingClientRect();
54
54
  }
55
+
56
+ /**
57
+ * Read data attribute from closest element with that attribute.
58
+ *
59
+ * Returns `undefined` if no element is found or attribute is missing.
60
+ * Returns `true` if attribute is present without a value.
61
+ */
62
+ export function getContextualAttr(
63
+ el: Element | undefined,
64
+ attr: string
65
+ ): string | boolean | undefined {
66
+ const target = el?.closest(`[${attr}]`);
67
+ return target?.hasAttribute(attr) ? target?.getAttribute(attr) || true : undefined;
68
+ }