@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
|
@@ -39,47 +39,77 @@ function SearchFilterComponent_For_10_Conditional_6_Template(rf, ctx) { if (rf &
|
|
|
39
39
|
i0.ɵɵadvance();
|
|
40
40
|
i0.ɵɵtextInterpolate1(" ", ctx_r1.IsAllSelected(filter_r4.Category) ? "Clear" : "All", " ");
|
|
41
41
|
} }
|
|
42
|
-
function
|
|
43
|
-
i0.ɵɵelement(0, "i"
|
|
42
|
+
function SearchFilterComponent_For_10_Conditional_7_For_2_Conditional_0_Conditional_1_Template(rf, ctx) { if (rf & 1) {
|
|
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()();
|
|
57
|
+
} if (rf & 2) {
|
|
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);
|
|
44
65
|
} }
|
|
45
|
-
function
|
|
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) {
|
|
46
70
|
i0.ɵɵelement(0, "i");
|
|
47
71
|
} if (rf & 2) {
|
|
48
|
-
const
|
|
49
|
-
i0.ɵɵclassMap(
|
|
72
|
+
const option_r6 = i0.ɵɵnextContext(2).$implicit;
|
|
73
|
+
i0.ɵɵclassMap(option_r6.Icon + " filter-option-icon");
|
|
50
74
|
} }
|
|
51
|
-
function
|
|
52
|
-
const
|
|
53
|
-
i0.ɵɵelementStart(0, "label",
|
|
54
|
-
i0.ɵɵlistener("click", function
|
|
55
|
-
i0.ɵɵelementStart(1, "span",
|
|
56
|
-
i0.ɵɵconditionalCreate(2,
|
|
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);
|
|
57
81
|
i0.ɵɵelementEnd();
|
|
58
|
-
i0.ɵɵconditionalCreate(3,
|
|
59
|
-
i0.ɵɵelementStart(4, "span",
|
|
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);
|
|
60
84
|
i0.ɵɵtext(5);
|
|
61
85
|
i0.ɵɵelementEnd();
|
|
62
|
-
i0.ɵɵelementStart(6, "span",
|
|
86
|
+
i0.ɵɵelementStart(6, "span", 23);
|
|
63
87
|
i0.ɵɵtext(7);
|
|
64
88
|
i0.ɵɵelementEnd()();
|
|
65
89
|
} if (rf & 2) {
|
|
66
|
-
const
|
|
90
|
+
const option_r6 = i0.ɵɵnextContext().$implicit;
|
|
67
91
|
const filter_r4 = i0.ɵɵnextContext(2).$implicit;
|
|
68
92
|
const ctx_r1 = i0.ɵɵnextContext();
|
|
69
93
|
i0.ɵɵadvance();
|
|
70
|
-
i0.ɵɵclassProp("filter-checkbox-checked", ctx_r1.IsOptionSelected(filter_r4.Category,
|
|
94
|
+
i0.ɵɵclassProp("filter-checkbox-checked", ctx_r1.IsOptionSelected(filter_r4.Category, option_r6.Value));
|
|
71
95
|
i0.ɵɵadvance();
|
|
72
|
-
i0.ɵɵconditional(ctx_r1.IsOptionSelected(filter_r4.Category,
|
|
96
|
+
i0.ɵɵconditional(ctx_r1.IsOptionSelected(filter_r4.Category, option_r6.Value) ? 2 : -1);
|
|
73
97
|
i0.ɵɵadvance();
|
|
74
|
-
i0.ɵɵconditional(
|
|
98
|
+
i0.ɵɵconditional(option_r6.Icon ? 3 : -1);
|
|
75
99
|
i0.ɵɵadvance(2);
|
|
76
|
-
i0.ɵɵtextInterpolate(
|
|
100
|
+
i0.ɵɵtextInterpolate(option_r6.Label);
|
|
77
101
|
i0.ɵɵadvance(2);
|
|
78
|
-
i0.ɵɵtextInterpolate(
|
|
102
|
+
i0.ɵɵtextInterpolate(option_r6.Count);
|
|
103
|
+
} }
|
|
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);
|
|
79
109
|
} }
|
|
80
110
|
function SearchFilterComponent_For_10_Conditional_7_Template(rf, ctx) { if (rf & 1) {
|
|
81
111
|
i0.ɵɵelementStart(0, "div", 16);
|
|
82
|
-
i0.ɵɵrepeaterCreate(1, SearchFilterComponent_For_10_Conditional_7_For_2_Template,
|
|
112
|
+
i0.ɵɵrepeaterCreate(1, SearchFilterComponent_For_10_Conditional_7_For_2_Template, 2, 1, null, null, _forTrack1);
|
|
83
113
|
i0.ɵɵelementEnd();
|
|
84
114
|
} if (rf & 2) {
|
|
85
115
|
const filter_r4 = i0.ɵɵnextContext().$implicit;
|
|
@@ -113,7 +143,48 @@ function SearchFilterComponent_For_10_Template(rf, ctx) { if (rf & 1) {
|
|
|
113
143
|
i0.ɵɵadvance();
|
|
114
144
|
i0.ɵɵconditional(!ctx_r1.IsCategoryCollapsed(filter_r4.Category) ? 7 : -1);
|
|
115
145
|
} }
|
|
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
|
+
} }
|
|
116
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) {
|
|
117
188
|
i0.ɵɵelementStart(0, "div", 9)(1, "p");
|
|
118
189
|
i0.ɵɵtext(2, "No filters available");
|
|
119
190
|
i0.ɵɵelementEnd()();
|
|
@@ -126,9 +197,16 @@ export class SearchFilterComponent {
|
|
|
126
197
|
ActiveFilters = {};
|
|
127
198
|
/** Whether the filter panel is collapsible */
|
|
128
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;
|
|
129
206
|
FilterChanged = new EventEmitter();
|
|
130
207
|
FiltersCleared = new EventEmitter();
|
|
131
208
|
CloseRequested = new EventEmitter();
|
|
209
|
+
MinScoreChanged = new EventEmitter();
|
|
132
210
|
/** Collapsed state per category */
|
|
133
211
|
CollapsedCategories = new Set();
|
|
134
212
|
/** Toggle a filter option */
|
|
@@ -205,8 +283,21 @@ export class SearchFilterComponent {
|
|
|
205
283
|
const selection = this.ActiveFilters[category] ?? [];
|
|
206
284
|
return selection.length === filter.Options.length;
|
|
207
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
|
+
}
|
|
208
299
|
static ɵfac = function SearchFilterComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || SearchFilterComponent)(); };
|
|
209
|
-
static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: SearchFilterComponent, selectors: [["mj-search-filter"]], inputs: { Filters: "Filters", ActiveFilters: "ActiveFilters", Collapsible: "Collapsible" }, outputs: { FilterChanged: "FilterChanged", FiltersCleared: "FiltersCleared", CloseRequested: "CloseRequested" }, standalone: false, decls:
|
|
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) {
|
|
210
301
|
i0.ɵɵelementStart(0, "div", 0)(1, "div", 1)(2, "span", 2);
|
|
211
302
|
i0.ɵɵelement(3, "i", 3);
|
|
212
303
|
i0.ɵɵtext(4, " Filters ");
|
|
@@ -218,7 +309,8 @@ export class SearchFilterComponent {
|
|
|
218
309
|
i0.ɵɵelement(8, "i", 7);
|
|
219
310
|
i0.ɵɵelementEnd()()();
|
|
220
311
|
i0.ɵɵrepeaterCreate(9, SearchFilterComponent_For_10_Template, 8, 6, "div", 8, _forTrack0);
|
|
221
|
-
i0.ɵɵconditionalCreate(11, SearchFilterComponent_Conditional_11_Template,
|
|
312
|
+
i0.ɵɵconditionalCreate(11, SearchFilterComponent_Conditional_11_Template, 10, 3, "div", 8);
|
|
313
|
+
i0.ɵɵconditionalCreate(12, SearchFilterComponent_Conditional_12_Template, 3, 0, "div", 9);
|
|
222
314
|
i0.ɵɵelementEnd();
|
|
223
315
|
} if (rf & 2) {
|
|
224
316
|
i0.ɵɵadvance(6);
|
|
@@ -226,24 +318,34 @@ export class SearchFilterComponent {
|
|
|
226
318
|
i0.ɵɵadvance(3);
|
|
227
319
|
i0.ɵɵrepeater(ctx.Filters);
|
|
228
320
|
i0.ɵɵadvance(2);
|
|
229
|
-
i0.ɵɵconditional(ctx.
|
|
230
|
-
|
|
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}"] });
|
|
231
325
|
}
|
|
232
326
|
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(SearchFilterComponent, [{
|
|
233
327
|
type: Component,
|
|
234
|
-
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 <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.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-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"] }]
|
|
235
329
|
}], null, { Filters: [{
|
|
236
330
|
type: Input
|
|
237
331
|
}], ActiveFilters: [{
|
|
238
332
|
type: Input
|
|
239
333
|
}], Collapsible: [{
|
|
240
334
|
type: Input
|
|
335
|
+
}], ShowRelevanceSlider: [{
|
|
336
|
+
type: Input
|
|
337
|
+
}], MinScorePercent: [{
|
|
338
|
+
type: Input
|
|
339
|
+
}], ServerMinScorePercent: [{
|
|
340
|
+
type: Input
|
|
241
341
|
}], FilterChanged: [{
|
|
242
342
|
type: Output
|
|
243
343
|
}], FiltersCleared: [{
|
|
244
344
|
type: Output
|
|
245
345
|
}], CloseRequested: [{
|
|
246
346
|
type: Output
|
|
347
|
+
}], MinScoreChanged: [{
|
|
348
|
+
type: Output
|
|
247
349
|
}] }); })();
|
|
248
350
|
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(SearchFilterComponent, { className: "SearchFilterComponent", filePath: "src/lib/search-filter.component.ts", lineNumber: 24 }); })();
|
|
249
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 @Output() CloseRequested = 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 /** 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","<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 <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":"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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 /** 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"}
|