1- import type { VNode } from '@algolia/autocomplete-js' ;
1+ import type { AutocompleteComponents , VNode } from '@algolia/autocomplete-js' ;
2+ import type { Hit } from '@algolia/client-search' ;
23
3- import type { AlgoliaRecord , HighlightedHierarchy } from './types' ;
4+ import type { AlgoliaRecord } from './types' ;
45
56export const templates = {
67 poweredBy : ( { hostname } : { hostname : string } ) : VNode => {
@@ -19,13 +20,11 @@ export const templates = {
1920 } ,
2021
2122 item : (
22- record : AlgoliaRecord ,
23- title : Array < string | VNode > ,
24- description : Array < string | VNode > ,
25- hierarchy : HighlightedHierarchy | null
26- ) : VNode => {
23+ hit : AlgoliaRecord ,
24+ components : AutocompleteComponents
25+ ) : JSX . Element => {
2726 return (
28- < a href = { record . url } >
27+ < a href = { hit . url } >
2928 < div className = "aa-ItemContent" >
3029 < div className = "aa-ItemIcon" >
3130 < svg width = "20" height = "20" viewBox = "0 0 20 20" >
@@ -39,15 +38,19 @@ export const templates = {
3938 </ svg >
4039 </ div >
4140 < div >
42- < div className = "aa-ItemTitle" > { hierarchy ?. lvl0 ?? title } </ div >
43- { hierarchy && (
41+ < div className = "aa-ItemTitle" >
42+ { hit . hierarchy ?. lvl0 ?? (
43+ < components . Highlight hit = { hit } attribute = "title" />
44+ ) }
45+ </ div >
46+ { hit . hierarchy && (
4447 < div className = "aa-ItemHierarchy" >
45- { hierarchyToBreadcrumbVNodes ( hierarchy ) }
48+ { hierarchyToBreadcrumbs ( hit , components ) }
4649 </ div >
4750 ) }
48- { description && (
49- < div className = "aa-ItemDescription" > { description } </ div >
50- ) }
51+ < div className = "aa-ItemDescription" >
52+ { getSuggestionSnippet ( hit , components ) }
53+ </ div >
5154 </ div >
5255 </ div >
5356 </ a >
@@ -56,26 +59,63 @@ export const templates = {
5659} ;
5760
5861/**
59- * Transform a highlighted hierarchy object into an array of VNode[] .
62+ * Transform a highlighted hierarchy object into an array of Highlighted elements .
6063 * 3 levels max are returned.
6164 *
62- * @param hierarchy - An highlighted hierarchy, i.e. { lvl0: (string | VNode)[], lvl1: (string | VNode)[], lvl2: (string | VNode)[], ... }.
63- * @returns An array of VNode[], representing of the highlighted hierarchy starting from lvl1.
64- * Between each VNode[] we insert a ' > ' character to render them as breadcrumbs eventually.
65+ * @param hit - A record with a hierarchy field ( { lvl0: string, lvl1: string, lvl2: string, ... } ).
66+ * @param components - Autocomplete components.
67+ * @returns An array of JSX.Elements | string, representing of the highlighted hierarchy starting from lvl1.
68+ * Between each element, we insert a ' > ' character to render them as breadcrumbs eventually.
6569 */
66- function hierarchyToBreadcrumbVNodes (
67- hierarchy : HighlightedHierarchy
68- ) : Array < string | Array < string | VNode > > {
69- const breadcrumbVNodeArray : Array < string | Array < string | VNode > > = [ ] ;
70+ function hierarchyToBreadcrumbs (
71+ hit : Hit < AlgoliaRecord > ,
72+ components : AutocompleteComponents
73+ ) : Array < JSX . Element | string > {
74+ const breadcrumbArray : Array < JSX . Element | string > = [ ] ;
7075 let addedLevels = 0 ;
76+ if ( ! hit . hierarchy ) {
77+ return breadcrumbArray ;
78+ }
7179 for ( let i = 1 ; i < 7 && addedLevels < 3 ; ++ i ) {
72- if ( hierarchy [ `lvl${ i } ` ] && hierarchy [ `lvl${ i } ` ] . length > 0 ) {
80+ if ( hit . hierarchy [ `lvl${ i } ` ] && hit . hierarchy [ `lvl${ i } ` ] . length > 0 ) {
7381 if ( addedLevels > 0 ) {
74- breadcrumbVNodeArray . push ( ' > ' ) ;
82+ breadcrumbArray . push ( ' > ' ) ;
7583 }
76- breadcrumbVNodeArray . push ( hierarchy [ `lvl${ i } ` ] ) ;
84+ breadcrumbArray . push (
85+ < components . Highlight hit = { hit } attribute = "description" />
86+ ) ;
7787 ++ addedLevels ;
7888 }
7989 }
80- return breadcrumbVNodeArray ;
90+ return breadcrumbArray ;
91+ }
92+
93+ function getSuggestionSnippet (
94+ hit : Hit < AlgoliaRecord > ,
95+ components : AutocompleteComponents
96+ ) : JSX . Element | string {
97+ // If they are defined as `searchableAttributes`, 'description' and 'content' are always
98+ // present in the `_snippetResult`, even if they don't match.
99+ // So we need to have 1 check on the presence and 1 check on the match
100+ const description = hit . _snippetResult ?. description ;
101+ const content = hit . _snippetResult ?. content ;
102+
103+ // Take in priority props that matches the search
104+ if ( description && description . matchLevel === 'full' ) {
105+ return < components . Snippet hit = { hit } attribute = "description" /> ;
106+ }
107+ if ( content && content . matchLevel === 'full' ) {
108+ return < components . Snippet hit = { hit } attribute = "content" /> ;
109+ }
110+
111+ // Otherwise take the prop that was at least correctly returned
112+ if ( description && ! content ) {
113+ return < components . Snippet hit = { hit } attribute = "description" /> ;
114+ }
115+ if ( content ) {
116+ return < components . Snippet hit = { hit } attribute = "content" /> ;
117+ }
118+
119+ // Otherwise raw value or empty
120+ return hit . description || hit . content || '' ;
81121}
0 commit comments