Enhance MarketFuzzySearch component with improved suggestion display and input focus handling
- Added focus and blur event handlers to manage input focus state. - Updated suggestion display to show both search suggestions and recent searches in a unified dropdown. - Improved styling for suggestion buttons to enhance user interaction. - Ensured input field is focused after clearing search, improving usability. These changes enhance the search experience by providing clearer suggestions and better input management.
This commit is contained in:
parent
06d4cc4d4a
commit
f2a432b6df
1 changed files with 69 additions and 54 deletions
|
|
@ -11,6 +11,8 @@
|
||||||
:model-value="searchQuery"
|
:model-value="searchQuery"
|
||||||
@update:model-value="handleSearchChange"
|
@update:model-value="handleSearchChange"
|
||||||
@keydown="handleKeydown"
|
@keydown="handleKeydown"
|
||||||
|
@focus="handleFocus"
|
||||||
|
@blur="handleBlur"
|
||||||
:placeholder="enhancedPlaceholder"
|
:placeholder="enhancedPlaceholder"
|
||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
class="pl-10 pr-20"
|
class="pl-10 pr-20"
|
||||||
|
|
@ -56,48 +58,54 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Search Suggestions (when no input but focused) -->
|
<!-- Floating Search Suggestions & Recent Searches -->
|
||||||
<div v-if="showSuggestions && !searchQuery && isFocused" class="mt-2 p-3 border rounded-lg bg-muted/50">
|
<div
|
||||||
<div class="text-sm font-medium mb-2">Try searching for:</div>
|
v-if="(showSuggestions || showRecentSearches) && !searchQuery && isFocused"
|
||||||
<div class="flex flex-wrap gap-1">
|
class="absolute top-full left-0 right-0 z-50 mt-1 max-h-80 overflow-y-auto bg-popover border border-border rounded-md shadow-lg"
|
||||||
<Button
|
>
|
||||||
v-for="suggestion in searchSuggestions"
|
<!-- Search Suggestions -->
|
||||||
:key="suggestion"
|
<div v-if="showSuggestions && searchSuggestions.length > 0" class="p-3 border-b border-border">
|
||||||
variant="ghost"
|
<div class="text-sm font-medium mb-2 text-foreground">Try searching for:</div>
|
||||||
size="sm"
|
<div class="flex flex-wrap gap-1">
|
||||||
@click="applySuggestion(suggestion)"
|
<Button
|
||||||
class="h-6 px-2 text-xs"
|
v-for="suggestion in searchSuggestions"
|
||||||
>
|
:key="suggestion"
|
||||||
{{ suggestion }}
|
variant="ghost"
|
||||||
</Button>
|
size="sm"
|
||||||
|
@click="applySuggestion(suggestion)"
|
||||||
|
class="h-6 px-2 text-xs hover:bg-accent hover:text-accent-foreground"
|
||||||
|
>
|
||||||
|
{{ suggestion }}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Recent Searches -->
|
<!-- Recent Searches -->
|
||||||
<div v-if="showRecentSearches && recentSearches.length > 0 && !searchQuery && isFocused" class="mt-2 p-3 border rounded-lg bg-muted/50">
|
<div v-if="showRecentSearches && recentSearches.length > 0" class="p-3">
|
||||||
<div class="flex items-center justify-between mb-2">
|
<div class="flex items-center justify-between mb-2">
|
||||||
<div class="text-sm font-medium">Recent searches:</div>
|
<div class="text-sm font-medium text-foreground">Recent searches:</div>
|
||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="sm"
|
size="sm"
|
||||||
@click="clearRecentSearches"
|
@click="clearRecentSearches"
|
||||||
class="h-5 px-1 text-xs"
|
class="h-5 px-1 text-xs hover:bg-destructive hover:text-destructive-foreground"
|
||||||
>
|
>
|
||||||
Clear
|
Clear
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-wrap gap-1">
|
<div class="flex flex-wrap gap-1">
|
||||||
<Button
|
<Button
|
||||||
v-for="recent in recentSearches.slice(0, 5)"
|
v-for="recent in recentSearches.slice(0, 5)"
|
||||||
:key="recent"
|
:key="recent"
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="sm"
|
size="sm"
|
||||||
@click="applyRecentSearch(recent)"
|
@click="applyRecentSearch(recent)"
|
||||||
class="h-6 px-2 text-xs"
|
class="h-6 px-2 text-xs hover:bg-accent hover:text-accent-foreground"
|
||||||
>
|
>
|
||||||
<History class="w-3 h-3 mr-1" />
|
<History class="w-3 h-3 mr-1" />
|
||||||
{{ recent }}
|
{{ recent }}
|
||||||
</Button>
|
</Button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -267,7 +275,14 @@ const handleClear = () => {
|
||||||
emit('search', '')
|
emit('search', '')
|
||||||
emit('results', filteredItems.value)
|
emit('results', filteredItems.value)
|
||||||
emit('clear')
|
emit('clear')
|
||||||
searchInputRef.value?.focus()
|
|
||||||
|
// Focus the input after clearing
|
||||||
|
if (searchInputRef.value) {
|
||||||
|
const inputElement = searchInputRef.value.$el?.querySelector('input') || searchInputRef.value.$el
|
||||||
|
if (inputElement && typeof inputElement.focus === 'function') {
|
||||||
|
inputElement.focus()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleKeydown = (event: KeyboardEvent) => {
|
const handleKeydown = (event: KeyboardEvent) => {
|
||||||
|
|
@ -275,8 +290,11 @@ const handleKeydown = (event: KeyboardEvent) => {
|
||||||
if (event.key === 'Escape') {
|
if (event.key === 'Escape') {
|
||||||
if (searchQuery.value) {
|
if (searchQuery.value) {
|
||||||
handleClear()
|
handleClear()
|
||||||
} else {
|
} else if (searchInputRef.value) {
|
||||||
searchInputRef.value?.blur()
|
const inputElement = searchInputRef.value.$el?.querySelector('input') || searchInputRef.value.$el
|
||||||
|
if (inputElement && typeof inputElement.blur === 'function') {
|
||||||
|
inputElement.blur()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
}
|
}
|
||||||
|
|
@ -323,7 +341,14 @@ const handleGlobalKeydown = (event: KeyboardEvent) => {
|
||||||
// ⌘K or Ctrl+K to focus search
|
// ⌘K or Ctrl+K to focus search
|
||||||
if ((event.metaKey || event.ctrlKey) && event.key === 'k') {
|
if ((event.metaKey || event.ctrlKey) && event.key === 'k') {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
searchInputRef.value?.focus()
|
console.log('⌘K/Ctrl+K pressed, focusing search input', !!searchInputRef.value)
|
||||||
|
if (searchInputRef.value) {
|
||||||
|
// Access the underlying HTML input element from the Shadcn Input component
|
||||||
|
const inputElement = searchInputRef.value.$el?.querySelector('input') || searchInputRef.value.$el
|
||||||
|
if (inputElement && typeof inputElement.focus === 'function') {
|
||||||
|
inputElement.focus()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -335,20 +360,10 @@ watch(filteredItems, (items) => {
|
||||||
// Setup and cleanup
|
// Setup and cleanup
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
document.addEventListener('keydown', handleGlobalKeydown)
|
document.addEventListener('keydown', handleGlobalKeydown)
|
||||||
|
|
||||||
if (searchInputRef.value) {
|
|
||||||
searchInputRef.value.addEventListener('focus', handleFocus)
|
|
||||||
searchInputRef.value.addEventListener('blur', handleBlur)
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
document.removeEventListener('keydown', handleGlobalKeydown)
|
document.removeEventListener('keydown', handleGlobalKeydown)
|
||||||
|
|
||||||
if (searchInputRef.value) {
|
|
||||||
searchInputRef.value.removeEventListener('focus', handleFocus)
|
|
||||||
searchInputRef.value.removeEventListener('blur', handleBlur)
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue