@hellotext/hellotext 2.4.4 → 2.4.51

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.
@@ -30,6 +30,14 @@ let WebchatMessagesAPI = /*#__PURE__*/function () {
30
30
  headers: _hellotext.default.headers
31
31
  });
32
32
  }
33
+ }, {
34
+ key: "catchUp",
35
+ value: function catchUp(afterId) {
36
+ return this.index({
37
+ after_id: afterId,
38
+ session: _hellotext.default.session
39
+ });
40
+ }
33
41
  }, {
34
42
  key: "create",
35
43
  value: async function create(formData) {
@@ -32,6 +32,14 @@ var WebchatMessagesAPI = /*#__PURE__*/function () {
32
32
  }
33
33
  return index;
34
34
  }()
35
+ }, {
36
+ key: "catchUp",
37
+ value: function catchUp(afterId) {
38
+ return this.index({
39
+ after_id: afterId,
40
+ session: Hellotext.session
41
+ });
42
+ }
35
43
  }, {
36
44
  key: "create",
37
45
  value: function () {
@@ -13,6 +13,7 @@ function _toPrimitive(input, hint) { if (typeof input !== "object" || input ===
13
13
  let ApplicationChannel = /*#__PURE__*/function () {
14
14
  function ApplicationChannel() {
15
15
  _classCallCheck(this, ApplicationChannel);
16
+ ApplicationChannel.channels.add(this);
16
17
  }
17
18
  _createClass(ApplicationChannel, [{
18
19
  key: "send",
@@ -26,18 +27,20 @@ let ApplicationChannel = /*#__PURE__*/function () {
26
27
  identifier: JSON.stringify(identifier),
27
28
  data: JSON.stringify(data || {})
28
29
  };
29
- if (this.webSocket.readyState === WebSocket.OPEN) {
30
- this.webSocket.send(JSON.stringify(payload));
30
+ const socket = ApplicationChannel.ensureWebSocket();
31
+ const message = JSON.stringify(payload);
32
+ if (socket.readyState === WebSocket.OPEN) {
33
+ socket.send(message);
31
34
  } else {
32
- this.webSocket.addEventListener('open', () => {
33
- this.webSocket.send(JSON.stringify(payload));
35
+ socket.addEventListener('open', () => {
36
+ socket.send(message);
34
37
  });
35
38
  }
36
39
  }
37
40
  }, {
38
41
  key: "onMessage",
39
42
  value: function onMessage(callback) {
40
- this.webSocket.addEventListener('message', event => {
43
+ const handler = event => {
41
44
  const data = JSON.parse(event.data);
42
45
  const {
43
46
  type,
@@ -47,24 +50,154 @@ let ApplicationChannel = /*#__PURE__*/function () {
47
50
  return;
48
51
  }
49
52
  callback(message);
50
- });
53
+ };
54
+ ApplicationChannel.messageHandlers.add(handler);
55
+ ApplicationChannel.ensureWebSocket().addEventListener('message', handler);
56
+ }
57
+ }, {
58
+ key: "onDisconnect",
59
+ value: function onDisconnect(callback) {
60
+ ApplicationChannel.disconnectHandlers.add(callback);
61
+ }
62
+ }, {
63
+ key: "onSubscriptionConfirmed",
64
+ value: function onSubscriptionConfirmed(callback) {
65
+ ApplicationChannel.subscriptionConfirmHandlers.add(callback);
51
66
  }
52
67
  }, {
53
68
  key: "webSocket",
54
69
  get: function () {
55
- if (!ApplicationChannel.webSocket) {
56
- return ApplicationChannel.webSocket = new WebSocket(_core.Configuration.actionCableUrl);
57
- }
58
- return ApplicationChannel.webSocket;
70
+ return ApplicationChannel.ensureWebSocket();
59
71
  }
60
72
  }, {
61
73
  key: "ignoredEvents",
62
74
  get: function () {
63
75
  return ['ping', 'confirm_subscription', 'welcome'];
64
76
  }
77
+ }], [{
78
+ key: "ensureWebSocket",
79
+ value: function ensureWebSocket() {
80
+ if (this.webSocket && !this.closedWebSocket(this.webSocket)) {
81
+ return this.webSocket;
82
+ }
83
+ if (this.webSocket) {
84
+ this.needsResubscribe = true;
85
+ }
86
+ return this.openWebSocket();
87
+ }
88
+ }, {
89
+ key: "openWebSocket",
90
+ value: function openWebSocket() {
91
+ this.clearReconnectTimeout();
92
+ const socket = new WebSocket(_core.Configuration.actionCableUrl);
93
+ this.webSocket = socket;
94
+ this.installWebSocketHandlers(socket);
95
+ return socket;
96
+ }
97
+ }, {
98
+ key: "installWebSocketHandlers",
99
+ value: function installWebSocketHandlers(socket) {
100
+ socket.addEventListener('open', () => this.handleOpen(socket));
101
+ socket.addEventListener('close', () => this.handleDisconnect(socket));
102
+ socket.addEventListener('error', () => this.handleDisconnect(socket));
103
+ socket.addEventListener('message', event => this.handleControlMessage(event));
104
+ this.messageHandlers.forEach(handler => {
105
+ socket.addEventListener('message', handler);
106
+ });
107
+ }
108
+ }, {
109
+ key: "handleOpen",
110
+ value: function handleOpen(socket) {
111
+ if (socket !== this.webSocket) {
112
+ return;
113
+ }
114
+ this.reconnectAttempts = 0;
115
+ if (!this.needsResubscribe) {
116
+ return;
117
+ }
118
+ this.needsResubscribe = false;
119
+ this.resubscribeChannels();
120
+ }
121
+ }, {
122
+ key: "handleControlMessage",
123
+ value: function handleControlMessage(event) {
124
+ let data;
125
+ try {
126
+ data = JSON.parse(event.data);
127
+ } catch {
128
+ return;
129
+ }
130
+ if (data.type !== 'confirm_subscription') {
131
+ return;
132
+ }
133
+ this.subscriptionConfirmHandlers.forEach(callback => callback(data.identifier));
134
+ }
135
+ }, {
136
+ key: "handleDisconnect",
137
+ value: function handleDisconnect(socket) {
138
+ if (socket !== this.webSocket) {
139
+ return;
140
+ }
141
+ this.disconnectHandlers.forEach(callback => callback());
142
+ this.webSocket = null;
143
+ this.needsResubscribe = true;
144
+ this.scheduleReconnect();
145
+ }
146
+ }, {
147
+ key: "scheduleReconnect",
148
+ value: function scheduleReconnect() {
149
+ if (this.reconnectTimeout) {
150
+ return;
151
+ }
152
+ this.reconnectTimeout = setTimeout(() => {
153
+ this.reconnectTimeout = null;
154
+ this.reconnectAttempts += 1;
155
+ this.openWebSocket();
156
+ }, this.reconnectDelay);
157
+ }
158
+ }, {
159
+ key: "clearReconnectTimeout",
160
+ value: function clearReconnectTimeout() {
161
+ if (this.reconnectTimeout) {
162
+ clearTimeout(this.reconnectTimeout);
163
+ this.reconnectTimeout = null;
164
+ }
165
+ }
166
+ }, {
167
+ key: "resubscribeChannels",
168
+ value: function resubscribeChannels() {
169
+ this.channels.forEach(channel => {
170
+ const resubscribe = channel.resubscribe || channel.subscribe;
171
+ if (typeof resubscribe === 'function') {
172
+ resubscribe.call(channel);
173
+ }
174
+ });
175
+ }
176
+ }, {
177
+ key: "closedWebSocket",
178
+ value: function closedWebSocket(socket) {
179
+ return socket.readyState === WebSocket.CLOSED || socket.readyState === WebSocket.CLOSING;
180
+ }
181
+ }, {
182
+ key: "reconnectDelay",
183
+ get: function () {
184
+ const delay = Math.min(this.reconnectMaxDelay, this.reconnectBaseDelay * 2 ** this.reconnectAttempts);
185
+ const jitter = Math.round(delay * this.reconnectJitter * Math.random());
186
+ return delay + jitter;
187
+ }
65
188
  }]);
66
189
  return ApplicationChannel;
67
190
  }();
68
191
  ApplicationChannel.webSocket = void 0;
192
+ ApplicationChannel.channels = new Set();
193
+ ApplicationChannel.messageHandlers = new Set();
194
+ ApplicationChannel.disconnectHandlers = new Set();
195
+ ApplicationChannel.subscriptionConfirmHandlers = new Set();
196
+ ApplicationChannel.reconnectTimeout = null;
197
+ ApplicationChannel.reconnectAttempts = 0;
198
+ ApplicationChannel.reconnectBaseDelay = 500;
199
+ ApplicationChannel.reconnectMaxDelay = 10000;
200
+ ApplicationChannel.reconnectJitter = 0.3;
201
+ ApplicationChannel.needsResubscribe = false;
69
202
  var _default = ApplicationChannel;
70
203
  exports.default = _default;
@@ -7,6 +7,7 @@ import { Configuration } from '../core';
7
7
  var ApplicationChannel = /*#__PURE__*/function () {
8
8
  function ApplicationChannel() {
9
9
  _classCallCheck(this, ApplicationChannel);
10
+ ApplicationChannel.channels.add(this);
10
11
  }
11
12
  _createClass(ApplicationChannel, [{
12
13
  key: "send",
@@ -21,18 +22,20 @@ var ApplicationChannel = /*#__PURE__*/function () {
21
22
  identifier: JSON.stringify(identifier),
22
23
  data: JSON.stringify(data || {})
23
24
  };
24
- if (this.webSocket.readyState === WebSocket.OPEN) {
25
- this.webSocket.send(JSON.stringify(payload));
25
+ var socket = ApplicationChannel.ensureWebSocket();
26
+ var message = JSON.stringify(payload);
27
+ if (socket.readyState === WebSocket.OPEN) {
28
+ socket.send(message);
26
29
  } else {
27
- this.webSocket.addEventListener('open', () => {
28
- this.webSocket.send(JSON.stringify(payload));
30
+ socket.addEventListener('open', () => {
31
+ socket.send(message);
29
32
  });
30
33
  }
31
34
  }
32
35
  }, {
33
36
  key: "onMessage",
34
37
  value: function onMessage(callback) {
35
- this.webSocket.addEventListener('message', event => {
38
+ var handler = event => {
36
39
  var data = JSON.parse(event.data);
37
40
  var {
38
41
  type,
@@ -42,23 +45,153 @@ var ApplicationChannel = /*#__PURE__*/function () {
42
45
  return;
43
46
  }
44
47
  callback(message);
45
- });
48
+ };
49
+ ApplicationChannel.messageHandlers.add(handler);
50
+ ApplicationChannel.ensureWebSocket().addEventListener('message', handler);
51
+ }
52
+ }, {
53
+ key: "onDisconnect",
54
+ value: function onDisconnect(callback) {
55
+ ApplicationChannel.disconnectHandlers.add(callback);
56
+ }
57
+ }, {
58
+ key: "onSubscriptionConfirmed",
59
+ value: function onSubscriptionConfirmed(callback) {
60
+ ApplicationChannel.subscriptionConfirmHandlers.add(callback);
46
61
  }
47
62
  }, {
48
63
  key: "webSocket",
49
64
  get: function get() {
50
- if (!ApplicationChannel.webSocket) {
51
- return ApplicationChannel.webSocket = new WebSocket(Configuration.actionCableUrl);
52
- }
53
- return ApplicationChannel.webSocket;
65
+ return ApplicationChannel.ensureWebSocket();
54
66
  }
55
67
  }, {
56
68
  key: "ignoredEvents",
57
69
  get: function get() {
58
70
  return ['ping', 'confirm_subscription', 'welcome'];
59
71
  }
72
+ }], [{
73
+ key: "ensureWebSocket",
74
+ value: function ensureWebSocket() {
75
+ if (this.webSocket && !this.closedWebSocket(this.webSocket)) {
76
+ return this.webSocket;
77
+ }
78
+ if (this.webSocket) {
79
+ this.needsResubscribe = true;
80
+ }
81
+ return this.openWebSocket();
82
+ }
83
+ }, {
84
+ key: "openWebSocket",
85
+ value: function openWebSocket() {
86
+ this.clearReconnectTimeout();
87
+ var socket = new WebSocket(Configuration.actionCableUrl);
88
+ this.webSocket = socket;
89
+ this.installWebSocketHandlers(socket);
90
+ return socket;
91
+ }
92
+ }, {
93
+ key: "installWebSocketHandlers",
94
+ value: function installWebSocketHandlers(socket) {
95
+ socket.addEventListener('open', () => this.handleOpen(socket));
96
+ socket.addEventListener('close', () => this.handleDisconnect(socket));
97
+ socket.addEventListener('error', () => this.handleDisconnect(socket));
98
+ socket.addEventListener('message', event => this.handleControlMessage(event));
99
+ this.messageHandlers.forEach(handler => {
100
+ socket.addEventListener('message', handler);
101
+ });
102
+ }
103
+ }, {
104
+ key: "handleOpen",
105
+ value: function handleOpen(socket) {
106
+ if (socket !== this.webSocket) {
107
+ return;
108
+ }
109
+ this.reconnectAttempts = 0;
110
+ if (!this.needsResubscribe) {
111
+ return;
112
+ }
113
+ this.needsResubscribe = false;
114
+ this.resubscribeChannels();
115
+ }
116
+ }, {
117
+ key: "handleControlMessage",
118
+ value: function handleControlMessage(event) {
119
+ var data;
120
+ try {
121
+ data = JSON.parse(event.data);
122
+ } catch (_unused) {
123
+ return;
124
+ }
125
+ if (data.type !== 'confirm_subscription') {
126
+ return;
127
+ }
128
+ this.subscriptionConfirmHandlers.forEach(callback => callback(data.identifier));
129
+ }
130
+ }, {
131
+ key: "handleDisconnect",
132
+ value: function handleDisconnect(socket) {
133
+ if (socket !== this.webSocket) {
134
+ return;
135
+ }
136
+ this.disconnectHandlers.forEach(callback => callback());
137
+ this.webSocket = null;
138
+ this.needsResubscribe = true;
139
+ this.scheduleReconnect();
140
+ }
141
+ }, {
142
+ key: "scheduleReconnect",
143
+ value: function scheduleReconnect() {
144
+ if (this.reconnectTimeout) {
145
+ return;
146
+ }
147
+ this.reconnectTimeout = setTimeout(() => {
148
+ this.reconnectTimeout = null;
149
+ this.reconnectAttempts += 1;
150
+ this.openWebSocket();
151
+ }, this.reconnectDelay);
152
+ }
153
+ }, {
154
+ key: "clearReconnectTimeout",
155
+ value: function clearReconnectTimeout() {
156
+ if (this.reconnectTimeout) {
157
+ clearTimeout(this.reconnectTimeout);
158
+ this.reconnectTimeout = null;
159
+ }
160
+ }
161
+ }, {
162
+ key: "resubscribeChannels",
163
+ value: function resubscribeChannels() {
164
+ this.channels.forEach(channel => {
165
+ var resubscribe = channel.resubscribe || channel.subscribe;
166
+ if (typeof resubscribe === 'function') {
167
+ resubscribe.call(channel);
168
+ }
169
+ });
170
+ }
171
+ }, {
172
+ key: "closedWebSocket",
173
+ value: function closedWebSocket(socket) {
174
+ return socket.readyState === WebSocket.CLOSED || socket.readyState === WebSocket.CLOSING;
175
+ }
176
+ }, {
177
+ key: "reconnectDelay",
178
+ get: function get() {
179
+ var delay = Math.min(this.reconnectMaxDelay, this.reconnectBaseDelay * 2 ** this.reconnectAttempts);
180
+ var jitter = Math.round(delay * this.reconnectJitter * Math.random());
181
+ return delay + jitter;
182
+ }
60
183
  }]);
61
184
  return ApplicationChannel;
62
185
  }();
63
186
  ApplicationChannel.webSocket = void 0;
187
+ ApplicationChannel.channels = new Set();
188
+ ApplicationChannel.messageHandlers = new Set();
189
+ ApplicationChannel.disconnectHandlers = new Set();
190
+ ApplicationChannel.subscriptionConfirmHandlers = new Set();
191
+ ApplicationChannel.reconnectTimeout = null;
192
+ ApplicationChannel.reconnectAttempts = 0;
193
+ ApplicationChannel.reconnectBaseDelay = 500;
194
+ ApplicationChannel.reconnectMaxDelay = 10000;
195
+ ApplicationChannel.reconnectJitter = 0.3;
196
+ ApplicationChannel.needsResubscribe = false;
64
197
  export default ApplicationChannel;
@@ -30,12 +30,25 @@ let WebchatChannel = /*#__PURE__*/function (_ApplicationChannel) {
30
30
  _this.id = id;
31
31
  _this.session = session;
32
32
  _this.conversation = conversation;
33
+ // Keep our own subscription intent instead of trusting the socket state.
34
+ // The shared WebSocket can reconnect independently, but an explicit
35
+ // unsubscribe means this channel should not silently join again.
36
+ _this.subscribed = false;
37
+ _this.awaitingReconnectConfirmation = false;
38
+ _this.reconnectCallbacks = new Set();
39
+
40
+ // ActionCable confirms subscriptions at the socket level. This channel
41
+ // listens for those confirmations so the controller can wait until Rails has
42
+ // accepted this exact WebchatChannel subscription before fetching missed
43
+ // messages from the REST catch-up endpoint.
44
+ _this.onSubscriptionConfirmed(identifier => _this.handleSubscriptionConfirmed(identifier));
33
45
  _this.subscribe();
34
46
  return _this;
35
47
  }
36
48
  _createClass(WebchatChannel, [{
37
49
  key: "subscribe",
38
50
  value: function subscribe() {
51
+ this.subscribed = true;
39
52
  const params = {
40
53
  channel: 'WebchatChannel',
41
54
  id: this.id,
@@ -50,6 +63,7 @@ let WebchatChannel = /*#__PURE__*/function (_ApplicationChannel) {
50
63
  }, {
51
64
  key: "unsubscribe",
52
65
  value: function unsubscribe() {
66
+ this.subscribed = false;
53
67
  const params = {
54
68
  channel: 'WebchatChannel',
55
69
  id: this.id,
@@ -61,6 +75,49 @@ let WebchatChannel = /*#__PURE__*/function (_ApplicationChannel) {
61
75
  identifier: params
62
76
  });
63
77
  }
78
+ }, {
79
+ key: "resubscribe",
80
+ value: function resubscribe() {
81
+ if (this.subscribed === false) return;
82
+
83
+ // Reconnect recovery has two phases: send the subscription command first,
84
+ // then wait for ActionCable to confirm it. Catch-up work should run after
85
+ // that confirmation so broadcasts and REST backfill are pointed at the same
86
+ // live conversation subscription.
87
+ this.awaitingReconnectConfirmation = true;
88
+ this.subscribe();
89
+ }
90
+ }, {
91
+ key: "onReconnect",
92
+ value: function onReconnect(callback) {
93
+ this.reconnectCallbacks.add(callback);
94
+ }
95
+ }, {
96
+ key: "handleSubscriptionConfirmed",
97
+ value: function handleSubscriptionConfirmed(identifier) {
98
+ if (!this.awaitingReconnectConfirmation || !this.matchesIdentifier(identifier)) return;
99
+
100
+ // Only the first matching confirmation completes the reconnect cycle. This
101
+ // prevents unrelated subscription confirmations on the shared socket from
102
+ // triggering duplicate catch-up requests.
103
+ this.awaitingReconnectConfirmation = false;
104
+ this.reconnectCallbacks.forEach(callback => callback());
105
+ }
106
+ }, {
107
+ key: "matchesIdentifier",
108
+ value: function matchesIdentifier(identifier) {
109
+ let params;
110
+ try {
111
+ params = typeof identifier === 'string' ? JSON.parse(identifier) : identifier;
112
+ } catch {
113
+ return false;
114
+ }
115
+
116
+ // ActionCable sends the same identifier payload we used when subscribing.
117
+ // Matching every routing key keeps a confirmation for another webchat,
118
+ // session, or conversation from being treated as this channel's reconnect.
119
+ return params.channel === 'WebchatChannel' && params.id === this.id && params.session === this.session && params.conversation === this.conversation;
120
+ }
64
121
  }, {
65
122
  key: "startTypingIndicator",
66
123
  value: function startTypingIndicator() {
@@ -23,12 +23,25 @@ var WebchatChannel = /*#__PURE__*/function (_ApplicationChannel) {
23
23
  _this.id = id;
24
24
  _this.session = session;
25
25
  _this.conversation = conversation;
26
+ // Keep our own subscription intent instead of trusting the socket state.
27
+ // The shared WebSocket can reconnect independently, but an explicit
28
+ // unsubscribe means this channel should not silently join again.
29
+ _this.subscribed = false;
30
+ _this.awaitingReconnectConfirmation = false;
31
+ _this.reconnectCallbacks = new Set();
32
+
33
+ // ActionCable confirms subscriptions at the socket level. This channel
34
+ // listens for those confirmations so the controller can wait until Rails has
35
+ // accepted this exact WebchatChannel subscription before fetching missed
36
+ // messages from the REST catch-up endpoint.
37
+ _this.onSubscriptionConfirmed(identifier => _this.handleSubscriptionConfirmed(identifier));
26
38
  _this.subscribe();
27
39
  return _this;
28
40
  }
29
41
  _createClass(WebchatChannel, [{
30
42
  key: "subscribe",
31
43
  value: function subscribe() {
44
+ this.subscribed = true;
32
45
  var params = {
33
46
  channel: 'WebchatChannel',
34
47
  id: this.id,
@@ -43,6 +56,7 @@ var WebchatChannel = /*#__PURE__*/function (_ApplicationChannel) {
43
56
  }, {
44
57
  key: "unsubscribe",
45
58
  value: function unsubscribe() {
59
+ this.subscribed = false;
46
60
  var params = {
47
61
  channel: 'WebchatChannel',
48
62
  id: this.id,
@@ -54,6 +68,49 @@ var WebchatChannel = /*#__PURE__*/function (_ApplicationChannel) {
54
68
  identifier: params
55
69
  });
56
70
  }
71
+ }, {
72
+ key: "resubscribe",
73
+ value: function resubscribe() {
74
+ if (this.subscribed === false) return;
75
+
76
+ // Reconnect recovery has two phases: send the subscription command first,
77
+ // then wait for ActionCable to confirm it. Catch-up work should run after
78
+ // that confirmation so broadcasts and REST backfill are pointed at the same
79
+ // live conversation subscription.
80
+ this.awaitingReconnectConfirmation = true;
81
+ this.subscribe();
82
+ }
83
+ }, {
84
+ key: "onReconnect",
85
+ value: function onReconnect(callback) {
86
+ this.reconnectCallbacks.add(callback);
87
+ }
88
+ }, {
89
+ key: "handleSubscriptionConfirmed",
90
+ value: function handleSubscriptionConfirmed(identifier) {
91
+ if (!this.awaitingReconnectConfirmation || !this.matchesIdentifier(identifier)) return;
92
+
93
+ // Only the first matching confirmation completes the reconnect cycle. This
94
+ // prevents unrelated subscription confirmations on the shared socket from
95
+ // triggering duplicate catch-up requests.
96
+ this.awaitingReconnectConfirmation = false;
97
+ this.reconnectCallbacks.forEach(callback => callback());
98
+ }
99
+ }, {
100
+ key: "matchesIdentifier",
101
+ value: function matchesIdentifier(identifier) {
102
+ var params;
103
+ try {
104
+ params = typeof identifier === 'string' ? JSON.parse(identifier) : identifier;
105
+ } catch (_unused) {
106
+ return false;
107
+ }
108
+
109
+ // ActionCable sends the same identifier payload we used when subscribing.
110
+ // Matching every routing key keeps a confirmation for another webchat,
111
+ // session, or conversation from being treated as this channel's reconnect.
112
+ return params.channel === 'WebchatChannel' && params.id === this.id && params.session === this.session && params.conversation === this.conversation;
113
+ }
57
114
  }, {
58
115
  key: "startTypingIndicator",
59
116
  value: function startTypingIndicator() {
@@ -30,6 +30,13 @@ let _default = /*#__PURE__*/function (_Controller) {
30
30
  key: "connect",
31
31
  value: function connect() {
32
32
  this.updateFades();
33
+ this.observeContainerSize();
34
+ }
35
+ }, {
36
+ key: "disconnect",
37
+ value: function disconnect() {
38
+ var _this$resizeObserver;
39
+ (_this$resizeObserver = this.resizeObserver) === null || _this$resizeObserver === void 0 ? void 0 : _this$resizeObserver.disconnect();
33
40
  }
34
41
  }, {
35
42
  key: "setId",
@@ -221,6 +228,13 @@ let _default = /*#__PURE__*/function (_Controller) {
221
228
  value: function getPageStartOffset() {
222
229
  return Number.isFinite(this.pageStartOffsetValue) ? this.pageStartOffsetValue : 0;
223
230
  }
231
+ }, {
232
+ key: "observeContainerSize",
233
+ value: function observeContainerSize() {
234
+ if (!this.hasCarouselContainerTarget || !window.ResizeObserver) return;
235
+ this.resizeObserver = new ResizeObserver(() => this.updateFades());
236
+ this.resizeObserver.observe(this.carouselContainerTarget);
237
+ }
224
238
  }, {
225
239
  key: "updateFades",
226
240
  value: function updateFades() {
@@ -26,6 +26,13 @@ var _default = /*#__PURE__*/function (_Controller) {
26
26
  key: "connect",
27
27
  value: function connect() {
28
28
  this.updateFades();
29
+ this.observeContainerSize();
30
+ }
31
+ }, {
32
+ key: "disconnect",
33
+ value: function disconnect() {
34
+ var _this$resizeObserver;
35
+ (_this$resizeObserver = this.resizeObserver) === null || _this$resizeObserver === void 0 ? void 0 : _this$resizeObserver.disconnect();
29
36
  }
30
37
  }, {
31
38
  key: "setId",
@@ -217,6 +224,13 @@ var _default = /*#__PURE__*/function (_Controller) {
217
224
  value: function getPageStartOffset() {
218
225
  return Number.isFinite(this.pageStartOffsetValue) ? this.pageStartOffsetValue : 0;
219
226
  }
227
+ }, {
228
+ key: "observeContainerSize",
229
+ value: function observeContainerSize() {
230
+ if (!this.hasCarouselContainerTarget || !window.ResizeObserver) return;
231
+ this.resizeObserver = new ResizeObserver(() => this.updateFades());
232
+ this.resizeObserver.observe(this.carouselContainerTarget);
233
+ }
220
234
  }, {
221
235
  key: "updateFades",
222
236
  value: function updateFades() {