@alaarab/ogrid-angular 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.
@@ -2,7 +2,7 @@ import { signal, computed, effect } from '@angular/core';
2
2
  import { DataGridStateService } from '../services/datagrid-state.service';
3
3
  import { ColumnReorderService } from '../services/column-reorder.service';
4
4
  import { VirtualScrollService } from '../services/virtual-scroll.service';
5
- import { buildHeaderRows, DEFAULT_MIN_COLUMN_WIDTH, CELL_PADDING, CHECKBOX_COLUMN_WIDTH, ROW_NUMBER_COLUMN_WIDTH, } from '@alaarab/ogrid-core';
5
+ import { buildHeaderRows, DEFAULT_MIN_COLUMN_WIDTH, CELL_PADDING, CHECKBOX_COLUMN_WIDTH, ROW_NUMBER_COLUMN_WIDTH, measureColumnContentWidth, } from '@alaarab/ogrid-core';
6
6
  import { getHeaderFilterConfig, getCellRenderDescriptor, resolveCellDisplayContent, resolveCellStyle, buildPopoverEditorProps, } from '../utils';
7
7
  /**
8
8
  * Abstract base class containing all shared TypeScript logic for DataGridTable components.
@@ -209,7 +209,7 @@ export class BaseDataGridTableComponent {
209
209
  this.columnReorderService.columns.set(cols);
210
210
  this.columnReorderService.columnOrder.set(p.columnOrder);
211
211
  this.columnReorderService.onColumnOrderChange.set(p.onColumnOrderChange);
212
- this.columnReorderService.enabled.set(!!p.onColumnOrderChange);
212
+ this.columnReorderService.enabled.set(p.columnReorder === true);
213
213
  }
214
214
  });
215
215
  // Wire virtual scroll service inputs
@@ -428,16 +428,18 @@ export class BaseDataGridTableComponent {
428
428
  // --- Column sorting methods ---
429
429
  onSortAsc(columnId) {
430
430
  const props = this.getProps();
431
- props?.onColumnSort?.(columnId);
431
+ props?.onColumnSort?.(columnId, 'asc');
432
432
  }
433
433
  onSortDesc(columnId) {
434
434
  const props = this.getProps();
435
- props?.onColumnSort?.(columnId);
435
+ props?.onColumnSort?.(columnId, 'desc');
436
436
  }
437
- onClearSort() {
438
- // Clearing sort is handled by sorting the same column again
439
- // The logic in OGridService.handleSort will toggle: asc -> desc -> (clear via callback)
440
- // For now, we don't have an explicit clear, so we just don't call anything
437
+ onClearSort(columnId) {
438
+ const props = this.getProps();
439
+ const col = columnId ?? props?.sortBy;
440
+ if (col) {
441
+ props?.onColumnSort?.(col, null);
442
+ }
441
443
  }
442
444
  getSortState(columnId) {
443
445
  const props = this.getProps();
@@ -451,7 +453,7 @@ export class BaseDataGridTableComponent {
451
453
  const col = this.visibleCols().find((c) => c.columnId === columnId);
452
454
  if (!col)
453
455
  return;
454
- const width = this.measureColumnContentWidth(columnId);
456
+ const width = measureColumnContentWidth(columnId, col.minWidth, this.tableContainerEl() ?? undefined);
455
457
  this.state().layout.setColumnSizingOverrides({
456
458
  ...this.columnSizingOverrides(),
457
459
  [columnId]: { widthPx: width },
@@ -459,9 +461,10 @@ export class BaseDataGridTableComponent {
459
461
  this.state().layout.onColumnResized?.(columnId, width);
460
462
  }
461
463
  onAutosizeAllColumns() {
464
+ const tableEl = this.tableContainerEl() ?? undefined;
462
465
  const overrides = {};
463
466
  for (const col of this.visibleCols()) {
464
- const width = this.measureColumnContentWidth(col.columnId);
467
+ const width = measureColumnContentWidth(col.columnId, col.minWidth, tableEl);
465
468
  overrides[col.columnId] = { widthPx: width };
466
469
  this.state().layout.onColumnResized?.(col.columnId, width);
467
470
  }
@@ -470,18 +473,4 @@ export class BaseDataGridTableComponent {
470
473
  ...overrides,
471
474
  });
472
475
  }
473
- measureColumnContentWidth(columnId) {
474
- const tableEl = this.tableContainerEl();
475
- if (!tableEl)
476
- return DEFAULT_MIN_COLUMN_WIDTH;
477
- const cells = Array.from(tableEl.querySelectorAll(`[data-column-id="${columnId}"]`));
478
- if (cells.length === 0)
479
- return DEFAULT_MIN_COLUMN_WIDTH;
480
- let maxWidth = DEFAULT_MIN_COLUMN_WIDTH;
481
- for (const cell of cells) {
482
- const rect = cell.getBoundingClientRect();
483
- maxWidth = Math.max(maxWidth, Math.ceil(rect.width) + CELL_PADDING);
484
- }
485
- return maxWidth;
486
- }
487
476
  }
@@ -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 } from '@angular/core';
7
+ import { Component, Input, ViewEncapsulation } from '@angular/core';
8
8
  import { CommonModule } from '@angular/common';
9
9
  import { SideBarComponent } from './sidebar.component';
10
10
  import { GRID_BORDER_RADIUS } from '@alaarab/ogrid-core';
@@ -36,32 +36,83 @@ OGridLayoutComponent = __decorate([
36
36
  Component({
37
37
  selector: 'ogrid-layout',
38
38
  standalone: true,
39
+ encapsulation: ViewEncapsulation.None,
39
40
  imports: [CommonModule, SideBarComponent],
40
41
  styles: [`
42
+ /* ─── OGrid Theme Variables ─── */
43
+ :root {
44
+ --ogrid-bg: #ffffff;
45
+ --ogrid-fg: rgba(0, 0, 0, 0.87);
46
+ --ogrid-fg-secondary: rgba(0, 0, 0, 0.6);
47
+ --ogrid-fg-muted: rgba(0, 0, 0, 0.5);
48
+ --ogrid-border: rgba(0, 0, 0, 0.12);
49
+ --ogrid-header-bg: rgba(0, 0, 0, 0.04);
50
+ --ogrid-hover-bg: rgba(0, 0, 0, 0.04);
51
+ --ogrid-selected-row-bg: #e6f0fb;
52
+ --ogrid-active-cell-bg: rgba(0, 0, 0, 0.02);
53
+ --ogrid-range-bg: rgba(33, 115, 70, 0.12);
54
+ --ogrid-accent: #0078d4;
55
+ --ogrid-selection-color: #217346;
56
+ --ogrid-loading-overlay: rgba(255, 255, 255, 0.7);
57
+ }
58
+ @media (prefers-color-scheme: dark) {
59
+ :root:not([data-theme="light"]) {
60
+ --ogrid-bg: #1e1e1e;
61
+ --ogrid-fg: rgba(255, 255, 255, 0.87);
62
+ --ogrid-fg-secondary: rgba(255, 255, 255, 0.6);
63
+ --ogrid-fg-muted: rgba(255, 255, 255, 0.5);
64
+ --ogrid-border: rgba(255, 255, 255, 0.12);
65
+ --ogrid-header-bg: rgba(255, 255, 255, 0.06);
66
+ --ogrid-hover-bg: rgba(255, 255, 255, 0.08);
67
+ --ogrid-selected-row-bg: #1a3a5c;
68
+ --ogrid-active-cell-bg: rgba(255, 255, 255, 0.06);
69
+ --ogrid-range-bg: rgba(46, 160, 67, 0.15);
70
+ --ogrid-accent: #4da6ff;
71
+ --ogrid-selection-color: #2ea043;
72
+ --ogrid-loading-overlay: rgba(0, 0, 0, 0.7);
73
+ }
74
+ }
75
+ [data-theme="dark"] {
76
+ --ogrid-bg: #1e1e1e;
77
+ --ogrid-fg: rgba(255, 255, 255, 0.87);
78
+ --ogrid-fg-secondary: rgba(255, 255, 255, 0.6);
79
+ --ogrid-fg-muted: rgba(255, 255, 255, 0.5);
80
+ --ogrid-border: rgba(255, 255, 255, 0.12);
81
+ --ogrid-header-bg: rgba(255, 255, 255, 0.06);
82
+ --ogrid-hover-bg: rgba(255, 255, 255, 0.08);
83
+ --ogrid-selected-row-bg: #1a3a5c;
84
+ --ogrid-active-cell-bg: rgba(255, 255, 255, 0.06);
85
+ --ogrid-range-bg: rgba(46, 160, 67, 0.15);
86
+ --ogrid-accent: #4da6ff;
87
+ --ogrid-selection-color: #2ea043;
88
+ --ogrid-loading-overlay: rgba(0, 0, 0, 0.7);
89
+ }
90
+ :host { display: block; height: 100%; }
41
91
  .ogrid-layout-root { display: flex; flex-direction: column; height: 100%; }
42
92
  .ogrid-layout-container {
43
- border: 1px solid var(--ogrid-border, #e0e0e0);
93
+ border: 1px solid var(--ogrid-border, rgba(0, 0, 0, 0.12));
44
94
  overflow: hidden; display: flex; flex-direction: column;
45
- flex: 1; min-height: 0; background: var(--ogrid-bg, #fff);
95
+ flex: 1; min-height: 0; background: var(--ogrid-bg, #ffffff);
96
+ color: var(--ogrid-fg, rgba(0, 0, 0, 0.87));
46
97
  }
47
98
  .ogrid-layout-toolbar {
48
99
  display: flex; justify-content: space-between; align-items: center;
49
- padding: 6px 12px; background: var(--ogrid-header-bg, #f5f5f5);
100
+ padding: 6px 12px; background: var(--ogrid-header-bg, rgba(0, 0, 0, 0.04));
50
101
  gap: 8px; flex-wrap: wrap; min-height: 0;
51
102
  }
52
103
  .ogrid-layout-toolbar--has-below { border-bottom: none; }
53
- .ogrid-layout-toolbar--no-below { border-bottom: 1px solid var(--ogrid-border, #e0e0e0); }
104
+ .ogrid-layout-toolbar--no-below { border-bottom: 1px solid var(--ogrid-border, rgba(0, 0, 0, 0.12)); }
54
105
  .ogrid-layout-toolbar-left { display: flex; align-items: center; gap: 8px; }
55
106
  .ogrid-layout-toolbar-right { display: flex; align-items: center; gap: 8px; }
56
107
  .ogrid-layout-toolbar-below {
57
- border-bottom: 1px solid var(--ogrid-border, #e0e0e0);
58
- padding: 6px 12px; background: var(--ogrid-header-bg, #f5f5f5);
108
+ border-bottom: 1px solid var(--ogrid-border, rgba(0, 0, 0, 0.12));
109
+ padding: 6px 12px; background: var(--ogrid-header-bg, rgba(0, 0, 0, 0.04));
59
110
  }
60
111
  .ogrid-layout-grid-area { width: 100%; min-width: 0; min-height: 0; flex: 1; display: flex; }
61
- .ogrid-layout-grid-content { flex: 1; min-width: 0; min-height: 0; display: flex; flex-direction: column; overflow: hidden; }
112
+ .ogrid-layout-grid-content { flex: 1; min-width: 0; min-height: 0; display: flex; flex-direction: column; }
62
113
  .ogrid-layout-footer {
63
- border-top: 1px solid var(--ogrid-border, #e0e0e0);
64
- background: var(--ogrid-header-bg, #f5f5f5); padding: 6px 12px;
114
+ border-top: 1px solid var(--ogrid-border, rgba(0, 0, 0, 0.12));
115
+ background: var(--ogrid-header-bg, rgba(0, 0, 0, 0.04)); padding: 6px 12px;
65
116
  }
66
117
  `],
67
118
  template: `
@@ -90,24 +141,18 @@ OGridLayoutComponent = __decorate([
90
141
  </div>
91
142
  }
92
143
 
93
- <!-- Grid area -->
94
- @if (sideBar) {
95
- <div class="ogrid-layout-grid-area">
96
- @if (sideBar?.position === 'left') {
97
- <ogrid-sidebar [sideBarProps]="sideBar"></ogrid-sidebar>
98
- }
99
- <div class="ogrid-layout-grid-content">
100
- <ng-content></ng-content>
101
- </div>
102
- @if (sideBar?.position !== 'left') {
103
- <ogrid-sidebar [sideBarProps]="sideBar"></ogrid-sidebar>
104
- }
105
- </div>
106
- } @else {
144
+ <!-- Grid area (single ng-content to avoid Angular content projection issues) -->
145
+ <div class="ogrid-layout-grid-area">
146
+ @if (sideBar && sideBar.position === 'left') {
147
+ <ogrid-sidebar [sideBarProps]="sideBar"></ogrid-sidebar>
148
+ }
107
149
  <div class="ogrid-layout-grid-content">
108
150
  <ng-content></ng-content>
109
151
  </div>
110
- }
152
+ @if (sideBar && sideBar.position !== 'left') {
153
+ <ogrid-sidebar [sideBarProps]="sideBar"></ogrid-sidebar>
154
+ }
155
+ </div>
111
156
 
112
157
  <!-- Footer strip (pagination) -->
113
158
  @if (hasPagination) {
@@ -106,56 +106,56 @@ SideBarComponent = __decorate([
106
106
  .ogrid-sidebar-tab-strip {
107
107
  display: flex; flex-direction: column;
108
108
  width: var(--ogrid-sidebar-tab-size, 36px);
109
- background: var(--ogrid-header-bg, #f5f5f5);
109
+ background: var(--ogrid-header-bg, rgba(0, 0, 0, 0.04));
110
110
  }
111
- .ogrid-sidebar-tab-strip--left { border-right: 1px solid var(--ogrid-border, #e0e0e0); }
112
- .ogrid-sidebar-tab-strip--right { border-left: 1px solid var(--ogrid-border, #e0e0e0); }
111
+ .ogrid-sidebar-tab-strip--left { border-right: 1px solid var(--ogrid-border, rgba(0, 0, 0, 0.12)); }
112
+ .ogrid-sidebar-tab-strip--right { border-left: 1px solid var(--ogrid-border, rgba(0, 0, 0, 0.12)); }
113
113
  .ogrid-sidebar-tab {
114
114
  width: var(--ogrid-sidebar-tab-size, 36px);
115
115
  height: var(--ogrid-sidebar-tab-size, 36px);
116
116
  border: none; cursor: pointer;
117
- color: var(--ogrid-fg, #242424); font-size: 14px;
117
+ color: var(--ogrid-fg, rgba(0, 0, 0, 0.87)); font-size: 14px;
118
118
  display: flex; align-items: center; justify-content: center;
119
119
  background: transparent; font-weight: normal;
120
120
  }
121
- .ogrid-sidebar-tab--active { background: var(--ogrid-bg, #fff); font-weight: bold; }
121
+ .ogrid-sidebar-tab--active { background: var(--ogrid-bg, #ffffff); font-weight: bold; }
122
122
  .ogrid-sidebar-panel {
123
123
  width: var(--ogrid-sidebar-panel-width, 240px);
124
124
  display: flex; flex-direction: column; overflow: hidden;
125
- background: var(--ogrid-bg, #fff); color: var(--ogrid-fg, #242424);
125
+ background: var(--ogrid-bg, #ffffff); color: var(--ogrid-fg, rgba(0, 0, 0, 0.87));
126
126
  }
127
- .ogrid-sidebar-panel--left { border-right: 1px solid var(--ogrid-border, #e0e0e0); }
128
- .ogrid-sidebar-panel--right { border-left: 1px solid var(--ogrid-border, #e0e0e0); }
127
+ .ogrid-sidebar-panel--left { border-right: 1px solid var(--ogrid-border, rgba(0, 0, 0, 0.12)); }
128
+ .ogrid-sidebar-panel--right { border-left: 1px solid var(--ogrid-border, rgba(0, 0, 0, 0.12)); }
129
129
  .ogrid-sidebar-panel-header {
130
130
  display: flex; justify-content: space-between; align-items: center;
131
- padding: 8px 12px; border-bottom: 1px solid var(--ogrid-border, #e0e0e0); font-weight: 600;
131
+ padding: 8px 12px; border-bottom: 1px solid var(--ogrid-border, rgba(0, 0, 0, 0.12)); font-weight: 600;
132
132
  }
133
133
  .ogrid-sidebar-panel-close {
134
134
  border: none; background: transparent; cursor: pointer;
135
- font-size: 16px; color: var(--ogrid-fg, #242424);
135
+ font-size: 16px; color: var(--ogrid-fg, rgba(0, 0, 0, 0.87));
136
136
  }
137
137
  .ogrid-sidebar-panel-body { flex: 1; overflow-y: auto; padding: 8px 12px; }
138
138
  .ogrid-sidebar-actions { display: flex; gap: 8px; margin-bottom: 8px; }
139
139
  .ogrid-sidebar-action-btn {
140
140
  flex: 1; cursor: pointer;
141
- background: var(--ogrid-bg-subtle, #f3f2f1); color: var(--ogrid-fg, #242424);
142
- border: 1px solid var(--ogrid-border, #e0e0e0); border-radius: 4px; padding: 4px 8px;
141
+ background: var(--ogrid-header-bg, rgba(0, 0, 0, 0.04)); color: var(--ogrid-fg, rgba(0, 0, 0, 0.87));
142
+ border: 1px solid var(--ogrid-border, rgba(0, 0, 0, 0.12)); border-radius: 4px; padding: 4px 8px;
143
143
  }
144
144
  .ogrid-sidebar-col-label { display: flex; align-items: center; gap: 6px; padding: 2px 0; cursor: pointer; }
145
- .ogrid-sidebar-empty { color: var(--ogrid-muted, #999); font-style: italic; }
145
+ .ogrid-sidebar-empty { color: var(--ogrid-fg-muted, rgba(0, 0, 0, 0.5)); font-style: italic; }
146
146
  .ogrid-sidebar-filter-group { margin-bottom: 12px; }
147
147
  .ogrid-sidebar-filter-label { font-weight: 500; margin-bottom: 4px; font-size: 13px; }
148
148
  .ogrid-sidebar-text-input {
149
149
  width: 100%; box-sizing: border-box; padding: 4px 6px;
150
- background: var(--ogrid-bg, #fff); color: var(--ogrid-fg, #242424);
151
- border: 1px solid var(--ogrid-border, #e0e0e0); border-radius: 4px;
150
+ background: var(--ogrid-bg, #ffffff); color: var(--ogrid-fg, rgba(0, 0, 0, 0.87));
151
+ border: 1px solid var(--ogrid-border, rgba(0, 0, 0, 0.12)); border-radius: 4px;
152
152
  }
153
153
  .ogrid-sidebar-date-row { display: flex; flex-direction: column; gap: 4px; }
154
154
  .ogrid-sidebar-date-label { display: flex; align-items: center; gap: 4px; font-size: 12px; }
155
155
  .ogrid-sidebar-date-input {
156
156
  flex: 1; padding: 2px 4px;
157
- background: var(--ogrid-bg, #fff); color: var(--ogrid-fg, #242424);
158
- border: 1px solid var(--ogrid-border, #e0e0e0); border-radius: 4px;
157
+ background: var(--ogrid-bg, #ffffff); color: var(--ogrid-fg, rgba(0, 0, 0, 0.87));
158
+ border: 1px solid var(--ogrid-border, rgba(0, 0, 0, 0.12)); border-radius: 4px;
159
159
  }
160
160
  .ogrid-sidebar-multiselect-list { max-height: 120px; overflow-y: auto; }
161
161
  .ogrid-sidebar-multiselect-item {
@@ -220,7 +220,7 @@ SideBarComponent = __decorate([
220
220
  </div>
221
221
  @for (col of sideBarProps?.columns ?? []; track col.columnId) {
222
222
  <label class="ogrid-sidebar-col-label">
223
- <input type="checkbox" [checked]="sideBarProps?.visibleColumns?.has(col.columnId)" (change)="onVisibilityChange(col.columnId, ($event.target as HTMLInputElement).checked)" [disabled]="col.required" />
223
+ <input type="checkbox" [checked]="sideBarProps?.visibleColumns?.has(col.columnId)" (change)="onVisibilityChange(col.columnId, $any($event.target).checked)" [disabled]="col.required" />
224
224
  <span>{{ col.name }}</span>
225
225
  </label>
226
226
  }
@@ -237,7 +237,7 @@ SideBarComponent = __decorate([
237
237
  type="text"
238
238
  class="ogrid-sidebar-text-input"
239
239
  [value]="getTextFilterValue(col.filterField)"
240
- (input)="onTextFilterChange(col.filterField, ($event.target as HTMLInputElement).value)"
240
+ (input)="onTextFilterChange(col.filterField, $any($event.target).value)"
241
241
  [placeholder]="'Filter ' + col.name + '...'"
242
242
  [attr.aria-label]="'Filter ' + col.name"
243
243
  />
@@ -246,11 +246,11 @@ SideBarComponent = __decorate([
246
246
  <div class="ogrid-sidebar-date-row">
247
247
  <label class="ogrid-sidebar-date-label">
248
248
  From:
249
- <input type="date" class="ogrid-sidebar-date-input" [value]="getDateFrom(col.filterField)" (change)="onDateFromChange(col.filterField, ($event.target as HTMLInputElement).value)" [attr.aria-label]="col.name + ' from date'" />
249
+ <input type="date" class="ogrid-sidebar-date-input" [value]="getDateFrom(col.filterField)" (change)="onDateFromChange(col.filterField, $any($event.target).value)" [attr.aria-label]="col.name + ' from date'" />
250
250
  </label>
251
251
  <label class="ogrid-sidebar-date-label">
252
252
  To:
253
- <input type="date" class="ogrid-sidebar-date-input" [value]="getDateTo(col.filterField)" (change)="onDateToChange(col.filterField, ($event.target as HTMLInputElement).value)" [attr.aria-label]="col.name + ' to date'" />
253
+ <input type="date" class="ogrid-sidebar-date-input" [value]="getDateTo(col.filterField)" (change)="onDateToChange(col.filterField, $any($event.target).value)" [attr.aria-label]="col.name + ' to date'" />
254
254
  </label>
255
255
  </div>
256
256
  }
@@ -258,7 +258,7 @@ SideBarComponent = __decorate([
258
258
  <div class="ogrid-sidebar-multiselect-list" role="group" [attr.aria-label]="col.name + ' options'">
259
259
  @for (opt of getFilterOptions(col.filterField); track opt) {
260
260
  <label class="ogrid-sidebar-multiselect-item">
261
- <input type="checkbox" [checked]="isMultiSelectChecked(col.filterField, opt)" (change)="onMultiSelectChange(col.filterField, opt, ($event.target as HTMLInputElement).checked)" />
261
+ <input type="checkbox" [checked]="isMultiSelectChecked(col.filterField, opt)" (change)="onMultiSelectChange(col.filterField, opt, $any($event.target).checked)" />
262
262
  <span>{{ opt }}</span>
263
263
  </label>
264
264
  }
@@ -1081,7 +1081,7 @@ let DataGridStateService = class DataGridStateService {
1081
1081
  headerFilterInput: {
1082
1082
  sortBy: p?.sortBy,
1083
1083
  sortDirection: p?.sortDirection ?? 'asc',
1084
- onColumnSort: (columnKey) => p?.onColumnSort(columnKey),
1084
+ onColumnSort: (columnKey, direction) => p?.onColumnSort(columnKey, direction),
1085
1085
  filters: p?.filters ?? {},
1086
1086
  onFilterChange: (key, value) => p?.onFilterChange(key, value),
1087
1087
  filterOptions: p?.filterOptions ?? {},
@@ -5,7 +5,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
5
5
  return c > 3 && r && Object.defineProperty(target, key, r), r;
6
6
  };
7
7
  import { Injectable, signal, computed, effect, DestroyRef, inject } from '@angular/core';
8
- import { mergeFilter, deriveFilterOptionsFromData, getMultiSelectFilterFields, flattenColumns, processClientSideData, } from '@alaarab/ogrid-core';
8
+ import { mergeFilter, deriveFilterOptionsFromData, getMultiSelectFilterFields, flattenColumns, processClientSideData, computeNextSortState, } from '@alaarab/ogrid-core';
9
9
  const DEFAULT_PAGE_SIZE = 25;
10
10
  const EMPTY_LOADING_OPTIONS = {};
11
11
  const DEFAULT_PANELS = ['columns', 'filters'];
@@ -67,6 +67,7 @@ let OGridService = class OGridService {
67
67
  this.onFirstDataRendered = signal(undefined);
68
68
  this.onError = signal(undefined);
69
69
  this.columnChooserProp = signal(undefined);
70
+ this.columnReorder = signal(undefined);
70
71
  this.virtualScroll = signal(undefined);
71
72
  this.ariaLabel = signal(undefined);
72
73
  this.ariaLabelledBy = signal(undefined);
@@ -215,7 +216,7 @@ let OGridService = class OGridService {
215
216
  getRowId: this.getRowId(),
216
217
  sortBy: this.sort().field,
217
218
  sortDirection: this.sort().direction,
218
- onColumnSort: (columnKey) => this.handleSort(columnKey),
219
+ onColumnSort: (columnKey, direction) => this.handleSort(columnKey, direction),
219
220
  visibleColumns: this.visibleColumns(),
220
221
  columnOrder: this.columnOrder(),
221
222
  onColumnOrderChange: this.onColumnOrderChange(),
@@ -246,6 +247,7 @@ let OGridService = class OGridService {
246
247
  getUserByEmail: this.dataSource()?.getUserByEmail?.bind(this.dataSource()),
247
248
  layoutMode: this.layoutMode(),
248
249
  suppressHorizontalScroll: this.suppressHorizontalScroll(),
250
+ columnReorder: this.columnReorder(),
249
251
  virtualScroll: this.virtualScroll(),
250
252
  'aria-label': this.ariaLabel(),
251
253
  'aria-labelledby': this.ariaLabelledBy(),
@@ -401,22 +403,8 @@ let OGridService = class OGridService {
401
403
  this.internalVisibleColumnsOverride.set(cols);
402
404
  this.onVisibleColumnsChange()?.(cols);
403
405
  }
404
- handleSort(columnKey) {
405
- const sort = this.sort();
406
- if (sort.field === columnKey) {
407
- // Cycle: asc → desc → clear
408
- if (sort.direction === 'asc') {
409
- this.setSort({ field: columnKey, direction: 'desc' });
410
- }
411
- else {
412
- // Clear sort (empty field means no column is sorted)
413
- this.setSort({ field: '', direction: 'asc' });
414
- }
415
- }
416
- else {
417
- // Start new sort
418
- this.setSort({ field: columnKey, direction: 'asc' });
419
- }
406
+ handleSort(columnKey, direction) {
407
+ this.setSort(computeNextSortState(this.sort(), columnKey, direction));
420
408
  }
421
409
  handleFilterChange(key, value) {
422
410
  this.setFilters(mergeFilter(this.filters(), key, value));
@@ -531,6 +519,8 @@ let OGridService = class OGridService {
531
519
  this.onError.set(props.onError);
532
520
  if (props.columnChooser !== undefined)
533
521
  this.columnChooserProp.set(props.columnChooser);
522
+ if (props.columnReorder !== undefined)
523
+ this.columnReorder.set(props.columnReorder);
534
524
  if (props.virtualScroll !== undefined)
535
525
  this.virtualScroll.set(props.virtualScroll);
536
526
  if (props.entityLabelPlural !== undefined)
@@ -87,7 +87,7 @@ export declare abstract class BaseDataGridTableComponent<T = unknown> {
87
87
  readonly headerFilterInput: import("@angular/core").Signal<{
88
88
  sortBy?: string;
89
89
  sortDirection: "asc" | "desc";
90
- onColumnSort: (columnKey: string) => void;
90
+ onColumnSort: (columnKey: string, direction?: "asc" | "desc" | null) => void;
91
91
  filters: import("@alaarab/ogrid-core").IFilters;
92
92
  onFilterChange: (key: string, value: import("@alaarab/ogrid-core").FilterValue | undefined) => void;
93
93
  filterOptions: Record<string, string[]>;
@@ -187,9 +187,8 @@ export declare abstract class BaseDataGridTableComponent<T = unknown> {
187
187
  };
188
188
  onSortAsc(columnId: string): void;
189
189
  onSortDesc(columnId: string): void;
190
- onClearSort(): void;
190
+ onClearSort(columnId?: string): void;
191
191
  getSortState(columnId: string): 'asc' | 'desc' | null;
192
192
  onAutosizeColumn(columnId: string): void;
193
193
  onAutosizeAllColumns(): void;
194
- private measureColumnContentWidth;
195
194
  }
@@ -88,7 +88,7 @@ export interface DataGridViewModelState<T> {
88
88
  headerFilterInput: {
89
89
  sortBy?: string;
90
90
  sortDirection: 'asc' | 'desc';
91
- onColumnSort: (columnKey: string) => void;
91
+ onColumnSort: (columnKey: string, direction?: 'asc' | 'desc' | null) => void;
92
92
  filters: IFilters;
93
93
  onFilterChange: (key: string, value: FilterValue | undefined) => void;
94
94
  filterOptions: Record<string, string[]>;
@@ -101,6 +101,7 @@ export declare class OGridService<T> {
101
101
  readonly onFirstDataRendered: import("@angular/core").WritableSignal<(() => void) | undefined>;
102
102
  readonly onError: import("@angular/core").WritableSignal<((error: unknown) => void) | undefined>;
103
103
  readonly columnChooserProp: import("@angular/core").WritableSignal<boolean | "toolbar" | "sidebar" | undefined>;
104
+ readonly columnReorder: import("@angular/core").WritableSignal<boolean | undefined>;
104
105
  readonly virtualScroll: import("@angular/core").WritableSignal<IVirtualScrollConfig | undefined>;
105
106
  readonly ariaLabel: import("@angular/core").WritableSignal<string | undefined>;
106
107
  readonly ariaLabelledBy: import("@angular/core").WritableSignal<string | undefined>;
@@ -179,7 +180,7 @@ export declare class OGridService<T> {
179
180
  }): void;
180
181
  setFilters(f: IFilters): void;
181
182
  setVisibleColumns(cols: Set<string>): void;
182
- handleSort(columnKey: string): void;
183
+ handleSort(columnKey: string, direction?: 'asc' | 'desc' | null): void;
183
184
  handleFilterChange(key: string, value: FilterValue | undefined): void;
184
185
  handleVisibilityChange(columnKey: string, isVisible: boolean): void;
185
186
  handleSelectionChange(event: IRowSelectionChangeEvent<T>): void;
@@ -57,6 +57,7 @@ interface IOGridBaseProps<T> {
57
57
  layoutMode?: 'content' | 'fill';
58
58
  suppressHorizontalScroll?: boolean;
59
59
  sideBar?: boolean | ISideBarDef;
60
+ columnReorder?: boolean;
60
61
  virtualScroll?: IVirtualScrollConfig;
61
62
  pageSizeOptions?: number[];
62
63
  onFirstDataRendered?: () => void;
@@ -85,7 +86,7 @@ export interface IOGridDataGridProps<T> {
85
86
  getRowId: (item: T) => RowId;
86
87
  sortBy?: string;
87
88
  sortDirection: 'asc' | 'desc';
88
- onColumnSort: (columnKey: string) => void;
89
+ onColumnSort: (columnKey: string, direction?: 'asc' | 'desc' | null) => void;
89
90
  visibleColumns: Set<string>;
90
91
  columnOrder?: string[];
91
92
  onColumnOrderChange?: (order: string[]) => void;
@@ -120,6 +121,7 @@ export interface IOGridDataGridProps<T> {
120
121
  loadingFilterOptions: Record<string, boolean>;
121
122
  peopleSearch?: (query: string) => Promise<UserLike[]>;
122
123
  getUserByEmail?: (email: string) => Promise<UserLike | undefined>;
124
+ columnReorder?: boolean;
123
125
  virtualScroll?: IVirtualScrollConfig;
124
126
  emptyState?: {
125
127
  onClearAll: () => void;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alaarab/ogrid-angular",
3
- "version": "2.0.11",
3
+ "version": "2.0.13",
4
4
  "description": "OGrid Angular – Angular services, signals, and headless components for OGrid data grids.",
5
5
  "main": "dist/esm/index.js",
6
6
  "module": "dist/esm/index.js",
@@ -35,7 +35,7 @@
35
35
  "node": ">=18"
36
36
  },
37
37
  "dependencies": {
38
- "@alaarab/ogrid-core": "2.0.11"
38
+ "@alaarab/ogrid-core": "2.0.13"
39
39
  },
40
40
  "peerDependencies": {
41
41
  "@angular/core": "^21.0.0",