turbo-rails 2.0.16 → 2.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7de922709135b46c78da967fc906370e96bace376d0c03b205b5b7fb0bf0c1db
4
- data.tar.gz: 36c2463b1a996f8d225a3ba33eb73bb07d2c34eccf8316a4670cebca2ed1c90b
3
+ metadata.gz: 5324994da4e834ec4efc08d381586e0deb95b1c49131601976f40711cf9cb7a5
4
+ data.tar.gz: d425c87cee99f1756786bc72b26a6a3151006f8409e35432ceb5795e0c054fdb
5
5
  SHA512:
6
- metadata.gz: 88303af9181adb99632e23da782c7ab7063c7ce194271b3b9dbf92d89d1e1d2cd3cf1a88147f38ca957d74f7ff310cbf455e3c636cf705ef76cc46b6583b84b1
7
- data.tar.gz: '02446187359ca3060de17e6bc435f159d5d5aa98afee83280487372bf63a70b901a44314119119ee9ff4947fba8b93d971d6f143815240cbd0bed9dcd5a7d69e'
6
+ metadata.gz: 553197bfc4ebc3fa859d7678c7ea845625884e8859698a89c66ecf81e79559c4baa2c670f3a9401742cf2d01ad0965f4f729142c03cab579a5df971dfbc96f7b
7
+ data.tar.gz: ba86c285da8476f788d4dd6990dd7e632914dbdef83317799ae83839dc45d3465da477ced36f528d187208972d9f653cec533e1d42ed8eba6a130787823c07f9
data/README.md CHANGED
@@ -206,7 +206,7 @@ Turbo can coexist with Rails UJS, but you need to take a series of upgrade steps
206
206
 
207
207
  ## Testing
208
208
 
209
- The [`Turbo::TestAssertions`](./lib/turbo/test_assertions.rb) concern provides Turbo Stream test helpers that assert the presence or absence ofs s `<turbo-stream>` elements in a rendered fragment of HTML. `Turbo::TestAssertions` are automatically included in [`ActiveSupport::TestCase`](https://edgeapi.rubyonrails.org/classes/ActiveSupport/TestCase.html) and depend on the presence of [`rails-dom-testing`](https://github.com/rails/rails-dom-testing/) assertions.
209
+ The [`Turbo::TestAssertions`](./lib/turbo/test_assertions.rb) concern provides Turbo Stream test helpers that assert the presence or absence of `<turbo-stream>` elements in a rendered fragment of HTML. `Turbo::TestAssertions` are automatically included in [`ActiveSupport::TestCase`](https://edgeapi.rubyonrails.org/classes/ActiveSupport/TestCase.html) and depend on the presence of [`rails-dom-testing`](https://github.com/rails/rails-dom-testing/) assertions.
210
210
 
211
211
  The [`Turbo::TestAssertions::IntegrationTestAssertions`](./lib/turbo/test_assertions/integration_test_assertions.rb) are built on top of `Turbo::TestAssertions`, and add support for passing a `status:` keyword. They are automatically included in [`ActionDispatch::IntegrationTest`](https://edgeguides.rubyonrails.org/testing.html#integration-testing).
212
212
 
@@ -1,5 +1,5 @@
1
1
  /*!
2
- Turbo 8.0.13
2
+ Turbo 8.0.17
3
3
  Copyright © 2025 37signals LLC
4
4
  */
5
5
  (function(prototype) {
@@ -409,7 +409,11 @@ function doesNotTargetIFrame(name) {
409
409
  }
410
410
 
411
411
  function findLinkFromClickTarget(target) {
412
- return findClosestRecursively(target, "a[href]:not([target^=_]):not([download])");
412
+ const link = findClosestRecursively(target, "a[href], a[xlink\\:href]");
413
+ if (!link) return null;
414
+ if (link.hasAttribute("download")) return null;
415
+ if (link.hasAttribute("target") && link.target !== "_self") return null;
416
+ return link;
413
417
  }
414
418
 
415
419
  function getLocationForLink(link) {
@@ -488,8 +492,8 @@ function getExtension(url) {
488
492
  }
489
493
 
490
494
  function isPrefixedBy(baseURL, url) {
491
- const prefix = getPrefix(url);
492
- return baseURL.href === expandURL(prefix).href || baseURL.href.startsWith(prefix);
495
+ const prefix = addTrailingSlash(url.origin + url.pathname);
496
+ return addTrailingSlash(baseURL.href) === prefix || baseURL.href.startsWith(prefix);
493
497
  }
494
498
 
495
499
  function locationIsVisitable(location, rootLocation) {
@@ -517,10 +521,6 @@ function getLastPathComponent(url) {
517
521
  return getPathComponents(url).slice(-1)[0];
518
522
  }
519
523
 
520
- function getPrefix(url) {
521
- return addTrailingSlash(url.origin + url.pathname);
522
- }
523
-
524
524
  function addTrailingSlash(value) {
525
525
  return value.endsWith("/") ? value : value + "/";
526
526
  }
@@ -588,14 +588,12 @@ class LimitedSet extends Set {
588
588
 
589
589
  const recentRequests = new LimitedSet(20);
590
590
 
591
- const nativeFetch = window.fetch;
592
-
593
591
  function fetchWithTurboHeaders(url, options = {}) {
594
592
  const modifiedHeaders = new Headers(options.headers || {});
595
593
  const requestUID = uuid();
596
594
  recentRequests.add(requestUID);
597
595
  modifiedHeaders.append("X-Turbo-Request-Id", requestUID);
598
- return nativeFetch(url, {
596
+ return window.fetch(url, {
599
597
  ...options,
600
598
  headers: modifiedHeaders
601
599
  });
@@ -1243,8 +1241,8 @@ class View {
1243
1241
  scrollToAnchor(anchor) {
1244
1242
  const element = this.snapshot.getElementForAnchor(anchor);
1245
1243
  if (element) {
1246
- this.scrollToElement(element);
1247
1244
  this.focusElement(element);
1245
+ this.scrollToElement(element);
1248
1246
  } else {
1249
1247
  this.scrollToPosition({
1250
1248
  x: 0,
@@ -2281,12 +2279,21 @@ function morphElements(currentElement, newElement, {callbacks: callbacks, ...opt
2281
2279
  });
2282
2280
  }
2283
2281
 
2284
- function morphChildren(currentElement, newElement) {
2282
+ function morphChildren(currentElement, newElement, options = {}) {
2285
2283
  morphElements(currentElement, newElement.childNodes, {
2284
+ ...options,
2286
2285
  morphStyle: "innerHTML"
2287
2286
  });
2288
2287
  }
2289
2288
 
2289
+ function shouldRefreshFrameWithMorphing(currentFrame, newFrame) {
2290
+ return currentFrame instanceof FrameElement && newFrame instanceof Element && newFrame.nodeName === "TURBO-FRAME" && currentFrame.shouldReloadWithMorph && currentFrame.id === newFrame.id && (!newFrame.getAttribute("src") || urlsAreEqual(currentFrame.src, newFrame.getAttribute("src"))) && !currentFrame.closest("[data-turbo-permanent]");
2291
+ }
2292
+
2293
+ function closestFrameReloadableWithMorphing(node) {
2294
+ return node.parentElement.closest("turbo-frame[src][refresh=morph]");
2295
+ }
2296
+
2290
2297
  class DefaultIdiomorphCallbacks {
2291
2298
  #beforeNodeMorphed;
2292
2299
  constructor({beforeNodeMorphed: beforeNodeMorphed} = {}) {
@@ -2344,7 +2351,17 @@ class MorphingFrameRenderer extends FrameRenderer {
2344
2351
  newElement: newElement
2345
2352
  }
2346
2353
  });
2347
- morphChildren(currentElement, newElement);
2354
+ morphChildren(currentElement, newElement, {
2355
+ callbacks: {
2356
+ beforeNodeMorphed: (node, newNode) => {
2357
+ if (shouldRefreshFrameWithMorphing(node, newNode) && closestFrameReloadableWithMorphing(node) === currentElement) {
2358
+ node.reload();
2359
+ return false;
2360
+ }
2361
+ return true;
2362
+ }
2363
+ }
2364
+ });
2348
2365
  }
2349
2366
  async preservingPermanentElements(callback) {
2350
2367
  return await callback();
@@ -2596,7 +2613,8 @@ class PageSnapshot extends Snapshot {
2596
2613
  return this.getSetting("visit-control") != "reload";
2597
2614
  }
2598
2615
  get prefersViewTransitions() {
2599
- return this.headSnapshot.getMetaValue("view-transition") === "same-origin";
2616
+ const viewTransitionEnabled = this.getSetting("view-transition") === "true" || this.headSnapshot.getMetaValue("view-transition") === "same-origin";
2617
+ return viewTransitionEnabled && !window.matchMedia("(prefers-reduced-motion: reduce)").matches;
2600
2618
  }
2601
2619
  get shouldMorphPage() {
2602
2620
  return this.getSetting("refresh-method") === "morph";
@@ -3011,6 +3029,7 @@ class BrowserAdapter {
3011
3029
  }
3012
3030
  visitStarted(visit) {
3013
3031
  this.location = visit.location;
3032
+ this.redirectedToLocation = null;
3014
3033
  visit.loadCachedSnapshot();
3015
3034
  visit.issueRequest();
3016
3035
  visit.goToSamePageAnchor();
@@ -3025,6 +3044,9 @@ class BrowserAdapter {
3025
3044
  }
3026
3045
  visitRequestCompleted(visit) {
3027
3046
  visit.loadResponse();
3047
+ if (visit.response.redirected) {
3048
+ this.redirectedToLocation = visit.redirectedToLocation;
3049
+ }
3028
3050
  }
3029
3051
  visitRequestFailedWithStatusCode(visit, statusCode) {
3030
3052
  switch (statusCode) {
@@ -3095,7 +3117,7 @@ class BrowserAdapter {
3095
3117
  dispatch("turbo:reload", {
3096
3118
  detail: reason
3097
3119
  });
3098
- window.location.href = this.location?.toString() || window.location.href;
3120
+ window.location.href = (this.redirectedToLocation || this.location)?.toString() || window.location.href;
3099
3121
  }
3100
3122
  get navigator() {
3101
3123
  return this.session.navigator;
@@ -3338,6 +3360,7 @@ class LinkPrefetchObserver {
3338
3360
  if (this.delegate.canPrefetchRequestToLocation(link, location)) {
3339
3361
  this.#prefetchedLink = link;
3340
3362
  const fetchRequest = new FetchRequest(this, FetchMethod.get, location, new URLSearchParams, target);
3363
+ fetchRequest.fetchOptions.priority = "low";
3341
3364
  prefetchCache.setLater(location.toString(), fetchRequest, this.#cacheTtl);
3342
3365
  }
3343
3366
  }
@@ -3988,12 +4011,15 @@ class MorphingPageRenderer extends PageRenderer {
3988
4011
  static renderElement(currentElement, newElement) {
3989
4012
  morphElements(currentElement, newElement, {
3990
4013
  callbacks: {
3991
- beforeNodeMorphed: element => !canRefreshFrame(element)
4014
+ beforeNodeMorphed: (node, newNode) => {
4015
+ if (shouldRefreshFrameWithMorphing(node, newNode) && !closestFrameReloadableWithMorphing(node)) {
4016
+ node.reload();
4017
+ return false;
4018
+ }
4019
+ return true;
4020
+ }
3992
4021
  }
3993
4022
  });
3994
- for (const frame of currentElement.querySelectorAll("turbo-frame")) {
3995
- if (canRefreshFrame(frame)) frame.reload();
3996
- }
3997
4023
  dispatch("turbo:morph", {
3998
4024
  detail: {
3999
4025
  currentElement: currentElement,
@@ -4012,10 +4038,6 @@ class MorphingPageRenderer extends PageRenderer {
4012
4038
  }
4013
4039
  }
4014
4040
 
4015
- function canRefreshFrame(frame) {
4016
- return frame instanceof FrameElement && frame.src && frame.refresh === "morph" && !frame.closest("[data-turbo-permanent]");
4017
- }
4018
-
4019
4041
  class SnapshotCache {
4020
4042
  keys=[];
4021
4043
  snapshots={};
@@ -4616,6 +4638,14 @@ function setFormMode(mode) {
4616
4638
  config.forms.mode = mode;
4617
4639
  }
4618
4640
 
4641
+ function morphBodyElements(currentBody, newBody) {
4642
+ MorphingPageRenderer.renderElement(currentBody, newBody);
4643
+ }
4644
+
4645
+ function morphTurboFrameElements(currentFrame, newFrame) {
4646
+ MorphingFrameRenderer.renderElement(currentFrame, newFrame);
4647
+ }
4648
+
4619
4649
  var Turbo = Object.freeze({
4620
4650
  __proto__: null,
4621
4651
  navigator: navigator$1,
@@ -4635,7 +4665,11 @@ var Turbo = Object.freeze({
4635
4665
  clearCache: clearCache,
4636
4666
  setProgressBarDelay: setProgressBarDelay,
4637
4667
  setConfirmMethod: setConfirmMethod,
4638
- setFormMode: setFormMode
4668
+ setFormMode: setFormMode,
4669
+ morphBodyElements: morphBodyElements,
4670
+ morphTurboFrameElements: morphTurboFrameElements,
4671
+ morphChildren: morphChildren,
4672
+ morphElements: morphElements
4639
4673
  });
4640
4674
 
4641
4675
  class TurboFrameMissingError extends Error {}
@@ -5330,6 +5364,10 @@ var Turbo$1 = Object.freeze({
5330
5364
  fetchEnctypeFromString: fetchEnctypeFromString,
5331
5365
  fetchMethodFromString: fetchMethodFromString,
5332
5366
  isSafe: isSafe,
5367
+ morphBodyElements: morphBodyElements,
5368
+ morphChildren: morphChildren,
5369
+ morphElements: morphElements,
5370
+ morphTurboFrameElements: morphTurboFrameElements,
5333
5371
  navigator: navigator$1,
5334
5372
  registerAdapter: registerAdapter,
5335
5373
  renderStreamMessage: renderStreamMessage,