rails_modal_manager 1.0.44 → 1.0.46

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b0752ed8f79cdbe227d9a4010cab4ac2f73cf59e75dade5572bc98cda6374e2d
4
- data.tar.gz: 5d9d4f28f9f9a4535d5646e707540e21e641f49e5b757a4d50d9cc7afbf1ef54
3
+ metadata.gz: 7dec9a40ef3da3620d41315569b51f9b0a3ac6a291bdd81edf77ca6271858464
4
+ data.tar.gz: 719e56407709affe6b21f781ad92cc5496f3ed1c13de711b6f4cbc3769915910
5
5
  SHA512:
6
- metadata.gz: fc8042fd0499d9f7076c8ad822ef9849a324f12a733d23a5449b1b07677f9bb73b8b9e9d88324baec9b448ffcdf983d83a1a9d3f6f2e346b87da23fdfd39e9fb
7
- data.tar.gz: eb4dc333dd595d3bac849153b8e5f7cde6ecefecf3e5d9fa056309e3a479e8e96d70e9734aabd219170a920e79f3e009099128d3a0b5220fb5c5d394d5925545
6
+ metadata.gz: 41af209ea06bad87c813444fe5a4e7ba074d685709d9f1dbf916e5dfbd983120d2445de1436bec01ee7b9d3b675084887824010e7ffce4704df4b0c4d74b3757
7
+ data.tar.gz: 14ed339f8138e4e4586f4b13d4fd078e570752bd92f2e7367d2d8c3b128dde332c12f755e7343be703e8810bf5f5f73abe623fbb25ccc740452befa6fc3bca65
@@ -297,6 +297,20 @@ export default class extends Controller {
297
297
  const modal = this.element.closest('.rmm-modal')
298
298
  if (!modal) return
299
299
 
300
+ // Invalidate any pending AJAX responses + abort network requests
301
+ const contentContainer = modal.querySelector('.rmm-tab-content')
302
+ if (contentContainer) {
303
+ // Increment shared counter to invalidate all pending responses
304
+ contentContainer.dataset.rmmRequestId = (parseInt(contentContainer.dataset.rmmRequestId || '0', 10) + 1)
305
+ }
306
+ modal.querySelectorAll('[data-controller*="rmm-submenu"]').forEach(el => {
307
+ const ctrl = this.application.getControllerForElementAndIdentifier(el, 'rmm-submenu')
308
+ if (ctrl && ctrl.currentAbortController) {
309
+ ctrl.currentAbortController.abort()
310
+ ctrl.currentAbortController = null
311
+ }
312
+ })
313
+
300
314
  // Hide all submenu groups
301
315
  modal.querySelectorAll('.rmm-submenu-group').forEach(group => {
302
316
  group.classList.remove('rmm-submenu-group-active')
@@ -45,7 +45,6 @@ export default class extends Controller {
45
45
  this.ajaxCache = new Map()
46
46
  this.currentItemId = null
47
47
  this.currentAbortController = null
48
- this.currentRequestId = 0
49
48
 
50
49
  // Find initial active item
51
50
  const activeItem = this.element.querySelector('.rmm-submenu-item.rmm-active')
@@ -54,6 +53,19 @@ export default class extends Controller {
54
53
  }
55
54
  }
56
55
 
56
+ /**
57
+ * Increment the shared request counter on the content container.
58
+ * Called synchronously from selectItem/forceReloadActive BEFORE async loadAjaxContent.
59
+ * This ensures any pending async response is immediately invalidated.
60
+ */
61
+ _incrementRequestId() {
62
+ const modal = this.element.closest('.rmm-modal')
63
+ if (!modal) return
64
+ const container = modal.querySelector('.rmm-tab-content')
65
+ if (!container) return
66
+ container.dataset.rmmRequestId = (parseInt(container.dataset.rmmRequestId || '0', 10) + 1)
67
+ }
68
+
57
69
  selectItem(e) {
58
70
  const item = e.currentTarget
59
71
  const itemId = item.dataset.itemId
@@ -75,6 +87,8 @@ export default class extends Controller {
75
87
 
76
88
  // Handle content switching based on load mode
77
89
  if (this.loadModeValue === 'ajax') {
90
+ // Synchronously invalidate any pending requests BEFORE starting new one
91
+ this._incrementRequestId()
78
92
  this.loadAjaxContent(item)
79
93
  } else {
80
94
  this.switchPanel(item)
@@ -99,6 +113,8 @@ export default class extends Controller {
99
113
  const activeItem = this.element.querySelector('.rmm-submenu-item.rmm-active')
100
114
  if (activeItem) {
101
115
  if (this.loadModeValue === 'ajax') {
116
+ // Synchronously invalidate any pending requests
117
+ this._incrementRequestId()
102
118
  this.loadAjaxContent(activeItem)
103
119
  } else {
104
120
  this.switchPanel(activeItem)
@@ -142,8 +158,8 @@ export default class extends Controller {
142
158
 
143
159
  /**
144
160
  * AJAX mode: Load content from URL
145
- * Uses AbortController to cancel previous pending requests and
146
- * validates response against current active item to prevent race conditions.
161
+ * Uses AbortController to cancel pending network requests and
162
+ * a shared request counter (on the DOM) to discard stale responses.
147
163
  */
148
164
  async loadAjaxContent(item) {
149
165
  const url = item.dataset.url
@@ -159,20 +175,23 @@ export default class extends Controller {
159
175
  // Check cache
160
176
  if (this.cacheAjaxValue && this.ajaxCache.has(url)) {
161
177
  contentContainer.innerHTML = this.ajaxCache.get(url)
178
+ contentContainer.classList.remove('rmm-tab-loading')
162
179
  this.dispatch('contentLoaded', {
163
180
  detail: { modalId: this.modalIdValue, itemId: item.dataset.itemId, fromCache: true }
164
181
  })
165
182
  return
166
183
  }
167
184
 
168
- // Abort previous pending request
185
+ // Abort previous pending request from this controller instance
169
186
  if (this.currentAbortController) {
170
187
  this.currentAbortController.abort()
171
188
  }
172
189
 
173
190
  const abortController = new AbortController()
174
191
  this.currentAbortController = abortController
175
- const requestId = ++this.currentRequestId
192
+
193
+ // Read the request ID that was set synchronously by selectItem/forceReloadActive
194
+ const requestId = parseInt(contentContainer.dataset.rmmRequestId || '0', 10)
176
195
 
177
196
  // Show loading state
178
197
  contentContainer.classList.add('rmm-tab-loading')
@@ -193,8 +212,8 @@ export default class extends Controller {
193
212
 
194
213
  const html = await response.text()
195
214
 
196
- // Validate: only update if this is still the latest request
197
- if (this.currentRequestId !== requestId) {
215
+ // Validate: discard if a newer request has been initiated
216
+ if (parseInt(contentContainer.dataset.rmmRequestId, 10) !== requestId) {
198
217
  return
199
218
  }
200
219
 
@@ -216,6 +235,11 @@ export default class extends Controller {
216
235
  return
217
236
  }
218
237
 
238
+ // Also discard errors from stale requests
239
+ if (parseInt(contentContainer.dataset.rmmRequestId, 10) !== requestId) {
240
+ return
241
+ }
242
+
219
243
  console.error('RMM Submenu: Failed to load content', error)
220
244
  contentContainer.innerHTML = `
221
245
  <div class="rmm-tab-error">
@@ -259,6 +283,7 @@ export default class extends Controller {
259
283
  if (url) {
260
284
  this.ajaxCache.delete(url)
261
285
  }
286
+ this._incrementRequestId()
262
287
  this.loadAjaxContent(activeItem)
263
288
  }
264
289
  }
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RailsModalManager
4
- VERSION = "1.0.44"
4
+ VERSION = "1.0.46"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rails_modal_manager
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.44
4
+ version: 1.0.46
5
5
  platform: ruby
6
6
  authors:
7
7
  - reshacs