@memberjunction/ng-search 5.27.0 → 5.28.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"search-results.component.d.ts","sourceRoot":"","sources":["../../src/lib/search-results.component.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"search-results.component.d.ts","sourceRoot":"","sources":["../../src/lib/search-results.component.ts"],"names":[],"mappings":"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"}
@@ -7,6 +7,7 @@
7
7
  */
8
8
  import { Component, EventEmitter, Input, Output, ChangeDetectorRef, inject } from '@angular/core';
9
9
  import { Metadata } from '@memberjunction/core';
10
+ import { EscapeHTML } from '@memberjunction/global';
10
11
  import * as i0 from "@angular/core";
11
12
  import * as i1 from "@angular/common";
12
13
  import * as i2 from "@memberjunction/ng-shared-generic";
@@ -577,11 +578,15 @@ export class SearchResultsComponent {
577
578
  * The input is regex-escaped so user text is treated as a literal string.
578
579
  */
579
580
  HighlightMatch(text) {
580
- if (!this.HighlightText || !text)
581
+ if (!text)
581
582
  return text;
582
- const escaped = this.HighlightText.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
583
+ const safeText = EscapeHTML(text);
584
+ if (!this.HighlightText)
585
+ return safeText;
586
+ const safeHighlightText = EscapeHTML(this.HighlightText);
587
+ const escaped = safeHighlightText.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
583
588
  const regex = new RegExp(`(${escaped})`, 'gi');
584
- return text.replace(regex, '<mark class="search-highlight">$1</mark>');
589
+ return safeText.replace(regex, '<mark class="search-highlight">$1</mark>');
585
590
  }
586
591
  /** Format a score as a percentage */
587
592
  FormatScore(score) {
@@ -728,5 +733,5 @@ export class SearchResultsComponent {
728
733
  }], MoreLikeThisRequested: [{
729
734
  type: Output
730
735
  }] }); })();
731
- (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(SearchResultsComponent, { className: "SearchResultsComponent", filePath: "src/lib/search-results.component.ts", lineNumber: 26 }); })();
736
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(SearchResultsComponent, { className: "SearchResultsComponent", filePath: "src/lib/search-results.component.ts", lineNumber: 27 }); })();
732
737
  //# sourceMappingURL=search-results.component.js.map
@@ -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 { Metadata } from '@memberjunction/core';\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 {\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 if (!this.HighlightText || !text) return text;\n const escaped = this.HighlightText.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n const regex = new RegExp(`(${escaped})`, 'gi');\n return text.replace(regex, '<mark class=\"search-highlight\">$1</mark>');\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 = new Metadata();\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 <div class=\"results-empty\">\n <i class=\"fa-solid fa-search results-empty-icon\"></i>\n <p class=\"results-empty-text\">No results to display</p>\n </div>\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)\">\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)\">\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 <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\">\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 { Metadata } from '@memberjunction/core';\nimport { EscapeHTML } from '@memberjunction/global';\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 {\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 if (!text) return text;\n const safeText = EscapeHTML(text);\n if (!this.HighlightText) return safeText;\n\n const safeHighlightText = EscapeHTML(this.HighlightText);\n const escaped = safeHighlightText.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n const regex = new RegExp(`(${escaped})`, 'gi');\n return safeText.replace(regex, '<mark class=\"search-highlight\">$1</mark>');\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 = new Metadata();\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 <div class=\"results-empty\">\n <i class=\"fa-solid fa-search results-empty-icon\"></i>\n <p class=\"results-empty-text\">No results to display</p>\n </div>\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)\">\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)\">\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 <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\">\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"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@memberjunction/ng-search",
3
- "version": "5.27.0",
3
+ "version": "5.28.0",
4
4
  "description": "MemberJunction: Reusable search overlay and results components - command palette / Spotlight style search",
5
5
  "main": "./dist/public-api.js",
6
6
  "typings": "./dist/public-api.d.ts",
@@ -27,11 +27,11 @@
27
27
  "@angular/forms": "21.1.3"
28
28
  },
29
29
  "dependencies": {
30
- "@memberjunction/core": "5.27.0",
31
- "@memberjunction/global": "5.27.0",
32
- "@memberjunction/graphql-dataprovider": "5.27.0",
33
- "@memberjunction/core-entities": "5.27.0",
34
- "@memberjunction/ng-shared-generic": "5.27.0",
30
+ "@memberjunction/core": "5.28.0",
31
+ "@memberjunction/global": "5.28.0",
32
+ "@memberjunction/graphql-dataprovider": "5.28.0",
33
+ "@memberjunction/core-entities": "5.28.0",
34
+ "@memberjunction/ng-shared-generic": "5.28.0",
35
35
  "rxjs": "^7.8.2",
36
36
  "tslib": "^2.8.1"
37
37
  },