@alaarab/ogrid-angular-primeng 2.0.2

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.
@@ -0,0 +1,94 @@
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, output, signal, computed } from '@angular/core';
8
+ import { CommonModule } from '@angular/common';
9
+ let ColumnChooserComponent = class ColumnChooserComponent {
10
+ constructor() {
11
+ this.columns = input.required();
12
+ this.visibleColumns = input.required();
13
+ this.visibilityChange = output();
14
+ this.open = signal(false);
15
+ this.visibleCount = computed(() => this.visibleColumns().size);
16
+ this.totalCount = computed(() => this.columns().length);
17
+ }
18
+ onToggle(columnKey, checked) {
19
+ this.visibilityChange.emit({ columnKey, visible: checked });
20
+ }
21
+ onSelectAll() {
22
+ for (const col of this.columns()) {
23
+ if (!this.visibleColumns().has(col.columnId)) {
24
+ this.visibilityChange.emit({ columnKey: col.columnId, visible: true });
25
+ }
26
+ }
27
+ }
28
+ onClearAll() {
29
+ for (const col of this.columns()) {
30
+ if (col.required !== true && this.visibleColumns().has(col.columnId)) {
31
+ this.visibilityChange.emit({ columnKey: col.columnId, visible: false });
32
+ }
33
+ }
34
+ }
35
+ };
36
+ ColumnChooserComponent = __decorate([
37
+ Component({
38
+ selector: 'ogrid-primeng-column-chooser',
39
+ standalone: true,
40
+ imports: [CommonModule],
41
+ template: `
42
+ <div style="position:relative;display:inline-block">
43
+ <button
44
+ type="button"
45
+ class="p-button p-button-text p-button-sm"
46
+ (click)="open.set(!open())"
47
+ [attr.aria-expanded]="open()"
48
+ aria-haspopup="listbox"
49
+ style="display:flex;align-items:center;gap:6px;font-size:13px"
50
+ >
51
+ <span aria-hidden>&#9881;</span>
52
+ <span>Column Visibility ({{ visibleCount() }} of {{ totalCount() }})</span>
53
+ <span aria-hidden>{{ open() ? '\u25B2' : '\u25BC' }}</span>
54
+ </button>
55
+
56
+ @if (open()) {
57
+ <div
58
+ style="position:absolute;right:0;top:100%;z-index:100;min-width:220px;max-height:320px;overflow-y:auto;background:var(--ogrid-bg, #fff);border:1px solid var(--ogrid-border, #e0e0e0);border-radius:6px;box-shadow:0 2px 8px rgba(0,0,0,0.12);padding:8px 0"
59
+ >
60
+ <div style="padding:4px 12px;font-weight:600;font-size:12px;color:var(--ogrid-muted, #666)">
61
+ Select Columns ({{ visibleCount() }} of {{ totalCount() }})
62
+ </div>
63
+ @for (col of columns(); track col.columnId) {
64
+ <label style="display:flex;align-items:center;gap:8px;padding:4px 12px;cursor:pointer;font-size:13px">
65
+ <input
66
+ type="checkbox"
67
+ [checked]="visibleColumns().has(col.columnId)"
68
+ (change)="onToggle(col.columnId, $any($event.target).checked)"
69
+ [disabled]="col.required === true"
70
+ />
71
+ {{ col.name }}
72
+ </label>
73
+ }
74
+ <div style="display:flex;gap:4px;padding:8px 12px;border-top:1px solid var(--ogrid-border, #e0e0e0);margin-top:4px">
75
+ <button
76
+ type="button"
77
+ class="p-button p-button-text p-button-sm"
78
+ (click)="onClearAll()"
79
+ style="flex:1;font-size:12px"
80
+ >Clear All</button>
81
+ <button
82
+ type="button"
83
+ class="p-button p-button-text p-button-sm"
84
+ (click)="onSelectAll()"
85
+ style="flex:1;font-size:12px"
86
+ >Select All</button>
87
+ </div>
88
+ </div>
89
+ }
90
+ </div>
91
+ `,
92
+ })
93
+ ], ColumnChooserComponent);
94
+ export { ColumnChooserComponent };
@@ -0,0 +1,383 @@
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, signal, computed, viewChild, DestroyRef, inject, effect } from '@angular/core';
8
+ import { CommonModule } from '@angular/common';
9
+ let ColumnHeaderFilterComponent = class ColumnHeaderFilterComponent {
10
+ constructor() {
11
+ this.destroyRef = inject(DestroyRef);
12
+ this.columnKey = input.required();
13
+ this.columnName = input.required();
14
+ this.filterType = input('none');
15
+ this.isSorted = input(false);
16
+ this.isSortedDescending = input(false);
17
+ this.onSort = input(undefined);
18
+ // Multi-select filter
19
+ this.selectedValues = input(undefined);
20
+ this.onFilterChange = input(undefined);
21
+ this.options = input([]);
22
+ this.isLoadingOptions = input(false);
23
+ // Text filter
24
+ this.textValue = input('');
25
+ this.onTextChange = input(undefined);
26
+ // People filter
27
+ this.selectedUser = input(undefined);
28
+ this.onUserChange = input(undefined);
29
+ this.peopleSearch = input(undefined);
30
+ // Date filter
31
+ this.dateValue = input(undefined);
32
+ this.onDateChange = input(undefined);
33
+ // Internal state
34
+ this.isFilterOpen = signal(false);
35
+ this.tempTextValue = signal('');
36
+ this.searchText = signal('');
37
+ this.tempSelected = signal(new Set());
38
+ this.peopleSearchText = signal('');
39
+ this.peopleSuggestions = signal([]);
40
+ this.isPeopleLoading = signal(false);
41
+ this.tempDateFrom = signal('');
42
+ this.tempDateTo = signal('');
43
+ this.filterTrigger = viewChild('filterTrigger');
44
+ this.filterPanel = viewChild('filterPanel');
45
+ this.peopleDebounceTimer = null;
46
+ this.clickOutsideHandler = (e) => {
47
+ const trigger = this.filterTrigger()?.nativeElement;
48
+ const panel = this.filterPanel()?.nativeElement;
49
+ if (trigger && !trigger.contains(e.target) && (!panel || !panel.contains(e.target))) {
50
+ this.isFilterOpen.set(false);
51
+ }
52
+ };
53
+ this.hasActiveFilter = computed(() => {
54
+ const ft = this.filterType();
55
+ if (ft === 'text')
56
+ return !!this.textValue();
57
+ if (ft === 'multiSelect') {
58
+ const sv = this.selectedValues();
59
+ return sv != null && sv.length > 0;
60
+ }
61
+ if (ft === 'people')
62
+ return this.selectedUser() != null;
63
+ if (ft === 'date') {
64
+ const dv = this.dateValue();
65
+ return dv != null && (dv.from != null || dv.to != null);
66
+ }
67
+ return false;
68
+ });
69
+ this.filteredOptions = computed(() => {
70
+ const search = this.searchText().toLowerCase();
71
+ const opts = this.options() ?? [];
72
+ return search ? opts.filter((o) => o.toLowerCase().includes(search)) : opts;
73
+ });
74
+ // Sync temp values when filter opens
75
+ effect(() => {
76
+ if (this.isFilterOpen()) {
77
+ this.tempTextValue.set(this.textValue() ?? '');
78
+ this.searchText.set('');
79
+ const sv = this.selectedValues();
80
+ this.tempSelected.set(new Set(sv ?? []));
81
+ const dv = this.dateValue();
82
+ this.tempDateFrom.set(dv?.from ?? '');
83
+ this.tempDateTo.set(dv?.to ?? '');
84
+ document.addEventListener('mousedown', this.clickOutsideHandler, true);
85
+ }
86
+ else {
87
+ document.removeEventListener('mousedown', this.clickOutsideHandler, true);
88
+ }
89
+ });
90
+ this.destroyRef.onDestroy(() => {
91
+ document.removeEventListener('mousedown', this.clickOutsideHandler, true);
92
+ if (this.peopleDebounceTimer)
93
+ clearTimeout(this.peopleDebounceTimer);
94
+ });
95
+ }
96
+ handleSortClick() {
97
+ this.onSort()?.();
98
+ }
99
+ toggleFilter() {
100
+ this.isFilterOpen.update((v) => !v);
101
+ }
102
+ // Text filter
103
+ handleTextApply() {
104
+ const value = this.tempTextValue().trim();
105
+ this.onTextChange()?.(value);
106
+ this.isFilterOpen.set(false);
107
+ }
108
+ handleTextClear() {
109
+ this.tempTextValue.set('');
110
+ this.onTextChange()?.('');
111
+ this.isFilterOpen.set(false);
112
+ }
113
+ // Multi-select filter
114
+ handleCheckboxChange(opt, checked) {
115
+ this.tempSelected.update((prev) => {
116
+ const next = new Set(prev);
117
+ if (checked)
118
+ next.add(opt);
119
+ else
120
+ next.delete(opt);
121
+ return next;
122
+ });
123
+ }
124
+ handleSelectAllOptions() {
125
+ this.tempSelected.set(new Set(this.filteredOptions()));
126
+ }
127
+ handleClearSelection() {
128
+ this.tempSelected.set(new Set());
129
+ }
130
+ handleApplyMultiSelect() {
131
+ this.onFilterChange()?.(Array.from(this.tempSelected()));
132
+ this.isFilterOpen.set(false);
133
+ }
134
+ // People filter
135
+ onPeopleSearchInput(value) {
136
+ this.peopleSearchText.set(value);
137
+ if (this.peopleDebounceTimer)
138
+ clearTimeout(this.peopleDebounceTimer);
139
+ const searcher = this.peopleSearch();
140
+ if (!searcher || !value.trim()) {
141
+ this.peopleSuggestions.set([]);
142
+ return;
143
+ }
144
+ this.isPeopleLoading.set(true);
145
+ this.peopleDebounceTimer = setTimeout(() => {
146
+ searcher(value.trim())
147
+ .then((results) => {
148
+ this.peopleSuggestions.set(results);
149
+ this.isPeopleLoading.set(false);
150
+ })
151
+ .catch(() => {
152
+ this.peopleSuggestions.set([]);
153
+ this.isPeopleLoading.set(false);
154
+ });
155
+ }, 300);
156
+ }
157
+ handleUserSelect(user) {
158
+ this.onUserChange()?.(user);
159
+ this.isFilterOpen.set(false);
160
+ }
161
+ handleClearUser() {
162
+ this.onUserChange()?.(undefined);
163
+ this.isFilterOpen.set(false);
164
+ }
165
+ // Date filter
166
+ handleDateApply() {
167
+ const from = this.tempDateFrom() || undefined;
168
+ const to = this.tempDateTo() || undefined;
169
+ this.onDateChange()?.(from || to ? { from, to } : undefined);
170
+ this.isFilterOpen.set(false);
171
+ }
172
+ handleDateClear() {
173
+ this.tempDateFrom.set('');
174
+ this.tempDateTo.set('');
175
+ this.onDateChange()?.(undefined);
176
+ this.isFilterOpen.set(false);
177
+ }
178
+ };
179
+ ColumnHeaderFilterComponent = __decorate([
180
+ Component({
181
+ selector: 'ogrid-primeng-column-header-filter',
182
+ standalone: true,
183
+ imports: [CommonModule],
184
+ template: `
185
+ <div style="display:flex;align-items:center;flex:1;min-width:0;gap:4px">
186
+ <div style="flex:1;min-width:0;overflow:hidden">
187
+ <span style="overflow:hidden;text-overflow:ellipsis;white-space:nowrap;display:block" [title]="columnName()" data-header-label>
188
+ {{ columnName() }}
189
+ </span>
190
+ </div>
191
+ <div style="display:flex;align-items:center;gap:2px;flex-shrink:0">
192
+ @if (onSort()) {
193
+ <button
194
+ type="button"
195
+ (click)="handleSortClick()"
196
+ [attr.aria-label]="'Sort by ' + columnName()"
197
+ [title]="isSorted() ? (isSortedDescending() ? 'Sorted descending' : 'Sorted ascending') : 'Sort'"
198
+ style="border:none;background:transparent;cursor:pointer;padding:2px 4px;font-size:12px;color:var(--ogrid-fg, #242424)"
199
+ [style.font-weight]="isSorted() ? 'bold' : 'normal'"
200
+ >
201
+ {{ isSorted() ? (isSortedDescending() ? '\u2193' : '\u2191') : '\u21C5' }}
202
+ </button>
203
+ }
204
+
205
+ @if (filterType() !== 'none') {
206
+ <button
207
+ type="button"
208
+ #filterTrigger
209
+ (click)="toggleFilter()"
210
+ [attr.aria-label]="'Filter ' + columnName()"
211
+ [title]="'Filter ' + columnName()"
212
+ style="border:none;background:transparent;cursor:pointer;padding:2px 4px;font-size:12px;position:relative;color:var(--ogrid-fg, #242424)"
213
+ [style.font-weight]="hasActiveFilter() ? 'bold' : 'normal'"
214
+ >
215
+ &#9662;
216
+ @if (hasActiveFilter()) {
217
+ <span style="position:absolute;top:0;right:0;width:6px;height:6px;border-radius:50%;background:var(--ogrid-selection, #217346)"></span>
218
+ }
219
+ </button>
220
+
221
+ @if (isFilterOpen()) {
222
+ <div
223
+ #filterPanel
224
+ style="position:absolute;top:100%;left:0;z-index:200;min-width:200px;background:var(--ogrid-bg, #fff);border:1px solid var(--ogrid-border, #e0e0e0);border-radius:6px;box-shadow:0 2px 8px rgba(0,0,0,0.12);font-weight:normal"
225
+ >
226
+ <div style="padding:8px 12px;font-weight:600;font-size:12px;border-bottom:1px solid var(--ogrid-border, #e0e0e0)">
227
+ Filter: {{ columnName() }}
228
+ </div>
229
+
230
+ @if (filterType() === 'text') {
231
+ <div style="padding:8px 12px">
232
+ <input
233
+ type="text"
234
+ [value]="tempTextValue()"
235
+ (input)="tempTextValue.set($any($event.target).value)"
236
+ placeholder="Filter text..."
237
+ style="width:100%;box-sizing:border-box;padding:6px 8px;border:1px solid var(--ogrid-border, #e0e0e0);border-radius:4px;font-size:13px;background:var(--ogrid-bg, #fff);color:var(--ogrid-fg, #242424)"
238
+ [attr.aria-label]="'Filter ' + columnName()"
239
+ />
240
+ </div>
241
+ <div style="display:flex;justify-content:flex-end;gap:6px;padding:6px 12px;border-top:1px solid var(--ogrid-border, #e0e0e0)">
242
+ <button
243
+ type="button"
244
+ class="p-button p-button-text p-button-sm"
245
+ (click)="handleTextClear()"
246
+ [disabled]="!textValue()"
247
+ style="font-size:12px"
248
+ >Clear</button>
249
+ <button
250
+ type="button"
251
+ class="p-button p-button-sm"
252
+ (click)="handleTextApply()"
253
+ style="font-size:12px"
254
+ >Apply</button>
255
+ </div>
256
+ }
257
+
258
+ @if (filterType() === 'multiSelect') {
259
+ <div style="padding:8px 12px">
260
+ <input
261
+ type="text"
262
+ [value]="searchText()"
263
+ (input)="searchText.set($any($event.target).value)"
264
+ placeholder="Search options..."
265
+ style="width:100%;box-sizing:border-box;padding:6px 8px;border:1px solid var(--ogrid-border, #e0e0e0);border-radius:4px;font-size:13px;margin-bottom:6px;background:var(--ogrid-bg, #fff);color:var(--ogrid-fg, #242424)"
266
+ [attr.aria-label]="'Search ' + columnName() + ' options'"
267
+ />
268
+ @if (isLoadingOptions()) {
269
+ <div style="padding:8px 0;color:var(--ogrid-muted, #999);font-size:12px">Loading...</div>
270
+ } @else {
271
+ <div style="display:flex;gap:4px;margin-bottom:6px">
272
+ <button type="button" class="p-button p-button-text p-button-sm" (click)="handleSelectAllOptions()" style="font-size:11px">All</button>
273
+ <button type="button" class="p-button p-button-text p-button-sm" (click)="handleClearSelection()" style="font-size:11px">None</button>
274
+ </div>
275
+ <div style="max-height:160px;overflow-y:auto" role="group" [attr.aria-label]="columnName() + ' filter options'">
276
+ @for (opt of filteredOptions(); track opt) {
277
+ <label style="display:flex;align-items:center;gap:6px;padding:2px 0;cursor:pointer;font-size:13px">
278
+ <input
279
+ type="checkbox"
280
+ [checked]="tempSelected().has(opt)"
281
+ (change)="handleCheckboxChange(opt, $any($event.target).checked)"
282
+ />
283
+ {{ opt }}
284
+ </label>
285
+ }
286
+ </div>
287
+ }
288
+ </div>
289
+ <div style="display:flex;justify-content:flex-end;gap:6px;padding:6px 12px;border-top:1px solid var(--ogrid-border, #e0e0e0)">
290
+ <button
291
+ type="button"
292
+ class="p-button p-button-sm"
293
+ (click)="handleApplyMultiSelect()"
294
+ style="font-size:12px"
295
+ >Apply</button>
296
+ </div>
297
+ }
298
+
299
+ @if (filterType() === 'people') {
300
+ <div style="padding:8px 12px">
301
+ @if (selectedUser()) {
302
+ <div style="display:flex;align-items:center;justify-content:space-between;padding:4px 0;margin-bottom:4px">
303
+ <span style="font-size:13px">{{ selectedUser()!.displayName ?? selectedUser()!.email }}</span>
304
+ <button type="button" class="p-button p-button-text p-button-sm" (click)="handleClearUser()" style="font-size:11px">Clear</button>
305
+ </div>
306
+ }
307
+ <input
308
+ type="text"
309
+ [value]="peopleSearchText()"
310
+ (input)="onPeopleSearchInput($any($event.target).value)"
311
+ placeholder="Search people..."
312
+ style="width:100%;box-sizing:border-box;padding:6px 8px;border:1px solid var(--ogrid-border, #e0e0e0);border-radius:4px;font-size:13px;background:var(--ogrid-bg, #fff);color:var(--ogrid-fg, #242424)"
313
+ [attr.aria-label]="'Search people for ' + columnName()"
314
+ />
315
+ @if (isPeopleLoading()) {
316
+ <div style="padding:8px 0;color:var(--ogrid-muted, #999);font-size:12px">Loading...</div>
317
+ }
318
+ @for (user of peopleSuggestions(); track user.email) {
319
+ <button
320
+ type="button"
321
+ (click)="handleUserSelect(user)"
322
+ style="display:block;width:100%;text-align:left;padding:6px 0;border:none;background:transparent;cursor:pointer;font-size:13px;color:var(--ogrid-fg, #242424)"
323
+ >
324
+ {{ user.displayName ?? user.email }}
325
+ </button>
326
+ }
327
+ </div>
328
+ }
329
+
330
+ @if (filterType() === 'date') {
331
+ <div style="padding:8px 12px;display:flex;flex-direction:column;gap:6px">
332
+ <label style="display:flex;align-items:center;gap:6px;font-size:12px">
333
+ From:
334
+ <input
335
+ type="date"
336
+ [value]="tempDateFrom()"
337
+ (change)="tempDateFrom.set($any($event.target).value)"
338
+ style="flex:1;padding:4px 6px;border:1px solid var(--ogrid-border, #e0e0e0);border-radius:4px;background:var(--ogrid-bg, #fff);color:var(--ogrid-fg, #242424)"
339
+ />
340
+ </label>
341
+ <label style="display:flex;align-items:center;gap:6px;font-size:12px">
342
+ To:
343
+ <input
344
+ type="date"
345
+ [value]="tempDateTo()"
346
+ (change)="tempDateTo.set($any($event.target).value)"
347
+ style="flex:1;padding:4px 6px;border:1px solid var(--ogrid-border, #e0e0e0);border-radius:4px;background:var(--ogrid-bg, #fff);color:var(--ogrid-fg, #242424)"
348
+ />
349
+ </label>
350
+ </div>
351
+ <div style="display:flex;justify-content:flex-end;gap:6px;padding:6px 12px;border-top:1px solid var(--ogrid-border, #e0e0e0)">
352
+ <button
353
+ type="button"
354
+ class="p-button p-button-text p-button-sm"
355
+ (click)="handleDateClear()"
356
+ [disabled]="!tempDateFrom() && !tempDateTo()"
357
+ style="font-size:12px"
358
+ >Clear</button>
359
+ <button
360
+ type="button"
361
+ class="p-button p-button-sm"
362
+ (click)="handleDateApply()"
363
+ style="font-size:12px"
364
+ >Apply</button>
365
+ </div>
366
+ }
367
+ </div>
368
+ }
369
+ }
370
+ </div>
371
+ </div>
372
+ `,
373
+ styles: [`
374
+ :host {
375
+ display: flex;
376
+ position: relative;
377
+ flex: 1;
378
+ min-width: 0;
379
+ }
380
+ `],
381
+ })
382
+ ], ColumnHeaderFilterComponent);
383
+ export { ColumnHeaderFilterComponent };