@toolbox-web/grid-angular 0.10.0 → 0.11.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 +28 -22
- package/fesm2022/toolbox-web-grid-angular-features-export.mjs +123 -4
- package/fesm2022/toolbox-web-grid-angular-features-export.mjs.map +1 -1
- package/fesm2022/toolbox-web-grid-angular-features-filtering.mjs +123 -1
- package/fesm2022/toolbox-web-grid-angular-features-filtering.mjs.map +1 -1
- package/fesm2022/toolbox-web-grid-angular-features-print.mjs +89 -1
- package/fesm2022/toolbox-web-grid-angular-features-print.mjs.map +1 -1
- package/fesm2022/toolbox-web-grid-angular-features-selection.mjs +133 -1
- package/fesm2022/toolbox-web-grid-angular-features-selection.mjs.map +1 -1
- package/fesm2022/toolbox-web-grid-angular-features-undo-redo.mjs +106 -1
- package/fesm2022/toolbox-web-grid-angular-features-undo-redo.mjs.map +1 -1
- package/fesm2022/toolbox-web-grid-angular.mjs +649 -64
- 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 +113 -1
- package/types/toolbox-web-grid-angular-features-export.d.ts.map +1 -1
- package/types/toolbox-web-grid-angular-features-filtering.d.ts +120 -1
- package/types/toolbox-web-grid-angular-features-filtering.d.ts.map +1 -1
- package/types/toolbox-web-grid-angular-features-print.d.ts +91 -1
- package/types/toolbox-web-grid-angular-features-print.d.ts.map +1 -1
- package/types/toolbox-web-grid-angular-features-selection.d.ts +114 -1
- package/types/toolbox-web-grid-angular-features-selection.d.ts.map +1 -1
- package/types/toolbox-web-grid-angular-features-undo-redo.d.ts +107 -1
- package/types/toolbox-web-grid-angular-features-undo-redo.d.ts.map +1 -1
- package/types/toolbox-web-grid-angular.d.ts +383 -115
- package/types/toolbox-web-grid-angular.d.ts.map +1 -1
|
@@ -2,9 +2,11 @@ import * as i0 from '@angular/core';
|
|
|
2
2
|
import { inject, ElementRef, contentChild, TemplateRef, effect, Directive, input, DestroyRef, InjectionToken, Injectable, makeEnvironmentProviders, EventEmitter, createComponent, signal, afterNextRender, computed, output, EnvironmentInjector, ApplicationRef, ViewContainerRef } from '@angular/core';
|
|
3
3
|
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
4
4
|
import { FormGroup } from '@angular/forms';
|
|
5
|
-
import { startWith } from 'rxjs/operators';
|
|
5
|
+
import { startWith, debounceTime } from 'rxjs/operators';
|
|
6
6
|
import { DataGridElement } from '@toolbox-web/grid';
|
|
7
7
|
|
|
8
|
+
// #endregion
|
|
9
|
+
// #region Utilities
|
|
8
10
|
/**
|
|
9
11
|
* Type guard to check if a value is an Angular component class.
|
|
10
12
|
*
|
|
@@ -28,6 +30,7 @@ function isComponentClass(value) {
|
|
|
28
30
|
const fnString = Function.prototype.toString.call(value);
|
|
29
31
|
return fnString.startsWith('class ') || fnString.startsWith('class{');
|
|
30
32
|
}
|
|
33
|
+
// #endregion
|
|
31
34
|
|
|
32
35
|
// Global registry mapping DOM elements to their templates
|
|
33
36
|
const editorTemplateRegistry = new Map();
|
|
@@ -281,13 +284,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImpor
|
|
|
281
284
|
}], propDecorators: { showExpandColumn: [{ type: i0.Input, args: [{ isSignal: true, alias: "showExpandColumn", required: false }] }], animation: [{ type: i0.Input, args: [{ isSignal: true, alias: "animation", required: false }] }], template: [{ type: i0.ContentChild, args: [i0.forwardRef(() => TemplateRef), { isSignal: true }] }] } });
|
|
282
285
|
|
|
283
286
|
// Symbol for storing form context on the grid element
|
|
284
|
-
const FORM_ARRAY_CONTEXT = Symbol('formArrayContext');
|
|
287
|
+
const FORM_ARRAY_CONTEXT$1 = Symbol('formArrayContext');
|
|
285
288
|
/**
|
|
286
289
|
* Gets the FormArrayContext from a grid element, if present.
|
|
287
290
|
* @internal
|
|
288
291
|
*/
|
|
289
292
|
function getFormArrayContext(gridElement) {
|
|
290
|
-
return gridElement[FORM_ARRAY_CONTEXT];
|
|
293
|
+
return gridElement[FORM_ARRAY_CONTEXT$1];
|
|
291
294
|
}
|
|
292
295
|
/**
|
|
293
296
|
* Directive that binds a FormArray directly to the grid.
|
|
@@ -347,6 +350,7 @@ class GridFormArray {
|
|
|
347
350
|
rowCommitListener = null;
|
|
348
351
|
touchListener = null;
|
|
349
352
|
valueChangesSubscription = null;
|
|
353
|
+
statusChangesSubscriptions = [];
|
|
350
354
|
/**
|
|
351
355
|
* The FormArray to bind to the grid.
|
|
352
356
|
*/
|
|
@@ -358,6 +362,7 @@ class GridFormArray {
|
|
|
358
362
|
* - After a cell commit, if the FormControl is invalid, the cell is marked with `setInvalid()`
|
|
359
363
|
* - When a FormControl becomes valid, `clearInvalid()` is called
|
|
360
364
|
* - On `row-commit`, if the row's FormGroup has invalid controls, the commit is prevented
|
|
365
|
+
* - In grid mode: validation state is synced on initial render and updated reactively
|
|
361
366
|
*
|
|
362
367
|
* @default true
|
|
363
368
|
*/
|
|
@@ -412,6 +417,15 @@ class GridFormArray {
|
|
|
412
417
|
}
|
|
413
418
|
};
|
|
414
419
|
grid.addEventListener('click', this.touchListener);
|
|
420
|
+
// If in grid mode with syncValidation, set up reactive validation
|
|
421
|
+
// Wait for grid to be ready so we can detect the editing mode
|
|
422
|
+
grid.ready?.().then(() => {
|
|
423
|
+
if (this.syncValidation() && this.#isGridMode()) {
|
|
424
|
+
this.#setupReactiveValidation();
|
|
425
|
+
// Sync initial validation state for all existing controls
|
|
426
|
+
this.#syncAllValidationState();
|
|
427
|
+
}
|
|
428
|
+
});
|
|
415
429
|
}
|
|
416
430
|
ngOnDestroy() {
|
|
417
431
|
const grid = this.elementRef.nativeElement;
|
|
@@ -429,8 +443,93 @@ class GridFormArray {
|
|
|
429
443
|
if (this.valueChangesSubscription) {
|
|
430
444
|
this.valueChangesSubscription.unsubscribe();
|
|
431
445
|
}
|
|
446
|
+
// Clean up status change subscriptions (grid mode)
|
|
447
|
+
this.statusChangesSubscriptions.forEach((sub) => sub.unsubscribe());
|
|
448
|
+
this.statusChangesSubscriptions = [];
|
|
432
449
|
this.#clearFormContext(grid);
|
|
433
450
|
}
|
|
451
|
+
/**
|
|
452
|
+
* Checks if the EditingPlugin is in 'grid' mode.
|
|
453
|
+
*/
|
|
454
|
+
#isGridMode() {
|
|
455
|
+
const grid = this.elementRef.nativeElement;
|
|
456
|
+
if (!grid)
|
|
457
|
+
return false;
|
|
458
|
+
const editingPlugin = grid.getPluginByName?.('editing');
|
|
459
|
+
return editingPlugin?.config?.mode === 'grid';
|
|
460
|
+
}
|
|
461
|
+
/**
|
|
462
|
+
* Sets up reactive validation syncing for grid mode.
|
|
463
|
+
* Subscribes to statusChanges on all FormControls to update validation state in real-time.
|
|
464
|
+
*/
|
|
465
|
+
#setupReactiveValidation() {
|
|
466
|
+
const formArray = this.formArray();
|
|
467
|
+
const grid = this.elementRef.nativeElement;
|
|
468
|
+
if (!grid)
|
|
469
|
+
return;
|
|
470
|
+
// Clean up any existing subscriptions
|
|
471
|
+
this.statusChangesSubscriptions.forEach((sub) => sub.unsubscribe());
|
|
472
|
+
this.statusChangesSubscriptions = [];
|
|
473
|
+
// Subscribe to each FormGroup's statusChanges
|
|
474
|
+
for (let rowIndex = 0; rowIndex < formArray.length; rowIndex++) {
|
|
475
|
+
const rowFormGroup = this.#getRowFormGroup(rowIndex);
|
|
476
|
+
if (!rowFormGroup)
|
|
477
|
+
continue;
|
|
478
|
+
// Get the row ID for this row
|
|
479
|
+
const rowId = this.#getRowId(grid, rowIndex);
|
|
480
|
+
if (!rowId)
|
|
481
|
+
continue;
|
|
482
|
+
// Subscribe to status changes for each control in the FormGroup
|
|
483
|
+
Object.keys(rowFormGroup.controls).forEach((field) => {
|
|
484
|
+
const control = rowFormGroup.get(field);
|
|
485
|
+
if (!control)
|
|
486
|
+
return;
|
|
487
|
+
const sub = control.statusChanges.pipe(debounceTime(50), takeUntilDestroyed(this.destroyRef)).subscribe(() => {
|
|
488
|
+
this.#syncControlValidationToGrid(rowId, field, control);
|
|
489
|
+
});
|
|
490
|
+
this.statusChangesSubscriptions.push(sub);
|
|
491
|
+
});
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
/**
|
|
495
|
+
* Syncs validation state for all controls in the FormArray.
|
|
496
|
+
* Called once on init in grid mode to show pre-existing validation errors.
|
|
497
|
+
*/
|
|
498
|
+
#syncAllValidationState() {
|
|
499
|
+
const formArray = this.formArray();
|
|
500
|
+
const grid = this.elementRef.nativeElement;
|
|
501
|
+
if (!grid)
|
|
502
|
+
return;
|
|
503
|
+
for (let rowIndex = 0; rowIndex < formArray.length; rowIndex++) {
|
|
504
|
+
const rowFormGroup = this.#getRowFormGroup(rowIndex);
|
|
505
|
+
if (!rowFormGroup)
|
|
506
|
+
continue;
|
|
507
|
+
const rowId = this.#getRowId(grid, rowIndex);
|
|
508
|
+
if (!rowId)
|
|
509
|
+
continue;
|
|
510
|
+
Object.keys(rowFormGroup.controls).forEach((field) => {
|
|
511
|
+
const control = rowFormGroup.get(field);
|
|
512
|
+
if (control) {
|
|
513
|
+
this.#syncControlValidationToGrid(rowId, field, control);
|
|
514
|
+
}
|
|
515
|
+
});
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
/**
|
|
519
|
+
* Gets the row ID for a given row index using the grid's getRowId method.
|
|
520
|
+
*/
|
|
521
|
+
#getRowId(grid, rowIndex) {
|
|
522
|
+
try {
|
|
523
|
+
const rows = grid.rows;
|
|
524
|
+
const row = rows?.[rowIndex];
|
|
525
|
+
if (!row)
|
|
526
|
+
return undefined;
|
|
527
|
+
return grid.getRowId?.(row);
|
|
528
|
+
}
|
|
529
|
+
catch {
|
|
530
|
+
return undefined;
|
|
531
|
+
}
|
|
532
|
+
}
|
|
434
533
|
/**
|
|
435
534
|
* Checks if the FormArray contains FormGroups.
|
|
436
535
|
*/
|
|
@@ -518,13 +617,13 @@ class GridFormArray {
|
|
|
518
617
|
return hasErrors ? errors : null;
|
|
519
618
|
},
|
|
520
619
|
};
|
|
521
|
-
grid[FORM_ARRAY_CONTEXT] = context;
|
|
620
|
+
grid[FORM_ARRAY_CONTEXT$1] = context;
|
|
522
621
|
}
|
|
523
622
|
/**
|
|
524
623
|
* Clears the FormArrayContext from the grid element.
|
|
525
624
|
*/
|
|
526
625
|
#clearFormContext(grid) {
|
|
527
|
-
delete grid[FORM_ARRAY_CONTEXT];
|
|
626
|
+
delete grid[FORM_ARRAY_CONTEXT$1];
|
|
528
627
|
}
|
|
529
628
|
/**
|
|
530
629
|
* Handles cell-commit events by updating the FormControl in the FormGroup.
|
|
@@ -1185,7 +1284,7 @@ function getAnyEditorTemplate(element) {
|
|
|
1185
1284
|
* ```typescript
|
|
1186
1285
|
* import { Component, inject, EnvironmentInjector, ApplicationRef, ViewContainerRef } from '@angular/core';
|
|
1187
1286
|
* import { GridElement } from '@toolbox-web/grid';
|
|
1188
|
-
* import {
|
|
1287
|
+
* import { GridAdapter } from '@toolbox-web/grid-angular';
|
|
1189
1288
|
*
|
|
1190
1289
|
* @Component({
|
|
1191
1290
|
* selector: 'app-root',
|
|
@@ -1196,7 +1295,7 @@ function getAnyEditorTemplate(element) {
|
|
|
1196
1295
|
* const injector = inject(EnvironmentInjector);
|
|
1197
1296
|
* const appRef = inject(ApplicationRef);
|
|
1198
1297
|
* const viewContainerRef = inject(ViewContainerRef);
|
|
1199
|
-
* GridElement.registerAdapter(new
|
|
1298
|
+
* GridElement.registerAdapter(new GridAdapter(injector, appRef, viewContainerRef));
|
|
1200
1299
|
* }
|
|
1201
1300
|
* }
|
|
1202
1301
|
* ```
|
|
@@ -1235,7 +1334,7 @@ function getAnyEditorTemplate(element) {
|
|
|
1235
1334
|
* - Handles editor callbacks (onCommit/onCancel)
|
|
1236
1335
|
* - Manages view lifecycle and change detection
|
|
1237
1336
|
*/
|
|
1238
|
-
class
|
|
1337
|
+
class GridAdapter {
|
|
1239
1338
|
injector;
|
|
1240
1339
|
appRef;
|
|
1241
1340
|
viewContainerRef;
|
|
@@ -1265,9 +1364,9 @@ class AngularGridAdapter {
|
|
|
1265
1364
|
*
|
|
1266
1365
|
* @example
|
|
1267
1366
|
* ```typescript
|
|
1268
|
-
* import {
|
|
1367
|
+
* import { GridAdapter, type GridConfig } from '@toolbox-web/grid-angular';
|
|
1269
1368
|
*
|
|
1270
|
-
* const config:
|
|
1369
|
+
* const config: GridConfig<Employee> = {
|
|
1271
1370
|
* columns: [
|
|
1272
1371
|
* { field: 'status', renderer: StatusBadgeComponent, editor: StatusEditorComponent },
|
|
1273
1372
|
* ],
|
|
@@ -1275,7 +1374,7 @@ class AngularGridAdapter {
|
|
|
1275
1374
|
*
|
|
1276
1375
|
* // In component
|
|
1277
1376
|
* constructor() {
|
|
1278
|
-
* const adapter = inject(
|
|
1377
|
+
* const adapter = inject(GridAdapter); // or create new instance
|
|
1279
1378
|
* this.processedConfig = adapter.processGridConfig(config);
|
|
1280
1379
|
* }
|
|
1281
1380
|
* ```
|
|
@@ -1746,6 +1845,11 @@ class AngularGridAdapter {
|
|
|
1746
1845
|
this.componentRefs = [];
|
|
1747
1846
|
}
|
|
1748
1847
|
}
|
|
1848
|
+
/**
|
|
1849
|
+
* @deprecated Use `GridAdapter` instead. This alias will be removed in a future version.
|
|
1850
|
+
* @see {@link GridAdapter}
|
|
1851
|
+
*/
|
|
1852
|
+
const AngularGridAdapter = GridAdapter;
|
|
1749
1853
|
|
|
1750
1854
|
/**
|
|
1751
1855
|
* Icon configuration registry for Angular applications.
|
|
@@ -2424,6 +2528,490 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImpor
|
|
|
2424
2528
|
type: Directive
|
|
2425
2529
|
}], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], row: [{ type: i0.Input, args: [{ isSignal: true, alias: "row", required: false }] }], column: [{ type: i0.Input, args: [{ isSignal: true, alias: "column", required: false }] }], control: [{ type: i0.Input, args: [{ isSignal: true, alias: "control", required: false }] }], commit: [{ type: i0.Output, args: ["commit"] }], cancel: [{ type: i0.Output, args: ["cancel"] }] } });
|
|
2426
2530
|
|
|
2531
|
+
// Symbol for storing form context on the grid element (shared with GridFormArray)
|
|
2532
|
+
const FORM_ARRAY_CONTEXT = Symbol('formArrayContext');
|
|
2533
|
+
/**
|
|
2534
|
+
* Gets the FormArrayContext from a grid element, if present.
|
|
2535
|
+
* @internal
|
|
2536
|
+
*/
|
|
2537
|
+
function getLazyFormContext(gridElement) {
|
|
2538
|
+
return gridElement[FORM_ARRAY_CONTEXT];
|
|
2539
|
+
}
|
|
2540
|
+
/**
|
|
2541
|
+
* Directive that provides lazy FormGroup creation for grid editing.
|
|
2542
|
+
*
|
|
2543
|
+
* Unlike `GridFormArray` which creates all FormGroups upfront, this directive
|
|
2544
|
+
* creates FormGroups on-demand only when a row enters edit mode. This provides
|
|
2545
|
+
* much better performance for large datasets while still enabling full
|
|
2546
|
+
* Angular Reactive Forms integration.
|
|
2547
|
+
*
|
|
2548
|
+
* ## Key Benefits
|
|
2549
|
+
*
|
|
2550
|
+
* - **Performance**: Only creates FormGroups for rows being edited (20-100x fewer controls)
|
|
2551
|
+
* - **Same DX**: Editors still receive `control` in their context for validation
|
|
2552
|
+
* - **Memory efficient**: FormGroups are cleaned up when rows exit edit mode
|
|
2553
|
+
*
|
|
2554
|
+
* ## Usage
|
|
2555
|
+
*
|
|
2556
|
+
* ```typescript
|
|
2557
|
+
* import { Component, inject, signal } from '@angular/core';
|
|
2558
|
+
* import { FormBuilder, Validators, ReactiveFormsModule } from '@angular/forms';
|
|
2559
|
+
* import { Grid, GridLazyForm, TbwEditor } from '@toolbox-web/grid-angular';
|
|
2560
|
+
*
|
|
2561
|
+
* @Component({
|
|
2562
|
+
* imports: [Grid, GridLazyForm, TbwEditor, ReactiveFormsModule],
|
|
2563
|
+
* template: \`
|
|
2564
|
+
* <tbw-grid
|
|
2565
|
+
* [rows]="employees()"
|
|
2566
|
+
* [lazyForm]="createRowForm"
|
|
2567
|
+
* [gridConfig]="config">
|
|
2568
|
+
*
|
|
2569
|
+
* <tbw-grid-column field="firstName">
|
|
2570
|
+
* <input *tbwEditor="let _; control as ctrl"
|
|
2571
|
+
* [formControl]="ctrl"
|
|
2572
|
+
* [class.is-invalid]="ctrl?.invalid && ctrl?.touched" />
|
|
2573
|
+
* </tbw-grid-column>
|
|
2574
|
+
* </tbw-grid>
|
|
2575
|
+
* \`
|
|
2576
|
+
* })
|
|
2577
|
+
* export class MyComponent {
|
|
2578
|
+
* private fb = inject(FormBuilder);
|
|
2579
|
+
* employees = signal(generateEmployees(1000));
|
|
2580
|
+
*
|
|
2581
|
+
* // Factory called when editing starts - only include editable fields!
|
|
2582
|
+
* createRowForm = (row: Employee): FormGroup => this.fb.group({
|
|
2583
|
+
* firstName: [row.firstName, Validators.required],
|
|
2584
|
+
* lastName: [row.lastName, Validators.minLength(2)],
|
|
2585
|
+
* salary: [row.salary, [Validators.required, Validators.min(0)]],
|
|
2586
|
+
* });
|
|
2587
|
+
*
|
|
2588
|
+
* gridConfig = { columns: [...] };
|
|
2589
|
+
* }
|
|
2590
|
+
* ```
|
|
2591
|
+
*
|
|
2592
|
+
* ## How It Works
|
|
2593
|
+
*
|
|
2594
|
+
* 1. Rows come from `[rows]` input (plain data array)
|
|
2595
|
+
* 2. When a cell enters edit mode, the FormGroup is created lazily
|
|
2596
|
+
* 3. Editors receive the FormControl in their template context
|
|
2597
|
+
* 4. On commit, FormGroup values are synced back to the row
|
|
2598
|
+
* 5. FormGroup is cleaned up when the row exits edit mode (configurable)
|
|
2599
|
+
*
|
|
2600
|
+
* ## Performance Comparison
|
|
2601
|
+
*
|
|
2602
|
+
* | Rows | GridFormArray (20 fields) | GridLazyForm |
|
|
2603
|
+
* |------|---------------------------|--------------|
|
|
2604
|
+
* | 100 | 2,000 controls | ~20 controls |
|
|
2605
|
+
* | 500 | 10,000 controls | ~20 controls |
|
|
2606
|
+
* | 1000 | 20,000 controls | ~20 controls |
|
|
2607
|
+
*
|
|
2608
|
+
* @see GridFormArray For small datasets with full upfront validation
|
|
2609
|
+
*/
|
|
2610
|
+
class GridLazyForm {
|
|
2611
|
+
elementRef = inject((ElementRef));
|
|
2612
|
+
// Cache of FormGroups by row reference
|
|
2613
|
+
formGroupCache = new Map();
|
|
2614
|
+
// Map from row reference to rowIndex (needed for getControl)
|
|
2615
|
+
rowIndexMap = new Map();
|
|
2616
|
+
// Track which row is currently being edited
|
|
2617
|
+
editingRowIndex = null;
|
|
2618
|
+
cellCommitListener = null;
|
|
2619
|
+
rowCommitListener = null;
|
|
2620
|
+
rowsChangeListener = null;
|
|
2621
|
+
/**
|
|
2622
|
+
* Factory function to create a FormGroup for a row.
|
|
2623
|
+
* Called lazily when the row first enters edit mode.
|
|
2624
|
+
*
|
|
2625
|
+
* @example
|
|
2626
|
+
* ```typescript
|
|
2627
|
+
* createRowForm = (row: Employee): FormGroup => this.fb.group({
|
|
2628
|
+
* firstName: [row.firstName, Validators.required],
|
|
2629
|
+
* lastName: [row.lastName],
|
|
2630
|
+
* salary: [row.salary, [Validators.min(0)]],
|
|
2631
|
+
* });
|
|
2632
|
+
* ```
|
|
2633
|
+
*/
|
|
2634
|
+
lazyForm = input.required(...(ngDevMode ? [{ debugName: "lazyForm" }] : []));
|
|
2635
|
+
/**
|
|
2636
|
+
* Whether to automatically sync Angular validation state to grid's visual invalid styling.
|
|
2637
|
+
*
|
|
2638
|
+
* When enabled:
|
|
2639
|
+
* - After a cell commit, if the FormControl is invalid, the cell is marked with `setInvalid()`
|
|
2640
|
+
* - When a FormControl becomes valid, `clearInvalid()` is called
|
|
2641
|
+
* - On `row-commit`, if the row's FormGroup has invalid controls, the commit is prevented
|
|
2642
|
+
*
|
|
2643
|
+
* @default true
|
|
2644
|
+
*/
|
|
2645
|
+
syncValidation = input(true, ...(ngDevMode ? [{ debugName: "syncValidation" }] : []));
|
|
2646
|
+
/**
|
|
2647
|
+
* Whether to keep FormGroups cached after a row exits edit mode.
|
|
2648
|
+
*
|
|
2649
|
+
* - `true`: FormGroups are kept, preserving dirty/touched state across edit sessions
|
|
2650
|
+
* - `false`: FormGroups are disposed when the row exits edit mode (default)
|
|
2651
|
+
*
|
|
2652
|
+
* @default false
|
|
2653
|
+
*/
|
|
2654
|
+
keepFormGroups = input(false, ...(ngDevMode ? [{ debugName: "keepFormGroups" }] : []));
|
|
2655
|
+
/**
|
|
2656
|
+
* Emitted when a row's form values change.
|
|
2657
|
+
* Useful for auto-save, validation display, or syncing to external state.
|
|
2658
|
+
*/
|
|
2659
|
+
rowFormChange = output();
|
|
2660
|
+
ngOnInit() {
|
|
2661
|
+
const grid = this.elementRef.nativeElement;
|
|
2662
|
+
if (!grid)
|
|
2663
|
+
return;
|
|
2664
|
+
// Store the form context for AngularGridAdapter to access
|
|
2665
|
+
this.#storeFormContext(grid);
|
|
2666
|
+
// Listen for cell-commit to update FormControl and sync validation
|
|
2667
|
+
this.cellCommitListener = (e) => {
|
|
2668
|
+
const detail = e.detail;
|
|
2669
|
+
this.#handleCellCommit(detail);
|
|
2670
|
+
};
|
|
2671
|
+
grid.addEventListener('cell-commit', this.cellCommitListener);
|
|
2672
|
+
// Listen for row-commit to sync FormGroup values back to row and cleanup
|
|
2673
|
+
this.rowCommitListener = (e) => {
|
|
2674
|
+
const detail = e.detail;
|
|
2675
|
+
this.#handleRowCommit(e, detail);
|
|
2676
|
+
};
|
|
2677
|
+
grid.addEventListener('row-commit', this.rowCommitListener);
|
|
2678
|
+
// Listen for rows-change to update row index mappings
|
|
2679
|
+
this.rowsChangeListener = () => {
|
|
2680
|
+
this.#updateRowIndexMap();
|
|
2681
|
+
};
|
|
2682
|
+
grid.addEventListener('rows-change', this.rowsChangeListener);
|
|
2683
|
+
// Initial row index mapping
|
|
2684
|
+
this.#updateRowIndexMap();
|
|
2685
|
+
}
|
|
2686
|
+
ngOnDestroy() {
|
|
2687
|
+
const grid = this.elementRef.nativeElement;
|
|
2688
|
+
if (!grid)
|
|
2689
|
+
return;
|
|
2690
|
+
if (this.cellCommitListener) {
|
|
2691
|
+
grid.removeEventListener('cell-commit', this.cellCommitListener);
|
|
2692
|
+
}
|
|
2693
|
+
if (this.rowCommitListener) {
|
|
2694
|
+
grid.removeEventListener('row-commit', this.rowCommitListener);
|
|
2695
|
+
}
|
|
2696
|
+
if (this.rowsChangeListener) {
|
|
2697
|
+
grid.removeEventListener('rows-change', this.rowsChangeListener);
|
|
2698
|
+
}
|
|
2699
|
+
this.#clearFormContext(grid);
|
|
2700
|
+
this.formGroupCache.clear();
|
|
2701
|
+
this.rowIndexMap.clear();
|
|
2702
|
+
}
|
|
2703
|
+
// #region FormArrayContext Implementation
|
|
2704
|
+
/**
|
|
2705
|
+
* Gets or creates the FormGroup for a row.
|
|
2706
|
+
* This is the core lazy initialization logic.
|
|
2707
|
+
*/
|
|
2708
|
+
#getOrCreateFormGroup(row, rowIndex) {
|
|
2709
|
+
let formGroup = this.formGroupCache.get(row);
|
|
2710
|
+
if (!formGroup) {
|
|
2711
|
+
const factory = this.lazyForm();
|
|
2712
|
+
formGroup = factory(row, rowIndex);
|
|
2713
|
+
this.formGroupCache.set(row, formGroup);
|
|
2714
|
+
this.rowIndexMap.set(row, rowIndex);
|
|
2715
|
+
}
|
|
2716
|
+
return formGroup;
|
|
2717
|
+
}
|
|
2718
|
+
/**
|
|
2719
|
+
* Gets the FormGroup for a row if it exists (without creating).
|
|
2720
|
+
*/
|
|
2721
|
+
#getRowFormGroup(rowIndex) {
|
|
2722
|
+
const grid = this.elementRef.nativeElement;
|
|
2723
|
+
const rows = grid?.rows;
|
|
2724
|
+
if (!rows || rowIndex < 0 || rowIndex >= rows.length)
|
|
2725
|
+
return undefined;
|
|
2726
|
+
const row = rows[rowIndex];
|
|
2727
|
+
return this.formGroupCache.get(row);
|
|
2728
|
+
}
|
|
2729
|
+
/**
|
|
2730
|
+
* Stores the FormArrayContext on the grid element.
|
|
2731
|
+
* This uses the same interface as GridFormArray for compatibility.
|
|
2732
|
+
*/
|
|
2733
|
+
#storeFormContext(grid) {
|
|
2734
|
+
const context = {
|
|
2735
|
+
getRow: (rowIndex) => {
|
|
2736
|
+
const rows = grid.rows;
|
|
2737
|
+
if (!rows || rowIndex < 0 || rowIndex >= rows.length)
|
|
2738
|
+
return null;
|
|
2739
|
+
return rows[rowIndex];
|
|
2740
|
+
},
|
|
2741
|
+
updateField: (rowIndex, field, value) => {
|
|
2742
|
+
const formGroup = this.#getRowFormGroup(rowIndex);
|
|
2743
|
+
if (formGroup) {
|
|
2744
|
+
const control = formGroup.get(field);
|
|
2745
|
+
if (control) {
|
|
2746
|
+
control.setValue(value);
|
|
2747
|
+
control.markAsDirty();
|
|
2748
|
+
}
|
|
2749
|
+
}
|
|
2750
|
+
},
|
|
2751
|
+
getValue: () => {
|
|
2752
|
+
return (grid.rows ?? []);
|
|
2753
|
+
},
|
|
2754
|
+
// Always true for lazy forms - we create FormGroups on demand
|
|
2755
|
+
hasFormGroups: true,
|
|
2756
|
+
getControl: (rowIndex, field) => {
|
|
2757
|
+
const rows = grid.rows;
|
|
2758
|
+
if (!rows || rowIndex < 0 || rowIndex >= rows.length)
|
|
2759
|
+
return undefined;
|
|
2760
|
+
const row = rows[rowIndex];
|
|
2761
|
+
// LAZY: Create the FormGroup when first needed
|
|
2762
|
+
const formGroup = this.#getOrCreateFormGroup(row, rowIndex);
|
|
2763
|
+
return formGroup.get(field) ?? undefined;
|
|
2764
|
+
},
|
|
2765
|
+
getRowFormGroup: (rowIndex) => {
|
|
2766
|
+
const rows = grid.rows;
|
|
2767
|
+
if (!rows || rowIndex < 0 || rowIndex >= rows.length)
|
|
2768
|
+
return undefined;
|
|
2769
|
+
const row = rows[rowIndex];
|
|
2770
|
+
// LAZY: Create the FormGroup when first needed
|
|
2771
|
+
return this.#getOrCreateFormGroup(row, rowIndex);
|
|
2772
|
+
},
|
|
2773
|
+
isRowValid: (rowIndex) => {
|
|
2774
|
+
const formGroup = this.#getRowFormGroup(rowIndex);
|
|
2775
|
+
// If no FormGroup exists yet, consider it valid (not edited)
|
|
2776
|
+
if (!formGroup)
|
|
2777
|
+
return true;
|
|
2778
|
+
return formGroup.valid;
|
|
2779
|
+
},
|
|
2780
|
+
isRowTouched: (rowIndex) => {
|
|
2781
|
+
const formGroup = this.#getRowFormGroup(rowIndex);
|
|
2782
|
+
if (!formGroup)
|
|
2783
|
+
return false;
|
|
2784
|
+
return formGroup.touched;
|
|
2785
|
+
},
|
|
2786
|
+
isRowDirty: (rowIndex) => {
|
|
2787
|
+
const formGroup = this.#getRowFormGroup(rowIndex);
|
|
2788
|
+
if (!formGroup)
|
|
2789
|
+
return false;
|
|
2790
|
+
return formGroup.dirty;
|
|
2791
|
+
},
|
|
2792
|
+
getRowErrors: (rowIndex) => {
|
|
2793
|
+
const formGroup = this.#getRowFormGroup(rowIndex);
|
|
2794
|
+
if (!formGroup)
|
|
2795
|
+
return null;
|
|
2796
|
+
const errors = {};
|
|
2797
|
+
let hasErrors = false;
|
|
2798
|
+
Object.keys(formGroup.controls).forEach((field) => {
|
|
2799
|
+
const control = formGroup.get(field);
|
|
2800
|
+
if (control?.errors) {
|
|
2801
|
+
errors[field] = control.errors;
|
|
2802
|
+
hasErrors = true;
|
|
2803
|
+
}
|
|
2804
|
+
});
|
|
2805
|
+
if (formGroup.errors) {
|
|
2806
|
+
errors['_group'] = formGroup.errors;
|
|
2807
|
+
hasErrors = true;
|
|
2808
|
+
}
|
|
2809
|
+
return hasErrors ? errors : null;
|
|
2810
|
+
},
|
|
2811
|
+
};
|
|
2812
|
+
grid[FORM_ARRAY_CONTEXT] = context;
|
|
2813
|
+
}
|
|
2814
|
+
/**
|
|
2815
|
+
* Clears the FormArrayContext from the grid element.
|
|
2816
|
+
*/
|
|
2817
|
+
#clearFormContext(grid) {
|
|
2818
|
+
delete grid[FORM_ARRAY_CONTEXT];
|
|
2819
|
+
}
|
|
2820
|
+
// #endregion
|
|
2821
|
+
// #region Event Handlers
|
|
2822
|
+
/**
|
|
2823
|
+
* Updates the row index map when rows change.
|
|
2824
|
+
* This ensures we can find FormGroups by row reference after sorting/filtering.
|
|
2825
|
+
*/
|
|
2826
|
+
#updateRowIndexMap() {
|
|
2827
|
+
const grid = this.elementRef.nativeElement;
|
|
2828
|
+
const rows = grid?.rows;
|
|
2829
|
+
if (!rows)
|
|
2830
|
+
return;
|
|
2831
|
+
this.rowIndexMap.clear();
|
|
2832
|
+
rows.forEach((row, index) => {
|
|
2833
|
+
if (this.formGroupCache.has(row)) {
|
|
2834
|
+
this.rowIndexMap.set(row, index);
|
|
2835
|
+
}
|
|
2836
|
+
});
|
|
2837
|
+
}
|
|
2838
|
+
/**
|
|
2839
|
+
* Handles cell-commit events by updating the FormControl in the FormGroup.
|
|
2840
|
+
*/
|
|
2841
|
+
#handleCellCommit(detail) {
|
|
2842
|
+
const { rowIndex, field, value, rowId } = detail;
|
|
2843
|
+
const formGroup = this.#getRowFormGroup(rowIndex);
|
|
2844
|
+
if (formGroup) {
|
|
2845
|
+
const control = formGroup.get(field);
|
|
2846
|
+
if (control) {
|
|
2847
|
+
control.setValue(value);
|
|
2848
|
+
control.markAsDirty();
|
|
2849
|
+
control.markAsTouched();
|
|
2850
|
+
// Sync Angular validation state to grid's visual invalid styling
|
|
2851
|
+
if (this.syncValidation() && rowId) {
|
|
2852
|
+
this.#syncControlValidationToGrid(rowId, field, control);
|
|
2853
|
+
}
|
|
2854
|
+
// Emit change event
|
|
2855
|
+
const grid = this.elementRef.nativeElement;
|
|
2856
|
+
const row = grid?.rows?.[rowIndex];
|
|
2857
|
+
if (row) {
|
|
2858
|
+
this.rowFormChange.emit({
|
|
2859
|
+
rowIndex,
|
|
2860
|
+
rowId,
|
|
2861
|
+
row,
|
|
2862
|
+
formGroup,
|
|
2863
|
+
values: formGroup.value,
|
|
2864
|
+
valid: formGroup.valid,
|
|
2865
|
+
dirty: formGroup.dirty,
|
|
2866
|
+
});
|
|
2867
|
+
}
|
|
2868
|
+
}
|
|
2869
|
+
}
|
|
2870
|
+
}
|
|
2871
|
+
/**
|
|
2872
|
+
* Handles row-commit events.
|
|
2873
|
+
* - Prevents commit if FormGroup is invalid (when syncValidation is true)
|
|
2874
|
+
* - Syncs FormGroup values back to the row
|
|
2875
|
+
* - Cleans up FormGroup if keepFormGroups is false
|
|
2876
|
+
*/
|
|
2877
|
+
#handleRowCommit(event, detail) {
|
|
2878
|
+
const { rowIndex, rowId } = detail;
|
|
2879
|
+
const grid = this.elementRef.nativeElement;
|
|
2880
|
+
const rows = grid?.rows;
|
|
2881
|
+
if (!rows || rowIndex < 0 || rowIndex >= rows.length)
|
|
2882
|
+
return;
|
|
2883
|
+
const row = rows[rowIndex];
|
|
2884
|
+
const formGroup = this.formGroupCache.get(row);
|
|
2885
|
+
if (!formGroup)
|
|
2886
|
+
return;
|
|
2887
|
+
// Prevent commit if invalid (when syncValidation is enabled)
|
|
2888
|
+
if (this.syncValidation() && formGroup.invalid) {
|
|
2889
|
+
// Mark all controls as touched to show validation errors
|
|
2890
|
+
formGroup.markAllAsTouched();
|
|
2891
|
+
event.preventDefault();
|
|
2892
|
+
return;
|
|
2893
|
+
}
|
|
2894
|
+
// Sync FormGroup values back to the row object
|
|
2895
|
+
if (formGroup.dirty) {
|
|
2896
|
+
const formValue = formGroup.value;
|
|
2897
|
+
Object.keys(formValue).forEach((field) => {
|
|
2898
|
+
if (field in row) {
|
|
2899
|
+
row[field] = formValue[field];
|
|
2900
|
+
}
|
|
2901
|
+
});
|
|
2902
|
+
}
|
|
2903
|
+
// Clean up FormGroup if not keeping them
|
|
2904
|
+
if (!this.keepFormGroups()) {
|
|
2905
|
+
this.formGroupCache.delete(row);
|
|
2906
|
+
this.rowIndexMap.delete(row);
|
|
2907
|
+
// Clear any validation state in the grid
|
|
2908
|
+
if (rowId) {
|
|
2909
|
+
const editingPlugin = grid.getPluginByName?.('editing');
|
|
2910
|
+
editingPlugin?.clearRowInvalid(rowId);
|
|
2911
|
+
}
|
|
2912
|
+
}
|
|
2913
|
+
}
|
|
2914
|
+
// #endregion
|
|
2915
|
+
// #region Validation Sync
|
|
2916
|
+
/**
|
|
2917
|
+
* Syncs a FormControl's validation state to the grid's visual invalid styling.
|
|
2918
|
+
*/
|
|
2919
|
+
#syncControlValidationToGrid(rowId, field, control) {
|
|
2920
|
+
const grid = this.elementRef.nativeElement;
|
|
2921
|
+
if (!grid)
|
|
2922
|
+
return;
|
|
2923
|
+
const editingPlugin = grid.getPluginByName?.('editing');
|
|
2924
|
+
if (!editingPlugin)
|
|
2925
|
+
return;
|
|
2926
|
+
if (control.invalid) {
|
|
2927
|
+
const errorMessage = this.#getFirstErrorMessage(control);
|
|
2928
|
+
editingPlugin.setInvalid(rowId, field, errorMessage);
|
|
2929
|
+
}
|
|
2930
|
+
else {
|
|
2931
|
+
editingPlugin.clearInvalid(rowId, field);
|
|
2932
|
+
}
|
|
2933
|
+
}
|
|
2934
|
+
/**
|
|
2935
|
+
* Gets a human-readable error message from the first validation error.
|
|
2936
|
+
*/
|
|
2937
|
+
#getFirstErrorMessage(control) {
|
|
2938
|
+
const errors = control.errors;
|
|
2939
|
+
if (!errors)
|
|
2940
|
+
return '';
|
|
2941
|
+
const firstKey = Object.keys(errors)[0];
|
|
2942
|
+
const error = errors[firstKey];
|
|
2943
|
+
switch (firstKey) {
|
|
2944
|
+
case 'required':
|
|
2945
|
+
return 'This field is required';
|
|
2946
|
+
case 'minlength':
|
|
2947
|
+
return `Minimum length is ${error.requiredLength}`;
|
|
2948
|
+
case 'maxlength':
|
|
2949
|
+
return `Maximum length is ${error.requiredLength}`;
|
|
2950
|
+
case 'min':
|
|
2951
|
+
return `Minimum value is ${error.min}`;
|
|
2952
|
+
case 'max':
|
|
2953
|
+
return `Maximum value is ${error.max}`;
|
|
2954
|
+
case 'email':
|
|
2955
|
+
return 'Invalid email address';
|
|
2956
|
+
case 'pattern':
|
|
2957
|
+
return 'Invalid format';
|
|
2958
|
+
default:
|
|
2959
|
+
return typeof error === 'string' ? error : (error?.message ?? `Validation error: ${firstKey}`);
|
|
2960
|
+
}
|
|
2961
|
+
}
|
|
2962
|
+
// #endregion
|
|
2963
|
+
// #region Public API
|
|
2964
|
+
/**
|
|
2965
|
+
* Gets the FormGroup for a row, if it exists.
|
|
2966
|
+
* Unlike the context methods, this does NOT create a FormGroup lazily.
|
|
2967
|
+
*
|
|
2968
|
+
* @param rowIndex The row index
|
|
2969
|
+
* @returns The FormGroup or undefined
|
|
2970
|
+
*/
|
|
2971
|
+
getFormGroup(rowIndex) {
|
|
2972
|
+
return this.#getRowFormGroup(rowIndex);
|
|
2973
|
+
}
|
|
2974
|
+
/**
|
|
2975
|
+
* Gets all cached FormGroups.
|
|
2976
|
+
* Useful for bulk validation or inspection.
|
|
2977
|
+
*
|
|
2978
|
+
* @returns Map of row objects to their FormGroups
|
|
2979
|
+
*/
|
|
2980
|
+
getAllFormGroups() {
|
|
2981
|
+
return this.formGroupCache;
|
|
2982
|
+
}
|
|
2983
|
+
/**
|
|
2984
|
+
* Clears all cached FormGroups.
|
|
2985
|
+
* Useful when the underlying data changes significantly.
|
|
2986
|
+
*/
|
|
2987
|
+
clearAllFormGroups() {
|
|
2988
|
+
this.formGroupCache.clear();
|
|
2989
|
+
this.rowIndexMap.clear();
|
|
2990
|
+
}
|
|
2991
|
+
/**
|
|
2992
|
+
* Validates all currently cached FormGroups.
|
|
2993
|
+
*
|
|
2994
|
+
* @returns true if all FormGroups are valid, false otherwise
|
|
2995
|
+
*/
|
|
2996
|
+
validateAll() {
|
|
2997
|
+
for (const formGroup of this.formGroupCache.values()) {
|
|
2998
|
+
if (formGroup.invalid) {
|
|
2999
|
+
formGroup.markAllAsTouched();
|
|
3000
|
+
return false;
|
|
3001
|
+
}
|
|
3002
|
+
}
|
|
3003
|
+
return true;
|
|
3004
|
+
}
|
|
3005
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: GridLazyForm, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
3006
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.1.1", type: GridLazyForm, isStandalone: true, selector: "tbw-grid[lazyForm]", inputs: { lazyForm: { classPropertyName: "lazyForm", publicName: "lazyForm", isSignal: true, isRequired: true, transformFunction: null }, syncValidation: { classPropertyName: "syncValidation", publicName: "syncValidation", isSignal: true, isRequired: false, transformFunction: null }, keepFormGroups: { classPropertyName: "keepFormGroups", publicName: "keepFormGroups", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { rowFormChange: "rowFormChange" }, ngImport: i0 });
|
|
3007
|
+
}
|
|
3008
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: GridLazyForm, decorators: [{
|
|
3009
|
+
type: Directive,
|
|
3010
|
+
args: [{
|
|
3011
|
+
selector: 'tbw-grid[lazyForm]',
|
|
3012
|
+
}]
|
|
3013
|
+
}], propDecorators: { lazyForm: [{ type: i0.Input, args: [{ isSignal: true, alias: "lazyForm", required: true }] }], syncValidation: [{ type: i0.Input, args: [{ isSignal: true, alias: "syncValidation", required: false }] }], keepFormGroups: [{ type: i0.Input, args: [{ isSignal: true, alias: "keepFormGroups", required: false }] }], rowFormChange: [{ type: i0.Output, args: ["rowFormChange"] }] } });
|
|
3014
|
+
|
|
2427
3015
|
/**
|
|
2428
3016
|
* Directive that automatically registers the Angular adapter with tbw-grid elements.
|
|
2429
3017
|
*
|
|
@@ -2454,7 +3042,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImpor
|
|
|
2454
3042
|
* ```
|
|
2455
3043
|
*
|
|
2456
3044
|
* The directive automatically:
|
|
2457
|
-
* - Creates
|
|
3045
|
+
* - Creates a GridAdapter instance
|
|
2458
3046
|
* - Registers it with the GridElement
|
|
2459
3047
|
* - Injects custom styles into the grid
|
|
2460
3048
|
* - Handles cleanup on destruction
|
|
@@ -2467,11 +3055,18 @@ class Grid {
|
|
|
2467
3055
|
iconRegistry = inject(GridIconRegistry, { optional: true });
|
|
2468
3056
|
adapter = null;
|
|
2469
3057
|
constructor() {
|
|
2470
|
-
// Effect to process
|
|
3058
|
+
// Effect to process gridConfig and apply to grid
|
|
2471
3059
|
// This merges feature input plugins with the user's config plugins
|
|
2472
3060
|
effect(() => {
|
|
2473
|
-
const
|
|
3061
|
+
const deprecatedAngularConfig = this.angularConfig();
|
|
2474
3062
|
const userGridConfig = this.gridConfig();
|
|
3063
|
+
// Emit deprecation warning if angularConfig is used
|
|
3064
|
+
if (deprecatedAngularConfig && !userGridConfig) {
|
|
3065
|
+
console.warn('[tbw-grid] The [angularConfig] input is deprecated. Use [gridConfig] instead. ' +
|
|
3066
|
+
'The gridConfig input now accepts GridConfig directly.');
|
|
3067
|
+
}
|
|
3068
|
+
// Use gridConfig preferentially, fall back to deprecated angularConfig
|
|
3069
|
+
const angularCfg = userGridConfig ?? deprecatedAngularConfig;
|
|
2475
3070
|
if (!this.adapter)
|
|
2476
3071
|
return;
|
|
2477
3072
|
// Create plugins from feature inputs
|
|
@@ -2495,25 +3090,24 @@ class Grid {
|
|
|
2495
3090
|
// Registry icons are base, config.icons override them
|
|
2496
3091
|
const registryIcons = this.iconRegistry?.getAll();
|
|
2497
3092
|
if (registryIcons && Object.keys(registryIcons).length > 0) {
|
|
2498
|
-
const existingIcons = angularCfg?.icons ||
|
|
3093
|
+
const existingIcons = angularCfg?.icons || {};
|
|
2499
3094
|
coreConfigOverrides['icons'] = { ...registryIcons, ...existingIcons };
|
|
2500
3095
|
}
|
|
2501
|
-
// If
|
|
3096
|
+
// If gridConfig is provided, process it (converts component classes to renderer functions)
|
|
2502
3097
|
const processedConfig = angularCfg ? this.adapter.processGridConfig(angularCfg) : null;
|
|
2503
|
-
// IMPORTANT: If user is NOT using
|
|
3098
|
+
// IMPORTANT: If user is NOT using gridConfig input, and there are no feature plugins
|
|
2504
3099
|
// or config overrides to merge, do NOT overwrite grid.gridConfig.
|
|
2505
3100
|
// This allows [gridConfig]="myConfig" binding to work correctly without the directive
|
|
2506
3101
|
// creating a new object that strips properties like typeDefaults.
|
|
2507
3102
|
const hasFeaturePlugins = featurePlugins.length > 0;
|
|
2508
3103
|
const hasConfigOverrides = Object.keys(coreConfigOverrides).length > 0;
|
|
2509
|
-
// Use the gridConfig input signal (preferred) or fallback to what's on the grid element
|
|
2510
3104
|
// The input signal gives us reactive tracking of the user's config
|
|
2511
|
-
const existingConfig =
|
|
2512
|
-
if (!processedConfig && !hasFeaturePlugins && !hasConfigOverrides && !
|
|
3105
|
+
const existingConfig = angularCfg || {};
|
|
3106
|
+
if (!processedConfig && !hasFeaturePlugins && !hasConfigOverrides && !angularCfg) {
|
|
2513
3107
|
// Nothing to merge and no config input - let the user's DOM binding work directly
|
|
2514
3108
|
return;
|
|
2515
3109
|
}
|
|
2516
|
-
// Merge:
|
|
3110
|
+
// Merge: processed config < feature plugins
|
|
2517
3111
|
const configPlugins = processedConfig?.plugins || existingConfig.plugins || [];
|
|
2518
3112
|
const mergedPlugins = [...featurePlugins, ...configPlugins];
|
|
2519
3113
|
// Build the final config, preserving ALL existing properties (including typeDefaults)
|
|
@@ -2642,65 +3236,55 @@ class Grid {
|
|
|
2642
3236
|
*/
|
|
2643
3237
|
loading = input(...(ngDevMode ? [undefined, { debugName: "loading" }] : []));
|
|
2644
3238
|
/**
|
|
2645
|
-
*
|
|
3239
|
+
* Grid configuration object with optional Angular-specific extensions.
|
|
2646
3240
|
*
|
|
2647
|
-
*
|
|
2648
|
-
*
|
|
2649
|
-
* but allows the directive to properly merge feature plugins and config overrides.
|
|
3241
|
+
* Accepts Angular-augmented `GridConfig` from `@toolbox-web/grid-angular`.
|
|
3242
|
+
* You can specify Angular component classes directly for renderers and editors.
|
|
2650
3243
|
*
|
|
2651
|
-
*
|
|
3244
|
+
* Component classes must implement the appropriate interfaces:
|
|
3245
|
+
* - Renderers: `CellRenderer<TRow, TValue>` - requires `value()` and `row()` signal inputs
|
|
3246
|
+
* - Editors: `CellEditor<TRow, TValue>` - adds `commit` and `cancel` outputs
|
|
2652
3247
|
*
|
|
2653
3248
|
* @example
|
|
2654
3249
|
* ```typescript
|
|
3250
|
+
* // Simple config with plain renderers
|
|
2655
3251
|
* config: GridConfig = {
|
|
3252
|
+
* columns: [
|
|
3253
|
+
* { field: 'name', header: 'Name' },
|
|
3254
|
+
* { field: 'active', type: 'boolean' }
|
|
3255
|
+
* ],
|
|
2656
3256
|
* typeDefaults: {
|
|
2657
3257
|
* boolean: { renderer: (ctx) => ctx.value ? '✓' : '✗' }
|
|
2658
3258
|
* }
|
|
2659
3259
|
* };
|
|
2660
|
-
*
|
|
2661
|
-
*
|
|
2662
|
-
*
|
|
3260
|
+
*
|
|
3261
|
+
* // Config with component classes
|
|
3262
|
+
* config: GridConfig<Employee> = {
|
|
3263
|
+
* columns: [
|
|
3264
|
+
* { field: 'name', header: 'Name' },
|
|
3265
|
+
* { field: 'bonus', header: 'Bonus', editable: true, editor: BonusEditorComponent }
|
|
3266
|
+
* ]
|
|
3267
|
+
* };
|
|
2663
3268
|
* ```
|
|
2664
3269
|
*
|
|
2665
3270
|
* ```html
|
|
2666
|
-
* <tbw-grid [gridConfig]="config" [
|
|
3271
|
+
* <tbw-grid [gridConfig]="config" [rows]="employees"></tbw-grid>
|
|
2667
3272
|
* ```
|
|
2668
3273
|
*/
|
|
2669
3274
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2670
3275
|
gridConfig = input(...(ngDevMode ? [undefined, { debugName: "gridConfig" }] : []));
|
|
2671
3276
|
/**
|
|
2672
|
-
*
|
|
2673
|
-
*
|
|
2674
|
-
* Use this input when you want to specify Angular component classes directly in column configs.
|
|
2675
|
-
* Components must implement the appropriate interfaces:
|
|
2676
|
-
* - Renderers: `AngularCellRenderer<TRow, TValue>` - requires `value()` and `row()` signal inputs
|
|
2677
|
-
* - Editors: `AngularCellEditor<TRow, TValue>` - adds `commit` and `cancel` outputs
|
|
2678
|
-
*
|
|
2679
|
-
* The directive automatically processes component classes and converts them to grid-compatible
|
|
2680
|
-
* renderer/editor functions before applying to the grid.
|
|
2681
|
-
*
|
|
2682
|
-
* @example
|
|
2683
|
-
* ```typescript
|
|
2684
|
-
* // Component that implements AngularCellEditor
|
|
2685
|
-
* @Component({...})
|
|
2686
|
-
* export class BonusEditorComponent implements AngularCellEditor<Employee, number> {
|
|
2687
|
-
* value = input.required<number>();
|
|
2688
|
-
* row = input.required<Employee>();
|
|
2689
|
-
* commit = output<number>();
|
|
2690
|
-
* cancel = output<void>();
|
|
2691
|
-
* }
|
|
3277
|
+
* @deprecated Use `gridConfig` instead. This input will be removed in a future version.
|
|
2692
3278
|
*
|
|
2693
|
-
*
|
|
2694
|
-
*
|
|
2695
|
-
* columns: [
|
|
2696
|
-
* { field: 'name', header: 'Name' },
|
|
2697
|
-
* { field: 'bonus', header: 'Bonus', editable: true, editor: BonusEditorComponent }
|
|
2698
|
-
* ]
|
|
2699
|
-
* };
|
|
2700
|
-
* ```
|
|
3279
|
+
* The `angularConfig` name was inconsistent with React and Vue adapters, which both use `gridConfig`.
|
|
3280
|
+
* The `gridConfig` input now accepts `GridConfig` directly.
|
|
2701
3281
|
*
|
|
2702
3282
|
* ```html
|
|
2703
|
-
*
|
|
3283
|
+
* <!-- Before -->
|
|
3284
|
+
* <tbw-grid [angularConfig]="config" />
|
|
3285
|
+
*
|
|
3286
|
+
* <!-- After -->
|
|
3287
|
+
* <tbw-grid [gridConfig]="config" />
|
|
2704
3288
|
* ```
|
|
2705
3289
|
*/
|
|
2706
3290
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
@@ -3016,7 +3600,7 @@ class Grid {
|
|
|
3016
3600
|
* <tbw-grid [export]="{ filename: 'data.csv' }" />
|
|
3017
3601
|
* ```
|
|
3018
3602
|
*/
|
|
3019
|
-
exportFeature = input(...(ngDevMode ?
|
|
3603
|
+
exportFeature = input(undefined, { ...(ngDevMode ? { debugName: "exportFeature" } : {}), alias: 'export' });
|
|
3020
3604
|
/**
|
|
3021
3605
|
* Enable print functionality.
|
|
3022
3606
|
*
|
|
@@ -3334,7 +3918,7 @@ class Grid {
|
|
|
3334
3918
|
eventListeners = new Map();
|
|
3335
3919
|
ngOnInit() {
|
|
3336
3920
|
// Create and register the adapter
|
|
3337
|
-
this.adapter = new
|
|
3921
|
+
this.adapter = new GridAdapter(this.injector, this.appRef, this.viewContainerRef);
|
|
3338
3922
|
DataGridElement.registerAdapter(this.adapter);
|
|
3339
3923
|
const grid = this.elementRef.nativeElement;
|
|
3340
3924
|
// Wire up all event listeners based on eventOutputMap
|
|
@@ -3535,12 +4119,12 @@ class Grid {
|
|
|
3535
4119
|
}
|
|
3536
4120
|
}
|
|
3537
4121
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: Grid, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
3538
|
-
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.1.1", type: Grid, isStandalone: true, selector: "tbw-grid", inputs: { customStyles: { classPropertyName: "customStyles", publicName: "customStyles", isSignal: true, isRequired: false, transformFunction: null }, sortable: { classPropertyName: "sortable", publicName: "sortable", isSignal: true, isRequired: false, transformFunction: null }, filterable: { classPropertyName: "filterable", publicName: "filterable", isSignal: true, isRequired: false, transformFunction: null }, selectable: { classPropertyName: "selectable", publicName: "selectable", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, gridConfig: { classPropertyName: "gridConfig", publicName: "gridConfig", isSignal: true, isRequired: false, transformFunction: null }, angularConfig: { classPropertyName: "angularConfig", publicName: "angularConfig", isSignal: true, isRequired: false, transformFunction: null }, selection: { classPropertyName: "selection", publicName: "selection", isSignal: true, isRequired: false, transformFunction: null }, editing: { classPropertyName: "editing", publicName: "editing", isSignal: true, isRequired: false, transformFunction: null }, clipboard: { classPropertyName: "clipboard", publicName: "clipboard", isSignal: true, isRequired: false, transformFunction: null }, contextMenu: { classPropertyName: "contextMenu", publicName: "contextMenu", isSignal: true, isRequired: false, transformFunction: null }, multiSort: { classPropertyName: "multiSort", publicName: "multiSort", isSignal: true, isRequired: false, transformFunction: null }, sorting: { classPropertyName: "sorting", publicName: "sorting", isSignal: true, isRequired: false, transformFunction: null }, filtering: { classPropertyName: "filtering", publicName: "filtering", isSignal: true, isRequired: false, transformFunction: null }, reorder: { classPropertyName: "reorder", publicName: "reorder", isSignal: true, isRequired: false, transformFunction: null }, visibility: { classPropertyName: "visibility", publicName: "visibility", isSignal: true, isRequired: false, transformFunction: null }, pinnedColumns: { classPropertyName: "pinnedColumns", publicName: "pinnedColumns", isSignal: true, isRequired: false, transformFunction: null }, groupingColumns: { classPropertyName: "groupingColumns", publicName: "groupingColumns", isSignal: true, isRequired: false, transformFunction: null }, columnVirtualization: { classPropertyName: "columnVirtualization", publicName: "columnVirtualization", isSignal: true, isRequired: false, transformFunction: null }, rowReorder: { classPropertyName: "rowReorder", publicName: "rowReorder", isSignal: true, isRequired: false, transformFunction: null }, groupingRows: { classPropertyName: "groupingRows", publicName: "groupingRows", isSignal: true, isRequired: false, transformFunction: null }, pinnedRows: { classPropertyName: "pinnedRows", publicName: "pinnedRows", isSignal: true, isRequired: false, transformFunction: null }, tree: { classPropertyName: "tree", publicName: "tree", isSignal: true, isRequired: false, transformFunction: null }, masterDetail: { classPropertyName: "masterDetail", publicName: "masterDetail", isSignal: true, isRequired: false, transformFunction: null }, responsive: { classPropertyName: "responsive", publicName: "responsive", isSignal: true, isRequired: false, transformFunction: null }, undoRedo: { classPropertyName: "undoRedo", publicName: "undoRedo", isSignal: true, isRequired: false, transformFunction: null }, exportFeature: { classPropertyName: "exportFeature", publicName: "
|
|
4122
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.1.1", type: Grid, isStandalone: true, selector: "tbw-grid", inputs: { customStyles: { classPropertyName: "customStyles", publicName: "customStyles", isSignal: true, isRequired: false, transformFunction: null }, sortable: { classPropertyName: "sortable", publicName: "sortable", isSignal: true, isRequired: false, transformFunction: null }, filterable: { classPropertyName: "filterable", publicName: "filterable", isSignal: true, isRequired: false, transformFunction: null }, selectable: { classPropertyName: "selectable", publicName: "selectable", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, gridConfig: { classPropertyName: "gridConfig", publicName: "gridConfig", isSignal: true, isRequired: false, transformFunction: null }, angularConfig: { classPropertyName: "angularConfig", publicName: "angularConfig", isSignal: true, isRequired: false, transformFunction: null }, selection: { classPropertyName: "selection", publicName: "selection", isSignal: true, isRequired: false, transformFunction: null }, editing: { classPropertyName: "editing", publicName: "editing", isSignal: true, isRequired: false, transformFunction: null }, clipboard: { classPropertyName: "clipboard", publicName: "clipboard", isSignal: true, isRequired: false, transformFunction: null }, contextMenu: { classPropertyName: "contextMenu", publicName: "contextMenu", isSignal: true, isRequired: false, transformFunction: null }, multiSort: { classPropertyName: "multiSort", publicName: "multiSort", isSignal: true, isRequired: false, transformFunction: null }, sorting: { classPropertyName: "sorting", publicName: "sorting", isSignal: true, isRequired: false, transformFunction: null }, filtering: { classPropertyName: "filtering", publicName: "filtering", isSignal: true, isRequired: false, transformFunction: null }, reorder: { classPropertyName: "reorder", publicName: "reorder", isSignal: true, isRequired: false, transformFunction: null }, visibility: { classPropertyName: "visibility", publicName: "visibility", isSignal: true, isRequired: false, transformFunction: null }, pinnedColumns: { classPropertyName: "pinnedColumns", publicName: "pinnedColumns", isSignal: true, isRequired: false, transformFunction: null }, groupingColumns: { classPropertyName: "groupingColumns", publicName: "groupingColumns", isSignal: true, isRequired: false, transformFunction: null }, columnVirtualization: { classPropertyName: "columnVirtualization", publicName: "columnVirtualization", isSignal: true, isRequired: false, transformFunction: null }, rowReorder: { classPropertyName: "rowReorder", publicName: "rowReorder", isSignal: true, isRequired: false, transformFunction: null }, groupingRows: { classPropertyName: "groupingRows", publicName: "groupingRows", isSignal: true, isRequired: false, transformFunction: null }, pinnedRows: { classPropertyName: "pinnedRows", publicName: "pinnedRows", isSignal: true, isRequired: false, transformFunction: null }, tree: { classPropertyName: "tree", publicName: "tree", isSignal: true, isRequired: false, transformFunction: null }, masterDetail: { classPropertyName: "masterDetail", publicName: "masterDetail", isSignal: true, isRequired: false, transformFunction: null }, responsive: { classPropertyName: "responsive", publicName: "responsive", isSignal: true, isRequired: false, transformFunction: null }, undoRedo: { classPropertyName: "undoRedo", publicName: "undoRedo", isSignal: true, isRequired: false, transformFunction: null }, exportFeature: { classPropertyName: "exportFeature", publicName: "export", isSignal: true, isRequired: false, transformFunction: null }, print: { classPropertyName: "print", publicName: "print", isSignal: true, isRequired: false, transformFunction: null }, pivot: { classPropertyName: "pivot", publicName: "pivot", isSignal: true, isRequired: false, transformFunction: null }, serverSide: { classPropertyName: "serverSide", publicName: "serverSide", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { cellClick: "cellClick", rowClick: "rowClick", cellActivate: "cellActivate", cellChange: "cellChange", cellCommit: "cellCommit", rowCommit: "rowCommit", changedRowsReset: "changedRowsReset", sortChange: "sortChange", filterChange: "filterChange", columnResize: "columnResize", columnMove: "columnMove", columnVisibility: "columnVisibility", columnStateChange: "columnStateChange", selectionChange: "selectionChange", rowMove: "rowMove", groupToggle: "groupToggle", treeExpand: "treeExpand", detailExpand: "detailExpand", responsiveChange: "responsiveChange", copy: "copy", paste: "paste", undoRedoAction: "undoRedoAction", exportComplete: "exportComplete", printStart: "printStart", printComplete: "printComplete" }, ngImport: i0 });
|
|
3539
4123
|
}
|
|
3540
4124
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: Grid, decorators: [{
|
|
3541
4125
|
type: Directive,
|
|
3542
4126
|
args: [{ selector: 'tbw-grid' }]
|
|
3543
|
-
}], ctorParameters: () => [], propDecorators: { customStyles: [{ type: i0.Input, args: [{ isSignal: true, alias: "customStyles", required: false }] }], sortable: [{ type: i0.Input, args: [{ isSignal: true, alias: "sortable", required: false }] }], filterable: [{ type: i0.Input, args: [{ isSignal: true, alias: "filterable", required: false }] }], selectable: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectable", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], gridConfig: [{ type: i0.Input, args: [{ isSignal: true, alias: "gridConfig", required: false }] }], angularConfig: [{ type: i0.Input, args: [{ isSignal: true, alias: "angularConfig", required: false }] }], selection: [{ type: i0.Input, args: [{ isSignal: true, alias: "selection", required: false }] }], editing: [{ type: i0.Input, args: [{ isSignal: true, alias: "editing", required: false }] }], clipboard: [{ type: i0.Input, args: [{ isSignal: true, alias: "clipboard", required: false }] }], contextMenu: [{ type: i0.Input, args: [{ isSignal: true, alias: "contextMenu", required: false }] }], multiSort: [{ type: i0.Input, args: [{ isSignal: true, alias: "multiSort", required: false }] }], sorting: [{ type: i0.Input, args: [{ isSignal: true, alias: "sorting", required: false }] }], filtering: [{ type: i0.Input, args: [{ isSignal: true, alias: "filtering", required: false }] }], reorder: [{ type: i0.Input, args: [{ isSignal: true, alias: "reorder", required: false }] }], visibility: [{ type: i0.Input, args: [{ isSignal: true, alias: "visibility", required: false }] }], pinnedColumns: [{ type: i0.Input, args: [{ isSignal: true, alias: "pinnedColumns", required: false }] }], groupingColumns: [{ type: i0.Input, args: [{ isSignal: true, alias: "groupingColumns", required: false }] }], columnVirtualization: [{ type: i0.Input, args: [{ isSignal: true, alias: "columnVirtualization", required: false }] }], rowReorder: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowReorder", required: false }] }], groupingRows: [{ type: i0.Input, args: [{ isSignal: true, alias: "groupingRows", required: false }] }], pinnedRows: [{ type: i0.Input, args: [{ isSignal: true, alias: "pinnedRows", required: false }] }], tree: [{ type: i0.Input, args: [{ isSignal: true, alias: "tree", required: false }] }], masterDetail: [{ type: i0.Input, args: [{ isSignal: true, alias: "masterDetail", required: false }] }], responsive: [{ type: i0.Input, args: [{ isSignal: true, alias: "responsive", required: false }] }], undoRedo: [{ type: i0.Input, args: [{ isSignal: true, alias: "undoRedo", required: false }] }], exportFeature: [{ type: i0.Input, args: [{ isSignal: true, alias: "
|
|
4127
|
+
}], ctorParameters: () => [], propDecorators: { customStyles: [{ type: i0.Input, args: [{ isSignal: true, alias: "customStyles", required: false }] }], sortable: [{ type: i0.Input, args: [{ isSignal: true, alias: "sortable", required: false }] }], filterable: [{ type: i0.Input, args: [{ isSignal: true, alias: "filterable", required: false }] }], selectable: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectable", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], gridConfig: [{ type: i0.Input, args: [{ isSignal: true, alias: "gridConfig", required: false }] }], angularConfig: [{ type: i0.Input, args: [{ isSignal: true, alias: "angularConfig", required: false }] }], selection: [{ type: i0.Input, args: [{ isSignal: true, alias: "selection", required: false }] }], editing: [{ type: i0.Input, args: [{ isSignal: true, alias: "editing", required: false }] }], clipboard: [{ type: i0.Input, args: [{ isSignal: true, alias: "clipboard", required: false }] }], contextMenu: [{ type: i0.Input, args: [{ isSignal: true, alias: "contextMenu", required: false }] }], multiSort: [{ type: i0.Input, args: [{ isSignal: true, alias: "multiSort", required: false }] }], sorting: [{ type: i0.Input, args: [{ isSignal: true, alias: "sorting", required: false }] }], filtering: [{ type: i0.Input, args: [{ isSignal: true, alias: "filtering", required: false }] }], reorder: [{ type: i0.Input, args: [{ isSignal: true, alias: "reorder", required: false }] }], visibility: [{ type: i0.Input, args: [{ isSignal: true, alias: "visibility", required: false }] }], pinnedColumns: [{ type: i0.Input, args: [{ isSignal: true, alias: "pinnedColumns", required: false }] }], groupingColumns: [{ type: i0.Input, args: [{ isSignal: true, alias: "groupingColumns", required: false }] }], columnVirtualization: [{ type: i0.Input, args: [{ isSignal: true, alias: "columnVirtualization", required: false }] }], rowReorder: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowReorder", required: false }] }], groupingRows: [{ type: i0.Input, args: [{ isSignal: true, alias: "groupingRows", required: false }] }], pinnedRows: [{ type: i0.Input, args: [{ isSignal: true, alias: "pinnedRows", required: false }] }], tree: [{ type: i0.Input, args: [{ isSignal: true, alias: "tree", required: false }] }], masterDetail: [{ type: i0.Input, args: [{ isSignal: true, alias: "masterDetail", required: false }] }], responsive: [{ type: i0.Input, args: [{ isSignal: true, alias: "responsive", required: false }] }], undoRedo: [{ type: i0.Input, args: [{ isSignal: true, alias: "undoRedo", required: false }] }], exportFeature: [{ type: i0.Input, args: [{ isSignal: true, alias: "export", required: false }] }], print: [{ type: i0.Input, args: [{ isSignal: true, alias: "print", required: false }] }], pivot: [{ type: i0.Input, args: [{ isSignal: true, alias: "pivot", required: false }] }], serverSide: [{ type: i0.Input, args: [{ isSignal: true, alias: "serverSide", required: false }] }], cellClick: [{ type: i0.Output, args: ["cellClick"] }], rowClick: [{ type: i0.Output, args: ["rowClick"] }], cellActivate: [{ type: i0.Output, args: ["cellActivate"] }], cellChange: [{ type: i0.Output, args: ["cellChange"] }], cellCommit: [{ type: i0.Output, args: ["cellCommit"] }], rowCommit: [{ type: i0.Output, args: ["rowCommit"] }], changedRowsReset: [{ type: i0.Output, args: ["changedRowsReset"] }], sortChange: [{ type: i0.Output, args: ["sortChange"] }], filterChange: [{ type: i0.Output, args: ["filterChange"] }], columnResize: [{ type: i0.Output, args: ["columnResize"] }], columnMove: [{ type: i0.Output, args: ["columnMove"] }], columnVisibility: [{ type: i0.Output, args: ["columnVisibility"] }], columnStateChange: [{ type: i0.Output, args: ["columnStateChange"] }], selectionChange: [{ type: i0.Output, args: ["selectionChange"] }], rowMove: [{ type: i0.Output, args: ["rowMove"] }], groupToggle: [{ type: i0.Output, args: ["groupToggle"] }], treeExpand: [{ type: i0.Output, args: ["treeExpand"] }], detailExpand: [{ type: i0.Output, args: ["detailExpand"] }], responsiveChange: [{ type: i0.Output, args: ["responsiveChange"] }], copy: [{ type: i0.Output, args: ["copy"] }], paste: [{ type: i0.Output, args: ["paste"] }], undoRedoAction: [{ type: i0.Output, args: ["undoRedoAction"] }], exportComplete: [{ type: i0.Output, args: ["exportComplete"] }], printStart: [{ type: i0.Output, args: ["printStart"] }], printComplete: [{ type: i0.Output, args: ["printComplete"] }] } });
|
|
3544
4128
|
|
|
3545
4129
|
/**
|
|
3546
4130
|
* @packageDocumentation
|
|
@@ -3548,10 +4132,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImpor
|
|
|
3548
4132
|
*
|
|
3549
4133
|
* Provides directives for seamless Angular integration with the grid component.
|
|
3550
4134
|
*/
|
|
4135
|
+
// Primary export - use this
|
|
3551
4136
|
|
|
3552
4137
|
/**
|
|
3553
4138
|
* Generated bundle index. Do not edit.
|
|
3554
4139
|
*/
|
|
3555
4140
|
|
|
3556
|
-
export { AngularGridAdapter, BaseGridEditor, GRID_ICONS, GRID_TYPE_DEFAULTS, Grid, GridColumnEditor, GridColumnView, GridDetailView, GridFormArray, GridIconRegistry, GridResponsiveCard, GridToolPanel, GridTypeRegistry, TbwEditor as TbwCellEditor, TbwRenderer as TbwCellView, TbwEditor, TbwRenderer, clearFeatureRegistry, createPluginFromFeature, getFeatureFactory, getFormArrayContext, getRegisteredFeatures, injectGrid, isComponentClass, isFeatureRegistered, provideGridIcons, provideGridTypeDefaults, registerFeature };
|
|
4141
|
+
export { AngularGridAdapter, BaseGridEditor, GRID_ICONS, GRID_TYPE_DEFAULTS, Grid, GridAdapter, GridColumnEditor, GridColumnView, GridDetailView, GridFormArray, GridIconRegistry, GridLazyForm, GridResponsiveCard, GridToolPanel, GridTypeRegistry, TbwEditor as TbwCellEditor, TbwRenderer as TbwCellView, TbwEditor, TbwRenderer, clearFeatureRegistry, createPluginFromFeature, getFeatureFactory, getFormArrayContext, getLazyFormContext, getRegisteredFeatures, injectGrid, isComponentClass, isFeatureRegistered, provideGridIcons, provideGridTypeDefaults, registerFeature };
|
|
3557
4142
|
//# sourceMappingURL=toolbox-web-grid-angular.mjs.map
|