11<template >
22 <NewVisitor />
3- <div class =" map-container" @keyup = " handleKeyUp " >
3+ <div class =" map-container" >
44 <leaflet-map
55 v-if =" center"
66 v-model:center =" center"
1212 >
1313 <!-- SEARCH -->
1414 <template v-slot :topleft >
15- <form @submit.prevent =" onSearch" >
15+ <form @submit.prevent =" onSearch" @dblclick.stop @mousedown.stop @mousemove.stop >
1616 <v-text-field
17- :rounded =" xs || undefined"
18- :density =" xs ? 'compact' : 'default'"
1917 class =" map-search"
2018 ref =" searchField"
21- prepend-inner-icon =" mdi-magnify"
22- placeholder =" Search for a location"
19+ placeholder =" City or zip code"
2320 single-line
2421 variant =" solo"
25- clearable
2622 hide-details
2723 v-model =" searchInput"
2824 type =" search"
25+ @focus =" isSearchFocused = true"
26+ @blur =" isSearchFocused = false"
2927 >
3028 <template v-slot :append-inner >
31- <v-btn :disabled =" !searchInput" variant =" text" flat color =" #0080BC" @click =" onSearch" >
32- Go<v-icon end >mdi-chevron-right</v-icon >
29+ <span v-if =" !isMobile && !isSearchFocused" class =" text-subtitle-2 text-grey-darken-1" >{{ searchShortcut }}</span >
30+
31+ <v-btn icon tile :disabled =" !searchInput" variant =" text" flat color =" #0080BC" @click =" onSearch" >
32+ <v-icon >mdi-magnify</v-icon >
3333 </v-btn >
3434 </template >
3535 </v-text-field >
5252
5353<script setup lang="ts">
5454import ' leaflet/dist/leaflet.css' ;
55- import { ref , onMounted , computed , watch } from ' vue' ;
55+ import { ref , onMounted , computed , onUnmounted } from ' vue' ;
5656import { useRouter } from ' vue-router'
5757import type { Ref } from ' vue' ;
5858import { BoundingBox } from ' @/services/apiService' ;
5959import { geocodeQuery } from ' @/services/apiService' ;
60- import { useDisplay , useTheme } from ' vuetify' ;
60+ import { useDisplay } from ' vuetify' ;
6161import { useGlobalStore } from ' @/stores/global' ;
6262import { useTilesStore } from ' @/stores/tiles' ;
6363import L from ' leaflet' ;
@@ -74,22 +74,28 @@ const bounds: Ref<BoundingBox|null> = ref(null);
7474const searchField: Ref <any | null > = ref (null );
7575const searchInput: Ref <string > = ref (' ' ); // For the text input field
7676const searchQuery: Ref <string > = ref (' ' ); // For URL and boundaries (persistent)
77+ const isSearchFocused: Ref <boolean > = ref (false );
7778const geojson: Ref <GeoJSON .GeoJsonObject | null > = ref (null );
7879const tilesStore = useTilesStore ();
7980
8081const { fetchVisibleTiles } = tilesStore ;
8182const alprs = computed (() => tilesStore .allNodes );
8283
8384const router = useRouter ();
84- const { xs } = useDisplay ();
85+ const { xs : isMobile } = useDisplay ();
8586
8687const globalStore = useGlobalStore ();
8788
8889const setCurrentLocation = globalStore .setCurrentLocation ;
8990const currentLocation = computed (() => globalStore .currentLocation );
9091
91- function handleKeyUp(event : KeyboardEvent ) {
92- if (event .key === ' /' && searchField .value .value !== document .activeElement ) {
92+ const searchShortcut = computed (() => {
93+ const isMac = navigator .userAgent .toUpperCase ().indexOf (' MAC' ) >= 0 ;
94+ return isMac ? ' ⌘K' : ' Ctrl+K' ;
95+ });
96+
97+ function handleSearchShortcut(event : KeyboardEvent ) {
98+ if ((event .ctrlKey || event .metaKey ) && event .key === ' k' && searchField .value .value !== document .activeElement ) {
9399 searchField .value .focus ();
94100 event .preventDefault ();
95101 }
@@ -200,6 +206,8 @@ function updateMarkers() {
200206}
201207
202208onMounted (() => {
209+ document .addEventListener (' keydown' , handleSearchShortcut );
210+
203211 // Expected hash format like #map=<ZOOM_LEVEL:int>/<LATITUDE:float>/<LONGITUDE:float>/<QUERY:text>
204212 const hash = router .currentRoute .value .hash ;
205213 if (hash ) {
@@ -224,6 +232,10 @@ onMounted(() => {
224232 }
225233});
226234
235+ onUnmounted (() => {
236+ document .removeEventListener (' keydown' , handleSearchShortcut );
237+ });
238+
227239 </script >
228240
229241<style scoped>
0 commit comments