@alaarab/ogrid-angular-material 2.0.8 → 2.0.11
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/column-chooser/column-chooser.component.js +5 -34
- package/dist/esm/column-header-filter/column-header-filter.component.js +67 -213
- package/dist/esm/column-header-menu/column-header-menu.component.js +91 -58
- package/dist/esm/datagrid-table/datagrid-table.component.js +74 -30
- package/dist/esm/datagrid-table/inline-cell-editor.component.js +48 -20
- package/dist/esm/datagrid-table/popover-cell-editor.component.js +38 -18
- package/dist/esm/index.js +2 -0
- package/dist/esm/ogrid/ogrid.component.js +25 -17
- package/dist/esm/pagination-controls/pagination-controls.component.js +14 -31
- package/dist/types/column-chooser/column-chooser.component.d.ts +2 -20
- package/dist/types/column-header-filter/column-header-filter.component.d.ts +6 -68
- package/dist/types/column-header-menu/column-header-menu.component.d.ts +13 -13
- package/dist/types/datagrid-table/datagrid-table.component.d.ts +16 -3
- package/dist/types/datagrid-table/inline-cell-editor.component.d.ts +13 -10
- package/dist/types/datagrid-table/popover-cell-editor.component.d.ts +9 -9
- package/dist/types/index.d.ts +0 -2
- package/dist/types/ogrid/ogrid.component.d.ts +7 -4
- package/dist/types/pagination-controls/pagination-controls.component.d.ts +2 -10
- package/package.json +2 -2
|
@@ -4,43 +4,14 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
4
4
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
5
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
6
|
};
|
|
7
|
-
import { Component,
|
|
7
|
+
import { Component, ChangeDetectionStrategy } from '@angular/core';
|
|
8
|
+
import { BaseColumnChooserComponent } from '@alaarab/ogrid-angular';
|
|
8
9
|
/**
|
|
9
10
|
* Column visibility chooser dropdown using Angular Material styling.
|
|
10
11
|
* Standalone component with inline template.
|
|
11
12
|
*/
|
|
12
|
-
let ColumnChooserComponent = class ColumnChooserComponent {
|
|
13
|
-
constructor() {
|
|
14
|
-
this.columns = input.required();
|
|
15
|
-
this.visibleColumns = input.required();
|
|
16
|
-
this.visibilityChange = output();
|
|
17
|
-
this.isOpen = signal(false);
|
|
18
|
-
this.visibleCount = computed(() => this.visibleColumns().size);
|
|
19
|
-
this.totalCount = computed(() => this.columns().length);
|
|
20
|
-
}
|
|
21
|
-
toggle() {
|
|
22
|
-
this.isOpen.update((v) => !v);
|
|
23
|
-
}
|
|
24
|
-
onCheckboxChange(columnKey, event) {
|
|
25
|
-
const checked = event.target.checked;
|
|
26
|
-
this.visibilityChange.emit({ columnKey, visible: checked });
|
|
27
|
-
}
|
|
28
|
-
selectAll() {
|
|
29
|
-
for (const col of this.columns()) {
|
|
30
|
-
if (!this.visibleColumns().has(col.columnId)) {
|
|
31
|
-
this.visibilityChange.emit({ columnKey: col.columnId, visible: true });
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
clearAll() {
|
|
36
|
-
for (const col of this.columns()) {
|
|
37
|
-
if (this.visibleColumns().has(col.columnId)) {
|
|
38
|
-
this.visibilityChange.emit({ columnKey: col.columnId, visible: false });
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
}
|
|
13
|
+
let ColumnChooserComponent = class ColumnChooserComponent extends BaseColumnChooserComponent {
|
|
42
14
|
onDocumentClick(event) {
|
|
43
|
-
// Close dropdown when clicking outside
|
|
44
15
|
const el = event.target;
|
|
45
16
|
if (!el.closest('ogrid-column-chooser')) {
|
|
46
17
|
this.isOpen.set(false);
|
|
@@ -71,11 +42,11 @@ ColumnChooserComponent = __decorate([
|
|
|
71
42
|
</div>
|
|
72
43
|
|
|
73
44
|
<div class="ogrid-column-chooser__list">
|
|
74
|
-
@for (col of columns
|
|
45
|
+
@for (col of columns; track col.columnId) {
|
|
75
46
|
<label class="ogrid-column-chooser__item">
|
|
76
47
|
<input
|
|
77
48
|
type="checkbox"
|
|
78
|
-
[checked]="visibleColumns
|
|
49
|
+
[checked]="visibleColumns.has(col.columnId)"
|
|
79
50
|
(change)="onCheckboxChange(col.columnId, $event)"
|
|
80
51
|
/>
|
|
81
52
|
<span>{{ col.name }}</span>
|
|
@@ -4,195 +4,23 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
4
4
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
5
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
6
|
};
|
|
7
|
-
import { Component,
|
|
7
|
+
import { Component, ChangeDetectionStrategy, ViewChild } from '@angular/core';
|
|
8
|
+
import { BaseColumnHeaderFilterComponent } from '@alaarab/ogrid-angular';
|
|
8
9
|
/**
|
|
9
10
|
* Column header filter component with sort + filter icon + popover.
|
|
10
11
|
* Standalone component with inline template.
|
|
11
12
|
*/
|
|
12
|
-
let ColumnHeaderFilterComponent = class ColumnHeaderFilterComponent {
|
|
13
|
-
|
|
14
|
-
this.
|
|
15
|
-
this.columnName = input.required();
|
|
16
|
-
this.filterType = input.required();
|
|
17
|
-
this.isSorted = input(false);
|
|
18
|
-
this.isSortedDescending = input(false);
|
|
19
|
-
this.onSort = input(undefined);
|
|
20
|
-
this.selectedValues = input(undefined);
|
|
21
|
-
this.onFilterChange = input(undefined);
|
|
22
|
-
this.options = input(undefined);
|
|
23
|
-
this.isLoadingOptions = input(false);
|
|
24
|
-
this.textValue = input('');
|
|
25
|
-
this.onTextChange = input(undefined);
|
|
26
|
-
this.selectedUser = input(undefined);
|
|
27
|
-
this.onUserChange = input(undefined);
|
|
28
|
-
this.peopleSearch = input(undefined);
|
|
29
|
-
this.dateValue = input(undefined);
|
|
30
|
-
this.onDateChange = input(undefined);
|
|
31
|
-
this.headerEl = viewChild('headerEl');
|
|
32
|
-
// Internal state
|
|
33
|
-
this.isFilterOpen = signal(false);
|
|
34
|
-
this.tempTextValue = signal('');
|
|
35
|
-
this.searchText = signal('');
|
|
36
|
-
this.tempSelected = signal(new Set());
|
|
37
|
-
this.peopleSearchText = signal('');
|
|
38
|
-
this.peopleSuggestions = signal([]);
|
|
39
|
-
this.isPeopleLoading = signal(false);
|
|
40
|
-
this.tempDateFrom = signal('');
|
|
41
|
-
this.tempDateTo = signal('');
|
|
42
|
-
// Popover position
|
|
43
|
-
this.popoverTop = signal(0);
|
|
44
|
-
this.popoverLeft = signal(0);
|
|
45
|
-
this.peopleDebounceTimer = null;
|
|
46
|
-
this.hasActiveFilter = computed(() => {
|
|
47
|
-
const ft = this.filterType();
|
|
48
|
-
if (ft === 'text')
|
|
49
|
-
return !!this.textValue();
|
|
50
|
-
if (ft === 'multiSelect')
|
|
51
|
-
return (this.selectedValues()?.length ?? 0) > 0;
|
|
52
|
-
if (ft === 'people')
|
|
53
|
-
return this.selectedUser() != null;
|
|
54
|
-
if (ft === 'date')
|
|
55
|
-
return this.dateValue() != null;
|
|
56
|
-
return false;
|
|
57
|
-
});
|
|
58
|
-
this.filteredOptions = computed(() => {
|
|
59
|
-
const opts = this.options() ?? [];
|
|
60
|
-
const search = this.searchText().toLowerCase().trim();
|
|
61
|
-
if (!search)
|
|
62
|
-
return opts;
|
|
63
|
-
return opts.filter((o) => o.toLowerCase().includes(search));
|
|
64
|
-
});
|
|
13
|
+
let ColumnHeaderFilterComponent = class ColumnHeaderFilterComponent extends BaseColumnHeaderFilterComponent {
|
|
14
|
+
getHeaderEl() {
|
|
15
|
+
return this.headerEl;
|
|
65
16
|
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
}
|
|
69
|
-
toggleFilter(event) {
|
|
70
|
-
event.stopPropagation();
|
|
71
|
-
if (this.isFilterOpen()) {
|
|
72
|
-
this.isFilterOpen.set(false);
|
|
73
|
-
return;
|
|
74
|
-
}
|
|
75
|
-
// Initialize temp state from current values
|
|
76
|
-
this.tempTextValue.set(this.textValue());
|
|
77
|
-
this.tempSelected.set(new Set(this.selectedValues() ?? []));
|
|
78
|
-
this.searchText.set('');
|
|
79
|
-
this.peopleSearchText.set('');
|
|
80
|
-
this.peopleSuggestions.set([]);
|
|
81
|
-
const dv = this.dateValue();
|
|
82
|
-
this.tempDateFrom.set(dv?.from ?? '');
|
|
83
|
-
this.tempDateTo.set(dv?.to ?? '');
|
|
84
|
-
// Compute popover position
|
|
85
|
-
const el = this.headerEl()?.nativeElement;
|
|
86
|
-
if (el) {
|
|
87
|
-
const rect = el.getBoundingClientRect();
|
|
88
|
-
this.popoverTop.set(rect.bottom + 4);
|
|
89
|
-
this.popoverLeft.set(rect.left);
|
|
90
|
-
}
|
|
91
|
-
this.isFilterOpen.set(true);
|
|
92
|
-
}
|
|
93
|
-
// --- Text filter ---
|
|
94
|
-
onTextKeydown(event) {
|
|
95
|
-
event.stopPropagation();
|
|
96
|
-
if (event.key === 'Enter') {
|
|
97
|
-
event.preventDefault();
|
|
98
|
-
this.handleTextApply();
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
handleTextApply() {
|
|
102
|
-
this.onTextChange()(this.tempTextValue());
|
|
103
|
-
this.isFilterOpen.set(false);
|
|
104
|
-
}
|
|
105
|
-
handleTextClear() {
|
|
106
|
-
this.tempTextValue.set('');
|
|
107
|
-
this.onTextChange()('');
|
|
108
|
-
this.isFilterOpen.set(false);
|
|
109
|
-
}
|
|
110
|
-
// --- MultiSelect filter ---
|
|
111
|
-
handleCheckboxChange(option, event) {
|
|
112
|
-
const checked = event.target.checked;
|
|
113
|
-
this.tempSelected.update((s) => {
|
|
114
|
-
const next = new Set(s);
|
|
115
|
-
if (checked)
|
|
116
|
-
next.add(option);
|
|
117
|
-
else
|
|
118
|
-
next.delete(option);
|
|
119
|
-
return next;
|
|
120
|
-
});
|
|
121
|
-
}
|
|
122
|
-
handleSelectAllFiltered() {
|
|
123
|
-
this.tempSelected.update((s) => {
|
|
124
|
-
const next = new Set(s);
|
|
125
|
-
for (const opt of this.filteredOptions())
|
|
126
|
-
next.add(opt);
|
|
127
|
-
return next;
|
|
128
|
-
});
|
|
129
|
-
}
|
|
130
|
-
handleClearSelection() {
|
|
131
|
-
this.tempSelected.set(new Set());
|
|
132
|
-
}
|
|
133
|
-
handleApplyMultiSelect() {
|
|
134
|
-
this.onFilterChange()(Array.from(this.tempSelected()));
|
|
135
|
-
this.isFilterOpen.set(false);
|
|
136
|
-
}
|
|
137
|
-
// --- People filter ---
|
|
138
|
-
onPeopleSearchInput(event) {
|
|
139
|
-
const value = event.target.value;
|
|
140
|
-
this.peopleSearchText.set(value);
|
|
141
|
-
if (this.peopleDebounceTimer)
|
|
142
|
-
clearTimeout(this.peopleDebounceTimer);
|
|
143
|
-
const query = value.trim();
|
|
144
|
-
if (!query) {
|
|
145
|
-
this.peopleSuggestions.set([]);
|
|
146
|
-
this.isPeopleLoading.set(false);
|
|
147
|
-
return;
|
|
148
|
-
}
|
|
149
|
-
this.isPeopleLoading.set(true);
|
|
150
|
-
this.peopleDebounceTimer = setTimeout(() => {
|
|
151
|
-
const fn = this.peopleSearch();
|
|
152
|
-
if (!fn)
|
|
153
|
-
return;
|
|
154
|
-
fn(query).then((results) => {
|
|
155
|
-
this.peopleSuggestions.set(results);
|
|
156
|
-
this.isPeopleLoading.set(false);
|
|
157
|
-
}).catch(() => {
|
|
158
|
-
this.peopleSuggestions.set([]);
|
|
159
|
-
this.isPeopleLoading.set(false);
|
|
160
|
-
});
|
|
161
|
-
}, 300);
|
|
162
|
-
}
|
|
163
|
-
handleUserSelect(user) {
|
|
164
|
-
this.onUserChange()(user);
|
|
165
|
-
this.isFilterOpen.set(false);
|
|
166
|
-
}
|
|
167
|
-
handleClearUser() {
|
|
168
|
-
this.onUserChange()(undefined);
|
|
169
|
-
this.isFilterOpen.set(false);
|
|
170
|
-
}
|
|
171
|
-
// --- Date filter ---
|
|
172
|
-
handleDateApply() {
|
|
173
|
-
const from = this.tempDateFrom();
|
|
174
|
-
const to = this.tempDateTo();
|
|
175
|
-
if (!from && !to) {
|
|
176
|
-
this.onDateChange()(undefined);
|
|
177
|
-
}
|
|
178
|
-
else {
|
|
179
|
-
this.onDateChange()({ from: from || undefined, to: to || undefined });
|
|
180
|
-
}
|
|
181
|
-
this.isFilterOpen.set(false);
|
|
182
|
-
}
|
|
183
|
-
handleDateClear() {
|
|
184
|
-
this.tempDateFrom.set('');
|
|
185
|
-
this.tempDateTo.set('');
|
|
186
|
-
this.onDateChange()(undefined);
|
|
187
|
-
this.isFilterOpen.set(false);
|
|
188
|
-
}
|
|
189
|
-
onDocumentClick(event) {
|
|
190
|
-
const el = event.target;
|
|
191
|
-
if (!el.closest('ogrid-column-header-filter') && !el.closest('.ogrid-header-filter__popover')) {
|
|
192
|
-
this.isFilterOpen.set(false);
|
|
193
|
-
}
|
|
17
|
+
onDocumentClickWrapper(event) {
|
|
18
|
+
this.onDocumentClick(event, 'ogrid-column-header-filter');
|
|
194
19
|
}
|
|
195
20
|
};
|
|
21
|
+
__decorate([
|
|
22
|
+
ViewChild('headerEl')
|
|
23
|
+
], ColumnHeaderFilterComponent.prototype, "headerEl", void 0);
|
|
196
24
|
ColumnHeaderFilterComponent = __decorate([
|
|
197
25
|
Component({
|
|
198
26
|
selector: 'ogrid-column-header-filter',
|
|
@@ -201,23 +29,23 @@ ColumnHeaderFilterComponent = __decorate([
|
|
|
201
29
|
template: `
|
|
202
30
|
<div class="ogrid-header-filter" #headerEl>
|
|
203
31
|
<div class="ogrid-header-filter__label">
|
|
204
|
-
<span class="ogrid-header-filter__name" [title]="columnName
|
|
205
|
-
{{ columnName
|
|
32
|
+
<span class="ogrid-header-filter__name" [title]="columnName" data-header-label>
|
|
33
|
+
{{ columnName }}
|
|
206
34
|
</span>
|
|
207
35
|
</div>
|
|
208
36
|
|
|
209
37
|
<div class="ogrid-header-filter__actions">
|
|
210
|
-
@if (onSort
|
|
38
|
+
@if (onSort) {
|
|
211
39
|
<button
|
|
212
40
|
class="ogrid-header-filter__btn"
|
|
213
|
-
[class.ogrid-header-filter__btn--active]="isSorted
|
|
214
|
-
(click)="onSort
|
|
215
|
-
[attr.aria-label]="'Sort by ' + columnName
|
|
216
|
-
[title]="isSorted
|
|
41
|
+
[class.ogrid-header-filter__btn--active]="isSorted"
|
|
42
|
+
(click)="onSort!()"
|
|
43
|
+
[attr.aria-label]="'Sort by ' + columnName"
|
|
44
|
+
[title]="isSorted ? (isSortedDescending ? 'Sorted descending' : 'Sorted ascending') : 'Sort'"
|
|
217
45
|
>
|
|
218
|
-
@if (isSorted
|
|
46
|
+
@if (isSorted && isSortedDescending) {
|
|
219
47
|
▼
|
|
220
|
-
} @else if (isSorted
|
|
48
|
+
} @else if (isSorted) {
|
|
221
49
|
▲
|
|
222
50
|
} @else {
|
|
223
51
|
↕
|
|
@@ -225,15 +53,15 @@ ColumnHeaderFilterComponent = __decorate([
|
|
|
225
53
|
</button>
|
|
226
54
|
}
|
|
227
55
|
|
|
228
|
-
@if (filterType
|
|
56
|
+
@if (filterType !== 'none') {
|
|
229
57
|
<button
|
|
230
|
-
class="ogrid-header-
|
|
231
|
-
[class.ogrid-header-
|
|
58
|
+
class="ogrid-header-filter__filter-btn"
|
|
59
|
+
[class.ogrid-header-filter__filter-btn--active]="hasActiveFilter() || isFilterOpen()"
|
|
232
60
|
(click)="toggleFilter($event)"
|
|
233
|
-
[attr.aria-label]="'Filter ' + columnName
|
|
234
|
-
[title]="'Filter ' + columnName
|
|
61
|
+
[attr.aria-label]="'Filter ' + columnName"
|
|
62
|
+
[title]="'Filter ' + columnName"
|
|
235
63
|
>
|
|
236
|
-
|
|
64
|
+
<span class="ogrid-header-filter__funnel"></span>
|
|
237
65
|
@if (hasActiveFilter()) {
|
|
238
66
|
<span class="ogrid-header-filter__dot"></span>
|
|
239
67
|
}
|
|
@@ -242,7 +70,7 @@ ColumnHeaderFilterComponent = __decorate([
|
|
|
242
70
|
</div>
|
|
243
71
|
</div>
|
|
244
72
|
|
|
245
|
-
@if (isFilterOpen() && filterType
|
|
73
|
+
@if (isFilterOpen() && filterType !== 'none') {
|
|
246
74
|
<div
|
|
247
75
|
class="ogrid-header-filter__popover"
|
|
248
76
|
[style.top.px]="popoverTop()"
|
|
@@ -250,10 +78,10 @@ ColumnHeaderFilterComponent = __decorate([
|
|
|
250
78
|
(click)="$event.stopPropagation()"
|
|
251
79
|
>
|
|
252
80
|
<div class="ogrid-header-filter__popover-header">
|
|
253
|
-
Filter: {{ columnName
|
|
81
|
+
Filter: {{ columnName }}
|
|
254
82
|
</div>
|
|
255
83
|
|
|
256
|
-
@switch (filterType
|
|
84
|
+
@switch (filterType) {
|
|
257
85
|
@case ('text') {
|
|
258
86
|
<div class="ogrid-header-filter__popover-body" style="width: 260px;">
|
|
259
87
|
<div style="padding: 12px;">
|
|
@@ -286,7 +114,7 @@ ColumnHeaderFilterComponent = __decorate([
|
|
|
286
114
|
autocomplete="off"
|
|
287
115
|
/>
|
|
288
116
|
<div class="ogrid-header-filter__options-info">
|
|
289
|
-
{{ filteredOptions().length }} of {{ (options
|
|
117
|
+
{{ filteredOptions().length }} of {{ (options ?? []).length }} options
|
|
290
118
|
</div>
|
|
291
119
|
</div>
|
|
292
120
|
<div class="ogrid-header-filter__select-actions">
|
|
@@ -296,7 +124,7 @@ ColumnHeaderFilterComponent = __decorate([
|
|
|
296
124
|
<button class="ogrid-header-filter__action-btn" (click)="handleClearSelection()">Clear</button>
|
|
297
125
|
</div>
|
|
298
126
|
<div class="ogrid-header-filter__options-list">
|
|
299
|
-
@if (isLoadingOptions
|
|
127
|
+
@if (isLoadingOptions) {
|
|
300
128
|
<div class="ogrid-header-filter__loading">Loading...</div>
|
|
301
129
|
} @else if (filteredOptions().length === 0) {
|
|
302
130
|
<div class="ogrid-header-filter__empty">No options found</div>
|
|
@@ -321,14 +149,14 @@ ColumnHeaderFilterComponent = __decorate([
|
|
|
321
149
|
}
|
|
322
150
|
@case ('people') {
|
|
323
151
|
<div class="ogrid-header-filter__popover-body" style="width: 300px;">
|
|
324
|
-
@if (selectedUser
|
|
152
|
+
@if (selectedUser) {
|
|
325
153
|
<div class="ogrid-header-filter__people-selected">
|
|
326
154
|
<div class="ogrid-header-filter__people-info-label">Currently filtered by:</div>
|
|
327
155
|
<div class="ogrid-header-filter__people-card">
|
|
328
|
-
<div class="ogrid-header-filter__people-avatar">{{ selectedUser
|
|
156
|
+
<div class="ogrid-header-filter__people-avatar">{{ selectedUser!.displayName?.[0] ?? '?' }}</div>
|
|
329
157
|
<div class="ogrid-header-filter__people-details">
|
|
330
|
-
<div>{{ selectedUser
|
|
331
|
-
<div class="ogrid-header-filter__people-email">{{ selectedUser
|
|
158
|
+
<div>{{ selectedUser!.displayName }}</div>
|
|
159
|
+
<div class="ogrid-header-filter__people-email">{{ selectedUser!.email }}</div>
|
|
332
160
|
</div>
|
|
333
161
|
<button class="ogrid-header-filter__btn" (click)="handleClearUser()" aria-label="Remove filter">×</button>
|
|
334
162
|
</div>
|
|
@@ -364,7 +192,7 @@ ColumnHeaderFilterComponent = __decorate([
|
|
|
364
192
|
<div class="ogrid-header-filter__empty">Type to search...</div>
|
|
365
193
|
}
|
|
366
194
|
</div>
|
|
367
|
-
@if (selectedUser
|
|
195
|
+
@if (selectedUser) {
|
|
368
196
|
<div style="padding: 8px 12px; border-top: 1px solid rgba(0,0,0,0.12);">
|
|
369
197
|
<button class="ogrid-header-filter__action-btn" style="width: 100%;" (click)="handleClearUser()">Clear Filter</button>
|
|
370
198
|
</div>
|
|
@@ -392,7 +220,7 @@ ColumnHeaderFilterComponent = __decorate([
|
|
|
392
220
|
}
|
|
393
221
|
`,
|
|
394
222
|
styles: [`
|
|
395
|
-
:host { display: flex; align-items: center;
|
|
223
|
+
:host { display: flex; align-items: center; flex: 1; min-width: 0; position: relative; }
|
|
396
224
|
.ogrid-header-filter { display: flex; align-items: center; width: 100%; min-width: 0; }
|
|
397
225
|
.ogrid-header-filter__label { flex: 1; min-width: 0; overflow: hidden; }
|
|
398
226
|
.ogrid-header-filter__name {
|
|
@@ -406,17 +234,43 @@ ColumnHeaderFilterComponent = __decorate([
|
|
|
406
234
|
display: inline-flex; align-items: center; justify-content: center; position: relative;
|
|
407
235
|
color: rgba(0,0,0,0.54);
|
|
408
236
|
}
|
|
409
|
-
.ogrid-header-filter__btn:hover { background: rgba(0,0,0,0.
|
|
237
|
+
.ogrid-header-filter__btn:hover { background: rgba(0,0,0,0.08); }
|
|
410
238
|
.ogrid-header-filter__btn--active { color: var(--mat-sys-primary, #1976d2); }
|
|
239
|
+
.ogrid-header-filter__filter-btn {
|
|
240
|
+
width: 24px; height: 24px; padding: 2px; border: none; border-radius: 4px;
|
|
241
|
+
background: transparent; cursor: pointer; line-height: 1;
|
|
242
|
+
display: inline-flex; align-items: center; justify-content: center; position: relative;
|
|
243
|
+
opacity: 0.6; transition: opacity 0.15s;
|
|
244
|
+
}
|
|
245
|
+
.ogrid-header-filter:hover .ogrid-header-filter__filter-btn { opacity: 0.8; }
|
|
246
|
+
.ogrid-header-filter__filter-btn:hover { background: rgba(0,0,0,0.08); opacity: 1 !important; }
|
|
247
|
+
.ogrid-header-filter__filter-btn--active { opacity: 1 !important; }
|
|
248
|
+
.ogrid-header-filter__funnel {
|
|
249
|
+
display: block; width: 0; height: 0;
|
|
250
|
+
border-left: 5px solid transparent; border-right: 5px solid transparent;
|
|
251
|
+
border-top: 6px solid rgba(0,0,0,0.65);
|
|
252
|
+
position: relative;
|
|
253
|
+
}
|
|
254
|
+
.ogrid-header-filter__funnel::after {
|
|
255
|
+
content: ''; display: block; width: 2px; height: 4px;
|
|
256
|
+
background: rgba(0,0,0,0.65); position: absolute;
|
|
257
|
+
top: -1px; left: -1px;
|
|
258
|
+
}
|
|
259
|
+
.ogrid-header-filter__filter-btn--active .ogrid-header-filter__funnel {
|
|
260
|
+
border-top-color: var(--mat-sys-primary, #1976d2);
|
|
261
|
+
}
|
|
262
|
+
.ogrid-header-filter__filter-btn--active .ogrid-header-filter__funnel::after {
|
|
263
|
+
background: var(--mat-sys-primary, #1976d2);
|
|
264
|
+
}
|
|
411
265
|
.ogrid-header-filter__dot {
|
|
412
266
|
position: absolute; top: 2px; right: 2px;
|
|
413
267
|
width: 6px; height: 6px; border-radius: 50%;
|
|
414
268
|
background: var(--mat-sys-primary, #1976d2);
|
|
415
269
|
}
|
|
416
270
|
.ogrid-header-filter__popover {
|
|
417
|
-
position: fixed; z-index:
|
|
418
|
-
background: #
|
|
419
|
-
border-radius:
|
|
271
|
+
position: fixed; z-index: 10000;
|
|
272
|
+
background: #ffffff; border: 1px solid rgba(0,0,0,0.2);
|
|
273
|
+
border-radius: 8px; box-shadow: 0 4px 16px rgba(0,0,0,0.2), 0 1px 4px rgba(0,0,0,0.1);
|
|
420
274
|
margin-top: 4px;
|
|
421
275
|
}
|
|
422
276
|
.ogrid-header-filter__popover-header {
|
|
@@ -476,7 +330,7 @@ ColumnHeaderFilterComponent = __decorate([
|
|
|
476
330
|
.ogrid-header-filter__people-option:hover { background: rgba(0,0,0,0.04); }
|
|
477
331
|
`],
|
|
478
332
|
host: {
|
|
479
|
-
'(document:click)': '
|
|
333
|
+
'(document:click)': 'onDocumentClickWrapper($event)',
|
|
480
334
|
},
|
|
481
335
|
})
|
|
482
336
|
], ColumnHeaderFilterComponent);
|
|
@@ -4,96 +4,129 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
4
4
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
5
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
6
|
};
|
|
7
|
-
import { Component,
|
|
7
|
+
import { Component, ChangeDetectionStrategy, ViewChild, computed, Input } from '@angular/core';
|
|
8
8
|
import { MatMenuModule, MatMenuTrigger } from '@angular/material/menu';
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import { COLUMN_HEADER_MENU_ITEMS } from '@alaarab/ogrid-core';
|
|
9
|
+
import { MatDividerModule } from '@angular/material/divider';
|
|
10
|
+
import { getColumnHeaderMenuItems } from '@alaarab/ogrid-core';
|
|
12
11
|
/**
|
|
13
|
-
* Column header dropdown menu for pin/unpin actions.
|
|
12
|
+
* Column header dropdown menu for pin/unpin, sort, and autosize actions.
|
|
14
13
|
* Uses Angular Material MatMenu.
|
|
15
14
|
*/
|
|
16
15
|
let ColumnHeaderMenuComponent = class ColumnHeaderMenuComponent {
|
|
17
16
|
constructor() {
|
|
18
|
-
this.
|
|
19
|
-
this.
|
|
20
|
-
this.
|
|
21
|
-
this.
|
|
22
|
-
this.
|
|
23
|
-
this.
|
|
24
|
-
this.
|
|
25
|
-
this.
|
|
26
|
-
|
|
17
|
+
this.canPinLeft = true;
|
|
18
|
+
this.canPinRight = true;
|
|
19
|
+
this.canUnpin = false;
|
|
20
|
+
this.currentSort = null;
|
|
21
|
+
this.isSortable = true;
|
|
22
|
+
this.isResizable = true;
|
|
23
|
+
this.handlers = {};
|
|
24
|
+
this.menuItems = computed(() => getColumnHeaderMenuItems({
|
|
25
|
+
canPinLeft: this.canPinLeft,
|
|
26
|
+
canPinRight: this.canPinRight,
|
|
27
|
+
canUnpin: this.canUnpin,
|
|
28
|
+
currentSort: this.currentSort,
|
|
29
|
+
isSortable: this.isSortable,
|
|
30
|
+
isResizable: this.isResizable,
|
|
31
|
+
}));
|
|
27
32
|
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
33
|
+
handleMenuItemClick(itemId) {
|
|
34
|
+
const h = this.handlers;
|
|
35
|
+
const actionMap = {
|
|
36
|
+
pinLeft: h.onPinLeft,
|
|
37
|
+
pinRight: h.onPinRight,
|
|
38
|
+
unpin: h.onUnpin,
|
|
39
|
+
sortAsc: h.onSortAsc,
|
|
40
|
+
sortDesc: h.onSortDesc,
|
|
41
|
+
clearSort: h.onClearSort,
|
|
42
|
+
autosizeThis: h.onAutosizeThis,
|
|
43
|
+
autosizeAll: h.onAutosizeAll,
|
|
44
|
+
};
|
|
45
|
+
const action = actionMap[itemId];
|
|
46
|
+
if (action) {
|
|
47
|
+
action();
|
|
48
|
+
h.onClose?.();
|
|
41
49
|
}
|
|
42
50
|
}
|
|
43
51
|
};
|
|
52
|
+
__decorate([
|
|
53
|
+
Input({ required: true })
|
|
54
|
+
], ColumnHeaderMenuComponent.prototype, "columnId", void 0);
|
|
55
|
+
__decorate([
|
|
56
|
+
Input()
|
|
57
|
+
], ColumnHeaderMenuComponent.prototype, "canPinLeft", void 0);
|
|
58
|
+
__decorate([
|
|
59
|
+
Input()
|
|
60
|
+
], ColumnHeaderMenuComponent.prototype, "canPinRight", void 0);
|
|
61
|
+
__decorate([
|
|
62
|
+
Input()
|
|
63
|
+
], ColumnHeaderMenuComponent.prototype, "canUnpin", void 0);
|
|
64
|
+
__decorate([
|
|
65
|
+
Input()
|
|
66
|
+
], ColumnHeaderMenuComponent.prototype, "currentSort", void 0);
|
|
67
|
+
__decorate([
|
|
68
|
+
Input()
|
|
69
|
+
], ColumnHeaderMenuComponent.prototype, "isSortable", void 0);
|
|
70
|
+
__decorate([
|
|
71
|
+
Input()
|
|
72
|
+
], ColumnHeaderMenuComponent.prototype, "isResizable", void 0);
|
|
73
|
+
__decorate([
|
|
74
|
+
Input()
|
|
75
|
+
], ColumnHeaderMenuComponent.prototype, "handlers", void 0);
|
|
76
|
+
__decorate([
|
|
77
|
+
ViewChild(MatMenuTrigger)
|
|
78
|
+
], ColumnHeaderMenuComponent.prototype, "menuTrigger", void 0);
|
|
44
79
|
ColumnHeaderMenuComponent = __decorate([
|
|
45
80
|
Component({
|
|
46
81
|
selector: 'column-header-menu',
|
|
47
82
|
standalone: true,
|
|
48
|
-
imports: [MatMenuModule, MatButtonModule, MatIconModule],
|
|
49
83
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
84
|
+
imports: [MatMenuModule, MatDividerModule],
|
|
50
85
|
template: `
|
|
51
86
|
<button
|
|
52
|
-
mat-icon-button
|
|
53
87
|
[matMenuTriggerFor]="menu"
|
|
54
88
|
class="column-header-menu-trigger"
|
|
55
|
-
[attr.aria-label]="'Column options for ' + columnId
|
|
89
|
+
[attr.aria-label]="'Column options for ' + columnId"
|
|
56
90
|
>
|
|
57
|
-
|
|
91
|
+
⋮
|
|
58
92
|
</button>
|
|
59
93
|
|
|
60
94
|
<mat-menu #menu="matMenu">
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
{{ menuItems[1].label }}
|
|
74
|
-
</button>
|
|
75
|
-
<button
|
|
76
|
-
mat-menu-item
|
|
77
|
-
[disabled]="!canUnpin()"
|
|
78
|
-
(click)="handleUnpin()"
|
|
79
|
-
>
|
|
80
|
-
{{ menuItems[2].label }}
|
|
81
|
-
</button>
|
|
95
|
+
@for (item of menuItems(); track item.id) {
|
|
96
|
+
<button
|
|
97
|
+
mat-menu-item
|
|
98
|
+
[disabled]="item.disabled"
|
|
99
|
+
(click)="handleMenuItemClick(item.id)"
|
|
100
|
+
>
|
|
101
|
+
{{ item.label }}
|
|
102
|
+
</button>
|
|
103
|
+
@if (item.divider) {
|
|
104
|
+
<mat-divider></mat-divider>
|
|
105
|
+
}
|
|
106
|
+
}
|
|
82
107
|
</mat-menu>
|
|
83
108
|
`,
|
|
84
109
|
styles: [`
|
|
110
|
+
:host { flex-shrink: 0; }
|
|
85
111
|
.column-header-menu-trigger {
|
|
86
|
-
opacity: 0;
|
|
87
|
-
transition: opacity 0.15s;
|
|
88
112
|
width: 24px;
|
|
89
113
|
height: 24px;
|
|
90
|
-
line-height: 24px;
|
|
91
114
|
padding: 0;
|
|
115
|
+
border: none;
|
|
116
|
+
border-radius: 4px;
|
|
117
|
+
background: transparent;
|
|
118
|
+
cursor: pointer;
|
|
119
|
+
font-size: 16px;
|
|
120
|
+
line-height: 1;
|
|
121
|
+
display: inline-flex;
|
|
122
|
+
align-items: center;
|
|
123
|
+
justify-content: center;
|
|
124
|
+
color: rgba(0,0,0,0.54);
|
|
92
125
|
}
|
|
93
126
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
127
|
+
.column-header-menu-trigger:hover {
|
|
128
|
+
background: rgba(0,0,0,0.08);
|
|
129
|
+
color: rgba(0,0,0,0.87);
|
|
97
130
|
}
|
|
98
131
|
`],
|
|
99
132
|
})
|