|
2 | 2 | Base, |
3 | 3 | DragSort, |
4 | 4 | Select, |
| 5 | + deferUntil, |
5 | 6 | firstFocusableElement, |
6 | 7 | hasAttr, |
7 | 8 | isCtrlKeyPressed, |
@@ -175,7 +176,8 @@ export class NestedElementManager extends Base<NestedElementManagerSettings> { |
175 | 176 | #activateBound: any[] = []; |
176 | 177 | /** Teardown callbacks for per-card native listeners (delete items, …). */ |
177 | 178 | #disposers: Array<() => void> = []; |
178 | | - #afterInitTimeout: ReturnType<typeof setTimeout> | null = null; |
| 179 | + /** Aborts the after-init `elementEditor` lookup on `destroy()`. */ |
| 180 | + #afterInitController: AbortController | null = null; |
179 | 181 |
|
180 | 182 | constructor( |
181 | 183 | container: HTMLElement | string, |
@@ -235,28 +237,39 @@ export class NestedElementManager extends Base<NestedElementManagerSettings> { |
235 | 237 | this.#initCreateButton(); |
236 | 238 | } |
237 | 239 |
|
238 | | - // The owner's element editor boots after this manager does; look it up a |
239 | | - // beat later (legacy parity, including the delay). |
240 | | - this.#afterInitTimeout = setTimeout(() => { |
241 | | - this.elementEditor = $(this.container) |
242 | | - .closest('form') |
243 | | - .data('elementEditor'); |
244 | | - |
245 | | - if (this.elementEditor) { |
246 | | - this.elementEditor.on('update', () => { |
247 | | - this.settings.ownerId = this.elementEditor.getDraftElementId( |
248 | | - this.settings.ownerId |
249 | | - ); |
| 240 | + // The owner's element editor boots after this manager does; keep checking |
| 241 | + // until it's attached (legacy parity: the poll interval mirrors the old |
| 242 | + // fixed delay, but retries instead of gambling on a single check). |
| 243 | + const $form = $(this.container).closest('form'); |
| 244 | + this.#afterInitController = new AbortController(); |
| 245 | + |
| 246 | + if ($form.length) { |
| 247 | + deferUntil( |
| 248 | + () => $form.data('elementEditor'), |
| 249 | + 100, |
| 250 | + this.#afterInitController.signal |
| 251 | + ) |
| 252 | + .then((elementEditor) => { |
| 253 | + this.elementEditor = elementEditor; |
| 254 | + this.elementEditor.on('update', () => { |
| 255 | + this.settings.ownerId = this.elementEditor.getDraftElementId( |
| 256 | + this.settings.ownerId |
| 257 | + ); |
| 258 | + |
| 259 | + if (this.elementIndex) { |
| 260 | + this.elementIndex.settings.criteria[this.settings.ownerIdParam!] = |
| 261 | + this.settings.ownerId; |
| 262 | + } |
| 263 | + }); |
250 | 264 |
|
251 | | - if (this.elementIndex) { |
252 | | - this.elementIndex.settings.criteria[this.settings.ownerIdParam!] = |
253 | | - this.settings.ownerId; |
254 | | - } |
| 265 | + this.trigger('afterInit'); |
| 266 | + }) |
| 267 | + .catch(() => { |
| 268 | + // Destroyed before the editor showed up — nothing left to do. |
255 | 269 | }); |
256 | | - } |
257 | | - |
| 270 | + } else { |
258 | 271 | this.trigger('afterInit'); |
259 | | - }, 100); |
| 272 | + } |
260 | 273 |
|
261 | 274 | // NOTE: `Craft.cp` has no unregister API for this; the callback holds this |
262 | 275 | // instance until the page unloads (legacy parity — see README). |
@@ -1355,10 +1368,8 @@ export class NestedElementManager extends Base<NestedElementManagerSettings> { |
1355 | 1368 | // --- Teardown --------------------------------------------------------------- |
1356 | 1369 |
|
1357 | 1370 | override destroy(): void { |
1358 | | - if (this.#afterInitTimeout !== null) { |
1359 | | - clearTimeout(this.#afterInitTimeout); |
1360 | | - this.#afterInitTimeout = null; |
1361 | | - } |
| 1371 | + this.#afterInitController?.abort(); |
| 1372 | + this.#afterInitController = null; |
1362 | 1373 |
|
1363 | 1374 | for (const $bound of this.#activateBound) { |
1364 | 1375 | $bound.off('activate'); |
|
0 commit comments