@alaarab/ogrid-angular-material 2.0.2 → 2.0.3
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.
|
@@ -5,115 +5,8 @@ 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, computed, effect, ChangeDetectionStrategy, viewChild, } from '@angular/core';
|
|
8
|
-
import { DataGridStateService,
|
|
8
|
+
import { DataGridStateService, ColumnReorderService, VirtualScrollService, buildHeaderRows, CHECKBOX_COLUMN_WIDTH, DEFAULT_MIN_COLUMN_WIDTH, getHeaderFilterConfig, getCellRenderDescriptor, resolveCellDisplayContent, resolveCellStyle, } from '@alaarab/ogrid-angular';
|
|
9
9
|
import { ColumnHeaderFilterComponent } from '../column-header-filter/column-header-filter.component';
|
|
10
|
-
function getHeaderFilterConfig(col, input) {
|
|
11
|
-
const filterable = col.filterable && typeof col.filterable === 'object' ? col.filterable : null;
|
|
12
|
-
const filterType = (filterable?.type ?? 'none');
|
|
13
|
-
const filterField = filterable?.filterField ?? col.columnId;
|
|
14
|
-
const sortable = col.sortable !== false;
|
|
15
|
-
const filterValue = input.filters[filterField];
|
|
16
|
-
const base = {
|
|
17
|
-
columnKey: col.columnId,
|
|
18
|
-
columnName: col.name,
|
|
19
|
-
filterType,
|
|
20
|
-
isSorted: input.sortBy === col.columnId,
|
|
21
|
-
isSortedDescending: input.sortBy === col.columnId && input.sortDirection === 'desc',
|
|
22
|
-
onSort: sortable ? () => input.onColumnSort(col.columnId) : undefined,
|
|
23
|
-
};
|
|
24
|
-
if (filterType === 'text') {
|
|
25
|
-
return {
|
|
26
|
-
...base,
|
|
27
|
-
textValue: filterValue?.type === 'text' ? filterValue.value : '',
|
|
28
|
-
onTextChange: (v) => input.onFilterChange(filterField, v.trim() ? { type: 'text', value: v } : undefined),
|
|
29
|
-
};
|
|
30
|
-
}
|
|
31
|
-
if (filterType === 'people') {
|
|
32
|
-
return {
|
|
33
|
-
...base,
|
|
34
|
-
selectedUser: filterValue?.type === 'people' ? filterValue.value : undefined,
|
|
35
|
-
onUserChange: (u) => input.onFilterChange(filterField, u ? { type: 'people', value: u } : undefined),
|
|
36
|
-
peopleSearch: input.peopleSearch,
|
|
37
|
-
};
|
|
38
|
-
}
|
|
39
|
-
if (filterType === 'multiSelect') {
|
|
40
|
-
return {
|
|
41
|
-
...base,
|
|
42
|
-
options: input.filterOptions[filterField] ?? [],
|
|
43
|
-
isLoadingOptions: input.loadingFilterOptions[filterField] ?? false,
|
|
44
|
-
selectedValues: filterValue?.type === 'multiSelect' ? filterValue.value : [],
|
|
45
|
-
onFilterChange: (values) => input.onFilterChange(filterField, values.length ? { type: 'multiSelect', value: values } : undefined),
|
|
46
|
-
};
|
|
47
|
-
}
|
|
48
|
-
if (filterType === 'date') {
|
|
49
|
-
return {
|
|
50
|
-
...base,
|
|
51
|
-
dateValue: filterValue?.type === 'date' ? filterValue.value : undefined,
|
|
52
|
-
onDateChange: (v) => input.onFilterChange(filterField, v ? { type: 'date', value: v } : undefined),
|
|
53
|
-
};
|
|
54
|
-
}
|
|
55
|
-
return base;
|
|
56
|
-
}
|
|
57
|
-
function getCellRenderDescriptor(item, col, rowIndex, colIdx, input) {
|
|
58
|
-
const rowId = input.getRowId(item);
|
|
59
|
-
const globalColIndex = colIdx + input.colOffset;
|
|
60
|
-
const colEditable = col.editable === true || (typeof col.editable === 'function' && col.editable(item));
|
|
61
|
-
const canEditInline = input.editable !== false && !!colEditable && !!input.onCellValueChanged && typeof col.cellEditor !== 'function';
|
|
62
|
-
const canEditPopup = input.editable !== false && !!colEditable && !!input.onCellValueChanged && typeof col.cellEditor === 'function';
|
|
63
|
-
const canEditAny = canEditInline || canEditPopup;
|
|
64
|
-
const isEditing = input.editingCell?.rowId === rowId && input.editingCell?.columnId === col.columnId;
|
|
65
|
-
const isActive = input.activeCell?.rowIndex === rowIndex && input.activeCell?.columnIndex === globalColIndex;
|
|
66
|
-
const inRange = input.selectionRange != null && isInSelectionRange(input.selectionRange, rowIndex, colIdx);
|
|
67
|
-
const isInCutRange = input.cutRange != null && isInSelectionRange(input.cutRange, rowIndex, colIdx);
|
|
68
|
-
const isSelectionEndCell = !input.isDragging && input.copyRange == null && input.cutRange == null &&
|
|
69
|
-
input.selectionRange != null && rowIndex === input.selectionRange.endRow && colIdx === input.selectionRange.endCol;
|
|
70
|
-
let mode = 'display';
|
|
71
|
-
let editorType;
|
|
72
|
-
const value = getCellValue(item, col);
|
|
73
|
-
if (isEditing && canEditInline) {
|
|
74
|
-
mode = 'editing-inline';
|
|
75
|
-
if (col.cellEditor === 'text' || col.cellEditor === 'select' || col.cellEditor === 'checkbox' || col.cellEditor === 'richSelect' || col.cellEditor === 'date') {
|
|
76
|
-
editorType = col.cellEditor;
|
|
77
|
-
}
|
|
78
|
-
else if (col.type === 'date')
|
|
79
|
-
editorType = 'date';
|
|
80
|
-
else if (col.type === 'boolean')
|
|
81
|
-
editorType = 'checkbox';
|
|
82
|
-
else
|
|
83
|
-
editorType = 'text';
|
|
84
|
-
}
|
|
85
|
-
else if (isEditing && canEditPopup) {
|
|
86
|
-
mode = 'editing-popover';
|
|
87
|
-
}
|
|
88
|
-
return {
|
|
89
|
-
mode, editorType, value,
|
|
90
|
-
isActive, isInRange: inRange, isInCutRange, isSelectionEndCell,
|
|
91
|
-
canEditAny, globalColIndex, rowId, rowIndex, displayValue: value,
|
|
92
|
-
};
|
|
93
|
-
}
|
|
94
|
-
function resolveCellDisplayContent(col, item, displayValue) {
|
|
95
|
-
if (col.renderCell && typeof col.renderCell === 'function') {
|
|
96
|
-
const result = col.renderCell(item);
|
|
97
|
-
return result != null ? String(result) : '';
|
|
98
|
-
}
|
|
99
|
-
if (col.valueFormatter)
|
|
100
|
-
return String(col.valueFormatter(displayValue, item) ?? '');
|
|
101
|
-
if (displayValue == null)
|
|
102
|
-
return '';
|
|
103
|
-
if (col.type === 'date') {
|
|
104
|
-
const d = new Date(String(displayValue));
|
|
105
|
-
if (!Number.isNaN(d.getTime()))
|
|
106
|
-
return d.toLocaleDateString();
|
|
107
|
-
}
|
|
108
|
-
if (col.type === 'boolean')
|
|
109
|
-
return displayValue ? 'True' : 'False';
|
|
110
|
-
return String(displayValue);
|
|
111
|
-
}
|
|
112
|
-
function resolveCellStyle(col, item) {
|
|
113
|
-
if (!col.cellStyle)
|
|
114
|
-
return undefined;
|
|
115
|
-
return typeof col.cellStyle === 'function' ? col.cellStyle(item) : col.cellStyle;
|
|
116
|
-
}
|
|
117
10
|
/**
|
|
118
11
|
* DataGridTable component using native HTML table with Material Design-inspired styling.
|
|
119
12
|
* Standalone component — this is the workhorse of the grid.
|
|
@@ -124,6 +17,8 @@ let DataGridTableComponent = class DataGridTableComponent {
|
|
|
124
17
|
this.wrapperRef = viewChild('wrapperEl');
|
|
125
18
|
this.tableContainerRef = viewChild('tableContainerEl');
|
|
126
19
|
this.stateService = new DataGridStateService();
|
|
20
|
+
this.columnReorderService = new ColumnReorderService();
|
|
21
|
+
this.virtualScrollService = new VirtualScrollService();
|
|
127
22
|
this.lastMouseShift = false;
|
|
128
23
|
// --- Delegated state ---
|
|
129
24
|
this.state = computed(() => this.stateService.getState());
|
|
@@ -211,8 +106,28 @@ let DataGridTableComponent = class DataGridTableComponent {
|
|
|
211
106
|
});
|
|
212
107
|
effect(() => {
|
|
213
108
|
const el = this.wrapperRef()?.nativeElement;
|
|
214
|
-
if (el)
|
|
109
|
+
if (el) {
|
|
215
110
|
this.stateService.wrapperEl.set(el);
|
|
111
|
+
this.columnReorderService.wrapperEl.set(el);
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
// Wire column reorder service inputs
|
|
115
|
+
effect(() => {
|
|
116
|
+
const p = this.propsInput();
|
|
117
|
+
if (p) {
|
|
118
|
+
const cols = this.visibleCols();
|
|
119
|
+
this.columnReorderService.columns.set(cols);
|
|
120
|
+
this.columnReorderService.columnOrder.set(p.columnOrder);
|
|
121
|
+
this.columnReorderService.onColumnOrderChange.set(p.onColumnOrderChange);
|
|
122
|
+
this.columnReorderService.enabled.set(!!p.onColumnOrderChange);
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
// Wire virtual scroll service inputs
|
|
126
|
+
effect(() => {
|
|
127
|
+
const p = this.propsInput();
|
|
128
|
+
if (p) {
|
|
129
|
+
this.virtualScrollService.totalRows.set(p.items.length);
|
|
130
|
+
}
|
|
216
131
|
});
|
|
217
132
|
}
|
|
218
133
|
// --- Helper methods ---
|
|
@@ -351,6 +266,9 @@ let DataGridTableComponent = class DataGridTableComponent {
|
|
|
351
266
|
onRedo() {
|
|
352
267
|
this.state().interaction.onRedo?.();
|
|
353
268
|
}
|
|
269
|
+
onHeaderMouseDown(columnId, event) {
|
|
270
|
+
this.columnReorderService.handleHeaderMouseDown(columnId, event);
|
|
271
|
+
}
|
|
354
272
|
};
|
|
355
273
|
DataGridTableComponent = __decorate([
|
|
356
274
|
Component({
|
|
@@ -416,9 +334,12 @@ DataGridTableComponent = __decorate([
|
|
|
416
334
|
[class.ogrid-datagrid-th--pinned-left]="col.pinned === 'left' || (isFreezeCol && colIdx === 0)"
|
|
417
335
|
[class.ogrid-datagrid-th--pinned-right]="col.pinned === 'right'"
|
|
418
336
|
[attr.rowSpan]="headerRows().length > 1 ? headerRows().length - rowIdx : null"
|
|
337
|
+
[attr.data-column-id]="col.columnId"
|
|
419
338
|
[style.minWidth.px]="col.minWidth ?? 80"
|
|
420
339
|
[style.width.px]="colW"
|
|
421
340
|
[style.maxWidth.px]="colW"
|
|
341
|
+
[style.cursor]="columnReorderService.isDragging() ? 'grabbing' : 'grab'"
|
|
342
|
+
(mousedown)="onHeaderMouseDown(col.columnId, $event)"
|
|
422
343
|
>
|
|
423
344
|
<ogrid-column-header-filter
|
|
424
345
|
[columnKey]="col.columnId"
|
|
@@ -586,6 +507,10 @@ DataGridTableComponent = __decorate([
|
|
|
586
507
|
</div>
|
|
587
508
|
</div>
|
|
588
509
|
|
|
510
|
+
@if (columnReorderService.isDragging() && columnReorderService.dropIndicatorX() !== null) {
|
|
511
|
+
<div class="ogrid-datagrid-drop-indicator" [style.left.px]="columnReorderService.dropIndicatorX()"></div>
|
|
512
|
+
}
|
|
513
|
+
|
|
589
514
|
@if (menuPosition()) {
|
|
590
515
|
<div
|
|
591
516
|
class="ogrid-datagrid-context-menu-overlay"
|
|
@@ -744,6 +669,11 @@ DataGridTableComponent = __decorate([
|
|
|
744
669
|
border-radius: 50%; animation: ogrid-spin 0.8s linear infinite;
|
|
745
670
|
}
|
|
746
671
|
@keyframes ogrid-spin { to { transform: rotate(360deg); } }
|
|
672
|
+
.ogrid-datagrid-drop-indicator {
|
|
673
|
+
position: absolute; top: 0; bottom: 0; width: 3px;
|
|
674
|
+
background: var(--ogrid-primary, #217346);
|
|
675
|
+
pointer-events: none; z-index: 100; transition: left 0.05s;
|
|
676
|
+
}
|
|
747
677
|
.ogrid-datagrid-context-menu-overlay {
|
|
748
678
|
position: fixed; inset: 0; z-index: 1000;
|
|
749
679
|
}
|
|
@@ -1,38 +1,5 @@
|
|
|
1
|
-
import
|
|
2
|
-
type
|
|
3
|
-
interface CellRenderDescriptor {
|
|
4
|
-
mode: CellRenderMode;
|
|
5
|
-
editorType?: 'text' | 'select' | 'checkbox' | 'richSelect' | 'date';
|
|
6
|
-
value?: unknown;
|
|
7
|
-
isActive: boolean;
|
|
8
|
-
isInRange: boolean;
|
|
9
|
-
isInCutRange: boolean;
|
|
10
|
-
isSelectionEndCell: boolean;
|
|
11
|
-
canEditAny: boolean;
|
|
12
|
-
globalColIndex: number;
|
|
13
|
-
rowId: RowId;
|
|
14
|
-
rowIndex: number;
|
|
15
|
-
displayValue?: unknown;
|
|
16
|
-
}
|
|
17
|
-
interface HeaderFilterConfig {
|
|
18
|
-
columnKey: string;
|
|
19
|
-
columnName: string;
|
|
20
|
-
filterType: ColumnFilterType;
|
|
21
|
-
isSorted: boolean;
|
|
22
|
-
isSortedDescending: boolean;
|
|
23
|
-
onSort: (() => void) | undefined;
|
|
24
|
-
selectedValues?: string[];
|
|
25
|
-
onFilterChange?: (values: string[]) => void;
|
|
26
|
-
options?: string[];
|
|
27
|
-
isLoadingOptions?: boolean;
|
|
28
|
-
textValue?: string;
|
|
29
|
-
onTextChange?: (value: string) => void;
|
|
30
|
-
selectedUser?: UserLike;
|
|
31
|
-
onUserChange?: (user: UserLike | undefined) => void;
|
|
32
|
-
peopleSearch?: (query: string) => Promise<UserLike[]>;
|
|
33
|
-
dateValue?: IDateFilterValue;
|
|
34
|
-
onDateChange?: (value: IDateFilterValue | undefined) => void;
|
|
35
|
-
}
|
|
1
|
+
import { ColumnReorderService, VirtualScrollService } from '@alaarab/ogrid-angular';
|
|
2
|
+
import type { IOGridDataGridProps, IColumnDef, RowId, CellRenderDescriptor, HeaderFilterConfig } from '@alaarab/ogrid-angular';
|
|
36
3
|
/**
|
|
37
4
|
* DataGridTable component using native HTML table with Material Design-inspired styling.
|
|
38
5
|
* Standalone component — this is the workhorse of the grid.
|
|
@@ -42,6 +9,8 @@ export declare class DataGridTableComponent<T> {
|
|
|
42
9
|
private readonly wrapperRef;
|
|
43
10
|
private readonly tableContainerRef;
|
|
44
11
|
private readonly stateService;
|
|
12
|
+
readonly columnReorderService: ColumnReorderService<T>;
|
|
13
|
+
readonly virtualScrollService: VirtualScrollService;
|
|
45
14
|
private lastMouseShift;
|
|
46
15
|
constructor();
|
|
47
16
|
private readonly state;
|
|
@@ -95,11 +64,11 @@ export declare class DataGridTableComponent<T> {
|
|
|
95
64
|
sortBy?: string;
|
|
96
65
|
sortDirection: "asc" | "desc";
|
|
97
66
|
onColumnSort: (columnKey: string) => void;
|
|
98
|
-
filters: IFilters;
|
|
99
|
-
onFilterChange: (key: string, value: FilterValue | undefined) => void;
|
|
67
|
+
filters: import("@alaarab/ogrid-angular").IFilters;
|
|
68
|
+
onFilterChange: (key: string, value: import("@alaarab/ogrid-angular").FilterValue | undefined) => void;
|
|
100
69
|
filterOptions: Record<string, string[]>;
|
|
101
70
|
loadingFilterOptions: Record<string, boolean>;
|
|
102
|
-
peopleSearch?: (query: string) => Promise<UserLike[]>;
|
|
71
|
+
peopleSearch?: (query: string) => Promise<import("@alaarab/ogrid-angular").UserLike[]>;
|
|
103
72
|
}>;
|
|
104
73
|
readonly cellDescriptorInput: import("@angular/core").Signal<{
|
|
105
74
|
editingCell: {
|
|
@@ -157,5 +126,5 @@ export declare class DataGridTableComponent<T> {
|
|
|
157
126
|
handleSelectAllCells(): void;
|
|
158
127
|
onUndo(): void;
|
|
159
128
|
onRedo(): void;
|
|
129
|
+
onHeaderMouseDown(columnId: string, event: MouseEvent): void;
|
|
160
130
|
}
|
|
161
|
-
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alaarab/ogrid-angular-material",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.3",
|
|
4
4
|
"description": "OGrid Angular Material – MatTable-based data grid with sorting, filtering, pagination, column chooser, and CSV export.",
|
|
5
5
|
"main": "dist/esm/index.js",
|
|
6
6
|
"module": "dist/esm/index.js",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"files": ["dist", "README.md", "LICENSE"],
|
|
23
23
|
"engines": { "node": ">=18" },
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@alaarab/ogrid-angular": "2.0.
|
|
25
|
+
"@alaarab/ogrid-angular": "2.0.3"
|
|
26
26
|
},
|
|
27
27
|
"peerDependencies": {
|
|
28
28
|
"@angular/core": "^21.0.0",
|