@alaarab/ogrid-angular-primeng 2.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,633 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ import { Component, input, inject, signal, computed, effect, viewChild, ChangeDetectionStrategy, } from '@angular/core';
8
+ import { CommonModule } from '@angular/common';
9
+ import { DataGridStateService, StatusBarComponent, GridContextMenuComponent, MarchingAntsOverlayComponent, EmptyStateComponent, DEFAULT_MIN_COLUMN_WIDTH, buildHeaderRows, getCellValue, } from '@alaarab/ogrid-angular';
10
+ import { ColumnHeaderFilterComponent } from '../column-header-filter/column-header-filter.component';
11
+ import { InlineCellEditorComponent } from './inline-cell-editor.component';
12
+ let DataGridTableComponent = class DataGridTableComponent {
13
+ constructor() {
14
+ this.stateService = inject(DataGridStateService);
15
+ this.wrapperRef = viewChild('wrapper');
16
+ this.tableContainerRef = viewChild('tableContainer');
17
+ // Inputs mapped from IOGridDataGridProps
18
+ this.items = input.required();
19
+ this.columns = input.required();
20
+ this.getRowIdFn = input.required({ alias: 'getRowId' });
21
+ this.sortBy = input(undefined);
22
+ this.sortDirection = input('asc');
23
+ this.onColumnSort = input.required();
24
+ this.visibleColumns = input.required();
25
+ this.columnOrder = input(undefined);
26
+ this.onColumnOrderChange = input(undefined);
27
+ this.onColumnResized = input(undefined);
28
+ this.onColumnPinned = input(undefined);
29
+ this.pinnedColumns = input(undefined);
30
+ this.initialColumnWidths = input(undefined);
31
+ this.freezeRows = input(undefined);
32
+ this.freezeCols = input(undefined);
33
+ this.layoutMode = input('fill');
34
+ this.suppressHorizontalScroll = input(undefined);
35
+ this.isLoading = input(false);
36
+ this.loadingMessage = input('Loading\u2026');
37
+ this.editable = input(undefined);
38
+ this.cellSelection = input(undefined);
39
+ this.onCellValueChanged = input(undefined);
40
+ this.onUndo = input(undefined);
41
+ this.onRedo = input(undefined);
42
+ this.canUndo = input(undefined);
43
+ this.canRedo = input(undefined);
44
+ this.rowSelectionMode = input('none', { alias: 'rowSelection' });
45
+ this.selectedRows = input(undefined);
46
+ this.onSelectionChange = input(undefined);
47
+ this.statusBar = input(undefined);
48
+ this.filters = input.required();
49
+ this.onFilterChange = input.required();
50
+ this.filterOptions = input({});
51
+ this.loadingFilterOptions = input({});
52
+ this.peopleSearch = input(undefined);
53
+ this.getUserByEmail = input(undefined);
54
+ this.emptyState = input(undefined);
55
+ this.onCellError = input(undefined);
56
+ this.ariaLabel = input(undefined, { alias: 'aria-label' });
57
+ this.ariaLabelledBy = input(undefined, { alias: 'aria-labelledby' });
58
+ this.defaultMinWidth = DEFAULT_MIN_COLUMN_WIDTH;
59
+ this.statusBarClasses = {
60
+ statusBar: 'ogrid-status-bar',
61
+ statusBarItem: 'ogrid-status-bar-item',
62
+ statusBarLabel: 'ogrid-status-bar-label',
63
+ statusBarValue: 'ogrid-status-bar-value',
64
+ };
65
+ // Column sizing
66
+ this.columnSizingOverrides = signal({});
67
+ this.resizeStartX = 0;
68
+ this.resizeColumnId = '';
69
+ this.resizeStartWidth = 0;
70
+ // Last shift state for row checkbox
71
+ this.lastMouseShift = false;
72
+ this.state = computed(() => this.stateService.getState());
73
+ this.tableContainerEl = computed(() => this.tableContainerRef()?.nativeElement ?? null);
74
+ this.resolvedAriaLabel = computed(() => this.ariaLabel() ?? (this.ariaLabelledBy() ? undefined : 'Data grid'));
75
+ this.headerRows = computed(() => buildHeaderRows(this.columns(), this.visibleColumns()));
76
+ this.allowOverflowX = computed(() => {
77
+ const s = this.state();
78
+ return !this.suppressHorizontalScroll() && s.layout.containerWidth > 0 &&
79
+ (s.layout.minTableWidth > s.layout.containerWidth || s.layout.desiredTableWidth > s.layout.containerWidth);
80
+ });
81
+ this.tableWidthStyle = computed(() => {
82
+ const s = this.state();
83
+ if (s.viewModels.showEmptyInGrid)
84
+ return '100%';
85
+ if (this.allowOverflowX())
86
+ return 'fit-content';
87
+ if (this.layoutMode() === 'content')
88
+ return 'fit-content';
89
+ return '100%';
90
+ });
91
+ this.tableMinWidthStyle = computed(() => {
92
+ const s = this.state();
93
+ if (s.viewModels.showEmptyInGrid)
94
+ return '100%';
95
+ if (this.allowOverflowX())
96
+ return 'max-content';
97
+ if (this.layoutMode() === 'content')
98
+ return 'max-content';
99
+ return '100%';
100
+ });
101
+ this.selectedCellCount = computed(() => {
102
+ const range = this.state().interaction.selectionRange;
103
+ if (!range)
104
+ return undefined;
105
+ return (Math.abs(range.endRow - range.startRow) + 1) *
106
+ (Math.abs(range.endCol - range.startCol) + 1);
107
+ });
108
+ // Wire inputs to DataGridStateService
109
+ effect(() => {
110
+ const props = this.buildProps();
111
+ this.stateService.props.set(props);
112
+ });
113
+ effect(() => {
114
+ const el = this.wrapperRef()?.nativeElement ?? null;
115
+ this.stateService.wrapperEl.set(el);
116
+ });
117
+ // Initialize column sizing from initial widths
118
+ effect(() => {
119
+ const iw = this.initialColumnWidths();
120
+ if (iw) {
121
+ this.columnSizingOverrides.set({ ...iw });
122
+ }
123
+ });
124
+ }
125
+ trackByRowId(_index, item) {
126
+ return this.getRowIdFn()(item);
127
+ }
128
+ getColumnWidth(col) {
129
+ const override = this.columnSizingOverrides()[col.columnId];
130
+ if (override)
131
+ return override;
132
+ return col.idealWidth ?? col.defaultWidth ?? undefined;
133
+ }
134
+ getFilterConfig(col) {
135
+ const s = this.state();
136
+ const hfi = s.viewModels.headerFilterInput;
137
+ const filterable = col.filterable && typeof col.filterable === 'object' ? col.filterable : null;
138
+ const filterType = filterable?.type ?? 'none';
139
+ const filterField = filterable?.filterField ?? col.columnId;
140
+ const sortable = col.sortable !== false;
141
+ const filterValue = hfi.filters[filterField];
142
+ const base = {
143
+ filterType,
144
+ isSorted: hfi.sortBy === col.columnId,
145
+ isSortedDescending: hfi.sortBy === col.columnId && hfi.sortDirection === 'desc',
146
+ onSort: sortable ? () => hfi.onColumnSort(col.columnId) : undefined,
147
+ };
148
+ if (filterType === 'text') {
149
+ return {
150
+ ...base,
151
+ textValue: filterValue?.type === 'text' ? filterValue.value : '',
152
+ onTextChange: (v) => hfi.onFilterChange(filterField, v.trim() ? { type: 'text', value: v } : undefined),
153
+ };
154
+ }
155
+ if (filterType === 'multiSelect') {
156
+ return {
157
+ ...base,
158
+ options: hfi.filterOptions[filterField] ?? [],
159
+ isLoadingOptions: hfi.loadingFilterOptions[filterField] ?? false,
160
+ selectedValues: filterValue?.type === 'multiSelect' ? filterValue.value : [],
161
+ onFilterChange: (values) => hfi.onFilterChange(filterField, values.length ? { type: 'multiSelect', value: values } : undefined),
162
+ };
163
+ }
164
+ if (filterType === 'people') {
165
+ return {
166
+ ...base,
167
+ selectedUser: filterValue?.type === 'people' ? filterValue.value : undefined,
168
+ onUserChange: (u) => hfi.onFilterChange(filterField, u ? { type: 'people', value: u } : undefined),
169
+ peopleSearch: hfi.peopleSearch,
170
+ };
171
+ }
172
+ if (filterType === 'date') {
173
+ return {
174
+ ...base,
175
+ dateValue: filterValue?.type === 'date' ? filterValue.value : undefined,
176
+ onDateChange: (v) => hfi.onFilterChange(filterField, v ? { type: 'date', value: v } : undefined),
177
+ };
178
+ }
179
+ return base;
180
+ }
181
+ getCellValueFn(item, col) {
182
+ return getCellValue(item, col);
183
+ }
184
+ resolveCellDisplay(col, item) {
185
+ if (col.renderCell && typeof col.renderCell === 'function') {
186
+ const result = col.renderCell(item);
187
+ return result ?? '';
188
+ }
189
+ const value = getCellValue(item, col);
190
+ if (col.valueFormatter)
191
+ return String(col.valueFormatter(value, item) ?? '');
192
+ if (value == null)
193
+ return '';
194
+ if (col.type === 'date') {
195
+ const d = new Date(String(value));
196
+ if (!Number.isNaN(d.getTime()))
197
+ return d.toLocaleDateString();
198
+ }
199
+ if (col.type === 'boolean')
200
+ return value ? 'True' : 'False';
201
+ return String(value);
202
+ }
203
+ getCellStyleObj(col, item) {
204
+ if (!col.cellStyle)
205
+ return null;
206
+ return typeof col.cellStyle === 'function'
207
+ ? col.cellStyle(item)
208
+ : col.cellStyle;
209
+ }
210
+ canEditCell(col, item) {
211
+ const colEditable = col.editable === true || (typeof col.editable === 'function' && col.editable(item));
212
+ return this.editable() !== false && !!colEditable && this.onCellValueChanged() != null && typeof col.cellEditor !== 'function';
213
+ }
214
+ isEditingCell(item, col) {
215
+ const editing = this.state().editing.editingCell;
216
+ if (!editing)
217
+ return false;
218
+ return editing.rowId === this.getRowIdFn()(item) && editing.columnId === col.columnId;
219
+ }
220
+ getEditorType(col, _item) {
221
+ if (col.cellEditor === 'text' || col.cellEditor === 'select' || col.cellEditor === 'checkbox' || col.cellEditor === 'date' || col.cellEditor === 'richSelect') {
222
+ return col.cellEditor;
223
+ }
224
+ if (col.type === 'date')
225
+ return 'date';
226
+ if (col.type === 'boolean')
227
+ return 'checkbox';
228
+ return 'text';
229
+ }
230
+ isActiveCell(rowIndex, colIdx) {
231
+ const ac = this.state().interaction.activeCell;
232
+ if (!ac)
233
+ return false;
234
+ return ac.rowIndex === rowIndex && ac.columnIndex === colIdx + this.state().layout.colOffset;
235
+ }
236
+ isInSelectionRange(rowIndex, colIdx) {
237
+ const range = this.state().interaction.selectionRange;
238
+ if (!range)
239
+ return false;
240
+ const minR = Math.min(range.startRow, range.endRow);
241
+ const maxR = Math.max(range.startRow, range.endRow);
242
+ const minC = Math.min(range.startCol, range.endCol);
243
+ const maxC = Math.max(range.startCol, range.endCol);
244
+ return rowIndex >= minR && rowIndex <= maxR && colIdx >= minC && colIdx <= maxC;
245
+ }
246
+ isSelectionEndCell(rowIndex, colIdx) {
247
+ const s = this.state();
248
+ const range = s.interaction.selectionRange;
249
+ if (!range || s.interaction.isDragging || s.interaction.copyRange || s.interaction.cutRange)
250
+ return false;
251
+ return rowIndex === range.endRow && colIdx === range.endCol;
252
+ }
253
+ getCellBackground(rowIndex, colIdx) {
254
+ if (this.isInSelectionRange(rowIndex, colIdx))
255
+ return 'var(--ogrid-range-bg, rgba(33, 115, 70, 0.08))';
256
+ return null;
257
+ }
258
+ // --- Event handlers ---
259
+ onMouseDown(e) {
260
+ this.lastMouseShift = e.shiftKey;
261
+ }
262
+ onGridKeyDown(e) {
263
+ this.state().interaction.handleGridKeyDown(e);
264
+ }
265
+ onCellMouseDown(e, rowIndex, globalColIndex) {
266
+ this.state().interaction.handleCellMouseDown(e, rowIndex, globalColIndex);
267
+ }
268
+ onCellDblClick(item, col, rowIndex, colIdx) {
269
+ if (this.canEditCell(col, item)) {
270
+ this.stateService.setEditingCell({ rowId: this.getRowIdFn()(item), columnId: col.columnId });
271
+ }
272
+ }
273
+ onCellContextMenu(e) {
274
+ this.state().contextMenu.handleCellContextMenu(e);
275
+ }
276
+ onCellEditorCommit(item, col, rowIndex, colIdx, newValue) {
277
+ const oldValue = getCellValue(item, col);
278
+ this.stateService.commitCellEdit(item, col.columnId, oldValue, newValue, rowIndex, colIdx + this.state().layout.colOffset);
279
+ }
280
+ onFillHandleMouseDown(e) {
281
+ this.state().interaction.handleFillHandleMouseDown(e);
282
+ }
283
+ onSelectAllChange(checked) {
284
+ this.state().rowSelection.handleSelectAll(checked);
285
+ }
286
+ onRowClick(e, item) {
287
+ if (this.rowSelectionMode() !== 'single')
288
+ return;
289
+ const rowId = this.getRowIdFn()(item);
290
+ const ids = this.state().rowSelection.selectedRowIds;
291
+ this.state().rowSelection.updateSelection(ids.has(rowId) ? new Set() : new Set([rowId]));
292
+ }
293
+ onRowCheckboxChange(item, checked, rowIndex, e) {
294
+ const rowId = this.getRowIdFn()(item);
295
+ this.state().rowSelection.handleRowCheckboxChange(rowId, checked, rowIndex, this.lastMouseShift);
296
+ }
297
+ handlePaste() {
298
+ void this.state().interaction.handlePaste();
299
+ }
300
+ onResizeStart(e, col) {
301
+ e.preventDefault();
302
+ this.resizeStartX = e.clientX;
303
+ this.resizeColumnId = col.columnId;
304
+ this.resizeStartWidth = this.getColumnWidth(col) ?? col.minWidth ?? DEFAULT_MIN_COLUMN_WIDTH;
305
+ const onMove = (me) => {
306
+ const delta = me.clientX - this.resizeStartX;
307
+ const minW = col.minWidth ?? DEFAULT_MIN_COLUMN_WIDTH;
308
+ const newWidth = Math.max(minW, this.resizeStartWidth + delta);
309
+ this.columnSizingOverrides.update((prev) => ({ ...prev, [this.resizeColumnId]: newWidth }));
310
+ };
311
+ const onUp = () => {
312
+ window.removeEventListener('mousemove', onMove, true);
313
+ window.removeEventListener('mouseup', onUp, true);
314
+ const finalWidth = this.columnSizingOverrides()[this.resizeColumnId];
315
+ if (finalWidth) {
316
+ this.onColumnResized()?.(this.resizeColumnId, finalWidth);
317
+ const overrides = {};
318
+ for (const [id, w] of Object.entries(this.columnSizingOverrides())) {
319
+ overrides[id] = { widthPx: w };
320
+ }
321
+ this.state().layout.setColumnSizingOverrides(overrides);
322
+ }
323
+ };
324
+ window.addEventListener('mousemove', onMove, true);
325
+ window.addEventListener('mouseup', onUp, true);
326
+ }
327
+ buildProps() {
328
+ return {
329
+ items: this.items(),
330
+ columns: this.columns(),
331
+ getRowId: this.getRowIdFn(),
332
+ sortBy: this.sortBy(),
333
+ sortDirection: this.sortDirection(),
334
+ onColumnSort: this.onColumnSort(),
335
+ visibleColumns: this.visibleColumns(),
336
+ columnOrder: this.columnOrder(),
337
+ onColumnOrderChange: this.onColumnOrderChange(),
338
+ onColumnResized: this.onColumnResized(),
339
+ onColumnPinned: this.onColumnPinned(),
340
+ pinnedColumns: this.pinnedColumns(),
341
+ initialColumnWidths: this.initialColumnWidths(),
342
+ freezeRows: this.freezeRows(),
343
+ freezeCols: this.freezeCols(),
344
+ layoutMode: this.layoutMode(),
345
+ suppressHorizontalScroll: this.suppressHorizontalScroll(),
346
+ isLoading: this.isLoading(),
347
+ loadingMessage: this.loadingMessage(),
348
+ editable: this.editable(),
349
+ cellSelection: this.cellSelection(),
350
+ onCellValueChanged: this.onCellValueChanged(),
351
+ onUndo: this.onUndo(),
352
+ onRedo: this.onRedo(),
353
+ canUndo: this.canUndo(),
354
+ canRedo: this.canRedo(),
355
+ rowSelection: this.rowSelectionMode(),
356
+ selectedRows: this.selectedRows(),
357
+ onSelectionChange: this.onSelectionChange(),
358
+ statusBar: this.statusBar(),
359
+ filters: this.filters(),
360
+ onFilterChange: this.onFilterChange(),
361
+ filterOptions: this.filterOptions(),
362
+ loadingFilterOptions: this.loadingFilterOptions(),
363
+ peopleSearch: this.peopleSearch(),
364
+ getUserByEmail: this.getUserByEmail(),
365
+ emptyState: this.emptyState(),
366
+ onCellError: this.onCellError(),
367
+ 'aria-label': this.ariaLabel(),
368
+ 'aria-labelledby': this.ariaLabelledBy(),
369
+ };
370
+ }
371
+ };
372
+ DataGridTableComponent = __decorate([
373
+ Component({
374
+ selector: 'ogrid-primeng-datagrid-table',
375
+ standalone: true,
376
+ imports: [
377
+ CommonModule,
378
+ StatusBarComponent,
379
+ GridContextMenuComponent,
380
+ MarchingAntsOverlayComponent,
381
+ EmptyStateComponent,
382
+ ColumnHeaderFilterComponent,
383
+ InlineCellEditorComponent,
384
+ ],
385
+ changeDetection: ChangeDetectionStrategy.OnPush,
386
+ providers: [DataGridStateService],
387
+ template: `
388
+ <div style="position:relative;flex:1;min-height:0;display:flex;flex-direction:column">
389
+ <div
390
+ #wrapper
391
+ tabindex="0"
392
+ role="region"
393
+ [attr.aria-label]="resolvedAriaLabel()"
394
+ [attr.aria-labelledby]="ariaLabelledBy()"
395
+ [attr.data-empty]="state().viewModels.showEmptyInGrid ? 'true' : null"
396
+ [attr.data-column-count]="state().layout.totalColCount"
397
+ [attr.data-freeze-rows]="freezeRows() != null && freezeRows()! >= 1 ? freezeRows() : null"
398
+ [attr.data-freeze-cols]="freezeCols() != null && freezeCols()! >= 1 ? freezeCols() : null"
399
+ [attr.data-overflow-x]="allowOverflowX() ? 'true' : 'false'"
400
+ [attr.data-has-selection]="rowSelectionMode() !== 'none' ? 'true' : null"
401
+ (contextmenu)="$event.preventDefault()"
402
+ (keydown)="onGridKeyDown($event)"
403
+ (mousedown)="onMouseDown($event)"
404
+ style="flex:1;min-height:0;overflow:auto;outline:none;position:relative;font-size:13px;color:var(--ogrid-fg, #242424)"
405
+ [style.--data-table-column-count]="state().layout.totalColCount"
406
+ [style.--data-table-width]="tableWidthStyle()"
407
+ [style.--data-table-min-width]="tableMinWidthStyle()"
408
+ >
409
+ <div style="position:relative">
410
+ <div [class.loading-dimmed]="isLoading() && items().length > 0" style="position:relative">
411
+ <div #tableContainer style="position:relative">
412
+ <table
413
+ style="width:var(--data-table-width, 100%);min-width:var(--data-table-min-width, 100%);border-collapse:collapse;table-layout:fixed"
414
+ >
415
+ <thead style="position:sticky;top:0;z-index:3;background:var(--ogrid-header-bg, #f5f5f5)">
416
+ @for (row of headerRows(); track $index; let rowIdx = $index) {
417
+ <tr>
418
+ @if (rowIdx === headerRows().length - 1 && state().layout.hasCheckboxCol) {
419
+ <th
420
+ scope="col"
421
+ rowSpan="1"
422
+ style="width:48px;min-width:48px;max-width:48px;padding:6px 4px;text-align:center;border-bottom:1px solid var(--ogrid-border, #e0e0e0)"
423
+ >
424
+ <input
425
+ type="checkbox"
426
+ [checked]="state().rowSelection.allSelected"
427
+ [indeterminate]="state().rowSelection.someSelected && !state().rowSelection.allSelected"
428
+ (change)="onSelectAllChange($any($event.target).checked)"
429
+ aria-label="Select all rows"
430
+ />
431
+ </th>
432
+ }
433
+ @if (rowIdx === 0 && rowIdx < headerRows().length - 1 && state().layout.hasCheckboxCol) {
434
+ <th [attr.rowSpan]="headerRows().length - 1"></th>
435
+ }
436
+ @for (cell of row; track $index; let cellIdx = $index) {
437
+ @if (cell.isGroup) {
438
+ <th
439
+ [attr.colSpan]="cell.colSpan"
440
+ scope="colgroup"
441
+ style="padding:6px 8px;text-align:center;font-weight:600;border-bottom:1px solid var(--ogrid-border, #e0e0e0)"
442
+ >
443
+ {{ cell.label }}
444
+ </th>
445
+ } @else {
446
+ <th
447
+ scope="col"
448
+ [attr.data-column-id]="cell.columnDef!.columnId"
449
+ [attr.rowSpan]="headerRows().length > 1 && rowIdx < headerRows().length - 1 ? headerRows().length - rowIdx : null"
450
+ style="padding:6px 8px;text-align:left;font-weight:600;border-bottom:1px solid var(--ogrid-border, #e0e0e0);position:relative"
451
+ [style.min-width.px]="cell.columnDef!.minWidth ?? defaultMinWidth"
452
+ [style.width.px]="getColumnWidth(cell.columnDef!)"
453
+ [style.max-width.px]="getColumnWidth(cell.columnDef!)"
454
+ >
455
+ <ogrid-primeng-column-header-filter
456
+ [columnKey]="cell.columnDef!.columnId"
457
+ [columnName]="cell.columnDef!.name"
458
+ [filterType]="getFilterConfig(cell.columnDef!).filterType"
459
+ [isSorted]="getFilterConfig(cell.columnDef!).isSorted ?? false"
460
+ [isSortedDescending]="getFilterConfig(cell.columnDef!).isSortedDescending ?? false"
461
+ [onSort]="getFilterConfig(cell.columnDef!).onSort"
462
+ [selectedValues]="getFilterConfig(cell.columnDef!).selectedValues"
463
+ [onFilterChange]="getFilterConfig(cell.columnDef!).onFilterChange"
464
+ [options]="getFilterConfig(cell.columnDef!).options ?? []"
465
+ [isLoadingOptions]="getFilterConfig(cell.columnDef!).isLoadingOptions ?? false"
466
+ [textValue]="getFilterConfig(cell.columnDef!).textValue ?? ''"
467
+ [onTextChange]="getFilterConfig(cell.columnDef!).onTextChange"
468
+ [selectedUser]="getFilterConfig(cell.columnDef!).selectedUser"
469
+ [onUserChange]="getFilterConfig(cell.columnDef!).onUserChange"
470
+ [peopleSearch]="getFilterConfig(cell.columnDef!).peopleSearch"
471
+ [dateValue]="getFilterConfig(cell.columnDef!).dateValue"
472
+ [onDateChange]="getFilterConfig(cell.columnDef!).onDateChange"
473
+ ></ogrid-primeng-column-header-filter>
474
+ <div
475
+ style="position:absolute;top:0;right:0;bottom:0;width:4px;cursor:col-resize"
476
+ (mousedown)="onResizeStart($event, cell.columnDef!)"
477
+ [attr.aria-label]="'Resize ' + cell.columnDef!.name"
478
+ ></div>
479
+ </th>
480
+ }
481
+ }
482
+ </tr>
483
+ }
484
+ </thead>
485
+
486
+ @if (!state().viewModels.showEmptyInGrid) {
487
+ <tbody>
488
+ @for (item of items(); track trackByRowId($index, item); let rowIndex = $index) {
489
+ <tr
490
+ [attr.data-row-id]="getRowIdFn()(item)"
491
+ [style.background]="state().rowSelection.selectedRowIds.has(getRowIdFn()(item)) ? 'var(--ogrid-selected-bg, #e8f0fe)' : null"
492
+ (click)="onRowClick($event, item)"
493
+ >
494
+ @if (state().layout.hasCheckboxCol) {
495
+ <td
496
+ style="width:48px;min-width:48px;max-width:48px;padding:6px 4px;text-align:center;border-bottom:1px solid var(--ogrid-border, #f0f0f0)"
497
+ [attr.data-row-index]="rowIndex"
498
+ [attr.data-col-index]="0"
499
+ (click)="$event.stopPropagation()"
500
+ >
501
+ <input
502
+ type="checkbox"
503
+ [checked]="state().rowSelection.selectedRowIds.has(getRowIdFn()(item))"
504
+ (change)="onRowCheckboxChange(item, $any($event.target).checked, rowIndex, $event)"
505
+ [attr.aria-label]="'Select row ' + (rowIndex + 1)"
506
+ />
507
+ </td>
508
+ }
509
+ @for (col of state().layout.visibleCols; track col.columnId; let colIdx = $index) {
510
+ <td
511
+ style="padding:0;border-bottom:1px solid var(--ogrid-border, #f0f0f0);position:relative"
512
+ [style.min-width.px]="col.minWidth ?? defaultMinWidth"
513
+ [style.width.px]="getColumnWidth(col)"
514
+ [style.max-width.px]="getColumnWidth(col)"
515
+ [style.text-align]="col.type === 'numeric' ? 'right' : col.type === 'boolean' ? 'center' : null"
516
+ >
517
+ @if (isEditingCell(item, col)) {
518
+ <ogrid-primeng-inline-cell-editor
519
+ [value]="getCellValueFn(item, col)"
520
+ [item]="item"
521
+ [column]="col"
522
+ [rowIndex]="rowIndex"
523
+ [editorType]="getEditorType(col, item)"
524
+ (commit)="onCellEditorCommit(item, col, rowIndex, colIdx, $event)"
525
+ (cancel)="state().editing.setEditingCell(null)"
526
+ ></ogrid-primeng-inline-cell-editor>
527
+ } @else {
528
+ <div
529
+ [attr.data-row-index]="rowIndex"
530
+ [attr.data-col-index]="colIdx + state().layout.colOffset"
531
+ (mousedown)="onCellMouseDown($event, rowIndex, colIdx + state().layout.colOffset)"
532
+ (dblclick)="onCellDblClick(item, col, rowIndex, colIdx)"
533
+ (contextmenu)="onCellContextMenu($event)"
534
+ style="padding:6px 10px;min-height:20px;cursor:default;overflow:hidden;text-overflow:ellipsis;white-space:nowrap"
535
+ [style.cursor]="canEditCell(col, item) ? 'cell' : 'default'"
536
+ [style.background]="getCellBackground(rowIndex, colIdx)"
537
+ [style.outline]="isActiveCell(rowIndex, colIdx) ? '2px solid var(--ogrid-selection, #217346)' : null"
538
+ [style.outline-offset]="isActiveCell(rowIndex, colIdx) ? '-2px' : null"
539
+ >
540
+ <span [style]="getCellStyleObj(col, item)">{{ resolveCellDisplay(col, item) }}</span>
541
+ @if (canEditCell(col, item) && isSelectionEndCell(rowIndex, colIdx)) {
542
+ <div
543
+ (mousedown)="onFillHandleMouseDown($event)"
544
+ style="position:absolute;bottom:-3px;right:-3px;width:7px;height:7px;background:var(--ogrid-selection, #217346);cursor:crosshair;z-index:2"
545
+ aria-label="Fill handle"
546
+ ></div>
547
+ }
548
+ </div>
549
+ }
550
+ </td>
551
+ }
552
+ </tr>
553
+ }
554
+ </tbody>
555
+ }
556
+ </table>
557
+
558
+ <ogrid-marching-ants-overlay
559
+ [containerEl]="tableContainerEl()"
560
+ [selectionRange]="state().interaction.selectionRange"
561
+ [copyRange]="state().interaction.copyRange"
562
+ [cutRange]="state().interaction.cutRange"
563
+ [colOffset]="state().layout.colOffset"
564
+ ></ogrid-marching-ants-overlay>
565
+
566
+ @if (state().viewModels.showEmptyInGrid && emptyState()) {
567
+ <div style="display:flex;align-items:center;justify-content:center;padding:48px 24px;text-align:center;color:var(--ogrid-muted, #999)">
568
+ <div>
569
+ <div style="font-weight:600;margin-bottom:8px">No results found</div>
570
+ <ogrid-empty-state
571
+ [message]="emptyState()?.message"
572
+ [hasActiveFilters]="emptyState()?.hasActiveFilters ?? false"
573
+ [render]="emptyState()?.render"
574
+ (clearAll)="emptyState()?.onClearAll()"
575
+ ></ogrid-empty-state>
576
+ </div>
577
+ </div>
578
+ }
579
+ </div>
580
+ </div>
581
+ </div>
582
+ </div>
583
+
584
+ @if (state().contextMenu.menuPosition) {
585
+ <ogrid-context-menu
586
+ [x]="state().contextMenu.menuPosition!.x"
587
+ [y]="state().contextMenu.menuPosition!.y"
588
+ [hasSelection]="state().interaction.hasCellSelection"
589
+ [canUndoProp]="state().interaction.canUndo"
590
+ [canRedoProp]="state().interaction.canRedo"
591
+ (copy)="state().interaction.handleCopy()"
592
+ (cut)="state().interaction.handleCut()"
593
+ (paste)="handlePaste()"
594
+ (selectAll)="state().interaction.handleSelectAllCells()"
595
+ (undoAction)="state().interaction.onUndo?.()"
596
+ (redoAction)="state().interaction.onRedo?.()"
597
+ (close)="state().contextMenu.closeContextMenu()"
598
+ ></ogrid-context-menu>
599
+ }
600
+
601
+ @if (state().viewModels.statusBarConfig) {
602
+ <ogrid-status-bar
603
+ [totalCount]="state().viewModels.statusBarConfig!.totalCount"
604
+ [filteredCount]="state().viewModels.statusBarConfig!.filteredCount"
605
+ [selectedCount]="state().viewModels.statusBarConfig!.selectedCount ?? state().rowSelection.selectedRowIds.size"
606
+ [selectedCellCount]="selectedCellCount()"
607
+ [aggregation]="state().viewModels.statusBarConfig!.aggregation"
608
+ [suppressRowCount]="state().viewModels.statusBarConfig!.suppressRowCount"
609
+ [classNames]="statusBarClasses"
610
+ ></ogrid-status-bar>
611
+ }
612
+
613
+ @if (isLoading()) {
614
+ <div
615
+ style="position:absolute;inset:0;display:flex;align-items:center;justify-content:center;background:rgba(255,255,255,0.7);z-index:10"
616
+ aria-live="polite"
617
+ >
618
+ <div style="display:flex;align-items:center;gap:8px;color:var(--ogrid-fg, #242424)">
619
+ <span style="font-size:14px">{{ loadingMessage() }}</span>
620
+ </div>
621
+ </div>
622
+ }
623
+ </div>
624
+ `,
625
+ styles: [`
626
+ .loading-dimmed {
627
+ opacity: 0.5;
628
+ pointer-events: none;
629
+ }
630
+ `],
631
+ })
632
+ ], DataGridTableComponent);
633
+ export { DataGridTableComponent };