cable_ready 5.0.1 → 5.0.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.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -0
- data/app/assets/javascripts/cable_ready.js +85 -2
- data/app/assets/javascripts/cable_ready.umd.js +84 -2
- data/app/helpers/cable_ready/view_helper.rb +2 -1
- data/lib/cable_ready/channels.rb +2 -0
- data/lib/cable_ready/config.rb +2 -0
- data/lib/cable_ready/version.rb +1 -1
- data/yarn.lock +15 -10
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 740e012630c79e657256a99d309e96704f2dfc7080d056edcd68c23ab18aebb2
|
4
|
+
data.tar.gz: 356b0038be58993b208f1128465d46a0ed4bf0cf858038e2a44b86275b8447f7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: eea5efc0262818516332c01f9e6ef98fc935c0f56e0f0a73f7ccf43c88f8774d7ccc9fd6fef4acf2f83ca77a446735a836f55a23d1391fbcc5c8811b667c5d3c
|
7
|
+
data.tar.gz: 0b199e3a2950c6103e2099a194973db49ac89f53a8698693ef12767665f20497bec903336a9f197f93b006120e35e14645ad37c18b9b2ecedb87d2eee8aae7d8
|
data/Gemfile.lock
CHANGED
@@ -1061,6 +1061,56 @@ var Log = {
|
|
1061
1061
|
morphEnd: morphEnd
|
1062
1062
|
};
|
1063
1063
|
|
1064
|
+
class AppearanceObserver {
|
1065
|
+
constructor(delegate, element = null) {
|
1066
|
+
this.delegate = delegate;
|
1067
|
+
this.element = element || delegate;
|
1068
|
+
this.started = false;
|
1069
|
+
this.intersecting = false;
|
1070
|
+
this.intersectionObserver = new IntersectionObserver(this.intersect);
|
1071
|
+
}
|
1072
|
+
start() {
|
1073
|
+
if (!this.started) {
|
1074
|
+
this.started = true;
|
1075
|
+
this.intersectionObserver.observe(this.element);
|
1076
|
+
this.observeVisibility();
|
1077
|
+
}
|
1078
|
+
}
|
1079
|
+
stop() {
|
1080
|
+
if (this.started) {
|
1081
|
+
this.started = false;
|
1082
|
+
this.intersectionObserver.unobserve(this.element);
|
1083
|
+
this.unobserveVisibility();
|
1084
|
+
}
|
1085
|
+
}
|
1086
|
+
observeVisibility=() => {
|
1087
|
+
document.addEventListener("visibilitychange", this.handleVisibilityChange);
|
1088
|
+
};
|
1089
|
+
unobserveVisibility=() => {
|
1090
|
+
document.removeEventListener("visibilitychange", this.handleVisibilityChange);
|
1091
|
+
};
|
1092
|
+
intersect=entries => {
|
1093
|
+
entries.forEach((entry => {
|
1094
|
+
if (entry.target === this.element) {
|
1095
|
+
if (entry.isIntersecting && document.visibilityState === "visible") {
|
1096
|
+
this.intersecting = true;
|
1097
|
+
this.delegate.appearedInViewport();
|
1098
|
+
} else {
|
1099
|
+
this.intersecting = false;
|
1100
|
+
this.delegate.disappearedFromViewport();
|
1101
|
+
}
|
1102
|
+
}
|
1103
|
+
}));
|
1104
|
+
};
|
1105
|
+
handleVisibilityChange=event => {
|
1106
|
+
if (document.visibilityState === "visible" && this.intersecting) {
|
1107
|
+
this.delegate.appearedInViewport();
|
1108
|
+
} else {
|
1109
|
+
this.delegate.disappearedFromViewport();
|
1110
|
+
}
|
1111
|
+
};
|
1112
|
+
}
|
1113
|
+
|
1064
1114
|
const template = `\n<style>\n :host {\n display: block;\n }\n</style>\n<slot></slot>\n`;
|
1065
1115
|
|
1066
1116
|
class UpdatesForElement extends SubscribingElement {
|
@@ -1075,6 +1125,9 @@ class UpdatesForElement extends SubscribingElement {
|
|
1075
1125
|
shadowRoot.innerHTML = template;
|
1076
1126
|
this.triggerElementLog = new BoundedQueue(10);
|
1077
1127
|
this.targetElementLog = new BoundedQueue(10);
|
1128
|
+
this.appearanceObserver = new AppearanceObserver(this);
|
1129
|
+
this.visible = false;
|
1130
|
+
this.didTransitionToVisible = false;
|
1078
1131
|
}
|
1079
1132
|
async connectedCallback() {
|
1080
1133
|
if (this.preview) return;
|
@@ -1085,6 +1138,14 @@ class UpdatesForElement extends SubscribingElement {
|
|
1085
1138
|
} else {
|
1086
1139
|
console.error("The `cable_ready_updates_for` helper cannot connect. You must initialize CableReady with an Action Cable consumer.");
|
1087
1140
|
}
|
1141
|
+
if (this.observeAppearance) {
|
1142
|
+
this.appearanceObserver.start();
|
1143
|
+
}
|
1144
|
+
}
|
1145
|
+
disconnectedCallback() {
|
1146
|
+
if (this.observeAppearance) {
|
1147
|
+
this.appearanceObserver.stop();
|
1148
|
+
}
|
1088
1149
|
}
|
1089
1150
|
async update(data) {
|
1090
1151
|
this.lastUpdateTimestamp = new Date;
|
@@ -1095,7 +1156,8 @@ class UpdatesForElement extends SubscribingElement {
|
|
1095
1156
|
return;
|
1096
1157
|
}
|
1097
1158
|
// first <cable-ready-updates-for> element in the DOM *at any given moment* updates all of the others
|
1098
|
-
|
1159
|
+
// if the element becomes visible though, we have to overrule and load it
|
1160
|
+
if (blocks[0].element !== this && !this.didTransitionToVisible) {
|
1099
1161
|
this.triggerElementLog.push(`${(new Date).toLocaleString()}: ${Log.cancel(this.lastUpdateTimestamp, "Update already requested")}`);
|
1100
1162
|
return;
|
1101
1163
|
}
|
@@ -1121,6 +1183,17 @@ class UpdatesForElement extends SubscribingElement {
|
|
1121
1183
|
block.process(data, this.html, this.index, this.lastUpdateTimestamp);
|
1122
1184
|
}));
|
1123
1185
|
}
|
1186
|
+
appearedInViewport() {
|
1187
|
+
if (!this.visible) {
|
1188
|
+
// transition from invisible to visible forces update
|
1189
|
+
this.didTransitionToVisible = true;
|
1190
|
+
this.update({});
|
1191
|
+
}
|
1192
|
+
this.visible = true;
|
1193
|
+
}
|
1194
|
+
disappearedFromViewport() {
|
1195
|
+
this.visible = false;
|
1196
|
+
}
|
1124
1197
|
get query() {
|
1125
1198
|
return `${this.tagName}[identifier="${this.identifier}"]`;
|
1126
1199
|
}
|
@@ -1130,6 +1203,9 @@ class UpdatesForElement extends SubscribingElement {
|
|
1130
1203
|
get debounce() {
|
1131
1204
|
return this.hasAttribute("debounce") ? parseInt(this.getAttribute("debounce")) : 20;
|
1132
1205
|
}
|
1206
|
+
get observeAppearance() {
|
1207
|
+
return this.hasAttribute("observe-appearance");
|
1208
|
+
}
|
1133
1209
|
}
|
1134
1210
|
|
1135
1211
|
class Block {
|
@@ -1159,6 +1235,7 @@ class Block {
|
|
1159
1235
|
onBeforeElUpdated: shouldMorph(operation),
|
1160
1236
|
onElUpdated: _ => {
|
1161
1237
|
this.element.removeAttribute("updating");
|
1238
|
+
this.element.didTransitionToVisible = false;
|
1162
1239
|
dispatch(this.element, "cable-ready:after-update", operation);
|
1163
1240
|
assignFocus(operation.focusSelector);
|
1164
1241
|
}
|
@@ -1185,7 +1262,7 @@ class Block {
|
|
1185
1262
|
}
|
1186
1263
|
shouldUpdate(data) {
|
1187
1264
|
// if everything that could prevent an update is false, update this block
|
1188
|
-
return !this.ignoresInnerUpdates && this.hasChangesSelectedForUpdate(data);
|
1265
|
+
return !this.ignoresInnerUpdates && this.hasChangesSelectedForUpdate(data) && (!this.observeAppearance || this.visible);
|
1189
1266
|
}
|
1190
1267
|
hasChangesSelectedForUpdate(data) {
|
1191
1268
|
// if there's an only attribute, only update if at least one of the attributes changed is in the allow list
|
@@ -1205,6 +1282,12 @@ class Block {
|
|
1205
1282
|
get query() {
|
1206
1283
|
return this.element.query;
|
1207
1284
|
}
|
1285
|
+
get visible() {
|
1286
|
+
return this.element.visible;
|
1287
|
+
}
|
1288
|
+
get observeAppearance() {
|
1289
|
+
return this.element.observeAppearance;
|
1290
|
+
}
|
1208
1291
|
}
|
1209
1292
|
|
1210
1293
|
const registerInnerUpdates = () => {
|
@@ -982,6 +982,55 @@
|
|
982
982
|
morphStart: morphStart,
|
983
983
|
morphEnd: morphEnd
|
984
984
|
};
|
985
|
+
class AppearanceObserver {
|
986
|
+
constructor(delegate, element = null) {
|
987
|
+
this.delegate = delegate;
|
988
|
+
this.element = element || delegate;
|
989
|
+
this.started = false;
|
990
|
+
this.intersecting = false;
|
991
|
+
this.intersectionObserver = new IntersectionObserver(this.intersect);
|
992
|
+
}
|
993
|
+
start() {
|
994
|
+
if (!this.started) {
|
995
|
+
this.started = true;
|
996
|
+
this.intersectionObserver.observe(this.element);
|
997
|
+
this.observeVisibility();
|
998
|
+
}
|
999
|
+
}
|
1000
|
+
stop() {
|
1001
|
+
if (this.started) {
|
1002
|
+
this.started = false;
|
1003
|
+
this.intersectionObserver.unobserve(this.element);
|
1004
|
+
this.unobserveVisibility();
|
1005
|
+
}
|
1006
|
+
}
|
1007
|
+
observeVisibility=() => {
|
1008
|
+
document.addEventListener("visibilitychange", this.handleVisibilityChange);
|
1009
|
+
};
|
1010
|
+
unobserveVisibility=() => {
|
1011
|
+
document.removeEventListener("visibilitychange", this.handleVisibilityChange);
|
1012
|
+
};
|
1013
|
+
intersect=entries => {
|
1014
|
+
entries.forEach((entry => {
|
1015
|
+
if (entry.target === this.element) {
|
1016
|
+
if (entry.isIntersecting && document.visibilityState === "visible") {
|
1017
|
+
this.intersecting = true;
|
1018
|
+
this.delegate.appearedInViewport();
|
1019
|
+
} else {
|
1020
|
+
this.intersecting = false;
|
1021
|
+
this.delegate.disappearedFromViewport();
|
1022
|
+
}
|
1023
|
+
}
|
1024
|
+
}));
|
1025
|
+
};
|
1026
|
+
handleVisibilityChange=event => {
|
1027
|
+
if (document.visibilityState === "visible" && this.intersecting) {
|
1028
|
+
this.delegate.appearedInViewport();
|
1029
|
+
} else {
|
1030
|
+
this.delegate.disappearedFromViewport();
|
1031
|
+
}
|
1032
|
+
};
|
1033
|
+
}
|
985
1034
|
const template = `\n<style>\n :host {\n display: block;\n }\n</style>\n<slot></slot>\n`;
|
986
1035
|
class UpdatesForElement extends SubscribingElement {
|
987
1036
|
static get tagName() {
|
@@ -995,6 +1044,9 @@
|
|
995
1044
|
shadowRoot.innerHTML = template;
|
996
1045
|
this.triggerElementLog = new BoundedQueue(10);
|
997
1046
|
this.targetElementLog = new BoundedQueue(10);
|
1047
|
+
this.appearanceObserver = new AppearanceObserver(this);
|
1048
|
+
this.visible = false;
|
1049
|
+
this.didTransitionToVisible = false;
|
998
1050
|
}
|
999
1051
|
async connectedCallback() {
|
1000
1052
|
if (this.preview) return;
|
@@ -1005,6 +1057,14 @@
|
|
1005
1057
|
} else {
|
1006
1058
|
console.error("The `cable_ready_updates_for` helper cannot connect. You must initialize CableReady with an Action Cable consumer.");
|
1007
1059
|
}
|
1060
|
+
if (this.observeAppearance) {
|
1061
|
+
this.appearanceObserver.start();
|
1062
|
+
}
|
1063
|
+
}
|
1064
|
+
disconnectedCallback() {
|
1065
|
+
if (this.observeAppearance) {
|
1066
|
+
this.appearanceObserver.stop();
|
1067
|
+
}
|
1008
1068
|
}
|
1009
1069
|
async update(data) {
|
1010
1070
|
this.lastUpdateTimestamp = new Date;
|
@@ -1015,7 +1075,8 @@
|
|
1015
1075
|
return;
|
1016
1076
|
}
|
1017
1077
|
// first <cable-ready-updates-for> element in the DOM *at any given moment* updates all of the others
|
1018
|
-
|
1078
|
+
// if the element becomes visible though, we have to overrule and load it
|
1079
|
+
if (blocks[0].element !== this && !this.didTransitionToVisible) {
|
1019
1080
|
this.triggerElementLog.push(`${(new Date).toLocaleString()}: ${Log.cancel(this.lastUpdateTimestamp, "Update already requested")}`);
|
1020
1081
|
return;
|
1021
1082
|
}
|
@@ -1041,6 +1102,17 @@
|
|
1041
1102
|
block.process(data, this.html, this.index, this.lastUpdateTimestamp);
|
1042
1103
|
}));
|
1043
1104
|
}
|
1105
|
+
appearedInViewport() {
|
1106
|
+
if (!this.visible) {
|
1107
|
+
// transition from invisible to visible forces update
|
1108
|
+
this.didTransitionToVisible = true;
|
1109
|
+
this.update({});
|
1110
|
+
}
|
1111
|
+
this.visible = true;
|
1112
|
+
}
|
1113
|
+
disappearedFromViewport() {
|
1114
|
+
this.visible = false;
|
1115
|
+
}
|
1044
1116
|
get query() {
|
1045
1117
|
return `${this.tagName}[identifier="${this.identifier}"]`;
|
1046
1118
|
}
|
@@ -1050,6 +1122,9 @@
|
|
1050
1122
|
get debounce() {
|
1051
1123
|
return this.hasAttribute("debounce") ? parseInt(this.getAttribute("debounce")) : 20;
|
1052
1124
|
}
|
1125
|
+
get observeAppearance() {
|
1126
|
+
return this.hasAttribute("observe-appearance");
|
1127
|
+
}
|
1053
1128
|
}
|
1054
1129
|
class Block {
|
1055
1130
|
constructor(element) {
|
@@ -1078,6 +1153,7 @@
|
|
1078
1153
|
onBeforeElUpdated: shouldMorph(operation),
|
1079
1154
|
onElUpdated: _ => {
|
1080
1155
|
this.element.removeAttribute("updating");
|
1156
|
+
this.element.didTransitionToVisible = false;
|
1081
1157
|
dispatch(this.element, "cable-ready:after-update", operation);
|
1082
1158
|
assignFocus(operation.focusSelector);
|
1083
1159
|
}
|
@@ -1104,7 +1180,7 @@
|
|
1104
1180
|
}
|
1105
1181
|
shouldUpdate(data) {
|
1106
1182
|
// if everything that could prevent an update is false, update this block
|
1107
|
-
return !this.ignoresInnerUpdates && this.hasChangesSelectedForUpdate(data);
|
1183
|
+
return !this.ignoresInnerUpdates && this.hasChangesSelectedForUpdate(data) && (!this.observeAppearance || this.visible);
|
1108
1184
|
}
|
1109
1185
|
hasChangesSelectedForUpdate(data) {
|
1110
1186
|
// if there's an only attribute, only update if at least one of the attributes changed is in the allow list
|
@@ -1124,6 +1200,12 @@
|
|
1124
1200
|
get query() {
|
1125
1201
|
return this.element.query;
|
1126
1202
|
}
|
1203
|
+
get visible() {
|
1204
|
+
return this.element.visible;
|
1205
|
+
}
|
1206
|
+
get observeAppearance() {
|
1207
|
+
return this.element.observeAppearance;
|
1208
|
+
}
|
1127
1209
|
}
|
1128
1210
|
const registerInnerUpdates = () => {
|
1129
1211
|
document.addEventListener("stimulus-reflex:before", (event => {
|
@@ -27,12 +27,13 @@ module CableReady
|
|
27
27
|
tag.cable_ready_stream_from(**build_options(*keys, html_options))
|
28
28
|
end
|
29
29
|
|
30
|
-
def cable_ready_updates_for(*keys, url: nil, debounce: nil, only: nil, ignore_inner_updates: false, html_options: {}, &block)
|
30
|
+
def cable_ready_updates_for(*keys, url: nil, debounce: nil, only: nil, ignore_inner_updates: false, observe_appearance: false, html_options: {}, &block)
|
31
31
|
options = build_options(*keys, html_options)
|
32
32
|
options[:url] = url if url
|
33
33
|
options[:debounce] = debounce if debounce
|
34
34
|
options[:only] = only if only
|
35
35
|
options[:"ignore-inner-updates"] = "" if ignore_inner_updates
|
36
|
+
options[:"observe-appearance"] = "" if observe_appearance
|
36
37
|
tag.cable_ready_updates_for(**options) { capture(&block) }
|
37
38
|
end
|
38
39
|
|
data/lib/cable_ready/channels.rb
CHANGED
data/lib/cable_ready/config.rb
CHANGED
data/lib/cable_ready/version.rb
CHANGED
data/yarn.lock
CHANGED
@@ -4892,20 +4892,25 @@ semver-compare@^1.0.0:
|
|
4892
4892
|
resolved "https://registry.yarnpkg.com/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc"
|
4893
4893
|
integrity sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==
|
4894
4894
|
|
4895
|
-
semver@6.3.0
|
4895
|
+
semver@6.3.0:
|
4896
4896
|
version "6.3.0"
|
4897
4897
|
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
|
4898
4898
|
integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==
|
4899
4899
|
|
4900
4900
|
semver@^5.5.0, semver@^5.6.0:
|
4901
|
-
version "5.7.
|
4902
|
-
resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.
|
4903
|
-
integrity sha512-
|
4901
|
+
version "5.7.2"
|
4902
|
+
resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8"
|
4903
|
+
integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==
|
4904
|
+
|
4905
|
+
semver@^6.0.0, semver@^6.1.2, semver@^6.3.0:
|
4906
|
+
version "6.3.1"
|
4907
|
+
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4"
|
4908
|
+
integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==
|
4904
4909
|
|
4905
4910
|
semver@^7.3.4:
|
4906
|
-
version "7.
|
4907
|
-
resolved "https://registry.yarnpkg.com/semver/-/semver-7.
|
4908
|
-
integrity sha512-
|
4911
|
+
version "7.5.4"
|
4912
|
+
resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e"
|
4913
|
+
integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==
|
4909
4914
|
dependencies:
|
4910
4915
|
lru-cache "^6.0.0"
|
4911
4916
|
|
@@ -5728,9 +5733,9 @@ which@^2.0.1:
|
|
5728
5733
|
isexe "^2.0.0"
|
5729
5734
|
|
5730
5735
|
word-wrap@~1.2.3:
|
5731
|
-
version "1.2.
|
5732
|
-
resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.
|
5733
|
-
integrity sha512-
|
5736
|
+
version "1.2.4"
|
5737
|
+
resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.4.tgz#cb4b50ec9aca570abd1f52f33cd45b6c61739a9f"
|
5738
|
+
integrity sha512-2V81OA4ugVo5pRo46hAoD2ivUJx8jXmWXfUkY4KFNw0hEptvN0QfH3K4nHiwzGeKl5rFKedV48QVoqYavy4YpA==
|
5734
5739
|
|
5735
5740
|
wordwrap@^1.0.0:
|
5736
5741
|
version "1.0.0"
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cable_ready
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 5.0.
|
4
|
+
version: 5.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nathan Hopkins
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-11-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: actionpack
|
@@ -303,7 +303,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
303
303
|
- !ruby/object:Gem::Version
|
304
304
|
version: '0'
|
305
305
|
requirements: []
|
306
|
-
rubygems_version: 3.4.
|
306
|
+
rubygems_version: 3.4.19
|
307
307
|
signing_key:
|
308
308
|
specification_version: 4
|
309
309
|
summary: Out-of-Band Server Triggered DOM Operations
|