|
1 | 1 | import { type JSX, createElement } from 'react'; |
2 | 2 |
|
3 | | -import type { StoredDocSearchHit } from './types'; |
| 3 | +import type { DocSearchHit, StoredDocSearchHit } from './types'; |
4 | 4 |
|
5 | | -function getPropertyByPath(object: Record<string, any>, path: string): any { |
6 | | - const parts = path.split('.'); |
| 5 | +type HierarchyLevel = 'lvl0' | 'lvl1' | 'lvl2' | 'lvl3' | 'lvl4' | 'lvl5' | 'lvl6'; |
| 6 | +type SnippetAttribute = 'content' | `hierarchy.${HierarchyLevel}`; |
| 7 | +type SnippetHit = StoredDocSearchHit & |
| 8 | + Partial<Pick<DocSearchHit, '_highlightResult' | '_snippetResult'>>; |
7 | 9 |
|
8 | | - return parts.reduce((prev, current) => { |
9 | | - if (prev?.[current]) return prev[current]; |
| 10 | +function parseHierarchyAttribute(attribute: string): HierarchyLevel | null { |
| 11 | + if (!attribute.startsWith('hierarchy.')) { |
10 | 12 | return null; |
11 | | - }, object); |
| 13 | + } |
| 14 | + |
| 15 | + const level = attribute.replace('hierarchy.', '') as HierarchyLevel; |
| 16 | + return ['lvl0', 'lvl1', 'lvl2', 'lvl3', 'lvl4', 'lvl5', 'lvl6'].includes(level) |
| 17 | + ? level |
| 18 | + : null; |
| 19 | +} |
| 20 | + |
| 21 | +function getRawValue(hit: SnippetHit, attribute: SnippetAttribute): string { |
| 22 | + if (attribute === 'content') { |
| 23 | + return hit.content ?? ''; |
| 24 | + } |
| 25 | + |
| 26 | + const level = parseHierarchyAttribute(attribute); |
| 27 | + if (!level) { |
| 28 | + return ''; |
| 29 | + } |
| 30 | + |
| 31 | + return hit[attribute] ?? hit.hierarchy[level] ?? ''; |
| 32 | +} |
| 33 | + |
| 34 | +function getHighlightedValue(hit: SnippetHit, attribute: SnippetAttribute): string { |
| 35 | + const highlight = hit._highlightResult; |
| 36 | + if (!highlight) { |
| 37 | + return ''; |
| 38 | + } |
| 39 | + |
| 40 | + if (attribute === 'content') { |
| 41 | + return highlight.content?.value ?? ''; |
| 42 | + } |
| 43 | + |
| 44 | + const level = parseHierarchyAttribute(attribute); |
| 45 | + if (!level) { |
| 46 | + return ''; |
| 47 | + } |
| 48 | + |
| 49 | + return highlight[attribute]?.value ?? highlight.hierarchy?.[level]?.value ?? ''; |
| 50 | +} |
| 51 | + |
| 52 | +function getSnippetValue(hit: SnippetHit, attribute: SnippetAttribute): string { |
| 53 | + const snippet = hit._snippetResult; |
| 54 | + if (!snippet) { |
| 55 | + return ''; |
| 56 | + } |
| 57 | + |
| 58 | + if (attribute === 'content') { |
| 59 | + return snippet.content?.value ?? ''; |
| 60 | + } |
| 61 | + |
| 62 | + const level = parseHierarchyAttribute(attribute); |
| 63 | + if (!level) { |
| 64 | + return ''; |
| 65 | + } |
| 66 | + |
| 67 | + return snippet[attribute]?.value ?? snippet.hierarchy?.[level]?.value ?? ''; |
12 | 68 | } |
13 | 69 |
|
14 | 70 | interface SnippetProps<TItem> { |
15 | 71 | hit: TItem; |
16 | | - attribute: string; |
| 72 | + attribute: SnippetAttribute; |
17 | 73 | tagName?: string; |
18 | 74 | [prop: string]: unknown; |
19 | 75 | } |
20 | 76 |
|
21 | | -export function Snippet<TItem extends StoredDocSearchHit>({ |
| 77 | +export function Snippet<TItem extends SnippetHit>({ |
22 | 78 | hit, |
23 | 79 | attribute, |
24 | 80 | tagName = 'span', |
25 | 81 | ...rest |
26 | 82 | }: SnippetProps<TItem>): JSX.Element { |
| 83 | + const highlightValue = getHighlightedValue(hit, attribute); |
| 84 | + const rawValue = getRawValue(hit, attribute); |
| 85 | + const baseValue = highlightValue || rawValue; |
| 86 | + const snippetValue = getSnippetValue(hit, attribute); |
| 87 | + |
| 88 | + let displayValue = baseValue; |
| 89 | + |
| 90 | + if (snippetValue && baseValue) { |
| 91 | + let formattedSnippet = snippetValue; |
| 92 | + if (baseValue.substring(0, 20) !== snippetValue.substring(0, 20)) { |
| 93 | + formattedSnippet = `… ${formattedSnippet}`; |
| 94 | + } |
| 95 | + if ( |
| 96 | + baseValue.substring(baseValue.length - 20, baseValue.length) !== |
| 97 | + snippetValue.substring(snippetValue.length - 20, snippetValue.length) |
| 98 | + ) { |
| 99 | + formattedSnippet = `${formattedSnippet} …`; |
| 100 | + } |
| 101 | + displayValue = formattedSnippet; |
| 102 | + } else if (snippetValue) { |
| 103 | + displayValue = snippetValue; |
| 104 | + } |
| 105 | + |
27 | 106 | return createElement(tagName, { |
28 | 107 | ...rest, |
29 | 108 | dangerouslySetInnerHTML: { |
30 | | - __html: getPropertyByPath(hit, `_snippetResult.${attribute}.value`) || getPropertyByPath(hit, attribute), |
| 109 | + __html: displayValue, |
31 | 110 | }, |
32 | 111 | }); |
33 | 112 | } |
0 commit comments