@hotwired/turbo 8.0.13 → 8.0.17
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/CHANGELOG.md +3 -0
- package/dist/turbo.es2017-esm.js +113 -30
- package/dist/turbo.es2017-umd.js +116 -29
- package/package.json +4 -4
package/CHANGELOG.md
ADDED
package/dist/turbo.es2017-esm.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
Turbo 8.0.
|
|
2
|
+
Turbo 8.0.14
|
|
3
3
|
Copyright © 2025 37signals LLC
|
|
4
4
|
*/
|
|
5
5
|
/**
|
|
@@ -555,7 +555,13 @@ function doesNotTargetIFrame(name) {
|
|
|
555
555
|
}
|
|
556
556
|
|
|
557
557
|
function findLinkFromClickTarget(target) {
|
|
558
|
-
|
|
558
|
+
const link = findClosestRecursively(target, "a[href], a[xlink\\:href]");
|
|
559
|
+
|
|
560
|
+
if (!link) return null
|
|
561
|
+
if (link.hasAttribute("download")) return null
|
|
562
|
+
if (link.hasAttribute("target") && link.target !== "_self") return null
|
|
563
|
+
|
|
564
|
+
return link
|
|
559
565
|
}
|
|
560
566
|
|
|
561
567
|
function getLocationForLink(link) {
|
|
@@ -642,8 +648,8 @@ function getExtension(url) {
|
|
|
642
648
|
}
|
|
643
649
|
|
|
644
650
|
function isPrefixedBy(baseURL, url) {
|
|
645
|
-
const prefix =
|
|
646
|
-
return baseURL.href ===
|
|
651
|
+
const prefix = addTrailingSlash(url.origin + url.pathname);
|
|
652
|
+
return addTrailingSlash(baseURL.href) === prefix || baseURL.href.startsWith(prefix)
|
|
647
653
|
}
|
|
648
654
|
|
|
649
655
|
function locationIsVisitable(location, rootLocation) {
|
|
@@ -671,10 +677,6 @@ function getLastPathComponent(url) {
|
|
|
671
677
|
return getPathComponents(url).slice(-1)[0]
|
|
672
678
|
}
|
|
673
679
|
|
|
674
|
-
function getPrefix(url) {
|
|
675
|
-
return addTrailingSlash(url.origin + url.pathname)
|
|
676
|
-
}
|
|
677
|
-
|
|
678
680
|
function addTrailingSlash(value) {
|
|
679
681
|
return value.endsWith("/") ? value : value + "/"
|
|
680
682
|
}
|
|
@@ -755,15 +757,13 @@ class LimitedSet extends Set {
|
|
|
755
757
|
|
|
756
758
|
const recentRequests = new LimitedSet(20);
|
|
757
759
|
|
|
758
|
-
const nativeFetch = window.fetch;
|
|
759
|
-
|
|
760
760
|
function fetchWithTurboHeaders(url, options = {}) {
|
|
761
761
|
const modifiedHeaders = new Headers(options.headers || {});
|
|
762
762
|
const requestUID = uuid();
|
|
763
763
|
recentRequests.add(requestUID);
|
|
764
764
|
modifiedHeaders.append("X-Turbo-Request-Id", requestUID);
|
|
765
765
|
|
|
766
|
-
return
|
|
766
|
+
return window.fetch(url, {
|
|
767
767
|
...options,
|
|
768
768
|
headers: modifiedHeaders
|
|
769
769
|
})
|
|
@@ -1499,8 +1499,8 @@ class View {
|
|
|
1499
1499
|
scrollToAnchor(anchor) {
|
|
1500
1500
|
const element = this.snapshot.getElementForAnchor(anchor);
|
|
1501
1501
|
if (element) {
|
|
1502
|
-
this.scrollToElement(element);
|
|
1503
1502
|
this.focusElement(element);
|
|
1503
|
+
this.scrollToElement(element);
|
|
1504
1504
|
} else {
|
|
1505
1505
|
this.scrollToPosition({ x: 0, y: 0 });
|
|
1506
1506
|
}
|
|
@@ -3332,6 +3332,14 @@ var Idiomorph = (function () {
|
|
|
3332
3332
|
};
|
|
3333
3333
|
})();
|
|
3334
3334
|
|
|
3335
|
+
/**
|
|
3336
|
+
* Morph the state of the currentElement based on the attributes and contents of
|
|
3337
|
+
* the newElement. Morphing may dispatch turbo:before-morph-element,
|
|
3338
|
+
* turbo:before-morph-attribute, and turbo:morph-element events.
|
|
3339
|
+
*
|
|
3340
|
+
* @param currentElement Element destination of morphing changes
|
|
3341
|
+
* @param newElement Element source of morphing changes
|
|
3342
|
+
*/
|
|
3335
3343
|
function morphElements(currentElement, newElement, { callbacks, ...options } = {}) {
|
|
3336
3344
|
Idiomorph.morph(currentElement, newElement, {
|
|
3337
3345
|
...options,
|
|
@@ -3339,12 +3347,37 @@ function morphElements(currentElement, newElement, { callbacks, ...options } = {
|
|
|
3339
3347
|
});
|
|
3340
3348
|
}
|
|
3341
3349
|
|
|
3342
|
-
|
|
3350
|
+
/**
|
|
3351
|
+
* Morph the child elements of the currentElement based on the child elements of
|
|
3352
|
+
* the newElement. Morphing children may dispatch turbo:before-morph-element,
|
|
3353
|
+
* turbo:before-morph-attribute, and turbo:morph-element events.
|
|
3354
|
+
*
|
|
3355
|
+
* @param currentElement Element destination of morphing children changes
|
|
3356
|
+
* @param newElement Element source of morphing children changes
|
|
3357
|
+
*/
|
|
3358
|
+
function morphChildren(currentElement, newElement, options = {}) {
|
|
3343
3359
|
morphElements(currentElement, newElement.childNodes, {
|
|
3360
|
+
...options,
|
|
3344
3361
|
morphStyle: "innerHTML"
|
|
3345
3362
|
});
|
|
3346
3363
|
}
|
|
3347
3364
|
|
|
3365
|
+
function shouldRefreshFrameWithMorphing(currentFrame, newFrame) {
|
|
3366
|
+
return currentFrame instanceof FrameElement &&
|
|
3367
|
+
// newFrame cannot yet be an instance of FrameElement because custom
|
|
3368
|
+
// elements don't get initialized until they're attached to the DOM, so
|
|
3369
|
+
// test its Element#nodeName instead
|
|
3370
|
+
newFrame instanceof Element && newFrame.nodeName === "TURBO-FRAME" &&
|
|
3371
|
+
currentFrame.shouldReloadWithMorph &&
|
|
3372
|
+
currentFrame.id === newFrame.id &&
|
|
3373
|
+
(!newFrame.getAttribute("src") || urlsAreEqual(currentFrame.src, newFrame.getAttribute("src"))) &&
|
|
3374
|
+
!currentFrame.closest("[data-turbo-permanent]")
|
|
3375
|
+
}
|
|
3376
|
+
|
|
3377
|
+
function closestFrameReloadableWithMorphing(node) {
|
|
3378
|
+
return node.parentElement.closest("turbo-frame[src][refresh=morph]")
|
|
3379
|
+
}
|
|
3380
|
+
|
|
3348
3381
|
class DefaultIdiomorphCallbacks {
|
|
3349
3382
|
#beforeNodeMorphed
|
|
3350
3383
|
|
|
@@ -3403,7 +3436,20 @@ class MorphingFrameRenderer extends FrameRenderer {
|
|
|
3403
3436
|
detail: { currentElement, newElement }
|
|
3404
3437
|
});
|
|
3405
3438
|
|
|
3406
|
-
morphChildren(currentElement, newElement
|
|
3439
|
+
morphChildren(currentElement, newElement, {
|
|
3440
|
+
callbacks: {
|
|
3441
|
+
beforeNodeMorphed: (node, newNode) => {
|
|
3442
|
+
if (
|
|
3443
|
+
shouldRefreshFrameWithMorphing(node, newNode) &&
|
|
3444
|
+
closestFrameReloadableWithMorphing(node) === currentElement
|
|
3445
|
+
) {
|
|
3446
|
+
node.reload();
|
|
3447
|
+
return false
|
|
3448
|
+
}
|
|
3449
|
+
return true
|
|
3450
|
+
}
|
|
3451
|
+
}
|
|
3452
|
+
});
|
|
3407
3453
|
}
|
|
3408
3454
|
|
|
3409
3455
|
async preservingPermanentElements(callback) {
|
|
@@ -3712,7 +3758,8 @@ class PageSnapshot extends Snapshot {
|
|
|
3712
3758
|
}
|
|
3713
3759
|
|
|
3714
3760
|
get prefersViewTransitions() {
|
|
3715
|
-
|
|
3761
|
+
const viewTransitionEnabled = this.getSetting("view-transition") === "true" || this.headSnapshot.getMetaValue("view-transition") === "same-origin";
|
|
3762
|
+
return viewTransitionEnabled && !window.matchMedia("(prefers-reduced-motion: reduce)").matches
|
|
3716
3763
|
}
|
|
3717
3764
|
|
|
3718
3765
|
get shouldMorphPage() {
|
|
@@ -4200,6 +4247,8 @@ class BrowserAdapter {
|
|
|
4200
4247
|
|
|
4201
4248
|
visitStarted(visit) {
|
|
4202
4249
|
this.location = visit.location;
|
|
4250
|
+
this.redirectedToLocation = null;
|
|
4251
|
+
|
|
4203
4252
|
visit.loadCachedSnapshot();
|
|
4204
4253
|
visit.issueRequest();
|
|
4205
4254
|
visit.goToSamePageAnchor();
|
|
@@ -4216,6 +4265,10 @@ class BrowserAdapter {
|
|
|
4216
4265
|
|
|
4217
4266
|
visitRequestCompleted(visit) {
|
|
4218
4267
|
visit.loadResponse();
|
|
4268
|
+
|
|
4269
|
+
if (visit.response.redirected) {
|
|
4270
|
+
this.redirectedToLocation = visit.redirectedToLocation;
|
|
4271
|
+
}
|
|
4219
4272
|
}
|
|
4220
4273
|
|
|
4221
4274
|
visitRequestFailedWithStatusCode(visit, statusCode) {
|
|
@@ -4305,7 +4358,7 @@ class BrowserAdapter {
|
|
|
4305
4358
|
reload(reason) {
|
|
4306
4359
|
dispatch("turbo:reload", { detail: reason });
|
|
4307
4360
|
|
|
4308
|
-
window.location.href = this.location?.toString() || window.location.href;
|
|
4361
|
+
window.location.href = (this.redirectedToLocation || this.location)?.toString() || window.location.href;
|
|
4309
4362
|
}
|
|
4310
4363
|
|
|
4311
4364
|
get navigator() {
|
|
@@ -4618,6 +4671,8 @@ class LinkPrefetchObserver {
|
|
|
4618
4671
|
target
|
|
4619
4672
|
);
|
|
4620
4673
|
|
|
4674
|
+
fetchRequest.fetchOptions.priority = "low";
|
|
4675
|
+
|
|
4621
4676
|
prefetchCache.setLater(location.toString(), fetchRequest, this.#cacheTtl);
|
|
4622
4677
|
}
|
|
4623
4678
|
}
|
|
@@ -5427,14 +5482,19 @@ class MorphingPageRenderer extends PageRenderer {
|
|
|
5427
5482
|
static renderElement(currentElement, newElement) {
|
|
5428
5483
|
morphElements(currentElement, newElement, {
|
|
5429
5484
|
callbacks: {
|
|
5430
|
-
beforeNodeMorphed:
|
|
5485
|
+
beforeNodeMorphed: (node, newNode) => {
|
|
5486
|
+
if (
|
|
5487
|
+
shouldRefreshFrameWithMorphing(node, newNode) &&
|
|
5488
|
+
!closestFrameReloadableWithMorphing(node)
|
|
5489
|
+
) {
|
|
5490
|
+
node.reload();
|
|
5491
|
+
return false
|
|
5492
|
+
}
|
|
5493
|
+
return true
|
|
5494
|
+
}
|
|
5431
5495
|
}
|
|
5432
5496
|
});
|
|
5433
5497
|
|
|
5434
|
-
for (const frame of currentElement.querySelectorAll("turbo-frame")) {
|
|
5435
|
-
if (canRefreshFrame(frame)) frame.reload();
|
|
5436
|
-
}
|
|
5437
|
-
|
|
5438
5498
|
dispatch("turbo:morph", { detail: { currentElement, newElement } });
|
|
5439
5499
|
}
|
|
5440
5500
|
|
|
@@ -5451,13 +5511,6 @@ class MorphingPageRenderer extends PageRenderer {
|
|
|
5451
5511
|
}
|
|
5452
5512
|
}
|
|
5453
5513
|
|
|
5454
|
-
function canRefreshFrame(frame) {
|
|
5455
|
-
return frame instanceof FrameElement &&
|
|
5456
|
-
frame.src &&
|
|
5457
|
-
frame.refresh === "morph" &&
|
|
5458
|
-
!frame.closest("[data-turbo-permanent]")
|
|
5459
|
-
}
|
|
5460
|
-
|
|
5461
5514
|
class SnapshotCache {
|
|
5462
5515
|
keys = []
|
|
5463
5516
|
snapshots = {}
|
|
@@ -6281,6 +6334,32 @@ function setFormMode(mode) {
|
|
|
6281
6334
|
config.forms.mode = mode;
|
|
6282
6335
|
}
|
|
6283
6336
|
|
|
6337
|
+
/**
|
|
6338
|
+
* Morph the state of the currentBody based on the attributes and contents of
|
|
6339
|
+
* the newBody. Morphing body elements may dispatch turbo:morph,
|
|
6340
|
+
* turbo:before-morph-element, turbo:before-morph-attribute, and
|
|
6341
|
+
* turbo:morph-element events.
|
|
6342
|
+
*
|
|
6343
|
+
* @param currentBody HTMLBodyElement destination of morphing changes
|
|
6344
|
+
* @param newBody HTMLBodyElement source of morphing changes
|
|
6345
|
+
*/
|
|
6346
|
+
function morphBodyElements(currentBody, newBody) {
|
|
6347
|
+
MorphingPageRenderer.renderElement(currentBody, newBody);
|
|
6348
|
+
}
|
|
6349
|
+
|
|
6350
|
+
/**
|
|
6351
|
+
* Morph the child elements of the currentFrame based on the child elements of
|
|
6352
|
+
* the newFrame. Morphing turbo-frame elements may dispatch turbo:before-frame-morph,
|
|
6353
|
+
* turbo:before-morph-element, turbo:before-morph-attribute, and
|
|
6354
|
+
* turbo:morph-element events.
|
|
6355
|
+
*
|
|
6356
|
+
* @param currentFrame FrameElement destination of morphing children changes
|
|
6357
|
+
* @param newFrame FrameElement source of morphing children changes
|
|
6358
|
+
*/
|
|
6359
|
+
function morphTurboFrameElements(currentFrame, newFrame) {
|
|
6360
|
+
MorphingFrameRenderer.renderElement(currentFrame, newFrame);
|
|
6361
|
+
}
|
|
6362
|
+
|
|
6284
6363
|
var Turbo = /*#__PURE__*/Object.freeze({
|
|
6285
6364
|
__proto__: null,
|
|
6286
6365
|
navigator: navigator$1,
|
|
@@ -6300,7 +6379,11 @@ var Turbo = /*#__PURE__*/Object.freeze({
|
|
|
6300
6379
|
clearCache: clearCache,
|
|
6301
6380
|
setProgressBarDelay: setProgressBarDelay,
|
|
6302
6381
|
setConfirmMethod: setConfirmMethod,
|
|
6303
|
-
setFormMode: setFormMode
|
|
6382
|
+
setFormMode: setFormMode,
|
|
6383
|
+
morphBodyElements: morphBodyElements,
|
|
6384
|
+
morphTurboFrameElements: morphTurboFrameElements,
|
|
6385
|
+
morphChildren: morphChildren,
|
|
6386
|
+
morphElements: morphElements
|
|
6304
6387
|
});
|
|
6305
6388
|
|
|
6306
6389
|
class TurboFrameMissingError extends Error {}
|
|
@@ -7175,4 +7258,4 @@ if (customElements.get("turbo-stream-source") === undefined) {
|
|
|
7175
7258
|
window.Turbo = { ...Turbo, StreamActions };
|
|
7176
7259
|
start();
|
|
7177
7260
|
|
|
7178
|
-
export { FetchEnctype, FetchMethod, FetchRequest, FetchResponse, FrameElement, FrameLoadingStyle, FrameRenderer, PageRenderer, PageSnapshot, StreamActions, StreamElement, StreamSourceElement, cache, clearCache, config, connectStreamSource, disconnectStreamSource, fetchWithTurboHeaders as fetch, fetchEnctypeFromString, fetchMethodFromString, isSafe, navigator$1 as navigator, registerAdapter, renderStreamMessage, session, setConfirmMethod, setFormMode, setProgressBarDelay, start, visit };
|
|
7261
|
+
export { FetchEnctype, FetchMethod, FetchRequest, FetchResponse, FrameElement, FrameLoadingStyle, FrameRenderer, PageRenderer, PageSnapshot, StreamActions, StreamElement, StreamSourceElement, cache, clearCache, config, connectStreamSource, disconnectStreamSource, fetchWithTurboHeaders as fetch, fetchEnctypeFromString, fetchMethodFromString, isSafe, morphBodyElements, morphChildren, morphElements, morphTurboFrameElements, navigator$1 as navigator, registerAdapter, renderStreamMessage, session, setConfirmMethod, setFormMode, setProgressBarDelay, start, visit };
|
package/dist/turbo.es2017-umd.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
Turbo 8.0.
|
|
2
|
+
Turbo 8.0.14
|
|
3
3
|
Copyright © 2025 37signals LLC
|
|
4
4
|
*/
|
|
5
5
|
(function (global, factory) {
|
|
@@ -561,7 +561,13 @@ Copyright © 2025 37signals LLC
|
|
|
561
561
|
}
|
|
562
562
|
|
|
563
563
|
function findLinkFromClickTarget(target) {
|
|
564
|
-
|
|
564
|
+
const link = findClosestRecursively(target, "a[href], a[xlink\\:href]");
|
|
565
|
+
|
|
566
|
+
if (!link) return null
|
|
567
|
+
if (link.hasAttribute("download")) return null
|
|
568
|
+
if (link.hasAttribute("target") && link.target !== "_self") return null
|
|
569
|
+
|
|
570
|
+
return link
|
|
565
571
|
}
|
|
566
572
|
|
|
567
573
|
function getLocationForLink(link) {
|
|
@@ -648,8 +654,8 @@ Copyright © 2025 37signals LLC
|
|
|
648
654
|
}
|
|
649
655
|
|
|
650
656
|
function isPrefixedBy(baseURL, url) {
|
|
651
|
-
const prefix =
|
|
652
|
-
return baseURL.href ===
|
|
657
|
+
const prefix = addTrailingSlash(url.origin + url.pathname);
|
|
658
|
+
return addTrailingSlash(baseURL.href) === prefix || baseURL.href.startsWith(prefix)
|
|
653
659
|
}
|
|
654
660
|
|
|
655
661
|
function locationIsVisitable(location, rootLocation) {
|
|
@@ -677,10 +683,6 @@ Copyright © 2025 37signals LLC
|
|
|
677
683
|
return getPathComponents(url).slice(-1)[0]
|
|
678
684
|
}
|
|
679
685
|
|
|
680
|
-
function getPrefix(url) {
|
|
681
|
-
return addTrailingSlash(url.origin + url.pathname)
|
|
682
|
-
}
|
|
683
|
-
|
|
684
686
|
function addTrailingSlash(value) {
|
|
685
687
|
return value.endsWith("/") ? value : value + "/"
|
|
686
688
|
}
|
|
@@ -761,15 +763,13 @@ Copyright © 2025 37signals LLC
|
|
|
761
763
|
|
|
762
764
|
const recentRequests = new LimitedSet(20);
|
|
763
765
|
|
|
764
|
-
const nativeFetch = window.fetch;
|
|
765
|
-
|
|
766
766
|
function fetchWithTurboHeaders(url, options = {}) {
|
|
767
767
|
const modifiedHeaders = new Headers(options.headers || {});
|
|
768
768
|
const requestUID = uuid();
|
|
769
769
|
recentRequests.add(requestUID);
|
|
770
770
|
modifiedHeaders.append("X-Turbo-Request-Id", requestUID);
|
|
771
771
|
|
|
772
|
-
return
|
|
772
|
+
return window.fetch(url, {
|
|
773
773
|
...options,
|
|
774
774
|
headers: modifiedHeaders
|
|
775
775
|
})
|
|
@@ -1505,8 +1505,8 @@ Copyright © 2025 37signals LLC
|
|
|
1505
1505
|
scrollToAnchor(anchor) {
|
|
1506
1506
|
const element = this.snapshot.getElementForAnchor(anchor);
|
|
1507
1507
|
if (element) {
|
|
1508
|
-
this.scrollToElement(element);
|
|
1509
1508
|
this.focusElement(element);
|
|
1509
|
+
this.scrollToElement(element);
|
|
1510
1510
|
} else {
|
|
1511
1511
|
this.scrollToPosition({ x: 0, y: 0 });
|
|
1512
1512
|
}
|
|
@@ -3338,6 +3338,14 @@ Copyright © 2025 37signals LLC
|
|
|
3338
3338
|
};
|
|
3339
3339
|
})();
|
|
3340
3340
|
|
|
3341
|
+
/**
|
|
3342
|
+
* Morph the state of the currentElement based on the attributes and contents of
|
|
3343
|
+
* the newElement. Morphing may dispatch turbo:before-morph-element,
|
|
3344
|
+
* turbo:before-morph-attribute, and turbo:morph-element events.
|
|
3345
|
+
*
|
|
3346
|
+
* @param currentElement Element destination of morphing changes
|
|
3347
|
+
* @param newElement Element source of morphing changes
|
|
3348
|
+
*/
|
|
3341
3349
|
function morphElements(currentElement, newElement, { callbacks, ...options } = {}) {
|
|
3342
3350
|
Idiomorph.morph(currentElement, newElement, {
|
|
3343
3351
|
...options,
|
|
@@ -3345,12 +3353,37 @@ Copyright © 2025 37signals LLC
|
|
|
3345
3353
|
});
|
|
3346
3354
|
}
|
|
3347
3355
|
|
|
3348
|
-
|
|
3356
|
+
/**
|
|
3357
|
+
* Morph the child elements of the currentElement based on the child elements of
|
|
3358
|
+
* the newElement. Morphing children may dispatch turbo:before-morph-element,
|
|
3359
|
+
* turbo:before-morph-attribute, and turbo:morph-element events.
|
|
3360
|
+
*
|
|
3361
|
+
* @param currentElement Element destination of morphing children changes
|
|
3362
|
+
* @param newElement Element source of morphing children changes
|
|
3363
|
+
*/
|
|
3364
|
+
function morphChildren(currentElement, newElement, options = {}) {
|
|
3349
3365
|
morphElements(currentElement, newElement.childNodes, {
|
|
3366
|
+
...options,
|
|
3350
3367
|
morphStyle: "innerHTML"
|
|
3351
3368
|
});
|
|
3352
3369
|
}
|
|
3353
3370
|
|
|
3371
|
+
function shouldRefreshFrameWithMorphing(currentFrame, newFrame) {
|
|
3372
|
+
return currentFrame instanceof FrameElement &&
|
|
3373
|
+
// newFrame cannot yet be an instance of FrameElement because custom
|
|
3374
|
+
// elements don't get initialized until they're attached to the DOM, so
|
|
3375
|
+
// test its Element#nodeName instead
|
|
3376
|
+
newFrame instanceof Element && newFrame.nodeName === "TURBO-FRAME" &&
|
|
3377
|
+
currentFrame.shouldReloadWithMorph &&
|
|
3378
|
+
currentFrame.id === newFrame.id &&
|
|
3379
|
+
(!newFrame.getAttribute("src") || urlsAreEqual(currentFrame.src, newFrame.getAttribute("src"))) &&
|
|
3380
|
+
!currentFrame.closest("[data-turbo-permanent]")
|
|
3381
|
+
}
|
|
3382
|
+
|
|
3383
|
+
function closestFrameReloadableWithMorphing(node) {
|
|
3384
|
+
return node.parentElement.closest("turbo-frame[src][refresh=morph]")
|
|
3385
|
+
}
|
|
3386
|
+
|
|
3354
3387
|
class DefaultIdiomorphCallbacks {
|
|
3355
3388
|
#beforeNodeMorphed
|
|
3356
3389
|
|
|
@@ -3409,7 +3442,20 @@ Copyright © 2025 37signals LLC
|
|
|
3409
3442
|
detail: { currentElement, newElement }
|
|
3410
3443
|
});
|
|
3411
3444
|
|
|
3412
|
-
morphChildren(currentElement, newElement
|
|
3445
|
+
morphChildren(currentElement, newElement, {
|
|
3446
|
+
callbacks: {
|
|
3447
|
+
beforeNodeMorphed: (node, newNode) => {
|
|
3448
|
+
if (
|
|
3449
|
+
shouldRefreshFrameWithMorphing(node, newNode) &&
|
|
3450
|
+
closestFrameReloadableWithMorphing(node) === currentElement
|
|
3451
|
+
) {
|
|
3452
|
+
node.reload();
|
|
3453
|
+
return false
|
|
3454
|
+
}
|
|
3455
|
+
return true
|
|
3456
|
+
}
|
|
3457
|
+
}
|
|
3458
|
+
});
|
|
3413
3459
|
}
|
|
3414
3460
|
|
|
3415
3461
|
async preservingPermanentElements(callback) {
|
|
@@ -3718,7 +3764,8 @@ Copyright © 2025 37signals LLC
|
|
|
3718
3764
|
}
|
|
3719
3765
|
|
|
3720
3766
|
get prefersViewTransitions() {
|
|
3721
|
-
|
|
3767
|
+
const viewTransitionEnabled = this.getSetting("view-transition") === "true" || this.headSnapshot.getMetaValue("view-transition") === "same-origin";
|
|
3768
|
+
return viewTransitionEnabled && !window.matchMedia("(prefers-reduced-motion: reduce)").matches
|
|
3722
3769
|
}
|
|
3723
3770
|
|
|
3724
3771
|
get shouldMorphPage() {
|
|
@@ -4206,6 +4253,8 @@ Copyright © 2025 37signals LLC
|
|
|
4206
4253
|
|
|
4207
4254
|
visitStarted(visit) {
|
|
4208
4255
|
this.location = visit.location;
|
|
4256
|
+
this.redirectedToLocation = null;
|
|
4257
|
+
|
|
4209
4258
|
visit.loadCachedSnapshot();
|
|
4210
4259
|
visit.issueRequest();
|
|
4211
4260
|
visit.goToSamePageAnchor();
|
|
@@ -4222,6 +4271,10 @@ Copyright © 2025 37signals LLC
|
|
|
4222
4271
|
|
|
4223
4272
|
visitRequestCompleted(visit) {
|
|
4224
4273
|
visit.loadResponse();
|
|
4274
|
+
|
|
4275
|
+
if (visit.response.redirected) {
|
|
4276
|
+
this.redirectedToLocation = visit.redirectedToLocation;
|
|
4277
|
+
}
|
|
4225
4278
|
}
|
|
4226
4279
|
|
|
4227
4280
|
visitRequestFailedWithStatusCode(visit, statusCode) {
|
|
@@ -4311,7 +4364,7 @@ Copyright © 2025 37signals LLC
|
|
|
4311
4364
|
reload(reason) {
|
|
4312
4365
|
dispatch("turbo:reload", { detail: reason });
|
|
4313
4366
|
|
|
4314
|
-
window.location.href = this.location?.toString() || window.location.href;
|
|
4367
|
+
window.location.href = (this.redirectedToLocation || this.location)?.toString() || window.location.href;
|
|
4315
4368
|
}
|
|
4316
4369
|
|
|
4317
4370
|
get navigator() {
|
|
@@ -4624,6 +4677,8 @@ Copyright © 2025 37signals LLC
|
|
|
4624
4677
|
target
|
|
4625
4678
|
);
|
|
4626
4679
|
|
|
4680
|
+
fetchRequest.fetchOptions.priority = "low";
|
|
4681
|
+
|
|
4627
4682
|
prefetchCache.setLater(location.toString(), fetchRequest, this.#cacheTtl);
|
|
4628
4683
|
}
|
|
4629
4684
|
}
|
|
@@ -5433,14 +5488,19 @@ Copyright © 2025 37signals LLC
|
|
|
5433
5488
|
static renderElement(currentElement, newElement) {
|
|
5434
5489
|
morphElements(currentElement, newElement, {
|
|
5435
5490
|
callbacks: {
|
|
5436
|
-
beforeNodeMorphed:
|
|
5491
|
+
beforeNodeMorphed: (node, newNode) => {
|
|
5492
|
+
if (
|
|
5493
|
+
shouldRefreshFrameWithMorphing(node, newNode) &&
|
|
5494
|
+
!closestFrameReloadableWithMorphing(node)
|
|
5495
|
+
) {
|
|
5496
|
+
node.reload();
|
|
5497
|
+
return false
|
|
5498
|
+
}
|
|
5499
|
+
return true
|
|
5500
|
+
}
|
|
5437
5501
|
}
|
|
5438
5502
|
});
|
|
5439
5503
|
|
|
5440
|
-
for (const frame of currentElement.querySelectorAll("turbo-frame")) {
|
|
5441
|
-
if (canRefreshFrame(frame)) frame.reload();
|
|
5442
|
-
}
|
|
5443
|
-
|
|
5444
5504
|
dispatch("turbo:morph", { detail: { currentElement, newElement } });
|
|
5445
5505
|
}
|
|
5446
5506
|
|
|
@@ -5457,13 +5517,6 @@ Copyright © 2025 37signals LLC
|
|
|
5457
5517
|
}
|
|
5458
5518
|
}
|
|
5459
5519
|
|
|
5460
|
-
function canRefreshFrame(frame) {
|
|
5461
|
-
return frame instanceof FrameElement &&
|
|
5462
|
-
frame.src &&
|
|
5463
|
-
frame.refresh === "morph" &&
|
|
5464
|
-
!frame.closest("[data-turbo-permanent]")
|
|
5465
|
-
}
|
|
5466
|
-
|
|
5467
5520
|
class SnapshotCache {
|
|
5468
5521
|
keys = []
|
|
5469
5522
|
snapshots = {}
|
|
@@ -6287,6 +6340,32 @@ Copyright © 2025 37signals LLC
|
|
|
6287
6340
|
config.forms.mode = mode;
|
|
6288
6341
|
}
|
|
6289
6342
|
|
|
6343
|
+
/**
|
|
6344
|
+
* Morph the state of the currentBody based on the attributes and contents of
|
|
6345
|
+
* the newBody. Morphing body elements may dispatch turbo:morph,
|
|
6346
|
+
* turbo:before-morph-element, turbo:before-morph-attribute, and
|
|
6347
|
+
* turbo:morph-element events.
|
|
6348
|
+
*
|
|
6349
|
+
* @param currentBody HTMLBodyElement destination of morphing changes
|
|
6350
|
+
* @param newBody HTMLBodyElement source of morphing changes
|
|
6351
|
+
*/
|
|
6352
|
+
function morphBodyElements(currentBody, newBody) {
|
|
6353
|
+
MorphingPageRenderer.renderElement(currentBody, newBody);
|
|
6354
|
+
}
|
|
6355
|
+
|
|
6356
|
+
/**
|
|
6357
|
+
* Morph the child elements of the currentFrame based on the child elements of
|
|
6358
|
+
* the newFrame. Morphing turbo-frame elements may dispatch turbo:before-frame-morph,
|
|
6359
|
+
* turbo:before-morph-element, turbo:before-morph-attribute, and
|
|
6360
|
+
* turbo:morph-element events.
|
|
6361
|
+
*
|
|
6362
|
+
* @param currentFrame FrameElement destination of morphing children changes
|
|
6363
|
+
* @param newFrame FrameElement source of morphing children changes
|
|
6364
|
+
*/
|
|
6365
|
+
function morphTurboFrameElements(currentFrame, newFrame) {
|
|
6366
|
+
MorphingFrameRenderer.renderElement(currentFrame, newFrame);
|
|
6367
|
+
}
|
|
6368
|
+
|
|
6290
6369
|
var Turbo = /*#__PURE__*/Object.freeze({
|
|
6291
6370
|
__proto__: null,
|
|
6292
6371
|
navigator: navigator$1,
|
|
@@ -6306,7 +6385,11 @@ Copyright © 2025 37signals LLC
|
|
|
6306
6385
|
clearCache: clearCache,
|
|
6307
6386
|
setProgressBarDelay: setProgressBarDelay,
|
|
6308
6387
|
setConfirmMethod: setConfirmMethod,
|
|
6309
|
-
setFormMode: setFormMode
|
|
6388
|
+
setFormMode: setFormMode,
|
|
6389
|
+
morphBodyElements: morphBodyElements,
|
|
6390
|
+
morphTurboFrameElements: morphTurboFrameElements,
|
|
6391
|
+
morphChildren: morphChildren,
|
|
6392
|
+
morphElements: morphElements
|
|
6310
6393
|
});
|
|
6311
6394
|
|
|
6312
6395
|
class TurboFrameMissingError extends Error {}
|
|
@@ -7202,6 +7285,10 @@ Copyright © 2025 37signals LLC
|
|
|
7202
7285
|
exports.fetchEnctypeFromString = fetchEnctypeFromString;
|
|
7203
7286
|
exports.fetchMethodFromString = fetchMethodFromString;
|
|
7204
7287
|
exports.isSafe = isSafe;
|
|
7288
|
+
exports.morphBodyElements = morphBodyElements;
|
|
7289
|
+
exports.morphChildren = morphChildren;
|
|
7290
|
+
exports.morphElements = morphElements;
|
|
7291
|
+
exports.morphTurboFrameElements = morphTurboFrameElements;
|
|
7205
7292
|
exports.navigator = navigator$1;
|
|
7206
7293
|
exports.registerAdapter = registerAdapter;
|
|
7207
7294
|
exports.renderStreamMessage = renderStreamMessage;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hotwired/turbo",
|
|
3
|
-
"version": "8.0.
|
|
3
|
+
"version": "8.0.17",
|
|
4
4
|
"description": "The speed of a single-page web application without having to write any JavaScript",
|
|
5
5
|
"module": "dist/turbo.es2017-esm.js",
|
|
6
6
|
"main": "dist/turbo.es2017-umd.js",
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
36
|
"@open-wc/testing": "^3.1.7",
|
|
37
|
-
"@playwright/test": "~1.
|
|
37
|
+
"@playwright/test": "~1.51.1",
|
|
38
38
|
"@rollup/plugin-node-resolve": "13.1.3",
|
|
39
39
|
"@web/dev-server-esbuild": "^0.3.3",
|
|
40
40
|
"@web/test-runner": "^0.15.0",
|
|
@@ -59,10 +59,10 @@
|
|
|
59
59
|
"test:browser": "playwright test",
|
|
60
60
|
"test:unit": "NODE_OPTIONS=--inspect web-test-runner",
|
|
61
61
|
"test:unit:win": "SET NODE_OPTIONS=--inspect & web-test-runner",
|
|
62
|
-
"release": "yarn build &&
|
|
62
|
+
"release": "yarn build && yarn publish",
|
|
63
63
|
"lint": "eslint . --ext .js"
|
|
64
64
|
},
|
|
65
65
|
"engines": {
|
|
66
|
-
"node": ">=
|
|
66
|
+
"node": ">= 18"
|
|
67
67
|
}
|
|
68
68
|
}
|