@alaarab/ogrid-angular-primeng 2.0.11 → 2.0.13

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.
@@ -37,7 +37,7 @@ ColumnChooserComponent = __decorate([
37
37
  <div
38
38
  style="position:absolute;right:0;top:100%;z-index:100;min-width:220px;max-height:320px;overflow-y:auto;background:var(--ogrid-bg, #fff);border:1px solid var(--ogrid-border, #e0e0e0);border-radius:6px;box-shadow:0 2px 8px rgba(0,0,0,0.12);padding:8px 0"
39
39
  >
40
- <div style="padding:4px 12px;font-weight:600;font-size:12px;color:var(--ogrid-muted, #666)">
40
+ <div style="padding:4px 12px;font-weight:600;font-size:12px;color:var(--ogrid-fg-secondary, rgba(0, 0, 0, 0.6))">
41
41
  Select Columns ({{ visibleCount() }} of {{ totalCount() }})
42
42
  </div>
43
43
  @for (col of columns; track col.columnId) {
@@ -148,7 +148,7 @@ ColumnHeaderFilterComponent = __decorate([
148
148
  [attr.aria-label]="'Search ' + columnName + ' options'"
149
149
  />
150
150
  @if (isLoadingOptions) {
151
- <div style="padding:8px 0;color:var(--ogrid-muted, #999);font-size:12px">Loading...</div>
151
+ <div style="padding:8px 0;color:var(--ogrid-fg-muted, rgba(0, 0, 0, 0.5));font-size:12px">Loading...</div>
152
152
  } @else {
153
153
  <div style="display:flex;gap:4px;margin-bottom:6px">
154
154
  <button type="button" class="p-button p-button-text p-button-sm" (click)="handleSelectAllOptions()" style="font-size:11px">All</button>
@@ -160,7 +160,7 @@ ColumnHeaderFilterComponent = __decorate([
160
160
  <input
161
161
  type="checkbox"
162
162
  [checked]="tempSelected().has(opt)"
163
- (change)="handleCheckboxChange(opt, $any($event.target).checked)"
163
+ (change)="handleCheckboxChange(opt, $event)"
164
164
  />
165
165
  {{ opt }}
166
166
  </label>
@@ -195,7 +195,7 @@ ColumnHeaderFilterComponent = __decorate([
195
195
  [attr.aria-label]="'Search people for ' + columnName"
196
196
  />
197
197
  @if (isPeopleLoading()) {
198
- <div style="padding:8px 0;color:var(--ogrid-muted, #999);font-size:12px">Loading...</div>
198
+ <div style="padding:8px 0;color:var(--ogrid-fg-muted, rgba(0, 0, 0, 0.5));font-size:12px">Loading...</div>
199
199
  }
200
200
  @for (user of peopleSuggestions(); track user.email) {
201
201
  <button
@@ -114,15 +114,19 @@ ColumnHeaderMenuComponent = __decorate([
114
114
  `,
115
115
  styles: [`
116
116
  .column-header-menu-trigger {
117
- opacity: 0;
118
- transition: opacity 0.15s;
119
117
  padding: 0.25rem;
120
- min-width: auto;
118
+ min-width: 24px;
119
+ height: 24px;
120
+ display: inline-flex;
121
+ align-items: center;
122
+ justify-content: center;
123
+ color: var(--ogrid-fg-secondary, rgba(0, 0, 0, 0.6));
124
+ border-radius: 4px;
125
+ transition: background-color 0.15s;
121
126
  }
122
-
123
- :host:hover .column-header-menu-trigger,
124
- .column-header-menu-trigger:focus {
125
- opacity: 1;
127
+ .column-header-menu-trigger:hover {
128
+ background: var(--ogrid-hover-bg, rgba(0, 0, 0, 0.04));
129
+ color: var(--ogrid-fg, rgba(0, 0, 0, 0.87));
126
130
  }
127
131
  `],
128
132
  })
@@ -4,7 +4,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
4
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
5
  return c > 3 && r && Object.defineProperty(target, key, r), r;
6
6
  };
7
- import { Component, Input, signal, computed, ViewChild, ChangeDetectionStrategy, } from '@angular/core';
7
+ import { Component, Input, signal, computed, ViewChild, ChangeDetectionStrategy, ViewEncapsulation, } from '@angular/core';
8
8
  import { BaseDataGridTableComponent, DataGridStateService, StatusBarComponent, GridContextMenuComponent, MarchingAntsOverlayComponent, EmptyStateComponent, DEFAULT_MIN_COLUMN_WIDTH, getCellValue, resolveCellDisplayContent, resolveCellStyle, } from '@alaarab/ogrid-angular';
9
9
  import { ColumnHeaderFilterComponent } from '../column-header-filter/column-header-filter.component';
10
10
  import { ColumnHeaderMenuComponent } from '../column-header-menu/column-header-menu.component';
@@ -25,6 +25,7 @@ let DataGridTableComponent = class DataGridTableComponent extends BaseDataGridTa
25
25
  this.freezeColsInput = undefined;
26
26
  this.layoutMode = 'fill';
27
27
  this.suppressHorizontalScroll = undefined;
28
+ this.columnReorder = undefined;
28
29
  this.isLoadingInput = false;
29
30
  this.loadingMessageInput = 'Loading\u2026';
30
31
  this.editable = undefined;
@@ -56,6 +57,13 @@ let DataGridTableComponent = class DataGridTableComponent extends BaseDataGridTa
56
57
  statusBarLabel: 'ogrid-status-bar-label',
57
58
  statusBarValue: 'ogrid-status-bar-value',
58
59
  };
60
+ this.contextMenuClasses = {
61
+ contextMenu: 'ogrid-context-menu',
62
+ contextMenuItem: 'ogrid-context-menu-item',
63
+ contextMenuItemLabel: 'ogrid-context-menu-item-label',
64
+ contextMenuItemShortcut: 'ogrid-context-menu-item-shortcut',
65
+ contextMenuDivider: 'ogrid-context-menu-divider',
66
+ };
59
67
  // PrimeNG uses flat number overrides for column sizing
60
68
  this.primengColumnSizingOverrides = signal({});
61
69
  this.propsSignal = signal(undefined);
@@ -223,7 +231,7 @@ let DataGridTableComponent = class DataGridTableComponent extends BaseDataGridTa
223
231
  onUnpin: () => this.onUnpinColumn(columnId),
224
232
  onSortAsc: () => this.onSortAsc(columnId),
225
233
  onSortDesc: () => this.onSortDesc(columnId),
226
- onClearSort: () => this.onClearSort(),
234
+ onClearSort: () => this.onClearSort(columnId),
227
235
  onAutosizeThis: () => this.onAutosizeColumn(columnId),
228
236
  onAutosizeAll: () => this.onAutosizeAllColumns(),
229
237
  onClose: () => { }
@@ -281,6 +289,7 @@ let DataGridTableComponent = class DataGridTableComponent extends BaseDataGridTa
281
289
  freezeCols: this.freezeColsInput,
282
290
  layoutMode: this.layoutMode,
283
291
  suppressHorizontalScroll: this.suppressHorizontalScroll,
292
+ columnReorder: this.columnReorder,
284
293
  isLoading: this.isLoadingInput,
285
294
  loadingMessage: this.loadingMessageInput,
286
295
  editable: this.editable,
@@ -367,6 +376,9 @@ __decorate([
367
376
  __decorate([
368
377
  Input()
369
378
  ], DataGridTableComponent.prototype, "suppressHorizontalScroll", void 0);
379
+ __decorate([
380
+ Input()
381
+ ], DataGridTableComponent.prototype, "columnReorder", void 0);
370
382
  __decorate([
371
383
  Input({ alias: 'isLoading' })
372
384
  ], DataGridTableComponent.prototype, "isLoadingInput", void 0);
@@ -460,13 +472,16 @@ DataGridTableComponent = __decorate([
460
472
  PopoverCellEditorComponent,
461
473
  ],
462
474
  changeDetection: ChangeDetectionStrategy.OnPush,
475
+ encapsulation: ViewEncapsulation.None,
463
476
  providers: [DataGridStateService],
464
477
  template: `
465
- <div style="position:relative;flex:1;min-height:0;display:flex;flex-direction:column">
478
+ <div class="ogrid-root">
466
479
  <div
467
480
  #wrapper
468
481
  tabindex="0"
469
482
  role="region"
483
+ class="ogrid-scroll-wrapper"
484
+ [class.ogrid-scroll-wrapper--loading-empty]="isLoading() && items().length === 0"
470
485
  [attr.aria-label]="resolvedAriaLabel()"
471
486
  [attr.aria-labelledby]="ariaLabelledBy()"
472
487
  [attr.data-empty]="showEmptyInGrid() ? 'true' : null"
@@ -479,26 +494,19 @@ DataGridTableComponent = __decorate([
479
494
  (keydown)="onGridKeyDown($event)"
480
495
  (mousedown)="onWrapperMouseDown($event)"
481
496
  (scroll)="onWrapperScroll($event)"
482
- style="flex:1;min-height:0;overflow:auto;outline:none;position:relative;font-size:13px;color:var(--ogrid-fg, #242424)"
483
497
  [style.--data-table-column-count]="state().layout.totalColCount"
484
498
  [style.--data-table-width]="tableWidthStyle()"
485
499
  [style.--data-table-min-width]="tableMinWidthStyle()"
486
500
  >
487
- <div style="position:relative">
488
- <div [class.loading-dimmed]="isLoading() && items().length > 0" style="position:relative">
489
- <div #tableContainer style="position:relative">
490
- <table
491
- style="width:var(--data-table-width, 100%);min-width:var(--data-table-min-width, 100%);border-collapse:collapse;table-layout:fixed"
492
- >
493
- <thead style="z-index:3;background:var(--ogrid-header-bg, #f5f5f5)">
501
+ <div class="ogrid-table-wrapper">
502
+ <div [class.loading-dimmed]="isLoading() && items().length > 0" class="ogrid-table-wrapper">
503
+ <div #tableContainer class="ogrid-table-wrapper">
504
+ <table class="ogrid-table">
505
+ <thead class="ogrid-thead">
494
506
  @for (row of headerRows(); track $index; let rowIdx = $index) {
495
507
  <tr>
496
508
  @if (rowIdx === headerRows().length - 1 && hasCheckboxCol()) {
497
- <th
498
- scope="col"
499
- rowSpan="1"
500
- style="width:48px;min-width:48px;max-width:48px;padding:6px 4px;text-align:center;border-bottom:1px solid var(--ogrid-border, #e0e0e0)"
501
- >
509
+ <th scope="col" rowSpan="1" class="ogrid-checkbox-header">
502
510
  <input
503
511
  type="checkbox"
504
512
  [checked]="allSelected()"
@@ -512,23 +520,19 @@ DataGridTableComponent = __decorate([
512
520
  <th [attr.rowSpan]="headerRows().length - 1"></th>
513
521
  }
514
522
  @if (rowIdx === headerRows().length - 1 && hasRowNumbersCol()) {
515
- <th
516
- scope="col"
517
- rowSpan="1"
518
- 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"
519
- >
523
+ <th scope="col" rowSpan="1" class="ogrid-row-number-header">
520
524
  #
521
525
  </th>
522
526
  }
523
527
  @if (rowIdx === 0 && rowIdx < headerRows().length - 1 && hasRowNumbersCol()) {
524
- <th [attr.rowSpan]="headerRows().length - 1" style="width:50px;min-width:50px"></th>
528
+ <th [attr.rowSpan]="headerRows().length - 1" class="ogrid-row-number-spacer"></th>
525
529
  }
526
530
  @for (cell of row; track $index; let cellIdx = $index) {
527
531
  @if (cell.isGroup) {
528
532
  <th
529
533
  [attr.colSpan]="cell.colSpan"
530
534
  scope="colgroup"
531
- style="padding:6px 8px;text-align:center;font-weight:600;border-bottom:1px solid var(--ogrid-border, #e0e0e0)"
535
+ class="ogrid-column-group-header"
532
536
  >
533
537
  {{ cell.label }}
534
538
  </th>
@@ -537,11 +541,11 @@ DataGridTableComponent = __decorate([
537
541
  @let pinned = isPinned(col.columnId);
538
542
  <th
539
543
  scope="col"
544
+ class="ogrid-header-cell"
540
545
  [attr.data-column-id]="col.columnId"
541
546
  [attr.rowSpan]="headerRows().length > 1 && rowIdx < headerRows().length - 1 ? headerRows().length - rowIdx : null"
542
547
  [class.ogrid-th-pinned-left]="pinned === 'left'"
543
548
  [class.ogrid-th-pinned-right]="pinned === 'right'"
544
- style="padding:6px 8px;text-align:left;font-weight:600;border-bottom:1px solid var(--ogrid-border, #e0e0e0);position:sticky;top:0;background:var(--ogrid-header-bg, #f5f5f5);z-index:3"
545
549
  [style.min-width.px]="col.minWidth ?? defaultMinWidth"
546
550
  [style.width.px]="getColumnWidth(col)"
547
551
  [style.max-width.px]="getColumnWidth(col)"
@@ -550,7 +554,7 @@ DataGridTableComponent = __decorate([
550
554
  [style.cursor]="columnReorderService.isDragging() ? 'grabbing' : 'grab'"
551
555
  (mousedown)="onHeaderMouseDown(col.columnId, $event)"
552
556
  >
553
- <div style="display:flex;align-items:center;gap:4px;">
557
+ <div class="ogrid-header-content">
554
558
  <ogrid-primeng-column-header-filter
555
559
  [columnKey]="col.columnId"
556
560
  [columnName]="col.name"
@@ -584,7 +588,7 @@ DataGridTableComponent = __decorate([
584
588
  />
585
589
  </div>
586
590
  <div
587
- style="position:absolute;top:0;right:0;bottom:0;width:4px;cursor:col-resize"
591
+ class="ogrid-resize-handle"
588
592
  (mousedown)="onResizeStartPrimeng($event, col)"
589
593
  [attr.aria-label]="'Resize ' + col.name"
590
594
  ></div>
@@ -611,7 +615,7 @@ DataGridTableComponent = __decorate([
611
615
  >
612
616
  @if (hasCheckboxCol()) {
613
617
  <td
614
- style="width:48px;min-width:48px;max-width:48px;padding:6px 4px;text-align:center;border-bottom:1px solid var(--ogrid-border, #f0f0f0)"
618
+ class="ogrid-checkbox-cell"
615
619
  [attr.data-row-index]="rowIndex"
616
620
  [attr.data-col-index]="0"
617
621
  (click)="$event.stopPropagation()"
@@ -625,18 +629,18 @@ DataGridTableComponent = __decorate([
625
629
  </td>
626
630
  }
627
631
  @if (hasRowNumbersCol()) {
628
- <td
629
- 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"
630
- >
632
+ <td class="ogrid-row-number-cell">
633
+
631
634
  {{ rowNumberOffset() + rowIndex + 1 }}
632
635
  </td>
633
636
  }
634
637
  @for (col of visibleCols(); track col.columnId; let colIdx = $index) {
635
638
  @let pinned = isPinned(col.columnId);
636
639
  <td
640
+ [attr.data-column-id]="col.columnId"
637
641
  [class.ogrid-td-pinned-left]="pinned === 'left'"
638
642
  [class.ogrid-td-pinned-right]="pinned === 'right'"
639
- style="padding:0;border-bottom:1px solid var(--ogrid-border, #f0f0f0);position:relative"
643
+ class="ogrid-data-cell"
640
644
  [style.min-width.px]="col.minWidth ?? defaultMinWidth"
641
645
  [style.width.px]="getColumnWidth(col)"
642
646
  [style.max-width.px]="getColumnWidth(col)"
@@ -672,7 +676,7 @@ DataGridTableComponent = __decorate([
672
676
  (mousedown)="onCellMouseDown($event, rowIndex, colIdx + colOffset())"
673
677
  (dblclick)="onCellDblClickPrimeng(item, col, rowIndex, colIdx)"
674
678
  (contextmenu)="onCellContextMenu($event)"
675
- style="padding:6px 10px;min-height:20px;cursor:default;overflow:hidden;text-overflow:ellipsis;white-space:nowrap"
679
+ class="ogrid-cell-content"
676
680
  [style.cursor]="canEditCell(col, item) ? 'cell' : 'default'"
677
681
  [style.background]="getCellBackground(rowIndex, colIdx)"
678
682
  [style.outline]="isActiveCell(rowIndex, colIdx) ? '2px solid var(--ogrid-selection, #217346)' : null"
@@ -682,7 +686,7 @@ DataGridTableComponent = __decorate([
682
686
  @if (canEditCell(col, item) && isSelectionEndCell(rowIndex, colIdx)) {
683
687
  <div
684
688
  (mousedown)="onFillHandleMouseDown($event)"
685
- style="position:absolute;bottom:-3px;right:-3px;width:7px;height:7px;background:var(--ogrid-selection, #217346);cursor:crosshair;z-index:2"
689
+ class="ogrid-fill-handle"
686
690
  aria-label="Fill handle"
687
691
  ></div>
688
692
  }
@@ -712,9 +716,9 @@ DataGridTableComponent = __decorate([
712
716
  ></ogrid-marching-ants-overlay>
713
717
 
714
718
  @if (showEmptyInGrid() && emptyState()) {
715
- <div style="display:flex;align-items:center;justify-content:center;padding:48px 24px;text-align:center;color:var(--ogrid-muted, #999)">
719
+ <div class="ogrid-empty-container">
716
720
  <div>
717
- <div style="font-weight:600;margin-bottom:8px">No results found</div>
721
+ <div class="ogrid-empty-title">No results found</div>
718
722
  <ogrid-empty-state
719
723
  [message]="emptyState()?.message"
720
724
  [hasActiveFilters]="emptyState()?.hasActiveFilters ?? false"
@@ -740,6 +744,7 @@ DataGridTableComponent = __decorate([
740
744
  [hasSelection]="hasCellSelection()"
741
745
  [canUndoProp]="canUndo()"
742
746
  [canRedoProp]="canRedo()"
747
+ [classNames]="contextMenuClasses"
743
748
  (copy)="handleCopy()"
744
749
  (cut)="handleCut()"
745
750
  (paste)="handlePaste()"
@@ -763,18 +768,230 @@ DataGridTableComponent = __decorate([
763
768
  }
764
769
 
765
770
  @if (isLoading()) {
766
- <div
767
- style="position:absolute;inset:0;display:flex;align-items:center;justify-content:center;background:rgba(255,255,255,0.7);z-index:10"
768
- aria-live="polite"
769
- >
770
- <div style="display:flex;align-items:center;gap:8px;color:var(--ogrid-fg, #242424)">
771
- <span style="font-size:14px">{{ loadingMessage() }}</span>
771
+ <div class="ogrid-loading-overlay" aria-live="polite">
772
+ <div class="ogrid-loading-content">
773
+ <span class="ogrid-loading-text">{{ loadingMessage() }}</span>
772
774
  </div>
773
775
  </div>
774
776
  }
775
777
  </div>
776
778
  `,
777
779
  styles: [`
780
+ /* ─── OGrid Theme Variables ─── */
781
+ :root {
782
+ --ogrid-bg: #ffffff;
783
+ --ogrid-fg: rgba(0, 0, 0, 0.87);
784
+ --ogrid-fg-secondary: rgba(0, 0, 0, 0.6);
785
+ --ogrid-fg-muted: rgba(0, 0, 0, 0.5);
786
+ --ogrid-border: rgba(0, 0, 0, 0.12);
787
+ --ogrid-header-bg: rgba(0, 0, 0, 0.04);
788
+ --ogrid-hover-bg: rgba(0, 0, 0, 0.04);
789
+ --ogrid-selected-row-bg: #e6f0fb;
790
+ --ogrid-active-cell-bg: rgba(0, 0, 0, 0.02);
791
+ --ogrid-range-bg: rgba(33, 115, 70, 0.12);
792
+ --ogrid-accent: #0078d4;
793
+ --ogrid-selection-color: #217346;
794
+ --ogrid-loading-overlay: rgba(255, 255, 255, 0.7);
795
+ }
796
+ @media (prefers-color-scheme: dark) {
797
+ :root:not([data-theme="light"]) {
798
+ --ogrid-bg: #1e1e1e;
799
+ --ogrid-fg: rgba(255, 255, 255, 0.87);
800
+ --ogrid-fg-secondary: rgba(255, 255, 255, 0.6);
801
+ --ogrid-fg-muted: rgba(255, 255, 255, 0.5);
802
+ --ogrid-border: rgba(255, 255, 255, 0.12);
803
+ --ogrid-header-bg: rgba(255, 255, 255, 0.06);
804
+ --ogrid-hover-bg: rgba(255, 255, 255, 0.08);
805
+ --ogrid-selected-row-bg: #1a3a5c;
806
+ --ogrid-active-cell-bg: rgba(255, 255, 255, 0.06);
807
+ --ogrid-range-bg: rgba(46, 160, 67, 0.15);
808
+ --ogrid-accent: #4da6ff;
809
+ --ogrid-selection-color: #2ea043;
810
+ --ogrid-loading-overlay: rgba(0, 0, 0, 0.7);
811
+ }
812
+ }
813
+ [data-theme="dark"] {
814
+ --ogrid-bg: #1e1e1e;
815
+ --ogrid-fg: rgba(255, 255, 255, 0.87);
816
+ --ogrid-fg-secondary: rgba(255, 255, 255, 0.6);
817
+ --ogrid-fg-muted: rgba(255, 255, 255, 0.5);
818
+ --ogrid-border: rgba(255, 255, 255, 0.12);
819
+ --ogrid-header-bg: rgba(255, 255, 255, 0.06);
820
+ --ogrid-hover-bg: rgba(255, 255, 255, 0.08);
821
+ --ogrid-selected-row-bg: #1a3a5c;
822
+ --ogrid-active-cell-bg: rgba(255, 255, 255, 0.06);
823
+ --ogrid-range-bg: rgba(46, 160, 67, 0.15);
824
+ --ogrid-accent: #4da6ff;
825
+ --ogrid-selection-color: #2ea043;
826
+ --ogrid-loading-overlay: rgba(0, 0, 0, 0.7);
827
+ }
828
+ :host { display: block; }
829
+ .ogrid-root {
830
+ position: relative;
831
+ flex: 1;
832
+ min-height: 0;
833
+ display: flex;
834
+ flex-direction: column;
835
+ overflow: hidden;
836
+ }
837
+ .ogrid-scroll-wrapper {
838
+ flex: 1;
839
+ min-height: 0;
840
+ overflow: auto;
841
+ position: relative;
842
+ background: var(--ogrid-bg, #ffffff);
843
+ color: var(--ogrid-fg, rgba(0, 0, 0, 0.87));
844
+ }
845
+ .ogrid-scroll-wrapper--loading-empty { min-height: 200px; }
846
+ .ogrid-table-wrapper {
847
+ position: relative;
848
+ }
849
+ .ogrid-table {
850
+ width: var(--data-table-width, 100%);
851
+ min-width: var(--data-table-min-width, 100%);
852
+ border-collapse: collapse;
853
+ table-layout: fixed;
854
+ }
855
+ .ogrid-thead {
856
+ z-index: 3;
857
+ background: var(--ogrid-header-bg, #f5f5f5);
858
+ position: sticky;
859
+ top: 0;
860
+ }
861
+ .ogrid-checkbox-header {
862
+ width: 48px;
863
+ min-width: 48px;
864
+ max-width: 48px;
865
+ text-align: center;
866
+ background: var(--ogrid-header-bg, #f5f5f5);
867
+ border-bottom: 2px solid var(--ogrid-border, #e0e0e0);
868
+ position: sticky;
869
+ top: 0;
870
+ z-index: 3;
871
+ }
872
+ .ogrid-row-number-header {
873
+ width: 50px;
874
+ min-width: 50px;
875
+ max-width: 50px;
876
+ text-align: center;
877
+ font-weight: 600;
878
+ background: var(--ogrid-header-bg, #f5f5f5);
879
+ border-bottom: 2px solid var(--ogrid-border, #e0e0e0);
880
+ position: sticky;
881
+ top: 0;
882
+ z-index: 3;
883
+ }
884
+ .ogrid-row-number-spacer {
885
+ width: 50px;
886
+ min-width: 50px;
887
+ max-width: 50px;
888
+ background: var(--ogrid-header-bg, #f5f5f5);
889
+ }
890
+ .ogrid-column-group-header {
891
+ text-align: center;
892
+ font-weight: 600;
893
+ background: var(--ogrid-header-bg, #f5f5f5);
894
+ border-bottom: 2px solid var(--ogrid-border, #e0e0e0);
895
+ padding: 6px 10px;
896
+ }
897
+ .ogrid-header-cell {
898
+ background: var(--ogrid-header-bg, #f5f5f5);
899
+ border-bottom: 2px solid var(--ogrid-border, #e0e0e0);
900
+ padding: 0;
901
+ position: relative;
902
+ user-select: none;
903
+ }
904
+ .ogrid-header-content {
905
+ display: flex;
906
+ align-items: center;
907
+ gap: 4px;
908
+ padding: 6px 10px;
909
+ }
910
+ .ogrid-resize-handle {
911
+ position: absolute;
912
+ top: 0;
913
+ right: 0;
914
+ bottom: 0;
915
+ width: 4px;
916
+ cursor: col-resize;
917
+ }
918
+ .ogrid-checkbox-cell {
919
+ width: 48px;
920
+ min-width: 48px;
921
+ max-width: 48px;
922
+ padding: 6px 4px;
923
+ text-align: center;
924
+ border-bottom: 1px solid var(--ogrid-border, #f0f0f0);
925
+ }
926
+ .ogrid-row-number-cell {
927
+ width: 50px;
928
+ min-width: 50px;
929
+ max-width: 50px;
930
+ padding: 6px;
931
+ text-align: center;
932
+ font-weight: 600;
933
+ font-variant-numeric: tabular-nums;
934
+ color: var(--ogrid-fg-secondary, rgba(0, 0, 0, 0.6));
935
+ background: var(--ogrid-header-bg, rgba(0, 0, 0, 0.04));
936
+ border-bottom: 1px solid var(--ogrid-border, #f0f0f0);
937
+ position: sticky;
938
+ left: 0;
939
+ z-index: 3;
940
+ }
941
+ .ogrid-data-cell {
942
+ padding: 0;
943
+ border-bottom: 1px solid var(--ogrid-border, #f0f0f0);
944
+ position: relative;
945
+ }
946
+ .ogrid-cell-content {
947
+ padding: 6px 10px;
948
+ min-height: 20px;
949
+ cursor: default;
950
+ overflow: hidden;
951
+ text-overflow: ellipsis;
952
+ white-space: nowrap;
953
+ }
954
+ .ogrid-scroll-wrapper [data-drag-range] { background: var(--ogrid-range-bg, rgba(33, 115, 70, 0.12)) !important; }
955
+ .ogrid-fill-handle {
956
+ position: absolute;
957
+ bottom: -3px;
958
+ right: -3px;
959
+ width: 7px;
960
+ height: 7px;
961
+ background: var(--ogrid-selection, #217346);
962
+ cursor: crosshair;
963
+ z-index: 2;
964
+ }
965
+ .ogrid-empty-container {
966
+ display: flex;
967
+ align-items: center;
968
+ justify-content: center;
969
+ padding: 48px 24px;
970
+ text-align: center;
971
+ color: var(--ogrid-fg-muted, rgba(0, 0, 0, 0.5));
972
+ }
973
+ .ogrid-empty-title {
974
+ font-weight: 600;
975
+ margin-bottom: 8px;
976
+ }
977
+ .ogrid-loading-overlay {
978
+ position: absolute;
979
+ inset: 0;
980
+ display: flex;
981
+ align-items: center;
982
+ justify-content: center;
983
+ background: var(--ogrid-loading-overlay, rgba(255, 255, 255, 0.7));
984
+ z-index: 10;
985
+ }
986
+ .ogrid-loading-content {
987
+ display: flex;
988
+ align-items: center;
989
+ gap: 8px;
990
+ color: var(--ogrid-fg, #242424);
991
+ }
992
+ .ogrid-loading-text {
993
+ font-size: 14px;
994
+ }
778
995
  .loading-dimmed {
779
996
  opacity: 0.5;
780
997
  pointer-events: none;
@@ -830,10 +1047,86 @@ DataGridTableComponent = __decorate([
830
1047
  outline: 2px solid var(--primary-color, #6366f1);
831
1048
  outline-offset: 2px;
832
1049
  }
1050
+
1051
+ /* Context menu */
1052
+ .ogrid-context-menu {
1053
+ position: fixed;
1054
+ z-index: 10000;
1055
+ min-width: 160px;
1056
+ padding: 4px 0;
1057
+ background: var(--ogrid-bg, #fff);
1058
+ border: 1px solid var(--ogrid-border, rgba(0, 0, 0, 0.12));
1059
+ border-radius: 6px;
1060
+ box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);
1061
+ }
1062
+ .ogrid-context-menu-item {
1063
+ display: flex;
1064
+ align-items: center;
1065
+ justify-content: space-between;
1066
+ gap: 24px;
1067
+ width: 100%;
1068
+ padding: 6px 12px;
1069
+ border: none;
1070
+ background: none;
1071
+ font-size: 13px;
1072
+ text-align: left;
1073
+ cursor: pointer;
1074
+ color: var(--ogrid-fg, rgba(0, 0, 0, 0.87));
1075
+ }
1076
+ .ogrid-context-menu-item:hover:not(:disabled) {
1077
+ background: var(--ogrid-hover-bg, rgba(0, 0, 0, 0.04));
1078
+ }
1079
+ .ogrid-context-menu-item:disabled {
1080
+ opacity: 0.5;
1081
+ cursor: not-allowed;
1082
+ }
1083
+ .ogrid-context-menu-item-label {
1084
+ flex: 1;
1085
+ }
1086
+ .ogrid-context-menu-item-shortcut {
1087
+ color: var(--ogrid-fg-muted, rgba(0, 0, 0, 0.5));
1088
+ font-size: 0.85em;
1089
+ }
1090
+ .ogrid-context-menu-divider {
1091
+ height: 1px;
1092
+ margin: 4px 0;
1093
+ background: var(--ogrid-border, rgba(0, 0, 0, 0.12));
1094
+ }
833
1095
  ::ng-deep .p-checkbox:focus-visible {
834
1096
  outline: 2px solid var(--primary-color, #6366f1);
835
1097
  outline-offset: 2px;
836
1098
  }
1099
+
1100
+ /* PrimeNG Menu popup overrides — must use !important to win over PrimeNG's CSS-variable-based defaults */
1101
+ .p-menu {
1102
+ background: var(--ogrid-bg, #ffffff) !important;
1103
+ color: var(--ogrid-fg, rgba(0, 0, 0, 0.87)) !important;
1104
+ border: 1px solid var(--ogrid-border, rgba(0, 0, 0, 0.12)) !important;
1105
+ border-radius: 4px !important;
1106
+ padding: 4px 0 !important;
1107
+ }
1108
+ .p-menu-overlay {
1109
+ 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;
1110
+ }
1111
+ .p-menu-item-content {
1112
+ color: var(--ogrid-fg, rgba(0, 0, 0, 0.87)) !important;
1113
+ }
1114
+ .p-menu-item-link {
1115
+ color: var(--ogrid-fg, rgba(0, 0, 0, 0.87)) !important;
1116
+ padding: 6px 12px !important;
1117
+ }
1118
+ .p-menu-item-label {
1119
+ color: var(--ogrid-fg, rgba(0, 0, 0, 0.87)) !important;
1120
+ font-size: 0.875rem !important;
1121
+ }
1122
+ .p-menu-item:not(.p-disabled) .p-menu-item-content:hover {
1123
+ background: var(--ogrid-hover-bg, rgba(0, 0, 0, 0.04)) !important;
1124
+ color: var(--ogrid-fg, rgba(0, 0, 0, 0.87)) !important;
1125
+ }
1126
+ .p-menu-separator {
1127
+ border-color: var(--ogrid-border, rgba(0, 0, 0, 0.12)) !important;
1128
+ margin: 4px 0 !important;
1129
+ }
837
1130
  `],
838
1131
  })
839
1132
  ], DataGridTableComponent);
@@ -107,9 +107,10 @@ PopoverCellEditorComponent = __decorate([
107
107
  display: flex; align-items: center; justify-content: center;
108
108
  }
109
109
  .ogrid-popover-editor-content {
110
- background: #fff; border-radius: 4px; padding: 16px;
111
- box-shadow: 0 4px 12px rgba(0,0,0,0.15);
110
+ background: var(--ogrid-bg, #ffffff); border-radius: 4px; padding: 16px;
111
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
112
112
  max-width: 90vw; max-height: 90vh; overflow: auto;
113
+ color: var(--ogrid-fg, rgba(0, 0, 0, 0.87));
113
114
  }
114
115
  `],
115
116
  })
@@ -18,7 +18,7 @@ let OGridComponent = class OGridComponent {
18
18
  this.service = inject(OGridService);
19
19
  this.propsSignal = signal(undefined);
20
20
  // Stable callback references (avoid re-creating every template eval)
21
- this.onColumnSortFn = (columnKey) => this.service.handleSort(columnKey);
21
+ this.onColumnSortFn = (columnKey, direction) => this.service.handleSort(columnKey, direction);
22
22
  this.onColumnResizedFn = (columnId, width) => this.service.handleColumnResized(columnId, width);
23
23
  this.onColumnPinnedFn = (columnId, pinned) => this.service.handleColumnPinned(columnId, pinned);
24
24
  this.onSelectionChangeFn = (event) => this.service.handleSelectionChange(event);
@@ -60,6 +60,7 @@ OGridComponent = __decorate([
60
60
  ],
61
61
  changeDetection: ChangeDetectionStrategy.OnPush,
62
62
  providers: [OGridService],
63
+ styles: [`:host { display: block; height: 100%; }`],
63
64
  template: `
64
65
  <ogrid-layout
65
66
  [className]="service.className()"
@@ -114,6 +115,7 @@ OGridComponent = __decorate([
114
115
  [getUserByEmail]="service.dataSource()?.getUserByEmail?.bind(service.dataSource())"
115
116
  [layoutMode]="service.layoutMode()"
116
117
  [suppressHorizontalScroll]="service.suppressHorizontalScroll()"
118
+ [columnReorder]="service.columnReorder()"
117
119
  [aria-label]="service.ariaLabel()"
118
120
  [aria-labelledby]="service.ariaLabelledBy()"
119
121
  [emptyState]="emptyStateObj"
@@ -9,7 +9,7 @@ export declare class DataGridTableComponent<T = unknown> extends BaseDataGridTab
9
9
  getRowIdInput: (item: T) => RowId;
10
10
  sortBy: string | undefined;
11
11
  sortDirection: 'asc' | 'desc';
12
- onColumnSort: (columnKey: string) => void;
12
+ onColumnSort: (columnKey: string, direction?: 'asc' | 'desc' | null) => void;
13
13
  visibleColumns: Set<string>;
14
14
  columnOrder: string[] | undefined;
15
15
  onColumnOrderChange: ((order: string[]) => void) | undefined;
@@ -21,6 +21,7 @@ export declare class DataGridTableComponent<T = unknown> extends BaseDataGridTab
21
21
  freezeColsInput: number | undefined;
22
22
  layoutMode: 'content' | 'fill';
23
23
  suppressHorizontalScroll: boolean | undefined;
24
+ columnReorder: boolean | undefined;
24
25
  isLoadingInput: boolean;
25
26
  loadingMessageInput: string;
26
27
  editable: boolean | undefined;
@@ -68,6 +69,13 @@ export declare class DataGridTableComponent<T = unknown> extends BaseDataGridTab
68
69
  statusBarLabel: string;
69
70
  statusBarValue: string;
70
71
  };
72
+ readonly contextMenuClasses: {
73
+ contextMenu: string;
74
+ contextMenuItem: string;
75
+ contextMenuItemLabel: string;
76
+ contextMenuItemShortcut: string;
77
+ contextMenuDivider: string;
78
+ };
71
79
  private readonly primengColumnSizingOverrides;
72
80
  private readonly propsSignal;
73
81
  private resizeStartX;
@@ -4,7 +4,7 @@ export declare class OGridComponent<T = unknown> {
4
4
  readonly service: OGridService<T>;
5
5
  private readonly propsSignal;
6
6
  set props(value: IOGridProps<T>);
7
- readonly onColumnSortFn: (columnKey: string) => void;
7
+ readonly onColumnSortFn: (columnKey: string, direction?: "asc" | "desc" | null) => void;
8
8
  readonly onColumnResizedFn: (columnId: string, width: number) => void;
9
9
  readonly onColumnPinnedFn: (columnId: string, pinned: "left" | "right" | null) => void;
10
10
  readonly onSelectionChangeFn: (event: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alaarab/ogrid-angular-primeng",
3
- "version": "2.0.11",
3
+ "version": "2.0.13",
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",
@@ -14,7 +14,9 @@
14
14
  },
15
15
  "scripts": {
16
16
  "build": "rimraf dist && tsc -p tsconfig.build.json",
17
- "test": "jest --passWithNoTests"
17
+ "test": "jest --passWithNoTests",
18
+ "storybook": "ng run angular-primeng:storybook",
19
+ "build-storybook": "ng run angular-primeng:build-storybook"
18
20
  },
19
21
  "keywords": [
20
22
  "ogrid",
@@ -35,7 +37,7 @@
35
37
  "node": ">=18"
36
38
  },
37
39
  "dependencies": {
38
- "@alaarab/ogrid-angular": "2.0.11"
40
+ "@alaarab/ogrid-angular": "2.0.13"
39
41
  },
40
42
  "peerDependencies": {
41
43
  "@angular/core": "^21.0.0",
@@ -49,10 +51,18 @@
49
51
  "@angular/platform-browser": "^21.1.4",
50
52
  "@angular/platform-browser-dynamic": "^21.1.4",
51
53
  "@angular/animations": "^21.1.4",
54
+ "@angular/router": "^21.1.4",
52
55
  "primeng": "^21.1.1",
53
56
  "rxjs": "^7.8.2",
54
57
  "zone.js": "^0.15.0",
55
- "typescript": "^5.9.3"
58
+ "typescript": "^5.9.3",
59
+ "@angular/cli": "^21.1.4",
60
+ "@angular/compiler-cli": "^21.1.4",
61
+ "@angular-devkit/core": "^21.1.4",
62
+ "@angular-devkit/architect": "^0.2101.4",
63
+ "@angular-devkit/build-angular": "^21.1.4",
64
+ "@storybook/angular": "^10.2.8",
65
+ "storybook": "^10.2.8"
56
66
  },
57
67
  "sideEffects": false,
58
68
  "publishConfig": {