inviton-powerduck 0.0.340 → 0.0.342
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.
package/common/ladda-lite.ts
CHANGED
|
@@ -319,8 +319,8 @@ namespace laddaLiteUtils {
|
|
|
319
319
|
}
|
|
320
320
|
}
|
|
321
321
|
|
|
322
|
-
if (!retTarget.
|
|
323
|
-
retTarget.
|
|
322
|
+
if (!retTarget.classList.contains('ladda-button-root')) {
|
|
323
|
+
retTarget.classList.add('ladda-button-root');
|
|
324
324
|
}
|
|
325
325
|
|
|
326
326
|
return retTarget;
|
|
@@ -368,58 +368,76 @@ namespace laddaLiteUtils {
|
|
|
368
368
|
spinner: any;
|
|
369
369
|
}
|
|
370
370
|
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
371
|
+
/**
|
|
372
|
+
* Brings `button` to a known-good ladda state and returns its (cached)
|
|
373
|
+
* instance, (re)creating only whatever is missing. Safe — and meant — to be
|
|
374
|
+
* called on every spin.
|
|
375
|
+
*
|
|
376
|
+
* Why idempotent instead of "create once": the host <button> is usually owned
|
|
377
|
+
* by a framework (Vue, here). When that framework re-renders the button it
|
|
378
|
+
* overwrites `className` and can rebuild the button's children, which strips
|
|
379
|
+
* the `ladda-*` classes (the spinner CSS is entirely scoped to `.ladda-button`)
|
|
380
|
+
* and/or the `.ladda-label` / `.ladda-spinner` wrappers. The original
|
|
381
|
+
* create-once code then reused a cached instance and never restored any of it,
|
|
382
|
+
* so the spinner silently broke on the 2nd/3rd activation. Re-asserting the
|
|
383
|
+
* full contract here (without double-wrapping) makes it self-healing.
|
|
384
|
+
*/
|
|
385
|
+
export const ensureLadda = (button: any): LaddaInstance | null => {
|
|
386
|
+
if (!globalState.windowExists || button == null) {
|
|
387
|
+
return null;
|
|
374
388
|
}
|
|
375
389
|
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
const docRange = document.createRange();
|
|
381
|
-
docRange.selectNodeContents(button);
|
|
382
|
-
docRange.surroundContents(laddaLabel);
|
|
383
|
-
button.appendChild(laddaLabel);
|
|
390
|
+
// Re-assert classes/attrs every time (a framework re-render may have dropped them).
|
|
391
|
+
button.classList.add('ladda-button-root');
|
|
392
|
+
button.classList.add('ladda-button');
|
|
393
|
+
button.setAttribute('data-style', 'zoom-in');
|
|
384
394
|
|
|
385
|
-
//
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
button.
|
|
389
|
-
button.
|
|
395
|
+
// Wrap the (framework-rendered) content into a single .ladda-label, exactly
|
|
396
|
+
// once. If a re-render removed it, re-wrap; if it survived, reuse it as-is so
|
|
397
|
+
// we never produce nested .ladda-label > .ladda-label.
|
|
398
|
+
let spinnerHost: HTMLElement = button.querySelector(':scope > .ladda-spinner');
|
|
399
|
+
let label: HTMLElement = button.querySelector(':scope > .ladda-label');
|
|
400
|
+
if (label == null) {
|
|
401
|
+
label = document.createElement('span');
|
|
402
|
+
label.setAttribute('class', 'ladda-label');
|
|
403
|
+
const content = Array.prototype.slice.call(button.childNodes).filter((n: any) => n !== spinnerHost);
|
|
404
|
+
content.forEach((n: any) => label.appendChild(n));
|
|
405
|
+
button.insertBefore(label, button.firstChild);
|
|
406
|
+
}
|
|
390
407
|
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
408
|
+
if (spinnerHost == null) {
|
|
409
|
+
spinnerHost = document.createElement('span');
|
|
410
|
+
spinnerHost.setAttribute('class', 'ladda-spinner');
|
|
411
|
+
button.appendChild(spinnerHost);
|
|
412
|
+
}
|
|
394
413
|
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
414
|
+
// Resolve (or (re)create) the cached spinner instance for this button.
|
|
415
|
+
let id = button.getAttribute(ID_ATTRIBUTE);
|
|
416
|
+
let laddaInstance: LaddaInstance = id != null ? instanceCache[id] : null;
|
|
417
|
+
if (laddaInstance == null) {
|
|
418
|
+
id = String(Math.floor(Math.random() * 999999999 + 1));
|
|
419
|
+
button.setAttribute(ID_ATTRIBUTE, id);
|
|
420
|
+
laddaInstance = {
|
|
421
|
+
timer: <any>null,
|
|
422
|
+
spinner: createSpinner(button),
|
|
423
|
+
};
|
|
424
|
+
instanceCache[id] = laddaInstance;
|
|
425
|
+
}
|
|
399
426
|
|
|
400
|
-
instanceCache[newId] = laddaInstance;
|
|
401
427
|
return laddaInstance;
|
|
402
428
|
};
|
|
403
429
|
}
|
|
404
430
|
|
|
405
431
|
export class LaddaLite {
|
|
406
432
|
static showSpin(target: HTMLElement): void {
|
|
407
|
-
if (target == null) {
|
|
408
|
-
return;
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
if (!globalState.windowExists) {
|
|
433
|
+
if (target == null || !globalState.windowExists) {
|
|
412
434
|
return;
|
|
413
435
|
}
|
|
414
436
|
|
|
415
|
-
let laddaInstance;
|
|
416
437
|
const button = laddaLiteUtils.getTarget(target);
|
|
417
|
-
const
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
laddaInstance = laddaLiteUtils.createLaddaInstance(button);
|
|
421
|
-
} else {
|
|
422
|
-
laddaInstance = laddaLiteUtils.instanceCache[instanceId];
|
|
438
|
+
const laddaInstance = laddaLiteUtils.ensureLadda(button);
|
|
439
|
+
if (laddaInstance == null) {
|
|
440
|
+
return;
|
|
423
441
|
}
|
|
424
442
|
|
|
425
443
|
clearTimeout(laddaInstance.timer);
|
|
@@ -429,20 +447,21 @@ export class LaddaLite {
|
|
|
429
447
|
}
|
|
430
448
|
|
|
431
449
|
static hideSpin(target: HTMLElement): void {
|
|
432
|
-
if (target == null) {
|
|
433
|
-
return;
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
if (!globalState.windowExists) {
|
|
450
|
+
if (target == null || !globalState.windowExists) {
|
|
437
451
|
return;
|
|
438
452
|
}
|
|
439
453
|
|
|
440
454
|
const button = laddaLiteUtils.getTarget(target);
|
|
441
455
|
const instanceId = laddaLiteUtils.getInstanceId(button);
|
|
442
|
-
const laddaInstance = laddaLiteUtils.instanceCache[instanceId];
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
456
|
+
const laddaInstance = instanceId != null ? laddaLiteUtils.instanceCache[instanceId] : null;
|
|
457
|
+
|
|
458
|
+
// Always re-enable and clear the loading flag, even if the cached instance
|
|
459
|
+
// was lost (e.g. the framework replaced the button element) — never strand a
|
|
460
|
+
// disabled button.
|
|
461
|
+
button.disabled = false;
|
|
462
|
+
button.removeAttribute('data-loading');
|
|
463
|
+
|
|
464
|
+
if (laddaInstance != null) {
|
|
446
465
|
laddaInstance.timer = setTimeout(() => {
|
|
447
466
|
laddaInstance.spinner.stop();
|
|
448
467
|
}, 1000);
|
|
@@ -26,35 +26,64 @@ class LaddaButtonComponent extends TsxComponent<LaddaButtonArgs> implements Ladd
|
|
|
26
26
|
@Prop() loading!: boolean;
|
|
27
27
|
@Prop() clicked: (e: any) => Promise<any>;
|
|
28
28
|
|
|
29
|
+
// Whether the spinner should currently be showing. Tracked so `updated()` can
|
|
30
|
+
// restore the ladda DOM after a Vue re-render strips it (Ladda mutates this
|
|
31
|
+
// Vue-owned <button> out-of-band, so any patch can wipe its classes/wrappers).
|
|
32
|
+
private _spinning: boolean = false;
|
|
33
|
+
|
|
29
34
|
@Watch('loading')
|
|
30
35
|
onLoadingChanged(val: boolean) {
|
|
31
36
|
if (val) {
|
|
32
|
-
|
|
37
|
+
this.spin(this.$el as HTMLElement);
|
|
33
38
|
} else {
|
|
34
|
-
|
|
39
|
+
this.unspin(this.$el as HTMLElement);
|
|
35
40
|
}
|
|
36
41
|
}
|
|
37
42
|
|
|
38
43
|
mounted() {
|
|
39
44
|
if (this.loading) {
|
|
40
|
-
|
|
45
|
+
this.spin(this.$el as HTMLElement);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
updated() {
|
|
50
|
+
// Vue just patched this button. If we're mid-spin and the patch stripped
|
|
51
|
+
// ladda's class or spinner wrapper, re-assert it (ensureLadda is idempotent,
|
|
52
|
+
// so this is a no-op when nothing was lost).
|
|
53
|
+
if (!this._spinning) {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const el = this.$el as HTMLElement;
|
|
58
|
+
if (!el.classList.contains('ladda-button') || el.querySelector('.ladda-spinner') == null) {
|
|
59
|
+
LaddaLite.showSpin(el);
|
|
41
60
|
}
|
|
42
61
|
}
|
|
43
62
|
|
|
63
|
+
private spin(target: HTMLElement) {
|
|
64
|
+
this._spinning = true;
|
|
65
|
+
LaddaLite.showSpin(target);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
private unspin(target: HTMLElement) {
|
|
69
|
+
this._spinning = false;
|
|
70
|
+
LaddaLite.hideSpin(target);
|
|
71
|
+
}
|
|
72
|
+
|
|
44
73
|
handleButtonClicked(e) {
|
|
45
74
|
let targetElem = e.target as HTMLElement;
|
|
46
75
|
if (targetElem.nodeName.toLowerCase() != 'button') {
|
|
47
76
|
targetElem = targetElem.closest('button') as HTMLElement;
|
|
48
77
|
}
|
|
49
78
|
|
|
50
|
-
|
|
79
|
+
this.spin(targetElem);
|
|
51
80
|
|
|
52
81
|
this.clicked(e)
|
|
53
82
|
.then(() => {
|
|
54
|
-
|
|
83
|
+
this.unspin(targetElem);
|
|
55
84
|
})
|
|
56
85
|
.catch(() => {
|
|
57
|
-
|
|
86
|
+
this.unspin(targetElem);
|
|
58
87
|
});
|
|
59
88
|
}
|
|
60
89
|
|
|
@@ -231,6 +231,16 @@ export class OpenStreetMapComponent extends TsxComponent<OpenStreetMapArgs> impl
|
|
|
231
231
|
const raf = (globalThis as any).requestAnimationFrame as ((cb: () => void) => number) | undefined;
|
|
232
232
|
if (typeof raf !== 'function') {
|
|
233
233
|
this.invalidateSize();
|
|
234
|
+
|
|
235
|
+
// QA_AT-32: see rAF branch — re-fit bbox maps after the container gains
|
|
236
|
+
// real size (invalidateSize alone does not re-zoom). Deferred to the next
|
|
237
|
+
// tick so it runs after invalidateSize() has recomputed Leaflet's _size.
|
|
238
|
+
if (this.bBox != null) {
|
|
239
|
+
this.$nextTick(() => {
|
|
240
|
+
this.onBoundingBoxChanged();
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
|
|
234
244
|
return;
|
|
235
245
|
}
|
|
236
246
|
|
|
@@ -241,6 +251,22 @@ export class OpenStreetMapComponent extends TsxComponent<OpenStreetMapArgs> impl
|
|
|
241
251
|
this._pendingResizeRaf = raf(() => {
|
|
242
252
|
this._pendingResizeRaf = null;
|
|
243
253
|
this.invalidateSize();
|
|
254
|
+
|
|
255
|
+
// QA_AT-32: extend the QA_AT-21 self-heal — invalidateSize() only fixes
|
|
256
|
+
// tiles, not zoom/center. A bbox map's zoom comes from fitBounds()
|
|
257
|
+
// (getZoomValue() returns undefined when bBox != null), and the initial
|
|
258
|
+
// fitBounds ran inside the 100ms modal-mount delay while this tab pane was
|
|
259
|
+
// display:none (0×0) → Leaflet clamped to ~min zoom (whole world). Now that
|
|
260
|
+
// the container has its real size, re-fit the bounds. Guarded on `this.bBox`
|
|
261
|
+
// so GPS-marker maps (fixed default zoom) are untouched. Deferred to the
|
|
262
|
+
// next tick because invalidateSize() recomputes Leaflet's cached _size in
|
|
263
|
+
// a $nextTick of its own — running fitBounds in the same tick would fit
|
|
264
|
+
// against the still-stale 0×0 size (re-clamping to whole world).
|
|
265
|
+
if (this.bBox != null) {
|
|
266
|
+
this.$nextTick(() => {
|
|
267
|
+
this.onBoundingBoxChanged();
|
|
268
|
+
});
|
|
269
|
+
}
|
|
244
270
|
});
|
|
245
271
|
});
|
|
246
272
|
this._resizeObserver.observe(el);
|