github.com/criteo/command-launcher@v0.0.0-20230407142452-fb616f546e98/gh-pages/assets/js/index.js (about) 1 var suggestions = document.getElementById('suggestions'); 2 var search = document.getElementById('search'); 3 4 if (search !== null) { 5 document.addEventListener('keydown', inputFocus); 6 } 7 8 function inputFocus(e) { 9 if (e.ctrlKey && e.key === '/' ) { 10 e.preventDefault(); 11 search.focus(); 12 } 13 if (e.key === 'Escape' ) { 14 search.blur(); 15 suggestions.classList.add('d-none'); 16 } 17 } 18 19 document.addEventListener('click', function(event) { 20 21 var isClickInsideElement = suggestions.contains(event.target); 22 23 if (!isClickInsideElement) { 24 suggestions.classList.add('d-none'); 25 } 26 27 }); 28 29 /* 30 Source: 31 - https://dev.to/shubhamprakash/trap-focus-using-javascript-6a3 32 */ 33 34 document.addEventListener('keydown',suggestionFocus); 35 36 function suggestionFocus(e) { 37 const suggestionsHidden = suggestions.classList.contains('d-none'); 38 if (suggestionsHidden) return; 39 40 const focusableSuggestions= [...suggestions.querySelectorAll('a')]; 41 if (focusableSuggestions.length === 0) return; 42 43 const index = focusableSuggestions.indexOf(document.activeElement); 44 45 if (e.key === "ArrowUp") { 46 e.preventDefault(); 47 const nextIndex = index > 0 ? index - 1 : 0; 48 focusableSuggestions[nextIndex].focus(); 49 } 50 else if (e.key === "ArrowDown") { 51 e.preventDefault(); 52 const nextIndex= index + 1 < focusableSuggestions.length ? index + 1 : index; 53 focusableSuggestions[nextIndex].focus(); 54 } 55 56 } 57 58 /* 59 Source: 60 - https://github.com/nextapps-de/flexsearch#index-documents-field-search 61 - https://raw.githack.com/nextapps-de/flexsearch/master/demo/autocomplete.html 62 */ 63 64 (function(){ 65 66 var index = new FlexSearch.Document({ 67 tokenize: "forward", 68 cache: 100, 69 document: { 70 id: 'id', 71 store: [ 72 "href", "title", "description" 73 ], 74 field: [ 75 'title', 76 'description', 77 'content', 78 ], 79 index: ["title", "description", "content"] 80 } 81 }); 82 83 84 // Not yet supported: https://github.com/nextapps-de/flexsearch#complex-documents 85 86 /* 87 var docs = [ 88 {{ range $index, $page := (where .Site.Pages "Section" "docs") -}} 89 { 90 id: {{ $index }}, 91 href: "{{ .Permalink }}", 92 title: {{ .Title | jsonify }}, 93 description: {{ .Params.description | jsonify }}, 94 content: {{ .Content | jsonify }} 95 }, 96 {{ end -}} 97 ]; 98 */ 99 100 // https://discourse.gohugo.io/t/range-length-or-last-element/3803/2 101 102 {{ $list := slice }} 103 {{- if and (isset .Site.Params.options "searchsectionsindex") (not (eq (len .Site.Params.options.searchSectionsIndex) 0)) }} 104 {{- if eq .Site.Params.options.searchSectionsIndex "ALL" }} 105 {{- $list = .Site.Pages }} 106 {{- else }} 107 {{- $list = (where .Site.Pages "Type" "in" .Site.Params.options.searchSectionsIndex) }} 108 {{- if (in .Site.Params.options.searchSectionsIndex "HomePage") }} 109 {{ $list = $list | append .Site.Home }} 110 {{- end }} 111 {{- end }} 112 {{- else }} 113 {{- $list = (where .Site.Pages "Section" "docs") }} 114 {{- end }} 115 116 {{ $len := (len $list) -}} 117 118 {{ range $index, $element := $list -}} 119 index.add( 120 { 121 id: {{ $index }}, 122 href: "{{ .Permalink | absURL }}", 123 title: {{ .Title | jsonify }}, 124 {{ with .Description -}} 125 description: {{ . | jsonify }}, 126 {{ else -}} 127 description: {{ .Summary | plainify | jsonify }}, 128 {{ end -}} 129 content: {{ .Plain | jsonify }} 130 } 131 ); 132 {{ end -}} 133 134 search.addEventListener('input', show_results, true); 135 136 function show_results(){ 137 const maxResult = 5; 138 var searchQuery = this.value; 139 var results = index.search(searchQuery, {limit: maxResult, enrich: true}); 140 141 // flatten results since index.search() returns results for each indexed field 142 const flatResults = new Map(); // keyed by href to dedupe results 143 for (const result of results.flatMap(r => r.result)) { 144 if (flatResults.has(result.doc.href)) continue; 145 flatResults.set(result.doc.href, result.doc); 146 } 147 148 suggestions.innerHTML = ""; 149 suggestions.classList.remove('d-none'); 150 151 // inform user that no results were found 152 if (flatResults.size === 0 && searchQuery) { 153 const noResultsMessage = document.createElement('div') 154 noResultsMessage.innerHTML = `No results for "<strong>${searchQuery}</strong>"` 155 noResultsMessage.classList.add("suggestion__no-results"); 156 suggestions.appendChild(noResultsMessage); 157 return; 158 } 159 160 // construct a list of suggestions 161 for(const [href, doc] of flatResults) { 162 const entry = document.createElement('div'); 163 suggestions.appendChild(entry); 164 165 const a = document.createElement('a'); 166 a.href = href; 167 entry.appendChild(a); 168 169 const title = document.createElement('span'); 170 title.textContent = doc.title; 171 title.classList.add("suggestion__title"); 172 a.appendChild(title); 173 174 const description = document.createElement('span'); 175 description.textContent = doc.description; 176 description.classList.add("suggestion__description"); 177 a.appendChild(description); 178 179 suggestions.appendChild(entry); 180 181 if(suggestions.childElementCount == maxResult) break; 182 } 183 } 184 }());