]> BookStack Code Mirror - bookstack/blob - resources/js/components/global-search.js
Updated global search component to new format
[bookstack] / resources / js / components / global-search.js
1 import {htmlToDom} from "../services/dom";
2 import {debounce} from "../services/util";
3 import {KeyboardNavigationHandler} from "../services/keyboard-navigation";
4 import {Component} from "./component";
5
6 /**
7  * Global (header) search box handling.
8  * Mainly to show live results preview.
9  */
10 export class GlobalSearch extends Component {
11
12     setup() {
13         this.container = this.$el;
14         this.input = this.$refs.input;
15         this.suggestions = this.$refs.suggestions;
16         this.suggestionResultsWrap = this.$refs.suggestionResults;
17         this.loadingWrap = this.$refs.loading;
18         this.button = this.$refs.button;
19
20         this.setupListeners();
21     }
22
23     setupListeners() {
24         const updateSuggestionsDebounced = debounce(this.updateSuggestions.bind(this), 200, false);
25
26         // Handle search input changes
27         this.input.addEventListener('input', () => {
28             const value = this.input.value;
29             if (value.length > 0) {
30                 this.loadingWrap.style.display = 'block';
31                 this.suggestionResultsWrap.style.opacity = '0.5';
32                 updateSuggestionsDebounced(value);
33             }  else {
34                 this.hideSuggestions();
35             }
36         });
37
38         // Allow double click to show auto-click suggestions
39         this.input.addEventListener('dblclick', () => {
40             this.input.setAttribute('autocomplete', 'on');
41             this.button.focus();
42             this.input.focus();
43         });
44
45         new KeyboardNavigationHandler(this.container, () => {
46             this.hideSuggestions();
47         });
48     }
49
50     /**
51      * @param {String} search
52      */
53     async updateSuggestions(search) {
54         const {data: results} = await window.$http.get('/search/suggest', {term: search});
55         if (!this.input.value) {
56             return;
57         }
58         
59         const resultDom = htmlToDom(results);
60
61         this.suggestionResultsWrap.innerHTML = '';
62         this.suggestionResultsWrap.style.opacity = '1';
63         this.loadingWrap.style.display = 'none';
64         this.suggestionResultsWrap.append(resultDom);
65         if (!this.container.classList.contains('search-active')) {
66             this.showSuggestions();
67         }
68     }
69
70     showSuggestions() {
71         this.container.classList.add('search-active');
72         window.requestAnimationFrame(() => {
73             this.suggestions.classList.add('search-suggestions-animation');
74         })
75     }
76
77     hideSuggestions() {
78         this.container.classList.remove('search-active');
79         this.suggestions.classList.remove('search-suggestions-animation');
80         this.suggestionResultsWrap.innerHTML = '';
81     }
82 }