turbo-rails 2.0.0.pre.beta.1 → 2.0.0.pre.beta.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d64d1046010fe176342eb0dc1269ce42575b3875e8fbea14eebd282455eb28ee
4
- data.tar.gz: 0da2bd6fdcf894118945c41d3f297c5e3638d1ff732a58b89980f033cc950ead
3
+ metadata.gz: d87101284e042e02ec6ba74cdf085367415619ee0c0f1c8b4013ad18022c00c7
4
+ data.tar.gz: 1d514a6ece035de00be6c8af254662476343db15827ca02b27fbcd7caab14d5f
5
5
  SHA512:
6
- metadata.gz: 02ce010eaa7cf96ee30e931499da76ff7beb95525c7cd1a2edd775f6aa354e8524cdbcd7bae2c8806416c6566bb7876ab3d1550fb0f29c18fe007daf2f8b0003
7
- data.tar.gz: f31df5f93132755af3045511f998e38886240656eca467de4de278682ffd1e7c101402aab704988ab6294309d79dc8e767d72ea4d4283c26eb1ac974b7bfd2a6
6
+ metadata.gz: 1b81a8c738ec5f9ff1c3584a4598e116eb5e54557295717e8eaa6ae582d66408c0554477a1bbf7a623bc8d409e45db1e984892c7a784aea2047163c719fb97a7
7
+ data.tar.gz: 1f86ba10f8bd0b76a722eac75490bc309e292e5dd04c7a488b153792ae9f00c9aef4cedf6dc28534569366c7089667abeaa8ac7ad9458ed50a82be58fec13f42
@@ -1,5 +1,5 @@
1
1
  /*!
2
- Turbo 8.0.0-beta.1
2
+ Turbo 8.0.0-beta.2
3
3
  Copyright © 2023 37signals LLC
4
4
  */
5
5
  (function(prototype) {
@@ -485,12 +485,31 @@ async function around(callback, reader) {
485
485
  return [ before, after ];
486
486
  }
487
487
 
488
- function fetch(url, options = {}) {
488
+ class LimitedSet extends Set {
489
+ constructor(maxSize) {
490
+ super();
491
+ this.maxSize = maxSize;
492
+ }
493
+ add(value) {
494
+ if (this.size >= this.maxSize) {
495
+ const iterator = this.values();
496
+ const oldestValue = iterator.next().value;
497
+ this.delete(oldestValue);
498
+ }
499
+ super.add(value);
500
+ }
501
+ }
502
+
503
+ const recentRequests = new LimitedSet(20);
504
+
505
+ const nativeFetch = window.fetch;
506
+
507
+ function fetchWithTurboHeaders(url, options = {}) {
489
508
  const modifiedHeaders = new Headers(options.headers || {});
490
509
  const requestUID = uuid();
491
- window.Turbo.session.recentRequests.add(requestUID);
510
+ recentRequests.add(requestUID);
492
511
  modifiedHeaders.append("X-Turbo-Request-Id", requestUID);
493
- return window.fetch(url, {
512
+ return nativeFetch(url, {
494
513
  ...options,
495
514
  headers: modifiedHeaders
496
515
  });
@@ -609,7 +628,7 @@ class FetchRequest {
609
628
  await this.#allowRequestToBeIntercepted(fetchOptions);
610
629
  try {
611
630
  this.delegate.requestStarted(this);
612
- const response = await fetch(this.url.href, fetchOptions);
631
+ const response = await fetchWithTurboHeaders(this.url.href, fetchOptions);
613
632
  return await this.receive(response);
614
633
  } catch (error) {
615
634
  if (error.name !== "AbortError") {
@@ -848,6 +867,7 @@ class FormSubmission {
848
867
  this.state = FormSubmissionState.waiting;
849
868
  this.submitter?.setAttribute("disabled", "");
850
869
  this.setSubmitsWith();
870
+ markAsBusy(this.formElement);
851
871
  dispatch("turbo:submit-start", {
852
872
  target: this.formElement,
853
873
  detail: {
@@ -895,6 +915,7 @@ class FormSubmission {
895
915
  this.state = FormSubmissionState.stopped;
896
916
  this.submitter?.removeAttribute("disabled");
897
917
  this.resetSubmitterText();
918
+ clearBusyState(this.formElement);
898
919
  dispatch("turbo:submit-end", {
899
920
  target: this.formElement,
900
921
  detail: {
@@ -1176,6 +1197,12 @@ class View {
1176
1197
  this.element.removeAttribute("data-turbo-preview");
1177
1198
  }
1178
1199
  }
1200
+ markVisitDirection(direction) {
1201
+ this.element.setAttribute("data-turbo-visit-direction", direction);
1202
+ }
1203
+ unmarkVisitDirection() {
1204
+ this.element.removeAttribute("data-turbo-visit-direction");
1205
+ }
1179
1206
  async renderSnapshot(renderer) {
1180
1207
  await renderer.render();
1181
1208
  }
@@ -1480,14 +1507,14 @@ class FrameRenderer extends Renderer {
1480
1507
  return true;
1481
1508
  }
1482
1509
  async render() {
1483
- await nextAnimationFrame();
1510
+ await nextRepaint();
1484
1511
  this.preservingPermanentElements((() => {
1485
1512
  this.loadFrameElement();
1486
1513
  }));
1487
1514
  this.scrollFrameIntoView();
1488
- await nextAnimationFrame();
1515
+ await nextRepaint();
1489
1516
  this.focusFirstAutofocusableElement();
1490
- await nextAnimationFrame();
1517
+ await nextRepaint();
1491
1518
  this.activateScriptElements();
1492
1519
  }
1493
1520
  loadFrameElement() {
@@ -1846,6 +1873,12 @@ const SystemStatusCode = {
1846
1873
  contentTypeMismatch: -2
1847
1874
  };
1848
1875
 
1876
+ const Direction = {
1877
+ advance: "forward",
1878
+ restore: "back",
1879
+ replace: "none"
1880
+ };
1881
+
1849
1882
  class Visit {
1850
1883
  identifier=uuid();
1851
1884
  timingMetrics={};
@@ -1861,7 +1894,7 @@ class Visit {
1861
1894
  this.delegate = delegate;
1862
1895
  this.location = location;
1863
1896
  this.restorationIdentifier = restorationIdentifier || uuid();
1864
- const {action: action, historyChanged: historyChanged, referrer: referrer, snapshot: snapshot, snapshotHTML: snapshotHTML, response: response, visitCachedSnapshot: visitCachedSnapshot, willRender: willRender, updateHistory: updateHistory, shouldCacheSnapshot: shouldCacheSnapshot, acceptsStreamResponse: acceptsStreamResponse} = {
1897
+ const {action: action, historyChanged: historyChanged, referrer: referrer, snapshot: snapshot, snapshotHTML: snapshotHTML, response: response, visitCachedSnapshot: visitCachedSnapshot, willRender: willRender, updateHistory: updateHistory, shouldCacheSnapshot: shouldCacheSnapshot, acceptsStreamResponse: acceptsStreamResponse, direction: direction} = {
1865
1898
  ...defaultOptions,
1866
1899
  ...options
1867
1900
  };
@@ -1878,6 +1911,7 @@ class Visit {
1878
1911
  this.scrolled = !willRender;
1879
1912
  this.shouldCacheSnapshot = shouldCacheSnapshot;
1880
1913
  this.acceptsStreamResponse = acceptsStreamResponse;
1914
+ this.direction = direction || Direction[action];
1881
1915
  }
1882
1916
  get adapter() {
1883
1917
  return this.delegate.adapter;
@@ -2098,7 +2132,7 @@ class Visit {
2098
2132
  this.finishRequest();
2099
2133
  }
2100
2134
  performScroll() {
2101
- if (!this.scrolled && !this.view.forceReloaded && !this.view.snapshot.shouldPreserveScrollPosition) {
2135
+ if (!this.scrolled && !this.view.forceReloaded && !this.view.shouldPreserveScrollPosition(this)) {
2102
2136
  if (this.action == "restore") {
2103
2137
  this.scrollToRestoredPosition() || this.scrollToAnchor() || this.view.scrollToTop();
2104
2138
  } else {
@@ -2162,9 +2196,7 @@ class Visit {
2162
2196
  }
2163
2197
  async render(callback) {
2164
2198
  this.cancelRender();
2165
- await new Promise((resolve => {
2166
- this.frame = requestAnimationFrame((() => resolve()));
2167
- }));
2199
+ this.frame = await nextRepaint();
2168
2200
  await callback();
2169
2201
  delete this.frame;
2170
2202
  }
@@ -2386,6 +2418,7 @@ class History {
2386
2418
  restorationData={};
2387
2419
  started=false;
2388
2420
  pageLoaded=false;
2421
+ currentIndex=0;
2389
2422
  constructor(delegate) {
2390
2423
  this.delegate = delegate;
2391
2424
  }
@@ -2393,6 +2426,7 @@ class History {
2393
2426
  if (!this.started) {
2394
2427
  addEventListener("popstate", this.onPopState, false);
2395
2428
  addEventListener("load", this.onPageLoad, false);
2429
+ this.currentIndex = history.state?.turbo?.restorationIndex || 0;
2396
2430
  this.started = true;
2397
2431
  this.replace(new URL(window.location.href));
2398
2432
  }
@@ -2411,9 +2445,11 @@ class History {
2411
2445
  this.update(history.replaceState, location, restorationIdentifier);
2412
2446
  }
2413
2447
  update(method, location, restorationIdentifier = uuid()) {
2448
+ if (method === history.pushState) ++this.currentIndex;
2414
2449
  const state = {
2415
2450
  turbo: {
2416
- restorationIdentifier: restorationIdentifier
2451
+ restorationIdentifier: restorationIdentifier,
2452
+ restorationIndex: this.currentIndex
2417
2453
  }
2418
2454
  };
2419
2455
  method.call(history, state, "", location.href);
@@ -2448,9 +2484,11 @@ class History {
2448
2484
  const {turbo: turbo} = event.state || {};
2449
2485
  if (turbo) {
2450
2486
  this.location = new URL(window.location.href);
2451
- const {restorationIdentifier: restorationIdentifier} = turbo;
2487
+ const {restorationIdentifier: restorationIdentifier, restorationIndex: restorationIndex} = turbo;
2452
2488
  this.restorationIdentifier = restorationIdentifier;
2453
- this.delegate.historyPoppedToLocationWithRestorationIdentifier(this.location, restorationIdentifier);
2489
+ const direction = restorationIndex > this.currentIndex ? "forward" : "back";
2490
+ this.delegate.historyPoppedToLocationWithRestorationIdentifierAndDirection(this.location, restorationIdentifier, direction);
2491
+ this.currentIndex = restorationIndex;
2454
2492
  }
2455
2493
  }
2456
2494
  };
@@ -2725,7 +2763,7 @@ async function withAutofocusFromFragment(fragment, callback) {
2725
2763
  elementWithAutofocus.id = willAutofocusId;
2726
2764
  }
2727
2765
  callback();
2728
- await nextAnimationFrame();
2766
+ await nextRepaint();
2729
2767
  const hasNoActiveElement = document.activeElement == null || document.activeElement == document.body;
2730
2768
  if (hasNoActiveElement && willAutofocusId) {
2731
2769
  const elementToAutofocus = document.getElementById(willAutofocusId);
@@ -3663,7 +3701,10 @@ class PageView extends View {
3663
3701
  return this.snapshotCache.get(location);
3664
3702
  }
3665
3703
  isPageRefresh(visit) {
3666
- return !visit || this.lastRenderedLocation.href === visit.location.href && visit.action === "replace";
3704
+ return !visit || this.lastRenderedLocation.pathname === visit.location.pathname && visit.action === "replace";
3705
+ }
3706
+ shouldPreserveScrollPosition(visit) {
3707
+ return this.isPageRefresh(visit) && this.snapshot.shouldPreserveScrollPosition;
3667
3708
  }
3668
3709
  get snapshot() {
3669
3710
  return PageSnapshot.fromElement(this.element);
@@ -3672,24 +3713,25 @@ class PageView extends View {
3672
3713
 
3673
3714
  class Preloader {
3674
3715
  selector="a[data-turbo-preload]";
3675
- constructor(delegate) {
3716
+ constructor(delegate, snapshotCache) {
3676
3717
  this.delegate = delegate;
3677
- }
3678
- get snapshotCache() {
3679
- return this.delegate.navigator.view.snapshotCache;
3718
+ this.snapshotCache = snapshotCache;
3680
3719
  }
3681
3720
  start() {
3682
3721
  if (document.readyState === "loading") {
3683
- return document.addEventListener("DOMContentLoaded", (() => {
3684
- this.preloadOnLoadLinksForView(document.body);
3685
- }));
3722
+ document.addEventListener("DOMContentLoaded", this.#preloadAll);
3686
3723
  } else {
3687
3724
  this.preloadOnLoadLinksForView(document.body);
3688
3725
  }
3689
3726
  }
3727
+ stop() {
3728
+ document.removeEventListener("DOMContentLoaded", this.#preloadAll);
3729
+ }
3690
3730
  preloadOnLoadLinksForView(element) {
3691
3731
  for (const link of element.querySelectorAll(this.selector)) {
3692
- this.preloadURL(link);
3732
+ if (this.delegate.shouldPreloadLink(link)) {
3733
+ this.preloadURL(link);
3734
+ }
3693
3735
  }
3694
3736
  }
3695
3737
  async preloadURL(link) {
@@ -3697,33 +3739,27 @@ class Preloader {
3697
3739
  if (this.snapshotCache.has(location)) {
3698
3740
  return;
3699
3741
  }
3700
- try {
3701
- const response = await fetch(location.toString(), {
3702
- headers: {
3703
- "Sec-Purpose": "prefetch",
3704
- Accept: "text/html"
3705
- }
3706
- });
3707
- const responseText = await response.text();
3708
- const snapshot = PageSnapshot.fromHTMLString(responseText);
3709
- this.snapshotCache.put(location, snapshot);
3710
- } catch (_) {}
3742
+ const fetchRequest = new FetchRequest(this, FetchMethod.get, location, new URLSearchParams, link);
3743
+ await fetchRequest.perform();
3711
3744
  }
3712
- }
3713
-
3714
- class LimitedSet extends Set {
3715
- constructor(maxSize) {
3716
- super();
3717
- this.maxSize = maxSize;
3745
+ prepareRequest(fetchRequest) {
3746
+ fetchRequest.headers["Sec-Purpose"] = "prefetch";
3718
3747
  }
3719
- add(value) {
3720
- if (this.size >= this.maxSize) {
3721
- const iterator = this.values();
3722
- const oldestValue = iterator.next().value;
3723
- this.delete(oldestValue);
3724
- }
3725
- super.add(value);
3748
+ async requestSucceededWithResponse(fetchRequest, fetchResponse) {
3749
+ try {
3750
+ const responseHTML = await fetchResponse.responseHTML;
3751
+ const snapshot = PageSnapshot.fromHTMLString(responseHTML);
3752
+ this.snapshotCache.put(fetchRequest.url, snapshot);
3753
+ } catch (_) {}
3726
3754
  }
3755
+ requestStarted(fetchRequest) {}
3756
+ requestErrored(fetchRequest) {}
3757
+ requestFinished(fetchRequest) {}
3758
+ requestPreventedHandlingResponse(fetchRequest, fetchResponse) {}
3759
+ requestFailedWithResponse(fetchRequest, fetchResponse) {}
3760
+ #preloadAll=() => {
3761
+ this.preloadOnLoadLinksForView(document.body);
3762
+ };
3727
3763
  }
3728
3764
 
3729
3765
  class Cache {
@@ -3750,7 +3786,6 @@ class Cache {
3750
3786
  class Session {
3751
3787
  navigator=new Navigator(this);
3752
3788
  history=new History(this);
3753
- preloader=new Preloader(this);
3754
3789
  view=new PageView(this, document.documentElement);
3755
3790
  adapter=new BrowserAdapter(this);
3756
3791
  pageObserver=new PageObserver(this);
@@ -3763,12 +3798,15 @@ class Session {
3763
3798
  frameRedirector=new FrameRedirector(this, document.documentElement);
3764
3799
  streamMessageRenderer=new StreamMessageRenderer;
3765
3800
  cache=new Cache(this);
3766
- recentRequests=new LimitedSet(20);
3767
3801
  drive=true;
3768
3802
  enabled=true;
3769
3803
  progressBarDelay=500;
3770
3804
  started=false;
3771
3805
  formMode="on";
3806
+ constructor(recentRequests) {
3807
+ this.recentRequests = recentRequests;
3808
+ this.preloader = new Preloader(this, this.view.snapshotCache);
3809
+ }
3772
3810
  start() {
3773
3811
  if (!this.started) {
3774
3812
  this.pageObserver.start();
@@ -3799,6 +3837,7 @@ class Session {
3799
3837
  this.streamObserver.stop();
3800
3838
  this.frameRedirector.stop();
3801
3839
  this.history.stop();
3840
+ this.preloader.stop();
3802
3841
  this.started = false;
3803
3842
  }
3804
3843
  }
@@ -3847,11 +3886,24 @@ class Session {
3847
3886
  get restorationIdentifier() {
3848
3887
  return this.history.restorationIdentifier;
3849
3888
  }
3850
- historyPoppedToLocationWithRestorationIdentifier(location, restorationIdentifier) {
3889
+ shouldPreloadLink(element) {
3890
+ const isUnsafe = element.hasAttribute("data-turbo-method");
3891
+ const isStream = element.hasAttribute("data-turbo-stream");
3892
+ const frameTarget = element.getAttribute("data-turbo-frame");
3893
+ const frame = frameTarget == "_top" ? null : document.getElementById(frameTarget) || findClosestRecursively(element, "turbo-frame:not([disabled])");
3894
+ if (isUnsafe || isStream || frame instanceof FrameElement) {
3895
+ return false;
3896
+ } else {
3897
+ const location = new URL(element.href);
3898
+ return this.elementIsNavigatable(element) && locationIsVisitable(location, this.snapshot.rootLocation);
3899
+ }
3900
+ }
3901
+ historyPoppedToLocationWithRestorationIdentifierAndDirection(location, restorationIdentifier, direction) {
3851
3902
  if (this.enabled) {
3852
3903
  this.navigator.startVisit(location, restorationIdentifier, {
3853
3904
  action: "restore",
3854
- historyChanged: true
3905
+ historyChanged: true,
3906
+ direction: direction
3855
3907
  });
3856
3908
  } else {
3857
3909
  this.adapter.pageInvalidated({
@@ -3889,6 +3941,7 @@ class Session {
3889
3941
  visitStarted(visit) {
3890
3942
  if (!visit.acceptsStreamResponse) {
3891
3943
  markAsBusy(document.documentElement);
3944
+ this.view.markVisitDirection(visit.direction);
3892
3945
  }
3893
3946
  extendURLWithDeprecatedProperties(visit.location);
3894
3947
  if (!visit.silent) {
@@ -3896,6 +3949,7 @@ class Session {
3896
3949
  }
3897
3950
  }
3898
3951
  visitCompleted(visit) {
3952
+ this.view.unmarkVisitDirection();
3899
3953
  clearBusyState(document.documentElement);
3900
3954
  this.notifyApplicationAfterPageLoad(visit.getTimingMetrics());
3901
3955
  }
@@ -4086,7 +4140,7 @@ const deprecatedLocationPropertyDescriptors = {
4086
4140
  }
4087
4141
  };
4088
4142
 
4089
- const session = new Session;
4143
+ const session = new Session(recentRequests);
4090
4144
 
4091
4145
  const {cache: cache, navigator: navigator$1} = session;
4092
4146
 
@@ -4139,7 +4193,7 @@ var Turbo = Object.freeze({
4139
4193
  PageRenderer: PageRenderer,
4140
4194
  PageSnapshot: PageSnapshot,
4141
4195
  FrameRenderer: FrameRenderer,
4142
- fetch: fetch,
4196
+ fetch: fetchWithTurboHeaders,
4143
4197
  start: start,
4144
4198
  registerAdapter: registerAdapter,
4145
4199
  visit: visit,
@@ -4806,11 +4860,14 @@ if (customElements.get("turbo-stream-source") === undefined) {
4806
4860
  }
4807
4861
  })();
4808
4862
 
4809
- window.Turbo = Turbo;
4863
+ window.Turbo = {
4864
+ ...Turbo,
4865
+ StreamActions: StreamActions
4866
+ };
4810
4867
 
4811
4868
  start();
4812
4869
 
4813
- var turbo_es2017Esm = Object.freeze({
4870
+ var Turbo$1 = Object.freeze({
4814
4871
  __proto__: null,
4815
4872
  FetchEnctype: FetchEnctype,
4816
4873
  FetchMethod: FetchMethod,
@@ -4828,7 +4885,7 @@ var turbo_es2017Esm = Object.freeze({
4828
4885
  clearCache: clearCache,
4829
4886
  connectStreamSource: connectStreamSource,
4830
4887
  disconnectStreamSource: disconnectStreamSource,
4831
- fetch: fetch,
4888
+ fetch: fetchWithTurboHeaders,
4832
4889
  fetchEnctypeFromString: fetchEnctypeFromString,
4833
4890
  fetchMethodFromString: fetchMethodFromString,
4834
4891
  isSafe: isSafe,
@@ -4979,6 +5036,8 @@ function isBodyInit(body) {
4979
5036
  return body instanceof FormData || body instanceof URLSearchParams;
4980
5037
  }
4981
5038
 
5039
+ window.Turbo = Turbo$1;
5040
+
4982
5041
  addEventListener("turbo:before-fetch-request", encodeMethodIntoRequestBody);
4983
5042
 
4984
5043
  var adapters = {
@@ -5514,4 +5573,4 @@ var index = Object.freeze({
5514
5573
  getConfig: getConfig
5515
5574
  });
5516
5575
 
5517
- export { turbo_es2017Esm as Turbo, cable };
5576
+ export { Turbo$1 as Turbo, cable };