@toolbox-web/grid-angular 0.14.3 → 0.15.0
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.
|
@@ -3207,6 +3207,7 @@ let anchorCounter = 0;
|
|
|
3207
3207
|
* - **Escape handling** — closes the panel and returns focus to the inline input
|
|
3208
3208
|
* - **Synthetic Tab dispatch** — advances grid focus after overlay close
|
|
3209
3209
|
* - **Automatic teardown** — removes the panel from `<body>` and cleans up listeners
|
|
3210
|
+
* - **External focus registration** — auto-registers the panel via `grid.registerExternalFocusContainer()` so the grid keeps `data-has-focus` and editors stay open while the overlay has focus
|
|
3210
3211
|
*
|
|
3211
3212
|
* ## Usage
|
|
3212
3213
|
*
|
|
@@ -3329,6 +3330,9 @@ class BaseOverlayEditor extends BaseGridEditor {
|
|
|
3329
3330
|
}
|
|
3330
3331
|
// Move panel to body so it escapes grid overflow clipping
|
|
3331
3332
|
document.body.appendChild(panel);
|
|
3333
|
+
// Register the panel as an external focus container on the grid
|
|
3334
|
+
// so focus moving into the overlay is treated as "still in the grid"
|
|
3335
|
+
this._getGridElement()?.registerExternalFocusContainer?.(panel);
|
|
3332
3336
|
// Set up click-outside detection
|
|
3333
3337
|
this._abortCtrl = new AbortController();
|
|
3334
3338
|
document.addEventListener('pointerdown', (e) => this._onDocumentPointerDown(e), {
|
|
@@ -3388,6 +3392,10 @@ class BaseOverlayEditor extends BaseGridEditor {
|
|
|
3388
3392
|
this._abortCtrl = null;
|
|
3389
3393
|
this._focusObserver?.disconnect();
|
|
3390
3394
|
this._focusObserver = null;
|
|
3395
|
+
// Unregister the panel from the grid's external focus container registry
|
|
3396
|
+
if (this._panel) {
|
|
3397
|
+
this._getGridElement()?.unregisterExternalFocusContainer?.(this._panel);
|
|
3398
|
+
}
|
|
3391
3399
|
if (this._panel?.parentNode) {
|
|
3392
3400
|
this._panel.parentNode.removeChild(this._panel);
|
|
3393
3401
|
}
|
|
@@ -3500,6 +3508,10 @@ class BaseOverlayEditor extends BaseGridEditor {
|
|
|
3500
3508
|
_getCell() {
|
|
3501
3509
|
return this._elementRef.nativeElement.closest('[part="cell"]') ?? null;
|
|
3502
3510
|
}
|
|
3511
|
+
/** Find the parent `<tbw-grid>` element for this editor. */
|
|
3512
|
+
_getGridElement() {
|
|
3513
|
+
return this._elementRef.nativeElement.closest('tbw-grid') ?? null;
|
|
3514
|
+
}
|
|
3503
3515
|
/**
|
|
3504
3516
|
* JS fallback positioning for browsers without CSS Anchor Positioning.
|
|
3505
3517
|
* Uses `getBoundingClientRect()` with viewport overflow detection.
|
|
@@ -3597,13 +3609,18 @@ class BaseOverlayEditor extends BaseGridEditor {
|
|
|
3597
3609
|
if (!cell)
|
|
3598
3610
|
return;
|
|
3599
3611
|
let justOpened = false;
|
|
3612
|
+
let pendingHideRaf = 0;
|
|
3600
3613
|
this._focusObserver = new MutationObserver((mutations) => {
|
|
3601
3614
|
for (const mutation of mutations) {
|
|
3602
3615
|
if (mutation.type !== 'attributes' || mutation.attributeName !== 'class')
|
|
3603
3616
|
continue;
|
|
3604
3617
|
const isFocused = cell.classList.contains('cell-focus');
|
|
3605
3618
|
if (isFocused && !this._isOpen) {
|
|
3606
|
-
// Cell just gained focus — open overlay
|
|
3619
|
+
// Cell just gained focus — cancel any pending hide and open overlay.
|
|
3620
|
+
if (pendingHideRaf) {
|
|
3621
|
+
cancelAnimationFrame(pendingHideRaf);
|
|
3622
|
+
pendingHideRaf = 0;
|
|
3623
|
+
}
|
|
3607
3624
|
justOpened = true;
|
|
3608
3625
|
this.showOverlay();
|
|
3609
3626
|
this.onOverlayOpened();
|
|
@@ -3615,8 +3632,20 @@ class BaseOverlayEditor extends BaseGridEditor {
|
|
|
3615
3632
|
}, 0);
|
|
3616
3633
|
}
|
|
3617
3634
|
else if (!isFocused && this._isOpen && !justOpened) {
|
|
3618
|
-
// Cell lost focus — hide
|
|
3619
|
-
|
|
3635
|
+
// Cell lost focus — defer hide to allow render cycles to settle.
|
|
3636
|
+
// Re-renders (e.g., from ResizeObserver after a footer appears)
|
|
3637
|
+
// may transiently toggle cell-focus within the same frame.
|
|
3638
|
+
// Deferring to the next animation frame lets the render pipeline
|
|
3639
|
+
// finish before we decide whether the overlay should actually close.
|
|
3640
|
+
if (pendingHideRaf)
|
|
3641
|
+
cancelAnimationFrame(pendingHideRaf);
|
|
3642
|
+
pendingHideRaf = requestAnimationFrame(() => {
|
|
3643
|
+
pendingHideRaf = 0;
|
|
3644
|
+
// Re-check settled state — cell-focus may have been re-applied
|
|
3645
|
+
if (!cell.classList.contains('cell-focus') && this._isOpen) {
|
|
3646
|
+
this.hideOverlay(true);
|
|
3647
|
+
}
|
|
3648
|
+
});
|
|
3620
3649
|
}
|
|
3621
3650
|
}
|
|
3622
3651
|
});
|