@alaarab/ogrid-angular 2.1.3 → 2.1.5
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/index.js +4606 -30
- package/dist/types/components/base-datagrid-table.component.d.ts +8 -8
- package/package.json +4 -4
- package/dist/esm/components/base-column-chooser.component.js +0 -78
- package/dist/esm/components/base-column-header-filter.component.js +0 -281
- package/dist/esm/components/base-datagrid-table.component.js +0 -648
- package/dist/esm/components/base-inline-cell-editor.component.js +0 -253
- package/dist/esm/components/base-ogrid.component.js +0 -36
- package/dist/esm/components/base-pagination-controls.component.js +0 -72
- package/dist/esm/components/base-popover-cell-editor.component.js +0 -114
- package/dist/esm/components/empty-state.component.js +0 -58
- package/dist/esm/components/grid-context-menu.component.js +0 -153
- package/dist/esm/components/inline-cell-editor-template.js +0 -107
- package/dist/esm/components/marching-ants-overlay.component.js +0 -164
- package/dist/esm/components/ogrid-layout.component.js +0 -188
- package/dist/esm/components/sidebar.component.js +0 -274
- package/dist/esm/components/status-bar.component.js +0 -71
- package/dist/esm/services/column-reorder.service.js +0 -180
- package/dist/esm/services/datagrid-editing.service.js +0 -52
- package/dist/esm/services/datagrid-interaction.service.js +0 -667
- package/dist/esm/services/datagrid-layout.service.js +0 -151
- package/dist/esm/services/datagrid-state.service.js +0 -591
- package/dist/esm/services/ogrid.service.js +0 -746
- package/dist/esm/services/virtual-scroll.service.js +0 -91
- package/dist/esm/styles/ogrid-theme-vars.js +0 -53
- package/dist/esm/types/columnTypes.js +0 -1
- package/dist/esm/types/dataGridTypes.js +0 -1
- package/dist/esm/types/index.js +0 -1
- package/dist/esm/utils/dataGridViewModel.js +0 -6
- package/dist/esm/utils/debounce.js +0 -68
- package/dist/esm/utils/index.js +0 -8
- package/dist/esm/utils/latestRef.js +0 -41
|
@@ -1,274 +0,0 @@
|
|
|
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
|
-
import { Component, Input, ChangeDetectionStrategy } from '@angular/core';
|
|
8
|
-
import { NgTemplateOutlet } from '@angular/common';
|
|
9
|
-
const PANEL_LABELS = { columns: 'Columns', filters: 'Filters' };
|
|
10
|
-
let SideBarComponent = class SideBarComponent {
|
|
11
|
-
constructor() {
|
|
12
|
-
this.sideBarProps = null;
|
|
13
|
-
this.panelLabels = PANEL_LABELS;
|
|
14
|
-
}
|
|
15
|
-
onTabClick(panel) {
|
|
16
|
-
const props = this.sideBarProps;
|
|
17
|
-
if (props)
|
|
18
|
-
props.onPanelChange(props.activePanel === panel ? null : panel);
|
|
19
|
-
}
|
|
20
|
-
allVisible() {
|
|
21
|
-
const props = this.sideBarProps;
|
|
22
|
-
if (!props)
|
|
23
|
-
return false;
|
|
24
|
-
return props.columns.every((c) => props.visibleColumns.has(c.columnId));
|
|
25
|
-
}
|
|
26
|
-
onSelectAll() {
|
|
27
|
-
const props = this.sideBarProps;
|
|
28
|
-
if (!props)
|
|
29
|
-
return;
|
|
30
|
-
const next = new Set(props.visibleColumns);
|
|
31
|
-
props.columns.forEach((c) => next.add(c.columnId));
|
|
32
|
-
props.onSetVisibleColumns(next);
|
|
33
|
-
}
|
|
34
|
-
onClearAll() {
|
|
35
|
-
const props = this.sideBarProps;
|
|
36
|
-
if (!props)
|
|
37
|
-
return;
|
|
38
|
-
const next = new Set();
|
|
39
|
-
props.columns.forEach((c) => {
|
|
40
|
-
if (c.required && props.visibleColumns.has(c.columnId))
|
|
41
|
-
next.add(c.columnId);
|
|
42
|
-
});
|
|
43
|
-
props.onSetVisibleColumns(next);
|
|
44
|
-
}
|
|
45
|
-
onVisibilityChange(columnKey, visible) {
|
|
46
|
-
this.sideBarProps?.onVisibilityChange(columnKey, visible);
|
|
47
|
-
}
|
|
48
|
-
getTextFilterValue(filterField) {
|
|
49
|
-
const filters = this.sideBarProps?.filters;
|
|
50
|
-
const fv = filters?.[filterField];
|
|
51
|
-
return fv?.type === 'text' ? fv.value : '';
|
|
52
|
-
}
|
|
53
|
-
onTextFilterChange(filterField, value) {
|
|
54
|
-
this.sideBarProps?.onFilterChange(filterField, value ? { type: 'text', value } : undefined);
|
|
55
|
-
}
|
|
56
|
-
getDateFrom(filterField) {
|
|
57
|
-
const fv = this.sideBarProps?.filters?.[filterField];
|
|
58
|
-
return fv?.type === 'date' ? (fv.value.from ?? '') : '';
|
|
59
|
-
}
|
|
60
|
-
getDateTo(filterField) {
|
|
61
|
-
const fv = this.sideBarProps?.filters?.[filterField];
|
|
62
|
-
return fv?.type === 'date' ? (fv.value.to ?? '') : '';
|
|
63
|
-
}
|
|
64
|
-
onDateFromChange(filterField, value) {
|
|
65
|
-
const fv = this.sideBarProps?.filters?.[filterField];
|
|
66
|
-
const existing = fv?.type === 'date' ? fv.value : {};
|
|
67
|
-
const from = value || undefined;
|
|
68
|
-
const to = existing.to;
|
|
69
|
-
this.sideBarProps?.onFilterChange(filterField, from || to ? { type: 'date', value: { from, to } } : undefined);
|
|
70
|
-
}
|
|
71
|
-
onDateToChange(filterField, value) {
|
|
72
|
-
const fv = this.sideBarProps?.filters?.[filterField];
|
|
73
|
-
const existing = fv?.type === 'date' ? fv.value : {};
|
|
74
|
-
const to = value || undefined;
|
|
75
|
-
const from = existing.from;
|
|
76
|
-
this.sideBarProps?.onFilterChange(filterField, from || to ? { type: 'date', value: { from, to } } : undefined);
|
|
77
|
-
}
|
|
78
|
-
getFilterOptions(filterField) {
|
|
79
|
-
return this.sideBarProps?.filterOptions?.[filterField] ?? [];
|
|
80
|
-
}
|
|
81
|
-
isMultiSelectChecked(filterField, opt) {
|
|
82
|
-
const fv = this.sideBarProps?.filters?.[filterField];
|
|
83
|
-
return fv?.type === 'multiSelect' ? fv.value.includes(opt) : false;
|
|
84
|
-
}
|
|
85
|
-
onMultiSelectChange(filterField, opt, checked) {
|
|
86
|
-
const fv = this.sideBarProps?.filters?.[filterField];
|
|
87
|
-
const current = fv?.type === 'multiSelect' ? fv.value : [];
|
|
88
|
-
const next = checked ? [...current, opt] : current.filter((v) => v !== opt);
|
|
89
|
-
this.sideBarProps?.onFilterChange(filterField, next.length > 0 ? { type: 'multiSelect', value: next } : undefined);
|
|
90
|
-
}
|
|
91
|
-
};
|
|
92
|
-
__decorate([
|
|
93
|
-
Input()
|
|
94
|
-
], SideBarComponent.prototype, "sideBarProps", void 0);
|
|
95
|
-
SideBarComponent = __decorate([
|
|
96
|
-
Component({
|
|
97
|
-
selector: 'ogrid-sidebar',
|
|
98
|
-
standalone: true,
|
|
99
|
-
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
100
|
-
imports: [NgTemplateOutlet],
|
|
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, rgba(0, 0, 0, 0.04));
|
|
107
|
-
}
|
|
108
|
-
.ogrid-sidebar-tab-strip--left { border-right: 1px solid var(--ogrid-border, rgba(0, 0, 0, 0.12)); }
|
|
109
|
-
.ogrid-sidebar-tab-strip--right { border-left: 1px solid var(--ogrid-border, rgba(0, 0, 0, 0.12)); }
|
|
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, rgba(0, 0, 0, 0.87)); 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, #ffffff); 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, #ffffff); color: var(--ogrid-fg, rgba(0, 0, 0, 0.87));
|
|
123
|
-
}
|
|
124
|
-
.ogrid-sidebar-panel--left { border-right: 1px solid var(--ogrid-border, rgba(0, 0, 0, 0.12)); }
|
|
125
|
-
.ogrid-sidebar-panel--right { border-left: 1px solid var(--ogrid-border, rgba(0, 0, 0, 0.12)); }
|
|
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, rgba(0, 0, 0, 0.12)); font-weight: 600;
|
|
129
|
-
}
|
|
130
|
-
.ogrid-sidebar-panel-close {
|
|
131
|
-
border: none; background: transparent; cursor: pointer;
|
|
132
|
-
font-size: 16px; color: var(--ogrid-fg, rgba(0, 0, 0, 0.87));
|
|
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-header-bg, rgba(0, 0, 0, 0.04)); color: var(--ogrid-fg, rgba(0, 0, 0, 0.87));
|
|
139
|
-
border: 1px solid var(--ogrid-border, rgba(0, 0, 0, 0.12)); 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-fg-muted, rgba(0, 0, 0, 0.5)); 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, #ffffff); color: var(--ogrid-fg, rgba(0, 0, 0, 0.87));
|
|
148
|
-
border: 1px solid var(--ogrid-border, rgba(0, 0, 0, 0.12)); 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, #ffffff); color: var(--ogrid-fg, rgba(0, 0, 0, 0.87));
|
|
155
|
-
border: 1px solid var(--ogrid-border, rgba(0, 0, 0, 0.12)); 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
|
-
`],
|
|
163
|
-
template: `
|
|
164
|
-
<div class="ogrid-sidebar-root" role="complementary" aria-label="Side bar">
|
|
165
|
-
@if (sideBarProps?.position === 'left') {
|
|
166
|
-
<ng-container *ngTemplateOutlet="tabStripTpl"></ng-container>
|
|
167
|
-
<ng-container *ngTemplateOutlet="panelContentTpl"></ng-container>
|
|
168
|
-
}
|
|
169
|
-
@if (sideBarProps?.position === 'right') {
|
|
170
|
-
<ng-container *ngTemplateOutlet="panelContentTpl"></ng-container>
|
|
171
|
-
<ng-container *ngTemplateOutlet="tabStripTpl"></ng-container>
|
|
172
|
-
}
|
|
173
|
-
</div>
|
|
174
|
-
|
|
175
|
-
<ng-template #tabStripTpl>
|
|
176
|
-
<div
|
|
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'"
|
|
180
|
-
role="tablist"
|
|
181
|
-
aria-label="Side bar tabs"
|
|
182
|
-
>
|
|
183
|
-
@for (panel of sideBarProps?.panels ?? []; track panel) {
|
|
184
|
-
<button
|
|
185
|
-
role="tab"
|
|
186
|
-
class="ogrid-sidebar-tab"
|
|
187
|
-
[class.ogrid-sidebar-tab--active]="sideBarProps?.activePanel === panel"
|
|
188
|
-
[attr.aria-selected]="sideBarProps?.activePanel === panel"
|
|
189
|
-
[attr.aria-label]="panelLabels[panel]"
|
|
190
|
-
(click)="onTabClick(panel)"
|
|
191
|
-
[title]="panelLabels[panel]"
|
|
192
|
-
>
|
|
193
|
-
{{ panel === 'columns' ? '\u2261' : '\u2A65' }}
|
|
194
|
-
</button>
|
|
195
|
-
}
|
|
196
|
-
</div>
|
|
197
|
-
</ng-template>
|
|
198
|
-
|
|
199
|
-
<ng-template #panelContentTpl>
|
|
200
|
-
@if (sideBarProps?.activePanel) {
|
|
201
|
-
<div
|
|
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'"
|
|
206
|
-
[attr.aria-label]="panelLabels[sideBarProps!.activePanel!]"
|
|
207
|
-
>
|
|
208
|
-
<div class="ogrid-sidebar-panel-header">
|
|
209
|
-
<span>{{ panelLabels[sideBarProps!.activePanel!] }}</span>
|
|
210
|
-
<button (click)="sideBarProps?.onPanelChange(null)" class="ogrid-sidebar-panel-close" aria-label="Close panel">×</button>
|
|
211
|
-
</div>
|
|
212
|
-
<div class="ogrid-sidebar-panel-body">
|
|
213
|
-
@if (sideBarProps?.activePanel === 'columns') {
|
|
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>
|
|
217
|
-
</div>
|
|
218
|
-
@for (col of sideBarProps?.columns ?? []; track col.columnId) {
|
|
219
|
-
<label class="ogrid-sidebar-col-label">
|
|
220
|
-
<input type="checkbox" [checked]="sideBarProps?.visibleColumns?.has(col.columnId)" (change)="onVisibilityChange(col.columnId, $any($event.target).checked)" [disabled]="col.required" />
|
|
221
|
-
<span>{{ col.name }}</span>
|
|
222
|
-
</label>
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
@if (sideBarProps?.activePanel === 'filters') {
|
|
226
|
-
@if ((sideBarProps?.filterableColumns ?? []).length === 0) {
|
|
227
|
-
<div class="ogrid-sidebar-empty">No filterable columns</div>
|
|
228
|
-
}
|
|
229
|
-
@for (col of sideBarProps?.filterableColumns ?? []; track col.columnId) {
|
|
230
|
-
<div class="ogrid-sidebar-filter-group">
|
|
231
|
-
<div class="ogrid-sidebar-filter-label">{{ col.name }}</div>
|
|
232
|
-
@if (col.filterType === 'text') {
|
|
233
|
-
<input
|
|
234
|
-
type="text"
|
|
235
|
-
class="ogrid-sidebar-text-input"
|
|
236
|
-
[value]="getTextFilterValue(col.filterField)"
|
|
237
|
-
(input)="onTextFilterChange(col.filterField, $any($event.target).value)"
|
|
238
|
-
[placeholder]="'Filter ' + col.name + '...'"
|
|
239
|
-
[attr.aria-label]="'Filter ' + col.name"
|
|
240
|
-
/>
|
|
241
|
-
}
|
|
242
|
-
@if (col.filterType === 'date') {
|
|
243
|
-
<div class="ogrid-sidebar-date-row">
|
|
244
|
-
<label class="ogrid-sidebar-date-label">
|
|
245
|
-
From:
|
|
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'" />
|
|
247
|
-
</label>
|
|
248
|
-
<label class="ogrid-sidebar-date-label">
|
|
249
|
-
To:
|
|
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'" />
|
|
251
|
-
</label>
|
|
252
|
-
</div>
|
|
253
|
-
}
|
|
254
|
-
@if (col.filterType === 'multiSelect') {
|
|
255
|
-
<div class="ogrid-sidebar-multiselect-list" role="group" [attr.aria-label]="col.name + ' options'">
|
|
256
|
-
@for (opt of getFilterOptions(col.filterField); track opt) {
|
|
257
|
-
<label class="ogrid-sidebar-multiselect-item">
|
|
258
|
-
<input type="checkbox" [checked]="isMultiSelectChecked(col.filterField, opt)" (change)="onMultiSelectChange(col.filterField, opt, $any($event.target).checked)" />
|
|
259
|
-
<span>{{ opt }}</span>
|
|
260
|
-
</label>
|
|
261
|
-
}
|
|
262
|
-
</div>
|
|
263
|
-
}
|
|
264
|
-
</div>
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
</div>
|
|
268
|
-
</div>
|
|
269
|
-
}
|
|
270
|
-
</ng-template>
|
|
271
|
-
`,
|
|
272
|
-
})
|
|
273
|
-
], SideBarComponent);
|
|
274
|
-
export { SideBarComponent };
|
|
@@ -1,71 +0,0 @@
|
|
|
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
|
-
import { Component, Input, ChangeDetectionStrategy } from '@angular/core';
|
|
8
|
-
import { getStatusBarParts } from '@alaarab/ogrid-core';
|
|
9
|
-
let StatusBarComponent = class StatusBarComponent {
|
|
10
|
-
constructor() {
|
|
11
|
-
this.filteredCount = undefined;
|
|
12
|
-
this.selectedCount = undefined;
|
|
13
|
-
this.selectedCellCount = undefined;
|
|
14
|
-
this.aggregation = undefined;
|
|
15
|
-
this.suppressRowCount = undefined;
|
|
16
|
-
this.classNames = undefined;
|
|
17
|
-
this.cachedParts = [];
|
|
18
|
-
}
|
|
19
|
-
ngOnChanges() {
|
|
20
|
-
this.cachedParts = getStatusBarParts({
|
|
21
|
-
totalCount: this.totalCount,
|
|
22
|
-
filteredCount: this.filteredCount,
|
|
23
|
-
selectedCount: this.selectedCount,
|
|
24
|
-
selectedCellCount: this.selectedCellCount,
|
|
25
|
-
aggregation: this.aggregation ?? undefined,
|
|
26
|
-
suppressRowCount: this.suppressRowCount,
|
|
27
|
-
});
|
|
28
|
-
}
|
|
29
|
-
getParts() {
|
|
30
|
-
return this.cachedParts;
|
|
31
|
-
}
|
|
32
|
-
};
|
|
33
|
-
__decorate([
|
|
34
|
-
Input({ required: true })
|
|
35
|
-
], StatusBarComponent.prototype, "totalCount", void 0);
|
|
36
|
-
__decorate([
|
|
37
|
-
Input()
|
|
38
|
-
], StatusBarComponent.prototype, "filteredCount", void 0);
|
|
39
|
-
__decorate([
|
|
40
|
-
Input()
|
|
41
|
-
], StatusBarComponent.prototype, "selectedCount", void 0);
|
|
42
|
-
__decorate([
|
|
43
|
-
Input()
|
|
44
|
-
], StatusBarComponent.prototype, "selectedCellCount", void 0);
|
|
45
|
-
__decorate([
|
|
46
|
-
Input()
|
|
47
|
-
], StatusBarComponent.prototype, "aggregation", void 0);
|
|
48
|
-
__decorate([
|
|
49
|
-
Input()
|
|
50
|
-
], StatusBarComponent.prototype, "suppressRowCount", void 0);
|
|
51
|
-
__decorate([
|
|
52
|
-
Input()
|
|
53
|
-
], StatusBarComponent.prototype, "classNames", void 0);
|
|
54
|
-
StatusBarComponent = __decorate([
|
|
55
|
-
Component({
|
|
56
|
-
selector: 'ogrid-status-bar',
|
|
57
|
-
standalone: true,
|
|
58
|
-
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
59
|
-
template: `
|
|
60
|
-
<div [class]="classNames?.statusBar ?? ''" role="status" aria-live="polite">
|
|
61
|
-
@for (part of getParts(); track part.key) {
|
|
62
|
-
<span [class]="classNames?.statusBarItem ?? ''">
|
|
63
|
-
<span [class]="classNames?.statusBarLabel ?? ''">{{ part.label }}</span>
|
|
64
|
-
<span [class]="classNames?.statusBarValue ?? ''">{{ part.value.toLocaleString() }}</span>
|
|
65
|
-
</span>
|
|
66
|
-
}
|
|
67
|
-
</div>
|
|
68
|
-
`,
|
|
69
|
-
})
|
|
70
|
-
], StatusBarComponent);
|
|
71
|
-
export { StatusBarComponent };
|
|
@@ -1,180 +0,0 @@
|
|
|
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
|
-
import { Injectable, signal, DestroyRef, inject } from '@angular/core';
|
|
8
|
-
import { reorderColumnArray } from '@alaarab/ogrid-core';
|
|
9
|
-
/** Width of the resize handle zone on the right edge of each header cell. */
|
|
10
|
-
const RESIZE_HANDLE_ZONE = 8;
|
|
11
|
-
/**
|
|
12
|
-
* Manages column reorder drag interactions with RAF-throttled updates.
|
|
13
|
-
* Angular signals-based port of React's useColumnReorder hook.
|
|
14
|
-
*/
|
|
15
|
-
let ColumnReorderService = class ColumnReorderService {
|
|
16
|
-
constructor() {
|
|
17
|
-
this.destroyRef = inject(DestroyRef);
|
|
18
|
-
// --- Input signals (set by consuming component) ---
|
|
19
|
-
this.columns = signal([]);
|
|
20
|
-
this.columnOrder = signal(undefined);
|
|
21
|
-
this.onColumnOrderChange = signal(undefined);
|
|
22
|
-
this.enabled = signal(true);
|
|
23
|
-
this.wrapperEl = signal(null);
|
|
24
|
-
// --- Internal state ---
|
|
25
|
-
this.isDragging = signal(false);
|
|
26
|
-
this.dropIndicatorX = signal(null);
|
|
27
|
-
// Imperative drag tracking (not reactive)
|
|
28
|
-
this.rafId = 0;
|
|
29
|
-
this.cleanupFn = null;
|
|
30
|
-
// Refs for latest values (captured in closure)
|
|
31
|
-
this.latestDropTargetIndex = null;
|
|
32
|
-
this.destroyRef.onDestroy(() => {
|
|
33
|
-
if (this.cleanupFn) {
|
|
34
|
-
this.cleanupFn();
|
|
35
|
-
this.cleanupFn = null;
|
|
36
|
-
}
|
|
37
|
-
if (this.rafId) {
|
|
38
|
-
cancelAnimationFrame(this.rafId);
|
|
39
|
-
this.rafId = 0;
|
|
40
|
-
}
|
|
41
|
-
});
|
|
42
|
-
}
|
|
43
|
-
/**
|
|
44
|
-
* Call this from the header cell's mousedown handler.
|
|
45
|
-
* @param columnId - The column being dragged
|
|
46
|
-
* @param event - The native MouseEvent
|
|
47
|
-
*/
|
|
48
|
-
handleHeaderMouseDown(columnId, event) {
|
|
49
|
-
if (!this.enabled())
|
|
50
|
-
return;
|
|
51
|
-
if (!this.onColumnOrderChange())
|
|
52
|
-
return;
|
|
53
|
-
// Gate on left-click only
|
|
54
|
-
if (event.button !== 0)
|
|
55
|
-
return;
|
|
56
|
-
// Skip if in resize handle zone (right 8px of the header cell)
|
|
57
|
-
const target = event.currentTarget;
|
|
58
|
-
const rect = target.getBoundingClientRect();
|
|
59
|
-
if (event.clientX > rect.right - RESIZE_HANDLE_ZONE)
|
|
60
|
-
return;
|
|
61
|
-
// Skip column groups — only reorder leaf columns
|
|
62
|
-
const cols = this.columns();
|
|
63
|
-
const colIndex = cols.findIndex((c) => c.columnId === columnId);
|
|
64
|
-
if (colIndex === -1)
|
|
65
|
-
return;
|
|
66
|
-
event.preventDefault();
|
|
67
|
-
const startX = event.clientX;
|
|
68
|
-
let hasMoved = false;
|
|
69
|
-
this.latestDropTargetIndex = null;
|
|
70
|
-
// Lock text selection during drag
|
|
71
|
-
const prevUserSelect = document.body.style.userSelect;
|
|
72
|
-
document.body.style.userSelect = 'none';
|
|
73
|
-
const onMove = (moveEvent) => {
|
|
74
|
-
// Require a small minimum drag distance before activating
|
|
75
|
-
if (!hasMoved && Math.abs(moveEvent.clientX - startX) < 5)
|
|
76
|
-
return;
|
|
77
|
-
if (!hasMoved) {
|
|
78
|
-
hasMoved = true;
|
|
79
|
-
this.isDragging.set(true);
|
|
80
|
-
}
|
|
81
|
-
if (this.rafId)
|
|
82
|
-
cancelAnimationFrame(this.rafId);
|
|
83
|
-
this.rafId = requestAnimationFrame(() => {
|
|
84
|
-
this.rafId = 0;
|
|
85
|
-
const wrapper = this.wrapperEl();
|
|
86
|
-
if (!wrapper)
|
|
87
|
-
return;
|
|
88
|
-
const headerCells = wrapper.querySelectorAll('th[data-column-id]');
|
|
89
|
-
const rects = [];
|
|
90
|
-
for (let i = 0; i < headerCells.length; i++) {
|
|
91
|
-
const th = headerCells[i];
|
|
92
|
-
const id = th.getAttribute('data-column-id');
|
|
93
|
-
if (!id)
|
|
94
|
-
continue;
|
|
95
|
-
const thRect = th.getBoundingClientRect();
|
|
96
|
-
rects.push({
|
|
97
|
-
columnId: id,
|
|
98
|
-
left: thRect.left,
|
|
99
|
-
right: thRect.right,
|
|
100
|
-
centerX: thRect.left + thRect.width / 2,
|
|
101
|
-
});
|
|
102
|
-
}
|
|
103
|
-
const result = this.calculateDrop(columnId, moveEvent.clientX, rects);
|
|
104
|
-
this.latestDropTargetIndex = result.dropIndex;
|
|
105
|
-
this.dropIndicatorX.set(result.indicatorX);
|
|
106
|
-
});
|
|
107
|
-
};
|
|
108
|
-
const cleanup = () => {
|
|
109
|
-
window.removeEventListener('mousemove', onMove, true);
|
|
110
|
-
window.removeEventListener('mouseup', onUp, true);
|
|
111
|
-
this.cleanupFn = null;
|
|
112
|
-
// Restore user-select
|
|
113
|
-
document.body.style.userSelect = prevUserSelect;
|
|
114
|
-
// Cancel pending RAF
|
|
115
|
-
if (this.rafId) {
|
|
116
|
-
cancelAnimationFrame(this.rafId);
|
|
117
|
-
this.rafId = 0;
|
|
118
|
-
}
|
|
119
|
-
};
|
|
120
|
-
const onUp = () => {
|
|
121
|
-
cleanup();
|
|
122
|
-
if (hasMoved && this.latestDropTargetIndex != null) {
|
|
123
|
-
const currentOrder = this.columnOrder() ?? this.columns().map((c) => c.columnId);
|
|
124
|
-
const newOrder = reorderColumnArray(currentOrder, columnId, this.latestDropTargetIndex);
|
|
125
|
-
this.onColumnOrderChange()?.(newOrder);
|
|
126
|
-
}
|
|
127
|
-
this.isDragging.set(false);
|
|
128
|
-
this.dropIndicatorX.set(null);
|
|
129
|
-
};
|
|
130
|
-
window.addEventListener('mousemove', onMove, true);
|
|
131
|
-
window.addEventListener('mouseup', onUp, true);
|
|
132
|
-
this.cleanupFn = cleanup;
|
|
133
|
-
}
|
|
134
|
-
/**
|
|
135
|
-
* Calculate drop target from mouse position and header cell rects.
|
|
136
|
-
* Same logic as React's useColumnReorder inline calculation.
|
|
137
|
-
*/
|
|
138
|
-
calculateDrop(draggedColumnId, mouseX, rects) {
|
|
139
|
-
if (rects.length === 0) {
|
|
140
|
-
return { dropIndex: null, indicatorX: null };
|
|
141
|
-
}
|
|
142
|
-
const order = this.columnOrder() ?? this.columns().map((c) => c.columnId);
|
|
143
|
-
const currentIndex = order.indexOf(draggedColumnId);
|
|
144
|
-
// Find which column the mouse is closest to
|
|
145
|
-
let bestIndex = 0;
|
|
146
|
-
let indicatorX = null;
|
|
147
|
-
if (mouseX <= rects[0].centerX) {
|
|
148
|
-
// Before the first column
|
|
149
|
-
bestIndex = 0;
|
|
150
|
-
indicatorX = rects[0].left;
|
|
151
|
-
}
|
|
152
|
-
else if (mouseX >= rects[rects.length - 1].centerX) {
|
|
153
|
-
// After the last column
|
|
154
|
-
bestIndex = rects.length;
|
|
155
|
-
indicatorX = rects[rects.length - 1].right;
|
|
156
|
-
}
|
|
157
|
-
else {
|
|
158
|
-
for (let i = 0; i < rects.length - 1; i++) {
|
|
159
|
-
if (mouseX >= rects[i].centerX && mouseX < rects[i + 1].centerX) {
|
|
160
|
-
bestIndex = i + 1;
|
|
161
|
-
indicatorX = rects[i].right;
|
|
162
|
-
break;
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
// Map visual index back to order array index
|
|
167
|
-
const targetOrderIndex = bestIndex < rects.length
|
|
168
|
-
? order.indexOf(rects[bestIndex]?.columnId ?? '')
|
|
169
|
-
: order.length;
|
|
170
|
-
// Check if this is a no-op (dropping at same position)
|
|
171
|
-
if (currentIndex === targetOrderIndex || currentIndex + 1 === targetOrderIndex) {
|
|
172
|
-
return { dropIndex: targetOrderIndex, indicatorX: null };
|
|
173
|
-
}
|
|
174
|
-
return { dropIndex: targetOrderIndex, indicatorX };
|
|
175
|
-
}
|
|
176
|
-
};
|
|
177
|
-
ColumnReorderService = __decorate([
|
|
178
|
-
Injectable()
|
|
179
|
-
], ColumnReorderService);
|
|
180
|
-
export { ColumnReorderService };
|
|
@@ -1,52 +0,0 @@
|
|
|
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
|
-
}
|