@toolbox-web/grid-angular 2.6.1 → 2.6.2
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 +2 -0
- package/fesm2022/toolbox-web-grid-angular-features-grouping-rows.mjs +14 -6
- package/fesm2022/toolbox-web-grid-angular-features-grouping-rows.mjs.map +1 -1
- package/fesm2022/toolbox-web-grid-angular-features-master-detail.mjs +39 -20
- package/fesm2022/toolbox-web-grid-angular-features-master-detail.mjs.map +1 -1
- package/fesm2022/toolbox-web-grid-angular-features-undo-redo.mjs +2 -2
- package/fesm2022/toolbox-web-grid-angular-features-undo-redo.mjs.map +1 -1
- package/fesm2022/toolbox-web-grid-angular.mjs.map +1 -1
- package/package.json +1 -1
- package/types/toolbox-web-grid-angular-features-grouping-rows.d.ts +10 -3
- package/types/toolbox-web-grid-angular-features-grouping-rows.d.ts.map +1 -1
- package/types/toolbox-web-grid-angular-features-master-detail.d.ts +12 -4
- package/types/toolbox-web-grid-angular-features-master-detail.d.ts.map +1 -1
- package/types/toolbox-web-grid-angular-features-undo-redo.d.ts +3 -1
- package/types/toolbox-web-grid-angular-features-undo-redo.d.ts.map +1 -1
- package/types/toolbox-web-grid-angular.d.ts.map +1 -1
package/README.md
CHANGED
|
@@ -1204,11 +1204,13 @@ export class TextFilterComponent extends BaseFilterPanel {
|
|
|
1204
1204
|
| `selectionChange` | `OutputEmitterRef<SelectionChangeDetail>` | Selection changed |
|
|
1205
1205
|
| `rowMove` | `OutputEmitterRef<RowMoveDetail>` | Row moved (drag & drop) |
|
|
1206
1206
|
| `groupToggle` | `OutputEmitterRef<GroupToggleDetail>` | Group expanded/collapsed |
|
|
1207
|
+
| `groupLoadError` | `OutputEmitterRef<GroupLoadErrorDetail>` | Server group rows failed |
|
|
1207
1208
|
| `treeExpand` | `OutputEmitterRef<TreeExpandDetail>` | Tree node expanded/collapsed |
|
|
1208
1209
|
| `treeLoadStart` | `OutputEmitterRef<TreeLoadStartDetail>` | Lazy child loading started |
|
|
1209
1210
|
| `treeLoadEnd` | `OutputEmitterRef<TreeLoadEndDetail>` | Lazy child loading finished |
|
|
1210
1211
|
| `treeLoadError` | `OutputEmitterRef<TreeLoadErrorDetail>` | Lazy child loading failed |
|
|
1211
1212
|
| `detailExpand` | `OutputEmitterRef<DetailExpandDetail>` | Detail panel toggled |
|
|
1213
|
+
| `detailLoadError` | `OutputEmitterRef<DetailLoadErrorDetail>` | Server detail data failed |
|
|
1212
1214
|
| `responsiveChange` | `OutputEmitterRef<ResponsiveChangeDetail>` | Responsive mode changed |
|
|
1213
1215
|
| `copy` | `OutputEmitterRef<CopyDetail>` | Data copied to clipboard |
|
|
1214
1216
|
| `paste` | `OutputEmitterRef<PasteDetail>` | Data pasted from clipboard |
|
|
@@ -5,13 +5,13 @@ import { inject, ElementRef, input, output, Directive } from '@angular/core';
|
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* `GridGroupingRowsDirective` — owns `[groupingRows]` and the group
|
|
8
|
-
* toggle/expand/collapse outputs on `<tbw-grid>`. See
|
|
8
|
+
* toggle/expand/collapse/load-error outputs on `<tbw-grid>`. See
|
|
9
9
|
* `GridFilteringDirective` for the full rationale.
|
|
10
10
|
*
|
|
11
11
|
* @category Directive
|
|
12
12
|
*/
|
|
13
13
|
/**
|
|
14
|
-
* Owns the binding(s) `[groupingRows], [groupToggle], [groupExpand], [groupCollapse]` on `<tbw-grid>` for the matching feature plugin. See `GridFilteringDirective` for the full rationale.
|
|
14
|
+
* Owns the binding(s) `[groupingRows], [groupToggle], [groupExpand], [groupCollapse], [groupLoadError]` on `<tbw-grid>` for the matching feature plugin. See `GridFilteringDirective` for the full rationale.
|
|
15
15
|
*
|
|
16
16
|
* @category Directive
|
|
17
17
|
*/
|
|
@@ -22,8 +22,15 @@ class GridGroupingRowsDirective {
|
|
|
22
22
|
groupToggle = output();
|
|
23
23
|
groupExpand = output();
|
|
24
24
|
groupCollapse = output();
|
|
25
|
+
/**
|
|
26
|
+
* Fired when loading a pre-defined group's rows through the ServerSide data
|
|
27
|
+
* source (`getChildRows()`) fails. Re-expanding the group retries the fetch.
|
|
28
|
+
*
|
|
29
|
+
* @since 2.7.0
|
|
30
|
+
*/
|
|
31
|
+
groupLoadError = output();
|
|
25
32
|
listeners = new Map();
|
|
26
|
-
static EVENTS = ['group-toggle', 'group-expand', 'group-collapse'];
|
|
33
|
+
static EVENTS = ['group-toggle', 'group-expand', 'group-collapse', 'group-load-error'];
|
|
27
34
|
constructor() {
|
|
28
35
|
const grid = this.elementRef.nativeElement;
|
|
29
36
|
registerFeatureClaim(grid, 'groupingRows', () => this.groupingRows());
|
|
@@ -40,6 +47,7 @@ class GridGroupingRowsDirective {
|
|
|
40
47
|
wire('group-toggle', this.groupToggle);
|
|
41
48
|
wire('group-expand', this.groupExpand);
|
|
42
49
|
wire('group-collapse', this.groupCollapse);
|
|
50
|
+
wire('group-load-error', this.groupLoadError);
|
|
43
51
|
}
|
|
44
52
|
ngOnDestroy() {
|
|
45
53
|
const grid = this.elementRef.nativeElement;
|
|
@@ -51,15 +59,15 @@ class GridGroupingRowsDirective {
|
|
|
51
59
|
unclaimEvent(grid, ev);
|
|
52
60
|
}
|
|
53
61
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: GridGroupingRowsDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
54
|
-
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.6", type: GridGroupingRowsDirective, isStandalone: true, selector: "tbw-grid[groupingRows], tbw-grid[groupToggle], tbw-grid[groupExpand], tbw-grid[groupCollapse]", inputs: { groupingRows: { classPropertyName: "groupingRows", publicName: "groupingRows", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { groupToggle: "groupToggle", groupExpand: "groupExpand", groupCollapse: "groupCollapse" }, ngImport: i0 });
|
|
62
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.6", type: GridGroupingRowsDirective, isStandalone: true, selector: "tbw-grid[groupingRows], tbw-grid[groupToggle], tbw-grid[groupExpand], tbw-grid[groupCollapse], tbw-grid[groupLoadError]", inputs: { groupingRows: { classPropertyName: "groupingRows", publicName: "groupingRows", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { groupToggle: "groupToggle", groupExpand: "groupExpand", groupCollapse: "groupCollapse", groupLoadError: "groupLoadError" }, ngImport: i0 });
|
|
55
63
|
}
|
|
56
64
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: GridGroupingRowsDirective, decorators: [{
|
|
57
65
|
type: Directive,
|
|
58
66
|
args: [{
|
|
59
|
-
selector: 'tbw-grid[groupingRows], tbw-grid[groupToggle], tbw-grid[groupExpand], tbw-grid[groupCollapse]',
|
|
67
|
+
selector: 'tbw-grid[groupingRows], tbw-grid[groupToggle], tbw-grid[groupExpand], tbw-grid[groupCollapse], tbw-grid[groupLoadError]',
|
|
60
68
|
standalone: true,
|
|
61
69
|
}]
|
|
62
|
-
}], ctorParameters: () => [], propDecorators: { groupingRows: [{ type: i0.Input, args: [{ isSignal: true, alias: "groupingRows", required: false }] }], groupToggle: [{ type: i0.Output, args: ["groupToggle"] }], groupExpand: [{ type: i0.Output, args: ["groupExpand"] }], groupCollapse: [{ type: i0.Output, args: ["groupCollapse"] }] } });
|
|
70
|
+
}], ctorParameters: () => [], propDecorators: { groupingRows: [{ type: i0.Input, args: [{ isSignal: true, alias: "groupingRows", required: false }] }], groupToggle: [{ type: i0.Output, args: ["groupToggle"] }], groupExpand: [{ type: i0.Output, args: ["groupExpand"] }], groupCollapse: [{ type: i0.Output, args: ["groupCollapse"] }], groupLoadError: [{ type: i0.Output, args: ["groupLoadError"] }] } });
|
|
63
71
|
|
|
64
72
|
/**
|
|
65
73
|
* Row grouping feature for @toolbox-web/grid-angular
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"toolbox-web-grid-angular-features-grouping-rows.mjs","sources":["../../../../libs/grid-angular/features/grouping-rows/src/grid-grouping-rows.directive.ts","../../../../libs/grid-angular/features/grouping-rows/src/index.ts","../../../../libs/grid-angular/features/grouping-rows/src/toolbox-web-grid-angular-features-grouping-rows.ts"],"sourcesContent":["/**\n * `GridGroupingRowsDirective` — owns `[groupingRows]` and the group\n * toggle/expand/collapse outputs on `<tbw-grid>`. See\n * `GridFilteringDirective` for the full rationale.\n *\n * @category Directive\n */\nimport { Directive, ElementRef, inject, input, OnDestroy, OnInit, output, type Type } from '@angular/core';\nimport type { DataGridElement } from '@toolbox-web/grid';\nimport { claimEvent, registerFeatureClaim, unclaimEvent, unregisterFeatureClaim } from '@toolbox-web/grid-angular';\nimport type {\n GroupingRowsConfig as CoreGroupingRowsConfig,\n GroupCollapseDetail,\n GroupExpandDetail,\n GroupRowRenderParams,\n GroupToggleDetail,\n} from '@toolbox-web/grid/plugins/grouping-rows';\n\n/**\n * Grouping-rows config widened to accept an Angular component class as\n * `groupRowRenderer`.\n *\n * Component instances receive the {@link GroupRowRenderParams} fields as\n * inputs (`key`, `value`, `depth`, `rows`, `expanded`, `toggleExpand`).\n *\n * @since 1.7.0\n */\nexport type GroupingRowsConfig = Omit<CoreGroupingRowsConfig, 'groupRowRenderer'> & {\n groupRowRenderer?: CoreGroupingRowsConfig['groupRowRenderer'] | Type<unknown>;\n};\n\n/**\n * Owns the binding(s) `[groupingRows], [groupToggle], [groupExpand], [groupCollapse]` on `<tbw-grid>` for the matching feature plugin. See `GridFilteringDirective` for the full rationale.\n *\n * @category Directive\n */\n@Directive({\n selector
|
|
1
|
+
{"version":3,"file":"toolbox-web-grid-angular-features-grouping-rows.mjs","sources":["../../../../libs/grid-angular/features/grouping-rows/src/grid-grouping-rows.directive.ts","../../../../libs/grid-angular/features/grouping-rows/src/index.ts","../../../../libs/grid-angular/features/grouping-rows/src/toolbox-web-grid-angular-features-grouping-rows.ts"],"sourcesContent":["/**\n * `GridGroupingRowsDirective` — owns `[groupingRows]` and the group\n * toggle/expand/collapse/load-error outputs on `<tbw-grid>`. See\n * `GridFilteringDirective` for the full rationale.\n *\n * @category Directive\n */\nimport { Directive, ElementRef, inject, input, OnDestroy, OnInit, output, type Type } from '@angular/core';\nimport type { DataGridElement } from '@toolbox-web/grid';\nimport { claimEvent, registerFeatureClaim, unclaimEvent, unregisterFeatureClaim } from '@toolbox-web/grid-angular';\nimport type {\n GroupingRowsConfig as CoreGroupingRowsConfig,\n GroupCollapseDetail,\n GroupExpandDetail,\n GroupLoadErrorDetail,\n GroupRowRenderParams,\n GroupToggleDetail,\n} from '@toolbox-web/grid/plugins/grouping-rows';\n\n/**\n * Grouping-rows config widened to accept an Angular component class as\n * `groupRowRenderer`.\n *\n * Component instances receive the {@link GroupRowRenderParams} fields as\n * inputs (`key`, `value`, `depth`, `rows`, `expanded`, `toggleExpand`).\n *\n * @since 1.7.0\n */\nexport type GroupingRowsConfig = Omit<CoreGroupingRowsConfig, 'groupRowRenderer'> & {\n groupRowRenderer?: CoreGroupingRowsConfig['groupRowRenderer'] | Type<unknown>;\n};\n\n/**\n * Owns the binding(s) `[groupingRows], [groupToggle], [groupExpand], [groupCollapse], [groupLoadError]` on `<tbw-grid>` for the matching feature plugin. See `GridFilteringDirective` for the full rationale.\n *\n * @category Directive\n */\n@Directive({\n selector:\n 'tbw-grid[groupingRows], tbw-grid[groupToggle], tbw-grid[groupExpand], tbw-grid[groupCollapse], tbw-grid[groupLoadError]',\n standalone: true,\n})\nexport class GridGroupingRowsDirective implements OnInit, OnDestroy {\n private readonly elementRef = inject(ElementRef<DataGridElement>);\n\n readonly groupingRows = input<GroupingRowsConfig>();\n readonly groupToggle = output<GroupToggleDetail>();\n readonly groupExpand = output<GroupExpandDetail>();\n readonly groupCollapse = output<GroupCollapseDetail>();\n /**\n * Fired when loading a pre-defined group's rows through the ServerSide data\n * source (`getChildRows()`) fails. Re-expanding the group retries the fetch.\n *\n * @since 2.7.0\n */\n readonly groupLoadError = output<GroupLoadErrorDetail>();\n\n private readonly listeners = new Map<string, (e: Event) => void>();\n private static readonly EVENTS = ['group-toggle', 'group-expand', 'group-collapse', 'group-load-error'] as const;\n\n constructor() {\n const grid = this.elementRef.nativeElement;\n registerFeatureClaim(grid, 'groupingRows', () => this.groupingRows());\n for (const ev of GridGroupingRowsDirective.EVENTS) claimEvent(grid, ev);\n }\n\n ngOnInit(): void {\n const grid = this.elementRef.nativeElement;\n const wire = <T>(name: string, out: { emit: (v: T) => void }): void => {\n const l = (e: Event): void => out.emit((e as CustomEvent<T>).detail);\n grid.addEventListener(name, l);\n this.listeners.set(name, l);\n };\n wire<GroupToggleDetail>('group-toggle', this.groupToggle);\n wire<GroupExpandDetail>('group-expand', this.groupExpand);\n wire<GroupCollapseDetail>('group-collapse', this.groupCollapse);\n wire<GroupLoadErrorDetail>('group-load-error', this.groupLoadError);\n }\n\n ngOnDestroy(): void {\n const grid = this.elementRef.nativeElement;\n for (const [name, l] of this.listeners) grid.removeEventListener(name, l);\n this.listeners.clear();\n unregisterFeatureClaim(grid, 'groupingRows');\n for (const ev of GridGroupingRowsDirective.EVENTS) unclaimEvent(grid, ev);\n }\n}\n","/**\n * Row grouping feature for @toolbox-web/grid-angular\n *\n * Import this module to enable the `groupingRows` input on Grid directive.\n *\n * @example\n * ```typescript\n * import '@toolbox-web/grid-angular/features/grouping-rows';\n *\n * <tbw-grid [groupingRows]=\"{ groupBy: ['department'] }\" />\n * ```\n *\n * @packageDocumentation\n */\n\nimport { isComponentClass, registerFeatureConfigPreprocessor } from '@toolbox-web/grid-angular';\nimport '@toolbox-web/grid/features/grouping-rows';\nimport type {\n GroupingRowsConfig as CoreGroupingRowsConfig,\n GroupRowRenderParams,\n} from '@toolbox-web/grid/plugins/grouping-rows';\nexport { GridGroupingRowsDirective, type GroupingRowsConfig } from './grid-grouping-rows.directive';\nexport type { _Augmentation as _GroupingRowsAugmentation } from '@toolbox-web/grid/features/grouping-rows';\n\n// Bridge any Angular component classes embedded in the user-supplied config\n// (e.g. group-row renderer) to plain renderer functions before the core\n// plugin factory consumes the config.\nregisterFeatureConfigPreprocessor('groupingRows', (config, adapter) => {\n if (!config || typeof config !== 'object') return config;\n const cfg = config as CoreGroupingRowsConfig;\n if (cfg.groupRowRenderer && isComponentClass(cfg.groupRowRenderer)) {\n const mount = adapter.mountComponentRenderer<GroupRowRenderParams>(cfg.groupRowRenderer, (p) => ({\n key: p.key,\n value: p.value,\n depth: p.depth,\n rows: p.rows,\n expanded: p.expanded,\n toggleExpand: p.toggleExpand,\n }));\n return { ...cfg, groupRowRenderer: (params: GroupRowRenderParams) => mount(params).hostElement };\n }\n return cfg;\n});\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
|
|
@@ -102,8 +102,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImpor
|
|
|
102
102
|
}], 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 }] }] } });
|
|
103
103
|
|
|
104
104
|
/**
|
|
105
|
-
* `GridMasterDetailDirective` — owns `[masterDetail]
|
|
106
|
-
* on `<tbw-grid>`. See `GridFilteringDirective` for the
|
|
105
|
+
* `GridMasterDetailDirective` — owns `[masterDetail]`, `(detailExpand)` and
|
|
106
|
+
* `(detailLoadError)` on `<tbw-grid>`. See `GridFilteringDirective` for the
|
|
107
|
+
* full rationale.
|
|
107
108
|
*
|
|
108
109
|
* Note: this directive is independent of the higher-level
|
|
109
110
|
* `<tbw-grid-detail>` template element + `GridDetailView` wrapper, which
|
|
@@ -113,7 +114,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImpor
|
|
|
113
114
|
* @category Directive
|
|
114
115
|
*/
|
|
115
116
|
/**
|
|
116
|
-
* Owns the binding(s) `[masterDetail], [detailExpand]` on `<tbw-grid>` for the matching feature plugin. See `GridFilteringDirective` for the full rationale.
|
|
117
|
+
* Owns the binding(s) `[masterDetail], [detailExpand], [detailLoadError]` on `<tbw-grid>` for the matching feature plugin. See `GridFilteringDirective` for the full rationale.
|
|
117
118
|
*
|
|
118
119
|
* @category Directive
|
|
119
120
|
*/
|
|
@@ -122,36 +123,50 @@ class GridMasterDetailDirective {
|
|
|
122
123
|
masterDetail = input(/* @ts-ignore */
|
|
123
124
|
...(ngDevMode ? [undefined, { debugName: "masterDetail" }] : /* istanbul ignore next */ []));
|
|
124
125
|
detailExpand = output();
|
|
125
|
-
|
|
126
|
+
/**
|
|
127
|
+
* Fired when loading detail data through the ServerSide data source
|
|
128
|
+
* (`getChildRows()`) fails. Re-expanding the row retries the fetch.
|
|
129
|
+
*
|
|
130
|
+
* @since 2.7.0
|
|
131
|
+
*/
|
|
132
|
+
detailLoadError = output();
|
|
133
|
+
listeners = new Map();
|
|
134
|
+
static EVENTS = ['detail-expand', 'detail-load-error'];
|
|
126
135
|
constructor() {
|
|
127
136
|
const grid = this.elementRef.nativeElement;
|
|
128
137
|
registerFeatureClaim(grid, 'masterDetail', () => this.masterDetail());
|
|
129
|
-
|
|
138
|
+
for (const ev of GridMasterDetailDirective.EVENTS)
|
|
139
|
+
claimEvent(grid, ev);
|
|
130
140
|
}
|
|
131
141
|
ngOnInit() {
|
|
132
142
|
const grid = this.elementRef.nativeElement;
|
|
133
|
-
|
|
134
|
-
|
|
143
|
+
const wire = (name, out) => {
|
|
144
|
+
const l = (e) => out.emit(e.detail);
|
|
145
|
+
grid.addEventListener(name, l);
|
|
146
|
+
this.listeners.set(name, l);
|
|
147
|
+
};
|
|
148
|
+
wire('detail-expand', this.detailExpand);
|
|
149
|
+
wire('detail-load-error', this.detailLoadError);
|
|
135
150
|
}
|
|
136
151
|
ngOnDestroy() {
|
|
137
152
|
const grid = this.elementRef.nativeElement;
|
|
138
|
-
|
|
139
|
-
grid.removeEventListener(
|
|
140
|
-
|
|
141
|
-
}
|
|
153
|
+
for (const [name, l] of this.listeners)
|
|
154
|
+
grid.removeEventListener(name, l);
|
|
155
|
+
this.listeners.clear();
|
|
142
156
|
unregisterFeatureClaim(grid, 'masterDetail');
|
|
143
|
-
|
|
157
|
+
for (const ev of GridMasterDetailDirective.EVENTS)
|
|
158
|
+
unclaimEvent(grid, ev);
|
|
144
159
|
}
|
|
145
160
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: GridMasterDetailDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
146
|
-
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.6", type: GridMasterDetailDirective, isStandalone: true, selector: "tbw-grid[masterDetail], tbw-grid[detailExpand]", inputs: { masterDetail: { classPropertyName: "masterDetail", publicName: "masterDetail", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { detailExpand: "detailExpand" }, ngImport: i0 });
|
|
161
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.6", type: GridMasterDetailDirective, isStandalone: true, selector: "tbw-grid[masterDetail], tbw-grid[detailExpand], tbw-grid[detailLoadError]", inputs: { masterDetail: { classPropertyName: "masterDetail", publicName: "masterDetail", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { detailExpand: "detailExpand", detailLoadError: "detailLoadError" }, ngImport: i0 });
|
|
147
162
|
}
|
|
148
163
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: GridMasterDetailDirective, decorators: [{
|
|
149
164
|
type: Directive,
|
|
150
165
|
args: [{
|
|
151
|
-
selector: 'tbw-grid[masterDetail], tbw-grid[detailExpand]',
|
|
166
|
+
selector: 'tbw-grid[masterDetail], tbw-grid[detailExpand], tbw-grid[detailLoadError]',
|
|
152
167
|
standalone: true,
|
|
153
168
|
}]
|
|
154
|
-
}], ctorParameters: () => [], propDecorators: { masterDetail: [{ type: i0.Input, args: [{ isSignal: true, alias: "masterDetail", required: false }] }], detailExpand: [{ type: i0.Output, args: ["detailExpand"] }] } });
|
|
169
|
+
}], ctorParameters: () => [], propDecorators: { masterDetail: [{ type: i0.Input, args: [{ isSignal: true, alias: "masterDetail", required: false }] }], detailExpand: [{ type: i0.Output, args: ["detailExpand"] }], detailLoadError: [{ type: i0.Output, args: ["detailLoadError"] }] } });
|
|
155
170
|
|
|
156
171
|
/**
|
|
157
172
|
* Master-detail feature for @toolbox-web/grid-angular
|
|
@@ -243,14 +258,18 @@ registerFeatureConfigPreprocessor('masterDetail', (config, adapter) => {
|
|
|
243
258
|
row: ctx.row,
|
|
244
259
|
rowIndex: ctx.rowIndex,
|
|
245
260
|
}));
|
|
261
|
+
// Cached per row so re-renders reuse the instance, but only while it is alive:
|
|
262
|
+
// MasterDetailPlugin calls `adapter.unmount()` (destroying the ComponentRef)
|
|
263
|
+
// when a detail panel is collapsed, scrolled out, or re-rendered after async
|
|
264
|
+
// data/error, and the next render must mount a fresh component.
|
|
246
265
|
const cached = new Map();
|
|
247
266
|
const detailRenderer = (row, rowIndex) => {
|
|
248
267
|
const existing = cached.get(row);
|
|
249
|
-
if (existing)
|
|
250
|
-
return existing;
|
|
251
|
-
const
|
|
252
|
-
cached.set(row,
|
|
253
|
-
return hostElement;
|
|
268
|
+
if (existing && !existing.componentRef.hostView.destroyed)
|
|
269
|
+
return existing.hostElement;
|
|
270
|
+
const mounted = mount({ row, rowIndex });
|
|
271
|
+
cached.set(row, mounted);
|
|
272
|
+
return mounted.hostElement;
|
|
254
273
|
};
|
|
255
274
|
return { ...cfg, detailRenderer };
|
|
256
275
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"toolbox-web-grid-angular-features-master-detail.mjs","sources":["../../../../libs/grid-angular/features/master-detail/src/grid-detail-view.directive.ts","../../../../libs/grid-angular/features/master-detail/src/grid-master-detail.directive.ts","../../../../libs/grid-angular/features/master-detail/src/index.ts","../../../../libs/grid-angular/features/master-detail/src/toolbox-web-grid-angular-features-master-detail.ts"],"sourcesContent":["import { contentChild, Directive, effect, ElementRef, inject, input, TemplateRef } from '@angular/core';\nimport type { ExpandCollapseAnimation } from '@toolbox-web/grid';\n\n/**\n * Context object passed to the detail renderer template.\n * Contains the row data for the expanded detail view.\n * @since 0.1.0\n */\nexport interface GridDetailContext<TRow = unknown> {\n /** The row data (implicit binding for let-row) */\n $implicit: TRow;\n /** The row data (explicit binding) */\n row: TRow;\n}\n\n// Global registry mapping DOM elements to their templates\nconst detailTemplateRegistry = new Map<HTMLElement, TemplateRef<GridDetailContext>>();\n\n/**\n * Gets the detail template registered for a given grid element.\n * Used by AngularGridAdapter to retrieve templates at render time.\n * @since 0.1.0\n */\nexport function getDetailTemplate(gridElement: HTMLElement): TemplateRef<GridDetailContext> | undefined {\n // Look for tbw-grid-detail child and get its template\n const detailElement = gridElement.querySelector('tbw-grid-detail');\n if (detailElement) {\n return detailTemplateRegistry.get(detailElement as HTMLElement);\n }\n return undefined;\n}\n\n/**\n * Directive that captures an `<ng-template>` for use as a master-detail row renderer.\n *\n * This enables declarative Angular component usage for expandable detail rows\n * that appear below the main row when expanded.\n *\n * ## Usage\n *\n * ```html\n * <tbw-grid [rows]=\"rows\" [gridConfig]=\"config\">\n * <tbw-grid-detail [showExpandColumn]=\"true\" animation=\"slide\">\n * <ng-template let-row>\n * <app-detail-panel [employee]=\"row\" />\n * </ng-template>\n * </tbw-grid-detail>\n * </tbw-grid>\n * ```\n *\n * The template context provides:\n * - `$implicit` / `row`: The full row data object\n *\n * Import the directive from the master-detail feature entry:\n *\n * ```typescript\n * import { GridDetailView } from '@toolbox-web/grid-angular/features/master-detail';\n *\n * @Component({\n * imports: [GridDetailView],\n * // ...\n * })\n * ```\n *\n * @example\n * ```html\n * <tbw-grid [rows]=\"rows\" [gridConfig]=\"config\">\n * <tbw-grid-detail [showExpandColumn]=\"true\" animation=\"slide\">\n * <ng-template let-row>\n * <app-detail-panel [employee]=\"row\" />\n * </ng-template>\n * </tbw-grid-detail>\n * </tbw-grid>\n * ```\n *\n * @category Directive\n * @since 0.1.0\n */\n@Directive({ selector: 'tbw-grid-detail' })\nexport class GridDetailView {\n private elementRef = inject(ElementRef<HTMLElement>);\n\n /** Whether to show the expand/collapse column. Default: true */\n showExpandColumn = input<boolean>(true);\n\n /** Animation style for expand/collapse. Default: 'slide' */\n animation = input<ExpandCollapseAnimation>('slide');\n\n /**\n * Query for the ng-template content child.\n */\n template = contentChild(TemplateRef<GridDetailContext>);\n\n /** Effect that triggers when the template is available */\n private onTemplateReceived = effect(() => {\n const template = this.template();\n if (template) {\n // Register the template for this element\n detailTemplateRegistry.set(this.elementRef.nativeElement, template);\n }\n });\n\n /**\n * Static type guard for template context.\n * Enables type inference in templates.\n */\n static ngTemplateContextGuard(dir: GridDetailView, ctx: unknown): ctx is GridDetailContext {\n return true;\n }\n}\n","/**\n * `GridMasterDetailDirective` — owns `[masterDetail]` and `(detailExpand)`\n * on `<tbw-grid>`. See `GridFilteringDirective` for the full rationale.\n *\n * Note: this directive is independent of the higher-level\n * `<tbw-grid-detail>` template element + `GridDetailView` wrapper, which\n * uses a different bridging mechanism (template registration). Apps using\n * the template form do not need this directive.\n *\n * @category Directive\n */\nimport { Directive, ElementRef, inject, input, OnDestroy, OnInit, output, type Type } from '@angular/core';\nimport type { DataGridElement } from '@toolbox-web/grid';\nimport { claimEvent, registerFeatureClaim, unclaimEvent, unregisterFeatureClaim } from '@toolbox-web/grid-angular';\nimport type {\n MasterDetailConfig as CoreMasterDetailConfig,\n DetailExpandDetail,\n} from '@toolbox-web/grid/plugins/master-detail';\n\n/**\n * Angular-specific master-detail config that allows an Angular component class\n * as `detailRenderer`.\n *\n * Extends the core `MasterDetailConfig` to accept a `Type<unknown>` for\n * `detailRenderer` in addition to the vanilla\n * `(row, rowIndex) => HTMLElement | string` signature. Bridging to vanilla\n * DOM is handled by the side-effect import\n * `@toolbox-web/grid-angular/features/master-detail`.\n *\n * Re-exported from the feature entry under the same name as the core type\n * so Angular consumers see a single canonical `MasterDetailConfig` from\n * `@toolbox-web/grid-angular/features/master-detail`.\n *\n * @since 1.7.1\n */\nexport type MasterDetailConfig = Omit<CoreMasterDetailConfig, 'detailRenderer'> & {\n detailRenderer?: CoreMasterDetailConfig['detailRenderer'] | Type<unknown>;\n};\n\n/**\n * Owns the binding(s) `[masterDetail], [detailExpand]` on `<tbw-grid>` for the matching feature plugin. See `GridFilteringDirective` for the full rationale.\n *\n * @category Directive\n */\n@Directive({\n selector: 'tbw-grid[masterDetail], tbw-grid[detailExpand]',\n standalone: true,\n})\nexport class GridMasterDetailDirective implements OnInit, OnDestroy {\n private readonly elementRef = inject(ElementRef<DataGridElement>);\n\n readonly masterDetail = input<MasterDetailConfig>();\n readonly detailExpand = output<DetailExpandDetail>();\n\n private listener?: (e: Event) => void;\n\n constructor() {\n const grid = this.elementRef.nativeElement;\n registerFeatureClaim(grid, 'masterDetail', () => this.masterDetail());\n claimEvent(grid, 'detail-expand');\n }\n\n ngOnInit(): void {\n const grid = this.elementRef.nativeElement;\n this.listener = (e: Event): void => this.detailExpand.emit((e as CustomEvent<DetailExpandDetail>).detail);\n grid.addEventListener('detail-expand', this.listener);\n }\n\n ngOnDestroy(): void {\n const grid = this.elementRef.nativeElement;\n if (this.listener) {\n grid.removeEventListener('detail-expand', this.listener);\n this.listener = undefined;\n }\n unregisterFeatureClaim(grid, 'masterDetail');\n unclaimEvent(grid, 'detail-expand');\n }\n}\n","/**\n * Master-detail feature for @toolbox-web/grid-angular\n *\n * Import this module to enable the `[masterDetail]` input on the `Grid`\n * directive AND to wire `<tbw-grid-detail>` Angular templates into the\n * MasterDetailPlugin's `detailRenderer`.\n *\n * @example\n * ```typescript\n * // In your bootstrap (e.g. main.ts or app.component.ts):\n * import '@toolbox-web/grid-angular/features/master-detail';\n * ```\n *\n * ```html\n * <tbw-grid [masterDetail]=\"{ showExpandColumn: true }\">\n * <tbw-grid-detail>\n * <ng-template let-row>...</ng-template>\n * </tbw-grid-detail>\n * </tbw-grid>\n * ```\n *\n * @packageDocumentation\n */\n\nimport type { Type } from '@angular/core';\nimport {\n isComponentClass,\n registerDetailRendererBridge,\n registerFeatureConfigPreprocessor,\n registerTemplateBridge,\n type GridAdapter,\n} from '@toolbox-web/grid-angular';\nimport '@toolbox-web/grid/features/master-detail';\nimport { getDetailTemplate, type GridDetailContext } from './grid-detail-view.directive';\nimport type { MasterDetailConfig } from './grid-master-detail.directive';\nexport { GridMasterDetailDirective } from './grid-master-detail.directive';\nexport type { MasterDetailConfig } from './grid-master-detail.directive';\nexport type { _Augmentation as _MasterDetailAugmentation } from '@toolbox-web/grid/features/master-detail';\n\n// ---------------------------------------------------------------------------\n// Master-detail-owned directive + helpers. These physically live in this\n// secondary entry (the runtime behaviour lives here too).\n// ---------------------------------------------------------------------------\nexport { getDetailTemplate, GridDetailView } from './grid-detail-view.directive';\nexport type { GridDetailContext } from './grid-detail-view.directive';\n\n/**\n * Subset of `MasterDetailPlugin` we touch from the bridge. Avoids importing\n * the plugin class itself (would defeat tree-shaking and re-introduce the\n * coupling we just removed from the core directive).\n */\ninterface MasterDetailPluginLike {\n refreshDetailRenderer?: () => void;\n}\n\n// Install the row renderer bridge on the adapter. This is what\n// `adapter.createDetailRenderer(grid)` and `adapter.parseDetailElement(el)`\n// delegate to. Without this import, both methods return undefined.\nregisterDetailRendererBridge(<TRow = unknown>(gridElement: HTMLElement, adapter: GridAdapter) => {\n // Type inferred from `getDetailTemplate` (same package instance as `adapter`),\n // not from a local `import type { TemplateRef } from '@angular/core'` — the\n // latter resolves through Bun's `.bun/` cache during ng-packagr build and\n // produces a brand-incompatible TemplateRef vs the built adapter `.d.ts`.\n const template = getDetailTemplate(gridElement);\n if (!template) return undefined;\n\n return (row: TRow) => {\n const context: GridDetailContext<TRow> = { $implicit: row, row };\n const viewRef = adapter.createTrackedEmbeddedView(template, context);\n const container = document.createElement('div');\n viewRef.rootNodes.forEach((node: Node) => container.appendChild(node));\n return container;\n };\n});\n\n// Wire <tbw-grid-detail> Angular templates into the MasterDetailPlugin once\n// content templates are registered. The plugin's `parseLightDomDetail()`\n// already calls back into the adapter via `grid.__frameworkAdapter.parseDetailElement`\n// (which the `Grid` directive set in `ngOnInit`), so all we need to do here\n// is trigger a refresh once Angular's content templates are available.\nregisterTemplateBridge(({ grid }) => {\n // Only act when the user actually placed a <tbw-grid-detail> in light DOM.\n const detailElement = grid.querySelector('tbw-grid-detail');\n if (!detailElement) return;\n\n // The plugin must already have been attached (via [masterDetail] input,\n // gridConfig.features.masterDetail, or gridConfig.plugins). Use the runtime\n // lookup so we don't miss plugins instantiated from `gridConfig.features`,\n // which never appear in `gridConfig.plugins`.\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const existingPlugin = (grid as any).getPluginByName?.('masterDetail') as MasterDetailPluginLike | undefined;\n\n if (!existingPlugin) {\n console.warn(\n '[tbw-grid-angular] <tbw-grid-detail> found but MasterDetailPlugin is not configured.\\n' +\n 'Add the [masterDetail] input, set gridConfig.features.masterDetail, or include\\n' +\n 'MasterDetailPlugin in gridConfig.plugins:\\n\\n' +\n ' <tbw-grid [masterDetail]=\"{ showExpandColumn: true }\">...</tbw-grid>\\n\\n' +\n 'or:\\n\\n' +\n ' gridConfig = { features: { masterDetail: { showExpandColumn: true } } };',\n );\n return;\n }\n\n existingPlugin.refreshDetailRenderer?.();\n});\n\n/**\n * Bridge any Angular component class used as `masterDetail.detailRenderer` to\n * a plain `(row, rowIndex) => HTMLElement` function before the core plugin\n * factory consumes the config. Without this preprocessor, a component class\n * passed as `detailRenderer` would be invoked as a function at render time\n * and crash. Light-DOM `<tbw-grid-detail>` templates continue to win via\n * `parseLightDomDetail` inside the plugin.\n *\n * @since 1.7.1\n */\nregisterFeatureConfigPreprocessor('masterDetail', (config, adapter) => {\n if (!config || typeof config !== 'object') return config;\n const cfg = config as MasterDetailConfig;\n if (!isComponentClass(cfg.detailRenderer)) return config;\n\n const componentClass = cfg.detailRenderer as Type<unknown>;\n const mount = adapter.mountComponentRenderer<{ row: unknown; rowIndex: number }>(componentClass, (ctx) => ({\n row: ctx.row,\n rowIndex: ctx.rowIndex,\n }));\n const cached = new Map<unknown, HTMLElement>();\n const detailRenderer: MasterDetailConfig['detailRenderer'] = (row, rowIndex) => {\n const existing = cached.get(row);\n if (existing) return existing;\n const { hostElement } = mount({ row, rowIndex });\n cached.set(row, hostElement);\n return hostElement;\n };\n return { ...cfg, detailRenderer };\n});\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;;AAeA;AACA,MAAM,sBAAsB,GAAG,IAAI,GAAG,EAA+C;AAErF;;;;AAIG;AACG,SAAU,iBAAiB,CAAC,WAAwB,EAAA;;IAExD,MAAM,aAAa,GAAG,WAAW,CAAC,aAAa,CAAC,iBAAiB,CAAC;IAClE,IAAI,aAAa,EAAE;AACjB,QAAA,OAAO,sBAAsB,CAAC,GAAG,CAAC,aAA4B,CAAC;IACjE;AACA,IAAA,OAAO,SAAS;AAClB;AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6CG;MAEU,cAAc,CAAA;AACjB,IAAA,UAAU,GAAG,MAAM,EAAC,UAAuB,EAAC;;IAGpD,gBAAgB,GAAG,KAAK,CAAU,IAAI;yFAAC;;IAGvC,SAAS,GAAG,KAAK,CAA0B,OAAO;kFAAC;AAEnD;;AAEG;AACH,IAAA,QAAQ,GAAG,YAAY,EAAC,WAA8B;iFAAC;;AAG/C,IAAA,kBAAkB,GAAG,MAAM,CAAC,MAAK;AACvC,QAAA,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,EAAE;QAChC,IAAI,QAAQ,EAAE;;YAEZ,sBAAsB,CAAC,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,aAAa,EAAE,QAAQ,CAAC;QACrE;IACF,CAAC;2FAAC;AAEF;;;AAGG;AACH,IAAA,OAAO,sBAAsB,CAAC,GAAmB,EAAE,GAAY,EAAA;AAC7D,QAAA,OAAO,IAAI;IACb;uGA7BW,cAAc,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;2FAAd,cAAc,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,iBAAA,EAAA,MAAA,EAAA,EAAA,gBAAA,EAAA,EAAA,iBAAA,EAAA,kBAAA,EAAA,UAAA,EAAA,kBAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,SAAA,EAAA,EAAA,iBAAA,EAAA,WAAA,EAAA,UAAA,EAAA,WAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,EAAA,OAAA,EAAA,CAAA,EAAA,YAAA,EAAA,UAAA,EAAA,KAAA,EAAA,IAAA,EAAA,SAAA,GAYD,WAA8B,CAAA,EAAA,WAAA,EAAA,IAAA,EAAA,QAAA,EAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,EAAA,EAAA,CAAA;;2FAZ3C,cAAc,EAAA,UAAA,EAAA,CAAA;kBAD1B,SAAS;mBAAC,EAAE,QAAQ,EAAE,iBAAiB,EAAE;+SAahB,WAA8B,CAAA,EAAA,EAAA,QAAA,EAAA,IAAA,EAAA,CAAA,EAAA,CAAA,EAAA,EAAA,CAAA;;AC3FxD;;;;;;;;;;AAUG;AA6BH;;;;AAIG;MAKU,yBAAyB,CAAA;AACnB,IAAA,UAAU,GAAG,MAAM,EAAC,UAA2B,EAAC;AAExD,IAAA,YAAY,GAAG,KAAK;gGAAsB;IAC1C,YAAY,GAAG,MAAM,EAAsB;AAE5C,IAAA,QAAQ;AAEhB,IAAA,WAAA,GAAA;AACE,QAAA,MAAM,IAAI,GAAG,IAAI,CAAC,UAAU,CAAC,aAAa;AAC1C,QAAA,oBAAoB,CAAC,IAAI,EAAE,cAAc,EAAE,MAAM,IAAI,CAAC,YAAY,EAAE,CAAC;AACrE,QAAA,UAAU,CAAC,IAAI,EAAE,eAAe,CAAC;IACnC;IAEA,QAAQ,GAAA;AACN,QAAA,MAAM,IAAI,GAAG,IAAI,CAAC,UAAU,CAAC,aAAa;AAC1C,QAAA,IAAI,CAAC,QAAQ,GAAG,CAAC,CAAQ,KAAW,IAAI,CAAC,YAAY,CAAC,IAAI,CAAE,CAAqC,CAAC,MAAM,CAAC;QACzG,IAAI,CAAC,gBAAgB,CAAC,eAAe,EAAE,IAAI,CAAC,QAAQ,CAAC;IACvD;IAEA,WAAW,GAAA;AACT,QAAA,MAAM,IAAI,GAAG,IAAI,CAAC,UAAU,CAAC,aAAa;AAC1C,QAAA,IAAI,IAAI,CAAC,QAAQ,EAAE;YACjB,IAAI,CAAC,mBAAmB,CAAC,eAAe,EAAE,IAAI,CAAC,QAAQ,CAAC;AACxD,YAAA,IAAI,CAAC,QAAQ,GAAG,SAAS;QAC3B;AACA,QAAA,sBAAsB,CAAC,IAAI,EAAE,cAAc,CAAC;AAC5C,QAAA,YAAY,CAAC,IAAI,EAAE,eAAe,CAAC;IACrC;uGA5BW,yBAAyB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;2FAAzB,yBAAyB,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,gDAAA,EAAA,MAAA,EAAA,EAAA,YAAA,EAAA,EAAA,iBAAA,EAAA,cAAA,EAAA,UAAA,EAAA,cAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,EAAA,OAAA,EAAA,EAAA,YAAA,EAAA,cAAA,EAAA,EAAA,QAAA,EAAA,EAAA,EAAA,CAAA;;2FAAzB,yBAAyB,EAAA,UAAA,EAAA,CAAA;kBAJrC,SAAS;AAAC,YAAA,IAAA,EAAA,CAAA;AACT,oBAAA,QAAQ,EAAE,gDAAgD;AAC1D,oBAAA,UAAU,EAAE,IAAI;AACjB,iBAAA;;;AC/CD;;;;;;;;;;;;;;;;;;;;;;AAsBG;AAiCH;AACA;AACA;AACA,4BAA4B,CAAC,CAAiB,WAAwB,EAAE,OAAoB,KAAI;;;;;AAK9F,IAAA,MAAM,QAAQ,GAAG,iBAAiB,CAAC,WAAW,CAAC;AAC/C,IAAA,IAAI,CAAC,QAAQ;AAAE,QAAA,OAAO,SAAS;IAE/B,OAAO,CAAC,GAAS,KAAI;QACnB,MAAM,OAAO,GAA4B,EAAE,SAAS,EAAE,GAAG,EAAE,GAAG,EAAE;QAChE,MAAM,OAAO,GAAG,OAAO,CAAC,yBAAyB,CAAC,QAAQ,EAAE,OAAO,CAAC;QACpE,MAAM,SAAS,GAAG,QAAQ,CAAC,aAAa,CAAC,KAAK,CAAC;AAC/C,QAAA,OAAO,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,IAAU,KAAK,SAAS,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;AACtE,QAAA,OAAO,SAAS;AAClB,IAAA,CAAC;AACH,CAAC,CAAC;AAEF;AACA;AACA;AACA;AACA;AACA,sBAAsB,CAAC,CAAC,EAAE,IAAI,EAAE,KAAI;;IAElC,MAAM,aAAa,GAAG,IAAI,CAAC,aAAa,CAAC,iBAAiB,CAAC;AAC3D,IAAA,IAAI,CAAC,aAAa;QAAE;;;;;;IAOpB,MAAM,cAAc,GAAI,IAAY,CAAC,eAAe,GAAG,cAAc,CAAuC;IAE5G,IAAI,CAAC,cAAc,EAAE;QACnB,OAAO,CAAC,IAAI,CACV,wFAAwF;YACtF,kFAAkF;YAClF,+CAA+C;YAC/C,4EAA4E;YAC5E,SAAS;AACT,YAAA,4EAA4E,CAC/E;QACD;IACF;AAEA,IAAA,cAAc,CAAC,qBAAqB,IAAI;AAC1C,CAAC,CAAC;AAEF;;;;;;;;;AASG;AACH,iCAAiC,CAAC,cAAc,EAAE,CAAC,MAAM,EAAE,OAAO,KAAI;AACpE,IAAA,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ;AAAE,QAAA,OAAO,MAAM;IACxD,MAAM,GAAG,GAAG,MAA4B;AACxC,IAAA,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,cAAc,CAAC;AAAE,QAAA,OAAO,MAAM;AAExD,IAAA,MAAM,cAAc,GAAG,GAAG,CAAC,cAA+B;AAC1D,IAAA,MAAM,KAAK,GAAG,OAAO,CAAC,sBAAsB,CAAqC,cAAc,EAAE,CAAC,GAAG,MAAM;QACzG,GAAG,EAAE,GAAG,CAAC,GAAG;QACZ,QAAQ,EAAE,GAAG,CAAC,QAAQ;AACvB,KAAA,CAAC,CAAC;AACH,IAAA,MAAM,MAAM,GAAG,IAAI,GAAG,EAAwB;AAC9C,IAAA,MAAM,cAAc,GAAyC,CAAC,GAAG,EAAE,QAAQ,KAAI;QAC7E,MAAM,QAAQ,GAAG,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC;AAChC,QAAA,IAAI,QAAQ;AAAE,YAAA,OAAO,QAAQ;AAC7B,QAAA,MAAM,EAAE,WAAW,EAAE,GAAG,KAAK,CAAC,EAAE,GAAG,EAAE,QAAQ,EAAE,CAAC;AAChD,QAAA,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,WAAW,CAAC;AAC5B,QAAA,OAAO,WAAW;AACpB,IAAA,CAAC;AACD,IAAA,OAAO,EAAE,GAAG,GAAG,EAAE,cAAc,EAAE;AACnC,CAAC,CAAC;;ACxIF;;AAEG;;;;"}
|
|
1
|
+
{"version":3,"file":"toolbox-web-grid-angular-features-master-detail.mjs","sources":["../../../../libs/grid-angular/features/master-detail/src/grid-detail-view.directive.ts","../../../../libs/grid-angular/features/master-detail/src/grid-master-detail.directive.ts","../../../../libs/grid-angular/features/master-detail/src/index.ts","../../../../libs/grid-angular/features/master-detail/src/toolbox-web-grid-angular-features-master-detail.ts"],"sourcesContent":["import { contentChild, Directive, effect, ElementRef, inject, input, TemplateRef } from '@angular/core';\nimport type { ExpandCollapseAnimation } from '@toolbox-web/grid';\n\n/**\n * Context object passed to the detail renderer template.\n * Contains the row data for the expanded detail view.\n * @since 0.1.0\n */\nexport interface GridDetailContext<TRow = unknown> {\n /** The row data (implicit binding for let-row) */\n $implicit: TRow;\n /** The row data (explicit binding) */\n row: TRow;\n}\n\n// Global registry mapping DOM elements to their templates\nconst detailTemplateRegistry = new Map<HTMLElement, TemplateRef<GridDetailContext>>();\n\n/**\n * Gets the detail template registered for a given grid element.\n * Used by AngularGridAdapter to retrieve templates at render time.\n * @since 0.1.0\n */\nexport function getDetailTemplate(gridElement: HTMLElement): TemplateRef<GridDetailContext> | undefined {\n // Look for tbw-grid-detail child and get its template\n const detailElement = gridElement.querySelector('tbw-grid-detail');\n if (detailElement) {\n return detailTemplateRegistry.get(detailElement as HTMLElement);\n }\n return undefined;\n}\n\n/**\n * Directive that captures an `<ng-template>` for use as a master-detail row renderer.\n *\n * This enables declarative Angular component usage for expandable detail rows\n * that appear below the main row when expanded.\n *\n * ## Usage\n *\n * ```html\n * <tbw-grid [rows]=\"rows\" [gridConfig]=\"config\">\n * <tbw-grid-detail [showExpandColumn]=\"true\" animation=\"slide\">\n * <ng-template let-row>\n * <app-detail-panel [employee]=\"row\" />\n * </ng-template>\n * </tbw-grid-detail>\n * </tbw-grid>\n * ```\n *\n * The template context provides:\n * - `$implicit` / `row`: The full row data object\n *\n * Import the directive from the master-detail feature entry:\n *\n * ```typescript\n * import { GridDetailView } from '@toolbox-web/grid-angular/features/master-detail';\n *\n * @Component({\n * imports: [GridDetailView],\n * // ...\n * })\n * ```\n *\n * @example\n * ```html\n * <tbw-grid [rows]=\"rows\" [gridConfig]=\"config\">\n * <tbw-grid-detail [showExpandColumn]=\"true\" animation=\"slide\">\n * <ng-template let-row>\n * <app-detail-panel [employee]=\"row\" />\n * </ng-template>\n * </tbw-grid-detail>\n * </tbw-grid>\n * ```\n *\n * @category Directive\n * @since 0.1.0\n */\n@Directive({ selector: 'tbw-grid-detail' })\nexport class GridDetailView {\n private elementRef = inject(ElementRef<HTMLElement>);\n\n /** Whether to show the expand/collapse column. Default: true */\n showExpandColumn = input<boolean>(true);\n\n /** Animation style for expand/collapse. Default: 'slide' */\n animation = input<ExpandCollapseAnimation>('slide');\n\n /**\n * Query for the ng-template content child.\n */\n template = contentChild(TemplateRef<GridDetailContext>);\n\n /** Effect that triggers when the template is available */\n private onTemplateReceived = effect(() => {\n const template = this.template();\n if (template) {\n // Register the template for this element\n detailTemplateRegistry.set(this.elementRef.nativeElement, template);\n }\n });\n\n /**\n * Static type guard for template context.\n * Enables type inference in templates.\n */\n static ngTemplateContextGuard(dir: GridDetailView, ctx: unknown): ctx is GridDetailContext {\n return true;\n }\n}\n","/**\n * `GridMasterDetailDirective` — owns `[masterDetail]`, `(detailExpand)` and\n * `(detailLoadError)` on `<tbw-grid>`. See `GridFilteringDirective` for the\n * full rationale.\n *\n * Note: this directive is independent of the higher-level\n * `<tbw-grid-detail>` template element + `GridDetailView` wrapper, which\n * uses a different bridging mechanism (template registration). Apps using\n * the template form do not need this directive.\n *\n * @category Directive\n */\nimport { Directive, ElementRef, inject, input, OnDestroy, OnInit, output, type Type } from '@angular/core';\nimport type { DataGridElement } from '@toolbox-web/grid';\nimport { claimEvent, registerFeatureClaim, unclaimEvent, unregisterFeatureClaim } from '@toolbox-web/grid-angular';\nimport type {\n MasterDetailConfig as CoreMasterDetailConfig,\n DetailExpandDetail,\n DetailLoadErrorDetail,\n} from '@toolbox-web/grid/plugins/master-detail';\n\n/**\n * Angular-specific master-detail config that allows an Angular component class\n * as `detailRenderer`.\n *\n * Extends the core `MasterDetailConfig` to accept a `Type<unknown>` for\n * `detailRenderer` in addition to the vanilla\n * `(row, rowIndex) => HTMLElement | string` signature. Bridging to vanilla\n * DOM is handled by the side-effect import\n * `@toolbox-web/grid-angular/features/master-detail`.\n *\n * Re-exported from the feature entry under the same name as the core type\n * so Angular consumers see a single canonical `MasterDetailConfig` from\n * `@toolbox-web/grid-angular/features/master-detail`.\n *\n * @since 1.7.1\n */\nexport type MasterDetailConfig = Omit<CoreMasterDetailConfig, 'detailRenderer'> & {\n detailRenderer?: CoreMasterDetailConfig['detailRenderer'] | Type<unknown>;\n};\n\n/**\n * Owns the binding(s) `[masterDetail], [detailExpand], [detailLoadError]` on `<tbw-grid>` for the matching feature plugin. See `GridFilteringDirective` for the full rationale.\n *\n * @category Directive\n */\n@Directive({\n selector: 'tbw-grid[masterDetail], tbw-grid[detailExpand], tbw-grid[detailLoadError]',\n standalone: true,\n})\nexport class GridMasterDetailDirective implements OnInit, OnDestroy {\n private readonly elementRef = inject(ElementRef<DataGridElement>);\n\n readonly masterDetail = input<MasterDetailConfig>();\n readonly detailExpand = output<DetailExpandDetail>();\n /**\n * Fired when loading detail data through the ServerSide data source\n * (`getChildRows()`) fails. Re-expanding the row retries the fetch.\n *\n * @since 2.7.0\n */\n readonly detailLoadError = output<DetailLoadErrorDetail>();\n\n private readonly listeners = new Map<string, (e: Event) => void>();\n private static readonly EVENTS = ['detail-expand', 'detail-load-error'] as const;\n\n constructor() {\n const grid = this.elementRef.nativeElement;\n registerFeatureClaim(grid, 'masterDetail', () => this.masterDetail());\n for (const ev of GridMasterDetailDirective.EVENTS) claimEvent(grid, ev);\n }\n\n ngOnInit(): void {\n const grid = this.elementRef.nativeElement;\n const wire = <T>(name: string, out: { emit: (v: T) => void }): void => {\n const l = (e: Event): void => out.emit((e as CustomEvent<T>).detail);\n grid.addEventListener(name, l);\n this.listeners.set(name, l);\n };\n wire<DetailExpandDetail>('detail-expand', this.detailExpand);\n wire<DetailLoadErrorDetail>('detail-load-error', this.detailLoadError);\n }\n\n ngOnDestroy(): void {\n const grid = this.elementRef.nativeElement;\n for (const [name, l] of this.listeners) grid.removeEventListener(name, l);\n this.listeners.clear();\n unregisterFeatureClaim(grid, 'masterDetail');\n for (const ev of GridMasterDetailDirective.EVENTS) unclaimEvent(grid, ev);\n }\n}\n","/**\n * Master-detail feature for @toolbox-web/grid-angular\n *\n * Import this module to enable the `[masterDetail]` input on the `Grid`\n * directive AND to wire `<tbw-grid-detail>` Angular templates into the\n * MasterDetailPlugin's `detailRenderer`.\n *\n * @example\n * ```typescript\n * // In your bootstrap (e.g. main.ts or app.component.ts):\n * import '@toolbox-web/grid-angular/features/master-detail';\n * ```\n *\n * ```html\n * <tbw-grid [masterDetail]=\"{ showExpandColumn: true }\">\n * <tbw-grid-detail>\n * <ng-template let-row>...</ng-template>\n * </tbw-grid-detail>\n * </tbw-grid>\n * ```\n *\n * @packageDocumentation\n */\n\nimport type { Type } from '@angular/core';\nimport {\n isComponentClass,\n registerDetailRendererBridge,\n registerFeatureConfigPreprocessor,\n registerTemplateBridge,\n type GridAdapter,\n} from '@toolbox-web/grid-angular';\nimport '@toolbox-web/grid/features/master-detail';\nimport { getDetailTemplate, type GridDetailContext } from './grid-detail-view.directive';\nimport type { MasterDetailConfig } from './grid-master-detail.directive';\nexport { GridMasterDetailDirective } from './grid-master-detail.directive';\nexport type { MasterDetailConfig } from './grid-master-detail.directive';\nexport type { _Augmentation as _MasterDetailAugmentation } from '@toolbox-web/grid/features/master-detail';\n\n// ---------------------------------------------------------------------------\n// Master-detail-owned directive + helpers. These physically live in this\n// secondary entry (the runtime behaviour lives here too).\n// ---------------------------------------------------------------------------\nexport { getDetailTemplate, GridDetailView } from './grid-detail-view.directive';\nexport type { GridDetailContext } from './grid-detail-view.directive';\n\n/**\n * Subset of `MasterDetailPlugin` we touch from the bridge. Avoids importing\n * the plugin class itself (would defeat tree-shaking and re-introduce the\n * coupling we just removed from the core directive).\n */\ninterface MasterDetailPluginLike {\n refreshDetailRenderer?: () => void;\n}\n\n// Install the row renderer bridge on the adapter. This is what\n// `adapter.createDetailRenderer(grid)` and `adapter.parseDetailElement(el)`\n// delegate to. Without this import, both methods return undefined.\nregisterDetailRendererBridge(<TRow = unknown>(gridElement: HTMLElement, adapter: GridAdapter) => {\n // Type inferred from `getDetailTemplate` (same package instance as `adapter`),\n // not from a local `import type { TemplateRef } from '@angular/core'` — the\n // latter resolves through Bun's `.bun/` cache during ng-packagr build and\n // produces a brand-incompatible TemplateRef vs the built adapter `.d.ts`.\n const template = getDetailTemplate(gridElement);\n if (!template) return undefined;\n\n return (row: TRow) => {\n const context: GridDetailContext<TRow> = { $implicit: row, row };\n const viewRef = adapter.createTrackedEmbeddedView(template, context);\n const container = document.createElement('div');\n viewRef.rootNodes.forEach((node: Node) => container.appendChild(node));\n return container;\n };\n});\n\n// Wire <tbw-grid-detail> Angular templates into the MasterDetailPlugin once\n// content templates are registered. The plugin's `parseLightDomDetail()`\n// already calls back into the adapter via `grid.__frameworkAdapter.parseDetailElement`\n// (which the `Grid` directive set in `ngOnInit`), so all we need to do here\n// is trigger a refresh once Angular's content templates are available.\nregisterTemplateBridge(({ grid }) => {\n // Only act when the user actually placed a <tbw-grid-detail> in light DOM.\n const detailElement = grid.querySelector('tbw-grid-detail');\n if (!detailElement) return;\n\n // The plugin must already have been attached (via [masterDetail] input,\n // gridConfig.features.masterDetail, or gridConfig.plugins). Use the runtime\n // lookup so we don't miss plugins instantiated from `gridConfig.features`,\n // which never appear in `gridConfig.plugins`.\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const existingPlugin = (grid as any).getPluginByName?.('masterDetail') as MasterDetailPluginLike | undefined;\n\n if (!existingPlugin) {\n console.warn(\n '[tbw-grid-angular] <tbw-grid-detail> found but MasterDetailPlugin is not configured.\\n' +\n 'Add the [masterDetail] input, set gridConfig.features.masterDetail, or include\\n' +\n 'MasterDetailPlugin in gridConfig.plugins:\\n\\n' +\n ' <tbw-grid [masterDetail]=\"{ showExpandColumn: true }\">...</tbw-grid>\\n\\n' +\n 'or:\\n\\n' +\n ' gridConfig = { features: { masterDetail: { showExpandColumn: true } } };',\n );\n return;\n }\n\n existingPlugin.refreshDetailRenderer?.();\n});\n\n/**\n * Bridge any Angular component class used as `masterDetail.detailRenderer` to\n * a plain `(row, rowIndex) => HTMLElement` function before the core plugin\n * factory consumes the config. Without this preprocessor, a component class\n * passed as `detailRenderer` would be invoked as a function at render time\n * and crash. Light-DOM `<tbw-grid-detail>` templates continue to win via\n * `parseLightDomDetail` inside the plugin.\n *\n * @since 1.7.1\n */\nregisterFeatureConfigPreprocessor('masterDetail', (config, adapter) => {\n if (!config || typeof config !== 'object') return config;\n const cfg = config as MasterDetailConfig;\n if (!isComponentClass(cfg.detailRenderer)) return config;\n\n const componentClass = cfg.detailRenderer as Type<unknown>;\n const mount = adapter.mountComponentRenderer<{ row: unknown; rowIndex: number }>(componentClass, (ctx) => ({\n row: ctx.row,\n rowIndex: ctx.rowIndex,\n }));\n // Cached per row so re-renders reuse the instance, but only while it is alive:\n // MasterDetailPlugin calls `adapter.unmount()` (destroying the ComponentRef)\n // when a detail panel is collapsed, scrolled out, or re-rendered after async\n // data/error, and the next render must mount a fresh component.\n const cached = new Map<unknown, ReturnType<typeof mount>>();\n const detailRenderer: MasterDetailConfig['detailRenderer'] = (row, rowIndex) => {\n const existing = cached.get(row);\n if (existing && !existing.componentRef.hostView.destroyed) return existing.hostElement;\n const mounted = mount({ row, rowIndex });\n cached.set(row, mounted);\n return mounted.hostElement;\n };\n return { ...cfg, detailRenderer };\n});\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
|
|
@@ -254,7 +254,7 @@ function injectGridUndoRedo(selector = 'tbw-grid') {
|
|
|
254
254
|
},
|
|
255
255
|
getUndoStack: () => getPlugin()?.getUndoStack() ?? [],
|
|
256
256
|
getRedoStack: () => getPlugin()?.getRedoStack() ?? [],
|
|
257
|
-
recordEdit: (rowIndex, field, oldValue, newValue) => {
|
|
257
|
+
recordEdit: (rowIndex, field, oldValue, newValue, rowId) => {
|
|
258
258
|
const plugin = getPlugin();
|
|
259
259
|
if (!plugin) {
|
|
260
260
|
console.warn(`[tbw-grid:undoRedo] UndoRedoPlugin not found.\n\n` +
|
|
@@ -262,7 +262,7 @@ function injectGridUndoRedo(selector = 'tbw-grid') {
|
|
|
262
262
|
` <tbw-grid [editing]="'dblclick'" [undoRedo]="true" />`);
|
|
263
263
|
return;
|
|
264
264
|
}
|
|
265
|
-
plugin.recordEdit(rowIndex, field, oldValue, newValue);
|
|
265
|
+
plugin.recordEdit(rowIndex, field, oldValue, newValue, rowId);
|
|
266
266
|
syncSignals();
|
|
267
267
|
},
|
|
268
268
|
beginTransaction: () => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"toolbox-web-grid-angular-features-undo-redo.mjs","sources":["../../../../libs/grid-angular/features/undo-redo/src/grid-undo-redo.directive.ts","../../../../libs/grid-angular/features/undo-redo/src/index.ts","../../../../libs/grid-angular/features/undo-redo/src/toolbox-web-grid-angular-features-undo-redo.ts"],"sourcesContent":["/**\n * `GridUndoRedoDirective` — owns `[undoRedo]`, `(undo)` and `(redo)` on\n * `<tbw-grid>`. See `GridFilteringDirective` for the full rationale.\n *\n * @category Directive\n */\nimport { Directive, ElementRef, inject, input, OnDestroy, OnInit, output } from '@angular/core';\nimport type { DataGridElement } from '@toolbox-web/grid';\nimport { claimEvent, registerFeatureClaim, unclaimEvent, unregisterFeatureClaim } from '@toolbox-web/grid-angular';\nimport type { UndoRedoConfig, UndoRedoDetail } from '@toolbox-web/grid/plugins/undo-redo';\n\n/**\n * Owns the binding(s) `[undoRedo], [undo], [redo]` on `<tbw-grid>` for the matching feature plugin. See `GridFilteringDirective` for the full rationale.\n *\n * @category Directive\n */\n@Directive({\n selector: 'tbw-grid[undoRedo], tbw-grid[undo], tbw-grid[redo]',\n standalone: true,\n})\nexport class GridUndoRedoDirective implements OnInit, OnDestroy {\n private readonly elementRef = inject(ElementRef<DataGridElement>);\n\n readonly undoRedo = input<boolean | UndoRedoConfig>();\n readonly undo = output<UndoRedoDetail>();\n readonly redo = output<UndoRedoDetail>();\n\n private readonly listeners = new Map<string, (e: Event) => void>();\n\n constructor() {\n const grid = this.elementRef.nativeElement;\n registerFeatureClaim(grid, 'undoRedo', () => this.undoRedo());\n claimEvent(grid, 'undo');\n claimEvent(grid, 'redo');\n }\n\n ngOnInit(): void {\n const grid = this.elementRef.nativeElement;\n const wire = (name: string, out: { emit: (v: UndoRedoDetail) => void }): void => {\n const l = (e: Event): void => out.emit((e as CustomEvent<UndoRedoDetail>).detail);\n grid.addEventListener(name, l);\n this.listeners.set(name, l);\n };\n wire('undo', this.undo);\n wire('redo', this.redo);\n }\n\n ngOnDestroy(): void {\n const grid = this.elementRef.nativeElement;\n for (const [name, l] of this.listeners) grid.removeEventListener(name, l);\n this.listeners.clear();\n unregisterFeatureClaim(grid, 'undoRedo');\n unclaimEvent(grid, 'undo');\n unclaimEvent(grid, 'redo');\n }\n}\n","/**\n * Undo/Redo feature for @toolbox-web/grid-angular\n *\n * Import this module to enable the `undoRedo` input on Grid directive.\n * Also exports `injectGridUndoRedo()` for programmatic undo/redo control.\n * Requires editing feature to be enabled.\n *\n * @example\n * ```typescript\n * import '@toolbox-web/grid-angular/features/editing';\n * import '@toolbox-web/grid-angular/features/undo-redo';\n *\n * <tbw-grid [editing]=\"'dblclick'\" [undoRedo]=\"true\" />\n * ```\n *\n * @example Using injectGridUndoRedo\n * ```typescript\n * import { injectGridUndoRedo } from '@toolbox-web/grid-angular/features/undo-redo';\n *\n * @Component({...})\n * export class MyComponent {\n * private undoRedo = injectGridUndoRedo();\n *\n * undo() { this.undoRedo.undo(); }\n * redo() { this.undoRedo.redo(); }\n * }\n * ```\n *\n * @packageDocumentation\n */\n\nimport { afterNextRender, DestroyRef, ElementRef, inject, signal, type Signal } from '@angular/core';\nimport type { DataGridElement } from '@toolbox-web/grid';\nimport '@toolbox-web/grid/features/undo-redo';\nimport { UndoRedoPlugin, type UndoRedoAction } from '@toolbox-web/grid/plugins/undo-redo';\nexport { GridUndoRedoDirective } from './grid-undo-redo.directive';\nexport type { _Augmentation as _UndoRedoAugmentation } from '@toolbox-web/grid/features/undo-redo';\n\n/**\n * Undo/Redo methods returned from injectGridUndoRedo.\n *\n * Uses lazy discovery - the grid is found on first method call, not during initialization.\n */\nexport interface UndoRedoMethods {\n /**\n * Undo the last edit action.\n * @returns The undone action (or compound action), or null if nothing to undo\n */\n undo: () => UndoRedoAction | null;\n\n /**\n * Redo the last undone action.\n * @returns The redone action (or compound action), or null if nothing to redo\n */\n redo: () => UndoRedoAction | null;\n\n /**\n * Reactive signal indicating whether undo is available.\n * Updates automatically when edits are made, undone, redone, or history is cleared.\n *\n * @example\n * ```typescript\n * // In template:\n * // <button [disabled]=\"!undoRedo.canUndo()\">Undo</button>\n *\n * // In computed:\n * readonly undoAvailable = computed(() => this.undoRedo.canUndo());\n * ```\n */\n canUndo: Signal<boolean>;\n\n /**\n * Reactive signal indicating whether redo is available.\n * Updates automatically when edits are made, undone, redone, or history is cleared.\n *\n * @example\n * ```typescript\n * // In template:\n * // <button [disabled]=\"!undoRedo.canRedo()\">Redo</button>\n *\n * // In computed:\n * readonly redoAvailable = computed(() => this.undoRedo.canRedo());\n * ```\n */\n canRedo: Signal<boolean>;\n\n /**\n * Clear all undo/redo history.\n */\n clearHistory: () => void;\n\n /**\n * Get a copy of the current undo stack.\n */\n getUndoStack: () => UndoRedoAction[];\n\n /**\n * Get a copy of the current redo stack.\n */\n getRedoStack: () => UndoRedoAction[];\n\n /**\n * Manually record an edit action.\n * If a transaction is active, the action is buffered; otherwise it's pushed to the undo stack.\n */\n recordEdit: (rowIndex: number, field: string, oldValue: unknown, newValue: unknown) => void;\n\n /**\n * Begin a transaction. All edits recorded until `endTransaction()` are grouped\n * into a single compound action for undo/redo.\n * @throws If a transaction is already active\n */\n beginTransaction: () => void;\n\n /**\n * End the active transaction and push the compound action to the undo stack.\n * If only one edit was recorded, it's pushed as a plain EditAction.\n * If no edits were recorded, the transaction is discarded.\n * @throws If no transaction is active\n */\n endTransaction: () => void;\n\n /**\n * Signal indicating if grid is ready.\n */\n isReady: Signal<boolean>;\n}\n\n/**\n * Angular inject function for programmatic undo/redo control.\n *\n * Uses **lazy grid discovery** - the grid element is found when methods are called,\n * not during initialization.\n *\n * @example\n * ```typescript\n * import { Component } from '@angular/core';\n * import { Grid } from '@toolbox-web/grid-angular';\n * import '@toolbox-web/grid-angular/features/editing';\n * import '@toolbox-web/grid-angular/features/undo-redo';\n * import { injectGridUndoRedo } from '@toolbox-web/grid-angular/features/undo-redo';\n *\n * @Component({\n * selector: 'app-my-grid',\n * imports: [Grid],\n * template: `\n * <button (click)=\"undoRedo.undo()\" [disabled]=\"!undoRedo.canUndo()\">Undo</button>\n * <button (click)=\"undoRedo.redo()\" [disabled]=\"!undoRedo.canRedo()\">Redo</button>\n * <tbw-grid [rows]=\"rows\" [editing]=\"'dblclick'\" [undoRedo]=\"true\"></tbw-grid>\n * `\n * })\n * export class MyGridComponent {\n * undoRedo = injectGridUndoRedo();\n * }\n * ```\n *\n * @param selector - Optional CSS selector to target a specific grid element.\n * Defaults to `'tbw-grid'` (first grid in the component). Use when the\n * component contains multiple grids, e.g. `'tbw-grid.primary'` or `'#my-grid'`.\n */\nexport function injectGridUndoRedo(selector = 'tbw-grid'): UndoRedoMethods {\n const elementRef = inject(ElementRef);\n const destroyRef = inject(DestroyRef);\n const isReady = signal(false);\n\n // Reactive undo/redo availability signals\n const canUndoSignal = signal(false);\n const canRedoSignal = signal(false);\n\n let cachedGrid: DataGridElement | null = null;\n let readyPromiseStarted = false;\n let listenerAttached = false;\n\n /**\n * Sync canUndo/canRedo signals with the current plugin state.\n */\n const syncSignals = (): void => {\n const plugin = getPlugin();\n if (plugin) {\n canUndoSignal.set(plugin.canUndo());\n canRedoSignal.set(plugin.canRedo());\n }\n };\n\n /**\n * Attach event listeners to the grid for undo/redo state changes.\n * Listens for `undo`, `redo`, and `cell-commit` DOM events.\n */\n const attachListeners = (grid: DataGridElement): void => {\n if (listenerAttached) return;\n listenerAttached = true;\n\n const unsubs = [grid.on('undo', syncSignals), grid.on('redo', syncSignals), grid.on('cell-commit', syncSignals)];\n\n destroyRef.onDestroy(() => {\n unsubs.forEach((fn) => fn());\n });\n };\n\n const getGrid = (): DataGridElement | null => {\n if (cachedGrid) return cachedGrid;\n\n const grid = elementRef.nativeElement.querySelector(selector) as DataGridElement | null;\n if (grid) {\n cachedGrid = grid;\n attachListeners(grid);\n if (!readyPromiseStarted) {\n readyPromiseStarted = true;\n grid.ready?.().then(() => {\n if (grid.getPluginByName('undoRedo')) {\n isReady.set(true);\n } else {\n setTimeout(() => isReady.set(true), 0);\n }\n });\n }\n }\n return grid;\n };\n\n const getPlugin = (): UndoRedoPlugin | undefined => {\n return getGrid()?.getPluginByName('undoRedo') as UndoRedoPlugin | undefined;\n };\n\n // Eagerly discover the grid after the first render so event listeners\n // are attached and isReady updates without requiring a programmatic\n // method call. Falls back to MutationObserver for lazy-rendered content.\n afterNextRender(() => {\n const grid = getGrid();\n if (grid) {\n grid.ready?.().then(() => {\n if (grid.getPluginByName('undoRedo')) {\n syncSignals();\n } else {\n setTimeout(syncSignals, 0);\n }\n });\n return;\n }\n\n const host = elementRef.nativeElement as HTMLElement;\n const observer = new MutationObserver(() => {\n const discovered = getGrid();\n if (discovered) {\n observer.disconnect();\n discovered.ready?.().then(() => {\n if (discovered.getPluginByName('undoRedo')) {\n syncSignals();\n } else {\n setTimeout(syncSignals, 0);\n }\n });\n }\n });\n observer.observe(host, { childList: true, subtree: true });\n\n destroyRef.onDestroy(() => observer.disconnect());\n });\n\n return {\n isReady: isReady.asReadonly(),\n canUndo: canUndoSignal.asReadonly(),\n canRedo: canRedoSignal.asReadonly(),\n\n undo: () => {\n const plugin = getPlugin();\n if (!plugin) {\n console.warn(\n `[tbw-grid:undoRedo] UndoRedoPlugin not found.\\n\\n` +\n ` → Enable undo/redo on the grid:\\n` +\n ` <tbw-grid [editing]=\"'dblclick'\" [undoRedo]=\"true\" />`,\n );\n return null;\n }\n const result = plugin.undo();\n syncSignals();\n return result;\n },\n\n redo: () => {\n const plugin = getPlugin();\n if (!plugin) {\n console.warn(\n `[tbw-grid:undoRedo] UndoRedoPlugin not found.\\n\\n` +\n ` → Enable undo/redo on the grid:\\n` +\n ` <tbw-grid [editing]=\"'dblclick'\" [undoRedo]=\"true\" />`,\n );\n return null;\n }\n const result = plugin.redo();\n syncSignals();\n return result;\n },\n\n clearHistory: () => {\n const plugin = getPlugin();\n if (!plugin) {\n console.warn(\n `[tbw-grid:undoRedo] UndoRedoPlugin not found.\\n\\n` +\n ` → Enable undo/redo on the grid:\\n` +\n ` <tbw-grid [editing]=\"'dblclick'\" [undoRedo]=\"true\" />`,\n );\n return;\n }\n plugin.clearHistory();\n syncSignals();\n },\n\n getUndoStack: () => getPlugin()?.getUndoStack() ?? [],\n\n getRedoStack: () => getPlugin()?.getRedoStack() ?? [],\n\n recordEdit: (rowIndex: number, field: string, oldValue: unknown, newValue: unknown) => {\n const plugin = getPlugin();\n if (!plugin) {\n console.warn(\n `[tbw-grid:undoRedo] UndoRedoPlugin not found.\\n\\n` +\n ` → Enable undo/redo on the grid:\\n` +\n ` <tbw-grid [editing]=\"'dblclick'\" [undoRedo]=\"true\" />`,\n );\n return;\n }\n plugin.recordEdit(rowIndex, field, oldValue, newValue);\n syncSignals();\n },\n\n beginTransaction: () => {\n const plugin = getPlugin();\n if (!plugin) {\n console.warn(\n `[tbw-grid:undoRedo] UndoRedoPlugin not found.\\n\\n` +\n ` → Enable undo/redo on the grid:\\n` +\n ` <tbw-grid [editing]=\"'dblclick'\" [undoRedo]=\"true\" />`,\n );\n return;\n }\n plugin.beginTransaction();\n },\n\n endTransaction: () => {\n const plugin = getPlugin();\n if (!plugin) {\n console.warn(\n `[tbw-grid:undoRedo] UndoRedoPlugin not found.\\n\\n` +\n ` → Enable undo/redo on the grid:\\n` +\n ` <tbw-grid [editing]=\"'dblclick'\" [undoRedo]=\"true\" />`,\n );\n return;\n }\n plugin.endTransaction();\n syncSignals();\n },\n };\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"toolbox-web-grid-angular-features-undo-redo.mjs","sources":["../../../../libs/grid-angular/features/undo-redo/src/grid-undo-redo.directive.ts","../../../../libs/grid-angular/features/undo-redo/src/index.ts","../../../../libs/grid-angular/features/undo-redo/src/toolbox-web-grid-angular-features-undo-redo.ts"],"sourcesContent":["/**\n * `GridUndoRedoDirective` — owns `[undoRedo]`, `(undo)` and `(redo)` on\n * `<tbw-grid>`. See `GridFilteringDirective` for the full rationale.\n *\n * @category Directive\n */\nimport { Directive, ElementRef, inject, input, OnDestroy, OnInit, output } from '@angular/core';\nimport type { DataGridElement } from '@toolbox-web/grid';\nimport { claimEvent, registerFeatureClaim, unclaimEvent, unregisterFeatureClaim } from '@toolbox-web/grid-angular';\nimport type { UndoRedoConfig, UndoRedoDetail } from '@toolbox-web/grid/plugins/undo-redo';\n\n/**\n * Owns the binding(s) `[undoRedo], [undo], [redo]` on `<tbw-grid>` for the matching feature plugin. See `GridFilteringDirective` for the full rationale.\n *\n * @category Directive\n */\n@Directive({\n selector: 'tbw-grid[undoRedo], tbw-grid[undo], tbw-grid[redo]',\n standalone: true,\n})\nexport class GridUndoRedoDirective implements OnInit, OnDestroy {\n private readonly elementRef = inject(ElementRef<DataGridElement>);\n\n readonly undoRedo = input<boolean | UndoRedoConfig>();\n readonly undo = output<UndoRedoDetail>();\n readonly redo = output<UndoRedoDetail>();\n\n private readonly listeners = new Map<string, (e: Event) => void>();\n\n constructor() {\n const grid = this.elementRef.nativeElement;\n registerFeatureClaim(grid, 'undoRedo', () => this.undoRedo());\n claimEvent(grid, 'undo');\n claimEvent(grid, 'redo');\n }\n\n ngOnInit(): void {\n const grid = this.elementRef.nativeElement;\n const wire = (name: string, out: { emit: (v: UndoRedoDetail) => void }): void => {\n const l = (e: Event): void => out.emit((e as CustomEvent<UndoRedoDetail>).detail);\n grid.addEventListener(name, l);\n this.listeners.set(name, l);\n };\n wire('undo', this.undo);\n wire('redo', this.redo);\n }\n\n ngOnDestroy(): void {\n const grid = this.elementRef.nativeElement;\n for (const [name, l] of this.listeners) grid.removeEventListener(name, l);\n this.listeners.clear();\n unregisterFeatureClaim(grid, 'undoRedo');\n unclaimEvent(grid, 'undo');\n unclaimEvent(grid, 'redo');\n }\n}\n","/**\n * Undo/Redo feature for @toolbox-web/grid-angular\n *\n * Import this module to enable the `undoRedo` input on Grid directive.\n * Also exports `injectGridUndoRedo()` for programmatic undo/redo control.\n * Requires editing feature to be enabled.\n *\n * @example\n * ```typescript\n * import '@toolbox-web/grid-angular/features/editing';\n * import '@toolbox-web/grid-angular/features/undo-redo';\n *\n * <tbw-grid [editing]=\"'dblclick'\" [undoRedo]=\"true\" />\n * ```\n *\n * @example Using injectGridUndoRedo\n * ```typescript\n * import { injectGridUndoRedo } from '@toolbox-web/grid-angular/features/undo-redo';\n *\n * @Component({...})\n * export class MyComponent {\n * private undoRedo = injectGridUndoRedo();\n *\n * undo() { this.undoRedo.undo(); }\n * redo() { this.undoRedo.redo(); }\n * }\n * ```\n *\n * @packageDocumentation\n */\n\nimport { afterNextRender, DestroyRef, ElementRef, inject, signal, type Signal } from '@angular/core';\nimport type { DataGridElement } from '@toolbox-web/grid';\nimport '@toolbox-web/grid/features/undo-redo';\nimport { UndoRedoPlugin, type UndoRedoAction } from '@toolbox-web/grid/plugins/undo-redo';\nexport { GridUndoRedoDirective } from './grid-undo-redo.directive';\nexport type { _Augmentation as _UndoRedoAugmentation } from '@toolbox-web/grid/features/undo-redo';\n\n/**\n * Undo/Redo methods returned from injectGridUndoRedo.\n *\n * Uses lazy discovery - the grid is found on first method call, not during initialization.\n */\nexport interface UndoRedoMethods {\n /**\n * Undo the last edit action.\n * @returns The undone action (or compound action), or null if nothing to undo\n */\n undo: () => UndoRedoAction | null;\n\n /**\n * Redo the last undone action.\n * @returns The redone action (or compound action), or null if nothing to redo\n */\n redo: () => UndoRedoAction | null;\n\n /**\n * Reactive signal indicating whether undo is available.\n * Updates automatically when edits are made, undone, redone, or history is cleared.\n *\n * @example\n * ```typescript\n * // In template:\n * // <button [disabled]=\"!undoRedo.canUndo()\">Undo</button>\n *\n * // In computed:\n * readonly undoAvailable = computed(() => this.undoRedo.canUndo());\n * ```\n */\n canUndo: Signal<boolean>;\n\n /**\n * Reactive signal indicating whether redo is available.\n * Updates automatically when edits are made, undone, redone, or history is cleared.\n *\n * @example\n * ```typescript\n * // In template:\n * // <button [disabled]=\"!undoRedo.canRedo()\">Redo</button>\n *\n * // In computed:\n * readonly redoAvailable = computed(() => this.undoRedo.canRedo());\n * ```\n */\n canRedo: Signal<boolean>;\n\n /**\n * Clear all undo/redo history.\n */\n clearHistory: () => void;\n\n /**\n * Get a copy of the current undo stack.\n */\n getUndoStack: () => UndoRedoAction[];\n\n /**\n * Get a copy of the current redo stack.\n */\n getRedoStack: () => UndoRedoAction[];\n\n /**\n * Manually record an edit action.\n * If a transaction is active, the action is buffered; otherwise it's pushed to the undo stack.\n * Pass `rowId` (since 2.6.2) — e.g. `cell-commit`'s `detail.rowId` — when the row may not be\n * in the view; otherwise it is resolved from `rowIndex`.\n */\n recordEdit: (rowIndex: number, field: string, oldValue: unknown, newValue: unknown, rowId?: string) => void;\n\n /**\n * Begin a transaction. All edits recorded until `endTransaction()` are grouped\n * into a single compound action for undo/redo.\n * @throws If a transaction is already active\n */\n beginTransaction: () => void;\n\n /**\n * End the active transaction and push the compound action to the undo stack.\n * If only one edit was recorded, it's pushed as a plain EditAction.\n * If no edits were recorded, the transaction is discarded.\n * @throws If no transaction is active\n */\n endTransaction: () => void;\n\n /**\n * Signal indicating if grid is ready.\n */\n isReady: Signal<boolean>;\n}\n\n/**\n * Angular inject function for programmatic undo/redo control.\n *\n * Uses **lazy grid discovery** - the grid element is found when methods are called,\n * not during initialization.\n *\n * @example\n * ```typescript\n * import { Component } from '@angular/core';\n * import { Grid } from '@toolbox-web/grid-angular';\n * import '@toolbox-web/grid-angular/features/editing';\n * import '@toolbox-web/grid-angular/features/undo-redo';\n * import { injectGridUndoRedo } from '@toolbox-web/grid-angular/features/undo-redo';\n *\n * @Component({\n * selector: 'app-my-grid',\n * imports: [Grid],\n * template: `\n * <button (click)=\"undoRedo.undo()\" [disabled]=\"!undoRedo.canUndo()\">Undo</button>\n * <button (click)=\"undoRedo.redo()\" [disabled]=\"!undoRedo.canRedo()\">Redo</button>\n * <tbw-grid [rows]=\"rows\" [editing]=\"'dblclick'\" [undoRedo]=\"true\"></tbw-grid>\n * `\n * })\n * export class MyGridComponent {\n * undoRedo = injectGridUndoRedo();\n * }\n * ```\n *\n * @param selector - Optional CSS selector to target a specific grid element.\n * Defaults to `'tbw-grid'` (first grid in the component). Use when the\n * component contains multiple grids, e.g. `'tbw-grid.primary'` or `'#my-grid'`.\n */\nexport function injectGridUndoRedo(selector = 'tbw-grid'): UndoRedoMethods {\n const elementRef = inject(ElementRef);\n const destroyRef = inject(DestroyRef);\n const isReady = signal(false);\n\n // Reactive undo/redo availability signals\n const canUndoSignal = signal(false);\n const canRedoSignal = signal(false);\n\n let cachedGrid: DataGridElement | null = null;\n let readyPromiseStarted = false;\n let listenerAttached = false;\n\n /**\n * Sync canUndo/canRedo signals with the current plugin state.\n */\n const syncSignals = (): void => {\n const plugin = getPlugin();\n if (plugin) {\n canUndoSignal.set(plugin.canUndo());\n canRedoSignal.set(plugin.canRedo());\n }\n };\n\n /**\n * Attach event listeners to the grid for undo/redo state changes.\n * Listens for `undo`, `redo`, and `cell-commit` DOM events.\n */\n const attachListeners = (grid: DataGridElement): void => {\n if (listenerAttached) return;\n listenerAttached = true;\n\n const unsubs = [grid.on('undo', syncSignals), grid.on('redo', syncSignals), grid.on('cell-commit', syncSignals)];\n\n destroyRef.onDestroy(() => {\n unsubs.forEach((fn) => fn());\n });\n };\n\n const getGrid = (): DataGridElement | null => {\n if (cachedGrid) return cachedGrid;\n\n const grid = elementRef.nativeElement.querySelector(selector) as DataGridElement | null;\n if (grid) {\n cachedGrid = grid;\n attachListeners(grid);\n if (!readyPromiseStarted) {\n readyPromiseStarted = true;\n grid.ready?.().then(() => {\n if (grid.getPluginByName('undoRedo')) {\n isReady.set(true);\n } else {\n setTimeout(() => isReady.set(true), 0);\n }\n });\n }\n }\n return grid;\n };\n\n const getPlugin = (): UndoRedoPlugin | undefined => {\n return getGrid()?.getPluginByName('undoRedo') as UndoRedoPlugin | undefined;\n };\n\n // Eagerly discover the grid after the first render so event listeners\n // are attached and isReady updates without requiring a programmatic\n // method call. Falls back to MutationObserver for lazy-rendered content.\n afterNextRender(() => {\n const grid = getGrid();\n if (grid) {\n grid.ready?.().then(() => {\n if (grid.getPluginByName('undoRedo')) {\n syncSignals();\n } else {\n setTimeout(syncSignals, 0);\n }\n });\n return;\n }\n\n const host = elementRef.nativeElement as HTMLElement;\n const observer = new MutationObserver(() => {\n const discovered = getGrid();\n if (discovered) {\n observer.disconnect();\n discovered.ready?.().then(() => {\n if (discovered.getPluginByName('undoRedo')) {\n syncSignals();\n } else {\n setTimeout(syncSignals, 0);\n }\n });\n }\n });\n observer.observe(host, { childList: true, subtree: true });\n\n destroyRef.onDestroy(() => observer.disconnect());\n });\n\n return {\n isReady: isReady.asReadonly(),\n canUndo: canUndoSignal.asReadonly(),\n canRedo: canRedoSignal.asReadonly(),\n\n undo: () => {\n const plugin = getPlugin();\n if (!plugin) {\n console.warn(\n `[tbw-grid:undoRedo] UndoRedoPlugin not found.\\n\\n` +\n ` → Enable undo/redo on the grid:\\n` +\n ` <tbw-grid [editing]=\"'dblclick'\" [undoRedo]=\"true\" />`,\n );\n return null;\n }\n const result = plugin.undo();\n syncSignals();\n return result;\n },\n\n redo: () => {\n const plugin = getPlugin();\n if (!plugin) {\n console.warn(\n `[tbw-grid:undoRedo] UndoRedoPlugin not found.\\n\\n` +\n ` → Enable undo/redo on the grid:\\n` +\n ` <tbw-grid [editing]=\"'dblclick'\" [undoRedo]=\"true\" />`,\n );\n return null;\n }\n const result = plugin.redo();\n syncSignals();\n return result;\n },\n\n clearHistory: () => {\n const plugin = getPlugin();\n if (!plugin) {\n console.warn(\n `[tbw-grid:undoRedo] UndoRedoPlugin not found.\\n\\n` +\n ` → Enable undo/redo on the grid:\\n` +\n ` <tbw-grid [editing]=\"'dblclick'\" [undoRedo]=\"true\" />`,\n );\n return;\n }\n plugin.clearHistory();\n syncSignals();\n },\n\n getUndoStack: () => getPlugin()?.getUndoStack() ?? [],\n\n getRedoStack: () => getPlugin()?.getRedoStack() ?? [],\n\n recordEdit: (rowIndex: number, field: string, oldValue: unknown, newValue: unknown, rowId?: string) => {\n const plugin = getPlugin();\n if (!plugin) {\n console.warn(\n `[tbw-grid:undoRedo] UndoRedoPlugin not found.\\n\\n` +\n ` → Enable undo/redo on the grid:\\n` +\n ` <tbw-grid [editing]=\"'dblclick'\" [undoRedo]=\"true\" />`,\n );\n return;\n }\n plugin.recordEdit(rowIndex, field, oldValue, newValue, rowId);\n syncSignals();\n },\n\n beginTransaction: () => {\n const plugin = getPlugin();\n if (!plugin) {\n console.warn(\n `[tbw-grid:undoRedo] UndoRedoPlugin not found.\\n\\n` +\n ` → Enable undo/redo on the grid:\\n` +\n ` <tbw-grid [editing]=\"'dblclick'\" [undoRedo]=\"true\" />`,\n );\n return;\n }\n plugin.beginTransaction();\n },\n\n endTransaction: () => {\n const plugin = getPlugin();\n if (!plugin) {\n console.warn(\n `[tbw-grid:undoRedo] UndoRedoPlugin not found.\\n\\n` +\n ` → Enable undo/redo on the grid:\\n` +\n ` <tbw-grid [editing]=\"'dblclick'\" [undoRedo]=\"true\" />`,\n );\n return;\n }\n plugin.endTransaction();\n syncSignals();\n },\n };\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
|