@toolbox-web/grid-angular 0.14.2 → 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.
- package/README.md +9 -20
- package/fesm2022/toolbox-web-grid-angular-features-export.mjs +1 -2
- package/fesm2022/toolbox-web-grid-angular-features-export.mjs.map +1 -1
- package/fesm2022/toolbox-web-grid-angular-features-filtering.mjs +1 -2
- package/fesm2022/toolbox-web-grid-angular-features-filtering.mjs.map +1 -1
- package/fesm2022/toolbox-web-grid-angular-features-print.mjs +1 -2
- package/fesm2022/toolbox-web-grid-angular-features-print.mjs.map +1 -1
- package/fesm2022/toolbox-web-grid-angular-features-selection.mjs +1 -2
- package/fesm2022/toolbox-web-grid-angular-features-selection.mjs.map +1 -1
- package/fesm2022/toolbox-web-grid-angular-features-undo-redo.mjs +1 -2
- package/fesm2022/toolbox-web-grid-angular-features-undo-redo.mjs.map +1 -1
- package/fesm2022/toolbox-web-grid-angular.mjs +65 -7
- package/fesm2022/toolbox-web-grid-angular.mjs.map +1 -1
- package/package.json +1 -1
- package/types/toolbox-web-grid-angular-features-export.d.ts +1 -2
- package/types/toolbox-web-grid-angular-features-export.d.ts.map +1 -1
- package/types/toolbox-web-grid-angular-features-filtering.d.ts +1 -2
- package/types/toolbox-web-grid-angular-features-filtering.d.ts.map +1 -1
- package/types/toolbox-web-grid-angular-features-print.d.ts +1 -2
- package/types/toolbox-web-grid-angular-features-print.d.ts.map +1 -1
- package/types/toolbox-web-grid-angular-features-selection.d.ts +1 -2
- package/types/toolbox-web-grid-angular-features-selection.d.ts.map +1 -1
- package/types/toolbox-web-grid-angular-features-undo-redo.d.ts +1 -2
- package/types/toolbox-web-grid-angular-features-undo-redo.d.ts.map +1 -1
- package/types/toolbox-web-grid-angular.d.ts +24 -7
- package/types/toolbox-web-grid-angular.d.ts.map +1 -1
|
@@ -2310,13 +2310,12 @@ function provideGridIcons(icons) {
|
|
|
2310
2310
|
* ## Usage
|
|
2311
2311
|
*
|
|
2312
2312
|
* ```typescript
|
|
2313
|
-
* import { Component
|
|
2313
|
+
* import { Component } from '@angular/core';
|
|
2314
2314
|
* import { Grid, injectGrid } from '@toolbox-web/grid-angular';
|
|
2315
2315
|
*
|
|
2316
2316
|
* @Component({
|
|
2317
2317
|
* selector: 'app-my-grid',
|
|
2318
2318
|
* imports: [Grid],
|
|
2319
|
-
* schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
|
2320
2319
|
* template: `
|
|
2321
2320
|
* <button (click)="handleResize()">Force Layout</button>
|
|
2322
2321
|
* <button (click)="handleExport()" [disabled]="!grid.isReady()">Export</button>
|
|
@@ -2872,9 +2871,14 @@ class BaseGridEditor {
|
|
|
2872
2871
|
const grid = this.elementRef.nativeElement.closest('tbw-grid');
|
|
2873
2872
|
if (!grid)
|
|
2874
2873
|
return;
|
|
2874
|
+
const beforeHandler = () => this.onBeforeEditClose();
|
|
2875
|
+
grid.addEventListener('before-edit-close', beforeHandler, { once: true });
|
|
2875
2876
|
const handler = () => this.onEditClose();
|
|
2876
2877
|
grid.addEventListener('edit-close', handler, { once: true });
|
|
2877
|
-
this._editCloseCleanup = () =>
|
|
2878
|
+
this._editCloseCleanup = () => {
|
|
2879
|
+
grid.removeEventListener('before-edit-close', beforeHandler);
|
|
2880
|
+
grid.removeEventListener('edit-close', handler);
|
|
2881
|
+
};
|
|
2878
2882
|
}
|
|
2879
2883
|
// ============================================================================
|
|
2880
2884
|
// Methods
|
|
@@ -2891,6 +2895,18 @@ class BaseGridEditor {
|
|
|
2891
2895
|
isCellFocused() {
|
|
2892
2896
|
return this.elementRef.nativeElement.closest('[part="cell"]')?.classList.contains('cell-focus') ?? false;
|
|
2893
2897
|
}
|
|
2898
|
+
/**
|
|
2899
|
+
* Called **before** the grid clears editing state and destroys editor DOM.
|
|
2900
|
+
*
|
|
2901
|
+
* At this point the commit callback is still active, so subclasses can
|
|
2902
|
+
* call {@link commitValue} to flush any pending/deferred values.
|
|
2903
|
+
*
|
|
2904
|
+
* This fires only on the **commit** path (not on revert/cancel).
|
|
2905
|
+
* Use {@link onEditClose} for cleanup that should happen on both paths.
|
|
2906
|
+
*/
|
|
2907
|
+
onBeforeEditClose() {
|
|
2908
|
+
// Default: no-op. Subclasses override to flush pending values.
|
|
2909
|
+
}
|
|
2894
2910
|
/**
|
|
2895
2911
|
* Called when the grid ends the editing session for this cell.
|
|
2896
2912
|
*
|
|
@@ -3191,6 +3207,7 @@ let anchorCounter = 0;
|
|
|
3191
3207
|
* - **Escape handling** — closes the panel and returns focus to the inline input
|
|
3192
3208
|
* - **Synthetic Tab dispatch** — advances grid focus after overlay close
|
|
3193
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
|
|
3194
3211
|
*
|
|
3195
3212
|
* ## Usage
|
|
3196
3213
|
*
|
|
@@ -3313,6 +3330,9 @@ class BaseOverlayEditor extends BaseGridEditor {
|
|
|
3313
3330
|
}
|
|
3314
3331
|
// Move panel to body so it escapes grid overflow clipping
|
|
3315
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);
|
|
3316
3336
|
// Set up click-outside detection
|
|
3317
3337
|
this._abortCtrl = new AbortController();
|
|
3318
3338
|
document.addEventListener('pointerdown', (e) => this._onDocumentPointerDown(e), {
|
|
@@ -3372,6 +3392,10 @@ class BaseOverlayEditor extends BaseGridEditor {
|
|
|
3372
3392
|
this._abortCtrl = null;
|
|
3373
3393
|
this._focusObserver?.disconnect();
|
|
3374
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
|
+
}
|
|
3375
3399
|
if (this._panel?.parentNode) {
|
|
3376
3400
|
this._panel.parentNode.removeChild(this._panel);
|
|
3377
3401
|
}
|
|
@@ -3484,6 +3508,10 @@ class BaseOverlayEditor extends BaseGridEditor {
|
|
|
3484
3508
|
_getCell() {
|
|
3485
3509
|
return this._elementRef.nativeElement.closest('[part="cell"]') ?? null;
|
|
3486
3510
|
}
|
|
3511
|
+
/** Find the parent `<tbw-grid>` element for this editor. */
|
|
3512
|
+
_getGridElement() {
|
|
3513
|
+
return this._elementRef.nativeElement.closest('tbw-grid') ?? null;
|
|
3514
|
+
}
|
|
3487
3515
|
/**
|
|
3488
3516
|
* JS fallback positioning for browsers without CSS Anchor Positioning.
|
|
3489
3517
|
* Uses `getBoundingClientRect()` with viewport overflow detection.
|
|
@@ -3570,24 +3598,54 @@ class BaseOverlayEditor extends BaseGridEditor {
|
|
|
3570
3598
|
* `cell-focus` class changes. This handles row-editing mode where
|
|
3571
3599
|
* all editors exist simultaneously but only the focused cell's
|
|
3572
3600
|
* editor should have its overlay visible.
|
|
3601
|
+
*
|
|
3602
|
+
* A `justOpened` flash guard suppresses the observer from
|
|
3603
|
+
* immediately closing the overlay when `beginBulkEdit()` moves
|
|
3604
|
+
* focus to the first editable column. Without this guard,
|
|
3605
|
+
* double-click triggers a "flash open then close" effect.
|
|
3573
3606
|
*/
|
|
3574
3607
|
_setupFocusObserver() {
|
|
3575
3608
|
const cell = this._getCell();
|
|
3576
3609
|
if (!cell)
|
|
3577
3610
|
return;
|
|
3611
|
+
let justOpened = false;
|
|
3612
|
+
let pendingHideRaf = 0;
|
|
3578
3613
|
this._focusObserver = new MutationObserver((mutations) => {
|
|
3579
3614
|
for (const mutation of mutations) {
|
|
3580
3615
|
if (mutation.type !== 'attributes' || mutation.attributeName !== 'class')
|
|
3581
3616
|
continue;
|
|
3582
3617
|
const isFocused = cell.classList.contains('cell-focus');
|
|
3583
3618
|
if (isFocused && !this._isOpen) {
|
|
3584
|
-
// 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
|
+
}
|
|
3624
|
+
justOpened = true;
|
|
3585
3625
|
this.showOverlay();
|
|
3586
3626
|
this.onOverlayOpened();
|
|
3627
|
+
// Clear the guard after a macrotask so that an immediate
|
|
3628
|
+
// focus-away (e.g. beginBulkEdit focus adjustment) does
|
|
3629
|
+
// not close the overlay in the same event loop tick.
|
|
3630
|
+
setTimeout(() => {
|
|
3631
|
+
justOpened = false;
|
|
3632
|
+
}, 0);
|
|
3587
3633
|
}
|
|
3588
|
-
else if (!isFocused && this._isOpen) {
|
|
3589
|
-
// Cell lost focus — hide
|
|
3590
|
-
|
|
3634
|
+
else if (!isFocused && this._isOpen && !justOpened) {
|
|
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
|
+
});
|
|
3591
3649
|
}
|
|
3592
3650
|
}
|
|
3593
3651
|
});
|