@hotwired/turbo-rails 7.2.0-beta.2 → 7.2.0-rc.2
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.
|
@@ -385,6 +385,10 @@ function getAttribute(attributeName, ...elements) {
|
|
|
385
385
|
return null;
|
|
386
386
|
}
|
|
387
387
|
|
|
388
|
+
function hasAttribute(attributeName, ...elements) {
|
|
389
|
+
return elements.some((element => element && element.hasAttribute(attributeName)));
|
|
390
|
+
}
|
|
391
|
+
|
|
388
392
|
function markAsBusy(...elements) {
|
|
389
393
|
for (const element of elements) {
|
|
390
394
|
if (element.localName == "turbo-frame") {
|
|
@@ -519,7 +523,9 @@ class FetchRequest {
|
|
|
519
523
|
return await this.receive(response);
|
|
520
524
|
} catch (error) {
|
|
521
525
|
if (error.name !== "AbortError") {
|
|
522
|
-
this.
|
|
526
|
+
if (this.willDelegateErrorHandling(error)) {
|
|
527
|
+
this.delegate.requestErrored(this, error);
|
|
528
|
+
}
|
|
523
529
|
throw error;
|
|
524
530
|
}
|
|
525
531
|
} finally {
|
|
@@ -583,6 +589,17 @@ class FetchRequest {
|
|
|
583
589
|
});
|
|
584
590
|
if (event.defaultPrevented) await requestInterception;
|
|
585
591
|
}
|
|
592
|
+
willDelegateErrorHandling(error) {
|
|
593
|
+
const event = dispatch("turbo:fetch-request-error", {
|
|
594
|
+
target: this.target,
|
|
595
|
+
cancelable: true,
|
|
596
|
+
detail: {
|
|
597
|
+
request: this,
|
|
598
|
+
error: error
|
|
599
|
+
}
|
|
600
|
+
});
|
|
601
|
+
return !event.defaultPrevented;
|
|
602
|
+
}
|
|
586
603
|
}
|
|
587
604
|
|
|
588
605
|
class AppearanceObserver {
|
|
@@ -684,7 +701,7 @@ class FormSubmission {
|
|
|
684
701
|
this.fetchRequest = new FetchRequest(this, this.method, this.location, this.body, this.formElement);
|
|
685
702
|
this.mustRedirect = mustRedirect;
|
|
686
703
|
}
|
|
687
|
-
static confirmMethod(message, _element) {
|
|
704
|
+
static confirmMethod(message, _element, _submitter) {
|
|
688
705
|
return Promise.resolve(confirm(message));
|
|
689
706
|
}
|
|
690
707
|
get method() {
|
|
@@ -718,17 +735,11 @@ class FormSubmission {
|
|
|
718
735
|
get stringFormData() {
|
|
719
736
|
return [ ...this.formData ].reduce(((entries, [name, value]) => entries.concat(typeof value == "string" ? [ [ name, value ] ] : [])), []);
|
|
720
737
|
}
|
|
721
|
-
get confirmationMessage() {
|
|
722
|
-
var _a;
|
|
723
|
-
return ((_a = this.submitter) === null || _a === void 0 ? void 0 : _a.getAttribute("data-turbo-confirm")) || this.formElement.getAttribute("data-turbo-confirm");
|
|
724
|
-
}
|
|
725
|
-
get needsConfirmation() {
|
|
726
|
-
return this.confirmationMessage !== null;
|
|
727
|
-
}
|
|
728
738
|
async start() {
|
|
729
739
|
const {initialized: initialized, requesting: requesting} = FormSubmissionState;
|
|
730
|
-
|
|
731
|
-
|
|
740
|
+
const confirmationMessage = getAttribute("data-turbo-confirm", this.submitter, this.formElement);
|
|
741
|
+
if (typeof confirmationMessage === "string") {
|
|
742
|
+
const answer = await FormSubmission.confirmMethod(confirmationMessage, this.formElement, this.submitter);
|
|
732
743
|
if (!answer) {
|
|
733
744
|
return;
|
|
734
745
|
}
|
|
@@ -802,13 +813,6 @@ class FormSubmission {
|
|
|
802
813
|
success: false,
|
|
803
814
|
error: error
|
|
804
815
|
};
|
|
805
|
-
dispatch("turbo:fetch-request-error", {
|
|
806
|
-
target: this.formElement,
|
|
807
|
-
detail: {
|
|
808
|
-
request: request,
|
|
809
|
-
error: error
|
|
810
|
-
}
|
|
811
|
-
});
|
|
812
816
|
this.delegate.formSubmissionErrored(this, error);
|
|
813
817
|
}
|
|
814
818
|
requestFinished(_request) {
|
|
@@ -827,7 +831,7 @@ class FormSubmission {
|
|
|
827
831
|
return !request.isIdempotent && this.mustRedirect;
|
|
828
832
|
}
|
|
829
833
|
requestAcceptsTurboStreamResponse(request) {
|
|
830
|
-
return !request.isIdempotent ||
|
|
834
|
+
return !request.isIdempotent || hasAttribute("data-turbo-stream", this.submitter, this.formElement);
|
|
831
835
|
}
|
|
832
836
|
}
|
|
833
837
|
|
|
@@ -893,10 +897,10 @@ class Snapshot {
|
|
|
893
897
|
return null;
|
|
894
898
|
}
|
|
895
899
|
get permanentElements() {
|
|
896
|
-
return
|
|
900
|
+
return queryPermanentElementsAll(this.element);
|
|
897
901
|
}
|
|
898
902
|
getPermanentElementById(id) {
|
|
899
|
-
return this.element
|
|
903
|
+
return getPermanentElementById(this.element, id);
|
|
900
904
|
}
|
|
901
905
|
getPermanentElementMapForSnapshot(snapshot) {
|
|
902
906
|
const permanentElementMap = {};
|
|
@@ -911,6 +915,14 @@ class Snapshot {
|
|
|
911
915
|
}
|
|
912
916
|
}
|
|
913
917
|
|
|
918
|
+
function getPermanentElementById(node, id) {
|
|
919
|
+
return node.querySelector(`#${id}[data-turbo-permanent]`);
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
function queryPermanentElementsAll(node) {
|
|
923
|
+
return node.querySelectorAll("[id][data-turbo-permanent]");
|
|
924
|
+
}
|
|
925
|
+
|
|
914
926
|
class FormSubmitObserver {
|
|
915
927
|
constructor(delegate, eventTarget) {
|
|
916
928
|
this.started = false;
|
|
@@ -1143,14 +1155,18 @@ class FormLinkClickObserver {
|
|
|
1143
1155
|
if (method) form.setAttribute("method", method);
|
|
1144
1156
|
const turboFrame = link.getAttribute("data-turbo-frame");
|
|
1145
1157
|
if (turboFrame) form.setAttribute("data-turbo-frame", turboFrame);
|
|
1158
|
+
const turboAction = link.getAttribute("data-turbo-action");
|
|
1159
|
+
if (turboAction) form.setAttribute("data-turbo-action", turboAction);
|
|
1146
1160
|
const turboConfirm = link.getAttribute("data-turbo-confirm");
|
|
1147
1161
|
if (turboConfirm) form.setAttribute("data-turbo-confirm", turboConfirm);
|
|
1148
1162
|
const turboStream = link.hasAttribute("data-turbo-stream");
|
|
1149
1163
|
if (turboStream) form.setAttribute("data-turbo-stream", "");
|
|
1150
1164
|
this.delegate.submittedFormLinkToLocation(link, location, form);
|
|
1151
1165
|
document.body.appendChild(form);
|
|
1152
|
-
form.
|
|
1153
|
-
|
|
1166
|
+
form.addEventListener("turbo:submit-end", (() => form.remove()), {
|
|
1167
|
+
once: true
|
|
1168
|
+
});
|
|
1169
|
+
requestAnimationFrame((() => form.requestSubmit()));
|
|
1154
1170
|
}
|
|
1155
1171
|
}
|
|
1156
1172
|
|
|
@@ -1522,22 +1538,22 @@ function elementIsTracked(element) {
|
|
|
1522
1538
|
}
|
|
1523
1539
|
|
|
1524
1540
|
function elementIsScript(element) {
|
|
1525
|
-
const tagName = element.
|
|
1541
|
+
const tagName = element.localName;
|
|
1526
1542
|
return tagName == "script";
|
|
1527
1543
|
}
|
|
1528
1544
|
|
|
1529
1545
|
function elementIsNoscript(element) {
|
|
1530
|
-
const tagName = element.
|
|
1546
|
+
const tagName = element.localName;
|
|
1531
1547
|
return tagName == "noscript";
|
|
1532
1548
|
}
|
|
1533
1549
|
|
|
1534
1550
|
function elementIsStylesheet(element) {
|
|
1535
|
-
const tagName = element.
|
|
1551
|
+
const tagName = element.localName;
|
|
1536
1552
|
return tagName == "style" || tagName == "link" && element.getAttribute("rel") == "stylesheet";
|
|
1537
1553
|
}
|
|
1538
1554
|
|
|
1539
1555
|
function elementIsMetaElementWithName(element, name) {
|
|
1540
|
-
const tagName = element.
|
|
1556
|
+
const tagName = element.localName;
|
|
1541
1557
|
return tagName == "meta" && element.getAttribute("name") == name;
|
|
1542
1558
|
}
|
|
1543
1559
|
|
|
@@ -1563,7 +1579,18 @@ class PageSnapshot extends Snapshot {
|
|
|
1563
1579
|
return new this(body, new HeadSnapshot(head));
|
|
1564
1580
|
}
|
|
1565
1581
|
clone() {
|
|
1566
|
-
|
|
1582
|
+
const clonedElement = this.element.cloneNode(true);
|
|
1583
|
+
const selectElements = this.element.querySelectorAll("select");
|
|
1584
|
+
const clonedSelectElements = clonedElement.querySelectorAll("select");
|
|
1585
|
+
for (const [index, source] of selectElements.entries()) {
|
|
1586
|
+
const clone = clonedSelectElements[index];
|
|
1587
|
+
for (const option of clone.selectedOptions) option.selected = false;
|
|
1588
|
+
for (const option of source.selectedOptions) clone.options[option.index].selected = true;
|
|
1589
|
+
}
|
|
1590
|
+
for (const clonedPasswordInput of clonedElement.querySelectorAll('input[type="password"]')) {
|
|
1591
|
+
clonedPasswordInput.value = "";
|
|
1592
|
+
}
|
|
1593
|
+
return new PageSnapshot(clonedElement, this.headSnapshot);
|
|
1567
1594
|
}
|
|
1568
1595
|
get headElement() {
|
|
1569
1596
|
return this.headSnapshot.element;
|
|
@@ -1616,7 +1643,8 @@ const defaultOptions = {
|
|
|
1616
1643
|
willRender: true,
|
|
1617
1644
|
updateHistory: true,
|
|
1618
1645
|
shouldCacheSnapshot: true,
|
|
1619
|
-
acceptsStreamResponse: false
|
|
1646
|
+
acceptsStreamResponse: false,
|
|
1647
|
+
initiator: document.documentElement
|
|
1620
1648
|
};
|
|
1621
1649
|
|
|
1622
1650
|
var SystemStatusCode;
|
|
@@ -1629,7 +1657,6 @@ var SystemStatusCode;
|
|
|
1629
1657
|
|
|
1630
1658
|
class Visit {
|
|
1631
1659
|
constructor(delegate, location, restorationIdentifier, options = {}) {
|
|
1632
|
-
this.identifier = uuid();
|
|
1633
1660
|
this.timingMetrics = {};
|
|
1634
1661
|
this.followedRedirect = false;
|
|
1635
1662
|
this.historyChanged = false;
|
|
@@ -1645,7 +1672,7 @@ class Visit {
|
|
|
1645
1672
|
resolve: resolve,
|
|
1646
1673
|
reject: reject
|
|
1647
1674
|
}));
|
|
1648
|
-
const {action: action, historyChanged: historyChanged, referrer: referrer, snapshotHTML: snapshotHTML, response: response, visitCachedSnapshot: visitCachedSnapshot, willRender: willRender, updateHistory: updateHistory, shouldCacheSnapshot: shouldCacheSnapshot, acceptsStreamResponse: acceptsStreamResponse} = Object.assign(Object.assign({}, defaultOptions), options);
|
|
1675
|
+
const {action: action, historyChanged: historyChanged, referrer: referrer, snapshotHTML: snapshotHTML, response: response, visitCachedSnapshot: visitCachedSnapshot, willRender: willRender, updateHistory: updateHistory, shouldCacheSnapshot: shouldCacheSnapshot, acceptsStreamResponse: acceptsStreamResponse, initiator: initiator} = Object.assign(Object.assign({}, defaultOptions), options);
|
|
1649
1676
|
this.action = action;
|
|
1650
1677
|
this.historyChanged = historyChanged;
|
|
1651
1678
|
this.referrer = referrer;
|
|
@@ -1658,6 +1685,7 @@ class Visit {
|
|
|
1658
1685
|
this.scrolled = !willRender;
|
|
1659
1686
|
this.shouldCacheSnapshot = shouldCacheSnapshot;
|
|
1660
1687
|
this.acceptsStreamResponse = acceptsStreamResponse;
|
|
1688
|
+
this.initiator = initiator;
|
|
1661
1689
|
}
|
|
1662
1690
|
get adapter() {
|
|
1663
1691
|
return this.delegate.adapter;
|
|
@@ -1724,7 +1752,7 @@ class Visit {
|
|
|
1724
1752
|
if (this.hasPreloadedResponse()) {
|
|
1725
1753
|
this.simulateRequest();
|
|
1726
1754
|
} else if (this.shouldIssueRequest() && !this.request) {
|
|
1727
|
-
this.request = new FetchRequest(this, FetchMethod.get, this.location);
|
|
1755
|
+
this.request = new FetchRequest(this, FetchMethod.get, this.location, undefined, this.initiator);
|
|
1728
1756
|
this.request.perform();
|
|
1729
1757
|
}
|
|
1730
1758
|
}
|
|
@@ -1814,7 +1842,6 @@ class Visit {
|
|
|
1814
1842
|
if (this.redirectedToLocation && !this.followedRedirect && ((_a = this.response) === null || _a === void 0 ? void 0 : _a.redirected)) {
|
|
1815
1843
|
this.adapter.visitProposedToLocation(this.redirectedToLocation, {
|
|
1816
1844
|
action: "replace",
|
|
1817
|
-
willRender: false,
|
|
1818
1845
|
response: this.response
|
|
1819
1846
|
});
|
|
1820
1847
|
this.followedRedirect = true;
|
|
@@ -2229,7 +2256,7 @@ class Navigator {
|
|
|
2229
2256
|
this.delegate = delegate;
|
|
2230
2257
|
}
|
|
2231
2258
|
proposeVisit(location, options = {}) {
|
|
2232
|
-
if (this.delegate.
|
|
2259
|
+
if (this.delegate.allowsVisitingLocation(location, options)) {
|
|
2233
2260
|
if (locationIsVisitable(location, this.view.snapshot.rootLocation)) {
|
|
2234
2261
|
return this.delegate.visitProposedToLocation(location, options);
|
|
2235
2262
|
} else {
|
|
@@ -2442,6 +2469,31 @@ class ScrollObserver {
|
|
|
2442
2469
|
}
|
|
2443
2470
|
}
|
|
2444
2471
|
|
|
2472
|
+
class StreamMessageRenderer {
|
|
2473
|
+
render({fragment: fragment}) {
|
|
2474
|
+
Bardo.preservingPermanentElements(this, getPermanentElementMapForFragment(fragment), (() => document.documentElement.appendChild(fragment)));
|
|
2475
|
+
}
|
|
2476
|
+
enteringBardo(currentPermanentElement, newPermanentElement) {
|
|
2477
|
+
newPermanentElement.replaceWith(currentPermanentElement.cloneNode(true));
|
|
2478
|
+
}
|
|
2479
|
+
leavingBardo() {}
|
|
2480
|
+
}
|
|
2481
|
+
|
|
2482
|
+
function getPermanentElementMapForFragment(fragment) {
|
|
2483
|
+
const permanentElementsInDocument = queryPermanentElementsAll(document.documentElement);
|
|
2484
|
+
const permanentElementMap = {};
|
|
2485
|
+
for (const permanentElementInDocument of permanentElementsInDocument) {
|
|
2486
|
+
const {id: id} = permanentElementInDocument;
|
|
2487
|
+
for (const streamElement of fragment.querySelectorAll("turbo-stream")) {
|
|
2488
|
+
const elementInStream = getPermanentElementById(streamElement.templateElement.content, id);
|
|
2489
|
+
if (elementInStream) {
|
|
2490
|
+
permanentElementMap[id] = [ permanentElementInDocument, elementInStream ];
|
|
2491
|
+
}
|
|
2492
|
+
}
|
|
2493
|
+
}
|
|
2494
|
+
return permanentElementMap;
|
|
2495
|
+
}
|
|
2496
|
+
|
|
2445
2497
|
class StreamObserver {
|
|
2446
2498
|
constructor(delegate) {
|
|
2447
2499
|
this.sources = new Set;
|
|
@@ -2803,6 +2855,7 @@ class Session {
|
|
|
2803
2855
|
this.streamObserver = new StreamObserver(this);
|
|
2804
2856
|
this.formLinkClickObserver = new FormLinkClickObserver(this, document.documentElement);
|
|
2805
2857
|
this.frameRedirector = new FrameRedirector(this, document.documentElement);
|
|
2858
|
+
this.streamMessageRenderer = new StreamMessageRenderer;
|
|
2806
2859
|
this.drive = true;
|
|
2807
2860
|
this.enabled = true;
|
|
2808
2861
|
this.progressBarDelay = 500;
|
|
@@ -2846,7 +2899,7 @@ class Session {
|
|
|
2846
2899
|
this.adapter = adapter;
|
|
2847
2900
|
}
|
|
2848
2901
|
visit(location, options = {}) {
|
|
2849
|
-
const frameElement = document.getElementById(options.frame
|
|
2902
|
+
const frameElement = options.frame ? document.getElementById(options.frame) : null;
|
|
2850
2903
|
if (frameElement instanceof FrameElement) {
|
|
2851
2904
|
frameElement.src = location.toString();
|
|
2852
2905
|
return frameElement.loaded;
|
|
@@ -2861,7 +2914,7 @@ class Session {
|
|
|
2861
2914
|
this.streamObserver.disconnectStreamSource(source);
|
|
2862
2915
|
}
|
|
2863
2916
|
renderStreamMessage(message) {
|
|
2864
|
-
|
|
2917
|
+
this.streamMessageRenderer.render(StreamMessage.wrap(message));
|
|
2865
2918
|
}
|
|
2866
2919
|
clearCache() {
|
|
2867
2920
|
this.view.clearSnapshotCache();
|
|
@@ -2907,23 +2960,28 @@ class Session {
|
|
|
2907
2960
|
const acceptsStreamResponse = link.hasAttribute("data-turbo-stream");
|
|
2908
2961
|
this.visit(location.href, {
|
|
2909
2962
|
action: action,
|
|
2910
|
-
acceptsStreamResponse: acceptsStreamResponse
|
|
2963
|
+
acceptsStreamResponse: acceptsStreamResponse,
|
|
2964
|
+
initiator: link
|
|
2911
2965
|
});
|
|
2912
2966
|
}
|
|
2913
|
-
|
|
2914
|
-
return this.locationWithActionIsSamePage(location, action) || this.applicationAllowsVisitingLocation(location);
|
|
2967
|
+
allowsVisitingLocation(location, options = {}) {
|
|
2968
|
+
return this.locationWithActionIsSamePage(location, options.action) || this.applicationAllowsVisitingLocation(location, options);
|
|
2915
2969
|
}
|
|
2916
2970
|
visitProposedToLocation(location, options) {
|
|
2917
2971
|
extendURLWithDeprecatedProperties(location);
|
|
2918
2972
|
return this.adapter.visitProposedToLocation(location, options);
|
|
2919
2973
|
}
|
|
2920
2974
|
visitStarted(visit) {
|
|
2975
|
+
if (!visit.acceptsStreamResponse) {
|
|
2976
|
+
markAsBusy(document.documentElement);
|
|
2977
|
+
}
|
|
2921
2978
|
extendURLWithDeprecatedProperties(visit.location);
|
|
2922
2979
|
if (!visit.silent) {
|
|
2923
|
-
this.notifyApplicationAfterVisitingLocation(visit.location, visit.action);
|
|
2980
|
+
this.notifyApplicationAfterVisitingLocation(visit.location, visit.action, visit.initiator);
|
|
2924
2981
|
}
|
|
2925
2982
|
}
|
|
2926
2983
|
visitCompleted(visit) {
|
|
2984
|
+
clearBusyState(document.documentElement);
|
|
2927
2985
|
this.notifyApplicationAfterPageLoad(visit.getTimingMetrics());
|
|
2928
2986
|
}
|
|
2929
2987
|
locationWithActionIsSamePage(location, action) {
|
|
@@ -2982,16 +3040,12 @@ class Session {
|
|
|
2982
3040
|
frameRendered(fetchResponse, frame) {
|
|
2983
3041
|
this.notifyApplicationAfterFrameRender(fetchResponse, frame);
|
|
2984
3042
|
}
|
|
2985
|
-
frameMissing(frame, fetchResponse) {
|
|
2986
|
-
console.warn(`Completing full-page visit as matching frame for #${frame.id} was missing from the response`);
|
|
2987
|
-
return this.visit(fetchResponse.location);
|
|
2988
|
-
}
|
|
2989
3043
|
applicationAllowsFollowingLinkToLocation(link, location, ev) {
|
|
2990
3044
|
const event = this.notifyApplicationAfterClickingLinkToLocation(link, location, ev);
|
|
2991
3045
|
return !event.defaultPrevented;
|
|
2992
3046
|
}
|
|
2993
|
-
applicationAllowsVisitingLocation(location) {
|
|
2994
|
-
const event = this.notifyApplicationBeforeVisitingLocation(location);
|
|
3047
|
+
applicationAllowsVisitingLocation(location, options = {}) {
|
|
3048
|
+
const event = this.notifyApplicationBeforeVisitingLocation(location, options.initiator);
|
|
2995
3049
|
return !event.defaultPrevented;
|
|
2996
3050
|
}
|
|
2997
3051
|
notifyApplicationAfterClickingLinkToLocation(link, location, event) {
|
|
@@ -3004,17 +3058,18 @@ class Session {
|
|
|
3004
3058
|
cancelable: true
|
|
3005
3059
|
});
|
|
3006
3060
|
}
|
|
3007
|
-
notifyApplicationBeforeVisitingLocation(location) {
|
|
3061
|
+
notifyApplicationBeforeVisitingLocation(location, element) {
|
|
3008
3062
|
return dispatch("turbo:before-visit", {
|
|
3063
|
+
target: element,
|
|
3009
3064
|
detail: {
|
|
3010
3065
|
url: location.href
|
|
3011
3066
|
},
|
|
3012
3067
|
cancelable: true
|
|
3013
3068
|
});
|
|
3014
3069
|
}
|
|
3015
|
-
notifyApplicationAfterVisitingLocation(location, action) {
|
|
3016
|
-
markAsBusy(document.documentElement);
|
|
3070
|
+
notifyApplicationAfterVisitingLocation(location, action, element) {
|
|
3017
3071
|
return dispatch("turbo:visit", {
|
|
3072
|
+
target: element,
|
|
3018
3073
|
detail: {
|
|
3019
3074
|
url: location.href,
|
|
3020
3075
|
action: action
|
|
@@ -3036,7 +3091,6 @@ class Session {
|
|
|
3036
3091
|
return dispatch("turbo:render");
|
|
3037
3092
|
}
|
|
3038
3093
|
notifyApplicationAfterPageLoad(timing = {}) {
|
|
3039
|
-
clearBusyState(document.documentElement);
|
|
3040
3094
|
return dispatch("turbo:load", {
|
|
3041
3095
|
detail: {
|
|
3042
3096
|
url: this.location.href,
|
|
@@ -3334,8 +3388,9 @@ class FrameController {
|
|
|
3334
3388
|
session.frameRendered(fetchResponse, this.element);
|
|
3335
3389
|
session.frameLoaded(this.element);
|
|
3336
3390
|
this.fetchResponseLoaded(fetchResponse);
|
|
3337
|
-
} else if (this.
|
|
3338
|
-
|
|
3391
|
+
} else if (this.willHandleFrameMissingFromResponse(fetchResponse)) {
|
|
3392
|
+
console.warn(`A matching frame for #${this.element.id} was missing from the response, transforming into full-page Visit.`);
|
|
3393
|
+
this.visitResponse(fetchResponse.response);
|
|
3339
3394
|
}
|
|
3340
3395
|
}
|
|
3341
3396
|
} catch (error) {
|
|
@@ -3390,19 +3445,13 @@ class FrameController {
|
|
|
3390
3445
|
await this.loadResponse(response);
|
|
3391
3446
|
this.resolveVisitPromise();
|
|
3392
3447
|
}
|
|
3393
|
-
requestFailedWithResponse(request, response) {
|
|
3448
|
+
async requestFailedWithResponse(request, response) {
|
|
3394
3449
|
console.error(response);
|
|
3450
|
+
await this.loadResponse(response);
|
|
3395
3451
|
this.resolveVisitPromise();
|
|
3396
3452
|
}
|
|
3397
3453
|
requestErrored(request, error) {
|
|
3398
3454
|
console.error(error);
|
|
3399
|
-
dispatch("turbo:fetch-request-error", {
|
|
3400
|
-
target: this.element,
|
|
3401
|
-
detail: {
|
|
3402
|
-
request: request,
|
|
3403
|
-
error: error
|
|
3404
|
-
}
|
|
3405
|
-
});
|
|
3406
3455
|
this.resolveVisitPromise();
|
|
3407
3456
|
}
|
|
3408
3457
|
requestFinished(_request) {
|
|
@@ -3501,17 +3550,38 @@ class FrameController {
|
|
|
3501
3550
|
session.history.update(method, expandURL(this.frame.src || ""), this.restorationIdentifier);
|
|
3502
3551
|
}
|
|
3503
3552
|
}
|
|
3504
|
-
|
|
3553
|
+
willHandleFrameMissingFromResponse(fetchResponse) {
|
|
3505
3554
|
this.element.setAttribute("complete", "");
|
|
3555
|
+
const response = fetchResponse.response;
|
|
3556
|
+
const visit = async (url, options = {}) => {
|
|
3557
|
+
if (url instanceof Response) {
|
|
3558
|
+
this.visitResponse(url);
|
|
3559
|
+
} else {
|
|
3560
|
+
session.visit(url, options);
|
|
3561
|
+
}
|
|
3562
|
+
};
|
|
3506
3563
|
const event = dispatch("turbo:frame-missing", {
|
|
3507
3564
|
target: this.element,
|
|
3508
3565
|
detail: {
|
|
3509
|
-
|
|
3566
|
+
response: response,
|
|
3567
|
+
visit: visit
|
|
3510
3568
|
},
|
|
3511
3569
|
cancelable: true
|
|
3512
3570
|
});
|
|
3513
3571
|
return !event.defaultPrevented;
|
|
3514
3572
|
}
|
|
3573
|
+
async visitResponse(response) {
|
|
3574
|
+
const wrapped = new FetchResponse(response);
|
|
3575
|
+
const responseHTML = await wrapped.responseHTML;
|
|
3576
|
+
const {location: location, redirected: redirected, statusCode: statusCode} = wrapped;
|
|
3577
|
+
return session.visit(location, {
|
|
3578
|
+
response: {
|
|
3579
|
+
redirected: redirected,
|
|
3580
|
+
statusCode: statusCode,
|
|
3581
|
+
responseHTML: responseHTML
|
|
3582
|
+
}
|
|
3583
|
+
});
|
|
3584
|
+
}
|
|
3515
3585
|
findFrameElement(element, submitter) {
|
|
3516
3586
|
var _a;
|
|
3517
3587
|
const id = getAttribute("data-turbo-frame", submitter, element) || this.element.getAttribute("target");
|
|
@@ -3647,6 +3717,9 @@ function activateElement(element, currentURL) {
|
|
|
3647
3717
|
}
|
|
3648
3718
|
|
|
3649
3719
|
class StreamElement extends HTMLElement {
|
|
3720
|
+
static async renderElement(newElement) {
|
|
3721
|
+
await newElement.performAction();
|
|
3722
|
+
}
|
|
3650
3723
|
async connectedCallback() {
|
|
3651
3724
|
try {
|
|
3652
3725
|
await this.render();
|
|
@@ -3659,9 +3732,10 @@ class StreamElement extends HTMLElement {
|
|
|
3659
3732
|
async render() {
|
|
3660
3733
|
var _a;
|
|
3661
3734
|
return (_a = this.renderPromise) !== null && _a !== void 0 ? _a : this.renderPromise = (async () => {
|
|
3662
|
-
|
|
3735
|
+
const event = this.beforeRenderEvent;
|
|
3736
|
+
if (this.dispatchEvent(event)) {
|
|
3663
3737
|
await nextAnimationFrame();
|
|
3664
|
-
|
|
3738
|
+
await event.detail.render(this);
|
|
3665
3739
|
}
|
|
3666
3740
|
})();
|
|
3667
3741
|
}
|
|
@@ -3702,7 +3776,11 @@ class StreamElement extends HTMLElement {
|
|
|
3702
3776
|
return this.templateElement.content.cloneNode(true);
|
|
3703
3777
|
}
|
|
3704
3778
|
get templateElement() {
|
|
3705
|
-
if (this.firstElementChild
|
|
3779
|
+
if (this.firstElementChild === null) {
|
|
3780
|
+
const template = this.ownerDocument.createElement("template");
|
|
3781
|
+
this.appendChild(template);
|
|
3782
|
+
return template;
|
|
3783
|
+
} else if (this.firstElementChild instanceof HTMLTemplateElement) {
|
|
3706
3784
|
return this.firstElementChild;
|
|
3707
3785
|
}
|
|
3708
3786
|
this.raise("first child element must be a <template> element");
|
|
@@ -3728,7 +3806,8 @@ class StreamElement extends HTMLElement {
|
|
|
3728
3806
|
bubbles: true,
|
|
3729
3807
|
cancelable: true,
|
|
3730
3808
|
detail: {
|
|
3731
|
-
newStream: this
|
|
3809
|
+
newStream: this,
|
|
3810
|
+
render: StreamElement.renderElement
|
|
3732
3811
|
}
|
|
3733
3812
|
});
|
|
3734
3813
|
}
|
|
@@ -3813,10 +3892,16 @@ start();
|
|
|
3813
3892
|
|
|
3814
3893
|
var turbo_es2017Esm = Object.freeze({
|
|
3815
3894
|
__proto__: null,
|
|
3895
|
+
FrameElement: FrameElement,
|
|
3896
|
+
get FrameLoadingStyle() {
|
|
3897
|
+
return FrameLoadingStyle;
|
|
3898
|
+
},
|
|
3816
3899
|
FrameRenderer: FrameRenderer,
|
|
3817
3900
|
PageRenderer: PageRenderer,
|
|
3818
3901
|
PageSnapshot: PageSnapshot,
|
|
3819
3902
|
StreamActions: StreamActions,
|
|
3903
|
+
StreamElement: StreamElement,
|
|
3904
|
+
StreamSourceElement: StreamSourceElement,
|
|
3820
3905
|
cache: cache,
|
|
3821
3906
|
clearCache: clearCache,
|
|
3822
3907
|
connectStreamSource: connectStreamSource,
|
|
@@ -3907,13 +3992,26 @@ class TurboCableStreamSourceElement extends HTMLElement {
|
|
|
3907
3992
|
|
|
3908
3993
|
customElements.define("turbo-cable-stream-source", TurboCableStreamSourceElement);
|
|
3909
3994
|
|
|
3910
|
-
function
|
|
3911
|
-
if (
|
|
3912
|
-
|
|
3995
|
+
function encodeMethodIntoRequestBody(event) {
|
|
3996
|
+
if (event.target instanceof HTMLFormElement) {
|
|
3997
|
+
const {target: form, detail: {fetchOptions: fetchOptions}} = event;
|
|
3998
|
+
form.addEventListener("turbo:submit-start", (({detail: {formSubmission: {submitter: submitter}}}) => {
|
|
3999
|
+
const method = submitter && submitter.formMethod || fetchOptions.body.get("_method") || form.getAttribute("method");
|
|
4000
|
+
if (!/get/i.test(method)) {
|
|
4001
|
+
if (/post/i.test(method)) {
|
|
4002
|
+
fetchOptions.body.delete("_method");
|
|
4003
|
+
} else {
|
|
4004
|
+
fetchOptions.body.set("_method", method);
|
|
4005
|
+
}
|
|
4006
|
+
fetchOptions.method = "post";
|
|
4007
|
+
}
|
|
4008
|
+
}), {
|
|
4009
|
+
once: true
|
|
4010
|
+
});
|
|
3913
4011
|
}
|
|
3914
4012
|
}
|
|
3915
4013
|
|
|
3916
|
-
addEventListener("turbo:
|
|
4014
|
+
addEventListener("turbo:before-fetch-request", encodeMethodIntoRequestBody);
|
|
3917
4015
|
|
|
3918
4016
|
var adapters = {
|
|
3919
4017
|
logger: self.console,
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export function encodeMethodIntoRequestBody(event) {
|
|
2
|
+
if (event.target instanceof HTMLFormElement) {
|
|
3
|
+
const { target: form, detail: { fetchOptions } } = event
|
|
4
|
+
|
|
5
|
+
form.addEventListener("turbo:submit-start", ({ detail: { formSubmission: { submitter } } }) => {
|
|
6
|
+
const method = (submitter && submitter.formMethod) || fetchOptions.body.get("_method") || form.getAttribute("method")
|
|
7
|
+
|
|
8
|
+
if (!/get/i.test(method)) {
|
|
9
|
+
if (/post/i.test(method)) {
|
|
10
|
+
fetchOptions.body.delete("_method")
|
|
11
|
+
} else {
|
|
12
|
+
fetchOptions.body.set("_method", method)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
fetchOptions.method = "post"
|
|
16
|
+
}
|
|
17
|
+
}, { once: true })
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import "./cable_stream_source_element"
|
|
2
|
-
import { overrideMethodWithFormmethod } from "./form_submissions"
|
|
3
2
|
|
|
4
3
|
import * as Turbo from "@hotwired/turbo"
|
|
5
4
|
export { Turbo }
|
|
@@ -7,4 +6,6 @@ export { Turbo }
|
|
|
7
6
|
import * as cable from "./cable"
|
|
8
7
|
export { cable }
|
|
9
8
|
|
|
10
|
-
|
|
9
|
+
import { encodeMethodIntoRequestBody } from "./fetch_requests"
|
|
10
|
+
|
|
11
|
+
addEventListener("turbo:before-fetch-request", encodeMethodIntoRequestBody)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hotwired/turbo-rails",
|
|
3
|
-
"version": "7.2.0-
|
|
3
|
+
"version": "7.2.0-rc.2",
|
|
4
4
|
"description": "The speed of a single-page web application without having to write any JavaScript",
|
|
5
5
|
"module": "app/javascript/turbo/index.js",
|
|
6
6
|
"main": "app/assets/javascripts/turbo.js",
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"release": "npm publish && git commit -am \"$npm_package_name v$npm_package_version\" && git push"
|
|
14
14
|
},
|
|
15
15
|
"dependencies": {
|
|
16
|
-
"@hotwired/turbo": "^7.2.0-
|
|
16
|
+
"@hotwired/turbo": "^7.2.0-rc.2",
|
|
17
17
|
"@rails/actioncable": "^7.0"
|
|
18
18
|
},
|
|
19
19
|
"devDependencies": {
|