@alaarab/ogrid-angular 2.0.5 → 2.0.6
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.
- package/dist/esm/components/base-datagrid-table.component.js +309 -0
- package/dist/esm/components/empty-state.component.js +7 -1
- package/dist/esm/components/marching-ants-overlay.component.js +7 -8
- package/dist/esm/components/ogrid-layout.component.js +39 -26
- package/dist/esm/components/sidebar.component.js +89 -44
- package/dist/esm/index.js +1 -0
- package/dist/types/components/base-datagrid-table.component.d.ts +160 -0
- package/dist/types/index.d.ts +1 -0
- package/package.json +5 -4
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
import { signal, computed, effect } from '@angular/core';
|
|
2
|
+
import { DataGridStateService } from '../services/datagrid-state.service';
|
|
3
|
+
import { ColumnReorderService } from '../services/column-reorder.service';
|
|
4
|
+
import { VirtualScrollService } from '../services/virtual-scroll.service';
|
|
5
|
+
import { buildHeaderRows, DEFAULT_MIN_COLUMN_WIDTH, } from '@alaarab/ogrid-core';
|
|
6
|
+
import { getHeaderFilterConfig, getCellRenderDescriptor, resolveCellDisplayContent, resolveCellStyle, } from '../utils';
|
|
7
|
+
/**
|
|
8
|
+
* Abstract base class containing all shared TypeScript logic for DataGridTable components.
|
|
9
|
+
* Framework-specific UI packages extend this with their templates and style overrides.
|
|
10
|
+
*
|
|
11
|
+
* Subclasses must:
|
|
12
|
+
* 1. Provide a @Component decorator with template and styles
|
|
13
|
+
* 2. Call `initBase()` in the constructor (effects require injection context)
|
|
14
|
+
* 3. Implement abstract accessors for propsInput, wrapperRef, and tableContainerRef
|
|
15
|
+
*/
|
|
16
|
+
export class BaseDataGridTableComponent {
|
|
17
|
+
constructor() {
|
|
18
|
+
this.stateService = new DataGridStateService();
|
|
19
|
+
this.columnReorderService = new ColumnReorderService();
|
|
20
|
+
this.virtualScrollService = new VirtualScrollService();
|
|
21
|
+
this.lastMouseShift = false;
|
|
22
|
+
this.columnSizingVersion = signal(0);
|
|
23
|
+
// --- Delegated state ---
|
|
24
|
+
this.state = computed(() => this.stateService.getState());
|
|
25
|
+
this.tableContainerEl = computed(() => this.getTableContainerRef()?.nativeElement ?? null);
|
|
26
|
+
this.items = computed(() => this.getProps()?.items ?? []);
|
|
27
|
+
this.getRowId = computed(() => this.getProps()?.getRowId ?? ((item) => item['id']));
|
|
28
|
+
this.isLoading = computed(() => this.getProps()?.isLoading ?? false);
|
|
29
|
+
this.loadingMessage = computed(() => 'Loading\u2026');
|
|
30
|
+
this.freezeRows = computed(() => this.getProps()?.freezeRows);
|
|
31
|
+
this.freezeCols = computed(() => this.getProps()?.freezeCols);
|
|
32
|
+
this.layoutModeFit = computed(() => (this.getProps()?.layoutMode ?? 'fill') === 'content');
|
|
33
|
+
this.ariaLabel = computed(() => this.getProps()?.['aria-label'] ?? 'Data grid');
|
|
34
|
+
this.ariaLabelledBy = computed(() => this.getProps()?.['aria-labelledby']);
|
|
35
|
+
this.emptyState = computed(() => this.getProps()?.emptyState);
|
|
36
|
+
this.currentPage = computed(() => this.getProps()?.currentPage ?? 1);
|
|
37
|
+
this.pageSize = computed(() => this.getProps()?.pageSize ?? 25);
|
|
38
|
+
this.rowNumberOffset = computed(() => this.hasRowNumbersCol() ? (this.currentPage() - 1) * this.pageSize() : 0);
|
|
39
|
+
// State service outputs
|
|
40
|
+
this.visibleCols = computed(() => this.state().layout.visibleCols);
|
|
41
|
+
this.hasCheckboxCol = computed(() => this.state().layout.hasCheckboxCol);
|
|
42
|
+
this.hasRowNumbersCol = computed(() => this.state().layout.hasRowNumbersCol);
|
|
43
|
+
this.colOffset = computed(() => this.state().layout.colOffset);
|
|
44
|
+
this.containerWidth = computed(() => this.state().layout.containerWidth);
|
|
45
|
+
this.minTableWidth = computed(() => this.state().layout.minTableWidth);
|
|
46
|
+
this.desiredTableWidth = computed(() => this.state().layout.desiredTableWidth);
|
|
47
|
+
this.columnSizingOverrides = computed(() => this.state().layout.columnSizingOverrides);
|
|
48
|
+
this.selectedRowIds = computed(() => this.state().rowSelection.selectedRowIds);
|
|
49
|
+
this.allSelected = computed(() => this.state().rowSelection.allSelected);
|
|
50
|
+
this.someSelected = computed(() => this.state().rowSelection.someSelected);
|
|
51
|
+
this.editingCell = computed(() => this.state().editing.editingCell);
|
|
52
|
+
this.pendingEditorValue = computed(() => this.state().editing.pendingEditorValue);
|
|
53
|
+
this.activeCell = computed(() => this.state().interaction.activeCell);
|
|
54
|
+
this.selectionRange = computed(() => this.state().interaction.selectionRange);
|
|
55
|
+
this.hasCellSelection = computed(() => this.state().interaction.hasCellSelection);
|
|
56
|
+
this.cutRange = computed(() => this.state().interaction.cutRange);
|
|
57
|
+
this.copyRange = computed(() => this.state().interaction.copyRange);
|
|
58
|
+
this.canUndo = computed(() => this.state().interaction.canUndo);
|
|
59
|
+
this.canRedo = computed(() => this.state().interaction.canRedo);
|
|
60
|
+
this.isDragging = computed(() => this.state().interaction.isDragging);
|
|
61
|
+
this.menuPosition = computed(() => this.state().contextMenu.menuPosition);
|
|
62
|
+
this.statusBarConfig = computed(() => this.state().viewModels.statusBarConfig);
|
|
63
|
+
this.showEmptyInGrid = computed(() => this.state().viewModels.showEmptyInGrid);
|
|
64
|
+
this.headerFilterInput = computed(() => this.state().viewModels.headerFilterInput);
|
|
65
|
+
this.cellDescriptorInput = computed(() => this.state().viewModels.cellDescriptorInput);
|
|
66
|
+
this.allowOverflowX = computed(() => {
|
|
67
|
+
const p = this.getProps();
|
|
68
|
+
if (p?.suppressHorizontalScroll)
|
|
69
|
+
return false;
|
|
70
|
+
const cw = this.containerWidth();
|
|
71
|
+
const mtw = this.minTableWidth();
|
|
72
|
+
const dtw = this.desiredTableWidth();
|
|
73
|
+
return cw > 0 && (mtw > cw || dtw > cw);
|
|
74
|
+
});
|
|
75
|
+
this.selectionCellCount = computed(() => {
|
|
76
|
+
const sr = this.selectionRange();
|
|
77
|
+
if (!sr)
|
|
78
|
+
return undefined;
|
|
79
|
+
return (Math.abs(sr.endRow - sr.startRow) + 1) * (Math.abs(sr.endCol - sr.startCol) + 1);
|
|
80
|
+
});
|
|
81
|
+
// Header rows from column definition
|
|
82
|
+
this.headerRows = computed(() => {
|
|
83
|
+
const p = this.getProps();
|
|
84
|
+
if (!p)
|
|
85
|
+
return [];
|
|
86
|
+
return buildHeaderRows(p.columns, p.visibleColumns);
|
|
87
|
+
});
|
|
88
|
+
// Pre-computed column layouts
|
|
89
|
+
this.columnLayouts = computed(() => {
|
|
90
|
+
const cols = this.visibleCols();
|
|
91
|
+
const fc = this.freezeCols();
|
|
92
|
+
const props = this.getProps();
|
|
93
|
+
const pinnedCols = props?.pinnedColumns ?? {};
|
|
94
|
+
return cols.map((col, colIdx) => {
|
|
95
|
+
const isFreezeCol = fc != null && fc >= 1 && colIdx < fc;
|
|
96
|
+
const runtimePinned = pinnedCols[col.columnId];
|
|
97
|
+
const pinnedLeft = runtimePinned === 'left' || col.pinned === 'left' || (isFreezeCol && colIdx === 0);
|
|
98
|
+
const pinnedRight = runtimePinned === 'right' || col.pinned === 'right';
|
|
99
|
+
const w = this.getColumnWidth(col);
|
|
100
|
+
return {
|
|
101
|
+
col,
|
|
102
|
+
pinnedLeft,
|
|
103
|
+
pinnedRight,
|
|
104
|
+
minWidth: col.minWidth ?? DEFAULT_MIN_COLUMN_WIDTH,
|
|
105
|
+
width: w,
|
|
106
|
+
};
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Initialize base wiring effects. Must be called from subclass constructor
|
|
112
|
+
* (effects need to run inside an injection context).
|
|
113
|
+
*/
|
|
114
|
+
initBase() {
|
|
115
|
+
// Wire props to state service
|
|
116
|
+
effect(() => {
|
|
117
|
+
const p = this.getProps();
|
|
118
|
+
if (p)
|
|
119
|
+
this.stateService.props.set(p);
|
|
120
|
+
});
|
|
121
|
+
// Wire wrapper element
|
|
122
|
+
effect(() => {
|
|
123
|
+
const el = this.getWrapperRef()?.nativeElement;
|
|
124
|
+
if (el) {
|
|
125
|
+
this.stateService.wrapperEl.set(el);
|
|
126
|
+
this.columnReorderService.wrapperEl.set(el);
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
// Wire column reorder service inputs
|
|
130
|
+
effect(() => {
|
|
131
|
+
const p = this.getProps();
|
|
132
|
+
if (p) {
|
|
133
|
+
const cols = this.visibleCols();
|
|
134
|
+
this.columnReorderService.columns.set(cols);
|
|
135
|
+
this.columnReorderService.columnOrder.set(p.columnOrder);
|
|
136
|
+
this.columnReorderService.onColumnOrderChange.set(p.onColumnOrderChange);
|
|
137
|
+
this.columnReorderService.enabled.set(!!p.onColumnOrderChange);
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
// Wire virtual scroll service inputs
|
|
141
|
+
effect(() => {
|
|
142
|
+
const p = this.getProps();
|
|
143
|
+
if (p) {
|
|
144
|
+
this.virtualScrollService.totalRows.set(p.items.length);
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
// --- Helper methods ---
|
|
149
|
+
asColumnDef(colDef) {
|
|
150
|
+
return colDef;
|
|
151
|
+
}
|
|
152
|
+
visibleColIndex(col) {
|
|
153
|
+
return this.visibleCols().indexOf(col);
|
|
154
|
+
}
|
|
155
|
+
getColumnWidth(col) {
|
|
156
|
+
const overrides = this.columnSizingOverrides();
|
|
157
|
+
const override = overrides[col.columnId];
|
|
158
|
+
if (override)
|
|
159
|
+
return override.widthPx;
|
|
160
|
+
return col.defaultWidth ?? col.minWidth ?? DEFAULT_MIN_COLUMN_WIDTH;
|
|
161
|
+
}
|
|
162
|
+
getFilterConfig(col) {
|
|
163
|
+
return getHeaderFilterConfig(col, this.headerFilterInput());
|
|
164
|
+
}
|
|
165
|
+
getCellDescriptor(item, col, rowIndex, colIdx) {
|
|
166
|
+
return getCellRenderDescriptor(item, col, rowIndex, colIdx, this.cellDescriptorInput());
|
|
167
|
+
}
|
|
168
|
+
resolveCellContent(col, item, displayValue) {
|
|
169
|
+
return resolveCellDisplayContent(col, item, displayValue);
|
|
170
|
+
}
|
|
171
|
+
resolveCellStyleFn(col, item) {
|
|
172
|
+
return resolveCellStyle(col, item);
|
|
173
|
+
}
|
|
174
|
+
getSelectValues(col) {
|
|
175
|
+
const params = col.cellEditorParams;
|
|
176
|
+
if (params && typeof params === 'object' && 'values' in params) {
|
|
177
|
+
return params.values.map(String);
|
|
178
|
+
}
|
|
179
|
+
return [];
|
|
180
|
+
}
|
|
181
|
+
formatDateForInput(value) {
|
|
182
|
+
if (!value)
|
|
183
|
+
return '';
|
|
184
|
+
const d = new Date(String(value));
|
|
185
|
+
if (Number.isNaN(d.getTime()))
|
|
186
|
+
return '';
|
|
187
|
+
return d.toISOString().split('T')[0];
|
|
188
|
+
}
|
|
189
|
+
// --- Event handlers ---
|
|
190
|
+
onWrapperMouseDown(event) {
|
|
191
|
+
this.lastMouseShift = event.shiftKey;
|
|
192
|
+
}
|
|
193
|
+
onGridKeyDown(event) {
|
|
194
|
+
this.state().interaction.handleGridKeyDown(event);
|
|
195
|
+
}
|
|
196
|
+
onCellMouseDown(event, rowIndex, globalColIndex) {
|
|
197
|
+
this.state().interaction.handleCellMouseDown(event, rowIndex, globalColIndex);
|
|
198
|
+
}
|
|
199
|
+
onCellClick(rowIndex, globalColIndex) {
|
|
200
|
+
this.state().interaction.setActiveCell({ rowIndex, columnIndex: globalColIndex });
|
|
201
|
+
}
|
|
202
|
+
onCellContextMenu(event) {
|
|
203
|
+
this.state().contextMenu.handleCellContextMenu(event);
|
|
204
|
+
}
|
|
205
|
+
onCellDblClick(rowId, columnId) {
|
|
206
|
+
this.state().editing.setEditingCell({ rowId, columnId });
|
|
207
|
+
}
|
|
208
|
+
onFillHandleMouseDown(event) {
|
|
209
|
+
this.state().interaction.handleFillHandleMouseDown(event);
|
|
210
|
+
}
|
|
211
|
+
onResizeStart(event, col) {
|
|
212
|
+
event.preventDefault();
|
|
213
|
+
const startX = event.clientX;
|
|
214
|
+
const startWidth = this.getColumnWidth(col);
|
|
215
|
+
const minWidth = col.minWidth ?? DEFAULT_MIN_COLUMN_WIDTH;
|
|
216
|
+
const onMove = (e) => {
|
|
217
|
+
const delta = e.clientX - startX;
|
|
218
|
+
const newWidth = Math.max(minWidth, startWidth + delta);
|
|
219
|
+
const overrides = { ...this.columnSizingOverrides(), [col.columnId]: { widthPx: newWidth } };
|
|
220
|
+
this.state().layout.setColumnSizingOverrides(overrides);
|
|
221
|
+
this.columnSizingVersion.update(v => v + 1);
|
|
222
|
+
};
|
|
223
|
+
const onUp = () => {
|
|
224
|
+
window.removeEventListener('mousemove', onMove);
|
|
225
|
+
window.removeEventListener('mouseup', onUp);
|
|
226
|
+
const finalWidth = this.getColumnWidth(col);
|
|
227
|
+
this.state().layout.onColumnResized?.(col.columnId, finalWidth);
|
|
228
|
+
};
|
|
229
|
+
window.addEventListener('mousemove', onMove);
|
|
230
|
+
window.addEventListener('mouseup', onUp);
|
|
231
|
+
}
|
|
232
|
+
onSelectAllChange(event) {
|
|
233
|
+
const checked = event.target.checked;
|
|
234
|
+
this.state().rowSelection.handleSelectAll(!!checked);
|
|
235
|
+
}
|
|
236
|
+
onRowClick(event, rowId) {
|
|
237
|
+
const p = this.getProps();
|
|
238
|
+
if (p?.rowSelection !== 'single')
|
|
239
|
+
return;
|
|
240
|
+
const ids = this.selectedRowIds();
|
|
241
|
+
this.state().rowSelection.updateSelection(ids.has(rowId) ? new Set() : new Set([rowId]));
|
|
242
|
+
}
|
|
243
|
+
onRowCheckboxChange(rowId, event, rowIndex) {
|
|
244
|
+
const checked = event.target.checked;
|
|
245
|
+
this.state().rowSelection.handleRowCheckboxChange(rowId, checked, rowIndex, this.lastMouseShift);
|
|
246
|
+
}
|
|
247
|
+
commitEdit(item, columnId, oldValue, newValue, rowIndex, globalColIndex) {
|
|
248
|
+
this.state().editing.commitCellEdit(item, columnId, oldValue, newValue, rowIndex, globalColIndex);
|
|
249
|
+
}
|
|
250
|
+
cancelEdit() {
|
|
251
|
+
this.state().editing.setEditingCell(null);
|
|
252
|
+
}
|
|
253
|
+
onEditorKeydown(event, item, columnId, oldValue, rowIndex, globalColIndex) {
|
|
254
|
+
if (event.key === 'Enter') {
|
|
255
|
+
event.preventDefault();
|
|
256
|
+
const newValue = event.target.value;
|
|
257
|
+
this.commitEdit(item, columnId, oldValue, newValue, rowIndex, globalColIndex);
|
|
258
|
+
}
|
|
259
|
+
else if (event.key === 'Escape') {
|
|
260
|
+
event.preventDefault();
|
|
261
|
+
this.cancelEdit();
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
closeContextMenu() {
|
|
265
|
+
this.state().contextMenu.closeContextMenu();
|
|
266
|
+
}
|
|
267
|
+
handleCopy() {
|
|
268
|
+
this.state().interaction.handleCopy();
|
|
269
|
+
}
|
|
270
|
+
handleCut() {
|
|
271
|
+
this.state().interaction.handleCut();
|
|
272
|
+
}
|
|
273
|
+
handlePaste() {
|
|
274
|
+
void this.state().interaction.handlePaste();
|
|
275
|
+
}
|
|
276
|
+
handleSelectAllCells() {
|
|
277
|
+
this.state().interaction.handleSelectAllCells();
|
|
278
|
+
}
|
|
279
|
+
onUndo() {
|
|
280
|
+
this.state().interaction.onUndo?.();
|
|
281
|
+
}
|
|
282
|
+
onRedo() {
|
|
283
|
+
this.state().interaction.onRedo?.();
|
|
284
|
+
}
|
|
285
|
+
onHeaderMouseDown(columnId, event) {
|
|
286
|
+
this.columnReorderService.handleHeaderMouseDown(columnId, event);
|
|
287
|
+
}
|
|
288
|
+
// --- Column pinning methods ---
|
|
289
|
+
onPinColumn(columnId, side) {
|
|
290
|
+
const props = this.getProps();
|
|
291
|
+
props?.onColumnPinned?.(columnId, side);
|
|
292
|
+
}
|
|
293
|
+
onUnpinColumn(columnId) {
|
|
294
|
+
const props = this.getProps();
|
|
295
|
+
props?.onColumnPinned?.(columnId, null);
|
|
296
|
+
}
|
|
297
|
+
isPinned(columnId) {
|
|
298
|
+
const props = this.getProps();
|
|
299
|
+
return props?.pinnedColumns?.[columnId];
|
|
300
|
+
}
|
|
301
|
+
getPinState(columnId) {
|
|
302
|
+
const pinned = this.isPinned(columnId);
|
|
303
|
+
return {
|
|
304
|
+
canPinLeft: pinned !== 'left',
|
|
305
|
+
canPinRight: pinned !== 'right',
|
|
306
|
+
canUnpin: !!pinned,
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
}
|
|
@@ -19,6 +19,12 @@ EmptyStateComponent = __decorate([
|
|
|
19
19
|
selector: 'ogrid-empty-state',
|
|
20
20
|
standalone: true,
|
|
21
21
|
imports: [CommonModule],
|
|
22
|
+
styles: [`
|
|
23
|
+
.ogrid-empty-state-clear-btn {
|
|
24
|
+
background: none; border: none; color: inherit;
|
|
25
|
+
text-decoration: underline; cursor: pointer; padding: 0; font: inherit;
|
|
26
|
+
}
|
|
27
|
+
`],
|
|
22
28
|
template: `
|
|
23
29
|
@if (render()) {
|
|
24
30
|
<ng-container [ngTemplateOutlet]="render()!"></ng-container>
|
|
@@ -26,7 +32,7 @@ EmptyStateComponent = __decorate([
|
|
|
26
32
|
{{ message() }}
|
|
27
33
|
} @else if (hasActiveFilters()) {
|
|
28
34
|
No items match your current filters. Try adjusting your search or
|
|
29
|
-
<button type="button" (click)="clearAll.emit()"
|
|
35
|
+
<button type="button" (click)="clearAll.emit()" class="ogrid-empty-state-clear-btn">
|
|
30
36
|
clear all filters
|
|
31
37
|
</button>
|
|
32
38
|
to see all items.
|
|
@@ -104,17 +104,19 @@ MarchingAntsOverlayComponent = __decorate([
|
|
|
104
104
|
selector: 'ogrid-marching-ants-overlay',
|
|
105
105
|
standalone: true,
|
|
106
106
|
imports: [CommonModule],
|
|
107
|
+
styles: [`
|
|
108
|
+
.ogrid-marching-ants-svg { position: absolute; pointer-events: none; overflow: visible; }
|
|
109
|
+
.ogrid-marching-ants-svg--selection { z-index: 4; }
|
|
110
|
+
.ogrid-marching-ants-svg--clip { z-index: 5; }
|
|
111
|
+
`],
|
|
107
112
|
template: `
|
|
108
113
|
@if (selRect() && !clipRangeMatchesSel()) {
|
|
109
114
|
<svg
|
|
110
|
-
|
|
115
|
+
class="ogrid-marching-ants-svg ogrid-marching-ants-svg--selection"
|
|
111
116
|
[style.top.px]="selRect()!.top"
|
|
112
117
|
[style.left.px]="selRect()!.left"
|
|
113
118
|
[style.width.px]="selRect()!.width"
|
|
114
119
|
[style.height.px]="selRect()!.height"
|
|
115
|
-
[style.pointer-events]="'none'"
|
|
116
|
-
[style.z-index]="4"
|
|
117
|
-
[style.overflow]="'visible'"
|
|
118
120
|
aria-hidden="true"
|
|
119
121
|
>
|
|
120
122
|
<rect
|
|
@@ -129,14 +131,11 @@ MarchingAntsOverlayComponent = __decorate([
|
|
|
129
131
|
}
|
|
130
132
|
@if (clipRect()) {
|
|
131
133
|
<svg
|
|
132
|
-
|
|
134
|
+
class="ogrid-marching-ants-svg ogrid-marching-ants-svg--clip"
|
|
133
135
|
[style.top.px]="clipRect()!.top"
|
|
134
136
|
[style.left.px]="clipRect()!.left"
|
|
135
137
|
[style.width.px]="clipRect()!.width"
|
|
136
138
|
[style.height.px]="clipRect()!.height"
|
|
137
|
-
[style.pointer-events]="'none'"
|
|
138
|
-
[style.z-index]="5"
|
|
139
|
-
[style.overflow]="'visible'"
|
|
140
139
|
aria-hidden="true"
|
|
141
140
|
>
|
|
142
141
|
<rect
|
|
@@ -23,34 +23,47 @@ OGridLayoutComponent = __decorate([
|
|
|
23
23
|
selector: 'ogrid-layout',
|
|
24
24
|
standalone: true,
|
|
25
25
|
imports: [CommonModule, SideBarComponent],
|
|
26
|
+
styles: [`
|
|
27
|
+
.ogrid-layout-root { display: flex; flex-direction: column; height: 100%; }
|
|
28
|
+
.ogrid-layout-container {
|
|
29
|
+
border: 1px solid var(--ogrid-border, #e0e0e0);
|
|
30
|
+
overflow: hidden; display: flex; flex-direction: column;
|
|
31
|
+
flex: 1; min-height: 0; background: var(--ogrid-bg, #fff);
|
|
32
|
+
}
|
|
33
|
+
.ogrid-layout-toolbar {
|
|
34
|
+
display: flex; justify-content: space-between; align-items: center;
|
|
35
|
+
padding: 6px 12px; background: var(--ogrid-header-bg, #f5f5f5);
|
|
36
|
+
gap: 8px; flex-wrap: wrap; min-height: 0;
|
|
37
|
+
}
|
|
38
|
+
.ogrid-layout-toolbar--has-below { border-bottom: none; }
|
|
39
|
+
.ogrid-layout-toolbar--no-below { border-bottom: 1px solid var(--ogrid-border, #e0e0e0); }
|
|
40
|
+
.ogrid-layout-toolbar-left { display: flex; align-items: center; gap: 8px; }
|
|
41
|
+
.ogrid-layout-toolbar-right { display: flex; align-items: center; gap: 8px; }
|
|
42
|
+
.ogrid-layout-toolbar-below {
|
|
43
|
+
border-bottom: 1px solid var(--ogrid-border, #e0e0e0);
|
|
44
|
+
padding: 6px 12px; background: var(--ogrid-header-bg, #f5f5f5);
|
|
45
|
+
}
|
|
46
|
+
.ogrid-layout-grid-area { width: 100%; min-width: 0; min-height: 0; flex: 1; display: flex; }
|
|
47
|
+
.ogrid-layout-grid-content { flex: 1; min-width: 0; min-height: 0; display: flex; flex-direction: column; }
|
|
48
|
+
.ogrid-layout-footer {
|
|
49
|
+
border-top: 1px solid var(--ogrid-border, #e0e0e0);
|
|
50
|
+
background: var(--ogrid-header-bg, #f5f5f5); padding: 6px 12px;
|
|
51
|
+
}
|
|
52
|
+
`],
|
|
26
53
|
template: `
|
|
27
|
-
<div [class]="className() ?? ''
|
|
28
|
-
<div [style.border]="
|
|
29
|
-
[style.border-radius.px]="borderRadius"
|
|
30
|
-
[style.overflow]="'hidden'"
|
|
31
|
-
[style.display]="'flex'"
|
|
32
|
-
[style.flex-direction]="'column'"
|
|
33
|
-
[style.flex]="1"
|
|
34
|
-
[style.min-height]="0"
|
|
35
|
-
[style.background]="'var(--ogrid-bg, #fff)'"
|
|
36
|
-
>
|
|
54
|
+
<div [class]="(className() ?? '') + ' ogrid-layout-root'">
|
|
55
|
+
<div class="ogrid-layout-container" [style.border-radius.px]="borderRadius">
|
|
37
56
|
<!-- Toolbar strip -->
|
|
38
57
|
@if (hasToolbar()) {
|
|
39
58
|
<div
|
|
40
|
-
|
|
41
|
-
[
|
|
42
|
-
[
|
|
43
|
-
[style.padding]="'6px 12px'"
|
|
44
|
-
[style.background]="'var(--ogrid-header-bg, #f5f5f5)'"
|
|
45
|
-
[style.gap.px]="8"
|
|
46
|
-
[style.flex-wrap]="'wrap'"
|
|
47
|
-
[style.min-height]="0"
|
|
48
|
-
[style.border-bottom]="hasToolbarBelow() ? 'none' : '1px solid var(--ogrid-border, #e0e0e0)'"
|
|
59
|
+
class="ogrid-layout-toolbar"
|
|
60
|
+
[class.ogrid-layout-toolbar--has-below]="hasToolbarBelow()"
|
|
61
|
+
[class.ogrid-layout-toolbar--no-below]="!hasToolbarBelow()"
|
|
49
62
|
>
|
|
50
|
-
<div
|
|
63
|
+
<div class="ogrid-layout-toolbar-left">
|
|
51
64
|
<ng-content select="[toolbar]"></ng-content>
|
|
52
65
|
</div>
|
|
53
|
-
<div
|
|
66
|
+
<div class="ogrid-layout-toolbar-right">
|
|
54
67
|
<ng-content select="[toolbarEnd]"></ng-content>
|
|
55
68
|
</div>
|
|
56
69
|
</div>
|
|
@@ -58,18 +71,18 @@ OGridLayoutComponent = __decorate([
|
|
|
58
71
|
|
|
59
72
|
<!-- Secondary toolbar row -->
|
|
60
73
|
@if (hasToolbarBelow()) {
|
|
61
|
-
<div
|
|
74
|
+
<div class="ogrid-layout-toolbar-below">
|
|
62
75
|
<ng-content select="[toolbarBelow]"></ng-content>
|
|
63
76
|
</div>
|
|
64
77
|
}
|
|
65
78
|
|
|
66
79
|
<!-- Grid area -->
|
|
67
80
|
@if (sideBar()) {
|
|
68
|
-
<div
|
|
81
|
+
<div class="ogrid-layout-grid-area">
|
|
69
82
|
@if (sideBar()?.position === 'left') {
|
|
70
83
|
<ogrid-sidebar [sideBarProps]="sideBar()"></ogrid-sidebar>
|
|
71
84
|
}
|
|
72
|
-
<div
|
|
85
|
+
<div class="ogrid-layout-grid-content">
|
|
73
86
|
<ng-content></ng-content>
|
|
74
87
|
</div>
|
|
75
88
|
@if (sideBar()?.position !== 'left') {
|
|
@@ -77,14 +90,14 @@ OGridLayoutComponent = __decorate([
|
|
|
77
90
|
}
|
|
78
91
|
</div>
|
|
79
92
|
} @else {
|
|
80
|
-
<div
|
|
93
|
+
<div class="ogrid-layout-grid-content">
|
|
81
94
|
<ng-content></ng-content>
|
|
82
95
|
</div>
|
|
83
96
|
}
|
|
84
97
|
|
|
85
98
|
<!-- Footer strip (pagination) -->
|
|
86
99
|
@if (hasPagination()) {
|
|
87
|
-
<div
|
|
100
|
+
<div class="ogrid-layout-footer">
|
|
88
101
|
<ng-content select="[pagination]"></ng-content>
|
|
89
102
|
</div>
|
|
90
103
|
}
|
|
@@ -98,8 +98,70 @@ SideBarComponent = __decorate([
|
|
|
98
98
|
selector: 'ogrid-sidebar',
|
|
99
99
|
standalone: true,
|
|
100
100
|
imports: [CommonModule],
|
|
101
|
+
styles: [`
|
|
102
|
+
.ogrid-sidebar-root { display: flex; flex-direction: row; flex-shrink: 0; }
|
|
103
|
+
.ogrid-sidebar-tab-strip {
|
|
104
|
+
display: flex; flex-direction: column;
|
|
105
|
+
width: var(--ogrid-sidebar-tab-size, 36px);
|
|
106
|
+
background: var(--ogrid-header-bg, #f5f5f5);
|
|
107
|
+
}
|
|
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); }
|
|
110
|
+
.ogrid-sidebar-tab {
|
|
111
|
+
width: var(--ogrid-sidebar-tab-size, 36px);
|
|
112
|
+
height: var(--ogrid-sidebar-tab-size, 36px);
|
|
113
|
+
border: none; cursor: pointer;
|
|
114
|
+
color: var(--ogrid-fg, #242424); font-size: 14px;
|
|
115
|
+
display: flex; align-items: center; justify-content: center;
|
|
116
|
+
background: transparent; font-weight: normal;
|
|
117
|
+
}
|
|
118
|
+
.ogrid-sidebar-tab--active { background: var(--ogrid-bg, #fff); font-weight: bold; }
|
|
119
|
+
.ogrid-sidebar-panel {
|
|
120
|
+
width: var(--ogrid-sidebar-panel-width, 240px);
|
|
121
|
+
display: flex; flex-direction: column; overflow: hidden;
|
|
122
|
+
background: var(--ogrid-bg, #fff); color: var(--ogrid-fg, #242424);
|
|
123
|
+
}
|
|
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); }
|
|
126
|
+
.ogrid-sidebar-panel-header {
|
|
127
|
+
display: flex; justify-content: space-between; align-items: center;
|
|
128
|
+
padding: 8px 12px; border-bottom: 1px solid var(--ogrid-border, #e0e0e0); font-weight: 600;
|
|
129
|
+
}
|
|
130
|
+
.ogrid-sidebar-panel-close {
|
|
131
|
+
border: none; background: transparent; cursor: pointer;
|
|
132
|
+
font-size: 16px; color: var(--ogrid-fg, #242424);
|
|
133
|
+
}
|
|
134
|
+
.ogrid-sidebar-panel-body { flex: 1; overflow-y: auto; padding: 8px 12px; }
|
|
135
|
+
.ogrid-sidebar-actions { display: flex; gap: 8px; margin-bottom: 8px; }
|
|
136
|
+
.ogrid-sidebar-action-btn {
|
|
137
|
+
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;
|
|
140
|
+
}
|
|
141
|
+
.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; }
|
|
143
|
+
.ogrid-sidebar-filter-group { margin-bottom: 12px; }
|
|
144
|
+
.ogrid-sidebar-filter-label { font-weight: 500; margin-bottom: 4px; font-size: 13px; }
|
|
145
|
+
.ogrid-sidebar-text-input {
|
|
146
|
+
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;
|
|
149
|
+
}
|
|
150
|
+
.ogrid-sidebar-date-row { display: flex; flex-direction: column; gap: 4px; }
|
|
151
|
+
.ogrid-sidebar-date-label { display: flex; align-items: center; gap: 4px; font-size: 12px; }
|
|
152
|
+
.ogrid-sidebar-date-input {
|
|
153
|
+
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;
|
|
156
|
+
}
|
|
157
|
+
.ogrid-sidebar-multiselect-list { max-height: 120px; overflow-y: auto; }
|
|
158
|
+
.ogrid-sidebar-multiselect-item {
|
|
159
|
+
display: flex; align-items: center; gap: 4px;
|
|
160
|
+
padding: 1px 0; cursor: pointer; font-size: 13px;
|
|
161
|
+
}
|
|
162
|
+
`],
|
|
101
163
|
template: `
|
|
102
|
-
<div
|
|
164
|
+
<div class="ogrid-sidebar-root" role="complementary" aria-label="Side bar">
|
|
103
165
|
@if (sideBarProps()?.position === 'left') {
|
|
104
166
|
<ng-container *ngTemplateOutlet="tabStripTpl"></ng-container>
|
|
105
167
|
<ng-container *ngTemplateOutlet="panelContentTpl"></ng-container>
|
|
@@ -112,33 +174,21 @@ SideBarComponent = __decorate([
|
|
|
112
174
|
|
|
113
175
|
<ng-template #tabStripTpl>
|
|
114
176
|
<div
|
|
115
|
-
|
|
116
|
-
[
|
|
117
|
-
[
|
|
118
|
-
[style.background]="'var(--ogrid-header-bg, #f5f5f5)'"
|
|
119
|
-
[style.border-left]="sideBarProps()?.position === 'right' ? '1px solid var(--ogrid-border, #e0e0e0)' : 'none'"
|
|
120
|
-
[style.border-right]="sideBarProps()?.position === 'left' ? '1px solid var(--ogrid-border, #e0e0e0)' : 'none'"
|
|
177
|
+
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'"
|
|
121
180
|
role="tablist"
|
|
122
181
|
aria-label="Side bar tabs"
|
|
123
182
|
>
|
|
124
183
|
@for (panel of sideBarProps()?.panels ?? []; track panel) {
|
|
125
184
|
<button
|
|
126
185
|
role="tab"
|
|
186
|
+
class="ogrid-sidebar-tab"
|
|
187
|
+
[class.ogrid-sidebar-tab--active]="sideBarProps()?.activePanel === panel"
|
|
127
188
|
[attr.aria-selected]="sideBarProps()?.activePanel === panel"
|
|
128
189
|
[attr.aria-label]="panelLabels[panel]"
|
|
129
190
|
(click)="onTabClick(panel)"
|
|
130
191
|
[title]="panelLabels[panel]"
|
|
131
|
-
[style.width.px]="tabWidth"
|
|
132
|
-
[style.height.px]="tabWidth"
|
|
133
|
-
[style.border]="'none'"
|
|
134
|
-
[style.cursor]="'pointer'"
|
|
135
|
-
[style.color]="'var(--ogrid-fg, #242424)'"
|
|
136
|
-
[style.font-size.px]="14"
|
|
137
|
-
[style.display]="'flex'"
|
|
138
|
-
[style.align-items]="'center'"
|
|
139
|
-
[style.justify-content]="'center'"
|
|
140
|
-
[style.background]="sideBarProps()?.activePanel === panel ? 'var(--ogrid-bg, #fff)' : 'transparent'"
|
|
141
|
-
[style.font-weight]="sideBarProps()?.activePanel === panel ? 'bold' : 'normal'"
|
|
142
192
|
>
|
|
143
193
|
{{ panel === 'columns' ? '\u2261' : '\u2A65' }}
|
|
144
194
|
</button>
|
|
@@ -150,28 +200,23 @@ SideBarComponent = __decorate([
|
|
|
150
200
|
@if (sideBarProps()?.activePanel) {
|
|
151
201
|
<div
|
|
152
202
|
role="tabpanel"
|
|
203
|
+
class="ogrid-sidebar-panel"
|
|
204
|
+
[class.ogrid-sidebar-panel--left]="sideBarProps()?.position === 'left'"
|
|
205
|
+
[class.ogrid-sidebar-panel--right]="sideBarProps()?.position === 'right'"
|
|
153
206
|
[attr.aria-label]="panelLabels[sideBarProps()!.activePanel!]"
|
|
154
|
-
[style.width.px]="panelWidth"
|
|
155
|
-
[style.display]="'flex'"
|
|
156
|
-
[style.flex-direction]="'column'"
|
|
157
|
-
[style.overflow]="'hidden'"
|
|
158
|
-
[style.background]="'var(--ogrid-bg, #fff)'"
|
|
159
|
-
[style.color]="'var(--ogrid-fg, #242424)'"
|
|
160
|
-
[style.border-left]="sideBarProps()?.position === 'right' ? '1px solid var(--ogrid-border, #e0e0e0)' : 'none'"
|
|
161
|
-
[style.border-right]="sideBarProps()?.position === 'left' ? '1px solid var(--ogrid-border, #e0e0e0)' : 'none'"
|
|
162
207
|
>
|
|
163
|
-
<div
|
|
208
|
+
<div class="ogrid-sidebar-panel-header">
|
|
164
209
|
<span>{{ panelLabels[sideBarProps()!.activePanel!] }}</span>
|
|
165
|
-
<button (click)="sideBarProps()?.onPanelChange(null)"
|
|
210
|
+
<button (click)="sideBarProps()?.onPanelChange(null)" class="ogrid-sidebar-panel-close" aria-label="Close panel">×</button>
|
|
166
211
|
</div>
|
|
167
|
-
<div
|
|
212
|
+
<div class="ogrid-sidebar-panel-body">
|
|
168
213
|
@if (sideBarProps()?.activePanel === 'columns') {
|
|
169
|
-
<div
|
|
170
|
-
<button (click)="onSelectAll()" [disabled]="allVisible()"
|
|
171
|
-
<button (click)="onClearAll()"
|
|
214
|
+
<div class="ogrid-sidebar-actions">
|
|
215
|
+
<button (click)="onSelectAll()" [disabled]="allVisible()" class="ogrid-sidebar-action-btn">Select All</button>
|
|
216
|
+
<button (click)="onClearAll()" class="ogrid-sidebar-action-btn">Clear All</button>
|
|
172
217
|
</div>
|
|
173
218
|
@for (col of sideBarProps()?.columns ?? []; track col.columnId) {
|
|
174
|
-
<label
|
|
219
|
+
<label class="ogrid-sidebar-col-label">
|
|
175
220
|
<input type="checkbox" [checked]="sideBarProps()?.visibleColumns?.has(col.columnId)" (change)="onVisibilityChange(col.columnId, $any($event.target).checked)" [disabled]="col.required" />
|
|
176
221
|
<span>{{ col.name }}</span>
|
|
177
222
|
</label>
|
|
@@ -179,37 +224,37 @@ SideBarComponent = __decorate([
|
|
|
179
224
|
}
|
|
180
225
|
@if (sideBarProps()?.activePanel === 'filters') {
|
|
181
226
|
@if ((sideBarProps()?.filterableColumns ?? []).length === 0) {
|
|
182
|
-
<div
|
|
227
|
+
<div class="ogrid-sidebar-empty">No filterable columns</div>
|
|
183
228
|
}
|
|
184
229
|
@for (col of sideBarProps()?.filterableColumns ?? []; track col.columnId) {
|
|
185
|
-
<div
|
|
186
|
-
<div
|
|
230
|
+
<div class="ogrid-sidebar-filter-group">
|
|
231
|
+
<div class="ogrid-sidebar-filter-label">{{ col.name }}</div>
|
|
187
232
|
@if (col.filterType === 'text') {
|
|
188
233
|
<input
|
|
189
234
|
type="text"
|
|
235
|
+
class="ogrid-sidebar-text-input"
|
|
190
236
|
[value]="getTextFilterValue(col.filterField)"
|
|
191
237
|
(input)="onTextFilterChange(col.filterField, $any($event.target).value)"
|
|
192
238
|
[placeholder]="'Filter ' + col.name + '...'"
|
|
193
239
|
[attr.aria-label]="'Filter ' + col.name"
|
|
194
|
-
style="width:100%;box-sizing:border-box;padding:4px 6px;background:var(--ogrid-bg, #fff);color:var(--ogrid-fg, #242424);border:1px solid var(--ogrid-border, #e0e0e0);border-radius:4px"
|
|
195
240
|
/>
|
|
196
241
|
}
|
|
197
242
|
@if (col.filterType === 'date') {
|
|
198
|
-
<div
|
|
199
|
-
<label
|
|
243
|
+
<div class="ogrid-sidebar-date-row">
|
|
244
|
+
<label class="ogrid-sidebar-date-label">
|
|
200
245
|
From:
|
|
201
|
-
<input type="date" [value]="getDateFrom(col.filterField)" (change)="onDateFromChange(col.filterField, $any($event.target).value)" [attr.aria-label]="col.name + ' from date'"
|
|
246
|
+
<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'" />
|
|
202
247
|
</label>
|
|
203
|
-
<label
|
|
248
|
+
<label class="ogrid-sidebar-date-label">
|
|
204
249
|
To:
|
|
205
|
-
<input type="date" [value]="getDateTo(col.filterField)" (change)="onDateToChange(col.filterField, $any($event.target).value)" [attr.aria-label]="col.name + ' to date'"
|
|
250
|
+
<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'" />
|
|
206
251
|
</label>
|
|
207
252
|
</div>
|
|
208
253
|
}
|
|
209
254
|
@if (col.filterType === 'multiSelect') {
|
|
210
|
-
<div
|
|
255
|
+
<div class="ogrid-sidebar-multiselect-list" role="group" [attr.aria-label]="col.name + ' options'">
|
|
211
256
|
@for (opt of getFilterOptions(col.filterField); track opt) {
|
|
212
|
-
<label
|
|
257
|
+
<label class="ogrid-sidebar-multiselect-item">
|
|
213
258
|
<input type="checkbox" [checked]="isMultiSelectChecked(col.filterField, opt)" (change)="onMultiSelectChange(col.filterField, opt, $any($event.target).checked)" />
|
|
214
259
|
<span>{{ opt }}</span>
|
|
215
260
|
</label>
|
package/dist/esm/index.js
CHANGED
|
@@ -13,4 +13,5 @@ export { GridContextMenuComponent } from './components/grid-context-menu.compone
|
|
|
13
13
|
export { SideBarComponent } from './components/sidebar.component';
|
|
14
14
|
export { MarchingAntsOverlayComponent } from './components/marching-ants-overlay.component';
|
|
15
15
|
export { EmptyStateComponent } from './components/empty-state.component';
|
|
16
|
+
export { BaseDataGridTableComponent } from './components/base-datagrid-table.component';
|
|
16
17
|
export { getHeaderFilterConfig, getCellRenderDescriptor, resolveCellDisplayContent, resolveCellStyle, createDebouncedSignal, createDebouncedCallback, debounce, createLatestRef, createLatestCallback, } from './utils';
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { ElementRef } from '@angular/core';
|
|
2
|
+
import { DataGridStateService } from '../services/datagrid-state.service';
|
|
3
|
+
import { ColumnReorderService } from '../services/column-reorder.service';
|
|
4
|
+
import { VirtualScrollService } from '../services/virtual-scroll.service';
|
|
5
|
+
import type { IOGridDataGridProps, IColumnDef, RowId } from '../types';
|
|
6
|
+
import type { HeaderFilterConfig, CellRenderDescriptor } from '../utils';
|
|
7
|
+
/**
|
|
8
|
+
* Abstract base class containing all shared TypeScript logic for DataGridTable components.
|
|
9
|
+
* Framework-specific UI packages extend this with their templates and style overrides.
|
|
10
|
+
*
|
|
11
|
+
* Subclasses must:
|
|
12
|
+
* 1. Provide a @Component decorator with template and styles
|
|
13
|
+
* 2. Call `initBase()` in the constructor (effects require injection context)
|
|
14
|
+
* 3. Implement abstract accessors for propsInput, wrapperRef, and tableContainerRef
|
|
15
|
+
*/
|
|
16
|
+
export declare abstract class BaseDataGridTableComponent<T = any> {
|
|
17
|
+
readonly stateService: DataGridStateService<T>;
|
|
18
|
+
readonly columnReorderService: ColumnReorderService<T>;
|
|
19
|
+
readonly virtualScrollService: VirtualScrollService;
|
|
20
|
+
protected lastMouseShift: boolean;
|
|
21
|
+
readonly columnSizingVersion: import("@angular/core").WritableSignal<number>;
|
|
22
|
+
/** Return the IOGridDataGridProps from however the subclass receives them */
|
|
23
|
+
protected abstract getProps(): IOGridDataGridProps<T> | undefined;
|
|
24
|
+
/** Return the wrapper element ref */
|
|
25
|
+
protected abstract getWrapperRef(): ElementRef<HTMLElement> | undefined;
|
|
26
|
+
/** Return the table container element ref */
|
|
27
|
+
protected abstract getTableContainerRef(): ElementRef<HTMLElement> | undefined;
|
|
28
|
+
readonly state: import("@angular/core").Signal<import("../services/datagrid-state.service").DataGridStateResult<T>>;
|
|
29
|
+
readonly tableContainerEl: import("@angular/core").Signal<HTMLElement | null>;
|
|
30
|
+
readonly items: import("@angular/core").Signal<T[]>;
|
|
31
|
+
readonly getRowId: import("@angular/core").Signal<(item: T) => RowId>;
|
|
32
|
+
readonly isLoading: import("@angular/core").Signal<boolean>;
|
|
33
|
+
readonly loadingMessage: import("@angular/core").Signal<string>;
|
|
34
|
+
readonly freezeRows: import("@angular/core").Signal<number | undefined>;
|
|
35
|
+
readonly freezeCols: import("@angular/core").Signal<number | undefined>;
|
|
36
|
+
readonly layoutModeFit: import("@angular/core").Signal<boolean>;
|
|
37
|
+
readonly ariaLabel: import("@angular/core").Signal<string>;
|
|
38
|
+
readonly ariaLabelledBy: import("@angular/core").Signal<string | undefined>;
|
|
39
|
+
readonly emptyState: import("@angular/core").Signal<{
|
|
40
|
+
onClearAll: () => void;
|
|
41
|
+
hasActiveFilters: boolean;
|
|
42
|
+
message?: string;
|
|
43
|
+
render?: import("@angular/core").TemplateRef<unknown>;
|
|
44
|
+
} | undefined>;
|
|
45
|
+
readonly currentPage: import("@angular/core").Signal<number>;
|
|
46
|
+
readonly pageSize: import("@angular/core").Signal<number>;
|
|
47
|
+
readonly rowNumberOffset: import("@angular/core").Signal<number>;
|
|
48
|
+
readonly visibleCols: import("@angular/core").Signal<IColumnDef<T>[]>;
|
|
49
|
+
readonly hasCheckboxCol: import("@angular/core").Signal<boolean>;
|
|
50
|
+
readonly hasRowNumbersCol: import("@angular/core").Signal<boolean>;
|
|
51
|
+
readonly colOffset: import("@angular/core").Signal<number>;
|
|
52
|
+
readonly containerWidth: import("@angular/core").Signal<number>;
|
|
53
|
+
readonly minTableWidth: import("@angular/core").Signal<number>;
|
|
54
|
+
readonly desiredTableWidth: import("@angular/core").Signal<number>;
|
|
55
|
+
readonly columnSizingOverrides: import("@angular/core").Signal<Record<string, {
|
|
56
|
+
widthPx: number;
|
|
57
|
+
}>>;
|
|
58
|
+
readonly selectedRowIds: import("@angular/core").Signal<Set<RowId>>;
|
|
59
|
+
readonly allSelected: import("@angular/core").Signal<boolean>;
|
|
60
|
+
readonly someSelected: import("@angular/core").Signal<boolean>;
|
|
61
|
+
readonly editingCell: import("@angular/core").Signal<{
|
|
62
|
+
rowId: RowId;
|
|
63
|
+
columnId: string;
|
|
64
|
+
} | null>;
|
|
65
|
+
readonly pendingEditorValue: import("@angular/core").Signal<unknown>;
|
|
66
|
+
readonly activeCell: import("@angular/core").Signal<import("@alaarab/ogrid-core").IActiveCell | null>;
|
|
67
|
+
readonly selectionRange: import("@angular/core").Signal<import("@alaarab/ogrid-core").ISelectionRange | null>;
|
|
68
|
+
readonly hasCellSelection: import("@angular/core").Signal<boolean>;
|
|
69
|
+
readonly cutRange: import("@angular/core").Signal<import("@alaarab/ogrid-core").ISelectionRange | null>;
|
|
70
|
+
readonly copyRange: import("@angular/core").Signal<import("@alaarab/ogrid-core").ISelectionRange | null>;
|
|
71
|
+
readonly canUndo: import("@angular/core").Signal<boolean>;
|
|
72
|
+
readonly canRedo: import("@angular/core").Signal<boolean>;
|
|
73
|
+
readonly isDragging: import("@angular/core").Signal<boolean>;
|
|
74
|
+
readonly menuPosition: import("@angular/core").Signal<{
|
|
75
|
+
x: number;
|
|
76
|
+
y: number;
|
|
77
|
+
} | null>;
|
|
78
|
+
readonly statusBarConfig: import("@angular/core").Signal<import("@alaarab/ogrid-core").IStatusBarProps | null>;
|
|
79
|
+
readonly showEmptyInGrid: import("@angular/core").Signal<boolean>;
|
|
80
|
+
readonly headerFilterInput: import("@angular/core").Signal<{
|
|
81
|
+
sortBy?: string;
|
|
82
|
+
sortDirection: "asc" | "desc";
|
|
83
|
+
onColumnSort: (columnKey: string) => void;
|
|
84
|
+
filters: import("@alaarab/ogrid-core").IFilters;
|
|
85
|
+
onFilterChange: (key: string, value: import("@alaarab/ogrid-core").FilterValue | undefined) => void;
|
|
86
|
+
filterOptions: Record<string, string[]>;
|
|
87
|
+
loadingFilterOptions: Record<string, boolean>;
|
|
88
|
+
peopleSearch?: (query: string) => Promise<import("@alaarab/ogrid-core").UserLike[]>;
|
|
89
|
+
}>;
|
|
90
|
+
readonly cellDescriptorInput: import("@angular/core").Signal<{
|
|
91
|
+
editingCell: {
|
|
92
|
+
rowId: RowId;
|
|
93
|
+
columnId: string;
|
|
94
|
+
} | null;
|
|
95
|
+
activeCell: import("@alaarab/ogrid-core").IActiveCell | null;
|
|
96
|
+
selectionRange: import("@alaarab/ogrid-core").ISelectionRange | null;
|
|
97
|
+
cutRange: import("@alaarab/ogrid-core").ISelectionRange | null;
|
|
98
|
+
copyRange: import("@alaarab/ogrid-core").ISelectionRange | null;
|
|
99
|
+
colOffset: number;
|
|
100
|
+
itemsLength: number;
|
|
101
|
+
getRowId: (item: T) => RowId;
|
|
102
|
+
editable?: boolean;
|
|
103
|
+
onCellValueChanged?: ((event: import("@alaarab/ogrid-core").ICellValueChangedEvent<T>) => void) | undefined;
|
|
104
|
+
isDragging: boolean;
|
|
105
|
+
}>;
|
|
106
|
+
readonly allowOverflowX: import("@angular/core").Signal<boolean>;
|
|
107
|
+
readonly selectionCellCount: import("@angular/core").Signal<number | undefined>;
|
|
108
|
+
readonly headerRows: import("@angular/core").Signal<import("@alaarab/ogrid-core").HeaderRow<T>[]>;
|
|
109
|
+
readonly columnLayouts: import("@angular/core").Signal<{
|
|
110
|
+
col: IColumnDef<T>;
|
|
111
|
+
pinnedLeft: boolean;
|
|
112
|
+
pinnedRight: boolean;
|
|
113
|
+
minWidth: number;
|
|
114
|
+
width: number;
|
|
115
|
+
}[]>;
|
|
116
|
+
/**
|
|
117
|
+
* Initialize base wiring effects. Must be called from subclass constructor
|
|
118
|
+
* (effects need to run inside an injection context).
|
|
119
|
+
*/
|
|
120
|
+
protected initBase(): void;
|
|
121
|
+
asColumnDef(colDef: unknown): IColumnDef<T>;
|
|
122
|
+
visibleColIndex(col: IColumnDef<T>): number;
|
|
123
|
+
getColumnWidth(col: IColumnDef<T>): number;
|
|
124
|
+
getFilterConfig(col: IColumnDef<T>): HeaderFilterConfig;
|
|
125
|
+
getCellDescriptor(item: T, col: IColumnDef<T>, rowIndex: number, colIdx: number): CellRenderDescriptor;
|
|
126
|
+
resolveCellContent(col: IColumnDef<T>, item: T, displayValue: unknown): string;
|
|
127
|
+
resolveCellStyleFn(col: IColumnDef<T>, item: T): Record<string, string> | undefined;
|
|
128
|
+
getSelectValues(col: IColumnDef<T>): string[];
|
|
129
|
+
formatDateForInput(value: unknown): string;
|
|
130
|
+
onWrapperMouseDown(event: MouseEvent): void;
|
|
131
|
+
onGridKeyDown(event: KeyboardEvent): void;
|
|
132
|
+
onCellMouseDown(event: MouseEvent, rowIndex: number, globalColIndex: number): void;
|
|
133
|
+
onCellClick(rowIndex: number, globalColIndex: number): void;
|
|
134
|
+
onCellContextMenu(event: MouseEvent): void;
|
|
135
|
+
onCellDblClick(rowId: RowId, columnId: string): void;
|
|
136
|
+
onFillHandleMouseDown(event: MouseEvent): void;
|
|
137
|
+
onResizeStart(event: MouseEvent, col: IColumnDef<T>): void;
|
|
138
|
+
onSelectAllChange(event: Event): void;
|
|
139
|
+
onRowClick(event: MouseEvent, rowId: RowId): void;
|
|
140
|
+
onRowCheckboxChange(rowId: RowId, event: Event, rowIndex: number): void;
|
|
141
|
+
commitEdit(item: T, columnId: string, oldValue: unknown, newValue: unknown, rowIndex: number, globalColIndex: number): void;
|
|
142
|
+
cancelEdit(): void;
|
|
143
|
+
onEditorKeydown(event: KeyboardEvent, item: T, columnId: string, oldValue: unknown, rowIndex: number, globalColIndex: number): void;
|
|
144
|
+
closeContextMenu(): void;
|
|
145
|
+
handleCopy(): void;
|
|
146
|
+
handleCut(): void;
|
|
147
|
+
handlePaste(): void;
|
|
148
|
+
handleSelectAllCells(): void;
|
|
149
|
+
onUndo(): void;
|
|
150
|
+
onRedo(): void;
|
|
151
|
+
onHeaderMouseDown(columnId: string, event: MouseEvent): void;
|
|
152
|
+
onPinColumn(columnId: string, side: 'left' | 'right'): void;
|
|
153
|
+
onUnpinColumn(columnId: string): void;
|
|
154
|
+
isPinned(columnId: string): 'left' | 'right' | undefined;
|
|
155
|
+
getPinState(columnId: string): {
|
|
156
|
+
canPinLeft: boolean;
|
|
157
|
+
canPinRight: boolean;
|
|
158
|
+
canUnpin: boolean;
|
|
159
|
+
};
|
|
160
|
+
}
|
package/dist/types/index.d.ts
CHANGED
|
@@ -17,5 +17,6 @@ export { SideBarComponent } from './components/sidebar.component';
|
|
|
17
17
|
export type { SideBarProps, SideBarFilterColumn } from './components/sidebar.component';
|
|
18
18
|
export { MarchingAntsOverlayComponent } from './components/marching-ants-overlay.component';
|
|
19
19
|
export { EmptyStateComponent } from './components/empty-state.component';
|
|
20
|
+
export { BaseDataGridTableComponent } from './components/base-datagrid-table.component';
|
|
20
21
|
export type { HeaderFilterConfigInput, HeaderFilterConfig, CellRenderDescriptorInput, CellRenderDescriptor, CellRenderMode, } from './utils';
|
|
21
22
|
export { getHeaderFilterConfig, getCellRenderDescriptor, resolveCellDisplayContent, resolveCellStyle, createDebouncedSignal, createDebouncedCallback, debounce, createLatestRef, createLatestCallback, } from './utils';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alaarab/ogrid-angular",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.6",
|
|
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",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"files": ["dist", "README.md", "LICENSE"],
|
|
23
23
|
"engines": { "node": ">=18" },
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@alaarab/ogrid-core": "2.0.
|
|
25
|
+
"@alaarab/ogrid-core": "2.0.6"
|
|
26
26
|
},
|
|
27
27
|
"peerDependencies": {
|
|
28
28
|
"@angular/core": "^21.0.0",
|
|
@@ -34,9 +34,10 @@
|
|
|
34
34
|
"@angular/compiler": "^21.1.4",
|
|
35
35
|
"@angular/platform-browser": "^21.1.4",
|
|
36
36
|
"@angular/platform-browser-dynamic": "^21.1.4",
|
|
37
|
-
"rxjs": "^7.8.
|
|
37
|
+
"rxjs": "^7.8.2",
|
|
38
38
|
"zone.js": "^0.15.0",
|
|
39
|
-
"typescript": "^5.
|
|
39
|
+
"typescript": "^5.9.3"
|
|
40
40
|
},
|
|
41
|
+
"sideEffects": false,
|
|
41
42
|
"publishConfig": { "access": "public" }
|
|
42
43
|
}
|