@alaarab/ogrid-angular-primeng 2.0.2 → 2.0.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.
@@ -0,0 +1,136 @@
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, Output, EventEmitter, ViewChild } from '@angular/core';
8
+ import { ButtonModule } from 'primeng/button';
9
+ import { MenuModule } from 'primeng/menu';
10
+ import { COLUMN_HEADER_MENU_ITEMS } from '@alaarab/ogrid-core';
11
+ /**
12
+ * Column header dropdown menu for pin/unpin actions.
13
+ * Uses PrimeNG Menu component.
14
+ */
15
+ let ColumnHeaderMenuComponent = class ColumnHeaderMenuComponent {
16
+ constructor() {
17
+ this.pinLeft = new EventEmitter();
18
+ this.pinRight = new EventEmitter();
19
+ this.unpin = new EventEmitter();
20
+ this._canPinLeft = true;
21
+ this._canPinRight = true;
22
+ this._canUnpin = false;
23
+ this.menuModel = [];
24
+ }
25
+ set canPinLeft(value) {
26
+ this._canPinLeft = value;
27
+ this.updateMenuModel();
28
+ }
29
+ set canPinRight(value) {
30
+ this._canPinRight = value;
31
+ this.updateMenuModel();
32
+ }
33
+ set canUnpin(value) {
34
+ this._canUnpin = value;
35
+ this.updateMenuModel();
36
+ }
37
+ ngOnInit() {
38
+ this.updateMenuModel();
39
+ }
40
+ updateMenuModel() {
41
+ this.menuModel = [
42
+ {
43
+ label: COLUMN_HEADER_MENU_ITEMS[0].label,
44
+ disabled: !this._canPinLeft,
45
+ command: () => this.handlePinLeft(),
46
+ },
47
+ {
48
+ label: COLUMN_HEADER_MENU_ITEMS[1].label,
49
+ disabled: !this._canPinRight,
50
+ command: () => this.handlePinRight(),
51
+ },
52
+ {
53
+ label: COLUMN_HEADER_MENU_ITEMS[2].label,
54
+ disabled: !this._canUnpin,
55
+ command: () => this.handleUnpin(),
56
+ },
57
+ ];
58
+ }
59
+ handlePinLeft() {
60
+ if (this._canPinLeft) {
61
+ this.pinLeft.emit();
62
+ }
63
+ }
64
+ handlePinRight() {
65
+ if (this._canPinRight) {
66
+ this.pinRight.emit();
67
+ }
68
+ }
69
+ handleUnpin() {
70
+ if (this._canUnpin) {
71
+ this.unpin.emit();
72
+ }
73
+ }
74
+ };
75
+ __decorate([
76
+ Input()
77
+ ], ColumnHeaderMenuComponent.prototype, "columnId", void 0);
78
+ __decorate([
79
+ Input()
80
+ ], ColumnHeaderMenuComponent.prototype, "canPinLeft", null);
81
+ __decorate([
82
+ Input()
83
+ ], ColumnHeaderMenuComponent.prototype, "canPinRight", null);
84
+ __decorate([
85
+ Input()
86
+ ], ColumnHeaderMenuComponent.prototype, "canUnpin", null);
87
+ __decorate([
88
+ Output()
89
+ ], ColumnHeaderMenuComponent.prototype, "pinLeft", void 0);
90
+ __decorate([
91
+ Output()
92
+ ], ColumnHeaderMenuComponent.prototype, "pinRight", void 0);
93
+ __decorate([
94
+ Output()
95
+ ], ColumnHeaderMenuComponent.prototype, "unpin", void 0);
96
+ __decorate([
97
+ ViewChild('menu')
98
+ ], ColumnHeaderMenuComponent.prototype, "menu", void 0);
99
+ ColumnHeaderMenuComponent = __decorate([
100
+ Component({
101
+ selector: 'column-header-menu',
102
+ standalone: true,
103
+ imports: [ButtonModule, MenuModule],
104
+ template: `
105
+ <button
106
+ pButton
107
+ type="button"
108
+ icon="pi pi-ellipsis-v"
109
+ class="p-button-text p-button-sm column-header-menu-trigger"
110
+ (click)="menu.toggle($event)"
111
+ [attr.aria-label]="'Column options for ' + columnId"
112
+ ></button>
113
+
114
+ <p-menu
115
+ #menu
116
+ [model]="menuModel"
117
+ [popup]="true"
118
+ appendTo="body"
119
+ ></p-menu>
120
+ `,
121
+ styles: [`
122
+ .column-header-menu-trigger {
123
+ opacity: 0;
124
+ transition: opacity 0.15s;
125
+ padding: 0.25rem;
126
+ min-width: auto;
127
+ }
128
+
129
+ :host:hover .column-header-menu-trigger,
130
+ .column-header-menu-trigger:focus {
131
+ opacity: 1;
132
+ }
133
+ `],
134
+ })
135
+ ], ColumnHeaderMenuComponent);
136
+ export { ColumnHeaderMenuComponent };
@@ -6,12 +6,15 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
6
6
  };
7
7
  import { Component, input, inject, signal, computed, effect, viewChild, ChangeDetectionStrategy, } from '@angular/core';
8
8
  import { CommonModule } from '@angular/common';
9
- import { DataGridStateService, StatusBarComponent, GridContextMenuComponent, MarchingAntsOverlayComponent, EmptyStateComponent, DEFAULT_MIN_COLUMN_WIDTH, buildHeaderRows, getCellValue, } from '@alaarab/ogrid-angular';
9
+ import { DataGridStateService, ColumnReorderService, VirtualScrollService, StatusBarComponent, GridContextMenuComponent, MarchingAntsOverlayComponent, EmptyStateComponent, DEFAULT_MIN_COLUMN_WIDTH, buildHeaderRows, getCellValue, getHeaderFilterConfig, resolveCellDisplayContent, resolveCellStyle, } from '@alaarab/ogrid-angular';
10
10
  import { ColumnHeaderFilterComponent } from '../column-header-filter/column-header-filter.component';
11
+ import { ColumnHeaderMenuComponent } from '../column-header-menu/column-header-menu.component';
11
12
  import { InlineCellEditorComponent } from './inline-cell-editor.component';
12
13
  let DataGridTableComponent = class DataGridTableComponent {
13
14
  constructor() {
14
15
  this.stateService = inject(DataGridStateService);
16
+ this.columnReorderService = new ColumnReorderService();
17
+ this.virtualScrollService = new VirtualScrollService();
15
18
  this.wrapperRef = viewChild('wrapper');
16
19
  this.tableContainerRef = viewChild('tableContainer');
17
20
  // Inputs mapped from IOGridDataGridProps
@@ -55,6 +58,9 @@ let DataGridTableComponent = class DataGridTableComponent {
55
58
  this.onCellError = input(undefined);
56
59
  this.ariaLabel = input(undefined, { alias: 'aria-label' });
57
60
  this.ariaLabelledBy = input(undefined, { alias: 'aria-labelledby' });
61
+ this.showRowNumbers = input(false);
62
+ this.currentPage = input(1);
63
+ this.pageSize = input(25);
58
64
  this.defaultMinWidth = DEFAULT_MIN_COLUMN_WIDTH;
59
65
  this.statusBarClasses = {
60
66
  statusBar: 'ogrid-status-bar',
@@ -69,9 +75,11 @@ let DataGridTableComponent = class DataGridTableComponent {
69
75
  this.resizeStartWidth = 0;
70
76
  // Last shift state for row checkbox
71
77
  this.lastMouseShift = false;
78
+ this.columnSizingVersion = signal(0);
72
79
  this.state = computed(() => this.stateService.getState());
73
80
  this.tableContainerEl = computed(() => this.tableContainerRef()?.nativeElement ?? null);
74
81
  this.resolvedAriaLabel = computed(() => this.ariaLabel() ?? (this.ariaLabelledBy() ? undefined : 'Data grid'));
82
+ this.rowNumberOffset = computed(() => this.state().layout.hasRowNumbersCol ? (this.currentPage() - 1) * this.pageSize() : 0);
75
83
  this.headerRows = computed(() => buildHeaderRows(this.columns(), this.visibleColumns()));
76
84
  this.allowOverflowX = computed(() => {
77
85
  const s = this.state();
@@ -113,6 +121,19 @@ let DataGridTableComponent = class DataGridTableComponent {
113
121
  effect(() => {
114
122
  const el = this.wrapperRef()?.nativeElement ?? null;
115
123
  this.stateService.wrapperEl.set(el);
124
+ this.columnReorderService.wrapperEl.set(el);
125
+ });
126
+ // Wire column reorder service inputs
127
+ effect(() => {
128
+ const cols = this.state().layout.visibleCols;
129
+ this.columnReorderService.columns.set(cols);
130
+ this.columnReorderService.columnOrder.set(this.columnOrder());
131
+ this.columnReorderService.onColumnOrderChange.set(this.onColumnOrderChange());
132
+ this.columnReorderService.enabled.set(!!this.onColumnOrderChange());
133
+ });
134
+ // Wire virtual scroll service inputs
135
+ effect(() => {
136
+ this.virtualScrollService.totalRows.set(this.items().length);
116
137
  });
117
138
  // Initialize column sizing from initial widths
118
139
  effect(() => {
@@ -133,79 +154,17 @@ let DataGridTableComponent = class DataGridTableComponent {
133
154
  }
134
155
  getFilterConfig(col) {
135
156
  const s = this.state();
136
- const hfi = s.viewModels.headerFilterInput;
137
- const filterable = col.filterable && typeof col.filterable === 'object' ? col.filterable : null;
138
- const filterType = filterable?.type ?? 'none';
139
- const filterField = filterable?.filterField ?? col.columnId;
140
- const sortable = col.sortable !== false;
141
- const filterValue = hfi.filters[filterField];
142
- const base = {
143
- filterType,
144
- isSorted: hfi.sortBy === col.columnId,
145
- isSortedDescending: hfi.sortBy === col.columnId && hfi.sortDirection === 'desc',
146
- onSort: sortable ? () => hfi.onColumnSort(col.columnId) : undefined,
147
- };
148
- if (filterType === 'text') {
149
- return {
150
- ...base,
151
- textValue: filterValue?.type === 'text' ? filterValue.value : '',
152
- onTextChange: (v) => hfi.onFilterChange(filterField, v.trim() ? { type: 'text', value: v } : undefined),
153
- };
154
- }
155
- if (filterType === 'multiSelect') {
156
- return {
157
- ...base,
158
- options: hfi.filterOptions[filterField] ?? [],
159
- isLoadingOptions: hfi.loadingFilterOptions[filterField] ?? false,
160
- selectedValues: filterValue?.type === 'multiSelect' ? filterValue.value : [],
161
- onFilterChange: (values) => hfi.onFilterChange(filterField, values.length ? { type: 'multiSelect', value: values } : undefined),
162
- };
163
- }
164
- if (filterType === 'people') {
165
- return {
166
- ...base,
167
- selectedUser: filterValue?.type === 'people' ? filterValue.value : undefined,
168
- onUserChange: (u) => hfi.onFilterChange(filterField, u ? { type: 'people', value: u } : undefined),
169
- peopleSearch: hfi.peopleSearch,
170
- };
171
- }
172
- if (filterType === 'date') {
173
- return {
174
- ...base,
175
- dateValue: filterValue?.type === 'date' ? filterValue.value : undefined,
176
- onDateChange: (v) => hfi.onFilterChange(filterField, v ? { type: 'date', value: v } : undefined),
177
- };
178
- }
179
- return base;
157
+ return getHeaderFilterConfig(col, s.viewModels.headerFilterInput);
180
158
  }
181
159
  getCellValueFn(item, col) {
182
160
  return getCellValue(item, col);
183
161
  }
184
162
  resolveCellDisplay(col, item) {
185
- if (col.renderCell && typeof col.renderCell === 'function') {
186
- const result = col.renderCell(item);
187
- return result ?? '';
188
- }
189
163
  const value = getCellValue(item, col);
190
- if (col.valueFormatter)
191
- return String(col.valueFormatter(value, item) ?? '');
192
- if (value == null)
193
- return '';
194
- if (col.type === 'date') {
195
- const d = new Date(String(value));
196
- if (!Number.isNaN(d.getTime()))
197
- return d.toLocaleDateString();
198
- }
199
- if (col.type === 'boolean')
200
- return value ? 'True' : 'False';
201
- return String(value);
164
+ return resolveCellDisplayContent(col, item, value);
202
165
  }
203
166
  getCellStyleObj(col, item) {
204
- if (!col.cellStyle)
205
- return null;
206
- return typeof col.cellStyle === 'function'
207
- ? col.cellStyle(item)
208
- : col.cellStyle;
167
+ return resolveCellStyle(col, item) ?? null;
209
168
  }
210
169
  canEditCell(col, item) {
211
170
  const colEditable = col.editable === true || (typeof col.editable === 'function' && col.editable(item));
@@ -297,6 +256,9 @@ let DataGridTableComponent = class DataGridTableComponent {
297
256
  handlePaste() {
298
257
  void this.state().interaction.handlePaste();
299
258
  }
259
+ onHeaderMouseDown(columnId, event) {
260
+ this.columnReorderService.handleHeaderMouseDown(columnId, event);
261
+ }
300
262
  onResizeStart(e, col) {
301
263
  e.preventDefault();
302
264
  this.resizeStartX = e.clientX;
@@ -307,6 +269,7 @@ let DataGridTableComponent = class DataGridTableComponent {
307
269
  const minW = col.minWidth ?? DEFAULT_MIN_COLUMN_WIDTH;
308
270
  const newWidth = Math.max(minW, this.resizeStartWidth + delta);
309
271
  this.columnSizingOverrides.update((prev) => ({ ...prev, [this.resizeColumnId]: newWidth }));
272
+ this.columnSizingVersion.update(v => v + 1);
310
273
  };
311
274
  const onUp = () => {
312
275
  window.removeEventListener('mousemove', onMove, true);
@@ -324,6 +287,24 @@ let DataGridTableComponent = class DataGridTableComponent {
324
287
  window.addEventListener('mousemove', onMove, true);
325
288
  window.addEventListener('mouseup', onUp, true);
326
289
  }
290
+ // --- Column pinning methods ---
291
+ onPinColumn(columnId, side) {
292
+ this.onColumnPinned()?.(columnId, side);
293
+ }
294
+ onUnpinColumn(columnId) {
295
+ this.onColumnPinned()?.(columnId, null);
296
+ }
297
+ isPinned(columnId) {
298
+ return this.pinnedColumns()?.[columnId];
299
+ }
300
+ getPinState(columnId) {
301
+ const pinned = this.isPinned(columnId);
302
+ return {
303
+ canPinLeft: pinned !== 'left',
304
+ canPinRight: pinned !== 'right',
305
+ canUnpin: !!pinned,
306
+ };
307
+ }
327
308
  buildProps() {
328
309
  return {
329
310
  items: this.items(),
@@ -355,6 +336,9 @@ let DataGridTableComponent = class DataGridTableComponent {
355
336
  rowSelection: this.rowSelectionMode(),
356
337
  selectedRows: this.selectedRows(),
357
338
  onSelectionChange: this.onSelectionChange(),
339
+ showRowNumbers: this.showRowNumbers(),
340
+ currentPage: this.currentPage(),
341
+ pageSize: this.pageSize(),
358
342
  statusBar: this.statusBar(),
359
343
  filters: this.filters(),
360
344
  onFilterChange: this.onFilterChange(),
@@ -380,6 +364,7 @@ DataGridTableComponent = __decorate([
380
364
  MarchingAntsOverlayComponent,
381
365
  EmptyStateComponent,
382
366
  ColumnHeaderFilterComponent,
367
+ ColumnHeaderMenuComponent,
383
368
  InlineCellEditorComponent,
384
369
  ],
385
370
  changeDetection: ChangeDetectionStrategy.OnPush,
@@ -433,6 +418,18 @@ DataGridTableComponent = __decorate([
433
418
  @if (rowIdx === 0 && rowIdx < headerRows().length - 1 && state().layout.hasCheckboxCol) {
434
419
  <th [attr.rowSpan]="headerRows().length - 1"></th>
435
420
  }
421
+ @if (rowIdx === headerRows().length - 1 && state().layout.hasRowNumbersCol) {
422
+ <th
423
+ scope="col"
424
+ rowSpan="1"
425
+ style="width:50px;min-width:50px;max-width:50px;padding:6px;text-align:center;font-weight:600;background:var(--ogrid-bg-subtle,#fafafa);color:var(--ogrid-text-secondary,#666);border-bottom:1px solid var(--ogrid-border,#e0e0e0);position:sticky;left:0;z-index:4"
426
+ >
427
+ #
428
+ </th>
429
+ }
430
+ @if (rowIdx === 0 && rowIdx < headerRows().length - 1 && state().layout.hasRowNumbersCol) {
431
+ <th [attr.rowSpan]="headerRows().length - 1" style="width:50px;min-width:50px"></th>
432
+ }
436
433
  @for (cell of row; track $index; let cellIdx = $index) {
437
434
  @if (cell.isGroup) {
438
435
  <th
@@ -443,34 +440,51 @@ DataGridTableComponent = __decorate([
443
440
  {{ cell.label }}
444
441
  </th>
445
442
  } @else {
443
+ @let pinned = isPinned(cell.columnDef!.columnId);
446
444
  <th
447
445
  scope="col"
448
446
  [attr.data-column-id]="cell.columnDef!.columnId"
449
447
  [attr.rowSpan]="headerRows().length > 1 && rowIdx < headerRows().length - 1 ? headerRows().length - rowIdx : null"
448
+ [class.ogrid-th-pinned-left]="pinned === 'left'"
449
+ [class.ogrid-th-pinned-right]="pinned === 'right'"
450
450
  style="padding:6px 8px;text-align:left;font-weight:600;border-bottom:1px solid var(--ogrid-border, #e0e0e0);position:relative"
451
451
  [style.min-width.px]="cell.columnDef!.minWidth ?? defaultMinWidth"
452
452
  [style.width.px]="getColumnWidth(cell.columnDef!)"
453
453
  [style.max-width.px]="getColumnWidth(cell.columnDef!)"
454
+ [style.cursor]="columnReorderService.isDragging() ? 'grabbing' : 'grab'"
455
+ (mousedown)="onHeaderMouseDown(cell.columnDef!.columnId, $event)"
454
456
  >
455
- <ogrid-primeng-column-header-filter
456
- [columnKey]="cell.columnDef!.columnId"
457
- [columnName]="cell.columnDef!.name"
458
- [filterType]="getFilterConfig(cell.columnDef!).filterType"
459
- [isSorted]="getFilterConfig(cell.columnDef!).isSorted ?? false"
460
- [isSortedDescending]="getFilterConfig(cell.columnDef!).isSortedDescending ?? false"
461
- [onSort]="getFilterConfig(cell.columnDef!).onSort"
462
- [selectedValues]="getFilterConfig(cell.columnDef!).selectedValues"
463
- [onFilterChange]="getFilterConfig(cell.columnDef!).onFilterChange"
464
- [options]="getFilterConfig(cell.columnDef!).options ?? []"
465
- [isLoadingOptions]="getFilterConfig(cell.columnDef!).isLoadingOptions ?? false"
466
- [textValue]="getFilterConfig(cell.columnDef!).textValue ?? ''"
467
- [onTextChange]="getFilterConfig(cell.columnDef!).onTextChange"
468
- [selectedUser]="getFilterConfig(cell.columnDef!).selectedUser"
469
- [onUserChange]="getFilterConfig(cell.columnDef!).onUserChange"
470
- [peopleSearch]="getFilterConfig(cell.columnDef!).peopleSearch"
471
- [dateValue]="getFilterConfig(cell.columnDef!).dateValue"
472
- [onDateChange]="getFilterConfig(cell.columnDef!).onDateChange"
473
- ></ogrid-primeng-column-header-filter>
457
+ <div style="display:flex;align-items:center;gap:4px;">
458
+ <ogrid-primeng-column-header-filter
459
+ [columnKey]="cell.columnDef!.columnId"
460
+ [columnName]="cell.columnDef!.name"
461
+ [filterType]="getFilterConfig(cell.columnDef!).filterType"
462
+ [isSorted]="getFilterConfig(cell.columnDef!).isSorted ?? false"
463
+ [isSortedDescending]="getFilterConfig(cell.columnDef!).isSortedDescending ?? false"
464
+ [onSort]="getFilterConfig(cell.columnDef!).onSort"
465
+ [selectedValues]="getFilterConfig(cell.columnDef!).selectedValues"
466
+ [onFilterChange]="getFilterConfig(cell.columnDef!).onFilterChange"
467
+ [options]="getFilterConfig(cell.columnDef!).options ?? []"
468
+ [isLoadingOptions]="getFilterConfig(cell.columnDef!).isLoadingOptions ?? false"
469
+ [textValue]="getFilterConfig(cell.columnDef!).textValue ?? ''"
470
+ [onTextChange]="getFilterConfig(cell.columnDef!).onTextChange"
471
+ [selectedUser]="getFilterConfig(cell.columnDef!).selectedUser"
472
+ [onUserChange]="getFilterConfig(cell.columnDef!).onUserChange"
473
+ [peopleSearch]="getFilterConfig(cell.columnDef!).peopleSearch"
474
+ [dateValue]="getFilterConfig(cell.columnDef!).dateValue"
475
+ [onDateChange]="getFilterConfig(cell.columnDef!).onDateChange"
476
+ ></ogrid-primeng-column-header-filter>
477
+ @let pinState = getPinState(cell.columnDef!.columnId);
478
+ <column-header-menu
479
+ [columnId]="cell.columnDef!.columnId"
480
+ [onPinLeft]="() => onPinColumn(cell.columnDef!.columnId, 'left')"
481
+ [onPinRight]="() => onPinColumn(cell.columnDef!.columnId, 'right')"
482
+ [onUnpin]="() => onUnpinColumn(cell.columnDef!.columnId)"
483
+ [canPinLeft]="pinState.canPinLeft"
484
+ [canPinRight]="pinState.canPinRight"
485
+ [canUnpin]="pinState.canUnpin"
486
+ />
487
+ </div>
474
488
  <div
475
489
  style="position:absolute;top:0;right:0;bottom:0;width:4px;cursor:col-resize"
476
490
  (mousedown)="onResizeStart($event, cell.columnDef!)"
@@ -506,8 +520,18 @@ DataGridTableComponent = __decorate([
506
520
  />
507
521
  </td>
508
522
  }
523
+ @if (state().layout.hasRowNumbersCol) {
524
+ <td
525
+ style="width:50px;min-width:50px;max-width:50px;padding:6px;text-align:center;font-weight:600;font-variant-numeric:tabular-nums;color:var(--ogrid-text-secondary,#666);background:var(--ogrid-bg-subtle,#fafafa);border-bottom:1px solid var(--ogrid-border,#f0f0f0);position:sticky;left:0;z-index:3"
526
+ >
527
+ {{ rowNumberOffset() + rowIndex + 1 }}
528
+ </td>
529
+ }
509
530
  @for (col of state().layout.visibleCols; track col.columnId; let colIdx = $index) {
531
+ @let pinned = isPinned(col.columnId);
510
532
  <td
533
+ [class.ogrid-td-pinned-left]="pinned === 'left'"
534
+ [class.ogrid-td-pinned-right]="pinned === 'right'"
511
535
  style="padding:0;border-bottom:1px solid var(--ogrid-border, #f0f0f0);position:relative"
512
536
  [style.min-width.px]="col.minWidth ?? defaultMinWidth"
513
537
  [style.width.px]="getColumnWidth(col)"
@@ -561,6 +585,7 @@ DataGridTableComponent = __decorate([
561
585
  [copyRange]="state().interaction.copyRange"
562
586
  [cutRange]="state().interaction.cutRange"
563
587
  [colOffset]="state().layout.colOffset"
588
+ [columnSizingVersion]="columnSizingVersion()"
564
589
  ></ogrid-marching-ants-overlay>
565
590
 
566
591
  @if (state().viewModels.showEmptyInGrid && emptyState()) {
@@ -581,6 +606,10 @@ DataGridTableComponent = __decorate([
581
606
  </div>
582
607
  </div>
583
608
 
609
+ @if (columnReorderService.isDragging() && columnReorderService.dropIndicatorX() !== null) {
610
+ <div class="ogrid-drop-indicator" [style.left.px]="columnReorderService.dropIndicatorX()"></div>
611
+ }
612
+
584
613
  @if (state().contextMenu.menuPosition) {
585
614
  <ogrid-context-menu
586
615
  [x]="state().contextMenu.menuPosition!.x"
@@ -627,6 +656,44 @@ DataGridTableComponent = __decorate([
627
656
  opacity: 0.5;
628
657
  pointer-events: none;
629
658
  }
659
+ .ogrid-drop-indicator {
660
+ position: absolute;
661
+ top: 0;
662
+ bottom: 0;
663
+ width: 3px;
664
+ background: var(--ogrid-primary, #217346);
665
+ pointer-events: none;
666
+ z-index: 100;
667
+ transition: left 0.05s;
668
+ }
669
+ .ogrid-th-pinned-left {
670
+ position: sticky;
671
+ left: 0;
672
+ z-index: 10;
673
+ background: var(--ogrid-header-bg, #f5f5f5);
674
+ border-left: 2px solid var(--ogrid-primary, #217346);
675
+ }
676
+ .ogrid-th-pinned-right {
677
+ position: sticky;
678
+ right: 0;
679
+ z-index: 10;
680
+ background: var(--ogrid-header-bg, #f5f5f5);
681
+ border-right: 2px solid var(--ogrid-primary, #217346);
682
+ }
683
+ .ogrid-td-pinned-left {
684
+ position: sticky;
685
+ left: 0;
686
+ z-index: 5;
687
+ background: var(--ogrid-bg, #fff);
688
+ border-left: 2px solid var(--ogrid-primary, #217346);
689
+ }
690
+ .ogrid-td-pinned-right {
691
+ position: sticky;
692
+ right: 0;
693
+ z-index: 5;
694
+ background: var(--ogrid-bg, #fff);
695
+ border-right: 2px solid var(--ogrid-primary, #217346);
696
+ }
630
697
  `],
631
698
  })
632
699
  ], DataGridTableComponent);
@@ -70,6 +70,14 @@ let InlineCellEditorComponent = class InlineCellEditorComponent {
70
70
  onTextBlur() {
71
71
  this.commitValue(this.localValue());
72
72
  }
73
+ getInputStyle() {
74
+ const baseStyle = 'width:100%;box-sizing:border-box;padding:6px 10px;border:2px solid var(--ogrid-selection, #217346);border-radius:2px;outline:none;font:inherit;background:var(--ogrid-bg, #fff);color:var(--ogrid-fg, #242424);';
75
+ const col = this.column();
76
+ if (col.type === 'numeric') {
77
+ return baseStyle + 'text-align:right;';
78
+ }
79
+ return baseStyle;
80
+ }
73
81
  };
74
82
  InlineCellEditorComponent = __decorate([
75
83
  Component({
@@ -86,7 +94,7 @@ InlineCellEditorComponent = __decorate([
86
94
  (input)="localValue.set($any($event.target).value)"
87
95
  (keydown)="onTextKeyDown($event)"
88
96
  (blur)="onTextBlur()"
89
- style="width:100%;box-sizing:border-box;padding:6px 10px;border:2px solid var(--ogrid-selection, #217346);border-radius:2px;outline:none;font:inherit;background:var(--ogrid-bg, #fff);color:var(--ogrid-fg, #242424)"
97
+ [style]="getInputStyle()"
90
98
  />
91
99
  }
92
100
  @case ('select') {
@@ -122,7 +130,7 @@ InlineCellEditorComponent = __decorate([
122
130
  (change)="commitValue($any($event.target).value)"
123
131
  (keydown)="onTextKeyDown($event)"
124
132
  (blur)="onTextBlur()"
125
- style="width:100%;box-sizing:border-box;padding:6px 10px;border:2px solid var(--ogrid-selection, #217346);border-radius:2px;outline:none;font:inherit;background:var(--ogrid-bg, #fff);color:var(--ogrid-fg, #242424)"
133
+ [style]="getInputStyle()"
126
134
  />
127
135
  }
128
136
  @default {
@@ -133,7 +141,7 @@ InlineCellEditorComponent = __decorate([
133
141
  (input)="localValue.set($any($event.target).value)"
134
142
  (keydown)="onTextKeyDown($event)"
135
143
  (blur)="onTextBlur()"
136
- style="width:100%;box-sizing:border-box;padding:6px 10px;border:2px solid var(--ogrid-selection, #217346);border-radius:2px;outline:none;font:inherit;background:var(--ogrid-bg, #fff);color:var(--ogrid-fg, #242424)"
144
+ [style]="getInputStyle()"
137
145
  />
138
146
  }
139
147
  }
package/dist/esm/index.js CHANGED
@@ -7,3 +7,4 @@ export { InlineCellEditorComponent } from './datagrid-table/inline-cell-editor.c
7
7
  export { ColumnHeaderFilterComponent } from './column-header-filter/column-header-filter.component';
8
8
  export { ColumnChooserComponent } from './column-chooser/column-chooser.component';
9
9
  export { PaginationControlsComponent } from './pagination-controls/pagination-controls.component';
10
+ export { ColumnHeaderMenuComponent } from './column-header-menu/column-header-menu.component';
@@ -0,0 +1,26 @@
1
+ import { EventEmitter } from '@angular/core';
2
+ import type { Menu } from 'primeng/menu';
3
+ import type { MenuItem } from 'primeng/api';
4
+ /**
5
+ * Column header dropdown menu for pin/unpin actions.
6
+ * Uses PrimeNG Menu component.
7
+ */
8
+ export declare class ColumnHeaderMenuComponent {
9
+ columnId: string;
10
+ set canPinLeft(value: boolean);
11
+ set canPinRight(value: boolean);
12
+ set canUnpin(value: boolean);
13
+ pinLeft: EventEmitter<void>;
14
+ pinRight: EventEmitter<void>;
15
+ unpin: EventEmitter<void>;
16
+ menu: Menu;
17
+ private _canPinLeft;
18
+ private _canPinRight;
19
+ private _canUnpin;
20
+ menuModel: MenuItem[];
21
+ ngOnInit(): void;
22
+ private updateMenuModel;
23
+ handlePinLeft(): void;
24
+ handlePinRight(): void;
25
+ handleUnpin(): void;
26
+ }
@@ -1,7 +1,10 @@
1
1
  import { ElementRef } from '@angular/core';
2
- import type { IColumnDef, IColumnGroupDef, RowId } from '@alaarab/ogrid-angular';
2
+ import { ColumnReorderService, VirtualScrollService } from '@alaarab/ogrid-angular';
3
+ import type { IColumnDef, IColumnGroupDef, RowId, HeaderFilterConfig } from '@alaarab/ogrid-angular';
3
4
  export declare class DataGridTableComponent<T = unknown> {
4
5
  private readonly stateService;
6
+ readonly columnReorderService: ColumnReorderService<T>;
7
+ readonly virtualScrollService: VirtualScrollService;
5
8
  readonly wrapperRef: import("@angular/core").Signal<ElementRef<HTMLDivElement> | undefined>;
6
9
  readonly tableContainerRef: import("@angular/core").Signal<ElementRef<HTMLDivElement> | undefined>;
7
10
  readonly items: import("@angular/core").InputSignal<T[]>;
@@ -58,6 +61,9 @@ export declare class DataGridTableComponent<T = unknown> {
58
61
  readonly onCellError: import("@angular/core").InputSignal<((error: Error) => void) | undefined>;
59
62
  readonly ariaLabel: import("@angular/core").InputSignal<string | undefined>;
60
63
  readonly ariaLabelledBy: import("@angular/core").InputSignal<string | undefined>;
64
+ readonly showRowNumbers: import("@angular/core").InputSignal<boolean>;
65
+ readonly currentPage: import("@angular/core").InputSignal<number>;
66
+ readonly pageSize: import("@angular/core").InputSignal<number>;
61
67
  readonly defaultMinWidth = 80;
62
68
  readonly statusBarClasses: {
63
69
  statusBar: string;
@@ -70,40 +76,20 @@ export declare class DataGridTableComponent<T = unknown> {
70
76
  private resizeColumnId;
71
77
  private resizeStartWidth;
72
78
  private lastMouseShift;
79
+ private columnSizingVersion;
73
80
  constructor();
74
81
  readonly state: import("@angular/core").Signal<import("@alaarab/ogrid-angular").DataGridStateResult<T>>;
75
82
  readonly tableContainerEl: import("@angular/core").Signal<HTMLDivElement | null>;
76
83
  readonly resolvedAriaLabel: import("@angular/core").Signal<string | undefined>;
77
- readonly headerRows: import("@angular/core").Signal<import("@alaarab/ogrid-angular").HeaderRow<T>[]>;
84
+ readonly rowNumberOffset: import("@angular/core").Signal<number>;
85
+ readonly headerRows: import("@angular/core").Signal<import("@alaarab/ogrid-core").HeaderRow<T>[]>;
78
86
  readonly allowOverflowX: import("@angular/core").Signal<boolean>;
79
87
  readonly tableWidthStyle: import("@angular/core").Signal<"100%" | "fit-content">;
80
88
  readonly tableMinWidthStyle: import("@angular/core").Signal<"100%" | "max-content">;
81
89
  readonly selectedCellCount: import("@angular/core").Signal<number | undefined>;
82
90
  trackByRowId(_index: number, item: T): RowId;
83
91
  getColumnWidth(col: IColumnDef<T>): number | undefined;
84
- getFilterConfig(col: IColumnDef<T>): {
85
- filterType: string;
86
- isSorted?: boolean;
87
- isSortedDescending?: boolean;
88
- onSort?: () => void;
89
- selectedValues?: string[];
90
- onFilterChange?: (values: string[]) => void;
91
- options?: string[];
92
- isLoadingOptions?: boolean;
93
- textValue?: string;
94
- onTextChange?: (value: string) => void;
95
- selectedUser?: unknown;
96
- onUserChange?: (user: unknown) => void;
97
- peopleSearch?: (query: string) => Promise<unknown[]>;
98
- dateValue?: {
99
- from?: string;
100
- to?: string;
101
- };
102
- onDateChange?: (value: {
103
- from?: string;
104
- to?: string;
105
- } | undefined) => void;
106
- };
92
+ getFilterConfig(col: IColumnDef<T>): HeaderFilterConfig;
107
93
  getCellValueFn(item: T, col: IColumnDef<T>): unknown;
108
94
  resolveCellDisplay(col: IColumnDef<T>, item: T): string;
109
95
  getCellStyleObj(col: IColumnDef<T>, item: T): Record<string, string> | null;
@@ -125,6 +111,15 @@ export declare class DataGridTableComponent<T = unknown> {
125
111
  onRowClick(e: MouseEvent, item: T): void;
126
112
  onRowCheckboxChange(item: T, checked: boolean, rowIndex: number, e: Event): void;
127
113
  handlePaste(): void;
114
+ onHeaderMouseDown(columnId: string, event: MouseEvent): void;
128
115
  onResizeStart(e: MouseEvent, col: IColumnDef<T>): void;
116
+ onPinColumn(columnId: string, side: 'left' | 'right'): void;
117
+ onUnpinColumn(columnId: string): void;
118
+ isPinned(columnId: string): 'left' | 'right' | undefined;
119
+ getPinState(columnId: string): {
120
+ canPinLeft: boolean;
121
+ canPinRight: boolean;
122
+ canUnpin: boolean;
123
+ };
129
124
  private buildProps;
130
125
  }
@@ -18,4 +18,5 @@ export declare class InlineCellEditorComponent<T = unknown> implements AfterView
18
18
  onSelectKeyDown(e: KeyboardEvent): void;
19
19
  onCheckboxKeyDown(e: KeyboardEvent): void;
20
20
  onTextBlur(): void;
21
+ getInputStyle(): string;
21
22
  }
@@ -5,3 +5,4 @@ export { InlineCellEditorComponent } from './datagrid-table/inline-cell-editor.c
5
5
  export { ColumnHeaderFilterComponent } from './column-header-filter/column-header-filter.component';
6
6
  export { ColumnChooserComponent } from './column-chooser/column-chooser.component';
7
7
  export { PaginationControlsComponent } from './pagination-controls/pagination-controls.component';
8
+ export { ColumnHeaderMenuComponent } from './column-header-menu/column-header-menu.component';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alaarab/ogrid-angular-primeng",
3
- "version": "2.0.2",
3
+ "version": "2.0.4",
4
4
  "description": "OGrid PrimeNG – PrimeNG Table-based data grid with sorting, filtering, pagination, column chooser, and CSV export.",
5
5
  "main": "dist/esm/index.js",
6
6
  "module": "dist/esm/index.js",
@@ -22,7 +22,7 @@
22
22
  "files": ["dist", "README.md", "LICENSE"],
23
23
  "engines": { "node": ">=18" },
24
24
  "dependencies": {
25
- "@alaarab/ogrid-angular": "2.0.2"
25
+ "@alaarab/ogrid-angular": "2.0.4"
26
26
  },
27
27
  "peerDependencies": {
28
28
  "@angular/core": "^21.0.0",