From 9deeb448e550fe28c22e5a9c4acc2adcfed71571 Mon Sep 17 00:00:00 2001 From: Terry Truong Date: Sun, 1 May 2022 00:19:29 +1000 Subject: Enable search-suggestion sending/displaying/selection --- src/components/SearchModal.vue | 123 ++++++++++++++++++++++++++++++++--------- 1 file changed, 98 insertions(+), 25 deletions(-) (limited to 'src/components') diff --git a/src/components/SearchModal.vue b/src/components/SearchModal.vue index cd4bede..1005b14 100644 --- a/src/components/SearchModal.vue +++ b/src/components/SearchModal.vue @@ -6,6 +6,13 @@ import type {TolMap} from '../tol'; // Displays a search box, and sends search requests export default defineComponent({ + data(){ + return { + searchSuggs: [] as string[], // Holds suggestions for the search string + focusedSuggIdx: null as null | number, // Denotes a search-suggestion selected using the arrow keys + lastSuggReqId: 0, // Used to prevent late search-suggestion server-responses from taking effect + }; + }, props: { tolMap: {type: Object as PropType, required: true}, uiOpts: {type: Object, required: true}, @@ -16,39 +23,95 @@ export default defineComponent({ this.$emit('search-close'); } }, - onSearchEnter(){ + onEnter(){ + // Check for a focused search-suggestion + if (this.focusedSuggIdx != null){ + this.resolveSearch(this.searchSuggs[this.focusedSuggIdx]); + return; + } + // Ask server if input valid is valid name let input = this.$refs.searchInput as HTMLInputElement; - // Query server let url = new URL(window.location.href); url.pathname = '/data/search'; url.search = '?name=' + encodeURIComponent(input.value); fetch(url.toString()) .then(response => response.json()) - .then(tolNodeName => { - // Search successful. Get nodes in parent-chain, add to tolMap, then emit event. - url.pathname = '/data/chain'; - url.search = '?name=' + encodeURIComponent(tolNodeName); - fetch(url.toString()) - .then(response => response.json()) - .then(obj => { - Object.getOwnPropertyNames(obj).forEach(key => {this.tolMap.set(key, obj[key])}); - this.$emit('search-node', tolNodeName); - }) - .catch(error => { - console.log('ERROR loading tolnode chain', error); - }); + .then(results => { + if (results.length == 0){ + input.value = ''; + // Trigger failure animation + input.classList.remove('animate-red-then-fade'); + input.offsetWidth; // Triggers reflow + input.classList.add('animate-red-then-fade'); + } else { + this.resolveSearch(results[0]) + } }) .catch(error => { - input.value = ''; - // Trigger failure animation - input.classList.remove('animate-red-then-fade'); - input.offsetWidth; // Triggers reflow - input.classList.add('animate-red-then-fade'); + console.log('ERROR getting search results from server', error); + }); + }, + resolveSearch(tolNodeName: string){ + // Asks server for nodes in parent-chain, updates tolMap, then emits search event + let url = new URL(window.location.href); + url.pathname = '/data/chain'; + url.search = '?name=' + encodeURIComponent(tolNodeName); + fetch(url.toString()) + .then(response => response.json()) + .then(obj => { + Object.getOwnPropertyNames(obj).forEach(key => {this.tolMap.set(key, obj[key])}); + this.$emit('search-node', tolNodeName); + }) + .catch(error => { + console.log('ERROR loading tolnode chain', error); }); }, focusInput(){ (this.$refs.searchInput as HTMLInputElement).focus(); }, + onInput(){ + let input = this.$refs.searchInput as HTMLInputElement; + // Check for empty input + if (input.value.length == 0){ + this.searchSuggs = []; + this.focusedSuggIdx = null; + return; + } + // Ask server for search-suggestions + let url = new URL(window.location.href); + url.pathname = '/data/search'; + url.search = '?name=' + encodeURIComponent(input.value); + this.lastSuggReqId += 1; + let suggsId = this.lastSuggReqId; + fetch(url.toString()) + .then(response => response.json()) + .then(results => { + if (this.lastSuggReqId == suggsId){ + this.searchSuggs = results; + this.focusedSuggIdx = null; + } + }) + }, + onDownKey(){ + // Select next search-suggestion, if any + if (this.searchSuggs.length > 0){ + if (this.focusedSuggIdx == null){ + this.focusedSuggIdx = 0; + } else { + this.focusedSuggIdx = Math.min(this.focusedSuggIdx + 1, this.searchSuggs.length - 1); + } + } + }, + onUpKey(){ + // Select previous search-suggestion, or cancel selection + if (this.focusedSuggIdx != null){ + if (this.focusedSuggIdx == 0){ + this.focusedSuggIdx = null; + } else { + this.focusedSuggIdx -= 1; + } + } + }, }, mounted(){ (this.$refs.searchInput as HTMLInputElement).focus(); @@ -60,12 +123,22 @@ export default defineComponent({ -- cgit v1.2.3