@memberjunction/ng-search 6.1.4 → 6.2.0-edge.1
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/lib/search-composite.component.d.ts +10 -2
- package/dist/lib/search-composite.component.d.ts.map +1 -1
- package/dist/lib/search-composite.component.js +33 -17
- package/dist/lib/search-composite.component.js.map +1 -1
- package/dist/lib/search-input.component.d.ts +6 -2
- package/dist/lib/search-input.component.d.ts.map +1 -1
- package/dist/lib/search-input.component.js +19 -11
- package/dist/lib/search-input.component.js.map +1 -1
- package/dist/lib/search-overlay.component.d.ts +12 -2
- package/dist/lib/search-overlay.component.d.ts.map +1 -1
- package/dist/lib/search-overlay.component.js +39 -19
- package/dist/lib/search-overlay.component.js.map +1 -1
- package/dist/lib/search-results.component.js +3 -3
- package/dist/lib/search-results.component.js.map +1 -1
- package/dist/lib/search-suggest.component.d.ts +8 -0
- package/dist/lib/search-suggest.component.d.ts.map +1 -1
- package/dist/lib/search-suggest.component.js +33 -17
- package/dist/lib/search-suggest.component.js.map +1 -1
- package/dist/lib/search-types.d.ts +4 -0
- 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.map +1 -1
- package/dist/lib/search.service.js +48 -13
- package/dist/lib/search.service.js.map +1 -1
- package/package.json +9 -9
|
@@ -373,7 +373,7 @@ function SearchResultsComponent_ng_template_3_Conditional_14_Template(rf, ctx) {
|
|
|
373
373
|
i0.ɵɵadvance();
|
|
374
374
|
i0.ɵɵconditional(result_r12.Snippet ? 1 : -1);
|
|
375
375
|
i0.ɵɵadvance(5);
|
|
376
|
-
i0.ɵɵtextInterpolate(result_r12.EntityName);
|
|
376
|
+
i0.ɵɵtextInterpolate(result_r12.EntityDisplayName || result_r12.EntityName);
|
|
377
377
|
i0.ɵɵadvance(4);
|
|
378
378
|
i0.ɵɵtextInterpolate(result_r12.RecordID);
|
|
379
379
|
i0.ɵɵadvance(4);
|
|
@@ -418,7 +418,7 @@ function SearchResultsComponent_ng_template_3_Template(rf, ctx) { if (rf & 1) {
|
|
|
418
418
|
i0.ɵɵadvance(2);
|
|
419
419
|
i0.ɵɵproperty("innerHTML", ctx_r0.HighlightMatch(result_r12.RecordName || result_r12.Title), i0.ɵɵsanitizeHtml);
|
|
420
420
|
i0.ɵɵadvance();
|
|
421
|
-
i0.ɵɵproperty("innerHTML", ctx_r0.HighlightMatch(result_r12.EntityName), i0.ɵɵsanitizeHtml);
|
|
421
|
+
i0.ɵɵproperty("innerHTML", ctx_r0.HighlightMatch(result_r12.EntityDisplayName || result_r12.EntityName), i0.ɵɵsanitizeHtml);
|
|
422
422
|
i0.ɵɵadvance();
|
|
423
423
|
i0.ɵɵconditional(result_r12.Tags && result_r12.Tags.length > 0 ? 7 : -1);
|
|
424
424
|
i0.ɵɵadvance();
|
|
@@ -687,7 +687,7 @@ export class SearchResultsComponent extends BaseAngularComponent {
|
|
|
687
687
|
}
|
|
688
688
|
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(SearchResultsComponent, [{
|
|
689
689
|
type: Component,
|
|
690
|
-
args: [{ standalone: false, selector: 'mj-search-results', template: "@if (IsLoading) {\n <div class=\"results-loading\">\n <mj-loading text=\"Searching...\" size=\"medium\"></mj-loading>\n </div>\n} @else if ((DisplayMode === 'flat' ? FlatResults.length : ResultGroups.length) === 0 && TotalCount === 0) {\n <mj-empty-state\n Variant=\"no-results\"\n Title=\"No results to display\" />\n} @else {\n <!-- Results summary -->\n @if (ShowSummary) {\n <div class=\"results-summary\">\n <span>{{ TotalCount }} results</span>\n @if (ElapsedMs > 0) {\n <span class=\"results-summary-time\"> in {{ FormatTime(ElapsedMs) }}</span>\n }\n </div>\n }\n\n @if (DisplayMode === 'flat') {\n <!-- \u2550\u2550\u2550\u2550\u2550\u2550 FLAT BLENDED LIST (sorted by score) \u2550\u2550\u2550\u2550\u2550\u2550 -->\n <div class=\"results-scroll-area\">\n @for (result of PagedResults; track result.ID) {\n <ng-container *ngTemplateOutlet=\"resultCardTpl; context: { $implicit: result }\"></ng-container>\n }\n </div>\n\n <!-- Google-style pagination (sticky at bottom) -->\n @if (TotalPages > 1) {\n <div class=\"results-pager\">\n <button class=\"pager-btn pager-prev\" [disabled]=\"CurrentPage === 1\"\n (click)=\"GoToPage(CurrentPage - 1)\" aria-label=\"Previous page\">\n <i class=\"fa-solid fa-chevron-left\"></i>\n </button>\n @for (p of PageNumbers; track $index) {\n @if (p === -1) {\n <span class=\"pager-ellipsis\">...</span>\n } @else {\n <button class=\"pager-btn pager-num\" [class.pager-active]=\"p === CurrentPage\"\n (click)=\"GoToPage(p)\">{{ p }}</button>\n }\n }\n <button class=\"pager-btn pager-next\" [disabled]=\"CurrentPage === TotalPages\"\n (click)=\"GoToPage(CurrentPage + 1)\" aria-label=\"Next page\">\n <i class=\"fa-solid fa-chevron-right\"></i>\n </button>\n </div>\n }\n } @else {\n <!-- \u2550\u2550\u2550\u2550\u2550\u2550 GROUPED BY SOURCE TYPE \u2550\u2550\u2550\u2550\u2550\u2550 -->\n @for (group of ResultGroups; track group.SourceType) {\n <div class=\"results-group\">\n <div class=\"results-group-header\" (click)=\"ToggleGroup(group.SourceType)\">\n @if (ShowSourceIcons) {\n <i [class]=\"group.Icon + ' results-group-icon'\"></i>\n }\n <span class=\"results-group-label\">{{ group.Label }}</span>\n <span class=\"results-group-count\">({{ group.TotalCount }})</span>\n <i class=\"fa-solid results-group-chevron\"\n [class.fa-chevron-down]=\"!IsGroupExpanded(group.SourceType)\"\n [class.fa-chevron-up]=\"IsGroupExpanded(group.SourceType)\"></i>\n </div>\n\n @for (result of GetVisibleResults(group); track result.ID) {\n <ng-container *ngTemplateOutlet=\"resultCardTpl; context: { $implicit: result }\"></ng-container>\n }\n\n @if (HasMoreResults(group)) {\n <button class=\"results-show-more\" (click)=\"ToggleGroup(group.SourceType)\">\n <i class=\"fa-solid fa-chevron-down\"></i>\n Show {{ GetHiddenCount(group) }} more results\n </button>\n }\n </div>\n }\n }\n}\n\n<!-- \u2550\u2550\u2550 Shared result card template \u2550\u2550\u2550 -->\n<ng-template #resultCardTpl let-result>\n <div class=\"result-card\" [class.result-card-expanded]=\"IsExpanded(result.ID)\">\n <!-- Main row -->\n <div class=\"result-card-main\" (click)=\"ToggleExpand(result)\">\n <div class=\"result-card-icon-wrap\">\n <i [class]=\"result.EntityIcon || result.SourceIcon || 'fa-solid fa-database'\"></i>\n </div>\n <div class=\"result-card-body\">\n <div class=\"result-card-title\" [innerHTML]=\"HighlightMatch(result.RecordName || result.Title)\"></div>\n <div class=\"result-card-entity\" [innerHTML]=\"HighlightMatch(result.EntityName)\"></div>\n @if (result.Tags && result.Tags.length > 0) {\n <div class=\"result-card-tags\">\n @for (tag of result.Tags.slice(0, 5); track tag) {\n <span class=\"result-tag-chip\" [innerHTML]=\"HighlightMatch(tag)\"></span>\n }\n @if (result.Tags.length > 5) {\n <span class=\"result-tag-more\">+{{ result.Tags.length - 5 }}</span>\n }\n </div>\n }\n </div>\n @if (ShowScores) {\n <div class=\"result-card-score\">\n <div class=\"score-circle\" [class.score-high]=\"result.Score >= 0.40\" [class.score-medium]=\"result.Score >= 0.15 && result.Score < 0.40\" [class.score-low]=\"result.Score < 0.15\">\n <span class=\"score-value\">{{ FormatScore(result.Score) }}</span>\n </div>\n </div>\n }\n <div class=\"result-card-actions\">\n <button class=\"result-open-btn\" (click)=\"OnOpenRecord(result, $event)\"\n [title]=\"result.ResultType === 'storage-file' ? 'Open file' : 'Open record'\"\n [attr.aria-label]=\"result.ResultType === 'storage-file' ? 'Open file' : 'Open record'\">\n <i [class]=\"result.ResultType === 'storage-file' ? 'fa-solid fa-external-link' : 'fa-solid fa-arrow-up-right-from-square'\"></i>\n </button>\n <button class=\"result-expand-btn\" (click)=\"ToggleExpand(result, $event)\" title=\"Expand details\" aria-label=\"Expand details\">\n <i class=\"fa-solid\" [class.fa-chevron-down]=\"!IsExpanded(result.ID)\"\n [class.fa-chevron-up]=\"IsExpanded(result.ID)\"></i>\n </button>\n </div>\n </div>\n\n <!-- Expanded detail -->\n @if (IsExpanded(result.ID)) {\n <div class=\"result-card-detail\">\n @if (result.Snippet) {\n <div class=\"detail-section\">\n <span class=\"detail-label\">Preview</span>\n <p class=\"detail-snippet\" [innerHTML]=\"HighlightMatch(result.Snippet)\"></p>\n </div>\n }\n <div class=\"detail-meta\">\n <div class=\"detail-meta-item\">\n <i class=\"fa-solid fa-layer-group\"></i>\n <span>{{ result.EntityName }}</span>\n </div>\n <div class=\"detail-meta-item\">\n <i class=\"fa-solid fa-fingerprint\"></i>\n <span class=\"detail-record-id\">{{ result.RecordID }}</span>\n </div>\n <!-- SR-1: Show overall score consistently -->\n <div class=\"detail-meta-item\">\n <i class=\"fa-solid fa-signal\"></i>\n <span>Relevance: {{ FormatScore(result.Score) }}</span>\n </div>\n @if (result.ScoreBreakdown) {\n <div class=\"detail-meta-item\">\n <i class=\"fa-solid fa-chart-bar\"></i>\n <span>\n @if (result.ScoreBreakdown.Vector) { Vector: {{ FormatScore(result.ScoreBreakdown.Vector) }} }\n @if (result.ScoreBreakdown.FullText) { FTS: {{ FormatScore(result.ScoreBreakdown.FullText) }} }\n </span>\n </div>\n }\n </div>\n @if (ShowTags && result.Tags.length > 0) {\n <div class=\"detail-tags\">\n @for (tag of result.Tags; track tag) {\n <span class=\"result-card-tag\" [innerHTML]=\"HighlightMatch(tag)\"></span>\n }\n </div>\n }\n @if (GetResolvedLinks(result).length > 0) {\n <div class=\"detail-links\">\n @for (link of GetResolvedLinks(result); track link.Value) {\n @if (link.Type === 'URL') {\n <a [href]=\"link.Value\" target=\"_blank\" class=\"detail-link\" (click)=\"$event.stopPropagation()\">\n <i class=\"fa-solid fa-arrow-up-right-from-square\"></i>\n {{ TruncateUrl(link.Value) }}\n </a>\n } @else if (link.Type === 'Email') {\n <a [href]=\"'mailto:' + link.Value\" class=\"detail-link detail-link-email\" (click)=\"$event.stopPropagation()\">\n <i class=\"fa-solid fa-envelope\"></i>\n {{ link.Value }}\n </a>\n }\n }\n </div>\n }\n <div class=\"detail-actions\">\n <button class=\"detail-open-btn\" (click)=\"OnOpenRecord(result, $event)\">\n @if (result.ResultType === 'storage-file') {\n <i class=\"fa-solid fa-external-link\"></i> Open File\n } @else {\n <i class=\"fa-solid fa-arrow-up-right-from-square\"></i> Open Record\n }\n </button>\n <button class=\"detail-similar-btn\" (click)=\"OnMoreLikeThis(result, $event)\">\n <i class=\"fa-solid fa-shuffle\"></i> See Similar Items\n </button>\n </div>\n </div>\n }\n </div>\n</ng-template>\n", styles: ["/* Search Results Component */\n\n:host {\n display: flex;\n flex-direction: column;\n min-height: 0;\n flex: 1;\n}\n\n.results-scroll-area {\n flex: 1;\n overflow-y: auto;\n min-height: 0;\n}\n\n.results-loading {\n display: flex;\n justify-content: center;\n padding: 3rem 1rem;\n}\n\n.results-summary {\n padding: 0.75rem 1rem;\n color: var(--mj-text-secondary);\n font-size: 0.85rem;\n border-bottom: 1px solid var(--mj-border-subtle);\n}\n\n.results-summary-time {\n color: var(--mj-text-muted);\n}\n\n/* Group Header */\n.results-group {\n border-bottom: 1px solid var(--mj-border-subtle);\n}\n\n.results-group-header {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n padding: 0.75rem 1rem;\n cursor: pointer;\n transition: background 0.1s ease;\n}\n\n.results-group-header:hover {\n background: var(--mj-bg-surface-hover);\n}\n\n.results-group-icon {\n color: var(--mj-brand-primary);\n font-size: 0.85rem;\n}\n\n.results-group-label {\n font-weight: 600;\n font-size: 0.85rem;\n color: var(--mj-text-primary);\n flex: 1;\n}\n\n.results-group-count {\n font-size: 0.78rem;\n color: var(--mj-text-muted);\n}\n\n.results-group-chevron {\n color: var(--mj-text-muted);\n font-size: 0.7rem;\n transition: transform 0.2s ease;\n}\n\n/* Result Card \u2014 expandable */\n.result-card {\n border: 1px solid var(--mj-border-subtle);\n border-radius: 8px;\n margin: 0 0 0.35rem 1.5rem;\n overflow: hidden;\n transition: border-color 0.15s, box-shadow 0.15s;\n}\n\n.result-card:hover {\n border-color: var(--mj-border-default);\n}\n\n.result-card-expanded {\n border-color: var(--mj-brand-primary);\n box-shadow: 0 2px 8px color-mix(in srgb, var(--mj-brand-primary) 10%, transparent);\n}\n\n.result-card-main {\n display: flex;\n align-items: center;\n gap: 0.75rem;\n padding: 0.65rem 0.85rem;\n cursor: pointer;\n transition: background 0.1s ease;\n}\n\n.result-card-main:hover {\n background: var(--mj-bg-surface-hover);\n}\n\n.result-card-icon-wrap {\n width: 2rem;\n height: 2rem;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 6px;\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n font-size: 0.8rem;\n flex-shrink: 0;\n}\n\n.result-card-body {\n flex: 1;\n min-width: 0;\n}\n\n.result-card-title {\n color: var(--mj-text-primary);\n font-weight: 600;\n font-size: 0.85rem;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.result-card-entity {\n color: var(--mj-text-muted);\n font-size: 0.7rem;\n}\n\n.result-card-tags {\n display: flex;\n flex-wrap: wrap;\n gap: 4px;\n margin-top: 4px;\n}\n\n.result-tag-chip {\n display: inline-block;\n padding: 1px 7px;\n border-radius: 10px;\n font-size: 0.65rem;\n font-weight: 500;\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n border: 1px solid color-mix(in srgb, var(--mj-brand-primary) 20%, var(--mj-border-default));\n}\n\n.result-tag-more {\n display: inline-block;\n padding: 1px 6px;\n font-size: 0.6rem;\n color: var(--mj-text-muted);\n}\n\n.result-card-actions {\n display: flex;\n align-items: center;\n gap: 4px;\n flex-shrink: 0;\n}\n\n.result-open-btn,\n.result-expand-btn {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 28px;\n height: 28px;\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n background: transparent;\n color: var(--mj-text-muted);\n font-size: 11px;\n cursor: pointer;\n transition: all 0.15s;\n}\n\n.result-open-btn:hover {\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n border-color: var(--mj-brand-primary);\n color: var(--mj-brand-primary);\n}\n\n.result-expand-btn:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-primary);\n}\n\n/* Expanded detail panel */\n.result-card-detail {\n padding: 0.75rem 1rem 0.85rem 3.5rem;\n border-top: 1px solid var(--mj-border-subtle);\n background: var(--mj-bg-surface-card);\n}\n\n.detail-section {\n margin-bottom: 0.65rem;\n}\n\n.detail-label {\n font-size: 0.7rem;\n font-weight: 600;\n color: var(--mj-text-muted);\n text-transform: uppercase;\n letter-spacing: 0.04em;\n margin-bottom: 0.25rem;\n display: block;\n}\n\n.detail-snippet {\n margin: 0;\n font-size: 0.82rem;\n color: var(--mj-text-secondary);\n line-height: 1.5;\n}\n\n.detail-meta {\n display: flex;\n flex-wrap: wrap;\n gap: 0.75rem;\n margin-bottom: 0.65rem;\n}\n\n.detail-meta-item {\n display: flex;\n align-items: center;\n gap: 5px;\n font-size: 0.75rem;\n color: var(--mj-text-muted);\n}\n\n.detail-meta-item i {\n font-size: 0.65rem;\n}\n\n.detail-record-id {\n font-family: monospace;\n font-size: 0.7rem;\n max-width: 180px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.detail-tags {\n display: flex;\n flex-wrap: wrap;\n gap: 4px;\n margin-bottom: 0.65rem;\n}\n\n.detail-links {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n margin-bottom: 8px;\n}\n\n.detail-link {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 3px 8px;\n border-radius: 6px;\n font-size: 0.72rem;\n color: var(--mj-text-link);\n background: color-mix(in srgb, var(--mj-brand-primary) 5%, var(--mj-bg-surface));\n border: 1px solid color-mix(in srgb, var(--mj-brand-primary) 15%, var(--mj-border-default));\n text-decoration: none;\n transition: background 0.15s ease;\n}\n\n.detail-link:hover {\n background: color-mix(in srgb, var(--mj-brand-primary) 12%, var(--mj-bg-surface));\n text-decoration: underline;\n}\n\n.detail-link i {\n font-size: 0.6rem;\n}\n\n.detail-actions {\n display: flex;\n gap: 8px;\n}\n\n.detail-open-btn {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 6px 14px;\n border: 1px solid var(--mj-brand-primary);\n border-radius: 6px;\n background: transparent;\n color: var(--mj-brand-primary);\n font-size: 0.78rem;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.15s;\n}\n\n.detail-open-btn:hover {\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n}\n\n.detail-similar-btn {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 6px 14px;\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n background: transparent;\n color: var(--mj-text-secondary);\n font-size: 0.78rem;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.15s;\n}\n\n.detail-similar-btn:hover {\n border-color: var(--mj-brand-primary);\n color: var(--mj-brand-primary);\n background: color-mix(in srgb, var(--mj-brand-primary) 5%, var(--mj-bg-surface));\n}\n\n.result-card-snippet {\n color: var(--mj-text-secondary);\n font-size: 0.82rem;\n line-height: 1.4;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n}\n\n.result-card-tags {\n display: flex;\n flex-wrap: wrap;\n gap: 0.25rem;\n margin-top: 0.35rem;\n}\n\n.result-card-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-card-tag-more {\n color: var(--mj-brand-primary);\n}\n\n.result-card-score {\n flex-shrink: 0;\n margin-top: 0.2rem;\n}\n\n.score-circle {\n width: 2.5rem;\n height: 2.5rem;\n border-radius: 50%;\n display: flex;\n align-items: center;\n justify-content: center;\n /* Default: neutral gray */\n background: color-mix(in srgb, var(--mj-text-muted) 10%, var(--mj-bg-surface));\n border: 2px solid color-mix(in srgb, var(--mj-text-muted) 25%, var(--mj-bg-surface));\n}\n.score-circle.score-high {\n background: color-mix(in srgb, var(--mj-status-success) 15%, var(--mj-bg-surface));\n border-color: color-mix(in srgb, var(--mj-status-success) 40%, var(--mj-bg-surface));\n}\n.score-circle.score-medium {\n background: color-mix(in srgb, var(--mj-status-warning) 15%, var(--mj-bg-surface));\n border-color: color-mix(in srgb, var(--mj-status-warning) 40%, var(--mj-bg-surface));\n}\n.score-circle.score-low {\n background: color-mix(in srgb, var(--mj-text-muted) 10%, var(--mj-bg-surface));\n border-color: color-mix(in srgb, var(--mj-text-muted) 25%, var(--mj-bg-surface));\n}\n\n.score-value {\n font-size: 0.7rem;\n font-weight: 700;\n color: var(--mj-text-secondary);\n}\n.score-high .score-value {\n color: var(--mj-status-success-text, var(--mj-status-success));\n}\n.score-medium .score-value {\n color: var(--mj-status-warning-text, var(--mj-status-warning));\n}\n.score-low .score-value {\n color: var(--mj-text-muted);\n}\n\n/* Show More Button */\n.results-show-more {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n width: 100%;\n padding: 0.5rem 1rem;\n border: none;\n background: transparent;\n color: var(--mj-brand-primary);\n font-size: 0.82rem;\n cursor: pointer;\n justify-content: center;\n transition: background 0.1s ease;\n}\n\n.results-show-more:hover {\n background: var(--mj-bg-surface-hover);\n}\n\n/* Google-style Pagination \u2014 pinned at bottom */\n.results-pager {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 4px;\n padding: 0.75rem 0;\n flex-shrink: 0;\n border-top: 1px solid var(--mj-border-subtle);\n background: var(--mj-bg-surface);\n}\n\n.pager-btn {\n display: flex;\n align-items: center;\n justify-content: center;\n min-width: 32px;\n height: 32px;\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n background: var(--mj-bg-surface);\n color: var(--mj-text-secondary);\n font-size: 0.82rem;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.15s ease;\n padding: 0 6px;\n}\n\n.pager-btn:hover:not(:disabled) {\n background: var(--mj-bg-surface-hover);\n border-color: var(--mj-brand-primary);\n color: var(--mj-brand-primary);\n}\n\n.pager-btn:disabled {\n opacity: 0.35;\n cursor: default;\n}\n\n.pager-active {\n background: var(--mj-brand-primary);\n border-color: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n}\n\n.pager-active:hover:not(:disabled) {\n background: var(--mj-brand-primary-hover);\n border-color: var(--mj-brand-primary-hover);\n color: var(--mj-text-inverse);\n}\n\n.pager-ellipsis {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 28px;\n color: var(--mj-text-muted);\n font-size: 0.8rem;\n}\n\n.pager-prev i,\n.pager-next i {\n font-size: 0.7rem;\n}\n\n/* Search text highlighting */\n:host ::ng-deep .search-highlight,\n.search-highlight {\n background: color-mix(in srgb, var(--mj-status-warning) 35%, transparent);\n color: inherit;\n padding: 1px 2px;\n border-radius: 2px;\n}\n"] }]
|
|
690
|
+
args: [{ standalone: false, selector: 'mj-search-results', template: "@if (IsLoading) {\n <div class=\"results-loading\">\n <mj-loading text=\"Searching...\" size=\"medium\"></mj-loading>\n </div>\n} @else if ((DisplayMode === 'flat' ? FlatResults.length : ResultGroups.length) === 0 && TotalCount === 0) {\n <mj-empty-state\n Variant=\"no-results\"\n Title=\"No results to display\" />\n} @else {\n <!-- Results summary -->\n @if (ShowSummary) {\n <div class=\"results-summary\">\n <span>{{ TotalCount }} results</span>\n @if (ElapsedMs > 0) {\n <span class=\"results-summary-time\"> in {{ FormatTime(ElapsedMs) }}</span>\n }\n </div>\n }\n\n @if (DisplayMode === 'flat') {\n <!-- \u2550\u2550\u2550\u2550\u2550\u2550 FLAT BLENDED LIST (sorted by score) \u2550\u2550\u2550\u2550\u2550\u2550 -->\n <div class=\"results-scroll-area\">\n @for (result of PagedResults; track result.ID) {\n <ng-container *ngTemplateOutlet=\"resultCardTpl; context: { $implicit: result }\"></ng-container>\n }\n </div>\n\n <!-- Google-style pagination (sticky at bottom) -->\n @if (TotalPages > 1) {\n <div class=\"results-pager\">\n <button class=\"pager-btn pager-prev\" [disabled]=\"CurrentPage === 1\"\n (click)=\"GoToPage(CurrentPage - 1)\" aria-label=\"Previous page\">\n <i class=\"fa-solid fa-chevron-left\"></i>\n </button>\n @for (p of PageNumbers; track $index) {\n @if (p === -1) {\n <span class=\"pager-ellipsis\">...</span>\n } @else {\n <button class=\"pager-btn pager-num\" [class.pager-active]=\"p === CurrentPage\"\n (click)=\"GoToPage(p)\">{{ p }}</button>\n }\n }\n <button class=\"pager-btn pager-next\" [disabled]=\"CurrentPage === TotalPages\"\n (click)=\"GoToPage(CurrentPage + 1)\" aria-label=\"Next page\">\n <i class=\"fa-solid fa-chevron-right\"></i>\n </button>\n </div>\n }\n } @else {\n <!-- \u2550\u2550\u2550\u2550\u2550\u2550 GROUPED BY SOURCE TYPE \u2550\u2550\u2550\u2550\u2550\u2550 -->\n @for (group of ResultGroups; track group.SourceType) {\n <div class=\"results-group\">\n <div class=\"results-group-header\" (click)=\"ToggleGroup(group.SourceType)\">\n @if (ShowSourceIcons) {\n <i [class]=\"group.Icon + ' results-group-icon'\"></i>\n }\n <span class=\"results-group-label\">{{ group.Label }}</span>\n <span class=\"results-group-count\">({{ group.TotalCount }})</span>\n <i class=\"fa-solid results-group-chevron\"\n [class.fa-chevron-down]=\"!IsGroupExpanded(group.SourceType)\"\n [class.fa-chevron-up]=\"IsGroupExpanded(group.SourceType)\"></i>\n </div>\n\n @for (result of GetVisibleResults(group); track result.ID) {\n <ng-container *ngTemplateOutlet=\"resultCardTpl; context: { $implicit: result }\"></ng-container>\n }\n\n @if (HasMoreResults(group)) {\n <button class=\"results-show-more\" (click)=\"ToggleGroup(group.SourceType)\">\n <i class=\"fa-solid fa-chevron-down\"></i>\n Show {{ GetHiddenCount(group) }} more results\n </button>\n }\n </div>\n }\n }\n}\n\n<!-- \u2550\u2550\u2550 Shared result card template \u2550\u2550\u2550 -->\n<ng-template #resultCardTpl let-result>\n <div class=\"result-card\" [class.result-card-expanded]=\"IsExpanded(result.ID)\">\n <!-- Main row -->\n <div class=\"result-card-main\" (click)=\"ToggleExpand(result)\">\n <div class=\"result-card-icon-wrap\">\n <i [class]=\"result.EntityIcon || result.SourceIcon || 'fa-solid fa-database'\"></i>\n </div>\n <div class=\"result-card-body\">\n <div class=\"result-card-title\" [innerHTML]=\"HighlightMatch(result.RecordName || result.Title)\"></div>\n <div class=\"result-card-entity\" [innerHTML]=\"HighlightMatch(result.EntityDisplayName || result.EntityName)\"></div>\n @if (result.Tags && result.Tags.length > 0) {\n <div class=\"result-card-tags\">\n @for (tag of result.Tags.slice(0, 5); track tag) {\n <span class=\"result-tag-chip\" [innerHTML]=\"HighlightMatch(tag)\"></span>\n }\n @if (result.Tags.length > 5) {\n <span class=\"result-tag-more\">+{{ result.Tags.length - 5 }}</span>\n }\n </div>\n }\n </div>\n @if (ShowScores) {\n <div class=\"result-card-score\">\n <div class=\"score-circle\" [class.score-high]=\"result.Score >= 0.40\" [class.score-medium]=\"result.Score >= 0.15 && result.Score < 0.40\" [class.score-low]=\"result.Score < 0.15\">\n <span class=\"score-value\">{{ FormatScore(result.Score) }}</span>\n </div>\n </div>\n }\n <div class=\"result-card-actions\">\n <button class=\"result-open-btn\" (click)=\"OnOpenRecord(result, $event)\"\n [title]=\"result.ResultType === 'storage-file' ? 'Open file' : 'Open record'\"\n [attr.aria-label]=\"result.ResultType === 'storage-file' ? 'Open file' : 'Open record'\">\n <i [class]=\"result.ResultType === 'storage-file' ? 'fa-solid fa-external-link' : 'fa-solid fa-arrow-up-right-from-square'\"></i>\n </button>\n <button class=\"result-expand-btn\" (click)=\"ToggleExpand(result, $event)\" title=\"Expand details\" aria-label=\"Expand details\">\n <i class=\"fa-solid\" [class.fa-chevron-down]=\"!IsExpanded(result.ID)\"\n [class.fa-chevron-up]=\"IsExpanded(result.ID)\"></i>\n </button>\n </div>\n </div>\n\n <!-- Expanded detail -->\n @if (IsExpanded(result.ID)) {\n <div class=\"result-card-detail\">\n @if (result.Snippet) {\n <div class=\"detail-section\">\n <span class=\"detail-label\">Preview</span>\n <p class=\"detail-snippet\" [innerHTML]=\"HighlightMatch(result.Snippet)\"></p>\n </div>\n }\n <div class=\"detail-meta\">\n <div class=\"detail-meta-item\">\n <i class=\"fa-solid fa-layer-group\"></i>\n <span>{{ result.EntityDisplayName || result.EntityName }}</span>\n </div>\n <div class=\"detail-meta-item\">\n <i class=\"fa-solid fa-fingerprint\"></i>\n <span class=\"detail-record-id\">{{ result.RecordID }}</span>\n </div>\n <!-- SR-1: Show overall score consistently -->\n <div class=\"detail-meta-item\">\n <i class=\"fa-solid fa-signal\"></i>\n <span>Relevance: {{ FormatScore(result.Score) }}</span>\n </div>\n @if (result.ScoreBreakdown) {\n <div class=\"detail-meta-item\">\n <i class=\"fa-solid fa-chart-bar\"></i>\n <span>\n @if (result.ScoreBreakdown.Vector) { Vector: {{ FormatScore(result.ScoreBreakdown.Vector) }} }\n @if (result.ScoreBreakdown.FullText) { FTS: {{ FormatScore(result.ScoreBreakdown.FullText) }} }\n </span>\n </div>\n }\n </div>\n @if (ShowTags && result.Tags.length > 0) {\n <div class=\"detail-tags\">\n @for (tag of result.Tags; track tag) {\n <span class=\"result-card-tag\" [innerHTML]=\"HighlightMatch(tag)\"></span>\n }\n </div>\n }\n @if (GetResolvedLinks(result).length > 0) {\n <div class=\"detail-links\">\n @for (link of GetResolvedLinks(result); track link.Value) {\n @if (link.Type === 'URL') {\n <a [href]=\"link.Value\" target=\"_blank\" class=\"detail-link\" (click)=\"$event.stopPropagation()\">\n <i class=\"fa-solid fa-arrow-up-right-from-square\"></i>\n {{ TruncateUrl(link.Value) }}\n </a>\n } @else if (link.Type === 'Email') {\n <a [href]=\"'mailto:' + link.Value\" class=\"detail-link detail-link-email\" (click)=\"$event.stopPropagation()\">\n <i class=\"fa-solid fa-envelope\"></i>\n {{ link.Value }}\n </a>\n }\n }\n </div>\n }\n <div class=\"detail-actions\">\n <button class=\"detail-open-btn\" (click)=\"OnOpenRecord(result, $event)\">\n @if (result.ResultType === 'storage-file') {\n <i class=\"fa-solid fa-external-link\"></i> Open File\n } @else {\n <i class=\"fa-solid fa-arrow-up-right-from-square\"></i> Open Record\n }\n </button>\n <button class=\"detail-similar-btn\" (click)=\"OnMoreLikeThis(result, $event)\">\n <i class=\"fa-solid fa-shuffle\"></i> See Similar Items\n </button>\n </div>\n </div>\n }\n </div>\n</ng-template>\n", styles: ["/* Search Results Component */\n\n:host {\n display: flex;\n flex-direction: column;\n min-height: 0;\n flex: 1;\n}\n\n.results-scroll-area {\n flex: 1;\n overflow-y: auto;\n min-height: 0;\n}\n\n.results-loading {\n display: flex;\n justify-content: center;\n padding: 3rem 1rem;\n}\n\n.results-summary {\n padding: 0.75rem 1rem;\n color: var(--mj-text-secondary);\n font-size: 0.85rem;\n border-bottom: 1px solid var(--mj-border-subtle);\n}\n\n.results-summary-time {\n color: var(--mj-text-muted);\n}\n\n/* Group Header */\n.results-group {\n border-bottom: 1px solid var(--mj-border-subtle);\n}\n\n.results-group-header {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n padding: 0.75rem 1rem;\n cursor: pointer;\n transition: background 0.1s ease;\n}\n\n.results-group-header:hover {\n background: var(--mj-bg-surface-hover);\n}\n\n.results-group-icon {\n color: var(--mj-brand-primary);\n font-size: 0.85rem;\n}\n\n.results-group-label {\n font-weight: 600;\n font-size: 0.85rem;\n color: var(--mj-text-primary);\n flex: 1;\n}\n\n.results-group-count {\n font-size: 0.78rem;\n color: var(--mj-text-muted);\n}\n\n.results-group-chevron {\n color: var(--mj-text-muted);\n font-size: 0.7rem;\n transition: transform 0.2s ease;\n}\n\n/* Result Card \u2014 expandable */\n.result-card {\n border: 1px solid var(--mj-border-subtle);\n border-radius: 8px;\n margin: 0 0 0.35rem 1.5rem;\n overflow: hidden;\n transition: border-color 0.15s, box-shadow 0.15s;\n}\n\n.result-card:hover {\n border-color: var(--mj-border-default);\n}\n\n.result-card-expanded {\n border-color: var(--mj-brand-primary);\n box-shadow: 0 2px 8px color-mix(in srgb, var(--mj-brand-primary) 10%, transparent);\n}\n\n.result-card-main {\n display: flex;\n align-items: center;\n gap: 0.75rem;\n padding: 0.65rem 0.85rem;\n cursor: pointer;\n transition: background 0.1s ease;\n}\n\n.result-card-main:hover {\n background: var(--mj-bg-surface-hover);\n}\n\n.result-card-icon-wrap {\n width: 2rem;\n height: 2rem;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 6px;\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n font-size: 0.8rem;\n flex-shrink: 0;\n}\n\n.result-card-body {\n flex: 1;\n min-width: 0;\n}\n\n.result-card-title {\n color: var(--mj-text-primary);\n font-weight: 600;\n font-size: 0.85rem;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.result-card-entity {\n color: var(--mj-text-muted);\n font-size: 0.7rem;\n}\n\n.result-card-tags {\n display: flex;\n flex-wrap: wrap;\n gap: 4px;\n margin-top: 4px;\n}\n\n.result-tag-chip {\n display: inline-block;\n padding: 1px 7px;\n border-radius: 10px;\n font-size: 0.65rem;\n font-weight: 500;\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n border: 1px solid color-mix(in srgb, var(--mj-brand-primary) 20%, var(--mj-border-default));\n}\n\n.result-tag-more {\n display: inline-block;\n padding: 1px 6px;\n font-size: 0.6rem;\n color: var(--mj-text-muted);\n}\n\n.result-card-actions {\n display: flex;\n align-items: center;\n gap: 4px;\n flex-shrink: 0;\n}\n\n.result-open-btn,\n.result-expand-btn {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 28px;\n height: 28px;\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n background: transparent;\n color: var(--mj-text-muted);\n font-size: 11px;\n cursor: pointer;\n transition: all 0.15s;\n}\n\n.result-open-btn:hover {\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n border-color: var(--mj-brand-primary);\n color: var(--mj-brand-primary);\n}\n\n.result-expand-btn:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-primary);\n}\n\n/* Expanded detail panel */\n.result-card-detail {\n padding: 0.75rem 1rem 0.85rem 3.5rem;\n border-top: 1px solid var(--mj-border-subtle);\n background: var(--mj-bg-surface-card);\n}\n\n.detail-section {\n margin-bottom: 0.65rem;\n}\n\n.detail-label {\n font-size: 0.7rem;\n font-weight: 600;\n color: var(--mj-text-muted);\n text-transform: uppercase;\n letter-spacing: 0.04em;\n margin-bottom: 0.25rem;\n display: block;\n}\n\n.detail-snippet {\n margin: 0;\n font-size: 0.82rem;\n color: var(--mj-text-secondary);\n line-height: 1.5;\n}\n\n.detail-meta {\n display: flex;\n flex-wrap: wrap;\n gap: 0.75rem;\n margin-bottom: 0.65rem;\n}\n\n.detail-meta-item {\n display: flex;\n align-items: center;\n gap: 5px;\n font-size: 0.75rem;\n color: var(--mj-text-muted);\n}\n\n.detail-meta-item i {\n font-size: 0.65rem;\n}\n\n.detail-record-id {\n font-family: monospace;\n font-size: 0.7rem;\n max-width: 180px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.detail-tags {\n display: flex;\n flex-wrap: wrap;\n gap: 4px;\n margin-bottom: 0.65rem;\n}\n\n.detail-links {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n margin-bottom: 8px;\n}\n\n.detail-link {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 3px 8px;\n border-radius: 6px;\n font-size: 0.72rem;\n color: var(--mj-text-link);\n background: color-mix(in srgb, var(--mj-brand-primary) 5%, var(--mj-bg-surface));\n border: 1px solid color-mix(in srgb, var(--mj-brand-primary) 15%, var(--mj-border-default));\n text-decoration: none;\n transition: background 0.15s ease;\n}\n\n.detail-link:hover {\n background: color-mix(in srgb, var(--mj-brand-primary) 12%, var(--mj-bg-surface));\n text-decoration: underline;\n}\n\n.detail-link i {\n font-size: 0.6rem;\n}\n\n.detail-actions {\n display: flex;\n gap: 8px;\n}\n\n.detail-open-btn {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 6px 14px;\n border: 1px solid var(--mj-brand-primary);\n border-radius: 6px;\n background: transparent;\n color: var(--mj-brand-primary);\n font-size: 0.78rem;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.15s;\n}\n\n.detail-open-btn:hover {\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n}\n\n.detail-similar-btn {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 6px 14px;\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n background: transparent;\n color: var(--mj-text-secondary);\n font-size: 0.78rem;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.15s;\n}\n\n.detail-similar-btn:hover {\n border-color: var(--mj-brand-primary);\n color: var(--mj-brand-primary);\n background: color-mix(in srgb, var(--mj-brand-primary) 5%, var(--mj-bg-surface));\n}\n\n.result-card-snippet {\n color: var(--mj-text-secondary);\n font-size: 0.82rem;\n line-height: 1.4;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n}\n\n.result-card-tags {\n display: flex;\n flex-wrap: wrap;\n gap: 0.25rem;\n margin-top: 0.35rem;\n}\n\n.result-card-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-card-tag-more {\n color: var(--mj-brand-primary);\n}\n\n.result-card-score {\n flex-shrink: 0;\n margin-top: 0.2rem;\n}\n\n.score-circle {\n width: 2.5rem;\n height: 2.5rem;\n border-radius: 50%;\n display: flex;\n align-items: center;\n justify-content: center;\n /* Default: neutral gray */\n background: color-mix(in srgb, var(--mj-text-muted) 10%, var(--mj-bg-surface));\n border: 2px solid color-mix(in srgb, var(--mj-text-muted) 25%, var(--mj-bg-surface));\n}\n.score-circle.score-high {\n background: color-mix(in srgb, var(--mj-status-success) 15%, var(--mj-bg-surface));\n border-color: color-mix(in srgb, var(--mj-status-success) 40%, var(--mj-bg-surface));\n}\n.score-circle.score-medium {\n background: color-mix(in srgb, var(--mj-status-warning) 15%, var(--mj-bg-surface));\n border-color: color-mix(in srgb, var(--mj-status-warning) 40%, var(--mj-bg-surface));\n}\n.score-circle.score-low {\n background: color-mix(in srgb, var(--mj-text-muted) 10%, var(--mj-bg-surface));\n border-color: color-mix(in srgb, var(--mj-text-muted) 25%, var(--mj-bg-surface));\n}\n\n.score-value {\n font-size: 0.7rem;\n font-weight: 700;\n color: var(--mj-text-secondary);\n}\n.score-high .score-value {\n color: var(--mj-status-success-text, var(--mj-status-success));\n}\n.score-medium .score-value {\n color: var(--mj-status-warning-text, var(--mj-status-warning));\n}\n.score-low .score-value {\n color: var(--mj-text-muted);\n}\n\n/* Show More Button */\n.results-show-more {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n width: 100%;\n padding: 0.5rem 1rem;\n border: none;\n background: transparent;\n color: var(--mj-brand-primary);\n font-size: 0.82rem;\n cursor: pointer;\n justify-content: center;\n transition: background 0.1s ease;\n}\n\n.results-show-more:hover {\n background: var(--mj-bg-surface-hover);\n}\n\n/* Google-style Pagination \u2014 pinned at bottom */\n.results-pager {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 4px;\n padding: 0.75rem 0;\n flex-shrink: 0;\n border-top: 1px solid var(--mj-border-subtle);\n background: var(--mj-bg-surface);\n}\n\n.pager-btn {\n display: flex;\n align-items: center;\n justify-content: center;\n min-width: 32px;\n height: 32px;\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n background: var(--mj-bg-surface);\n color: var(--mj-text-secondary);\n font-size: 0.82rem;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.15s ease;\n padding: 0 6px;\n}\n\n.pager-btn:hover:not(:disabled) {\n background: var(--mj-bg-surface-hover);\n border-color: var(--mj-brand-primary);\n color: var(--mj-brand-primary);\n}\n\n.pager-btn:disabled {\n opacity: 0.35;\n cursor: default;\n}\n\n.pager-active {\n background: var(--mj-brand-primary);\n border-color: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n}\n\n.pager-active:hover:not(:disabled) {\n background: var(--mj-brand-primary-hover);\n border-color: var(--mj-brand-primary-hover);\n color: var(--mj-text-inverse);\n}\n\n.pager-ellipsis {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 28px;\n color: var(--mj-text-muted);\n font-size: 0.8rem;\n}\n\n.pager-prev i,\n.pager-next i {\n font-size: 0.7rem;\n}\n\n/* Search text highlighting */\n:host ::ng-deep .search-highlight,\n.search-highlight {\n background: color-mix(in srgb, var(--mj-status-warning) 35%, transparent);\n color: inherit;\n padding: 1px 2px;\n border-radius: 2px;\n}\n"] }]
|
|
691
691
|
}], null, { ResultGroups: [{
|
|
692
692
|
type: Input
|
|
693
693
|
}], FlatResults: [{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"search-results.component.js","sourceRoot":"","sources":["../../src/lib/search-results.component.ts","../../src/lib/search-results.component.html"],"names":[],"mappings":"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","sourcesContent":["/**\n * @fileoverview Search Results Component\n *\n * Displays search results grouped by source type with expandable sections.\n * Used both within the search overlay and as a standalone results view\n * in the Knowledge Hub search dashboard.\n */\n\nimport {\n Component,\n EventEmitter,\n Input,\n Output,\n ChangeDetectorRef,\n inject\n} from '@angular/core';\nimport { SearchResultItem, SearchResultGroup, SearchResultSelectedEvent } from './search-types';\nimport { EscapeHTML, HighlightSearchMatches } from '@memberjunction/global';\nimport { BaseAngularComponent } from '@memberjunction/ng-base-types';\n\n@Component({\n standalone: false,\n selector: 'mj-search-results',\n templateUrl: './search-results.component.html',\n styleUrls: ['./search-results.component.css']\n})\nexport class SearchResultsComponent extends BaseAngularComponent {\n private cdr = inject(ChangeDetectorRef);\n\n /** Grouped search results to display (used when DisplayMode is 'grouped') */\n @Input() ResultGroups: SearchResultGroup[] = [];\n\n /** Flat list of all results sorted by score (used when DisplayMode is 'flat') */\n @Input() FlatResults: SearchResultItem[] = [];\n\n /** Display mode: 'flat' for blended list sorted by score, 'grouped' for source-type groups */\n @Input() DisplayMode: 'flat' | 'grouped' = 'flat';\n\n /** Whether results are currently loading */\n @Input() IsLoading = false;\n\n /** Total result count */\n @Input() TotalCount = 0;\n\n /** Elapsed search time in ms */\n @Input() ElapsedMs = 0;\n\n /** Maximum results per group before \"Show more\" (grouped mode only) */\n @Input() MaxPerGroup = 5;\n\n /** Results per page in flat mode */\n @Input() PageSize = 10;\n\n /** Whether to show the results summary line (count + time). Set false when parent provides its own header. */\n @Input() ShowSummary = true;\n\n /** Whether to show score badges */\n @Input() ShowScores = true;\n\n /** Whether to show tags */\n @Input() ShowTags = true;\n\n /** Whether to show source type icons */\n @Input() ShowSourceIcons = true;\n\n /** Text to highlight within result cards (e.g. from a client-side filter) */\n @Input() HighlightText = '';\n\n @Output() ResultSelected = new EventEmitter<SearchResultSelectedEvent>();\n\n /** Emitted when user clicks \"Open Record\" — parent handles navigation */\n @Output() OpenRecordRequested = new EventEmitter<SearchResultItem>();\n\n /** Emitted when user clicks \"See Similar Items\" — parent runs a \"more like this\" search */\n @Output() MoreLikeThisRequested = new EventEmitter<SearchResultItem>();\n\n /** Current page (1-based) for flat mode pagination */\n public CurrentPage = 1;\n\n /** Tracks which groups are expanded */\n public ExpandedGroups = new Set<string>();\n\n /** Set of expanded result card IDs — multiple can be open simultaneously */\n public ExpandedResultIDs = new Set<string>();\n\n // ── Flat mode pagination ──\n\n /** Get results for the current page in flat mode */\n public get PagedResults(): SearchResultItem[] {\n const start = (this.CurrentPage - 1) * this.PageSize;\n return this.FlatResults.slice(start, start + this.PageSize);\n }\n\n /** Total number of pages */\n public get TotalPages(): number {\n return Math.ceil(this.FlatResults.length / this.PageSize);\n }\n\n /** Page numbers to display in the pager */\n public get PageNumbers(): number[] {\n const total = this.TotalPages;\n if (total <= 7) return Array.from({ length: total }, (_, i) => i + 1);\n\n const pages: number[] = [1];\n const start = Math.max(2, this.CurrentPage - 2);\n const end = Math.min(total - 1, this.CurrentPage + 2);\n if (start > 2) pages.push(-1); // ellipsis marker\n for (let i = start; i <= end; i++) pages.push(i);\n if (end < total - 1) pages.push(-1); // ellipsis marker\n pages.push(total);\n return pages;\n }\n\n /** Navigate to a specific page */\n public GoToPage(page: number): void {\n if (page < 1 || page > this.TotalPages) return;\n this.CurrentPage = page;\n this.cdr.detectChanges();\n }\n\n // ── Grouped mode ──\n\n /** Toggle group expansion */\n public ToggleGroup(sourceType: string): void {\n if (this.ExpandedGroups.has(sourceType)) {\n this.ExpandedGroups.delete(sourceType);\n } else {\n this.ExpandedGroups.add(sourceType);\n }\n this.cdr.detectChanges();\n }\n\n /** Whether a group is expanded */\n public IsGroupExpanded(sourceType: string): boolean {\n return this.ExpandedGroups.has(sourceType);\n }\n\n /** Get visible results for a group (respecting max per group unless expanded) */\n public GetVisibleResults(group: SearchResultGroup): SearchResultItem[] {\n if (this.IsGroupExpanded(group.SourceType)) {\n return group.Results;\n }\n return group.Results.slice(0, this.MaxPerGroup);\n }\n\n /** Whether a group has more results than shown */\n public HasMoreResults(group: SearchResultGroup): boolean {\n return group.Results.length > this.MaxPerGroup && !this.IsGroupExpanded(group.SourceType);\n }\n\n /** Get the count of hidden results */\n public GetHiddenCount(group: SearchResultGroup): number {\n return group.Results.length - this.MaxPerGroup;\n }\n\n /** Handle clicking a result */\n public OnResultClick(result: SearchResultItem, event: MouseEvent): void {\n this.ResultSelected.emit({\n Result: result,\n OpenInNewTab: event.metaKey || event.ctrlKey\n });\n }\n\n /** Toggle expand/collapse of a result card — multiple can be open */\n public ToggleExpand(result: SearchResultItem, event?: MouseEvent): void {\n if (event) event.stopPropagation();\n if (this.ExpandedResultIDs.has(result.ID)) {\n this.ExpandedResultIDs.delete(result.ID);\n } else {\n this.ExpandedResultIDs.add(result.ID);\n }\n this.cdr.detectChanges();\n }\n\n /** Check if a result card is expanded */\n public IsExpanded(resultID: string): boolean {\n return this.ExpandedResultIDs.has(resultID);\n }\n\n /** Handle \"Open Record\" button click */\n public OnOpenRecord(result: SearchResultItem, event: MouseEvent): void {\n event.stopPropagation();\n this.OpenRecordRequested.emit(result);\n }\n\n /** Handle \"See Similar Items\" button click — emits the result for a \"more like this\" search */\n public OnMoreLikeThis(result: SearchResultItem, event: MouseEvent): void {\n event.stopPropagation();\n this.MoreLikeThisRequested.emit(result);\n }\n\n /**\n * Wrap substrings matching HighlightText in <mark> tags for visual emphasis.\n * The input is regex-escaped so user text is treated as a literal string.\n */\n public HighlightMatch(text: string): string {\n return HighlightSearchMatches(text, this.HighlightText, 'search-highlight');\n }\n\n /** Format a score as a percentage */\n public FormatScore(score: number): string {\n return `${Math.round(score * 100)}%`;\n }\n\n /** Format elapsed time */\n public FormatTime(ms: number): string {\n if (ms < 1000) return `${ms}ms`;\n return `${(ms / 1000).toFixed(1)}s`;\n }\n\n /** Resolved link cache per result ID */\n private resolvedLinksCache = new Map<string, { Value: string; Label: string; Type: 'URL' | 'Email' }[]>();\n\n /**\n * Resolve URL and Email fields from the result's RawMetadata using entity ExtendedType metadata.\n * Returns clickable link entries with proper type classification.\n */\n public GetResolvedLinks(result: SearchResultItem): { Value: string; Label: string; Type: 'URL' | 'Email' }[] {\n if (this.resolvedLinksCache.has(result.ID)) {\n return this.resolvedLinksCache.get(result.ID)!;\n }\n\n const links: { Value: string; Label: string; Type: 'URL' | 'Email' }[] = [];\n\n if (result.RawMetadata) {\n try {\n const meta = JSON.parse(result.RawMetadata) as Record<string, unknown>;\n const linkFields = this.GetLinkFields(result.EntityName);\n\n for (const field of linkFields) {\n const value = meta[field.Name];\n if (value && typeof value === 'string' && value.trim().length > 0) {\n links.push({\n Value: value,\n Label: field.DisplayName,\n Type: field.Type\n });\n }\n }\n } catch { /* ignore parse errors */ }\n }\n\n // Fallback: extract URLs from snippet if no ExtendedType fields found\n if (links.length === 0) {\n const urls = this.ExtractUrls(result.Snippet);\n for (const url of urls) {\n links.push({ Value: url, Label: '', Type: 'URL' });\n }\n }\n\n this.resolvedLinksCache.set(result.ID, links);\n return links;\n }\n\n /** Extract URLs from snippet text */\n public ExtractUrls(snippet: string): string[] {\n if (!snippet) return [];\n const urlRegex = /https?:\\/\\/[^\\s,;)>\"']+/g;\n const matches = snippet.match(urlRegex);\n return matches ? [...new Set(matches)] : [];\n }\n\n /** Truncate a URL for display */\n public TruncateUrl(url: string): string {\n try {\n const parsed = new URL(url);\n const path = parsed.pathname.length > 30\n ? parsed.pathname.substring(0, 30) + '...'\n : parsed.pathname;\n return parsed.hostname + path;\n } catch {\n return url.length > 50 ? url.substring(0, 50) + '...' : url;\n }\n }\n\n /** Cache of URL/Email field info per entity name */\n private linkFieldsCache = new Map<string, { Name: string; DisplayName: string; Type: 'URL' | 'Email' }[]>();\n\n /**\n * Get URL and Email fields for an entity using ExtendedType from entity metadata.\n * Returns fields with ExtendedType='URL' or ExtendedType='Email'.\n */\n public GetLinkFields(entityName: string): { Name: string; DisplayName: string; Type: 'URL' | 'Email' }[] {\n if (this.linkFieldsCache.has(entityName)) {\n return this.linkFieldsCache.get(entityName)!;\n }\n\n try {\n const md = this.ProviderToUse;\n const entityInfo = md.Entities.find(e => e.Name === entityName);\n if (!entityInfo) {\n this.linkFieldsCache.set(entityName, []);\n return [];\n }\n\n const linkFields = entityInfo.Fields\n .filter(f => f.ExtendedType === 'URL' || f.ExtendedType === 'Email')\n .map(f => ({\n Name: f.Name,\n DisplayName: f.DisplayNameOrName,\n Type: f.ExtendedType as 'URL' | 'Email'\n }));\n\n this.linkFieldsCache.set(entityName, linkFields);\n return linkFields;\n } catch {\n this.linkFieldsCache.set(entityName, []);\n return [];\n }\n }\n}\n","@if (IsLoading) {\n <div class=\"results-loading\">\n <mj-loading text=\"Searching...\" size=\"medium\"></mj-loading>\n </div>\n} @else if ((DisplayMode === 'flat' ? FlatResults.length : ResultGroups.length) === 0 && TotalCount === 0) {\n <mj-empty-state\n Variant=\"no-results\"\n Title=\"No results to display\" />\n} @else {\n <!-- Results summary -->\n @if (ShowSummary) {\n <div class=\"results-summary\">\n <span>{{ TotalCount }} results</span>\n @if (ElapsedMs > 0) {\n <span class=\"results-summary-time\"> in {{ FormatTime(ElapsedMs) }}</span>\n }\n </div>\n }\n\n @if (DisplayMode === 'flat') {\n <!-- ══════ FLAT BLENDED LIST (sorted by score) ══════ -->\n <div class=\"results-scroll-area\">\n @for (result of PagedResults; track result.ID) {\n <ng-container *ngTemplateOutlet=\"resultCardTpl; context: { $implicit: result }\"></ng-container>\n }\n </div>\n\n <!-- Google-style pagination (sticky at bottom) -->\n @if (TotalPages > 1) {\n <div class=\"results-pager\">\n <button class=\"pager-btn pager-prev\" [disabled]=\"CurrentPage === 1\"\n (click)=\"GoToPage(CurrentPage - 1)\" aria-label=\"Previous page\">\n <i class=\"fa-solid fa-chevron-left\"></i>\n </button>\n @for (p of PageNumbers; track $index) {\n @if (p === -1) {\n <span class=\"pager-ellipsis\">...</span>\n } @else {\n <button class=\"pager-btn pager-num\" [class.pager-active]=\"p === CurrentPage\"\n (click)=\"GoToPage(p)\">{{ p }}</button>\n }\n }\n <button class=\"pager-btn pager-next\" [disabled]=\"CurrentPage === TotalPages\"\n (click)=\"GoToPage(CurrentPage + 1)\" aria-label=\"Next page\">\n <i class=\"fa-solid fa-chevron-right\"></i>\n </button>\n </div>\n }\n } @else {\n <!-- ══════ GROUPED BY SOURCE TYPE ══════ -->\n @for (group of ResultGroups; track group.SourceType) {\n <div class=\"results-group\">\n <div class=\"results-group-header\" (click)=\"ToggleGroup(group.SourceType)\">\n @if (ShowSourceIcons) {\n <i [class]=\"group.Icon + ' results-group-icon'\"></i>\n }\n <span class=\"results-group-label\">{{ group.Label }}</span>\n <span class=\"results-group-count\">({{ group.TotalCount }})</span>\n <i class=\"fa-solid results-group-chevron\"\n [class.fa-chevron-down]=\"!IsGroupExpanded(group.SourceType)\"\n [class.fa-chevron-up]=\"IsGroupExpanded(group.SourceType)\"></i>\n </div>\n\n @for (result of GetVisibleResults(group); track result.ID) {\n <ng-container *ngTemplateOutlet=\"resultCardTpl; context: { $implicit: result }\"></ng-container>\n }\n\n @if (HasMoreResults(group)) {\n <button class=\"results-show-more\" (click)=\"ToggleGroup(group.SourceType)\">\n <i class=\"fa-solid fa-chevron-down\"></i>\n Show {{ GetHiddenCount(group) }} more results\n </button>\n }\n </div>\n }\n }\n}\n\n<!-- ═══ Shared result card template ═══ -->\n<ng-template #resultCardTpl let-result>\n <div class=\"result-card\" [class.result-card-expanded]=\"IsExpanded(result.ID)\">\n <!-- Main row -->\n <div class=\"result-card-main\" (click)=\"ToggleExpand(result)\">\n <div class=\"result-card-icon-wrap\">\n <i [class]=\"result.EntityIcon || result.SourceIcon || 'fa-solid fa-database'\"></i>\n </div>\n <div class=\"result-card-body\">\n <div class=\"result-card-title\" [innerHTML]=\"HighlightMatch(result.RecordName || result.Title)\"></div>\n <div class=\"result-card-entity\" [innerHTML]=\"HighlightMatch(result.EntityName)\"></div>\n @if (result.Tags && result.Tags.length > 0) {\n <div class=\"result-card-tags\">\n @for (tag of result.Tags.slice(0, 5); track tag) {\n <span class=\"result-tag-chip\" [innerHTML]=\"HighlightMatch(tag)\"></span>\n }\n @if (result.Tags.length > 5) {\n <span class=\"result-tag-more\">+{{ result.Tags.length - 5 }}</span>\n }\n </div>\n }\n </div>\n @if (ShowScores) {\n <div class=\"result-card-score\">\n <div class=\"score-circle\" [class.score-high]=\"result.Score >= 0.40\" [class.score-medium]=\"result.Score >= 0.15 && result.Score < 0.40\" [class.score-low]=\"result.Score < 0.15\">\n <span class=\"score-value\">{{ FormatScore(result.Score) }}</span>\n </div>\n </div>\n }\n <div class=\"result-card-actions\">\n <button class=\"result-open-btn\" (click)=\"OnOpenRecord(result, $event)\"\n [title]=\"result.ResultType === 'storage-file' ? 'Open file' : 'Open record'\"\n [attr.aria-label]=\"result.ResultType === 'storage-file' ? 'Open file' : 'Open record'\">\n <i [class]=\"result.ResultType === 'storage-file' ? 'fa-solid fa-external-link' : 'fa-solid fa-arrow-up-right-from-square'\"></i>\n </button>\n <button class=\"result-expand-btn\" (click)=\"ToggleExpand(result, $event)\" title=\"Expand details\" aria-label=\"Expand details\">\n <i class=\"fa-solid\" [class.fa-chevron-down]=\"!IsExpanded(result.ID)\"\n [class.fa-chevron-up]=\"IsExpanded(result.ID)\"></i>\n </button>\n </div>\n </div>\n\n <!-- Expanded detail -->\n @if (IsExpanded(result.ID)) {\n <div class=\"result-card-detail\">\n @if (result.Snippet) {\n <div class=\"detail-section\">\n <span class=\"detail-label\">Preview</span>\n <p class=\"detail-snippet\" [innerHTML]=\"HighlightMatch(result.Snippet)\"></p>\n </div>\n }\n <div class=\"detail-meta\">\n <div class=\"detail-meta-item\">\n <i class=\"fa-solid fa-layer-group\"></i>\n <span>{{ result.EntityName }}</span>\n </div>\n <div class=\"detail-meta-item\">\n <i class=\"fa-solid fa-fingerprint\"></i>\n <span class=\"detail-record-id\">{{ result.RecordID }}</span>\n </div>\n <!-- SR-1: Show overall score consistently -->\n <div class=\"detail-meta-item\">\n <i class=\"fa-solid fa-signal\"></i>\n <span>Relevance: {{ FormatScore(result.Score) }}</span>\n </div>\n @if (result.ScoreBreakdown) {\n <div class=\"detail-meta-item\">\n <i class=\"fa-solid fa-chart-bar\"></i>\n <span>\n @if (result.ScoreBreakdown.Vector) { Vector: {{ FormatScore(result.ScoreBreakdown.Vector) }} }\n @if (result.ScoreBreakdown.FullText) { FTS: {{ FormatScore(result.ScoreBreakdown.FullText) }} }\n </span>\n </div>\n }\n </div>\n @if (ShowTags && result.Tags.length > 0) {\n <div class=\"detail-tags\">\n @for (tag of result.Tags; track tag) {\n <span class=\"result-card-tag\" [innerHTML]=\"HighlightMatch(tag)\"></span>\n }\n </div>\n }\n @if (GetResolvedLinks(result).length > 0) {\n <div class=\"detail-links\">\n @for (link of GetResolvedLinks(result); track link.Value) {\n @if (link.Type === 'URL') {\n <a [href]=\"link.Value\" target=\"_blank\" class=\"detail-link\" (click)=\"$event.stopPropagation()\">\n <i class=\"fa-solid fa-arrow-up-right-from-square\"></i>\n {{ TruncateUrl(link.Value) }}\n </a>\n } @else if (link.Type === 'Email') {\n <a [href]=\"'mailto:' + link.Value\" class=\"detail-link detail-link-email\" (click)=\"$event.stopPropagation()\">\n <i class=\"fa-solid fa-envelope\"></i>\n {{ link.Value }}\n </a>\n }\n }\n </div>\n }\n <div class=\"detail-actions\">\n <button class=\"detail-open-btn\" (click)=\"OnOpenRecord(result, $event)\">\n @if (result.ResultType === 'storage-file') {\n <i class=\"fa-solid fa-external-link\"></i> Open File\n } @else {\n <i class=\"fa-solid fa-arrow-up-right-from-square\"></i> Open Record\n }\n </button>\n <button class=\"detail-similar-btn\" (click)=\"OnMoreLikeThis(result, $event)\">\n <i class=\"fa-solid fa-shuffle\"></i> See Similar Items\n </button>\n </div>\n </div>\n }\n </div>\n</ng-template>\n"]}
|
|
1
|
+
{"version":3,"file":"search-results.component.js","sourceRoot":"","sources":["../../src/lib/search-results.component.ts","../../src/lib/search-results.component.html"],"names":[],"mappings":"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","sourcesContent":["/**\n * @fileoverview Search Results Component\n *\n * Displays search results grouped by source type with expandable sections.\n * Used both within the search overlay and as a standalone results view\n * in the Knowledge Hub search dashboard.\n */\n\nimport {\n Component,\n EventEmitter,\n Input,\n Output,\n ChangeDetectorRef,\n inject\n} from '@angular/core';\nimport { SearchResultItem, SearchResultGroup, SearchResultSelectedEvent } from './search-types';\nimport { EscapeHTML, HighlightSearchMatches } from '@memberjunction/global';\nimport { BaseAngularComponent } from '@memberjunction/ng-base-types';\n\n@Component({\n standalone: false,\n selector: 'mj-search-results',\n templateUrl: './search-results.component.html',\n styleUrls: ['./search-results.component.css']\n})\nexport class SearchResultsComponent extends BaseAngularComponent {\n private cdr = inject(ChangeDetectorRef);\n\n /** Grouped search results to display (used when DisplayMode is 'grouped') */\n @Input() ResultGroups: SearchResultGroup[] = [];\n\n /** Flat list of all results sorted by score (used when DisplayMode is 'flat') */\n @Input() FlatResults: SearchResultItem[] = [];\n\n /** Display mode: 'flat' for blended list sorted by score, 'grouped' for source-type groups */\n @Input() DisplayMode: 'flat' | 'grouped' = 'flat';\n\n /** Whether results are currently loading */\n @Input() IsLoading = false;\n\n /** Total result count */\n @Input() TotalCount = 0;\n\n /** Elapsed search time in ms */\n @Input() ElapsedMs = 0;\n\n /** Maximum results per group before \"Show more\" (grouped mode only) */\n @Input() MaxPerGroup = 5;\n\n /** Results per page in flat mode */\n @Input() PageSize = 10;\n\n /** Whether to show the results summary line (count + time). Set false when parent provides its own header. */\n @Input() ShowSummary = true;\n\n /** Whether to show score badges */\n @Input() ShowScores = true;\n\n /** Whether to show tags */\n @Input() ShowTags = true;\n\n /** Whether to show source type icons */\n @Input() ShowSourceIcons = true;\n\n /** Text to highlight within result cards (e.g. from a client-side filter) */\n @Input() HighlightText = '';\n\n @Output() ResultSelected = new EventEmitter<SearchResultSelectedEvent>();\n\n /** Emitted when user clicks \"Open Record\" — parent handles navigation */\n @Output() OpenRecordRequested = new EventEmitter<SearchResultItem>();\n\n /** Emitted when user clicks \"See Similar Items\" — parent runs a \"more like this\" search */\n @Output() MoreLikeThisRequested = new EventEmitter<SearchResultItem>();\n\n /** Current page (1-based) for flat mode pagination */\n public CurrentPage = 1;\n\n /** Tracks which groups are expanded */\n public ExpandedGroups = new Set<string>();\n\n /** Set of expanded result card IDs — multiple can be open simultaneously */\n public ExpandedResultIDs = new Set<string>();\n\n // ── Flat mode pagination ──\n\n /** Get results for the current page in flat mode */\n public get PagedResults(): SearchResultItem[] {\n const start = (this.CurrentPage - 1) * this.PageSize;\n return this.FlatResults.slice(start, start + this.PageSize);\n }\n\n /** Total number of pages */\n public get TotalPages(): number {\n return Math.ceil(this.FlatResults.length / this.PageSize);\n }\n\n /** Page numbers to display in the pager */\n public get PageNumbers(): number[] {\n const total = this.TotalPages;\n if (total <= 7) return Array.from({ length: total }, (_, i) => i + 1);\n\n const pages: number[] = [1];\n const start = Math.max(2, this.CurrentPage - 2);\n const end = Math.min(total - 1, this.CurrentPage + 2);\n if (start > 2) pages.push(-1); // ellipsis marker\n for (let i = start; i <= end; i++) pages.push(i);\n if (end < total - 1) pages.push(-1); // ellipsis marker\n pages.push(total);\n return pages;\n }\n\n /** Navigate to a specific page */\n public GoToPage(page: number): void {\n if (page < 1 || page > this.TotalPages) return;\n this.CurrentPage = page;\n this.cdr.detectChanges();\n }\n\n // ── Grouped mode ──\n\n /** Toggle group expansion */\n public ToggleGroup(sourceType: string): void {\n if (this.ExpandedGroups.has(sourceType)) {\n this.ExpandedGroups.delete(sourceType);\n } else {\n this.ExpandedGroups.add(sourceType);\n }\n this.cdr.detectChanges();\n }\n\n /** Whether a group is expanded */\n public IsGroupExpanded(sourceType: string): boolean {\n return this.ExpandedGroups.has(sourceType);\n }\n\n /** Get visible results for a group (respecting max per group unless expanded) */\n public GetVisibleResults(group: SearchResultGroup): SearchResultItem[] {\n if (this.IsGroupExpanded(group.SourceType)) {\n return group.Results;\n }\n return group.Results.slice(0, this.MaxPerGroup);\n }\n\n /** Whether a group has more results than shown */\n public HasMoreResults(group: SearchResultGroup): boolean {\n return group.Results.length > this.MaxPerGroup && !this.IsGroupExpanded(group.SourceType);\n }\n\n /** Get the count of hidden results */\n public GetHiddenCount(group: SearchResultGroup): number {\n return group.Results.length - this.MaxPerGroup;\n }\n\n /** Handle clicking a result */\n public OnResultClick(result: SearchResultItem, event: MouseEvent): void {\n this.ResultSelected.emit({\n Result: result,\n OpenInNewTab: event.metaKey || event.ctrlKey\n });\n }\n\n /** Toggle expand/collapse of a result card — multiple can be open */\n public ToggleExpand(result: SearchResultItem, event?: MouseEvent): void {\n if (event) event.stopPropagation();\n if (this.ExpandedResultIDs.has(result.ID)) {\n this.ExpandedResultIDs.delete(result.ID);\n } else {\n this.ExpandedResultIDs.add(result.ID);\n }\n this.cdr.detectChanges();\n }\n\n /** Check if a result card is expanded */\n public IsExpanded(resultID: string): boolean {\n return this.ExpandedResultIDs.has(resultID);\n }\n\n /** Handle \"Open Record\" button click */\n public OnOpenRecord(result: SearchResultItem, event: MouseEvent): void {\n event.stopPropagation();\n this.OpenRecordRequested.emit(result);\n }\n\n /** Handle \"See Similar Items\" button click — emits the result for a \"more like this\" search */\n public OnMoreLikeThis(result: SearchResultItem, event: MouseEvent): void {\n event.stopPropagation();\n this.MoreLikeThisRequested.emit(result);\n }\n\n /**\n * Wrap substrings matching HighlightText in <mark> tags for visual emphasis.\n * The input is regex-escaped so user text is treated as a literal string.\n */\n public HighlightMatch(text: string): string {\n return HighlightSearchMatches(text, this.HighlightText, 'search-highlight');\n }\n\n /** Format a score as a percentage */\n public FormatScore(score: number): string {\n return `${Math.round(score * 100)}%`;\n }\n\n /** Format elapsed time */\n public FormatTime(ms: number): string {\n if (ms < 1000) return `${ms}ms`;\n return `${(ms / 1000).toFixed(1)}s`;\n }\n\n /** Resolved link cache per result ID */\n private resolvedLinksCache = new Map<string, { Value: string; Label: string; Type: 'URL' | 'Email' }[]>();\n\n /**\n * Resolve URL and Email fields from the result's RawMetadata using entity ExtendedType metadata.\n * Returns clickable link entries with proper type classification.\n */\n public GetResolvedLinks(result: SearchResultItem): { Value: string; Label: string; Type: 'URL' | 'Email' }[] {\n if (this.resolvedLinksCache.has(result.ID)) {\n return this.resolvedLinksCache.get(result.ID)!;\n }\n\n const links: { Value: string; Label: string; Type: 'URL' | 'Email' }[] = [];\n\n if (result.RawMetadata) {\n try {\n const meta = JSON.parse(result.RawMetadata) as Record<string, unknown>;\n const linkFields = this.GetLinkFields(result.EntityName);\n\n for (const field of linkFields) {\n const value = meta[field.Name];\n if (value && typeof value === 'string' && value.trim().length > 0) {\n links.push({\n Value: value,\n Label: field.DisplayName,\n Type: field.Type\n });\n }\n }\n } catch { /* ignore parse errors */ }\n }\n\n // Fallback: extract URLs from snippet if no ExtendedType fields found\n if (links.length === 0) {\n const urls = this.ExtractUrls(result.Snippet);\n for (const url of urls) {\n links.push({ Value: url, Label: '', Type: 'URL' });\n }\n }\n\n this.resolvedLinksCache.set(result.ID, links);\n return links;\n }\n\n /** Extract URLs from snippet text */\n public ExtractUrls(snippet: string): string[] {\n if (!snippet) return [];\n const urlRegex = /https?:\\/\\/[^\\s,;)>\"']+/g;\n const matches = snippet.match(urlRegex);\n return matches ? [...new Set(matches)] : [];\n }\n\n /** Truncate a URL for display */\n public TruncateUrl(url: string): string {\n try {\n const parsed = new URL(url);\n const path = parsed.pathname.length > 30\n ? parsed.pathname.substring(0, 30) + '...'\n : parsed.pathname;\n return parsed.hostname + path;\n } catch {\n return url.length > 50 ? url.substring(0, 50) + '...' : url;\n }\n }\n\n /** Cache of URL/Email field info per entity name */\n private linkFieldsCache = new Map<string, { Name: string; DisplayName: string; Type: 'URL' | 'Email' }[]>();\n\n /**\n * Get URL and Email fields for an entity using ExtendedType from entity metadata.\n * Returns fields with ExtendedType='URL' or ExtendedType='Email'.\n */\n public GetLinkFields(entityName: string): { Name: string; DisplayName: string; Type: 'URL' | 'Email' }[] {\n if (this.linkFieldsCache.has(entityName)) {\n return this.linkFieldsCache.get(entityName)!;\n }\n\n try {\n const md = this.ProviderToUse;\n const entityInfo = md.Entities.find(e => e.Name === entityName);\n if (!entityInfo) {\n this.linkFieldsCache.set(entityName, []);\n return [];\n }\n\n const linkFields = entityInfo.Fields\n .filter(f => f.ExtendedType === 'URL' || f.ExtendedType === 'Email')\n .map(f => ({\n Name: f.Name,\n DisplayName: f.DisplayNameOrName,\n Type: f.ExtendedType as 'URL' | 'Email'\n }));\n\n this.linkFieldsCache.set(entityName, linkFields);\n return linkFields;\n } catch {\n this.linkFieldsCache.set(entityName, []);\n return [];\n }\n }\n}\n","@if (IsLoading) {\n <div class=\"results-loading\">\n <mj-loading text=\"Searching...\" size=\"medium\"></mj-loading>\n </div>\n} @else if ((DisplayMode === 'flat' ? FlatResults.length : ResultGroups.length) === 0 && TotalCount === 0) {\n <mj-empty-state\n Variant=\"no-results\"\n Title=\"No results to display\" />\n} @else {\n <!-- Results summary -->\n @if (ShowSummary) {\n <div class=\"results-summary\">\n <span>{{ TotalCount }} results</span>\n @if (ElapsedMs > 0) {\n <span class=\"results-summary-time\"> in {{ FormatTime(ElapsedMs) }}</span>\n }\n </div>\n }\n\n @if (DisplayMode === 'flat') {\n <!-- ══════ FLAT BLENDED LIST (sorted by score) ══════ -->\n <div class=\"results-scroll-area\">\n @for (result of PagedResults; track result.ID) {\n <ng-container *ngTemplateOutlet=\"resultCardTpl; context: { $implicit: result }\"></ng-container>\n }\n </div>\n\n <!-- Google-style pagination (sticky at bottom) -->\n @if (TotalPages > 1) {\n <div class=\"results-pager\">\n <button class=\"pager-btn pager-prev\" [disabled]=\"CurrentPage === 1\"\n (click)=\"GoToPage(CurrentPage - 1)\" aria-label=\"Previous page\">\n <i class=\"fa-solid fa-chevron-left\"></i>\n </button>\n @for (p of PageNumbers; track $index) {\n @if (p === -1) {\n <span class=\"pager-ellipsis\">...</span>\n } @else {\n <button class=\"pager-btn pager-num\" [class.pager-active]=\"p === CurrentPage\"\n (click)=\"GoToPage(p)\">{{ p }}</button>\n }\n }\n <button class=\"pager-btn pager-next\" [disabled]=\"CurrentPage === TotalPages\"\n (click)=\"GoToPage(CurrentPage + 1)\" aria-label=\"Next page\">\n <i class=\"fa-solid fa-chevron-right\"></i>\n </button>\n </div>\n }\n } @else {\n <!-- ══════ GROUPED BY SOURCE TYPE ══════ -->\n @for (group of ResultGroups; track group.SourceType) {\n <div class=\"results-group\">\n <div class=\"results-group-header\" (click)=\"ToggleGroup(group.SourceType)\">\n @if (ShowSourceIcons) {\n <i [class]=\"group.Icon + ' results-group-icon'\"></i>\n }\n <span class=\"results-group-label\">{{ group.Label }}</span>\n <span class=\"results-group-count\">({{ group.TotalCount }})</span>\n <i class=\"fa-solid results-group-chevron\"\n [class.fa-chevron-down]=\"!IsGroupExpanded(group.SourceType)\"\n [class.fa-chevron-up]=\"IsGroupExpanded(group.SourceType)\"></i>\n </div>\n\n @for (result of GetVisibleResults(group); track result.ID) {\n <ng-container *ngTemplateOutlet=\"resultCardTpl; context: { $implicit: result }\"></ng-container>\n }\n\n @if (HasMoreResults(group)) {\n <button class=\"results-show-more\" (click)=\"ToggleGroup(group.SourceType)\">\n <i class=\"fa-solid fa-chevron-down\"></i>\n Show {{ GetHiddenCount(group) }} more results\n </button>\n }\n </div>\n }\n }\n}\n\n<!-- ═══ Shared result card template ═══ -->\n<ng-template #resultCardTpl let-result>\n <div class=\"result-card\" [class.result-card-expanded]=\"IsExpanded(result.ID)\">\n <!-- Main row -->\n <div class=\"result-card-main\" (click)=\"ToggleExpand(result)\">\n <div class=\"result-card-icon-wrap\">\n <i [class]=\"result.EntityIcon || result.SourceIcon || 'fa-solid fa-database'\"></i>\n </div>\n <div class=\"result-card-body\">\n <div class=\"result-card-title\" [innerHTML]=\"HighlightMatch(result.RecordName || result.Title)\"></div>\n <div class=\"result-card-entity\" [innerHTML]=\"HighlightMatch(result.EntityDisplayName || result.EntityName)\"></div>\n @if (result.Tags && result.Tags.length > 0) {\n <div class=\"result-card-tags\">\n @for (tag of result.Tags.slice(0, 5); track tag) {\n <span class=\"result-tag-chip\" [innerHTML]=\"HighlightMatch(tag)\"></span>\n }\n @if (result.Tags.length > 5) {\n <span class=\"result-tag-more\">+{{ result.Tags.length - 5 }}</span>\n }\n </div>\n }\n </div>\n @if (ShowScores) {\n <div class=\"result-card-score\">\n <div class=\"score-circle\" [class.score-high]=\"result.Score >= 0.40\" [class.score-medium]=\"result.Score >= 0.15 && result.Score < 0.40\" [class.score-low]=\"result.Score < 0.15\">\n <span class=\"score-value\">{{ FormatScore(result.Score) }}</span>\n </div>\n </div>\n }\n <div class=\"result-card-actions\">\n <button class=\"result-open-btn\" (click)=\"OnOpenRecord(result, $event)\"\n [title]=\"result.ResultType === 'storage-file' ? 'Open file' : 'Open record'\"\n [attr.aria-label]=\"result.ResultType === 'storage-file' ? 'Open file' : 'Open record'\">\n <i [class]=\"result.ResultType === 'storage-file' ? 'fa-solid fa-external-link' : 'fa-solid fa-arrow-up-right-from-square'\"></i>\n </button>\n <button class=\"result-expand-btn\" (click)=\"ToggleExpand(result, $event)\" title=\"Expand details\" aria-label=\"Expand details\">\n <i class=\"fa-solid\" [class.fa-chevron-down]=\"!IsExpanded(result.ID)\"\n [class.fa-chevron-up]=\"IsExpanded(result.ID)\"></i>\n </button>\n </div>\n </div>\n\n <!-- Expanded detail -->\n @if (IsExpanded(result.ID)) {\n <div class=\"result-card-detail\">\n @if (result.Snippet) {\n <div class=\"detail-section\">\n <span class=\"detail-label\">Preview</span>\n <p class=\"detail-snippet\" [innerHTML]=\"HighlightMatch(result.Snippet)\"></p>\n </div>\n }\n <div class=\"detail-meta\">\n <div class=\"detail-meta-item\">\n <i class=\"fa-solid fa-layer-group\"></i>\n <span>{{ result.EntityDisplayName || result.EntityName }}</span>\n </div>\n <div class=\"detail-meta-item\">\n <i class=\"fa-solid fa-fingerprint\"></i>\n <span class=\"detail-record-id\">{{ result.RecordID }}</span>\n </div>\n <!-- SR-1: Show overall score consistently -->\n <div class=\"detail-meta-item\">\n <i class=\"fa-solid fa-signal\"></i>\n <span>Relevance: {{ FormatScore(result.Score) }}</span>\n </div>\n @if (result.ScoreBreakdown) {\n <div class=\"detail-meta-item\">\n <i class=\"fa-solid fa-chart-bar\"></i>\n <span>\n @if (result.ScoreBreakdown.Vector) { Vector: {{ FormatScore(result.ScoreBreakdown.Vector) }} }\n @if (result.ScoreBreakdown.FullText) { FTS: {{ FormatScore(result.ScoreBreakdown.FullText) }} }\n </span>\n </div>\n }\n </div>\n @if (ShowTags && result.Tags.length > 0) {\n <div class=\"detail-tags\">\n @for (tag of result.Tags; track tag) {\n <span class=\"result-card-tag\" [innerHTML]=\"HighlightMatch(tag)\"></span>\n }\n </div>\n }\n @if (GetResolvedLinks(result).length > 0) {\n <div class=\"detail-links\">\n @for (link of GetResolvedLinks(result); track link.Value) {\n @if (link.Type === 'URL') {\n <a [href]=\"link.Value\" target=\"_blank\" class=\"detail-link\" (click)=\"$event.stopPropagation()\">\n <i class=\"fa-solid fa-arrow-up-right-from-square\"></i>\n {{ TruncateUrl(link.Value) }}\n </a>\n } @else if (link.Type === 'Email') {\n <a [href]=\"'mailto:' + link.Value\" class=\"detail-link detail-link-email\" (click)=\"$event.stopPropagation()\">\n <i class=\"fa-solid fa-envelope\"></i>\n {{ link.Value }}\n </a>\n }\n }\n </div>\n }\n <div class=\"detail-actions\">\n <button class=\"detail-open-btn\" (click)=\"OnOpenRecord(result, $event)\">\n @if (result.ResultType === 'storage-file') {\n <i class=\"fa-solid fa-external-link\"></i> Open File\n } @else {\n <i class=\"fa-solid fa-arrow-up-right-from-square\"></i> Open Record\n }\n </button>\n <button class=\"detail-similar-btn\" (click)=\"OnMoreLikeThis(result, $event)\">\n <i class=\"fa-solid fa-shuffle\"></i> See Similar Items\n </button>\n </div>\n </div>\n }\n </div>\n</ng-template>\n"]}
|
|
@@ -83,14 +83,22 @@ export declare class SearchSuggestComponent implements OnInit {
|
|
|
83
83
|
handling knows when the highlight has walked off the end of the list. */
|
|
84
84
|
get NavigableItemCount(): number;
|
|
85
85
|
/** Whether to show recent searches section */
|
|
86
|
+
get ShowRecentSection(): boolean;
|
|
87
|
+
/** @deprecated Use {@link ShowRecentSection}. */
|
|
86
88
|
get showRecentSection(): boolean;
|
|
87
89
|
/** Whether to show preview results section */
|
|
90
|
+
get ShowPreviewSection(): boolean;
|
|
91
|
+
/** @deprecated Use {@link ShowPreviewSection}. */
|
|
88
92
|
get showPreviewSection(): boolean;
|
|
89
93
|
/** Whether to show the recently-opened-records section (idle state only) */
|
|
94
|
+
get ShowRecentRecordsSection(): boolean;
|
|
95
|
+
/** @deprecated Use {@link ShowRecentRecordsSection}. */
|
|
90
96
|
get showRecentRecordsSection(): boolean;
|
|
91
97
|
/** Visible recently opened records (capped at 3) */
|
|
92
98
|
get VisibleRecentRecords(): RecentRecordItem[];
|
|
93
99
|
/** Whether to show the "See all N results" footer */
|
|
100
|
+
get ShowSeeAllFooter(): boolean;
|
|
101
|
+
/** @deprecated Use {@link ShowSeeAllFooter}. */
|
|
94
102
|
get showSeeAllFooter(): boolean;
|
|
95
103
|
/** Visible recent searches (capped at 5) */
|
|
96
104
|
get VisibleRecentSearches(): RecentSearch[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"search-suggest.component.d.ts","sourceRoot":"","sources":["../../src/lib/search-suggest.component.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"search-suggest.component.d.ts","sourceRoot":"","sources":["../../src/lib/search-suggest.component.ts"],"names":[],"mappings":"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"}
|
|
@@ -78,7 +78,7 @@ function SearchSuggestComponent_Conditional_0_Conditional_3_For_6_Template(rf, c
|
|
|
78
78
|
i0.ɵɵadvance(3);
|
|
79
79
|
i0.ɵɵtextInterpolate(record_r7.RecordName);
|
|
80
80
|
i0.ɵɵadvance(2);
|
|
81
|
-
i0.ɵɵtextInterpolate(record_r7.EntityName);
|
|
81
|
+
i0.ɵɵtextInterpolate(record_r7.EntityDisplayName || record_r7.EntityName);
|
|
82
82
|
} }
|
|
83
83
|
function SearchSuggestComponent_Conditional_0_Conditional_3_Template(rf, ctx) { if (rf & 1) {
|
|
84
84
|
i0.ɵɵelementStart(0, "div", 2)(1, "div", 7);
|
|
@@ -119,7 +119,7 @@ function SearchSuggestComponent_Conditional_0_Conditional_4_Conditional_0_For_2_
|
|
|
119
119
|
i0.ɵɵadvance(3);
|
|
120
120
|
i0.ɵɵtextInterpolate(result_r10.RecordName || result_r10.Title);
|
|
121
121
|
i0.ɵɵadvance(2);
|
|
122
|
-
i0.ɵɵtextInterpolate(result_r10.EntityName);
|
|
122
|
+
i0.ɵɵtextInterpolate(result_r10.EntityDisplayName || result_r10.EntityName);
|
|
123
123
|
i0.ɵɵadvance();
|
|
124
124
|
i0.ɵɵclassMap("suggest-result-score " + ctx_r1.GetScoreClass(result_r10.Score));
|
|
125
125
|
i0.ɵɵadvance();
|
|
@@ -380,7 +380,7 @@ export class SearchSuggestComponent {
|
|
|
380
380
|
SelectHighlighted() {
|
|
381
381
|
if (this.HighlightedIndex < 0)
|
|
382
382
|
return;
|
|
383
|
-
if (this.
|
|
383
|
+
if (this.ShowRecentSection) {
|
|
384
384
|
// Navigating recent searches
|
|
385
385
|
const visibleRecent = this.VisibleRecentSearches;
|
|
386
386
|
if (this.HighlightedIndex < visibleRecent.length) {
|
|
@@ -388,9 +388,9 @@ export class SearchSuggestComponent {
|
|
|
388
388
|
return;
|
|
389
389
|
}
|
|
390
390
|
}
|
|
391
|
-
if (this.
|
|
391
|
+
if (this.ShowRecentRecordsSection) {
|
|
392
392
|
// Navigating recently opened records (indexes continue after recents)
|
|
393
|
-
const offset = this.
|
|
393
|
+
const offset = this.ShowRecentSection ? this.VisibleRecentSearches.length : 0;
|
|
394
394
|
const recordIndex = this.HighlightedIndex - offset;
|
|
395
395
|
const visibleRecords = this.VisibleRecentRecords;
|
|
396
396
|
if (recordIndex >= 0 && recordIndex < visibleRecords.length) {
|
|
@@ -398,9 +398,9 @@ export class SearchSuggestComponent {
|
|
|
398
398
|
return;
|
|
399
399
|
}
|
|
400
400
|
}
|
|
401
|
-
if (this.
|
|
401
|
+
if (this.ShowPreviewSection) {
|
|
402
402
|
// Navigating preview results
|
|
403
|
-
const offset = this.
|
|
403
|
+
const offset = this.ShowRecentSection ? this.VisibleRecentSearches.length : 0;
|
|
404
404
|
const resultIndex = this.HighlightedIndex - offset;
|
|
405
405
|
const visibleResults = this.VisiblePreviewResults;
|
|
406
406
|
if (resultIndex >= 0 && resultIndex < visibleResults.length) {
|
|
@@ -408,7 +408,7 @@ export class SearchSuggestComponent {
|
|
|
408
408
|
return;
|
|
409
409
|
}
|
|
410
410
|
// "See all" footer
|
|
411
|
-
if (resultIndex === visibleResults.length && this.
|
|
411
|
+
if (resultIndex === visibleResults.length && this.ShowSeeAllFooter) {
|
|
412
412
|
this.OnSeeAllClick();
|
|
413
413
|
}
|
|
414
414
|
}
|
|
@@ -425,24 +425,40 @@ export class SearchSuggestComponent {
|
|
|
425
425
|
}
|
|
426
426
|
// --- Template getters ---
|
|
427
427
|
/** Whether to show recent searches section */
|
|
428
|
-
get
|
|
428
|
+
get ShowRecentSection() {
|
|
429
429
|
return this.ShowRecent && this.Query.length < this.MinQueryLength && this.RecentSearches.length > 0;
|
|
430
430
|
}
|
|
431
|
+
/** @deprecated Use {@link ShowRecentSection}. */
|
|
432
|
+
get showRecentSection() {
|
|
433
|
+
return this.ShowRecentSection;
|
|
434
|
+
}
|
|
431
435
|
/** Whether to show preview results section */
|
|
432
|
-
get
|
|
436
|
+
get ShowPreviewSection() {
|
|
433
437
|
return this.Query.length >= this.MinQueryLength;
|
|
434
438
|
}
|
|
439
|
+
/** @deprecated Use {@link ShowPreviewSection}. */
|
|
440
|
+
get showPreviewSection() {
|
|
441
|
+
return this.ShowPreviewSection;
|
|
442
|
+
}
|
|
435
443
|
/** Whether to show the recently-opened-records section (idle state only) */
|
|
436
|
-
get
|
|
444
|
+
get ShowRecentRecordsSection() {
|
|
437
445
|
return this.Query.length < this.MinQueryLength && this.RecentRecords.length > 0;
|
|
438
446
|
}
|
|
447
|
+
/** @deprecated Use {@link ShowRecentRecordsSection}. */
|
|
448
|
+
get showRecentRecordsSection() {
|
|
449
|
+
return this.ShowRecentRecordsSection;
|
|
450
|
+
}
|
|
439
451
|
/** Visible recently opened records (capped at 3) */
|
|
440
452
|
get VisibleRecentRecords() {
|
|
441
453
|
return this.RecentRecords.slice(0, 3);
|
|
442
454
|
}
|
|
443
455
|
/** Whether to show the "See all N results" footer */
|
|
456
|
+
get ShowSeeAllFooter() {
|
|
457
|
+
return this.ShowPreviewSection && this.TotalCount > 0;
|
|
458
|
+
}
|
|
459
|
+
/** @deprecated Use {@link ShowSeeAllFooter}. */
|
|
444
460
|
get showSeeAllFooter() {
|
|
445
|
-
return this.
|
|
461
|
+
return this.ShowSeeAllFooter;
|
|
446
462
|
}
|
|
447
463
|
/** Visible recent searches (capped at 5) */
|
|
448
464
|
get VisibleRecentSearches() {
|
|
@@ -531,15 +547,15 @@ export class SearchSuggestComponent {
|
|
|
531
547
|
// --- Private ---
|
|
532
548
|
getNavigableItemCount() {
|
|
533
549
|
let count = 0;
|
|
534
|
-
if (this.
|
|
550
|
+
if (this.ShowRecentSection) {
|
|
535
551
|
count += this.VisibleRecentSearches.length;
|
|
536
552
|
}
|
|
537
|
-
if (this.
|
|
553
|
+
if (this.ShowRecentRecordsSection) {
|
|
538
554
|
count += this.VisibleRecentRecords.length;
|
|
539
555
|
}
|
|
540
|
-
if (this.
|
|
556
|
+
if (this.ShowPreviewSection) {
|
|
541
557
|
count += this.VisiblePreviewResults.length;
|
|
542
|
-
if (this.
|
|
558
|
+
if (this.ShowSeeAllFooter) {
|
|
543
559
|
count += 1; // "See all" item
|
|
544
560
|
}
|
|
545
561
|
}
|
|
@@ -586,7 +602,7 @@ export class SearchSuggestComponent {
|
|
|
586
602
|
}
|
|
587
603
|
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(SearchSuggestComponent, [{
|
|
588
604
|
type: Component,
|
|
589
|
-
args: [{ standalone: false, selector: 'mj-search-suggest', template: "@if (IsOpen) {\n <div class=\"suggest-dropdown\">\n @if (IsLoading && showPreviewSection) {\n <div class=\"suggest-loading\">\n <i class=\"fa-solid fa-spinner fa-spin suggest-loading-icon\"></i>\n <span>Searching...</span>\n </div>\n }\n\n <!-- Recent searches section -->\n @if (showRecentSection) {\n <div class=\"suggest-section\">\n <div class=\"suggest-section-header\">\n <i class=\"fa-solid fa-clock suggest-section-icon\"></i>\n <span>Recent Searches</span>\n <button type=\"button\" class=\"suggest-clear-link\" aria-label=\"Clear recent searches\"\n (mousedown)=\"$event.preventDefault()\"\n (click)=\"OnClearRecentClick($event)\">Clear</button>\n </div>\n @for (recent of VisibleRecentSearches; track recent.Query; let i = $index) {\n <div\n class=\"suggest-item suggest-recent-item\"\n [class.suggest-item-highlighted]=\"IsHighlighted(i)\"\n (mousedown)=\"OnRecentClick(recent); $event.preventDefault()\"\n (mouseenter)=\"HighlightedIndex = i\"\n >\n <i class=\"fa-solid fa-clock suggest-item-icon\"></i>\n <span class=\"suggest-item-text\">{{ recent.Query }}</span>\n <span class=\"suggest-item-meta\">{{ recent.ResultCount }} results</span>\n </div>\n }\n </div>\n }\n\n <!-- Recently opened records section (idle state) -->\n @if (showRecentRecordsSection) {\n <div class=\"suggest-section\">\n <div class=\"suggest-section-header\">\n <i class=\"fa-solid fa-clock-rotate-left suggest-section-icon\"></i>\n <span>Recently Opened</span>\n </div>\n @for (record of VisibleRecentRecords; track record.EntityName + record.RecordID; let i = $index) {\n <div\n class=\"suggest-item suggest-recent-item\"\n [class.suggest-item-highlighted]=\"IsHighlighted((showRecentSection ? VisibleRecentSearches.length : 0) + i)\"\n (mousedown)=\"OnRecentRecordClick(record); $event.preventDefault()\"\n (mouseenter)=\"HighlightedIndex = (showRecentSection ? VisibleRecentSearches.length : 0) + i\"\n >\n <i class=\"fa-solid fa-file-lines suggest-item-icon\"></i>\n <span class=\"suggest-item-text\">{{ record.RecordName }}</span>\n <span class=\"suggest-item-meta\">{{ record.EntityName }}</span>\n </div>\n }\n </div>\n }\n\n <!-- Preview results section -->\n @if (showPreviewSection && !IsLoading) {\n @if (VisiblePreviewResults.length > 0) {\n <div class=\"suggest-section\">\n @for (result of VisiblePreviewResults; track result.ID; let i = $index) {\n <div\n class=\"suggest-item suggest-result-item\"\n [class.suggest-item-highlighted]=\"IsHighlighted((showRecentSection ? VisibleRecentSearches.length : 0) + i)\"\n (mousedown)=\"OnResultClick(result); $event.preventDefault()\"\n (mouseenter)=\"HighlightedIndex = (showRecentSection ? VisibleRecentSearches.length : 0) + i\"\n >\n <div class=\"suggest-result-icon\">\n <i [class]=\"result.EntityIcon || result.SourceIcon || 'fa-solid fa-database'\"></i>\n </div>\n <div class=\"suggest-result-body\">\n <div class=\"suggest-result-title\">{{ result.RecordName || result.Title }}</div>\n <div class=\"suggest-result-entity\">{{ result.EntityName }}</div>\n </div>\n <span class=\"suggest-result-score\" [class]=\"'suggest-result-score ' + GetScoreClass(result.Score)\">{{ FormatScore(result.Score) }}</span>\n </div>\n }\n </div>\n } @else if (!IsLoading) {\n <div class=\"suggest-empty\">\n <span>No results found</span>\n </div>\n }\n\n <!-- See all footer -->\n @if (showSeeAllFooter) {\n <div\n class=\"suggest-see-all\"\n [class.suggest-item-highlighted]=\"IsHighlighted((showRecentSection ? VisibleRecentSearches.length : 0) + VisiblePreviewResults.length)\"\n (mousedown)=\"OnSeeAllClick(); $event.preventDefault()\"\n (mouseenter)=\"HighlightedIndex = (showRecentSection ? VisibleRecentSearches.length : 0) + VisiblePreviewResults.length\"\n >\n <i class=\"fa-solid fa-arrow-right suggest-see-all-icon\"></i>\n <span>See all {{ TotalCount }} results</span>\n </div>\n }\n }\n\n <!-- Min relevance filter bar -->\n @if (showPreviewSection) {\n <div class=\"suggest-filter-bar\">\n <div class=\"suggest-filter-toggle\" (mousedown)=\"ToggleFilter($event)\">\n <i class=\"fa-solid fa-sliders suggest-filter-icon\"></i>\n <span class=\"suggest-filter-label\">\n @if (MinRelevancePercent > 0) {\n Min relevance: {{ MinRelevancePercent }}%\n } @else {\n Showing all results\n }\n </span>\n <i class=\"fa-solid fa-chevron-down suggest-filter-chevron\" [class.suggest-filter-chevron-open]=\"IsFilterOpen\"></i>\n </div>\n\n @if (IsFilterOpen) {\n <div class=\"suggest-filter-popover\">\n <div class=\"suggest-filter-popover-header\">Min Relevance</div>\n @for (option of MinRelevanceOptions; track option) {\n <div\n class=\"suggest-filter-option\"\n [class.suggest-filter-option-active]=\"IsSelectedRelevance(option)\"\n (mousedown)=\"SetMinRelevance(option, $event)\"\n >\n <span>{{ FormatRelevanceOption(option) }}</span>\n @if (IsSelectedRelevance(option)) {\n <i class=\"fa-solid fa-check suggest-filter-check\"></i>\n }\n </div>\n }\n </div>\n }\n </div>\n }\n\n <!-- Empty state when no recents of either kind and query too short -->\n @if (!showRecentSection && !showRecentRecordsSection && !showPreviewSection) {\n <div class=\"suggest-empty\">\n <span>Type at least {{ MinQueryLength }} characters to search</span>\n </div>\n }\n\n <!-- Host-supplied promo row (e.g. command palette nudge) -->\n @if (ShowPromo && PromoText) {\n <div class=\"suggest-promo\">\n <i class=\"fa-solid fa-wand-magic-sparkles suggest-promo-icon\"></i>\n <span class=\"suggest-promo-text\">{{ PromoText }}</span>\n <button type=\"button\" class=\"suggest-promo-action\"\n (mousedown)=\"$event.preventDefault()\"\n (click)=\"OnPromoAccept()\">{{ PromoActionLabel }}</button>\n <button type=\"button\" class=\"suggest-promo-dismiss\" aria-label=\"Dismiss\" title=\"Dismiss\"\n (mousedown)=\"$event.preventDefault()\"\n (click)=\"OnPromoDismiss(); $event.stopPropagation()\">\n <i class=\"fa-solid fa-xmark\"></i>\n </button>\n </div>\n }\n </div>\n}\n", styles: ["/* Search Suggest Dropdown */\n\n.suggest-dropdown {\n position: absolute;\n top: 100%;\n left: 0;\n right: 0;\n z-index: 99999;\n margin-top: 4px;\n border: 1px solid var(--mj-border-default);\n border-radius: 10px;\n background: var(--mj-bg-surface);\n box-shadow: 0 8px 30px rgba(0, 0, 0, 0.12), 0 2px 8px rgba(0, 0, 0, 0.06);\n overflow: visible;\n animation: suggestSlideIn 0.15s ease;\n}\n\n@keyframes suggestSlideIn {\n from {\n opacity: 0;\n transform: translateY(-4px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n\n/* Sections */\n.suggest-section {\n padding: 0.25rem 0;\n}\n\n.suggest-section + .suggest-section {\n border-top: 1px solid var(--mj-border-subtle);\n}\n\n.suggest-section-header {\n display: flex;\n align-items: center;\n gap: 0.4rem;\n padding: 0.5rem 0.75rem 0.3rem;\n font-size: 0.7rem;\n font-weight: 600;\n color: var(--mj-text-muted);\n text-transform: uppercase;\n letter-spacing: 0.04em;\n}\n\n.suggest-section-icon {\n font-size: 0.65rem;\n}\n\n.suggest-clear-link {\n margin-left: auto;\n padding: 0;\n border: none;\n background: transparent;\n font-family: inherit;\n font-size: 0.68rem;\n font-weight: var(--mj-font-medium);\n color: var(--mj-text-link, var(--mj-brand-primary));\n cursor: pointer;\n text-transform: none;\n letter-spacing: normal;\n}\n\n.suggest-clear-link:hover {\n text-decoration: underline;\n}\n\n.suggest-clear-link:focus-visible {\n outline: 2px solid var(--mj-border-focus);\n outline-offset: 2px;\n border-radius: var(--mj-radius-sm);\n}\n\n/* Items */\n.suggest-item {\n display: flex;\n align-items: center;\n gap: 0.6rem;\n padding: 0.45rem 0.75rem;\n cursor: pointer;\n transition: background 0.08s ease;\n}\n\n.suggest-item:hover,\n.suggest-item-highlighted {\n background: var(--mj-bg-surface-hover);\n}\n\n.suggest-item-icon {\n color: var(--mj-text-muted);\n font-size: 0.75rem;\n width: 1rem;\n text-align: center;\n flex-shrink: 0;\n}\n\n.suggest-item-text {\n flex: 1;\n font-size: 0.85rem;\n color: var(--mj-text-primary);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.suggest-item-meta {\n font-size: 0.72rem;\n color: var(--mj-text-muted);\n flex-shrink: 0;\n}\n\n/* Result items */\n.suggest-result-item {\n gap: 0.65rem;\n}\n\n.suggest-result-icon {\n width: 1.75rem;\n height: 1.75rem;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 6px;\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n font-size: 0.72rem;\n flex-shrink: 0;\n}\n\n.suggest-result-body {\n flex: 1;\n min-width: 0;\n}\n\n.suggest-result-title {\n font-size: 0.85rem;\n font-weight: 500;\n color: var(--mj-text-primary);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.suggest-result-entity {\n font-size: 0.7rem;\n color: var(--mj-text-muted);\n}\n\n/* Score badge \u2014 base styles */\n.suggest-result-score {\n font-size: 0.68rem;\n font-weight: 600;\n padding: 0.1rem 0.4rem;\n border-radius: 9999px;\n flex-shrink: 0;\n /* Default: neutral gray */\n color: var(--mj-text-muted);\n background: color-mix(in srgb, var(--mj-text-muted) 10%, var(--mj-bg-surface));\n}\n\n/* Score badge \u2014 high relevance (green, \u226540%) */\n.suggest-score-high {\n color: var(--mj-status-success-text, var(--mj-status-success));\n background: color-mix(in srgb, var(--mj-status-success) 12%, var(--mj-bg-surface));\n}\n\n/* Score badge \u2014 medium relevance (amber, 15\u201339%) */\n.suggest-score-medium {\n color: var(--mj-status-warning-text, var(--mj-status-warning));\n background: color-mix(in srgb, var(--mj-status-warning) 12%, var(--mj-bg-surface));\n}\n\n/* Score badge \u2014 low relevance (gray, <15%) */\n.suggest-score-low {\n color: var(--mj-text-muted);\n background: color-mix(in srgb, var(--mj-text-muted) 10%, var(--mj-bg-surface));\n}\n\n/* Loading */\n.suggest-loading {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 0.5rem;\n padding: 0.85rem 0.75rem;\n color: var(--mj-text-muted);\n font-size: 0.82rem;\n}\n\n.suggest-loading-icon {\n color: var(--mj-brand-primary);\n}\n\n/* Empty state */\n.suggest-empty {\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 1rem 0.75rem;\n color: var(--mj-text-muted);\n font-size: 0.82rem;\n}\n\n/* See all footer */\n.suggest-see-all {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n padding: 0.55rem 0.75rem;\n border-top: 1px solid var(--mj-border-subtle);\n cursor: pointer;\n color: var(--mj-brand-primary);\n font-size: 0.82rem;\n font-weight: 500;\n transition: background 0.08s ease;\n}\n\n.suggest-see-all:hover,\n.suggest-see-all.suggest-item-highlighted {\n background: var(--mj-bg-surface-hover);\n}\n\n.suggest-see-all-icon {\n font-size: 0.72rem;\n}\n\n/* --- Min Relevance Filter Bar --- */\n.suggest-filter-bar {\n position: relative;\n border-top: 1px solid var(--mj-border-subtle);\n padding: 0.35rem 0.75rem;\n}\n\n.suggest-filter-toggle {\n display: flex;\n align-items: center;\n gap: 0.4rem;\n cursor: pointer;\n font-size: 0.7rem;\n color: var(--mj-text-muted);\n transition: color 0.1s ease;\n user-select: none;\n}\n\n.suggest-filter-toggle:hover {\n color: var(--mj-text-secondary);\n}\n\n.suggest-filter-icon {\n font-size: 0.65rem;\n}\n\n.suggest-filter-label {\n flex: 1;\n}\n\n.suggest-filter-chevron {\n font-size: 0.55rem;\n transition: transform 0.15s ease;\n}\n\n.suggest-filter-chevron-open {\n transform: rotate(180deg);\n}\n\n/* Filter popover */\n.suggest-filter-popover {\n position: absolute;\n top: 100%;\n left: 0.5rem;\n right: 0.5rem;\n margin-top: 4px;\n background: var(--mj-bg-surface-elevated, var(--mj-bg-surface));\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);\n overflow: hidden;\n z-index: 10;\n animation: suggestSlideIn 0.12s ease;\n}\n\n.suggest-filter-popover-header {\n padding: 0.4rem 0.6rem 0.25rem;\n font-size: 0.65rem;\n font-weight: 600;\n color: var(--mj-text-muted);\n text-transform: uppercase;\n letter-spacing: 0.04em;\n}\n\n.suggest-filter-option {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 0.35rem 0.6rem;\n cursor: pointer;\n font-size: 0.78rem;\n color: var(--mj-text-primary);\n transition: background 0.08s ease;\n}\n\n.suggest-filter-option:hover {\n background: var(--mj-bg-surface-hover);\n}\n\n.suggest-filter-option-active {\n color: var(--mj-brand-primary);\n font-weight: 600;\n}\n\n.suggest-filter-check {\n font-size: 0.65rem;\n color: var(--mj-brand-primary);\n}\n\n/* Host-supplied promo row \u2014 a quiet whisper pinned to the dropdown's bottom\n edge, never a banner. */\n.suggest-promo {\n display: flex;\n align-items: center;\n gap: var(--mj-space-2);\n padding: var(--mj-space-2) var(--mj-space-3);\n border-top: 1px solid var(--mj-border-subtle);\n background: var(--mj-bg-surface-card);\n font-size: 0.78rem;\n color: var(--mj-text-muted);\n}\n\n.suggest-promo-icon {\n color: var(--mj-brand-primary);\n flex-shrink: 0;\n}\n\n.suggest-promo-text {\n flex: 1;\n min-width: 0;\n}\n\n.suggest-promo-action {\n flex-shrink: 0;\n padding: 0.2rem 0.6rem;\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md);\n background: transparent;\n color: var(--mj-brand-primary);\n font-size: 0.75rem;\n font-weight: var(--mj-font-medium);\n cursor: pointer;\n transition: var(--mj-transition-colors);\n}\n\n.suggest-promo-action:hover {\n background: color-mix(in srgb, var(--mj-brand-primary) 8%, var(--mj-bg-surface));\n border-color: var(--mj-brand-primary);\n}\n\n.suggest-promo-dismiss {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 22px;\n height: 22px;\n padding: 0;\n border: none;\n border-radius: var(--mj-radius-full);\n background: transparent;\n color: var(--mj-text-disabled);\n font-size: 0.75rem;\n cursor: pointer;\n transition: var(--mj-transition-colors);\n flex-shrink: 0;\n}\n\n.suggest-promo-dismiss:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-secondary);\n}\n"] }]
|
|
605
|
+
args: [{ standalone: false, selector: 'mj-search-suggest', template: "@if (IsOpen) {\n <div class=\"suggest-dropdown\">\n @if (IsLoading && showPreviewSection) {\n <div class=\"suggest-loading\">\n <i class=\"fa-solid fa-spinner fa-spin suggest-loading-icon\"></i>\n <span>Searching...</span>\n </div>\n }\n\n <!-- Recent searches section -->\n @if (showRecentSection) {\n <div class=\"suggest-section\">\n <div class=\"suggest-section-header\">\n <i class=\"fa-solid fa-clock suggest-section-icon\"></i>\n <span>Recent Searches</span>\n <button type=\"button\" class=\"suggest-clear-link\" aria-label=\"Clear recent searches\"\n (mousedown)=\"$event.preventDefault()\"\n (click)=\"OnClearRecentClick($event)\">Clear</button>\n </div>\n @for (recent of VisibleRecentSearches; track recent.Query; let i = $index) {\n <div\n class=\"suggest-item suggest-recent-item\"\n [class.suggest-item-highlighted]=\"IsHighlighted(i)\"\n (mousedown)=\"OnRecentClick(recent); $event.preventDefault()\"\n (mouseenter)=\"HighlightedIndex = i\"\n >\n <i class=\"fa-solid fa-clock suggest-item-icon\"></i>\n <span class=\"suggest-item-text\">{{ recent.Query }}</span>\n <span class=\"suggest-item-meta\">{{ recent.ResultCount }} results</span>\n </div>\n }\n </div>\n }\n\n <!-- Recently opened records section (idle state) -->\n @if (showRecentRecordsSection) {\n <div class=\"suggest-section\">\n <div class=\"suggest-section-header\">\n <i class=\"fa-solid fa-clock-rotate-left suggest-section-icon\"></i>\n <span>Recently Opened</span>\n </div>\n @for (record of VisibleRecentRecords; track record.EntityName + record.RecordID; let i = $index) {\n <div\n class=\"suggest-item suggest-recent-item\"\n [class.suggest-item-highlighted]=\"IsHighlighted((showRecentSection ? VisibleRecentSearches.length : 0) + i)\"\n (mousedown)=\"OnRecentRecordClick(record); $event.preventDefault()\"\n (mouseenter)=\"HighlightedIndex = (showRecentSection ? VisibleRecentSearches.length : 0) + i\"\n >\n <i class=\"fa-solid fa-file-lines suggest-item-icon\"></i>\n <span class=\"suggest-item-text\">{{ record.RecordName }}</span>\n <span class=\"suggest-item-meta\">{{ record.EntityDisplayName || record.EntityName }}</span>\n </div>\n }\n </div>\n }\n\n <!-- Preview results section -->\n @if (showPreviewSection && !IsLoading) {\n @if (VisiblePreviewResults.length > 0) {\n <div class=\"suggest-section\">\n @for (result of VisiblePreviewResults; track result.ID; let i = $index) {\n <div\n class=\"suggest-item suggest-result-item\"\n [class.suggest-item-highlighted]=\"IsHighlighted((showRecentSection ? VisibleRecentSearches.length : 0) + i)\"\n (mousedown)=\"OnResultClick(result); $event.preventDefault()\"\n (mouseenter)=\"HighlightedIndex = (showRecentSection ? VisibleRecentSearches.length : 0) + i\"\n >\n <div class=\"suggest-result-icon\">\n <i [class]=\"result.EntityIcon || result.SourceIcon || 'fa-solid fa-database'\"></i>\n </div>\n <div class=\"suggest-result-body\">\n <div class=\"suggest-result-title\">{{ result.RecordName || result.Title }}</div>\n <div class=\"suggest-result-entity\">{{ result.EntityDisplayName || result.EntityName }}</div>\n </div>\n <span class=\"suggest-result-score\" [class]=\"'suggest-result-score ' + GetScoreClass(result.Score)\">{{ FormatScore(result.Score) }}</span>\n </div>\n }\n </div>\n } @else if (!IsLoading) {\n <div class=\"suggest-empty\">\n <span>No results found</span>\n </div>\n }\n\n <!-- See all footer -->\n @if (showSeeAllFooter) {\n <div\n class=\"suggest-see-all\"\n [class.suggest-item-highlighted]=\"IsHighlighted((showRecentSection ? VisibleRecentSearches.length : 0) + VisiblePreviewResults.length)\"\n (mousedown)=\"OnSeeAllClick(); $event.preventDefault()\"\n (mouseenter)=\"HighlightedIndex = (showRecentSection ? VisibleRecentSearches.length : 0) + VisiblePreviewResults.length\"\n >\n <i class=\"fa-solid fa-arrow-right suggest-see-all-icon\"></i>\n <span>See all {{ TotalCount }} results</span>\n </div>\n }\n }\n\n <!-- Min relevance filter bar -->\n @if (showPreviewSection) {\n <div class=\"suggest-filter-bar\">\n <div class=\"suggest-filter-toggle\" (mousedown)=\"ToggleFilter($event)\">\n <i class=\"fa-solid fa-sliders suggest-filter-icon\"></i>\n <span class=\"suggest-filter-label\">\n @if (MinRelevancePercent > 0) {\n Min relevance: {{ MinRelevancePercent }}%\n } @else {\n Showing all results\n }\n </span>\n <i class=\"fa-solid fa-chevron-down suggest-filter-chevron\" [class.suggest-filter-chevron-open]=\"IsFilterOpen\"></i>\n </div>\n\n @if (IsFilterOpen) {\n <div class=\"suggest-filter-popover\">\n <div class=\"suggest-filter-popover-header\">Min Relevance</div>\n @for (option of MinRelevanceOptions; track option) {\n <div\n class=\"suggest-filter-option\"\n [class.suggest-filter-option-active]=\"IsSelectedRelevance(option)\"\n (mousedown)=\"SetMinRelevance(option, $event)\"\n >\n <span>{{ FormatRelevanceOption(option) }}</span>\n @if (IsSelectedRelevance(option)) {\n <i class=\"fa-solid fa-check suggest-filter-check\"></i>\n }\n </div>\n }\n </div>\n }\n </div>\n }\n\n <!-- Empty state when no recents of either kind and query too short -->\n @if (!showRecentSection && !showRecentRecordsSection && !showPreviewSection) {\n <div class=\"suggest-empty\">\n <span>Type at least {{ MinQueryLength }} characters to search</span>\n </div>\n }\n\n <!-- Host-supplied promo row (e.g. command palette nudge) -->\n @if (ShowPromo && PromoText) {\n <div class=\"suggest-promo\">\n <i class=\"fa-solid fa-wand-magic-sparkles suggest-promo-icon\"></i>\n <span class=\"suggest-promo-text\">{{ PromoText }}</span>\n <button type=\"button\" class=\"suggest-promo-action\"\n (mousedown)=\"$event.preventDefault()\"\n (click)=\"OnPromoAccept()\">{{ PromoActionLabel }}</button>\n <button type=\"button\" class=\"suggest-promo-dismiss\" aria-label=\"Dismiss\" title=\"Dismiss\"\n (mousedown)=\"$event.preventDefault()\"\n (click)=\"OnPromoDismiss(); $event.stopPropagation()\">\n <i class=\"fa-solid fa-xmark\"></i>\n </button>\n </div>\n }\n </div>\n}\n", styles: ["/* Search Suggest Dropdown */\n\n.suggest-dropdown {\n position: absolute;\n top: 100%;\n left: 0;\n right: 0;\n z-index: 99999;\n margin-top: 4px;\n border: 1px solid var(--mj-border-default);\n border-radius: 10px;\n background: var(--mj-bg-surface);\n box-shadow: 0 8px 30px rgba(0, 0, 0, 0.12), 0 2px 8px rgba(0, 0, 0, 0.06);\n overflow: visible;\n animation: suggestSlideIn 0.15s ease;\n}\n\n@keyframes suggestSlideIn {\n from {\n opacity: 0;\n transform: translateY(-4px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n\n/* Sections */\n.suggest-section {\n padding: 0.25rem 0;\n}\n\n.suggest-section + .suggest-section {\n border-top: 1px solid var(--mj-border-subtle);\n}\n\n.suggest-section-header {\n display: flex;\n align-items: center;\n gap: 0.4rem;\n padding: 0.5rem 0.75rem 0.3rem;\n font-size: 0.7rem;\n font-weight: 600;\n color: var(--mj-text-muted);\n text-transform: uppercase;\n letter-spacing: 0.04em;\n}\n\n.suggest-section-icon {\n font-size: 0.65rem;\n}\n\n.suggest-clear-link {\n margin-left: auto;\n padding: 0;\n border: none;\n background: transparent;\n font-family: inherit;\n font-size: 0.68rem;\n font-weight: var(--mj-font-medium);\n color: var(--mj-text-link, var(--mj-brand-primary));\n cursor: pointer;\n text-transform: none;\n letter-spacing: normal;\n}\n\n.suggest-clear-link:hover {\n text-decoration: underline;\n}\n\n.suggest-clear-link:focus-visible {\n outline: 2px solid var(--mj-border-focus);\n outline-offset: 2px;\n border-radius: var(--mj-radius-sm);\n}\n\n/* Items */\n.suggest-item {\n display: flex;\n align-items: center;\n gap: 0.6rem;\n padding: 0.45rem 0.75rem;\n cursor: pointer;\n transition: background 0.08s ease;\n}\n\n.suggest-item:hover,\n.suggest-item-highlighted {\n background: var(--mj-bg-surface-hover);\n}\n\n.suggest-item-icon {\n color: var(--mj-text-muted);\n font-size: 0.75rem;\n width: 1rem;\n text-align: center;\n flex-shrink: 0;\n}\n\n.suggest-item-text {\n flex: 1;\n font-size: 0.85rem;\n color: var(--mj-text-primary);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.suggest-item-meta {\n font-size: 0.72rem;\n color: var(--mj-text-muted);\n flex-shrink: 0;\n}\n\n/* Result items */\n.suggest-result-item {\n gap: 0.65rem;\n}\n\n.suggest-result-icon {\n width: 1.75rem;\n height: 1.75rem;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 6px;\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n font-size: 0.72rem;\n flex-shrink: 0;\n}\n\n.suggest-result-body {\n flex: 1;\n min-width: 0;\n}\n\n.suggest-result-title {\n font-size: 0.85rem;\n font-weight: 500;\n color: var(--mj-text-primary);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.suggest-result-entity {\n font-size: 0.7rem;\n color: var(--mj-text-muted);\n}\n\n/* Score badge \u2014 base styles */\n.suggest-result-score {\n font-size: 0.68rem;\n font-weight: 600;\n padding: 0.1rem 0.4rem;\n border-radius: 9999px;\n flex-shrink: 0;\n /* Default: neutral gray */\n color: var(--mj-text-muted);\n background: color-mix(in srgb, var(--mj-text-muted) 10%, var(--mj-bg-surface));\n}\n\n/* Score badge \u2014 high relevance (green, \u226540%) */\n.suggest-score-high {\n color: var(--mj-status-success-text, var(--mj-status-success));\n background: color-mix(in srgb, var(--mj-status-success) 12%, var(--mj-bg-surface));\n}\n\n/* Score badge \u2014 medium relevance (amber, 15\u201339%) */\n.suggest-score-medium {\n color: var(--mj-status-warning-text, var(--mj-status-warning));\n background: color-mix(in srgb, var(--mj-status-warning) 12%, var(--mj-bg-surface));\n}\n\n/* Score badge \u2014 low relevance (gray, <15%) */\n.suggest-score-low {\n color: var(--mj-text-muted);\n background: color-mix(in srgb, var(--mj-text-muted) 10%, var(--mj-bg-surface));\n}\n\n/* Loading */\n.suggest-loading {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 0.5rem;\n padding: 0.85rem 0.75rem;\n color: var(--mj-text-muted);\n font-size: 0.82rem;\n}\n\n.suggest-loading-icon {\n color: var(--mj-brand-primary);\n}\n\n/* Empty state */\n.suggest-empty {\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 1rem 0.75rem;\n color: var(--mj-text-muted);\n font-size: 0.82rem;\n}\n\n/* See all footer */\n.suggest-see-all {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n padding: 0.55rem 0.75rem;\n border-top: 1px solid var(--mj-border-subtle);\n cursor: pointer;\n color: var(--mj-brand-primary);\n font-size: 0.82rem;\n font-weight: 500;\n transition: background 0.08s ease;\n}\n\n.suggest-see-all:hover,\n.suggest-see-all.suggest-item-highlighted {\n background: var(--mj-bg-surface-hover);\n}\n\n.suggest-see-all-icon {\n font-size: 0.72rem;\n}\n\n/* --- Min Relevance Filter Bar --- */\n.suggest-filter-bar {\n position: relative;\n border-top: 1px solid var(--mj-border-subtle);\n padding: 0.35rem 0.75rem;\n}\n\n.suggest-filter-toggle {\n display: flex;\n align-items: center;\n gap: 0.4rem;\n cursor: pointer;\n font-size: 0.7rem;\n color: var(--mj-text-muted);\n transition: color 0.1s ease;\n user-select: none;\n}\n\n.suggest-filter-toggle:hover {\n color: var(--mj-text-secondary);\n}\n\n.suggest-filter-icon {\n font-size: 0.65rem;\n}\n\n.suggest-filter-label {\n flex: 1;\n}\n\n.suggest-filter-chevron {\n font-size: 0.55rem;\n transition: transform 0.15s ease;\n}\n\n.suggest-filter-chevron-open {\n transform: rotate(180deg);\n}\n\n/* Filter popover */\n.suggest-filter-popover {\n position: absolute;\n top: 100%;\n left: 0.5rem;\n right: 0.5rem;\n margin-top: 4px;\n background: var(--mj-bg-surface-elevated, var(--mj-bg-surface));\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);\n overflow: hidden;\n z-index: 10;\n animation: suggestSlideIn 0.12s ease;\n}\n\n.suggest-filter-popover-header {\n padding: 0.4rem 0.6rem 0.25rem;\n font-size: 0.65rem;\n font-weight: 600;\n color: var(--mj-text-muted);\n text-transform: uppercase;\n letter-spacing: 0.04em;\n}\n\n.suggest-filter-option {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 0.35rem 0.6rem;\n cursor: pointer;\n font-size: 0.78rem;\n color: var(--mj-text-primary);\n transition: background 0.08s ease;\n}\n\n.suggest-filter-option:hover {\n background: var(--mj-bg-surface-hover);\n}\n\n.suggest-filter-option-active {\n color: var(--mj-brand-primary);\n font-weight: 600;\n}\n\n.suggest-filter-check {\n font-size: 0.65rem;\n color: var(--mj-brand-primary);\n}\n\n/* Host-supplied promo row \u2014 a quiet whisper pinned to the dropdown's bottom\n edge, never a banner. */\n.suggest-promo {\n display: flex;\n align-items: center;\n gap: var(--mj-space-2);\n padding: var(--mj-space-2) var(--mj-space-3);\n border-top: 1px solid var(--mj-border-subtle);\n background: var(--mj-bg-surface-card);\n font-size: 0.78rem;\n color: var(--mj-text-muted);\n}\n\n.suggest-promo-icon {\n color: var(--mj-brand-primary);\n flex-shrink: 0;\n}\n\n.suggest-promo-text {\n flex: 1;\n min-width: 0;\n}\n\n.suggest-promo-action {\n flex-shrink: 0;\n padding: 0.2rem 0.6rem;\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md);\n background: transparent;\n color: var(--mj-brand-primary);\n font-size: 0.75rem;\n font-weight: var(--mj-font-medium);\n cursor: pointer;\n transition: var(--mj-transition-colors);\n}\n\n.suggest-promo-action:hover {\n background: color-mix(in srgb, var(--mj-brand-primary) 8%, var(--mj-bg-surface));\n border-color: var(--mj-brand-primary);\n}\n\n.suggest-promo-dismiss {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 22px;\n height: 22px;\n padding: 0;\n border: none;\n border-radius: var(--mj-radius-full);\n background: transparent;\n color: var(--mj-text-disabled);\n font-size: 0.75rem;\n cursor: pointer;\n transition: var(--mj-transition-colors);\n flex-shrink: 0;\n}\n\n.suggest-promo-dismiss:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-secondary);\n}\n"] }]
|
|
590
606
|
}], null, { IsOpen: [{
|
|
591
607
|
type: Input
|
|
592
608
|
}], PreviewResults: [{
|