]> BookStack Code Mirror - bookstack/blob - resources/assets/js/vues/components/autosuggest.js
switch spaces to tabs
[bookstack] / resources / assets / js / vues / components / autosuggest.js
1
2 const template = `
3     <div>
4         <input :value="value" :autosuggest-type="type" ref="input"
5             :placeholder="placeholder" :name="name"
6             @input="inputUpdate($event.target.value)" @focus="inputUpdate($event.target.value)"
7             @blur="inputBlur"
8             @keydown="inputKeydown"
9         />
10         <ul class="suggestion-box" v-if="showSuggestions">
11             <li v-for="(suggestion, i) in suggestions"
12                 @click="selectSuggestion(suggestion)"
13                 :class="{active: (i === active)}">{{suggestion}}</li>
14         </ul>
15     </div>
16     
17 `;
18
19 function data() {
20     return {
21         suggestions: [],
22         showSuggestions: false,
23         active: 0,
24     };
25 }
26
27 const ajaxCache = {};
28
29 const props = ['url', 'type', 'value', 'placeholder', 'name'];
30
31 function getNameInputVal(valInput) {
32     let parentRow = valInput.parentNode.parentNode;
33     let nameInput = parentRow.querySelector('[autosuggest-type="name"]');
34     return (nameInput === null) ? '' : nameInput.value;
35 }
36
37 const methods = {
38
39     inputUpdate(inputValue) {
40         this.$emit('input', inputValue);
41         let params = {};
42
43         if (this.type === 'value') {
44             let nameVal = getNameInputVal(this.$el);
45             if (nameVal !== "") params.name = nameVal;
46         }
47
48         this.getSuggestions(inputValue.slice(0, 3), params).then(suggestions => {
49             if (inputValue.length === 0) {
50                 this.displaySuggestions(suggestions.slice(0, 6));
51                 return;
52             }
53             // Filter to suggestions containing searched term
54             suggestions = suggestions.filter(item => {
55                 return item.toLowerCase().indexOf(inputValue.toLowerCase()) !== -1;
56             }).slice(0, 4);
57             this.displaySuggestions(suggestions);
58         });
59     },
60
61     inputBlur() {
62         setTimeout(() => {
63             this.$emit('blur');
64             this.showSuggestions = false;
65         }, 100);
66     },
67
68     inputKeydown(event) {
69         if (event.keyCode === 13) event.preventDefault();
70         if (!this.showSuggestions) return;
71
72         // Down arrow
73         if (event.keyCode === 40) {
74             this.active = (this.active === this.suggestions.length - 1) ? 0 : this.active+1;
75         }
76         // Up Arrow
77         else if (event.keyCode === 38) {
78             this.active = (this.active === 0) ? this.suggestions.length - 1 : this.active-1;
79         }
80         // Enter or tab keys
81         else if ((event.keyCode === 13 || event.keyCode === 9) && !event.shiftKey) {
82             this.selectSuggestion(this.suggestions[this.active]);
83         }
84         // Escape key
85         else if (event.keyCode === 27) {
86             this.showSuggestions = false;
87         }
88     },
89
90     displaySuggestions(suggestions) {
91         if (suggestions.length === 0) {
92             this.suggestions = [];
93             this.showSuggestions = false;
94             return;
95         }
96
97         this.suggestions = suggestions;
98         this.showSuggestions = true;
99         this.active = 0;
100     },
101
102     selectSuggestion(suggestion) {
103         this.$refs.input.value = suggestion;
104         this.$refs.input.focus();
105         this.$emit('input', suggestion);
106         this.showSuggestions = false;
107     },
108
109     /**
110      * Get suggestions from BookStack. Store and use local cache if already searched.
111      * @param {String} input
112      * @param {Object} params
113      */
114     getSuggestions(input, params) {
115         params.search = input;
116         let cacheKey = `${this.url}:${JSON.stringify(params)}`;
117
118         if (typeof ajaxCache[cacheKey] !== "undefined") return Promise.resolve(ajaxCache[cacheKey]);
119
120         return this.$http.get(this.url, {params}).then(resp => {
121             ajaxCache[cacheKey] = resp.data;
122             return resp.data;
123         });
124     }
125
126 };
127
128 module.exports = {template, data, props, methods};