@toolbox-web/grid-angular 0.13.1 → 0.13.3
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
CHANGED
|
@@ -559,7 +559,7 @@ The grid can be used as an Angular form control with `formControlName` or `formC
|
|
|
559
559
|
```typescript
|
|
560
560
|
import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
|
561
561
|
import { FormControl, ReactiveFormsModule } from '@angular/forms';
|
|
562
|
-
import { Grid,
|
|
562
|
+
import { Grid, GridFormArray } from '@toolbox-web/grid-angular';
|
|
563
563
|
import { EditingPlugin } from '@toolbox-web/grid/plugins/editing';
|
|
564
564
|
import type { GridConfig } from '@toolbox-web/grid';
|
|
565
565
|
|
|
@@ -569,7 +569,7 @@ interface Employee {
|
|
|
569
569
|
}
|
|
570
570
|
|
|
571
571
|
@Component({
|
|
572
|
-
imports: [Grid,
|
|
572
|
+
imports: [Grid, GridFormArray, ReactiveFormsModule],
|
|
573
573
|
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
|
574
574
|
template: `
|
|
575
575
|
<tbw-grid [formControl]="employeesControl" [gridConfig]="config" style="height: 400px; display: block;" />
|
|
@@ -602,11 +602,11 @@ export class MyComponent {
|
|
|
602
602
|
```typescript
|
|
603
603
|
import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
|
604
604
|
import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms';
|
|
605
|
-
import { Grid,
|
|
605
|
+
import { Grid, GridFormArray } from '@toolbox-web/grid-angular';
|
|
606
606
|
import { EditingPlugin } from '@toolbox-web/grid/plugins/editing';
|
|
607
607
|
|
|
608
608
|
@Component({
|
|
609
|
-
imports: [Grid,
|
|
609
|
+
imports: [Grid, GridFormArray, ReactiveFormsModule],
|
|
610
610
|
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
|
611
611
|
template: `
|
|
612
612
|
<form [formGroup]="form">
|
|
@@ -670,7 +670,7 @@ Angular's form system automatically adds these classes to the grid element:
|
|
|
670
670
|
|
|
671
671
|
Additionally, when the control is disabled:
|
|
672
672
|
|
|
673
|
-
- `.form-disabled` - Added by `
|
|
673
|
+
- `.form-disabled` - Added by `GridFormArray`
|
|
674
674
|
|
|
675
675
|
You can style these states:
|
|
676
676
|
|
|
@@ -692,7 +692,7 @@ For fine-grained control over validation and form state at the cell level, use a
|
|
|
692
692
|
```typescript
|
|
693
693
|
import { Component, CUSTOM_ELEMENTS_SCHEMA, input, output } from '@angular/core';
|
|
694
694
|
import { FormArray, FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
|
|
695
|
-
import { Grid,
|
|
695
|
+
import { Grid, GridFormArray, TbwEditor, TbwRenderer } from '@toolbox-web/grid-angular';
|
|
696
696
|
import { EditingPlugin } from '@toolbox-web/grid/plugins/editing';
|
|
697
697
|
|
|
698
698
|
// Custom editor that uses the FormControl directly
|
|
@@ -731,7 +731,7 @@ export class ValidatedInputComponent {
|
|
|
731
731
|
}
|
|
732
732
|
|
|
733
733
|
@Component({
|
|
734
|
-
imports: [Grid,
|
|
734
|
+
imports: [Grid, GridFormArray, TbwRenderer, TbwEditor, ReactiveFormsModule, ValidatedInputComponent],
|
|
735
735
|
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
|
736
736
|
template: `
|
|
737
737
|
<tbw-grid [formControl]="employeesArray" [gridConfig]="config">
|
|
@@ -826,12 +826,12 @@ if (context?.hasFormGroups) {
|
|
|
826
826
|
|
|
827
827
|
The adapter provides base classes that eliminate boilerplate when building custom editors and filter panels.
|
|
828
828
|
|
|
829
|
-
| Base Class
|
|
830
|
-
|
|
|
831
|
-
| `BaseGridEditor`
|
|
832
|
-
| `BaseGridEditorCVA` | `BaseGridEditor` | Adds `ControlValueAccessor` for dual grid + standalone form use
|
|
833
|
-
| `BaseOverlayEditor` | `BaseGridEditor` | Floating overlay panel with CSS Anchor Positioning, focus gating, click-outside detection
|
|
834
|
-
| `BaseFilterPanel`
|
|
829
|
+
| Base Class | Extends | Purpose |
|
|
830
|
+
| ------------------- | ---------------- | ----------------------------------------------------------------------------------------------------- |
|
|
831
|
+
| `BaseGridEditor` | — | Common inputs (`value`, `row`, `column`, `control`), outputs (`commit`, `cancel`), validation helpers |
|
|
832
|
+
| `BaseGridEditorCVA` | `BaseGridEditor` | Adds `ControlValueAccessor` for dual grid + standalone form use |
|
|
833
|
+
| `BaseOverlayEditor` | `BaseGridEditor` | Floating overlay panel with CSS Anchor Positioning, focus gating, click-outside detection |
|
|
834
|
+
| `BaseFilterPanel` | — | Ready-made `params` input for `FilteringPlugin`, with `applyAndClose()` / `clearAndClose()` helpers |
|
|
835
835
|
|
|
836
836
|
### BaseOverlayEditor Example
|
|
837
837
|
|
|
@@ -853,7 +853,7 @@ import { BaseOverlayEditor } from '@toolbox-web/grid-angular';
|
|
|
853
853
|
<input type="date" [value]="currentValue()" (change)="selectAndClose($event.target.value)" />
|
|
854
854
|
<button (click)="hideOverlay()">Cancel</button>
|
|
855
855
|
</div>
|
|
856
|
-
|
|
856
|
+
`,
|
|
857
857
|
})
|
|
858
858
|
export class DateEditorComponent extends BaseOverlayEditor<MyRow, string> implements AfterViewInit {
|
|
859
859
|
@ViewChild('panel') panelRef!: ElementRef<HTMLElement>;
|
|
@@ -866,8 +866,12 @@ export class DateEditorComponent extends BaseOverlayEditor<MyRow, string> implem
|
|
|
866
866
|
if (this.isCellFocused()) this.showOverlay();
|
|
867
867
|
}
|
|
868
868
|
|
|
869
|
-
protected getInlineInput() {
|
|
870
|
-
|
|
869
|
+
protected getInlineInput() {
|
|
870
|
+
return this.inputRef?.nativeElement ?? null;
|
|
871
|
+
}
|
|
872
|
+
protected onOverlayOutsideClick() {
|
|
873
|
+
this.hideOverlay();
|
|
874
|
+
}
|
|
871
875
|
|
|
872
876
|
selectAndClose(date: string): void {
|
|
873
877
|
this.commitValue(date);
|
|
@@ -888,7 +892,7 @@ import { BaseFilterPanel } from '@toolbox-web/grid-angular';
|
|
|
888
892
|
<input #input (keydown.enter)="applyAndClose()" />
|
|
889
893
|
<button (click)="applyAndClose()">Apply</button>
|
|
890
894
|
<button (click)="clearAndClose()">Clear</button>
|
|
891
|
-
|
|
895
|
+
`,
|
|
892
896
|
})
|
|
893
897
|
export class TextFilterComponent extends BaseFilterPanel {
|
|
894
898
|
@ViewChild('input') input!: ElementRef<HTMLInputElement>;
|
|
@@ -908,7 +912,7 @@ export class TextFilterComponent extends BaseFilterPanel {
|
|
|
908
912
|
| Directive | Selector | Description |
|
|
909
913
|
| ------------------ | ---------------------------------------------------- | -------------------------------------- |
|
|
910
914
|
| `Grid` | `tbw-grid` | Main directive, auto-registers adapter |
|
|
911
|
-
| `
|
|
915
|
+
| `GridFormArray` | `tbw-grid[formControlName]`, `tbw-grid[formControl]` | Reactive Forms integration |
|
|
912
916
|
| `TbwRenderer` | `*tbwRenderer` | Structural directive for cell views |
|
|
913
917
|
| `TbwEditor` | `*tbwEditor` | Structural directive for cell editors |
|
|
914
918
|
| `GridColumnView` | `tbw-grid-column-view` | Nested directive for cell views |
|
|
@@ -918,12 +922,12 @@ export class TextFilterComponent extends BaseFilterPanel {
|
|
|
918
922
|
|
|
919
923
|
### Base Classes
|
|
920
924
|
|
|
921
|
-
| Class
|
|
922
|
-
|
|
|
923
|
-
| `BaseGridEditor<TRow, TValue>`
|
|
925
|
+
| Class | Description |
|
|
926
|
+
| --------------------------------- | -------------------------------------------------------------------- |
|
|
927
|
+
| `BaseGridEditor<TRow, TValue>` | Base class for inline cell editors with validation helpers |
|
|
924
928
|
| `BaseGridEditorCVA<TRow, TValue>` | `BaseGridEditor` + `ControlValueAccessor` for dual grid/form editors |
|
|
925
|
-
| `BaseOverlayEditor<TRow, TValue>` | `BaseGridEditor` + floating overlay panel infrastructure
|
|
926
|
-
| `BaseFilterPanel`
|
|
929
|
+
| `BaseOverlayEditor<TRow, TValue>` | `BaseGridEditor` + floating overlay panel infrastructure |
|
|
930
|
+
| `BaseFilterPanel` | Base class for custom filter panels with `params` input |
|
|
927
931
|
|
|
928
932
|
### Type Registry
|
|
929
933
|
|
|
@@ -951,12 +955,29 @@ export class TextFilterComponent extends BaseFilterPanel {
|
|
|
951
955
|
|
|
952
956
|
### Grid Directive Outputs
|
|
953
957
|
|
|
954
|
-
| Output
|
|
955
|
-
|
|
|
956
|
-
| `cellCommit`
|
|
957
|
-
| `rowCommit`
|
|
958
|
-
| `sortChange`
|
|
959
|
-
| `columnResize`
|
|
958
|
+
| Output | Type | Description |
|
|
959
|
+
| ------------------- | ------------------------------------------ | ---------------------------- |
|
|
960
|
+
| `cellCommit` | `OutputEmitterRef<CellCommitEvent>` | Cell value committed |
|
|
961
|
+
| `rowCommit` | `OutputEmitterRef<RowCommitEvent>` | Row edit committed |
|
|
962
|
+
| `sortChange` | `OutputEmitterRef<SortChangeDetail>` | Sort state changed |
|
|
963
|
+
| `columnResize` | `OutputEmitterRef<ColumnResizeDetail>` | Column resized |
|
|
964
|
+
| `cellClick` | `OutputEmitterRef<CellClickDetail>` | Cell clicked |
|
|
965
|
+
| `rowClick` | `OutputEmitterRef<RowClickDetail>` | Row clicked |
|
|
966
|
+
| `cellActivate` | `OutputEmitterRef<CellActivateDetail>` | Cell focus changed |
|
|
967
|
+
| `cellChange` | `OutputEmitterRef<CellChangeDetail>` | Cell value changed |
|
|
968
|
+
| `changedRowsReset` | `OutputEmitterRef<ChangedRowsResetDetail>` | Changed rows cache cleared |
|
|
969
|
+
| `filterChange` | `OutputEmitterRef<FilterChangeDetail>` | Filter state changed |
|
|
970
|
+
| `columnMove` | `OutputEmitterRef<ColumnMoveDetail>` | Column moved |
|
|
971
|
+
| `columnVisibility` | `OutputEmitterRef<ColumnVisibilityDetail>` | Column visibility changed |
|
|
972
|
+
| `columnStateChange` | `OutputEmitterRef<GridColumnState>` | Column state changed |
|
|
973
|
+
| `selectionChange` | `OutputEmitterRef<SelectionChangeDetail>` | Selection changed |
|
|
974
|
+
| `rowMove` | `OutputEmitterRef<RowMoveDetail>` | Row moved (drag & drop) |
|
|
975
|
+
| `groupToggle` | `OutputEmitterRef<GroupToggleDetail>` | Group expanded/collapsed |
|
|
976
|
+
| `treeExpand` | `OutputEmitterRef<TreeExpandDetail>` | Tree node expanded/collapsed |
|
|
977
|
+
| `detailExpand` | `OutputEmitterRef<DetailExpandDetail>` | Detail panel toggled |
|
|
978
|
+
| `responsiveChange` | `OutputEmitterRef<ResponsiveChangeDetail>` | Responsive mode changed |
|
|
979
|
+
| `copy` | `OutputEmitterRef<CopyDetail>` | Data copied to clipboard |
|
|
980
|
+
| `paste` | `OutputEmitterRef<PasteDetail>` | Data pasted from clipboard |
|
|
960
981
|
|
|
961
982
|
### GridDetailView Inputs
|
|
962
983
|
|
|
@@ -1008,12 +1029,7 @@ import type {
|
|
|
1008
1029
|
} from '@toolbox-web/grid-angular';
|
|
1009
1030
|
|
|
1010
1031
|
// Base classes for custom editors and filter panels
|
|
1011
|
-
import {
|
|
1012
|
-
BaseGridEditor,
|
|
1013
|
-
BaseGridEditorCVA,
|
|
1014
|
-
BaseOverlayEditor,
|
|
1015
|
-
BaseFilterPanel,
|
|
1016
|
-
} from '@toolbox-web/grid-angular';
|
|
1032
|
+
import { BaseGridEditor, BaseGridEditorCVA, BaseOverlayEditor, BaseFilterPanel } from '@toolbox-web/grid-angular';
|
|
1017
1033
|
|
|
1018
1034
|
// Type guard for component class detection
|
|
1019
1035
|
import { isComponentClass } from '@toolbox-web/grid-angular';
|
|
@@ -1141,6 +1141,88 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImpor
|
|
|
1141
1141
|
args: [{ selector: '[tbwEditor]' }]
|
|
1142
1142
|
}], ctorParameters: () => [] });
|
|
1143
1143
|
|
|
1144
|
+
/**
|
|
1145
|
+
* Editor Wiring Helpers
|
|
1146
|
+
*
|
|
1147
|
+
* Pure functions for wiring up commit/cancel handlers on editor components.
|
|
1148
|
+
* Extracted from GridAdapter to enable unit testing without Angular DI.
|
|
1149
|
+
*
|
|
1150
|
+
* @internal
|
|
1151
|
+
*/
|
|
1152
|
+
// #region subscribeToOutput
|
|
1153
|
+
/**
|
|
1154
|
+
* Subscribes to an Angular output on a component instance.
|
|
1155
|
+
* Works with both EventEmitter and OutputEmitterRef (signal outputs).
|
|
1156
|
+
*
|
|
1157
|
+
* @param instance - The component instance (as a plain record)
|
|
1158
|
+
* @param outputName - Name of the output property
|
|
1159
|
+
* @param callback - Callback to invoke when the output emits
|
|
1160
|
+
* @returns `true` if the output was found and subscribed, `false` otherwise
|
|
1161
|
+
* @internal
|
|
1162
|
+
*/
|
|
1163
|
+
function subscribeToOutput(instance, outputName, callback) {
|
|
1164
|
+
const output = instance[outputName];
|
|
1165
|
+
if (!output)
|
|
1166
|
+
return false;
|
|
1167
|
+
// Check if it's an Observable-like (EventEmitter or OutputEmitterRef)
|
|
1168
|
+
if (typeof output.subscribe === 'function') {
|
|
1169
|
+
output.subscribe(callback);
|
|
1170
|
+
return true;
|
|
1171
|
+
}
|
|
1172
|
+
return false;
|
|
1173
|
+
}
|
|
1174
|
+
// #endregion
|
|
1175
|
+
// #region wireEditorCallbacks
|
|
1176
|
+
/**
|
|
1177
|
+
* Wire up commit/cancel handlers for an editor component.
|
|
1178
|
+
*
|
|
1179
|
+
* Supports both Angular outputs and DOM CustomEvents. When both fire
|
|
1180
|
+
* (the BaseGridEditor pattern), a per-action flag prevents the callback
|
|
1181
|
+
* from running twice.
|
|
1182
|
+
*
|
|
1183
|
+
* @param hostElement - The host DOM element for the editor
|
|
1184
|
+
* @param instance - The component instance (as a plain record)
|
|
1185
|
+
* @param commit - Callback to invoke when committing a value
|
|
1186
|
+
* @param cancel - Callback to invoke when cancelling the edit
|
|
1187
|
+
* @internal
|
|
1188
|
+
*/
|
|
1189
|
+
function wireEditorCallbacks(hostElement, instance, commit, cancel) {
|
|
1190
|
+
// Guard: when both Angular output AND DOM event fire (BaseGridEditor.commitValue
|
|
1191
|
+
// emits both), only the first should call commit/cancel(). The flags prevent
|
|
1192
|
+
// double-fires that cause redundant cell-commit events and extra dirty-tracking work.
|
|
1193
|
+
let commitHandledByOutput = false;
|
|
1194
|
+
let cancelHandledByOutput = false;
|
|
1195
|
+
subscribeToOutput(instance, 'commit', (value) => {
|
|
1196
|
+
commitHandledByOutput = true;
|
|
1197
|
+
commit(value);
|
|
1198
|
+
});
|
|
1199
|
+
subscribeToOutput(instance, 'cancel', () => {
|
|
1200
|
+
cancelHandledByOutput = true;
|
|
1201
|
+
cancel();
|
|
1202
|
+
});
|
|
1203
|
+
// Also listen for DOM CustomEvents as a fallback for editors that don't
|
|
1204
|
+
// have Angular commit/cancel outputs (e.g., third-party web components).
|
|
1205
|
+
hostElement.addEventListener('commit', (e) => {
|
|
1206
|
+
e.stopPropagation();
|
|
1207
|
+
if (commitHandledByOutput) {
|
|
1208
|
+
// Already handled by the Angular output subscription — reset and skip.
|
|
1209
|
+
commitHandledByOutput = false;
|
|
1210
|
+
return;
|
|
1211
|
+
}
|
|
1212
|
+
const customEvent = e;
|
|
1213
|
+
commit(customEvent.detail);
|
|
1214
|
+
});
|
|
1215
|
+
hostElement.addEventListener('cancel', (e) => {
|
|
1216
|
+
e.stopPropagation();
|
|
1217
|
+
if (cancelHandledByOutput) {
|
|
1218
|
+
cancelHandledByOutput = false;
|
|
1219
|
+
return;
|
|
1220
|
+
}
|
|
1221
|
+
cancel();
|
|
1222
|
+
});
|
|
1223
|
+
}
|
|
1224
|
+
// #endregion
|
|
1225
|
+
|
|
1144
1226
|
/**
|
|
1145
1227
|
* Type-level default registry for Angular applications.
|
|
1146
1228
|
*
|
|
@@ -1872,24 +1954,6 @@ class GridAdapter {
|
|
|
1872
1954
|
componentRef.changeDetectorRef.detectChanges();
|
|
1873
1955
|
return { hostElement, componentRef };
|
|
1874
1956
|
}
|
|
1875
|
-
/**
|
|
1876
|
-
* Wires up commit/cancel handlers for an editor component.
|
|
1877
|
-
* Supports both Angular outputs and DOM CustomEvents.
|
|
1878
|
-
* @internal
|
|
1879
|
-
*/
|
|
1880
|
-
wireEditorCallbacks(hostElement, componentRef, commit, cancel) {
|
|
1881
|
-
// Subscribe to Angular outputs (commit/cancel) on the component instance.
|
|
1882
|
-
// This works with Angular's output() signal API.
|
|
1883
|
-
const instance = componentRef.instance;
|
|
1884
|
-
this.subscribeToOutput(instance, 'commit', commit);
|
|
1885
|
-
this.subscribeToOutput(instance, 'cancel', cancel);
|
|
1886
|
-
// Also listen for DOM events as fallback (for components that dispatch CustomEvents)
|
|
1887
|
-
hostElement.addEventListener('commit', (e) => {
|
|
1888
|
-
const customEvent = e;
|
|
1889
|
-
commit(customEvent.detail);
|
|
1890
|
-
});
|
|
1891
|
-
hostElement.addEventListener('cancel', () => cancel());
|
|
1892
|
-
}
|
|
1893
1957
|
/**
|
|
1894
1958
|
* Creates a renderer function from an Angular component class.
|
|
1895
1959
|
* @internal
|
|
@@ -1935,7 +1999,7 @@ class GridAdapter {
|
|
|
1935
1999
|
row: ctx.row,
|
|
1936
2000
|
column: ctx.column,
|
|
1937
2001
|
}, true);
|
|
1938
|
-
|
|
2002
|
+
wireEditorCallbacks(hostElement, componentRef.instance, (value) => ctx.commit(value), () => ctx.cancel());
|
|
1939
2003
|
// Auto-update editor when value changes externally (e.g., via updateRow cascade).
|
|
1940
2004
|
// This keeps Angular component editors in sync without manual DOM patching.
|
|
1941
2005
|
ctx.onValueChange?.((newVal) => {
|
|
@@ -1987,20 +2051,6 @@ class GridAdapter {
|
|
|
1987
2051
|
container.appendChild(hostElement);
|
|
1988
2052
|
};
|
|
1989
2053
|
}
|
|
1990
|
-
/**
|
|
1991
|
-
* Subscribes to an Angular output on a component instance.
|
|
1992
|
-
* Works with both EventEmitter and OutputEmitterRef (signal outputs).
|
|
1993
|
-
* @internal
|
|
1994
|
-
*/
|
|
1995
|
-
subscribeToOutput(instance, outputName, callback) {
|
|
1996
|
-
const output = instance[outputName];
|
|
1997
|
-
if (!output)
|
|
1998
|
-
return;
|
|
1999
|
-
// Check if it's an Observable-like (EventEmitter or OutputEmitterRef)
|
|
2000
|
-
if (typeof output.subscribe === 'function') {
|
|
2001
|
-
output.subscribe(callback);
|
|
2002
|
-
}
|
|
2003
|
-
}
|
|
2004
2054
|
/**
|
|
2005
2055
|
* Sets component inputs using Angular's setInput API.
|
|
2006
2056
|
* @internal
|
|
@@ -2261,7 +2311,7 @@ function injectGrid() {
|
|
|
2261
2311
|
// Wait for grid to be ready
|
|
2262
2312
|
gridElement.ready?.().then(async () => {
|
|
2263
2313
|
isReady.set(true);
|
|
2264
|
-
const effectiveConfig = gridElement.getConfig?.();
|
|
2314
|
+
const effectiveConfig = await gridElement.getConfig?.();
|
|
2265
2315
|
if (effectiveConfig) {
|
|
2266
2316
|
config.set(effectiveConfig);
|
|
2267
2317
|
}
|