@jigisha_ruparel/taf-client-filter-bar-angular 0.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.
|
@@ -0,0 +1,363 @@
|
|
|
1
|
+
import * as i1 from '@angular/common';
|
|
2
|
+
import { CommonModule } from '@angular/common';
|
|
3
|
+
import * as i0 from '@angular/core';
|
|
4
|
+
import { Directive, EventEmitter, booleanAttribute, Output, Input, ChangeDetectionStrategy, Component } from '@angular/core';
|
|
5
|
+
|
|
6
|
+
/** Marks host content to place at the end of the bar (an export button, a view switch). */
|
|
7
|
+
class TafFilterBarEndDirective {
|
|
8
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: TafFilterBarEndDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
9
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.1.5", type: TafFilterBarEndDirective, isStandalone: true, selector: "[tafFilterBarEnd]", ngImport: i0 });
|
|
10
|
+
}
|
|
11
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: TafFilterBarEndDirective, decorators: [{
|
|
12
|
+
type: Directive,
|
|
13
|
+
args: [{ selector: '[tafFilterBarEnd]', standalone: true }]
|
|
14
|
+
}] });
|
|
15
|
+
/**
|
|
16
|
+
* `<taf-filter-bar>` — the row of controls that sits above a list: a search box, some selects, a
|
|
17
|
+
* chip group, a date range, and a Clear that appears once anything is set.
|
|
18
|
+
*
|
|
19
|
+
* The bar owns no state. It renders `[value]`, and every interaction emits `(changed)` with the one
|
|
20
|
+
* key that moved plus `(valueChange)` with the whole record, so a host can keep either one signal per
|
|
21
|
+
* filter or a single object — whichever it already has.
|
|
22
|
+
*
|
|
23
|
+
* Values by kind: `search`/`select`/`date` → string; `chips` → string[]; `date-range` →
|
|
24
|
+
* `{ from: string; to: string }`; `toggle` → boolean.
|
|
25
|
+
*
|
|
26
|
+
* Ships NO theme — `taf-filter-bar__*` class hooks plus `data-kind` / `data-active`, and
|
|
27
|
+
* `ViewEncapsulation.None` is deliberately NOT used here: the controls are plain elements the host
|
|
28
|
+
* app styles through those classes.
|
|
29
|
+
*/
|
|
30
|
+
class TafFilterBarComponent {
|
|
31
|
+
filters = [];
|
|
32
|
+
value = {};
|
|
33
|
+
/** Show a Clear button once any filter is set. */
|
|
34
|
+
showClear = true;
|
|
35
|
+
clearLabel = 'Clear';
|
|
36
|
+
/** Default debounce for `search` filters that do not set their own, in ms. */
|
|
37
|
+
searchDebounce = 200;
|
|
38
|
+
/** The one filter that moved. */
|
|
39
|
+
changed = new EventEmitter();
|
|
40
|
+
/** The whole record after that change. */
|
|
41
|
+
valueChange = new EventEmitter();
|
|
42
|
+
/** Clear pressed — the host decides what "empty" means for its own state. */
|
|
43
|
+
cleared = new EventEmitter();
|
|
44
|
+
timer;
|
|
45
|
+
ngOnDestroy() {
|
|
46
|
+
if (this.timer)
|
|
47
|
+
clearTimeout(this.timer);
|
|
48
|
+
}
|
|
49
|
+
/** True when anything is set — what makes Clear appear. */
|
|
50
|
+
get isDirty() {
|
|
51
|
+
return this.filters.some((f) => {
|
|
52
|
+
const v = this.value[f.key];
|
|
53
|
+
if (Array.isArray(v))
|
|
54
|
+
return v.length > 0;
|
|
55
|
+
if (v && typeof v === 'object')
|
|
56
|
+
return Object.values(v).some(Boolean);
|
|
57
|
+
return v !== undefined && v !== null && v !== '' && v !== false;
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
trackKey = (_, f) => f.key;
|
|
61
|
+
options(f) {
|
|
62
|
+
const raw = f.options ?? [];
|
|
63
|
+
const vk = f.optionValue;
|
|
64
|
+
const lk = f.optionLabel;
|
|
65
|
+
if (!vk && !lk)
|
|
66
|
+
return raw;
|
|
67
|
+
return raw.map((o) => ({
|
|
68
|
+
value: String(o[vk ?? 'value'] ?? ''),
|
|
69
|
+
label: String(o[lk ?? 'label'] ?? o[vk ?? 'value'] ?? ''),
|
|
70
|
+
}));
|
|
71
|
+
}
|
|
72
|
+
asText(key) {
|
|
73
|
+
const v = this.value[key];
|
|
74
|
+
return v === undefined || v === null ? '' : String(v);
|
|
75
|
+
}
|
|
76
|
+
range(key) {
|
|
77
|
+
const v = (this.value[key] ?? {});
|
|
78
|
+
return { from: v.from ?? '', to: v.to ?? '' };
|
|
79
|
+
}
|
|
80
|
+
isPicked(key, option) {
|
|
81
|
+
const v = this.value[key];
|
|
82
|
+
return Array.isArray(v) ? v.includes(option) : false;
|
|
83
|
+
}
|
|
84
|
+
onSearch(f, text) {
|
|
85
|
+
const wait = f.debounce ?? this.searchDebounce;
|
|
86
|
+
if (this.timer)
|
|
87
|
+
clearTimeout(this.timer);
|
|
88
|
+
if (!wait) {
|
|
89
|
+
this.set(f.key, text);
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
this.timer = setTimeout(() => this.set(f.key, text), wait);
|
|
93
|
+
}
|
|
94
|
+
toggleChip(key, option) {
|
|
95
|
+
const current = Array.isArray(this.value[key]) ? ([...this.value[key]]) : [];
|
|
96
|
+
const at = current.indexOf(option);
|
|
97
|
+
if (at >= 0)
|
|
98
|
+
current.splice(at, 1);
|
|
99
|
+
else
|
|
100
|
+
current.push(option);
|
|
101
|
+
this.set(key, current);
|
|
102
|
+
}
|
|
103
|
+
setRange(key, edge, text) {
|
|
104
|
+
this.set(key, { ...this.range(key), [edge]: text });
|
|
105
|
+
}
|
|
106
|
+
set(key, next) {
|
|
107
|
+
this.value = { ...this.value, [key]: next };
|
|
108
|
+
this.changed.emit({ key, value: next });
|
|
109
|
+
this.valueChange.emit(this.value);
|
|
110
|
+
}
|
|
111
|
+
clear() {
|
|
112
|
+
const empty = {};
|
|
113
|
+
for (const f of this.filters) {
|
|
114
|
+
empty[f.key] =
|
|
115
|
+
f.kind === 'chips' ? [] : f.kind === 'date-range' ? { from: '', to: '' } : f.kind === 'toggle' ? false : '';
|
|
116
|
+
}
|
|
117
|
+
this.value = empty;
|
|
118
|
+
this.cleared.emit();
|
|
119
|
+
this.valueChange.emit(this.value);
|
|
120
|
+
}
|
|
121
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: TafFilterBarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
122
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "16.1.0", version: "22.1.5", type: TafFilterBarComponent, isStandalone: true, selector: "taf-filter-bar", inputs: { filters: "filters", value: "value", showClear: ["showClear", "showClear", booleanAttribute], clearLabel: "clearLabel", searchDebounce: "searchDebounce" }, outputs: { changed: "changed", valueChange: "valueChange", cleared: "cleared" }, ngImport: i0, template: `
|
|
123
|
+
<div class="taf-filter-bar" role="search">
|
|
124
|
+
<ng-container *ngFor="let f of filters; trackBy: trackKey">
|
|
125
|
+
<ng-container [ngSwitch]="f.kind">
|
|
126
|
+
<input
|
|
127
|
+
*ngSwitchCase="'search'"
|
|
128
|
+
class="taf-filter-bar__field taf-filter-bar__search"
|
|
129
|
+
type="search"
|
|
130
|
+
data-kind="search"
|
|
131
|
+
[attr.data-key]="f.key"
|
|
132
|
+
[attr.aria-label]="f.label || 'Search'"
|
|
133
|
+
[placeholder]="f.label || ''"
|
|
134
|
+
[disabled]="!!f.disabled"
|
|
135
|
+
[style.width.px]="f.width || null"
|
|
136
|
+
[value]="asText(f.key)"
|
|
137
|
+
(input)="onSearch(f, $any($event.target).value)"
|
|
138
|
+
/>
|
|
139
|
+
|
|
140
|
+
<select
|
|
141
|
+
*ngSwitchCase="'select'"
|
|
142
|
+
class="taf-filter-bar__field taf-filter-bar__select"
|
|
143
|
+
data-kind="select"
|
|
144
|
+
[attr.data-key]="f.key"
|
|
145
|
+
[attr.data-active]="asText(f.key) ? '' : null"
|
|
146
|
+
[attr.aria-label]="f.title || f.label || f.key"
|
|
147
|
+
[disabled]="!!f.disabled"
|
|
148
|
+
[style.width.px]="f.width || null"
|
|
149
|
+
[value]="asText(f.key)"
|
|
150
|
+
(change)="set(f.key, $any($event.target).value)"
|
|
151
|
+
>
|
|
152
|
+
<option value="" [selected]="!asText(f.key)">{{ f.label || 'All' }}</option>
|
|
153
|
+
<option
|
|
154
|
+
*ngFor="let o of options(f)"
|
|
155
|
+
[value]="o.value"
|
|
156
|
+
[selected]="asText(f.key) === o.value"
|
|
157
|
+
>{{ o.label }}</option>
|
|
158
|
+
</select>
|
|
159
|
+
|
|
160
|
+
<div *ngSwitchCase="'chips'" class="taf-filter-bar__chips" data-kind="chips" [attr.data-key]="f.key">
|
|
161
|
+
<button
|
|
162
|
+
*ngFor="let o of options(f)"
|
|
163
|
+
type="button"
|
|
164
|
+
class="taf-filter-bar__chip"
|
|
165
|
+
[attr.data-active]="isPicked(f.key, o.value) ? '' : null"
|
|
166
|
+
[attr.aria-pressed]="isPicked(f.key, o.value)"
|
|
167
|
+
[disabled]="!!f.disabled"
|
|
168
|
+
(click)="toggleChip(f.key, o.value)"
|
|
169
|
+
>{{ o.label }}</button>
|
|
170
|
+
</div>
|
|
171
|
+
|
|
172
|
+
<input
|
|
173
|
+
*ngSwitchCase="'date'"
|
|
174
|
+
class="taf-filter-bar__field taf-filter-bar__date"
|
|
175
|
+
type="date"
|
|
176
|
+
data-kind="date"
|
|
177
|
+
[attr.data-key]="f.key"
|
|
178
|
+
[attr.aria-label]="f.label || f.key"
|
|
179
|
+
[attr.title]="f.title || f.label || null"
|
|
180
|
+
[disabled]="!!f.disabled"
|
|
181
|
+
[value]="asText(f.key)"
|
|
182
|
+
(input)="set(f.key, $any($event.target).value)"
|
|
183
|
+
/>
|
|
184
|
+
|
|
185
|
+
<div *ngSwitchCase="'date-range'" class="taf-filter-bar__range" data-kind="date-range" [attr.data-key]="f.key">
|
|
186
|
+
<input
|
|
187
|
+
type="date"
|
|
188
|
+
class="taf-filter-bar__field taf-filter-bar__date"
|
|
189
|
+
[attr.aria-label]="(f.label || f.key) + ' from'"
|
|
190
|
+
title="From"
|
|
191
|
+
[disabled]="!!f.disabled"
|
|
192
|
+
[value]="range(f.key).from"
|
|
193
|
+
(input)="setRange(f.key, 'from', $any($event.target).value)"
|
|
194
|
+
/>
|
|
195
|
+
<span class="taf-filter-bar__range-sep" aria-hidden="true">–</span>
|
|
196
|
+
<input
|
|
197
|
+
type="date"
|
|
198
|
+
class="taf-filter-bar__field taf-filter-bar__date"
|
|
199
|
+
[attr.aria-label]="(f.label || f.key) + ' to'"
|
|
200
|
+
title="To"
|
|
201
|
+
[disabled]="!!f.disabled"
|
|
202
|
+
[value]="range(f.key).to"
|
|
203
|
+
(input)="setRange(f.key, 'to', $any($event.target).value)"
|
|
204
|
+
/>
|
|
205
|
+
</div>
|
|
206
|
+
|
|
207
|
+
<label *ngSwitchCase="'toggle'" class="taf-filter-bar__toggle" data-kind="toggle" [attr.data-key]="f.key">
|
|
208
|
+
<input
|
|
209
|
+
type="checkbox"
|
|
210
|
+
[disabled]="!!f.disabled"
|
|
211
|
+
[checked]="!!value[f.key]"
|
|
212
|
+
(change)="set(f.key, $any($event.target).checked)"
|
|
213
|
+
/>
|
|
214
|
+
{{ f.label }}
|
|
215
|
+
</label>
|
|
216
|
+
</ng-container>
|
|
217
|
+
</ng-container>
|
|
218
|
+
|
|
219
|
+
<span class="taf-filter-bar__spacer"></span>
|
|
220
|
+
<ng-content select="[tafFilterBarEnd]"></ng-content>
|
|
221
|
+
<button
|
|
222
|
+
*ngIf="showClear && isDirty"
|
|
223
|
+
type="button"
|
|
224
|
+
class="taf-filter-bar__clear"
|
|
225
|
+
(click)="clear()"
|
|
226
|
+
>{{ clearLabel }}</button>
|
|
227
|
+
</div>
|
|
228
|
+
`, isInline: true, styles: ["taf-filter-bar{display:contents}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { kind: "directive", type: i1.NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
229
|
+
}
|
|
230
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: TafFilterBarComponent, decorators: [{
|
|
231
|
+
type: Component,
|
|
232
|
+
args: [{ selector: 'taf-filter-bar', standalone: true, imports: [CommonModule], changeDetection: ChangeDetectionStrategy.OnPush, template: `
|
|
233
|
+
<div class="taf-filter-bar" role="search">
|
|
234
|
+
<ng-container *ngFor="let f of filters; trackBy: trackKey">
|
|
235
|
+
<ng-container [ngSwitch]="f.kind">
|
|
236
|
+
<input
|
|
237
|
+
*ngSwitchCase="'search'"
|
|
238
|
+
class="taf-filter-bar__field taf-filter-bar__search"
|
|
239
|
+
type="search"
|
|
240
|
+
data-kind="search"
|
|
241
|
+
[attr.data-key]="f.key"
|
|
242
|
+
[attr.aria-label]="f.label || 'Search'"
|
|
243
|
+
[placeholder]="f.label || ''"
|
|
244
|
+
[disabled]="!!f.disabled"
|
|
245
|
+
[style.width.px]="f.width || null"
|
|
246
|
+
[value]="asText(f.key)"
|
|
247
|
+
(input)="onSearch(f, $any($event.target).value)"
|
|
248
|
+
/>
|
|
249
|
+
|
|
250
|
+
<select
|
|
251
|
+
*ngSwitchCase="'select'"
|
|
252
|
+
class="taf-filter-bar__field taf-filter-bar__select"
|
|
253
|
+
data-kind="select"
|
|
254
|
+
[attr.data-key]="f.key"
|
|
255
|
+
[attr.data-active]="asText(f.key) ? '' : null"
|
|
256
|
+
[attr.aria-label]="f.title || f.label || f.key"
|
|
257
|
+
[disabled]="!!f.disabled"
|
|
258
|
+
[style.width.px]="f.width || null"
|
|
259
|
+
[value]="asText(f.key)"
|
|
260
|
+
(change)="set(f.key, $any($event.target).value)"
|
|
261
|
+
>
|
|
262
|
+
<option value="" [selected]="!asText(f.key)">{{ f.label || 'All' }}</option>
|
|
263
|
+
<option
|
|
264
|
+
*ngFor="let o of options(f)"
|
|
265
|
+
[value]="o.value"
|
|
266
|
+
[selected]="asText(f.key) === o.value"
|
|
267
|
+
>{{ o.label }}</option>
|
|
268
|
+
</select>
|
|
269
|
+
|
|
270
|
+
<div *ngSwitchCase="'chips'" class="taf-filter-bar__chips" data-kind="chips" [attr.data-key]="f.key">
|
|
271
|
+
<button
|
|
272
|
+
*ngFor="let o of options(f)"
|
|
273
|
+
type="button"
|
|
274
|
+
class="taf-filter-bar__chip"
|
|
275
|
+
[attr.data-active]="isPicked(f.key, o.value) ? '' : null"
|
|
276
|
+
[attr.aria-pressed]="isPicked(f.key, o.value)"
|
|
277
|
+
[disabled]="!!f.disabled"
|
|
278
|
+
(click)="toggleChip(f.key, o.value)"
|
|
279
|
+
>{{ o.label }}</button>
|
|
280
|
+
</div>
|
|
281
|
+
|
|
282
|
+
<input
|
|
283
|
+
*ngSwitchCase="'date'"
|
|
284
|
+
class="taf-filter-bar__field taf-filter-bar__date"
|
|
285
|
+
type="date"
|
|
286
|
+
data-kind="date"
|
|
287
|
+
[attr.data-key]="f.key"
|
|
288
|
+
[attr.aria-label]="f.label || f.key"
|
|
289
|
+
[attr.title]="f.title || f.label || null"
|
|
290
|
+
[disabled]="!!f.disabled"
|
|
291
|
+
[value]="asText(f.key)"
|
|
292
|
+
(input)="set(f.key, $any($event.target).value)"
|
|
293
|
+
/>
|
|
294
|
+
|
|
295
|
+
<div *ngSwitchCase="'date-range'" class="taf-filter-bar__range" data-kind="date-range" [attr.data-key]="f.key">
|
|
296
|
+
<input
|
|
297
|
+
type="date"
|
|
298
|
+
class="taf-filter-bar__field taf-filter-bar__date"
|
|
299
|
+
[attr.aria-label]="(f.label || f.key) + ' from'"
|
|
300
|
+
title="From"
|
|
301
|
+
[disabled]="!!f.disabled"
|
|
302
|
+
[value]="range(f.key).from"
|
|
303
|
+
(input)="setRange(f.key, 'from', $any($event.target).value)"
|
|
304
|
+
/>
|
|
305
|
+
<span class="taf-filter-bar__range-sep" aria-hidden="true">–</span>
|
|
306
|
+
<input
|
|
307
|
+
type="date"
|
|
308
|
+
class="taf-filter-bar__field taf-filter-bar__date"
|
|
309
|
+
[attr.aria-label]="(f.label || f.key) + ' to'"
|
|
310
|
+
title="To"
|
|
311
|
+
[disabled]="!!f.disabled"
|
|
312
|
+
[value]="range(f.key).to"
|
|
313
|
+
(input)="setRange(f.key, 'to', $any($event.target).value)"
|
|
314
|
+
/>
|
|
315
|
+
</div>
|
|
316
|
+
|
|
317
|
+
<label *ngSwitchCase="'toggle'" class="taf-filter-bar__toggle" data-kind="toggle" [attr.data-key]="f.key">
|
|
318
|
+
<input
|
|
319
|
+
type="checkbox"
|
|
320
|
+
[disabled]="!!f.disabled"
|
|
321
|
+
[checked]="!!value[f.key]"
|
|
322
|
+
(change)="set(f.key, $any($event.target).checked)"
|
|
323
|
+
/>
|
|
324
|
+
{{ f.label }}
|
|
325
|
+
</label>
|
|
326
|
+
</ng-container>
|
|
327
|
+
</ng-container>
|
|
328
|
+
|
|
329
|
+
<span class="taf-filter-bar__spacer"></span>
|
|
330
|
+
<ng-content select="[tafFilterBarEnd]"></ng-content>
|
|
331
|
+
<button
|
|
332
|
+
*ngIf="showClear && isDirty"
|
|
333
|
+
type="button"
|
|
334
|
+
class="taf-filter-bar__clear"
|
|
335
|
+
(click)="clear()"
|
|
336
|
+
>{{ clearLabel }}</button>
|
|
337
|
+
</div>
|
|
338
|
+
`, styles: ["taf-filter-bar{display:contents}\n"] }]
|
|
339
|
+
}], propDecorators: { filters: [{
|
|
340
|
+
type: Input
|
|
341
|
+
}], value: [{
|
|
342
|
+
type: Input
|
|
343
|
+
}], showClear: [{
|
|
344
|
+
type: Input,
|
|
345
|
+
args: [{ transform: booleanAttribute }]
|
|
346
|
+
}], clearLabel: [{
|
|
347
|
+
type: Input
|
|
348
|
+
}], searchDebounce: [{
|
|
349
|
+
type: Input
|
|
350
|
+
}], changed: [{
|
|
351
|
+
type: Output
|
|
352
|
+
}], valueChange: [{
|
|
353
|
+
type: Output
|
|
354
|
+
}], cleared: [{
|
|
355
|
+
type: Output
|
|
356
|
+
}] } });
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* Generated bundle index. Do not edit.
|
|
360
|
+
*/
|
|
361
|
+
|
|
362
|
+
export { TafFilterBarComponent, TafFilterBarEndDirective };
|
|
363
|
+
//# sourceMappingURL=jigisha_ruparel-taf-client-filter-bar-angular.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jigisha_ruparel-taf-client-filter-bar-angular.mjs","sources":["../../src/taf-filter-bar.component.ts","../../src/jigisha_ruparel-taf-client-filter-bar-angular.ts"],"sourcesContent":["import { CommonModule } from '@angular/common';\nimport {\n ChangeDetectionStrategy,\n Component,\n Directive,\n EventEmitter,\n Input,\n OnDestroy,\n Output,\n booleanAttribute,\n} from '@angular/core';\n\n/** What a filter looks like on screen. */\nexport type TafFilterKind = 'search' | 'select' | 'chips' | 'date' | 'date-range' | 'toggle';\n\n/** One entry in a select or chip group. */\nexport interface TafFilterOption {\n value: string;\n label: string;\n}\n\n/** One filter in the bar. `key` is what the value is stored and emitted under. */\nexport interface TafFilterSpec {\n key: string;\n kind: TafFilterKind;\n /** Placeholder for a search box, the \"nothing selected\" entry for a select, the caption for a toggle. */\n label?: string;\n /** Options for `select` and `chips`. Raw rows work too — name the fields with optionValue/optionLabel. */\n options?: readonly TafFilterOption[] | readonly object[];\n optionValue?: string;\n optionLabel?: string;\n /** Width in px; the bar's own layout applies when unset. */\n width?: number;\n /** Search only: ms to wait after typing before emitting (0 emits on every keystroke). */\n debounce?: number;\n /** Shown instead of `label` inside the control, when both make sense (e.g. a select's own title). */\n title?: string;\n disabled?: boolean;\n}\n\n/** The bar's current state: one entry per filter key. */\nexport type TafFilterValue = Record<string, unknown>;\n\n/** Marks host content to place at the end of the bar (an export button, a view switch). */\n@Directive({ selector: '[tafFilterBarEnd]', standalone: true })\nexport class TafFilterBarEndDirective {}\n\n/**\n * `<taf-filter-bar>` — the row of controls that sits above a list: a search box, some selects, a\n * chip group, a date range, and a Clear that appears once anything is set.\n *\n * The bar owns no state. It renders `[value]`, and every interaction emits `(changed)` with the one\n * key that moved plus `(valueChange)` with the whole record, so a host can keep either one signal per\n * filter or a single object — whichever it already has.\n *\n * Values by kind: `search`/`select`/`date` → string; `chips` → string[]; `date-range` →\n * `{ from: string; to: string }`; `toggle` → boolean.\n *\n * Ships NO theme — `taf-filter-bar__*` class hooks plus `data-kind` / `data-active`, and\n * `ViewEncapsulation.None` is deliberately NOT used here: the controls are plain elements the host\n * app styles through those classes.\n */\n@Component({\n selector: 'taf-filter-bar',\n standalone: true,\n imports: [CommonModule],\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n <div class=\"taf-filter-bar\" role=\"search\">\n <ng-container *ngFor=\"let f of filters; trackBy: trackKey\">\n <ng-container [ngSwitch]=\"f.kind\">\n <input\n *ngSwitchCase=\"'search'\"\n class=\"taf-filter-bar__field taf-filter-bar__search\"\n type=\"search\"\n data-kind=\"search\"\n [attr.data-key]=\"f.key\"\n [attr.aria-label]=\"f.label || 'Search'\"\n [placeholder]=\"f.label || ''\"\n [disabled]=\"!!f.disabled\"\n [style.width.px]=\"f.width || null\"\n [value]=\"asText(f.key)\"\n (input)=\"onSearch(f, $any($event.target).value)\"\n />\n\n <select\n *ngSwitchCase=\"'select'\"\n class=\"taf-filter-bar__field taf-filter-bar__select\"\n data-kind=\"select\"\n [attr.data-key]=\"f.key\"\n [attr.data-active]=\"asText(f.key) ? '' : null\"\n [attr.aria-label]=\"f.title || f.label || f.key\"\n [disabled]=\"!!f.disabled\"\n [style.width.px]=\"f.width || null\"\n [value]=\"asText(f.key)\"\n (change)=\"set(f.key, $any($event.target).value)\"\n >\n <option value=\"\" [selected]=\"!asText(f.key)\">{{ f.label || 'All' }}</option>\n <option\n *ngFor=\"let o of options(f)\"\n [value]=\"o.value\"\n [selected]=\"asText(f.key) === o.value\"\n >{{ o.label }}</option>\n </select>\n\n <div *ngSwitchCase=\"'chips'\" class=\"taf-filter-bar__chips\" data-kind=\"chips\" [attr.data-key]=\"f.key\">\n <button\n *ngFor=\"let o of options(f)\"\n type=\"button\"\n class=\"taf-filter-bar__chip\"\n [attr.data-active]=\"isPicked(f.key, o.value) ? '' : null\"\n [attr.aria-pressed]=\"isPicked(f.key, o.value)\"\n [disabled]=\"!!f.disabled\"\n (click)=\"toggleChip(f.key, o.value)\"\n >{{ o.label }}</button>\n </div>\n\n <input\n *ngSwitchCase=\"'date'\"\n class=\"taf-filter-bar__field taf-filter-bar__date\"\n type=\"date\"\n data-kind=\"date\"\n [attr.data-key]=\"f.key\"\n [attr.aria-label]=\"f.label || f.key\"\n [attr.title]=\"f.title || f.label || null\"\n [disabled]=\"!!f.disabled\"\n [value]=\"asText(f.key)\"\n (input)=\"set(f.key, $any($event.target).value)\"\n />\n\n <div *ngSwitchCase=\"'date-range'\" class=\"taf-filter-bar__range\" data-kind=\"date-range\" [attr.data-key]=\"f.key\">\n <input\n type=\"date\"\n class=\"taf-filter-bar__field taf-filter-bar__date\"\n [attr.aria-label]=\"(f.label || f.key) + ' from'\"\n title=\"From\"\n [disabled]=\"!!f.disabled\"\n [value]=\"range(f.key).from\"\n (input)=\"setRange(f.key, 'from', $any($event.target).value)\"\n />\n <span class=\"taf-filter-bar__range-sep\" aria-hidden=\"true\">–</span>\n <input\n type=\"date\"\n class=\"taf-filter-bar__field taf-filter-bar__date\"\n [attr.aria-label]=\"(f.label || f.key) + ' to'\"\n title=\"To\"\n [disabled]=\"!!f.disabled\"\n [value]=\"range(f.key).to\"\n (input)=\"setRange(f.key, 'to', $any($event.target).value)\"\n />\n </div>\n\n <label *ngSwitchCase=\"'toggle'\" class=\"taf-filter-bar__toggle\" data-kind=\"toggle\" [attr.data-key]=\"f.key\">\n <input\n type=\"checkbox\"\n [disabled]=\"!!f.disabled\"\n [checked]=\"!!value[f.key]\"\n (change)=\"set(f.key, $any($event.target).checked)\"\n />\n {{ f.label }}\n </label>\n </ng-container>\n </ng-container>\n\n <span class=\"taf-filter-bar__spacer\"></span>\n <ng-content select=\"[tafFilterBarEnd]\"></ng-content>\n <button\n *ngIf=\"showClear && isDirty\"\n type=\"button\"\n class=\"taf-filter-bar__clear\"\n (click)=\"clear()\"\n >{{ clearLabel }}</button>\n </div>\n `,\n styles: [\n `\n taf-filter-bar { display: contents; }\n `,\n ],\n})\nexport class TafFilterBarComponent implements OnDestroy {\n @Input() filters: readonly TafFilterSpec[] = [];\n @Input() value: TafFilterValue = {};\n /** Show a Clear button once any filter is set. */\n @Input({ transform: booleanAttribute }) showClear = true;\n @Input() clearLabel = 'Clear';\n /** Default debounce for `search` filters that do not set their own, in ms. */\n @Input() searchDebounce = 200;\n\n /** The one filter that moved. */\n @Output() readonly changed = new EventEmitter<{ key: string; value: unknown }>();\n /** The whole record after that change. */\n @Output() readonly valueChange = new EventEmitter<TafFilterValue>();\n /** Clear pressed — the host decides what \"empty\" means for its own state. */\n @Output() readonly cleared = new EventEmitter<void>();\n\n private timer?: ReturnType<typeof setTimeout>;\n\n ngOnDestroy(): void {\n if (this.timer) clearTimeout(this.timer);\n }\n\n /** True when anything is set — what makes Clear appear. */\n get isDirty(): boolean {\n return this.filters.some((f) => {\n const v = this.value[f.key];\n if (Array.isArray(v)) return v.length > 0;\n if (v && typeof v === 'object') return Object.values(v as object).some(Boolean);\n return v !== undefined && v !== null && v !== '' && v !== false;\n });\n }\n\n protected trackKey = (_: number, f: TafFilterSpec) => f.key;\n\n protected options(f: TafFilterSpec): TafFilterOption[] {\n const raw = f.options ?? [];\n const vk = f.optionValue;\n const lk = f.optionLabel;\n if (!vk && !lk) return raw as TafFilterOption[];\n return (raw as readonly Record<string, unknown>[]).map((o) => ({\n value: String(o[vk ?? 'value'] ?? ''),\n label: String(o[lk ?? 'label'] ?? o[vk ?? 'value'] ?? ''),\n }));\n }\n\n protected asText(key: string): string {\n const v = this.value[key];\n return v === undefined || v === null ? '' : String(v);\n }\n\n protected range(key: string): { from: string; to: string } {\n const v = (this.value[key] ?? {}) as { from?: string; to?: string };\n return { from: v.from ?? '', to: v.to ?? '' };\n }\n\n protected isPicked(key: string, option: string): boolean {\n const v = this.value[key];\n return Array.isArray(v) ? v.includes(option) : false;\n }\n\n protected onSearch(f: TafFilterSpec, text: string): void {\n const wait = f.debounce ?? this.searchDebounce;\n if (this.timer) clearTimeout(this.timer);\n if (!wait) {\n this.set(f.key, text);\n return;\n }\n this.timer = setTimeout(() => this.set(f.key, text), wait);\n }\n\n protected toggleChip(key: string, option: string): void {\n const current = Array.isArray(this.value[key]) ? ([...(this.value[key] as string[])]) : [];\n const at = current.indexOf(option);\n if (at >= 0) current.splice(at, 1);\n else current.push(option);\n this.set(key, current);\n }\n\n protected setRange(key: string, edge: 'from' | 'to', text: string): void {\n this.set(key, { ...this.range(key), [edge]: text });\n }\n\n protected set(key: string, next: unknown): void {\n this.value = { ...this.value, [key]: next };\n this.changed.emit({ key, value: next });\n this.valueChange.emit(this.value);\n }\n\n protected clear(): void {\n const empty: TafFilterValue = {};\n for (const f of this.filters) {\n empty[f.key] =\n f.kind === 'chips' ? [] : f.kind === 'date-range' ? { from: '', to: '' } : f.kind === 'toggle' ? false : '';\n }\n this.value = empty;\n this.cleared.emit();\n this.valueChange.emit(this.value);\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;;AA2CA;MAEa,wBAAwB,CAAA;uGAAxB,wBAAwB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;2FAAxB,wBAAwB,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,mBAAA,EAAA,QAAA,EAAA,EAAA,EAAA,CAAA;;2FAAxB,wBAAwB,EAAA,UAAA,EAAA,CAAA;kBADpC,SAAS;AAAC,YAAA,IAAA,EAAA,CAAA,EAAE,QAAQ,EAAE,mBAAmB,EAAE,UAAU,EAAE,IAAI,EAAE;;AAG9D;;;;;;;;;;;;;;AAcG;MAuHU,qBAAqB,CAAA;IACvB,OAAO,GAA6B,EAAE;IACtC,KAAK,GAAmB,EAAE;;IAEK,SAAS,GAAG,IAAI;IAC/C,UAAU,GAAG,OAAO;;IAEpB,cAAc,GAAG,GAAG;;AAGV,IAAA,OAAO,GAAG,IAAI,YAAY,EAAmC;;AAE7D,IAAA,WAAW,GAAG,IAAI,YAAY,EAAkB;;AAEhD,IAAA,OAAO,GAAG,IAAI,YAAY,EAAQ;AAE7C,IAAA,KAAK;IAEb,WAAW,GAAA;QACT,IAAI,IAAI,CAAC,KAAK;AAAE,YAAA,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC;IAC1C;;AAGA,IAAA,IAAI,OAAO,GAAA;QACT,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,KAAI;YAC7B,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC;AAC3B,YAAA,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;AAAE,gBAAA,OAAO,CAAC,CAAC,MAAM,GAAG,CAAC;AACzC,YAAA,IAAI,CAAC,IAAI,OAAO,CAAC,KAAK,QAAQ;gBAAE,OAAO,MAAM,CAAC,MAAM,CAAC,CAAW,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC;AAC/E,YAAA,OAAO,CAAC,KAAK,SAAS,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,KAAK,KAAK;AACjE,QAAA,CAAC,CAAC;IACJ;IAEU,QAAQ,GAAG,CAAC,CAAS,EAAE,CAAgB,KAAK,CAAC,CAAC,GAAG;AAEjD,IAAA,OAAO,CAAC,CAAgB,EAAA;AAChC,QAAA,MAAM,GAAG,GAAG,CAAC,CAAC,OAAO,IAAI,EAAE;AAC3B,QAAA,MAAM,EAAE,GAAG,CAAC,CAAC,WAAW;AACxB,QAAA,MAAM,EAAE,GAAG,CAAC,CAAC,WAAW;AACxB,QAAA,IAAI,CAAC,EAAE,IAAI,CAAC,EAAE;AAAE,YAAA,OAAO,GAAwB;QAC/C,OAAQ,GAA0C,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM;YAC7D,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;AACrC,YAAA,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,EAAE,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;AAC1D,SAAA,CAAC,CAAC;IACL;AAEU,IAAA,MAAM,CAAC,GAAW,EAAA;QAC1B,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC;AACzB,QAAA,OAAO,CAAC,KAAK,SAAS,IAAI,CAAC,KAAK,IAAI,GAAG,EAAE,GAAG,MAAM,CAAC,CAAC,CAAC;IACvD;AAEU,IAAA,KAAK,CAAC,GAAW,EAAA;AACzB,QAAA,MAAM,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,CAAmC;AACnE,QAAA,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,IAAI,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE;IAC/C;IAEU,QAAQ,CAAC,GAAW,EAAE,MAAc,EAAA;QAC5C,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC;AACzB,QAAA,OAAO,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,KAAK;IACtD;IAEU,QAAQ,CAAC,CAAgB,EAAE,IAAY,EAAA;QAC/C,MAAM,IAAI,GAAG,CAAC,CAAC,QAAQ,IAAI,IAAI,CAAC,cAAc;QAC9C,IAAI,IAAI,CAAC,KAAK;AAAE,YAAA,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC;QACxC,IAAI,CAAC,IAAI,EAAE;YACT,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,IAAI,CAAC;YACrB;QACF;QACA,IAAI,CAAC,KAAK,GAAG,UAAU,CAAC,MAAM,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,IAAI,CAAC,EAAE,IAAI,CAAC;IAC5D;IAEU,UAAU,CAAC,GAAW,EAAE,MAAc,EAAA;AAC9C,QAAA,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAI,IAAI,CAAC,KAAK,CAAC,GAAG,CAAc,CAAC,IAAI,EAAE;QAC1F,MAAM,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC;QAClC,IAAI,EAAE,IAAI,CAAC;AAAE,YAAA,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC,CAAC;;AAC7B,YAAA,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC;AACzB,QAAA,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,OAAO,CAAC;IACxB;AAEU,IAAA,QAAQ,CAAC,GAAW,EAAE,IAAmB,EAAE,IAAY,EAAA;QAC/D,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,GAAG,IAAI,EAAE,CAAC;IACrD;IAEU,GAAG,CAAC,GAAW,EAAE,IAAa,EAAA;AACtC,QAAA,IAAI,CAAC,KAAK,GAAG,EAAE,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC,GAAG,GAAG,IAAI,EAAE;AAC3C,QAAA,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;QACvC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC;IACnC;IAEU,KAAK,GAAA;QACb,MAAM,KAAK,GAAmB,EAAE;AAChC,QAAA,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,OAAO,EAAE;AAC5B,YAAA,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC;gBACV,CAAC,CAAC,IAAI,KAAK,OAAO,GAAG,EAAE,GAAG,CAAC,CAAC,IAAI,KAAK,YAAY,GAAG,EAAE,IAAI,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,GAAG,CAAC,CAAC,IAAI,KAAK,QAAQ,GAAG,KAAK,GAAG,EAAE;QAC/G;AACA,QAAA,IAAI,CAAC,KAAK,GAAG,KAAK;AAClB,QAAA,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE;QACnB,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC;IACnC;uGAjGW,qBAAqB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;2FAArB,qBAAqB,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,gBAAA,EAAA,MAAA,EAAA,EAAA,OAAA,EAAA,SAAA,EAAA,KAAA,EAAA,OAAA,EAAA,SAAA,EAAA,CAAA,WAAA,EAAA,WAAA,EAIZ,gBAAgB,CAAA,EAAA,UAAA,EAAA,YAAA,EAAA,cAAA,EAAA,gBAAA,EAAA,EAAA,OAAA,EAAA,EAAA,OAAA,EAAA,SAAA,EAAA,WAAA,EAAA,aAAA,EAAA,OAAA,EAAA,SAAA,EAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,EArH1B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0GT,EAAA,CAAA,EAAA,QAAA,EAAA,IAAA,EAAA,MAAA,EAAA,CAAA,oCAAA,CAAA,EAAA,YAAA,EAAA,CAAA,EAAA,IAAA,EAAA,UAAA,EAAA,IAAA,EA5GS,YAAY,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAA,EAAA,CAAA,OAAA,EAAA,QAAA,EAAA,kBAAA,EAAA,MAAA,EAAA,CAAA,SAAA,EAAA,cAAA,EAAA,eAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAA,EAAA,CAAA,IAAA,EAAA,QAAA,EAAA,QAAA,EAAA,MAAA,EAAA,CAAA,MAAA,EAAA,UAAA,EAAA,UAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAA,EAAA,CAAA,QAAA,EAAA,QAAA,EAAA,YAAA,EAAA,MAAA,EAAA,CAAA,UAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAA,EAAA,CAAA,YAAA,EAAA,QAAA,EAAA,gBAAA,EAAA,MAAA,EAAA,CAAA,cAAA,CAAA,EAAA,CAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA;;2FAmHX,qBAAqB,EAAA,UAAA,EAAA,CAAA;kBAtHjC,SAAS;+BACE,gBAAgB,EAAA,UAAA,EACd,IAAI,EAAA,OAAA,EACP,CAAC,YAAY,CAAC,EAAA,eAAA,EACN,uBAAuB,CAAC,MAAM,EAAA,QAAA,EACrC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0GT,EAAA,CAAA,EAAA,MAAA,EAAA,CAAA,oCAAA,CAAA,EAAA;;sBAQA;;sBACA;;sBAEA,KAAK;uBAAC,EAAE,SAAS,EAAE,gBAAgB,EAAE;;sBACrC;;sBAEA;;sBAGA;;sBAEA;;sBAEA;;;AClMH;;AAEG;;"}
|
package/package.json
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@jigisha_ruparel/taf-client-filter-bar-angular",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "<taf-filter-bar> — the themeless row of list filters: search, selects, chip groups, date ranges and Clear.",
|
|
5
|
+
"publishConfig": {
|
|
6
|
+
"access": "public",
|
|
7
|
+
"directory": "dist"
|
|
8
|
+
},
|
|
9
|
+
"peerDependencies": {
|
|
10
|
+
"@angular/core": ">=17",
|
|
11
|
+
"@angular/common": ">=17"
|
|
12
|
+
},
|
|
13
|
+
"dependencies": {
|
|
14
|
+
"tslib": "^2.3.0"
|
|
15
|
+
},
|
|
16
|
+
"module": "fesm2022/jigisha_ruparel-taf-client-filter-bar-angular.mjs",
|
|
17
|
+
"typings": "types/jigisha_ruparel-taf-client-filter-bar-angular.d.ts",
|
|
18
|
+
"exports": {
|
|
19
|
+
"./package.json": {
|
|
20
|
+
"default": "./package.json"
|
|
21
|
+
},
|
|
22
|
+
".": {
|
|
23
|
+
"types": "./types/jigisha_ruparel-taf-client-filter-bar-angular.d.ts",
|
|
24
|
+
"default": "./fesm2022/jigisha_ruparel-taf-client-filter-bar-angular.mjs"
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"sideEffects": false,
|
|
28
|
+
"type": "module"
|
|
29
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { OnDestroy, EventEmitter } from '@angular/core';
|
|
3
|
+
|
|
4
|
+
/** What a filter looks like on screen. */
|
|
5
|
+
type TafFilterKind = 'search' | 'select' | 'chips' | 'date' | 'date-range' | 'toggle';
|
|
6
|
+
/** One entry in a select or chip group. */
|
|
7
|
+
interface TafFilterOption {
|
|
8
|
+
value: string;
|
|
9
|
+
label: string;
|
|
10
|
+
}
|
|
11
|
+
/** One filter in the bar. `key` is what the value is stored and emitted under. */
|
|
12
|
+
interface TafFilterSpec {
|
|
13
|
+
key: string;
|
|
14
|
+
kind: TafFilterKind;
|
|
15
|
+
/** Placeholder for a search box, the "nothing selected" entry for a select, the caption for a toggle. */
|
|
16
|
+
label?: string;
|
|
17
|
+
/** Options for `select` and `chips`. Raw rows work too — name the fields with optionValue/optionLabel. */
|
|
18
|
+
options?: readonly TafFilterOption[] | readonly object[];
|
|
19
|
+
optionValue?: string;
|
|
20
|
+
optionLabel?: string;
|
|
21
|
+
/** Width in px; the bar's own layout applies when unset. */
|
|
22
|
+
width?: number;
|
|
23
|
+
/** Search only: ms to wait after typing before emitting (0 emits on every keystroke). */
|
|
24
|
+
debounce?: number;
|
|
25
|
+
/** Shown instead of `label` inside the control, when both make sense (e.g. a select's own title). */
|
|
26
|
+
title?: string;
|
|
27
|
+
disabled?: boolean;
|
|
28
|
+
}
|
|
29
|
+
/** The bar's current state: one entry per filter key. */
|
|
30
|
+
type TafFilterValue = Record<string, unknown>;
|
|
31
|
+
/** Marks host content to place at the end of the bar (an export button, a view switch). */
|
|
32
|
+
declare class TafFilterBarEndDirective {
|
|
33
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<TafFilterBarEndDirective, never>;
|
|
34
|
+
static ɵdir: i0.ɵɵDirectiveDeclaration<TafFilterBarEndDirective, "[tafFilterBarEnd]", never, {}, {}, never, never, true, never>;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* `<taf-filter-bar>` — the row of controls that sits above a list: a search box, some selects, a
|
|
38
|
+
* chip group, a date range, and a Clear that appears once anything is set.
|
|
39
|
+
*
|
|
40
|
+
* The bar owns no state. It renders `[value]`, and every interaction emits `(changed)` with the one
|
|
41
|
+
* key that moved plus `(valueChange)` with the whole record, so a host can keep either one signal per
|
|
42
|
+
* filter or a single object — whichever it already has.
|
|
43
|
+
*
|
|
44
|
+
* Values by kind: `search`/`select`/`date` → string; `chips` → string[]; `date-range` →
|
|
45
|
+
* `{ from: string; to: string }`; `toggle` → boolean.
|
|
46
|
+
*
|
|
47
|
+
* Ships NO theme — `taf-filter-bar__*` class hooks plus `data-kind` / `data-active`, and
|
|
48
|
+
* `ViewEncapsulation.None` is deliberately NOT used here: the controls are plain elements the host
|
|
49
|
+
* app styles through those classes.
|
|
50
|
+
*/
|
|
51
|
+
declare class TafFilterBarComponent implements OnDestroy {
|
|
52
|
+
filters: readonly TafFilterSpec[];
|
|
53
|
+
value: TafFilterValue;
|
|
54
|
+
/** Show a Clear button once any filter is set. */
|
|
55
|
+
showClear: boolean;
|
|
56
|
+
clearLabel: string;
|
|
57
|
+
/** Default debounce for `search` filters that do not set their own, in ms. */
|
|
58
|
+
searchDebounce: number;
|
|
59
|
+
/** The one filter that moved. */
|
|
60
|
+
readonly changed: EventEmitter<{
|
|
61
|
+
key: string;
|
|
62
|
+
value: unknown;
|
|
63
|
+
}>;
|
|
64
|
+
/** The whole record after that change. */
|
|
65
|
+
readonly valueChange: EventEmitter<TafFilterValue>;
|
|
66
|
+
/** Clear pressed — the host decides what "empty" means for its own state. */
|
|
67
|
+
readonly cleared: EventEmitter<void>;
|
|
68
|
+
private timer?;
|
|
69
|
+
ngOnDestroy(): void;
|
|
70
|
+
/** True when anything is set — what makes Clear appear. */
|
|
71
|
+
get isDirty(): boolean;
|
|
72
|
+
protected trackKey: (_: number, f: TafFilterSpec) => string;
|
|
73
|
+
protected options(f: TafFilterSpec): TafFilterOption[];
|
|
74
|
+
protected asText(key: string): string;
|
|
75
|
+
protected range(key: string): {
|
|
76
|
+
from: string;
|
|
77
|
+
to: string;
|
|
78
|
+
};
|
|
79
|
+
protected isPicked(key: string, option: string): boolean;
|
|
80
|
+
protected onSearch(f: TafFilterSpec, text: string): void;
|
|
81
|
+
protected toggleChip(key: string, option: string): void;
|
|
82
|
+
protected setRange(key: string, edge: 'from' | 'to', text: string): void;
|
|
83
|
+
protected set(key: string, next: unknown): void;
|
|
84
|
+
protected clear(): void;
|
|
85
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<TafFilterBarComponent, never>;
|
|
86
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<TafFilterBarComponent, "taf-filter-bar", never, { "filters": { "alias": "filters"; "required": false; }; "value": { "alias": "value"; "required": false; }; "showClear": { "alias": "showClear"; "required": false; }; "clearLabel": { "alias": "clearLabel"; "required": false; }; "searchDebounce": { "alias": "searchDebounce"; "required": false; }; }, { "changed": "changed"; "valueChange": "valueChange"; "cleared": "cleared"; }, never, ["[tafFilterBarEnd]"], true, never>;
|
|
87
|
+
static ngAcceptInputType_showClear: unknown;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export { TafFilterBarComponent, TafFilterBarEndDirective };
|
|
91
|
+
export type { TafFilterKind, TafFilterOption, TafFilterSpec, TafFilterValue };
|