@memberjunction/ng-search 5.23.0 → 5.25.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.
Files changed (45) hide show
  1. package/dist/__tests__/search-results.component.test.d.ts +2 -0
  2. package/dist/__tests__/search-results.component.test.d.ts.map +1 -0
  3. package/dist/__tests__/search-results.component.test.js +263 -0
  4. package/dist/__tests__/search-results.component.test.js.map +1 -0
  5. package/dist/__tests__/search-types.test.js +6 -2
  6. package/dist/__tests__/search-types.test.js.map +1 -1
  7. package/dist/__tests__/search.service.test.js +167 -13
  8. package/dist/__tests__/search.service.test.js.map +1 -1
  9. package/dist/lib/module.d.ts +7 -4
  10. package/dist/lib/module.d.ts.map +1 -1
  11. package/dist/lib/module.js +19 -4
  12. package/dist/lib/module.js.map +1 -1
  13. package/dist/lib/search-composite.component.d.ts +79 -0
  14. package/dist/lib/search-composite.component.d.ts.map +1 -0
  15. package/dist/lib/search-composite.component.js +258 -0
  16. package/dist/lib/search-composite.component.js.map +1 -0
  17. package/dist/lib/search-filter.component.d.ts +19 -1
  18. package/dist/lib/search-filter.component.d.ts.map +1 -1
  19. package/dist/lib/search-filter.component.js +195 -44
  20. package/dist/lib/search-filter.component.js.map +1 -1
  21. package/dist/lib/search-input.component.d.ts +61 -0
  22. package/dist/lib/search-input.component.d.ts.map +1 -0
  23. package/dist/lib/search-input.component.js +192 -0
  24. package/dist/lib/search-input.component.js.map +1 -0
  25. package/dist/lib/search-overlay.component.js +2 -2
  26. package/dist/lib/search-results.component.d.ts +59 -7
  27. package/dist/lib/search-results.component.d.ts.map +1 -1
  28. package/dist/lib/search-results.component.js +530 -176
  29. package/dist/lib/search-results.component.js.map +1 -1
  30. package/dist/lib/search-suggest.component.d.ts +95 -0
  31. package/dist/lib/search-suggest.component.d.ts.map +1 -0
  32. package/dist/lib/search-suggest.component.js +491 -0
  33. package/dist/lib/search-suggest.component.js.map +1 -0
  34. package/dist/lib/search-types.d.ts +29 -1
  35. package/dist/lib/search-types.d.ts.map +1 -1
  36. package/dist/lib/search-types.js.map +1 -1
  37. package/dist/lib/search.service.d.ts +27 -5
  38. package/dist/lib/search.service.d.ts.map +1 -1
  39. package/dist/lib/search.service.js +222 -91
  40. package/dist/lib/search.service.js.map +1 -1
  41. package/dist/public-api.d.ts +3 -0
  42. package/dist/public-api.d.ts.map +1 -1
  43. package/dist/public-api.js +3 -0
  44. package/dist/public-api.js.map +1 -1
  45. package/package.json +6 -4
@@ -15,8 +15,16 @@ export declare class SearchFilterComponent {
15
15
  ActiveFilters: Record<string, string[]>;
16
16
  /** Whether the filter panel is collapsible */
17
17
  Collapsible: boolean;
18
+ /** Whether to show the min relevance slider */
19
+ ShowRelevanceSlider: boolean;
20
+ /** Current minimum score as a percentage (0-100) */
21
+ MinScorePercent: number;
22
+ /** The MinScore that was last sent to the server (for visual indicator) */
23
+ ServerMinScorePercent: number;
18
24
  FilterChanged: EventEmitter<SearchFilterChangeEvent>;
19
25
  FiltersCleared: EventEmitter<void>;
26
+ CloseRequested: EventEmitter<void>;
27
+ MinScoreChanged: EventEmitter<number>;
20
28
  /** Collapsed state per category */
21
29
  CollapsedCategories: Set<string>;
22
30
  /** Toggle a filter option */
@@ -33,7 +41,17 @@ export declare class SearchFilterComponent {
33
41
  GetActiveFilterCount(): number;
34
42
  /** Clear all filters */
35
43
  ClearAll(): void;
44
+ /** Select all options in a category, or clear all if already all selected */
45
+ SelectAll(category: string): void;
46
+ /** Check if all options in a category are selected */
47
+ IsAllSelected(category: string): boolean;
48
+ /** Handle relevance slider drag (preview only — updates display without triggering search) */
49
+ OnMinScorePreview(value: string): void;
50
+ /** Handle relevance slider release — commits the value and triggers re-query if needed */
51
+ OnMinScoreCommit(): void;
52
+ /** Handle relevance slider change (legacy — used when input event fires) */
53
+ OnMinScoreChange(value: string): void;
36
54
  static ɵfac: i0.ɵɵFactoryDeclaration<SearchFilterComponent, never>;
37
- static ɵcmp: i0.ɵɵComponentDeclaration<SearchFilterComponent, "mj-search-filter", never, { "Filters": { "alias": "Filters"; "required": false; }; "ActiveFilters": { "alias": "ActiveFilters"; "required": false; }; "Collapsible": { "alias": "Collapsible"; "required": false; }; }, { "FilterChanged": "FilterChanged"; "FiltersCleared": "FiltersCleared"; }, never, never, false, never>;
55
+ static ɵcmp: i0.ɵɵComponentDeclaration<SearchFilterComponent, "mj-search-filter", never, { "Filters": { "alias": "Filters"; "required": false; }; "ActiveFilters": { "alias": "ActiveFilters"; "required": false; }; "Collapsible": { "alias": "Collapsible"; "required": false; }; "ShowRelevanceSlider": { "alias": "ShowRelevanceSlider"; "required": false; }; "MinScorePercent": { "alias": "MinScorePercent"; "required": false; }; "ServerMinScorePercent": { "alias": "ServerMinScorePercent"; "required": false; }; }, { "FilterChanged": "FilterChanged"; "FiltersCleared": "FiltersCleared"; "CloseRequested": "CloseRequested"; "MinScoreChanged": "MinScoreChanged"; }, never, never, false, never>;
38
56
  }
39
57
  //# sourceMappingURL=search-filter.component.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"search-filter.component.d.ts","sourceRoot":"","sources":["../../src/lib/search-filter.component.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAEH,YAAY,EAKf,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,YAAY,EAAE,uBAAuB,EAAE,MAAM,gBAAgB,CAAC;;AAEvE,qBAMa,qBAAqB;IAC9B,OAAO,CAAC,GAAG,CAA6B;IAExC,mCAAmC;IAC1B,OAAO,EAAE,YAAY,EAAE,CAAM;IAEtC,yCAAyC;IAChC,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAM;IAEtD,8CAA8C;IACrC,WAAW,UAAQ;IAElB,aAAa,wCAA+C;IAC5D,cAAc,qBAA4B;IAEpD,mCAAmC;IAC5B,mBAAmB,cAAqB;IAE/C,6BAA6B;IACtB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAc1D,+BAA+B;IACxB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAU7C,uCAAuC;IAChC,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;IAIrD,qCAAqC;IAC9B,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO;IAIjE,sCAAsC;IAC/B,gBAAgB,IAAI,OAAO;IAIlC,oCAAoC;IAC7B,oBAAoB,IAAI,MAAM;IAIrC,wBAAwB;IACjB,QAAQ,IAAI,IAAI;yCAjEd,qBAAqB;2CAArB,qBAAqB;CAsEjC"}
1
+ {"version":3,"file":"search-filter.component.d.ts","sourceRoot":"","sources":["../../src/lib/search-filter.component.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAEH,YAAY,EAKf,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,YAAY,EAAE,uBAAuB,EAAE,MAAM,gBAAgB,CAAC;;AAEvE,qBAMa,qBAAqB;IAC9B,OAAO,CAAC,GAAG,CAA6B;IAExC,mCAAmC;IAC1B,OAAO,EAAE,YAAY,EAAE,CAAM;IAEtC,yCAAyC;IAChC,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAM;IAEtD,8CAA8C;IACrC,WAAW,UAAQ;IAE5B,+CAA+C;IACtC,mBAAmB,UAAQ;IAEpC,oDAAoD;IAC3C,eAAe,SAAK;IAE7B,2EAA2E;IAClE,qBAAqB,SAAK;IAEzB,aAAa,wCAA+C;IAC5D,cAAc,qBAA4B;IAC1C,cAAc,qBAA4B;IAC1C,eAAe,uBAA8B;IAEvD,mCAAmC;IAC5B,mBAAmB,cAAqB;IAE/C,6BAA6B;IACtB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAc1D,+BAA+B;IACxB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAU7C,uCAAuC;IAChC,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;IAIrD,qCAAqC;IAC9B,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO;IAIjE,sCAAsC;IAC/B,gBAAgB,IAAI,OAAO;IAIlC,oCAAoC;IAC7B,oBAAoB,IAAI,MAAM;IAIrC,wBAAwB;IACjB,QAAQ,IAAI,IAAI;IAMvB,6EAA6E;IACtE,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAgBxC,sDAAsD;IAC/C,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;IAO/C,8FAA8F;IACvF,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAI7C,0FAA0F;IACnF,gBAAgB,IAAI,IAAI;IAI/B,4EAA4E;IACrE,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;yCAtHnC,qBAAqB;2CAArB,qBAAqB;CA0HjC"}
@@ -8,10 +8,10 @@ import { Component, EventEmitter, Input, Output, ChangeDetectorRef, inject } fro
8
8
  import * as i0 from "@angular/core";
9
9
  const _forTrack0 = ($index, $item) => $item.Category;
10
10
  const _forTrack1 = ($index, $item) => $item.Value;
11
- function SearchFilterComponent_Conditional_5_Template(rf, ctx) { if (rf & 1) {
11
+ function SearchFilterComponent_Conditional_6_Template(rf, ctx) { if (rf & 1) {
12
12
  const _r1 = i0.ɵɵgetCurrentView();
13
- i0.ɵɵelementStart(0, "button", 7);
14
- i0.ɵɵlistener("click", function SearchFilterComponent_Conditional_5_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.ClearAll()); });
13
+ i0.ɵɵelementStart(0, "button", 10);
14
+ i0.ɵɵlistener("click", function SearchFilterComponent_Conditional_6_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.ClearAll()); });
15
15
  i0.ɵɵtext(1);
16
16
  i0.ɵɵelementEnd();
17
17
  } if (rf & 2) {
@@ -19,38 +19,75 @@ function SearchFilterComponent_Conditional_5_Template(rf, ctx) { if (rf & 1) {
19
19
  i0.ɵɵadvance();
20
20
  i0.ɵɵtextInterpolate1(" Clear (", ctx_r1.GetActiveFilterCount(), ") ");
21
21
  } }
22
- function SearchFilterComponent_For_7_Conditional_4_Template(rf, ctx) { if (rf & 1) {
23
- i0.ɵɵelement(0, "i", 12);
22
+ function SearchFilterComponent_For_10_Conditional_5_Template(rf, ctx) { if (rf & 1) {
23
+ i0.ɵɵelement(0, "i", 17);
24
24
  } if (rf & 2) {
25
25
  const filter_r4 = i0.ɵɵnextContext().$implicit;
26
26
  const ctx_r1 = i0.ɵɵnextContext();
27
27
  i0.ɵɵclassProp("fa-chevron-down", ctx_r1.IsCategoryCollapsed(filter_r4.Category))("fa-chevron-up", !ctx_r1.IsCategoryCollapsed(filter_r4.Category));
28
28
  } }
29
- function SearchFilterComponent_For_7_Conditional_5_For_2_Conditional_2_Template(rf, ctx) { if (rf & 1) {
30
- i0.ɵɵelement(0, "i", 16);
29
+ function SearchFilterComponent_For_10_Conditional_6_Template(rf, ctx) { if (rf & 1) {
30
+ const _r5 = i0.ɵɵgetCurrentView();
31
+ i0.ɵɵelementStart(0, "button", 18);
32
+ i0.ɵɵlistener("click", function SearchFilterComponent_For_10_Conditional_6_Template_button_click_0_listener($event) { i0.ɵɵrestoreView(_r5); const filter_r4 = i0.ɵɵnextContext().$implicit; const ctx_r1 = i0.ɵɵnextContext(); ctx_r1.SelectAll(filter_r4.Category); return i0.ɵɵresetView($event.stopPropagation()); });
33
+ i0.ɵɵtext(1);
34
+ i0.ɵɵelementEnd();
35
+ } if (rf & 2) {
36
+ const filter_r4 = i0.ɵɵnextContext().$implicit;
37
+ const ctx_r1 = i0.ɵɵnextContext();
38
+ i0.ɵɵproperty("title", ctx_r1.IsAllSelected(filter_r4.Category) ? "Clear all" : "Select all");
39
+ i0.ɵɵadvance();
40
+ i0.ɵɵtextInterpolate1(" ", ctx_r1.IsAllSelected(filter_r4.Category) ? "Clear" : "All", " ");
31
41
  } }
32
- function SearchFilterComponent_For_7_Conditional_5_For_2_Conditional_3_Template(rf, ctx) { if (rf & 1) {
42
+ function SearchFilterComponent_For_10_Conditional_7_For_2_Conditional_0_Conditional_1_Template(rf, ctx) { if (rf & 1) {
33
43
  i0.ɵɵelement(0, "i");
44
+ } if (rf & 2) {
45
+ const option_r6 = i0.ɵɵnextContext(2).$implicit;
46
+ i0.ɵɵclassMap(option_r6.Icon + " filter-option-icon");
47
+ } }
48
+ function SearchFilterComponent_For_10_Conditional_7_For_2_Conditional_0_Template(rf, ctx) { if (rf & 1) {
49
+ i0.ɵɵelementStart(0, "div", 19);
50
+ i0.ɵɵconditionalCreate(1, SearchFilterComponent_For_10_Conditional_7_For_2_Conditional_0_Conditional_1_Template, 1, 2, "i", 21);
51
+ i0.ɵɵelementStart(2, "span", 22);
52
+ i0.ɵɵtext(3);
53
+ i0.ɵɵelementEnd();
54
+ i0.ɵɵelementStart(4, "span", 23);
55
+ i0.ɵɵtext(5);
56
+ i0.ɵɵelementEnd()();
34
57
  } if (rf & 2) {
35
58
  const option_r6 = i0.ɵɵnextContext().$implicit;
59
+ i0.ɵɵadvance();
60
+ i0.ɵɵconditional(option_r6.Icon ? 1 : -1);
61
+ i0.ɵɵadvance(2);
62
+ i0.ɵɵtextInterpolate(option_r6.Label);
63
+ i0.ɵɵadvance(2);
64
+ i0.ɵɵtextInterpolate(option_r6.Count);
65
+ } }
66
+ function SearchFilterComponent_For_10_Conditional_7_For_2_Conditional_1_Conditional_2_Template(rf, ctx) { if (rf & 1) {
67
+ i0.ɵɵelement(0, "i", 26);
68
+ } }
69
+ function SearchFilterComponent_For_10_Conditional_7_For_2_Conditional_1_Conditional_3_Template(rf, ctx) { if (rf & 1) {
70
+ i0.ɵɵelement(0, "i");
71
+ } if (rf & 2) {
72
+ const option_r6 = i0.ɵɵnextContext(2).$implicit;
36
73
  i0.ɵɵclassMap(option_r6.Icon + " filter-option-icon");
37
74
  } }
38
- function SearchFilterComponent_For_7_Conditional_5_For_2_Template(rf, ctx) { if (rf & 1) {
39
- const _r5 = i0.ɵɵgetCurrentView();
40
- i0.ɵɵelementStart(0, "label", 14);
41
- i0.ɵɵlistener("click", function SearchFilterComponent_For_7_Conditional_5_For_2_Template_label_click_0_listener($event) { const option_r6 = i0.ɵɵrestoreView(_r5).$implicit; const filter_r4 = i0.ɵɵnextContext(2).$implicit; const ctx_r1 = i0.ɵɵnextContext(); ctx_r1.ToggleOption(filter_r4.Category, option_r6.Value); return i0.ɵɵresetView($event.preventDefault()); });
42
- i0.ɵɵelementStart(1, "span", 15);
43
- i0.ɵɵconditionalCreate(2, SearchFilterComponent_For_7_Conditional_5_For_2_Conditional_2_Template, 1, 0, "i", 16);
75
+ function SearchFilterComponent_For_10_Conditional_7_For_2_Conditional_1_Template(rf, ctx) { if (rf & 1) {
76
+ const _r7 = i0.ɵɵgetCurrentView();
77
+ i0.ɵɵelementStart(0, "label", 24);
78
+ i0.ɵɵlistener("click", function SearchFilterComponent_For_10_Conditional_7_For_2_Conditional_1_Template_label_click_0_listener($event) { i0.ɵɵrestoreView(_r7); const option_r6 = i0.ɵɵnextContext().$implicit; const filter_r4 = i0.ɵɵnextContext(2).$implicit; const ctx_r1 = i0.ɵɵnextContext(); ctx_r1.ToggleOption(filter_r4.Category, option_r6.Value); return i0.ɵɵresetView($event.preventDefault()); });
79
+ i0.ɵɵelementStart(1, "span", 25);
80
+ i0.ɵɵconditionalCreate(2, SearchFilterComponent_For_10_Conditional_7_For_2_Conditional_1_Conditional_2_Template, 1, 0, "i", 26);
44
81
  i0.ɵɵelementEnd();
45
- i0.ɵɵconditionalCreate(3, SearchFilterComponent_For_7_Conditional_5_For_2_Conditional_3_Template, 1, 2, "i", 17);
46
- i0.ɵɵelementStart(4, "span", 18);
82
+ i0.ɵɵconditionalCreate(3, SearchFilterComponent_For_10_Conditional_7_For_2_Conditional_1_Conditional_3_Template, 1, 2, "i", 21);
83
+ i0.ɵɵelementStart(4, "span", 22);
47
84
  i0.ɵɵtext(5);
48
85
  i0.ɵɵelementEnd();
49
- i0.ɵɵelementStart(6, "span", 19);
86
+ i0.ɵɵelementStart(6, "span", 23);
50
87
  i0.ɵɵtext(7);
51
88
  i0.ɵɵelementEnd()();
52
89
  } if (rf & 2) {
53
- const option_r6 = ctx.$implicit;
90
+ const option_r6 = i0.ɵɵnextContext().$implicit;
54
91
  const filter_r4 = i0.ɵɵnextContext(2).$implicit;
55
92
  const ctx_r1 = i0.ɵɵnextContext();
56
93
  i0.ɵɵadvance();
@@ -64,40 +101,91 @@ function SearchFilterComponent_For_7_Conditional_5_For_2_Template(rf, ctx) { if
64
101
  i0.ɵɵadvance(2);
65
102
  i0.ɵɵtextInterpolate(option_r6.Count);
66
103
  } }
67
- function SearchFilterComponent_For_7_Conditional_5_Template(rf, ctx) { if (rf & 1) {
68
- i0.ɵɵelementStart(0, "div", 11);
69
- i0.ɵɵrepeaterCreate(1, SearchFilterComponent_For_7_Conditional_5_For_2_Template, 8, 6, "label", 13, _forTrack1);
104
+ function SearchFilterComponent_For_10_Conditional_7_For_2_Template(rf, ctx) { if (rf & 1) {
105
+ i0.ɵɵconditionalCreate(0, SearchFilterComponent_For_10_Conditional_7_For_2_Conditional_0_Template, 6, 3, "div", 19)(1, SearchFilterComponent_For_10_Conditional_7_For_2_Conditional_1_Template, 8, 6, "label", 20);
106
+ } if (rf & 2) {
107
+ const filter_r4 = i0.ɵɵnextContext(2).$implicit;
108
+ i0.ɵɵconditional(filter_r4.Options.length === 1 ? 0 : 1);
109
+ } }
110
+ function SearchFilterComponent_For_10_Conditional_7_Template(rf, ctx) { if (rf & 1) {
111
+ i0.ɵɵelementStart(0, "div", 16);
112
+ i0.ɵɵrepeaterCreate(1, SearchFilterComponent_For_10_Conditional_7_For_2_Template, 2, 1, null, null, _forTrack1);
70
113
  i0.ɵɵelementEnd();
71
114
  } if (rf & 2) {
72
115
  const filter_r4 = i0.ɵɵnextContext().$implicit;
73
116
  i0.ɵɵadvance();
74
117
  i0.ɵɵrepeater(filter_r4.Options);
75
118
  } }
76
- function SearchFilterComponent_For_7_Template(rf, ctx) { if (rf & 1) {
119
+ function SearchFilterComponent_For_10_Template(rf, ctx) { if (rf & 1) {
77
120
  const _r3 = i0.ɵɵgetCurrentView();
78
- i0.ɵɵelementStart(0, "div", 5)(1, "div", 8);
79
- i0.ɵɵlistener("click", function SearchFilterComponent_For_7_Template_div_click_1_listener() { const filter_r4 = i0.ɵɵrestoreView(_r3).$implicit; const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.ToggleCategory(filter_r4.Category)); });
80
- i0.ɵɵelementStart(2, "span", 9);
81
- i0.ɵɵtext(3);
121
+ i0.ɵɵelementStart(0, "div", 8)(1, "div", 11)(2, "div", 12);
122
+ i0.ɵɵlistener("click", function SearchFilterComponent_For_10_Template_div_click_2_listener() { const filter_r4 = i0.ɵɵrestoreView(_r3).$implicit; const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.ToggleCategory(filter_r4.Category)); });
123
+ i0.ɵɵelementStart(3, "span", 13);
124
+ i0.ɵɵtext(4);
125
+ i0.ɵɵelementEnd();
126
+ i0.ɵɵconditionalCreate(5, SearchFilterComponent_For_10_Conditional_5_Template, 1, 4, "i", 14);
82
127
  i0.ɵɵelementEnd();
83
- i0.ɵɵconditionalCreate(4, SearchFilterComponent_For_7_Conditional_4_Template, 1, 4, "i", 10);
128
+ i0.ɵɵconditionalCreate(6, SearchFilterComponent_For_10_Conditional_6_Template, 2, 2, "button", 15);
84
129
  i0.ɵɵelementEnd();
85
- i0.ɵɵconditionalCreate(5, SearchFilterComponent_For_7_Conditional_5_Template, 3, 0, "div", 11);
130
+ i0.ɵɵconditionalCreate(7, SearchFilterComponent_For_10_Conditional_7_Template, 3, 0, "div", 16);
86
131
  i0.ɵɵelementEnd();
87
132
  } if (rf & 2) {
88
133
  const filter_r4 = ctx.$implicit;
89
134
  const ctx_r1 = i0.ɵɵnextContext();
90
- i0.ɵɵadvance();
135
+ i0.ɵɵadvance(2);
91
136
  i0.ɵɵclassProp("filter-category-collapsed", ctx_r1.IsCategoryCollapsed(filter_r4.Category));
92
137
  i0.ɵɵadvance(2);
93
138
  i0.ɵɵtextInterpolate(filter_r4.Category);
94
139
  i0.ɵɵadvance();
95
- i0.ɵɵconditional(ctx_r1.Collapsible ? 4 : -1);
140
+ i0.ɵɵconditional(ctx_r1.Collapsible ? 5 : -1);
96
141
  i0.ɵɵadvance();
97
- i0.ɵɵconditional(!ctx_r1.IsCategoryCollapsed(filter_r4.Category) ? 5 : -1);
142
+ i0.ɵɵconditional(!ctx_r1.IsCategoryCollapsed(filter_r4.Category) && filter_r4.Options.length > 1 ? 6 : -1);
143
+ i0.ɵɵadvance();
144
+ i0.ɵɵconditional(!ctx_r1.IsCategoryCollapsed(filter_r4.Category) ? 7 : -1);
98
145
  } }
99
- function SearchFilterComponent_Conditional_8_Template(rf, ctx) { if (rf & 1) {
100
- i0.ɵɵelementStart(0, "div", 6)(1, "p");
146
+ function SearchFilterComponent_Conditional_11_Conditional_9_Conditional_3_Template(rf, ctx) { if (rf & 1) {
147
+ i0.ɵɵelementStart(0, "span", 33);
148
+ i0.ɵɵelement(1, "i", 34);
149
+ i0.ɵɵtext(2, " Will re-query ");
150
+ i0.ɵɵelementEnd();
151
+ } }
152
+ function SearchFilterComponent_Conditional_11_Conditional_9_Template(rf, ctx) { if (rf & 1) {
153
+ i0.ɵɵelementStart(0, "div", 31)(1, "span", 32);
154
+ i0.ɵɵtext(2);
155
+ i0.ɵɵelementEnd();
156
+ i0.ɵɵconditionalCreate(3, SearchFilterComponent_Conditional_11_Conditional_9_Conditional_3_Template, 3, 0, "span", 33);
157
+ i0.ɵɵelementEnd();
158
+ } if (rf & 2) {
159
+ const ctx_r1 = i0.ɵɵnextContext(2);
160
+ i0.ɵɵadvance(2);
161
+ i0.ɵɵtextInterpolate1("Server threshold: ", ctx_r1.ServerMinScorePercent, "%");
162
+ i0.ɵɵadvance();
163
+ i0.ɵɵconditional(ctx_r1.MinScorePercent < ctx_r1.ServerMinScorePercent ? 3 : -1);
164
+ } }
165
+ function SearchFilterComponent_Conditional_11_Template(rf, ctx) { if (rf & 1) {
166
+ const _r8 = i0.ɵɵgetCurrentView();
167
+ i0.ɵɵelementStart(0, "div", 8)(1, "div", 11)(2, "div", 27)(3, "span", 13);
168
+ i0.ɵɵtext(4, "Min Relevance");
169
+ i0.ɵɵelementEnd()()();
170
+ i0.ɵɵelementStart(5, "div", 28)(6, "input", 29);
171
+ i0.ɵɵlistener("input", function SearchFilterComponent_Conditional_11_Template_input_input_6_listener($event) { i0.ɵɵrestoreView(_r8); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.OnMinScorePreview($event.target.value)); })("mouseup", function SearchFilterComponent_Conditional_11_Template_input_mouseup_6_listener() { i0.ɵɵrestoreView(_r8); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.OnMinScoreCommit()); })("touchend", function SearchFilterComponent_Conditional_11_Template_input_touchend_6_listener() { i0.ɵɵrestoreView(_r8); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.OnMinScoreCommit()); });
172
+ i0.ɵɵelementEnd();
173
+ i0.ɵɵelementStart(7, "span", 30);
174
+ i0.ɵɵtext(8);
175
+ i0.ɵɵelementEnd()();
176
+ i0.ɵɵconditionalCreate(9, SearchFilterComponent_Conditional_11_Conditional_9_Template, 4, 2, "div", 31);
177
+ i0.ɵɵelementEnd();
178
+ } if (rf & 2) {
179
+ const ctx_r1 = i0.ɵɵnextContext();
180
+ i0.ɵɵadvance(6);
181
+ i0.ɵɵproperty("value", ctx_r1.MinScorePercent);
182
+ i0.ɵɵadvance(2);
183
+ i0.ɵɵtextInterpolate1("", ctx_r1.MinScorePercent, "%");
184
+ i0.ɵɵadvance();
185
+ i0.ɵɵconditional(ctx_r1.ServerMinScorePercent > 0 ? 9 : -1);
186
+ } }
187
+ function SearchFilterComponent_Conditional_12_Template(rf, ctx) { if (rf & 1) {
188
+ i0.ɵɵelementStart(0, "div", 9)(1, "p");
101
189
  i0.ɵɵtext(2, "No filters available");
102
190
  i0.ɵɵelementEnd()();
103
191
  } }
@@ -109,8 +197,16 @@ export class SearchFilterComponent {
109
197
  ActiveFilters = {};
110
198
  /** Whether the filter panel is collapsible */
111
199
  Collapsible = true;
200
+ /** Whether to show the min relevance slider */
201
+ ShowRelevanceSlider = true;
202
+ /** Current minimum score as a percentage (0-100) */
203
+ MinScorePercent = 0;
204
+ /** The MinScore that was last sent to the server (for visual indicator) */
205
+ ServerMinScorePercent = 0;
112
206
  FilterChanged = new EventEmitter();
113
207
  FiltersCleared = new EventEmitter();
208
+ CloseRequested = new EventEmitter();
209
+ MinScoreChanged = new EventEmitter();
114
210
  /** Collapsed state per category */
115
211
  CollapsedCategories = new Set();
116
212
  /** Toggle a filter option */
@@ -162,39 +258,94 @@ export class SearchFilterComponent {
162
258
  this.FiltersCleared.emit();
163
259
  this.cdr.detectChanges();
164
260
  }
261
+ /** Select all options in a category, or clear all if already all selected */
262
+ SelectAll(category) {
263
+ const filter = this.Filters.find(f => f.Category === category);
264
+ if (!filter)
265
+ return;
266
+ const current = this.ActiveFilters[category] ?? [];
267
+ if (current.length === filter.Options.length) {
268
+ // All selected → clear
269
+ this.ActiveFilters[category] = [];
270
+ }
271
+ else {
272
+ // Select all
273
+ this.ActiveFilters[category] = filter.Options.map(o => o.Value);
274
+ }
275
+ this.FilterChanged.emit({ Category: category, SelectedValues: this.ActiveFilters[category] });
276
+ this.cdr.detectChanges();
277
+ }
278
+ /** Check if all options in a category are selected */
279
+ IsAllSelected(category) {
280
+ const filter = this.Filters.find(f => f.Category === category);
281
+ if (!filter)
282
+ return false;
283
+ const selection = this.ActiveFilters[category] ?? [];
284
+ return selection.length === filter.Options.length;
285
+ }
286
+ /** Handle relevance slider drag (preview only — updates display without triggering search) */
287
+ OnMinScorePreview(value) {
288
+ this.MinScorePercent = parseInt(value, 10);
289
+ }
290
+ /** Handle relevance slider release — commits the value and triggers re-query if needed */
291
+ OnMinScoreCommit() {
292
+ this.MinScoreChanged.emit(this.MinScorePercent);
293
+ }
294
+ /** Handle relevance slider change (legacy — used when input event fires) */
295
+ OnMinScoreChange(value) {
296
+ this.MinScorePercent = parseInt(value, 10);
297
+ this.MinScoreChanged.emit(this.MinScorePercent);
298
+ }
165
299
  static ɵfac = function SearchFilterComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || SearchFilterComponent)(); };
166
- static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: SearchFilterComponent, selectors: [["mj-search-filter"]], inputs: { Filters: "Filters", ActiveFilters: "ActiveFilters", Collapsible: "Collapsible" }, outputs: { FilterChanged: "FilterChanged", FiltersCleared: "FiltersCleared" }, standalone: false, decls: 9, vars: 2, consts: [[1, "filter-panel"], [1, "filter-panel-header"], [1, "filter-panel-title"], [1, "fa-solid", "fa-filter"], [1, "filter-clear-all"], [1, "filter-category"], [1, "filter-empty"], [1, "filter-clear-all", 3, "click"], [1, "filter-category-header", 3, "click"], [1, "filter-category-name"], [1, "fa-solid", "filter-category-chevron", 3, "fa-chevron-down", "fa-chevron-up"], [1, "filter-options"], [1, "fa-solid", "filter-category-chevron"], [1, "filter-option"], [1, "filter-option", 3, "click"], [1, "filter-checkbox"], [1, "fa-solid", "fa-check"], [3, "class"], [1, "filter-option-label"], [1, "filter-option-count"]], template: function SearchFilterComponent_Template(rf, ctx) { if (rf & 1) {
300
+ static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: SearchFilterComponent, selectors: [["mj-search-filter"]], inputs: { Filters: "Filters", ActiveFilters: "ActiveFilters", Collapsible: "Collapsible", ShowRelevanceSlider: "ShowRelevanceSlider", MinScorePercent: "MinScorePercent", ServerMinScorePercent: "ServerMinScorePercent" }, outputs: { FilterChanged: "FilterChanged", FiltersCleared: "FiltersCleared", CloseRequested: "CloseRequested", MinScoreChanged: "MinScoreChanged" }, standalone: false, decls: 13, vars: 3, consts: [[1, "filter-panel"], [1, "filter-panel-header"], [1, "filter-panel-title"], [1, "fa-solid", "fa-filter"], [1, "filter-header-actions"], [1, "filter-clear-all"], ["title", "Hide filters", 1, "filter-close-btn", 3, "click"], [1, "fa-solid", "fa-times"], [1, "filter-category"], [1, "filter-empty"], [1, "filter-clear-all", 3, "click"], [1, "filter-category-header-row"], [1, "filter-category-header", 3, "click"], [1, "filter-category-name"], [1, "fa-solid", "filter-category-chevron", 3, "fa-chevron-down", "fa-chevron-up"], [1, "filter-select-all", 3, "title"], [1, "filter-options"], [1, "fa-solid", "filter-category-chevron"], [1, "filter-select-all", 3, "click", "title"], [1, "filter-option", "filter-option-readonly"], [1, "filter-option"], [3, "class"], [1, "filter-option-label"], [1, "filter-option-count"], [1, "filter-option", 3, "click"], [1, "filter-checkbox"], [1, "fa-solid", "fa-check"], [1, "filter-category-header"], [1, "filter-relevance-slider"], ["type", "range", "min", "0", "max", "100", "step", "5", 1, "relevance-range", 3, "input", "mouseup", "touchend", "value"], [1, "relevance-value"], [1, "filter-server-threshold"], [1, "server-threshold-label"], [1, "server-threshold-warning"], [1, "fa-solid", "fa-arrow-rotate-right"]], template: function SearchFilterComponent_Template(rf, ctx) { if (rf & 1) {
167
301
  i0.ɵɵelementStart(0, "div", 0)(1, "div", 1)(2, "span", 2);
168
302
  i0.ɵɵelement(3, "i", 3);
169
303
  i0.ɵɵtext(4, " Filters ");
170
304
  i0.ɵɵelementEnd();
171
- i0.ɵɵconditionalCreate(5, SearchFilterComponent_Conditional_5_Template, 2, 1, "button", 4);
172
- i0.ɵɵelementEnd();
173
- i0.ɵɵrepeaterCreate(6, SearchFilterComponent_For_7_Template, 6, 5, "div", 5, _forTrack0);
174
- i0.ɵɵconditionalCreate(8, SearchFilterComponent_Conditional_8_Template, 3, 0, "div", 6);
305
+ i0.ɵɵelementStart(5, "div", 4);
306
+ i0.ɵɵconditionalCreate(6, SearchFilterComponent_Conditional_6_Template, 2, 1, "button", 5);
307
+ i0.ɵɵelementStart(7, "button", 6);
308
+ i0.ɵɵlistener("click", function SearchFilterComponent_Template_button_click_7_listener() { return ctx.CloseRequested.emit(); });
309
+ i0.ɵɵelement(8, "i", 7);
310
+ i0.ɵɵelementEnd()()();
311
+ i0.ɵɵrepeaterCreate(9, SearchFilterComponent_For_10_Template, 8, 6, "div", 8, _forTrack0);
312
+ i0.ɵɵconditionalCreate(11, SearchFilterComponent_Conditional_11_Template, 10, 3, "div", 8);
313
+ i0.ɵɵconditionalCreate(12, SearchFilterComponent_Conditional_12_Template, 3, 0, "div", 9);
175
314
  i0.ɵɵelementEnd();
176
315
  } if (rf & 2) {
177
- i0.ɵɵadvance(5);
178
- i0.ɵɵconditional(ctx.HasActiveFilters() ? 5 : -1);
179
- i0.ɵɵadvance();
316
+ i0.ɵɵadvance(6);
317
+ i0.ɵɵconditional(ctx.HasActiveFilters() ? 6 : -1);
318
+ i0.ɵɵadvance(3);
180
319
  i0.ɵɵrepeater(ctx.Filters);
181
320
  i0.ɵɵadvance(2);
182
- i0.ɵɵconditional(ctx.Filters.length === 0 ? 8 : -1);
183
- } }, styles: ["\n\n\n.filter-panel[_ngcontent-%COMP%] {\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n background: var(--mj-bg-surface);\n overflow: hidden;\n}\n\n.filter-panel-header[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 0.75rem 1rem;\n border-bottom: 1px solid var(--mj-border-subtle);\n}\n\n.filter-panel-title[_ngcontent-%COMP%] {\n font-weight: 600;\n font-size: 0.85rem;\n color: var(--mj-text-primary);\n display: flex;\n align-items: center;\n gap: 0.5rem;\n}\n\n.filter-clear-all[_ngcontent-%COMP%] {\n border: none;\n background: none;\n color: var(--mj-brand-primary);\n font-size: 0.78rem;\n cursor: pointer;\n padding: 0.15rem 0.4rem;\n border-radius: 4px;\n}\n\n.filter-clear-all[_ngcontent-%COMP%]:hover {\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n}\n\n\n\n.filter-category[_ngcontent-%COMP%] {\n border-bottom: 1px solid var(--mj-border-subtle);\n}\n\n.filter-category[_ngcontent-%COMP%]:last-child {\n border-bottom: none;\n}\n\n.filter-category-header[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 0.6rem 1rem;\n cursor: pointer;\n transition: background 0.1s ease;\n}\n\n.filter-category-header[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-hover);\n}\n\n.filter-category-name[_ngcontent-%COMP%] {\n font-weight: 500;\n font-size: 0.82rem;\n color: var(--mj-text-primary);\n}\n\n.filter-category-chevron[_ngcontent-%COMP%] {\n font-size: 0.65rem;\n color: var(--mj-text-muted);\n}\n\n\n\n.filter-options[_ngcontent-%COMP%] {\n padding: 0.25rem 0;\n}\n\n.filter-option[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n padding: 0.4rem 1rem;\n cursor: pointer;\n transition: background 0.1s ease;\n font-size: 0.82rem;\n}\n\n.filter-option[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-hover);\n}\n\n.filter-checkbox[_ngcontent-%COMP%] {\n width: 1rem;\n height: 1rem;\n border: 1.5px solid var(--mj-border-strong);\n border-radius: 3px;\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n font-size: 0.6rem;\n transition: all 0.15s ease;\n}\n\n.filter-checkbox-checked[_ngcontent-%COMP%] {\n background: var(--mj-brand-primary);\n border-color: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n}\n\n.filter-option-icon[_ngcontent-%COMP%] {\n color: var(--mj-text-muted);\n font-size: 0.78rem;\n width: 1rem;\n text-align: center;\n}\n\n.filter-option-label[_ngcontent-%COMP%] {\n flex: 1;\n color: var(--mj-text-secondary);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.filter-option-count[_ngcontent-%COMP%] {\n color: var(--mj-text-muted);\n font-size: 0.72rem;\n flex-shrink: 0;\n}\n\n\n\n.filter-empty[_ngcontent-%COMP%] {\n padding: 1.5rem 1rem;\n text-align: center;\n color: var(--mj-text-muted);\n font-size: 0.82rem;\n}"] });
321
+ i0.ɵɵconditional(ctx.ShowRelevanceSlider ? 11 : -1);
322
+ i0.ɵɵadvance();
323
+ i0.ɵɵconditional(ctx.Filters.length === 0 && !ctx.ShowRelevanceSlider ? 12 : -1);
324
+ } }, styles: ["\n\n\n.filter-panel[_ngcontent-%COMP%] {\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n background: var(--mj-bg-surface);\n overflow: hidden;\n}\n\n.filter-panel-header[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 0.75rem 1rem;\n border-bottom: 1px solid var(--mj-border-subtle);\n}\n\n.filter-panel-title[_ngcontent-%COMP%] {\n font-weight: 600;\n font-size: 0.85rem;\n color: var(--mj-text-primary);\n display: flex;\n align-items: center;\n gap: 0.5rem;\n}\n\n.filter-clear-all[_ngcontent-%COMP%] {\n border: none;\n background: none;\n color: var(--mj-brand-primary);\n font-size: 0.78rem;\n cursor: pointer;\n padding: 0.15rem 0.4rem;\n border-radius: 4px;\n}\n\n.filter-clear-all[_ngcontent-%COMP%]:hover {\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n}\n\n.filter-header-actions[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 6px;\n}\n\n.filter-close-btn[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 24px;\n height: 24px;\n border: none;\n border-radius: 4px;\n background: none;\n color: var(--mj-text-muted);\n font-size: 0.75rem;\n cursor: pointer;\n transition: all 0.15s;\n}\n\n.filter-close-btn[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-primary);\n}\n\n\n\n.filter-category[_ngcontent-%COMP%] {\n border-bottom: 1px solid var(--mj-border-subtle);\n}\n\n.filter-category[_ngcontent-%COMP%]:last-child {\n border-bottom: none;\n}\n\n.filter-category-header-row[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n}\n\n.filter-category-header[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 0.6rem 1rem;\n cursor: pointer;\n transition: background 0.1s ease;\n flex: 1;\n}\n\n.filter-category-header[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-hover);\n}\n\n.filter-select-all[_ngcontent-%COMP%] {\n padding: 2px 8px;\n border: 1px solid var(--mj-border-default);\n border-radius: 4px;\n background: none;\n color: var(--mj-text-muted);\n font-size: 0.65rem;\n font-weight: 500;\n cursor: pointer;\n margin-right: 8px;\n transition: color 0.15s ease, border-color 0.15s ease;\n}\n\n.filter-select-all[_ngcontent-%COMP%]:hover {\n color: var(--mj-brand-primary);\n border-color: var(--mj-brand-primary);\n}\n\n.filter-category-name[_ngcontent-%COMP%] {\n font-weight: 500;\n font-size: 0.82rem;\n color: var(--mj-text-primary);\n}\n\n.filter-category-chevron[_ngcontent-%COMP%] {\n font-size: 0.65rem;\n color: var(--mj-text-muted);\n}\n\n\n\n.filter-options[_ngcontent-%COMP%] {\n padding: 0.25rem 0;\n}\n\n.filter-option[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n padding: 0.4rem 1rem;\n cursor: pointer;\n transition: background 0.1s ease;\n font-size: 0.82rem;\n}\n\n.filter-option[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-hover);\n}\n\n.filter-option-readonly[_ngcontent-%COMP%] {\n cursor: default;\n opacity: 0.85;\n}\n.filter-option-readonly[_ngcontent-%COMP%]:hover {\n background: transparent;\n}\n\n.filter-checkbox[_ngcontent-%COMP%] {\n width: 1rem;\n height: 1rem;\n border: 1.5px solid var(--mj-border-strong);\n border-radius: 3px;\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n font-size: 0.6rem;\n transition: all 0.15s ease;\n}\n\n.filter-checkbox-checked[_ngcontent-%COMP%] {\n background: var(--mj-brand-primary);\n border-color: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n}\n\n.filter-option-icon[_ngcontent-%COMP%] {\n color: var(--mj-text-muted);\n font-size: 0.78rem;\n width: 1rem;\n text-align: center;\n}\n\n.filter-option-label[_ngcontent-%COMP%] {\n flex: 1;\n color: var(--mj-text-secondary);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.filter-option-count[_ngcontent-%COMP%] {\n color: var(--mj-text-muted);\n font-size: 0.72rem;\n flex-shrink: 0;\n}\n\n\n\n.filter-empty[_ngcontent-%COMP%] {\n padding: 1.5rem 1rem;\n text-align: center;\n color: var(--mj-text-muted);\n font-size: 0.82rem;\n}\n\n\n\n.filter-relevance-slider[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 8px 12px;\n}\n.relevance-range[_ngcontent-%COMP%] {\n flex: 1;\n height: 4px;\n -webkit-appearance: none;\n appearance: none;\n background: var(--mj-border-default);\n border-radius: 2px;\n outline: none;\n cursor: pointer;\n}\n.relevance-range[_ngcontent-%COMP%]::-webkit-slider-thumb {\n -webkit-appearance: none;\n width: 16px;\n height: 16px;\n border-radius: 50%;\n background: var(--mj-brand-primary);\n cursor: pointer;\n border: 2px solid var(--mj-bg-surface);\n box-shadow: 0 1px 3px rgba(0,0,0,0.2);\n}\n.relevance-range[_ngcontent-%COMP%]::-moz-range-thumb {\n width: 16px;\n height: 16px;\n border-radius: 50%;\n background: var(--mj-brand-primary);\n cursor: pointer;\n border: 2px solid var(--mj-bg-surface);\n box-shadow: 0 1px 3px rgba(0,0,0,0.2);\n}\n.relevance-value[_ngcontent-%COMP%] {\n font-size: 12px;\n font-weight: 600;\n color: var(--mj-brand-primary);\n min-width: 36px;\n text-align: right;\n}\n\n\n\n.filter-server-threshold[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 2px 12px 6px;\n font-size: 10px;\n color: var(--mj-text-muted);\n}\n.server-threshold-warning[_ngcontent-%COMP%] {\n color: var(--mj-status-warning-text, var(--mj-status-warning));\n font-weight: 600;\n}"] });
184
325
  }
185
326
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(SearchFilterComponent, [{
186
327
  type: Component,
187
- args: [{ standalone: false, selector: 'mj-search-filter', template: "<div class=\"filter-panel\">\n <div class=\"filter-panel-header\">\n <span class=\"filter-panel-title\">\n <i class=\"fa-solid fa-filter\"></i> Filters\n </span>\n @if (HasActiveFilters()) {\n <button class=\"filter-clear-all\" (click)=\"ClearAll()\">\n Clear ({{ GetActiveFilterCount() }})\n </button>\n }\n </div>\n\n @for (filter of Filters; track filter.Category) {\n <div class=\"filter-category\">\n <div\n class=\"filter-category-header\"\n (click)=\"ToggleCategory(filter.Category)\"\n [class.filter-category-collapsed]=\"IsCategoryCollapsed(filter.Category)\"\n >\n <span class=\"filter-category-name\">{{ filter.Category }}</span>\n @if (Collapsible) {\n <i class=\"fa-solid filter-category-chevron\"\n [class.fa-chevron-down]=\"IsCategoryCollapsed(filter.Category)\"\n [class.fa-chevron-up]=\"!IsCategoryCollapsed(filter.Category)\"></i>\n }\n </div>\n\n @if (!IsCategoryCollapsed(filter.Category)) {\n <div class=\"filter-options\">\n @for (option of filter.Options; track option.Value) {\n <label class=\"filter-option\" (click)=\"ToggleOption(filter.Category, option.Value); $event.preventDefault()\">\n <span class=\"filter-checkbox\" [class.filter-checkbox-checked]=\"IsOptionSelected(filter.Category, option.Value)\">\n @if (IsOptionSelected(filter.Category, option.Value)) {\n <i class=\"fa-solid fa-check\"></i>\n }\n </span>\n @if (option.Icon) {\n <i [class]=\"option.Icon + ' filter-option-icon'\"></i>\n }\n <span class=\"filter-option-label\">{{ option.Label }}</span>\n <span class=\"filter-option-count\">{{ option.Count }}</span>\n </label>\n }\n </div>\n }\n </div>\n }\n\n @if (Filters.length === 0) {\n <div class=\"filter-empty\">\n <p>No filters available</p>\n </div>\n }\n</div>\n", styles: ["/* Search Filter Panel */\n\n.filter-panel {\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n background: var(--mj-bg-surface);\n overflow: hidden;\n}\n\n.filter-panel-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 0.75rem 1rem;\n border-bottom: 1px solid var(--mj-border-subtle);\n}\n\n.filter-panel-title {\n font-weight: 600;\n font-size: 0.85rem;\n color: var(--mj-text-primary);\n display: flex;\n align-items: center;\n gap: 0.5rem;\n}\n\n.filter-clear-all {\n border: none;\n background: none;\n color: var(--mj-brand-primary);\n font-size: 0.78rem;\n cursor: pointer;\n padding: 0.15rem 0.4rem;\n border-radius: 4px;\n}\n\n.filter-clear-all:hover {\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n}\n\n/* Category */\n.filter-category {\n border-bottom: 1px solid var(--mj-border-subtle);\n}\n\n.filter-category:last-child {\n border-bottom: none;\n}\n\n.filter-category-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 0.6rem 1rem;\n cursor: pointer;\n transition: background 0.1s ease;\n}\n\n.filter-category-header:hover {\n background: var(--mj-bg-surface-hover);\n}\n\n.filter-category-name {\n font-weight: 500;\n font-size: 0.82rem;\n color: var(--mj-text-primary);\n}\n\n.filter-category-chevron {\n font-size: 0.65rem;\n color: var(--mj-text-muted);\n}\n\n/* Options */\n.filter-options {\n padding: 0.25rem 0;\n}\n\n.filter-option {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n padding: 0.4rem 1rem;\n cursor: pointer;\n transition: background 0.1s ease;\n font-size: 0.82rem;\n}\n\n.filter-option:hover {\n background: var(--mj-bg-surface-hover);\n}\n\n.filter-checkbox {\n width: 1rem;\n height: 1rem;\n border: 1.5px solid var(--mj-border-strong);\n border-radius: 3px;\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n font-size: 0.6rem;\n transition: all 0.15s ease;\n}\n\n.filter-checkbox-checked {\n background: var(--mj-brand-primary);\n border-color: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n}\n\n.filter-option-icon {\n color: var(--mj-text-muted);\n font-size: 0.78rem;\n width: 1rem;\n text-align: center;\n}\n\n.filter-option-label {\n flex: 1;\n color: var(--mj-text-secondary);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.filter-option-count {\n color: var(--mj-text-muted);\n font-size: 0.72rem;\n flex-shrink: 0;\n}\n\n/* Empty state */\n.filter-empty {\n padding: 1.5rem 1rem;\n text-align: center;\n color: var(--mj-text-muted);\n font-size: 0.82rem;\n}\n"] }]
328
+ args: [{ standalone: false, selector: 'mj-search-filter', template: "<div class=\"filter-panel\">\n <div class=\"filter-panel-header\">\n <span class=\"filter-panel-title\">\n <i class=\"fa-solid fa-filter\"></i> Filters\n </span>\n <div class=\"filter-header-actions\">\n @if (HasActiveFilters()) {\n <button class=\"filter-clear-all\" (click)=\"ClearAll()\">\n Clear ({{ GetActiveFilterCount() }})\n </button>\n }\n <button class=\"filter-close-btn\" (click)=\"CloseRequested.emit()\" title=\"Hide filters\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n </div>\n\n @for (filter of Filters; track filter.Category) {\n <div class=\"filter-category\">\n <div class=\"filter-category-header-row\">\n <div\n class=\"filter-category-header\"\n (click)=\"ToggleCategory(filter.Category)\"\n [class.filter-category-collapsed]=\"IsCategoryCollapsed(filter.Category)\"\n >\n <span class=\"filter-category-name\">{{ filter.Category }}</span>\n @if (Collapsible) {\n <i class=\"fa-solid filter-category-chevron\"\n [class.fa-chevron-down]=\"IsCategoryCollapsed(filter.Category)\"\n [class.fa-chevron-up]=\"!IsCategoryCollapsed(filter.Category)\"></i>\n }\n </div>\n @if (!IsCategoryCollapsed(filter.Category) && filter.Options.length > 1) {\n <button class=\"filter-select-all\"\n (click)=\"SelectAll(filter.Category); $event.stopPropagation()\"\n [title]=\"IsAllSelected(filter.Category) ? 'Clear all' : 'Select all'\">\n {{ IsAllSelected(filter.Category) ? 'Clear' : 'All' }}\n </button>\n }\n </div>\n\n @if (!IsCategoryCollapsed(filter.Category)) {\n <div class=\"filter-options\">\n @for (option of filter.Options; track option.Value) {\n @if (filter.Options.length === 1) {\n <!-- Single option: read-only label (no checkbox, not interactive) -->\n <div class=\"filter-option filter-option-readonly\">\n @if (option.Icon) {\n <i [class]=\"option.Icon + ' filter-option-icon'\"></i>\n }\n <span class=\"filter-option-label\">{{ option.Label }}</span>\n <span class=\"filter-option-count\">{{ option.Count }}</span>\n </div>\n } @else {\n <label class=\"filter-option\" (click)=\"ToggleOption(filter.Category, option.Value); $event.preventDefault()\">\n <span class=\"filter-checkbox\" [class.filter-checkbox-checked]=\"IsOptionSelected(filter.Category, option.Value)\">\n @if (IsOptionSelected(filter.Category, option.Value)) {\n <i class=\"fa-solid fa-check\"></i>\n }\n </span>\n @if (option.Icon) {\n <i [class]=\"option.Icon + ' filter-option-icon'\"></i>\n }\n <span class=\"filter-option-label\">{{ option.Label }}</span>\n <span class=\"filter-option-count\">{{ option.Count }}</span>\n </label>\n }\n }\n </div>\n }\n </div>\n }\n\n <!-- Relevance Threshold Slider -->\n @if (ShowRelevanceSlider) {\n <div class=\"filter-category\">\n <div class=\"filter-category-header-row\">\n <div class=\"filter-category-header\">\n <span class=\"filter-category-name\">Min Relevance</span>\n </div>\n </div>\n <div class=\"filter-relevance-slider\">\n <input\n type=\"range\"\n min=\"0\" max=\"100\" step=\"5\"\n [value]=\"MinScorePercent\"\n (input)=\"OnMinScorePreview($any($event.target).value)\"\n (mouseup)=\"OnMinScoreCommit()\"\n (touchend)=\"OnMinScoreCommit()\"\n class=\"relevance-range\" />\n <span class=\"relevance-value\">{{ MinScorePercent }}%</span>\n </div>\n @if (ServerMinScorePercent > 0) {\n <div class=\"filter-server-threshold\">\n <span class=\"server-threshold-label\">Server threshold: {{ ServerMinScorePercent }}%</span>\n @if (MinScorePercent < ServerMinScorePercent) {\n <span class=\"server-threshold-warning\">\n <i class=\"fa-solid fa-arrow-rotate-right\"></i> Will re-query\n </span>\n }\n </div>\n }\n </div>\n }\n\n @if (Filters.length === 0 && !ShowRelevanceSlider) {\n <div class=\"filter-empty\">\n <p>No filters available</p>\n </div>\n }\n</div>\n", styles: ["/* Search Filter Panel */\n\n.filter-panel {\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n background: var(--mj-bg-surface);\n overflow: hidden;\n}\n\n.filter-panel-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 0.75rem 1rem;\n border-bottom: 1px solid var(--mj-border-subtle);\n}\n\n.filter-panel-title {\n font-weight: 600;\n font-size: 0.85rem;\n color: var(--mj-text-primary);\n display: flex;\n align-items: center;\n gap: 0.5rem;\n}\n\n.filter-clear-all {\n border: none;\n background: none;\n color: var(--mj-brand-primary);\n font-size: 0.78rem;\n cursor: pointer;\n padding: 0.15rem 0.4rem;\n border-radius: 4px;\n}\n\n.filter-clear-all:hover {\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n}\n\n.filter-header-actions {\n display: flex;\n align-items: center;\n gap: 6px;\n}\n\n.filter-close-btn {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 24px;\n height: 24px;\n border: none;\n border-radius: 4px;\n background: none;\n color: var(--mj-text-muted);\n font-size: 0.75rem;\n cursor: pointer;\n transition: all 0.15s;\n}\n\n.filter-close-btn:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-primary);\n}\n\n/* Category */\n.filter-category {\n border-bottom: 1px solid var(--mj-border-subtle);\n}\n\n.filter-category:last-child {\n border-bottom: none;\n}\n\n.filter-category-header-row {\n display: flex;\n align-items: center;\n}\n\n.filter-category-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 0.6rem 1rem;\n cursor: pointer;\n transition: background 0.1s ease;\n flex: 1;\n}\n\n.filter-category-header:hover {\n background: var(--mj-bg-surface-hover);\n}\n\n.filter-select-all {\n padding: 2px 8px;\n border: 1px solid var(--mj-border-default);\n border-radius: 4px;\n background: none;\n color: var(--mj-text-muted);\n font-size: 0.65rem;\n font-weight: 500;\n cursor: pointer;\n margin-right: 8px;\n transition: color 0.15s ease, border-color 0.15s ease;\n}\n\n.filter-select-all:hover {\n color: var(--mj-brand-primary);\n border-color: var(--mj-brand-primary);\n}\n\n.filter-category-name {\n font-weight: 500;\n font-size: 0.82rem;\n color: var(--mj-text-primary);\n}\n\n.filter-category-chevron {\n font-size: 0.65rem;\n color: var(--mj-text-muted);\n}\n\n/* Options */\n.filter-options {\n padding: 0.25rem 0;\n}\n\n.filter-option {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n padding: 0.4rem 1rem;\n cursor: pointer;\n transition: background 0.1s ease;\n font-size: 0.82rem;\n}\n\n.filter-option:hover {\n background: var(--mj-bg-surface-hover);\n}\n\n.filter-option-readonly {\n cursor: default;\n opacity: 0.85;\n}\n.filter-option-readonly:hover {\n background: transparent;\n}\n\n.filter-checkbox {\n width: 1rem;\n height: 1rem;\n border: 1.5px solid var(--mj-border-strong);\n border-radius: 3px;\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n font-size: 0.6rem;\n transition: all 0.15s ease;\n}\n\n.filter-checkbox-checked {\n background: var(--mj-brand-primary);\n border-color: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n}\n\n.filter-option-icon {\n color: var(--mj-text-muted);\n font-size: 0.78rem;\n width: 1rem;\n text-align: center;\n}\n\n.filter-option-label {\n flex: 1;\n color: var(--mj-text-secondary);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.filter-option-count {\n color: var(--mj-text-muted);\n font-size: 0.72rem;\n flex-shrink: 0;\n}\n\n/* Empty state */\n.filter-empty {\n padding: 1.5rem 1rem;\n text-align: center;\n color: var(--mj-text-muted);\n font-size: 0.82rem;\n}\n\n/* Relevance Slider */\n.filter-relevance-slider {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 8px 12px;\n}\n.relevance-range {\n flex: 1;\n height: 4px;\n -webkit-appearance: none;\n appearance: none;\n background: var(--mj-border-default);\n border-radius: 2px;\n outline: none;\n cursor: pointer;\n}\n.relevance-range::-webkit-slider-thumb {\n -webkit-appearance: none;\n width: 16px;\n height: 16px;\n border-radius: 50%;\n background: var(--mj-brand-primary);\n cursor: pointer;\n border: 2px solid var(--mj-bg-surface);\n box-shadow: 0 1px 3px rgba(0,0,0,0.2);\n}\n.relevance-range::-moz-range-thumb {\n width: 16px;\n height: 16px;\n border-radius: 50%;\n background: var(--mj-brand-primary);\n cursor: pointer;\n border: 2px solid var(--mj-bg-surface);\n box-shadow: 0 1px 3px rgba(0,0,0,0.2);\n}\n.relevance-value {\n font-size: 12px;\n font-weight: 600;\n color: var(--mj-brand-primary);\n min-width: 36px;\n text-align: right;\n}\n\n/* Server threshold indicator */\n.filter-server-threshold {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 2px 12px 6px;\n font-size: 10px;\n color: var(--mj-text-muted);\n}\n.server-threshold-warning {\n color: var(--mj-status-warning-text, var(--mj-status-warning));\n font-weight: 600;\n}\n"] }]
188
329
  }], null, { Filters: [{
189
330
  type: Input
190
331
  }], ActiveFilters: [{
191
332
  type: Input
192
333
  }], Collapsible: [{
193
334
  type: Input
335
+ }], ShowRelevanceSlider: [{
336
+ type: Input
337
+ }], MinScorePercent: [{
338
+ type: Input
339
+ }], ServerMinScorePercent: [{
340
+ type: Input
194
341
  }], FilterChanged: [{
195
342
  type: Output
196
343
  }], FiltersCleared: [{
197
344
  type: Output
345
+ }], CloseRequested: [{
346
+ type: Output
347
+ }], MinScoreChanged: [{
348
+ type: Output
198
349
  }] }); })();
199
350
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(SearchFilterComponent, { className: "SearchFilterComponent", filePath: "src/lib/search-filter.component.ts", lineNumber: 24 }); })();
200
351
  //# sourceMappingURL=search-filter.component.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"search-filter.component.js","sourceRoot":"","sources":["../../src/lib/search-filter.component.ts","../../src/lib/search-filter.component.html"],"names":[],"mappings":"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","sourcesContent":["/**\n * @fileoverview Search Filter Component\n *\n * Faceted filter sidebar for narrowing search results.\n * Displays filter categories with checkable options and counts.\n */\n\nimport {\n Component,\n EventEmitter,\n Input,\n Output,\n ChangeDetectorRef,\n inject\n} from '@angular/core';\nimport { SearchFilter, SearchFilterChangeEvent } from './search-types';\n\n@Component({\n standalone: false,\n selector: 'mj-search-filter',\n templateUrl: './search-filter.component.html',\n styleUrls: ['./search-filter.component.css']\n})\nexport class SearchFilterComponent {\n private cdr = inject(ChangeDetectorRef);\n\n /** Available filters to display */\n @Input() Filters: SearchFilter[] = [];\n\n /** Currently active filter selections */\n @Input() ActiveFilters: Record<string, string[]> = {};\n\n /** Whether the filter panel is collapsible */\n @Input() Collapsible = true;\n\n @Output() FilterChanged = new EventEmitter<SearchFilterChangeEvent>();\n @Output() FiltersCleared = new EventEmitter<void>();\n\n /** Collapsed state per category */\n public CollapsedCategories = new Set<string>();\n\n /** Toggle a filter option */\n public ToggleOption(category: string, value: string): void {\n const current = this.ActiveFilters[category] ? [...this.ActiveFilters[category]] : [];\n const index = current.indexOf(value);\n if (index >= 0) {\n current.splice(index, 1);\n } else {\n current.push(value);\n }\n // Update local state to reflect change\n this.ActiveFilters = { ...this.ActiveFilters, [category]: current };\n this.FilterChanged.emit({ Category: category, SelectedValues: current });\n this.cdr.detectChanges();\n }\n\n /** Toggle category collapse */\n public ToggleCategory(category: string): void {\n if (!this.Collapsible) return;\n if (this.CollapsedCategories.has(category)) {\n this.CollapsedCategories.delete(category);\n } else {\n this.CollapsedCategories.add(category);\n }\n this.cdr.detectChanges();\n }\n\n /** Check if a category is collapsed */\n public IsCategoryCollapsed(category: string): boolean {\n return this.CollapsedCategories.has(category);\n }\n\n /** Check if an option is selected */\n public IsOptionSelected(category: string, value: string): boolean {\n return (this.ActiveFilters[category] ?? []).includes(value);\n }\n\n /** Check if any filters are active */\n public HasActiveFilters(): boolean {\n return Object.values(this.ActiveFilters).some(v => v.length > 0);\n }\n\n /** Get total active filter count */\n public GetActiveFilterCount(): number {\n return Object.values(this.ActiveFilters).reduce((sum, v) => sum + v.length, 0);\n }\n\n /** Clear all filters */\n public ClearAll(): void {\n this.ActiveFilters = {};\n this.FiltersCleared.emit();\n this.cdr.detectChanges();\n }\n}\n","<div class=\"filter-panel\">\n <div class=\"filter-panel-header\">\n <span class=\"filter-panel-title\">\n <i class=\"fa-solid fa-filter\"></i> Filters\n </span>\n @if (HasActiveFilters()) {\n <button class=\"filter-clear-all\" (click)=\"ClearAll()\">\n Clear ({{ GetActiveFilterCount() }})\n </button>\n }\n </div>\n\n @for (filter of Filters; track filter.Category) {\n <div class=\"filter-category\">\n <div\n class=\"filter-category-header\"\n (click)=\"ToggleCategory(filter.Category)\"\n [class.filter-category-collapsed]=\"IsCategoryCollapsed(filter.Category)\"\n >\n <span class=\"filter-category-name\">{{ filter.Category }}</span>\n @if (Collapsible) {\n <i class=\"fa-solid filter-category-chevron\"\n [class.fa-chevron-down]=\"IsCategoryCollapsed(filter.Category)\"\n [class.fa-chevron-up]=\"!IsCategoryCollapsed(filter.Category)\"></i>\n }\n </div>\n\n @if (!IsCategoryCollapsed(filter.Category)) {\n <div class=\"filter-options\">\n @for (option of filter.Options; track option.Value) {\n <label class=\"filter-option\" (click)=\"ToggleOption(filter.Category, option.Value); $event.preventDefault()\">\n <span class=\"filter-checkbox\" [class.filter-checkbox-checked]=\"IsOptionSelected(filter.Category, option.Value)\">\n @if (IsOptionSelected(filter.Category, option.Value)) {\n <i class=\"fa-solid fa-check\"></i>\n }\n </span>\n @if (option.Icon) {\n <i [class]=\"option.Icon + ' filter-option-icon'\"></i>\n }\n <span class=\"filter-option-label\">{{ option.Label }}</span>\n <span class=\"filter-option-count\">{{ option.Count }}</span>\n </label>\n }\n </div>\n }\n </div>\n }\n\n @if (Filters.length === 0) {\n <div class=\"filter-empty\">\n <p>No filters available</p>\n </div>\n }\n</div>\n"]}
1
+ {"version":3,"file":"search-filter.component.js","sourceRoot":"","sources":["../../src/lib/search-filter.component.ts","../../src/lib/search-filter.component.html"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EACH,SAAS,EACT,YAAY,EACZ,KAAK,EACL,MAAM,EACN,iBAAiB,EACjB,MAAM,EACT,MAAM,eAAe,CAAC;;;;;;ICPP,kCAAsD;IAArB,yLAAS,iBAAU,KAAC;IACjD,YACJ;IAAA,iBAAS;;;IADL,cACJ;IADI,sEACJ;;;IAkBQ,wBAEqE;;;;IAAlE,AADA,iFAA8D,kEACD;;;;IAIpE,kCAE8E;IADtE,gOAAS,oCAA0B,wBAAE,wBAAwB,KAAC;IAElE,YACJ;IAAA,iBAAS;;;;IAFD,6FAAqE;IACzE,cACJ;IADI,2FACJ;;;IAWgB,oBAAqD;;;IAAlD,qDAA6C;;;IAFxD,+BAAkD;IAC9C,+HAAmB;IAGnB,gCAAkC;IAAA,YAAkB;IAAA,iBAAO;IAC3D,gCAAkC;IAAA,YAAkB;IACxD,AADwD,iBAAO,EACzD;;;IALF,cAEC;IAFD,yCAEC;IACiC,eAAkB;IAAlB,qCAAkB;IAClB,eAAkB;IAAlB,qCAAkB;;;IAM5C,wBAAiC;;;IAIrC,oBAAqD;;;IAAlD,qDAA6C;;;;IAPxD,iCAA4G;IAA/E,oSAAS,wDAA2C,wBAAE,uBAAuB,KAAC;IACvG,gCAAgH;IAC5G,+HAAuD;IAG3D,iBAAO;IACP,+HAAmB;IAGnB,gCAAkC;IAAA,YAAkB;IAAA,iBAAO;IAC3D,gCAAkC;IAAA,YAAkB;IACxD,AADwD,iBAAO,EACvD;;;;;IAV0B,cAAiF;IAAjF,uGAAiF;IAC3G,cAEC;IAFD,uFAEC;IAEL,cAEC;IAFD,yCAEC;IACiC,eAAkB;IAAlB,qCAAkB;IAClB,eAAkB;IAAlB,qCAAkB;;;IAX1D,AATF,mHAAmC,+FAS1B;;;IATT,wDAsBC;;;IAxBT,+BAA4B;IACxB,+GAwBC;IACL,iBAAM;;;IAzBF,cAwBC;IAxBD,gCAwBC;;;;IA/CL,AADJ,AADJ,8BAA6B,cACe,cAKnC;IAFG,2MAAS,yCAA+B,KAAC;IAGzC,gCAAmC;IAAA,YAAqB;IAAA,iBAAO;IAC/D,6FAAmB;IAKvB,iBAAM;IACN,kGAA0E;IAO9E,iBAAM;IAEN,+FAA6C;IA6BjD,iBAAM;;;;IA/CM,eAAwE;IAAxE,2FAAwE;IAErC,eAAqB;IAArB,wCAAqB;IACxD,cAIC;IAJD,6CAIC;IAEL,cAMC;IAND,0GAMC;IAGL,cA4BC;IA5BD,0EA4BC;;;IA2BW,gCAAuC;IACnC,wBAA8C;IAAC,+BACnD;IAAA,iBAAO;;;IAJX,AADJ,+BAAqC,eACI;IAAA,YAA8C;IAAA,iBAAO;IAC1F,sHAA+C;IAKnD,iBAAM;;;IANmC,eAA8C;IAA9C,8EAA8C;IACnF,cAIC;IAJD,gFAIC;;;;IArBD,AADJ,AADJ,AADJ,8BAA6B,cACe,cACA,eACG;IAAA,6BAAa;IAExD,AADI,AADoD,iBAAO,EACrD,EACJ;IAEF,AADJ,+BAAqC,gBAQH;IAD1B,AADA,AADA,+LAAS,6CAA4C,KAAC,gLAC3C,yBAAkB,KAAC,kLAClB,yBAAkB,KAAC;IANnC,iBAO8B;IAC9B,gCAA8B;IAAA,YAAsB;IACxD,AADwD,iBAAO,EACzD;IACN,uGAAiC;IAUrC,iBAAM;;;IAjBM,eAAyB;IAAzB,8CAAyB;IAKC,eAAsB;IAAtB,sDAAsB;IAExD,cASC;IATD,2DASC;;;IAMD,AADJ,8BAA0B,QACnB;IAAA,oCAAoB;IAC3B,AAD2B,iBAAI,EACzB;;ADrFd,MAAM,OAAO,qBAAqB;IACtB,GAAG,GAAG,MAAM,CAAC,iBAAiB,CAAC,CAAC;IAExC,mCAAmC;IAC1B,OAAO,GAAmB,EAAE,CAAC;IAEtC,yCAAyC;IAChC,aAAa,GAA6B,EAAE,CAAC;IAEtD,8CAA8C;IACrC,WAAW,GAAG,IAAI,CAAC;IAE5B,+CAA+C;IACtC,mBAAmB,GAAG,IAAI,CAAC;IAEpC,oDAAoD;IAC3C,eAAe,GAAG,CAAC,CAAC;IAE7B,2EAA2E;IAClE,qBAAqB,GAAG,CAAC,CAAC;IAEzB,aAAa,GAAG,IAAI,YAAY,EAA2B,CAAC;IAC5D,cAAc,GAAG,IAAI,YAAY,EAAQ,CAAC;IAC1C,cAAc,GAAG,IAAI,YAAY,EAAQ,CAAC;IAC1C,eAAe,GAAG,IAAI,YAAY,EAAU,CAAC;IAEvD,mCAAmC;IAC5B,mBAAmB,GAAG,IAAI,GAAG,EAAU,CAAC;IAE/C,6BAA6B;IACtB,YAAY,CAAC,QAAgB,EAAE,KAAa;QAC/C,MAAM,OAAO,GAAG,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACtF,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;QACrC,IAAI,KAAK,IAAI,CAAC,EAAE,CAAC;YACb,OAAO,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;QAC7B,CAAC;aAAM,CAAC;YACJ,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACxB,CAAC;QACD,uCAAuC;QACvC,IAAI,CAAC,aAAa,GAAG,EAAE,GAAG,IAAI,CAAC,aAAa,EAAE,CAAC,QAAQ,CAAC,EAAE,OAAO,EAAE,CAAC;QACpE,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,cAAc,EAAE,OAAO,EAAE,CAAC,CAAC;QACzE,IAAI,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC;IAC7B,CAAC;IAED,+BAA+B;IACxB,cAAc,CAAC,QAAgB;QAClC,IAAI,CAAC,IAAI,CAAC,WAAW;YAAE,OAAO;QAC9B,IAAI,IAAI,CAAC,mBAAmB,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;YACzC,IAAI,CAAC,mBAAmB,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAC9C,CAAC;aAAM,CAAC;YACJ,IAAI,CAAC,mBAAmB,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAC3C,CAAC;QACD,IAAI,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC;IAC7B,CAAC;IAED,uCAAuC;IAChC,mBAAmB,CAAC,QAAgB;QACvC,OAAO,IAAI,CAAC,mBAAmB,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAClD,CAAC;IAED,qCAAqC;IAC9B,gBAAgB,CAAC,QAAgB,EAAE,KAAa;QACnD,OAAO,CAAC,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IAChE,CAAC;IAED,sCAAsC;IAC/B,gBAAgB;QACnB,OAAO,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IACrE,CAAC;IAED,oCAAoC;IAC7B,oBAAoB;QACvB,OAAO,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IACnF,CAAC;IAED,wBAAwB;IACjB,QAAQ;QACX,IAAI,CAAC,aAAa,GAAG,EAAE,CAAC;QACxB,IAAI,CAAC,cAAc,CAAC,IAAI,EAAE,CAAC;QAC3B,IAAI,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC;IAC7B,CAAC;IAED,6EAA6E;IACtE,SAAS,CAAC,QAAgB;QAC7B,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC;QAC/D,IAAI,CAAC,MAAM;YAAE,OAAO;QAEpB,MAAM,OAAO,GAAG,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC;QACnD,IAAI,OAAO,CAAC,MAAM,KAAK,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;YAC3C,uBAAuB;YACvB,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC;QACtC,CAAC;aAAM,CAAC;YACJ,aAAa;YACb,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;QACpE,CAAC;QACD,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,cAAc,EAAE,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;QAC9F,IAAI,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC;IAC7B,CAAC;IAED,sDAAsD;IAC/C,aAAa,CAAC,QAAgB;QACjC,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC;QAC/D,IAAI,CAAC,MAAM;YAAE,OAAO,KAAK,CAAC;QAC1B,MAAM,SAAS,GAAG,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC;QACrD,OAAO,SAAS,CAAC,MAAM,KAAK,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC;IACtD,CAAC;IAED,8FAA8F;IACvF,iBAAiB,CAAC,KAAa;QAClC,IAAI,CAAC,eAAe,GAAG,QAAQ,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IAC/C,CAAC;IAED,0FAA0F;IACnF,gBAAgB;QACnB,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;IACpD,CAAC;IAED,4EAA4E;IACrE,gBAAgB,CAAC,KAAa;QACjC,IAAI,CAAC,eAAe,GAAG,QAAQ,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QAC3C,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;IACpD,CAAC;+GAzHQ,qBAAqB;6DAArB,qBAAqB;YCrB1B,AADJ,AADJ,8BAA0B,aACW,cACI;YAC7B,uBAAkC;YAAC,yBACvC;YAAA,iBAAO;YACP,8BAAmC;YAC/B,0FAA0B;YAK1B,iCAAsF;YAArD,kGAAS,yBAAqB,IAAC;YAC5D,uBAAiC;YAG7C,AADI,AADI,iBAAS,EACP,EACJ;YAEN,yFAsDC;YAGD,0FAA2B;YA+B3B,yFAAoD;YAKxD,iBAAM;;YAxGM,eAIC;YAJD,iDAIC;YAOT,eAsDC;YAtDD,0BAsDC;YAGD,eA6BC;YA7BD,mDA6BC;YAED,cAIC;YAJD,gFAIC;;;iFDtFQ,qBAAqB;cANjC,SAAS;6BACM,KAAK,YACP,kBAAkB;;kBAQ3B,KAAK;;kBAGL,KAAK;;kBAGL,KAAK;;kBAGL,KAAK;;kBAGL,KAAK;;kBAGL,KAAK;;kBAEL,MAAM;;kBACN,MAAM;;kBACN,MAAM;;kBACN,MAAM;;kFAxBE,qBAAqB","sourcesContent":["/**\n * @fileoverview Search Filter Component\n *\n * Faceted filter sidebar for narrowing search results.\n * Displays filter categories with checkable options and counts.\n */\n\nimport {\n Component,\n EventEmitter,\n Input,\n Output,\n ChangeDetectorRef,\n inject\n} from '@angular/core';\nimport { SearchFilter, SearchFilterChangeEvent } from './search-types';\n\n@Component({\n standalone: false,\n selector: 'mj-search-filter',\n templateUrl: './search-filter.component.html',\n styleUrls: ['./search-filter.component.css']\n})\nexport class SearchFilterComponent {\n private cdr = inject(ChangeDetectorRef);\n\n /** Available filters to display */\n @Input() Filters: SearchFilter[] = [];\n\n /** Currently active filter selections */\n @Input() ActiveFilters: Record<string, string[]> = {};\n\n /** Whether the filter panel is collapsible */\n @Input() Collapsible = true;\n\n /** Whether to show the min relevance slider */\n @Input() ShowRelevanceSlider = true;\n\n /** Current minimum score as a percentage (0-100) */\n @Input() MinScorePercent = 0;\n\n /** The MinScore that was last sent to the server (for visual indicator) */\n @Input() ServerMinScorePercent = 0;\n\n @Output() FilterChanged = new EventEmitter<SearchFilterChangeEvent>();\n @Output() FiltersCleared = new EventEmitter<void>();\n @Output() CloseRequested = new EventEmitter<void>();\n @Output() MinScoreChanged = new EventEmitter<number>();\n\n /** Collapsed state per category */\n public CollapsedCategories = new Set<string>();\n\n /** Toggle a filter option */\n public ToggleOption(category: string, value: string): void {\n const current = this.ActiveFilters[category] ? [...this.ActiveFilters[category]] : [];\n const index = current.indexOf(value);\n if (index >= 0) {\n current.splice(index, 1);\n } else {\n current.push(value);\n }\n // Update local state to reflect change\n this.ActiveFilters = { ...this.ActiveFilters, [category]: current };\n this.FilterChanged.emit({ Category: category, SelectedValues: current });\n this.cdr.detectChanges();\n }\n\n /** Toggle category collapse */\n public ToggleCategory(category: string): void {\n if (!this.Collapsible) return;\n if (this.CollapsedCategories.has(category)) {\n this.CollapsedCategories.delete(category);\n } else {\n this.CollapsedCategories.add(category);\n }\n this.cdr.detectChanges();\n }\n\n /** Check if a category is collapsed */\n public IsCategoryCollapsed(category: string): boolean {\n return this.CollapsedCategories.has(category);\n }\n\n /** Check if an option is selected */\n public IsOptionSelected(category: string, value: string): boolean {\n return (this.ActiveFilters[category] ?? []).includes(value);\n }\n\n /** Check if any filters are active */\n public HasActiveFilters(): boolean {\n return Object.values(this.ActiveFilters).some(v => v.length > 0);\n }\n\n /** Get total active filter count */\n public GetActiveFilterCount(): number {\n return Object.values(this.ActiveFilters).reduce((sum, v) => sum + v.length, 0);\n }\n\n /** Clear all filters */\n public ClearAll(): void {\n this.ActiveFilters = {};\n this.FiltersCleared.emit();\n this.cdr.detectChanges();\n }\n\n /** Select all options in a category, or clear all if already all selected */\n public SelectAll(category: string): void {\n const filter = this.Filters.find(f => f.Category === category);\n if (!filter) return;\n\n const current = this.ActiveFilters[category] ?? [];\n if (current.length === filter.Options.length) {\n // All selected → clear\n this.ActiveFilters[category] = [];\n } else {\n // Select all\n this.ActiveFilters[category] = filter.Options.map(o => o.Value);\n }\n this.FilterChanged.emit({ Category: category, SelectedValues: this.ActiveFilters[category] });\n this.cdr.detectChanges();\n }\n\n /** Check if all options in a category are selected */\n public IsAllSelected(category: string): boolean {\n const filter = this.Filters.find(f => f.Category === category);\n if (!filter) return false;\n const selection = this.ActiveFilters[category] ?? [];\n return selection.length === filter.Options.length;\n }\n\n /** Handle relevance slider drag (preview only — updates display without triggering search) */\n public OnMinScorePreview(value: string): void {\n this.MinScorePercent = parseInt(value, 10);\n }\n\n /** Handle relevance slider release — commits the value and triggers re-query if needed */\n public OnMinScoreCommit(): void {\n this.MinScoreChanged.emit(this.MinScorePercent);\n }\n\n /** Handle relevance slider change (legacy — used when input event fires) */\n public OnMinScoreChange(value: string): void {\n this.MinScorePercent = parseInt(value, 10);\n this.MinScoreChanged.emit(this.MinScorePercent);\n }\n}\n","<div class=\"filter-panel\">\n <div class=\"filter-panel-header\">\n <span class=\"filter-panel-title\">\n <i class=\"fa-solid fa-filter\"></i> Filters\n </span>\n <div class=\"filter-header-actions\">\n @if (HasActiveFilters()) {\n <button class=\"filter-clear-all\" (click)=\"ClearAll()\">\n Clear ({{ GetActiveFilterCount() }})\n </button>\n }\n <button class=\"filter-close-btn\" (click)=\"CloseRequested.emit()\" title=\"Hide filters\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n </div>\n\n @for (filter of Filters; track filter.Category) {\n <div class=\"filter-category\">\n <div class=\"filter-category-header-row\">\n <div\n class=\"filter-category-header\"\n (click)=\"ToggleCategory(filter.Category)\"\n [class.filter-category-collapsed]=\"IsCategoryCollapsed(filter.Category)\"\n >\n <span class=\"filter-category-name\">{{ filter.Category }}</span>\n @if (Collapsible) {\n <i class=\"fa-solid filter-category-chevron\"\n [class.fa-chevron-down]=\"IsCategoryCollapsed(filter.Category)\"\n [class.fa-chevron-up]=\"!IsCategoryCollapsed(filter.Category)\"></i>\n }\n </div>\n @if (!IsCategoryCollapsed(filter.Category) && filter.Options.length > 1) {\n <button class=\"filter-select-all\"\n (click)=\"SelectAll(filter.Category); $event.stopPropagation()\"\n [title]=\"IsAllSelected(filter.Category) ? 'Clear all' : 'Select all'\">\n {{ IsAllSelected(filter.Category) ? 'Clear' : 'All' }}\n </button>\n }\n </div>\n\n @if (!IsCategoryCollapsed(filter.Category)) {\n <div class=\"filter-options\">\n @for (option of filter.Options; track option.Value) {\n @if (filter.Options.length === 1) {\n <!-- Single option: read-only label (no checkbox, not interactive) -->\n <div class=\"filter-option filter-option-readonly\">\n @if (option.Icon) {\n <i [class]=\"option.Icon + ' filter-option-icon'\"></i>\n }\n <span class=\"filter-option-label\">{{ option.Label }}</span>\n <span class=\"filter-option-count\">{{ option.Count }}</span>\n </div>\n } @else {\n <label class=\"filter-option\" (click)=\"ToggleOption(filter.Category, option.Value); $event.preventDefault()\">\n <span class=\"filter-checkbox\" [class.filter-checkbox-checked]=\"IsOptionSelected(filter.Category, option.Value)\">\n @if (IsOptionSelected(filter.Category, option.Value)) {\n <i class=\"fa-solid fa-check\"></i>\n }\n </span>\n @if (option.Icon) {\n <i [class]=\"option.Icon + ' filter-option-icon'\"></i>\n }\n <span class=\"filter-option-label\">{{ option.Label }}</span>\n <span class=\"filter-option-count\">{{ option.Count }}</span>\n </label>\n }\n }\n </div>\n }\n </div>\n }\n\n <!-- Relevance Threshold Slider -->\n @if (ShowRelevanceSlider) {\n <div class=\"filter-category\">\n <div class=\"filter-category-header-row\">\n <div class=\"filter-category-header\">\n <span class=\"filter-category-name\">Min Relevance</span>\n </div>\n </div>\n <div class=\"filter-relevance-slider\">\n <input\n type=\"range\"\n min=\"0\" max=\"100\" step=\"5\"\n [value]=\"MinScorePercent\"\n (input)=\"OnMinScorePreview($any($event.target).value)\"\n (mouseup)=\"OnMinScoreCommit()\"\n (touchend)=\"OnMinScoreCommit()\"\n class=\"relevance-range\" />\n <span class=\"relevance-value\">{{ MinScorePercent }}%</span>\n </div>\n @if (ServerMinScorePercent > 0) {\n <div class=\"filter-server-threshold\">\n <span class=\"server-threshold-label\">Server threshold: {{ ServerMinScorePercent }}%</span>\n @if (MinScorePercent < ServerMinScorePercent) {\n <span class=\"server-threshold-warning\">\n <i class=\"fa-solid fa-arrow-rotate-right\"></i> Will re-query\n </span>\n }\n </div>\n }\n </div>\n }\n\n @if (Filters.length === 0 && !ShowRelevanceSlider) {\n <div class=\"filter-empty\">\n <p>No filters available</p>\n </div>\n }\n</div>\n"]}
@@ -0,0 +1,61 @@
1
+ /**
2
+ * @fileoverview Search Input Component
3
+ *
4
+ * A reusable search text input for navbar/hero placement.
5
+ * Features debounced query emission, keyboard shortcut hint,
6
+ * clear button, and focus management.
7
+ */
8
+ import { ElementRef, EventEmitter, OnDestroy, OnInit } from '@angular/core';
9
+ import * as i0 from "@angular/core";
10
+ export declare class SearchInputComponent implements OnInit, OnDestroy {
11
+ private cdr;
12
+ private destroy$;
13
+ private queryInput$;
14
+ inputRef: ElementRef<HTMLInputElement>;
15
+ /** Placeholder text shown in the input */
16
+ Placeholder: string;
17
+ /** Current query value (two-way bindable via QueryChange) */
18
+ private _Query;
19
+ set Query(value: string);
20
+ get Query(): string;
21
+ /** Whether to show the keyboard shortcut hint badge */
22
+ ShowShortcutHint: boolean;
23
+ /** Text to display in the shortcut hint badge */
24
+ ShortcutHint: string;
25
+ /** Debounce time in milliseconds before QueryChange emits */
26
+ DebounceMs: number;
27
+ /** Emitted after debounce when query text changes */
28
+ QueryChange: EventEmitter<string>;
29
+ /** Emitted immediately when the user presses Enter */
30
+ QuerySubmit: EventEmitter<string>;
31
+ /** Emitted when the input receives focus */
32
+ InputFocused: EventEmitter<void>;
33
+ /** Emitted when the input loses focus */
34
+ InputBlurred: EventEmitter<void>;
35
+ /** Emitted when the user presses Escape in the input */
36
+ InputEscaped: EventEmitter<void>;
37
+ /** Emitted when the user clears the input via the clear button */
38
+ InputCleared: EventEmitter<void>;
39
+ /** Whether the input currently has focus */
40
+ IsFocused: boolean;
41
+ ngOnInit(): void;
42
+ ngOnDestroy(): void;
43
+ /** Programmatically focus the input element */
44
+ Focus(): void;
45
+ /** Clear the query and emit InputCleared */
46
+ Clear(): void;
47
+ /** Handle native input event */
48
+ OnInput(value: string): void;
49
+ /** Handle keydown events on the input */
50
+ OnKeydown(event: KeyboardEvent): void;
51
+ /** Handle focus event */
52
+ OnFocus(): void;
53
+ /** Handle click — re-opens suggest when input already has focus */
54
+ OnClick(): void;
55
+ /** Handle blur event */
56
+ OnBlur(): void;
57
+ private setupDebounce;
58
+ static ɵfac: i0.ɵɵFactoryDeclaration<SearchInputComponent, never>;
59
+ static ɵcmp: i0.ɵɵComponentDeclaration<SearchInputComponent, "mj-search-input", never, { "Placeholder": { "alias": "Placeholder"; "required": false; }; "Query": { "alias": "Query"; "required": false; }; "ShowShortcutHint": { "alias": "ShowShortcutHint"; "required": false; }; "ShortcutHint": { "alias": "ShortcutHint"; "required": false; }; "DebounceMs": { "alias": "DebounceMs"; "required": false; }; }, { "QueryChange": "QueryChange"; "QuerySubmit": "QuerySubmit"; "InputFocused": "InputFocused"; "InputBlurred": "InputBlurred"; "InputEscaped": "InputEscaped"; "InputCleared": "InputCleared"; }, never, never, false, never>;
60
+ }
61
+ //# sourceMappingURL=search-input.component.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search-input.component.d.ts","sourceRoot":"","sources":["../../src/lib/search-input.component.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAEH,UAAU,EACV,YAAY,EAEZ,SAAS,EACT,MAAM,EAKT,MAAM,eAAe,CAAC;;AAIvB,qBAMa,oBAAqB,YAAW,MAAM,EAAE,SAAS;IAC1D,OAAO,CAAC,GAAG,CAA6B;IACxC,OAAO,CAAC,QAAQ,CAAuB;IACvC,OAAO,CAAC,WAAW,CAAyB;IAEtB,QAAQ,EAAG,UAAU,CAAC,gBAAgB,CAAC,CAAC;IAI9D,0CAA0C;IACjC,WAAW,SAAe;IAEnC,6DAA6D;IAC7D,OAAO,CAAC,MAAM,CAAM;IAEpB,IACI,KAAK,CAAC,KAAK,EAAE,MAAM,EAKtB;IACD,IAAI,KAAK,IAAI,MAAM,CAElB;IAED,uDAAuD;IAC9C,gBAAgB,UAAQ;IAEjC,iDAAiD;IACxC,YAAY,SAAY;IAEjC,6DAA6D;IACpD,UAAU,SAAO;IAI1B,qDAAqD;IAC3C,WAAW,uBAA8B;IAEnD,sDAAsD;IAC5C,WAAW,uBAA8B;IAEnD,4CAA4C;IAClC,YAAY,qBAA4B;IAElD,yCAAyC;IAC/B,YAAY,qBAA4B;IAElD,wDAAwD;IAC9C,YAAY,qBAA4B;IAElD,kEAAkE;IACxD,YAAY,qBAA4B;IAIlD,4CAA4C;IACrC,SAAS,UAAS;IAEzB,QAAQ,IAAI,IAAI;IAIhB,WAAW,IAAI,IAAI;IAOnB,+CAA+C;IACxC,KAAK,IAAI,IAAI;IAIpB,4CAA4C;IACrC,KAAK,IAAI,IAAI;IAWpB,gCAAgC;IACzB,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAKnC,yCAAyC;IAClC,SAAS,CAAC,KAAK,EAAE,aAAa,GAAG,IAAI;IAa5C,yBAAyB;IAClB,OAAO,IAAI,IAAI;IAMtB,mEAAmE;IAC5D,OAAO,IAAI,IAAI;IAMtB,wBAAwB;IACjB,MAAM,IAAI,IAAI;IAQrB,OAAO,CAAC,aAAa;yCAnIZ,oBAAoB;2CAApB,oBAAoB;CA6IhC"}