turbo-rails 2.0.0.pre.beta.3 → 2.0.0.pre.rc.1

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: 1b7b21819e793a4171cd5ebdcbd3e9082b1337a3623f5241fccccbca26c70989
4
- data.tar.gz: 69d69e925673a080bfe40e719df3cf3c838c7188cceb30194146be63cbb929ba
3
+ metadata.gz: 629282c25f127e2c0942f0a9635086e30595dbebda133ce77184da1d153514d5
4
+ data.tar.gz: 7cb06dba5377f8bbca58b6066b0f13bbb01f1ed705cb11179d326583b9e1e8cf
5
5
  SHA512:
6
- metadata.gz: 81c645877d527bcac7d22acb0d6405bda1d15503008599926882f467d89ad544a1dd923c79b348150679e3cbcc65b7ea6a4835cb4ca0c03bf08a72002a023771
7
- data.tar.gz: 20303a3c019a1f5b8387581b4a29b3bb13b89d8232f58d18efe9de2eb09347eee2964fe78d650b5fb18821cf890092d30025f027f7b530ab53a53e1ee614e189
6
+ metadata.gz: ba7ff71a246d6c088408dafbf6778a734776bf54f8006a6b8d6c375f60e8f6b61b9db35b38ddeb7bb757a4d902fe2bf1c88408c0437f7eafef14854741e39aee
7
+ data.tar.gz: d0ce9c29add7525e7afdf8fcc691b937df92a51a39588d86d45ad88e5236dec28630d52722032024b67d534ca251dde1182666442bab98e9822a60e841548973
@@ -1,5 +1,5 @@
1
1
  /*!
2
- Turbo 8.0.0-beta.3
2
+ Turbo 8.0.0-rc.1
3
3
  Copyright © 2024 37signals LLC
4
4
  */
5
5
  (function(prototype) {
@@ -1228,7 +1228,8 @@ class View {
1228
1228
  return window;
1229
1229
  }
1230
1230
  async render(renderer) {
1231
- const {isPreview: isPreview, shouldRender: shouldRender, newSnapshot: snapshot} = renderer;
1231
+ const {isPreview: isPreview, shouldRender: shouldRender, willRender: willRender, newSnapshot: snapshot} = renderer;
1232
+ const shouldInvalidate = willRender;
1232
1233
  if (shouldRender) {
1233
1234
  try {
1234
1235
  this.renderPromise = new Promise((resolve => this.#resolveRenderPromise = resolve));
@@ -1237,7 +1238,8 @@ class View {
1237
1238
  const renderInterception = new Promise((resolve => this.#resolveInterceptionPromise = resolve));
1238
1239
  const options = {
1239
1240
  resume: this.#resolveInterceptionPromise,
1240
- render: this.renderer.renderElement
1241
+ render: this.renderer.renderElement,
1242
+ renderMethod: this.renderer.renderMethod
1241
1243
  };
1242
1244
  const immediateRender = this.delegate.allowsImmediateRender(snapshot, options);
1243
1245
  if (!immediateRender) await renderInterception;
@@ -1250,7 +1252,7 @@ class View {
1250
1252
  this.#resolveRenderPromise(undefined);
1251
1253
  delete this.renderPromise;
1252
1254
  }
1253
- } else {
1255
+ } else if (shouldInvalidate) {
1254
1256
  this.invalidate(renderer.reloadReason);
1255
1257
  }
1256
1258
  }
@@ -1623,8 +1625,6 @@ function readScrollBehavior(value, defaultValue) {
1623
1625
  }
1624
1626
  }
1625
1627
 
1626
- const ProgressBarID = "turbo-progress-bar";
1627
-
1628
1628
  class ProgressBar {
1629
1629
  static animationDuration=300;
1630
1630
  static get defaultCSS() {
@@ -1712,8 +1712,6 @@ class ProgressBar {
1712
1712
  }
1713
1713
  createStylesheetElement() {
1714
1714
  const element = document.createElement("style");
1715
- element.id = ProgressBarID;
1716
- element.setAttribute("data-turbo-permanent", "");
1717
1715
  element.type = "text/css";
1718
1716
  element.textContent = ProgressBar.defaultCSS;
1719
1717
  if (this.cspNonce) {
@@ -1969,6 +1967,7 @@ class Visit {
1969
1967
  this.snapshotHTML = snapshotHTML;
1970
1968
  this.response = response;
1971
1969
  this.isSamePage = this.delegate.locationWithActionIsSamePage(this.location, this.action);
1970
+ this.isPageRefresh = this.view.isPageRefresh(this);
1972
1971
  this.visitCachedSnapshot = visitCachedSnapshot;
1973
1972
  this.willRender = willRender;
1974
1973
  this.updateHistory = updateHistory;
@@ -2110,7 +2109,7 @@ class Visit {
2110
2109
  const isPreview = this.shouldIssueRequest();
2111
2110
  this.render((async () => {
2112
2111
  this.cacheSnapshot();
2113
- if (this.isSamePage) {
2112
+ if (this.isSamePage || this.isPageRefresh) {
2114
2113
  this.adapter.visitRendered(this);
2115
2114
  } else {
2116
2115
  if (this.view.renderPromise) await this.view.renderPromise;
@@ -2638,17 +2637,14 @@ class LinkPrefetchObserver {
2638
2637
  };
2639
2638
  prepareRequest(request) {
2640
2639
  const link = request.target;
2641
- request.headers["Sec-Purpose"] = "prefetch";
2642
- if (link.dataset.turboFrame && link.dataset.turboFrame !== "_top") {
2643
- request.headers["Turbo-Frame"] = link.dataset.turboFrame;
2644
- } else if (link.dataset.turboFrame !== "_top") {
2645
- const turboFrame = link.closest("turbo-frame");
2646
- if (turboFrame) {
2647
- request.headers["Turbo-Frame"] = turboFrame.id;
2648
- }
2640
+ request.headers["X-Sec-Purpose"] = "prefetch";
2641
+ const turboFrame = link.closest("turbo-frame");
2642
+ const turboFrameTarget = link.getAttribute("data-turbo-frame") || turboFrame?.getAttribute("target") || turboFrame?.id;
2643
+ if (turboFrameTarget && turboFrameTarget !== "_top") {
2644
+ request.headers["Turbo-Frame"] = turboFrameTarget;
2649
2645
  }
2650
2646
  if (link.hasAttribute("data-turbo-stream")) {
2651
- request.acceptResponseType("text/vnd.turbo-stream.html");
2647
+ request.acceptResponseType(StreamMessage.contentType);
2652
2648
  }
2653
2649
  }
2654
2650
  requestSucceededWithResponse() {}
@@ -2662,7 +2658,7 @@ class LinkPrefetchObserver {
2662
2658
  }
2663
2659
  #isPrefetchable(link) {
2664
2660
  const href = link.getAttribute("href");
2665
- if (!href || href === "#" || link.dataset.turbo === "false" || link.dataset.turboPrefetch === "false") {
2661
+ if (!href || href === "#" || link.getAttribute("data-turbo") === "false" || link.getAttribute("data-turbo-prefetch") === "false") {
2666
2662
  return false;
2667
2663
  }
2668
2664
  if (link.origin !== document.location.origin) {
@@ -2674,17 +2670,15 @@ class LinkPrefetchObserver {
2674
2670
  if (link.pathname + link.search === document.location.pathname + document.location.search) {
2675
2671
  return false;
2676
2672
  }
2677
- if (link.dataset.turboMethod && link.dataset.turboMethod !== "get") {
2673
+ const turboMethod = link.getAttribute("data-turbo-method");
2674
+ if (turboMethod && turboMethod !== "get") {
2678
2675
  return false;
2679
2676
  }
2680
2677
  if (targetsIframe(link)) {
2681
2678
  return false;
2682
2679
  }
2683
- if (link.pathname + link.search === document.location.pathname + document.location.search) {
2684
- return false;
2685
- }
2686
2680
  const turboPrefetchParent = findClosestRecursively(link, "[data-turbo-prefetch]");
2687
- if (turboPrefetchParent && turboPrefetchParent.dataset.turboPrefetch === "false") {
2681
+ if (turboPrefetchParent && turboPrefetchParent.getAttribute("data-turbo-prefetch") === "false") {
2688
2682
  return false;
2689
2683
  }
2690
2684
  return true;
@@ -3623,76 +3617,6 @@ var Idiomorph = function() {
3623
3617
  };
3624
3618
  }();
3625
3619
 
3626
- class MorphRenderer extends Renderer {
3627
- async render() {
3628
- if (this.willRender) await this.#morphBody();
3629
- }
3630
- get renderMethod() {
3631
- return "morph";
3632
- }
3633
- async #morphBody() {
3634
- this.#morphElements(this.currentElement, this.newElement);
3635
- this.#reloadRemoteFrames();
3636
- dispatch("turbo:morph", {
3637
- detail: {
3638
- currentElement: this.currentElement,
3639
- newElement: this.newElement
3640
- }
3641
- });
3642
- }
3643
- #morphElements(currentElement, newElement, morphStyle = "outerHTML") {
3644
- this.isMorphingTurboFrame = this.#isFrameReloadedWithMorph(currentElement);
3645
- Idiomorph.morph(currentElement, newElement, {
3646
- morphStyle: morphStyle,
3647
- callbacks: {
3648
- beforeNodeAdded: this.#shouldAddElement,
3649
- beforeNodeMorphed: this.#shouldMorphElement,
3650
- beforeNodeRemoved: this.#shouldRemoveElement
3651
- }
3652
- });
3653
- }
3654
- #shouldAddElement=node => !(node.id && node.hasAttribute("data-turbo-permanent") && document.getElementById(node.id));
3655
- #shouldMorphElement=(oldNode, newNode) => {
3656
- if (oldNode instanceof HTMLElement) {
3657
- return !oldNode.hasAttribute("data-turbo-permanent") && (this.isMorphingTurboFrame || !this.#isFrameReloadedWithMorph(oldNode));
3658
- } else {
3659
- return true;
3660
- }
3661
- };
3662
- #shouldRemoveElement=node => this.#shouldMorphElement(node);
3663
- #reloadRemoteFrames() {
3664
- this.#remoteFrames().forEach((frame => {
3665
- if (this.#isFrameReloadedWithMorph(frame)) {
3666
- this.#renderFrameWithMorph(frame);
3667
- frame.reload();
3668
- }
3669
- }));
3670
- }
3671
- #renderFrameWithMorph(frame) {
3672
- frame.addEventListener("turbo:before-frame-render", (event => {
3673
- event.detail.render = this.#morphFrameUpdate;
3674
- }), {
3675
- once: true
3676
- });
3677
- }
3678
- #morphFrameUpdate=(currentElement, newElement) => {
3679
- dispatch("turbo:before-frame-morph", {
3680
- target: currentElement,
3681
- detail: {
3682
- currentElement: currentElement,
3683
- newElement: newElement
3684
- }
3685
- });
3686
- this.#morphElements(currentElement, newElement.children, "innerHTML");
3687
- };
3688
- #isFrameReloadedWithMorph(element) {
3689
- return element.src && element.refresh === "morph";
3690
- }
3691
- #remoteFrames() {
3692
- return Array.from(document.querySelectorAll("turbo-frame[src]")).filter((frame => !frame.closest("[data-turbo-permanent]")));
3693
- }
3694
- }
3695
-
3696
3620
  class PageRenderer extends Renderer {
3697
3621
  static renderElement(currentElement, newElement) {
3698
3622
  if (document.body && newElement instanceof HTMLBodyElement) {
@@ -3756,7 +3680,7 @@ class PageRenderer extends Renderer {
3756
3680
  await mergedHeadElements;
3757
3681
  await newStylesheetElements;
3758
3682
  if (this.willRender) {
3759
- this.removeUnusedHeadStylesheetElements();
3683
+ this.removeUnusedDynamicStylesheetElements();
3760
3684
  }
3761
3685
  }
3762
3686
  async replaceBody() {
@@ -3781,8 +3705,8 @@ class PageRenderer extends Renderer {
3781
3705
  document.head.appendChild(activateScriptElement(element));
3782
3706
  }
3783
3707
  }
3784
- removeUnusedHeadStylesheetElements() {
3785
- for (const element of this.unusedHeadStylesheetElements) {
3708
+ removeUnusedDynamicStylesheetElements() {
3709
+ for (const element of this.unusedDynamicStylesheetElements) {
3786
3710
  document.head.removeChild(element);
3787
3711
  }
3788
3712
  }
@@ -3838,8 +3762,8 @@ class PageRenderer extends Renderer {
3838
3762
  async assignNewBody() {
3839
3763
  await this.renderElement(this.currentElement, this.newElement);
3840
3764
  }
3841
- get unusedHeadStylesheetElements() {
3842
- return this.oldHeadStylesheetElements.filter((element => !(element.hasAttribute("data-turbo-permanent") || element.hasAttribute("data-tag-name"))));
3765
+ get unusedDynamicStylesheetElements() {
3766
+ return this.oldHeadStylesheetElements.filter((element => element.getAttribute("data-turbo-track") === "dynamic"));
3843
3767
  }
3844
3768
  get oldHeadStylesheetElements() {
3845
3769
  return this.currentHeadSnapshot.getStylesheetElementsNotInSnapshot(this.newHeadSnapshot);
@@ -3861,6 +3785,109 @@ class PageRenderer extends Renderer {
3861
3785
  }
3862
3786
  }
3863
3787
 
3788
+ class MorphRenderer extends PageRenderer {
3789
+ async render() {
3790
+ if (this.willRender) await this.#morphBody();
3791
+ }
3792
+ get renderMethod() {
3793
+ return "morph";
3794
+ }
3795
+ async #morphBody() {
3796
+ this.#morphElements(this.currentElement, this.newElement);
3797
+ this.#reloadRemoteFrames();
3798
+ dispatch("turbo:morph", {
3799
+ detail: {
3800
+ currentElement: this.currentElement,
3801
+ newElement: this.newElement
3802
+ }
3803
+ });
3804
+ }
3805
+ #morphElements(currentElement, newElement, morphStyle = "outerHTML") {
3806
+ this.isMorphingTurboFrame = this.#isFrameReloadedWithMorph(currentElement);
3807
+ Idiomorph.morph(currentElement, newElement, {
3808
+ ignoreActiveValue: true,
3809
+ morphStyle: morphStyle,
3810
+ callbacks: {
3811
+ beforeNodeAdded: this.#shouldAddElement,
3812
+ beforeNodeMorphed: this.#shouldMorphElement,
3813
+ beforeAttributeUpdated: this.#shouldUpdateAttribute,
3814
+ beforeNodeRemoved: this.#shouldRemoveElement,
3815
+ afterNodeMorphed: this.#didMorphElement
3816
+ }
3817
+ });
3818
+ }
3819
+ #shouldAddElement=node => !(node.id && node.hasAttribute("data-turbo-permanent") && document.getElementById(node.id));
3820
+ #shouldMorphElement=(oldNode, newNode) => {
3821
+ if (oldNode instanceof HTMLElement) {
3822
+ if (!oldNode.hasAttribute("data-turbo-permanent") && (this.isMorphingTurboFrame || !this.#isFrameReloadedWithMorph(oldNode))) {
3823
+ const event = dispatch("turbo:before-morph-element", {
3824
+ cancelable: true,
3825
+ target: oldNode,
3826
+ detail: {
3827
+ newElement: newNode
3828
+ }
3829
+ });
3830
+ return !event.defaultPrevented;
3831
+ } else {
3832
+ return false;
3833
+ }
3834
+ }
3835
+ };
3836
+ #shouldUpdateAttribute=(attributeName, target, mutationType) => {
3837
+ const event = dispatch("turbo:before-morph-attribute", {
3838
+ cancelable: true,
3839
+ target: target,
3840
+ detail: {
3841
+ attributeName: attributeName,
3842
+ mutationType: mutationType
3843
+ }
3844
+ });
3845
+ return !event.defaultPrevented;
3846
+ };
3847
+ #didMorphElement=(oldNode, newNode) => {
3848
+ if (newNode instanceof HTMLElement) {
3849
+ dispatch("turbo:morph-element", {
3850
+ target: oldNode,
3851
+ detail: {
3852
+ newElement: newNode
3853
+ }
3854
+ });
3855
+ }
3856
+ };
3857
+ #shouldRemoveElement=node => this.#shouldMorphElement(node);
3858
+ #reloadRemoteFrames() {
3859
+ this.#remoteFrames().forEach((frame => {
3860
+ if (this.#isFrameReloadedWithMorph(frame)) {
3861
+ this.#renderFrameWithMorph(frame);
3862
+ frame.reload();
3863
+ }
3864
+ }));
3865
+ }
3866
+ #renderFrameWithMorph(frame) {
3867
+ frame.addEventListener("turbo:before-frame-render", (event => {
3868
+ event.detail.render = this.#morphFrameUpdate;
3869
+ }), {
3870
+ once: true
3871
+ });
3872
+ }
3873
+ #morphFrameUpdate=(currentElement, newElement) => {
3874
+ dispatch("turbo:before-frame-morph", {
3875
+ target: currentElement,
3876
+ detail: {
3877
+ currentElement: currentElement,
3878
+ newElement: newElement
3879
+ }
3880
+ });
3881
+ this.#morphElements(currentElement, newElement.children, "innerHTML");
3882
+ };
3883
+ #isFrameReloadedWithMorph(element) {
3884
+ return element.src && element.refresh === "morph";
3885
+ }
3886
+ #remoteFrames() {
3887
+ return Array.from(document.querySelectorAll("turbo-frame[src]")).filter((frame => !frame.closest("[data-turbo-permanent]")));
3888
+ }
3889
+ }
3890
+
3864
3891
  class SnapshotCache {
3865
3892
  keys=[];
3866
3893
  snapshots={};
@@ -3987,7 +4014,7 @@ class Preloader {
3987
4014
  await fetchRequest.perform();
3988
4015
  }
3989
4016
  prepareRequest(fetchRequest) {
3990
- fetchRequest.headers["Sec-Purpose"] = "prefetch";
4017
+ fetchRequest.headers["X-Sec-Purpose"] = "prefetch";
3991
4018
  }
3992
4019
  async requestSucceededWithResponse(fetchRequest, fetchResponse) {
3993
4020
  try {
@@ -4097,8 +4124,9 @@ class Session {
4097
4124
  visit(location, options = {}) {
4098
4125
  const frameElement = options.frame ? document.getElementById(options.frame) : null;
4099
4126
  if (frameElement instanceof FrameElement) {
4127
+ const action = options.action || getVisitAction(frameElement);
4128
+ frameElement.delegate.proposeVisitIfNavigatedWithAction(frameElement, action);
4100
4129
  frameElement.src = location.toString();
4101
- frameElement.loaded;
4102
4130
  } else {
4103
4131
  this.navigator.proposeVisit(expandURL(location), options);
4104
4132
  }
@@ -4568,7 +4596,7 @@ class FrameController {
4568
4596
  }
4569
4597
  }
4570
4598
  elementAppearedInViewport(element) {
4571
- this.proposeVisitIfNavigatedWithAction(element, element);
4599
+ this.proposeVisitIfNavigatedWithAction(element, getVisitAction(element));
4572
4600
  this.#loadSourceURL();
4573
4601
  }
4574
4602
  willSubmitFormLinkToLocation(link) {
@@ -4628,7 +4656,7 @@ class FrameController {
4628
4656
  }
4629
4657
  formSubmissionSucceededWithResponse(formSubmission, response) {
4630
4658
  const frame = this.#findFrameElement(formSubmission.formElement, formSubmission.submitter);
4631
- frame.delegate.proposeVisitIfNavigatedWithAction(frame, formSubmission.formElement, formSubmission.submitter);
4659
+ frame.delegate.proposeVisitIfNavigatedWithAction(frame, getVisitAction(formSubmission.submitter, formSubmission.formElement, frame));
4632
4660
  frame.delegate.loadResponse(response);
4633
4661
  if (!formSubmission.isSafe) {
4634
4662
  session.clearCache();
@@ -4705,13 +4733,13 @@ class FrameController {
4705
4733
  }
4706
4734
  #navigateFrame(element, url, submitter) {
4707
4735
  const frame = this.#findFrameElement(element, submitter);
4708
- frame.delegate.proposeVisitIfNavigatedWithAction(frame, element, submitter);
4736
+ frame.delegate.proposeVisitIfNavigatedWithAction(frame, getVisitAction(submitter, element, frame));
4709
4737
  this.#withCurrentNavigationElement(element, (() => {
4710
4738
  frame.src = url;
4711
4739
  }));
4712
4740
  }
4713
- proposeVisitIfNavigatedWithAction(frame, element, submitter) {
4714
- this.action = getVisitAction(submitter, element, frame);
4741
+ proposeVisitIfNavigatedWithAction(frame, action = null) {
4742
+ this.action = action;
4715
4743
  if (this.action) {
4716
4744
  const pageSnapshot = PageSnapshot.fromElement(frame).clone();
4717
4745
  const {visitCachedSnapshot: visitCachedSnapshot} = frame.delegate;