@alaarab/ogrid-angular-primeng 2.0.11 → 2.0.12

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,15 @@ 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"
470
484
  [attr.aria-label]="resolvedAriaLabel()"
471
485
  [attr.aria-labelledby]="ariaLabelledBy()"
472
486
  [attr.data-empty]="showEmptyInGrid() ? 'true' : null"
@@ -479,26 +493,19 @@ DataGridTableComponent = __decorate([
479
493
  (keydown)="onGridKeyDown($event)"
480
494
  (mousedown)="onWrapperMouseDown($event)"
481
495
  (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
496
  [style.--data-table-column-count]="state().layout.totalColCount"
484
497
  [style.--data-table-width]="tableWidthStyle()"
485
498
  [style.--data-table-min-width]="tableMinWidthStyle()"
486
499
  >
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)">
500
+ <div class="ogrid-table-wrapper">
501
+ <div [class.loading-dimmed]="isLoading() && items().length > 0" class="ogrid-table-wrapper">
502
+ <div #tableContainer class="ogrid-table-wrapper">
503
+ <table class="ogrid-table">
504
+ <thead class="ogrid-thead">
494
505
  @for (row of headerRows(); track $index; let rowIdx = $index) {
495
506
  <tr>
496
507
  @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
- >
508
+ <th scope="col" rowSpan="1" class="ogrid-checkbox-header">
502
509
  <input
503
510
  type="checkbox"
504
511
  [checked]="allSelected()"
@@ -512,23 +519,19 @@ DataGridTableComponent = __decorate([
512
519
  <th [attr.rowSpan]="headerRows().length - 1"></th>
513
520
  }
514
521
  @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
- >
522
+ <th scope="col" rowSpan="1" class="ogrid-row-number-header">
520
523
  #
521
524
  </th>
522
525
  }
523
526
  @if (rowIdx === 0 && rowIdx < headerRows().length - 1 && hasRowNumbersCol()) {
524
- <th [attr.rowSpan]="headerRows().length - 1" style="width:50px;min-width:50px"></th>
527
+ <th [attr.rowSpan]="headerRows().length - 1" class="ogrid-row-number-spacer"></th>
525
528
  }
526
529
  @for (cell of row; track $index; let cellIdx = $index) {
527
530
  @if (cell.isGroup) {
528
531
  <th
529
532
  [attr.colSpan]="cell.colSpan"
530
533
  scope="colgroup"
531
- style="padding:6px 8px;text-align:center;font-weight:600;border-bottom:1px solid var(--ogrid-border, #e0e0e0)"
534
+ class="ogrid-column-group-header"
532
535
  >
533
536
  {{ cell.label }}
534
537
  </th>
@@ -537,11 +540,11 @@ DataGridTableComponent = __decorate([
537
540
  @let pinned = isPinned(col.columnId);
538
541
  <th
539
542
  scope="col"
543
+ class="ogrid-header-cell"
540
544
  [attr.data-column-id]="col.columnId"
541
545
  [attr.rowSpan]="headerRows().length > 1 && rowIdx < headerRows().length - 1 ? headerRows().length - rowIdx : null"
542
546
  [class.ogrid-th-pinned-left]="pinned === 'left'"
543
547
  [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
548
  [style.min-width.px]="col.minWidth ?? defaultMinWidth"
546
549
  [style.width.px]="getColumnWidth(col)"
547
550
  [style.max-width.px]="getColumnWidth(col)"
@@ -550,7 +553,7 @@ DataGridTableComponent = __decorate([
550
553
  [style.cursor]="columnReorderService.isDragging() ? 'grabbing' : 'grab'"
551
554
  (mousedown)="onHeaderMouseDown(col.columnId, $event)"
552
555
  >
553
- <div style="display:flex;align-items:center;gap:4px;">
556
+ <div class="ogrid-header-content">
554
557
  <ogrid-primeng-column-header-filter
555
558
  [columnKey]="col.columnId"
556
559
  [columnName]="col.name"
@@ -584,7 +587,7 @@ DataGridTableComponent = __decorate([
584
587
  />
585
588
  </div>
586
589
  <div
587
- style="position:absolute;top:0;right:0;bottom:0;width:4px;cursor:col-resize"
590
+ class="ogrid-resize-handle"
588
591
  (mousedown)="onResizeStartPrimeng($event, col)"
589
592
  [attr.aria-label]="'Resize ' + col.name"
590
593
  ></div>
@@ -611,7 +614,7 @@ DataGridTableComponent = __decorate([
611
614
  >
612
615
  @if (hasCheckboxCol()) {
613
616
  <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)"
617
+ class="ogrid-checkbox-cell"
615
618
  [attr.data-row-index]="rowIndex"
616
619
  [attr.data-col-index]="0"
617
620
  (click)="$event.stopPropagation()"
@@ -625,18 +628,18 @@ DataGridTableComponent = __decorate([
625
628
  </td>
626
629
  }
627
630
  @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
- >
631
+ <td class="ogrid-row-number-cell">
632
+
631
633
  {{ rowNumberOffset() + rowIndex + 1 }}
632
634
  </td>
633
635
  }
634
636
  @for (col of visibleCols(); track col.columnId; let colIdx = $index) {
635
637
  @let pinned = isPinned(col.columnId);
636
638
  <td
639
+ [attr.data-column-id]="col.columnId"
637
640
  [class.ogrid-td-pinned-left]="pinned === 'left'"
638
641
  [class.ogrid-td-pinned-right]="pinned === 'right'"
639
- style="padding:0;border-bottom:1px solid var(--ogrid-border, #f0f0f0);position:relative"
642
+ class="ogrid-data-cell"
640
643
  [style.min-width.px]="col.minWidth ?? defaultMinWidth"
641
644
  [style.width.px]="getColumnWidth(col)"
642
645
  [style.max-width.px]="getColumnWidth(col)"
@@ -672,7 +675,7 @@ DataGridTableComponent = __decorate([
672
675
  (mousedown)="onCellMouseDown($event, rowIndex, colIdx + colOffset())"
673
676
  (dblclick)="onCellDblClickPrimeng(item, col, rowIndex, colIdx)"
674
677
  (contextmenu)="onCellContextMenu($event)"
675
- style="padding:6px 10px;min-height:20px;cursor:default;overflow:hidden;text-overflow:ellipsis;white-space:nowrap"
678
+ class="ogrid-cell-content"
676
679
  [style.cursor]="canEditCell(col, item) ? 'cell' : 'default'"
677
680
  [style.background]="getCellBackground(rowIndex, colIdx)"
678
681
  [style.outline]="isActiveCell(rowIndex, colIdx) ? '2px solid var(--ogrid-selection, #217346)' : null"
@@ -682,7 +685,7 @@ DataGridTableComponent = __decorate([
682
685
  @if (canEditCell(col, item) && isSelectionEndCell(rowIndex, colIdx)) {
683
686
  <div
684
687
  (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"
688
+ class="ogrid-fill-handle"
686
689
  aria-label="Fill handle"
687
690
  ></div>
688
691
  }
@@ -712,9 +715,9 @@ DataGridTableComponent = __decorate([
712
715
  ></ogrid-marching-ants-overlay>
713
716
 
714
717
  @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)">
718
+ <div class="ogrid-empty-container">
716
719
  <div>
717
- <div style="font-weight:600;margin-bottom:8px">No results found</div>
720
+ <div class="ogrid-empty-title">No results found</div>
718
721
  <ogrid-empty-state
719
722
  [message]="emptyState()?.message"
720
723
  [hasActiveFilters]="emptyState()?.hasActiveFilters ?? false"
@@ -740,6 +743,7 @@ DataGridTableComponent = __decorate([
740
743
  [hasSelection]="hasCellSelection()"
741
744
  [canUndoProp]="canUndo()"
742
745
  [canRedoProp]="canRedo()"
746
+ [classNames]="contextMenuClasses"
743
747
  (copy)="handleCopy()"
744
748
  (cut)="handleCut()"
745
749
  (paste)="handlePaste()"
@@ -763,18 +767,229 @@ DataGridTableComponent = __decorate([
763
767
  }
764
768
 
765
769
  @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>
770
+ <div class="ogrid-loading-overlay" aria-live="polite">
771
+ <div class="ogrid-loading-content">
772
+ <span class="ogrid-loading-text">{{ loadingMessage() }}</span>
772
773
  </div>
773
774
  </div>
774
775
  }
775
776
  </div>
776
777
  `,
777
778
  styles: [`
779
+ /* ─── OGrid Theme Variables ─── */
780
+ :root {
781
+ --ogrid-bg: #ffffff;
782
+ --ogrid-fg: rgba(0, 0, 0, 0.87);
783
+ --ogrid-fg-secondary: rgba(0, 0, 0, 0.6);
784
+ --ogrid-fg-muted: rgba(0, 0, 0, 0.5);
785
+ --ogrid-border: rgba(0, 0, 0, 0.12);
786
+ --ogrid-header-bg: rgba(0, 0, 0, 0.04);
787
+ --ogrid-hover-bg: rgba(0, 0, 0, 0.04);
788
+ --ogrid-selected-row-bg: #e6f0fb;
789
+ --ogrid-active-cell-bg: rgba(0, 0, 0, 0.02);
790
+ --ogrid-range-bg: rgba(33, 115, 70, 0.12);
791
+ --ogrid-accent: #0078d4;
792
+ --ogrid-selection-color: #217346;
793
+ --ogrid-loading-overlay: rgba(255, 255, 255, 0.7);
794
+ }
795
+ @media (prefers-color-scheme: dark) {
796
+ :root:not([data-theme="light"]) {
797
+ --ogrid-bg: #1e1e1e;
798
+ --ogrid-fg: rgba(255, 255, 255, 0.87);
799
+ --ogrid-fg-secondary: rgba(255, 255, 255, 0.6);
800
+ --ogrid-fg-muted: rgba(255, 255, 255, 0.5);
801
+ --ogrid-border: rgba(255, 255, 255, 0.12);
802
+ --ogrid-header-bg: rgba(255, 255, 255, 0.06);
803
+ --ogrid-hover-bg: rgba(255, 255, 255, 0.08);
804
+ --ogrid-selected-row-bg: #1a3a5c;
805
+ --ogrid-active-cell-bg: rgba(255, 255, 255, 0.06);
806
+ --ogrid-range-bg: rgba(46, 160, 67, 0.15);
807
+ --ogrid-accent: #4da6ff;
808
+ --ogrid-selection-color: #2ea043;
809
+ --ogrid-loading-overlay: rgba(0, 0, 0, 0.7);
810
+ }
811
+ }
812
+ [data-theme="dark"] {
813
+ --ogrid-bg: #1e1e1e;
814
+ --ogrid-fg: rgba(255, 255, 255, 0.87);
815
+ --ogrid-fg-secondary: rgba(255, 255, 255, 0.6);
816
+ --ogrid-fg-muted: rgba(255, 255, 255, 0.5);
817
+ --ogrid-border: rgba(255, 255, 255, 0.12);
818
+ --ogrid-header-bg: rgba(255, 255, 255, 0.06);
819
+ --ogrid-hover-bg: rgba(255, 255, 255, 0.08);
820
+ --ogrid-selected-row-bg: #1a3a5c;
821
+ --ogrid-active-cell-bg: rgba(255, 255, 255, 0.06);
822
+ --ogrid-range-bg: rgba(46, 160, 67, 0.15);
823
+ --ogrid-accent: #4da6ff;
824
+ --ogrid-selection-color: #2ea043;
825
+ --ogrid-loading-overlay: rgba(0, 0, 0, 0.7);
826
+ }
827
+ :host { display: block; }
828
+ .ogrid-root {
829
+ position: relative;
830
+ flex: 1;
831
+ min-height: 0;
832
+ display: flex;
833
+ flex-direction: column;
834
+ overflow: hidden;
835
+ }
836
+ .ogrid-scroll-wrapper {
837
+ flex: 1;
838
+ min-height: 0;
839
+ overflow: auto;
840
+ position: relative;
841
+ background: var(--ogrid-bg, #ffffff);
842
+ color: var(--ogrid-fg, rgba(0, 0, 0, 0.87));
843
+ }
844
+ .ogrid-table-wrapper {
845
+ position: relative;
846
+ }
847
+ .ogrid-table {
848
+ width: var(--data-table-width, 100%);
849
+ min-width: var(--data-table-min-width, 100%);
850
+ border-collapse: collapse;
851
+ table-layout: fixed;
852
+ }
853
+ .ogrid-thead {
854
+ z-index: 3;
855
+ background: var(--ogrid-header-bg, #f5f5f5);
856
+ position: sticky;
857
+ top: 0;
858
+ }
859
+ .ogrid-checkbox-header {
860
+ width: 48px;
861
+ min-width: 48px;
862
+ max-width: 48px;
863
+ text-align: center;
864
+ background: var(--ogrid-header-bg, #f5f5f5);
865
+ border-bottom: 2px solid var(--ogrid-border, #e0e0e0);
866
+ position: sticky;
867
+ top: 0;
868
+ z-index: 3;
869
+ }
870
+ .ogrid-row-number-header {
871
+ width: 50px;
872
+ min-width: 50px;
873
+ max-width: 50px;
874
+ text-align: center;
875
+ font-weight: 600;
876
+ background: var(--ogrid-header-bg, #f5f5f5);
877
+ border-bottom: 2px solid var(--ogrid-border, #e0e0e0);
878
+ position: sticky;
879
+ top: 0;
880
+ z-index: 3;
881
+ }
882
+ .ogrid-row-number-spacer {
883
+ width: 50px;
884
+ min-width: 50px;
885
+ max-width: 50px;
886
+ background: var(--ogrid-header-bg, #f5f5f5);
887
+ }
888
+ .ogrid-column-group-header {
889
+ text-align: center;
890
+ font-weight: 600;
891
+ background: var(--ogrid-header-bg, #f5f5f5);
892
+ border-bottom: 2px solid var(--ogrid-border, #e0e0e0);
893
+ padding: 6px 10px;
894
+ }
895
+ .ogrid-header-cell {
896
+ background: var(--ogrid-header-bg, #f5f5f5);
897
+ border-bottom: 2px solid var(--ogrid-border, #e0e0e0);
898
+ padding: 0;
899
+ position: relative;
900
+ user-select: none;
901
+ }
902
+ .ogrid-header-content {
903
+ display: flex;
904
+ align-items: center;
905
+ gap: 4px;
906
+ padding: 6px 10px;
907
+ }
908
+ .ogrid-resize-handle {
909
+ position: absolute;
910
+ top: 0;
911
+ right: 0;
912
+ bottom: 0;
913
+ width: 4px;
914
+ cursor: col-resize;
915
+ }
916
+ .ogrid-checkbox-cell {
917
+ width: 48px;
918
+ min-width: 48px;
919
+ max-width: 48px;
920
+ padding: 6px 4px;
921
+ text-align: center;
922
+ border-bottom: 1px solid var(--ogrid-border, #f0f0f0);
923
+ }
924
+ .ogrid-row-number-cell {
925
+ width: 50px;
926
+ min-width: 50px;
927
+ max-width: 50px;
928
+ padding: 6px;
929
+ text-align: center;
930
+ font-weight: 600;
931
+ font-variant-numeric: tabular-nums;
932
+ color: var(--ogrid-fg-secondary, rgba(0, 0, 0, 0.6));
933
+ background: var(--ogrid-header-bg, rgba(0, 0, 0, 0.04));
934
+ border-bottom: 1px solid var(--ogrid-border, #f0f0f0);
935
+ position: sticky;
936
+ left: 0;
937
+ z-index: 3;
938
+ }
939
+ .ogrid-data-cell {
940
+ padding: 0;
941
+ border-bottom: 1px solid var(--ogrid-border, #f0f0f0);
942
+ position: relative;
943
+ }
944
+ .ogrid-cell-content {
945
+ padding: 6px 10px;
946
+ min-height: 20px;
947
+ cursor: default;
948
+ overflow: hidden;
949
+ text-overflow: ellipsis;
950
+ white-space: nowrap;
951
+ }
952
+ .ogrid-scroll-wrapper [data-drag-range] { background: var(--ogrid-range-bg, rgba(33, 115, 70, 0.12)) !important; }
953
+ .ogrid-fill-handle {
954
+ position: absolute;
955
+ bottom: -3px;
956
+ right: -3px;
957
+ width: 7px;
958
+ height: 7px;
959
+ background: var(--ogrid-selection, #217346);
960
+ cursor: crosshair;
961
+ z-index: 2;
962
+ }
963
+ .ogrid-empty-container {
964
+ display: flex;
965
+ align-items: center;
966
+ justify-content: center;
967
+ padding: 48px 24px;
968
+ text-align: center;
969
+ color: var(--ogrid-fg-muted, rgba(0, 0, 0, 0.5));
970
+ }
971
+ .ogrid-empty-title {
972
+ font-weight: 600;
973
+ margin-bottom: 8px;
974
+ }
975
+ .ogrid-loading-overlay {
976
+ position: absolute;
977
+ inset: 0;
978
+ display: flex;
979
+ align-items: center;
980
+ justify-content: center;
981
+ background: var(--ogrid-loading-overlay, rgba(255, 255, 255, 0.7));
982
+ z-index: 10;
983
+ }
984
+ .ogrid-loading-content {
985
+ display: flex;
986
+ align-items: center;
987
+ gap: 8px;
988
+ color: var(--ogrid-fg, #242424);
989
+ }
990
+ .ogrid-loading-text {
991
+ font-size: 14px;
992
+ }
778
993
  .loading-dimmed {
779
994
  opacity: 0.5;
780
995
  pointer-events: none;
@@ -830,10 +1045,86 @@ DataGridTableComponent = __decorate([
830
1045
  outline: 2px solid var(--primary-color, #6366f1);
831
1046
  outline-offset: 2px;
832
1047
  }
1048
+
1049
+ /* Context menu */
1050
+ .ogrid-context-menu {
1051
+ position: fixed;
1052
+ z-index: 10000;
1053
+ min-width: 160px;
1054
+ padding: 4px 0;
1055
+ background: var(--ogrid-bg, #fff);
1056
+ border: 1px solid var(--ogrid-border, rgba(0, 0, 0, 0.12));
1057
+ border-radius: 6px;
1058
+ box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);
1059
+ }
1060
+ .ogrid-context-menu-item {
1061
+ display: flex;
1062
+ align-items: center;
1063
+ justify-content: space-between;
1064
+ gap: 24px;
1065
+ width: 100%;
1066
+ padding: 6px 12px;
1067
+ border: none;
1068
+ background: none;
1069
+ font-size: 13px;
1070
+ text-align: left;
1071
+ cursor: pointer;
1072
+ color: var(--ogrid-fg, rgba(0, 0, 0, 0.87));
1073
+ }
1074
+ .ogrid-context-menu-item:hover:not(:disabled) {
1075
+ background: var(--ogrid-hover-bg, rgba(0, 0, 0, 0.04));
1076
+ }
1077
+ .ogrid-context-menu-item:disabled {
1078
+ opacity: 0.5;
1079
+ cursor: not-allowed;
1080
+ }
1081
+ .ogrid-context-menu-item-label {
1082
+ flex: 1;
1083
+ }
1084
+ .ogrid-context-menu-item-shortcut {
1085
+ color: var(--ogrid-fg-muted, rgba(0, 0, 0, 0.5));
1086
+ font-size: 0.85em;
1087
+ }
1088
+ .ogrid-context-menu-divider {
1089
+ height: 1px;
1090
+ margin: 4px 0;
1091
+ background: var(--ogrid-border, rgba(0, 0, 0, 0.12));
1092
+ }
833
1093
  ::ng-deep .p-checkbox:focus-visible {
834
1094
  outline: 2px solid var(--primary-color, #6366f1);
835
1095
  outline-offset: 2px;
836
1096
  }
1097
+
1098
+ /* PrimeNG Menu popup overrides — must use !important to win over PrimeNG's CSS-variable-based defaults */
1099
+ .p-menu {
1100
+ background: var(--ogrid-bg, #ffffff) !important;
1101
+ color: var(--ogrid-fg, rgba(0, 0, 0, 0.87)) !important;
1102
+ border: 1px solid var(--ogrid-border, rgba(0, 0, 0, 0.12)) !important;
1103
+ border-radius: 4px !important;
1104
+ padding: 4px 0 !important;
1105
+ }
1106
+ .p-menu-overlay {
1107
+ 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;
1108
+ }
1109
+ .p-menu-item-content {
1110
+ color: var(--ogrid-fg, rgba(0, 0, 0, 0.87)) !important;
1111
+ }
1112
+ .p-menu-item-link {
1113
+ color: var(--ogrid-fg, rgba(0, 0, 0, 0.87)) !important;
1114
+ padding: 6px 12px !important;
1115
+ }
1116
+ .p-menu-item-label {
1117
+ color: var(--ogrid-fg, rgba(0, 0, 0, 0.87)) !important;
1118
+ font-size: 0.875rem !important;
1119
+ }
1120
+ .p-menu-item:not(.p-disabled) .p-menu-item-content:hover {
1121
+ background: var(--ogrid-hover-bg, rgba(0, 0, 0, 0.04)) !important;
1122
+ color: var(--ogrid-fg, rgba(0, 0, 0, 0.87)) !important;
1123
+ }
1124
+ .p-menu-separator {
1125
+ border-color: var(--ogrid-border, rgba(0, 0, 0, 0.12)) !important;
1126
+ margin: 4px 0 !important;
1127
+ }
837
1128
  `],
838
1129
  })
839
1130
  ], 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.12",
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.12"
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": {