@memberjunction/ng-search 5.24.0 → 5.26.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.
- package/dist/__tests__/search-types.test.js +6 -2
- package/dist/__tests__/search-types.test.js.map +1 -1
- package/dist/__tests__/search.service.test.js +25 -18
- package/dist/__tests__/search.service.test.js.map +1 -1
- package/dist/lib/module.d.ts +7 -4
- package/dist/lib/module.d.ts.map +1 -1
- package/dist/lib/module.js +19 -4
- package/dist/lib/module.js.map +1 -1
- package/dist/lib/search-composite.component.d.ts +79 -0
- package/dist/lib/search-composite.component.d.ts.map +1 -0
- package/dist/lib/search-composite.component.js +258 -0
- package/dist/lib/search-composite.component.js.map +1 -0
- package/dist/lib/search-filter.component.d.ts +14 -1
- package/dist/lib/search-filter.component.d.ts.map +1 -1
- package/dist/lib/search-filter.component.js +128 -26
- package/dist/lib/search-filter.component.js.map +1 -1
- package/dist/lib/search-input.component.d.ts +61 -0
- package/dist/lib/search-input.component.d.ts.map +1 -0
- package/dist/lib/search-input.component.js +192 -0
- package/dist/lib/search-input.component.js.map +1 -0
- package/dist/lib/search-overlay.component.js +2 -2
- package/dist/lib/search-results.component.d.ts +9 -5
- package/dist/lib/search-results.component.d.ts.map +1 -1
- package/dist/lib/search-results.component.js +117 -98
- package/dist/lib/search-results.component.js.map +1 -1
- package/dist/lib/search-suggest.component.d.ts +95 -0
- package/dist/lib/search-suggest.component.d.ts.map +1 -0
- package/dist/lib/search-suggest.component.js +491 -0
- package/dist/lib/search-suggest.component.js.map +1 -0
- package/dist/lib/search-types.d.ts +27 -1
- package/dist/lib/search-types.d.ts.map +1 -1
- package/dist/lib/search-types.js.map +1 -1
- package/dist/lib/search.service.d.ts +27 -5
- package/dist/lib/search.service.d.ts.map +1 -1
- package/dist/lib/search.service.js +221 -92
- package/dist/lib/search.service.js.map +1 -1
- package/dist/public-api.d.ts +3 -0
- package/dist/public-api.d.ts.map +1 -1
- package/dist/public-api.js +3 -0
- package/dist/public-api.js.map +1 -1
- package/package.json +6 -4
|
@@ -0,0 +1,491 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Search Suggest Component
|
|
3
|
+
*
|
|
4
|
+
* Autocomplete dropdown showing preview results and recent searches.
|
|
5
|
+
* Supports keyboard navigation (up/down/enter) for accessibility.
|
|
6
|
+
* Designed to be paired with SearchInputComponent via SearchCompositeComponent.
|
|
7
|
+
*/
|
|
8
|
+
import { Component, EventEmitter, Input, Output, ChangeDetectorRef, inject } from '@angular/core';
|
|
9
|
+
import { StartupManager } from '@memberjunction/core';
|
|
10
|
+
import { UserInfoEngine } from '@memberjunction/core-entities';
|
|
11
|
+
import { MJEventType, MJGlobal } from '@memberjunction/global';
|
|
12
|
+
import * as i0 from "@angular/core";
|
|
13
|
+
const _forTrack0 = ($index, $item) => $item.Query;
|
|
14
|
+
const _forTrack1 = ($index, $item) => $item.ID;
|
|
15
|
+
function SearchSuggestComponent_Conditional_0_Conditional_1_Template(rf, ctx) { if (rf & 1) {
|
|
16
|
+
i0.ɵɵelementStart(0, "div", 1);
|
|
17
|
+
i0.ɵɵelement(1, "i", 5);
|
|
18
|
+
i0.ɵɵelementStart(2, "span");
|
|
19
|
+
i0.ɵɵtext(3, "Searching...");
|
|
20
|
+
i0.ɵɵelementEnd()();
|
|
21
|
+
} }
|
|
22
|
+
function SearchSuggestComponent_Conditional_0_Conditional_2_For_8_Template(rf, ctx) { if (rf & 1) {
|
|
23
|
+
const _r3 = i0.ɵɵgetCurrentView();
|
|
24
|
+
i0.ɵɵelementStart(0, "div", 10);
|
|
25
|
+
i0.ɵɵlistener("mousedown", function SearchSuggestComponent_Conditional_0_Conditional_2_For_8_Template_div_mousedown_0_listener($event) { const recent_r4 = i0.ɵɵrestoreView(_r3).$implicit; const ctx_r1 = i0.ɵɵnextContext(3); ctx_r1.OnRecentClick(recent_r4); return i0.ɵɵresetView($event.preventDefault()); })("mouseenter", function SearchSuggestComponent_Conditional_0_Conditional_2_For_8_Template_div_mouseenter_0_listener() { const ɵ$index_25_r5 = i0.ɵɵrestoreView(_r3).$index; const ctx_r1 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r1.HighlightedIndex = ɵ$index_25_r5); });
|
|
26
|
+
i0.ɵɵelement(1, "i", 11);
|
|
27
|
+
i0.ɵɵelementStart(2, "span", 12);
|
|
28
|
+
i0.ɵɵtext(3);
|
|
29
|
+
i0.ɵɵelementEnd();
|
|
30
|
+
i0.ɵɵelementStart(4, "span", 13);
|
|
31
|
+
i0.ɵɵtext(5);
|
|
32
|
+
i0.ɵɵelementEnd()();
|
|
33
|
+
} if (rf & 2) {
|
|
34
|
+
const recent_r4 = ctx.$implicit;
|
|
35
|
+
const ɵ$index_25_r5 = ctx.$index;
|
|
36
|
+
const ctx_r1 = i0.ɵɵnextContext(3);
|
|
37
|
+
i0.ɵɵclassProp("suggest-item-highlighted", ctx_r1.IsHighlighted(ɵ$index_25_r5));
|
|
38
|
+
i0.ɵɵadvance(3);
|
|
39
|
+
i0.ɵɵtextInterpolate(recent_r4.Query);
|
|
40
|
+
i0.ɵɵadvance(2);
|
|
41
|
+
i0.ɵɵtextInterpolate1("", recent_r4.ResultCount, " results");
|
|
42
|
+
} }
|
|
43
|
+
function SearchSuggestComponent_Conditional_0_Conditional_2_Template(rf, ctx) { if (rf & 1) {
|
|
44
|
+
const _r1 = i0.ɵɵgetCurrentView();
|
|
45
|
+
i0.ɵɵelementStart(0, "div", 2)(1, "div", 6);
|
|
46
|
+
i0.ɵɵelement(2, "i", 7);
|
|
47
|
+
i0.ɵɵelementStart(3, "span");
|
|
48
|
+
i0.ɵɵtext(4, "Recent Searches");
|
|
49
|
+
i0.ɵɵelementEnd();
|
|
50
|
+
i0.ɵɵelementStart(5, "span", 8);
|
|
51
|
+
i0.ɵɵlistener("mousedown", function SearchSuggestComponent_Conditional_0_Conditional_2_Template_span_mousedown_5_listener($event) { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.OnClearRecentClick($event)); });
|
|
52
|
+
i0.ɵɵtext(6, "Clear");
|
|
53
|
+
i0.ɵɵelementEnd()();
|
|
54
|
+
i0.ɵɵrepeaterCreate(7, SearchSuggestComponent_Conditional_0_Conditional_2_For_8_Template, 6, 4, "div", 9, _forTrack0);
|
|
55
|
+
i0.ɵɵelementEnd();
|
|
56
|
+
} if (rf & 2) {
|
|
57
|
+
const ctx_r1 = i0.ɵɵnextContext(2);
|
|
58
|
+
i0.ɵɵadvance(7);
|
|
59
|
+
i0.ɵɵrepeater(ctx_r1.VisibleRecentSearches);
|
|
60
|
+
} }
|
|
61
|
+
function SearchSuggestComponent_Conditional_0_Conditional_3_Conditional_0_For_2_Template(rf, ctx) { if (rf & 1) {
|
|
62
|
+
const _r6 = i0.ɵɵgetCurrentView();
|
|
63
|
+
i0.ɵɵelementStart(0, "div", 16);
|
|
64
|
+
i0.ɵɵlistener("mousedown", function SearchSuggestComponent_Conditional_0_Conditional_3_Conditional_0_For_2_Template_div_mousedown_0_listener($event) { const result_r7 = i0.ɵɵrestoreView(_r6).$implicit; const ctx_r1 = i0.ɵɵnextContext(4); ctx_r1.OnResultClick(result_r7); return i0.ɵɵresetView($event.preventDefault()); })("mouseenter", function SearchSuggestComponent_Conditional_0_Conditional_3_Conditional_0_For_2_Template_div_mouseenter_0_listener() { const ɵ$index_40_r8 = i0.ɵɵrestoreView(_r6).$index; const ctx_r1 = i0.ɵɵnextContext(4); return i0.ɵɵresetView(ctx_r1.HighlightedIndex = (ctx_r1.showRecentSection ? ctx_r1.VisibleRecentSearches.length : 0) + ɵ$index_40_r8); });
|
|
65
|
+
i0.ɵɵelementStart(1, "div", 17);
|
|
66
|
+
i0.ɵɵelement(2, "i");
|
|
67
|
+
i0.ɵɵelementEnd();
|
|
68
|
+
i0.ɵɵelementStart(3, "div", 18)(4, "div", 19);
|
|
69
|
+
i0.ɵɵtext(5);
|
|
70
|
+
i0.ɵɵelementEnd();
|
|
71
|
+
i0.ɵɵelementStart(6, "div", 20);
|
|
72
|
+
i0.ɵɵtext(7);
|
|
73
|
+
i0.ɵɵelementEnd()();
|
|
74
|
+
i0.ɵɵelementStart(8, "span", 21);
|
|
75
|
+
i0.ɵɵtext(9);
|
|
76
|
+
i0.ɵɵelementEnd()();
|
|
77
|
+
} if (rf & 2) {
|
|
78
|
+
const result_r7 = ctx.$implicit;
|
|
79
|
+
const ɵ$index_40_r8 = ctx.$index;
|
|
80
|
+
const ctx_r1 = i0.ɵɵnextContext(4);
|
|
81
|
+
i0.ɵɵclassProp("suggest-item-highlighted", ctx_r1.IsHighlighted((ctx_r1.showRecentSection ? ctx_r1.VisibleRecentSearches.length : 0) + ɵ$index_40_r8));
|
|
82
|
+
i0.ɵɵadvance(2);
|
|
83
|
+
i0.ɵɵclassMap(result_r7.EntityIcon || result_r7.SourceIcon || "fa-solid fa-database");
|
|
84
|
+
i0.ɵɵadvance(3);
|
|
85
|
+
i0.ɵɵtextInterpolate(result_r7.RecordName || result_r7.Title);
|
|
86
|
+
i0.ɵɵadvance(2);
|
|
87
|
+
i0.ɵɵtextInterpolate(result_r7.EntityName);
|
|
88
|
+
i0.ɵɵadvance();
|
|
89
|
+
i0.ɵɵclassMap("suggest-result-score " + ctx_r1.GetScoreClass(result_r7.Score));
|
|
90
|
+
i0.ɵɵadvance();
|
|
91
|
+
i0.ɵɵtextInterpolate(ctx_r1.FormatScore(result_r7.Score));
|
|
92
|
+
} }
|
|
93
|
+
function SearchSuggestComponent_Conditional_0_Conditional_3_Conditional_0_Template(rf, ctx) { if (rf & 1) {
|
|
94
|
+
i0.ɵɵelementStart(0, "div", 2);
|
|
95
|
+
i0.ɵɵrepeaterCreate(1, SearchSuggestComponent_Conditional_0_Conditional_3_Conditional_0_For_2_Template, 10, 9, "div", 15, _forTrack1);
|
|
96
|
+
i0.ɵɵelementEnd();
|
|
97
|
+
} if (rf & 2) {
|
|
98
|
+
const ctx_r1 = i0.ɵɵnextContext(3);
|
|
99
|
+
i0.ɵɵadvance();
|
|
100
|
+
i0.ɵɵrepeater(ctx_r1.VisiblePreviewResults);
|
|
101
|
+
} }
|
|
102
|
+
function SearchSuggestComponent_Conditional_0_Conditional_3_Conditional_1_Template(rf, ctx) { if (rf & 1) {
|
|
103
|
+
i0.ɵɵelementStart(0, "div", 4)(1, "span");
|
|
104
|
+
i0.ɵɵtext(2, "No results found");
|
|
105
|
+
i0.ɵɵelementEnd()();
|
|
106
|
+
} }
|
|
107
|
+
function SearchSuggestComponent_Conditional_0_Conditional_3_Conditional_2_Template(rf, ctx) { if (rf & 1) {
|
|
108
|
+
const _r9 = i0.ɵɵgetCurrentView();
|
|
109
|
+
i0.ɵɵelementStart(0, "div", 22);
|
|
110
|
+
i0.ɵɵlistener("mousedown", function SearchSuggestComponent_Conditional_0_Conditional_3_Conditional_2_Template_div_mousedown_0_listener($event) { i0.ɵɵrestoreView(_r9); const ctx_r1 = i0.ɵɵnextContext(3); ctx_r1.OnSeeAllClick(); return i0.ɵɵresetView($event.preventDefault()); })("mouseenter", function SearchSuggestComponent_Conditional_0_Conditional_3_Conditional_2_Template_div_mouseenter_0_listener() { i0.ɵɵrestoreView(_r9); const ctx_r1 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r1.HighlightedIndex = (ctx_r1.showRecentSection ? ctx_r1.VisibleRecentSearches.length : 0) + ctx_r1.VisiblePreviewResults.length); });
|
|
111
|
+
i0.ɵɵelement(1, "i", 23);
|
|
112
|
+
i0.ɵɵelementStart(2, "span");
|
|
113
|
+
i0.ɵɵtext(3);
|
|
114
|
+
i0.ɵɵelementEnd()();
|
|
115
|
+
} if (rf & 2) {
|
|
116
|
+
const ctx_r1 = i0.ɵɵnextContext(3);
|
|
117
|
+
i0.ɵɵclassProp("suggest-item-highlighted", ctx_r1.IsHighlighted((ctx_r1.showRecentSection ? ctx_r1.VisibleRecentSearches.length : 0) + ctx_r1.VisiblePreviewResults.length));
|
|
118
|
+
i0.ɵɵadvance(3);
|
|
119
|
+
i0.ɵɵtextInterpolate1("See all ", ctx_r1.TotalCount, " results");
|
|
120
|
+
} }
|
|
121
|
+
function SearchSuggestComponent_Conditional_0_Conditional_3_Template(rf, ctx) { if (rf & 1) {
|
|
122
|
+
i0.ɵɵconditionalCreate(0, SearchSuggestComponent_Conditional_0_Conditional_3_Conditional_0_Template, 3, 0, "div", 2)(1, SearchSuggestComponent_Conditional_0_Conditional_3_Conditional_1_Template, 3, 0, "div", 4);
|
|
123
|
+
i0.ɵɵconditionalCreate(2, SearchSuggestComponent_Conditional_0_Conditional_3_Conditional_2_Template, 4, 3, "div", 14);
|
|
124
|
+
} if (rf & 2) {
|
|
125
|
+
const ctx_r1 = i0.ɵɵnextContext(2);
|
|
126
|
+
i0.ɵɵconditional(ctx_r1.VisiblePreviewResults.length > 0 ? 0 : !ctx_r1.IsLoading ? 1 : -1);
|
|
127
|
+
i0.ɵɵadvance(2);
|
|
128
|
+
i0.ɵɵconditional(ctx_r1.showSeeAllFooter ? 2 : -1);
|
|
129
|
+
} }
|
|
130
|
+
function SearchSuggestComponent_Conditional_0_Conditional_4_Conditional_4_Template(rf, ctx) { if (rf & 1) {
|
|
131
|
+
i0.ɵɵtext(0);
|
|
132
|
+
} if (rf & 2) {
|
|
133
|
+
const ctx_r1 = i0.ɵɵnextContext(3);
|
|
134
|
+
i0.ɵɵtextInterpolate1(" Min relevance: ", ctx_r1.MinRelevancePercent, "% ");
|
|
135
|
+
} }
|
|
136
|
+
function SearchSuggestComponent_Conditional_0_Conditional_4_Conditional_5_Template(rf, ctx) { if (rf & 1) {
|
|
137
|
+
i0.ɵɵtext(0, " Showing all results ");
|
|
138
|
+
} }
|
|
139
|
+
function SearchSuggestComponent_Conditional_0_Conditional_4_Conditional_7_For_4_Conditional_3_Template(rf, ctx) { if (rf & 1) {
|
|
140
|
+
i0.ɵɵelement(0, "i", 32);
|
|
141
|
+
} }
|
|
142
|
+
function SearchSuggestComponent_Conditional_0_Conditional_4_Conditional_7_For_4_Template(rf, ctx) { if (rf & 1) {
|
|
143
|
+
const _r11 = i0.ɵɵgetCurrentView();
|
|
144
|
+
i0.ɵɵelementStart(0, "div", 31);
|
|
145
|
+
i0.ɵɵlistener("mousedown", function SearchSuggestComponent_Conditional_0_Conditional_4_Conditional_7_For_4_Template_div_mousedown_0_listener($event) { const option_r12 = i0.ɵɵrestoreView(_r11).$implicit; const ctx_r1 = i0.ɵɵnextContext(4); return i0.ɵɵresetView(ctx_r1.SetMinRelevance(option_r12, $event)); });
|
|
146
|
+
i0.ɵɵelementStart(1, "span");
|
|
147
|
+
i0.ɵɵtext(2);
|
|
148
|
+
i0.ɵɵelementEnd();
|
|
149
|
+
i0.ɵɵconditionalCreate(3, SearchSuggestComponent_Conditional_0_Conditional_4_Conditional_7_For_4_Conditional_3_Template, 1, 0, "i", 32);
|
|
150
|
+
i0.ɵɵelementEnd();
|
|
151
|
+
} if (rf & 2) {
|
|
152
|
+
const option_r12 = ctx.$implicit;
|
|
153
|
+
const ctx_r1 = i0.ɵɵnextContext(4);
|
|
154
|
+
i0.ɵɵclassProp("suggest-filter-option-active", ctx_r1.IsSelectedRelevance(option_r12));
|
|
155
|
+
i0.ɵɵadvance(2);
|
|
156
|
+
i0.ɵɵtextInterpolate(ctx_r1.FormatRelevanceOption(option_r12));
|
|
157
|
+
i0.ɵɵadvance();
|
|
158
|
+
i0.ɵɵconditional(ctx_r1.IsSelectedRelevance(option_r12) ? 3 : -1);
|
|
159
|
+
} }
|
|
160
|
+
function SearchSuggestComponent_Conditional_0_Conditional_4_Conditional_7_Template(rf, ctx) { if (rf & 1) {
|
|
161
|
+
i0.ɵɵelementStart(0, "div", 28)(1, "div", 29);
|
|
162
|
+
i0.ɵɵtext(2, "Min Relevance");
|
|
163
|
+
i0.ɵɵelementEnd();
|
|
164
|
+
i0.ɵɵrepeaterCreate(3, SearchSuggestComponent_Conditional_0_Conditional_4_Conditional_7_For_4_Template, 4, 4, "div", 30, i0.ɵɵrepeaterTrackByIdentity);
|
|
165
|
+
i0.ɵɵelementEnd();
|
|
166
|
+
} if (rf & 2) {
|
|
167
|
+
const ctx_r1 = i0.ɵɵnextContext(3);
|
|
168
|
+
i0.ɵɵadvance(3);
|
|
169
|
+
i0.ɵɵrepeater(ctx_r1.MinRelevanceOptions);
|
|
170
|
+
} }
|
|
171
|
+
function SearchSuggestComponent_Conditional_0_Conditional_4_Template(rf, ctx) { if (rf & 1) {
|
|
172
|
+
const _r10 = i0.ɵɵgetCurrentView();
|
|
173
|
+
i0.ɵɵelementStart(0, "div", 3)(1, "div", 24);
|
|
174
|
+
i0.ɵɵlistener("mousedown", function SearchSuggestComponent_Conditional_0_Conditional_4_Template_div_mousedown_1_listener($event) { i0.ɵɵrestoreView(_r10); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.ToggleFilter($event)); });
|
|
175
|
+
i0.ɵɵelement(2, "i", 25);
|
|
176
|
+
i0.ɵɵelementStart(3, "span", 26);
|
|
177
|
+
i0.ɵɵconditionalCreate(4, SearchSuggestComponent_Conditional_0_Conditional_4_Conditional_4_Template, 1, 1)(5, SearchSuggestComponent_Conditional_0_Conditional_4_Conditional_5_Template, 1, 0);
|
|
178
|
+
i0.ɵɵelementEnd();
|
|
179
|
+
i0.ɵɵelement(6, "i", 27);
|
|
180
|
+
i0.ɵɵelementEnd();
|
|
181
|
+
i0.ɵɵconditionalCreate(7, SearchSuggestComponent_Conditional_0_Conditional_4_Conditional_7_Template, 5, 0, "div", 28);
|
|
182
|
+
i0.ɵɵelementEnd();
|
|
183
|
+
} if (rf & 2) {
|
|
184
|
+
const ctx_r1 = i0.ɵɵnextContext(2);
|
|
185
|
+
i0.ɵɵadvance(4);
|
|
186
|
+
i0.ɵɵconditional(ctx_r1.MinRelevancePercent > 0 ? 4 : 5);
|
|
187
|
+
i0.ɵɵadvance(2);
|
|
188
|
+
i0.ɵɵclassProp("suggest-filter-chevron-open", ctx_r1.IsFilterOpen);
|
|
189
|
+
i0.ɵɵadvance();
|
|
190
|
+
i0.ɵɵconditional(ctx_r1.IsFilterOpen ? 7 : -1);
|
|
191
|
+
} }
|
|
192
|
+
function SearchSuggestComponent_Conditional_0_Conditional_5_Template(rf, ctx) { if (rf & 1) {
|
|
193
|
+
i0.ɵɵelementStart(0, "div", 4)(1, "span");
|
|
194
|
+
i0.ɵɵtext(2);
|
|
195
|
+
i0.ɵɵelementEnd()();
|
|
196
|
+
} if (rf & 2) {
|
|
197
|
+
const ctx_r1 = i0.ɵɵnextContext(2);
|
|
198
|
+
i0.ɵɵadvance(2);
|
|
199
|
+
i0.ɵɵtextInterpolate1("Type at least ", ctx_r1.MinQueryLength, " characters to search");
|
|
200
|
+
} }
|
|
201
|
+
function SearchSuggestComponent_Conditional_0_Template(rf, ctx) { if (rf & 1) {
|
|
202
|
+
i0.ɵɵelementStart(0, "div", 0);
|
|
203
|
+
i0.ɵɵconditionalCreate(1, SearchSuggestComponent_Conditional_0_Conditional_1_Template, 4, 0, "div", 1);
|
|
204
|
+
i0.ɵɵconditionalCreate(2, SearchSuggestComponent_Conditional_0_Conditional_2_Template, 9, 0, "div", 2);
|
|
205
|
+
i0.ɵɵconditionalCreate(3, SearchSuggestComponent_Conditional_0_Conditional_3_Template, 3, 2);
|
|
206
|
+
i0.ɵɵconditionalCreate(4, SearchSuggestComponent_Conditional_0_Conditional_4_Template, 8, 4, "div", 3);
|
|
207
|
+
i0.ɵɵconditionalCreate(5, SearchSuggestComponent_Conditional_0_Conditional_5_Template, 3, 1, "div", 4);
|
|
208
|
+
i0.ɵɵelementEnd();
|
|
209
|
+
} if (rf & 2) {
|
|
210
|
+
const ctx_r1 = i0.ɵɵnextContext();
|
|
211
|
+
i0.ɵɵadvance();
|
|
212
|
+
i0.ɵɵconditional(ctx_r1.IsLoading && ctx_r1.showPreviewSection ? 1 : -1);
|
|
213
|
+
i0.ɵɵadvance();
|
|
214
|
+
i0.ɵɵconditional(ctx_r1.showRecentSection ? 2 : -1);
|
|
215
|
+
i0.ɵɵadvance();
|
|
216
|
+
i0.ɵɵconditional(ctx_r1.showPreviewSection && !ctx_r1.IsLoading ? 3 : -1);
|
|
217
|
+
i0.ɵɵadvance();
|
|
218
|
+
i0.ɵɵconditional(ctx_r1.showPreviewSection ? 4 : -1);
|
|
219
|
+
i0.ɵɵadvance();
|
|
220
|
+
i0.ɵɵconditional(!ctx_r1.showRecentSection && !ctx_r1.showPreviewSection ? 5 : -1);
|
|
221
|
+
} }
|
|
222
|
+
/** User setting key for persisting the preview min relevance threshold */
|
|
223
|
+
const PREVIEW_MIN_RELEVANCE_KEY = 'search.previewMinRelevance';
|
|
224
|
+
/** Preset options for the min relevance filter */
|
|
225
|
+
const MIN_RELEVANCE_OPTIONS = [0, 15, 30, 50, 70];
|
|
226
|
+
export class SearchSuggestComponent {
|
|
227
|
+
cdr = inject(ChangeDetectorRef);
|
|
228
|
+
// --- Configuration Inputs ---
|
|
229
|
+
/** Whether the suggest dropdown is visible */
|
|
230
|
+
IsOpen = false;
|
|
231
|
+
/** Preview search results to display */
|
|
232
|
+
PreviewResults = [];
|
|
233
|
+
/** Recent search entries to show when query is empty/short */
|
|
234
|
+
RecentSearches = [];
|
|
235
|
+
/** Total count of results for the current query (may exceed PreviewResults.length) */
|
|
236
|
+
TotalCount = 0;
|
|
237
|
+
/** Whether a search is currently loading */
|
|
238
|
+
IsLoading = false;
|
|
239
|
+
/** Current query text — used to determine whether to show recent vs preview */
|
|
240
|
+
Query = '';
|
|
241
|
+
/** Minimum query length before preview results are shown */
|
|
242
|
+
MinQueryLength = 2;
|
|
243
|
+
/** Maximum number of preview results to display */
|
|
244
|
+
MaxPreviewResults = 8;
|
|
245
|
+
/** Whether to show the recent searches section */
|
|
246
|
+
ShowRecent = true;
|
|
247
|
+
// --- Outputs ---
|
|
248
|
+
/** Emitted when the user selects a preview result */
|
|
249
|
+
ResultSelected = new EventEmitter();
|
|
250
|
+
/** Emitted when the user selects a recent search */
|
|
251
|
+
RecentSelected = new EventEmitter();
|
|
252
|
+
/** Emitted when the user clicks "See all N results" */
|
|
253
|
+
SeeAllRequested = new EventEmitter();
|
|
254
|
+
/** Emitted when the user clicks "Clear" on recent searches */
|
|
255
|
+
ClearRecentRequested = new EventEmitter();
|
|
256
|
+
// --- Internal state ---
|
|
257
|
+
/** Currently keyboard-highlighted index (-1 for none) */
|
|
258
|
+
HighlightedIndex = -1;
|
|
259
|
+
/** Min relevance percentage (0-100) for filtering preview results */
|
|
260
|
+
MinRelevancePercent = 0;
|
|
261
|
+
/** Whether the filter popover is open */
|
|
262
|
+
IsFilterOpen = false;
|
|
263
|
+
/** Available min relevance presets */
|
|
264
|
+
MinRelevanceOptions = MIN_RELEVANCE_OPTIONS;
|
|
265
|
+
// --- Lifecycle ---
|
|
266
|
+
async ngOnInit() {
|
|
267
|
+
await this.loadMinRelevanceSetting();
|
|
268
|
+
}
|
|
269
|
+
// --- Public Methods ---
|
|
270
|
+
/** Move keyboard highlight up */
|
|
271
|
+
NavigateUp() {
|
|
272
|
+
const maxIndex = this.getNavigableItemCount() - 1;
|
|
273
|
+
if (maxIndex < 0)
|
|
274
|
+
return;
|
|
275
|
+
this.HighlightedIndex--;
|
|
276
|
+
if (this.HighlightedIndex < 0) {
|
|
277
|
+
this.HighlightedIndex = maxIndex;
|
|
278
|
+
}
|
|
279
|
+
this.cdr.detectChanges();
|
|
280
|
+
}
|
|
281
|
+
/** Move keyboard highlight down */
|
|
282
|
+
NavigateDown() {
|
|
283
|
+
const maxIndex = this.getNavigableItemCount() - 1;
|
|
284
|
+
if (maxIndex < 0)
|
|
285
|
+
return;
|
|
286
|
+
this.HighlightedIndex++;
|
|
287
|
+
if (this.HighlightedIndex > maxIndex) {
|
|
288
|
+
this.HighlightedIndex = 0;
|
|
289
|
+
}
|
|
290
|
+
this.cdr.detectChanges();
|
|
291
|
+
}
|
|
292
|
+
/** Select the currently highlighted item */
|
|
293
|
+
SelectHighlighted() {
|
|
294
|
+
if (this.HighlightedIndex < 0)
|
|
295
|
+
return;
|
|
296
|
+
if (this.showRecentSection) {
|
|
297
|
+
// Navigating recent searches
|
|
298
|
+
const visibleRecent = this.VisibleRecentSearches;
|
|
299
|
+
if (this.HighlightedIndex < visibleRecent.length) {
|
|
300
|
+
this.OnRecentClick(visibleRecent[this.HighlightedIndex]);
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
if (this.showPreviewSection) {
|
|
305
|
+
// Navigating preview results
|
|
306
|
+
const offset = this.showRecentSection ? this.VisibleRecentSearches.length : 0;
|
|
307
|
+
const resultIndex = this.HighlightedIndex - offset;
|
|
308
|
+
const visibleResults = this.VisiblePreviewResults;
|
|
309
|
+
if (resultIndex >= 0 && resultIndex < visibleResults.length) {
|
|
310
|
+
this.OnResultClick(visibleResults[resultIndex]);
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
// "See all" footer
|
|
314
|
+
if (resultIndex === visibleResults.length && this.showSeeAllFooter) {
|
|
315
|
+
this.OnSeeAllClick();
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
/** Reset the highlighted index */
|
|
320
|
+
ResetHighlight() {
|
|
321
|
+
this.HighlightedIndex = -1;
|
|
322
|
+
}
|
|
323
|
+
// --- Template getters ---
|
|
324
|
+
/** Whether to show recent searches section */
|
|
325
|
+
get showRecentSection() {
|
|
326
|
+
return this.ShowRecent && this.Query.length < this.MinQueryLength && this.RecentSearches.length > 0;
|
|
327
|
+
}
|
|
328
|
+
/** Whether to show preview results section */
|
|
329
|
+
get showPreviewSection() {
|
|
330
|
+
return this.Query.length >= this.MinQueryLength;
|
|
331
|
+
}
|
|
332
|
+
/** Whether to show the "See all N results" footer */
|
|
333
|
+
get showSeeAllFooter() {
|
|
334
|
+
return this.showPreviewSection && this.TotalCount > 0;
|
|
335
|
+
}
|
|
336
|
+
/** Visible recent searches (capped at 5) */
|
|
337
|
+
get VisibleRecentSearches() {
|
|
338
|
+
return this.RecentSearches.slice(0, 5);
|
|
339
|
+
}
|
|
340
|
+
/** Visible preview results filtered by min relevance, sorted by score descending, capped at MaxPreviewResults */
|
|
341
|
+
get VisiblePreviewResults() {
|
|
342
|
+
const minScore = this.MinRelevancePercent / 100;
|
|
343
|
+
return [...this.PreviewResults]
|
|
344
|
+
.filter(r => r.Score >= minScore)
|
|
345
|
+
.sort((a, b) => b.Score - a.Score)
|
|
346
|
+
.slice(0, this.MaxPreviewResults);
|
|
347
|
+
}
|
|
348
|
+
// --- Template event handlers ---
|
|
349
|
+
/** Handle clicking a preview result */
|
|
350
|
+
OnResultClick(result) {
|
|
351
|
+
this.ResultSelected.emit(result);
|
|
352
|
+
}
|
|
353
|
+
/** Handle clicking a recent search */
|
|
354
|
+
OnRecentClick(recent) {
|
|
355
|
+
this.RecentSelected.emit(recent.Query);
|
|
356
|
+
}
|
|
357
|
+
/** Handle clicking "See all" */
|
|
358
|
+
OnSeeAllClick() {
|
|
359
|
+
this.SeeAllRequested.emit(this.Query);
|
|
360
|
+
}
|
|
361
|
+
/** Handle clicking "Clear" on recent searches */
|
|
362
|
+
OnClearRecentClick(event) {
|
|
363
|
+
event.stopPropagation();
|
|
364
|
+
event.preventDefault();
|
|
365
|
+
this.ClearRecentRequested.emit();
|
|
366
|
+
}
|
|
367
|
+
/** Check if an item at the given index is highlighted */
|
|
368
|
+
IsHighlighted(index) {
|
|
369
|
+
return this.HighlightedIndex === index;
|
|
370
|
+
}
|
|
371
|
+
/** Format a score value as a percentage */
|
|
372
|
+
FormatScore(score) {
|
|
373
|
+
return `${Math.round(score * 100)}%`;
|
|
374
|
+
}
|
|
375
|
+
/** Get the CSS class for a score value: high (green), medium (amber), low (gray) */
|
|
376
|
+
GetScoreClass(score) {
|
|
377
|
+
if (score >= 0.40)
|
|
378
|
+
return 'suggest-score-high';
|
|
379
|
+
if (score >= 0.15)
|
|
380
|
+
return 'suggest-score-medium';
|
|
381
|
+
return 'suggest-score-low';
|
|
382
|
+
}
|
|
383
|
+
/** Toggle the filter popover */
|
|
384
|
+
ToggleFilter(event) {
|
|
385
|
+
event.stopPropagation();
|
|
386
|
+
event.preventDefault();
|
|
387
|
+
this.IsFilterOpen = !this.IsFilterOpen;
|
|
388
|
+
this.cdr.detectChanges();
|
|
389
|
+
}
|
|
390
|
+
/** Set the min relevance and persist */
|
|
391
|
+
SetMinRelevance(percent, event) {
|
|
392
|
+
event.stopPropagation();
|
|
393
|
+
event.preventDefault();
|
|
394
|
+
this.MinRelevancePercent = percent;
|
|
395
|
+
this.IsFilterOpen = false;
|
|
396
|
+
this.persistMinRelevanceSetting(percent);
|
|
397
|
+
this.cdr.detectChanges();
|
|
398
|
+
}
|
|
399
|
+
/** Whether a given option is the currently selected min relevance */
|
|
400
|
+
IsSelectedRelevance(percent) {
|
|
401
|
+
return this.MinRelevancePercent === percent;
|
|
402
|
+
}
|
|
403
|
+
/** Format a relevance option for display */
|
|
404
|
+
FormatRelevanceOption(percent) {
|
|
405
|
+
return percent === 0 ? 'Show all' : `≥ ${percent}%`;
|
|
406
|
+
}
|
|
407
|
+
// --- Private ---
|
|
408
|
+
getNavigableItemCount() {
|
|
409
|
+
let count = 0;
|
|
410
|
+
if (this.showRecentSection) {
|
|
411
|
+
count += this.VisibleRecentSearches.length;
|
|
412
|
+
}
|
|
413
|
+
if (this.showPreviewSection) {
|
|
414
|
+
count += this.VisiblePreviewResults.length;
|
|
415
|
+
if (this.showSeeAllFooter) {
|
|
416
|
+
count += 1; // "See all" item
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
return count;
|
|
420
|
+
}
|
|
421
|
+
async loadMinRelevanceSetting() {
|
|
422
|
+
try {
|
|
423
|
+
// Wait for all startup engines (including UserInfoEngine) to finish loading
|
|
424
|
+
MJGlobal.Instance.GetEventListener(true).subscribe(async (event) => {
|
|
425
|
+
switch (event.event) {
|
|
426
|
+
case MJEventType.LoggedIn:
|
|
427
|
+
// Wait for StartupManager to complete before refreshing
|
|
428
|
+
await StartupManager.Instance.Startup();
|
|
429
|
+
const engine = UserInfoEngine.Instance;
|
|
430
|
+
const saved = engine.GetSetting(PREVIEW_MIN_RELEVANCE_KEY);
|
|
431
|
+
if (saved != null) {
|
|
432
|
+
const parsed = parseInt(saved, 10);
|
|
433
|
+
if (!isNaN(parsed) && parsed >= 0 && parsed <= 100) {
|
|
434
|
+
this.MinRelevancePercent = parsed;
|
|
435
|
+
this.cdr.detectChanges();
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
});
|
|
440
|
+
}
|
|
441
|
+
catch (err) {
|
|
442
|
+
console.error('[SearchSuggest] loadMinRelevanceSetting error:', err);
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
persistMinRelevanceSetting(percent) {
|
|
446
|
+
try {
|
|
447
|
+
UserInfoEngine.Instance.SetSettingDebounced(PREVIEW_MIN_RELEVANCE_KEY, String(percent));
|
|
448
|
+
}
|
|
449
|
+
catch {
|
|
450
|
+
// Silently fail if UserInfoEngine not available
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
static ɵfac = function SearchSuggestComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || SearchSuggestComponent)(); };
|
|
454
|
+
static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: SearchSuggestComponent, selectors: [["mj-search-suggest"]], inputs: { IsOpen: "IsOpen", PreviewResults: "PreviewResults", RecentSearches: "RecentSearches", TotalCount: "TotalCount", IsLoading: "IsLoading", Query: "Query", MinQueryLength: "MinQueryLength", MaxPreviewResults: "MaxPreviewResults", ShowRecent: "ShowRecent" }, outputs: { ResultSelected: "ResultSelected", RecentSelected: "RecentSelected", SeeAllRequested: "SeeAllRequested", ClearRecentRequested: "ClearRecentRequested" }, standalone: false, decls: 1, vars: 1, consts: [[1, "suggest-dropdown"], [1, "suggest-loading"], [1, "suggest-section"], [1, "suggest-filter-bar"], [1, "suggest-empty"], [1, "fa-solid", "fa-spinner", "fa-spin", "suggest-loading-icon"], [1, "suggest-section-header"], [1, "fa-solid", "fa-clock", "suggest-section-icon"], [1, "suggest-clear-link", 3, "mousedown"], [1, "suggest-item", "suggest-recent-item", 3, "suggest-item-highlighted"], [1, "suggest-item", "suggest-recent-item", 3, "mousedown", "mouseenter"], [1, "fa-solid", "fa-clock", "suggest-item-icon"], [1, "suggest-item-text"], [1, "suggest-item-meta"], [1, "suggest-see-all", 3, "suggest-item-highlighted"], [1, "suggest-item", "suggest-result-item", 3, "suggest-item-highlighted"], [1, "suggest-item", "suggest-result-item", 3, "mousedown", "mouseenter"], [1, "suggest-result-icon"], [1, "suggest-result-body"], [1, "suggest-result-title"], [1, "suggest-result-entity"], [1, "suggest-result-score"], [1, "suggest-see-all", 3, "mousedown", "mouseenter"], [1, "fa-solid", "fa-arrow-right", "suggest-see-all-icon"], [1, "suggest-filter-toggle", 3, "mousedown"], [1, "fa-solid", "fa-sliders", "suggest-filter-icon"], [1, "suggest-filter-label"], [1, "fa-solid", "fa-chevron-down", "suggest-filter-chevron"], [1, "suggest-filter-popover"], [1, "suggest-filter-popover-header"], [1, "suggest-filter-option", 3, "suggest-filter-option-active"], [1, "suggest-filter-option", 3, "mousedown"], [1, "fa-solid", "fa-check", "suggest-filter-check"]], template: function SearchSuggestComponent_Template(rf, ctx) { if (rf & 1) {
|
|
455
|
+
i0.ɵɵconditionalCreate(0, SearchSuggestComponent_Conditional_0_Template, 6, 5, "div", 0);
|
|
456
|
+
} if (rf & 2) {
|
|
457
|
+
i0.ɵɵconditional(ctx.IsOpen ? 0 : -1);
|
|
458
|
+
} }, styles: ["\n\n\n.suggest-dropdown[_ngcontent-%COMP%] {\n position: absolute;\n top: 100%;\n left: 0;\n right: 0;\n z-index: 99999;\n margin-top: 4px;\n border: 1px solid var(--mj-border-default);\n border-radius: 10px;\n background: var(--mj-bg-surface);\n box-shadow: 0 8px 30px rgba(0, 0, 0, 0.12), 0 2px 8px rgba(0, 0, 0, 0.06);\n overflow: visible;\n animation: _ngcontent-%COMP%_suggestSlideIn 0.15s ease;\n}\n\n@keyframes _ngcontent-%COMP%_suggestSlideIn {\n from {\n opacity: 0;\n transform: translateY(-4px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n\n\n\n.suggest-section[_ngcontent-%COMP%] {\n padding: 0.25rem 0;\n}\n\n.suggest-section[_ngcontent-%COMP%] + .suggest-section[_ngcontent-%COMP%] {\n border-top: 1px solid var(--mj-border-subtle);\n}\n\n.suggest-section-header[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 0.4rem;\n padding: 0.5rem 0.75rem 0.3rem;\n font-size: 0.7rem;\n font-weight: 600;\n color: var(--mj-text-muted);\n text-transform: uppercase;\n letter-spacing: 0.04em;\n}\n\n.suggest-section-icon[_ngcontent-%COMP%] {\n font-size: 0.65rem;\n}\n\n.suggest-clear-link[_ngcontent-%COMP%] {\n margin-left: auto;\n font-size: 0.68rem;\n font-weight: 500;\n color: var(--mj-text-link, var(--mj-brand-primary));\n cursor: pointer;\n text-transform: none;\n letter-spacing: normal;\n}\n\n.suggest-clear-link[_ngcontent-%COMP%]:hover {\n text-decoration: underline;\n}\n\n\n\n.suggest-item[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 0.6rem;\n padding: 0.45rem 0.75rem;\n cursor: pointer;\n transition: background 0.08s ease;\n}\n\n.suggest-item[_ngcontent-%COMP%]:hover, \n.suggest-item-highlighted[_ngcontent-%COMP%] {\n background: var(--mj-bg-surface-hover);\n}\n\n.suggest-item-icon[_ngcontent-%COMP%] {\n color: var(--mj-text-muted);\n font-size: 0.75rem;\n width: 1rem;\n text-align: center;\n flex-shrink: 0;\n}\n\n.suggest-item-text[_ngcontent-%COMP%] {\n flex: 1;\n font-size: 0.85rem;\n color: var(--mj-text-primary);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.suggest-item-meta[_ngcontent-%COMP%] {\n font-size: 0.72rem;\n color: var(--mj-text-muted);\n flex-shrink: 0;\n}\n\n\n\n.suggest-result-item[_ngcontent-%COMP%] {\n gap: 0.65rem;\n}\n\n.suggest-result-icon[_ngcontent-%COMP%] {\n width: 1.75rem;\n height: 1.75rem;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 6px;\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n font-size: 0.72rem;\n flex-shrink: 0;\n}\n\n.suggest-result-body[_ngcontent-%COMP%] {\n flex: 1;\n min-width: 0;\n}\n\n.suggest-result-title[_ngcontent-%COMP%] {\n font-size: 0.85rem;\n font-weight: 500;\n color: var(--mj-text-primary);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.suggest-result-entity[_ngcontent-%COMP%] {\n font-size: 0.7rem;\n color: var(--mj-text-muted);\n}\n\n\n\n.suggest-result-score[_ngcontent-%COMP%] {\n font-size: 0.68rem;\n font-weight: 600;\n padding: 0.1rem 0.4rem;\n border-radius: 9999px;\n flex-shrink: 0;\n \n\n color: var(--mj-text-muted);\n background: color-mix(in srgb, var(--mj-text-muted) 10%, var(--mj-bg-surface));\n}\n\n\n\n.suggest-score-high[_ngcontent-%COMP%] {\n color: var(--mj-status-success-text, var(--mj-status-success));\n background: color-mix(in srgb, var(--mj-status-success) 12%, var(--mj-bg-surface));\n}\n\n\n\n.suggest-score-medium[_ngcontent-%COMP%] {\n color: var(--mj-status-warning-text, var(--mj-status-warning));\n background: color-mix(in srgb, var(--mj-status-warning) 12%, var(--mj-bg-surface));\n}\n\n\n\n.suggest-score-low[_ngcontent-%COMP%] {\n color: var(--mj-text-muted);\n background: color-mix(in srgb, var(--mj-text-muted) 10%, var(--mj-bg-surface));\n}\n\n\n\n.suggest-loading[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 0.5rem;\n padding: 0.85rem 0.75rem;\n color: var(--mj-text-muted);\n font-size: 0.82rem;\n}\n\n.suggest-loading-icon[_ngcontent-%COMP%] {\n color: var(--mj-brand-primary);\n}\n\n\n\n.suggest-empty[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 1rem 0.75rem;\n color: var(--mj-text-muted);\n font-size: 0.82rem;\n}\n\n\n\n.suggest-see-all[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n padding: 0.55rem 0.75rem;\n border-top: 1px solid var(--mj-border-subtle);\n cursor: pointer;\n color: var(--mj-brand-primary);\n font-size: 0.82rem;\n font-weight: 500;\n transition: background 0.08s ease;\n}\n\n.suggest-see-all[_ngcontent-%COMP%]:hover, \n.suggest-see-all.suggest-item-highlighted[_ngcontent-%COMP%] {\n background: var(--mj-bg-surface-hover);\n}\n\n.suggest-see-all-icon[_ngcontent-%COMP%] {\n font-size: 0.72rem;\n}\n\n\n\n.suggest-filter-bar[_ngcontent-%COMP%] {\n position: relative;\n border-top: 1px solid var(--mj-border-subtle);\n padding: 0.35rem 0.75rem;\n}\n\n.suggest-filter-toggle[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 0.4rem;\n cursor: pointer;\n font-size: 0.7rem;\n color: var(--mj-text-muted);\n transition: color 0.1s ease;\n user-select: none;\n}\n\n.suggest-filter-toggle[_ngcontent-%COMP%]:hover {\n color: var(--mj-text-secondary);\n}\n\n.suggest-filter-icon[_ngcontent-%COMP%] {\n font-size: 0.65rem;\n}\n\n.suggest-filter-label[_ngcontent-%COMP%] {\n flex: 1;\n}\n\n.suggest-filter-chevron[_ngcontent-%COMP%] {\n font-size: 0.55rem;\n transition: transform 0.15s ease;\n}\n\n.suggest-filter-chevron-open[_ngcontent-%COMP%] {\n transform: rotate(180deg);\n}\n\n\n\n.suggest-filter-popover[_ngcontent-%COMP%] {\n position: absolute;\n top: 100%;\n left: 0.5rem;\n right: 0.5rem;\n margin-top: 4px;\n background: var(--mj-bg-surface-elevated, var(--mj-bg-surface));\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);\n overflow: hidden;\n z-index: 10;\n animation: _ngcontent-%COMP%_suggestSlideIn 0.12s ease;\n}\n\n.suggest-filter-popover-header[_ngcontent-%COMP%] {\n padding: 0.4rem 0.6rem 0.25rem;\n font-size: 0.65rem;\n font-weight: 600;\n color: var(--mj-text-muted);\n text-transform: uppercase;\n letter-spacing: 0.04em;\n}\n\n.suggest-filter-option[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 0.35rem 0.6rem;\n cursor: pointer;\n font-size: 0.78rem;\n color: var(--mj-text-primary);\n transition: background 0.08s ease;\n}\n\n.suggest-filter-option[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-hover);\n}\n\n.suggest-filter-option-active[_ngcontent-%COMP%] {\n color: var(--mj-brand-primary);\n font-weight: 600;\n}\n\n.suggest-filter-check[_ngcontent-%COMP%] {\n font-size: 0.65rem;\n color: var(--mj-brand-primary);\n}"] });
|
|
459
|
+
}
|
|
460
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(SearchSuggestComponent, [{
|
|
461
|
+
type: Component,
|
|
462
|
+
args: [{ standalone: false, selector: 'mj-search-suggest', template: "@if (IsOpen) {\n <div class=\"suggest-dropdown\">\n @if (IsLoading && showPreviewSection) {\n <div class=\"suggest-loading\">\n <i class=\"fa-solid fa-spinner fa-spin suggest-loading-icon\"></i>\n <span>Searching...</span>\n </div>\n }\n\n <!-- Recent searches section -->\n @if (showRecentSection) {\n <div class=\"suggest-section\">\n <div class=\"suggest-section-header\">\n <i class=\"fa-solid fa-clock suggest-section-icon\"></i>\n <span>Recent Searches</span>\n <span class=\"suggest-clear-link\" (mousedown)=\"OnClearRecentClick($event)\">Clear</span>\n </div>\n @for (recent of VisibleRecentSearches; track recent.Query; let i = $index) {\n <div\n class=\"suggest-item suggest-recent-item\"\n [class.suggest-item-highlighted]=\"IsHighlighted(i)\"\n (mousedown)=\"OnRecentClick(recent); $event.preventDefault()\"\n (mouseenter)=\"HighlightedIndex = i\"\n >\n <i class=\"fa-solid fa-clock suggest-item-icon\"></i>\n <span class=\"suggest-item-text\">{{ recent.Query }}</span>\n <span class=\"suggest-item-meta\">{{ recent.ResultCount }} results</span>\n </div>\n }\n </div>\n }\n\n <!-- Preview results section -->\n @if (showPreviewSection && !IsLoading) {\n @if (VisiblePreviewResults.length > 0) {\n <div class=\"suggest-section\">\n @for (result of VisiblePreviewResults; track result.ID; let i = $index) {\n <div\n class=\"suggest-item suggest-result-item\"\n [class.suggest-item-highlighted]=\"IsHighlighted((showRecentSection ? VisibleRecentSearches.length : 0) + i)\"\n (mousedown)=\"OnResultClick(result); $event.preventDefault()\"\n (mouseenter)=\"HighlightedIndex = (showRecentSection ? VisibleRecentSearches.length : 0) + i\"\n >\n <div class=\"suggest-result-icon\">\n <i [class]=\"result.EntityIcon || result.SourceIcon || 'fa-solid fa-database'\"></i>\n </div>\n <div class=\"suggest-result-body\">\n <div class=\"suggest-result-title\">{{ result.RecordName || result.Title }}</div>\n <div class=\"suggest-result-entity\">{{ result.EntityName }}</div>\n </div>\n <span class=\"suggest-result-score\" [class]=\"'suggest-result-score ' + GetScoreClass(result.Score)\">{{ FormatScore(result.Score) }}</span>\n </div>\n }\n </div>\n } @else if (!IsLoading) {\n <div class=\"suggest-empty\">\n <span>No results found</span>\n </div>\n }\n\n <!-- See all footer -->\n @if (showSeeAllFooter) {\n <div\n class=\"suggest-see-all\"\n [class.suggest-item-highlighted]=\"IsHighlighted((showRecentSection ? VisibleRecentSearches.length : 0) + VisiblePreviewResults.length)\"\n (mousedown)=\"OnSeeAllClick(); $event.preventDefault()\"\n (mouseenter)=\"HighlightedIndex = (showRecentSection ? VisibleRecentSearches.length : 0) + VisiblePreviewResults.length\"\n >\n <i class=\"fa-solid fa-arrow-right suggest-see-all-icon\"></i>\n <span>See all {{ TotalCount }} results</span>\n </div>\n }\n }\n\n <!-- Min relevance filter bar -->\n @if (showPreviewSection) {\n <div class=\"suggest-filter-bar\">\n <div class=\"suggest-filter-toggle\" (mousedown)=\"ToggleFilter($event)\">\n <i class=\"fa-solid fa-sliders suggest-filter-icon\"></i>\n <span class=\"suggest-filter-label\">\n @if (MinRelevancePercent > 0) {\n Min relevance: {{ MinRelevancePercent }}%\n } @else {\n Showing all results\n }\n </span>\n <i class=\"fa-solid fa-chevron-down suggest-filter-chevron\" [class.suggest-filter-chevron-open]=\"IsFilterOpen\"></i>\n </div>\n\n @if (IsFilterOpen) {\n <div class=\"suggest-filter-popover\">\n <div class=\"suggest-filter-popover-header\">Min Relevance</div>\n @for (option of MinRelevanceOptions; track option) {\n <div\n class=\"suggest-filter-option\"\n [class.suggest-filter-option-active]=\"IsSelectedRelevance(option)\"\n (mousedown)=\"SetMinRelevance(option, $event)\"\n >\n <span>{{ FormatRelevanceOption(option) }}</span>\n @if (IsSelectedRelevance(option)) {\n <i class=\"fa-solid fa-check suggest-filter-check\"></i>\n }\n </div>\n }\n </div>\n }\n </div>\n }\n\n <!-- Empty state when no recent and query too short -->\n @if (!showRecentSection && !showPreviewSection) {\n <div class=\"suggest-empty\">\n <span>Type at least {{ MinQueryLength }} characters to search</span>\n </div>\n }\n </div>\n}\n", styles: ["/* Search Suggest Dropdown */\n\n.suggest-dropdown {\n position: absolute;\n top: 100%;\n left: 0;\n right: 0;\n z-index: 99999;\n margin-top: 4px;\n border: 1px solid var(--mj-border-default);\n border-radius: 10px;\n background: var(--mj-bg-surface);\n box-shadow: 0 8px 30px rgba(0, 0, 0, 0.12), 0 2px 8px rgba(0, 0, 0, 0.06);\n overflow: visible;\n animation: suggestSlideIn 0.15s ease;\n}\n\n@keyframes suggestSlideIn {\n from {\n opacity: 0;\n transform: translateY(-4px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n\n/* Sections */\n.suggest-section {\n padding: 0.25rem 0;\n}\n\n.suggest-section + .suggest-section {\n border-top: 1px solid var(--mj-border-subtle);\n}\n\n.suggest-section-header {\n display: flex;\n align-items: center;\n gap: 0.4rem;\n padding: 0.5rem 0.75rem 0.3rem;\n font-size: 0.7rem;\n font-weight: 600;\n color: var(--mj-text-muted);\n text-transform: uppercase;\n letter-spacing: 0.04em;\n}\n\n.suggest-section-icon {\n font-size: 0.65rem;\n}\n\n.suggest-clear-link {\n margin-left: auto;\n font-size: 0.68rem;\n font-weight: 500;\n color: var(--mj-text-link, var(--mj-brand-primary));\n cursor: pointer;\n text-transform: none;\n letter-spacing: normal;\n}\n\n.suggest-clear-link:hover {\n text-decoration: underline;\n}\n\n/* Items */\n.suggest-item {\n display: flex;\n align-items: center;\n gap: 0.6rem;\n padding: 0.45rem 0.75rem;\n cursor: pointer;\n transition: background 0.08s ease;\n}\n\n.suggest-item:hover,\n.suggest-item-highlighted {\n background: var(--mj-bg-surface-hover);\n}\n\n.suggest-item-icon {\n color: var(--mj-text-muted);\n font-size: 0.75rem;\n width: 1rem;\n text-align: center;\n flex-shrink: 0;\n}\n\n.suggest-item-text {\n flex: 1;\n font-size: 0.85rem;\n color: var(--mj-text-primary);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.suggest-item-meta {\n font-size: 0.72rem;\n color: var(--mj-text-muted);\n flex-shrink: 0;\n}\n\n/* Result items */\n.suggest-result-item {\n gap: 0.65rem;\n}\n\n.suggest-result-icon {\n width: 1.75rem;\n height: 1.75rem;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 6px;\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n font-size: 0.72rem;\n flex-shrink: 0;\n}\n\n.suggest-result-body {\n flex: 1;\n min-width: 0;\n}\n\n.suggest-result-title {\n font-size: 0.85rem;\n font-weight: 500;\n color: var(--mj-text-primary);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.suggest-result-entity {\n font-size: 0.7rem;\n color: var(--mj-text-muted);\n}\n\n/* Score badge \u2014 base styles */\n.suggest-result-score {\n font-size: 0.68rem;\n font-weight: 600;\n padding: 0.1rem 0.4rem;\n border-radius: 9999px;\n flex-shrink: 0;\n /* Default: neutral gray */\n color: var(--mj-text-muted);\n background: color-mix(in srgb, var(--mj-text-muted) 10%, var(--mj-bg-surface));\n}\n\n/* Score badge \u2014 high relevance (green, \u226540%) */\n.suggest-score-high {\n color: var(--mj-status-success-text, var(--mj-status-success));\n background: color-mix(in srgb, var(--mj-status-success) 12%, var(--mj-bg-surface));\n}\n\n/* Score badge \u2014 medium relevance (amber, 15\u201339%) */\n.suggest-score-medium {\n color: var(--mj-status-warning-text, var(--mj-status-warning));\n background: color-mix(in srgb, var(--mj-status-warning) 12%, var(--mj-bg-surface));\n}\n\n/* Score badge \u2014 low relevance (gray, <15%) */\n.suggest-score-low {\n color: var(--mj-text-muted);\n background: color-mix(in srgb, var(--mj-text-muted) 10%, var(--mj-bg-surface));\n}\n\n/* Loading */\n.suggest-loading {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 0.5rem;\n padding: 0.85rem 0.75rem;\n color: var(--mj-text-muted);\n font-size: 0.82rem;\n}\n\n.suggest-loading-icon {\n color: var(--mj-brand-primary);\n}\n\n/* Empty state */\n.suggest-empty {\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 1rem 0.75rem;\n color: var(--mj-text-muted);\n font-size: 0.82rem;\n}\n\n/* See all footer */\n.suggest-see-all {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n padding: 0.55rem 0.75rem;\n border-top: 1px solid var(--mj-border-subtle);\n cursor: pointer;\n color: var(--mj-brand-primary);\n font-size: 0.82rem;\n font-weight: 500;\n transition: background 0.08s ease;\n}\n\n.suggest-see-all:hover,\n.suggest-see-all.suggest-item-highlighted {\n background: var(--mj-bg-surface-hover);\n}\n\n.suggest-see-all-icon {\n font-size: 0.72rem;\n}\n\n/* --- Min Relevance Filter Bar --- */\n.suggest-filter-bar {\n position: relative;\n border-top: 1px solid var(--mj-border-subtle);\n padding: 0.35rem 0.75rem;\n}\n\n.suggest-filter-toggle {\n display: flex;\n align-items: center;\n gap: 0.4rem;\n cursor: pointer;\n font-size: 0.7rem;\n color: var(--mj-text-muted);\n transition: color 0.1s ease;\n user-select: none;\n}\n\n.suggest-filter-toggle:hover {\n color: var(--mj-text-secondary);\n}\n\n.suggest-filter-icon {\n font-size: 0.65rem;\n}\n\n.suggest-filter-label {\n flex: 1;\n}\n\n.suggest-filter-chevron {\n font-size: 0.55rem;\n transition: transform 0.15s ease;\n}\n\n.suggest-filter-chevron-open {\n transform: rotate(180deg);\n}\n\n/* Filter popover */\n.suggest-filter-popover {\n position: absolute;\n top: 100%;\n left: 0.5rem;\n right: 0.5rem;\n margin-top: 4px;\n background: var(--mj-bg-surface-elevated, var(--mj-bg-surface));\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);\n overflow: hidden;\n z-index: 10;\n animation: suggestSlideIn 0.12s ease;\n}\n\n.suggest-filter-popover-header {\n padding: 0.4rem 0.6rem 0.25rem;\n font-size: 0.65rem;\n font-weight: 600;\n color: var(--mj-text-muted);\n text-transform: uppercase;\n letter-spacing: 0.04em;\n}\n\n.suggest-filter-option {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 0.35rem 0.6rem;\n cursor: pointer;\n font-size: 0.78rem;\n color: var(--mj-text-primary);\n transition: background 0.08s ease;\n}\n\n.suggest-filter-option:hover {\n background: var(--mj-bg-surface-hover);\n}\n\n.suggest-filter-option-active {\n color: var(--mj-brand-primary);\n font-weight: 600;\n}\n\n.suggest-filter-check {\n font-size: 0.65rem;\n color: var(--mj-brand-primary);\n}\n"] }]
|
|
463
|
+
}], null, { IsOpen: [{
|
|
464
|
+
type: Input
|
|
465
|
+
}], PreviewResults: [{
|
|
466
|
+
type: Input
|
|
467
|
+
}], RecentSearches: [{
|
|
468
|
+
type: Input
|
|
469
|
+
}], TotalCount: [{
|
|
470
|
+
type: Input
|
|
471
|
+
}], IsLoading: [{
|
|
472
|
+
type: Input
|
|
473
|
+
}], Query: [{
|
|
474
|
+
type: Input
|
|
475
|
+
}], MinQueryLength: [{
|
|
476
|
+
type: Input
|
|
477
|
+
}], MaxPreviewResults: [{
|
|
478
|
+
type: Input
|
|
479
|
+
}], ShowRecent: [{
|
|
480
|
+
type: Input
|
|
481
|
+
}], ResultSelected: [{
|
|
482
|
+
type: Output
|
|
483
|
+
}], RecentSelected: [{
|
|
484
|
+
type: Output
|
|
485
|
+
}], SeeAllRequested: [{
|
|
486
|
+
type: Output
|
|
487
|
+
}], ClearRecentRequested: [{
|
|
488
|
+
type: Output
|
|
489
|
+
}] }); })();
|
|
490
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(SearchSuggestComponent, { className: "SearchSuggestComponent", filePath: "src/lib/search-suggest.component.ts", lineNumber: 36 }); })();
|
|
491
|
+
//# sourceMappingURL=search-suggest.component.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search-suggest.component.js","sourceRoot":"","sources":["../../src/lib/search-suggest.component.ts","../../src/lib/search-suggest.component.html"],"names":[],"mappings":"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","sourcesContent":["/**\n * @fileoverview Search Suggest Component\n *\n * Autocomplete dropdown showing preview results and recent searches.\n * Supports keyboard navigation (up/down/enter) for accessibility.\n * Designed to be paired with SearchInputComponent via SearchCompositeComponent.\n */\n\nimport {\n Component,\n EventEmitter,\n Input,\n OnInit,\n Output,\n ChangeDetectorRef,\n inject\n} from '@angular/core';\nimport { StartupManager } from '@memberjunction/core';\nimport { UserInfoEngine } from '@memberjunction/core-entities';\nimport { SearchResultItem } from './search-types';\nimport { RecentSearch } from './search.service';\nimport { MJEventType, MJGlobal } from '@memberjunction/global';\n\n/** User setting key for persisting the preview min relevance threshold */\nconst PREVIEW_MIN_RELEVANCE_KEY = 'search.previewMinRelevance';\n\n/** Preset options for the min relevance filter */\nconst MIN_RELEVANCE_OPTIONS = [0, 15, 30, 50, 70];\n\n@Component({\n standalone: false,\n selector: 'mj-search-suggest',\n templateUrl: './search-suggest.component.html',\n styleUrls: ['./search-suggest.component.css']\n})\nexport class SearchSuggestComponent implements OnInit {\n private cdr = inject(ChangeDetectorRef);\n\n // --- Configuration Inputs ---\n\n /** Whether the suggest dropdown is visible */\n @Input() IsOpen = false;\n\n /** Preview search results to display */\n @Input() PreviewResults: SearchResultItem[] = [];\n\n /** Recent search entries to show when query is empty/short */\n @Input() RecentSearches: RecentSearch[] = [];\n\n /** Total count of results for the current query (may exceed PreviewResults.length) */\n @Input() TotalCount = 0;\n\n /** Whether a search is currently loading */\n @Input() IsLoading = false;\n\n /** Current query text — used to determine whether to show recent vs preview */\n @Input() Query = '';\n\n /** Minimum query length before preview results are shown */\n @Input() MinQueryLength = 2;\n\n /** Maximum number of preview results to display */\n @Input() MaxPreviewResults = 8;\n\n /** Whether to show the recent searches section */\n @Input() ShowRecent = true;\n\n // --- Outputs ---\n\n /** Emitted when the user selects a preview result */\n @Output() ResultSelected = new EventEmitter<SearchResultItem>();\n\n /** Emitted when the user selects a recent search */\n @Output() RecentSelected = new EventEmitter<string>();\n\n /** Emitted when the user clicks \"See all N results\" */\n @Output() SeeAllRequested = new EventEmitter<string>();\n\n /** Emitted when the user clicks \"Clear\" on recent searches */\n @Output() ClearRecentRequested = new EventEmitter<void>();\n\n // --- Internal state ---\n\n /** Currently keyboard-highlighted index (-1 for none) */\n public HighlightedIndex = -1;\n\n /** Min relevance percentage (0-100) for filtering preview results */\n public MinRelevancePercent = 0;\n\n /** Whether the filter popover is open */\n public IsFilterOpen = false;\n\n /** Available min relevance presets */\n public readonly MinRelevanceOptions = MIN_RELEVANCE_OPTIONS;\n\n // --- Lifecycle ---\n\n async ngOnInit(): Promise<void> {\n await this.loadMinRelevanceSetting();\n }\n\n // --- Public Methods ---\n\n /** Move keyboard highlight up */\n public NavigateUp(): void {\n const maxIndex = this.getNavigableItemCount() - 1;\n if (maxIndex < 0) return;\n\n this.HighlightedIndex--;\n if (this.HighlightedIndex < 0) {\n this.HighlightedIndex = maxIndex;\n }\n this.cdr.detectChanges();\n }\n\n /** Move keyboard highlight down */\n public NavigateDown(): void {\n const maxIndex = this.getNavigableItemCount() - 1;\n if (maxIndex < 0) return;\n\n this.HighlightedIndex++;\n if (this.HighlightedIndex > maxIndex) {\n this.HighlightedIndex = 0;\n }\n this.cdr.detectChanges();\n }\n\n /** Select the currently highlighted item */\n public SelectHighlighted(): void {\n if (this.HighlightedIndex < 0) return;\n\n if (this.showRecentSection) {\n // Navigating recent searches\n const visibleRecent = this.VisibleRecentSearches;\n if (this.HighlightedIndex < visibleRecent.length) {\n this.OnRecentClick(visibleRecent[this.HighlightedIndex]);\n return;\n }\n }\n\n if (this.showPreviewSection) {\n // Navigating preview results\n const offset = this.showRecentSection ? this.VisibleRecentSearches.length : 0;\n const resultIndex = this.HighlightedIndex - offset;\n const visibleResults = this.VisiblePreviewResults;\n if (resultIndex >= 0 && resultIndex < visibleResults.length) {\n this.OnResultClick(visibleResults[resultIndex]);\n return;\n }\n\n // \"See all\" footer\n if (resultIndex === visibleResults.length && this.showSeeAllFooter) {\n this.OnSeeAllClick();\n }\n }\n }\n\n /** Reset the highlighted index */\n public ResetHighlight(): void {\n this.HighlightedIndex = -1;\n }\n\n // --- Template getters ---\n\n /** Whether to show recent searches section */\n public get showRecentSection(): boolean {\n return this.ShowRecent && this.Query.length < this.MinQueryLength && this.RecentSearches.length > 0;\n }\n\n /** Whether to show preview results section */\n public get showPreviewSection(): boolean {\n return this.Query.length >= this.MinQueryLength;\n }\n\n /** Whether to show the \"See all N results\" footer */\n public get showSeeAllFooter(): boolean {\n return this.showPreviewSection && this.TotalCount > 0;\n }\n\n /** Visible recent searches (capped at 5) */\n public get VisibleRecentSearches(): RecentSearch[] {\n return this.RecentSearches.slice(0, 5);\n }\n\n /** Visible preview results filtered by min relevance, sorted by score descending, capped at MaxPreviewResults */\n public get VisiblePreviewResults(): SearchResultItem[] {\n const minScore = this.MinRelevancePercent / 100;\n return [...this.PreviewResults]\n .filter(r => r.Score >= minScore)\n .sort((a, b) => b.Score - a.Score)\n .slice(0, this.MaxPreviewResults);\n }\n\n // --- Template event handlers ---\n\n /** Handle clicking a preview result */\n public OnResultClick(result: SearchResultItem): void {\n this.ResultSelected.emit(result);\n }\n\n /** Handle clicking a recent search */\n public OnRecentClick(recent: RecentSearch): void {\n this.RecentSelected.emit(recent.Query);\n }\n\n /** Handle clicking \"See all\" */\n public OnSeeAllClick(): void {\n this.SeeAllRequested.emit(this.Query);\n }\n\n /** Handle clicking \"Clear\" on recent searches */\n public OnClearRecentClick(event: MouseEvent): void {\n event.stopPropagation();\n event.preventDefault();\n this.ClearRecentRequested.emit();\n }\n\n /** Check if an item at the given index is highlighted */\n public IsHighlighted(index: number): boolean {\n return this.HighlightedIndex === index;\n }\n\n /** Format a score value as a percentage */\n public FormatScore(score: number): string {\n return `${Math.round(score * 100)}%`;\n }\n\n /** Get the CSS class for a score value: high (green), medium (amber), low (gray) */\n public GetScoreClass(score: number): string {\n if (score >= 0.40) return 'suggest-score-high';\n if (score >= 0.15) return 'suggest-score-medium';\n return 'suggest-score-low';\n }\n\n /** Toggle the filter popover */\n public ToggleFilter(event: MouseEvent): void {\n event.stopPropagation();\n event.preventDefault();\n this.IsFilterOpen = !this.IsFilterOpen;\n this.cdr.detectChanges();\n }\n\n /** Set the min relevance and persist */\n public SetMinRelevance(percent: number, event: MouseEvent): void {\n event.stopPropagation();\n event.preventDefault();\n this.MinRelevancePercent = percent;\n this.IsFilterOpen = false;\n this.persistMinRelevanceSetting(percent);\n this.cdr.detectChanges();\n }\n\n /** Whether a given option is the currently selected min relevance */\n public IsSelectedRelevance(percent: number): boolean {\n return this.MinRelevancePercent === percent;\n }\n\n /** Format a relevance option for display */\n public FormatRelevanceOption(percent: number): string {\n return percent === 0 ? 'Show all' : `≥ ${percent}%`;\n }\n\n // --- Private ---\n\n private getNavigableItemCount(): number {\n let count = 0;\n if (this.showRecentSection) {\n count += this.VisibleRecentSearches.length;\n }\n if (this.showPreviewSection) {\n count += this.VisiblePreviewResults.length;\n if (this.showSeeAllFooter) {\n count += 1; // \"See all\" item\n }\n }\n return count;\n }\n\n private async loadMinRelevanceSetting(): Promise<void> {\n try {\n // Wait for all startup engines (including UserInfoEngine) to finish loading\n MJGlobal.Instance.GetEventListener(true).subscribe( async (event) => {\n switch (event.event) {\n case MJEventType.LoggedIn:\n // Wait for StartupManager to complete before refreshing \n await StartupManager.Instance.Startup();\n\n const engine = UserInfoEngine.Instance;\n const saved = engine.GetSetting(PREVIEW_MIN_RELEVANCE_KEY);\n\n if (saved != null) {\n const parsed = parseInt(saved, 10);\n if (!isNaN(parsed) && parsed >= 0 && parsed <= 100) {\n this.MinRelevancePercent = parsed;\n this.cdr.detectChanges();\n }\n }\n }\n });\n } catch (err) {\n console.error('[SearchSuggest] loadMinRelevanceSetting error:', err);\n }\n }\n\n private persistMinRelevanceSetting(percent: number): void {\n try {\n UserInfoEngine.Instance.SetSettingDebounced(PREVIEW_MIN_RELEVANCE_KEY, String(percent));\n } catch {\n // Silently fail if UserInfoEngine not available\n }\n }\n}\n","@if (IsOpen) {\n <div class=\"suggest-dropdown\">\n @if (IsLoading && showPreviewSection) {\n <div class=\"suggest-loading\">\n <i class=\"fa-solid fa-spinner fa-spin suggest-loading-icon\"></i>\n <span>Searching...</span>\n </div>\n }\n\n <!-- Recent searches section -->\n @if (showRecentSection) {\n <div class=\"suggest-section\">\n <div class=\"suggest-section-header\">\n <i class=\"fa-solid fa-clock suggest-section-icon\"></i>\n <span>Recent Searches</span>\n <span class=\"suggest-clear-link\" (mousedown)=\"OnClearRecentClick($event)\">Clear</span>\n </div>\n @for (recent of VisibleRecentSearches; track recent.Query; let i = $index) {\n <div\n class=\"suggest-item suggest-recent-item\"\n [class.suggest-item-highlighted]=\"IsHighlighted(i)\"\n (mousedown)=\"OnRecentClick(recent); $event.preventDefault()\"\n (mouseenter)=\"HighlightedIndex = i\"\n >\n <i class=\"fa-solid fa-clock suggest-item-icon\"></i>\n <span class=\"suggest-item-text\">{{ recent.Query }}</span>\n <span class=\"suggest-item-meta\">{{ recent.ResultCount }} results</span>\n </div>\n }\n </div>\n }\n\n <!-- Preview results section -->\n @if (showPreviewSection && !IsLoading) {\n @if (VisiblePreviewResults.length > 0) {\n <div class=\"suggest-section\">\n @for (result of VisiblePreviewResults; track result.ID; let i = $index) {\n <div\n class=\"suggest-item suggest-result-item\"\n [class.suggest-item-highlighted]=\"IsHighlighted((showRecentSection ? VisibleRecentSearches.length : 0) + i)\"\n (mousedown)=\"OnResultClick(result); $event.preventDefault()\"\n (mouseenter)=\"HighlightedIndex = (showRecentSection ? VisibleRecentSearches.length : 0) + i\"\n >\n <div class=\"suggest-result-icon\">\n <i [class]=\"result.EntityIcon || result.SourceIcon || 'fa-solid fa-database'\"></i>\n </div>\n <div class=\"suggest-result-body\">\n <div class=\"suggest-result-title\">{{ result.RecordName || result.Title }}</div>\n <div class=\"suggest-result-entity\">{{ result.EntityName }}</div>\n </div>\n <span class=\"suggest-result-score\" [class]=\"'suggest-result-score ' + GetScoreClass(result.Score)\">{{ FormatScore(result.Score) }}</span>\n </div>\n }\n </div>\n } @else if (!IsLoading) {\n <div class=\"suggest-empty\">\n <span>No results found</span>\n </div>\n }\n\n <!-- See all footer -->\n @if (showSeeAllFooter) {\n <div\n class=\"suggest-see-all\"\n [class.suggest-item-highlighted]=\"IsHighlighted((showRecentSection ? VisibleRecentSearches.length : 0) + VisiblePreviewResults.length)\"\n (mousedown)=\"OnSeeAllClick(); $event.preventDefault()\"\n (mouseenter)=\"HighlightedIndex = (showRecentSection ? VisibleRecentSearches.length : 0) + VisiblePreviewResults.length\"\n >\n <i class=\"fa-solid fa-arrow-right suggest-see-all-icon\"></i>\n <span>See all {{ TotalCount }} results</span>\n </div>\n }\n }\n\n <!-- Min relevance filter bar -->\n @if (showPreviewSection) {\n <div class=\"suggest-filter-bar\">\n <div class=\"suggest-filter-toggle\" (mousedown)=\"ToggleFilter($event)\">\n <i class=\"fa-solid fa-sliders suggest-filter-icon\"></i>\n <span class=\"suggest-filter-label\">\n @if (MinRelevancePercent > 0) {\n Min relevance: {{ MinRelevancePercent }}%\n } @else {\n Showing all results\n }\n </span>\n <i class=\"fa-solid fa-chevron-down suggest-filter-chevron\" [class.suggest-filter-chevron-open]=\"IsFilterOpen\"></i>\n </div>\n\n @if (IsFilterOpen) {\n <div class=\"suggest-filter-popover\">\n <div class=\"suggest-filter-popover-header\">Min Relevance</div>\n @for (option of MinRelevanceOptions; track option) {\n <div\n class=\"suggest-filter-option\"\n [class.suggest-filter-option-active]=\"IsSelectedRelevance(option)\"\n (mousedown)=\"SetMinRelevance(option, $event)\"\n >\n <span>{{ FormatRelevanceOption(option) }}</span>\n @if (IsSelectedRelevance(option)) {\n <i class=\"fa-solid fa-check suggest-filter-check\"></i>\n }\n </div>\n }\n </div>\n }\n </div>\n }\n\n <!-- Empty state when no recent and query too short -->\n @if (!showRecentSection && !showPreviewSection) {\n <div class=\"suggest-empty\">\n <span>Type at least {{ MinQueryLength }} characters to search</span>\n </div>\n }\n </div>\n}\n"]}
|