diff options
| -rwxr-xr-x | backend/server.py | 13 | ||||
| -rw-r--r-- | src/components/SearchModal.vue | 26 |
2 files changed, 27 insertions, 12 deletions
diff --git a/backend/server.py b/backend/server.py index 15d0960..0786ee8 100755 --- a/backend/server.py +++ b/backend/server.py @@ -11,6 +11,7 @@ dbFile = "data/data.db" imgDir = "../public/img/" NODE_REQ_DEPTH = 1 # For a /node?name=name1 request, respond with name1's node, and descendent nodes in a subtree to some depth > 0 +SEARCH_SUGG_LIMIT = 5 usageInfo = f"usage: {sys.argv[0]}\n" usageInfo += "Starts a server that listens for GET requests to http://" + hostname + ":" + str(port) + ".\n" @@ -62,9 +63,15 @@ def nodeNameToFile(name, cur): def lookupName(name): cur = dbCon.cursor() results = [] - for row in cur.execute("SELECT name, alt_name FROM names WHERE alt_name = ?", (name,)): - results.append(row[0]) - return json.dumps(results) + hasMore = False + for row in cur.execute( + "SELECT DISTINCT name, alt_name FROM names WHERE alt_name LIKE ? LIMIT ?", + (name + "%", SEARCH_SUGG_LIMIT + 1)): + results.append({"name": row[0], "altName": row[1]}) + if len(results) > SEARCH_SUGG_LIMIT: + hasMore = True + del results[-1] + return json.dumps([results, hasMore]) class DbServer(BaseHTTPRequestHandler): def do_GET(self): diff --git a/src/components/SearchModal.vue b/src/components/SearchModal.vue index 1005b14..6d23dc2 100644 --- a/src/components/SearchModal.vue +++ b/src/components/SearchModal.vue @@ -4,11 +4,15 @@ import SearchIcon from './icon/SearchIcon.vue'; import {LayoutNode} from '../layout'; import type {TolMap} from '../tol'; +type SearchSugg = {name: string, altName: string}; // Represents a search string suggestion +type SearchSuggResponse = [SearchSugg[], boolean]; // Holds search suggestions and an indication of if there was more + // Displays a search box, and sends search requests export default defineComponent({ data(){ return { - searchSuggs: [] as string[], // Holds suggestions for the search string + searchSuggs: [] as SearchSugg[], + searchHasMoreSuggs: false, 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 }; @@ -26,10 +30,10 @@ export default defineComponent({ onEnter(){ // Check for a focused search-suggestion if (this.focusedSuggIdx != null){ - this.resolveSearch(this.searchSuggs[this.focusedSuggIdx]); + this.resolveSearch(this.searchSuggs[this.focusedSuggIdx].name); return; } - // Ask server if input valid is valid name + // Get tol-node-name from server let input = this.$refs.searchInput as HTMLInputElement; let url = new URL(window.location.href); url.pathname = '/data/search'; @@ -44,7 +48,7 @@ export default defineComponent({ input.offsetWidth; // Triggers reflow input.classList.add('animate-red-then-fade'); } else { - this.resolveSearch(results[0]) + this.resolveSearch(results[0].name) } }) .catch(error => { @@ -74,6 +78,7 @@ export default defineComponent({ // Check for empty input if (input.value.length == 0){ this.searchSuggs = []; + this.searchHasMoreSuggs = false; this.focusedSuggIdx = null; return; } @@ -85,9 +90,10 @@ export default defineComponent({ let suggsId = this.lastSuggReqId; fetch(url.toString()) .then(response => response.json()) - .then(results => { + .then((results: SearchSuggResponse) => { if (this.lastSuggReqId == suggsId){ - this.searchSuggs = results; + this.searchSuggs = results[0]; + this.searchHasMoreSuggs = results[1]; this.focusedSuggIdx = null; } }) @@ -130,11 +136,13 @@ export default defineComponent({ @keyup.enter="onEnter" @keyup.esc="onCloseClick" @input="onInput" @keydown.down.prevent="onDownKey" @keydown.up.prevent="onUpKey"/> <div class="absolute top-[100%] w-full"> - <div v-for="(item, idx) of searchSuggs" :key="item" + <div v-for="(sugg, idx) of searchSuggs" :style="{backgroundColor: idx == focusedSuggIdx ? '#a3a3a3' : 'white'}" - class="bg-white border p-1 hover:underline hover:cursor-pointer" @click="resolveSearch(item)"> - {{item}} + class="bg-white border p-1 hover:underline hover:cursor-pointer" + @click="resolveSearch(sugg.name)"> + {{sugg.name == sugg.altName ? sugg.name : `${sugg.altName} (aka ${sugg.name})`}} </div> + <div v-if="searchHasMoreSuggs" class="bg-white px-1 text-center border">...</div> </div> </div> <search-icon @click.stop="onEnter" ref="searchIcon" |
