@jigisha_ruparel/taf-client-filter-bar-angular 0.1.0 → 0.2.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.
@@ -1,7 +1,7 @@
1
1
  import * as i1 from '@angular/common';
2
2
  import { CommonModule } from '@angular/common';
3
3
  import * as i0 from '@angular/core';
4
- import { Directive, EventEmitter, booleanAttribute, Output, Input, ChangeDetectionStrategy, Component } from '@angular/core';
4
+ import { Directive, EventEmitter, Output, Input, ChangeDetectionStrategy, Component } from '@angular/core';
5
5
 
6
6
  /** Marks host content to place at the end of the bar (an export button, a view switch). */
7
7
  class TafFilterBarEndDirective {
@@ -20,8 +20,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImpor
20
20
  * key that moved plus `(valueChange)` with the whole record, so a host can keep either one signal per
21
21
  * filter or a single object — whichever it already has.
22
22
  *
23
- * Values by kind: `search`/`select`/`date` → string; `chips` → string[]; `date-range` →
24
- * `{ from: string; to: string }`; `toggle` → boolean.
23
+ * Values by kind: `search`/`select`/`date`/`segmented` → string; `chips` → string[]; `date-range` →
24
+ * `{ from: string; to: string }`; `toggle` → boolean. `segmented` is the single-select sibling of
25
+ * `chips`, for a status rail where exactly one choice is active.
25
26
  *
26
27
  * Ships NO theme — `taf-filter-bar__*` class hooks plus `data-kind` / `data-active`, and
27
28
  * `ViewEncapsulation.None` is deliberately NOT used here: the controls are plain elements the host
@@ -30,8 +31,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImpor
30
31
  class TafFilterBarComponent {
31
32
  filters = [];
32
33
  value = {};
33
- /** Show a Clear button once any filter is set. */
34
- showClear = true;
34
+ /**
35
+ * Whether the Clear button renders. Left unset it appears once any filter is set; set it
36
+ * explicitly to decide yourself — a host whose Clear also resets state the bar cannot see (tiles,
37
+ * a tree selection) needs that, or Clear would hide while those filters are still on.
38
+ */
39
+ showClear = null;
35
40
  clearLabel = 'Clear';
36
41
  /** Default debounce for `search` filters that do not set their own, in ms. */
37
42
  searchDebounce = 200;
@@ -46,7 +51,11 @@ class TafFilterBarComponent {
46
51
  if (this.timer)
47
52
  clearTimeout(this.timer);
48
53
  }
49
- /** True when anything is set what makes Clear appear. */
54
+ /** Clear renders when the host says so, or by default once anything is set. */
55
+ get clearVisible() {
56
+ return this.showClear ?? this.isDirty;
57
+ }
58
+ /** True when anything is set. */
50
59
  get isDirty() {
51
60
  return this.filters.some((f) => {
52
61
  const v = this.value[f.key];
@@ -67,6 +76,8 @@ class TafFilterBarComponent {
67
76
  return raw.map((o) => ({
68
77
  value: String(o[vk ?? 'value'] ?? ''),
69
78
  label: String(o[lk ?? 'label'] ?? o[vk ?? 'value'] ?? ''),
79
+ count: typeof o['count'] === 'number' ? o['count'] : undefined,
80
+ dot: typeof o['dot'] === 'string' ? o['dot'] : undefined,
70
81
  }));
71
82
  }
72
83
  asText(key) {
@@ -119,9 +130,16 @@ class TafFilterBarComponent {
119
130
  this.valueChange.emit(this.value);
120
131
  }
121
132
  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: `
133
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.1.5", type: TafFilterBarComponent, isStandalone: true, selector: "taf-filter-bar", inputs: { filters: "filters", value: "value", showClear: "showClear", clearLabel: "clearLabel", searchDebounce: "searchDebounce" }, outputs: { changed: "changed", valueChange: "valueChange", cleared: "cleared" }, ngImport: i0, template: `
123
134
  <div class="taf-filter-bar" role="search">
124
135
  <ng-container *ngFor="let f of filters; trackBy: trackKey">
136
+ <div
137
+ class="taf-filter-bar__group"
138
+ [attr.data-key]="f.key"
139
+ [attr.data-kind]="f.kind"
140
+ [attr.data-captioned]="f.caption ? '' : null"
141
+ >
142
+ <span class="taf-filter-bar__caption" *ngIf="f.caption">{{ f.caption }}</span>
125
143
  <ng-container [ngSwitch]="f.kind">
126
144
  <input
127
145
  *ngSwitchCase="'search'"
@@ -149,7 +167,7 @@ class TafFilterBarComponent {
149
167
  [value]="asText(f.key)"
150
168
  (change)="set(f.key, $any($event.target).value)"
151
169
  >
152
- <option value="" [selected]="!asText(f.key)">{{ f.label || 'All' }}</option>
170
+ <option *ngIf="f.allowEmpty !== false" value="" [selected]="!asText(f.key)">{{ f.label || 'All' }}</option>
153
171
  <option
154
172
  *ngFor="let o of options(f)"
155
173
  [value]="o.value"
@@ -166,7 +184,35 @@ class TafFilterBarComponent {
166
184
  [attr.aria-pressed]="isPicked(f.key, o.value)"
167
185
  [disabled]="!!f.disabled"
168
186
  (click)="toggleChip(f.key, o.value)"
169
- >{{ o.label }}</button>
187
+ >
188
+ <span class="taf-filter-bar__dot" *ngIf="o.dot" [style.background]="o.dot" aria-hidden="true"></span>
189
+ {{ o.label }}
190
+ <span class="taf-filter-bar__count" *ngIf="o.count !== undefined">{{ o.count }}</span>
191
+ </button>
192
+ </div>
193
+
194
+ <div
195
+ *ngSwitchCase="'segmented'"
196
+ class="taf-filter-bar__chips taf-filter-bar__chips--single"
197
+ data-kind="segmented"
198
+ role="radiogroup"
199
+ [attr.data-key]="f.key"
200
+ >
201
+ <button
202
+ *ngFor="let o of options(f)"
203
+ type="button"
204
+ role="radio"
205
+ class="taf-filter-bar__chip"
206
+ [attr.data-active]="asText(f.key) === o.value ? '' : null"
207
+ [attr.data-option]="o.value"
208
+ [attr.aria-checked]="asText(f.key) === o.value"
209
+ [disabled]="!!f.disabled"
210
+ (click)="set(f.key, o.value)"
211
+ >
212
+ <span class="taf-filter-bar__dot" *ngIf="o.dot" [style.background]="o.dot" aria-hidden="true"></span>
213
+ {{ o.label }}
214
+ <span class="taf-filter-bar__count" *ngIf="o.count !== undefined">{{ o.count }}</span>
215
+ </button>
170
216
  </div>
171
217
 
172
218
  <input
@@ -214,12 +260,13 @@ class TafFilterBarComponent {
214
260
  {{ f.label }}
215
261
  </label>
216
262
  </ng-container>
263
+ </div>
217
264
  </ng-container>
218
265
 
219
266
  <span class="taf-filter-bar__spacer"></span>
220
267
  <ng-content select="[tafFilterBarEnd]"></ng-content>
221
268
  <button
222
- *ngIf="showClear && isDirty"
269
+ *ngIf="clearVisible"
223
270
  type="button"
224
271
  class="taf-filter-bar__clear"
225
272
  (click)="clear()"
@@ -232,6 +279,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImpor
232
279
  args: [{ selector: 'taf-filter-bar', standalone: true, imports: [CommonModule], changeDetection: ChangeDetectionStrategy.OnPush, template: `
233
280
  <div class="taf-filter-bar" role="search">
234
281
  <ng-container *ngFor="let f of filters; trackBy: trackKey">
282
+ <div
283
+ class="taf-filter-bar__group"
284
+ [attr.data-key]="f.key"
285
+ [attr.data-kind]="f.kind"
286
+ [attr.data-captioned]="f.caption ? '' : null"
287
+ >
288
+ <span class="taf-filter-bar__caption" *ngIf="f.caption">{{ f.caption }}</span>
235
289
  <ng-container [ngSwitch]="f.kind">
236
290
  <input
237
291
  *ngSwitchCase="'search'"
@@ -259,7 +313,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImpor
259
313
  [value]="asText(f.key)"
260
314
  (change)="set(f.key, $any($event.target).value)"
261
315
  >
262
- <option value="" [selected]="!asText(f.key)">{{ f.label || 'All' }}</option>
316
+ <option *ngIf="f.allowEmpty !== false" value="" [selected]="!asText(f.key)">{{ f.label || 'All' }}</option>
263
317
  <option
264
318
  *ngFor="let o of options(f)"
265
319
  [value]="o.value"
@@ -276,7 +330,35 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImpor
276
330
  [attr.aria-pressed]="isPicked(f.key, o.value)"
277
331
  [disabled]="!!f.disabled"
278
332
  (click)="toggleChip(f.key, o.value)"
279
- >{{ o.label }}</button>
333
+ >
334
+ <span class="taf-filter-bar__dot" *ngIf="o.dot" [style.background]="o.dot" aria-hidden="true"></span>
335
+ {{ o.label }}
336
+ <span class="taf-filter-bar__count" *ngIf="o.count !== undefined">{{ o.count }}</span>
337
+ </button>
338
+ </div>
339
+
340
+ <div
341
+ *ngSwitchCase="'segmented'"
342
+ class="taf-filter-bar__chips taf-filter-bar__chips--single"
343
+ data-kind="segmented"
344
+ role="radiogroup"
345
+ [attr.data-key]="f.key"
346
+ >
347
+ <button
348
+ *ngFor="let o of options(f)"
349
+ type="button"
350
+ role="radio"
351
+ class="taf-filter-bar__chip"
352
+ [attr.data-active]="asText(f.key) === o.value ? '' : null"
353
+ [attr.data-option]="o.value"
354
+ [attr.aria-checked]="asText(f.key) === o.value"
355
+ [disabled]="!!f.disabled"
356
+ (click)="set(f.key, o.value)"
357
+ >
358
+ <span class="taf-filter-bar__dot" *ngIf="o.dot" [style.background]="o.dot" aria-hidden="true"></span>
359
+ {{ o.label }}
360
+ <span class="taf-filter-bar__count" *ngIf="o.count !== undefined">{{ o.count }}</span>
361
+ </button>
280
362
  </div>
281
363
 
282
364
  <input
@@ -324,12 +406,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImpor
324
406
  {{ f.label }}
325
407
  </label>
326
408
  </ng-container>
409
+ </div>
327
410
  </ng-container>
328
411
 
329
412
  <span class="taf-filter-bar__spacer"></span>
330
413
  <ng-content select="[tafFilterBarEnd]"></ng-content>
331
414
  <button
332
- *ngIf="showClear && isDirty"
415
+ *ngIf="clearVisible"
333
416
  type="button"
334
417
  class="taf-filter-bar__clear"
335
418
  (click)="clear()"
@@ -341,8 +424,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImpor
341
424
  }], value: [{
342
425
  type: Input
343
426
  }], showClear: [{
344
- type: Input,
345
- args: [{ transform: booleanAttribute }]
427
+ type: Input
346
428
  }], clearLabel: [{
347
429
  type: Input
348
430
  }], searchDebounce: [{
@@ -1 +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":"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"}
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' | 'segmented' | 'date' | 'date-range' | 'toggle';\n\n/** One entry in a select or chip group. */\nexport interface TafFilterOption {\n value: string;\n label: string;\n /** A live count beside the label — how many rows this choice would show. */\n count?: number;\n /** A colour for the chip's leading dot; the host theme decides what it means. */\n dot?: 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 /** A caption above the control, for a bar whose groups are labelled (DATE RANGE, STATUS, …). */\n caption?: string;\n /**\n * `select` only. False drops the injected \"nothing selected\" entry, for a preset picker whose\n * every option is a real value and which therefore always has one chosen.\n */\n allowEmpty?: 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`/`segmented` → string; `chips` → string[]; `date-range` →\n * `{ from: string; to: string }`; `toggle` → boolean. `segmented` is the single-select sibling of\n * `chips`, for a status rail where exactly one choice is active.\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 <div\n class=\"taf-filter-bar__group\"\n [attr.data-key]=\"f.key\"\n [attr.data-kind]=\"f.kind\"\n [attr.data-captioned]=\"f.caption ? '' : null\"\n >\n <span class=\"taf-filter-bar__caption\" *ngIf=\"f.caption\">{{ f.caption }}</span>\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 *ngIf=\"f.allowEmpty !== false\" 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 >\n <span class=\"taf-filter-bar__dot\" *ngIf=\"o.dot\" [style.background]=\"o.dot\" aria-hidden=\"true\"></span>\n {{ o.label }}\n <span class=\"taf-filter-bar__count\" *ngIf=\"o.count !== undefined\">{{ o.count }}</span>\n </button>\n </div>\n\n <div\n *ngSwitchCase=\"'segmented'\"\n class=\"taf-filter-bar__chips taf-filter-bar__chips--single\"\n data-kind=\"segmented\"\n role=\"radiogroup\"\n [attr.data-key]=\"f.key\"\n >\n <button\n *ngFor=\"let o of options(f)\"\n type=\"button\"\n role=\"radio\"\n class=\"taf-filter-bar__chip\"\n [attr.data-active]=\"asText(f.key) === o.value ? '' : null\"\n [attr.data-option]=\"o.value\"\n [attr.aria-checked]=\"asText(f.key) === o.value\"\n [disabled]=\"!!f.disabled\"\n (click)=\"set(f.key, o.value)\"\n >\n <span class=\"taf-filter-bar__dot\" *ngIf=\"o.dot\" [style.background]=\"o.dot\" aria-hidden=\"true\"></span>\n {{ o.label }}\n <span class=\"taf-filter-bar__count\" *ngIf=\"o.count !== undefined\">{{ o.count }}</span>\n </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 </div>\n </ng-container>\n\n <span class=\"taf-filter-bar__spacer\"></span>\n <ng-content select=\"[tafFilterBarEnd]\"></ng-content>\n <button\n *ngIf=\"clearVisible\"\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 /**\n * Whether the Clear button renders. Left unset it appears once any filter is set; set it\n * explicitly to decide yourself — a host whose Clear also resets state the bar cannot see (tiles,\n * a tree selection) needs that, or Clear would hide while those filters are still on.\n */\n @Input() showClear: boolean | null = null;\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 /** Clear renders when the host says so, or by default once anything is set. */\n get clearVisible(): boolean {\n return this.showClear ?? this.isDirty;\n }\n\n /** True when anything is set. */\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 count: typeof o['count'] === 'number' ? (o['count'] as number) : undefined,\n dot: typeof o['dot'] === 'string' ? (o['dot'] as string) : undefined,\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":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jigisha_ruparel/taf-client-filter-bar-angular",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "<taf-filter-bar> — the themeless row of list filters: search, selects, chip groups, date ranges and Clear.",
5
5
  "publishConfig": {
6
6
  "access": "public",
@@ -2,11 +2,15 @@ import * as i0 from '@angular/core';
2
2
  import { OnDestroy, EventEmitter } from '@angular/core';
3
3
 
4
4
  /** What a filter looks like on screen. */
5
- type TafFilterKind = 'search' | 'select' | 'chips' | 'date' | 'date-range' | 'toggle';
5
+ type TafFilterKind = 'search' | 'select' | 'chips' | 'segmented' | 'date' | 'date-range' | 'toggle';
6
6
  /** One entry in a select or chip group. */
7
7
  interface TafFilterOption {
8
8
  value: string;
9
9
  label: string;
10
+ /** A live count beside the label — how many rows this choice would show. */
11
+ count?: number;
12
+ /** A colour for the chip's leading dot; the host theme decides what it means. */
13
+ dot?: string;
10
14
  }
11
15
  /** One filter in the bar. `key` is what the value is stored and emitted under. */
12
16
  interface TafFilterSpec {
@@ -25,6 +29,13 @@ interface TafFilterSpec {
25
29
  /** Shown instead of `label` inside the control, when both make sense (e.g. a select's own title). */
26
30
  title?: string;
27
31
  disabled?: boolean;
32
+ /** A caption above the control, for a bar whose groups are labelled (DATE RANGE, STATUS, …). */
33
+ caption?: string;
34
+ /**
35
+ * `select` only. False drops the injected "nothing selected" entry, for a preset picker whose
36
+ * every option is a real value and which therefore always has one chosen.
37
+ */
38
+ allowEmpty?: boolean;
28
39
  }
29
40
  /** The bar's current state: one entry per filter key. */
30
41
  type TafFilterValue = Record<string, unknown>;
@@ -41,8 +52,9 @@ declare class TafFilterBarEndDirective {
41
52
  * key that moved plus `(valueChange)` with the whole record, so a host can keep either one signal per
42
53
  * filter or a single object — whichever it already has.
43
54
  *
44
- * Values by kind: `search`/`select`/`date` → string; `chips` → string[]; `date-range` →
45
- * `{ from: string; to: string }`; `toggle` → boolean.
55
+ * Values by kind: `search`/`select`/`date`/`segmented` → string; `chips` → string[]; `date-range` →
56
+ * `{ from: string; to: string }`; `toggle` → boolean. `segmented` is the single-select sibling of
57
+ * `chips`, for a status rail where exactly one choice is active.
46
58
  *
47
59
  * Ships NO theme — `taf-filter-bar__*` class hooks plus `data-kind` / `data-active`, and
48
60
  * `ViewEncapsulation.None` is deliberately NOT used here: the controls are plain elements the host
@@ -51,8 +63,12 @@ declare class TafFilterBarEndDirective {
51
63
  declare class TafFilterBarComponent implements OnDestroy {
52
64
  filters: readonly TafFilterSpec[];
53
65
  value: TafFilterValue;
54
- /** Show a Clear button once any filter is set. */
55
- showClear: boolean;
66
+ /**
67
+ * Whether the Clear button renders. Left unset it appears once any filter is set; set it
68
+ * explicitly to decide yourself — a host whose Clear also resets state the bar cannot see (tiles,
69
+ * a tree selection) needs that, or Clear would hide while those filters are still on.
70
+ */
71
+ showClear: boolean | null;
56
72
  clearLabel: string;
57
73
  /** Default debounce for `search` filters that do not set their own, in ms. */
58
74
  searchDebounce: number;
@@ -67,7 +83,9 @@ declare class TafFilterBarComponent implements OnDestroy {
67
83
  readonly cleared: EventEmitter<void>;
68
84
  private timer?;
69
85
  ngOnDestroy(): void;
70
- /** True when anything is set what makes Clear appear. */
86
+ /** Clear renders when the host says so, or by default once anything is set. */
87
+ get clearVisible(): boolean;
88
+ /** True when anything is set. */
71
89
  get isDirty(): boolean;
72
90
  protected trackKey: (_: number, f: TafFilterSpec) => string;
73
91
  protected options(f: TafFilterSpec): TafFilterOption[];
@@ -84,7 +102,6 @@ declare class TafFilterBarComponent implements OnDestroy {
84
102
  protected clear(): void;
85
103
  static ɵfac: i0.ɵɵFactoryDeclaration<TafFilterBarComponent, never>;
86
104
  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
105
  }
89
106
 
90
107
  export { TafFilterBarComponent, TafFilterBarEndDirective };