@toolbox-web/grid-angular 0.17.0 → 0.18.1
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/fesm2022/toolbox-web-grid-angular-features-selection.mjs +3 -4
- package/fesm2022/toolbox-web-grid-angular-features-selection.mjs.map +1 -1
- package/fesm2022/toolbox-web-grid-angular-features-sorting.mjs +1 -1
- package/fesm2022/toolbox-web-grid-angular-features-sorting.mjs.map +1 -1
- package/fesm2022/toolbox-web-grid-angular-features-undo-redo.mjs +2 -6
- package/fesm2022/toolbox-web-grid-angular-features-undo-redo.mjs.map +1 -1
- package/fesm2022/toolbox-web-grid-angular.mjs +332 -129
- package/fesm2022/toolbox-web-grid-angular.mjs.map +1 -1
- package/package.json +1 -1
- package/types/toolbox-web-grid-angular.d.ts +138 -52
- package/types/toolbox-web-grid-angular.d.ts.map +1 -1
|
@@ -356,9 +356,9 @@ function getFormArrayContext(gridElement) {
|
|
|
356
356
|
class GridFormArray {
|
|
357
357
|
destroyRef = inject(DestroyRef);
|
|
358
358
|
elementRef = inject((ElementRef));
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
359
|
+
cellCommitUnsub = null;
|
|
360
|
+
cellCancelUnsub = null;
|
|
361
|
+
rowCommitUnsub = null;
|
|
362
362
|
touchListener = null;
|
|
363
363
|
valueChangesSubscription = null;
|
|
364
364
|
statusChangesSubscriptions = [];
|
|
@@ -416,25 +416,19 @@ class GridFormArray {
|
|
|
416
416
|
// Store the form context on the grid element for other directives to access
|
|
417
417
|
this.#storeFormContext(grid);
|
|
418
418
|
// Intercept cell-commit events to update the FormArray
|
|
419
|
-
this.
|
|
420
|
-
const detail = e.detail;
|
|
419
|
+
this.cellCommitUnsub = grid.on('cell-commit', (detail) => {
|
|
421
420
|
this.#handleCellCommit(detail);
|
|
422
|
-
};
|
|
423
|
-
grid.addEventListener('cell-commit', this.cellCommitListener);
|
|
421
|
+
});
|
|
424
422
|
// Intercept cell-cancel events to revert FormControls (grid mode Escape)
|
|
425
|
-
this.
|
|
426
|
-
const detail = e.detail;
|
|
423
|
+
this.cellCancelUnsub = grid.on('cell-cancel', (detail) => {
|
|
427
424
|
this.#handleCellCancel(detail);
|
|
428
|
-
};
|
|
429
|
-
grid.addEventListener('cell-cancel', this.cellCancelListener);
|
|
425
|
+
});
|
|
430
426
|
// Intercept row-commit events to prevent if FormGroup is invalid
|
|
431
|
-
this.
|
|
427
|
+
this.rowCommitUnsub = grid.on('row-commit', (detail, event) => {
|
|
432
428
|
if (!this.syncValidation())
|
|
433
429
|
return;
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
};
|
|
437
|
-
grid.addEventListener('row-commit', this.rowCommitListener);
|
|
430
|
+
this.#handleRowCommit(event, detail);
|
|
431
|
+
});
|
|
438
432
|
// Mark FormArray as touched on first interaction
|
|
439
433
|
this.touchListener = () => {
|
|
440
434
|
this.formArray().markAsTouched();
|
|
@@ -459,15 +453,9 @@ class GridFormArray {
|
|
|
459
453
|
const grid = this.elementRef.nativeElement;
|
|
460
454
|
if (!grid)
|
|
461
455
|
return;
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
if (this.cellCancelListener) {
|
|
466
|
-
grid.removeEventListener('cell-cancel', this.cellCancelListener);
|
|
467
|
-
}
|
|
468
|
-
if (this.rowCommitListener) {
|
|
469
|
-
grid.removeEventListener('row-commit', this.rowCommitListener);
|
|
470
|
-
}
|
|
456
|
+
this.cellCommitUnsub?.();
|
|
457
|
+
this.cellCancelUnsub?.();
|
|
458
|
+
this.rowCommitUnsub?.();
|
|
471
459
|
if (this.touchListener) {
|
|
472
460
|
grid.removeEventListener('click', this.touchListener);
|
|
473
461
|
}
|
|
@@ -1566,14 +1554,28 @@ class GridAdapter {
|
|
|
1566
1554
|
* @returns Processed GridConfig with actual renderer/editor functions
|
|
1567
1555
|
*/
|
|
1568
1556
|
processGridConfig(config) {
|
|
1569
|
-
|
|
1557
|
+
return this.processConfig(config);
|
|
1558
|
+
}
|
|
1559
|
+
/**
|
|
1560
|
+
* FrameworkAdapter.processConfig implementation.
|
|
1561
|
+
* Called automatically by the grid's `set gridConfig` setter.
|
|
1562
|
+
*/
|
|
1563
|
+
processConfig(config) {
|
|
1564
|
+
// Cast to Angular's extended GridConfig since the config may contain
|
|
1565
|
+
// Angular component classes as renderers/editors at runtime
|
|
1566
|
+
const angularConfig = config;
|
|
1567
|
+
const result = { ...angularConfig };
|
|
1570
1568
|
// Process columns
|
|
1571
|
-
if (
|
|
1572
|
-
result.columns =
|
|
1569
|
+
if (angularConfig.columns) {
|
|
1570
|
+
result.columns = angularConfig.columns.map((col) => this.processColumn(col));
|
|
1573
1571
|
}
|
|
1574
1572
|
// Process typeDefaults - convert Angular component classes to renderer/editor functions
|
|
1575
|
-
if (
|
|
1576
|
-
result.typeDefaults = this.processTypeDefaults(
|
|
1573
|
+
if (angularConfig.typeDefaults) {
|
|
1574
|
+
result.typeDefaults = this.processTypeDefaults(angularConfig.typeDefaults);
|
|
1575
|
+
}
|
|
1576
|
+
// Process loadingRenderer - convert Angular component class to function
|
|
1577
|
+
if (angularConfig.loadingRenderer && isComponentClass(angularConfig.loadingRenderer)) {
|
|
1578
|
+
result.loadingRenderer = this.createComponentLoadingRenderer(angularConfig.loadingRenderer);
|
|
1577
1579
|
}
|
|
1578
1580
|
return result;
|
|
1579
1581
|
}
|
|
@@ -1621,6 +1623,14 @@ class GridAdapter {
|
|
|
1621
1623
|
if (column.editor && isComponentClass(column.editor)) {
|
|
1622
1624
|
processed.editor = this.createComponentEditor(column.editor);
|
|
1623
1625
|
}
|
|
1626
|
+
// Convert headerRenderer component class to function
|
|
1627
|
+
if (column.headerRenderer && isComponentClass(column.headerRenderer)) {
|
|
1628
|
+
processed.headerRenderer = this.createComponentHeaderRenderer(column.headerRenderer);
|
|
1629
|
+
}
|
|
1630
|
+
// Convert headerLabelRenderer component class to function
|
|
1631
|
+
if (column.headerLabelRenderer && isComponentClass(column.headerLabelRenderer)) {
|
|
1632
|
+
processed.headerLabelRenderer = this.createComponentHeaderLabelRenderer(column.headerLabelRenderer);
|
|
1633
|
+
}
|
|
1624
1634
|
return processed;
|
|
1625
1635
|
}
|
|
1626
1636
|
/**
|
|
@@ -2094,6 +2104,222 @@ class GridAdapter {
|
|
|
2094
2104
|
return hostElement;
|
|
2095
2105
|
};
|
|
2096
2106
|
}
|
|
2107
|
+
/**
|
|
2108
|
+
* Creates a header renderer function from an Angular component class.
|
|
2109
|
+
* Mounts the component with full header context (column, value, sortState, etc.).
|
|
2110
|
+
* @internal
|
|
2111
|
+
*/
|
|
2112
|
+
createComponentHeaderRenderer(componentClass) {
|
|
2113
|
+
return (ctx) => {
|
|
2114
|
+
const hostElement = document.createElement('span');
|
|
2115
|
+
hostElement.style.display = 'contents';
|
|
2116
|
+
const componentRef = createComponent(componentClass, {
|
|
2117
|
+
environmentInjector: this.injector,
|
|
2118
|
+
hostElement,
|
|
2119
|
+
});
|
|
2120
|
+
this.setComponentInputs(componentRef, {
|
|
2121
|
+
column: ctx.column,
|
|
2122
|
+
value: ctx.value,
|
|
2123
|
+
sortState: ctx.sortState,
|
|
2124
|
+
filterActive: ctx.filterActive,
|
|
2125
|
+
renderSortIcon: ctx.renderSortIcon,
|
|
2126
|
+
renderFilterButton: ctx.renderFilterButton,
|
|
2127
|
+
});
|
|
2128
|
+
this.appRef.attachView(componentRef.hostView);
|
|
2129
|
+
this.componentRefs.push(componentRef);
|
|
2130
|
+
componentRef.changeDetectorRef.detectChanges();
|
|
2131
|
+
return hostElement;
|
|
2132
|
+
};
|
|
2133
|
+
}
|
|
2134
|
+
/**
|
|
2135
|
+
* Creates a header label renderer function from an Angular component class.
|
|
2136
|
+
* Mounts the component with label context (column, value).
|
|
2137
|
+
* @internal
|
|
2138
|
+
*/
|
|
2139
|
+
createComponentHeaderLabelRenderer(componentClass) {
|
|
2140
|
+
return (ctx) => {
|
|
2141
|
+
const hostElement = document.createElement('span');
|
|
2142
|
+
hostElement.style.display = 'contents';
|
|
2143
|
+
const componentRef = createComponent(componentClass, {
|
|
2144
|
+
environmentInjector: this.injector,
|
|
2145
|
+
hostElement,
|
|
2146
|
+
});
|
|
2147
|
+
this.setComponentInputs(componentRef, {
|
|
2148
|
+
column: ctx.column,
|
|
2149
|
+
value: ctx.value,
|
|
2150
|
+
});
|
|
2151
|
+
this.appRef.attachView(componentRef.hostView);
|
|
2152
|
+
this.componentRefs.push(componentRef);
|
|
2153
|
+
componentRef.changeDetectorRef.detectChanges();
|
|
2154
|
+
return hostElement;
|
|
2155
|
+
};
|
|
2156
|
+
}
|
|
2157
|
+
/**
|
|
2158
|
+
* Creates a group header renderer function from an Angular component class.
|
|
2159
|
+
*
|
|
2160
|
+
* The component should accept group header inputs (id, label, columns, firstIndex, isImplicit).
|
|
2161
|
+
* Returns the host element directly (groupHeaderRenderer returns an element, not void).
|
|
2162
|
+
* @internal
|
|
2163
|
+
*/
|
|
2164
|
+
createComponentGroupHeaderRenderer(componentClass) {
|
|
2165
|
+
return (params) => {
|
|
2166
|
+
const hostElement = document.createElement('span');
|
|
2167
|
+
hostElement.style.display = 'contents';
|
|
2168
|
+
const componentRef = createComponent(componentClass, {
|
|
2169
|
+
environmentInjector: this.injector,
|
|
2170
|
+
hostElement,
|
|
2171
|
+
});
|
|
2172
|
+
this.setComponentInputs(componentRef, {
|
|
2173
|
+
id: params.id,
|
|
2174
|
+
label: params.label,
|
|
2175
|
+
columns: params.columns,
|
|
2176
|
+
firstIndex: params.firstIndex,
|
|
2177
|
+
isImplicit: params.isImplicit,
|
|
2178
|
+
});
|
|
2179
|
+
this.appRef.attachView(componentRef.hostView);
|
|
2180
|
+
this.componentRefs.push(componentRef);
|
|
2181
|
+
componentRef.changeDetectorRef.detectChanges();
|
|
2182
|
+
return hostElement;
|
|
2183
|
+
};
|
|
2184
|
+
}
|
|
2185
|
+
/**
|
|
2186
|
+
* Processes a GroupingColumnsConfig, converting component class references
|
|
2187
|
+
* to actual renderer functions.
|
|
2188
|
+
*
|
|
2189
|
+
* @param config - Angular grouping columns configuration with possible component class references
|
|
2190
|
+
* @returns Processed GroupingColumnsConfig with actual renderer functions
|
|
2191
|
+
*/
|
|
2192
|
+
processGroupingColumnsConfig(config) {
|
|
2193
|
+
if (config.groupHeaderRenderer && isComponentClass(config.groupHeaderRenderer)) {
|
|
2194
|
+
return {
|
|
2195
|
+
...config,
|
|
2196
|
+
groupHeaderRenderer: this.createComponentGroupHeaderRenderer(config.groupHeaderRenderer),
|
|
2197
|
+
};
|
|
2198
|
+
}
|
|
2199
|
+
return config;
|
|
2200
|
+
}
|
|
2201
|
+
/**
|
|
2202
|
+
* Processes a GroupingRowsConfig, converting component class references
|
|
2203
|
+
* to actual renderer functions.
|
|
2204
|
+
*
|
|
2205
|
+
* @param config - Angular grouping rows configuration with possible component class references
|
|
2206
|
+
* @returns Processed GroupingRowsConfig with actual renderer functions
|
|
2207
|
+
*/
|
|
2208
|
+
processGroupingRowsConfig(config) {
|
|
2209
|
+
if (config.groupRowRenderer && isComponentClass(config.groupRowRenderer)) {
|
|
2210
|
+
return {
|
|
2211
|
+
...config,
|
|
2212
|
+
groupRowRenderer: this.createComponentGroupRowRenderer(config.groupRowRenderer),
|
|
2213
|
+
};
|
|
2214
|
+
}
|
|
2215
|
+
return config;
|
|
2216
|
+
}
|
|
2217
|
+
/**
|
|
2218
|
+
* Processes a PinnedRowsConfig, converting component class references
|
|
2219
|
+
* in `customPanels[].render` to actual renderer functions.
|
|
2220
|
+
*
|
|
2221
|
+
* @param config - Angular pinned rows configuration with possible component class references
|
|
2222
|
+
* @returns Processed PinnedRowsConfig with actual renderer functions
|
|
2223
|
+
*/
|
|
2224
|
+
processPinnedRowsConfig(config) {
|
|
2225
|
+
if (!Array.isArray(config.customPanels))
|
|
2226
|
+
return config;
|
|
2227
|
+
const hasComponentRender = config.customPanels.some((panel) => isComponentClass(panel.render));
|
|
2228
|
+
if (!hasComponentRender)
|
|
2229
|
+
return config;
|
|
2230
|
+
return {
|
|
2231
|
+
...config,
|
|
2232
|
+
customPanels: config.customPanels.map((panel) => {
|
|
2233
|
+
if (!isComponentClass(panel.render))
|
|
2234
|
+
return panel;
|
|
2235
|
+
return {
|
|
2236
|
+
...panel,
|
|
2237
|
+
render: this.createComponentPinnedRowsPanelRenderer(panel.render),
|
|
2238
|
+
};
|
|
2239
|
+
}),
|
|
2240
|
+
};
|
|
2241
|
+
}
|
|
2242
|
+
/**
|
|
2243
|
+
* Creates a pinned rows panel renderer function from an Angular component class.
|
|
2244
|
+
*
|
|
2245
|
+
* The component should accept inputs from PinnedRowsContext (totalRows, filteredRows,
|
|
2246
|
+
* selectedRows, columns, rows, grid).
|
|
2247
|
+
* @internal
|
|
2248
|
+
*/
|
|
2249
|
+
createComponentPinnedRowsPanelRenderer(componentClass) {
|
|
2250
|
+
return (ctx) => {
|
|
2251
|
+
const hostElement = document.createElement('span');
|
|
2252
|
+
hostElement.style.display = 'contents';
|
|
2253
|
+
const componentRef = createComponent(componentClass, {
|
|
2254
|
+
environmentInjector: this.injector,
|
|
2255
|
+
hostElement,
|
|
2256
|
+
});
|
|
2257
|
+
this.setComponentInputs(componentRef, {
|
|
2258
|
+
totalRows: ctx.totalRows,
|
|
2259
|
+
filteredRows: ctx.filteredRows,
|
|
2260
|
+
selectedRows: ctx.selectedRows,
|
|
2261
|
+
columns: ctx.columns,
|
|
2262
|
+
rows: ctx.rows,
|
|
2263
|
+
grid: ctx.grid,
|
|
2264
|
+
});
|
|
2265
|
+
this.appRef.attachView(componentRef.hostView);
|
|
2266
|
+
this.componentRefs.push(componentRef);
|
|
2267
|
+
componentRef.changeDetectorRef.detectChanges();
|
|
2268
|
+
return hostElement;
|
|
2269
|
+
};
|
|
2270
|
+
}
|
|
2271
|
+
/**
|
|
2272
|
+
* Creates a loading renderer function from an Angular component class.
|
|
2273
|
+
*
|
|
2274
|
+
* The component should accept a `size` input ('large' | 'small').
|
|
2275
|
+
* @internal
|
|
2276
|
+
*/
|
|
2277
|
+
createComponentLoadingRenderer(componentClass) {
|
|
2278
|
+
return (ctx) => {
|
|
2279
|
+
const hostElement = document.createElement('span');
|
|
2280
|
+
hostElement.style.display = 'contents';
|
|
2281
|
+
const componentRef = createComponent(componentClass, {
|
|
2282
|
+
environmentInjector: this.injector,
|
|
2283
|
+
hostElement,
|
|
2284
|
+
});
|
|
2285
|
+
this.setComponentInputs(componentRef, {
|
|
2286
|
+
size: ctx.size,
|
|
2287
|
+
});
|
|
2288
|
+
this.appRef.attachView(componentRef.hostView);
|
|
2289
|
+
this.componentRefs.push(componentRef);
|
|
2290
|
+
componentRef.changeDetectorRef.detectChanges();
|
|
2291
|
+
return hostElement;
|
|
2292
|
+
};
|
|
2293
|
+
}
|
|
2294
|
+
/**
|
|
2295
|
+
* Creates a group row renderer function from an Angular component class.
|
|
2296
|
+
*
|
|
2297
|
+
* The component should accept group row inputs (key, value, depth, rows, expanded, toggleExpand).
|
|
2298
|
+
* Returns the host element directly (groupRowRenderer returns an element, not void).
|
|
2299
|
+
* @internal
|
|
2300
|
+
*/
|
|
2301
|
+
createComponentGroupRowRenderer(componentClass) {
|
|
2302
|
+
return (params) => {
|
|
2303
|
+
const hostElement = document.createElement('span');
|
|
2304
|
+
hostElement.style.display = 'contents';
|
|
2305
|
+
const componentRef = createComponent(componentClass, {
|
|
2306
|
+
environmentInjector: this.injector,
|
|
2307
|
+
hostElement,
|
|
2308
|
+
});
|
|
2309
|
+
this.setComponentInputs(componentRef, {
|
|
2310
|
+
key: params.key,
|
|
2311
|
+
value: params.value,
|
|
2312
|
+
depth: params.depth,
|
|
2313
|
+
rows: params.rows,
|
|
2314
|
+
expanded: params.expanded,
|
|
2315
|
+
toggleExpand: params.toggleExpand,
|
|
2316
|
+
});
|
|
2317
|
+
this.appRef.attachView(componentRef.hostView);
|
|
2318
|
+
this.componentRefs.push(componentRef);
|
|
2319
|
+
componentRef.changeDetectorRef.detectChanges();
|
|
2320
|
+
return hostElement;
|
|
2321
|
+
};
|
|
2322
|
+
}
|
|
2097
2323
|
/**
|
|
2098
2324
|
* Creates a filter panel renderer function from an Angular component class.
|
|
2099
2325
|
*
|
|
@@ -2202,7 +2428,7 @@ class GridAdapter {
|
|
|
2202
2428
|
}
|
|
2203
2429
|
}
|
|
2204
2430
|
/**
|
|
2205
|
-
* @deprecated Use `GridAdapter` instead. This alias will be removed in
|
|
2431
|
+
* @deprecated Use `GridAdapter` instead. This alias will be removed in v2.
|
|
2206
2432
|
* @see {@link GridAdapter}
|
|
2207
2433
|
*/
|
|
2208
2434
|
const AngularGridAdapter = GridAdapter;
|
|
@@ -2580,7 +2806,7 @@ function injectGrid() {
|
|
|
2580
2806
|
* ## Usage
|
|
2581
2807
|
*
|
|
2582
2808
|
* ```typescript
|
|
2583
|
-
* import { Component } from '@angular/core';
|
|
2809
|
+
* import { Component, viewChild, ElementRef } from '@angular/core';
|
|
2584
2810
|
* import { BaseFilterPanel } from '@toolbox-web/grid-angular';
|
|
2585
2811
|
*
|
|
2586
2812
|
* @Component({
|
|
@@ -2592,10 +2818,10 @@ function injectGrid() {
|
|
|
2592
2818
|
* `
|
|
2593
2819
|
* })
|
|
2594
2820
|
* export class TextFilterComponent extends BaseFilterPanel {
|
|
2595
|
-
*
|
|
2821
|
+
* input = viewChild.required<ElementRef<HTMLInputElement>>('input');
|
|
2596
2822
|
*
|
|
2597
2823
|
* applyFilter(): void {
|
|
2598
|
-
* this.params().applyTextFilter('contains', this.input.nativeElement.value);
|
|
2824
|
+
* this.params().applyTextFilter('contains', this.input().nativeElement.value);
|
|
2599
2825
|
* }
|
|
2600
2826
|
* }
|
|
2601
2827
|
* ```
|
|
@@ -2832,13 +3058,21 @@ class BaseGridEditor {
|
|
|
2832
3058
|
const grid = this.elementRef.nativeElement.closest('tbw-grid');
|
|
2833
3059
|
if (!grid)
|
|
2834
3060
|
return;
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
|
|
3061
|
+
let unsubBefore;
|
|
3062
|
+
let unsubClose;
|
|
3063
|
+
unsubBefore = grid.on('before-edit-close', () => {
|
|
3064
|
+
this.onBeforeEditClose();
|
|
3065
|
+
unsubBefore?.();
|
|
3066
|
+
unsubBefore = undefined;
|
|
3067
|
+
});
|
|
3068
|
+
unsubClose = grid.on('edit-close', () => {
|
|
3069
|
+
this.onEditClose();
|
|
3070
|
+
unsubClose?.();
|
|
3071
|
+
unsubClose = undefined;
|
|
3072
|
+
});
|
|
2839
3073
|
this._editCloseCleanup = () => {
|
|
2840
|
-
|
|
2841
|
-
|
|
3074
|
+
unsubBefore?.();
|
|
3075
|
+
unsubClose?.();
|
|
2842
3076
|
};
|
|
2843
3077
|
}
|
|
2844
3078
|
// ============================================================================
|
|
@@ -3188,7 +3422,7 @@ let anchorCounter = 0;
|
|
|
3188
3422
|
* ## Usage
|
|
3189
3423
|
*
|
|
3190
3424
|
* ```typescript
|
|
3191
|
-
* import { Component,
|
|
3425
|
+
* import { Component, viewChild, ElementRef, effect } from '@angular/core';
|
|
3192
3426
|
* import { BaseOverlayEditor } from '@toolbox-web/grid-angular';
|
|
3193
3427
|
*
|
|
3194
3428
|
* @Component({
|
|
@@ -3211,18 +3445,22 @@ let anchorCounter = 0;
|
|
|
3211
3445
|
* `
|
|
3212
3446
|
* })
|
|
3213
3447
|
* export class DateEditorComponent extends BaseOverlayEditor<MyRow, string> {
|
|
3214
|
-
*
|
|
3215
|
-
*
|
|
3448
|
+
* panelRef = viewChild.required<ElementRef<HTMLElement>>('panel');
|
|
3449
|
+
* inputRef = viewChild.required<ElementRef<HTMLInputElement>>('inlineInput');
|
|
3216
3450
|
*
|
|
3217
3451
|
* protected override overlayPosition = 'below' as const;
|
|
3218
3452
|
*
|
|
3219
|
-
*
|
|
3220
|
-
*
|
|
3221
|
-
*
|
|
3453
|
+
* constructor() {
|
|
3454
|
+
* super();
|
|
3455
|
+
* effect(() => {
|
|
3456
|
+
* const panel = this.panelRef().nativeElement;
|
|
3457
|
+
* this.initOverlay(panel);
|
|
3458
|
+
* if (this.isCellFocused()) this.showOverlay();
|
|
3459
|
+
* });
|
|
3222
3460
|
* }
|
|
3223
3461
|
*
|
|
3224
3462
|
* protected getInlineInput(): HTMLInputElement | null {
|
|
3225
|
-
* return this.inputRef?.nativeElement ?? null;
|
|
3463
|
+
* return this.inputRef()?.nativeElement ?? null;
|
|
3226
3464
|
* }
|
|
3227
3465
|
*
|
|
3228
3466
|
* protected onOverlayOutsideClick(): void {
|
|
@@ -3283,7 +3521,7 @@ class BaseOverlayEditor extends BaseGridEditor {
|
|
|
3283
3521
|
/**
|
|
3284
3522
|
* Initialise the overlay with the panel element.
|
|
3285
3523
|
*
|
|
3286
|
-
* Call this in `
|
|
3524
|
+
* Call this in an `effect()` or `afterNextRender()` with your `viewChild` panel reference.
|
|
3287
3525
|
* The panel is moved to `<body>` and hidden until {@link showOverlay} is called.
|
|
3288
3526
|
*
|
|
3289
3527
|
* @param panel - The overlay panel DOM element
|
|
@@ -3801,9 +4039,9 @@ class GridLazyForm {
|
|
|
3801
4039
|
rowIndexMap = new Map();
|
|
3802
4040
|
// Track which row is currently being edited
|
|
3803
4041
|
editingRowIndex = null;
|
|
3804
|
-
|
|
3805
|
-
|
|
3806
|
-
|
|
4042
|
+
cellCommitUnsub = null;
|
|
4043
|
+
rowCommitUnsub = null;
|
|
4044
|
+
rowsChangeUnsub = null;
|
|
3807
4045
|
/**
|
|
3808
4046
|
* Factory function to create a FormGroup for a row.
|
|
3809
4047
|
* Called lazily when the row first enters edit mode.
|
|
@@ -3850,22 +4088,17 @@ class GridLazyForm {
|
|
|
3850
4088
|
// Store the form context for AngularGridAdapter to access
|
|
3851
4089
|
this.#storeFormContext(grid);
|
|
3852
4090
|
// Listen for cell-commit to update FormControl and sync validation
|
|
3853
|
-
this.
|
|
3854
|
-
const detail = e.detail;
|
|
4091
|
+
this.cellCommitUnsub = grid.on('cell-commit', (detail) => {
|
|
3855
4092
|
this.#handleCellCommit(detail);
|
|
3856
|
-
};
|
|
3857
|
-
grid.addEventListener('cell-commit', this.cellCommitListener);
|
|
4093
|
+
});
|
|
3858
4094
|
// Listen for row-commit to sync FormGroup values back to row and cleanup
|
|
3859
|
-
this.
|
|
3860
|
-
|
|
3861
|
-
|
|
3862
|
-
};
|
|
3863
|
-
grid.addEventListener('row-commit', this.rowCommitListener);
|
|
4095
|
+
this.rowCommitUnsub = grid.on('row-commit', (detail, event) => {
|
|
4096
|
+
this.#handleRowCommit(event, detail);
|
|
4097
|
+
});
|
|
3864
4098
|
// Listen for rows-change to update row index mappings
|
|
3865
|
-
this.
|
|
4099
|
+
this.rowsChangeUnsub = grid.on('rows-change', () => {
|
|
3866
4100
|
this.#updateRowIndexMap();
|
|
3867
|
-
};
|
|
3868
|
-
grid.addEventListener('rows-change', this.rowsChangeListener);
|
|
4101
|
+
});
|
|
3869
4102
|
// Initial row index mapping
|
|
3870
4103
|
this.#updateRowIndexMap();
|
|
3871
4104
|
}
|
|
@@ -3873,15 +4106,9 @@ class GridLazyForm {
|
|
|
3873
4106
|
const grid = this.elementRef.nativeElement;
|
|
3874
4107
|
if (!grid)
|
|
3875
4108
|
return;
|
|
3876
|
-
|
|
3877
|
-
|
|
3878
|
-
|
|
3879
|
-
if (this.rowCommitListener) {
|
|
3880
|
-
grid.removeEventListener('row-commit', this.rowCommitListener);
|
|
3881
|
-
}
|
|
3882
|
-
if (this.rowsChangeListener) {
|
|
3883
|
-
grid.removeEventListener('rows-change', this.rowsChangeListener);
|
|
3884
|
-
}
|
|
4109
|
+
this.cellCommitUnsub?.();
|
|
4110
|
+
this.rowCommitUnsub?.();
|
|
4111
|
+
this.rowsChangeUnsub?.();
|
|
3885
4112
|
this.#clearFormContext(grid);
|
|
3886
4113
|
this.formGroupCache.clear();
|
|
3887
4114
|
this.rowIndexMap.clear();
|
|
@@ -4565,7 +4792,7 @@ class Grid {
|
|
|
4565
4792
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
4566
4793
|
gridConfig = input(...(ngDevMode ? [undefined, { debugName: "gridConfig" }] : []));
|
|
4567
4794
|
/**
|
|
4568
|
-
* @deprecated Use `gridConfig` instead. This input will be removed in
|
|
4795
|
+
* @deprecated Use `gridConfig` instead. This input will be removed in v2.
|
|
4569
4796
|
*
|
|
4570
4797
|
* The `angularConfig` name was inconsistent with React and Vue adapters, which both use `gridConfig`.
|
|
4571
4798
|
* The `gridConfig` input now accepts `GridConfig` directly.
|
|
@@ -4678,7 +4905,7 @@ class Grid {
|
|
|
4678
4905
|
*/
|
|
4679
4906
|
multiSort = input(...(ngDevMode ? [undefined, { debugName: "multiSort" }] : []));
|
|
4680
4907
|
/**
|
|
4681
|
-
* @deprecated Use `[multiSort]` instead. Will be removed in
|
|
4908
|
+
* @deprecated Use `[multiSort]` instead. Will be removed in v2.
|
|
4682
4909
|
*
|
|
4683
4910
|
* Enable column sorting. This is an alias for `[multiSort]`.
|
|
4684
4911
|
*
|
|
@@ -4719,7 +4946,7 @@ class Grid {
|
|
|
4719
4946
|
*/
|
|
4720
4947
|
reorderColumns = input(...(ngDevMode ? [undefined, { debugName: "reorderColumns" }] : []));
|
|
4721
4948
|
/**
|
|
4722
|
-
* @deprecated Use `reorderColumns` instead. Will be removed in v2.
|
|
4949
|
+
* @deprecated Use `reorderColumns` instead. Will be removed in v2.
|
|
4723
4950
|
*/
|
|
4724
4951
|
reorder = input(...(ngDevMode ? [undefined, { debugName: "reorder" }] : []));
|
|
4725
4952
|
/**
|
|
@@ -4765,7 +4992,7 @@ class Grid {
|
|
|
4765
4992
|
*
|
|
4766
4993
|
* @example
|
|
4767
4994
|
* ```html
|
|
4768
|
-
* <tbw-grid [groupingColumns]="
|
|
4995
|
+
* <tbw-grid [groupingColumns]="true" />
|
|
4769
4996
|
* ```
|
|
4770
4997
|
*/
|
|
4771
4998
|
groupingColumns = input(...(ngDevMode ? [undefined, { debugName: "groupingColumns" }] : []));
|
|
@@ -5220,54 +5447,13 @@ class Grid {
|
|
|
5220
5447
|
this.adapter = new GridAdapter(this.injector, this.appRef, this.viewContainerRef);
|
|
5221
5448
|
DataGridElement.registerAdapter(this.adapter);
|
|
5222
5449
|
const grid = this.elementRef.nativeElement;
|
|
5223
|
-
//
|
|
5224
|
-
//
|
|
5225
|
-
//
|
|
5226
|
-
// This converts Angular component classes to vanilla renderer/editor functions
|
|
5227
|
-
// before the grid's internal ConfigManager ever sees them.
|
|
5228
|
-
this.interceptElementGridConfig(grid);
|
|
5229
|
-
// Wire up all event listeners based on eventOutputMap
|
|
5230
|
-
this.setupEventListeners(grid);
|
|
5231
|
-
// Register adapter on the grid element so MasterDetailPlugin can use it
|
|
5232
|
-
// via the __frameworkAdapter hook during attach()
|
|
5450
|
+
// Register adapter on the grid element so processConfig is called
|
|
5451
|
+
// automatically by the grid's set gridConfig setter, and so
|
|
5452
|
+
// MasterDetailPlugin can use it via the __frameworkAdapter hook during attach()
|
|
5233
5453
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
5234
5454
|
grid.__frameworkAdapter = this.adapter;
|
|
5235
|
-
|
|
5236
|
-
|
|
5237
|
-
* Overrides the element's `gridConfig` property so every write is processed
|
|
5238
|
-
* through the adapter before reaching the grid core.
|
|
5239
|
-
*
|
|
5240
|
-
* Why: Angular with `CUSTOM_ELEMENTS_SCHEMA` may bind `[gridConfig]` to both
|
|
5241
|
-
* the directive input AND the native custom-element property. The directive
|
|
5242
|
-
* input feeds an effect that merges feature plugins, but the native property
|
|
5243
|
-
* receives the raw config (with component classes as editors/renderers).
|
|
5244
|
-
* Intercepting the setter guarantees only processed configs reach the grid.
|
|
5245
|
-
*/
|
|
5246
|
-
interceptElementGridConfig(grid) {
|
|
5247
|
-
const proto = Object.getPrototypeOf(grid);
|
|
5248
|
-
const desc = Object.getOwnPropertyDescriptor(proto, 'gridConfig');
|
|
5249
|
-
if (!desc?.set || !desc?.get)
|
|
5250
|
-
return;
|
|
5251
|
-
const originalSet = desc.set;
|
|
5252
|
-
const originalGet = desc.get;
|
|
5253
|
-
const adapter = this.adapter;
|
|
5254
|
-
// Instance-level override (does not affect the prototype or other grid elements)
|
|
5255
|
-
Object.defineProperty(grid, 'gridConfig', {
|
|
5256
|
-
get() {
|
|
5257
|
-
return originalGet.call(this);
|
|
5258
|
-
},
|
|
5259
|
-
set(value) {
|
|
5260
|
-
if (value && adapter) {
|
|
5261
|
-
// processGridConfig is idempotent: already-processed functions pass
|
|
5262
|
-
// through isComponentClass unchanged, so double-processing is safe.
|
|
5263
|
-
originalSet.call(this, adapter.processGridConfig(value));
|
|
5264
|
-
}
|
|
5265
|
-
else {
|
|
5266
|
-
originalSet.call(this, value);
|
|
5267
|
-
}
|
|
5268
|
-
},
|
|
5269
|
-
configurable: true,
|
|
5270
|
-
});
|
|
5455
|
+
// Wire up all event listeners based on eventOutputMap
|
|
5456
|
+
this.setupEventListeners(grid);
|
|
5271
5457
|
}
|
|
5272
5458
|
/**
|
|
5273
5459
|
* Sets up event listeners for all outputs using the eventOutputMap.
|
|
@@ -5311,11 +5497,32 @@ class Grid {
|
|
|
5311
5497
|
addPlugin('reorderColumns', this.reorderColumns() ?? this.reorder());
|
|
5312
5498
|
addPlugin('visibility', this.visibility());
|
|
5313
5499
|
addPlugin('pinnedColumns', this.pinnedColumns());
|
|
5314
|
-
|
|
5500
|
+
// Pre-process groupingColumns config to bridge Angular component classes
|
|
5501
|
+
const gcConfig = this.groupingColumns();
|
|
5502
|
+
if (gcConfig && typeof gcConfig === 'object' && this.adapter) {
|
|
5503
|
+
addPlugin('groupingColumns', this.adapter.processGroupingColumnsConfig(gcConfig));
|
|
5504
|
+
}
|
|
5505
|
+
else {
|
|
5506
|
+
addPlugin('groupingColumns', gcConfig);
|
|
5507
|
+
}
|
|
5315
5508
|
addPlugin('columnVirtualization', this.columnVirtualization());
|
|
5316
5509
|
addPlugin('reorderRows', this.reorderRows() ?? this.rowReorder());
|
|
5317
|
-
|
|
5318
|
-
|
|
5510
|
+
// Pre-process groupingRows config to bridge Angular component classes
|
|
5511
|
+
const grConfig = this.groupingRows();
|
|
5512
|
+
if (grConfig && typeof grConfig === 'object' && this.adapter) {
|
|
5513
|
+
addPlugin('groupingRows', this.adapter.processGroupingRowsConfig(grConfig));
|
|
5514
|
+
}
|
|
5515
|
+
else {
|
|
5516
|
+
addPlugin('groupingRows', grConfig);
|
|
5517
|
+
}
|
|
5518
|
+
// Pre-process pinnedRows config to bridge Angular component classes in customPanels
|
|
5519
|
+
const prConfig = this.pinnedRows();
|
|
5520
|
+
if (prConfig && typeof prConfig === 'object' && this.adapter) {
|
|
5521
|
+
addPlugin('pinnedRows', this.adapter.processPinnedRowsConfig(prConfig));
|
|
5522
|
+
}
|
|
5523
|
+
else {
|
|
5524
|
+
addPlugin('pinnedRows', prConfig);
|
|
5525
|
+
}
|
|
5319
5526
|
addPlugin('tree', this.tree());
|
|
5320
5527
|
addPlugin('masterDetail', this.masterDetail());
|
|
5321
5528
|
addPlugin('responsive', this.responsive());
|
|
@@ -5442,10 +5649,6 @@ class Grid {
|
|
|
5442
5649
|
}
|
|
5443
5650
|
ngOnDestroy() {
|
|
5444
5651
|
const grid = this.elementRef.nativeElement;
|
|
5445
|
-
// Remove the gridConfig interceptor (restores prototype behavior)
|
|
5446
|
-
if (grid) {
|
|
5447
|
-
delete grid['gridConfig'];
|
|
5448
|
-
}
|
|
5449
5652
|
// Cleanup all event listeners
|
|
5450
5653
|
if (grid) {
|
|
5451
5654
|
for (const [eventName, listener] of this.eventListeners) {
|