@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,192 @@
|
|
|
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 { Component, EventEmitter, Input, Output, ViewChild, ChangeDetectorRef, inject } from '@angular/core';
|
|
9
|
+
import { Subject } from 'rxjs';
|
|
10
|
+
import { debounceTime, takeUntil } from 'rxjs/operators';
|
|
11
|
+
import * as i0 from "@angular/core";
|
|
12
|
+
const _c0 = ["inputEl"];
|
|
13
|
+
function SearchInputComponent_Conditional_4_Template(rf, ctx) { if (rf & 1) {
|
|
14
|
+
const _r2 = i0.ɵɵgetCurrentView();
|
|
15
|
+
i0.ɵɵelementStart(0, "button", 6);
|
|
16
|
+
i0.ɵɵlistener("click", function SearchInputComponent_Conditional_4_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r2); const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.Clear()); });
|
|
17
|
+
i0.ɵɵelement(1, "i", 7);
|
|
18
|
+
i0.ɵɵelementEnd();
|
|
19
|
+
} }
|
|
20
|
+
function SearchInputComponent_Conditional_5_Template(rf, ctx) { if (rf & 1) {
|
|
21
|
+
i0.ɵɵelementStart(0, "kbd", 5);
|
|
22
|
+
i0.ɵɵtext(1);
|
|
23
|
+
i0.ɵɵelementEnd();
|
|
24
|
+
} if (rf & 2) {
|
|
25
|
+
const ctx_r2 = i0.ɵɵnextContext();
|
|
26
|
+
i0.ɵɵadvance();
|
|
27
|
+
i0.ɵɵtextInterpolate(ctx_r2.ShortcutHint);
|
|
28
|
+
} }
|
|
29
|
+
export class SearchInputComponent {
|
|
30
|
+
cdr = inject(ChangeDetectorRef);
|
|
31
|
+
destroy$ = new Subject();
|
|
32
|
+
queryInput$ = new Subject();
|
|
33
|
+
inputRef;
|
|
34
|
+
// --- Configuration Inputs ---
|
|
35
|
+
/** Placeholder text shown in the input */
|
|
36
|
+
Placeholder = 'Search...';
|
|
37
|
+
/** Current query value (two-way bindable via QueryChange) */
|
|
38
|
+
_Query = '';
|
|
39
|
+
set Query(value) {
|
|
40
|
+
if (value !== this._Query) {
|
|
41
|
+
this._Query = value;
|
|
42
|
+
this.cdr.detectChanges();
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
get Query() {
|
|
46
|
+
return this._Query;
|
|
47
|
+
}
|
|
48
|
+
/** Whether to show the keyboard shortcut hint badge */
|
|
49
|
+
ShowShortcutHint = true;
|
|
50
|
+
/** Text to display in the shortcut hint badge */
|
|
51
|
+
ShortcutHint = 'Ctrl+K';
|
|
52
|
+
/** Debounce time in milliseconds before QueryChange emits */
|
|
53
|
+
DebounceMs = 200;
|
|
54
|
+
// --- Outputs ---
|
|
55
|
+
/** Emitted after debounce when query text changes */
|
|
56
|
+
QueryChange = new EventEmitter();
|
|
57
|
+
/** Emitted immediately when the user presses Enter */
|
|
58
|
+
QuerySubmit = new EventEmitter();
|
|
59
|
+
/** Emitted when the input receives focus */
|
|
60
|
+
InputFocused = new EventEmitter();
|
|
61
|
+
/** Emitted when the input loses focus */
|
|
62
|
+
InputBlurred = new EventEmitter();
|
|
63
|
+
/** Emitted when the user presses Escape in the input */
|
|
64
|
+
InputEscaped = new EventEmitter();
|
|
65
|
+
/** Emitted when the user clears the input via the clear button */
|
|
66
|
+
InputCleared = new EventEmitter();
|
|
67
|
+
// --- Internal state ---
|
|
68
|
+
/** Whether the input currently has focus */
|
|
69
|
+
IsFocused = false;
|
|
70
|
+
ngOnInit() {
|
|
71
|
+
this.setupDebounce();
|
|
72
|
+
}
|
|
73
|
+
ngOnDestroy() {
|
|
74
|
+
this.destroy$.next();
|
|
75
|
+
this.destroy$.complete();
|
|
76
|
+
}
|
|
77
|
+
// --- Public Methods ---
|
|
78
|
+
/** Programmatically focus the input element */
|
|
79
|
+
Focus() {
|
|
80
|
+
this.inputRef?.nativeElement?.focus();
|
|
81
|
+
}
|
|
82
|
+
/** Clear the query and emit InputCleared */
|
|
83
|
+
Clear() {
|
|
84
|
+
this._Query = '';
|
|
85
|
+
this.queryInput$.next('');
|
|
86
|
+
this.QueryChange.emit('');
|
|
87
|
+
this.InputCleared.emit();
|
|
88
|
+
this.Focus();
|
|
89
|
+
this.cdr.detectChanges();
|
|
90
|
+
}
|
|
91
|
+
// --- Template event handlers ---
|
|
92
|
+
/** Handle native input event */
|
|
93
|
+
OnInput(value) {
|
|
94
|
+
this._Query = value;
|
|
95
|
+
this.queryInput$.next(value);
|
|
96
|
+
}
|
|
97
|
+
/** Handle keydown events on the input */
|
|
98
|
+
OnKeydown(event) {
|
|
99
|
+
switch (event.key) {
|
|
100
|
+
case 'Enter':
|
|
101
|
+
event.preventDefault();
|
|
102
|
+
this.QuerySubmit.emit(this._Query);
|
|
103
|
+
break;
|
|
104
|
+
case 'Escape':
|
|
105
|
+
event.preventDefault();
|
|
106
|
+
this.InputEscaped.emit();
|
|
107
|
+
break;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
/** Handle focus event */
|
|
111
|
+
OnFocus() {
|
|
112
|
+
this.IsFocused = true;
|
|
113
|
+
this.InputFocused.emit();
|
|
114
|
+
this.cdr.detectChanges();
|
|
115
|
+
}
|
|
116
|
+
/** Handle click — re-opens suggest when input already has focus */
|
|
117
|
+
OnClick() {
|
|
118
|
+
if (this.IsFocused) {
|
|
119
|
+
this.InputFocused.emit();
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
/** Handle blur event */
|
|
123
|
+
OnBlur() {
|
|
124
|
+
this.IsFocused = false;
|
|
125
|
+
this.InputBlurred.emit();
|
|
126
|
+
this.cdr.detectChanges();
|
|
127
|
+
}
|
|
128
|
+
// --- Private ---
|
|
129
|
+
setupDebounce() {
|
|
130
|
+
this.queryInput$
|
|
131
|
+
.pipe(debounceTime(this.DebounceMs), takeUntil(this.destroy$))
|
|
132
|
+
.subscribe(value => {
|
|
133
|
+
this.QueryChange.emit(value);
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
static ɵfac = function SearchInputComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || SearchInputComponent)(); };
|
|
137
|
+
static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: SearchInputComponent, selectors: [["mj-search-input"]], viewQuery: function SearchInputComponent_Query(rf, ctx) { if (rf & 1) {
|
|
138
|
+
i0.ɵɵviewQuery(_c0, 5);
|
|
139
|
+
} if (rf & 2) {
|
|
140
|
+
let _t;
|
|
141
|
+
i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.inputRef = _t.first);
|
|
142
|
+
} }, inputs: { Placeholder: "Placeholder", Query: "Query", ShowShortcutHint: "ShowShortcutHint", ShortcutHint: "ShortcutHint", DebounceMs: "DebounceMs" }, outputs: { QueryChange: "QueryChange", QuerySubmit: "QuerySubmit", InputFocused: "InputFocused", InputBlurred: "InputBlurred", InputEscaped: "InputEscaped", InputCleared: "InputCleared" }, standalone: false, decls: 6, vars: 6, consts: [["inputEl", ""], [1, "search-input-container"], [1, "fa-solid", "fa-magnifying-glass", "search-input-icon"], ["type", "text", "autocomplete", "off", "spellcheck", "false", 1, "mj-input", "search-input-field", 3, "input", "keydown", "focus", "blur", "click", "placeholder", "value"], ["title", "Clear search", "type", "button", 1, "search-input-clear"], [1, "search-input-shortcut"], ["title", "Clear search", "type", "button", 1, "search-input-clear", 3, "click"], [1, "fa-solid", "fa-times"]], template: function SearchInputComponent_Template(rf, ctx) { if (rf & 1) {
|
|
143
|
+
const _r1 = i0.ɵɵgetCurrentView();
|
|
144
|
+
i0.ɵɵelementStart(0, "div", 1);
|
|
145
|
+
i0.ɵɵelement(1, "i", 2);
|
|
146
|
+
i0.ɵɵelementStart(2, "input", 3, 0);
|
|
147
|
+
i0.ɵɵlistener("input", function SearchInputComponent_Template_input_input_2_listener($event) { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.OnInput($event.target.value)); })("keydown", function SearchInputComponent_Template_input_keydown_2_listener($event) { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.OnKeydown($event)); })("focus", function SearchInputComponent_Template_input_focus_2_listener() { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.OnFocus()); })("blur", function SearchInputComponent_Template_input_blur_2_listener() { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.OnBlur()); })("click", function SearchInputComponent_Template_input_click_2_listener() { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.OnClick()); });
|
|
148
|
+
i0.ɵɵelementEnd();
|
|
149
|
+
i0.ɵɵconditionalCreate(4, SearchInputComponent_Conditional_4_Template, 2, 0, "button", 4);
|
|
150
|
+
i0.ɵɵconditionalCreate(5, SearchInputComponent_Conditional_5_Template, 2, 1, "kbd", 5);
|
|
151
|
+
i0.ɵɵelementEnd();
|
|
152
|
+
} if (rf & 2) {
|
|
153
|
+
i0.ɵɵclassProp("search-input-focused", ctx.IsFocused);
|
|
154
|
+
i0.ɵɵadvance(2);
|
|
155
|
+
i0.ɵɵproperty("placeholder", ctx.Placeholder)("value", ctx.Query);
|
|
156
|
+
i0.ɵɵadvance(2);
|
|
157
|
+
i0.ɵɵconditional(ctx.Query.length > 0 ? 4 : -1);
|
|
158
|
+
i0.ɵɵadvance();
|
|
159
|
+
i0.ɵɵconditional(ctx.ShowShortcutHint && !ctx.IsFocused && ctx.Query.length === 0 ? 5 : -1);
|
|
160
|
+
} }, styles: ["\n\n\n.search-input-container[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n padding: 0.1rem 0.75rem;\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n background: var(--mj-bg-surface);\n transition: border-color 0.15s ease, box-shadow 0.15s ease;\n}\n\n.search-input-focused[_ngcontent-%COMP%] {\n border-color: var(--mj-brand-primary);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 15%, transparent);\n}\n\n.search-input-icon[_ngcontent-%COMP%] {\n color: var(--mj-text-muted);\n font-size: 0.85rem;\n flex-shrink: 0;\n transition: color 0.15s ease;\n}\n\n.search-input-focused[_ngcontent-%COMP%] .search-input-icon[_ngcontent-%COMP%] {\n color: var(--mj-brand-primary);\n}\n\n.search-input-field[_ngcontent-%COMP%] {\n flex: 1;\n border: none !important;\n background: transparent !important;\n padding: 0.2rem 0 !important;\n font-size: 0.9rem;\n color: var(--mj-text-primary);\n outline: none !important;\n box-shadow: none !important;\n min-width: 0;\n}\n\n.search-input-field[_ngcontent-%COMP%]::placeholder {\n color: var(--mj-text-muted);\n}\n\n.search-input-clear[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 22px;\n height: 22px;\n border: none;\n border-radius: 4px;\n background: transparent;\n color: var(--mj-text-muted);\n font-size: 0.7rem;\n cursor: pointer;\n flex-shrink: 0;\n transition: background 0.1s ease, color 0.1s ease;\n}\n\n.search-input-clear[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-primary);\n}\n\n.search-input-shortcut[_ngcontent-%COMP%] {\n display: inline-block;\n padding: 0.12rem 0.45rem;\n border-radius: 4px;\n border: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-muted);\n font-size: 0.7rem;\n font-family: inherit;\n flex-shrink: 0;\n line-height: 1.4;\n white-space: nowrap;\n}"] });
|
|
161
|
+
}
|
|
162
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(SearchInputComponent, [{
|
|
163
|
+
type: Component,
|
|
164
|
+
args: [{ standalone: false, selector: 'mj-search-input', template: "<div class=\"search-input-container\" [class.search-input-focused]=\"IsFocused\">\n <i class=\"fa-solid fa-magnifying-glass search-input-icon\"></i>\n <input\n #inputEl\n type=\"text\"\n class=\"mj-input search-input-field\"\n [placeholder]=\"Placeholder\"\n [value]=\"Query\"\n (input)=\"OnInput($any($event.target).value)\"\n (keydown)=\"OnKeydown($event)\"\n (focus)=\"OnFocus()\"\n (blur)=\"OnBlur()\"\n (click)=\"OnClick()\"\n autocomplete=\"off\"\n spellcheck=\"false\"\n />\n @if (Query.length > 0) {\n <button\n class=\"search-input-clear\"\n (click)=\"Clear()\"\n title=\"Clear search\"\n type=\"button\"\n >\n <i class=\"fa-solid fa-times\"></i>\n </button>\n }\n @if (ShowShortcutHint && !IsFocused && Query.length === 0) {\n <kbd class=\"search-input-shortcut\">{{ ShortcutHint }}</kbd>\n }\n</div>\n", styles: ["/* Search Input Component */\n\n.search-input-container {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n padding: 0.1rem 0.75rem;\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n background: var(--mj-bg-surface);\n transition: border-color 0.15s ease, box-shadow 0.15s ease;\n}\n\n.search-input-focused {\n border-color: var(--mj-brand-primary);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 15%, transparent);\n}\n\n.search-input-icon {\n color: var(--mj-text-muted);\n font-size: 0.85rem;\n flex-shrink: 0;\n transition: color 0.15s ease;\n}\n\n.search-input-focused .search-input-icon {\n color: var(--mj-brand-primary);\n}\n\n.search-input-field {\n flex: 1;\n border: none !important;\n background: transparent !important;\n padding: 0.2rem 0 !important;\n font-size: 0.9rem;\n color: var(--mj-text-primary);\n outline: none !important;\n box-shadow: none !important;\n min-width: 0;\n}\n\n.search-input-field::placeholder {\n color: var(--mj-text-muted);\n}\n\n.search-input-clear {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 22px;\n height: 22px;\n border: none;\n border-radius: 4px;\n background: transparent;\n color: var(--mj-text-muted);\n font-size: 0.7rem;\n cursor: pointer;\n flex-shrink: 0;\n transition: background 0.1s ease, color 0.1s ease;\n}\n\n.search-input-clear:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-primary);\n}\n\n.search-input-shortcut {\n display: inline-block;\n padding: 0.12rem 0.45rem;\n border-radius: 4px;\n border: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-muted);\n font-size: 0.7rem;\n font-family: inherit;\n flex-shrink: 0;\n line-height: 1.4;\n white-space: nowrap;\n}\n"] }]
|
|
165
|
+
}], null, { inputRef: [{
|
|
166
|
+
type: ViewChild,
|
|
167
|
+
args: ['inputEl']
|
|
168
|
+
}], Placeholder: [{
|
|
169
|
+
type: Input
|
|
170
|
+
}], Query: [{
|
|
171
|
+
type: Input
|
|
172
|
+
}], ShowShortcutHint: [{
|
|
173
|
+
type: Input
|
|
174
|
+
}], ShortcutHint: [{
|
|
175
|
+
type: Input
|
|
176
|
+
}], DebounceMs: [{
|
|
177
|
+
type: Input
|
|
178
|
+
}], QueryChange: [{
|
|
179
|
+
type: Output
|
|
180
|
+
}], QuerySubmit: [{
|
|
181
|
+
type: Output
|
|
182
|
+
}], InputFocused: [{
|
|
183
|
+
type: Output
|
|
184
|
+
}], InputBlurred: [{
|
|
185
|
+
type: Output
|
|
186
|
+
}], InputEscaped: [{
|
|
187
|
+
type: Output
|
|
188
|
+
}], InputCleared: [{
|
|
189
|
+
type: Output
|
|
190
|
+
}] }); })();
|
|
191
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(SearchInputComponent, { className: "SearchInputComponent", filePath: "src/lib/search-input.component.ts", lineNumber: 30 }); })();
|
|
192
|
+
//# sourceMappingURL=search-input.component.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search-input.component.js","sourceRoot":"","sources":["../../src/lib/search-input.component.ts","../../src/lib/search-input.component.html"],"names":[],"mappings":"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","sourcesContent":["/**\n * @fileoverview Search Input Component\n *\n * A reusable search text input for navbar/hero placement.\n * Features debounced query emission, keyboard shortcut hint,\n * clear button, and focus management.\n */\n\nimport {\n Component,\n ElementRef,\n EventEmitter,\n Input,\n OnDestroy,\n OnInit,\n Output,\n ViewChild,\n ChangeDetectorRef,\n inject\n} from '@angular/core';\nimport { Subject } from 'rxjs';\nimport { debounceTime, takeUntil } from 'rxjs/operators';\n\n@Component({\n standalone: false,\n selector: 'mj-search-input',\n templateUrl: './search-input.component.html',\n styleUrls: ['./search-input.component.css']\n})\nexport class SearchInputComponent implements OnInit, OnDestroy {\n private cdr = inject(ChangeDetectorRef);\n private destroy$ = new Subject<void>();\n private queryInput$ = new Subject<string>();\n\n @ViewChild('inputEl') inputRef!: ElementRef<HTMLInputElement>;\n\n // --- Configuration Inputs ---\n\n /** Placeholder text shown in the input */\n @Input() Placeholder = 'Search...';\n\n /** Current query value (two-way bindable via QueryChange) */\n private _Query = '';\n\n @Input()\n set Query(value: string) {\n if (value !== this._Query) {\n this._Query = value;\n this.cdr.detectChanges();\n }\n }\n get Query(): string {\n return this._Query;\n }\n\n /** Whether to show the keyboard shortcut hint badge */\n @Input() ShowShortcutHint = true;\n\n /** Text to display in the shortcut hint badge */\n @Input() ShortcutHint = 'Ctrl+K';\n\n /** Debounce time in milliseconds before QueryChange emits */\n @Input() DebounceMs = 200;\n\n // --- Outputs ---\n\n /** Emitted after debounce when query text changes */\n @Output() QueryChange = new EventEmitter<string>();\n\n /** Emitted immediately when the user presses Enter */\n @Output() QuerySubmit = new EventEmitter<string>();\n\n /** Emitted when the input receives focus */\n @Output() InputFocused = new EventEmitter<void>();\n\n /** Emitted when the input loses focus */\n @Output() InputBlurred = new EventEmitter<void>();\n\n /** Emitted when the user presses Escape in the input */\n @Output() InputEscaped = new EventEmitter<void>();\n\n /** Emitted when the user clears the input via the clear button */\n @Output() InputCleared = new EventEmitter<void>();\n\n // --- Internal state ---\n\n /** Whether the input currently has focus */\n public IsFocused = false;\n\n ngOnInit(): void {\n this.setupDebounce();\n }\n\n ngOnDestroy(): void {\n this.destroy$.next();\n this.destroy$.complete();\n }\n\n // --- Public Methods ---\n\n /** Programmatically focus the input element */\n public Focus(): void {\n this.inputRef?.nativeElement?.focus();\n }\n\n /** Clear the query and emit InputCleared */\n public Clear(): void {\n this._Query = '';\n this.queryInput$.next('');\n this.QueryChange.emit('');\n this.InputCleared.emit();\n this.Focus();\n this.cdr.detectChanges();\n }\n\n // --- Template event handlers ---\n\n /** Handle native input event */\n public OnInput(value: string): void {\n this._Query = value;\n this.queryInput$.next(value);\n }\n\n /** Handle keydown events on the input */\n public OnKeydown(event: KeyboardEvent): void {\n switch (event.key) {\n case 'Enter':\n event.preventDefault();\n this.QuerySubmit.emit(this._Query);\n break;\n case 'Escape':\n event.preventDefault();\n this.InputEscaped.emit();\n break;\n }\n }\n\n /** Handle focus event */\n public OnFocus(): void {\n this.IsFocused = true;\n this.InputFocused.emit();\n this.cdr.detectChanges();\n }\n\n /** Handle click — re-opens suggest when input already has focus */\n public OnClick(): void {\n if (this.IsFocused) {\n this.InputFocused.emit();\n }\n }\n\n /** Handle blur event */\n public OnBlur(): void {\n this.IsFocused = false;\n this.InputBlurred.emit();\n this.cdr.detectChanges();\n }\n\n // --- Private ---\n\n private setupDebounce(): void {\n this.queryInput$\n .pipe(\n debounceTime(this.DebounceMs),\n takeUntil(this.destroy$)\n )\n .subscribe(value => {\n this.QueryChange.emit(value);\n });\n }\n}\n","<div class=\"search-input-container\" [class.search-input-focused]=\"IsFocused\">\n <i class=\"fa-solid fa-magnifying-glass search-input-icon\"></i>\n <input\n #inputEl\n type=\"text\"\n class=\"mj-input search-input-field\"\n [placeholder]=\"Placeholder\"\n [value]=\"Query\"\n (input)=\"OnInput($any($event.target).value)\"\n (keydown)=\"OnKeydown($event)\"\n (focus)=\"OnFocus()\"\n (blur)=\"OnBlur()\"\n (click)=\"OnClick()\"\n autocomplete=\"off\"\n spellcheck=\"false\"\n />\n @if (Query.length > 0) {\n <button\n class=\"search-input-clear\"\n (click)=\"Clear()\"\n title=\"Clear search\"\n type=\"button\"\n >\n <i class=\"fa-solid fa-times\"></i>\n </button>\n }\n @if (ShowShortcutHint && !IsFocused && Query.length === 0) {\n <kbd class=\"search-input-shortcut\">{{ ShortcutHint }}</kbd>\n }\n</div>\n"]}
|
|
@@ -584,11 +584,11 @@ export class SearchOverlayComponent {
|
|
|
584
584
|
i0.ɵɵconditionalCreate(0, SearchOverlayComponent_Conditional_0_Template, 15, 7);
|
|
585
585
|
} if (rf & 2) {
|
|
586
586
|
i0.ɵɵconditional(ctx.IsOpen ? 0 : -1);
|
|
587
|
-
} }, styles: ["\n\n\n.search-backdrop[_ngcontent-%COMP%] {\n position: fixed;\n inset: 0;\n background: var(--mj-bg-overlay);\n backdrop-filter: blur(8px);\n z-index:
|
|
587
|
+
} }, styles: ["\n\n\n.search-backdrop[_ngcontent-%COMP%] {\n position: fixed;\n inset: 0;\n background: var(--mj-bg-overlay);\n backdrop-filter: blur(8px);\n z-index: 99998;\n animation: _ngcontent-%COMP%_backdropFadeIn 0.2s ease;\n}\n\n@keyframes _ngcontent-%COMP%_backdropFadeIn {\n from { opacity: 0; }\n to { opacity: 1; }\n}\n\n.search-spotlight[_ngcontent-%COMP%] {\n position: fixed;\n top: 12%;\n left: 50%;\n transform: translateX(-50%);\n width: 680px;\n max-width: calc(100vw - 2rem);\n max-height: 70vh;\n z-index: 99999;\n display: flex;\n flex-direction: column;\n border-radius: 14px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n box-shadow: 0 25px 60px rgba(0, 0, 0, 0.3);\n overflow: hidden;\n animation: _ngcontent-%COMP%_spotlightIn 0.25s ease;\n}\n\n@keyframes _ngcontent-%COMP%_spotlightIn {\n from {\n opacity: 0;\n transform: translateX(-50%) translateY(-10px) scale(0.98);\n }\n to {\n opacity: 1;\n transform: translateX(-50%) translateY(0) scale(1);\n }\n}\n\n\n\n.search-input-wrap[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n padding: 0.75rem 1rem;\n border-bottom: 1px solid var(--mj-border-default);\n gap: 0.75rem;\n}\n\n.search-icon[_ngcontent-%COMP%] {\n color: var(--mj-brand-primary);\n font-size: 1.1rem;\n flex-shrink: 0;\n}\n\n.search-input[_ngcontent-%COMP%] {\n flex: 1;\n border: none;\n background: transparent;\n font-size: 1.1rem;\n color: var(--mj-text-primary);\n outline: none;\n font-family: inherit;\n}\n\n.search-input[_ngcontent-%COMP%]::placeholder {\n color: var(--mj-text-muted);\n}\n\n.search-spinner[_ngcontent-%COMP%] {\n color: var(--mj-brand-primary);\n font-size: 1rem;\n}\n\n.search-shortcut-hint[_ngcontent-%COMP%] kbd[_ngcontent-%COMP%] {\n display: inline-block;\n padding: 0.15rem 0.5rem;\n border-radius: 4px;\n border: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-muted);\n font-size: 0.75rem;\n font-family: inherit;\n}\n\n\n\n.search-results-area[_ngcontent-%COMP%] {\n overflow-y: auto;\n max-height: calc(70vh - 56px);\n}\n\n\n\n.search-filter-bar[_ngcontent-%COMP%] {\n display: flex;\n flex-wrap: wrap;\n gap: 0.4rem;\n padding: 0.6rem 1rem;\n border-bottom: 1px solid var(--mj-border-subtle);\n}\n\n.filter-chip[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n gap: 0.35rem;\n padding: 0.25rem 0.6rem;\n border-radius: 9999px;\n border: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface);\n color: var(--mj-text-secondary);\n font-size: 0.78rem;\n cursor: pointer;\n transition: all 0.15s ease;\n}\n\n.filter-chip[_ngcontent-%COMP%]:hover {\n border-color: var(--mj-brand-primary);\n color: var(--mj-text-primary);\n}\n\n.filter-chip-active[_ngcontent-%COMP%] {\n background: color-mix(in srgb, var(--mj-brand-primary) 12%, var(--mj-bg-surface));\n border-color: var(--mj-brand-primary);\n color: var(--mj-brand-primary);\n}\n\n.filter-chip-count[_ngcontent-%COMP%] {\n opacity: 0.6;\n font-size: 0.72rem;\n}\n\n.filter-clear-btn[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n gap: 0.25rem;\n padding: 0.25rem 0.6rem;\n border-radius: 9999px;\n border: none;\n background: transparent;\n color: var(--mj-text-muted);\n font-size: 0.78rem;\n cursor: pointer;\n}\n\n.filter-clear-btn[_ngcontent-%COMP%]:hover {\n color: var(--mj-status-error);\n}\n\n\n\n.search-empty-state[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 2.5rem 1rem;\n gap: 0.5rem;\n}\n\n.search-empty-state-compact[_ngcontent-%COMP%] {\n padding: 1.5rem 1rem;\n}\n\n.search-empty-icon[_ngcontent-%COMP%] {\n font-size: 2rem;\n color: var(--mj-text-muted);\n margin-bottom: 0.5rem;\n}\n\n.search-empty-title[_ngcontent-%COMP%] {\n color: var(--mj-text-primary);\n font-weight: 600;\n font-size: 0.95rem;\n}\n\n.search-empty-text[_ngcontent-%COMP%] {\n color: var(--mj-text-muted);\n font-size: 0.85rem;\n}\n\n\n\n.search-recent[_ngcontent-%COMP%] {\n padding: 0.5rem 0;\n}\n\n.search-recent-header[_ngcontent-%COMP%] {\n padding: 0.5rem 1rem;\n}\n\n.search-recent-title[_ngcontent-%COMP%] {\n font-size: 0.75rem;\n color: var(--mj-text-muted);\n text-transform: uppercase;\n letter-spacing: 0.05em;\n font-weight: 600;\n}\n\n.search-recent-item[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 0.75rem;\n padding: 0.5rem 1rem;\n cursor: pointer;\n color: var(--mj-text-secondary);\n font-size: 0.9rem;\n transition: background 0.1s ease;\n}\n\n.search-recent-item[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-hover);\n}\n\n.search-recent-icon[_ngcontent-%COMP%] {\n font-size: 0.8rem;\n color: var(--mj-text-muted);\n}\n\n.search-recent-count[_ngcontent-%COMP%] {\n margin-left: auto;\n font-size: 0.75rem;\n color: var(--mj-text-muted);\n}\n\n\n\n.result-group[_ngcontent-%COMP%] {\n padding: 0.25rem 0;\n}\n\n.result-group-header[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n padding: 0.5rem 1rem;\n color: var(--mj-text-muted);\n font-size: 0.75rem;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n font-weight: 600;\n}\n\n.result-group-label[_ngcontent-%COMP%] {\n flex: 1;\n}\n\n.result-group-count[_ngcontent-%COMP%] {\n font-size: 0.72rem;\n color: var(--mj-text-disabled);\n}\n\n\n\n.result-item[_ngcontent-%COMP%] {\n display: flex;\n align-items: flex-start;\n gap: 0.75rem;\n padding: 0.6rem 1rem;\n cursor: pointer;\n transition: background 0.1s ease;\n}\n\n.result-item[_ngcontent-%COMP%]:hover, \n.result-item-highlighted[_ngcontent-%COMP%] {\n background: var(--mj-bg-surface-hover);\n}\n\n.result-item-icon[_ngcontent-%COMP%] {\n width: 2rem;\n height: 2rem;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 8px;\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-brand-primary);\n font-size: 0.85rem;\n flex-shrink: 0;\n margin-top: 0.1rem;\n}\n\n.result-item-content[_ngcontent-%COMP%] {\n flex: 1;\n min-width: 0;\n}\n\n.result-item-title[_ngcontent-%COMP%] {\n color: var(--mj-text-primary);\n font-weight: 500;\n font-size: 0.9rem;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.result-item-snippet[_ngcontent-%COMP%] {\n color: var(--mj-text-muted);\n font-size: 0.8rem;\n line-height: 1.4;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n margin-top: 0.15rem;\n}\n\n.result-item-tags[_ngcontent-%COMP%] {\n display: flex;\n gap: 0.3rem;\n margin-top: 0.3rem;\n}\n\n.result-tag[_ngcontent-%COMP%] {\n display: inline-block;\n padding: 0.1rem 0.4rem;\n border-radius: 4px;\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-muted);\n font-size: 0.7rem;\n}\n\n.result-item-score[_ngcontent-%COMP%] {\n flex-shrink: 0;\n margin-top: 0.2rem;\n}\n\n.score-badge[_ngcontent-%COMP%] {\n display: inline-block;\n padding: 0.15rem 0.45rem;\n border-radius: 9999px;\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 font-weight: 600;\n}\n\n\n\n.search-footer[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 0.5rem 1rem;\n border-top: 1px solid var(--mj-border-subtle);\n}\n\n.search-footer-count[_ngcontent-%COMP%] {\n color: var(--mj-text-muted);\n font-size: 0.78rem;\n}\n\n\n\n.agent-cta-btn[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n gap: 0.5rem;\n padding: 0.6rem 1.2rem;\n border-radius: 8px;\n border: none;\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n font-size: 0.85rem;\n font-weight: 500;\n cursor: pointer;\n transition: background 0.15s ease;\n}\n\n.agent-cta-btn[_ngcontent-%COMP%]:hover {\n background: var(--mj-brand-primary-hover);\n}\n\n.agent-cta-btn-small[_ngcontent-%COMP%] {\n padding: 0.35rem 0.8rem;\n font-size: 0.78rem;\n}\n\n\n\n@media (max-width: 640px) {\n .search-spotlight[_ngcontent-%COMP%] {\n top: 0;\n left: 0;\n right: 0;\n transform: none;\n width: 100%;\n max-width: 100%;\n max-height: 100vh;\n border-radius: 0;\n }\n\n .search-results-area[_ngcontent-%COMP%] {\n max-height: calc(100vh - 56px);\n }\n\n .search-filter-bar[_ngcontent-%COMP%] {\n overflow-x: auto;\n flex-wrap: nowrap;\n -webkit-overflow-scrolling: touch;\n }\n}"] });
|
|
588
588
|
}
|
|
589
589
|
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(SearchOverlayComponent, [{
|
|
590
590
|
type: Component,
|
|
591
|
-
args: [{ standalone: false, selector: 'mj-search-overlay', template: "@if (IsOpen) {\n <!-- Backdrop -->\n <div class=\"search-backdrop\" (click)=\"OnBackdropClick()\"></div>\n\n <!-- Spotlight container -->\n <div class=\"search-spotlight\" (click)=\"$event.stopPropagation()\">\n <!-- Search input area -->\n <div class=\"search-input-wrap\">\n <i class=\"fa-solid fa-magnifying-glass search-icon\"></i>\n <input\n #searchInput\n type=\"text\"\n class=\"search-input\"\n [placeholder]=\"Placeholder\"\n [value]=\"Query\"\n (input)=\"OnQueryInput($any($event.target).value)\"\n autocomplete=\"off\"\n spellcheck=\"false\"\n />\n @if (IsSearching) {\n <div class=\"search-spinner\">\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n </div>\n }\n <div class=\"search-shortcut-hint\">\n <kbd>ESC</kbd>\n </div>\n </div>\n\n <!-- Results area -->\n <div class=\"search-results-area\">\n @if (ShowFilters && Filters.length > 0) {\n <!-- Filter chips -->\n <div class=\"search-filter-bar\">\n @for (filter of Filters; track filter.Category) {\n @for (option of filter.Options; track option.Value) {\n <button\n class=\"filter-chip\"\n [class.filter-chip-active]=\"IsFilterActive(filter.Category, option.Value)\"\n (click)=\"OnFilterToggle(filter.Category, option.Value)\"\n >\n @if (option.Icon) {\n <i [class]=\"option.Icon\"></i>\n }\n <span>{{ option.Label }}</span>\n <span class=\"filter-chip-count\">{{ option.Count }}</span>\n </button>\n }\n }\n @if (hasActiveFilters()) {\n <button class=\"filter-clear-btn\" (click)=\"ClearAllFilters()\">\n <i class=\"fa-solid fa-times\"></i> Clear\n </button>\n }\n </div>\n }\n\n @if (HasSearched && AllResults.length === 0 && !IsSearching) {\n <!-- No results -->\n <div class=\"search-empty-state\">\n <i class=\"fa-solid fa-search search-empty-icon\"></i>\n <p class=\"search-empty-title\">No results found</p>\n <p class=\"search-empty-text\">Try different keywords or remove some filters</p>\n @if (ShowAgentCta) {\n <button class=\"agent-cta-btn\" (click)=\"OnAgentCtaClick()\">\n <i class=\"fa-solid fa-robot\"></i>\n {{ AgentCtaText }}\n </button>\n }\n </div>\n }\n\n @if (!HasSearched && !IsSearching) {\n <!-- Recent searches -->\n <div class=\"search-recent\">\n @if (searchService.RecentSearches$.value.length > 0) {\n <div class=\"search-recent-header\">\n <span class=\"search-recent-title\">Recent Searches</span>\n </div>\n @for (recent of searchService.RecentSearches$.value; track recent.Query; let i = $index) {\n @if (i < 5) {\n <div class=\"search-recent-item\" (click)=\"OnQueryInput(recent.Query)\">\n <i class=\"fa-solid fa-clock search-recent-icon\"></i>\n <span>{{ recent.Query }}</span>\n <span class=\"search-recent-count\">{{ recent.ResultCount }} results</span>\n </div>\n }\n }\n } @else {\n <div class=\"search-empty-state search-empty-state-compact\">\n <i class=\"fa-solid fa-magnifying-glass search-empty-icon\"></i>\n <p class=\"search-empty-text\">Start typing to search across all your knowledge</p>\n </div>\n }\n </div>\n }\n\n @if (AllResults.length > 0) {\n <!-- Grouped results -->\n @for (group of ResultGroups; track group.SourceType) {\n <div class=\"result-group\">\n <div class=\"result-group-header\">\n <i [class]=\"group.Icon\"></i>\n <span class=\"result-group-label\">{{ group.Label }}</span>\n <span class=\"result-group-count\">{{ group.TotalCount }}</span>\n </div>\n @for (result of group.Results; track result.ID; let ri = $index) {\n <div\n class=\"result-item\"\n [class.result-item-highlighted]=\"isResultHighlighted(result)\"\n (click)=\"OnResultClick(result, $event)\"\n (mouseenter)=\"HighlightedIndex = getFlatIndex(result)\"\n >\n <div class=\"result-item-icon\">\n <i [class]=\"result.SourceIcon\"></i>\n </div>\n <div class=\"result-item-content\">\n <div class=\"result-item-title\">{{ result.Title }}</div>\n <div class=\"result-item-snippet\">{{ result.Snippet }}</div>\n @if (result.Tags.length > 0) {\n <div class=\"result-item-tags\">\n @for (tag of result.Tags; track tag; let ti = $index) {\n @if (ti < 3) {\n <span class=\"result-tag\">{{ tag }}</span>\n }\n }\n </div>\n }\n </div>\n <div class=\"result-item-score\">\n <span class=\"score-badge\">{{ FormatScore(result.Score) }}</span>\n </div>\n </div>\n }\n </div>\n }\n\n <!-- Footer -->\n <div class=\"search-footer\">\n <span class=\"search-footer-count\">\n {{ TotalCount }} results in {{ FormatElapsedTime(ElapsedMs) }}\n </span>\n @if (ShowAgentCta) {\n <button class=\"agent-cta-btn agent-cta-btn-small\" (click)=\"OnAgentCtaClick()\">\n <i class=\"fa-solid fa-robot\"></i>\n {{ AgentCtaText }}\n </button>\n }\n </div>\n }\n </div>\n </div>\n}\n", styles: ["/* Search Overlay - Command Palette / Spotlight Style */\n\n.search-backdrop {\n position: fixed;\n inset: 0;\n background: var(--mj-bg-overlay);\n backdrop-filter: blur(8px);\n z-index: 1000;\n animation: backdropFadeIn 0.2s ease;\n}\n\n@keyframes backdropFadeIn {\n from { opacity: 0; }\n to { opacity: 1; }\n}\n\n.search-spotlight {\n position: fixed;\n top: 12%;\n left: 50%;\n transform: translateX(-50%);\n width: 680px;\n max-width: calc(100vw - 2rem);\n max-height: 70vh;\n z-index: 1001;\n display: flex;\n flex-direction: column;\n border-radius: 14px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n box-shadow: 0 25px 60px rgba(0, 0, 0, 0.3);\n overflow: hidden;\n animation: spotlightIn 0.25s ease;\n}\n\n@keyframes spotlightIn {\n from {\n opacity: 0;\n transform: translateX(-50%) translateY(-10px) scale(0.98);\n }\n to {\n opacity: 1;\n transform: translateX(-50%) translateY(0) scale(1);\n }\n}\n\n/* Search Input */\n.search-input-wrap {\n display: flex;\n align-items: center;\n padding: 0.75rem 1rem;\n border-bottom: 1px solid var(--mj-border-default);\n gap: 0.75rem;\n}\n\n.search-icon {\n color: var(--mj-brand-primary);\n font-size: 1.1rem;\n flex-shrink: 0;\n}\n\n.search-input {\n flex: 1;\n border: none;\n background: transparent;\n font-size: 1.1rem;\n color: var(--mj-text-primary);\n outline: none;\n font-family: inherit;\n}\n\n.search-input::placeholder {\n color: var(--mj-text-muted);\n}\n\n.search-spinner {\n color: var(--mj-brand-primary);\n font-size: 1rem;\n}\n\n.search-shortcut-hint kbd {\n display: inline-block;\n padding: 0.15rem 0.5rem;\n border-radius: 4px;\n border: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-muted);\n font-size: 0.75rem;\n font-family: inherit;\n}\n\n/* Results Area */\n.search-results-area {\n overflow-y: auto;\n max-height: calc(70vh - 56px);\n}\n\n/* Filter Bar */\n.search-filter-bar {\n display: flex;\n flex-wrap: wrap;\n gap: 0.4rem;\n padding: 0.6rem 1rem;\n border-bottom: 1px solid var(--mj-border-subtle);\n}\n\n.filter-chip {\n display: inline-flex;\n align-items: center;\n gap: 0.35rem;\n padding: 0.25rem 0.6rem;\n border-radius: 9999px;\n border: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface);\n color: var(--mj-text-secondary);\n font-size: 0.78rem;\n cursor: pointer;\n transition: all 0.15s ease;\n}\n\n.filter-chip:hover {\n border-color: var(--mj-brand-primary);\n color: var(--mj-text-primary);\n}\n\n.filter-chip-active {\n background: color-mix(in srgb, var(--mj-brand-primary) 12%, var(--mj-bg-surface));\n border-color: var(--mj-brand-primary);\n color: var(--mj-brand-primary);\n}\n\n.filter-chip-count {\n opacity: 0.6;\n font-size: 0.72rem;\n}\n\n.filter-clear-btn {\n display: inline-flex;\n align-items: center;\n gap: 0.25rem;\n padding: 0.25rem 0.6rem;\n border-radius: 9999px;\n border: none;\n background: transparent;\n color: var(--mj-text-muted);\n font-size: 0.78rem;\n cursor: pointer;\n}\n\n.filter-clear-btn:hover {\n color: var(--mj-status-error);\n}\n\n/* Empty State */\n.search-empty-state {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 2.5rem 1rem;\n gap: 0.5rem;\n}\n\n.search-empty-state-compact {\n padding: 1.5rem 1rem;\n}\n\n.search-empty-icon {\n font-size: 2rem;\n color: var(--mj-text-muted);\n margin-bottom: 0.5rem;\n}\n\n.search-empty-title {\n color: var(--mj-text-primary);\n font-weight: 600;\n font-size: 0.95rem;\n}\n\n.search-empty-text {\n color: var(--mj-text-muted);\n font-size: 0.85rem;\n}\n\n/* Recent Searches */\n.search-recent {\n padding: 0.5rem 0;\n}\n\n.search-recent-header {\n padding: 0.5rem 1rem;\n}\n\n.search-recent-title {\n font-size: 0.75rem;\n color: var(--mj-text-muted);\n text-transform: uppercase;\n letter-spacing: 0.05em;\n font-weight: 600;\n}\n\n.search-recent-item {\n display: flex;\n align-items: center;\n gap: 0.75rem;\n padding: 0.5rem 1rem;\n cursor: pointer;\n color: var(--mj-text-secondary);\n font-size: 0.9rem;\n transition: background 0.1s ease;\n}\n\n.search-recent-item:hover {\n background: var(--mj-bg-surface-hover);\n}\n\n.search-recent-icon {\n font-size: 0.8rem;\n color: var(--mj-text-muted);\n}\n\n.search-recent-count {\n margin-left: auto;\n font-size: 0.75rem;\n color: var(--mj-text-muted);\n}\n\n/* Result Groups */\n.result-group {\n padding: 0.25rem 0;\n}\n\n.result-group-header {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n padding: 0.5rem 1rem;\n color: var(--mj-text-muted);\n font-size: 0.75rem;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n font-weight: 600;\n}\n\n.result-group-label {\n flex: 1;\n}\n\n.result-group-count {\n font-size: 0.72rem;\n color: var(--mj-text-disabled);\n}\n\n/* Result Items */\n.result-item {\n display: flex;\n align-items: flex-start;\n gap: 0.75rem;\n padding: 0.6rem 1rem;\n cursor: pointer;\n transition: background 0.1s ease;\n}\n\n.result-item:hover,\n.result-item-highlighted {\n background: var(--mj-bg-surface-hover);\n}\n\n.result-item-icon {\n width: 2rem;\n height: 2rem;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 8px;\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-brand-primary);\n font-size: 0.85rem;\n flex-shrink: 0;\n margin-top: 0.1rem;\n}\n\n.result-item-content {\n flex: 1;\n min-width: 0;\n}\n\n.result-item-title {\n color: var(--mj-text-primary);\n font-weight: 500;\n font-size: 0.9rem;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.result-item-snippet {\n color: var(--mj-text-muted);\n font-size: 0.8rem;\n line-height: 1.4;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n margin-top: 0.15rem;\n}\n\n.result-item-tags {\n display: flex;\n gap: 0.3rem;\n margin-top: 0.3rem;\n}\n\n.result-tag {\n display: inline-block;\n padding: 0.1rem 0.4rem;\n border-radius: 4px;\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-muted);\n font-size: 0.7rem;\n}\n\n.result-item-score {\n flex-shrink: 0;\n margin-top: 0.2rem;\n}\n\n.score-badge {\n display: inline-block;\n padding: 0.15rem 0.45rem;\n border-radius: 9999px;\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 font-weight: 600;\n}\n\n/* Footer */\n.search-footer {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 0.5rem 1rem;\n border-top: 1px solid var(--mj-border-subtle);\n}\n\n.search-footer-count {\n color: var(--mj-text-muted);\n font-size: 0.78rem;\n}\n\n/* Agent CTA Button */\n.agent-cta-btn {\n display: inline-flex;\n align-items: center;\n gap: 0.5rem;\n padding: 0.6rem 1.2rem;\n border-radius: 8px;\n border: none;\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n font-size: 0.85rem;\n font-weight: 500;\n cursor: pointer;\n transition: background 0.15s ease;\n}\n\n.agent-cta-btn:hover {\n background: var(--mj-brand-primary-hover);\n}\n\n.agent-cta-btn-small {\n padding: 0.35rem 0.8rem;\n font-size: 0.78rem;\n}\n\n/* Mobile responsive */\n@media (max-width: 640px) {\n .search-spotlight {\n top: 0;\n left: 0;\n right: 0;\n transform: none;\n width: 100%;\n max-width: 100%;\n max-height: 100vh;\n border-radius: 0;\n }\n\n .search-results-area {\n max-height: calc(100vh - 56px);\n }\n\n .search-filter-bar {\n overflow-x: auto;\n flex-wrap: nowrap;\n -webkit-overflow-scrolling: touch;\n }\n}\n"] }]
|
|
591
|
+
args: [{ standalone: false, selector: 'mj-search-overlay', template: "@if (IsOpen) {\n <!-- Backdrop -->\n <div class=\"search-backdrop\" (click)=\"OnBackdropClick()\"></div>\n\n <!-- Spotlight container -->\n <div class=\"search-spotlight\" (click)=\"$event.stopPropagation()\">\n <!-- Search input area -->\n <div class=\"search-input-wrap\">\n <i class=\"fa-solid fa-magnifying-glass search-icon\"></i>\n <input\n #searchInput\n type=\"text\"\n class=\"search-input\"\n [placeholder]=\"Placeholder\"\n [value]=\"Query\"\n (input)=\"OnQueryInput($any($event.target).value)\"\n autocomplete=\"off\"\n spellcheck=\"false\"\n />\n @if (IsSearching) {\n <div class=\"search-spinner\">\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n </div>\n }\n <div class=\"search-shortcut-hint\">\n <kbd>ESC</kbd>\n </div>\n </div>\n\n <!-- Results area -->\n <div class=\"search-results-area\">\n @if (ShowFilters && Filters.length > 0) {\n <!-- Filter chips -->\n <div class=\"search-filter-bar\">\n @for (filter of Filters; track filter.Category) {\n @for (option of filter.Options; track option.Value) {\n <button\n class=\"filter-chip\"\n [class.filter-chip-active]=\"IsFilterActive(filter.Category, option.Value)\"\n (click)=\"OnFilterToggle(filter.Category, option.Value)\"\n >\n @if (option.Icon) {\n <i [class]=\"option.Icon\"></i>\n }\n <span>{{ option.Label }}</span>\n <span class=\"filter-chip-count\">{{ option.Count }}</span>\n </button>\n }\n }\n @if (hasActiveFilters()) {\n <button class=\"filter-clear-btn\" (click)=\"ClearAllFilters()\">\n <i class=\"fa-solid fa-times\"></i> Clear\n </button>\n }\n </div>\n }\n\n @if (HasSearched && AllResults.length === 0 && !IsSearching) {\n <!-- No results -->\n <div class=\"search-empty-state\">\n <i class=\"fa-solid fa-search search-empty-icon\"></i>\n <p class=\"search-empty-title\">No results found</p>\n <p class=\"search-empty-text\">Try different keywords or remove some filters</p>\n @if (ShowAgentCta) {\n <button class=\"agent-cta-btn\" (click)=\"OnAgentCtaClick()\">\n <i class=\"fa-solid fa-robot\"></i>\n {{ AgentCtaText }}\n </button>\n }\n </div>\n }\n\n @if (!HasSearched && !IsSearching) {\n <!-- Recent searches -->\n <div class=\"search-recent\">\n @if (searchService.RecentSearches$.value.length > 0) {\n <div class=\"search-recent-header\">\n <span class=\"search-recent-title\">Recent Searches</span>\n </div>\n @for (recent of searchService.RecentSearches$.value; track recent.Query; let i = $index) {\n @if (i < 5) {\n <div class=\"search-recent-item\" (click)=\"OnQueryInput(recent.Query)\">\n <i class=\"fa-solid fa-clock search-recent-icon\"></i>\n <span>{{ recent.Query }}</span>\n <span class=\"search-recent-count\">{{ recent.ResultCount }} results</span>\n </div>\n }\n }\n } @else {\n <div class=\"search-empty-state search-empty-state-compact\">\n <i class=\"fa-solid fa-magnifying-glass search-empty-icon\"></i>\n <p class=\"search-empty-text\">Start typing to search across all your knowledge</p>\n </div>\n }\n </div>\n }\n\n @if (AllResults.length > 0) {\n <!-- Grouped results -->\n @for (group of ResultGroups; track group.SourceType) {\n <div class=\"result-group\">\n <div class=\"result-group-header\">\n <i [class]=\"group.Icon\"></i>\n <span class=\"result-group-label\">{{ group.Label }}</span>\n <span class=\"result-group-count\">{{ group.TotalCount }}</span>\n </div>\n @for (result of group.Results; track result.ID; let ri = $index) {\n <div\n class=\"result-item\"\n [class.result-item-highlighted]=\"isResultHighlighted(result)\"\n (click)=\"OnResultClick(result, $event)\"\n (mouseenter)=\"HighlightedIndex = getFlatIndex(result)\"\n >\n <div class=\"result-item-icon\">\n <i [class]=\"result.SourceIcon\"></i>\n </div>\n <div class=\"result-item-content\">\n <div class=\"result-item-title\">{{ result.Title }}</div>\n <div class=\"result-item-snippet\">{{ result.Snippet }}</div>\n @if (result.Tags.length > 0) {\n <div class=\"result-item-tags\">\n @for (tag of result.Tags; track tag; let ti = $index) {\n @if (ti < 3) {\n <span class=\"result-tag\">{{ tag }}</span>\n }\n }\n </div>\n }\n </div>\n <div class=\"result-item-score\">\n <span class=\"score-badge\">{{ FormatScore(result.Score) }}</span>\n </div>\n </div>\n }\n </div>\n }\n\n <!-- Footer -->\n <div class=\"search-footer\">\n <span class=\"search-footer-count\">\n {{ TotalCount }} results in {{ FormatElapsedTime(ElapsedMs) }}\n </span>\n @if (ShowAgentCta) {\n <button class=\"agent-cta-btn agent-cta-btn-small\" (click)=\"OnAgentCtaClick()\">\n <i class=\"fa-solid fa-robot\"></i>\n {{ AgentCtaText }}\n </button>\n }\n </div>\n }\n </div>\n </div>\n}\n", styles: ["/* Search Overlay - Command Palette / Spotlight Style */\n\n.search-backdrop {\n position: fixed;\n inset: 0;\n background: var(--mj-bg-overlay);\n backdrop-filter: blur(8px);\n z-index: 99998;\n animation: backdropFadeIn 0.2s ease;\n}\n\n@keyframes backdropFadeIn {\n from { opacity: 0; }\n to { opacity: 1; }\n}\n\n.search-spotlight {\n position: fixed;\n top: 12%;\n left: 50%;\n transform: translateX(-50%);\n width: 680px;\n max-width: calc(100vw - 2rem);\n max-height: 70vh;\n z-index: 99999;\n display: flex;\n flex-direction: column;\n border-radius: 14px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n box-shadow: 0 25px 60px rgba(0, 0, 0, 0.3);\n overflow: hidden;\n animation: spotlightIn 0.25s ease;\n}\n\n@keyframes spotlightIn {\n from {\n opacity: 0;\n transform: translateX(-50%) translateY(-10px) scale(0.98);\n }\n to {\n opacity: 1;\n transform: translateX(-50%) translateY(0) scale(1);\n }\n}\n\n/* Search Input */\n.search-input-wrap {\n display: flex;\n align-items: center;\n padding: 0.75rem 1rem;\n border-bottom: 1px solid var(--mj-border-default);\n gap: 0.75rem;\n}\n\n.search-icon {\n color: var(--mj-brand-primary);\n font-size: 1.1rem;\n flex-shrink: 0;\n}\n\n.search-input {\n flex: 1;\n border: none;\n background: transparent;\n font-size: 1.1rem;\n color: var(--mj-text-primary);\n outline: none;\n font-family: inherit;\n}\n\n.search-input::placeholder {\n color: var(--mj-text-muted);\n}\n\n.search-spinner {\n color: var(--mj-brand-primary);\n font-size: 1rem;\n}\n\n.search-shortcut-hint kbd {\n display: inline-block;\n padding: 0.15rem 0.5rem;\n border-radius: 4px;\n border: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-muted);\n font-size: 0.75rem;\n font-family: inherit;\n}\n\n/* Results Area */\n.search-results-area {\n overflow-y: auto;\n max-height: calc(70vh - 56px);\n}\n\n/* Filter Bar */\n.search-filter-bar {\n display: flex;\n flex-wrap: wrap;\n gap: 0.4rem;\n padding: 0.6rem 1rem;\n border-bottom: 1px solid var(--mj-border-subtle);\n}\n\n.filter-chip {\n display: inline-flex;\n align-items: center;\n gap: 0.35rem;\n padding: 0.25rem 0.6rem;\n border-radius: 9999px;\n border: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface);\n color: var(--mj-text-secondary);\n font-size: 0.78rem;\n cursor: pointer;\n transition: all 0.15s ease;\n}\n\n.filter-chip:hover {\n border-color: var(--mj-brand-primary);\n color: var(--mj-text-primary);\n}\n\n.filter-chip-active {\n background: color-mix(in srgb, var(--mj-brand-primary) 12%, var(--mj-bg-surface));\n border-color: var(--mj-brand-primary);\n color: var(--mj-brand-primary);\n}\n\n.filter-chip-count {\n opacity: 0.6;\n font-size: 0.72rem;\n}\n\n.filter-clear-btn {\n display: inline-flex;\n align-items: center;\n gap: 0.25rem;\n padding: 0.25rem 0.6rem;\n border-radius: 9999px;\n border: none;\n background: transparent;\n color: var(--mj-text-muted);\n font-size: 0.78rem;\n cursor: pointer;\n}\n\n.filter-clear-btn:hover {\n color: var(--mj-status-error);\n}\n\n/* Empty State */\n.search-empty-state {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 2.5rem 1rem;\n gap: 0.5rem;\n}\n\n.search-empty-state-compact {\n padding: 1.5rem 1rem;\n}\n\n.search-empty-icon {\n font-size: 2rem;\n color: var(--mj-text-muted);\n margin-bottom: 0.5rem;\n}\n\n.search-empty-title {\n color: var(--mj-text-primary);\n font-weight: 600;\n font-size: 0.95rem;\n}\n\n.search-empty-text {\n color: var(--mj-text-muted);\n font-size: 0.85rem;\n}\n\n/* Recent Searches */\n.search-recent {\n padding: 0.5rem 0;\n}\n\n.search-recent-header {\n padding: 0.5rem 1rem;\n}\n\n.search-recent-title {\n font-size: 0.75rem;\n color: var(--mj-text-muted);\n text-transform: uppercase;\n letter-spacing: 0.05em;\n font-weight: 600;\n}\n\n.search-recent-item {\n display: flex;\n align-items: center;\n gap: 0.75rem;\n padding: 0.5rem 1rem;\n cursor: pointer;\n color: var(--mj-text-secondary);\n font-size: 0.9rem;\n transition: background 0.1s ease;\n}\n\n.search-recent-item:hover {\n background: var(--mj-bg-surface-hover);\n}\n\n.search-recent-icon {\n font-size: 0.8rem;\n color: var(--mj-text-muted);\n}\n\n.search-recent-count {\n margin-left: auto;\n font-size: 0.75rem;\n color: var(--mj-text-muted);\n}\n\n/* Result Groups */\n.result-group {\n padding: 0.25rem 0;\n}\n\n.result-group-header {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n padding: 0.5rem 1rem;\n color: var(--mj-text-muted);\n font-size: 0.75rem;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n font-weight: 600;\n}\n\n.result-group-label {\n flex: 1;\n}\n\n.result-group-count {\n font-size: 0.72rem;\n color: var(--mj-text-disabled);\n}\n\n/* Result Items */\n.result-item {\n display: flex;\n align-items: flex-start;\n gap: 0.75rem;\n padding: 0.6rem 1rem;\n cursor: pointer;\n transition: background 0.1s ease;\n}\n\n.result-item:hover,\n.result-item-highlighted {\n background: var(--mj-bg-surface-hover);\n}\n\n.result-item-icon {\n width: 2rem;\n height: 2rem;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 8px;\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-brand-primary);\n font-size: 0.85rem;\n flex-shrink: 0;\n margin-top: 0.1rem;\n}\n\n.result-item-content {\n flex: 1;\n min-width: 0;\n}\n\n.result-item-title {\n color: var(--mj-text-primary);\n font-weight: 500;\n font-size: 0.9rem;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.result-item-snippet {\n color: var(--mj-text-muted);\n font-size: 0.8rem;\n line-height: 1.4;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n margin-top: 0.15rem;\n}\n\n.result-item-tags {\n display: flex;\n gap: 0.3rem;\n margin-top: 0.3rem;\n}\n\n.result-tag {\n display: inline-block;\n padding: 0.1rem 0.4rem;\n border-radius: 4px;\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-muted);\n font-size: 0.7rem;\n}\n\n.result-item-score {\n flex-shrink: 0;\n margin-top: 0.2rem;\n}\n\n.score-badge {\n display: inline-block;\n padding: 0.15rem 0.45rem;\n border-radius: 9999px;\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 font-weight: 600;\n}\n\n/* Footer */\n.search-footer {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 0.5rem 1rem;\n border-top: 1px solid var(--mj-border-subtle);\n}\n\n.search-footer-count {\n color: var(--mj-text-muted);\n font-size: 0.78rem;\n}\n\n/* Agent CTA Button */\n.agent-cta-btn {\n display: inline-flex;\n align-items: center;\n gap: 0.5rem;\n padding: 0.6rem 1.2rem;\n border-radius: 8px;\n border: none;\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n font-size: 0.85rem;\n font-weight: 500;\n cursor: pointer;\n transition: background 0.15s ease;\n}\n\n.agent-cta-btn:hover {\n background: var(--mj-brand-primary-hover);\n}\n\n.agent-cta-btn-small {\n padding: 0.35rem 0.8rem;\n font-size: 0.78rem;\n}\n\n/* Mobile responsive */\n@media (max-width: 640px) {\n .search-spotlight {\n top: 0;\n left: 0;\n right: 0;\n transform: none;\n width: 100%;\n max-width: 100%;\n max-height: 100vh;\n border-radius: 0;\n }\n\n .search-results-area {\n max-height: calc(100vh - 56px);\n }\n\n .search-filter-bar {\n overflow-x: auto;\n flex-wrap: nowrap;\n -webkit-overflow-scrolling: touch;\n }\n}\n"] }]
|
|
592
592
|
}], null, { searchInputRef: [{
|
|
593
593
|
type: ViewChild,
|
|
594
594
|
args: ['searchInput']
|
|
@@ -34,12 +34,11 @@ export declare class SearchResultsComponent {
|
|
|
34
34
|
ShowTags: boolean;
|
|
35
35
|
/** Whether to show source type icons */
|
|
36
36
|
ShowSourceIcons: boolean;
|
|
37
|
+
/** Text to highlight within result cards (e.g. from a client-side filter) */
|
|
38
|
+
HighlightText: string;
|
|
37
39
|
ResultSelected: EventEmitter<SearchResultSelectedEvent>;
|
|
38
40
|
/** Emitted when user clicks "Open Record" — parent handles navigation */
|
|
39
|
-
OpenRecordRequested: EventEmitter<
|
|
40
|
-
EntityName: string;
|
|
41
|
-
RecordID: string;
|
|
42
|
-
}>;
|
|
41
|
+
OpenRecordRequested: EventEmitter<SearchResultItem>;
|
|
43
42
|
/** Emitted when user clicks "See Similar Items" — parent runs a "more like this" search */
|
|
44
43
|
MoreLikeThisRequested: EventEmitter<SearchResultItem>;
|
|
45
44
|
/** Current page (1-based) for flat mode pagination */
|
|
@@ -76,6 +75,11 @@ export declare class SearchResultsComponent {
|
|
|
76
75
|
OnOpenRecord(result: SearchResultItem, event: MouseEvent): void;
|
|
77
76
|
/** Handle "See Similar Items" button click — emits the result for a "more like this" search */
|
|
78
77
|
OnMoreLikeThis(result: SearchResultItem, event: MouseEvent): void;
|
|
78
|
+
/**
|
|
79
|
+
* Wrap substrings matching HighlightText in <mark> tags for visual emphasis.
|
|
80
|
+
* The input is regex-escaped so user text is treated as a literal string.
|
|
81
|
+
*/
|
|
82
|
+
HighlightMatch(text: string): string;
|
|
79
83
|
/** Format a score as a percentage */
|
|
80
84
|
FormatScore(score: number): string;
|
|
81
85
|
/** Format elapsed time */
|
|
@@ -107,6 +111,6 @@ export declare class SearchResultsComponent {
|
|
|
107
111
|
Type: 'URL' | 'Email';
|
|
108
112
|
}[];
|
|
109
113
|
static ɵfac: i0.ɵɵFactoryDeclaration<SearchResultsComponent, never>;
|
|
110
|
-
static ɵcmp: i0.ɵɵComponentDeclaration<SearchResultsComponent, "mj-search-results", never, { "ResultGroups": { "alias": "ResultGroups"; "required": false; }; "FlatResults": { "alias": "FlatResults"; "required": false; }; "DisplayMode": { "alias": "DisplayMode"; "required": false; }; "IsLoading": { "alias": "IsLoading"; "required": false; }; "TotalCount": { "alias": "TotalCount"; "required": false; }; "ElapsedMs": { "alias": "ElapsedMs"; "required": false; }; "MaxPerGroup": { "alias": "MaxPerGroup"; "required": false; }; "PageSize": { "alias": "PageSize"; "required": false; }; "ShowSummary": { "alias": "ShowSummary"; "required": false; }; "ShowScores": { "alias": "ShowScores"; "required": false; }; "ShowTags": { "alias": "ShowTags"; "required": false; }; "ShowSourceIcons": { "alias": "ShowSourceIcons"; "required": false; }; }, { "ResultSelected": "ResultSelected"; "OpenRecordRequested": "OpenRecordRequested"; "MoreLikeThisRequested": "MoreLikeThisRequested"; }, never, never, false, never>;
|
|
114
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<SearchResultsComponent, "mj-search-results", never, { "ResultGroups": { "alias": "ResultGroups"; "required": false; }; "FlatResults": { "alias": "FlatResults"; "required": false; }; "DisplayMode": { "alias": "DisplayMode"; "required": false; }; "IsLoading": { "alias": "IsLoading"; "required": false; }; "TotalCount": { "alias": "TotalCount"; "required": false; }; "ElapsedMs": { "alias": "ElapsedMs"; "required": false; }; "MaxPerGroup": { "alias": "MaxPerGroup"; "required": false; }; "PageSize": { "alias": "PageSize"; "required": false; }; "ShowSummary": { "alias": "ShowSummary"; "required": false; }; "ShowScores": { "alias": "ShowScores"; "required": false; }; "ShowTags": { "alias": "ShowTags"; "required": false; }; "ShowSourceIcons": { "alias": "ShowSourceIcons"; "required": false; }; "HighlightText": { "alias": "HighlightText"; "required": false; }; }, { "ResultSelected": "ResultSelected"; "OpenRecordRequested": "OpenRecordRequested"; "MoreLikeThisRequested": "MoreLikeThisRequested"; }, never, never, false, never>;
|
|
111
115
|
}
|
|
112
116
|
//# sourceMappingURL=search-results.component.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"search-results.component.d.ts","sourceRoot":"","sources":["../../src/lib/search-results.component.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAEH,YAAY,EAKf,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,yBAAyB,EAAE,MAAM,gBAAgB,CAAC;;AAGhG,qBAMa,sBAAsB;IAC/B,OAAO,CAAC,GAAG,CAA6B;IAExC,6EAA6E;IACpE,YAAY,EAAE,iBAAiB,EAAE,CAAM;IAEhD,iFAAiF;IACxE,WAAW,EAAE,gBAAgB,EAAE,CAAM;IAE9C,8FAA8F;IACrF,WAAW,EAAE,MAAM,GAAG,SAAS,CAAU;IAElD,4CAA4C;IACnC,SAAS,UAAS;IAE3B,yBAAyB;IAChB,UAAU,SAAK;IAExB,gCAAgC;IACvB,SAAS,SAAK;IAEvB,uEAAuE;IAC9D,WAAW,SAAK;IAEzB,oCAAoC;IAC3B,QAAQ,SAAM;IAEvB,8GAA8G;IACrG,WAAW,UAAQ;IAE5B,mCAAmC;IAC1B,UAAU,UAAQ;IAE3B,2BAA2B;IAClB,QAAQ,UAAQ;IAEzB,wCAAwC;IAC/B,eAAe,UAAQ;
|
|
1
|
+
{"version":3,"file":"search-results.component.d.ts","sourceRoot":"","sources":["../../src/lib/search-results.component.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAEH,YAAY,EAKf,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,yBAAyB,EAAE,MAAM,gBAAgB,CAAC;;AAGhG,qBAMa,sBAAsB;IAC/B,OAAO,CAAC,GAAG,CAA6B;IAExC,6EAA6E;IACpE,YAAY,EAAE,iBAAiB,EAAE,CAAM;IAEhD,iFAAiF;IACxE,WAAW,EAAE,gBAAgB,EAAE,CAAM;IAE9C,8FAA8F;IACrF,WAAW,EAAE,MAAM,GAAG,SAAS,CAAU;IAElD,4CAA4C;IACnC,SAAS,UAAS;IAE3B,yBAAyB;IAChB,UAAU,SAAK;IAExB,gCAAgC;IACvB,SAAS,SAAK;IAEvB,uEAAuE;IAC9D,WAAW,SAAK;IAEzB,oCAAoC;IAC3B,QAAQ,SAAM;IAEvB,8GAA8G;IACrG,WAAW,UAAQ;IAE5B,mCAAmC;IAC1B,UAAU,UAAQ;IAE3B,2BAA2B;IAClB,QAAQ,UAAQ;IAEzB,wCAAwC;IAC/B,eAAe,UAAQ;IAEhC,6EAA6E;IACpE,aAAa,SAAM;IAElB,cAAc,0CAAiD;IAEzE,yEAAyE;IAC/D,mBAAmB,iCAAwC;IAErE,2FAA2F;IACjF,qBAAqB,iCAAwC;IAEvE,sDAAsD;IAC/C,WAAW,SAAK;IAEvB,uCAAuC;IAChC,cAAc,cAAqB;IAE1C,4EAA4E;IACrE,iBAAiB,cAAqB;IAI7C,oDAAoD;IACpD,IAAW,YAAY,IAAI,gBAAgB,EAAE,CAG5C;IAED,4BAA4B;IAC5B,IAAW,UAAU,IAAI,MAAM,CAE9B;IAED,2CAA2C;IAC3C,IAAW,WAAW,IAAI,MAAM,EAAE,CAYjC;IAED,kCAAkC;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAQnC,6BAA6B;IACtB,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IAS5C,kCAAkC;IAC3B,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO;IAInD,iFAAiF;IAC1E,iBAAiB,CAAC,KAAK,EAAE,iBAAiB,GAAG,gBAAgB,EAAE;IAOtE,kDAAkD;IAC3C,cAAc,CAAC,KAAK,EAAE,iBAAiB,GAAG,OAAO;IAIxD,sCAAsC;IAC/B,cAAc,CAAC,KAAK,EAAE,iBAAiB,GAAG,MAAM;IAIvD,+BAA+B;IACxB,aAAa,CAAC,MAAM,EAAE,gBAAgB,EAAE,KAAK,EAAE,UAAU,GAAG,IAAI;IAOvE,qEAAqE;IAC9D,YAAY,CAAC,MAAM,EAAE,gBAAgB,EAAE,KAAK,CAAC,EAAE,UAAU,GAAG,IAAI;IAUvE,yCAAyC;IAClC,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;IAI5C,wCAAwC;IACjC,YAAY,CAAC,MAAM,EAAE,gBAAgB,EAAE,KAAK,EAAE,UAAU,GAAG,IAAI;IAKtE,+FAA+F;IACxF,cAAc,CAAC,MAAM,EAAE,gBAAgB,EAAE,KAAK,EAAE,UAAU,GAAG,IAAI;IAKxE;;;OAGG;IACI,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;IAO3C,qCAAqC;IAC9B,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IAIzC,0BAA0B;IACnB,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM;IAKrC,wCAAwC;IACxC,OAAO,CAAC,kBAAkB,CAAgF;IAE1G;;;OAGG;IACI,gBAAgB,CAAC,MAAM,EAAE,gBAAgB,GAAG;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,KAAK,GAAG,OAAO,CAAA;KAAE,EAAE;IAqC5G,qCAAqC;IAC9B,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE;IAO7C,iCAAiC;IAC1B,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM;IAYvC,oDAAoD;IACpD,OAAO,CAAC,eAAe,CAAqF;IAE5G;;;OAGG;IACI,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,KAAK,GAAG,OAAO,CAAA;KAAE,EAAE;yCAnQ/F,sBAAsB;2CAAtB,sBAAsB;CA+RlC"}
|