@alaarab/ogrid-angular-primeng 2.1.3 → 2.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/esm/index.js CHANGED
@@ -1,15 +1,1693 @@
1
- // Re-export all from base package for consumer convenience.
2
- // Note: This prevents tree-shaking of unused utilities.
3
- // Consider explicit named exports in a future major version.
4
- export * from '@alaarab/ogrid-angular';
5
- // PrimeNG UI components
6
- export { OGridComponent } from './ogrid/ogrid.component';
7
- export { DataGridTableComponent } from './datagrid-table/datagrid-table.component';
8
- export { InlineCellEditorComponent } from './datagrid-table/inline-cell-editor.component';
9
- export { PopoverCellEditorComponent } from './datagrid-table/popover-cell-editor.component';
10
- export { ColumnHeaderFilterComponent } from './column-header-filter/column-header-filter.component';
11
- // IColumnHeaderFilterProps is now exported from @alaarab/ogrid-angular (base class)
12
- export { ColumnChooserComponent } from './column-chooser/column-chooser.component';
13
- // IColumnChooserProps is now exported from @alaarab/ogrid-angular (base class)
14
- export { PaginationControlsComponent } from './pagination-controls/pagination-controls.component';
15
- export { ColumnHeaderMenuComponent } from './column-header-menu/column-header-menu.component';
1
+ import { INLINE_CELL_EDITOR_STYLES, INLINE_CELL_EDITOR_TEMPLATE, POPOVER_CELL_EDITOR_OVERLAY_STYLES, POPOVER_CELL_EDITOR_TEMPLATE, OGRID_THEME_VARS_CSS, DataGridStateService, ColumnReorderService, VirtualScrollService, StatusBarComponent, GridContextMenuComponent, MarchingAntsOverlayComponent, EmptyStateComponent, OGridService, OGridLayoutComponent, BaseColumnHeaderFilterComponent, getColumnHeaderMenuItems, BaseInlineCellEditorComponent, BasePopoverCellEditorComponent, BaseDataGridTableComponent, DEFAULT_MIN_COLUMN_WIDTH, BaseColumnChooserComponent, BasePaginationControlsComponent } from '@alaarab/ogrid-angular';
2
+ export { AUTOSIZE_EXTRA_PX, AUTOSIZE_MAX_PX, BaseColumnChooserComponent, BaseColumnHeaderFilterComponent, BaseDataGridTableComponent, BaseInlineCellEditorComponent, BaseOGridComponent, BasePaginationControlsComponent, BasePopoverCellEditorComponent, CELL_PADDING, CHECKBOX_COLUMN_WIDTH, COLUMN_HEADER_MENU_ITEMS, ColumnReorderService, DEFAULT_DEBOUNCE_MS, DEFAULT_MIN_COLUMN_WIDTH, DataGridEditingHelper, DataGridInteractionHelper, DataGridLayoutHelper, DataGridStateService, EmptyStateComponent, GRID_BORDER_RADIUS, GRID_CONTEXT_MENU_ITEMS, GridContextMenuComponent, INLINE_CELL_EDITOR_STYLES, INLINE_CELL_EDITOR_TEMPLATE, MAX_PAGE_BUTTONS, MarchingAntsOverlayComponent, OGRID_THEME_VARS_CSS, OGridLayoutComponent, OGridService, PAGE_SIZE_OPTIONS, PEOPLE_SEARCH_DEBOUNCE_MS, POPOVER_CELL_EDITOR_OVERLAY_STYLES, POPOVER_CELL_EDITOR_TEMPLATE, ROW_NUMBER_COLUMN_WIDTH, SIDEBAR_TRANSITION_MS, SideBarComponent, StatusBarComponent, UndoRedoStack, VirtualScrollService, Z_INDEX, applyCellDeletion, applyCutClear, applyFillValues, applyPastedValues, applyRangeRowSelection, areGridRowPropsEqual, booleanParser, buildCsvHeader, buildCsvRows, buildHeaderRows, buildInlineEditorProps, buildPopoverEditorProps, calculateDropTarget, clampSelectionToBounds, computeAggregations, computeArrowNavigation, computeAutoScrollSpeed, computeNextSortState, computeRowSelectionState, computeTabNavigation, computeTotalHeight, computeVisibleRange, createDebouncedCallback, createDebouncedSignal, createLatestCallback, currencyParser, dateParser, debounce, deriveFilterOptionsFromData, emailParser, escapeCsvValue, exportToCsv, findCtrlArrowTarget, flattenColumns, formatCellValueForTsv, formatSelectionAsTsv, formatShortcut, getCellRenderDescriptor, getCellValue, getColumnHeaderMenuItems, getContextMenuHandlers, getDataGridStatusBarConfig, getFilterField, getHeaderFilterConfig, getMultiSelectFilterFields, getPaginationViewModel, getPinStateForColumn, getScrollTopForRow, getStatusBarParts, injectGlobalStyles, isFilterConfig, isInSelectionRange, isRowInRange, measureColumnContentWidth, measureRange, mergeFilter, normalizeSelectionRange, numberParser, parseTsvClipboard, parseValue, processClientSideData, rangesEqual, reorderColumnArray, resolveCellDisplayContent, resolveCellStyle, toUserLike, triggerCsvDownload, validateColumns, validateRowIds } from '@alaarab/ogrid-angular';
3
+ import { ViewChild, Component, ChangeDetectionStrategy, Input, ViewEncapsulation, inject, DestroyRef, effect, computed, signal } from '@angular/core';
4
+ import { ButtonModule } from 'primeng/button';
5
+ import { MenuModule } from 'primeng/menu';
6
+
7
+ var __defProp = Object.defineProperty;
8
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
9
+ var __decorateClass = (decorators, target, key, kind) => {
10
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
11
+ for (var i = decorators.length - 1, decorator; i >= 0; i--)
12
+ if (decorator = decorators[i])
13
+ result = (kind ? decorator(target, key, result) : decorator(result)) || result;
14
+ if (kind && result) __defProp(target, key, result);
15
+ return result;
16
+ };
17
+ var ColumnHeaderFilterComponent = class extends BaseColumnHeaderFilterComponent {
18
+ constructor() {
19
+ super();
20
+ this.destroyRef = inject(DestroyRef);
21
+ this.clickOutsideHandler = (e) => {
22
+ const trigger = this.filterTrigger?.nativeElement;
23
+ const panel = this.filterPanel?.nativeElement;
24
+ if (trigger && !trigger.contains(e.target) && (!panel || !panel.contains(e.target))) {
25
+ this.isFilterOpen.set(false);
26
+ }
27
+ };
28
+ effect(() => {
29
+ if (this.isFilterOpen()) {
30
+ this.tempTextValue.set(this.textValue ?? "");
31
+ this.searchText.set("");
32
+ const sv = this.selectedValues;
33
+ this.tempSelected.set(new Set(sv ?? []));
34
+ const dv = this.dateValue;
35
+ this.tempDateFrom.set(dv?.from ?? "");
36
+ this.tempDateTo.set(dv?.to ?? "");
37
+ document.addEventListener("mousedown", this.clickOutsideHandler, true);
38
+ } else {
39
+ document.removeEventListener("mousedown", this.clickOutsideHandler, true);
40
+ }
41
+ });
42
+ this.destroyRef.onDestroy(() => {
43
+ document.removeEventListener("mousedown", this.clickOutsideHandler, true);
44
+ });
45
+ }
46
+ getHeaderEl() {
47
+ return this.filterTrigger;
48
+ }
49
+ handleSortClick() {
50
+ this.onSort?.();
51
+ }
52
+ toggleFilter() {
53
+ this.isFilterOpen.update((v) => !v);
54
+ }
55
+ // Adapter methods for template compatibility
56
+ handleSelectAllOptions() {
57
+ this.handleSelectAllFiltered();
58
+ }
59
+ };
60
+ __decorateClass([
61
+ ViewChild("filterTrigger")
62
+ ], ColumnHeaderFilterComponent.prototype, "filterTrigger", 2);
63
+ __decorateClass([
64
+ ViewChild("filterPanel")
65
+ ], ColumnHeaderFilterComponent.prototype, "filterPanel", 2);
66
+ ColumnHeaderFilterComponent = __decorateClass([
67
+ Component({
68
+ selector: "ogrid-primeng-column-header-filter",
69
+ standalone: true,
70
+ changeDetection: ChangeDetectionStrategy.OnPush,
71
+ template: `
72
+ <div style="display:flex;align-items:center;flex:1;min-width:0;gap:4px">
73
+ <div style="flex:1;min-width:0;overflow:hidden">
74
+ <span style="overflow:hidden;text-overflow:ellipsis;white-space:nowrap;display:block" [title]="columnName" data-header-label>
75
+ {{ columnName }}
76
+ </span>
77
+ </div>
78
+ <div style="display:flex;align-items:center;gap:2px;flex-shrink:0">
79
+ @if (filterType !== 'none') {
80
+ <button
81
+ type="button"
82
+ #filterTrigger
83
+ (click)="toggleFilter()"
84
+ [attr.aria-label]="'Filter ' + columnName"
85
+ [title]="'Filter ' + columnName"
86
+ style="border:none;background:transparent;cursor:pointer;padding:2px 4px;font-size:12px;position:relative;color:var(--ogrid-fg, #242424)"
87
+ [style.font-weight]="hasActiveFilter() ? 'bold' : 'normal'"
88
+ >
89
+ &#9662;
90
+ @if (hasActiveFilter()) {
91
+ <span style="position:absolute;top:0;right:0;width:6px;height:6px;border-radius:50%;background:var(--ogrid-selection, #217346)"></span>
92
+ }
93
+ </button>
94
+
95
+ @if (isFilterOpen()) {
96
+ <div
97
+ #filterPanel
98
+ style="position:absolute;top:100%;left:0;z-index:1000;min-width:200px;background:var(--ogrid-bg, #fff);border:1px solid var(--ogrid-border, rgba(0, 0, 0, 0.12));border-radius:6px;box-shadow:0 2px 8px rgba(0,0,0,0.12);font-weight:normal"
99
+ >
100
+ <div style="padding:8px 12px;font-weight:600;font-size:12px;border-bottom:1px solid var(--ogrid-border, rgba(0, 0, 0, 0.12))">
101
+ Filter: {{ columnName }}
102
+ </div>
103
+
104
+ @if (filterType === 'text') {
105
+ <div style="padding:8px 12px">
106
+ <input
107
+ type="text"
108
+ [value]="tempTextValue()"
109
+ (input)="tempTextValue.set($any($event.target).value)"
110
+ placeholder="Filter text..."
111
+ style="width:100%;box-sizing:border-box;padding:6px 8px;border:1px solid var(--ogrid-border, rgba(0, 0, 0, 0.12));border-radius:4px;font-size:13px;background:var(--ogrid-bg, #fff);color:var(--ogrid-fg, #242424)"
112
+ [attr.aria-label]="'Filter ' + columnName"
113
+ />
114
+ </div>
115
+ <div style="display:flex;justify-content:flex-end;gap:6px;padding:6px 12px;border-top:1px solid var(--ogrid-border, rgba(0, 0, 0, 0.12))">
116
+ <button
117
+ type="button"
118
+ class="p-button p-button-text p-button-sm"
119
+ (click)="handleTextClear()"
120
+ [disabled]="!textValue"
121
+ style="font-size:12px"
122
+ >Clear</button>
123
+ <button
124
+ type="button"
125
+ class="p-button p-button-sm"
126
+ (click)="handleTextApply()"
127
+ style="font-size:12px"
128
+ >Apply</button>
129
+ </div>
130
+ }
131
+
132
+ @if (filterType === 'multiSelect') {
133
+ <div style="padding:8px 12px">
134
+ <input
135
+ type="text"
136
+ [value]="searchText()"
137
+ (input)="searchText.set($any($event.target).value)"
138
+ placeholder="Search options..."
139
+ style="width:100%;box-sizing:border-box;padding:6px 8px;border:1px solid var(--ogrid-border, rgba(0, 0, 0, 0.12));border-radius:4px;font-size:13px;margin-bottom:6px;background:var(--ogrid-bg, #fff);color:var(--ogrid-fg, #242424)"
140
+ [attr.aria-label]="'Search ' + columnName + ' options'"
141
+ />
142
+ @if (isLoadingOptions) {
143
+ <div style="padding:8px 0;color:var(--ogrid-fg-muted, rgba(0, 0, 0, 0.5));font-size:12px">Loading...</div>
144
+ } @else {
145
+ <div style="display:flex;gap:4px;margin-bottom:6px">
146
+ <button type="button" class="p-button p-button-text p-button-sm" (click)="handleSelectAllOptions()" style="font-size:11px">All</button>
147
+ <button type="button" class="p-button p-button-text p-button-sm" (click)="handleClearSelection()" style="font-size:11px">None</button>
148
+ </div>
149
+ <div style="max-height:160px;overflow-y:auto" role="group" [attr.aria-label]="columnName + ' filter options'">
150
+ @for (opt of filteredOptions(); track opt) {
151
+ <label style="display:flex;align-items:center;gap:6px;padding:2px 0;cursor:pointer;font-size:13px">
152
+ <input
153
+ type="checkbox"
154
+ [checked]="tempSelected().has(opt)"
155
+ (change)="handleCheckboxChange(opt, $event)"
156
+ />
157
+ {{ opt }}
158
+ </label>
159
+ }
160
+ </div>
161
+ }
162
+ </div>
163
+ <div style="display:flex;justify-content:flex-end;gap:6px;padding:6px 12px;border-top:1px solid var(--ogrid-border, rgba(0, 0, 0, 0.12))">
164
+ <button
165
+ type="button"
166
+ class="p-button p-button-sm"
167
+ (click)="handleApplyMultiSelect()"
168
+ style="font-size:12px"
169
+ >Apply</button>
170
+ </div>
171
+ }
172
+
173
+ @if (filterType === 'people') {
174
+ <div style="padding:8px 12px">
175
+ @if (selectedUser) {
176
+ <div style="display:flex;align-items:center;justify-content:space-between;padding:4px 0;margin-bottom:4px">
177
+ <span style="font-size:13px">{{ selectedUser!.displayName ?? selectedUser!.email }}</span>
178
+ <button type="button" class="p-button p-button-text p-button-sm" (click)="handleClearUser()" style="font-size:11px">Clear</button>
179
+ </div>
180
+ }
181
+ <input
182
+ type="text"
183
+ [value]="peopleSearchText()"
184
+ (input)="onPeopleSearchInput($event)"
185
+ placeholder="Search people..."
186
+ style="width:100%;box-sizing:border-box;padding:6px 8px;border:1px solid var(--ogrid-border, rgba(0, 0, 0, 0.12));border-radius:4px;font-size:13px;background:var(--ogrid-bg, #fff);color:var(--ogrid-fg, #242424)"
187
+ [attr.aria-label]="'Search people for ' + columnName"
188
+ />
189
+ @if (isPeopleLoading()) {
190
+ <div style="padding:8px 0;color:var(--ogrid-fg-muted, rgba(0, 0, 0, 0.5));font-size:12px">Loading...</div>
191
+ }
192
+ @for (user of peopleSuggestions(); track user.email) {
193
+ <button
194
+ type="button"
195
+ (click)="handleUserSelect(user)"
196
+ style="display:block;width:100%;text-align:left;padding:6px 0;border:none;background:transparent;cursor:pointer;font-size:13px;color:var(--ogrid-fg, #242424)"
197
+ >
198
+ {{ user.displayName ?? user.email }}
199
+ </button>
200
+ }
201
+ </div>
202
+ }
203
+
204
+ @if (filterType === 'date') {
205
+ <div style="padding:8px 12px;display:flex;flex-direction:column;gap:6px">
206
+ <label style="display:flex;align-items:center;gap:6px;font-size:12px">
207
+ From:
208
+ <input
209
+ type="date"
210
+ [value]="tempDateFrom()"
211
+ (change)="tempDateFrom.set($any($event.target).value)"
212
+ style="flex:1;padding:4px 6px;border:1px solid var(--ogrid-border, rgba(0, 0, 0, 0.12));border-radius:4px;background:var(--ogrid-bg, #fff);color:var(--ogrid-fg, #242424)"
213
+ />
214
+ </label>
215
+ <label style="display:flex;align-items:center;gap:6px;font-size:12px">
216
+ To:
217
+ <input
218
+ type="date"
219
+ [value]="tempDateTo()"
220
+ (change)="tempDateTo.set($any($event.target).value)"
221
+ style="flex:1;padding:4px 6px;border:1px solid var(--ogrid-border, rgba(0, 0, 0, 0.12));border-radius:4px;background:var(--ogrid-bg, #fff);color:var(--ogrid-fg, #242424)"
222
+ />
223
+ </label>
224
+ </div>
225
+ <div style="display:flex;justify-content:flex-end;gap:6px;padding:6px 12px;border-top:1px solid var(--ogrid-border, rgba(0, 0, 0, 0.12))">
226
+ <button
227
+ type="button"
228
+ class="p-button p-button-text p-button-sm"
229
+ (click)="handleDateClear()"
230
+ [disabled]="!tempDateFrom() && !tempDateTo()"
231
+ style="font-size:12px"
232
+ >Clear</button>
233
+ <button
234
+ type="button"
235
+ class="p-button p-button-sm"
236
+ (click)="handleDateApply()"
237
+ style="font-size:12px"
238
+ >Apply</button>
239
+ </div>
240
+ }
241
+ </div>
242
+ }
243
+ }
244
+ </div>
245
+ </div>
246
+ `,
247
+ styles: [`
248
+ :host {
249
+ display: flex;
250
+ position: relative;
251
+ flex: 1;
252
+ min-width: 0;
253
+ }
254
+ `]
255
+ })
256
+ ], ColumnHeaderFilterComponent);
257
+ var ColumnHeaderMenuComponent = class {
258
+ constructor() {
259
+ this.canPinLeft = true;
260
+ this.canPinRight = true;
261
+ this.canUnpin = false;
262
+ this.currentSort = null;
263
+ this.isSortable = true;
264
+ this.isResizable = true;
265
+ this.handlers = {};
266
+ this.menuModel = computed(() => {
267
+ const items = getColumnHeaderMenuItems({
268
+ canPinLeft: this.canPinLeft,
269
+ canPinRight: this.canPinRight,
270
+ canUnpin: this.canUnpin,
271
+ currentSort: this.currentSort,
272
+ isSortable: this.isSortable,
273
+ isResizable: this.isResizable
274
+ });
275
+ const h = this.handlers;
276
+ const actionMap = {
277
+ pinLeft: h.onPinLeft,
278
+ pinRight: h.onPinRight,
279
+ unpin: h.onUnpin,
280
+ sortAsc: h.onSortAsc,
281
+ sortDesc: h.onSortDesc,
282
+ clearSort: h.onClearSort,
283
+ autosizeThis: h.onAutosizeThis,
284
+ autosizeAll: h.onAutosizeAll
285
+ };
286
+ const result = [];
287
+ for (const item of items) {
288
+ result.push({
289
+ label: item.label,
290
+ disabled: item.disabled,
291
+ command: () => {
292
+ const action = actionMap[item.id];
293
+ if (action) {
294
+ action();
295
+ h.onClose?.();
296
+ }
297
+ }
298
+ });
299
+ if (item.divider) {
300
+ result.push({ separator: true });
301
+ }
302
+ }
303
+ return result;
304
+ });
305
+ }
306
+ };
307
+ __decorateClass([
308
+ Input({ required: true })
309
+ ], ColumnHeaderMenuComponent.prototype, "columnId", 2);
310
+ __decorateClass([
311
+ Input()
312
+ ], ColumnHeaderMenuComponent.prototype, "canPinLeft", 2);
313
+ __decorateClass([
314
+ Input()
315
+ ], ColumnHeaderMenuComponent.prototype, "canPinRight", 2);
316
+ __decorateClass([
317
+ Input()
318
+ ], ColumnHeaderMenuComponent.prototype, "canUnpin", 2);
319
+ __decorateClass([
320
+ Input()
321
+ ], ColumnHeaderMenuComponent.prototype, "currentSort", 2);
322
+ __decorateClass([
323
+ Input()
324
+ ], ColumnHeaderMenuComponent.prototype, "isSortable", 2);
325
+ __decorateClass([
326
+ Input()
327
+ ], ColumnHeaderMenuComponent.prototype, "isResizable", 2);
328
+ __decorateClass([
329
+ Input()
330
+ ], ColumnHeaderMenuComponent.prototype, "handlers", 2);
331
+ __decorateClass([
332
+ ViewChild("menu")
333
+ ], ColumnHeaderMenuComponent.prototype, "menuRef", 2);
334
+ ColumnHeaderMenuComponent = __decorateClass([
335
+ Component({
336
+ selector: "column-header-menu",
337
+ standalone: true,
338
+ imports: [ButtonModule, MenuModule],
339
+ changeDetection: ChangeDetectionStrategy.OnPush,
340
+ template: `
341
+ <button
342
+ pButton
343
+ type="button"
344
+ icon="pi pi-ellipsis-v"
345
+ class="p-button-text p-button-sm column-header-menu-trigger"
346
+ (click)="menu.toggle($event)"
347
+ [attr.aria-label]="'Column options for ' + columnId"
348
+ ></button>
349
+
350
+ <p-menu
351
+ #menu
352
+ [model]="menuModel()"
353
+ [popup]="true"
354
+ appendTo="body"
355
+ ></p-menu>
356
+ `,
357
+ styles: [`
358
+ .column-header-menu-trigger {
359
+ padding: 0.25rem;
360
+ min-width: 24px;
361
+ height: 24px;
362
+ display: inline-flex;
363
+ align-items: center;
364
+ justify-content: center;
365
+ color: var(--ogrid-fg-secondary, rgba(0, 0, 0, 0.6));
366
+ border-radius: 4px;
367
+ transition: background-color 0.15s;
368
+ }
369
+ .column-header-menu-trigger:hover {
370
+ background: var(--ogrid-hover-bg, rgba(0, 0, 0, 0.04));
371
+ color: var(--ogrid-fg, rgba(0, 0, 0, 0.87));
372
+ }
373
+ `]
374
+ })
375
+ ], ColumnHeaderMenuComponent);
376
+ var InlineCellEditorComponent = class extends BaseInlineCellEditorComponent {
377
+ };
378
+ InlineCellEditorComponent = __decorateClass([
379
+ Component({
380
+ selector: "ogrid-primeng-inline-cell-editor",
381
+ standalone: true,
382
+ changeDetection: ChangeDetectionStrategy.OnPush,
383
+ template: INLINE_CELL_EDITOR_TEMPLATE,
384
+ styles: [INLINE_CELL_EDITOR_STYLES]
385
+ })
386
+ ], InlineCellEditorComponent);
387
+ var PopoverCellEditorComponent = class extends BasePopoverCellEditorComponent {
388
+ };
389
+ PopoverCellEditorComponent = __decorateClass([
390
+ Component({
391
+ selector: "ogrid-primeng-popover-cell-editor",
392
+ standalone: true,
393
+ changeDetection: ChangeDetectionStrategy.OnPush,
394
+ template: POPOVER_CELL_EDITOR_TEMPLATE,
395
+ styles: [`
396
+ ${POPOVER_CELL_EDITOR_OVERLAY_STYLES}
397
+ .ogrid-popover-anchor {
398
+ padding: 6px 10px; min-height: 20px; cursor: default; overflow: hidden;
399
+ text-overflow: ellipsis; white-space: nowrap;
400
+ outline: 2px solid var(--ogrid-selection, #217346); outline-offset: -2px;
401
+ }
402
+ `]
403
+ })
404
+ ], PopoverCellEditorComponent);
405
+
406
+ // src/datagrid-table/datagrid-table.component.ts
407
+ var DataGridTableComponent = class extends BaseDataGridTableComponent {
408
+ constructor() {
409
+ super();
410
+ this.sortBy = void 0;
411
+ this.sortDirection = "asc";
412
+ this.columnOrder = void 0;
413
+ this.onColumnOrderChange = void 0;
414
+ this.onColumnResized = void 0;
415
+ this.onColumnPinned = void 0;
416
+ this.pinnedColumnsInput = void 0;
417
+ this.initialColumnWidths = void 0;
418
+ this.layoutMode = "fill";
419
+ this.suppressHorizontalScroll = void 0;
420
+ this.stickyHeaderInput = void 0;
421
+ this.columnReorder = void 0;
422
+ this.isLoadingInput = false;
423
+ this.loadingMessageInput = "Loading\u2026";
424
+ this.editable = void 0;
425
+ this.cellSelection = void 0;
426
+ this.onCellValueChanged = void 0;
427
+ this.onUndoInput = void 0;
428
+ this.onRedoInput = void 0;
429
+ this.canUndoInput = void 0;
430
+ this.canRedoInput = void 0;
431
+ this.rowSelectionMode = "none";
432
+ this.selectedRows = void 0;
433
+ this.onSelectionChange = void 0;
434
+ this.statusBar = void 0;
435
+ this.filterOptions = {};
436
+ this.loadingFilterOptions = {};
437
+ this.peopleSearch = void 0;
438
+ this.getUserByEmail = void 0;
439
+ this.emptyStateInput = void 0;
440
+ this.onCellError = void 0;
441
+ this.ariaLabelInput = void 0;
442
+ this.ariaLabelledByInput = void 0;
443
+ this.showRowNumbers = false;
444
+ this.currentPageInput = 1;
445
+ this.pageSizeInput = 25;
446
+ this.defaultMinWidth = DEFAULT_MIN_COLUMN_WIDTH;
447
+ this.statusBarClasses = {
448
+ statusBar: "ogrid-status-bar",
449
+ statusBarItem: "ogrid-status-bar-item",
450
+ statusBarLabel: "ogrid-status-bar-label",
451
+ statusBarValue: "ogrid-status-bar-value"
452
+ };
453
+ this.contextMenuClasses = {
454
+ contextMenu: "ogrid-context-menu",
455
+ contextMenuItem: "ogrid-context-menu-item",
456
+ contextMenuItemLabel: "ogrid-context-menu-item-label",
457
+ contextMenuItemShortcut: "ogrid-context-menu-item-shortcut",
458
+ contextMenuDivider: "ogrid-context-menu-divider"
459
+ };
460
+ // PrimeNG uses flat number overrides for column sizing
461
+ this.primengColumnSizingOverrides = signal({});
462
+ this.propsSignal = signal(void 0);
463
+ this.resizeStartX = 0;
464
+ this.resizeColumnId = "";
465
+ this.resizeStartWidth = 0;
466
+ // Bound method reference for template
467
+ this.cancelEditHandler = () => this.cancelEdit();
468
+ // --- PrimeNG-specific computed signals ---
469
+ this.resolvedAriaLabel = computed(
470
+ () => this.ariaLabelInput ?? (this.ariaLabelledByInput ? void 0 : "Data grid")
471
+ );
472
+ this.tableWidthStyle = computed(() => {
473
+ if (this.showEmptyInGrid()) return "100%";
474
+ if (this.allowOverflowX()) return "fit-content";
475
+ if (this.layoutMode === "content") return "fit-content";
476
+ return "100%";
477
+ });
478
+ this.tableMinWidthStyle = computed(() => {
479
+ if (this.showEmptyInGrid()) return "100%";
480
+ if (this.allowOverflowX()) return "max-content";
481
+ if (this.layoutMode === "content") return "max-content";
482
+ return "100%";
483
+ });
484
+ this.initBase();
485
+ }
486
+ ngOnChanges(changes) {
487
+ if (changes["initialColumnWidths"]) {
488
+ const iw = this.initialColumnWidths;
489
+ if (iw) {
490
+ this.primengColumnSizingOverrides.set({ ...iw });
491
+ }
492
+ }
493
+ const next = this.buildProps();
494
+ const prev = this.propsSignal();
495
+ if (!prev || !this.shallowEqual(prev, next)) {
496
+ this.propsSignal.set(next);
497
+ }
498
+ }
499
+ /** Shallow-compare two props objects by their top-level keys. */
500
+ shallowEqual(a, b) {
501
+ const aObj = a;
502
+ const bObj = b;
503
+ const keysA = Object.keys(aObj);
504
+ const keysB = Object.keys(bObj);
505
+ if (keysA.length !== keysB.length) return false;
506
+ for (const key of keysA) {
507
+ if (aObj[key] !== bObj[key]) return false;
508
+ }
509
+ return true;
510
+ }
511
+ // --- Abstract method implementations ---
512
+ getProps() {
513
+ return this.propsSignal();
514
+ }
515
+ getWrapperRef() {
516
+ return this.wrapperRef;
517
+ }
518
+ getTableContainerRef() {
519
+ return this.tableContainerRefEl;
520
+ }
521
+ // --- Column width override (PrimeNG uses flat number instead of { widthPx }) ---
522
+ getColumnWidth(col) {
523
+ const override = this.primengColumnSizingOverrides()[col.columnId];
524
+ if (override) return override;
525
+ return col.idealWidth ?? col.defaultWidth ?? col.minWidth ?? DEFAULT_MIN_COLUMN_WIDTH;
526
+ }
527
+ // --- PrimeNG-specific helpers ---
528
+ trackByRowId(_index, item) {
529
+ return this.getRowIdInput(item);
530
+ }
531
+ // --- PrimeNG-specific event handlers ---
532
+ onSelectAllChangePrimeng(checked) {
533
+ this.state().rowSelection.handleSelectAll(checked);
534
+ }
535
+ onRowClickPrimeng(e, item) {
536
+ if (this.rowSelectionMode !== "single") return;
537
+ const rowId = this.getRowIdInput(item);
538
+ const ids = this.selectedRowIds();
539
+ this.state().rowSelection.updateSelection(ids.has(rowId) ? /* @__PURE__ */ new Set() : /* @__PURE__ */ new Set([rowId]));
540
+ }
541
+ onRowCheckboxChangePrimeng(item, checked, rowIndex, _e) {
542
+ const rowId = this.getRowIdInput(item);
543
+ this.state().rowSelection.handleRowCheckboxChange(rowId, checked, rowIndex, this.lastMouseShift);
544
+ }
545
+ onResizeStartPrimeng(e, col) {
546
+ e.preventDefault();
547
+ this.state().interaction.setActiveCell?.(null);
548
+ this.state().interaction.setSelectionRange?.(null);
549
+ this.getWrapperRef()?.nativeElement.focus({ preventScroll: true });
550
+ this.resizeStartX = e.clientX;
551
+ this.resizeColumnId = col.columnId;
552
+ this.resizeStartWidth = this.getColumnWidth(col);
553
+ const onMove = (me) => {
554
+ const delta = me.clientX - this.resizeStartX;
555
+ const minW = col.minWidth ?? DEFAULT_MIN_COLUMN_WIDTH;
556
+ const newWidth = Math.max(minW, this.resizeStartWidth + delta);
557
+ this.primengColumnSizingOverrides.update((prev) => ({ ...prev, [this.resizeColumnId]: newWidth }));
558
+ this.columnSizingVersion.update((v) => v + 1);
559
+ };
560
+ const onUp = () => {
561
+ window.removeEventListener("mousemove", onMove, true);
562
+ window.removeEventListener("mouseup", onUp, true);
563
+ const finalWidth = this.primengColumnSizingOverrides()[this.resizeColumnId];
564
+ if (finalWidth) {
565
+ this.onColumnResized?.(this.resizeColumnId, finalWidth);
566
+ const overrides = {};
567
+ for (const [id, w] of Object.entries(this.primengColumnSizingOverrides())) {
568
+ overrides[id] = { widthPx: w };
569
+ }
570
+ this.state().layout.setColumnSizingOverrides(overrides);
571
+ }
572
+ };
573
+ window.addEventListener("mousemove", onMove, true);
574
+ window.addEventListener("mouseup", onUp, true);
575
+ }
576
+ // --- Build props ---
577
+ buildProps() {
578
+ return {
579
+ items: this.itemsInput,
580
+ columns: this.columns,
581
+ getRowId: this.getRowIdInput,
582
+ sortBy: this.sortBy,
583
+ sortDirection: this.sortDirection,
584
+ onColumnSort: this.onColumnSort,
585
+ visibleColumns: this.visibleColumns,
586
+ columnOrder: this.columnOrder,
587
+ onColumnOrderChange: this.onColumnOrderChange,
588
+ onColumnResized: this.onColumnResized,
589
+ onColumnPinned: this.onColumnPinned,
590
+ pinnedColumns: this.pinnedColumnsInput,
591
+ initialColumnWidths: this.initialColumnWidths,
592
+ layoutMode: this.layoutMode,
593
+ suppressHorizontalScroll: this.suppressHorizontalScroll,
594
+ columnReorder: this.columnReorder,
595
+ isLoading: this.isLoadingInput,
596
+ loadingMessage: this.loadingMessageInput,
597
+ editable: this.editable,
598
+ cellSelection: this.cellSelection,
599
+ onCellValueChanged: this.onCellValueChanged,
600
+ onUndo: this.onUndoInput,
601
+ onRedo: this.onRedoInput,
602
+ canUndo: this.canUndoInput,
603
+ canRedo: this.canRedoInput,
604
+ rowSelection: this.rowSelectionMode,
605
+ selectedRows: this.selectedRows,
606
+ onSelectionChange: this.onSelectionChange,
607
+ showRowNumbers: this.showRowNumbers,
608
+ currentPage: this.currentPageInput,
609
+ pageSize: this.pageSizeInput,
610
+ statusBar: this.statusBar,
611
+ filters: this.filters,
612
+ onFilterChange: this.onFilterChange,
613
+ filterOptions: this.filterOptions,
614
+ loadingFilterOptions: this.loadingFilterOptions,
615
+ peopleSearch: this.peopleSearch,
616
+ getUserByEmail: this.getUserByEmail,
617
+ emptyState: this.emptyStateInput,
618
+ onCellError: this.onCellError,
619
+ stickyHeader: this.stickyHeaderInput,
620
+ "aria-label": this.ariaLabelInput,
621
+ "aria-labelledby": this.ariaLabelledByInput
622
+ };
623
+ }
624
+ };
625
+ __decorateClass([
626
+ ViewChild("wrapper")
627
+ ], DataGridTableComponent.prototype, "wrapperRef", 2);
628
+ __decorateClass([
629
+ ViewChild("tableContainer")
630
+ ], DataGridTableComponent.prototype, "tableContainerRefEl", 2);
631
+ __decorateClass([
632
+ Input({ required: true, alias: "items" })
633
+ ], DataGridTableComponent.prototype, "itemsInput", 2);
634
+ __decorateClass([
635
+ Input({ required: true })
636
+ ], DataGridTableComponent.prototype, "columns", 2);
637
+ __decorateClass([
638
+ Input({ required: true, alias: "getRowId" })
639
+ ], DataGridTableComponent.prototype, "getRowIdInput", 2);
640
+ __decorateClass([
641
+ Input()
642
+ ], DataGridTableComponent.prototype, "sortBy", 2);
643
+ __decorateClass([
644
+ Input()
645
+ ], DataGridTableComponent.prototype, "sortDirection", 2);
646
+ __decorateClass([
647
+ Input({ required: true })
648
+ ], DataGridTableComponent.prototype, "onColumnSort", 2);
649
+ __decorateClass([
650
+ Input({ required: true })
651
+ ], DataGridTableComponent.prototype, "visibleColumns", 2);
652
+ __decorateClass([
653
+ Input()
654
+ ], DataGridTableComponent.prototype, "columnOrder", 2);
655
+ __decorateClass([
656
+ Input()
657
+ ], DataGridTableComponent.prototype, "onColumnOrderChange", 2);
658
+ __decorateClass([
659
+ Input()
660
+ ], DataGridTableComponent.prototype, "onColumnResized", 2);
661
+ __decorateClass([
662
+ Input()
663
+ ], DataGridTableComponent.prototype, "onColumnPinned", 2);
664
+ __decorateClass([
665
+ Input({ alias: "pinnedColumns" })
666
+ ], DataGridTableComponent.prototype, "pinnedColumnsInput", 2);
667
+ __decorateClass([
668
+ Input()
669
+ ], DataGridTableComponent.prototype, "initialColumnWidths", 2);
670
+ __decorateClass([
671
+ Input()
672
+ ], DataGridTableComponent.prototype, "layoutMode", 2);
673
+ __decorateClass([
674
+ Input()
675
+ ], DataGridTableComponent.prototype, "suppressHorizontalScroll", 2);
676
+ __decorateClass([
677
+ Input()
678
+ ], DataGridTableComponent.prototype, "stickyHeaderInput", 2);
679
+ __decorateClass([
680
+ Input()
681
+ ], DataGridTableComponent.prototype, "columnReorder", 2);
682
+ __decorateClass([
683
+ Input({ alias: "isLoading" })
684
+ ], DataGridTableComponent.prototype, "isLoadingInput", 2);
685
+ __decorateClass([
686
+ Input({ alias: "loadingMessage" })
687
+ ], DataGridTableComponent.prototype, "loadingMessageInput", 2);
688
+ __decorateClass([
689
+ Input()
690
+ ], DataGridTableComponent.prototype, "editable", 2);
691
+ __decorateClass([
692
+ Input()
693
+ ], DataGridTableComponent.prototype, "cellSelection", 2);
694
+ __decorateClass([
695
+ Input()
696
+ ], DataGridTableComponent.prototype, "onCellValueChanged", 2);
697
+ __decorateClass([
698
+ Input({ alias: "onUndo" })
699
+ ], DataGridTableComponent.prototype, "onUndoInput", 2);
700
+ __decorateClass([
701
+ Input({ alias: "onRedo" })
702
+ ], DataGridTableComponent.prototype, "onRedoInput", 2);
703
+ __decorateClass([
704
+ Input({ alias: "canUndo" })
705
+ ], DataGridTableComponent.prototype, "canUndoInput", 2);
706
+ __decorateClass([
707
+ Input({ alias: "canRedo" })
708
+ ], DataGridTableComponent.prototype, "canRedoInput", 2);
709
+ __decorateClass([
710
+ Input({ alias: "rowSelection" })
711
+ ], DataGridTableComponent.prototype, "rowSelectionMode", 2);
712
+ __decorateClass([
713
+ Input()
714
+ ], DataGridTableComponent.prototype, "selectedRows", 2);
715
+ __decorateClass([
716
+ Input()
717
+ ], DataGridTableComponent.prototype, "onSelectionChange", 2);
718
+ __decorateClass([
719
+ Input()
720
+ ], DataGridTableComponent.prototype, "statusBar", 2);
721
+ __decorateClass([
722
+ Input({ required: true })
723
+ ], DataGridTableComponent.prototype, "filters", 2);
724
+ __decorateClass([
725
+ Input({ required: true })
726
+ ], DataGridTableComponent.prototype, "onFilterChange", 2);
727
+ __decorateClass([
728
+ Input()
729
+ ], DataGridTableComponent.prototype, "filterOptions", 2);
730
+ __decorateClass([
731
+ Input()
732
+ ], DataGridTableComponent.prototype, "loadingFilterOptions", 2);
733
+ __decorateClass([
734
+ Input()
735
+ ], DataGridTableComponent.prototype, "peopleSearch", 2);
736
+ __decorateClass([
737
+ Input()
738
+ ], DataGridTableComponent.prototype, "getUserByEmail", 2);
739
+ __decorateClass([
740
+ Input({ alias: "emptyState" })
741
+ ], DataGridTableComponent.prototype, "emptyStateInput", 2);
742
+ __decorateClass([
743
+ Input()
744
+ ], DataGridTableComponent.prototype, "onCellError", 2);
745
+ __decorateClass([
746
+ Input({ alias: "aria-label" })
747
+ ], DataGridTableComponent.prototype, "ariaLabelInput", 2);
748
+ __decorateClass([
749
+ Input({ alias: "aria-labelledby" })
750
+ ], DataGridTableComponent.prototype, "ariaLabelledByInput", 2);
751
+ __decorateClass([
752
+ Input()
753
+ ], DataGridTableComponent.prototype, "showRowNumbers", 2);
754
+ __decorateClass([
755
+ Input({ alias: "currentPage" })
756
+ ], DataGridTableComponent.prototype, "currentPageInput", 2);
757
+ __decorateClass([
758
+ Input({ alias: "pageSize" })
759
+ ], DataGridTableComponent.prototype, "pageSizeInput", 2);
760
+ DataGridTableComponent = __decorateClass([
761
+ Component({
762
+ selector: "ogrid-primeng-datagrid-table",
763
+ standalone: true,
764
+ imports: [
765
+ StatusBarComponent,
766
+ GridContextMenuComponent,
767
+ MarchingAntsOverlayComponent,
768
+ EmptyStateComponent,
769
+ ColumnHeaderFilterComponent,
770
+ ColumnHeaderMenuComponent,
771
+ InlineCellEditorComponent,
772
+ PopoverCellEditorComponent
773
+ ],
774
+ changeDetection: ChangeDetectionStrategy.OnPush,
775
+ encapsulation: ViewEncapsulation.None,
776
+ providers: [DataGridStateService, ColumnReorderService, VirtualScrollService],
777
+ template: `
778
+ <div class="ogrid-root">
779
+ <div
780
+ #wrapper
781
+ tabindex="0"
782
+ role="region"
783
+ class="ogrid-scroll-wrapper"
784
+ [class.ogrid-scroll-wrapper--loading-empty]="isLoading() && items().length === 0"
785
+ [attr.aria-label]="resolvedAriaLabel()"
786
+ [attr.aria-labelledby]="ariaLabelledBy()"
787
+ [attr.data-empty]="showEmptyInGrid() ? 'true' : null"
788
+ [attr.data-column-count]="state().layout.totalColCount"
789
+ [attr.data-overflow-x]="allowOverflowX() ? 'true' : 'false'"
790
+ [attr.data-has-selection]="rowSelectionMode !== 'none' ? 'true' : null"
791
+ (contextmenu)="$event.preventDefault()"
792
+ (keydown)="onGridKeyDown($event)"
793
+ (mousedown)="onWrapperMouseDown($event)"
794
+ (scroll)="onWrapperScroll($event)"
795
+ [style.--data-table-column-count]="state().layout.totalColCount"
796
+ [style.--data-table-width]="tableWidthStyle()"
797
+ [style.--data-table-min-width]="tableMinWidthStyle()"
798
+ [style.--ogrid-row-height]="rowHeightCssVar()"
799
+ >
800
+ <div class="ogrid-table-wrapper">
801
+ <div [class.loading-dimmed]="isLoading() && items().length > 0" class="ogrid-table-wrapper">
802
+ <div #tableContainer class="ogrid-table-wrapper">
803
+ <table class="ogrid-table">
804
+ <thead [class]="stickyHeader() ? 'ogrid-thead ogrid-sticky-header' : 'ogrid-thead'">
805
+ @for (row of headerRows(); track $index; let rowIdx = $index) {
806
+ <tr>
807
+ @if (rowIdx === headerRows().length - 1 && hasCheckboxCol()) {
808
+ <th scope="col" rowSpan="1" class="ogrid-checkbox-header">
809
+ <input
810
+ type="checkbox"
811
+ [checked]="allSelected()"
812
+ [indeterminate]="someSelected() && !allSelected()"
813
+ (change)="onSelectAllChangePrimeng($any($event.target).checked)"
814
+ aria-label="Select all rows"
815
+ />
816
+ </th>
817
+ }
818
+ @if (rowIdx === 0 && rowIdx < headerRows().length - 1 && hasCheckboxCol()) {
819
+ <th [attr.rowSpan]="headerRows().length - 1"></th>
820
+ }
821
+ @if (rowIdx === headerRows().length - 1 && hasRowNumbersCol()) {
822
+ <th scope="col" rowSpan="1" class="ogrid-row-number-header">
823
+ #
824
+ </th>
825
+ }
826
+ @if (rowIdx === 0 && rowIdx < headerRows().length - 1 && hasRowNumbersCol()) {
827
+ <th [attr.rowSpan]="headerRows().length - 1" class="ogrid-row-number-spacer"></th>
828
+ }
829
+ @for (cell of row; track cell.columnDef?.columnId ?? $index; let cellIdx = $index) {
830
+ @if (cell.isGroup) {
831
+ <th
832
+ [attr.colSpan]="cell.colSpan"
833
+ scope="colgroup"
834
+ class="ogrid-column-group-header"
835
+ >
836
+ {{ cell.label }}
837
+ </th>
838
+ } @else {
839
+ @let col = asColumnDef(cell.columnDef);
840
+ @let pinned = isPinned(col.columnId);
841
+ @let config = getFilterConfig(col);
842
+ @let sortState = getSortState(col.columnId);
843
+ @let ariaSort = sortState === 'asc' ? 'ascending' : sortState === 'desc' ? 'descending' : null;
844
+ <th
845
+ scope="col"
846
+ class="ogrid-header-cell"
847
+ [attr.data-column-id]="col.columnId"
848
+ [attr.aria-sort]="ariaSort"
849
+ [attr.rowSpan]="headerRows().length > 1 && rowIdx < headerRows().length - 1 ? headerRows().length - rowIdx : null"
850
+ [class.ogrid-th-pinned-left]="pinned === 'left'"
851
+ [class.ogrid-th-pinned-right]="pinned === 'right'"
852
+ [style.min-width.px]="getEffectiveMinWidth(col)"
853
+ [style.width.px]="getColumnWidth(col)"
854
+ [style.max-width.px]="getColumnWidth(col)"
855
+ [style.left.px]="pinned === 'left' ? getPinnedLeftOffset(col.columnId) : null"
856
+ [style.right.px]="pinned === 'right' ? getPinnedRightOffset(col.columnId) : null"
857
+ [style.cursor]="columnReorderService.isDragging() ? 'grabbing' : 'grab'"
858
+ (mousedown)="onHeaderMouseDown(col.columnId, $event)"
859
+ >
860
+ <div class="ogrid-header-content">
861
+ <ogrid-primeng-column-header-filter
862
+ [columnKey]="col.columnId"
863
+ [columnName]="col.name"
864
+ [filterType]="config.filterType"
865
+ [isSorted]="config.isSorted ?? false"
866
+ [isSortedDescending]="config.isSortedDescending ?? false"
867
+ [onSort]="config.onSort"
868
+ [selectedValues]="config.selectedValues"
869
+ [onFilterChange]="config.onFilterChange"
870
+ [options]="config.options ?? []"
871
+ [isLoadingOptions]="config.isLoadingOptions ?? false"
872
+ [textValue]="config.textValue ?? ''"
873
+ [onTextChange]="config.onTextChange"
874
+ [selectedUser]="config.selectedUser"
875
+ [onUserChange]="config.onUserChange"
876
+ [peopleSearch]="config.peopleSearch"
877
+ [dateValue]="config.dateValue"
878
+ [onDateChange]="config.onDateChange"
879
+ ></ogrid-primeng-column-header-filter>
880
+ @let colPinState = getPinState(col.columnId);
881
+ <column-header-menu
882
+ [columnId]="col.columnId"
883
+ [canPinLeft]="colPinState.canPinLeft"
884
+ [canPinRight]="colPinState.canPinRight"
885
+ [canUnpin]="colPinState.canUnpin"
886
+ [currentSort]="sortState"
887
+ [isSortable]="col.sortable !== false"
888
+ [isResizable]="col.resizable !== false"
889
+ [handlers]="getColumnMenuHandlersMemoized(col.columnId)"
890
+ />
891
+ </div>
892
+ <div
893
+ class="ogrid-resize-handle"
894
+ (mousedown)="onResizeStartPrimeng($event, col)"
895
+ [attr.aria-label]="'Resize ' + col.name"
896
+ ></div>
897
+ </th>
898
+ }
899
+ }
900
+ </tr>
901
+ }
902
+ </thead>
903
+
904
+ @if (!showEmptyInGrid()) {
905
+ <tbody>
906
+ @if (vsEnabled() && vsTopSpacerHeight() > 0) {
907
+ <tr [style.height.px]="vsTopSpacerHeight()"></tr>
908
+ }
909
+ @for (item of vsVisibleItems(); track trackByRowId($index, item); let localIdx = $index) {
910
+ @let rowIndex = vsStartIndex() + localIdx;
911
+ @let rowId = getRowIdInput(item);
912
+ @let isSelected = selectedRowIds().has(rowId);
913
+ <tr
914
+ [attr.data-row-id]="rowId"
915
+ [style.background]="isSelected ? 'var(--ogrid-selected-bg, #e8f0fe)' : null"
916
+ (click)="onRowClickPrimeng($event, item)"
917
+ >
918
+ @if (hasCheckboxCol()) {
919
+ <td
920
+ class="ogrid-checkbox-cell"
921
+ [attr.data-row-index]="rowIndex"
922
+ [attr.data-col-index]="0"
923
+ (click)="$event.stopPropagation()"
924
+ >
925
+ <input
926
+ type="checkbox"
927
+ [checked]="isSelected"
928
+ (change)="onRowCheckboxChangePrimeng(item, $any($event.target).checked, rowIndex, $event)"
929
+ [attr.aria-label]="'Select row ' + (rowIndex + 1)"
930
+ />
931
+ </td>
932
+ }
933
+ @if (hasRowNumbersCol()) {
934
+ <td class="ogrid-row-number-cell">
935
+
936
+ {{ rowNumberOffset() + rowIndex + 1 }}
937
+ </td>
938
+ }
939
+ @for (col of visibleCols(); track col.columnId; let colIdx = $index) {
940
+ @let pinned = isPinned(col.columnId);
941
+ <td
942
+ [attr.data-column-id]="col.columnId"
943
+ [class.ogrid-td-pinned-left]="pinned === 'left'"
944
+ [class.ogrid-td-pinned-right]="pinned === 'right'"
945
+ class="ogrid-data-cell"
946
+ [style.min-width.px]="getEffectiveMinWidth(col)"
947
+ [style.width.px]="getColumnWidth(col)"
948
+ [style.max-width.px]="getColumnWidth(col)"
949
+ [style.left.px]="pinned === 'left' ? getPinnedLeftOffset(col.columnId) : null"
950
+ [style.right.px]="pinned === 'right' ? getPinnedRightOffset(col.columnId) : null"
951
+ [style.text-align]="col.type === 'numeric' ? 'right' : col.type === 'boolean' ? 'center' : null"
952
+ >
953
+ @let descriptor = getCellDescriptor(item, col, rowIndex, colIdx);
954
+ @if (descriptor.mode === 'editing-inline') {
955
+ <div class="ogrid-editing-cell">
956
+ <ogrid-primeng-inline-cell-editor
957
+ [value]="descriptor.value"
958
+ [item]="item"
959
+ [column]="col"
960
+ [rowIndex]="rowIndex"
961
+ [editorType]="descriptor.editorType ?? 'text'"
962
+ (commit)="commitEdit(item, col.columnId, descriptor.value, $event, rowIndex, descriptor.globalColIndex)"
963
+ (cancel)="cancelEdit()"
964
+ ></ogrid-primeng-inline-cell-editor>
965
+ </div>
966
+ } @else if (descriptor.mode === 'editing-popover') {
967
+ @let editorProps = buildPopoverEditorProps(item, col, descriptor);
968
+ <ogrid-primeng-popover-cell-editor
969
+ [item]="item"
970
+ [column]="col"
971
+ [rowIndex]="rowIndex"
972
+ [globalColIndex]="descriptor.globalColIndex"
973
+ [displayValue]="descriptor.displayValue"
974
+ [editorProps]="editorProps"
975
+ [onCancel]="cancelEditHandler"
976
+ ></ogrid-primeng-popover-cell-editor>
977
+ } @else {
978
+ <div
979
+ [attr.data-row-index]="rowIndex"
980
+ [attr.data-col-index]="descriptor.globalColIndex"
981
+ (mousedown)="onCellMouseDown($event, rowIndex, descriptor.globalColIndex)"
982
+ (dblclick)="descriptor.canEditAny ? onCellDblClick(descriptor.rowId, col.columnId) : null"
983
+ (contextmenu)="onCellContextMenu($event)"
984
+ class="ogrid-cell-content"
985
+ [style.cursor]="descriptor.canEditAny ? 'cell' : 'default'"
986
+ [style.background]="descriptor.isInRange ? 'var(--ogrid-range-bg, rgba(33, 115, 70, 0.08))' : null"
987
+ [style.outline]="descriptor.isActive ? '2px solid var(--ogrid-selection, #217346)' : null"
988
+ [style.outline-offset]="descriptor.isActive ? '-2px' : null"
989
+ >
990
+ <span [style]="resolveCellStyleFn(col, item)">{{ resolveCellContent(col, item, descriptor.displayValue) }}</span>
991
+ @if (descriptor.canEditAny && descriptor.isSelectionEndCell) {
992
+ <div
993
+ (mousedown)="onFillHandleMouseDown($event)"
994
+ class="ogrid-fill-handle"
995
+ aria-label="Fill handle"
996
+ ></div>
997
+ }
998
+ </div>
999
+ }
1000
+ </td>
1001
+ }
1002
+ </tr>
1003
+ }
1004
+ @if (vsEnabled() && vsBottomSpacerHeight() > 0) {
1005
+ <tr [style.height.px]="vsBottomSpacerHeight()"></tr>
1006
+ }
1007
+ </tbody>
1008
+ }
1009
+ </table>
1010
+
1011
+ <ogrid-marching-ants-overlay
1012
+ [containerEl]="tableContainerEl()"
1013
+ [selectionRange]="state().interaction.selectionRange"
1014
+ [copyRange]="state().interaction.copyRange"
1015
+ [cutRange]="state().interaction.cutRange"
1016
+ [colOffset]="state().layout.colOffset"
1017
+ [columnSizingVersion]="columnSizingVersion()"
1018
+ [items]="items()"
1019
+ [visibleColumns]="propsVisibleColumns()"
1020
+ [columnOrder]="propsColumnOrder()"
1021
+ ></ogrid-marching-ants-overlay>
1022
+
1023
+ @if (showEmptyInGrid() && emptyState()) {
1024
+ <div class="ogrid-empty-container">
1025
+ <div>
1026
+ <div class="ogrid-empty-title">No results found</div>
1027
+ <ogrid-empty-state
1028
+ [message]="emptyState()?.message"
1029
+ [hasActiveFilters]="emptyState()?.hasActiveFilters ?? false"
1030
+ [render]="emptyState()?.render"
1031
+ (clearAll)="emptyState()?.onClearAll()"
1032
+ ></ogrid-empty-state>
1033
+ </div>
1034
+ </div>
1035
+ }
1036
+ </div>
1037
+ </div>
1038
+ </div>
1039
+ </div>
1040
+
1041
+ @if (columnReorderService.isDragging() && columnReorderService.dropIndicatorX() !== null) {
1042
+ <div class="ogrid-drop-indicator" [style.left.px]="columnReorderService.dropIndicatorX()"></div>
1043
+ }
1044
+
1045
+ @if (menuPosition()) {
1046
+ <ogrid-context-menu
1047
+ [x]="menuPosition()!.x"
1048
+ [y]="menuPosition()!.y"
1049
+ [hasSelection]="hasCellSelection()"
1050
+ [canUndoProp]="canUndo()"
1051
+ [canRedoProp]="canRedo()"
1052
+ [classNames]="contextMenuClasses"
1053
+ (copyAction)="handleCopy()"
1054
+ (cutAction)="handleCut()"
1055
+ (pasteAction)="handlePaste()"
1056
+ (selectAllAction)="handleSelectAllCells()"
1057
+ (undoAction)="onUndo()"
1058
+ (redoAction)="onRedo()"
1059
+ (closeAction)="closeContextMenu()"
1060
+ ></ogrid-context-menu>
1061
+ }
1062
+
1063
+ @let sbConfig = statusBarConfig();
1064
+ @if (sbConfig) {
1065
+ <ogrid-status-bar
1066
+ [totalCount]="sbConfig.totalCount"
1067
+ [filteredCount]="sbConfig.filteredCount"
1068
+ [selectedCount]="sbConfig.selectedCount ?? selectedRowIds().size"
1069
+ [selectedCellCount]="selectionCellCount()"
1070
+ [aggregation]="sbConfig.aggregation"
1071
+ [suppressRowCount]="sbConfig.suppressRowCount"
1072
+ [classNames]="statusBarClasses"
1073
+ ></ogrid-status-bar>
1074
+ }
1075
+
1076
+ @if (isLoading()) {
1077
+ <div class="ogrid-loading-overlay" aria-live="polite">
1078
+ <div class="ogrid-loading-content">
1079
+ <span class="ogrid-loading-text">{{ loadingMessage() }}</span>
1080
+ </div>
1081
+ </div>
1082
+ }
1083
+ </div>
1084
+ `,
1085
+ styles: [OGRID_THEME_VARS_CSS, `
1086
+ :host { display: block; }
1087
+ .ogrid-root {
1088
+ position: relative;
1089
+ flex: 1;
1090
+ min-height: 0;
1091
+ display: flex;
1092
+ flex-direction: column;
1093
+ overflow: hidden;
1094
+ }
1095
+ .ogrid-scroll-wrapper {
1096
+ flex: 1;
1097
+ min-height: 0;
1098
+ overflow: auto;
1099
+ position: relative;
1100
+ background: var(--ogrid-bg, #ffffff);
1101
+ color: var(--ogrid-fg, rgba(0, 0, 0, 0.87));
1102
+ }
1103
+ .ogrid-scroll-wrapper--loading-empty { min-height: 200px; }
1104
+ .ogrid-table-wrapper {
1105
+ position: relative;
1106
+ }
1107
+ .ogrid-table {
1108
+ width: var(--data-table-width, 100%);
1109
+ min-width: var(--data-table-min-width, 100%);
1110
+ border-collapse: collapse;
1111
+ table-layout: fixed;
1112
+ }
1113
+ .ogrid-table tbody tr { height: var(--ogrid-row-height, auto); }
1114
+ .ogrid-thead {
1115
+ z-index: 3;
1116
+ background: var(--ogrid-header-bg, #f5f5f5);
1117
+ }
1118
+ .ogrid-sticky-header { position: sticky; top: 0; }
1119
+ .ogrid-sticky-header .ogrid-checkbox-header,
1120
+ .ogrid-sticky-header .ogrid-row-number-header { position: sticky; top: 0; }
1121
+ .ogrid-checkbox-header {
1122
+ width: 48px;
1123
+ min-width: 48px;
1124
+ max-width: 48px;
1125
+ text-align: center;
1126
+ background: var(--ogrid-header-bg, #f5f5f5);
1127
+ border-bottom: 2px solid var(--ogrid-border, rgba(0, 0, 0, 0.12));
1128
+ z-index: 3;
1129
+ }
1130
+ .ogrid-row-number-header {
1131
+ width: 50px;
1132
+ min-width: 50px;
1133
+ max-width: 50px;
1134
+ text-align: center;
1135
+ font-weight: 600;
1136
+ background: var(--ogrid-header-bg, #f5f5f5);
1137
+ border-bottom: 2px solid var(--ogrid-border, rgba(0, 0, 0, 0.12));
1138
+ z-index: 3;
1139
+ }
1140
+ .ogrid-row-number-spacer {
1141
+ width: 50px;
1142
+ min-width: 50px;
1143
+ max-width: 50px;
1144
+ background: var(--ogrid-header-bg, #f5f5f5);
1145
+ }
1146
+ .ogrid-column-group-header {
1147
+ text-align: center;
1148
+ font-weight: 600;
1149
+ background: var(--ogrid-header-bg, #f5f5f5);
1150
+ border-bottom: 2px solid var(--ogrid-border, rgba(0, 0, 0, 0.12));
1151
+ padding: 6px 10px;
1152
+ }
1153
+ .ogrid-header-cell {
1154
+ background: var(--ogrid-header-bg, #f5f5f5);
1155
+ border-bottom: 2px solid var(--ogrid-border, rgba(0, 0, 0, 0.12));
1156
+ padding: 0;
1157
+ position: relative;
1158
+ user-select: none;
1159
+ }
1160
+ .ogrid-header-content {
1161
+ display: flex;
1162
+ align-items: center;
1163
+ gap: 4px;
1164
+ padding: 6px 10px;
1165
+ }
1166
+ .ogrid-resize-handle {
1167
+ position: absolute;
1168
+ top: 0;
1169
+ right: 0;
1170
+ bottom: 0;
1171
+ width: 4px;
1172
+ cursor: col-resize;
1173
+ }
1174
+ .ogrid-checkbox-cell {
1175
+ width: 48px;
1176
+ min-width: 48px;
1177
+ max-width: 48px;
1178
+ padding: 6px 4px;
1179
+ text-align: center;
1180
+ border-bottom: 1px solid var(--ogrid-border, rgba(0, 0, 0, 0.12));
1181
+ }
1182
+ .ogrid-row-number-cell {
1183
+ width: 50px;
1184
+ min-width: 50px;
1185
+ max-width: 50px;
1186
+ padding: 6px;
1187
+ text-align: center;
1188
+ font-weight: 600;
1189
+ font-variant-numeric: tabular-nums;
1190
+ color: var(--ogrid-fg-secondary, rgba(0, 0, 0, 0.6));
1191
+ background: var(--ogrid-header-bg, rgba(0, 0, 0, 0.04));
1192
+ border-bottom: 1px solid var(--ogrid-border, rgba(0, 0, 0, 0.12));
1193
+ position: sticky;
1194
+ left: 0;
1195
+ z-index: 3;
1196
+ }
1197
+ .ogrid-data-cell {
1198
+ padding: 0;
1199
+ border-bottom: 1px solid var(--ogrid-border, rgba(0, 0, 0, 0.12));
1200
+ position: relative;
1201
+ }
1202
+ .ogrid-cell-content {
1203
+ padding: 6px 10px;
1204
+ min-height: 20px;
1205
+ cursor: default;
1206
+ overflow: hidden;
1207
+ text-overflow: ellipsis;
1208
+ white-space: nowrap;
1209
+ }
1210
+ .ogrid-editing-cell {
1211
+ width: 100%; height: 100%; display: flex; align-items: center; box-sizing: border-box;
1212
+ outline: 2px solid var(--ogrid-selection-color, #217346); outline-offset: -1px;
1213
+ z-index: 2; position: relative; background: var(--ogrid-bg, #fff); overflow: visible; padding: 0;
1214
+ }
1215
+ .ogrid-scroll-wrapper [data-drag-range] { background: var(--ogrid-range-bg, rgba(33, 115, 70, 0.12)) !important; }
1216
+ .ogrid-fill-handle {
1217
+ position: absolute;
1218
+ bottom: -3px;
1219
+ right: -3px;
1220
+ width: 7px;
1221
+ height: 7px;
1222
+ background: var(--ogrid-selection, #217346);
1223
+ cursor: crosshair;
1224
+ z-index: 2;
1225
+ }
1226
+ .ogrid-empty-container {
1227
+ display: flex;
1228
+ align-items: center;
1229
+ justify-content: center;
1230
+ padding: 48px 24px;
1231
+ text-align: center;
1232
+ color: var(--ogrid-fg-muted, rgba(0, 0, 0, 0.5));
1233
+ }
1234
+ .ogrid-empty-title {
1235
+ font-weight: 600;
1236
+ margin-bottom: 8px;
1237
+ }
1238
+ .ogrid-loading-overlay {
1239
+ position: absolute;
1240
+ inset: 0;
1241
+ display: flex;
1242
+ align-items: center;
1243
+ justify-content: center;
1244
+ background: var(--ogrid-loading-overlay, rgba(255, 255, 255, 0.7));
1245
+ z-index: 10;
1246
+ }
1247
+ .ogrid-loading-content {
1248
+ display: flex;
1249
+ align-items: center;
1250
+ gap: 8px;
1251
+ color: var(--ogrid-fg, #242424);
1252
+ }
1253
+ .ogrid-loading-text {
1254
+ font-size: 14px;
1255
+ }
1256
+ .loading-dimmed {
1257
+ opacity: 0.5;
1258
+ pointer-events: none;
1259
+ }
1260
+ .ogrid-drop-indicator {
1261
+ position: absolute;
1262
+ top: 0;
1263
+ bottom: 0;
1264
+ width: 3px;
1265
+ background: var(--ogrid-primary, #217346);
1266
+ pointer-events: none;
1267
+ z-index: 100;
1268
+ transition: left 0.05s;
1269
+ }
1270
+ .ogrid-th-pinned-left {
1271
+ position: sticky;
1272
+ top: 0;
1273
+ left: 0;
1274
+ z-index: 10;
1275
+ background: var(--ogrid-header-bg, #f5f5f5);
1276
+ border-right: 1px solid var(--ogrid-border, rgba(0, 0, 0, 0.12));
1277
+ box-shadow: 2px 0 4px -1px rgba(0, 0, 0, 0.1);
1278
+ }
1279
+ .ogrid-th-pinned-right {
1280
+ position: sticky;
1281
+ top: 0;
1282
+ right: 0;
1283
+ z-index: 10;
1284
+ background: var(--ogrid-header-bg, #f5f5f5);
1285
+ border-left: 1px solid var(--ogrid-border, rgba(0, 0, 0, 0.12));
1286
+ box-shadow: -2px 0 4px -1px rgba(0, 0, 0, 0.1);
1287
+ }
1288
+ .ogrid-td-pinned-left {
1289
+ position: sticky;
1290
+ left: 0;
1291
+ z-index: 5;
1292
+ background: var(--ogrid-bg, #fff);
1293
+ border-right: 1px solid var(--ogrid-border, rgba(0, 0, 0, 0.12));
1294
+ box-shadow: 2px 0 4px -1px rgba(0, 0, 0, 0.1);
1295
+ }
1296
+ .ogrid-td-pinned-right {
1297
+ position: sticky;
1298
+ right: 0;
1299
+ z-index: 5;
1300
+ background: var(--ogrid-bg, #fff);
1301
+ border-left: 1px solid var(--ogrid-border, rgba(0, 0, 0, 0.12));
1302
+ box-shadow: -2px 0 4px -1px rgba(0, 0, 0, 0.1);
1303
+ }
1304
+ ::ng-deep th:focus-visible,
1305
+ ::ng-deep td:focus-visible {
1306
+ outline: 2px solid var(--primary-color, #6366f1);
1307
+ outline-offset: -2px;
1308
+ z-index: 11;
1309
+ }
1310
+ ::ng-deep .p-button:focus-visible,
1311
+ ::ng-deep button:focus-visible {
1312
+ outline: 2px solid var(--primary-color, #6366f1);
1313
+ outline-offset: 2px;
1314
+ }
1315
+
1316
+ /* Context menu */
1317
+ .ogrid-context-menu {
1318
+ position: fixed;
1319
+ z-index: 1000;
1320
+ min-width: 160px;
1321
+ padding: 4px 0;
1322
+ background: var(--ogrid-bg, #fff);
1323
+ border: 1px solid var(--ogrid-border, rgba(0, 0, 0, 0.12));
1324
+ border-radius: 6px;
1325
+ box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);
1326
+ }
1327
+ .ogrid-context-menu-item {
1328
+ display: flex;
1329
+ align-items: center;
1330
+ justify-content: space-between;
1331
+ gap: 24px;
1332
+ width: 100%;
1333
+ padding: 6px 12px;
1334
+ border: none;
1335
+ background: none;
1336
+ font-size: 13px;
1337
+ text-align: left;
1338
+ cursor: pointer;
1339
+ color: var(--ogrid-fg, rgba(0, 0, 0, 0.87));
1340
+ }
1341
+ .ogrid-context-menu-item:hover:not(:disabled) {
1342
+ background: var(--ogrid-hover-bg, rgba(0, 0, 0, 0.04));
1343
+ }
1344
+ .ogrid-context-menu-item:disabled {
1345
+ opacity: 0.5;
1346
+ cursor: not-allowed;
1347
+ }
1348
+ .ogrid-context-menu-item-label {
1349
+ flex: 1;
1350
+ }
1351
+ .ogrid-context-menu-item-shortcut {
1352
+ color: var(--ogrid-fg-muted, rgba(0, 0, 0, 0.5));
1353
+ font-size: 0.85em;
1354
+ }
1355
+ .ogrid-context-menu-divider {
1356
+ height: 1px;
1357
+ margin: 4px 0;
1358
+ background: var(--ogrid-border, rgba(0, 0, 0, 0.12));
1359
+ }
1360
+ ::ng-deep .p-checkbox:focus-visible {
1361
+ outline: 2px solid var(--primary-color, #6366f1);
1362
+ outline-offset: 2px;
1363
+ }
1364
+
1365
+ /* PrimeNG Menu popup overrides \u2014 must use !important to win over PrimeNG's CSS-variable-based defaults */
1366
+ .p-menu {
1367
+ background: var(--ogrid-bg, #ffffff) !important;
1368
+ color: var(--ogrid-fg, rgba(0, 0, 0, 0.87)) !important;
1369
+ border: 1px solid var(--ogrid-border, rgba(0, 0, 0, 0.12)) !important;
1370
+ border-radius: 4px !important;
1371
+ padding: 4px 0 !important;
1372
+ }
1373
+ .p-menu-overlay {
1374
+ 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;
1375
+ }
1376
+ .p-menu-item-content {
1377
+ color: var(--ogrid-fg, rgba(0, 0, 0, 0.87)) !important;
1378
+ }
1379
+ .p-menu-item-link {
1380
+ color: var(--ogrid-fg, rgba(0, 0, 0, 0.87)) !important;
1381
+ padding: 6px 12px !important;
1382
+ }
1383
+ .p-menu-item-label {
1384
+ color: var(--ogrid-fg, rgba(0, 0, 0, 0.87)) !important;
1385
+ font-size: 0.875rem !important;
1386
+ }
1387
+ .p-menu-item:not(.p-disabled) .p-menu-item-content:hover {
1388
+ background: var(--ogrid-hover-bg, rgba(0, 0, 0, 0.04)) !important;
1389
+ color: var(--ogrid-fg, rgba(0, 0, 0, 0.87)) !important;
1390
+ }
1391
+ .p-menu-separator {
1392
+ border-color: var(--ogrid-border, rgba(0, 0, 0, 0.12)) !important;
1393
+ margin: 4px 0 !important;
1394
+ }
1395
+ `]
1396
+ })
1397
+ ], DataGridTableComponent);
1398
+ var ColumnChooserComponent = class extends BaseColumnChooserComponent {
1399
+ // PrimeNG uses 'open' instead of 'isOpen'
1400
+ get open() {
1401
+ return this.isOpen;
1402
+ }
1403
+ };
1404
+ ColumnChooserComponent = __decorateClass([
1405
+ Component({
1406
+ selector: "ogrid-primeng-column-chooser",
1407
+ standalone: true,
1408
+ changeDetection: ChangeDetectionStrategy.OnPush,
1409
+ template: `
1410
+ <div style="position:relative;display:inline-block">
1411
+ <button
1412
+ type="button"
1413
+ class="p-button p-button-text p-button-sm"
1414
+ (click)="open.set(!open())"
1415
+ [attr.aria-expanded]="open()"
1416
+ aria-haspopup="listbox"
1417
+ style="display:flex;align-items:center;gap:6px;font-size:13px"
1418
+ >
1419
+ <span aria-hidden>&#9881;</span>
1420
+ <span>Column Visibility ({{ visibleCount() }} of {{ totalCount() }})</span>
1421
+ <span aria-hidden>{{ open() ? '\u25B2' : '\u25BC' }}</span>
1422
+ </button>
1423
+
1424
+ @if (open()) {
1425
+ <div
1426
+ style="position:absolute;right:0;top:100%;z-index:1000;min-width:220px;max-height:320px;overflow-y:auto;background:var(--ogrid-bg, #fff);border:1px solid var(--ogrid-border, rgba(0, 0, 0, 0.12));border-radius:6px;box-shadow:0 2px 8px rgba(0,0,0,0.12);padding:8px 0"
1427
+ >
1428
+ <div style="padding:4px 12px;font-weight:600;font-size:12px;color:var(--ogrid-fg-secondary, rgba(0, 0, 0, 0.6))">
1429
+ Select Columns ({{ visibleCount() }} of {{ totalCount() }})
1430
+ </div>
1431
+ @for (col of columns; track col.columnId) {
1432
+ <label style="display:flex;align-items:center;gap:8px;padding:4px 12px;cursor:pointer;font-size:13px">
1433
+ <input
1434
+ type="checkbox"
1435
+ [checked]="visibleColumns.has(col.columnId)"
1436
+ (change)="onToggle(col.columnId, $any($event.target).checked)"
1437
+ [disabled]="col.required === true"
1438
+ />
1439
+ {{ col.name }}
1440
+ </label>
1441
+ }
1442
+ <div style="display:flex;gap:4px;padding:8px 12px;border-top:1px solid var(--ogrid-border, rgba(0, 0, 0, 0.12));margin-top:4px">
1443
+ <button
1444
+ type="button"
1445
+ class="p-button p-button-text p-button-sm"
1446
+ (click)="onClearAll()"
1447
+ style="flex:1;font-size:12px"
1448
+ >Clear All</button>
1449
+ <button
1450
+ type="button"
1451
+ class="p-button p-button-text p-button-sm"
1452
+ (click)="onSelectAll()"
1453
+ style="flex:1;font-size:12px"
1454
+ >Select All</button>
1455
+ </div>
1456
+ </div>
1457
+ }
1458
+ </div>
1459
+ `
1460
+ })
1461
+ ], ColumnChooserComponent);
1462
+ var PaginationControlsComponent = class extends BasePaginationControlsComponent {
1463
+ };
1464
+ PaginationControlsComponent = __decorateClass([
1465
+ Component({
1466
+ selector: "ogrid-primeng-pagination-controls",
1467
+ standalone: true,
1468
+ changeDetection: ChangeDetectionStrategy.OnPush,
1469
+ template: `
1470
+ @if (vm()) {
1471
+ <div style="display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap;font-size:13px;color:var(--ogrid-fg, #242424)">
1472
+ <div>
1473
+ Showing {{ vm()!.startItem }} to {{ vm()!.endItem }} of {{ totalCount.toLocaleString() }} {{ labelPlural() }}
1474
+ </div>
1475
+
1476
+ <div style="display:flex;align-items:center;gap:4px" role="navigation" aria-label="Pagination">
1477
+ <button
1478
+ type="button"
1479
+ class="p-button p-button-text p-button-sm"
1480
+ [disabled]="currentPage === 1"
1481
+ (click)="pageChange.emit(1)"
1482
+ aria-label="First page"
1483
+ style="min-width:32px;padding:4px 8px"
1484
+ >&laquo;</button>
1485
+ <button
1486
+ type="button"
1487
+ class="p-button p-button-text p-button-sm"
1488
+ [disabled]="currentPage === 1"
1489
+ (click)="pageChange.emit(currentPage - 1)"
1490
+ aria-label="Previous page"
1491
+ style="min-width:32px;padding:4px 8px"
1492
+ >&lsaquo;</button>
1493
+
1494
+ @if (vm()!.showStartEllipsis) {
1495
+ <button
1496
+ type="button"
1497
+ class="p-button p-button-text p-button-sm"
1498
+ (click)="pageChange.emit(1)"
1499
+ aria-label="Page 1"
1500
+ style="min-width:32px;padding:4px 8px"
1501
+ >1</button>
1502
+ <span aria-hidden style="padding:0 4px">&hellip;</span>
1503
+ }
1504
+
1505
+ @for (pageNum of vm()!.pageNumbers; track pageNum) {
1506
+ <button
1507
+ type="button"
1508
+ class="p-button p-button-sm"
1509
+ [class.p-button-outlined]="currentPage !== pageNum"
1510
+ (click)="pageChange.emit(pageNum)"
1511
+ [attr.aria-label]="'Page ' + pageNum"
1512
+ [attr.aria-current]="currentPage === pageNum ? 'page' : null"
1513
+ style="min-width:32px;padding:4px 8px"
1514
+ >{{ pageNum }}</button>
1515
+ }
1516
+
1517
+ @if (vm()!.showEndEllipsis) {
1518
+ <span aria-hidden style="padding:0 4px">&hellip;</span>
1519
+ <button
1520
+ type="button"
1521
+ class="p-button p-button-text p-button-sm"
1522
+ (click)="pageChange.emit(vm()!.totalPages)"
1523
+ [attr.aria-label]="'Page ' + vm()!.totalPages"
1524
+ style="min-width:32px;padding:4px 8px"
1525
+ >{{ vm()!.totalPages }}</button>
1526
+ }
1527
+
1528
+ <button
1529
+ type="button"
1530
+ class="p-button p-button-text p-button-sm"
1531
+ [disabled]="currentPage >= vm()!.totalPages"
1532
+ (click)="pageChange.emit(currentPage + 1)"
1533
+ aria-label="Next page"
1534
+ style="min-width:32px;padding:4px 8px"
1535
+ >&rsaquo;</button>
1536
+ <button
1537
+ type="button"
1538
+ class="p-button p-button-text p-button-sm"
1539
+ [disabled]="currentPage >= vm()!.totalPages"
1540
+ (click)="pageChange.emit(vm()!.totalPages)"
1541
+ aria-label="Last page"
1542
+ style="min-width:32px;padding:4px 8px"
1543
+ >&raquo;</button>
1544
+ </div>
1545
+
1546
+ <div style="display:flex;align-items:center;gap:6px">
1547
+ <span style="font-size:12px">Rows</span>
1548
+ <select
1549
+ [value]="'' + pageSize"
1550
+ (change)="onPageSizeChange($any($event.target).value)"
1551
+ aria-label="Rows per page"
1552
+ style="padding:4px 6px;border:1px solid var(--ogrid-border, rgba(0, 0, 0, 0.12));border-radius:4px;background:var(--ogrid-bg, #fff);color:var(--ogrid-fg, #242424)"
1553
+ >
1554
+ @for (opt of vm()!.pageSizeOptions; track opt) {
1555
+ <option [value]="opt">{{ opt }}</option>
1556
+ }
1557
+ </select>
1558
+ </div>
1559
+ </div>
1560
+ }
1561
+ `
1562
+ })
1563
+ ], PaginationControlsComponent);
1564
+
1565
+ // src/ogrid/ogrid.component.ts
1566
+ var OGridComponent = class {
1567
+ constructor() {
1568
+ this.service = inject(OGridService);
1569
+ this.propsSignal = signal(void 0);
1570
+ // Stable callback references (avoid re-creating every template eval)
1571
+ this.onColumnSortFn = (columnKey, direction) => this.service.handleSort(columnKey, direction);
1572
+ this.onColumnResizedFn = (columnId, width) => this.service.handleColumnResized(columnId, width);
1573
+ this.onColumnPinnedFn = (columnId, pinned) => this.service.handleColumnPinned(columnId, pinned);
1574
+ this.onSelectionChangeFn = (event) => this.service.handleSelectionChange(event);
1575
+ this.onFilterChangeFn = (key, value) => this.service.handleFilterChange(key, value);
1576
+ this.clearAllFiltersFn = () => this.service.setFilters({});
1577
+ this.emptyStateComputed = computed(() => ({
1578
+ hasActiveFilters: this.service.hasActiveFilters(),
1579
+ onClearAll: this.clearAllFiltersFn,
1580
+ message: this.service.emptyState()?.message,
1581
+ render: this.service.emptyState()?.render
1582
+ }));
1583
+ effect(() => {
1584
+ const p = this.propsSignal();
1585
+ if (p) this.service.configure(p);
1586
+ });
1587
+ }
1588
+ set props(value) {
1589
+ this.propsSignal.set(value);
1590
+ }
1591
+ get showToolbar() {
1592
+ return this.service.columnChooserPlacement() === "toolbar" || this.service.toolbar() != null || this.service.fullScreen();
1593
+ }
1594
+ get emptyStateObj() {
1595
+ return this.emptyStateComputed();
1596
+ }
1597
+ onPageSizeChange(size) {
1598
+ this.service.setPageSize(size);
1599
+ }
1600
+ };
1601
+ __decorateClass([
1602
+ Input({ required: true })
1603
+ ], OGridComponent.prototype, "props", 1);
1604
+ OGridComponent = __decorateClass([
1605
+ Component({
1606
+ selector: "ogrid-primeng",
1607
+ standalone: true,
1608
+ imports: [
1609
+ OGridLayoutComponent,
1610
+ DataGridTableComponent,
1611
+ ColumnChooserComponent,
1612
+ PaginationControlsComponent
1613
+ ],
1614
+ changeDetection: ChangeDetectionStrategy.OnPush,
1615
+ providers: [OGridService],
1616
+ styles: [`:host { display: block; height: 100%; }`],
1617
+ template: `
1618
+ <ogrid-layout
1619
+ [className]="service.className()"
1620
+ [hasToolbar]="showToolbar"
1621
+ [hasToolbarBelow]="false"
1622
+ [hasPagination]="true"
1623
+ [sideBar]="service.sideBarProps()"
1624
+ [fullScreen]="service.fullScreen()"
1625
+ >
1626
+ <ng-content select="[toolbar]" toolbar></ng-content>
1627
+
1628
+ <div toolbarEnd>
1629
+ @if (service.columnChooserPlacement() === 'toolbar') {
1630
+ <ogrid-primeng-column-chooser
1631
+ [columns]="service.columnChooser().columns"
1632
+ [visibleColumns]="service.columnChooser().visibleColumns"
1633
+ (visibilityChange)="service.handleVisibilityChange($event.columnKey, $event.visible)"
1634
+ ></ogrid-primeng-column-chooser>
1635
+ }
1636
+ </div>
1637
+
1638
+ <ogrid-primeng-datagrid-table
1639
+ [items]="service.displayItems()"
1640
+ [columns]="service.columnsProp()"
1641
+ [getRowId]="service.getRowId()"
1642
+ [sortBy]="service.sort().field"
1643
+ [sortDirection]="service.sort().direction"
1644
+ [onColumnSort]="onColumnSortFn"
1645
+ [visibleColumns]="service.visibleColumns()"
1646
+ [columnOrder]="service.columnOrder()"
1647
+ [onColumnOrderChange]="service.onColumnOrderChange()"
1648
+ [onColumnResized]="onColumnResizedFn"
1649
+ [onColumnPinned]="onColumnPinnedFn"
1650
+ [editable]="service.editable()"
1651
+ [cellSelection]="service.cellSelection()"
1652
+ [onCellValueChanged]="service.onCellValueChanged()"
1653
+ [onUndo]="service.onUndo()"
1654
+ [onRedo]="service.onRedo()"
1655
+ [canUndo]="service.canUndo()"
1656
+ [canRedo]="service.canRedo()"
1657
+ [rowSelection]="service.rowSelection()"
1658
+ [selectedRows]="service.effectiveSelectedRows()"
1659
+ [onSelectionChange]="onSelectionChangeFn"
1660
+ [statusBar]="service.statusBarConfig()"
1661
+ [isLoading]="service.isLoadingResolved()"
1662
+ [filters]="service.filters()"
1663
+ [onFilterChange]="onFilterChangeFn"
1664
+ [filterOptions]="service.clientFilterOptions()"
1665
+ [loadingFilterOptions]="service.loadingFilterOptions()"
1666
+ [peopleSearch]="service.dataSource()?.searchPeople?.bind(service.dataSource())"
1667
+ [getUserByEmail]="service.dataSource()?.getUserByEmail?.bind(service.dataSource())"
1668
+ [layoutMode]="service.layoutMode()"
1669
+ [suppressHorizontalScroll]="service.suppressHorizontalScroll()"
1670
+ [stickyHeaderInput]="service.stickyHeader()"
1671
+ [columnReorder]="service.columnReorder()"
1672
+ [aria-label]="service.ariaLabel()"
1673
+ [aria-labelledby]="service.ariaLabelledBy()"
1674
+ [emptyState]="emptyStateObj"
1675
+ ></ogrid-primeng-datagrid-table>
1676
+
1677
+ <div pagination>
1678
+ <ogrid-primeng-pagination-controls
1679
+ [currentPage]="service.pagination().page"
1680
+ [pageSize]="service.pagination().pageSize"
1681
+ [totalCount]="service.pagination().displayTotalCount"
1682
+ [pageSizeOptions]="service.pageSizeOptions()"
1683
+ [entityLabelPlural]="service.entityLabelPlural()"
1684
+ (pageChange)="service.setPage($event)"
1685
+ (pageSizeChange)="onPageSizeChange($event)"
1686
+ ></ogrid-primeng-pagination-controls>
1687
+ </div>
1688
+ </ogrid-layout>
1689
+ `
1690
+ })
1691
+ ], OGridComponent);
1692
+
1693
+ export { ColumnChooserComponent, ColumnHeaderFilterComponent, ColumnHeaderMenuComponent, DataGridTableComponent, InlineCellEditorComponent, OGridComponent, PaginationControlsComponent, PopoverCellEditorComponent };