@alaarab/ogrid-angular-primeng 2.1.3 → 2.1.5

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.
@@ -1,1015 +0,0 @@
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, signal, computed, ViewChild, ChangeDetectionStrategy, ViewEncapsulation, } from '@angular/core';
8
- import { BaseDataGridTableComponent, DataGridStateService, ColumnReorderService, VirtualScrollService, StatusBarComponent, GridContextMenuComponent, MarchingAntsOverlayComponent, EmptyStateComponent, DEFAULT_MIN_COLUMN_WIDTH, OGRID_THEME_VARS_CSS, } from '@alaarab/ogrid-angular';
9
- import { ColumnHeaderFilterComponent } from '../column-header-filter/column-header-filter.component';
10
- import { ColumnHeaderMenuComponent } from '../column-header-menu/column-header-menu.component';
11
- import { InlineCellEditorComponent } from './inline-cell-editor.component';
12
- import { PopoverCellEditorComponent } from './popover-cell-editor.component';
13
- let DataGridTableComponent = class DataGridTableComponent extends BaseDataGridTableComponent {
14
- constructor() {
15
- super();
16
- this.sortBy = undefined;
17
- this.sortDirection = 'asc';
18
- this.columnOrder = undefined;
19
- this.onColumnOrderChange = undefined;
20
- this.onColumnResized = undefined;
21
- this.onColumnPinned = undefined;
22
- this.pinnedColumnsInput = undefined;
23
- this.initialColumnWidths = undefined;
24
- this.layoutMode = 'fill';
25
- this.suppressHorizontalScroll = undefined;
26
- this.stickyHeaderInput = undefined;
27
- this.columnReorder = undefined;
28
- this.isLoadingInput = false;
29
- this.loadingMessageInput = 'Loading\u2026';
30
- this.editable = undefined;
31
- this.cellSelection = undefined;
32
- this.onCellValueChanged = undefined;
33
- this.onUndoInput = undefined;
34
- this.onRedoInput = undefined;
35
- this.canUndoInput = undefined;
36
- this.canRedoInput = undefined;
37
- this.rowSelectionMode = 'none';
38
- this.selectedRows = undefined;
39
- this.onSelectionChange = undefined;
40
- this.statusBar = undefined;
41
- this.filterOptions = {};
42
- this.loadingFilterOptions = {};
43
- this.peopleSearch = undefined;
44
- this.getUserByEmail = undefined;
45
- this.emptyStateInput = undefined;
46
- this.onCellError = undefined;
47
- this.ariaLabelInput = undefined;
48
- this.ariaLabelledByInput = undefined;
49
- this.showRowNumbers = false;
50
- this.currentPageInput = 1;
51
- this.pageSizeInput = 25;
52
- this.defaultMinWidth = DEFAULT_MIN_COLUMN_WIDTH;
53
- this.statusBarClasses = {
54
- statusBar: 'ogrid-status-bar',
55
- statusBarItem: 'ogrid-status-bar-item',
56
- statusBarLabel: 'ogrid-status-bar-label',
57
- statusBarValue: 'ogrid-status-bar-value',
58
- };
59
- this.contextMenuClasses = {
60
- contextMenu: 'ogrid-context-menu',
61
- contextMenuItem: 'ogrid-context-menu-item',
62
- contextMenuItemLabel: 'ogrid-context-menu-item-label',
63
- contextMenuItemShortcut: 'ogrid-context-menu-item-shortcut',
64
- contextMenuDivider: 'ogrid-context-menu-divider',
65
- };
66
- // PrimeNG uses flat number overrides for column sizing
67
- this.primengColumnSizingOverrides = signal({});
68
- this.propsSignal = signal(undefined);
69
- this.resizeStartX = 0;
70
- this.resizeColumnId = '';
71
- this.resizeStartWidth = 0;
72
- // Bound method reference for template
73
- this.cancelEditHandler = () => this.cancelEdit();
74
- // --- PrimeNG-specific computed signals ---
75
- this.resolvedAriaLabel = computed(() => this.ariaLabelInput ?? (this.ariaLabelledByInput ? undefined : 'Data grid'));
76
- this.tableWidthStyle = computed(() => {
77
- if (this.showEmptyInGrid())
78
- return '100%';
79
- if (this.allowOverflowX())
80
- return 'fit-content';
81
- if (this.layoutMode === 'content')
82
- return 'fit-content';
83
- return '100%';
84
- });
85
- this.tableMinWidthStyle = computed(() => {
86
- if (this.showEmptyInGrid())
87
- return '100%';
88
- if (this.allowOverflowX())
89
- return 'max-content';
90
- if (this.layoutMode === 'content')
91
- return 'max-content';
92
- return '100%';
93
- });
94
- this.initBase();
95
- }
96
- ngOnChanges(changes) {
97
- // Initialize column sizing from initial widths
98
- if (changes['initialColumnWidths']) {
99
- const iw = this.initialColumnWidths;
100
- if (iw) {
101
- this.primengColumnSizingOverrides.set({ ...iw });
102
- }
103
- }
104
- // Rebuild props and only set if values actually changed (shallow compare)
105
- const next = this.buildProps();
106
- const prev = this.propsSignal();
107
- if (!prev || !this.shallowEqual(prev, next)) {
108
- this.propsSignal.set(next);
109
- }
110
- }
111
- /** Shallow-compare two props objects by their top-level keys. */
112
- shallowEqual(a, b) {
113
- const aObj = a;
114
- const bObj = b;
115
- const keysA = Object.keys(aObj);
116
- const keysB = Object.keys(bObj);
117
- if (keysA.length !== keysB.length)
118
- return false;
119
- for (const key of keysA) {
120
- if (aObj[key] !== bObj[key])
121
- return false;
122
- }
123
- return true;
124
- }
125
- // --- Abstract method implementations ---
126
- getProps() {
127
- return this.propsSignal();
128
- }
129
- getWrapperRef() {
130
- return this.wrapperRef;
131
- }
132
- getTableContainerRef() {
133
- return this.tableContainerRefEl;
134
- }
135
- // --- Column width override (PrimeNG uses flat number instead of { widthPx }) ---
136
- getColumnWidth(col) {
137
- const override = this.primengColumnSizingOverrides()[col.columnId];
138
- if (override)
139
- return override;
140
- return col.idealWidth ?? col.defaultWidth ?? col.minWidth ?? DEFAULT_MIN_COLUMN_WIDTH;
141
- }
142
- // --- PrimeNG-specific helpers ---
143
- trackByRowId(_index, item) {
144
- return this.getRowIdInput(item);
145
- }
146
- // --- PrimeNG-specific event handlers ---
147
- onSelectAllChangePrimeng(checked) {
148
- this.state().rowSelection.handleSelectAll(checked);
149
- }
150
- onRowClickPrimeng(e, item) {
151
- if (this.rowSelectionMode !== 'single')
152
- return;
153
- const rowId = this.getRowIdInput(item);
154
- const ids = this.selectedRowIds();
155
- this.state().rowSelection.updateSelection(ids.has(rowId) ? new Set() : new Set([rowId]));
156
- }
157
- onRowCheckboxChangePrimeng(item, checked, rowIndex, _e) {
158
- const rowId = this.getRowIdInput(item);
159
- this.state().rowSelection.handleRowCheckboxChange(rowId, checked, rowIndex, this.lastMouseShift);
160
- }
161
- onResizeStartPrimeng(e, col) {
162
- e.preventDefault();
163
- // Clear cell selection before resize so selection outlines don't persist during drag
164
- this.state().interaction.setActiveCell?.(null);
165
- this.state().interaction.setSelectionRange?.(null);
166
- this.getWrapperRef()?.nativeElement.focus({ preventScroll: true });
167
- this.resizeStartX = e.clientX;
168
- this.resizeColumnId = col.columnId;
169
- this.resizeStartWidth = this.getColumnWidth(col);
170
- const onMove = (me) => {
171
- const delta = me.clientX - this.resizeStartX;
172
- const minW = col.minWidth ?? DEFAULT_MIN_COLUMN_WIDTH;
173
- const newWidth = Math.max(minW, this.resizeStartWidth + delta);
174
- this.primengColumnSizingOverrides.update((prev) => ({ ...prev, [this.resizeColumnId]: newWidth }));
175
- this.columnSizingVersion.update(v => v + 1);
176
- };
177
- const onUp = () => {
178
- window.removeEventListener('mousemove', onMove, true);
179
- window.removeEventListener('mouseup', onUp, true);
180
- const finalWidth = this.primengColumnSizingOverrides()[this.resizeColumnId];
181
- if (finalWidth) {
182
- this.onColumnResized?.(this.resizeColumnId, finalWidth);
183
- const overrides = {};
184
- for (const [id, w] of Object.entries(this.primengColumnSizingOverrides())) {
185
- overrides[id] = { widthPx: w };
186
- }
187
- this.state().layout.setColumnSizingOverrides(overrides);
188
- }
189
- };
190
- window.addEventListener('mousemove', onMove, true);
191
- window.addEventListener('mouseup', onUp, true);
192
- }
193
- // --- Build props ---
194
- buildProps() {
195
- return {
196
- items: this.itemsInput,
197
- columns: this.columns,
198
- getRowId: this.getRowIdInput,
199
- sortBy: this.sortBy,
200
- sortDirection: this.sortDirection,
201
- onColumnSort: this.onColumnSort,
202
- visibleColumns: this.visibleColumns,
203
- columnOrder: this.columnOrder,
204
- onColumnOrderChange: this.onColumnOrderChange,
205
- onColumnResized: this.onColumnResized,
206
- onColumnPinned: this.onColumnPinned,
207
- pinnedColumns: this.pinnedColumnsInput,
208
- initialColumnWidths: this.initialColumnWidths,
209
- layoutMode: this.layoutMode,
210
- suppressHorizontalScroll: this.suppressHorizontalScroll,
211
- columnReorder: this.columnReorder,
212
- isLoading: this.isLoadingInput,
213
- loadingMessage: this.loadingMessageInput,
214
- editable: this.editable,
215
- cellSelection: this.cellSelection,
216
- onCellValueChanged: this.onCellValueChanged,
217
- onUndo: this.onUndoInput,
218
- onRedo: this.onRedoInput,
219
- canUndo: this.canUndoInput,
220
- canRedo: this.canRedoInput,
221
- rowSelection: this.rowSelectionMode,
222
- selectedRows: this.selectedRows,
223
- onSelectionChange: this.onSelectionChange,
224
- showRowNumbers: this.showRowNumbers,
225
- currentPage: this.currentPageInput,
226
- pageSize: this.pageSizeInput,
227
- statusBar: this.statusBar,
228
- filters: this.filters,
229
- onFilterChange: this.onFilterChange,
230
- filterOptions: this.filterOptions,
231
- loadingFilterOptions: this.loadingFilterOptions,
232
- peopleSearch: this.peopleSearch,
233
- getUserByEmail: this.getUserByEmail,
234
- emptyState: this.emptyStateInput,
235
- onCellError: this.onCellError,
236
- stickyHeader: this.stickyHeaderInput,
237
- 'aria-label': this.ariaLabelInput,
238
- 'aria-labelledby': this.ariaLabelledByInput,
239
- };
240
- }
241
- };
242
- __decorate([
243
- ViewChild('wrapper')
244
- ], DataGridTableComponent.prototype, "wrapperRef", void 0);
245
- __decorate([
246
- ViewChild('tableContainer')
247
- ], DataGridTableComponent.prototype, "tableContainerRefEl", void 0);
248
- __decorate([
249
- Input({ required: true, alias: 'items' })
250
- ], DataGridTableComponent.prototype, "itemsInput", void 0);
251
- __decorate([
252
- Input({ required: true })
253
- ], DataGridTableComponent.prototype, "columns", void 0);
254
- __decorate([
255
- Input({ required: true, alias: 'getRowId' })
256
- ], DataGridTableComponent.prototype, "getRowIdInput", void 0);
257
- __decorate([
258
- Input()
259
- ], DataGridTableComponent.prototype, "sortBy", void 0);
260
- __decorate([
261
- Input()
262
- ], DataGridTableComponent.prototype, "sortDirection", void 0);
263
- __decorate([
264
- Input({ required: true })
265
- ], DataGridTableComponent.prototype, "onColumnSort", void 0);
266
- __decorate([
267
- Input({ required: true })
268
- ], DataGridTableComponent.prototype, "visibleColumns", void 0);
269
- __decorate([
270
- Input()
271
- ], DataGridTableComponent.prototype, "columnOrder", void 0);
272
- __decorate([
273
- Input()
274
- ], DataGridTableComponent.prototype, "onColumnOrderChange", void 0);
275
- __decorate([
276
- Input()
277
- ], DataGridTableComponent.prototype, "onColumnResized", void 0);
278
- __decorate([
279
- Input()
280
- ], DataGridTableComponent.prototype, "onColumnPinned", void 0);
281
- __decorate([
282
- Input({ alias: 'pinnedColumns' })
283
- ], DataGridTableComponent.prototype, "pinnedColumnsInput", void 0);
284
- __decorate([
285
- Input()
286
- ], DataGridTableComponent.prototype, "initialColumnWidths", void 0);
287
- __decorate([
288
- Input()
289
- ], DataGridTableComponent.prototype, "layoutMode", void 0);
290
- __decorate([
291
- Input()
292
- ], DataGridTableComponent.prototype, "suppressHorizontalScroll", void 0);
293
- __decorate([
294
- Input()
295
- ], DataGridTableComponent.prototype, "stickyHeaderInput", void 0);
296
- __decorate([
297
- Input()
298
- ], DataGridTableComponent.prototype, "columnReorder", void 0);
299
- __decorate([
300
- Input({ alias: 'isLoading' })
301
- ], DataGridTableComponent.prototype, "isLoadingInput", void 0);
302
- __decorate([
303
- Input({ alias: 'loadingMessage' })
304
- ], DataGridTableComponent.prototype, "loadingMessageInput", void 0);
305
- __decorate([
306
- Input()
307
- ], DataGridTableComponent.prototype, "editable", void 0);
308
- __decorate([
309
- Input()
310
- ], DataGridTableComponent.prototype, "cellSelection", void 0);
311
- __decorate([
312
- Input()
313
- ], DataGridTableComponent.prototype, "onCellValueChanged", void 0);
314
- __decorate([
315
- Input({ alias: 'onUndo' })
316
- ], DataGridTableComponent.prototype, "onUndoInput", void 0);
317
- __decorate([
318
- Input({ alias: 'onRedo' })
319
- ], DataGridTableComponent.prototype, "onRedoInput", void 0);
320
- __decorate([
321
- Input({ alias: 'canUndo' })
322
- ], DataGridTableComponent.prototype, "canUndoInput", void 0);
323
- __decorate([
324
- Input({ alias: 'canRedo' })
325
- ], DataGridTableComponent.prototype, "canRedoInput", void 0);
326
- __decorate([
327
- Input({ alias: 'rowSelection' })
328
- ], DataGridTableComponent.prototype, "rowSelectionMode", void 0);
329
- __decorate([
330
- Input()
331
- ], DataGridTableComponent.prototype, "selectedRows", void 0);
332
- __decorate([
333
- Input()
334
- ], DataGridTableComponent.prototype, "onSelectionChange", void 0);
335
- __decorate([
336
- Input()
337
- ], DataGridTableComponent.prototype, "statusBar", void 0);
338
- __decorate([
339
- Input({ required: true })
340
- ], DataGridTableComponent.prototype, "filters", void 0);
341
- __decorate([
342
- Input({ required: true })
343
- ], DataGridTableComponent.prototype, "onFilterChange", void 0);
344
- __decorate([
345
- Input()
346
- ], DataGridTableComponent.prototype, "filterOptions", void 0);
347
- __decorate([
348
- Input()
349
- ], DataGridTableComponent.prototype, "loadingFilterOptions", void 0);
350
- __decorate([
351
- Input()
352
- ], DataGridTableComponent.prototype, "peopleSearch", void 0);
353
- __decorate([
354
- Input()
355
- ], DataGridTableComponent.prototype, "getUserByEmail", void 0);
356
- __decorate([
357
- Input({ alias: 'emptyState' })
358
- ], DataGridTableComponent.prototype, "emptyStateInput", void 0);
359
- __decorate([
360
- Input()
361
- ], DataGridTableComponent.prototype, "onCellError", void 0);
362
- __decorate([
363
- Input({ alias: 'aria-label' })
364
- ], DataGridTableComponent.prototype, "ariaLabelInput", void 0);
365
- __decorate([
366
- Input({ alias: 'aria-labelledby' })
367
- ], DataGridTableComponent.prototype, "ariaLabelledByInput", void 0);
368
- __decorate([
369
- Input()
370
- ], DataGridTableComponent.prototype, "showRowNumbers", void 0);
371
- __decorate([
372
- Input({ alias: 'currentPage' })
373
- ], DataGridTableComponent.prototype, "currentPageInput", void 0);
374
- __decorate([
375
- Input({ alias: 'pageSize' })
376
- ], DataGridTableComponent.prototype, "pageSizeInput", void 0);
377
- DataGridTableComponent = __decorate([
378
- Component({
379
- selector: 'ogrid-primeng-datagrid-table',
380
- standalone: true,
381
- imports: [
382
- StatusBarComponent,
383
- GridContextMenuComponent,
384
- MarchingAntsOverlayComponent,
385
- EmptyStateComponent,
386
- ColumnHeaderFilterComponent,
387
- ColumnHeaderMenuComponent,
388
- InlineCellEditorComponent,
389
- PopoverCellEditorComponent,
390
- ],
391
- changeDetection: ChangeDetectionStrategy.OnPush,
392
- encapsulation: ViewEncapsulation.None,
393
- providers: [DataGridStateService, ColumnReorderService, VirtualScrollService],
394
- template: `
395
- <div class="ogrid-root">
396
- <div
397
- #wrapper
398
- tabindex="0"
399
- role="region"
400
- class="ogrid-scroll-wrapper"
401
- [class.ogrid-scroll-wrapper--loading-empty]="isLoading() && items().length === 0"
402
- [attr.aria-label]="resolvedAriaLabel()"
403
- [attr.aria-labelledby]="ariaLabelledBy()"
404
- [attr.data-empty]="showEmptyInGrid() ? 'true' : null"
405
- [attr.data-column-count]="state().layout.totalColCount"
406
- [attr.data-overflow-x]="allowOverflowX() ? 'true' : 'false'"
407
- [attr.data-has-selection]="rowSelectionMode !== 'none' ? 'true' : null"
408
- (contextmenu)="$event.preventDefault()"
409
- (keydown)="onGridKeyDown($event)"
410
- (mousedown)="onWrapperMouseDown($event)"
411
- (scroll)="onWrapperScroll($event)"
412
- [style.--data-table-column-count]="state().layout.totalColCount"
413
- [style.--data-table-width]="tableWidthStyle()"
414
- [style.--data-table-min-width]="tableMinWidthStyle()"
415
- [style.--ogrid-row-height]="rowHeightCssVar()"
416
- >
417
- <div class="ogrid-table-wrapper">
418
- <div [class.loading-dimmed]="isLoading() && items().length > 0" class="ogrid-table-wrapper">
419
- <div #tableContainer class="ogrid-table-wrapper">
420
- <table class="ogrid-table">
421
- <thead [class]="stickyHeader() ? 'ogrid-thead ogrid-sticky-header' : 'ogrid-thead'">
422
- @for (row of headerRows(); track $index; let rowIdx = $index) {
423
- <tr>
424
- @if (rowIdx === headerRows().length - 1 && hasCheckboxCol()) {
425
- <th scope="col" rowSpan="1" class="ogrid-checkbox-header">
426
- <input
427
- type="checkbox"
428
- [checked]="allSelected()"
429
- [indeterminate]="someSelected() && !allSelected()"
430
- (change)="onSelectAllChangePrimeng($any($event.target).checked)"
431
- aria-label="Select all rows"
432
- />
433
- </th>
434
- }
435
- @if (rowIdx === 0 && rowIdx < headerRows().length - 1 && hasCheckboxCol()) {
436
- <th [attr.rowSpan]="headerRows().length - 1"></th>
437
- }
438
- @if (rowIdx === headerRows().length - 1 && hasRowNumbersCol()) {
439
- <th scope="col" rowSpan="1" class="ogrid-row-number-header">
440
- #
441
- </th>
442
- }
443
- @if (rowIdx === 0 && rowIdx < headerRows().length - 1 && hasRowNumbersCol()) {
444
- <th [attr.rowSpan]="headerRows().length - 1" class="ogrid-row-number-spacer"></th>
445
- }
446
- @for (cell of row; track cell.columnDef?.columnId ?? $index; let cellIdx = $index) {
447
- @if (cell.isGroup) {
448
- <th
449
- [attr.colSpan]="cell.colSpan"
450
- scope="colgroup"
451
- class="ogrid-column-group-header"
452
- >
453
- {{ cell.label }}
454
- </th>
455
- } @else {
456
- @let col = asColumnDef(cell.columnDef);
457
- @let pinned = isPinned(col.columnId);
458
- @let config = getFilterConfig(col);
459
- @let sortState = getSortState(col.columnId);
460
- @let ariaSort = sortState === 'asc' ? 'ascending' : sortState === 'desc' ? 'descending' : null;
461
- <th
462
- scope="col"
463
- class="ogrid-header-cell"
464
- [attr.data-column-id]="col.columnId"
465
- [attr.aria-sort]="ariaSort"
466
- [attr.rowSpan]="headerRows().length > 1 && rowIdx < headerRows().length - 1 ? headerRows().length - rowIdx : null"
467
- [class.ogrid-th-pinned-left]="pinned === 'left'"
468
- [class.ogrid-th-pinned-right]="pinned === 'right'"
469
- [style.min-width.px]="getEffectiveMinWidth(col)"
470
- [style.width.px]="getColumnWidth(col)"
471
- [style.max-width.px]="getColumnWidth(col)"
472
- [style.left.px]="pinned === 'left' ? getPinnedLeftOffset(col.columnId) : null"
473
- [style.right.px]="pinned === 'right' ? getPinnedRightOffset(col.columnId) : null"
474
- [style.cursor]="columnReorderService.isDragging() ? 'grabbing' : 'grab'"
475
- (mousedown)="onHeaderMouseDown(col.columnId, $event)"
476
- >
477
- <div class="ogrid-header-content">
478
- <ogrid-primeng-column-header-filter
479
- [columnKey]="col.columnId"
480
- [columnName]="col.name"
481
- [filterType]="config.filterType"
482
- [isSorted]="config.isSorted ?? false"
483
- [isSortedDescending]="config.isSortedDescending ?? false"
484
- [onSort]="config.onSort"
485
- [selectedValues]="config.selectedValues"
486
- [onFilterChange]="config.onFilterChange"
487
- [options]="config.options ?? []"
488
- [isLoadingOptions]="config.isLoadingOptions ?? false"
489
- [textValue]="config.textValue ?? ''"
490
- [onTextChange]="config.onTextChange"
491
- [selectedUser]="config.selectedUser"
492
- [onUserChange]="config.onUserChange"
493
- [peopleSearch]="config.peopleSearch"
494
- [dateValue]="config.dateValue"
495
- [onDateChange]="config.onDateChange"
496
- ></ogrid-primeng-column-header-filter>
497
- @let colPinState = getPinState(col.columnId);
498
- <column-header-menu
499
- [columnId]="col.columnId"
500
- [canPinLeft]="colPinState.canPinLeft"
501
- [canPinRight]="colPinState.canPinRight"
502
- [canUnpin]="colPinState.canUnpin"
503
- [currentSort]="sortState"
504
- [isSortable]="col.sortable !== false"
505
- [isResizable]="col.resizable !== false"
506
- [handlers]="getColumnMenuHandlersMemoized(col.columnId)"
507
- />
508
- </div>
509
- <div
510
- class="ogrid-resize-handle"
511
- (mousedown)="onResizeStartPrimeng($event, col)"
512
- [attr.aria-label]="'Resize ' + col.name"
513
- ></div>
514
- </th>
515
- }
516
- }
517
- </tr>
518
- }
519
- </thead>
520
-
521
- @if (!showEmptyInGrid()) {
522
- <tbody>
523
- @if (vsEnabled() && vsTopSpacerHeight() > 0) {
524
- <tr [style.height.px]="vsTopSpacerHeight()"></tr>
525
- }
526
- @for (item of vsVisibleItems(); track trackByRowId($index, item); let localIdx = $index) {
527
- @let rowIndex = vsStartIndex() + localIdx;
528
- @let rowId = getRowIdInput(item);
529
- @let isSelected = selectedRowIds().has(rowId);
530
- <tr
531
- [attr.data-row-id]="rowId"
532
- [style.background]="isSelected ? 'var(--ogrid-selected-bg, #e8f0fe)' : null"
533
- (click)="onRowClickPrimeng($event, item)"
534
- >
535
- @if (hasCheckboxCol()) {
536
- <td
537
- class="ogrid-checkbox-cell"
538
- [attr.data-row-index]="rowIndex"
539
- [attr.data-col-index]="0"
540
- (click)="$event.stopPropagation()"
541
- >
542
- <input
543
- type="checkbox"
544
- [checked]="isSelected"
545
- (change)="onRowCheckboxChangePrimeng(item, $any($event.target).checked, rowIndex, $event)"
546
- [attr.aria-label]="'Select row ' + (rowIndex + 1)"
547
- />
548
- </td>
549
- }
550
- @if (hasRowNumbersCol()) {
551
- <td class="ogrid-row-number-cell">
552
-
553
- {{ rowNumberOffset() + rowIndex + 1 }}
554
- </td>
555
- }
556
- @for (col of visibleCols(); track col.columnId; let colIdx = $index) {
557
- @let pinned = isPinned(col.columnId);
558
- <td
559
- [attr.data-column-id]="col.columnId"
560
- [class.ogrid-td-pinned-left]="pinned === 'left'"
561
- [class.ogrid-td-pinned-right]="pinned === 'right'"
562
- class="ogrid-data-cell"
563
- [style.min-width.px]="getEffectiveMinWidth(col)"
564
- [style.width.px]="getColumnWidth(col)"
565
- [style.max-width.px]="getColumnWidth(col)"
566
- [style.left.px]="pinned === 'left' ? getPinnedLeftOffset(col.columnId) : null"
567
- [style.right.px]="pinned === 'right' ? getPinnedRightOffset(col.columnId) : null"
568
- [style.text-align]="col.type === 'numeric' ? 'right' : col.type === 'boolean' ? 'center' : null"
569
- >
570
- @let descriptor = getCellDescriptor(item, col, rowIndex, colIdx);
571
- @if (descriptor.mode === 'editing-inline') {
572
- <div class="ogrid-editing-cell">
573
- <ogrid-primeng-inline-cell-editor
574
- [value]="descriptor.value"
575
- [item]="item"
576
- [column]="col"
577
- [rowIndex]="rowIndex"
578
- [editorType]="descriptor.editorType ?? 'text'"
579
- (commit)="commitEdit(item, col.columnId, descriptor.value, $event, rowIndex, descriptor.globalColIndex)"
580
- (cancel)="cancelEdit()"
581
- ></ogrid-primeng-inline-cell-editor>
582
- </div>
583
- } @else if (descriptor.mode === 'editing-popover') {
584
- @let editorProps = buildPopoverEditorProps(item, col, descriptor);
585
- <ogrid-primeng-popover-cell-editor
586
- [item]="item"
587
- [column]="col"
588
- [rowIndex]="rowIndex"
589
- [globalColIndex]="descriptor.globalColIndex"
590
- [displayValue]="descriptor.displayValue"
591
- [editorProps]="editorProps"
592
- [onCancel]="cancelEditHandler"
593
- ></ogrid-primeng-popover-cell-editor>
594
- } @else {
595
- <div
596
- [attr.data-row-index]="rowIndex"
597
- [attr.data-col-index]="descriptor.globalColIndex"
598
- (mousedown)="onCellMouseDown($event, rowIndex, descriptor.globalColIndex)"
599
- (dblclick)="descriptor.canEditAny ? onCellDblClick(descriptor.rowId, col.columnId) : null"
600
- (contextmenu)="onCellContextMenu($event)"
601
- class="ogrid-cell-content"
602
- [style.cursor]="descriptor.canEditAny ? 'cell' : 'default'"
603
- [style.background]="descriptor.isInRange ? 'var(--ogrid-range-bg, rgba(33, 115, 70, 0.08))' : null"
604
- [style.outline]="descriptor.isActive ? '2px solid var(--ogrid-selection, #217346)' : null"
605
- [style.outline-offset]="descriptor.isActive ? '-2px' : null"
606
- >
607
- <span [style]="resolveCellStyleFn(col, item)">{{ resolveCellContent(col, item, descriptor.displayValue) }}</span>
608
- @if (descriptor.canEditAny && descriptor.isSelectionEndCell) {
609
- <div
610
- (mousedown)="onFillHandleMouseDown($event)"
611
- class="ogrid-fill-handle"
612
- aria-label="Fill handle"
613
- ></div>
614
- }
615
- </div>
616
- }
617
- </td>
618
- }
619
- </tr>
620
- }
621
- @if (vsEnabled() && vsBottomSpacerHeight() > 0) {
622
- <tr [style.height.px]="vsBottomSpacerHeight()"></tr>
623
- }
624
- </tbody>
625
- }
626
- </table>
627
-
628
- <ogrid-marching-ants-overlay
629
- [containerEl]="tableContainerEl()"
630
- [selectionRange]="state().interaction.selectionRange"
631
- [copyRange]="state().interaction.copyRange"
632
- [cutRange]="state().interaction.cutRange"
633
- [colOffset]="state().layout.colOffset"
634
- [columnSizingVersion]="columnSizingVersion()"
635
- [items]="items()"
636
- [visibleColumns]="propsVisibleColumns()"
637
- [columnOrder]="propsColumnOrder()"
638
- ></ogrid-marching-ants-overlay>
639
-
640
- @if (showEmptyInGrid() && emptyState()) {
641
- <div class="ogrid-empty-container">
642
- <div>
643
- <div class="ogrid-empty-title">No results found</div>
644
- <ogrid-empty-state
645
- [message]="emptyState()?.message"
646
- [hasActiveFilters]="emptyState()?.hasActiveFilters ?? false"
647
- [render]="emptyState()?.render"
648
- (clearAll)="emptyState()?.onClearAll()"
649
- ></ogrid-empty-state>
650
- </div>
651
- </div>
652
- }
653
- </div>
654
- </div>
655
- </div>
656
- </div>
657
-
658
- @if (columnReorderService.isDragging() && columnReorderService.dropIndicatorX() !== null) {
659
- <div class="ogrid-drop-indicator" [style.left.px]="columnReorderService.dropIndicatorX()"></div>
660
- }
661
-
662
- @if (menuPosition()) {
663
- <ogrid-context-menu
664
- [x]="menuPosition()!.x"
665
- [y]="menuPosition()!.y"
666
- [hasSelection]="hasCellSelection()"
667
- [canUndoProp]="canUndo()"
668
- [canRedoProp]="canRedo()"
669
- [classNames]="contextMenuClasses"
670
- (copyAction)="handleCopy()"
671
- (cutAction)="handleCut()"
672
- (pasteAction)="handlePaste()"
673
- (selectAllAction)="handleSelectAllCells()"
674
- (undoAction)="onUndo()"
675
- (redoAction)="onRedo()"
676
- (closeAction)="closeContextMenu()"
677
- ></ogrid-context-menu>
678
- }
679
-
680
- @let sbConfig = statusBarConfig();
681
- @if (sbConfig) {
682
- <ogrid-status-bar
683
- [totalCount]="sbConfig.totalCount"
684
- [filteredCount]="sbConfig.filteredCount"
685
- [selectedCount]="sbConfig.selectedCount ?? selectedRowIds().size"
686
- [selectedCellCount]="selectionCellCount()"
687
- [aggregation]="sbConfig.aggregation"
688
- [suppressRowCount]="sbConfig.suppressRowCount"
689
- [classNames]="statusBarClasses"
690
- ></ogrid-status-bar>
691
- }
692
-
693
- @if (isLoading()) {
694
- <div class="ogrid-loading-overlay" aria-live="polite">
695
- <div class="ogrid-loading-content">
696
- <span class="ogrid-loading-text">{{ loadingMessage() }}</span>
697
- </div>
698
- </div>
699
- }
700
- </div>
701
- `,
702
- styles: [OGRID_THEME_VARS_CSS, `
703
- :host { display: block; }
704
- .ogrid-root {
705
- position: relative;
706
- flex: 1;
707
- min-height: 0;
708
- display: flex;
709
- flex-direction: column;
710
- overflow: hidden;
711
- }
712
- .ogrid-scroll-wrapper {
713
- flex: 1;
714
- min-height: 0;
715
- overflow: auto;
716
- position: relative;
717
- background: var(--ogrid-bg, #ffffff);
718
- color: var(--ogrid-fg, rgba(0, 0, 0, 0.87));
719
- }
720
- .ogrid-scroll-wrapper--loading-empty { min-height: 200px; }
721
- .ogrid-table-wrapper {
722
- position: relative;
723
- }
724
- .ogrid-table {
725
- width: var(--data-table-width, 100%);
726
- min-width: var(--data-table-min-width, 100%);
727
- border-collapse: collapse;
728
- table-layout: fixed;
729
- }
730
- .ogrid-table tbody tr { height: var(--ogrid-row-height, auto); }
731
- .ogrid-thead {
732
- z-index: 3;
733
- background: var(--ogrid-header-bg, #f5f5f5);
734
- }
735
- .ogrid-sticky-header { position: sticky; top: 0; }
736
- .ogrid-sticky-header .ogrid-checkbox-header,
737
- .ogrid-sticky-header .ogrid-row-number-header { position: sticky; top: 0; }
738
- .ogrid-checkbox-header {
739
- width: 48px;
740
- min-width: 48px;
741
- max-width: 48px;
742
- text-align: center;
743
- background: var(--ogrid-header-bg, #f5f5f5);
744
- border-bottom: 2px solid var(--ogrid-border, rgba(0, 0, 0, 0.12));
745
- z-index: 3;
746
- }
747
- .ogrid-row-number-header {
748
- width: 50px;
749
- min-width: 50px;
750
- max-width: 50px;
751
- text-align: center;
752
- font-weight: 600;
753
- background: var(--ogrid-header-bg, #f5f5f5);
754
- border-bottom: 2px solid var(--ogrid-border, rgba(0, 0, 0, 0.12));
755
- z-index: 3;
756
- }
757
- .ogrid-row-number-spacer {
758
- width: 50px;
759
- min-width: 50px;
760
- max-width: 50px;
761
- background: var(--ogrid-header-bg, #f5f5f5);
762
- }
763
- .ogrid-column-group-header {
764
- text-align: center;
765
- font-weight: 600;
766
- background: var(--ogrid-header-bg, #f5f5f5);
767
- border-bottom: 2px solid var(--ogrid-border, rgba(0, 0, 0, 0.12));
768
- padding: 6px 10px;
769
- }
770
- .ogrid-header-cell {
771
- background: var(--ogrid-header-bg, #f5f5f5);
772
- border-bottom: 2px solid var(--ogrid-border, rgba(0, 0, 0, 0.12));
773
- padding: 0;
774
- position: relative;
775
- user-select: none;
776
- }
777
- .ogrid-header-content {
778
- display: flex;
779
- align-items: center;
780
- gap: 4px;
781
- padding: 6px 10px;
782
- }
783
- .ogrid-resize-handle {
784
- position: absolute;
785
- top: 0;
786
- right: 0;
787
- bottom: 0;
788
- width: 4px;
789
- cursor: col-resize;
790
- }
791
- .ogrid-checkbox-cell {
792
- width: 48px;
793
- min-width: 48px;
794
- max-width: 48px;
795
- padding: 6px 4px;
796
- text-align: center;
797
- border-bottom: 1px solid var(--ogrid-border, rgba(0, 0, 0, 0.12));
798
- }
799
- .ogrid-row-number-cell {
800
- width: 50px;
801
- min-width: 50px;
802
- max-width: 50px;
803
- padding: 6px;
804
- text-align: center;
805
- font-weight: 600;
806
- font-variant-numeric: tabular-nums;
807
- color: var(--ogrid-fg-secondary, rgba(0, 0, 0, 0.6));
808
- background: var(--ogrid-header-bg, rgba(0, 0, 0, 0.04));
809
- border-bottom: 1px solid var(--ogrid-border, rgba(0, 0, 0, 0.12));
810
- position: sticky;
811
- left: 0;
812
- z-index: 3;
813
- }
814
- .ogrid-data-cell {
815
- padding: 0;
816
- border-bottom: 1px solid var(--ogrid-border, rgba(0, 0, 0, 0.12));
817
- position: relative;
818
- }
819
- .ogrid-cell-content {
820
- padding: 6px 10px;
821
- min-height: 20px;
822
- cursor: default;
823
- overflow: hidden;
824
- text-overflow: ellipsis;
825
- white-space: nowrap;
826
- }
827
- .ogrid-editing-cell {
828
- width: 100%; height: 100%; display: flex; align-items: center; box-sizing: border-box;
829
- outline: 2px solid var(--ogrid-selection-color, #217346); outline-offset: -1px;
830
- z-index: 2; position: relative; background: var(--ogrid-bg, #fff); overflow: visible; padding: 0;
831
- }
832
- .ogrid-scroll-wrapper [data-drag-range] { background: var(--ogrid-range-bg, rgba(33, 115, 70, 0.12)) !important; }
833
- .ogrid-fill-handle {
834
- position: absolute;
835
- bottom: -3px;
836
- right: -3px;
837
- width: 7px;
838
- height: 7px;
839
- background: var(--ogrid-selection, #217346);
840
- cursor: crosshair;
841
- z-index: 2;
842
- }
843
- .ogrid-empty-container {
844
- display: flex;
845
- align-items: center;
846
- justify-content: center;
847
- padding: 48px 24px;
848
- text-align: center;
849
- color: var(--ogrid-fg-muted, rgba(0, 0, 0, 0.5));
850
- }
851
- .ogrid-empty-title {
852
- font-weight: 600;
853
- margin-bottom: 8px;
854
- }
855
- .ogrid-loading-overlay {
856
- position: absolute;
857
- inset: 0;
858
- display: flex;
859
- align-items: center;
860
- justify-content: center;
861
- background: var(--ogrid-loading-overlay, rgba(255, 255, 255, 0.7));
862
- z-index: 10;
863
- }
864
- .ogrid-loading-content {
865
- display: flex;
866
- align-items: center;
867
- gap: 8px;
868
- color: var(--ogrid-fg, #242424);
869
- }
870
- .ogrid-loading-text {
871
- font-size: 14px;
872
- }
873
- .loading-dimmed {
874
- opacity: 0.5;
875
- pointer-events: none;
876
- }
877
- .ogrid-drop-indicator {
878
- position: absolute;
879
- top: 0;
880
- bottom: 0;
881
- width: 3px;
882
- background: var(--ogrid-primary, #217346);
883
- pointer-events: none;
884
- z-index: 100;
885
- transition: left 0.05s;
886
- }
887
- .ogrid-th-pinned-left {
888
- position: sticky;
889
- top: 0;
890
- left: 0;
891
- z-index: 10;
892
- background: var(--ogrid-header-bg, #f5f5f5);
893
- border-right: 1px solid var(--ogrid-border, rgba(0, 0, 0, 0.12));
894
- box-shadow: 2px 0 4px -1px rgba(0, 0, 0, 0.1);
895
- }
896
- .ogrid-th-pinned-right {
897
- position: sticky;
898
- top: 0;
899
- right: 0;
900
- z-index: 10;
901
- background: var(--ogrid-header-bg, #f5f5f5);
902
- border-left: 1px solid var(--ogrid-border, rgba(0, 0, 0, 0.12));
903
- box-shadow: -2px 0 4px -1px rgba(0, 0, 0, 0.1);
904
- }
905
- .ogrid-td-pinned-left {
906
- position: sticky;
907
- left: 0;
908
- z-index: 5;
909
- background: var(--ogrid-bg, #fff);
910
- border-right: 1px solid var(--ogrid-border, rgba(0, 0, 0, 0.12));
911
- box-shadow: 2px 0 4px -1px rgba(0, 0, 0, 0.1);
912
- }
913
- .ogrid-td-pinned-right {
914
- position: sticky;
915
- right: 0;
916
- z-index: 5;
917
- background: var(--ogrid-bg, #fff);
918
- border-left: 1px solid var(--ogrid-border, rgba(0, 0, 0, 0.12));
919
- box-shadow: -2px 0 4px -1px rgba(0, 0, 0, 0.1);
920
- }
921
- ::ng-deep th:focus-visible,
922
- ::ng-deep td:focus-visible {
923
- outline: 2px solid var(--primary-color, #6366f1);
924
- outline-offset: -2px;
925
- z-index: 11;
926
- }
927
- ::ng-deep .p-button:focus-visible,
928
- ::ng-deep button:focus-visible {
929
- outline: 2px solid var(--primary-color, #6366f1);
930
- outline-offset: 2px;
931
- }
932
-
933
- /* Context menu */
934
- .ogrid-context-menu {
935
- position: fixed;
936
- z-index: 1000;
937
- min-width: 160px;
938
- padding: 4px 0;
939
- background: var(--ogrid-bg, #fff);
940
- border: 1px solid var(--ogrid-border, rgba(0, 0, 0, 0.12));
941
- border-radius: 6px;
942
- box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);
943
- }
944
- .ogrid-context-menu-item {
945
- display: flex;
946
- align-items: center;
947
- justify-content: space-between;
948
- gap: 24px;
949
- width: 100%;
950
- padding: 6px 12px;
951
- border: none;
952
- background: none;
953
- font-size: 13px;
954
- text-align: left;
955
- cursor: pointer;
956
- color: var(--ogrid-fg, rgba(0, 0, 0, 0.87));
957
- }
958
- .ogrid-context-menu-item:hover:not(:disabled) {
959
- background: var(--ogrid-hover-bg, rgba(0, 0, 0, 0.04));
960
- }
961
- .ogrid-context-menu-item:disabled {
962
- opacity: 0.5;
963
- cursor: not-allowed;
964
- }
965
- .ogrid-context-menu-item-label {
966
- flex: 1;
967
- }
968
- .ogrid-context-menu-item-shortcut {
969
- color: var(--ogrid-fg-muted, rgba(0, 0, 0, 0.5));
970
- font-size: 0.85em;
971
- }
972
- .ogrid-context-menu-divider {
973
- height: 1px;
974
- margin: 4px 0;
975
- background: var(--ogrid-border, rgba(0, 0, 0, 0.12));
976
- }
977
- ::ng-deep .p-checkbox:focus-visible {
978
- outline: 2px solid var(--primary-color, #6366f1);
979
- outline-offset: 2px;
980
- }
981
-
982
- /* PrimeNG Menu popup overrides — must use !important to win over PrimeNG's CSS-variable-based defaults */
983
- .p-menu {
984
- background: var(--ogrid-bg, #ffffff) !important;
985
- color: var(--ogrid-fg, rgba(0, 0, 0, 0.87)) !important;
986
- border: 1px solid var(--ogrid-border, rgba(0, 0, 0, 0.12)) !important;
987
- border-radius: 4px !important;
988
- padding: 4px 0 !important;
989
- }
990
- .p-menu-overlay {
991
- box-shadow: 0 4px 16px rgba(0, 0, 0, 0.3), 0 0 0 1px var(--ogrid-border, rgba(0, 0, 0, 0.12)) !important;
992
- }
993
- .p-menu-item-content {
994
- color: var(--ogrid-fg, rgba(0, 0, 0, 0.87)) !important;
995
- }
996
- .p-menu-item-link {
997
- color: var(--ogrid-fg, rgba(0, 0, 0, 0.87)) !important;
998
- padding: 6px 12px !important;
999
- }
1000
- .p-menu-item-label {
1001
- color: var(--ogrid-fg, rgba(0, 0, 0, 0.87)) !important;
1002
- font-size: 0.875rem !important;
1003
- }
1004
- .p-menu-item:not(.p-disabled) .p-menu-item-content:hover {
1005
- background: var(--ogrid-hover-bg, rgba(0, 0, 0, 0.04)) !important;
1006
- color: var(--ogrid-fg, rgba(0, 0, 0, 0.87)) !important;
1007
- }
1008
- .p-menu-separator {
1009
- border-color: var(--ogrid-border, rgba(0, 0, 0, 0.12)) !important;
1010
- margin: 4px 0 !important;
1011
- }
1012
- `],
1013
- })
1014
- ], DataGridTableComponent);
1015
- export { DataGridTableComponent };