@alaarab/ogrid-angular 2.0.23 → 2.1.0
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-column-header-filter.component.js +26 -11
- package/dist/esm/components/base-datagrid-table.component.js +64 -46
- package/dist/esm/components/base-ogrid.component.js +36 -0
- package/dist/esm/components/base-popover-cell-editor.component.js +6 -7
- package/dist/esm/components/empty-state.component.js +2 -2
- package/dist/esm/components/grid-context-menu.component.js +17 -17
- package/dist/esm/components/ogrid-layout.component.js +2 -49
- package/dist/esm/components/sidebar.component.js +2 -6
- package/dist/esm/components/status-bar.component.js +6 -2
- package/dist/esm/index.js +5 -1
- package/dist/esm/services/datagrid-editing.service.js +52 -0
- package/dist/esm/services/datagrid-interaction.service.js +667 -0
- package/dist/esm/services/datagrid-layout.service.js +151 -0
- package/dist/esm/services/datagrid-state.service.js +130 -865
- package/dist/esm/services/ogrid.service.js +61 -26
- package/dist/esm/utils/index.js +1 -1
- package/dist/esm/utils/latestRef.js +0 -5
- package/dist/types/components/base-column-header-filter.component.d.ts +2 -0
- package/dist/types/components/base-datagrid-table.component.d.ts +21 -5
- package/dist/types/components/base-ogrid.component.d.ts +10 -0
- package/dist/types/components/grid-context-menu.component.d.ts +5 -5
- package/dist/types/components/sidebar.component.d.ts +0 -2
- package/dist/types/components/status-bar.component.d.ts +4 -1
- package/dist/types/index.d.ts +5 -1
- package/dist/types/services/datagrid-editing.service.d.ts +31 -0
- package/dist/types/services/datagrid-interaction.service.d.ts +86 -0
- package/dist/types/services/datagrid-layout.service.d.ts +36 -0
- package/dist/types/services/datagrid-state.service.d.ts +20 -39
- package/dist/types/services/ogrid.service.d.ts +8 -3
- package/dist/types/types/dataGridTypes.d.ts +1 -1
- package/dist/types/utils/index.d.ts +1 -1
- package/dist/types/utils/latestRef.d.ts +0 -5
- package/package.json +10 -3
|
@@ -116,12 +116,14 @@ export class BaseColumnHeaderFilterComponent {
|
|
|
116
116
|
}
|
|
117
117
|
}
|
|
118
118
|
handleTextApply() {
|
|
119
|
-
this.onTextChange
|
|
119
|
+
if (this.onTextChange)
|
|
120
|
+
this.onTextChange(this.tempTextValue());
|
|
120
121
|
this.isFilterOpen.set(false);
|
|
121
122
|
}
|
|
122
123
|
handleTextClear() {
|
|
123
124
|
this.tempTextValue.set('');
|
|
124
|
-
this.onTextChange
|
|
125
|
+
if (this.onTextChange)
|
|
126
|
+
this.onTextChange('');
|
|
125
127
|
this.isFilterOpen.set(false);
|
|
126
128
|
}
|
|
127
129
|
// --- MultiSelect filter handlers ---
|
|
@@ -148,7 +150,8 @@ export class BaseColumnHeaderFilterComponent {
|
|
|
148
150
|
this.tempSelected.set(new Set());
|
|
149
151
|
}
|
|
150
152
|
handleApplyMultiSelect() {
|
|
151
|
-
this.onFilterChange
|
|
153
|
+
if (this.onFilterChange)
|
|
154
|
+
this.onFilterChange(Array.from(this.tempSelected()));
|
|
152
155
|
this.isFilterOpen.set(false);
|
|
153
156
|
}
|
|
154
157
|
// --- People filter handlers ---
|
|
@@ -180,31 +183,43 @@ export class BaseColumnHeaderFilterComponent {
|
|
|
180
183
|
}, 300);
|
|
181
184
|
}
|
|
182
185
|
handleUserSelect(user) {
|
|
183
|
-
this.onUserChange
|
|
186
|
+
if (this.onUserChange)
|
|
187
|
+
this.onUserChange(user);
|
|
184
188
|
this.isFilterOpen.set(false);
|
|
185
189
|
}
|
|
186
190
|
handleClearUser() {
|
|
187
|
-
this.onUserChange
|
|
191
|
+
if (this.onUserChange)
|
|
192
|
+
this.onUserChange(undefined);
|
|
188
193
|
this.isFilterOpen.set(false);
|
|
189
194
|
}
|
|
190
195
|
// --- Date filter handlers ---
|
|
191
196
|
handleDateApply() {
|
|
192
197
|
const from = this.tempDateFrom();
|
|
193
198
|
const to = this.tempDateTo();
|
|
194
|
-
if (
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
+
if (this.onDateChange) {
|
|
200
|
+
if (!from && !to) {
|
|
201
|
+
this.onDateChange(undefined);
|
|
202
|
+
}
|
|
203
|
+
else {
|
|
204
|
+
this.onDateChange({ from: from || undefined, to: to || undefined });
|
|
205
|
+
}
|
|
199
206
|
}
|
|
200
207
|
this.isFilterOpen.set(false);
|
|
201
208
|
}
|
|
202
209
|
handleDateClear() {
|
|
203
210
|
this.tempDateFrom.set('');
|
|
204
211
|
this.tempDateTo.set('');
|
|
205
|
-
this.onDateChange
|
|
212
|
+
if (this.onDateChange)
|
|
213
|
+
this.onDateChange(undefined);
|
|
206
214
|
this.isFilterOpen.set(false);
|
|
207
215
|
}
|
|
216
|
+
/** Clean up debounce timer on destroy. */
|
|
217
|
+
ngOnDestroy() {
|
|
218
|
+
if (this.peopleDebounceTimer) {
|
|
219
|
+
clearTimeout(this.peopleDebounceTimer);
|
|
220
|
+
this.peopleDebounceTimer = null;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
208
223
|
// --- Document click handler (for click-outside to close) ---
|
|
209
224
|
onDocumentClick(event, selectorName) {
|
|
210
225
|
const el = event.target;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { signal, computed, effect } from '@angular/core';
|
|
1
|
+
import { signal, computed, effect, inject } 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';
|
|
@@ -15,13 +15,13 @@ import { getHeaderFilterConfig, getCellRenderDescriptor, resolveCellDisplayConte
|
|
|
15
15
|
*/
|
|
16
16
|
export class BaseDataGridTableComponent {
|
|
17
17
|
constructor() {
|
|
18
|
-
this.stateService =
|
|
19
|
-
this.columnReorderService =
|
|
20
|
-
this.virtualScrollService =
|
|
18
|
+
this.stateService = inject(DataGridStateService);
|
|
19
|
+
this.columnReorderService = inject(ColumnReorderService);
|
|
20
|
+
this.virtualScrollService = inject(VirtualScrollService);
|
|
21
21
|
this.lastMouseShift = false;
|
|
22
22
|
this.columnSizingVersion = signal(0);
|
|
23
23
|
/** Dirty flag — set when column layout changes, cleared after measurement. */
|
|
24
|
-
this.measureDirty = true;
|
|
24
|
+
this.measureDirty = signal(true);
|
|
25
25
|
/** DOM-measured column widths from the last layout pass.
|
|
26
26
|
* Used as a minWidth floor to prevent columns from shrinking
|
|
27
27
|
* when new data loads (e.g. server-side pagination). */
|
|
@@ -33,8 +33,16 @@ export class BaseDataGridTableComponent {
|
|
|
33
33
|
this.tableContainerElSignal = signal(null);
|
|
34
34
|
// --- Delegated state ---
|
|
35
35
|
this.state = computed(() => this.stateService.getState());
|
|
36
|
+
// Intermediate computed signals — narrow slices of state() so leaf computeds
|
|
37
|
+
// only recompute when their specific sub-state changes.
|
|
38
|
+
this.layoutState = computed(() => this.state().layout);
|
|
39
|
+
this.rowSelectionState = computed(() => this.state().rowSelection);
|
|
40
|
+
this.editingState = computed(() => this.state().editing);
|
|
41
|
+
this.interactionState = computed(() => this.state().interaction);
|
|
42
|
+
this.contextMenuState = computed(() => this.state().contextMenu);
|
|
43
|
+
this.viewModelsState = computed(() => this.state().viewModels);
|
|
44
|
+
this.pinningState = computed(() => this.state().pinning);
|
|
36
45
|
this.tableContainerEl = computed(() => this.tableContainerElSignal());
|
|
37
|
-
this.allItems = computed(() => this.getProps()?.items ?? []);
|
|
38
46
|
this.items = computed(() => this.getProps()?.items ?? []);
|
|
39
47
|
this.getRowId = computed(() => this.getProps()?.getRowId ?? ((item) => item['id']));
|
|
40
48
|
this.isLoading = computed(() => this.getProps()?.isLoading ?? false);
|
|
@@ -52,35 +60,35 @@ export class BaseDataGridTableComponent {
|
|
|
52
60
|
this.rowNumberOffset = computed(() => this.hasRowNumbersCol() ? (this.currentPage() - 1) * this.pageSize() : 0);
|
|
53
61
|
this.propsVisibleColumns = computed(() => this.getProps()?.visibleColumns);
|
|
54
62
|
this.propsColumnOrder = computed(() => this.getProps()?.columnOrder);
|
|
55
|
-
// State service outputs
|
|
56
|
-
this.visibleCols = computed(() => this.
|
|
57
|
-
this.hasCheckboxCol = computed(() => this.
|
|
58
|
-
this.hasRowNumbersCol = computed(() => this.
|
|
59
|
-
this.colOffset = computed(() => this.
|
|
60
|
-
this.containerWidth = computed(() => this.
|
|
61
|
-
this.minTableWidth = computed(() => this.
|
|
62
|
-
this.desiredTableWidth = computed(() => this.
|
|
63
|
-
this.columnSizingOverrides = computed(() => this.
|
|
64
|
-
this.selectedRowIds = computed(() => this.
|
|
65
|
-
this.allSelected = computed(() => this.
|
|
66
|
-
this.someSelected = computed(() => this.
|
|
67
|
-
this.editingCell = computed(() => this.
|
|
68
|
-
this.pendingEditorValue = computed(() => this.
|
|
69
|
-
this.activeCell = computed(() => this.
|
|
70
|
-
this.selectionRange = computed(() => this.
|
|
71
|
-
this.hasCellSelection = computed(() => this.
|
|
72
|
-
this.cutRange = computed(() => this.
|
|
73
|
-
this.copyRange = computed(() => this.
|
|
74
|
-
this.canUndo = computed(() => this.
|
|
75
|
-
this.canRedo = computed(() => this.
|
|
76
|
-
this.isDragging = computed(() => this.
|
|
77
|
-
this.menuPosition = computed(() => this.
|
|
78
|
-
this.statusBarConfig = computed(() => this.
|
|
79
|
-
this.showEmptyInGrid = computed(() => this.
|
|
80
|
-
this.headerFilterInput = computed(() => this.
|
|
81
|
-
this.cellDescriptorInput = computed(() => this.
|
|
63
|
+
// State service outputs — read from narrow intermediate signals
|
|
64
|
+
this.visibleCols = computed(() => this.layoutState().visibleCols);
|
|
65
|
+
this.hasCheckboxCol = computed(() => this.layoutState().hasCheckboxCol);
|
|
66
|
+
this.hasRowNumbersCol = computed(() => this.layoutState().hasRowNumbersCol);
|
|
67
|
+
this.colOffset = computed(() => this.layoutState().colOffset);
|
|
68
|
+
this.containerWidth = computed(() => this.layoutState().containerWidth);
|
|
69
|
+
this.minTableWidth = computed(() => this.layoutState().minTableWidth);
|
|
70
|
+
this.desiredTableWidth = computed(() => this.layoutState().desiredTableWidth);
|
|
71
|
+
this.columnSizingOverrides = computed(() => this.layoutState().columnSizingOverrides);
|
|
72
|
+
this.selectedRowIds = computed(() => this.rowSelectionState().selectedRowIds);
|
|
73
|
+
this.allSelected = computed(() => this.rowSelectionState().allSelected);
|
|
74
|
+
this.someSelected = computed(() => this.rowSelectionState().someSelected);
|
|
75
|
+
this.editingCell = computed(() => this.editingState().editingCell);
|
|
76
|
+
this.pendingEditorValue = computed(() => this.editingState().pendingEditorValue);
|
|
77
|
+
this.activeCell = computed(() => this.interactionState().activeCell);
|
|
78
|
+
this.selectionRange = computed(() => this.interactionState().selectionRange);
|
|
79
|
+
this.hasCellSelection = computed(() => this.interactionState().hasCellSelection);
|
|
80
|
+
this.cutRange = computed(() => this.interactionState().cutRange);
|
|
81
|
+
this.copyRange = computed(() => this.interactionState().copyRange);
|
|
82
|
+
this.canUndo = computed(() => this.interactionState().canUndo);
|
|
83
|
+
this.canRedo = computed(() => this.interactionState().canRedo);
|
|
84
|
+
this.isDragging = computed(() => this.interactionState().isDragging);
|
|
85
|
+
this.menuPosition = computed(() => this.contextMenuState().menuPosition);
|
|
86
|
+
this.statusBarConfig = computed(() => this.viewModelsState().statusBarConfig);
|
|
87
|
+
this.showEmptyInGrid = computed(() => this.viewModelsState().showEmptyInGrid);
|
|
88
|
+
this.headerFilterInput = computed(() => this.viewModelsState().headerFilterInput);
|
|
89
|
+
this.cellDescriptorInput = computed(() => this.viewModelsState().cellDescriptorInput);
|
|
82
90
|
// Pinning state
|
|
83
|
-
this.pinnedColumnsMap = computed(() => this.
|
|
91
|
+
this.pinnedColumnsMap = computed(() => this.pinningState().pinnedColumns);
|
|
84
92
|
// Virtual scrolling
|
|
85
93
|
this.vsEnabled = computed(() => this.virtualScrollService.isActive());
|
|
86
94
|
this.vsVisibleRange = computed(() => this.virtualScrollService.visibleRange());
|
|
@@ -95,7 +103,7 @@ export class BaseDataGridTableComponent {
|
|
|
95
103
|
return this.vsVisibleRange().offsetBottom;
|
|
96
104
|
});
|
|
97
105
|
this.vsVisibleItems = computed(() => {
|
|
98
|
-
const items = this.
|
|
106
|
+
const items = this.items();
|
|
99
107
|
if (!this.vsEnabled())
|
|
100
108
|
return items;
|
|
101
109
|
const range = this.vsVisibleRange();
|
|
@@ -107,8 +115,8 @@ export class BaseDataGridTableComponent {
|
|
|
107
115
|
return this.vsVisibleRange().startIndex;
|
|
108
116
|
});
|
|
109
117
|
// Popover editing
|
|
110
|
-
this.popoverAnchorEl = computed(() => this.
|
|
111
|
-
this.pendingEditorValueForPopover = computed(() => this.
|
|
118
|
+
this.popoverAnchorEl = computed(() => this.editingState().popoverAnchorEl);
|
|
119
|
+
this.pendingEditorValueForPopover = computed(() => this.editingState().pendingEditorValue);
|
|
112
120
|
this.allowOverflowX = computed(() => {
|
|
113
121
|
const p = this.getProps();
|
|
114
122
|
if (p?.suppressHorizontalScroll)
|
|
@@ -209,8 +217,8 @@ export class BaseDataGridTableComponent {
|
|
|
209
217
|
}
|
|
210
218
|
/** Lifecycle hook — re-measure column widths only when layout changed */
|
|
211
219
|
ngAfterViewChecked() {
|
|
212
|
-
if (this.measureDirty) {
|
|
213
|
-
this.measureDirty
|
|
220
|
+
if (this.measureDirty()) {
|
|
221
|
+
this.measureDirty.set(false);
|
|
214
222
|
this.measureColumnWidths();
|
|
215
223
|
}
|
|
216
224
|
}
|
|
@@ -245,8 +253,18 @@ export class BaseDataGridTableComponent {
|
|
|
245
253
|
}
|
|
246
254
|
}
|
|
247
255
|
/**
|
|
248
|
-
* Initialize base wiring effects. Must be called from subclass constructor
|
|
249
|
-
*
|
|
256
|
+
* Initialize base wiring effects. Must be called from subclass constructor.
|
|
257
|
+
*
|
|
258
|
+
* **Timing:** Angular requires `effect()` to be created inside an injection
|
|
259
|
+
* context (constructor or field initializer). On the first run, signals like
|
|
260
|
+
* `wrapperElSignal()` return `null` because the DOM hasn't been created yet.
|
|
261
|
+
* After `ngAfterViewInit` sets these signals, Angular's signal graph
|
|
262
|
+
* automatically re-runs each effect. The null guards inside each effect body
|
|
263
|
+
* ensure the first (null) run is a safe no-op.
|
|
264
|
+
*
|
|
265
|
+
* Sequence:
|
|
266
|
+
* 1. Constructor → `initBase()` → effects created, first run (signals null → no-ops)
|
|
267
|
+
* 2. `ngAfterViewInit` → `wrapperElSignal.set(el)` → effects re-run with real values
|
|
250
268
|
*/
|
|
251
269
|
initBase() {
|
|
252
270
|
// Wire props to state service
|
|
@@ -280,7 +298,7 @@ export class BaseDataGridTableComponent {
|
|
|
280
298
|
this.visibleCols();
|
|
281
299
|
this.columnSizingOverrides();
|
|
282
300
|
this.columnSizingVersion();
|
|
283
|
-
this.measureDirty
|
|
301
|
+
this.measureDirty.set(true);
|
|
284
302
|
});
|
|
285
303
|
// Wire virtual scroll service inputs
|
|
286
304
|
effect(() => {
|
|
@@ -468,7 +486,7 @@ export class BaseDataGridTableComponent {
|
|
|
468
486
|
this.state().interaction.handleCellMouseDown(event, rowIndex, globalColIndex);
|
|
469
487
|
}
|
|
470
488
|
onCellClick(rowIndex, globalColIndex) {
|
|
471
|
-
this.state().interaction.setActiveCell({ rowIndex, columnIndex: globalColIndex });
|
|
489
|
+
this.state().interaction.setActiveCell?.({ rowIndex, columnIndex: globalColIndex });
|
|
472
490
|
}
|
|
473
491
|
onCellContextMenu(event) {
|
|
474
492
|
this.state().contextMenu.handleCellContextMenu(event);
|
|
@@ -477,13 +495,13 @@ export class BaseDataGridTableComponent {
|
|
|
477
495
|
this.state().editing.setEditingCell({ rowId, columnId });
|
|
478
496
|
}
|
|
479
497
|
onFillHandleMouseDown(event) {
|
|
480
|
-
this.state().interaction.handleFillHandleMouseDown(event);
|
|
498
|
+
this.state().interaction.handleFillHandleMouseDown?.(event);
|
|
481
499
|
}
|
|
482
500
|
onResizeStart(event, col) {
|
|
483
501
|
event.preventDefault();
|
|
484
502
|
// Clear cell selection before resize (like React) so selection outlines don't persist during drag
|
|
485
|
-
this.state().interaction.setActiveCell(null);
|
|
486
|
-
this.state().interaction.setSelectionRange(null);
|
|
503
|
+
this.state().interaction.setActiveCell?.(null);
|
|
504
|
+
this.state().interaction.setSelectionRange?.(null);
|
|
487
505
|
this.getWrapperRef()?.nativeElement.focus({ preventScroll: true });
|
|
488
506
|
const startX = event.clientX;
|
|
489
507
|
const startWidth = this.getColumnWidth(col);
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
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
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* Base class for OGrid top-level components (Material, Radix).
|
|
9
|
+
* Contains all shared TypeScript logic. Subclasses provide a @Component
|
|
10
|
+
* decorator with their own selector, template, and imports.
|
|
11
|
+
*/
|
|
12
|
+
import { Input, signal, effect, inject } from '@angular/core';
|
|
13
|
+
import { OGridService } from '../services/ogrid.service';
|
|
14
|
+
export class BaseOGridComponent {
|
|
15
|
+
set props(value) {
|
|
16
|
+
this.propsSignal.set(value);
|
|
17
|
+
}
|
|
18
|
+
constructor() {
|
|
19
|
+
this.propsSignal = signal(undefined);
|
|
20
|
+
this.ogridService = inject(OGridService);
|
|
21
|
+
effect(() => {
|
|
22
|
+
const p = this.propsSignal();
|
|
23
|
+
if (p)
|
|
24
|
+
this.ogridService.configure(p);
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
get showToolbar() {
|
|
28
|
+
return this.ogridService.columnChooserPlacement() === 'toolbar' || this.ogridService.toolbar() != null;
|
|
29
|
+
}
|
|
30
|
+
onPageSizeChange(size) {
|
|
31
|
+
this.ogridService.pagination().setPageSize(size);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
__decorate([
|
|
35
|
+
Input({ required: true })
|
|
36
|
+
], BaseOGridComponent.prototype, "props", null);
|
|
@@ -59,27 +59,26 @@ export class BasePopoverCellEditorComponent {
|
|
|
59
59
|
setTimeout(() => this.showEditor.set(true), 0);
|
|
60
60
|
}
|
|
61
61
|
});
|
|
62
|
-
// Render custom editor component when container is available
|
|
63
|
-
effect(()
|
|
62
|
+
// Render custom editor component when container is available.
|
|
63
|
+
// Angular's effect() ignores return values — use onCleanup() for cleanup.
|
|
64
|
+
effect((onCleanup) => {
|
|
64
65
|
const container = this.editorContainerRef;
|
|
65
66
|
const props = this.editorProps;
|
|
66
67
|
const col = this.column;
|
|
67
68
|
if (!container || !this.showEditor() || typeof col.cellEditor !== 'function')
|
|
68
69
|
return;
|
|
69
|
-
|
|
70
|
-
const EditorComponent = col.cellEditor; // ComponentType
|
|
70
|
+
const EditorComponent = col.cellEditor;
|
|
71
71
|
const componentRef = createComponent(EditorComponent, {
|
|
72
72
|
environmentInjector: this.envInjector,
|
|
73
73
|
elementInjector: this.injector,
|
|
74
74
|
});
|
|
75
75
|
// Pass props to component instance
|
|
76
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
77
76
|
Object.assign(componentRef.instance, props);
|
|
78
77
|
componentRef.changeDetectorRef.detectChanges();
|
|
79
78
|
// Append to DOM
|
|
80
79
|
container.nativeElement.appendChild(componentRef.location.nativeElement);
|
|
81
|
-
// Cleanup
|
|
82
|
-
|
|
80
|
+
// Cleanup when effect re-runs or component is destroyed
|
|
81
|
+
onCleanup(() => componentRef.destroy());
|
|
83
82
|
});
|
|
84
83
|
}
|
|
85
84
|
handleOverlayClick() {
|
|
@@ -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 { Component, Input, Output, EventEmitter, ChangeDetectionStrategy } from '@angular/core';
|
|
8
|
-
import {
|
|
8
|
+
import { NgTemplateOutlet } from '@angular/common';
|
|
9
9
|
let EmptyStateComponent = class EmptyStateComponent {
|
|
10
10
|
constructor() {
|
|
11
11
|
this.message = undefined;
|
|
@@ -31,7 +31,7 @@ EmptyStateComponent = __decorate([
|
|
|
31
31
|
selector: 'ogrid-empty-state',
|
|
32
32
|
standalone: true,
|
|
33
33
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
34
|
-
imports: [
|
|
34
|
+
imports: [NgTemplateOutlet],
|
|
35
35
|
styles: [`
|
|
36
36
|
.ogrid-empty-state-clear-btn {
|
|
37
37
|
background: none; border: none; color: inherit;
|
|
@@ -13,23 +13,23 @@ let GridContextMenuComponent = class GridContextMenuComponent {
|
|
|
13
13
|
this.canUndoProp = false;
|
|
14
14
|
this.canRedoProp = false;
|
|
15
15
|
this.classNames = undefined;
|
|
16
|
-
this.
|
|
17
|
-
this.
|
|
18
|
-
this.
|
|
19
|
-
this.
|
|
16
|
+
this.copyAction = new EventEmitter();
|
|
17
|
+
this.cutAction = new EventEmitter();
|
|
18
|
+
this.pasteAction = new EventEmitter();
|
|
19
|
+
this.selectAllAction = new EventEmitter();
|
|
20
20
|
this.undoAction = new EventEmitter();
|
|
21
21
|
this.redoAction = new EventEmitter();
|
|
22
|
-
this.
|
|
22
|
+
this.closeAction = new EventEmitter();
|
|
23
23
|
this.menuItems = GRID_CONTEXT_MENU_ITEMS;
|
|
24
24
|
this.formatShortcutFn = formatShortcut;
|
|
25
25
|
this.clickOutsideHandler = (e) => {
|
|
26
26
|
const el = this.menuRef?.nativeElement;
|
|
27
27
|
if (el && !el.contains(e.target))
|
|
28
|
-
this.
|
|
28
|
+
this.closeAction.emit();
|
|
29
29
|
};
|
|
30
30
|
this.keyDownHandler = (e) => {
|
|
31
31
|
if (e.key === 'Escape')
|
|
32
|
-
this.
|
|
32
|
+
this.closeAction.emit();
|
|
33
33
|
};
|
|
34
34
|
// Register listeners once on init (no signal dependencies needed)
|
|
35
35
|
document.addEventListener('mousedown', this.clickOutsideHandler, true);
|
|
@@ -51,16 +51,16 @@ let GridContextMenuComponent = class GridContextMenuComponent {
|
|
|
51
51
|
onItemClick(id) {
|
|
52
52
|
switch (id) {
|
|
53
53
|
case 'copy':
|
|
54
|
-
this.
|
|
54
|
+
this.copyAction.emit();
|
|
55
55
|
break;
|
|
56
56
|
case 'cut':
|
|
57
|
-
this.
|
|
57
|
+
this.cutAction.emit();
|
|
58
58
|
break;
|
|
59
59
|
case 'paste':
|
|
60
|
-
this.
|
|
60
|
+
this.pasteAction.emit();
|
|
61
61
|
break;
|
|
62
62
|
case 'selectAll':
|
|
63
|
-
this.
|
|
63
|
+
this.selectAllAction.emit();
|
|
64
64
|
break;
|
|
65
65
|
case 'undo':
|
|
66
66
|
this.undoAction.emit();
|
|
@@ -69,7 +69,7 @@ let GridContextMenuComponent = class GridContextMenuComponent {
|
|
|
69
69
|
this.redoAction.emit();
|
|
70
70
|
break;
|
|
71
71
|
}
|
|
72
|
-
this.
|
|
72
|
+
this.closeAction.emit();
|
|
73
73
|
}
|
|
74
74
|
};
|
|
75
75
|
__decorate([
|
|
@@ -92,16 +92,16 @@ __decorate([
|
|
|
92
92
|
], GridContextMenuComponent.prototype, "classNames", void 0);
|
|
93
93
|
__decorate([
|
|
94
94
|
Output()
|
|
95
|
-
], GridContextMenuComponent.prototype, "
|
|
95
|
+
], GridContextMenuComponent.prototype, "copyAction", void 0);
|
|
96
96
|
__decorate([
|
|
97
97
|
Output()
|
|
98
|
-
], GridContextMenuComponent.prototype, "
|
|
98
|
+
], GridContextMenuComponent.prototype, "cutAction", void 0);
|
|
99
99
|
__decorate([
|
|
100
100
|
Output()
|
|
101
|
-
], GridContextMenuComponent.prototype, "
|
|
101
|
+
], GridContextMenuComponent.prototype, "pasteAction", void 0);
|
|
102
102
|
__decorate([
|
|
103
103
|
Output()
|
|
104
|
-
], GridContextMenuComponent.prototype, "
|
|
104
|
+
], GridContextMenuComponent.prototype, "selectAllAction", void 0);
|
|
105
105
|
__decorate([
|
|
106
106
|
Output()
|
|
107
107
|
], GridContextMenuComponent.prototype, "undoAction", void 0);
|
|
@@ -110,7 +110,7 @@ __decorate([
|
|
|
110
110
|
], GridContextMenuComponent.prototype, "redoAction", void 0);
|
|
111
111
|
__decorate([
|
|
112
112
|
Output()
|
|
113
|
-
], GridContextMenuComponent.prototype, "
|
|
113
|
+
], GridContextMenuComponent.prototype, "closeAction", void 0);
|
|
114
114
|
__decorate([
|
|
115
115
|
ViewChild('menuRef')
|
|
116
116
|
], GridContextMenuComponent.prototype, "menuRef", void 0);
|
|
@@ -7,6 +7,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
7
7
|
import { Component, Input, ViewEncapsulation, ChangeDetectionStrategy } from '@angular/core';
|
|
8
8
|
import { SideBarComponent } from './sidebar.component';
|
|
9
9
|
import { GRID_BORDER_RADIUS } from '@alaarab/ogrid-core';
|
|
10
|
+
import { OGRID_THEME_VARS_CSS } from '../styles/ogrid-theme-vars';
|
|
10
11
|
let OGridLayoutComponent = class OGridLayoutComponent {
|
|
11
12
|
constructor() {
|
|
12
13
|
this.hasToolbar = false;
|
|
@@ -38,55 +39,7 @@ OGridLayoutComponent = __decorate([
|
|
|
38
39
|
encapsulation: ViewEncapsulation.None,
|
|
39
40
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
40
41
|
imports: [SideBarComponent],
|
|
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
|
-
}
|
|
42
|
+
styles: [OGRID_THEME_VARS_CSS, `
|
|
90
43
|
:host { display: block; height: 100%; }
|
|
91
44
|
.ogrid-layout-root { display: flex; flex-direction: column; height: 100%; }
|
|
92
45
|
.ogrid-layout-container {
|
|
@@ -5,16 +5,12 @@ 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 { Component, Input, ChangeDetectionStrategy } from '@angular/core';
|
|
8
|
-
import {
|
|
9
|
-
const PANEL_WIDTH = 240;
|
|
10
|
-
const TAB_WIDTH = 36;
|
|
8
|
+
import { NgTemplateOutlet } from '@angular/common';
|
|
11
9
|
const PANEL_LABELS = { columns: 'Columns', filters: 'Filters' };
|
|
12
10
|
let SideBarComponent = class SideBarComponent {
|
|
13
11
|
constructor() {
|
|
14
12
|
this.sideBarProps = null;
|
|
15
13
|
this.panelLabels = PANEL_LABELS;
|
|
16
|
-
this.tabWidth = TAB_WIDTH;
|
|
17
|
-
this.panelWidth = PANEL_WIDTH;
|
|
18
14
|
}
|
|
19
15
|
onTabClick(panel) {
|
|
20
16
|
const props = this.sideBarProps;
|
|
@@ -101,7 +97,7 @@ SideBarComponent = __decorate([
|
|
|
101
97
|
selector: 'ogrid-sidebar',
|
|
102
98
|
standalone: true,
|
|
103
99
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
104
|
-
imports: [
|
|
100
|
+
imports: [NgTemplateOutlet],
|
|
105
101
|
styles: [`
|
|
106
102
|
.ogrid-sidebar-root { display: flex; flex-direction: row; flex-shrink: 0; }
|
|
107
103
|
.ogrid-sidebar-tab-strip {
|
|
@@ -14,9 +14,10 @@ let StatusBarComponent = class StatusBarComponent {
|
|
|
14
14
|
this.aggregation = undefined;
|
|
15
15
|
this.suppressRowCount = undefined;
|
|
16
16
|
this.classNames = undefined;
|
|
17
|
+
this.cachedParts = [];
|
|
17
18
|
}
|
|
18
|
-
|
|
19
|
-
|
|
19
|
+
ngOnChanges() {
|
|
20
|
+
this.cachedParts = getStatusBarParts({
|
|
20
21
|
totalCount: this.totalCount,
|
|
21
22
|
filteredCount: this.filteredCount,
|
|
22
23
|
selectedCount: this.selectedCount,
|
|
@@ -25,6 +26,9 @@ let StatusBarComponent = class StatusBarComponent {
|
|
|
25
26
|
suppressRowCount: this.suppressRowCount,
|
|
26
27
|
});
|
|
27
28
|
}
|
|
29
|
+
getParts() {
|
|
30
|
+
return this.cachedParts;
|
|
31
|
+
}
|
|
28
32
|
};
|
|
29
33
|
__decorate([
|
|
30
34
|
Input({ required: true })
|
package/dist/esm/index.js
CHANGED
|
@@ -6,6 +6,9 @@ export { toUserLike, isInSelectionRange, normalizeSelectionRange } from './types
|
|
|
6
6
|
// Services
|
|
7
7
|
export { OGridService } from './services/ogrid.service';
|
|
8
8
|
export { DataGridStateService } from './services/datagrid-state.service';
|
|
9
|
+
export { DataGridLayoutHelper } from './services/datagrid-layout.service';
|
|
10
|
+
export { DataGridEditingHelper } from './services/datagrid-editing.service';
|
|
11
|
+
export { DataGridInteractionHelper } from './services/datagrid-interaction.service';
|
|
9
12
|
export { ColumnReorderService } from './services/column-reorder.service';
|
|
10
13
|
export { VirtualScrollService } from './services/virtual-scroll.service';
|
|
11
14
|
// Components
|
|
@@ -15,6 +18,7 @@ export { GridContextMenuComponent } from './components/grid-context-menu.compone
|
|
|
15
18
|
export { SideBarComponent } from './components/sidebar.component';
|
|
16
19
|
export { MarchingAntsOverlayComponent } from './components/marching-ants-overlay.component';
|
|
17
20
|
export { EmptyStateComponent } from './components/empty-state.component';
|
|
21
|
+
export { BaseOGridComponent } from './components/base-ogrid.component';
|
|
18
22
|
export { BaseDataGridTableComponent } from './components/base-datagrid-table.component';
|
|
19
23
|
export { BaseColumnHeaderFilterComponent } from './components/base-column-header-filter.component';
|
|
20
24
|
export { BaseColumnChooserComponent } from './components/base-column-chooser.component';
|
|
@@ -24,4 +28,4 @@ export { INLINE_CELL_EDITOR_TEMPLATE, INLINE_CELL_EDITOR_STYLES } from './compon
|
|
|
24
28
|
export { BasePopoverCellEditorComponent, POPOVER_CELL_EDITOR_TEMPLATE, POPOVER_CELL_EDITOR_OVERLAY_STYLES } from './components/base-popover-cell-editor.component';
|
|
25
29
|
// Shared styles
|
|
26
30
|
export { OGRID_THEME_VARS_CSS } from './styles/ogrid-theme-vars';
|
|
27
|
-
export { getHeaderFilterConfig, getCellRenderDescriptor, resolveCellDisplayContent, resolveCellStyle, createDebouncedSignal, createDebouncedCallback, debounce,
|
|
31
|
+
export { getHeaderFilterConfig, getCellRenderDescriptor, resolveCellDisplayContent, resolveCellStyle, createDebouncedSignal, createDebouncedCallback, debounce, createLatestCallback, } from './utils';
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { signal } from '@angular/core';
|
|
2
|
+
import { parseValue, } from '@alaarab/ogrid-core';
|
|
3
|
+
/**
|
|
4
|
+
* Manages cell editing state, inline/popover editor, and commit/cancel logic.
|
|
5
|
+
* Extracted from DataGridStateService for modularity.
|
|
6
|
+
*
|
|
7
|
+
* Not @Injectable — instantiated and owned by DataGridStateService.
|
|
8
|
+
*/
|
|
9
|
+
export class DataGridEditingHelper {
|
|
10
|
+
constructor(getVisibleCols, getItems, getWrappedOnCellValueChanged, setActiveCellFn) {
|
|
11
|
+
this.editingCellSig = signal(null);
|
|
12
|
+
this.pendingEditorValueSig = signal(undefined);
|
|
13
|
+
this.popoverAnchorElSig = signal(null);
|
|
14
|
+
this.getVisibleCols = getVisibleCols;
|
|
15
|
+
this.getItems = getItems;
|
|
16
|
+
this.getWrappedOnCellValueChanged = getWrappedOnCellValueChanged;
|
|
17
|
+
this.setActiveCellFn = setActiveCellFn;
|
|
18
|
+
}
|
|
19
|
+
setEditingCell(cell) {
|
|
20
|
+
this.editingCellSig.set(cell);
|
|
21
|
+
}
|
|
22
|
+
setPendingEditorValue(value) {
|
|
23
|
+
this.pendingEditorValueSig.set(value);
|
|
24
|
+
}
|
|
25
|
+
commitCellEdit(item, columnId, oldValue, newValue, rowIndex, globalColIndex) {
|
|
26
|
+
const col = this.getVisibleCols().find((c) => c.columnId === columnId);
|
|
27
|
+
if (col) {
|
|
28
|
+
const result = parseValue(newValue, oldValue, item, col);
|
|
29
|
+
if (!result.valid) {
|
|
30
|
+
this.editingCellSig.set(null);
|
|
31
|
+
this.popoverAnchorElSig.set(null);
|
|
32
|
+
this.pendingEditorValueSig.set(undefined);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
newValue = result.value;
|
|
36
|
+
}
|
|
37
|
+
const onCellValueChanged = this.getWrappedOnCellValueChanged();
|
|
38
|
+
onCellValueChanged?.({ item, columnId, oldValue, newValue, rowIndex });
|
|
39
|
+
this.editingCellSig.set(null);
|
|
40
|
+
this.popoverAnchorElSig.set(null);
|
|
41
|
+
this.pendingEditorValueSig.set(undefined);
|
|
42
|
+
const items = this.getItems();
|
|
43
|
+
if (rowIndex < items.length - 1) {
|
|
44
|
+
this.setActiveCellFn({ rowIndex: rowIndex + 1, columnIndex: globalColIndex });
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
cancelPopoverEdit() {
|
|
48
|
+
this.editingCellSig.set(null);
|
|
49
|
+
this.popoverAnchorElSig.set(null);
|
|
50
|
+
this.pendingEditorValueSig.set(undefined);
|
|
51
|
+
}
|
|
52
|
+
}
|