turbo-rails 0.7.0 → 0.7.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/assets/javascripts/turbo.js +180 -99
- data/app/helpers/turbo/streams/action_helper.rb +2 -3
- data/app/models/turbo/streams/tag_builder.rb +2 -12
- data/lib/install/turbo_needs_redis.rb +9 -0
- data/lib/install/turbo_with_asset_pipeline.rb +20 -12
- data/lib/install/turbo_with_webpacker.rb +3 -23
- data/lib/tasks/turbo_tasks.rake +5 -0
- data/lib/turbo/engine.rb +1 -9
- data/lib/turbo/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8f47876ccf2310b44ddc2446fc87935f5284921f515412dfb42c100f53f636c7
|
4
|
+
data.tar.gz: 77553bb8bed6eeb53dc723a1cab189aa37e695e7b75201d27fdf32a6836d7771
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a0231dd2d133aaa53e2143ea9119b5753c4df4fcc7e61087e6218de680d42fe4f57e0f9508dcd72d2a5446ef828708754417d2dafd3e53ea72278f360eaea49f
|
7
|
+
data.tar.gz: cb7099f4d21432fa0a901fd4e2e692b1ea8fac7120ce6bc906475b3b0bfb72adf6920e67b0e4a3a5439c1d20e76c6c777b1707d9aa32effd5e555569656fb3a3
|
@@ -361,8 +361,10 @@ class FetchRequest {
|
|
361
361
|
const response = await fetch(this.url.href, fetchOptions);
|
362
362
|
return await this.receive(response);
|
363
363
|
} catch (error) {
|
364
|
-
|
365
|
-
|
364
|
+
if (error.name !== "AbortError") {
|
365
|
+
this.delegate.requestErrored(this, error);
|
366
|
+
throw error;
|
367
|
+
}
|
366
368
|
} finally {
|
367
369
|
this.delegate.requestFinished(this);
|
368
370
|
}
|
@@ -385,13 +387,15 @@ class FetchRequest {
|
|
385
387
|
return fetchResponse;
|
386
388
|
}
|
387
389
|
get fetchOptions() {
|
390
|
+
var _a;
|
388
391
|
return {
|
389
392
|
method: FetchMethod[this.method].toUpperCase(),
|
390
393
|
credentials: "same-origin",
|
391
394
|
headers: this.headers,
|
392
395
|
redirect: "follow",
|
393
396
|
body: this.body,
|
394
|
-
signal: this.abortSignal
|
397
|
+
signal: this.abortSignal,
|
398
|
+
referrer: (_a = this.delegate.referrer) === null || _a === void 0 ? void 0 : _a.href
|
395
399
|
};
|
396
400
|
}
|
397
401
|
get defaultHeaders() {
|
@@ -544,7 +548,8 @@ class FormSubmission {
|
|
544
548
|
}
|
545
549
|
get action() {
|
546
550
|
var _a;
|
547
|
-
|
551
|
+
const formElementAction = typeof this.formElement.action === "string" ? this.formElement.action : null;
|
552
|
+
return ((_a = this.submitter) === null || _a === void 0 ? void 0 : _a.getAttribute("formaction")) || this.formElement.getAttribute("action") || formElementAction || "";
|
548
553
|
}
|
549
554
|
get location() {
|
550
555
|
return expandURL(this.action);
|
@@ -691,11 +696,7 @@ class Snapshot {
|
|
691
696
|
return this.getElementForAnchor(anchor) != null;
|
692
697
|
}
|
693
698
|
getElementForAnchor(anchor) {
|
694
|
-
|
695
|
-
return this.element.querySelector(`[id='${anchor}'], a[name='${anchor}']`);
|
696
|
-
} catch (_a) {
|
697
|
-
return null;
|
698
|
-
}
|
699
|
+
return anchor ? this.element.querySelector(`[id='${anchor}'], a[name='${anchor}']`) : null;
|
699
700
|
}
|
700
701
|
get isConnected() {
|
701
702
|
return this.element.isConnected;
|
@@ -785,6 +786,12 @@ class View {
|
|
785
786
|
scrollToPosition({x: x, y: y}) {
|
786
787
|
this.scrollRoot.scrollTo(x, y);
|
787
788
|
}
|
789
|
+
scrollToTop() {
|
790
|
+
this.scrollToPosition({
|
791
|
+
x: 0,
|
792
|
+
y: 0
|
793
|
+
});
|
794
|
+
}
|
788
795
|
get scrollRoot() {
|
789
796
|
return window;
|
790
797
|
}
|
@@ -1354,6 +1361,9 @@ class Visit {
|
|
1354
1361
|
get restorationData() {
|
1355
1362
|
return this.history.getRestorationDataForIdentifier(this.restorationIdentifier);
|
1356
1363
|
}
|
1364
|
+
get silent() {
|
1365
|
+
return this.isSamePage;
|
1366
|
+
}
|
1357
1367
|
start() {
|
1358
1368
|
if (this.state == VisitState.initialized) {
|
1359
1369
|
this.recordTimingMetric(TimingMetric.visitStart);
|
@@ -1377,6 +1387,7 @@ class Visit {
|
|
1377
1387
|
this.state = VisitState.completed;
|
1378
1388
|
this.adapter.visitCompleted(this);
|
1379
1389
|
this.delegate.visitCompleted(this);
|
1390
|
+
this.followRedirect();
|
1380
1391
|
}
|
1381
1392
|
}
|
1382
1393
|
fail() {
|
@@ -1483,8 +1494,10 @@ class Visit {
|
|
1483
1494
|
}
|
1484
1495
|
followRedirect() {
|
1485
1496
|
if (this.redirectedToLocation && !this.followedRedirect) {
|
1486
|
-
this.
|
1487
|
-
|
1497
|
+
this.adapter.visitProposedToLocation(this.redirectedToLocation, {
|
1498
|
+
action: "replace",
|
1499
|
+
response: this.response
|
1500
|
+
});
|
1488
1501
|
this.followedRedirect = true;
|
1489
1502
|
}
|
1490
1503
|
}
|
@@ -1538,9 +1551,9 @@ class Visit {
|
|
1538
1551
|
performScroll() {
|
1539
1552
|
if (!this.scrolled) {
|
1540
1553
|
if (this.action == "restore") {
|
1541
|
-
this.scrollToRestoredPosition() || this.scrollToAnchor() || this.scrollToTop();
|
1554
|
+
this.scrollToRestoredPosition() || this.scrollToAnchor() || this.view.scrollToTop();
|
1542
1555
|
} else {
|
1543
|
-
this.scrollToAnchor() || this.scrollToTop();
|
1556
|
+
this.scrollToAnchor() || this.view.scrollToTop();
|
1544
1557
|
}
|
1545
1558
|
if (this.isSamePage) {
|
1546
1559
|
this.delegate.visitScrolledToSamePageLocation(this.view.lastRenderedLocation, this.location);
|
@@ -1562,12 +1575,6 @@ class Visit {
|
|
1562
1575
|
return true;
|
1563
1576
|
}
|
1564
1577
|
}
|
1565
|
-
scrollToTop() {
|
1566
|
-
this.view.scrollToPosition({
|
1567
|
-
x: 0,
|
1568
|
-
y: 0
|
1569
|
-
});
|
1570
|
-
}
|
1571
1578
|
recordTimingMetric(metric) {
|
1572
1579
|
this.timingMetrics[metric] = (new Date).getTime();
|
1573
1580
|
}
|
@@ -1666,14 +1673,20 @@ class BrowserAdapter {
|
|
1666
1673
|
this.progressBar.setValue(1);
|
1667
1674
|
this.hideProgressBar();
|
1668
1675
|
}
|
1669
|
-
visitCompleted(visit) {
|
1670
|
-
visit.followRedirect();
|
1671
|
-
}
|
1676
|
+
visitCompleted(visit) {}
|
1672
1677
|
pageInvalidated() {
|
1673
1678
|
this.reload();
|
1674
1679
|
}
|
1675
1680
|
visitFailed(visit) {}
|
1676
1681
|
visitRendered(visit) {}
|
1682
|
+
formSubmissionStarted(formSubmission) {
|
1683
|
+
this.progressBar.setValue(0);
|
1684
|
+
this.showProgressBarAfterDelay();
|
1685
|
+
}
|
1686
|
+
formSubmissionFinished(formSubmission) {
|
1687
|
+
this.progressBar.setValue(1);
|
1688
|
+
this.hideProgressBar();
|
1689
|
+
}
|
1677
1690
|
showProgressBarAfterDelay() {
|
1678
1691
|
this.progressBarTimeout = window.setTimeout(this.showProgressBar, this.session.progressBarDelay);
|
1679
1692
|
}
|
@@ -1891,7 +1904,8 @@ class LinkClickObserver {
|
|
1891
1904
|
};
|
1892
1905
|
this.clickBubbled = event => {
|
1893
1906
|
if (this.clickEventIsSignificant(event)) {
|
1894
|
-
const
|
1907
|
+
const target = event.composedPath && event.composedPath()[0] || event.target;
|
1908
|
+
const link = this.findLinkFromClickTarget(target);
|
1895
1909
|
if (link) {
|
1896
1910
|
const location = this.getLocationForLink(link);
|
1897
1911
|
if (this.delegate.willFollowLinkToLocation(link, location)) {
|
@@ -1937,7 +1951,7 @@ class Navigator {
|
|
1937
1951
|
this.delegate = delegate;
|
1938
1952
|
}
|
1939
1953
|
proposeVisit(location, options = {}) {
|
1940
|
-
if (this.delegate.
|
1954
|
+
if (this.delegate.allowsVisitingLocationWithAction(location, options.action)) {
|
1941
1955
|
this.delegate.visitProposedToLocation(location, options);
|
1942
1956
|
}
|
1943
1957
|
}
|
@@ -1978,7 +1992,11 @@ class Navigator {
|
|
1978
1992
|
get history() {
|
1979
1993
|
return this.delegate.history;
|
1980
1994
|
}
|
1981
|
-
formSubmissionStarted(formSubmission) {
|
1995
|
+
formSubmissionStarted(formSubmission) {
|
1996
|
+
if (typeof this.adapter.formSubmissionStarted === "function") {
|
1997
|
+
this.adapter.formSubmissionStarted(formSubmission);
|
1998
|
+
}
|
1999
|
+
}
|
1982
2000
|
async formSubmissionSucceededWithResponse(formSubmission, fetchResponse) {
|
1983
2001
|
if (formSubmission == this.formSubmission) {
|
1984
2002
|
const responseHTML = await fetchResponse.responseHTML;
|
@@ -2001,14 +2019,23 @@ class Navigator {
|
|
2001
2019
|
const responseHTML = await fetchResponse.responseHTML;
|
2002
2020
|
if (responseHTML) {
|
2003
2021
|
const snapshot = PageSnapshot.fromHTMLString(responseHTML);
|
2004
|
-
|
2022
|
+
if (fetchResponse.serverError) {
|
2023
|
+
await this.view.renderError(snapshot);
|
2024
|
+
} else {
|
2025
|
+
await this.view.renderPage(snapshot);
|
2026
|
+
}
|
2027
|
+
this.view.scrollToTop();
|
2005
2028
|
this.view.clearSnapshotCache();
|
2006
2029
|
}
|
2007
2030
|
}
|
2008
2031
|
formSubmissionErrored(formSubmission, error) {
|
2009
2032
|
console.error(error);
|
2010
2033
|
}
|
2011
|
-
formSubmissionFinished(formSubmission) {
|
2034
|
+
formSubmissionFinished(formSubmission) {
|
2035
|
+
if (typeof this.adapter.formSubmissionFinished === "function") {
|
2036
|
+
this.adapter.formSubmissionFinished(formSubmission);
|
2037
|
+
}
|
2038
|
+
}
|
2012
2039
|
visitStarted(visit) {
|
2013
2040
|
this.delegate.visitStarted(visit);
|
2014
2041
|
}
|
@@ -2016,7 +2043,10 @@ class Navigator {
|
|
2016
2043
|
this.delegate.visitCompleted(visit);
|
2017
2044
|
}
|
2018
2045
|
locationWithActionIsSamePage(location, action) {
|
2019
|
-
|
2046
|
+
const anchor = getAnchor(location);
|
2047
|
+
const currentAnchor = getAnchor(this.view.lastRenderedLocation);
|
2048
|
+
const isRestorationToTop = action === "restore" && typeof anchor === "undefined";
|
2049
|
+
return action !== "replace" && getRequestURL(location) === getRequestURL(this.view.lastRenderedLocation) && (isRestorationToTop || anchor != null && anchor !== currentAnchor);
|
2020
2050
|
}
|
2021
2051
|
visitScrolledToSamePageLocation(oldURL, newURL) {
|
2022
2052
|
this.delegate.visitScrolledToSamePageLocation(oldURL, newURL);
|
@@ -2408,6 +2438,7 @@ class Session {
|
|
2408
2438
|
this.scrollObserver = new ScrollObserver(this);
|
2409
2439
|
this.streamObserver = new StreamObserver(this);
|
2410
2440
|
this.frameRedirector = new FrameRedirector(document.documentElement);
|
2441
|
+
this.drive = true;
|
2411
2442
|
this.enabled = true;
|
2412
2443
|
this.progressBarDelay = 500;
|
2413
2444
|
this.started = false;
|
@@ -2485,7 +2516,7 @@ class Session {
|
|
2485
2516
|
});
|
2486
2517
|
}
|
2487
2518
|
willFollowLinkToLocation(link, location) {
|
2488
|
-
return
|
2519
|
+
return this.elementDriveEnabled(link) && this.locationIsVisitable(location) && this.applicationAllowsFollowingLinkToLocation(link, location);
|
2489
2520
|
}
|
2490
2521
|
followedLinkToLocation(link, location) {
|
2491
2522
|
const action = this.getActionForLink(link);
|
@@ -2494,13 +2525,12 @@ class Session {
|
|
2494
2525
|
});
|
2495
2526
|
}
|
2496
2527
|
convertLinkWithMethodClickToFormSubmission(link) {
|
2497
|
-
var _a;
|
2498
2528
|
const linkMethod = link.getAttribute("data-turbo-method");
|
2499
2529
|
if (linkMethod) {
|
2500
2530
|
const form = document.createElement("form");
|
2501
2531
|
form.method = linkMethod;
|
2502
2532
|
form.action = link.getAttribute("href") || "undefined";
|
2503
|
-
|
2533
|
+
document.body.appendChild(form);
|
2504
2534
|
return dispatch("submit", {
|
2505
2535
|
cancelable: true,
|
2506
2536
|
target: form
|
@@ -2509,8 +2539,8 @@ class Session {
|
|
2509
2539
|
return false;
|
2510
2540
|
}
|
2511
2541
|
}
|
2512
|
-
|
2513
|
-
return this.applicationAllowsVisitingLocation(location);
|
2542
|
+
allowsVisitingLocationWithAction(location, action) {
|
2543
|
+
return this.locationWithActionIsSamePage(location, action) || this.applicationAllowsVisitingLocation(location);
|
2514
2544
|
}
|
2515
2545
|
visitProposedToLocation(location, options) {
|
2516
2546
|
extendURLWithDeprecatedProperties(location);
|
@@ -2518,7 +2548,9 @@ class Session {
|
|
2518
2548
|
}
|
2519
2549
|
visitStarted(visit) {
|
2520
2550
|
extendURLWithDeprecatedProperties(visit.location);
|
2521
|
-
|
2551
|
+
if (!visit.silent) {
|
2552
|
+
this.notifyApplicationAfterVisitingLocation(visit.location, visit.action);
|
2553
|
+
}
|
2522
2554
|
}
|
2523
2555
|
visitCompleted(visit) {
|
2524
2556
|
this.notifyApplicationAfterPageLoad(visit.getTimingMetrics());
|
@@ -2530,7 +2562,7 @@ class Session {
|
|
2530
2562
|
this.notifyApplicationAfterVisitingSamePageLocation(oldURL, newURL);
|
2531
2563
|
}
|
2532
2564
|
willSubmitForm(form, submitter) {
|
2533
|
-
return
|
2565
|
+
return this.elementDriveEnabled(form) && this.elementDriveEnabled(submitter);
|
2534
2566
|
}
|
2535
2567
|
formSubmitted(form, submitter) {
|
2536
2568
|
this.navigator.submitForm(form, submitter);
|
@@ -2549,7 +2581,10 @@ class Session {
|
|
2549
2581
|
this.renderStreamMessage(message);
|
2550
2582
|
}
|
2551
2583
|
viewWillCacheSnapshot() {
|
2552
|
-
|
2584
|
+
var _a;
|
2585
|
+
if (!((_a = this.navigator.currentVisit) === null || _a === void 0 ? void 0 : _a.silent)) {
|
2586
|
+
this.notifyApplicationBeforeCachingSnapshot();
|
2587
|
+
}
|
2553
2588
|
}
|
2554
2589
|
allowsImmediateRender({element: element}, resume) {
|
2555
2590
|
const event = this.notifyApplicationBeforeRender(element, resume);
|
@@ -2562,6 +2597,12 @@ class Session {
|
|
2562
2597
|
viewInvalidated() {
|
2563
2598
|
this.adapter.pageInvalidated();
|
2564
2599
|
}
|
2600
|
+
frameLoaded(frame) {
|
2601
|
+
this.notifyApplicationAfterFrameLoad(frame);
|
2602
|
+
}
|
2603
|
+
frameRendered(fetchResponse, frame) {
|
2604
|
+
this.notifyApplicationAfterFrameRender(fetchResponse, frame);
|
2605
|
+
}
|
2565
2606
|
applicationAllowsFollowingLinkToLocation(link, location) {
|
2566
2607
|
const event = this.notifyApplicationAfterClickingLinkToLocation(link, location);
|
2567
2608
|
return !event.defaultPrevented;
|
@@ -2624,6 +2665,36 @@ class Session {
|
|
2624
2665
|
newURL: newURL.toString()
|
2625
2666
|
}));
|
2626
2667
|
}
|
2668
|
+
notifyApplicationAfterFrameLoad(frame) {
|
2669
|
+
return dispatch("turbo:frame-load", {
|
2670
|
+
target: frame
|
2671
|
+
});
|
2672
|
+
}
|
2673
|
+
notifyApplicationAfterFrameRender(fetchResponse, frame) {
|
2674
|
+
return dispatch("turbo:frame-render", {
|
2675
|
+
detail: {
|
2676
|
+
fetchResponse: fetchResponse
|
2677
|
+
},
|
2678
|
+
target: frame,
|
2679
|
+
cancelable: true
|
2680
|
+
});
|
2681
|
+
}
|
2682
|
+
elementDriveEnabled(element) {
|
2683
|
+
const container = element === null || element === void 0 ? void 0 : element.closest("[data-turbo]");
|
2684
|
+
if (this.drive) {
|
2685
|
+
if (container) {
|
2686
|
+
return container.getAttribute("data-turbo") != "false";
|
2687
|
+
} else {
|
2688
|
+
return true;
|
2689
|
+
}
|
2690
|
+
} else {
|
2691
|
+
if (container) {
|
2692
|
+
return container.getAttribute("data-turbo") == "true";
|
2693
|
+
} else {
|
2694
|
+
return false;
|
2695
|
+
}
|
2696
|
+
}
|
2697
|
+
}
|
2627
2698
|
getActionForLink(link) {
|
2628
2699
|
const action = link.getAttribute("data-turbo-action");
|
2629
2700
|
return isAction(action) ? action : "advance";
|
@@ -2636,15 +2707,6 @@ class Session {
|
|
2636
2707
|
}
|
2637
2708
|
}
|
2638
2709
|
|
2639
|
-
function elementIsNavigable(element) {
|
2640
|
-
const container = element === null || element === void 0 ? void 0 : element.closest("[data-turbo]");
|
2641
|
-
if (container) {
|
2642
|
-
return container.getAttribute("data-turbo") != "false";
|
2643
|
-
} else {
|
2644
|
-
return true;
|
2645
|
-
}
|
2646
|
-
}
|
2647
|
-
|
2648
2710
|
function extendURLWithDeprecatedProperties(url) {
|
2649
2711
|
Object.defineProperties(url, deprecatedLocationPropertyDescriptors);
|
2650
2712
|
}
|
@@ -2657,6 +2719,58 @@ const deprecatedLocationPropertyDescriptors = {
|
|
2657
2719
|
}
|
2658
2720
|
};
|
2659
2721
|
|
2722
|
+
const session = new Session;
|
2723
|
+
|
2724
|
+
const {navigator: navigator} = session;
|
2725
|
+
|
2726
|
+
function start() {
|
2727
|
+
session.start();
|
2728
|
+
}
|
2729
|
+
|
2730
|
+
function registerAdapter(adapter) {
|
2731
|
+
session.registerAdapter(adapter);
|
2732
|
+
}
|
2733
|
+
|
2734
|
+
function visit(location, options) {
|
2735
|
+
session.visit(location, options);
|
2736
|
+
}
|
2737
|
+
|
2738
|
+
function connectStreamSource(source) {
|
2739
|
+
session.connectStreamSource(source);
|
2740
|
+
}
|
2741
|
+
|
2742
|
+
function disconnectStreamSource(source) {
|
2743
|
+
session.disconnectStreamSource(source);
|
2744
|
+
}
|
2745
|
+
|
2746
|
+
function renderStreamMessage(message) {
|
2747
|
+
session.renderStreamMessage(message);
|
2748
|
+
}
|
2749
|
+
|
2750
|
+
function clearCache() {
|
2751
|
+
session.clearCache();
|
2752
|
+
}
|
2753
|
+
|
2754
|
+
function setProgressBarDelay(delay) {
|
2755
|
+
session.setProgressBarDelay(delay);
|
2756
|
+
}
|
2757
|
+
|
2758
|
+
var Turbo = Object.freeze({
|
2759
|
+
__proto__: null,
|
2760
|
+
navigator: navigator,
|
2761
|
+
session: session,
|
2762
|
+
PageRenderer: PageRenderer,
|
2763
|
+
PageSnapshot: PageSnapshot,
|
2764
|
+
start: start,
|
2765
|
+
registerAdapter: registerAdapter,
|
2766
|
+
visit: visit,
|
2767
|
+
connectStreamSource: connectStreamSource,
|
2768
|
+
disconnectStreamSource: disconnectStreamSource,
|
2769
|
+
renderStreamMessage: renderStreamMessage,
|
2770
|
+
clearCache: clearCache,
|
2771
|
+
setProgressBarDelay: setProgressBarDelay
|
2772
|
+
});
|
2773
|
+
|
2660
2774
|
class FrameController {
|
2661
2775
|
constructor(element) {
|
2662
2776
|
this.resolveVisitPromise = () => {};
|
@@ -2672,6 +2786,7 @@ class FrameController {
|
|
2672
2786
|
connect() {
|
2673
2787
|
if (!this.connected) {
|
2674
2788
|
this.connected = true;
|
2789
|
+
this.reloadable = false;
|
2675
2790
|
if (this.loadingStyle == FrameLoadingStyle.lazy) {
|
2676
2791
|
this.appearanceObserver.start();
|
2677
2792
|
}
|
@@ -2707,7 +2822,7 @@ class FrameController {
|
|
2707
2822
|
}
|
2708
2823
|
}
|
2709
2824
|
async loadSourceURL() {
|
2710
|
-
if (!this.settingSourceURL && this.enabled && this.isActive && this.sourceURL != this.currentURL) {
|
2825
|
+
if (!this.settingSourceURL && this.enabled && this.isActive && (this.reloadable || this.sourceURL != this.currentURL)) {
|
2711
2826
|
const previousURL = this.currentURL;
|
2712
2827
|
this.currentURL = this.sourceURL;
|
2713
2828
|
if (this.sourceURL) {
|
@@ -2716,6 +2831,7 @@ class FrameController {
|
|
2716
2831
|
this.appearanceObserver.stop();
|
2717
2832
|
await this.element.loaded;
|
2718
2833
|
this.hasBeenLoaded = true;
|
2834
|
+
session.frameLoaded(this.element);
|
2719
2835
|
} catch (error) {
|
2720
2836
|
this.currentURL = previousURL;
|
2721
2837
|
throw error;
|
@@ -2735,6 +2851,7 @@ class FrameController {
|
|
2735
2851
|
const renderer = new FrameRenderer(this.view.snapshot, snapshot, false);
|
2736
2852
|
if (this.view.renderPromise) await this.view.renderPromise;
|
2737
2853
|
await this.view.render(renderer);
|
2854
|
+
session.frameRendered(fetchResponse, this.element);
|
2738
2855
|
}
|
2739
2856
|
} catch (error) {
|
2740
2857
|
console.error(error);
|
@@ -2752,6 +2869,7 @@ class FrameController {
|
|
2752
2869
|
}
|
2753
2870
|
}
|
2754
2871
|
linkClickIntercepted(element, url) {
|
2872
|
+
this.reloadable = true;
|
2755
2873
|
this.navigateFrame(element, url);
|
2756
2874
|
}
|
2757
2875
|
shouldInterceptFormSubmission(element, submitter) {
|
@@ -2761,6 +2879,7 @@ class FrameController {
|
|
2761
2879
|
if (this.formSubmission) {
|
2762
2880
|
this.formSubmission.stop();
|
2763
2881
|
}
|
2882
|
+
this.reloadable = false;
|
2764
2883
|
this.formSubmission = new FormSubmission(this, element, submitter);
|
2765
2884
|
if (this.formSubmission.fetchRequest.isIdempotent) {
|
2766
2885
|
this.navigateFrame(element, this.formSubmission.fetchRequest.url.href);
|
@@ -2864,10 +2983,10 @@ class FrameController {
|
|
2864
2983
|
return !frameElement.disabled;
|
2865
2984
|
}
|
2866
2985
|
}
|
2867
|
-
if (!
|
2986
|
+
if (!session.elementDriveEnabled(element)) {
|
2868
2987
|
return false;
|
2869
2988
|
}
|
2870
|
-
if (submitter && !
|
2989
|
+
if (submitter && !session.elementDriveEnabled(submitter)) {
|
2871
2990
|
return false;
|
2872
2991
|
}
|
2873
2992
|
return true;
|
@@ -2883,6 +3002,18 @@ class FrameController {
|
|
2883
3002
|
return this.element.src;
|
2884
3003
|
}
|
2885
3004
|
}
|
3005
|
+
get reloadable() {
|
3006
|
+
const frame = this.findFrameElement(this.element);
|
3007
|
+
return frame.hasAttribute("reloadable");
|
3008
|
+
}
|
3009
|
+
set reloadable(value) {
|
3010
|
+
const frame = this.findFrameElement(this.element);
|
3011
|
+
if (value) {
|
3012
|
+
frame.setAttribute("reloadable", "");
|
3013
|
+
} else {
|
3014
|
+
frame.removeAttribute("reloadable");
|
3015
|
+
}
|
3016
|
+
}
|
2886
3017
|
set sourceURL(sourceURL) {
|
2887
3018
|
this.settingSourceURL = true;
|
2888
3019
|
this.element.src = sourceURL !== null && sourceURL !== void 0 ? sourceURL : null;
|
@@ -3089,57 +3220,6 @@ customElements.define("turbo-stream", StreamElement);
|
|
3089
3220
|
}
|
3090
3221
|
})();
|
3091
3222
|
|
3092
|
-
const session = new Session;
|
3093
|
-
|
3094
|
-
const {navigator: navigator} = session;
|
3095
|
-
|
3096
|
-
function start() {
|
3097
|
-
session.start();
|
3098
|
-
}
|
3099
|
-
|
3100
|
-
function registerAdapter(adapter) {
|
3101
|
-
session.registerAdapter(adapter);
|
3102
|
-
}
|
3103
|
-
|
3104
|
-
function visit(location, options) {
|
3105
|
-
session.visit(location, options);
|
3106
|
-
}
|
3107
|
-
|
3108
|
-
function connectStreamSource(source) {
|
3109
|
-
session.connectStreamSource(source);
|
3110
|
-
}
|
3111
|
-
|
3112
|
-
function disconnectStreamSource(source) {
|
3113
|
-
session.disconnectStreamSource(source);
|
3114
|
-
}
|
3115
|
-
|
3116
|
-
function renderStreamMessage(message) {
|
3117
|
-
session.renderStreamMessage(message);
|
3118
|
-
}
|
3119
|
-
|
3120
|
-
function clearCache() {
|
3121
|
-
session.clearCache();
|
3122
|
-
}
|
3123
|
-
|
3124
|
-
function setProgressBarDelay(delay) {
|
3125
|
-
session.setProgressBarDelay(delay);
|
3126
|
-
}
|
3127
|
-
|
3128
|
-
var Turbo = Object.freeze({
|
3129
|
-
__proto__: null,
|
3130
|
-
navigator: navigator,
|
3131
|
-
PageRenderer: PageRenderer,
|
3132
|
-
PageSnapshot: PageSnapshot,
|
3133
|
-
start: start,
|
3134
|
-
registerAdapter: registerAdapter,
|
3135
|
-
visit: visit,
|
3136
|
-
connectStreamSource: connectStreamSource,
|
3137
|
-
disconnectStreamSource: disconnectStreamSource,
|
3138
|
-
renderStreamMessage: renderStreamMessage,
|
3139
|
-
clearCache: clearCache,
|
3140
|
-
setProgressBarDelay: setProgressBarDelay
|
3141
|
-
});
|
3142
|
-
|
3143
3223
|
window.Turbo = Turbo;
|
3144
3224
|
|
3145
3225
|
start();
|
@@ -3154,6 +3234,7 @@ var turbo_es2017Esm = Object.freeze({
|
|
3154
3234
|
navigator: navigator,
|
3155
3235
|
registerAdapter: registerAdapter,
|
3156
3236
|
renderStreamMessage: renderStreamMessage,
|
3237
|
+
session: session,
|
3157
3238
|
setProgressBarDelay: setProgressBarDelay,
|
3158
3239
|
start: start,
|
3159
3240
|
visit: visit
|
@@ -12,10 +12,9 @@ module Turbo::Streams::ActionHelper
|
|
12
12
|
def turbo_stream_action_tag(action, target: nil, targets: nil, template: nil)
|
13
13
|
template = action.to_sym == :remove ? "" : "<template>#{template}</template>"
|
14
14
|
|
15
|
-
if target
|
16
|
-
target = convert_to_turbo_stream_dom_id(target)
|
15
|
+
if target = convert_to_turbo_stream_dom_id(target)
|
17
16
|
%(<turbo-stream action="#{action}" target="#{target}">#{template}</turbo-stream>).html_safe
|
18
|
-
elsif targets
|
17
|
+
elsif targets = convert_to_turbo_stream_dom_id(targets)
|
19
18
|
%(<turbo-stream action="#{action}" targets="#{targets}">#{template}</turbo-stream>).html_safe
|
20
19
|
else
|
21
20
|
raise ArgumentError, "target or targets must be supplied"
|
@@ -212,29 +212,19 @@ class Turbo::Streams::TagBuilder
|
|
212
212
|
|
213
213
|
# Send an action of the type <tt>name</tt> to <tt>target</tt>. Options described in the concrete methods.
|
214
214
|
def action(name, target, content = nil, allow_inferred_rendering: true, **rendering, &block)
|
215
|
-
target_name = extract_target_name_from(target)
|
216
215
|
template = render_template(target, content, allow_inferred_rendering: allow_inferred_rendering, **rendering, &block)
|
217
216
|
|
218
|
-
turbo_stream_action_tag name, target:
|
217
|
+
turbo_stream_action_tag name, target: target, template: template
|
219
218
|
end
|
220
219
|
|
221
220
|
# Send an action of the type <tt>name</tt> to <tt>targets</tt>. Options described in the concrete methods.
|
222
221
|
def action_all(name, targets, content = nil, allow_inferred_rendering: true, **rendering, &block)
|
223
|
-
targets_name = extract_target_name_from(targets)
|
224
222
|
template = render_template(targets, content, allow_inferred_rendering: allow_inferred_rendering, **rendering, &block)
|
225
223
|
|
226
|
-
turbo_stream_action_tag name, targets:
|
224
|
+
turbo_stream_action_tag name, targets: targets, template: template
|
227
225
|
end
|
228
226
|
|
229
227
|
private
|
230
|
-
def extract_target_name_from(target)
|
231
|
-
if target.respond_to?(:to_key)
|
232
|
-
ActionView::RecordIdentifier.dom_id(target)
|
233
|
-
else
|
234
|
-
target
|
235
|
-
end
|
236
|
-
end
|
237
|
-
|
238
228
|
def render_template(target, content = nil, allow_inferred_rendering: true, **rendering, &block)
|
239
229
|
case
|
240
230
|
when content
|
@@ -0,0 +1,9 @@
|
|
1
|
+
if (cable_config_path = Rails.root.join("config/cable.yml")).exist?
|
2
|
+
say "Enable redis in bundle"
|
3
|
+
uncomment_lines "Gemfile", %(gem 'redis')
|
4
|
+
|
5
|
+
say "Switch development cable to use redis"
|
6
|
+
gsub_file cable_config_path.to_s, /development:\n\s+adapter: async/, "development:\n adapter: redis\n url: redis://localhost:6379/1"
|
7
|
+
else
|
8
|
+
say 'ActionCable config file (config/cable.yml) is missing. Uncomment "gem \'redis\'" in your Gemfile and create config/cable.yml to use the Turbo Streams broadcast feature.'
|
9
|
+
end
|
@@ -1,15 +1,23 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
uncomment_lines "Gemfile", %(gem 'redis')
|
1
|
+
if (app_js_path = Rails.root.join("app/javascript/application.js")).exist?
|
2
|
+
say "Import turbo-rails in existing app/javascript/application.js"
|
3
|
+
append_to_file app_js_path, %(import "@hotwired/turbo-rails"\n)
|
4
|
+
else
|
5
|
+
say <<~INSTRUCTIONS, :red
|
6
|
+
You must import @hotwired/turbo-rails in your application.js.
|
7
|
+
INSTRUCTIONS
|
8
|
+
end
|
10
9
|
|
11
|
-
|
12
|
-
|
10
|
+
if (importmap_path = Rails.root.join("config/importmap.rb")).exist?
|
11
|
+
say "Pin @hotwired/turbo-rails in config/importmap.rb"
|
12
|
+
insert_into_file \
|
13
|
+
importmap_path.to_s,
|
14
|
+
%( pin "@hotwired/turbo-rails", to: "turbo.js"\n\n),
|
15
|
+
after: "Rails.application.config.importmap.draw do\n"
|
13
16
|
else
|
14
|
-
say
|
17
|
+
say <<~INSTRUCTIONS, :red
|
18
|
+
You must add @hotwired/turbo-rails to your importmap to reference them via ESM.
|
19
|
+
Example: pin "@hotwired/turbo-rails", to: "turbo.js"
|
20
|
+
INSTRUCTIONS
|
15
21
|
end
|
22
|
+
|
23
|
+
say "Run turbo:install:redis to switch on Redis and use it in development for turbo streams"
|
@@ -1,27 +1,7 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
ACTIVE_STORAGE_REGEX = /(import.*ActiveStorage|require.*@rails\/activestorage.*)/.freeze
|
4
|
-
CABLE_CONFIG_PATH = Rails.root.join("config/cable.yml")
|
5
|
-
|
6
|
-
abort "❌ Webpacker not found. Exiting." unless defined?(Webpacker::Engine)
|
1
|
+
say "Appending Turbo setup code to #{Webpacker.config.source_entry_path}/application.js"
|
2
|
+
append_to_file "#{Webpacker.config.source_entry_path}/application.js", %(\nimport "@hotwired/turbo-rails"\n)
|
7
3
|
|
8
4
|
say "Install Turbo"
|
9
5
|
run "yarn add @hotwired/turbo-rails"
|
10
|
-
insert_into_file "#{Webpacker.config.source_entry_path}/application.js", "import \"@hotwired/turbo-rails\"\n", before: ACTIVE_STORAGE_REGEX
|
11
|
-
|
12
|
-
say "Remove Turbolinks"
|
13
|
-
run "#{RbConfig.ruby} bin/bundle remove turbolinks"
|
14
|
-
run "#{RbConfig.ruby} bin/bundle", capture: true
|
15
|
-
run "#{RbConfig.ruby} bin/yarn remove turbolinks"
|
16
|
-
gsub_file "#{Webpacker.config.source_entry_path}/application.js", TURBOLINKS_REGEX, ''
|
17
|
-
gsub_file "#{Webpacker.config.source_entry_path}/application.js", /Turbolinks.start.*\n/, ''
|
18
|
-
|
19
|
-
if CABLE_CONFIG_PATH.exist?
|
20
|
-
say "Enable redis in bundle"
|
21
|
-
uncomment_lines "Gemfile", %(gem 'redis')
|
22
6
|
|
23
|
-
|
24
|
-
gsub_file CABLE_CONFIG_PATH.to_s, /development:\n\s+adapter: async/, "development:\n adapter: redis\n url: redis://localhost:6379/1"
|
25
|
-
else
|
26
|
-
say 'ActionCable config file (config/cable.yml) is missing. Uncomment "gem \'redis\'" in your Gemfile and create config/cable.yml to use the Turbo Streams broadcast feature.'
|
27
|
-
end
|
7
|
+
say "Run turbo:install:redis to switch on Redis and use it in development for turbo streams"
|
data/lib/tasks/turbo_tasks.rake
CHANGED
data/lib/turbo/engine.rb
CHANGED
@@ -22,15 +22,7 @@ module Turbo
|
|
22
22
|
|
23
23
|
initializer "turbo.assets" do
|
24
24
|
if Rails.application.config.respond_to?(:assets)
|
25
|
-
Rails.application.config.assets.precompile += %w( turbo )
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
initializer "turbo.importmap" do
|
30
|
-
if Rails.application.config.respond_to?(:importmap)
|
31
|
-
Rails.application.config.importmap.paths.tap do |paths|
|
32
|
-
paths.asset "@hotwired/turbo-rails", path: "turbo"
|
33
|
-
end
|
25
|
+
Rails.application.config.assets.precompile += %w( turbo.js )
|
34
26
|
end
|
35
27
|
end
|
36
28
|
|
data/lib/turbo/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: turbo-rails
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.7.
|
4
|
+
version: 0.7.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sam Stephenson
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2021-08-
|
13
|
+
date: 2021-08-25 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: rails
|
@@ -56,6 +56,7 @@ files:
|
|
56
56
|
- app/models/concerns/turbo/broadcastable.rb
|
57
57
|
- app/models/turbo/streams/tag_builder.rb
|
58
58
|
- config/routes.rb
|
59
|
+
- lib/install/turbo_needs_redis.rb
|
59
60
|
- lib/install/turbo_with_asset_pipeline.rb
|
60
61
|
- lib/install/turbo_with_webpacker.rb
|
61
62
|
- lib/tasks/turbo_tasks.rake
|