@gp-grid/angular 0.10.3 → 0.11.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1630 @@
1
+ import * as i0 from '@angular/core';
2
+ import { input, output, computed, TemplateRef, ChangeDetectionStrategy, Component, ViewChild, signal, effect, HostListener, inject, PLATFORM_ID } from '@angular/core';
3
+ import { NgTemplateOutlet, isPlatformBrowser } from '@angular/common';
4
+ import { isCellSelected, isCellActive, getFieldValue, formatCellValue, isCellInFillPreview, buildCellClasses, isCellEditing, calculateFilterPopupPosition, calculateScaledColumnPositions, getTotalWidth, calculateFillHandlePosition, DataSourceOwner, AutoScrollDriver, PendingRowDragController, InputEventAdapter, applyBatchInstructions, scrollCellIntoView, GridCore, createMutableClientDataSource } from '@gp-grid/core';
5
+ export { GridCore, createClientDataSource, createDataSourceFromArray, createMutableClientDataSource, createServerDataSource } from '@gp-grid/core';
6
+
7
+ const TEMPLATE$1 = `
8
+ <div
9
+ class="gp-grid-header"
10
+ [class.gp-grid-header--loading]="isLoading()"
11
+ [style.height.px]="headerHeight()">
12
+ <div
13
+ style="position: absolute; top: 0; left: 0;"
14
+ [style.transform]="transformStyle()"
15
+ [style.width.px]="innerWidth()"
16
+ [style.height.px]="headerHeight()">
17
+ @for (entry of visibleColumnsWithIndices(); track entry.originalIndex; let i = $index) {
18
+ @let colW = columnWidths()[i] ?? 0;
19
+ @let headerData = headers().get(entry.originalIndex);
20
+ @let tpl = headerTemplate(entry.column);
21
+ <div
22
+ class="gp-grid-header-cell"
23
+ [attr.data-col-index]="entry.originalIndex"
24
+ [style.left.px]="columnPositions()[i]"
25
+ [style.width.px]="colW"
26
+ [style.height.px]="headerHeight()"
27
+ (pointerdown)="onHeaderPointerDown($event, entry.originalIndex, colW)">
28
+ @if (tpl) {
29
+ <ng-container
30
+ [ngTemplateOutlet]="tpl"
31
+ [ngTemplateOutletContext]="{ $implicit: headerParams(entry.column, entry.originalIndex, headerData) }">
32
+ </ng-container>
33
+ } @else {
34
+ <span class="gp-grid-header-text">{{ entry.column.headerName ?? entry.column.field }}</span>
35
+ <span class="gp-grid-header-icons">
36
+ @if (sortingEnabled() && entry.column.sortable !== false) {
37
+ <span class="gp-grid-sort-arrows">
38
+ <span class="gp-grid-sort-arrows-stack">
39
+ <svg
40
+ [class]="'gp-grid-sort-arrow-up' + (headerData?.sortDirection === 'asc' ? ' active' : '')"
41
+ width="8" height="6" viewBox="0 0 8 6">
42
+ <path d="M4 0L8 6H0L4 0Z" fill="currentColor"/>
43
+ </svg>
44
+ <svg
45
+ [class]="'gp-grid-sort-arrow-down' + (headerData?.sortDirection === 'desc' ? ' active' : '')"
46
+ width="8" height="6" viewBox="0 0 8 6">
47
+ <path d="M4 6L0 0H8L4 6Z" fill="currentColor"/>
48
+ </svg>
49
+ </span>
50
+ @if ((headerData?.sortIndex ?? 0) > 0) {
51
+ <span class="gp-grid-sort-index">{{ headerData?.sortIndex }}</span>
52
+ }
53
+ </span>
54
+ }
55
+ @if (entry.column.filterable) {
56
+ <span
57
+ [class]="'gp-grid-filter-icon' + (headerData?.hasFilter ? ' active' : '')"
58
+ (pointerdown)="onFilterPointerDown($event, entry.originalIndex)">
59
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
60
+ <path d="M4 4h16l-6 8v5l-4 2v-7L4 4z"/>
61
+ </svg>
62
+ </span>
63
+ }
64
+ </span>
65
+ }
66
+ @if (entry.column.resizable !== false) {
67
+ <div
68
+ class="gp-grid-header-resize-handle"
69
+ (pointerdown)="onResizePointerDown($event, entry.originalIndex, colW)">
70
+ </div>
71
+ }
72
+ </div>
73
+ }
74
+ </div>
75
+ </div>
76
+ `;
77
+ class GridHeaderComponent {
78
+ headerHeight = input.required(...(ngDevMode ? [{ debugName: "headerHeight" }] : /* istanbul ignore next */ []));
79
+ scrollLeft = input.required(...(ngDevMode ? [{ debugName: "scrollLeft" }] : /* istanbul ignore next */ []));
80
+ contentWidth = input.required(...(ngDevMode ? [{ debugName: "contentWidth" }] : /* istanbul ignore next */ []));
81
+ totalWidth = input.required(...(ngDevMode ? [{ debugName: "totalWidth" }] : /* istanbul ignore next */ []));
82
+ isLoading = input.required(...(ngDevMode ? [{ debugName: "isLoading" }] : /* istanbul ignore next */ []));
83
+ visibleColumnsWithIndices = input.required(...(ngDevMode ? [{ debugName: "visibleColumnsWithIndices" }] : /* istanbul ignore next */ []));
84
+ columnPositions = input.required(...(ngDevMode ? [{ debugName: "columnPositions" }] : /* istanbul ignore next */ []));
85
+ columnWidths = input.required(...(ngDevMode ? [{ debugName: "columnWidths" }] : /* istanbul ignore next */ []));
86
+ headers = input.required(...(ngDevMode ? [{ debugName: "headers" }] : /* istanbul ignore next */ []));
87
+ sortingEnabled = input(true, ...(ngDevMode ? [{ debugName: "sortingEnabled" }] : /* istanbul ignore next */ []));
88
+ headerRenderers = input({}, ...(ngDevMode ? [{ debugName: "headerRenderers" }] : /* istanbul ignore next */ []));
89
+ globalHeaderRenderer = input(null, ...(ngDevMode ? [{ debugName: "globalHeaderRenderer" }] : /* istanbul ignore next */ []));
90
+ headerPointerDown = output();
91
+ filterPointerDown = output();
92
+ resizePointerDown = output();
93
+ headerSort = output();
94
+ headerFilterOpen = output();
95
+ innerWidth = computed(() => Math.max(this.contentWidth(), this.totalWidth()), ...(ngDevMode ? [{ debugName: "innerWidth" }] : /* istanbul ignore next */ []));
96
+ transformStyle = computed(() => `translateX(${-this.scrollLeft()}px)`, ...(ngDevMode ? [{ debugName: "transformStyle" }] : /* istanbul ignore next */ []));
97
+ onHeaderPointerDown(event, colIndex, colWidth) {
98
+ this.headerPointerDown.emit({ colIndex, colWidth, colHeight: this.headerHeight(), event });
99
+ }
100
+ onFilterPointerDown(event, colIndex) {
101
+ event.stopPropagation();
102
+ const cell = event.currentTarget.closest('.gp-grid-header-cell');
103
+ if (cell) {
104
+ this.filterPointerDown.emit({ colIndex, anchorEl: cell });
105
+ }
106
+ }
107
+ onResizePointerDown(event, colIndex, colWidth) {
108
+ event.stopPropagation();
109
+ this.resizePointerDown.emit({ colIndex, colWidth, event });
110
+ }
111
+ headerTemplate(column) {
112
+ const renderer = column.headerRenderer;
113
+ if (renderer instanceof TemplateRef) {
114
+ return renderer;
115
+ }
116
+ if (typeof renderer === 'string') {
117
+ const registered = this.headerRenderers()[renderer];
118
+ if (registered)
119
+ return registered;
120
+ }
121
+ return this.globalHeaderRenderer();
122
+ }
123
+ headerParams(column, colIndex, headerData) {
124
+ const sortable = this.sortingEnabled() && column.sortable !== false;
125
+ const filterable = column.filterable === true;
126
+ return {
127
+ column,
128
+ colIndex,
129
+ sortDirection: headerData?.sortDirection,
130
+ sortIndex: headerData?.sortIndex,
131
+ sortable,
132
+ filterable,
133
+ hasFilter: headerData?.hasFilter ?? false,
134
+ onSort: (direction, addToExisting) => {
135
+ if (sortable) {
136
+ const colId = column.colId ?? column.field;
137
+ this.headerSort.emit({ colId, direction, addToExisting });
138
+ }
139
+ },
140
+ onFilterClick: () => {
141
+ // The anchor is looked up via data-col-index — same pattern as the default filter icon.
142
+ // No-op here; consumers using a custom header template should use the exposed callback.
143
+ },
144
+ };
145
+ }
146
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: GridHeaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
147
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.7", type: GridHeaderComponent, isStandalone: true, selector: "gp-grid-header", inputs: { headerHeight: { classPropertyName: "headerHeight", publicName: "headerHeight", isSignal: true, isRequired: true, transformFunction: null }, scrollLeft: { classPropertyName: "scrollLeft", publicName: "scrollLeft", isSignal: true, isRequired: true, transformFunction: null }, contentWidth: { classPropertyName: "contentWidth", publicName: "contentWidth", isSignal: true, isRequired: true, transformFunction: null }, totalWidth: { classPropertyName: "totalWidth", publicName: "totalWidth", isSignal: true, isRequired: true, transformFunction: null }, isLoading: { classPropertyName: "isLoading", publicName: "isLoading", isSignal: true, isRequired: true, transformFunction: null }, visibleColumnsWithIndices: { classPropertyName: "visibleColumnsWithIndices", publicName: "visibleColumnsWithIndices", isSignal: true, isRequired: true, transformFunction: null }, columnPositions: { classPropertyName: "columnPositions", publicName: "columnPositions", isSignal: true, isRequired: true, transformFunction: null }, columnWidths: { classPropertyName: "columnWidths", publicName: "columnWidths", isSignal: true, isRequired: true, transformFunction: null }, headers: { classPropertyName: "headers", publicName: "headers", isSignal: true, isRequired: true, transformFunction: null }, sortingEnabled: { classPropertyName: "sortingEnabled", publicName: "sortingEnabled", isSignal: true, isRequired: false, transformFunction: null }, headerRenderers: { classPropertyName: "headerRenderers", publicName: "headerRenderers", isSignal: true, isRequired: false, transformFunction: null }, globalHeaderRenderer: { classPropertyName: "globalHeaderRenderer", publicName: "globalHeaderRenderer", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { headerPointerDown: "headerPointerDown", filterPointerDown: "filterPointerDown", resizePointerDown: "resizePointerDown", headerSort: "headerSort", headerFilterOpen: "headerFilterOpen" }, ngImport: i0, template: "\n <div\n class=\"gp-grid-header\"\n [class.gp-grid-header--loading]=\"isLoading()\"\n [style.height.px]=\"headerHeight()\">\n <div\n style=\"position: absolute; top: 0; left: 0;\"\n [style.transform]=\"transformStyle()\"\n [style.width.px]=\"innerWidth()\"\n [style.height.px]=\"headerHeight()\">\n @for (entry of visibleColumnsWithIndices(); track entry.originalIndex; let i = $index) {\n @let colW = columnWidths()[i] ?? 0;\n @let headerData = headers().get(entry.originalIndex);\n @let tpl = headerTemplate(entry.column);\n <div\n class=\"gp-grid-header-cell\"\n [attr.data-col-index]=\"entry.originalIndex\"\n [style.left.px]=\"columnPositions()[i]\"\n [style.width.px]=\"colW\"\n [style.height.px]=\"headerHeight()\"\n (pointerdown)=\"onHeaderPointerDown($event, entry.originalIndex, colW)\">\n @if (tpl) {\n <ng-container\n [ngTemplateOutlet]=\"tpl\"\n [ngTemplateOutletContext]=\"{ $implicit: headerParams(entry.column, entry.originalIndex, headerData) }\">\n </ng-container>\n } @else {\n <span class=\"gp-grid-header-text\">{{ entry.column.headerName ?? entry.column.field }}</span>\n <span class=\"gp-grid-header-icons\">\n @if (sortingEnabled() && entry.column.sortable !== false) {\n <span class=\"gp-grid-sort-arrows\">\n <span class=\"gp-grid-sort-arrows-stack\">\n <svg\n [class]=\"'gp-grid-sort-arrow-up' + (headerData?.sortDirection === 'asc' ? ' active' : '')\"\n width=\"8\" height=\"6\" viewBox=\"0 0 8 6\">\n <path d=\"M4 0L8 6H0L4 0Z\" fill=\"currentColor\"/>\n </svg>\n <svg\n [class]=\"'gp-grid-sort-arrow-down' + (headerData?.sortDirection === 'desc' ? ' active' : '')\"\n width=\"8\" height=\"6\" viewBox=\"0 0 8 6\">\n <path d=\"M4 6L0 0H8L4 6Z\" fill=\"currentColor\"/>\n </svg>\n </span>\n @if ((headerData?.sortIndex ?? 0) > 0) {\n <span class=\"gp-grid-sort-index\">{{ headerData?.sortIndex }}</span>\n }\n </span>\n }\n @if (entry.column.filterable) {\n <span\n [class]=\"'gp-grid-filter-icon' + (headerData?.hasFilter ? ' active' : '')\"\n (pointerdown)=\"onFilterPointerDown($event, entry.originalIndex)\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path d=\"M4 4h16l-6 8v5l-4 2v-7L4 4z\"/>\n </svg>\n </span>\n }\n </span>\n }\n @if (entry.column.resizable !== false) {\n <div\n class=\"gp-grid-header-resize-handle\"\n (pointerdown)=\"onResizePointerDown($event, entry.originalIndex, colW)\">\n </div>\n }\n </div>\n }\n </div>\n </div>\n", isInline: true, dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
148
+ }
149
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: GridHeaderComponent, decorators: [{
150
+ type: Component,
151
+ args: [{
152
+ selector: 'gp-grid-header',
153
+ standalone: true,
154
+ imports: [NgTemplateOutlet],
155
+ changeDetection: ChangeDetectionStrategy.OnPush,
156
+ template: TEMPLATE$1,
157
+ }]
158
+ }], propDecorators: { headerHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "headerHeight", required: true }] }], scrollLeft: [{ type: i0.Input, args: [{ isSignal: true, alias: "scrollLeft", required: true }] }], contentWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "contentWidth", required: true }] }], totalWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "totalWidth", required: true }] }], isLoading: [{ type: i0.Input, args: [{ isSignal: true, alias: "isLoading", required: true }] }], visibleColumnsWithIndices: [{ type: i0.Input, args: [{ isSignal: true, alias: "visibleColumnsWithIndices", required: true }] }], columnPositions: [{ type: i0.Input, args: [{ isSignal: true, alias: "columnPositions", required: true }] }], columnWidths: [{ type: i0.Input, args: [{ isSignal: true, alias: "columnWidths", required: true }] }], headers: [{ type: i0.Input, args: [{ isSignal: true, alias: "headers", required: true }] }], sortingEnabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "sortingEnabled", required: false }] }], headerRenderers: [{ type: i0.Input, args: [{ isSignal: true, alias: "headerRenderers", required: false }] }], globalHeaderRenderer: [{ type: i0.Input, args: [{ isSignal: true, alias: "globalHeaderRenderer", required: false }] }], headerPointerDown: [{ type: i0.Output, args: ["headerPointerDown"] }], filterPointerDown: [{ type: i0.Output, args: ["filterPointerDown"] }], resizePointerDown: [{ type: i0.Output, args: ["resizePointerDown"] }], headerSort: [{ type: i0.Output, args: ["headerSort"] }], headerFilterOpen: [{ type: i0.Output, args: ["headerFilterOpen"] }] } });
159
+
160
+ const GRID_BODY_TEMPLATE = `<div
161
+ #scrollContainer
162
+ style="height: 100%; overflow: auto; position: relative;"
163
+ (scroll)="onScroll()">
164
+ <div
165
+ style="position: relative; min-width: 100%"
166
+ [style.width.px]="innerWidth()"
167
+ [style.height.px]="sizerHeight()">
168
+ <div
169
+ class="gp-grid-rows-wrapper"
170
+ [style.width.px]="innerWidth()"
171
+ [style.transform]="wrapperTransform()">
172
+ @for (slot of slotsArray(); track slot.slotId) {
173
+ @if (slot.rowIndex >= 0) {
174
+ <div
175
+ [class]="rowClass(slot.rowIndex, slot.rowData)"
176
+ style="position: absolute; top: 0; left: 0"
177
+ [style.transform]="'translateY(' + slot.translateY + 'px)'"
178
+ [style.width.px]="innerWidth()"
179
+ [style.height.px]="rowHeight()"
180
+ >
181
+ @for (entry of visibleColumnWithIndices(); track entry.originalIndex; let i = $index) {
182
+ @let editing = isEditing(slot.rowIndex, entry.originalIndex);
183
+ <div
184
+ [class]="cellClass(slot.rowIndex, entry.originalIndex, entry.column, slot.rowData)"
185
+ style="position: absolute; top: 0;"
186
+ [attr.data-row-index]="slot.rowIndex"
187
+ [attr.data-col-index]="entry.originalIndex"
188
+ [style.left.px]="columnPositions()[i]"
189
+ [style.width.px]="columnWidths()[i]"
190
+ [style.height.px]="rowHeight()"
191
+ (pointerdown)="cellPointerDown.emit({ rowIndex: slot.rowIndex, colIndex: entry.originalIndex, event: $event })"
192
+ (mouseenter)="cellPointerEnter.emit({ rowIndex: slot.rowIndex, colIndex: entry.originalIndex })"
193
+ (mouseleave)="cellPointerLeave.emit()"
194
+ (dblclick)="cellDoubleClick.emit({ rowIndex: slot.rowIndex, colIndex: entry.originalIndex })"
195
+ >
196
+ @if (editing) {
197
+ @let etpl = editTemplate(entry.column);
198
+ @if (etpl) {
199
+ <ng-container
200
+ [ngTemplateOutlet]="etpl"
201
+ [ngTemplateOutletContext]="{ $implicit: editParams(slot.rowData, entry.column, slot.rowIndex, entry.originalIndex) }">
202
+ </ng-container>
203
+ } @else {
204
+ <input
205
+ class="gp-grid-edit-input"
206
+ type="text"
207
+ [value]="editInitialValue()"
208
+ autofocus
209
+ (focus)="onEditFocus($event)"
210
+ (input)="editValueChange.emit(asInput($event).value)"
211
+ (keydown)="onEditKeyDown($event)"
212
+ (blur)="editCommit.emit()" />
213
+ }
214
+ } @else {
215
+ @let tpl = cellTemplate(entry.column);
216
+ @if (tpl) {
217
+ <ng-container
218
+ [ngTemplateOutlet]="tpl"
219
+ [ngTemplateOutletContext]="{ $implicit: cellParams(slot.rowData, entry.column, slot.rowIndex, entry.originalIndex) }">
220
+ </ng-container>
221
+ } @else {
222
+ {{ cellDisplay(slot.rowData, entry.column, slot.rowIndex, entry.originalIndex) }}
223
+ }
224
+ }
225
+ </div>
226
+ }
227
+ </div>
228
+ }
229
+ }
230
+ @if (fillHandlePosition(); as fhp) {
231
+ @if (editingCell() === null) {
232
+ <div
233
+ class="gp-grid-fill-handle"
234
+ [style.top.px]="fhp.top"
235
+ [style.left.px]="fhp.left"
236
+ (pointerdown)="fillHandlePointerDown.emit({ event: $event })">
237
+ </div>
238
+ }
239
+ }
240
+ @if (rowDropIndicator(); as rd) {
241
+ <div
242
+ class="gp-grid-row-drop-indicator"
243
+ [style.transform]="'translateY(' + rd.dropIndicatorY + 'px)'"
244
+ [style.width.px]="rowDropIndicatorWidth()"></div>
245
+ }
246
+ </div>
247
+ </div>
248
+ @if (totalRows() === 0) {
249
+ <div class="gp-grid-empty">No data to display</div>
250
+ }
251
+ </div>
252
+ `;
253
+
254
+ class GridBodyComponent {
255
+ scrollContainer;
256
+ rowHeight = input.required(...(ngDevMode ? [{ debugName: "rowHeight" }] : /* istanbul ignore next */ []));
257
+ totalHeaderHeight = input.required(...(ngDevMode ? [{ debugName: "totalHeaderHeight" }] : /* istanbul ignore next */ []));
258
+ contentWidth = input.required(...(ngDevMode ? [{ debugName: "contentWidth" }] : /* istanbul ignore next */ []));
259
+ contentHeight = input.required(...(ngDevMode ? [{ debugName: "contentHeight" }] : /* istanbul ignore next */ []));
260
+ rowsWrapperOffset = input.required(...(ngDevMode ? [{ debugName: "rowsWrapperOffset" }] : /* istanbul ignore next */ []));
261
+ slotsArray = input.required(...(ngDevMode ? [{ debugName: "slotsArray" }] : /* istanbul ignore next */ []));
262
+ visibleColumnWithIndices = input.required(...(ngDevMode ? [{ debugName: "visibleColumnWithIndices" }] : /* istanbul ignore next */ []));
263
+ totalWidth = input.required(...(ngDevMode ? [{ debugName: "totalWidth" }] : /* istanbul ignore next */ []));
264
+ columnPositions = input.required(...(ngDevMode ? [{ debugName: "columnPositions" }] : /* istanbul ignore next */ []));
265
+ columnWidths = input.required(...(ngDevMode ? [{ debugName: "columnWidths" }] : /* istanbul ignore next */ []));
266
+ totalRows = input.required(...(ngDevMode ? [{ debugName: "totalRows" }] : /* istanbul ignore next */ []));
267
+ activeCell = input(null, ...(ngDevMode ? [{ debugName: "activeCell" }] : /* istanbul ignore next */ []));
268
+ selectionRange = input(null, ...(ngDevMode ? [{ debugName: "selectionRange" }] : /* istanbul ignore next */ []));
269
+ editingCell = input(null, ...(ngDevMode ? [{ debugName: "editingCell" }] : /* istanbul ignore next */ []));
270
+ cellRenderers = input({}, ...(ngDevMode ? [{ debugName: "cellRenderers" }] : /* istanbul ignore next */ []));
271
+ globalCellRenderer = input(null, ...(ngDevMode ? [{ debugName: "globalCellRenderer" }] : /* istanbul ignore next */ []));
272
+ editRenderers = input({}, ...(ngDevMode ? [{ debugName: "editRenderers" }] : /* istanbul ignore next */ []));
273
+ globalEditRenderer = input(null, ...(ngDevMode ? [{ debugName: "globalEditRenderer" }] : /* istanbul ignore next */ []));
274
+ hoverPosition = input(null, ...(ngDevMode ? [{ debugName: "hoverPosition" }] : /* istanbul ignore next */ []));
275
+ computeRowClasses = input(null, ...(ngDevMode ? [{ debugName: "computeRowClasses" }] : /* istanbul ignore next */ []));
276
+ computeCellClasses = input(null, ...(ngDevMode ? [{ debugName: "computeCellClasses" }] : /* istanbul ignore next */ []));
277
+ fillHandlePosition = input(null, ...(ngDevMode ? [{ debugName: "fillHandlePosition" }] : /* istanbul ignore next */ []));
278
+ dragState = input(null, ...(ngDevMode ? [{ debugName: "dragState" }] : /* istanbul ignore next */ []));
279
+ scrolled = output();
280
+ cellPointerDown = output();
281
+ cellPointerEnter = output();
282
+ cellPointerLeave = output();
283
+ cellDoubleClick = output();
284
+ editValueChange = output();
285
+ editCommit = output();
286
+ editCancel = output();
287
+ fillHandlePointerDown = output();
288
+ innerWidth = computed(() => Math.max(this.contentWidth(), this.totalWidth()), ...(ngDevMode ? [{ debugName: "innerWidth" }] : /* istanbul ignore next */ []));
289
+ sizerHeight = computed(() => Math.max(this.contentHeight() - this.totalHeaderHeight(), 0), ...(ngDevMode ? [{ debugName: "sizerHeight" }] : /* istanbul ignore next */ []));
290
+ rowDropIndicator = computed(() => {
291
+ const ds = this.dragState();
292
+ if (ds?.dragType !== 'row-drag')
293
+ return null;
294
+ if (ds.rowDrag === null || ds.rowDrag.dropTargetIndex === null)
295
+ return null;
296
+ return ds.rowDrag;
297
+ }, ...(ngDevMode ? [{ debugName: "rowDropIndicator" }] : /* istanbul ignore next */ []));
298
+ rowDropIndicatorWidth = computed(() => Math.max(this.contentWidth(), this.totalWidth()), ...(ngDevMode ? [{ debugName: "rowDropIndicatorWidth" }] : /* istanbul ignore next */ []));
299
+ wrapperTransform = computed(() => `translateY(${this.rowsWrapperOffset()}px)`, ...(ngDevMode ? [{ debugName: "wrapperTransform" }] : /* istanbul ignore next */ []));
300
+ onScroll() {
301
+ const el = this.scrollContainer.nativeElement;
302
+ this.scrolled.emit(el.scrollLeft);
303
+ }
304
+ cellParams(rowData, column, rowIndex, colIndex) {
305
+ return {
306
+ value: getFieldValue(rowData, column.field),
307
+ rowData,
308
+ column,
309
+ rowIndex,
310
+ colIndex,
311
+ isActive: isCellActive(rowIndex, colIndex, this.activeCell()),
312
+ isSelected: isCellSelected(rowIndex, colIndex, this.selectionRange()),
313
+ isEditing: false,
314
+ };
315
+ }
316
+ cellTemplate(column) {
317
+ const renderer = column.cellRenderer;
318
+ if (renderer instanceof TemplateRef) {
319
+ return renderer;
320
+ }
321
+ if (typeof renderer === 'string') {
322
+ const registered = this.cellRenderers()[renderer];
323
+ if (registered)
324
+ return registered;
325
+ }
326
+ return this.globalCellRenderer();
327
+ }
328
+ editTemplate(column) {
329
+ const renderer = column.editRenderer;
330
+ if (renderer instanceof TemplateRef) {
331
+ return renderer;
332
+ }
333
+ if (typeof renderer === 'string') {
334
+ const registered = this.editRenderers()[renderer];
335
+ if (registered)
336
+ return registered;
337
+ }
338
+ return this.globalEditRenderer();
339
+ }
340
+ editParams(rowData, column, rowIndex, colIndex) {
341
+ const ec = this.editingCell();
342
+ return {
343
+ value: getFieldValue(rowData, column.field),
344
+ rowData,
345
+ column,
346
+ rowIndex,
347
+ colIndex,
348
+ isActive: true,
349
+ isSelected: true,
350
+ isEditing: true,
351
+ initialValue: ec?.initialValue ?? null,
352
+ onValueChange: (newValue) => {
353
+ const s = newValue === null || newValue === undefined ? '' : String(newValue);
354
+ this.editValueChange.emit(s);
355
+ },
356
+ onCommit: () => this.editCommit.emit(),
357
+ onCancel: () => this.editCancel.emit(),
358
+ };
359
+ }
360
+ cellDisplay(rowData, column, rowIndex, colIndex) {
361
+ const renderer = column.cellRenderer;
362
+ const value = getFieldValue(rowData, column.field);
363
+ if (typeof renderer === 'function') {
364
+ const params = this.cellParams(rowData, column, rowIndex, colIndex);
365
+ const result = renderer(params);
366
+ return result === null || result === undefined ? '' : String(result);
367
+ }
368
+ return formatCellValue(value, column.valueFormatter);
369
+ }
370
+ cellClass(rowIndex, colIndex, column, rowData) {
371
+ const editingCell = this.editingCell();
372
+ // Read hoverPosition to register this signal as a dep so Angular re-renders on hover change.
373
+ this.hoverPosition();
374
+ const ds = this.dragState();
375
+ const inFillPreview = isCellInFillPreview(rowIndex, colIndex, ds?.dragType === "fill", ds?.fillSourceRange ?? null, ds?.fillTarget ?? null);
376
+ const base = buildCellClasses(isCellActive(rowIndex, colIndex, this.activeCell()), isCellSelected(rowIndex, colIndex, this.selectionRange()), isCellEditing(rowIndex, colIndex, editingCell), inFillPreview);
377
+ const withHandle = column.rowDrag === true
378
+ ? `${base} gp-grid-cell--row-drag-handle`
379
+ : base;
380
+ const fn = this.computeCellClasses();
381
+ if (fn === null)
382
+ return withHandle;
383
+ const extra = fn(rowIndex, colIndex, column, rowData);
384
+ if (extra.length === 0)
385
+ return withHandle;
386
+ return `${withHandle} ${extra.join(' ')}`;
387
+ }
388
+ rowClass(rowIndex, rowData) {
389
+ this.hoverPosition();
390
+ const fn = this.computeRowClasses();
391
+ if (fn === null)
392
+ return 'gp-grid-row';
393
+ const extra = fn(rowIndex, rowData);
394
+ if (extra.length === 0)
395
+ return 'gp-grid-row';
396
+ return `gp-grid-row ${extra.join(' ')}`;
397
+ }
398
+ isEditing(rowIndex, colIndex) {
399
+ return isCellEditing(rowIndex, colIndex, this.editingCell());
400
+ }
401
+ editInitialValue() {
402
+ const ec = this.editingCell();
403
+ if (ec === null || ec.initialValue === null || ec.initialValue === undefined)
404
+ return '';
405
+ return String(ec.initialValue);
406
+ }
407
+ asInput(event) {
408
+ return event.target;
409
+ }
410
+ onEditFocus(event) {
411
+ event.target.select();
412
+ }
413
+ onEditKeyDown(event) {
414
+ event.stopPropagation();
415
+ if (event.key === 'Enter') {
416
+ this.editCommit.emit();
417
+ }
418
+ else if (event.key === 'Escape') {
419
+ this.editCancel.emit();
420
+ }
421
+ }
422
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: GridBodyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
423
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.7", type: GridBodyComponent, isStandalone: true, selector: "gp-grid-body", inputs: { rowHeight: { classPropertyName: "rowHeight", publicName: "rowHeight", isSignal: true, isRequired: true, transformFunction: null }, totalHeaderHeight: { classPropertyName: "totalHeaderHeight", publicName: "totalHeaderHeight", isSignal: true, isRequired: true, transformFunction: null }, contentWidth: { classPropertyName: "contentWidth", publicName: "contentWidth", isSignal: true, isRequired: true, transformFunction: null }, contentHeight: { classPropertyName: "contentHeight", publicName: "contentHeight", isSignal: true, isRequired: true, transformFunction: null }, rowsWrapperOffset: { classPropertyName: "rowsWrapperOffset", publicName: "rowsWrapperOffset", isSignal: true, isRequired: true, transformFunction: null }, slotsArray: { classPropertyName: "slotsArray", publicName: "slotsArray", isSignal: true, isRequired: true, transformFunction: null }, visibleColumnWithIndices: { classPropertyName: "visibleColumnWithIndices", publicName: "visibleColumnWithIndices", isSignal: true, isRequired: true, transformFunction: null }, totalWidth: { classPropertyName: "totalWidth", publicName: "totalWidth", isSignal: true, isRequired: true, transformFunction: null }, columnPositions: { classPropertyName: "columnPositions", publicName: "columnPositions", isSignal: true, isRequired: true, transformFunction: null }, columnWidths: { classPropertyName: "columnWidths", publicName: "columnWidths", isSignal: true, isRequired: true, transformFunction: null }, totalRows: { classPropertyName: "totalRows", publicName: "totalRows", isSignal: true, isRequired: true, transformFunction: null }, activeCell: { classPropertyName: "activeCell", publicName: "activeCell", isSignal: true, isRequired: false, transformFunction: null }, selectionRange: { classPropertyName: "selectionRange", publicName: "selectionRange", isSignal: true, isRequired: false, transformFunction: null }, editingCell: { classPropertyName: "editingCell", publicName: "editingCell", isSignal: true, isRequired: false, transformFunction: null }, cellRenderers: { classPropertyName: "cellRenderers", publicName: "cellRenderers", isSignal: true, isRequired: false, transformFunction: null }, globalCellRenderer: { classPropertyName: "globalCellRenderer", publicName: "globalCellRenderer", isSignal: true, isRequired: false, transformFunction: null }, editRenderers: { classPropertyName: "editRenderers", publicName: "editRenderers", isSignal: true, isRequired: false, transformFunction: null }, globalEditRenderer: { classPropertyName: "globalEditRenderer", publicName: "globalEditRenderer", isSignal: true, isRequired: false, transformFunction: null }, hoverPosition: { classPropertyName: "hoverPosition", publicName: "hoverPosition", isSignal: true, isRequired: false, transformFunction: null }, computeRowClasses: { classPropertyName: "computeRowClasses", publicName: "computeRowClasses", isSignal: true, isRequired: false, transformFunction: null }, computeCellClasses: { classPropertyName: "computeCellClasses", publicName: "computeCellClasses", isSignal: true, isRequired: false, transformFunction: null }, fillHandlePosition: { classPropertyName: "fillHandlePosition", publicName: "fillHandlePosition", isSignal: true, isRequired: false, transformFunction: null }, dragState: { classPropertyName: "dragState", publicName: "dragState", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { scrolled: "scrolled", cellPointerDown: "cellPointerDown", cellPointerEnter: "cellPointerEnter", cellPointerLeave: "cellPointerLeave", cellDoubleClick: "cellDoubleClick", editValueChange: "editValueChange", editCommit: "editCommit", editCancel: "editCancel", fillHandlePointerDown: "fillHandlePointerDown" }, viewQueries: [{ propertyName: "scrollContainer", first: true, predicate: ["scrollContainer"], descendants: true }], ngImport: i0, template: "<div\n #scrollContainer\n style=\"height: 100%; overflow: auto; position: relative;\"\n (scroll)=\"onScroll()\">\n <div\n style=\"position: relative; min-width: 100%\"\n [style.width.px]=\"innerWidth()\"\n [style.height.px]=\"sizerHeight()\">\n <div\n class=\"gp-grid-rows-wrapper\"\n [style.width.px]=\"innerWidth()\"\n [style.transform]=\"wrapperTransform()\">\n @for (slot of slotsArray(); track slot.slotId) {\n @if (slot.rowIndex >= 0) {\n <div\n [class]=\"rowClass(slot.rowIndex, slot.rowData)\"\n style=\"position: absolute; top: 0; left: 0\"\n [style.transform]=\"'translateY(' + slot.translateY + 'px)'\"\n [style.width.px]=\"innerWidth()\"\n [style.height.px]=\"rowHeight()\"\n >\n @for (entry of visibleColumnWithIndices(); track entry.originalIndex; let i = $index) {\n @let editing = isEditing(slot.rowIndex, entry.originalIndex);\n <div\n [class]=\"cellClass(slot.rowIndex, entry.originalIndex, entry.column, slot.rowData)\"\n style=\"position: absolute; top: 0;\"\n [attr.data-row-index]=\"slot.rowIndex\"\n [attr.data-col-index]=\"entry.originalIndex\"\n [style.left.px]=\"columnPositions()[i]\"\n [style.width.px]=\"columnWidths()[i]\"\n [style.height.px]=\"rowHeight()\"\n (pointerdown)=\"cellPointerDown.emit({ rowIndex: slot.rowIndex, colIndex: entry.originalIndex, event: $event })\"\n (mouseenter)=\"cellPointerEnter.emit({ rowIndex: slot.rowIndex, colIndex: entry.originalIndex })\"\n (mouseleave)=\"cellPointerLeave.emit()\"\n (dblclick)=\"cellDoubleClick.emit({ rowIndex: slot.rowIndex, colIndex: entry.originalIndex })\"\n >\n @if (editing) {\n @let etpl = editTemplate(entry.column);\n @if (etpl) {\n <ng-container\n [ngTemplateOutlet]=\"etpl\"\n [ngTemplateOutletContext]=\"{ $implicit: editParams(slot.rowData, entry.column, slot.rowIndex, entry.originalIndex) }\">\n </ng-container>\n } @else {\n <input\n class=\"gp-grid-edit-input\"\n type=\"text\"\n [value]=\"editInitialValue()\"\n autofocus\n (focus)=\"onEditFocus($event)\"\n (input)=\"editValueChange.emit(asInput($event).value)\"\n (keydown)=\"onEditKeyDown($event)\"\n (blur)=\"editCommit.emit()\" />\n }\n } @else {\n @let tpl = cellTemplate(entry.column);\n @if (tpl) {\n <ng-container\n [ngTemplateOutlet]=\"tpl\"\n [ngTemplateOutletContext]=\"{ $implicit: cellParams(slot.rowData, entry.column, slot.rowIndex, entry.originalIndex) }\">\n </ng-container>\n } @else {\n {{ cellDisplay(slot.rowData, entry.column, slot.rowIndex, entry.originalIndex) }}\n }\n }\n </div>\n }\n </div>\n }\n }\n @if (fillHandlePosition(); as fhp) {\n @if (editingCell() === null) {\n <div\n class=\"gp-grid-fill-handle\"\n [style.top.px]=\"fhp.top\"\n [style.left.px]=\"fhp.left\"\n (pointerdown)=\"fillHandlePointerDown.emit({ event: $event })\">\n </div>\n }\n }\n @if (rowDropIndicator(); as rd) {\n <div\n class=\"gp-grid-row-drop-indicator\"\n [style.transform]=\"'translateY(' + rd.dropIndicatorY + 'px)'\"\n [style.width.px]=\"rowDropIndicatorWidth()\"></div>\n }\n </div>\n </div>\n @if (totalRows() === 0) {\n <div class=\"gp-grid-empty\">No data to display</div>\n }\n </div>\n", isInline: true, styles: [":host{display:flex;flex:1;min-height:0;overflow:hidden}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
424
+ }
425
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: GridBodyComponent, decorators: [{
426
+ type: Component,
427
+ args: [{ selector: "gp-grid-body", standalone: true, imports: [NgTemplateOutlet], changeDetection: ChangeDetectionStrategy.OnPush, template: GRID_BODY_TEMPLATE, styles: [":host{display:flex;flex:1;min-height:0;overflow:hidden}\n"] }]
428
+ }], propDecorators: { scrollContainer: [{
429
+ type: ViewChild,
430
+ args: ["scrollContainer"]
431
+ }], rowHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowHeight", required: true }] }], totalHeaderHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "totalHeaderHeight", required: true }] }], contentWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "contentWidth", required: true }] }], contentHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "contentHeight", required: true }] }], rowsWrapperOffset: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowsWrapperOffset", required: true }] }], slotsArray: [{ type: i0.Input, args: [{ isSignal: true, alias: "slotsArray", required: true }] }], visibleColumnWithIndices: [{ type: i0.Input, args: [{ isSignal: true, alias: "visibleColumnWithIndices", required: true }] }], totalWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "totalWidth", required: true }] }], columnPositions: [{ type: i0.Input, args: [{ isSignal: true, alias: "columnPositions", required: true }] }], columnWidths: [{ type: i0.Input, args: [{ isSignal: true, alias: "columnWidths", required: true }] }], totalRows: [{ type: i0.Input, args: [{ isSignal: true, alias: "totalRows", required: true }] }], activeCell: [{ type: i0.Input, args: [{ isSignal: true, alias: "activeCell", required: false }] }], selectionRange: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectionRange", required: false }] }], editingCell: [{ type: i0.Input, args: [{ isSignal: true, alias: "editingCell", required: false }] }], cellRenderers: [{ type: i0.Input, args: [{ isSignal: true, alias: "cellRenderers", required: false }] }], globalCellRenderer: [{ type: i0.Input, args: [{ isSignal: true, alias: "globalCellRenderer", required: false }] }], editRenderers: [{ type: i0.Input, args: [{ isSignal: true, alias: "editRenderers", required: false }] }], globalEditRenderer: [{ type: i0.Input, args: [{ isSignal: true, alias: "globalEditRenderer", required: false }] }], hoverPosition: [{ type: i0.Input, args: [{ isSignal: true, alias: "hoverPosition", required: false }] }], computeRowClasses: [{ type: i0.Input, args: [{ isSignal: true, alias: "computeRowClasses", required: false }] }], computeCellClasses: [{ type: i0.Input, args: [{ isSignal: true, alias: "computeCellClasses", required: false }] }], fillHandlePosition: [{ type: i0.Input, args: [{ isSignal: true, alias: "fillHandlePosition", required: false }] }], dragState: [{ type: i0.Input, args: [{ isSignal: true, alias: "dragState", required: false }] }], scrolled: [{ type: i0.Output, args: ["scrolled"] }], cellPointerDown: [{ type: i0.Output, args: ["cellPointerDown"] }], cellPointerEnter: [{ type: i0.Output, args: ["cellPointerEnter"] }], cellPointerLeave: [{ type: i0.Output, args: ["cellPointerLeave"] }], cellDoubleClick: [{ type: i0.Output, args: ["cellDoubleClick"] }], editValueChange: [{ type: i0.Output, args: ["editValueChange"] }], editCommit: [{ type: i0.Output, args: ["editCommit"] }], editCancel: [{ type: i0.Output, args: ["editCancel"] }], fillHandlePointerDown: [{ type: i0.Output, args: ["fillHandlePointerDown"] }] } });
432
+
433
+ const FILTER_POPUP_TEMPLATE = `
434
+ <div
435
+ #popupEl
436
+ class="gp-grid-filter-popup"
437
+ [style.position]="'fixed'"
438
+ [style.zIndex]="10000"
439
+ [style.top.px]="popupTop()"
440
+ [style.left.px]="popupLeft()"
441
+ [style.minWidth.px]="popupMinWidth()"
442
+ [style.visibility]="positioned() ? 'visible' : 'hidden'"
443
+ (keydown.escape)="close.emit()"
444
+ (click)="$event.stopPropagation()">
445
+
446
+ <div class="gp-grid-filter-header">
447
+ Filter: {{ column().headerName ?? column().field }}
448
+ </div>
449
+
450
+ <div [class]="'gp-grid-filter-content ' + (isNumberColumn() ? 'gp-grid-filter-number' : 'gp-grid-filter-text')">
451
+ @if (isNumberColumn()) {
452
+ @for (cond of numberConditions; track $index; let i = $index) {
453
+ <div class="gp-grid-filter-condition">
454
+ @if (i > 0) {
455
+ <div class="gp-grid-filter-combination">
456
+ <button
457
+ type="button"
458
+ [class.active]="numberConditions[i - 1]?.nextOperator === 'and'"
459
+ (click)="setNumberNextOp(i - 1, 'and')">
460
+ AND
461
+ </button>
462
+ <button
463
+ type="button"
464
+ [class.active]="numberConditions[i - 1]?.nextOperator === 'or'"
465
+ (click)="setNumberNextOp(i - 1, 'or')">
466
+ OR
467
+ </button>
468
+ </div>
469
+ }
470
+ <div class="gp-grid-filter-row">
471
+ <select
472
+ [value]="cond.operator"
473
+ (change)="onNumberOperatorChange(i, $any($event.target).value)">
474
+ @for (op of numberOperators; track op.value) {
475
+ <option [value]="op.value">{{ op.label }}</option>
476
+ }
477
+ </select>
478
+ @if (!isValueLessNumberOp(cond.operator)) {
479
+ <input
480
+ type="number"
481
+ [value]="cond.value"
482
+ (input)="cond.value = $any($event.target).value"
483
+ placeholder="Value" />
484
+ @if (cond.operator === 'between') {
485
+ <span class="gp-grid-filter-to">to</span>
486
+ <input
487
+ type="number"
488
+ [value]="cond.valueTo"
489
+ (input)="cond.valueTo = $any($event.target).value"
490
+ placeholder="Value" />
491
+ }
492
+ }
493
+ @if (numberConditions.length > 1) {
494
+ <button
495
+ type="button"
496
+ class="gp-grid-filter-remove"
497
+ (click)="removeNumberCondition(i)">×</button>
498
+ }
499
+ </div>
500
+ </div>
501
+ }
502
+ <button type="button" class="gp-grid-filter-add" (click)="addNumberCondition()">
503
+ + Add condition
504
+ </button>
505
+ } @else {
506
+ @if (showValuesMode()) {
507
+ <div class="gp-grid-filter-mode-toggle">
508
+ <button
509
+ type="button"
510
+ [class.active]="filterMode === 'values'"
511
+ (click)="filterMode = 'values'">
512
+ Values
513
+ </button>
514
+ <button
515
+ type="button"
516
+ [class.active]="filterMode === 'condition'"
517
+ (click)="filterMode = 'condition'">
518
+ Condition
519
+ </button>
520
+ </div>
521
+ }
522
+
523
+ @if (filterMode === 'values' && showValuesMode()) {
524
+ <input
525
+ class="gp-grid-filter-search"
526
+ type="text"
527
+ [value]="searchText"
528
+ (input)="searchText = $any($event.target).value"
529
+ placeholder="Search..." />
530
+ <div class="gp-grid-filter-actions">
531
+ <button type="button" (click)="selectAll()">Select All</button>
532
+ <button type="button" (click)="deselectAll()">Deselect All</button>
533
+ </div>
534
+ <div class="gp-grid-filter-list">
535
+ <label class="gp-grid-filter-option">
536
+ <input
537
+ type="checkbox"
538
+ [checked]="includeBlanks"
539
+ (change)="includeBlanks = $any($event.target).checked" />
540
+ <span class="gp-grid-filter-blank">(Blanks)</span>
541
+ </label>
542
+ @for (val of filteredUniqueValues(); track val) {
543
+ <label class="gp-grid-filter-option">
544
+ <input
545
+ type="checkbox"
546
+ [checked]="selectedValues.has(val)"
547
+ (change)="toggleValue(val, $any($event.target).checked)" />
548
+ <span>{{ val }}</span>
549
+ </label>
550
+ }
551
+ </div>
552
+ }
553
+
554
+ @if (filterMode === 'condition') {
555
+ @for (cond of textConditions; track $index; let i = $index) {
556
+ <div class="gp-grid-filter-condition">
557
+ @if (i > 0) {
558
+ <div class="gp-grid-filter-combination">
559
+ <button
560
+ type="button"
561
+ [class.active]="textConditions[i - 1]?.nextOperator === 'and'"
562
+ (click)="setTextNextOp(i - 1, 'and')">
563
+ AND
564
+ </button>
565
+ <button
566
+ type="button"
567
+ [class.active]="textConditions[i - 1]?.nextOperator === 'or'"
568
+ (click)="setTextNextOp(i - 1, 'or')">
569
+ OR
570
+ </button>
571
+ </div>
572
+ }
573
+ <div class="gp-grid-filter-row">
574
+ <select
575
+ [value]="cond.operator"
576
+ (change)="onTextOperatorChange(i, $any($event.target).value)">
577
+ @for (op of textOperators; track op.value) {
578
+ <option [value]="op.value">{{ op.label }}</option>
579
+ }
580
+ </select>
581
+ @if (!isValueLessTextOp(cond.operator)) {
582
+ <input
583
+ class="gp-grid-filter-text-input"
584
+ type="text"
585
+ [value]="cond.value"
586
+ (input)="cond.value = $any($event.target).value"
587
+ placeholder="Value" />
588
+ }
589
+ @if (textConditions.length > 1) {
590
+ <button
591
+ type="button"
592
+ class="gp-grid-filter-remove"
593
+ (click)="removeTextCondition(i)">×</button>
594
+ }
595
+ </div>
596
+ </div>
597
+ }
598
+ <button type="button" class="gp-grid-filter-add" (click)="addTextCondition()">
599
+ + Add condition
600
+ </button>
601
+ }
602
+ }
603
+
604
+ <div class="gp-grid-filter-buttons">
605
+ <button type="button" class="gp-grid-filter-btn-clear" (click)="handleClear()">
606
+ Clear
607
+ </button>
608
+ <button type="button" class="gp-grid-filter-btn-apply" (click)="handleApply()">
609
+ Apply
610
+ </button>
611
+ </div>
612
+ </div>
613
+ </div>
614
+ `;
615
+
616
+ const TEXT_OPERATORS = [
617
+ { value: 'contains', label: 'Contains' },
618
+ { value: 'notContains', label: 'Does not contain' },
619
+ { value: 'equals', label: 'Equals' },
620
+ { value: 'notEquals', label: 'Does not equal' },
621
+ { value: 'startsWith', label: 'Starts with' },
622
+ { value: 'endsWith', label: 'Ends with' },
623
+ { value: 'blank', label: 'Is blank' },
624
+ { value: 'notBlank', label: 'Is not blank' },
625
+ ];
626
+ const NUMBER_OPERATORS = [
627
+ { value: '=', label: 'Equals' },
628
+ { value: '!=', label: 'Does not equal' },
629
+ { value: '>', label: 'Greater than' },
630
+ { value: '<', label: 'Less than' },
631
+ { value: '>=', label: 'Greater than or equal' },
632
+ { value: '<=', label: 'Less than or equal' },
633
+ { value: 'between', label: 'Between' },
634
+ { value: 'blank', label: 'Is blank' },
635
+ { value: 'notBlank', label: 'Is not blank' },
636
+ ];
637
+ const VALUE_LESS_TEXT_OPERATORS = ['blank', 'notBlank'];
638
+ const VALUE_LESS_NUMBER_OPERATORS = ['blank', 'notBlank'];
639
+ const MAX_CHECKBOX_VALUES = 100;
640
+ const isValueLessTextOp = (operator) => VALUE_LESS_TEXT_OPERATORS.includes(operator);
641
+ const isValueLessNumberOp = (operator) => VALUE_LESS_NUMBER_OPERATORS.includes(operator);
642
+ const defaultTextCondition = () => ({
643
+ operator: 'contains',
644
+ value: '',
645
+ nextOperator: 'and',
646
+ });
647
+ const defaultNumberCondition = () => ({
648
+ operator: '=',
649
+ value: '',
650
+ valueTo: '',
651
+ nextOperator: 'and',
652
+ });
653
+ const computeUniqueValues = (distinctValues) => {
654
+ const seen = new Set();
655
+ const result = [];
656
+ for (const val of distinctValues) {
657
+ if (val === null || val === undefined || val === '')
658
+ continue;
659
+ const str = String(val);
660
+ if (!seen.has(str)) {
661
+ seen.add(str);
662
+ result.push(str);
663
+ }
664
+ }
665
+ return result.sort();
666
+ };
667
+ const initTextState = (filter, uniqueValues) => {
668
+ if (!filter)
669
+ return defaultTextState(uniqueValues);
670
+ const textConds = filter.conditions.filter((c) => c.type === 'text');
671
+ if (textConds.length === 0)
672
+ return defaultTextState(uniqueValues);
673
+ const firstCond = textConds[0];
674
+ if (firstCond?.selectedValues !== undefined) {
675
+ return {
676
+ filterMode: 'values',
677
+ selectedValues: new Set(firstCond.selectedValues),
678
+ includeBlanks: firstCond.includeBlank ?? true,
679
+ textConditions: [defaultTextCondition()],
680
+ };
681
+ }
682
+ return {
683
+ filterMode: 'condition',
684
+ selectedValues: new Set(uniqueValues),
685
+ includeBlanks: true,
686
+ textConditions: textConds.map((c, i) => ({
687
+ operator: c.operator,
688
+ value: c.value ?? '',
689
+ nextOperator: textConds[i]?.nextOperator ?? 'and',
690
+ })),
691
+ };
692
+ };
693
+ const defaultTextState = (uniqueValues) => ({
694
+ filterMode: 'values',
695
+ selectedValues: new Set(uniqueValues),
696
+ includeBlanks: true,
697
+ textConditions: [defaultTextCondition()],
698
+ });
699
+ const initNumberConditions = (filter) => {
700
+ if (!filter)
701
+ return [defaultNumberCondition()];
702
+ const numConds = filter.conditions.filter((c) => c.type === 'number');
703
+ if (numConds.length === 0)
704
+ return [defaultNumberCondition()];
705
+ return numConds.map((c, i) => ({
706
+ operator: c.operator,
707
+ value: c.value !== undefined ? String(c.value) : '',
708
+ valueTo: c.valueTo !== undefined ? String(c.valueTo) : '',
709
+ nextOperator: numConds[i]?.nextOperator ?? 'and',
710
+ }));
711
+ };
712
+ const buildTextFilter = (input) => {
713
+ if (input.filterMode === 'values') {
714
+ return buildValuesFilter(input);
715
+ }
716
+ return buildConditionTextFilter(input.textConditions);
717
+ };
718
+ const buildValuesFilter = (input) => {
719
+ const allSelected = input.uniqueValues.every(v => input.selectedValues.has(v));
720
+ if (allSelected && input.includeBlanks)
721
+ return null;
722
+ return {
723
+ conditions: [{
724
+ type: 'text',
725
+ operator: 'contains',
726
+ selectedValues: new Set(input.selectedValues),
727
+ includeBlank: input.includeBlanks,
728
+ }],
729
+ combination: 'or',
730
+ };
731
+ };
732
+ const buildConditionTextFilter = (textConditions) => {
733
+ const conditions = [];
734
+ for (let i = 0; i < textConditions.length; i++) {
735
+ const cond = textConditions[i];
736
+ if (!cond)
737
+ continue;
738
+ if (!isValueLessTextOp(cond.operator) && !cond.value)
739
+ continue;
740
+ const out = {
741
+ type: 'text',
742
+ operator: cond.operator,
743
+ };
744
+ if (!isValueLessTextOp(cond.operator))
745
+ out.value = cond.value;
746
+ linkNextOperator(conditions, i, textConditions);
747
+ conditions.push(out);
748
+ }
749
+ if (conditions.length === 0)
750
+ return null;
751
+ return { conditions, combination: textConditions[0]?.nextOperator ?? 'and' };
752
+ };
753
+ const buildNumberFilter = (numberConditions) => {
754
+ const conditions = [];
755
+ for (let i = 0; i < numberConditions.length; i++) {
756
+ const cond = numberConditions[i];
757
+ if (!cond)
758
+ continue;
759
+ if (!isValueLessNumberOp(cond.operator) && !cond.value)
760
+ continue;
761
+ const out = {
762
+ type: 'number',
763
+ operator: cond.operator,
764
+ };
765
+ if (!isValueLessNumberOp(cond.operator)) {
766
+ out.value = parseFloat(cond.value);
767
+ if (cond.operator === 'between' && cond.valueTo) {
768
+ out.valueTo = parseFloat(cond.valueTo);
769
+ }
770
+ }
771
+ linkNextOperator(conditions, i, numberConditions);
772
+ conditions.push(out);
773
+ }
774
+ if (conditions.length === 0)
775
+ return null;
776
+ return { conditions, combination: numberConditions[0]?.nextOperator ?? 'and' };
777
+ };
778
+ const linkNextOperator = (built, currentIndex, source) => {
779
+ const prev = built[built.length - 1];
780
+ if (prev === undefined || currentIndex === 0)
781
+ return;
782
+ const prevSource = source[currentIndex - 1];
783
+ prev.nextOperator = prevSource?.nextOperator ?? 'and';
784
+ };
785
+ const resolveColId = (column) => column.colId ?? column.field;
786
+ const isNumberColumn = (column) => column.cellDataType === 'number';
787
+
788
+ class FilterPopupComponent {
789
+ popupEl;
790
+ column = input.required(...(ngDevMode ? [{ debugName: "column" }] : /* istanbul ignore next */ []));
791
+ colIndex = input.required(...(ngDevMode ? [{ debugName: "colIndex" }] : /* istanbul ignore next */ []));
792
+ anchorEl = input.required(...(ngDevMode ? [{ debugName: "anchorEl" }] : /* istanbul ignore next */ []));
793
+ distinctValues = input.required(...(ngDevMode ? [{ debugName: "distinctValues" }] : /* istanbul ignore next */ []));
794
+ currentFilter = input(undefined, ...(ngDevMode ? [{ debugName: "currentFilter" }] : /* istanbul ignore next */ []));
795
+ apply = output();
796
+ close = output();
797
+ popupTop = signal(0, ...(ngDevMode ? [{ debugName: "popupTop" }] : /* istanbul ignore next */ []));
798
+ popupLeft = signal(0, ...(ngDevMode ? [{ debugName: "popupLeft" }] : /* istanbul ignore next */ []));
799
+ popupMinWidth = signal(200, ...(ngDevMode ? [{ debugName: "popupMinWidth" }] : /* istanbul ignore next */ []));
800
+ positioned = signal(false, ...(ngDevMode ? [{ debugName: "positioned" }] : /* istanbul ignore next */ []));
801
+ filterMode = 'values';
802
+ searchText = '';
803
+ selectedValues = new Set();
804
+ includeBlanks = true;
805
+ textConditions = [defaultTextCondition()];
806
+ numberConditions = [defaultNumberCondition()];
807
+ textOperators = TEXT_OPERATORS;
808
+ numberOperators = NUMBER_OPERATORS;
809
+ isValueLessTextOp = isValueLessTextOp;
810
+ isValueLessNumberOp = isValueLessNumberOp;
811
+ constructor() {
812
+ effect(() => {
813
+ this.anchorEl();
814
+ this.currentFilter();
815
+ this.initFromCurrentFilter();
816
+ requestAnimationFrame(() => this.updatePosition());
817
+ });
818
+ }
819
+ ngAfterViewInit() {
820
+ requestAnimationFrame(() => {
821
+ document.addEventListener('pointerdown', this.onDocumentPointerDown, true);
822
+ });
823
+ window.addEventListener('resize', this.onWindowResize);
824
+ }
825
+ ngOnDestroy() {
826
+ document.removeEventListener('pointerdown', this.onDocumentPointerDown, true);
827
+ window.removeEventListener('resize', this.onWindowResize);
828
+ }
829
+ onEscape() {
830
+ this.close.emit();
831
+ }
832
+ isNumberColumn() {
833
+ return isNumberColumn(this.column());
834
+ }
835
+ showValuesMode() {
836
+ return this.uniqueValues().length <= MAX_CHECKBOX_VALUES;
837
+ }
838
+ uniqueValues() {
839
+ return computeUniqueValues(this.distinctValues());
840
+ }
841
+ filteredUniqueValues() {
842
+ const search = this.searchText.toLowerCase();
843
+ if (!search)
844
+ return this.uniqueValues();
845
+ return this.uniqueValues().filter(v => v.toLowerCase().includes(search));
846
+ }
847
+ toggleValue(val, checked) {
848
+ if (checked) {
849
+ this.selectedValues.add(val);
850
+ }
851
+ else {
852
+ this.selectedValues.delete(val);
853
+ }
854
+ }
855
+ selectAll() {
856
+ this.includeBlanks = true;
857
+ for (const v of this.uniqueValues())
858
+ this.selectedValues.add(v);
859
+ }
860
+ deselectAll() {
861
+ this.includeBlanks = false;
862
+ this.selectedValues.clear();
863
+ }
864
+ onTextOperatorChange(index, value) {
865
+ setField(this.textConditions, index, 'operator', value);
866
+ }
867
+ onNumberOperatorChange(index, value) {
868
+ setField(this.numberConditions, index, 'operator', value);
869
+ }
870
+ addTextCondition() {
871
+ this.textConditions.push(defaultTextCondition());
872
+ }
873
+ addNumberCondition() {
874
+ this.numberConditions.push(defaultNumberCondition());
875
+ }
876
+ removeTextCondition(index) {
877
+ this.textConditions.splice(index, 1);
878
+ }
879
+ removeNumberCondition(index) {
880
+ this.numberConditions.splice(index, 1);
881
+ }
882
+ setTextNextOp(index, value) {
883
+ setField(this.textConditions, index, 'nextOperator', value);
884
+ }
885
+ setNumberNextOp(index, value) {
886
+ setField(this.numberConditions, index, 'nextOperator', value);
887
+ }
888
+ handleApply() {
889
+ this.apply.emit({
890
+ colId: resolveColId(this.column()),
891
+ filter: this.buildFilter(),
892
+ });
893
+ }
894
+ handleClear() {
895
+ this.apply.emit({ colId: resolveColId(this.column()), filter: null });
896
+ }
897
+ buildFilter() {
898
+ if (this.isNumberColumn())
899
+ return buildNumberFilter(this.numberConditions);
900
+ return buildTextFilter({
901
+ filterMode: this.filterMode,
902
+ uniqueValues: this.uniqueValues(),
903
+ selectedValues: this.selectedValues,
904
+ includeBlanks: this.includeBlanks,
905
+ textConditions: this.textConditions,
906
+ });
907
+ }
908
+ initFromCurrentFilter() {
909
+ const filter = this.currentFilter();
910
+ if (this.isNumberColumn()) {
911
+ this.numberConditions = initNumberConditions(filter);
912
+ return;
913
+ }
914
+ const state = initTextState(filter, this.uniqueValues());
915
+ this.filterMode = state.filterMode;
916
+ this.selectedValues = state.selectedValues;
917
+ this.includeBlanks = state.includeBlanks;
918
+ this.textConditions = state.textConditions;
919
+ }
920
+ updatePosition() {
921
+ if (!this.popupEl?.nativeElement)
922
+ return;
923
+ const pos = calculateFilterPopupPosition(this.anchorEl(), this.popupEl.nativeElement);
924
+ this.popupTop.set(pos.top);
925
+ this.popupLeft.set(pos.left);
926
+ this.popupMinWidth.set(pos.minWidth);
927
+ this.positioned.set(true);
928
+ }
929
+ onDocumentPointerDown = (event) => {
930
+ const target = event.target;
931
+ if (target.closest('.gp-grid-filter-icon'))
932
+ return;
933
+ if (this.popupEl?.nativeElement?.contains(target))
934
+ return;
935
+ this.close.emit();
936
+ };
937
+ onWindowResize = () => {
938
+ this.updatePosition();
939
+ };
940
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: FilterPopupComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
941
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.7", type: FilterPopupComponent, isStandalone: true, selector: "gp-grid-filter-popup", inputs: { column: { classPropertyName: "column", publicName: "column", isSignal: true, isRequired: true, transformFunction: null }, colIndex: { classPropertyName: "colIndex", publicName: "colIndex", isSignal: true, isRequired: true, transformFunction: null }, anchorEl: { classPropertyName: "anchorEl", publicName: "anchorEl", isSignal: true, isRequired: true, transformFunction: null }, distinctValues: { classPropertyName: "distinctValues", publicName: "distinctValues", isSignal: true, isRequired: true, transformFunction: null }, currentFilter: { classPropertyName: "currentFilter", publicName: "currentFilter", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { apply: "apply", close: "close" }, host: { listeners: { "keydown.escape": "onEscape()" } }, viewQueries: [{ propertyName: "popupEl", first: true, predicate: ["popupEl"], descendants: true }], ngImport: i0, template: "\n<div\n #popupEl\n class=\"gp-grid-filter-popup\"\n [style.position]=\"'fixed'\"\n [style.zIndex]=\"10000\"\n [style.top.px]=\"popupTop()\"\n [style.left.px]=\"popupLeft()\"\n [style.minWidth.px]=\"popupMinWidth()\"\n [style.visibility]=\"positioned() ? 'visible' : 'hidden'\"\n (keydown.escape)=\"close.emit()\"\n (click)=\"$event.stopPropagation()\">\n\n <div class=\"gp-grid-filter-header\">\n Filter: {{ column().headerName ?? column().field }}\n </div>\n\n <div [class]=\"'gp-grid-filter-content ' + (isNumberColumn() ? 'gp-grid-filter-number' : 'gp-grid-filter-text')\">\n @if (isNumberColumn()) {\n @for (cond of numberConditions; track $index; let i = $index) {\n <div class=\"gp-grid-filter-condition\">\n @if (i > 0) {\n <div class=\"gp-grid-filter-combination\">\n <button\n type=\"button\"\n [class.active]=\"numberConditions[i - 1]?.nextOperator === 'and'\"\n (click)=\"setNumberNextOp(i - 1, 'and')\">\n AND\n </button>\n <button\n type=\"button\"\n [class.active]=\"numberConditions[i - 1]?.nextOperator === 'or'\"\n (click)=\"setNumberNextOp(i - 1, 'or')\">\n OR\n </button>\n </div>\n }\n <div class=\"gp-grid-filter-row\">\n <select\n [value]=\"cond.operator\"\n (change)=\"onNumberOperatorChange(i, $any($event.target).value)\">\n @for (op of numberOperators; track op.value) {\n <option [value]=\"op.value\">{{ op.label }}</option>\n }\n </select>\n @if (!isValueLessNumberOp(cond.operator)) {\n <input\n type=\"number\"\n [value]=\"cond.value\"\n (input)=\"cond.value = $any($event.target).value\"\n placeholder=\"Value\" />\n @if (cond.operator === 'between') {\n <span class=\"gp-grid-filter-to\">to</span>\n <input\n type=\"number\"\n [value]=\"cond.valueTo\"\n (input)=\"cond.valueTo = $any($event.target).value\"\n placeholder=\"Value\" />\n }\n }\n @if (numberConditions.length > 1) {\n <button\n type=\"button\"\n class=\"gp-grid-filter-remove\"\n (click)=\"removeNumberCondition(i)\">\u00D7</button>\n }\n </div>\n </div>\n }\n <button type=\"button\" class=\"gp-grid-filter-add\" (click)=\"addNumberCondition()\">\n + Add condition\n </button>\n } @else {\n @if (showValuesMode()) {\n <div class=\"gp-grid-filter-mode-toggle\">\n <button\n type=\"button\"\n [class.active]=\"filterMode === 'values'\"\n (click)=\"filterMode = 'values'\">\n Values\n </button>\n <button\n type=\"button\"\n [class.active]=\"filterMode === 'condition'\"\n (click)=\"filterMode = 'condition'\">\n Condition\n </button>\n </div>\n }\n\n @if (filterMode === 'values' && showValuesMode()) {\n <input\n class=\"gp-grid-filter-search\"\n type=\"text\"\n [value]=\"searchText\"\n (input)=\"searchText = $any($event.target).value\"\n placeholder=\"Search...\" />\n <div class=\"gp-grid-filter-actions\">\n <button type=\"button\" (click)=\"selectAll()\">Select All</button>\n <button type=\"button\" (click)=\"deselectAll()\">Deselect All</button>\n </div>\n <div class=\"gp-grid-filter-list\">\n <label class=\"gp-grid-filter-option\">\n <input\n type=\"checkbox\"\n [checked]=\"includeBlanks\"\n (change)=\"includeBlanks = $any($event.target).checked\" />\n <span class=\"gp-grid-filter-blank\">(Blanks)</span>\n </label>\n @for (val of filteredUniqueValues(); track val) {\n <label class=\"gp-grid-filter-option\">\n <input\n type=\"checkbox\"\n [checked]=\"selectedValues.has(val)\"\n (change)=\"toggleValue(val, $any($event.target).checked)\" />\n <span>{{ val }}</span>\n </label>\n }\n </div>\n }\n\n @if (filterMode === 'condition') {\n @for (cond of textConditions; track $index; let i = $index) {\n <div class=\"gp-grid-filter-condition\">\n @if (i > 0) {\n <div class=\"gp-grid-filter-combination\">\n <button\n type=\"button\"\n [class.active]=\"textConditions[i - 1]?.nextOperator === 'and'\"\n (click)=\"setTextNextOp(i - 1, 'and')\">\n AND\n </button>\n <button\n type=\"button\"\n [class.active]=\"textConditions[i - 1]?.nextOperator === 'or'\"\n (click)=\"setTextNextOp(i - 1, 'or')\">\n OR\n </button>\n </div>\n }\n <div class=\"gp-grid-filter-row\">\n <select\n [value]=\"cond.operator\"\n (change)=\"onTextOperatorChange(i, $any($event.target).value)\">\n @for (op of textOperators; track op.value) {\n <option [value]=\"op.value\">{{ op.label }}</option>\n }\n </select>\n @if (!isValueLessTextOp(cond.operator)) {\n <input\n class=\"gp-grid-filter-text-input\"\n type=\"text\"\n [value]=\"cond.value\"\n (input)=\"cond.value = $any($event.target).value\"\n placeholder=\"Value\" />\n }\n @if (textConditions.length > 1) {\n <button\n type=\"button\"\n class=\"gp-grid-filter-remove\"\n (click)=\"removeTextCondition(i)\">\u00D7</button>\n }\n </div>\n </div>\n }\n <button type=\"button\" class=\"gp-grid-filter-add\" (click)=\"addTextCondition()\">\n + Add condition\n </button>\n }\n }\n\n <div class=\"gp-grid-filter-buttons\">\n <button type=\"button\" class=\"gp-grid-filter-btn-clear\" (click)=\"handleClear()\">\n Clear\n </button>\n <button type=\"button\" class=\"gp-grid-filter-btn-apply\" (click)=\"handleApply()\">\n Apply\n </button>\n </div>\n </div>\n</div>\n", isInline: true, changeDetection: i0.ChangeDetectionStrategy.Eager });
942
+ }
943
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: FilterPopupComponent, decorators: [{
944
+ type: Component,
945
+ args: [{
946
+ selector: 'gp-grid-filter-popup',
947
+ standalone: true,
948
+ imports: [],
949
+ changeDetection: ChangeDetectionStrategy.Eager,
950
+ template: FILTER_POPUP_TEMPLATE,
951
+ }]
952
+ }], ctorParameters: () => [], propDecorators: { popupEl: [{
953
+ type: ViewChild,
954
+ args: ['popupEl', { static: false }]
955
+ }], column: [{ type: i0.Input, args: [{ isSignal: true, alias: "column", required: true }] }], colIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "colIndex", required: true }] }], anchorEl: [{ type: i0.Input, args: [{ isSignal: true, alias: "anchorEl", required: true }] }], distinctValues: [{ type: i0.Input, args: [{ isSignal: true, alias: "distinctValues", required: true }] }], currentFilter: [{ type: i0.Input, args: [{ isSignal: true, alias: "currentFilter", required: false }] }], apply: [{ type: i0.Output, args: ["apply"] }], close: [{ type: i0.Output, args: ["close"] }], onEscape: [{
956
+ type: HostListener,
957
+ args: ['keydown.escape']
958
+ }] } });
959
+ const setField = (arr, index, key, value) => {
960
+ const item = arr[index];
961
+ if (item)
962
+ item[key] = value;
963
+ };
964
+
965
+ const TEMPLATE = `
966
+ @if (filterPopup(); as fp) {
967
+ <gp-grid-filter-popup
968
+ [column]="fp.column"
969
+ [colIndex]="fp.colIndex"
970
+ [anchorEl]="fp.anchorEl!"
971
+ [distinctValues]="fp.distinctValues"
972
+ [currentFilter]="fp.currentFilter"
973
+ (apply)="filterApply.emit($event)"
974
+ (close)="filterClose.emit()"
975
+ />
976
+ }
977
+ @if (isResizing()) {
978
+ <div class="gp-grid-column-resize-line" [style.left.px]="resizeLineLeft()"></div>
979
+ }
980
+ @if (isLoading()) {
981
+ <div
982
+ style="position: absolute; left: 0; right: 0; bottom: 0; z-index: 50; pointer-events: none;"
983
+ [style.top.px]="headerHeight()">
984
+ <div class="gp-grid-loading-overlay"></div>
985
+ <div class="gp-grid-loading">
986
+ <div class="gp-grid-loading-spinner"></div>
987
+ </div>
988
+ </div>
989
+ }
990
+ @if (errorMessage(); as msg) {
991
+ <div class="gp-grid-error">Error: {{ msg }}</div>
992
+ }
993
+ @if (columnMove(); as cm) {
994
+ <div
995
+ class="gp-grid-column-move-ghost"
996
+ [style.left.px]="cm.currentX - cm.ghostWidth / 2"
997
+ [style.top.px]="cm.currentY - cm.ghostHeight / 2"
998
+ [style.width.px]="cm.ghostWidth"
999
+ [style.height.px]="cm.ghostHeight">
1000
+ {{ columnMoveGhostText() }}
1001
+ </div>
1002
+ @if (columnMoveDropLeft() !== null) {
1003
+ <div
1004
+ class="gp-grid-column-drop-indicator"
1005
+ [style.left.px]="columnMoveDropLeft()"
1006
+ [style.height.px]="headerHeight()"></div>
1007
+ }
1008
+ }
1009
+ @if (rowDragGhost(); as rd) {
1010
+ <div
1011
+ class="gp-grid-row-drag-ghost"
1012
+ [style.left.px]="rd.currentX + 12"
1013
+ [style.top.px]="rd.currentY - rowHeight() / 2"
1014
+ [style.width.px]="rowDragGhostWidth()"
1015
+ [style.height.px]="rowHeight()"></div>
1016
+ }
1017
+ `;
1018
+ class GridOverlaysComponent {
1019
+ filterPopup = input(null, ...(ngDevMode ? [{ debugName: "filterPopup" }] : /* istanbul ignore next */ []));
1020
+ isLoading = input(false, ...(ngDevMode ? [{ debugName: "isLoading" }] : /* istanbul ignore next */ []));
1021
+ errorMessage = input(null, ...(ngDevMode ? [{ debugName: "errorMessage" }] : /* istanbul ignore next */ []));
1022
+ headerHeight = input.required(...(ngDevMode ? [{ debugName: "headerHeight" }] : /* istanbul ignore next */ []));
1023
+ rowHeight = input.required(...(ngDevMode ? [{ debugName: "rowHeight" }] : /* istanbul ignore next */ []));
1024
+ dragState = input.required(...(ngDevMode ? [{ debugName: "dragState" }] : /* istanbul ignore next */ []));
1025
+ visibleColumnWithIndices = input.required(...(ngDevMode ? [{ debugName: "visibleColumnWithIndices" }] : /* istanbul ignore next */ []));
1026
+ columnPositions = input.required(...(ngDevMode ? [{ debugName: "columnPositions" }] : /* istanbul ignore next */ []));
1027
+ scrollLeft = input.required(...(ngDevMode ? [{ debugName: "scrollLeft" }] : /* istanbul ignore next */ []));
1028
+ effectiveColumns = input.required(...(ngDevMode ? [{ debugName: "effectiveColumns" }] : /* istanbul ignore next */ []));
1029
+ totalWidth = input.required(...(ngDevMode ? [{ debugName: "totalWidth" }] : /* istanbul ignore next */ []));
1030
+ filterApply = output();
1031
+ filterClose = output();
1032
+ isResizing = computed(() => this.dragState().dragType === 'column-resize', ...(ngDevMode ? [{ debugName: "isResizing" }] : /* istanbul ignore next */ []));
1033
+ resizeLineLeft = computed(() => {
1034
+ const cr = this.dragState().columnResize;
1035
+ if (cr === null)
1036
+ return 0;
1037
+ const visibleIndex = this.visibleColumnWithIndices().findIndex(v => v.originalIndex === cr.colIndex);
1038
+ if (visibleIndex === -1)
1039
+ return 0;
1040
+ const positions = this.columnPositions();
1041
+ return (positions[visibleIndex] ?? 0) + cr.currentWidth - this.scrollLeft();
1042
+ }, ...(ngDevMode ? [{ debugName: "resizeLineLeft" }] : /* istanbul ignore next */ []));
1043
+ columnMove = computed(() => {
1044
+ if (this.dragState().dragType !== 'column-move')
1045
+ return null;
1046
+ return this.dragState().columnMove;
1047
+ }, ...(ngDevMode ? [{ debugName: "columnMove" }] : /* istanbul ignore next */ []));
1048
+ rowDragGhost = computed(() => {
1049
+ if (this.dragState().dragType !== 'row-drag')
1050
+ return null;
1051
+ return this.dragState().rowDrag;
1052
+ }, ...(ngDevMode ? [{ debugName: "rowDragGhost" }] : /* istanbul ignore next */ []));
1053
+ columnMoveGhostText = computed(() => {
1054
+ const cm = this.columnMove();
1055
+ if (cm === null)
1056
+ return '';
1057
+ const column = this.effectiveColumns()[cm.sourceColIndex];
1058
+ return column?.headerName ?? column?.field ?? '';
1059
+ }, ...(ngDevMode ? [{ debugName: "columnMoveGhostText" }] : /* istanbul ignore next */ []));
1060
+ columnMoveDropLeft = computed(() => {
1061
+ const cm = this.columnMove();
1062
+ if (cm === null || cm.dropTargetIndex === null)
1063
+ return null;
1064
+ const positions = this.columnPositions();
1065
+ return (positions[cm.dropTargetIndex] ?? 0) - this.scrollLeft();
1066
+ }, ...(ngDevMode ? [{ debugName: "columnMoveDropLeft" }] : /* istanbul ignore next */ []));
1067
+ rowDragGhostWidth = computed(() => Math.min(300, this.totalWidth()), ...(ngDevMode ? [{ debugName: "rowDragGhostWidth" }] : /* istanbul ignore next */ []));
1068
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: GridOverlaysComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1069
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.7", type: GridOverlaysComponent, isStandalone: true, selector: "gp-grid-overlays", inputs: { filterPopup: { classPropertyName: "filterPopup", publicName: "filterPopup", isSignal: true, isRequired: false, transformFunction: null }, isLoading: { classPropertyName: "isLoading", publicName: "isLoading", isSignal: true, isRequired: false, transformFunction: null }, errorMessage: { classPropertyName: "errorMessage", publicName: "errorMessage", isSignal: true, isRequired: false, transformFunction: null }, headerHeight: { classPropertyName: "headerHeight", publicName: "headerHeight", isSignal: true, isRequired: true, transformFunction: null }, rowHeight: { classPropertyName: "rowHeight", publicName: "rowHeight", isSignal: true, isRequired: true, transformFunction: null }, dragState: { classPropertyName: "dragState", publicName: "dragState", isSignal: true, isRequired: true, transformFunction: null }, visibleColumnWithIndices: { classPropertyName: "visibleColumnWithIndices", publicName: "visibleColumnWithIndices", isSignal: true, isRequired: true, transformFunction: null }, columnPositions: { classPropertyName: "columnPositions", publicName: "columnPositions", isSignal: true, isRequired: true, transformFunction: null }, scrollLeft: { classPropertyName: "scrollLeft", publicName: "scrollLeft", isSignal: true, isRequired: true, transformFunction: null }, effectiveColumns: { classPropertyName: "effectiveColumns", publicName: "effectiveColumns", isSignal: true, isRequired: true, transformFunction: null }, totalWidth: { classPropertyName: "totalWidth", publicName: "totalWidth", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { filterApply: "filterApply", filterClose: "filterClose" }, ngImport: i0, template: "\n @if (filterPopup(); as fp) {\n <gp-grid-filter-popup\n [column]=\"fp.column\"\n [colIndex]=\"fp.colIndex\"\n [anchorEl]=\"fp.anchorEl!\"\n [distinctValues]=\"fp.distinctValues\"\n [currentFilter]=\"fp.currentFilter\"\n (apply)=\"filterApply.emit($event)\"\n (close)=\"filterClose.emit()\"\n />\n }\n @if (isResizing()) {\n <div class=\"gp-grid-column-resize-line\" [style.left.px]=\"resizeLineLeft()\"></div>\n }\n @if (isLoading()) {\n <div\n style=\"position: absolute; left: 0; right: 0; bottom: 0; z-index: 50; pointer-events: none;\"\n [style.top.px]=\"headerHeight()\">\n <div class=\"gp-grid-loading-overlay\"></div>\n <div class=\"gp-grid-loading\">\n <div class=\"gp-grid-loading-spinner\"></div>\n </div>\n </div>\n }\n @if (errorMessage(); as msg) {\n <div class=\"gp-grid-error\">Error: {{ msg }}</div>\n }\n @if (columnMove(); as cm) {\n <div\n class=\"gp-grid-column-move-ghost\"\n [style.left.px]=\"cm.currentX - cm.ghostWidth / 2\"\n [style.top.px]=\"cm.currentY - cm.ghostHeight / 2\"\n [style.width.px]=\"cm.ghostWidth\"\n [style.height.px]=\"cm.ghostHeight\">\n {{ columnMoveGhostText() }}\n </div>\n @if (columnMoveDropLeft() !== null) {\n <div\n class=\"gp-grid-column-drop-indicator\"\n [style.left.px]=\"columnMoveDropLeft()\"\n [style.height.px]=\"headerHeight()\"></div>\n }\n }\n @if (rowDragGhost(); as rd) {\n <div\n class=\"gp-grid-row-drag-ghost\"\n [style.left.px]=\"rd.currentX + 12\"\n [style.top.px]=\"rd.currentY - rowHeight() / 2\"\n [style.width.px]=\"rowDragGhostWidth()\"\n [style.height.px]=\"rowHeight()\"></div>\n }\n", isInline: true, dependencies: [{ kind: "component", type: FilterPopupComponent, selector: "gp-grid-filter-popup", inputs: ["column", "colIndex", "anchorEl", "distinctValues", "currentFilter"], outputs: ["apply", "close"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1070
+ }
1071
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: GridOverlaysComponent, decorators: [{
1072
+ type: Component,
1073
+ args: [{
1074
+ selector: 'gp-grid-overlays',
1075
+ standalone: true,
1076
+ imports: [FilterPopupComponent],
1077
+ changeDetection: ChangeDetectionStrategy.OnPush,
1078
+ template: TEMPLATE,
1079
+ }]
1080
+ }], propDecorators: { filterPopup: [{ type: i0.Input, args: [{ isSignal: true, alias: "filterPopup", required: false }] }], isLoading: [{ type: i0.Input, args: [{ isSignal: true, alias: "isLoading", required: false }] }], errorMessage: [{ type: i0.Input, args: [{ isSignal: true, alias: "errorMessage", required: false }] }], headerHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "headerHeight", required: true }] }], rowHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowHeight", required: true }] }], dragState: [{ type: i0.Input, args: [{ isSignal: true, alias: "dragState", required: true }] }], visibleColumnWithIndices: [{ type: i0.Input, args: [{ isSignal: true, alias: "visibleColumnWithIndices", required: true }] }], columnPositions: [{ type: i0.Input, args: [{ isSignal: true, alias: "columnPositions", required: true }] }], scrollLeft: [{ type: i0.Input, args: [{ isSignal: true, alias: "scrollLeft", required: true }] }], effectiveColumns: [{ type: i0.Input, args: [{ isSignal: true, alias: "effectiveColumns", required: true }] }], totalWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "totalWidth", required: true }] }], filterApply: [{ type: i0.Output, args: ["filterApply"] }], filterClose: [{ type: i0.Output, args: ["filterClose"] }] } });
1081
+
1082
+ const GP_GRID_TEMPLATE = `
1083
+ <div #container
1084
+ [class]="'gp-grid-container' + (darkMode() ? ' gp-grid-container--dark' : '')"
1085
+ style="width: 100%; height: 100%; display: flex; flex-direction: column; position: relative; outline: none;"
1086
+ tabindex="0"
1087
+ (keydown)="onKeyDown($event)"
1088
+ (wheel)="onWheel($event)"
1089
+ >
1090
+ <gp-grid-header
1091
+ [headerHeight]="headerHeight()"
1092
+ [scrollLeft]="vm.scrollLeft()"
1093
+ [contentWidth]="vm.contentWidth()"
1094
+ [totalWidth]="vm.totalWidth()"
1095
+ [isLoading]="vm.isLoading()"
1096
+ [visibleColumnsWithIndices]="vm.visibleColumnWithIndices()"
1097
+ [columnPositions]="vm.columnPositions()"
1098
+ [columnWidths]="vm.columnWidths()"
1099
+ [headers]="vm.headerState()"
1100
+ [sortingEnabled]="sortingEnabled()"
1101
+ [headerRenderers]="headerRenderers()"
1102
+ [globalHeaderRenderer]="headerRenderer()"
1103
+ (headerPointerDown)="onHeaderPointerDown($event)"
1104
+ (filterPointerDown)="onFilterPointerDown($event)"
1105
+ (resizePointerDown)="onResizePointerDown($event)"
1106
+ (headerSort)="onHeaderSort($event)"
1107
+ />
1108
+ <gp-grid-body
1109
+ [rowHeight]="rowHeight()"
1110
+ [totalHeaderHeight]="headerHeight()"
1111
+ [contentWidth]="vm.contentWidth()"
1112
+ [contentHeight]="vm.contentHeight()"
1113
+ [totalWidth]="vm.totalWidth()"
1114
+ [rowsWrapperOffset]="vm.rowsWrapperOffset()"
1115
+ [slotsArray]="vm.slotsArray()"
1116
+ [visibleColumnWithIndices]="vm.visibleColumnWithIndices()"
1117
+ [columnPositions]="vm.columnPositions()"
1118
+ [columnWidths]="vm.columnWidths()"
1119
+ [totalRows]="vm.totalRows()"
1120
+ [activeCell]="vm.activeCell()"
1121
+ [selectionRange]="vm.selectionRange()"
1122
+ [editingCell]="vm.editingCell()"
1123
+ [cellRenderers]="cellRenderers()"
1124
+ [globalCellRenderer]="cellRenderer()"
1125
+ [editRenderers]="editRenderers()"
1126
+ [globalEditRenderer]="editRenderer()"
1127
+ [hoverPosition]="vm.hoverPosition()"
1128
+ [computeRowClasses]="computeRowClassesFn"
1129
+ [computeCellClasses]="computeCellClassesFn"
1130
+ [fillHandlePosition]="vm.fillHandlePosition()"
1131
+ [dragState]="vm.dragState()"
1132
+ (scrolled)="onBodyScroll($event)"
1133
+ (cellPointerDown)="onCellPointerDown($event)"
1134
+ (cellPointerEnter)="onCellPointerEnter($event)"
1135
+ (cellPointerLeave)="onCellPointerLeave()"
1136
+ (cellDoubleClick)="onCellDoubleClick($event)"
1137
+ (editValueChange)="onEditValueChange($event)"
1138
+ (editCommit)="onEditCommit()"
1139
+ (editCancel)="onEditCancel()"
1140
+ (fillHandlePointerDown)="onFillHandlePointerDown($event)"
1141
+ />
1142
+ <gp-grid-overlays
1143
+ [filterPopup]="vm.filterPopup()"
1144
+ [isLoading]="vm.isLoading()"
1145
+ [errorMessage]="vm.errorMessage()"
1146
+ [headerHeight]="headerHeight()"
1147
+ [rowHeight]="rowHeight()"
1148
+ [dragState]="vm.dragState()"
1149
+ [visibleColumnWithIndices]="vm.visibleColumnWithIndices()"
1150
+ [columnPositions]="vm.columnPositions()"
1151
+ [scrollLeft]="vm.scrollLeft()"
1152
+ [effectiveColumns]="vm.effectiveColumns()"
1153
+ [totalWidth]="vm.totalWidth()"
1154
+ (filterApply)="onFilterApply($event)"
1155
+ (filterClose)="onFilterClose()"
1156
+ />
1157
+ </div>
1158
+ `;
1159
+
1160
+ const INITIAL_DRAG_STATE = {
1161
+ isDragging: false,
1162
+ dragType: null,
1163
+ fillSourceRange: null,
1164
+ fillTarget: null,
1165
+ columnResize: null,
1166
+ columnMove: null,
1167
+ rowDrag: null,
1168
+ };
1169
+ /**
1170
+ * Reactive view-state container for GpGridComponent.
1171
+ *
1172
+ * Owns every signal and computed the template binds against, plus the
1173
+ * batch-change setters bag wired into those signals. The component
1174
+ * becomes a thin shell that holds lifecycle, event handlers, and inputs.
1175
+ *
1176
+ * Angular-specific (uses signal/computed from @angular/core) — lives in
1177
+ * the angular package, not core.
1178
+ */
1179
+ class GpGridViewModel {
1180
+ headerState = signal(new Map(), ...(ngDevMode ? [{ debugName: "headerState" }] : /* istanbul ignore next */ []));
1181
+ viewportWidth = signal(0, ...(ngDevMode ? [{ debugName: "viewportWidth" }] : /* istanbul ignore next */ []));
1182
+ scrollLeft = signal(0, ...(ngDevMode ? [{ debugName: "scrollLeft" }] : /* istanbul ignore next */ []));
1183
+ isLoading = signal(false, ...(ngDevMode ? [{ debugName: "isLoading" }] : /* istanbul ignore next */ []));
1184
+ errorMessage = signal(null, ...(ngDevMode ? [{ debugName: "errorMessage" }] : /* istanbul ignore next */ []));
1185
+ filterPopup = signal(null, ...(ngDevMode ? [{ debugName: "filterPopup" }] : /* istanbul ignore next */ []));
1186
+ pendingScrollTop = signal(null, ...(ngDevMode ? [{ debugName: "pendingScrollTop" }] : /* istanbul ignore next */ []));
1187
+ activeCell = signal(null, ...(ngDevMode ? [{ debugName: "activeCell" }] : /* istanbul ignore next */ []));
1188
+ selectionRange = signal(null, ...(ngDevMode ? [{ debugName: "selectionRange" }] : /* istanbul ignore next */ []));
1189
+ editingCell = signal(null, ...(ngDevMode ? [{ debugName: "editingCell" }] : /* istanbul ignore next */ []));
1190
+ hoverPosition = signal(null, ...(ngDevMode ? [{ debugName: "hoverPosition" }] : /* istanbul ignore next */ []));
1191
+ columnsOverride = signal(null, ...(ngDevMode ? [{ debugName: "columnsOverride" }] : /* istanbul ignore next */ []));
1192
+ dragState = signal(INITIAL_DRAG_STATE, ...(ngDevMode ? [{ debugName: "dragState" }] : /* istanbul ignore next */ []));
1193
+ contentWidth = signal(0, ...(ngDevMode ? [{ debugName: "contentWidth" }] : /* istanbul ignore next */ []));
1194
+ contentHeight = signal(0, ...(ngDevMode ? [{ debugName: "contentHeight" }] : /* istanbul ignore next */ []));
1195
+ rowsWrapperOffset = signal(0, ...(ngDevMode ? [{ debugName: "rowsWrapperOffset" }] : /* istanbul ignore next */ []));
1196
+ slots = signal(new Map(), ...(ngDevMode ? [{ debugName: "slots" }] : /* istanbul ignore next */ []));
1197
+ effectiveColumns;
1198
+ visibleColumnWithIndices;
1199
+ columnPositions;
1200
+ columnWidths;
1201
+ totalWidth;
1202
+ fillHandlePosition;
1203
+ slotsArray;
1204
+ totalRows;
1205
+ batchSetters;
1206
+ filterAnchorEl = null;
1207
+ constructor(deps) {
1208
+ this.effectiveColumns = computed(() => this.columnsOverride() ?? deps.getColumns(), ...(ngDevMode ? [{ debugName: "effectiveColumns" }] : /* istanbul ignore next */ []));
1209
+ this.visibleColumnWithIndices = computed(() => this.effectiveColumns()
1210
+ .map((col, index) => ({ column: col, originalIndex: index }))
1211
+ .filter(({ column }) => !column.hidden), ...(ngDevMode ? [{ debugName: "visibleColumnWithIndices" }] : /* istanbul ignore next */ []));
1212
+ const columnLayout = computed(() => calculateScaledColumnPositions(this.visibleColumnWithIndices().map(v => v.column), this.viewportWidth()), ...(ngDevMode ? [{ debugName: "columnLayout" }] : /* istanbul ignore next */ []));
1213
+ this.columnPositions = computed(() => columnLayout().positions, ...(ngDevMode ? [{ debugName: "columnPositions" }] : /* istanbul ignore next */ []));
1214
+ this.columnWidths = computed(() => columnLayout().widths, ...(ngDevMode ? [{ debugName: "columnWidths" }] : /* istanbul ignore next */ []));
1215
+ this.totalWidth = computed(() => getTotalWidth(this.columnPositions()), ...(ngDevMode ? [{ debugName: "totalWidth" }] : /* istanbul ignore next */ []));
1216
+ this.fillHandlePosition = computed(() => calculateFillHandlePosition({
1217
+ activeCell: this.activeCell(),
1218
+ selectionRange: this.selectionRange(),
1219
+ slots: this.slots(),
1220
+ columns: this.effectiveColumns(),
1221
+ visibleColumnsWithIndices: this.visibleColumnWithIndices(),
1222
+ columnPositions: this.columnPositions(),
1223
+ columnWidths: this.columnWidths(),
1224
+ rowHeight: deps.getRowHeight(),
1225
+ }), ...(ngDevMode ? [{ debugName: "fillHandlePosition" }] : /* istanbul ignore next */ []));
1226
+ this.slotsArray = computed(() => [...this.slots().values()], ...(ngDevMode ? [{ debugName: "slotsArray" }] : /* istanbul ignore next */ []));
1227
+ this.totalRows = computed(() => deps.getRows().length, ...(ngDevMode ? [{ debugName: "totalRows" }] : /* istanbul ignore next */ []));
1228
+ this.batchSetters = {
1229
+ setContentWidth: (v) => this.contentWidth.set(v),
1230
+ setContentHeight: (v) => this.contentHeight.set(v),
1231
+ setRowsWrapperOffset: (v) => this.rowsWrapperOffset.set(v),
1232
+ setIsLoading: (v) => this.isLoading.set(v),
1233
+ setErrorMessage: (v) => this.errorMessage.set(v),
1234
+ setPendingScrollTop: (v) => this.pendingScrollTop.set(v),
1235
+ setActiveCell: (v) => this.activeCell.set(v),
1236
+ setSelectionRange: (v) => this.selectionRange.set(v),
1237
+ setEditingCell: (v) => this.editingCell.set(v),
1238
+ setHoverPosition: (v) => this.hoverPosition.set(v),
1239
+ setColumnsOverride: (v) => this.columnsOverride.set(v),
1240
+ onFilterPopupChange: (v) => this.materializeFilterPopup(v),
1241
+ };
1242
+ }
1243
+ setFilterAnchor(el) {
1244
+ this.filterAnchorEl = el;
1245
+ }
1246
+ materializeFilterPopup(state) {
1247
+ if (state === null) {
1248
+ this.filterPopup.set(null);
1249
+ return;
1250
+ }
1251
+ if (state.isOpen && state.column) {
1252
+ this.filterPopup.set({
1253
+ colIndex: state.colIndex,
1254
+ column: state.column,
1255
+ distinctValues: state.distinctValues,
1256
+ currentFilter: state.currentFilter,
1257
+ anchorEl: this.filterAnchorEl,
1258
+ });
1259
+ }
1260
+ }
1261
+ }
1262
+
1263
+ /**
1264
+ * Owns the core grid instance plus every framework-agnostic adapter the
1265
+ * Angular component drives (auto-scroll, pending row-drag, input events,
1266
+ * data source ownership). The component becomes a thin shell that holds
1267
+ * lifecycle + Angular template bindings and delegates state work here.
1268
+ */
1269
+ class GpGridBindings {
1270
+ deps;
1271
+ dataSourceOwner = new DataSourceOwner();
1272
+ autoScroll;
1273
+ pendingRowDrag;
1274
+ input;
1275
+ coreRef = null;
1276
+ unsubscribe = null;
1277
+ resizeObserver = null;
1278
+ constructor(deps) {
1279
+ this.deps = deps;
1280
+ this.autoScroll = new AutoScrollDriver(() => this.deps.getBody(), (event) => this.input.dragMove(event));
1281
+ this.pendingRowDrag = new PendingRowDragController({
1282
+ getCore: () => this.coreRef,
1283
+ getContainer: this.deps.getContainer,
1284
+ isBrowser: this.deps.isBrowser,
1285
+ onDragConfirmed: (state) => this.deps.vm.dragState.set(state),
1286
+ });
1287
+ this.input = new InputEventAdapter({
1288
+ getCore: () => this.coreRef,
1289
+ getBodyEl: this.deps.getBody,
1290
+ autoScroll: this.autoScroll,
1291
+ pendingRowDrag: this.pendingRowDrag,
1292
+ onDragStateChange: (state) => this.deps.vm.dragState.set(state),
1293
+ });
1294
+ }
1295
+ attach(core) {
1296
+ this.coreRef = core;
1297
+ this.unsubscribe = core.onBatchInstruction((instructions) => {
1298
+ const vm = this.deps.vm;
1299
+ const maps = applyBatchInstructions(instructions, vm.slots(), vm.headerState(), vm.batchSetters);
1300
+ vm.slots.set(new Map(maps.slots));
1301
+ vm.headerState.set(new Map(maps.headers));
1302
+ });
1303
+ core.initialize();
1304
+ core.input.updateDeps({
1305
+ getHeaderHeight: this.deps.getHeaderHeight,
1306
+ getRowHeight: this.deps.getRowHeight,
1307
+ getColumnPositions: () => this.deps.vm.columnPositions(),
1308
+ getColumnCount: () => this.deps.vm.visibleColumnWithIndices().length,
1309
+ getOriginalColumnIndex: (visibleIndex) => this.deps.vm.visibleColumnWithIndices()[visibleIndex]?.originalIndex ?? visibleIndex,
1310
+ });
1311
+ }
1312
+ observeViewport(container, bodyEl) {
1313
+ this.deps.vm.viewportWidth.set(container.clientWidth);
1314
+ this.resizeObserver = new ResizeObserver((entries) => {
1315
+ const entry = entries[0];
1316
+ if (entry)
1317
+ this.deps.vm.viewportWidth.set(entry.contentRect.width);
1318
+ });
1319
+ this.resizeObserver.observe(container);
1320
+ this.coreRef?.setViewport(0, 0, container.clientWidth, bodyEl.clientHeight);
1321
+ }
1322
+ destroy() {
1323
+ this.autoScroll.stop();
1324
+ this.pendingRowDrag.cancel();
1325
+ this.pendingRowDrag.releaseLocks();
1326
+ this.unsubscribe?.();
1327
+ this.resizeObserver?.disconnect();
1328
+ this.coreRef?.destroy();
1329
+ this.dataSourceOwner.destroy();
1330
+ this.coreRef = null;
1331
+ }
1332
+ syncHighlighting(opts) {
1333
+ const core = this.coreRef;
1334
+ if (core?.highlight && opts) {
1335
+ core.highlight.updateOptions(opts);
1336
+ }
1337
+ }
1338
+ syncColumns(cols) {
1339
+ const core = this.coreRef;
1340
+ if (core === null)
1341
+ return;
1342
+ if (this.dataSourceOwner.syncColumns(cols))
1343
+ core.setColumns(cols);
1344
+ }
1345
+ syncRows(rows, dataSource) {
1346
+ const core = this.coreRef;
1347
+ if (core === null)
1348
+ return;
1349
+ const newDs = this.dataSourceOwner.syncRows(rows, dataSource);
1350
+ if (newDs !== null)
1351
+ core.setDataSource(newDs);
1352
+ }
1353
+ applyPendingScroll() {
1354
+ const top = this.deps.vm.pendingScrollTop();
1355
+ const body = this.deps.getBody();
1356
+ if (top !== null && body) {
1357
+ body.scrollTop = top;
1358
+ this.deps.vm.pendingScrollTop.set(null);
1359
+ }
1360
+ }
1361
+ scrollToRow(row) {
1362
+ const core = this.coreRef;
1363
+ const body = this.deps.getBody();
1364
+ if (core === null || body === null)
1365
+ return;
1366
+ scrollCellIntoView(core, body, row, this.deps.getRowHeight(), this.deps.vm.slots(), this.deps.vm.rowsWrapperOffset());
1367
+ }
1368
+ }
1369
+
1370
+ const buildGridCore = (inputs, emitters) => {
1371
+ const cellValueChanged = inputs.getRowId === undefined
1372
+ ? undefined
1373
+ : emitters.onCellValueChanged;
1374
+ return new GridCore({
1375
+ columns: inputs.columns,
1376
+ dataSource: inputs.dataSource,
1377
+ rowHeight: inputs.rowHeight,
1378
+ headerHeight: inputs.headerHeight,
1379
+ overscan: inputs.overscan,
1380
+ sortingEnabled: inputs.sortingEnabled,
1381
+ highlighting: inputs.highlighting,
1382
+ getRowId: inputs.getRowId,
1383
+ rowDragEntireRow: inputs.rowDragEntireRow,
1384
+ onRowDragEnd: emitters.onRowDragEnd,
1385
+ onCellValueChanged: cellValueChanged,
1386
+ onColumnResized: emitters.onColumnResized,
1387
+ onColumnMoved: emitters.onColumnMoved,
1388
+ });
1389
+ };
1390
+
1391
+ class GpGridComponent {
1392
+ container;
1393
+ body;
1394
+ isBrowser = isPlatformBrowser(inject(PLATFORM_ID));
1395
+ columns = input.required(...(ngDevMode ? [{ debugName: "columns" }] : /* istanbul ignore next */ []));
1396
+ rows = input([], ...(ngDevMode ? [{ debugName: "rows" }] : /* istanbul ignore next */ []));
1397
+ dataSource = input(null, ...(ngDevMode ? [{ debugName: "dataSource" }] : /* istanbul ignore next */ []));
1398
+ getRowId = input(null, ...(ngDevMode ? [{ debugName: "getRowId" }] : /* istanbul ignore next */ []));
1399
+ rowHeight = input(32, ...(ngDevMode ? [{ debugName: "rowHeight" }] : /* istanbul ignore next */ []));
1400
+ headerHeight = input(32, ...(ngDevMode ? [{ debugName: "headerHeight" }] : /* istanbul ignore next */ []));
1401
+ darkMode = input(false, ...(ngDevMode ? [{ debugName: "darkMode" }] : /* istanbul ignore next */ []));
1402
+ cellRenderers = input({}, ...(ngDevMode ? [{ debugName: "cellRenderers" }] : /* istanbul ignore next */ []));
1403
+ headerRenderers = input({}, ...(ngDevMode ? [{ debugName: "headerRenderers" }] : /* istanbul ignore next */ []));
1404
+ editRenderers = input({}, ...(ngDevMode ? [{ debugName: "editRenderers" }] : /* istanbul ignore next */ []));
1405
+ cellRenderer = input(null, ...(ngDevMode ? [{ debugName: "cellRenderer" }] : /* istanbul ignore next */ []));
1406
+ headerRenderer = input(null, ...(ngDevMode ? [{ debugName: "headerRenderer" }] : /* istanbul ignore next */ []));
1407
+ editRenderer = input(null, ...(ngDevMode ? [{ debugName: "editRenderer" }] : /* istanbul ignore next */ []));
1408
+ highlighting = input(null, ...(ngDevMode ? [{ debugName: "highlighting" }] : /* istanbul ignore next */ []));
1409
+ rowDragEntireRow = input(false, ...(ngDevMode ? [{ debugName: "rowDragEntireRow" }] : /* istanbul ignore next */ []));
1410
+ overscan = input(3, ...(ngDevMode ? [{ debugName: "overscan" }] : /* istanbul ignore next */ []));
1411
+ sortingEnabled = input(true, ...(ngDevMode ? [{ debugName: "sortingEnabled" }] : /* istanbul ignore next */ []));
1412
+ wheelDampening = input(0.1, ...(ngDevMode ? [{ debugName: "wheelDampening" }] : /* istanbul ignore next */ []));
1413
+ onRowDragEnd = output();
1414
+ onCellValueChanged = output();
1415
+ onColumnResized = output();
1416
+ onColumnMoved = output();
1417
+ vm = new GpGridViewModel({
1418
+ getColumns: () => this.columns(),
1419
+ getRows: () => this.rows(),
1420
+ getRowHeight: () => this.rowHeight(),
1421
+ });
1422
+ bindings = new GpGridBindings({
1423
+ vm: this.vm,
1424
+ isBrowser: this.isBrowser,
1425
+ getContainer: () => this.container?.nativeElement ?? null,
1426
+ getBody: () => this.body?.scrollContainer?.nativeElement ?? null,
1427
+ getRowHeight: () => this.rowHeight(),
1428
+ getHeaderHeight: () => this.headerHeight(),
1429
+ });
1430
+ constructor() {
1431
+ effect(() => this.bindings.applyPendingScroll());
1432
+ effect(() => this.bindings.syncHighlighting(this.highlighting()));
1433
+ effect(() => this.bindings.syncColumns(this.columns()));
1434
+ effect(() => this.bindings.syncRows(this.rows(), this.dataSource()));
1435
+ }
1436
+ ngOnInit() {
1437
+ const core = buildGridCore({
1438
+ columns: this.columns(),
1439
+ dataSource: this.bindings.dataSourceOwner.initialize(this.dataSource(), this.rows()),
1440
+ rowHeight: this.rowHeight(),
1441
+ headerHeight: this.headerHeight(),
1442
+ overscan: this.overscan(),
1443
+ sortingEnabled: this.sortingEnabled(),
1444
+ highlighting: (this.highlighting() ?? undefined),
1445
+ getRowId: this.getRowId() ?? undefined,
1446
+ rowDragEntireRow: this.rowDragEntireRow(),
1447
+ }, {
1448
+ onRowDragEnd: (source, target) => this.onRowDragEnd.emit({ source, target }),
1449
+ onCellValueChanged: (event) => this.onCellValueChanged.emit(event),
1450
+ onColumnResized: (colIndex, newWidth) => this.onColumnResized.emit({ colIndex, newWidth }),
1451
+ onColumnMoved: (fromIndex, toIndex) => this.onColumnMoved.emit({ fromIndex, toIndex }),
1452
+ });
1453
+ this.bindings.attach(core);
1454
+ }
1455
+ ngAfterViewInit() {
1456
+ if (this.isBrowser === false)
1457
+ return;
1458
+ this.bindings.observeViewport(this.container.nativeElement, this.body.scrollContainer.nativeElement);
1459
+ document.addEventListener('pointermove', this.onDocumentPointerMove, { passive: false });
1460
+ document.addEventListener('pointerup', this.onDocumentPointerUp);
1461
+ }
1462
+ ngOnDestroy() {
1463
+ this.bindings.destroy();
1464
+ if (this.isBrowser) {
1465
+ document.removeEventListener('pointermove', this.onDocumentPointerMove);
1466
+ document.removeEventListener('pointerup', this.onDocumentPointerUp);
1467
+ }
1468
+ }
1469
+ onBodyScroll(scrollLeft) {
1470
+ this.vm.scrollLeft.set(scrollLeft);
1471
+ const el = this.body.scrollContainer.nativeElement;
1472
+ this.bindings.coreRef?.setViewport(el.scrollTop, scrollLeft, el.clientWidth, el.clientHeight);
1473
+ }
1474
+ onHeaderPointerDown(evt) {
1475
+ if (this.bindings.input.headerPointerDown(evt.colIndex, evt.colWidth, evt.colHeight, evt.event)) {
1476
+ evt.event.preventDefault();
1477
+ }
1478
+ }
1479
+ onFilterPointerDown(evt) {
1480
+ this.vm.setFilterAnchor(evt.anchorEl);
1481
+ const rect = evt.anchorEl.getBoundingClientRect();
1482
+ this.bindings.coreRef?.openFilterPopup(evt.colIndex, {
1483
+ top: rect.top,
1484
+ left: rect.left,
1485
+ width: rect.width,
1486
+ height: rect.height,
1487
+ });
1488
+ }
1489
+ onCellPointerDown(evt) {
1490
+ const action = this.bindings.input.cellPointerDown(evt.rowIndex, evt.colIndex, evt.event);
1491
+ if (action.preventDefault)
1492
+ evt.event.preventDefault();
1493
+ if (action.focusContainer) {
1494
+ this.container.nativeElement.focus({ preventScroll: true });
1495
+ }
1496
+ }
1497
+ onCellPointerEnter(evt) {
1498
+ this.bindings.input.cellPointerEnter(evt.rowIndex, evt.colIndex);
1499
+ }
1500
+ onFillHandlePointerDown(evt) {
1501
+ const action = this.bindings.input.fillHandlePointerDown(this.vm.activeCell(), this.vm.selectionRange(), evt.event);
1502
+ if (action.preventDefault)
1503
+ evt.event.preventDefault();
1504
+ if (action.stopPropagation)
1505
+ evt.event.stopPropagation();
1506
+ }
1507
+ onCellPointerLeave() {
1508
+ this.bindings.input.cellPointerLeave();
1509
+ }
1510
+ computeRowClassesFn = (rowIndex, rowData) => {
1511
+ return this.bindings.coreRef?.highlight?.computeRowClasses(rowIndex, rowData) ?? [];
1512
+ };
1513
+ computeCellClassesFn = (rowIndex, colIndex, column, rowData) => {
1514
+ return this.bindings.coreRef?.highlight?.computeCombinedCellClasses(rowIndex, colIndex, column, rowData) ?? [];
1515
+ };
1516
+ onCellDoubleClick(evt) {
1517
+ this.bindings.coreRef?.startEdit(evt.rowIndex, evt.colIndex);
1518
+ }
1519
+ onEditValueChange(value) {
1520
+ this.bindings.coreRef?.updateEditValue(value);
1521
+ }
1522
+ onEditCommit() {
1523
+ this.bindings.coreRef?.commitEdit();
1524
+ }
1525
+ onEditCancel() {
1526
+ this.bindings.coreRef?.cancelEdit();
1527
+ }
1528
+ onHeaderSort(evt) {
1529
+ this.bindings.coreRef?.setSort(evt.colId, evt.direction, evt.addToExisting);
1530
+ }
1531
+ onWheel(event) {
1532
+ const bodyEl = this.body?.scrollContainer?.nativeElement;
1533
+ if (!bodyEl)
1534
+ return;
1535
+ const dampened = this.bindings.input.wheel(event.deltaY, event.deltaX, this.wheelDampening());
1536
+ if (dampened) {
1537
+ event.preventDefault();
1538
+ bodyEl.scrollTop += dampened.dy;
1539
+ bodyEl.scrollLeft += dampened.dx;
1540
+ }
1541
+ }
1542
+ onKeyDown(event) {
1543
+ const editing = this.vm.editingCell();
1544
+ const result = this.bindings.input.keyDown(event, this.vm.activeCell(), editing === null ? null : { row: editing.row, col: editing.col }, this.vm.filterPopup() !== null);
1545
+ if (result.preventDefault)
1546
+ event.preventDefault();
1547
+ if (result.scrollToCell)
1548
+ this.bindings.scrollToRow(result.scrollToCell.row);
1549
+ }
1550
+ onResizePointerDown(evt) {
1551
+ if (this.bindings.input.resizePointerDown(evt.colIndex, evt.colWidth, evt.event)) {
1552
+ evt.event.preventDefault();
1553
+ }
1554
+ }
1555
+ onFilterApply(event) {
1556
+ this.bindings.coreRef?.setFilter(event.colId, event.filter);
1557
+ this.vm.filterPopup.set(null);
1558
+ }
1559
+ onFilterClose() {
1560
+ this.bindings.coreRef?.closeFilterPopup();
1561
+ this.vm.filterPopup.set(null);
1562
+ }
1563
+ onDocumentPointerMove = (event) => {
1564
+ if (this.bindings.input.documentPointerMove(event))
1565
+ event.preventDefault();
1566
+ };
1567
+ onDocumentPointerUp = (_event) => {
1568
+ const { wasRowDrag } = this.bindings.input.documentPointerUp();
1569
+ if (wasRowDrag)
1570
+ this.bindings.pendingRowDrag.releaseLocks();
1571
+ };
1572
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: GpGridComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1573
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.7", type: GpGridComponent, isStandalone: true, selector: "gp-grid", inputs: { columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: true, transformFunction: null }, rows: { classPropertyName: "rows", publicName: "rows", isSignal: true, isRequired: false, transformFunction: null }, dataSource: { classPropertyName: "dataSource", publicName: "dataSource", isSignal: true, isRequired: false, transformFunction: null }, getRowId: { classPropertyName: "getRowId", publicName: "getRowId", isSignal: true, isRequired: false, transformFunction: null }, rowHeight: { classPropertyName: "rowHeight", publicName: "rowHeight", isSignal: true, isRequired: false, transformFunction: null }, headerHeight: { classPropertyName: "headerHeight", publicName: "headerHeight", isSignal: true, isRequired: false, transformFunction: null }, darkMode: { classPropertyName: "darkMode", publicName: "darkMode", isSignal: true, isRequired: false, transformFunction: null }, cellRenderers: { classPropertyName: "cellRenderers", publicName: "cellRenderers", isSignal: true, isRequired: false, transformFunction: null }, headerRenderers: { classPropertyName: "headerRenderers", publicName: "headerRenderers", isSignal: true, isRequired: false, transformFunction: null }, editRenderers: { classPropertyName: "editRenderers", publicName: "editRenderers", isSignal: true, isRequired: false, transformFunction: null }, cellRenderer: { classPropertyName: "cellRenderer", publicName: "cellRenderer", isSignal: true, isRequired: false, transformFunction: null }, headerRenderer: { classPropertyName: "headerRenderer", publicName: "headerRenderer", isSignal: true, isRequired: false, transformFunction: null }, editRenderer: { classPropertyName: "editRenderer", publicName: "editRenderer", isSignal: true, isRequired: false, transformFunction: null }, highlighting: { classPropertyName: "highlighting", publicName: "highlighting", isSignal: true, isRequired: false, transformFunction: null }, rowDragEntireRow: { classPropertyName: "rowDragEntireRow", publicName: "rowDragEntireRow", isSignal: true, isRequired: false, transformFunction: null }, overscan: { classPropertyName: "overscan", publicName: "overscan", isSignal: true, isRequired: false, transformFunction: null }, sortingEnabled: { classPropertyName: "sortingEnabled", publicName: "sortingEnabled", isSignal: true, isRequired: false, transformFunction: null }, wheelDampening: { classPropertyName: "wheelDampening", publicName: "wheelDampening", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { onRowDragEnd: "onRowDragEnd", onCellValueChanged: "onCellValueChanged", onColumnResized: "onColumnResized", onColumnMoved: "onColumnMoved" }, viewQueries: [{ propertyName: "container", first: true, predicate: ["container"], descendants: true, static: true }, { propertyName: "body", first: true, predicate: GridBodyComponent, descendants: true }], ngImport: i0, template: "\n <div #container\n [class]=\"'gp-grid-container' + (darkMode() ? ' gp-grid-container--dark' : '')\"\n style=\"width: 100%; height: 100%; display: flex; flex-direction: column; position: relative; outline: none;\"\n tabindex=\"0\"\n (keydown)=\"onKeyDown($event)\"\n (wheel)=\"onWheel($event)\"\n >\n <gp-grid-header\n [headerHeight]=\"headerHeight()\"\n [scrollLeft]=\"vm.scrollLeft()\"\n [contentWidth]=\"vm.contentWidth()\"\n [totalWidth]=\"vm.totalWidth()\"\n [isLoading]=\"vm.isLoading()\"\n [visibleColumnsWithIndices]=\"vm.visibleColumnWithIndices()\"\n [columnPositions]=\"vm.columnPositions()\"\n [columnWidths]=\"vm.columnWidths()\"\n [headers]=\"vm.headerState()\"\n [sortingEnabled]=\"sortingEnabled()\"\n [headerRenderers]=\"headerRenderers()\"\n [globalHeaderRenderer]=\"headerRenderer()\"\n (headerPointerDown)=\"onHeaderPointerDown($event)\"\n (filterPointerDown)=\"onFilterPointerDown($event)\"\n (resizePointerDown)=\"onResizePointerDown($event)\"\n (headerSort)=\"onHeaderSort($event)\"\n />\n <gp-grid-body\n [rowHeight]=\"rowHeight()\"\n [totalHeaderHeight]=\"headerHeight()\"\n [contentWidth]=\"vm.contentWidth()\"\n [contentHeight]=\"vm.contentHeight()\"\n [totalWidth]=\"vm.totalWidth()\"\n [rowsWrapperOffset]=\"vm.rowsWrapperOffset()\"\n [slotsArray]=\"vm.slotsArray()\"\n [visibleColumnWithIndices]=\"vm.visibleColumnWithIndices()\"\n [columnPositions]=\"vm.columnPositions()\"\n [columnWidths]=\"vm.columnWidths()\"\n [totalRows]=\"vm.totalRows()\"\n [activeCell]=\"vm.activeCell()\"\n [selectionRange]=\"vm.selectionRange()\"\n [editingCell]=\"vm.editingCell()\"\n [cellRenderers]=\"cellRenderers()\"\n [globalCellRenderer]=\"cellRenderer()\"\n [editRenderers]=\"editRenderers()\"\n [globalEditRenderer]=\"editRenderer()\"\n [hoverPosition]=\"vm.hoverPosition()\"\n [computeRowClasses]=\"computeRowClassesFn\"\n [computeCellClasses]=\"computeCellClassesFn\"\n [fillHandlePosition]=\"vm.fillHandlePosition()\"\n [dragState]=\"vm.dragState()\"\n (scrolled)=\"onBodyScroll($event)\"\n (cellPointerDown)=\"onCellPointerDown($event)\"\n (cellPointerEnter)=\"onCellPointerEnter($event)\"\n (cellPointerLeave)=\"onCellPointerLeave()\"\n (cellDoubleClick)=\"onCellDoubleClick($event)\"\n (editValueChange)=\"onEditValueChange($event)\"\n (editCommit)=\"onEditCommit()\"\n (editCancel)=\"onEditCancel()\"\n (fillHandlePointerDown)=\"onFillHandlePointerDown($event)\"\n />\n <gp-grid-overlays\n [filterPopup]=\"vm.filterPopup()\"\n [isLoading]=\"vm.isLoading()\"\n [errorMessage]=\"vm.errorMessage()\"\n [headerHeight]=\"headerHeight()\"\n [rowHeight]=\"rowHeight()\"\n [dragState]=\"vm.dragState()\"\n [visibleColumnWithIndices]=\"vm.visibleColumnWithIndices()\"\n [columnPositions]=\"vm.columnPositions()\"\n [scrollLeft]=\"vm.scrollLeft()\"\n [effectiveColumns]=\"vm.effectiveColumns()\"\n [totalWidth]=\"vm.totalWidth()\"\n (filterApply)=\"onFilterApply($event)\"\n (filterClose)=\"onFilterClose()\"\n />\n </div>\n ", isInline: true, styles: [":host{display:block;height:100%;min-height:0}\n"], dependencies: [{ kind: "component", type: GridHeaderComponent, selector: "gp-grid-header", inputs: ["headerHeight", "scrollLeft", "contentWidth", "totalWidth", "isLoading", "visibleColumnsWithIndices", "columnPositions", "columnWidths", "headers", "sortingEnabled", "headerRenderers", "globalHeaderRenderer"], outputs: ["headerPointerDown", "filterPointerDown", "resizePointerDown", "headerSort", "headerFilterOpen"] }, { kind: "component", type: GridBodyComponent, selector: "gp-grid-body", inputs: ["rowHeight", "totalHeaderHeight", "contentWidth", "contentHeight", "rowsWrapperOffset", "slotsArray", "visibleColumnWithIndices", "totalWidth", "columnPositions", "columnWidths", "totalRows", "activeCell", "selectionRange", "editingCell", "cellRenderers", "globalCellRenderer", "editRenderers", "globalEditRenderer", "hoverPosition", "computeRowClasses", "computeCellClasses", "fillHandlePosition", "dragState"], outputs: ["scrolled", "cellPointerDown", "cellPointerEnter", "cellPointerLeave", "cellDoubleClick", "editValueChange", "editCommit", "editCancel", "fillHandlePointerDown"] }, { kind: "component", type: GridOverlaysComponent, selector: "gp-grid-overlays", inputs: ["filterPopup", "isLoading", "errorMessage", "headerHeight", "rowHeight", "dragState", "visibleColumnWithIndices", "columnPositions", "scrollLeft", "effectiveColumns", "totalWidth"], outputs: ["filterApply", "filterClose"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1574
+ }
1575
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: GpGridComponent, decorators: [{
1576
+ type: Component,
1577
+ args: [{ selector: 'gp-grid', standalone: true, imports: [GridHeaderComponent, GridBodyComponent, GridOverlaysComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: GP_GRID_TEMPLATE, styles: [":host{display:block;height:100%;min-height:0}\n"] }]
1578
+ }], ctorParameters: () => [], propDecorators: { container: [{
1579
+ type: ViewChild,
1580
+ args: ['container', { static: true }]
1581
+ }], body: [{
1582
+ type: ViewChild,
1583
+ args: [GridBodyComponent]
1584
+ }], columns: [{ type: i0.Input, args: [{ isSignal: true, alias: "columns", required: true }] }], rows: [{ type: i0.Input, args: [{ isSignal: true, alias: "rows", required: false }] }], dataSource: [{ type: i0.Input, args: [{ isSignal: true, alias: "dataSource", required: false }] }], getRowId: [{ type: i0.Input, args: [{ isSignal: true, alias: "getRowId", required: false }] }], rowHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowHeight", required: false }] }], headerHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "headerHeight", required: false }] }], darkMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "darkMode", required: false }] }], cellRenderers: [{ type: i0.Input, args: [{ isSignal: true, alias: "cellRenderers", required: false }] }], headerRenderers: [{ type: i0.Input, args: [{ isSignal: true, alias: "headerRenderers", required: false }] }], editRenderers: [{ type: i0.Input, args: [{ isSignal: true, alias: "editRenderers", required: false }] }], cellRenderer: [{ type: i0.Input, args: [{ isSignal: true, alias: "cellRenderer", required: false }] }], headerRenderer: [{ type: i0.Input, args: [{ isSignal: true, alias: "headerRenderer", required: false }] }], editRenderer: [{ type: i0.Input, args: [{ isSignal: true, alias: "editRenderer", required: false }] }], highlighting: [{ type: i0.Input, args: [{ isSignal: true, alias: "highlighting", required: false }] }], rowDragEntireRow: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowDragEntireRow", required: false }] }], overscan: [{ type: i0.Input, args: [{ isSignal: true, alias: "overscan", required: false }] }], sortingEnabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "sortingEnabled", required: false }] }], wheelDampening: [{ type: i0.Input, args: [{ isSignal: true, alias: "wheelDampening", required: false }] }], onRowDragEnd: [{ type: i0.Output, args: ["onRowDragEnd"] }], onCellValueChanged: [{ type: i0.Output, args: ["onCellValueChanged"] }], onColumnResized: [{ type: i0.Output, args: ["onColumnResized"] }], onColumnMoved: [{ type: i0.Output, args: ["onColumnMoved"] }] } });
1585
+
1586
+ /**
1587
+ * Angular helper for efficient grid data mutations.
1588
+ *
1589
+ * Wraps `createMutableClientDataSource` to provide a simple API for
1590
+ * updating grid data without triggering full pipeline rebuilds.
1591
+ *
1592
+ * @example
1593
+ * ```ts
1594
+ * private readonly grid = createGridData(initialRows, {
1595
+ * getRowId: (row) => row.id,
1596
+ * });
1597
+ *
1598
+ * // Template:
1599
+ * // <gp-grid [dataSource]="grid.dataSource" [columns]="columns" />
1600
+ *
1601
+ * // Update a row imperatively:
1602
+ * this.grid.updateRow(42, { name: 'New name' });
1603
+ * ```
1604
+ */
1605
+ const createGridData = (initialData, options) => {
1606
+ const ds = createMutableClientDataSource(initialData, {
1607
+ getRowId: options.getRowId,
1608
+ debounceMs: options.debounceMs,
1609
+ useWorker: options.useWorker,
1610
+ parallelSort: options.parallelSort,
1611
+ });
1612
+ return {
1613
+ dataSource: ds,
1614
+ updateRow: ds.updateRow,
1615
+ addRows: ds.addRows,
1616
+ removeRows: ds.removeRows,
1617
+ updateCell: ds.updateCell,
1618
+ clear: ds.clear,
1619
+ getRowById: ds.getRowById,
1620
+ getTotalRowCount: ds.getTotalRowCount,
1621
+ flushTransactions: ds.flushTransactions,
1622
+ };
1623
+ };
1624
+
1625
+ /**
1626
+ * Generated bundle index. Do not edit.
1627
+ */
1628
+
1629
+ export { FilterPopupComponent, GpGridComponent, GridBodyComponent, GridHeaderComponent, GridOverlaysComponent, createGridData };
1630
+ //# sourceMappingURL=gp-grid-angular.mjs.map