@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.
Files changed (41) hide show
  1. package/dist/__tests__/search-types.test.js +6 -2
  2. package/dist/__tests__/search-types.test.js.map +1 -1
  3. package/dist/__tests__/search.service.test.js +25 -18
  4. package/dist/__tests__/search.service.test.js.map +1 -1
  5. package/dist/lib/module.d.ts +7 -4
  6. package/dist/lib/module.d.ts.map +1 -1
  7. package/dist/lib/module.js +19 -4
  8. package/dist/lib/module.js.map +1 -1
  9. package/dist/lib/search-composite.component.d.ts +79 -0
  10. package/dist/lib/search-composite.component.d.ts.map +1 -0
  11. package/dist/lib/search-composite.component.js +258 -0
  12. package/dist/lib/search-composite.component.js.map +1 -0
  13. package/dist/lib/search-filter.component.d.ts +14 -1
  14. package/dist/lib/search-filter.component.d.ts.map +1 -1
  15. package/dist/lib/search-filter.component.js +128 -26
  16. package/dist/lib/search-filter.component.js.map +1 -1
  17. package/dist/lib/search-input.component.d.ts +61 -0
  18. package/dist/lib/search-input.component.d.ts.map +1 -0
  19. package/dist/lib/search-input.component.js +192 -0
  20. package/dist/lib/search-input.component.js.map +1 -0
  21. package/dist/lib/search-overlay.component.js +2 -2
  22. package/dist/lib/search-results.component.d.ts +9 -5
  23. package/dist/lib/search-results.component.d.ts.map +1 -1
  24. package/dist/lib/search-results.component.js +117 -98
  25. package/dist/lib/search-results.component.js.map +1 -1
  26. package/dist/lib/search-suggest.component.d.ts +95 -0
  27. package/dist/lib/search-suggest.component.d.ts.map +1 -0
  28. package/dist/lib/search-suggest.component.js +491 -0
  29. package/dist/lib/search-suggest.component.js.map +1 -0
  30. package/dist/lib/search-types.d.ts +27 -1
  31. package/dist/lib/search-types.d.ts.map +1 -1
  32. package/dist/lib/search-types.js.map +1 -1
  33. package/dist/lib/search.service.d.ts +27 -5
  34. package/dist/lib/search.service.d.ts.map +1 -1
  35. package/dist/lib/search.service.js +221 -92
  36. package/dist/lib/search.service.js.map +1 -1
  37. package/dist/public-api.d.ts +3 -0
  38. package/dist/public-api.d.ts.map +1 -1
  39. package/dist/public-api.js +3 -0
  40. package/dist/public-api.js.map +1 -1
  41. package/package.json +6 -4
@@ -0,0 +1,258 @@
1
+ /**
2
+ * @fileoverview Search Composite Component
3
+ *
4
+ * Drop-in widget combining SearchInputComponent + SearchSuggestComponent.
5
+ * Handles query debouncing, preview search calls, recent searches,
6
+ * keyboard navigation delegation, and click-outside-to-close.
7
+ */
8
+ import { Component, ElementRef, EventEmitter, HostListener, Input, Output, ViewChild, ChangeDetectorRef, inject } from '@angular/core';
9
+ import { Subject } from 'rxjs';
10
+ import { takeUntil } from 'rxjs/operators';
11
+ import { SearchService } from './search.service';
12
+ import * as i0 from "@angular/core";
13
+ import * as i1 from "./search-input.component";
14
+ import * as i2 from "./search-suggest.component";
15
+ const _c0 = ["searchInput"];
16
+ const _c1 = ["searchSuggest"];
17
+ export class SearchCompositeComponent {
18
+ cdr = inject(ChangeDetectorRef);
19
+ elementRef = inject(ElementRef);
20
+ searchService = inject(SearchService);
21
+ destroy$ = new Subject();
22
+ searchInputRef;
23
+ searchSuggestRef;
24
+ // --- Configuration Inputs ---
25
+ /** Placeholder text for the search input */
26
+ Placeholder = 'Search...';
27
+ /** Whether to show keyboard shortcut hint on the input */
28
+ ShowShortcutHint = true;
29
+ /** Debounce time in milliseconds for query changes */
30
+ DebounceMs = 200;
31
+ /** Minimum query length before preview results are fetched */
32
+ MinQueryLength = 2;
33
+ /** Maximum number of preview results to show in the dropdown */
34
+ MaxPreviewResults = 8;
35
+ /** Whether to enable live preview search on typing */
36
+ EnablePreview = true;
37
+ /** Whether to enable recent searches display */
38
+ EnableRecent = true;
39
+ // --- Outputs ---
40
+ /** Emitted when a result is selected from the preview dropdown */
41
+ ResultSelected = new EventEmitter();
42
+ /** Emitted when the user presses Enter or selects a recent search (full search) */
43
+ SearchSubmitted = new EventEmitter();
44
+ /** Emitted when the user clicks "See all N results" */
45
+ SeeAllRequested = new EventEmitter();
46
+ // --- Internal state ---
47
+ Query = '';
48
+ IsSuggestOpen = false;
49
+ /** The current min relevance percent from the suggest dropdown filter */
50
+ get MinRelevancePercent() {
51
+ return this.searchSuggestRef?.MinRelevancePercent ?? 0;
52
+ }
53
+ PreviewResults = [];
54
+ RecentSearches = [];
55
+ PreviewTotalCount = 0;
56
+ IsPreviewLoading = false;
57
+ ngOnInit() {
58
+ this.subscribeToRecentSearches();
59
+ this.searchService.LoadRecentSearches(); // fire-and-forget — loads from UserInfoEngine
60
+ }
61
+ ngOnDestroy() {
62
+ this.destroy$.next();
63
+ this.destroy$.complete();
64
+ }
65
+ // --- Click outside to close ---
66
+ OnDocumentMousedown(event) {
67
+ const target = event.target;
68
+ if (target && !this.elementRef.nativeElement.contains(target)) {
69
+ this.closeSuggest();
70
+ }
71
+ }
72
+ // --- Keyboard navigation delegation ---
73
+ OnHostKeydown(event) {
74
+ if (!this.IsSuggestOpen)
75
+ return;
76
+ switch (event.key) {
77
+ case 'ArrowDown':
78
+ event.preventDefault();
79
+ this.searchSuggestRef?.NavigateDown();
80
+ break;
81
+ case 'ArrowUp':
82
+ event.preventDefault();
83
+ this.searchSuggestRef?.NavigateUp();
84
+ break;
85
+ case 'Enter':
86
+ if (this.searchSuggestRef?.HighlightedIndex >= 0) {
87
+ event.preventDefault();
88
+ this.searchSuggestRef.SelectHighlighted();
89
+ }
90
+ break;
91
+ }
92
+ }
93
+ // --- Event handlers from child components ---
94
+ /** Handle debounced query changes from the input */
95
+ OnQueryChange(query) {
96
+ this.Query = query;
97
+ if (query.length >= this.MinQueryLength && this.EnablePreview) {
98
+ this.executePreviewSearch(query);
99
+ }
100
+ else {
101
+ this.clearPreviewResults();
102
+ }
103
+ this.openSuggest();
104
+ }
105
+ /** Handle Enter press in the input */
106
+ OnQuerySubmit(query) {
107
+ // If a suggestion is highlighted, select it instead of searching
108
+ if (this.IsSuggestOpen && this.searchSuggestRef && this.searchSuggestRef.HighlightedIndex >= 0) {
109
+ this.searchSuggestRef.SelectHighlighted();
110
+ return;
111
+ }
112
+ if (query.trim().length > 0) {
113
+ this.SearchSubmitted.emit(query.trim());
114
+ this.closeSuggest();
115
+ }
116
+ }
117
+ /** Handle input focus */
118
+ OnInputFocused() {
119
+ this.openSuggest();
120
+ }
121
+ /** Handle Escape press in the input */
122
+ OnInputEscaped() {
123
+ if (this.IsSuggestOpen) {
124
+ this.closeSuggest();
125
+ }
126
+ }
127
+ /** Handle input cleared */
128
+ OnInputCleared() {
129
+ this.Query = '';
130
+ this.clearPreviewResults();
131
+ this.openSuggest();
132
+ }
133
+ /** Handle result selection from suggest dropdown */
134
+ OnResultSelected(result) {
135
+ this.ResultSelected.emit(result);
136
+ this.closeSuggest();
137
+ }
138
+ /** Handle recent search selection */
139
+ OnRecentSelected(query) {
140
+ this.Query = query;
141
+ this.SearchSubmitted.emit(query);
142
+ this.closeSuggest();
143
+ }
144
+ /** Handle "See all" click */
145
+ OnSeeAllRequested(query) {
146
+ this.SeeAllRequested.emit(query);
147
+ this.closeSuggest();
148
+ }
149
+ /** Handle "Clear" recent searches */
150
+ OnClearRecentRequested() {
151
+ this.searchService.ClearRecentSearches();
152
+ }
153
+ // --- Private ---
154
+ openSuggest() {
155
+ this.IsSuggestOpen = true;
156
+ this.searchSuggestRef?.ResetHighlight();
157
+ this.cdr.detectChanges();
158
+ }
159
+ closeSuggest() {
160
+ this.IsSuggestOpen = false;
161
+ this.searchSuggestRef?.ResetHighlight();
162
+ this.cdr.detectChanges();
163
+ }
164
+ subscribeToRecentSearches() {
165
+ this.searchService.RecentSearches$
166
+ .pipe(takeUntil(this.destroy$))
167
+ .subscribe(recents => {
168
+ this.RecentSearches = recents;
169
+ this.cdr.detectChanges();
170
+ });
171
+ }
172
+ async executePreviewSearch(query) {
173
+ this.IsPreviewLoading = true;
174
+ this.cdr.detectChanges();
175
+ try {
176
+ const response = await this.searchService.PreviewSearch(query, this.MaxPreviewResults);
177
+ // Only apply if query hasn't changed since we started
178
+ if (this.Query === query) {
179
+ this.PreviewResults = response.Results;
180
+ this.PreviewTotalCount = response.TotalCount;
181
+ }
182
+ }
183
+ catch {
184
+ this.PreviewResults = [];
185
+ this.PreviewTotalCount = 0;
186
+ }
187
+ finally {
188
+ this.IsPreviewLoading = false;
189
+ this.cdr.detectChanges();
190
+ }
191
+ }
192
+ clearPreviewResults() {
193
+ this.PreviewResults = [];
194
+ this.PreviewTotalCount = 0;
195
+ this.cdr.detectChanges();
196
+ }
197
+ static ɵfac = function SearchCompositeComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || SearchCompositeComponent)(); };
198
+ static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: SearchCompositeComponent, selectors: [["mj-search-composite"]], viewQuery: function SearchCompositeComponent_Query(rf, ctx) { if (rf & 1) {
199
+ i0.ɵɵviewQuery(_c0, 5)(_c1, 5);
200
+ } if (rf & 2) {
201
+ let _t;
202
+ i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.searchInputRef = _t.first);
203
+ i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.searchSuggestRef = _t.first);
204
+ } }, hostBindings: function SearchCompositeComponent_HostBindings(rf, ctx) { if (rf & 1) {
205
+ i0.ɵɵlistener("mousedown", function SearchCompositeComponent_mousedown_HostBindingHandler($event) { return ctx.OnDocumentMousedown($event); }, i0.ɵɵresolveDocument)("keydown", function SearchCompositeComponent_keydown_HostBindingHandler($event) { return ctx.OnHostKeydown($event); });
206
+ } }, inputs: { Placeholder: "Placeholder", ShowShortcutHint: "ShowShortcutHint", DebounceMs: "DebounceMs", MinQueryLength: "MinQueryLength", MaxPreviewResults: "MaxPreviewResults", EnablePreview: "EnablePreview", EnableRecent: "EnableRecent" }, outputs: { ResultSelected: "ResultSelected", SearchSubmitted: "SearchSubmitted", SeeAllRequested: "SeeAllRequested" }, standalone: false, decls: 5, vars: 13, consts: [["searchInput", ""], ["searchSuggest", ""], [1, "search-composite-wrap"], [3, "QueryChange", "QuerySubmit", "InputFocused", "InputEscaped", "InputCleared", "Placeholder", "Query", "ShowShortcutHint", "DebounceMs"], [3, "ResultSelected", "RecentSelected", "SeeAllRequested", "ClearRecentRequested", "IsOpen", "PreviewResults", "RecentSearches", "TotalCount", "IsLoading", "Query", "MinQueryLength", "MaxPreviewResults", "ShowRecent"]], template: function SearchCompositeComponent_Template(rf, ctx) { if (rf & 1) {
207
+ const _r1 = i0.ɵɵgetCurrentView();
208
+ i0.ɵɵelementStart(0, "div", 2)(1, "mj-search-input", 3, 0);
209
+ i0.ɵɵlistener("QueryChange", function SearchCompositeComponent_Template_mj_search_input_QueryChange_1_listener($event) { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.OnQueryChange($event)); })("QuerySubmit", function SearchCompositeComponent_Template_mj_search_input_QuerySubmit_1_listener($event) { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.OnQuerySubmit($event)); })("InputFocused", function SearchCompositeComponent_Template_mj_search_input_InputFocused_1_listener() { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.OnInputFocused()); })("InputEscaped", function SearchCompositeComponent_Template_mj_search_input_InputEscaped_1_listener() { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.OnInputEscaped()); })("InputCleared", function SearchCompositeComponent_Template_mj_search_input_InputCleared_1_listener() { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.OnInputCleared()); });
210
+ i0.ɵɵelementEnd();
211
+ i0.ɵɵelementStart(3, "mj-search-suggest", 4, 1);
212
+ i0.ɵɵlistener("ResultSelected", function SearchCompositeComponent_Template_mj_search_suggest_ResultSelected_3_listener($event) { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.OnResultSelected($event)); })("RecentSelected", function SearchCompositeComponent_Template_mj_search_suggest_RecentSelected_3_listener($event) { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.OnRecentSelected($event)); })("SeeAllRequested", function SearchCompositeComponent_Template_mj_search_suggest_SeeAllRequested_3_listener($event) { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.OnSeeAllRequested($event)); })("ClearRecentRequested", function SearchCompositeComponent_Template_mj_search_suggest_ClearRecentRequested_3_listener() { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.OnClearRecentRequested()); });
213
+ i0.ɵɵelementEnd()();
214
+ } if (rf & 2) {
215
+ i0.ɵɵadvance();
216
+ i0.ɵɵproperty("Placeholder", ctx.Placeholder)("Query", ctx.Query)("ShowShortcutHint", ctx.ShowShortcutHint)("DebounceMs", ctx.DebounceMs);
217
+ i0.ɵɵadvance(2);
218
+ i0.ɵɵproperty("IsOpen", ctx.IsSuggestOpen)("PreviewResults", ctx.PreviewResults)("RecentSearches", ctx.RecentSearches)("TotalCount", ctx.PreviewTotalCount)("IsLoading", ctx.IsPreviewLoading)("Query", ctx.Query)("MinQueryLength", ctx.MinQueryLength)("MaxPreviewResults", ctx.MaxPreviewResults)("ShowRecent", ctx.EnableRecent);
219
+ } }, dependencies: [i1.SearchInputComponent, i2.SearchSuggestComponent], styles: ["\n\n\n.search-composite-wrap[_ngcontent-%COMP%] {\n position: relative;\n display: inline-block;\n width: 100%;\n}"] });
220
+ }
221
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(SearchCompositeComponent, [{
222
+ type: Component,
223
+ args: [{ standalone: false, selector: 'mj-search-composite', template: "<div class=\"search-composite-wrap\">\n <mj-search-input\n #searchInput\n [Placeholder]=\"Placeholder\"\n [Query]=\"Query\"\n [ShowShortcutHint]=\"ShowShortcutHint\"\n [DebounceMs]=\"DebounceMs\"\n (QueryChange)=\"OnQueryChange($any($event))\"\n (QuerySubmit)=\"OnQuerySubmit($any($event))\"\n (InputFocused)=\"OnInputFocused()\"\n (InputEscaped)=\"OnInputEscaped()\"\n (InputCleared)=\"OnInputCleared()\"\n ></mj-search-input>\n\n <mj-search-suggest\n #searchSuggest\n [IsOpen]=\"IsSuggestOpen\"\n [PreviewResults]=\"PreviewResults\"\n [RecentSearches]=\"RecentSearches\"\n [TotalCount]=\"PreviewTotalCount\"\n [IsLoading]=\"IsPreviewLoading\"\n [Query]=\"Query\"\n [MinQueryLength]=\"MinQueryLength\"\n [MaxPreviewResults]=\"MaxPreviewResults\"\n [ShowRecent]=\"EnableRecent\"\n (ResultSelected)=\"OnResultSelected($any($event))\"\n (RecentSelected)=\"OnRecentSelected($any($event))\"\n (SeeAllRequested)=\"OnSeeAllRequested($any($event))\"\n (ClearRecentRequested)=\"OnClearRecentRequested()\"\n ></mj-search-suggest>\n</div>\n", styles: ["/* Search Composite Component */\n\n.search-composite-wrap {\n position: relative;\n display: inline-block;\n width: 100%;\n}\n"] }]
224
+ }], null, { searchInputRef: [{
225
+ type: ViewChild,
226
+ args: ['searchInput']
227
+ }], searchSuggestRef: [{
228
+ type: ViewChild,
229
+ args: ['searchSuggest']
230
+ }], Placeholder: [{
231
+ type: Input
232
+ }], ShowShortcutHint: [{
233
+ type: Input
234
+ }], DebounceMs: [{
235
+ type: Input
236
+ }], MinQueryLength: [{
237
+ type: Input
238
+ }], MaxPreviewResults: [{
239
+ type: Input
240
+ }], EnablePreview: [{
241
+ type: Input
242
+ }], EnableRecent: [{
243
+ type: Input
244
+ }], ResultSelected: [{
245
+ type: Output
246
+ }], SearchSubmitted: [{
247
+ type: Output
248
+ }], SeeAllRequested: [{
249
+ type: Output
250
+ }], OnDocumentMousedown: [{
251
+ type: HostListener,
252
+ args: ['document:mousedown', ['$event']]
253
+ }], OnHostKeydown: [{
254
+ type: HostListener,
255
+ args: ['keydown', ['$event']]
256
+ }] }); })();
257
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(SearchCompositeComponent, { className: "SearchCompositeComponent", filePath: "src/lib/search-composite.component.ts", lineNumber: 35 }); })();
258
+ //# sourceMappingURL=search-composite.component.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search-composite.component.js","sourceRoot":"","sources":["../../src/lib/search-composite.component.ts","../../src/lib/search-composite.component.html"],"names":[],"mappings":"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","sourcesContent":["/**\n * @fileoverview Search Composite Component\n *\n * Drop-in widget combining SearchInputComponent + SearchSuggestComponent.\n * Handles query debouncing, preview search calls, recent searches,\n * keyboard navigation delegation, and click-outside-to-close.\n */\n\nimport {\n Component,\n ElementRef,\n EventEmitter,\n HostListener,\n Input,\n OnDestroy,\n OnInit,\n Output,\n ViewChild,\n ChangeDetectorRef,\n inject\n} from '@angular/core';\nimport { Subject } from 'rxjs';\nimport { takeUntil } from 'rxjs/operators';\nimport { SearchService, RecentSearch } from './search.service';\nimport { SearchInputComponent } from './search-input.component';\nimport { SearchSuggestComponent } from './search-suggest.component';\nimport { SearchResultItem } from './search-types';\n\n@Component({\n standalone: false,\n selector: 'mj-search-composite',\n templateUrl: './search-composite.component.html',\n styleUrls: ['./search-composite.component.css']\n})\nexport class SearchCompositeComponent implements OnInit, OnDestroy {\n private cdr = inject(ChangeDetectorRef);\n private elementRef = inject(ElementRef);\n protected searchService = inject(SearchService);\n private destroy$ = new Subject<void>();\n\n @ViewChild('searchInput') searchInputRef!: SearchInputComponent;\n @ViewChild('searchSuggest') searchSuggestRef!: SearchSuggestComponent;\n\n // --- Configuration Inputs ---\n\n /** Placeholder text for the search input */\n @Input() Placeholder = 'Search...';\n\n /** Whether to show keyboard shortcut hint on the input */\n @Input() ShowShortcutHint = true;\n\n /** Debounce time in milliseconds for query changes */\n @Input() DebounceMs = 200;\n\n /** Minimum query length before preview results are fetched */\n @Input() MinQueryLength = 2;\n\n /** Maximum number of preview results to show in the dropdown */\n @Input() MaxPreviewResults = 8;\n\n /** Whether to enable live preview search on typing */\n @Input() EnablePreview = true;\n\n /** Whether to enable recent searches display */\n @Input() EnableRecent = true;\n\n // --- Outputs ---\n\n /** Emitted when a result is selected from the preview dropdown */\n @Output() ResultSelected = new EventEmitter<SearchResultItem>();\n\n /** Emitted when the user presses Enter or selects a recent search (full search) */\n @Output() SearchSubmitted = new EventEmitter<string>();\n\n /** Emitted when the user clicks \"See all N results\" */\n @Output() SeeAllRequested = new EventEmitter<string>();\n\n // --- Internal state ---\n\n public Query = '';\n public IsSuggestOpen = false;\n\n /** The current min relevance percent from the suggest dropdown filter */\n public get MinRelevancePercent(): number {\n return this.searchSuggestRef?.MinRelevancePercent ?? 0;\n }\n public PreviewResults: SearchResultItem[] = [];\n public RecentSearches: RecentSearch[] = [];\n public PreviewTotalCount = 0;\n public IsPreviewLoading = false;\n\n ngOnInit(): void {\n this.subscribeToRecentSearches();\n this.searchService.LoadRecentSearches(); // fire-and-forget — loads from UserInfoEngine\n }\n\n ngOnDestroy(): void {\n this.destroy$.next();\n this.destroy$.complete();\n }\n\n // --- Click outside to close ---\n\n @HostListener('document:mousedown', ['$event'])\n OnDocumentMousedown(event: MouseEvent): void {\n const target = event.target as Node | null;\n if (target && !this.elementRef.nativeElement.contains(target)) {\n this.closeSuggest();\n }\n }\n\n // --- Keyboard navigation delegation ---\n\n @HostListener('keydown', ['$event'])\n OnHostKeydown(event: KeyboardEvent): void {\n if (!this.IsSuggestOpen) return;\n\n switch (event.key) {\n case 'ArrowDown':\n event.preventDefault();\n this.searchSuggestRef?.NavigateDown();\n break;\n case 'ArrowUp':\n event.preventDefault();\n this.searchSuggestRef?.NavigateUp();\n break;\n case 'Enter':\n if (this.searchSuggestRef?.HighlightedIndex >= 0) {\n event.preventDefault();\n this.searchSuggestRef.SelectHighlighted();\n }\n break;\n }\n }\n\n // --- Event handlers from child components ---\n\n /** Handle debounced query changes from the input */\n public OnQueryChange(query: string): void {\n this.Query = query;\n\n if (query.length >= this.MinQueryLength && this.EnablePreview) {\n this.executePreviewSearch(query);\n } else {\n this.clearPreviewResults();\n }\n\n this.openSuggest();\n }\n\n /** Handle Enter press in the input */\n public OnQuerySubmit(query: string): void {\n // If a suggestion is highlighted, select it instead of searching\n if (this.IsSuggestOpen && this.searchSuggestRef && this.searchSuggestRef.HighlightedIndex >= 0) {\n this.searchSuggestRef.SelectHighlighted();\n return;\n }\n\n if (query.trim().length > 0) {\n this.SearchSubmitted.emit(query.trim());\n this.closeSuggest();\n }\n }\n\n /** Handle input focus */\n public OnInputFocused(): void {\n this.openSuggest();\n }\n\n /** Handle Escape press in the input */\n public OnInputEscaped(): void {\n if (this.IsSuggestOpen) {\n this.closeSuggest();\n }\n }\n\n /** Handle input cleared */\n public OnInputCleared(): void {\n this.Query = '';\n this.clearPreviewResults();\n this.openSuggest();\n }\n\n /** Handle result selection from suggest dropdown */\n public OnResultSelected(result: SearchResultItem): void {\n this.ResultSelected.emit(result);\n this.closeSuggest();\n }\n\n /** Handle recent search selection */\n public OnRecentSelected(query: string): void {\n this.Query = query;\n this.SearchSubmitted.emit(query);\n this.closeSuggest();\n }\n\n /** Handle \"See all\" click */\n public OnSeeAllRequested(query: string): void {\n this.SeeAllRequested.emit(query);\n this.closeSuggest();\n }\n\n /** Handle \"Clear\" recent searches */\n public OnClearRecentRequested(): void {\n this.searchService.ClearRecentSearches();\n }\n\n // --- Private ---\n\n private openSuggest(): void {\n this.IsSuggestOpen = true;\n this.searchSuggestRef?.ResetHighlight();\n this.cdr.detectChanges();\n }\n\n private closeSuggest(): void {\n this.IsSuggestOpen = false;\n this.searchSuggestRef?.ResetHighlight();\n this.cdr.detectChanges();\n }\n\n private subscribeToRecentSearches(): void {\n this.searchService.RecentSearches$\n .pipe(takeUntil(this.destroy$))\n .subscribe(recents => {\n this.RecentSearches = recents;\n this.cdr.detectChanges();\n });\n }\n\n private async executePreviewSearch(query: string): Promise<void> {\n this.IsPreviewLoading = true;\n this.cdr.detectChanges();\n\n try {\n const response = await this.searchService.PreviewSearch(query, this.MaxPreviewResults);\n // Only apply if query hasn't changed since we started\n if (this.Query === query) {\n this.PreviewResults = response.Results;\n this.PreviewTotalCount = response.TotalCount;\n }\n } catch {\n this.PreviewResults = [];\n this.PreviewTotalCount = 0;\n } finally {\n this.IsPreviewLoading = false;\n this.cdr.detectChanges();\n }\n }\n\n private clearPreviewResults(): void {\n this.PreviewResults = [];\n this.PreviewTotalCount = 0;\n this.cdr.detectChanges();\n }\n}\n","<div class=\"search-composite-wrap\">\n <mj-search-input\n #searchInput\n [Placeholder]=\"Placeholder\"\n [Query]=\"Query\"\n [ShowShortcutHint]=\"ShowShortcutHint\"\n [DebounceMs]=\"DebounceMs\"\n (QueryChange)=\"OnQueryChange($any($event))\"\n (QuerySubmit)=\"OnQuerySubmit($any($event))\"\n (InputFocused)=\"OnInputFocused()\"\n (InputEscaped)=\"OnInputEscaped()\"\n (InputCleared)=\"OnInputCleared()\"\n ></mj-search-input>\n\n <mj-search-suggest\n #searchSuggest\n [IsOpen]=\"IsSuggestOpen\"\n [PreviewResults]=\"PreviewResults\"\n [RecentSearches]=\"RecentSearches\"\n [TotalCount]=\"PreviewTotalCount\"\n [IsLoading]=\"IsPreviewLoading\"\n [Query]=\"Query\"\n [MinQueryLength]=\"MinQueryLength\"\n [MaxPreviewResults]=\"MaxPreviewResults\"\n [ShowRecent]=\"EnableRecent\"\n (ResultSelected)=\"OnResultSelected($any($event))\"\n (RecentSelected)=\"OnRecentSelected($any($event))\"\n (SeeAllRequested)=\"OnSeeAllRequested($any($event))\"\n (ClearRecentRequested)=\"OnClearRecentRequested()\"\n ></mj-search-suggest>\n</div>\n"]}
@@ -15,9 +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>;
20
26
  CloseRequested: EventEmitter<void>;
27
+ MinScoreChanged: EventEmitter<number>;
21
28
  /** Collapsed state per category */
22
29
  CollapsedCategories: Set<string>;
23
30
  /** Toggle a filter option */
@@ -38,7 +45,13 @@ export declare class SearchFilterComponent {
38
45
  SelectAll(category: string): void;
39
46
  /** Check if all options in a category are selected */
40
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;
41
54
  static ɵfac: i0.ɵɵFactoryDeclaration<SearchFilterComponent, never>;
42
- 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"; "CloseRequested": "CloseRequested"; }, 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>;
43
56
  }
44
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;IAC1C,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;IAMvB,6EAA6E;IACtE,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAgBxC,sDAAsD;IAC/C,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;yCA1FtC,qBAAqB;2CAArB,qBAAqB;CAgGjC"}
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"}