@alaarab/ogrid-angular 2.0.9 → 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.
Files changed (28) hide show
  1. package/dist/esm/components/base-column-chooser.component.js +78 -0
  2. package/dist/esm/components/base-column-header-filter.component.js +266 -0
  3. package/dist/esm/components/base-datagrid-table.component.js +106 -6
  4. package/dist/esm/components/base-pagination-controls.component.js +72 -0
  5. package/dist/esm/components/empty-state.component.js +22 -10
  6. package/dist/esm/components/grid-context-menu.component.js +65 -26
  7. package/dist/esm/components/marching-ants-overlay.component.js +78 -69
  8. package/dist/esm/components/ogrid-layout.component.js +94 -35
  9. package/dist/esm/components/sidebar.component.js +58 -55
  10. package/dist/esm/components/status-bar.component.js +43 -23
  11. package/dist/esm/index.js +3 -0
  12. package/dist/esm/services/datagrid-state.service.js +1 -1
  13. package/dist/esm/services/ogrid.service.js +8 -8
  14. package/dist/types/components/base-column-chooser.component.d.ts +37 -0
  15. package/dist/types/components/base-column-header-filter.component.d.ts +90 -0
  16. package/dist/types/components/base-datagrid-table.component.d.ts +19 -1
  17. package/dist/types/components/base-pagination-controls.component.d.ts +34 -0
  18. package/dist/types/components/empty-state.component.d.ts +5 -4
  19. package/dist/types/components/grid-context-menu.component.d.ts +16 -16
  20. package/dist/types/components/marching-ants-overlay.component.d.ts +14 -14
  21. package/dist/types/components/ogrid-layout.component.d.ts +5 -5
  22. package/dist/types/components/sidebar.component.d.ts +1 -1
  23. package/dist/types/components/status-bar.component.d.ts +10 -10
  24. package/dist/types/index.d.ts +5 -0
  25. package/dist/types/services/datagrid-state.service.d.ts +1 -1
  26. package/dist/types/services/ogrid.service.d.ts +2 -1
  27. package/dist/types/types/dataGridTypes.d.ts +3 -1
  28. package/package.json +2 -2
@@ -4,31 +4,31 @@ 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 } from '@angular/core';
8
8
  import { CommonModule } from '@angular/common';
9
9
  const PANEL_WIDTH = 240;
10
10
  const TAB_WIDTH = 36;
11
11
  const PANEL_LABELS = { columns: 'Columns', filters: 'Filters' };
12
12
  let SideBarComponent = class SideBarComponent {
13
13
  constructor() {
14
- this.sideBarProps = input(null);
14
+ this.sideBarProps = null;
15
15
  this.panelLabels = PANEL_LABELS;
16
16
  this.tabWidth = TAB_WIDTH;
17
17
  this.panelWidth = PANEL_WIDTH;
18
18
  }
19
19
  onTabClick(panel) {
20
- const props = this.sideBarProps();
20
+ const props = this.sideBarProps;
21
21
  if (props)
22
22
  props.onPanelChange(props.activePanel === panel ? null : panel);
23
23
  }
24
24
  allVisible() {
25
- const props = this.sideBarProps();
25
+ const props = this.sideBarProps;
26
26
  if (!props)
27
27
  return false;
28
28
  return props.columns.every((c) => props.visibleColumns.has(c.columnId));
29
29
  }
30
30
  onSelectAll() {
31
- const props = this.sideBarProps();
31
+ const props = this.sideBarProps;
32
32
  if (!props)
33
33
  return;
34
34
  const next = new Set(props.visibleColumns);
@@ -36,7 +36,7 @@ let SideBarComponent = class SideBarComponent {
36
36
  props.onSetVisibleColumns(next);
37
37
  }
38
38
  onClearAll() {
39
- const props = this.sideBarProps();
39
+ const props = this.sideBarProps;
40
40
  if (!props)
41
41
  return;
42
42
  const next = new Set();
@@ -47,52 +47,55 @@ let SideBarComponent = class SideBarComponent {
47
47
  props.onSetVisibleColumns(next);
48
48
  }
49
49
  onVisibilityChange(columnKey, visible) {
50
- this.sideBarProps()?.onVisibilityChange(columnKey, visible);
50
+ this.sideBarProps?.onVisibilityChange(columnKey, visible);
51
51
  }
52
52
  getTextFilterValue(filterField) {
53
- const filters = this.sideBarProps()?.filters;
53
+ const filters = this.sideBarProps?.filters;
54
54
  const fv = filters?.[filterField];
55
55
  return fv?.type === 'text' ? fv.value : '';
56
56
  }
57
57
  onTextFilterChange(filterField, value) {
58
- this.sideBarProps()?.onFilterChange(filterField, value ? { type: 'text', value } : undefined);
58
+ this.sideBarProps?.onFilterChange(filterField, value ? { type: 'text', value } : undefined);
59
59
  }
60
60
  getDateFrom(filterField) {
61
- const fv = this.sideBarProps()?.filters?.[filterField];
61
+ const fv = this.sideBarProps?.filters?.[filterField];
62
62
  return fv?.type === 'date' ? (fv.value.from ?? '') : '';
63
63
  }
64
64
  getDateTo(filterField) {
65
- const fv = this.sideBarProps()?.filters?.[filterField];
65
+ const fv = this.sideBarProps?.filters?.[filterField];
66
66
  return fv?.type === 'date' ? (fv.value.to ?? '') : '';
67
67
  }
68
68
  onDateFromChange(filterField, value) {
69
- const fv = this.sideBarProps()?.filters?.[filterField];
69
+ const fv = this.sideBarProps?.filters?.[filterField];
70
70
  const existing = fv?.type === 'date' ? fv.value : {};
71
71
  const from = value || undefined;
72
72
  const to = existing.to;
73
- this.sideBarProps()?.onFilterChange(filterField, from || to ? { type: 'date', value: { from, to } } : undefined);
73
+ this.sideBarProps?.onFilterChange(filterField, from || to ? { type: 'date', value: { from, to } } : undefined);
74
74
  }
75
75
  onDateToChange(filterField, value) {
76
- const fv = this.sideBarProps()?.filters?.[filterField];
76
+ const fv = this.sideBarProps?.filters?.[filterField];
77
77
  const existing = fv?.type === 'date' ? fv.value : {};
78
78
  const to = value || undefined;
79
79
  const from = existing.from;
80
- this.sideBarProps()?.onFilterChange(filterField, from || to ? { type: 'date', value: { from, to } } : undefined);
80
+ this.sideBarProps?.onFilterChange(filterField, from || to ? { type: 'date', value: { from, to } } : undefined);
81
81
  }
82
82
  getFilterOptions(filterField) {
83
- return this.sideBarProps()?.filterOptions?.[filterField] ?? [];
83
+ return this.sideBarProps?.filterOptions?.[filterField] ?? [];
84
84
  }
85
85
  isMultiSelectChecked(filterField, opt) {
86
- const fv = this.sideBarProps()?.filters?.[filterField];
86
+ const fv = this.sideBarProps?.filters?.[filterField];
87
87
  return fv?.type === 'multiSelect' ? fv.value.includes(opt) : false;
88
88
  }
89
89
  onMultiSelectChange(filterField, opt, checked) {
90
- const fv = this.sideBarProps()?.filters?.[filterField];
90
+ const fv = this.sideBarProps?.filters?.[filterField];
91
91
  const current = fv?.type === 'multiSelect' ? fv.value : [];
92
92
  const next = checked ? [...current, opt] : current.filter((v) => v !== opt);
93
- this.sideBarProps()?.onFilterChange(filterField, next.length > 0 ? { type: 'multiSelect', value: next } : undefined);
93
+ this.sideBarProps?.onFilterChange(filterField, next.length > 0 ? { type: 'multiSelect', value: next } : undefined);
94
94
  }
95
95
  };
96
+ __decorate([
97
+ Input()
98
+ ], SideBarComponent.prototype, "sideBarProps", void 0);
96
99
  SideBarComponent = __decorate([
97
100
  Component({
98
101
  selector: 'ogrid-sidebar',
@@ -103,56 +106,56 @@ SideBarComponent = __decorate([
103
106
  .ogrid-sidebar-tab-strip {
104
107
  display: flex; flex-direction: column;
105
108
  width: var(--ogrid-sidebar-tab-size, 36px);
106
- background: var(--ogrid-header-bg, #f5f5f5);
109
+ background: var(--ogrid-header-bg, rgba(0, 0, 0, 0.04));
107
110
  }
108
- .ogrid-sidebar-tab-strip--left { border-right: 1px solid var(--ogrid-border, #e0e0e0); }
109
- .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)); }
110
113
  .ogrid-sidebar-tab {
111
114
  width: var(--ogrid-sidebar-tab-size, 36px);
112
115
  height: var(--ogrid-sidebar-tab-size, 36px);
113
116
  border: none; cursor: pointer;
114
- color: var(--ogrid-fg, #242424); font-size: 14px;
117
+ color: var(--ogrid-fg, rgba(0, 0, 0, 0.87)); font-size: 14px;
115
118
  display: flex; align-items: center; justify-content: center;
116
119
  background: transparent; font-weight: normal;
117
120
  }
118
- .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; }
119
122
  .ogrid-sidebar-panel {
120
123
  width: var(--ogrid-sidebar-panel-width, 240px);
121
124
  display: flex; flex-direction: column; overflow: hidden;
122
- 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));
123
126
  }
124
- .ogrid-sidebar-panel--left { border-right: 1px solid var(--ogrid-border, #e0e0e0); }
125
- .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)); }
126
129
  .ogrid-sidebar-panel-header {
127
130
  display: flex; justify-content: space-between; align-items: center;
128
- 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;
129
132
  }
130
133
  .ogrid-sidebar-panel-close {
131
134
  border: none; background: transparent; cursor: pointer;
132
- font-size: 16px; color: var(--ogrid-fg, #242424);
135
+ font-size: 16px; color: var(--ogrid-fg, rgba(0, 0, 0, 0.87));
133
136
  }
134
137
  .ogrid-sidebar-panel-body { flex: 1; overflow-y: auto; padding: 8px 12px; }
135
138
  .ogrid-sidebar-actions { display: flex; gap: 8px; margin-bottom: 8px; }
136
139
  .ogrid-sidebar-action-btn {
137
140
  flex: 1; cursor: pointer;
138
- background: var(--ogrid-bg-subtle, #f3f2f1); color: var(--ogrid-fg, #242424);
139
- 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;
140
143
  }
141
144
  .ogrid-sidebar-col-label { display: flex; align-items: center; gap: 6px; padding: 2px 0; cursor: pointer; }
142
- .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; }
143
146
  .ogrid-sidebar-filter-group { margin-bottom: 12px; }
144
147
  .ogrid-sidebar-filter-label { font-weight: 500; margin-bottom: 4px; font-size: 13px; }
145
148
  .ogrid-sidebar-text-input {
146
149
  width: 100%; box-sizing: border-box; padding: 4px 6px;
147
- background: var(--ogrid-bg, #fff); color: var(--ogrid-fg, #242424);
148
- 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;
149
152
  }
150
153
  .ogrid-sidebar-date-row { display: flex; flex-direction: column; gap: 4px; }
151
154
  .ogrid-sidebar-date-label { display: flex; align-items: center; gap: 4px; font-size: 12px; }
152
155
  .ogrid-sidebar-date-input {
153
156
  flex: 1; padding: 2px 4px;
154
- background: var(--ogrid-bg, #fff); color: var(--ogrid-fg, #242424);
155
- 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;
156
159
  }
157
160
  .ogrid-sidebar-multiselect-list { max-height: 120px; overflow-y: auto; }
158
161
  .ogrid-sidebar-multiselect-item {
@@ -162,11 +165,11 @@ SideBarComponent = __decorate([
162
165
  `],
163
166
  template: `
164
167
  <div class="ogrid-sidebar-root" role="complementary" aria-label="Side bar">
165
- @if (sideBarProps()?.position === 'left') {
168
+ @if (sideBarProps?.position === 'left') {
166
169
  <ng-container *ngTemplateOutlet="tabStripTpl"></ng-container>
167
170
  <ng-container *ngTemplateOutlet="panelContentTpl"></ng-container>
168
171
  }
169
- @if (sideBarProps()?.position === 'right') {
172
+ @if (sideBarProps?.position === 'right') {
170
173
  <ng-container *ngTemplateOutlet="panelContentTpl"></ng-container>
171
174
  <ng-container *ngTemplateOutlet="tabStripTpl"></ng-container>
172
175
  }
@@ -175,17 +178,17 @@ SideBarComponent = __decorate([
175
178
  <ng-template #tabStripTpl>
176
179
  <div
177
180
  class="ogrid-sidebar-tab-strip"
178
- [class.ogrid-sidebar-tab-strip--left]="sideBarProps()?.position === 'left'"
179
- [class.ogrid-sidebar-tab-strip--right]="sideBarProps()?.position === 'right'"
181
+ [class.ogrid-sidebar-tab-strip--left]="sideBarProps?.position === 'left'"
182
+ [class.ogrid-sidebar-tab-strip--right]="sideBarProps?.position === 'right'"
180
183
  role="tablist"
181
184
  aria-label="Side bar tabs"
182
185
  >
183
- @for (panel of sideBarProps()?.panels ?? []; track panel) {
186
+ @for (panel of sideBarProps?.panels ?? []; track panel) {
184
187
  <button
185
188
  role="tab"
186
189
  class="ogrid-sidebar-tab"
187
- [class.ogrid-sidebar-tab--active]="sideBarProps()?.activePanel === panel"
188
- [attr.aria-selected]="sideBarProps()?.activePanel === panel"
190
+ [class.ogrid-sidebar-tab--active]="sideBarProps?.activePanel === panel"
191
+ [attr.aria-selected]="sideBarProps?.activePanel === panel"
189
192
  [attr.aria-label]="panelLabels[panel]"
190
193
  (click)="onTabClick(panel)"
191
194
  [title]="panelLabels[panel]"
@@ -197,36 +200,36 @@ SideBarComponent = __decorate([
197
200
  </ng-template>
198
201
 
199
202
  <ng-template #panelContentTpl>
200
- @if (sideBarProps()?.activePanel) {
203
+ @if (sideBarProps?.activePanel) {
201
204
  <div
202
205
  role="tabpanel"
203
206
  class="ogrid-sidebar-panel"
204
- [class.ogrid-sidebar-panel--left]="sideBarProps()?.position === 'left'"
205
- [class.ogrid-sidebar-panel--right]="sideBarProps()?.position === 'right'"
206
- [attr.aria-label]="panelLabels[sideBarProps()!.activePanel!]"
207
+ [class.ogrid-sidebar-panel--left]="sideBarProps?.position === 'left'"
208
+ [class.ogrid-sidebar-panel--right]="sideBarProps?.position === 'right'"
209
+ [attr.aria-label]="panelLabels[sideBarProps!.activePanel!]"
207
210
  >
208
211
  <div class="ogrid-sidebar-panel-header">
209
- <span>{{ panelLabels[sideBarProps()!.activePanel!] }}</span>
210
- <button (click)="sideBarProps()?.onPanelChange(null)" class="ogrid-sidebar-panel-close" aria-label="Close panel">&times;</button>
212
+ <span>{{ panelLabels[sideBarProps!.activePanel!] }}</span>
213
+ <button (click)="sideBarProps?.onPanelChange(null)" class="ogrid-sidebar-panel-close" aria-label="Close panel">&times;</button>
211
214
  </div>
212
215
  <div class="ogrid-sidebar-panel-body">
213
- @if (sideBarProps()?.activePanel === 'columns') {
216
+ @if (sideBarProps?.activePanel === 'columns') {
214
217
  <div class="ogrid-sidebar-actions">
215
218
  <button (click)="onSelectAll()" [disabled]="allVisible()" class="ogrid-sidebar-action-btn">Select All</button>
216
219
  <button (click)="onClearAll()" class="ogrid-sidebar-action-btn">Clear All</button>
217
220
  </div>
218
- @for (col of sideBarProps()?.columns ?? []; track col.columnId) {
221
+ @for (col of sideBarProps?.columns ?? []; track col.columnId) {
219
222
  <label class="ogrid-sidebar-col-label">
220
- <input type="checkbox" [checked]="sideBarProps()?.visibleColumns?.has(col.columnId)" (change)="onVisibilityChange(col.columnId, $any($event.target).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" />
221
224
  <span>{{ col.name }}</span>
222
225
  </label>
223
226
  }
224
227
  }
225
- @if (sideBarProps()?.activePanel === 'filters') {
226
- @if ((sideBarProps()?.filterableColumns ?? []).length === 0) {
228
+ @if (sideBarProps?.activePanel === 'filters') {
229
+ @if ((sideBarProps?.filterableColumns ?? []).length === 0) {
227
230
  <div class="ogrid-sidebar-empty">No filterable columns</div>
228
231
  }
229
- @for (col of sideBarProps()?.filterableColumns ?? []; track col.columnId) {
232
+ @for (col of sideBarProps?.filterableColumns ?? []; track col.columnId) {
230
233
  <div class="ogrid-sidebar-filter-group">
231
234
  <div class="ogrid-sidebar-filter-label">{{ col.name }}</div>
232
235
  @if (col.filterType === 'text') {
@@ -4,41 +4,61 @@ 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 } from '@angular/core';
8
8
  import { CommonModule } from '@angular/common';
9
9
  import { getStatusBarParts } from '@alaarab/ogrid-core';
10
10
  let StatusBarComponent = class StatusBarComponent {
11
11
  constructor() {
12
- this.totalCount = input.required();
13
- this.filteredCount = input(undefined);
14
- this.selectedCount = input(undefined);
15
- this.selectedCellCount = input(undefined);
16
- this.aggregation = input(undefined);
17
- this.suppressRowCount = input(undefined);
18
- this.classNames = input(undefined);
19
- this.parts = () => {
20
- return getStatusBarParts({
21
- totalCount: this.totalCount(),
22
- filteredCount: this.filteredCount(),
23
- selectedCount: this.selectedCount(),
24
- selectedCellCount: this.selectedCellCount(),
25
- aggregation: this.aggregation() ?? undefined,
26
- suppressRowCount: this.suppressRowCount(),
27
- });
28
- };
12
+ this.filteredCount = undefined;
13
+ this.selectedCount = undefined;
14
+ this.selectedCellCount = undefined;
15
+ this.aggregation = undefined;
16
+ this.suppressRowCount = undefined;
17
+ this.classNames = undefined;
18
+ }
19
+ getParts() {
20
+ return getStatusBarParts({
21
+ totalCount: this.totalCount,
22
+ filteredCount: this.filteredCount,
23
+ selectedCount: this.selectedCount,
24
+ selectedCellCount: this.selectedCellCount,
25
+ aggregation: this.aggregation ?? undefined,
26
+ suppressRowCount: this.suppressRowCount,
27
+ });
29
28
  }
30
29
  };
30
+ __decorate([
31
+ Input({ required: true })
32
+ ], StatusBarComponent.prototype, "totalCount", void 0);
33
+ __decorate([
34
+ Input()
35
+ ], StatusBarComponent.prototype, "filteredCount", void 0);
36
+ __decorate([
37
+ Input()
38
+ ], StatusBarComponent.prototype, "selectedCount", void 0);
39
+ __decorate([
40
+ Input()
41
+ ], StatusBarComponent.prototype, "selectedCellCount", void 0);
42
+ __decorate([
43
+ Input()
44
+ ], StatusBarComponent.prototype, "aggregation", void 0);
45
+ __decorate([
46
+ Input()
47
+ ], StatusBarComponent.prototype, "suppressRowCount", void 0);
48
+ __decorate([
49
+ Input()
50
+ ], StatusBarComponent.prototype, "classNames", void 0);
31
51
  StatusBarComponent = __decorate([
32
52
  Component({
33
53
  selector: 'ogrid-status-bar',
34
54
  standalone: true,
35
55
  imports: [CommonModule],
36
56
  template: `
37
- <div [class]="classNames()?.statusBar ?? ''" role="status" aria-live="polite">
38
- @for (part of parts(); track part.key) {
39
- <span [class]="classNames()?.statusBarItem ?? ''">
40
- <span [class]="classNames()?.statusBarLabel ?? ''">{{ part.label }}</span>
41
- <span [class]="classNames()?.statusBarValue ?? ''">{{ part.value.toLocaleString() }}</span>
57
+ <div [class]="classNames?.statusBar ?? ''" role="status" aria-live="polite">
58
+ @for (part of getParts(); track part.key) {
59
+ <span [class]="classNames?.statusBarItem ?? ''">
60
+ <span [class]="classNames?.statusBarLabel ?? ''">{{ part.label }}</span>
61
+ <span [class]="classNames?.statusBarValue ?? ''">{{ part.value.toLocaleString() }}</span>
42
62
  </span>
43
63
  }
44
64
  </div>
package/dist/esm/index.js CHANGED
@@ -16,4 +16,7 @@ export { SideBarComponent } from './components/sidebar.component';
16
16
  export { MarchingAntsOverlayComponent } from './components/marching-ants-overlay.component';
17
17
  export { EmptyStateComponent } from './components/empty-state.component';
18
18
  export { BaseDataGridTableComponent } from './components/base-datagrid-table.component';
19
+ export { BaseColumnHeaderFilterComponent } from './components/base-column-header-filter.component';
20
+ export { BaseColumnChooserComponent } from './components/base-column-chooser.component';
21
+ export { BasePaginationControlsComponent } from './components/base-pagination-controls.component';
19
22
  export { getHeaderFilterConfig, getCellRenderDescriptor, resolveCellDisplayContent, resolveCellStyle, createDebouncedSignal, createDebouncedCallback, debounce, createLatestRef, createLatestCallback, } from './utils';
@@ -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,12 +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
- this.setSort({
407
- field: columnKey,
408
- direction: sort.field === columnKey && sort.direction === 'asc' ? 'desc' : 'asc',
409
- });
406
+ handleSort(columnKey, direction) {
407
+ this.setSort(computeNextSortState(this.sort(), columnKey, direction));
410
408
  }
411
409
  handleFilterChange(key, value) {
412
410
  this.setFilters(mergeFilter(this.filters(), key, value));
@@ -521,6 +519,8 @@ let OGridService = class OGridService {
521
519
  this.onError.set(props.onError);
522
520
  if (props.columnChooser !== undefined)
523
521
  this.columnChooserProp.set(props.columnChooser);
522
+ if (props.columnReorder !== undefined)
523
+ this.columnReorder.set(props.columnReorder);
524
524
  if (props.virtualScroll !== undefined)
525
525
  this.virtualScroll.set(props.virtualScroll);
526
526
  if (props.entityLabelPlural !== undefined)
@@ -0,0 +1,37 @@
1
+ import { EventEmitter } from '@angular/core';
2
+ import type { IColumnDefinition } from '../types';
3
+ export interface IColumnChooserProps {
4
+ columns: IColumnDefinition[];
5
+ visibleColumns: Set<string>;
6
+ onVisibilityChange: (columnKey: string, visible: boolean) => void;
7
+ }
8
+ /**
9
+ * Abstract base class containing all shared TypeScript logic for ColumnChooser components.
10
+ * Framework-specific UI packages extend this with their templates and style overrides.
11
+ *
12
+ * Subclasses must:
13
+ * 1. Provide a @Component decorator with template and styles
14
+ * 2. Handle their own click-outside behavior (host binding or effect)
15
+ */
16
+ export declare abstract class BaseColumnChooserComponent {
17
+ private readonly _columns;
18
+ private readonly _visibleColumns;
19
+ set columns(v: IColumnDefinition[]);
20
+ get columns(): IColumnDefinition[];
21
+ set visibleColumns(v: Set<string>);
22
+ get visibleColumns(): Set<string>;
23
+ visibilityChange: EventEmitter<{
24
+ columnKey: string;
25
+ visible: boolean;
26
+ }>;
27
+ readonly isOpen: import("@angular/core").WritableSignal<boolean>;
28
+ readonly visibleCount: import("@angular/core").Signal<number>;
29
+ readonly totalCount: import("@angular/core").Signal<number>;
30
+ toggle(): void;
31
+ onCheckboxChange(columnKey: string, event: Event): void;
32
+ onToggle(columnKey: string, checked: boolean): void;
33
+ selectAll(): void;
34
+ clearAll(): void;
35
+ onClearAll(): void;
36
+ onSelectAll(): void;
37
+ }
@@ -0,0 +1,90 @@
1
+ import { ElementRef } from '@angular/core';
2
+ import type { ColumnFilterType, IDateFilterValue, UserLike } from '../types';
3
+ export interface IColumnHeaderFilterProps {
4
+ columnKey: string;
5
+ columnName: string;
6
+ filterType: ColumnFilterType;
7
+ isSorted?: boolean;
8
+ isSortedDescending?: boolean;
9
+ onSort?: () => void;
10
+ selectedValues?: string[];
11
+ onFilterChange?: (values: string[]) => void;
12
+ options?: string[];
13
+ isLoadingOptions?: boolean;
14
+ textValue?: string;
15
+ onTextChange?: (value: string) => void;
16
+ selectedUser?: UserLike;
17
+ onUserChange?: (user: UserLike | undefined) => void;
18
+ peopleSearch?: (query: string) => Promise<UserLike[]>;
19
+ dateValue?: IDateFilterValue;
20
+ onDateChange?: (value: IDateFilterValue | undefined) => void;
21
+ }
22
+ /**
23
+ * Abstract base class containing all shared TypeScript logic for ColumnHeaderFilter components.
24
+ * Framework-specific UI packages extend this with their templates and style overrides.
25
+ *
26
+ * Subclasses must:
27
+ * 1. Provide a @Component decorator with template and styles
28
+ * 2. Implement abstract accessor for headerEl (ViewChild reference)
29
+ */
30
+ export declare abstract class BaseColumnHeaderFilterComponent {
31
+ columnKey: string;
32
+ columnName: string;
33
+ private readonly _filterType;
34
+ private readonly _selectedValues;
35
+ private readonly _options;
36
+ private readonly _textValue;
37
+ private readonly _selectedUser;
38
+ private readonly _dateValue;
39
+ set filterType(v: ColumnFilterType);
40
+ get filterType(): ColumnFilterType;
41
+ set selectedValues(v: string[] | undefined);
42
+ get selectedValues(): string[] | undefined;
43
+ set options(v: string[] | undefined);
44
+ get options(): string[] | undefined;
45
+ set textValue(v: string);
46
+ get textValue(): string;
47
+ set selectedUser(v: UserLike | undefined);
48
+ get selectedUser(): UserLike | undefined;
49
+ set dateValue(v: IDateFilterValue | undefined);
50
+ get dateValue(): IDateFilterValue | undefined;
51
+ isSorted: boolean;
52
+ isSortedDescending: boolean;
53
+ onSort: (() => void) | undefined;
54
+ onFilterChange: ((values: string[]) => void) | undefined;
55
+ isLoadingOptions: boolean;
56
+ onTextChange: ((value: string) => void) | undefined;
57
+ onUserChange: ((user: UserLike | undefined) => void) | undefined;
58
+ peopleSearch: ((query: string) => Promise<UserLike[]>) | undefined;
59
+ onDateChange: ((value: IDateFilterValue | undefined) => void) | undefined;
60
+ protected abstract getHeaderEl(): ElementRef<HTMLElement> | undefined;
61
+ readonly isFilterOpen: import("@angular/core").WritableSignal<boolean>;
62
+ readonly tempTextValue: import("@angular/core").WritableSignal<string>;
63
+ readonly searchText: import("@angular/core").WritableSignal<string>;
64
+ readonly tempSelected: import("@angular/core").WritableSignal<Set<string>>;
65
+ readonly peopleSearchText: import("@angular/core").WritableSignal<string>;
66
+ readonly peopleSuggestions: import("@angular/core").WritableSignal<UserLike[]>;
67
+ readonly isPeopleLoading: import("@angular/core").WritableSignal<boolean>;
68
+ readonly tempDateFrom: import("@angular/core").WritableSignal<string>;
69
+ readonly tempDateTo: import("@angular/core").WritableSignal<string>;
70
+ readonly popoverTop: import("@angular/core").WritableSignal<number>;
71
+ readonly popoverLeft: import("@angular/core").WritableSignal<number>;
72
+ private peopleDebounceTimer;
73
+ readonly hasActiveFilter: import("@angular/core").Signal<boolean>;
74
+ readonly filteredOptions: import("@angular/core").Signal<string[]>;
75
+ asInputValue(event: Event): string;
76
+ toggleFilter(event: MouseEvent): void;
77
+ onTextKeydown(event: KeyboardEvent): void;
78
+ handleTextApply(): void;
79
+ handleTextClear(): void;
80
+ handleCheckboxChange(option: string, event: Event): void;
81
+ handleSelectAllFiltered(): void;
82
+ handleClearSelection(): void;
83
+ handleApplyMultiSelect(): void;
84
+ onPeopleSearchInput(event: Event): void;
85
+ handleUserSelect(user: UserLike): void;
86
+ handleClearUser(): void;
87
+ handleDateApply(): void;
88
+ handleDateClear(): void;
89
+ onDocumentClick(event: MouseEvent, selectorName: string): void;
90
+ }
@@ -19,12 +19,16 @@ export declare abstract class BaseDataGridTableComponent<T = unknown> {
19
19
  readonly virtualScrollService: VirtualScrollService;
20
20
  protected lastMouseShift: boolean;
21
21
  readonly columnSizingVersion: import("@angular/core").WritableSignal<number>;
22
+ protected readonly wrapperElSignal: import("@angular/core").WritableSignal<HTMLElement | null>;
23
+ protected readonly tableContainerElSignal: import("@angular/core").WritableSignal<HTMLElement | null>;
22
24
  /** Return the IOGridDataGridProps from however the subclass receives them */
23
25
  protected abstract getProps(): IOGridDataGridProps<T> | undefined;
24
26
  /** Return the wrapper element ref */
25
27
  protected abstract getWrapperRef(): ElementRef<HTMLElement> | undefined;
26
28
  /** Return the table container element ref */
27
29
  protected abstract getTableContainerRef(): ElementRef<HTMLElement> | undefined;
30
+ /** Lifecycle hook — populate element signals from @ViewChild refs */
31
+ ngAfterViewInit(): void;
28
32
  readonly state: import("@angular/core").Signal<import("../services/datagrid-state.service").DataGridStateResult<T>>;
29
33
  readonly tableContainerEl: import("@angular/core").Signal<HTMLElement | null>;
30
34
  readonly allItems: import("@angular/core").Signal<T[]>;
@@ -46,6 +50,8 @@ export declare abstract class BaseDataGridTableComponent<T = unknown> {
46
50
  readonly currentPage: import("@angular/core").Signal<number>;
47
51
  readonly pageSize: import("@angular/core").Signal<number>;
48
52
  readonly rowNumberOffset: import("@angular/core").Signal<number>;
53
+ readonly propsVisibleColumns: import("@angular/core").Signal<Set<string> | undefined>;
54
+ readonly propsColumnOrder: import("@angular/core").Signal<string[] | undefined>;
49
55
  readonly visibleCols: import("@angular/core").Signal<IColumnDef<T>[]>;
50
56
  readonly hasCheckboxCol: import("@angular/core").Signal<boolean>;
51
57
  readonly hasRowNumbersCol: import("@angular/core").Signal<boolean>;
@@ -81,7 +87,7 @@ export declare abstract class BaseDataGridTableComponent<T = unknown> {
81
87
  readonly headerFilterInput: import("@angular/core").Signal<{
82
88
  sortBy?: string;
83
89
  sortDirection: "asc" | "desc";
84
- onColumnSort: (columnKey: string) => void;
90
+ onColumnSort: (columnKey: string, direction?: "asc" | "desc" | null) => void;
85
91
  filters: import("@alaarab/ogrid-core").IFilters;
86
92
  onFilterChange: (key: string, value: import("@alaarab/ogrid-core").FilterValue | undefined) => void;
87
93
  filterOptions: Record<string, string[]>;
@@ -123,6 +129,10 @@ export declare abstract class BaseDataGridTableComponent<T = unknown> {
123
129
  minWidth: number;
124
130
  width: number;
125
131
  }[]>;
132
+ readonly pinningOffsets: import("@angular/core").Signal<{
133
+ leftOffsets: Record<string, number>;
134
+ rightOffsets: Record<string, number>;
135
+ }>;
126
136
  /**
127
137
  * Initialize base wiring effects. Must be called from subclass constructor
128
138
  * (effects need to run inside an injection context).
@@ -138,6 +148,8 @@ export declare abstract class BaseDataGridTableComponent<T = unknown> {
138
148
  buildPopoverEditorProps(item: T, col: IColumnDef<T>, descriptor: CellRenderDescriptor): unknown;
139
149
  getSelectValues(col: IColumnDef<T>): string[];
140
150
  formatDateForInput(value: unknown): string;
151
+ getPinnedLeftOffset(columnId: string): number | null;
152
+ getPinnedRightOffset(columnId: string): number | null;
141
153
  onWrapperScroll(event: Event): void;
142
154
  setPopoverAnchorEl(el: HTMLElement | null): void;
143
155
  setPendingEditorValue(value: unknown): void;
@@ -173,4 +185,10 @@ export declare abstract class BaseDataGridTableComponent<T = unknown> {
173
185
  canPinRight: boolean;
174
186
  canUnpin: boolean;
175
187
  };
188
+ onSortAsc(columnId: string): void;
189
+ onSortDesc(columnId: string): void;
190
+ onClearSort(columnId?: string): void;
191
+ getSortState(columnId: string): 'asc' | 'desc' | null;
192
+ onAutosizeColumn(columnId: string): void;
193
+ onAutosizeAllColumns(): void;
176
194
  }