mirror of
https://github.com/pacnpal/thrillwiki_laravel.git
synced 2025-12-20 09:51:10 -05:00
feat: implement autocomplete functionality for park search with keyboard navigation
This commit is contained in:
104
resources/views/livewire/autocomplete.blade.php
Normal file
104
resources/views/livewire/autocomplete.blade.php
Normal file
@@ -0,0 +1,104 @@
|
||||
<div
|
||||
x-data="{
|
||||
open: false,
|
||||
selected: null,
|
||||
selectedIndex: -1,
|
||||
|
||||
init() {
|
||||
this.$watch('open', value => {
|
||||
if (value === false) {
|
||||
this.selectedIndex = -1;
|
||||
}
|
||||
});
|
||||
|
||||
this.$watch('selectedIndex', value => {
|
||||
if (!this.open) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (value === -1) {
|
||||
this.selected = null;
|
||||
return;
|
||||
}
|
||||
|
||||
this.selected = this.$refs.results.children[value];
|
||||
});
|
||||
},
|
||||
|
||||
onKeyDown($event) {
|
||||
if (!this.open) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch ($event.key) {
|
||||
case 'ArrowDown':
|
||||
$event.preventDefault();
|
||||
if (this.selectedIndex === -1) {
|
||||
this.selectedIndex = 0;
|
||||
return;
|
||||
}
|
||||
if (this.selectedIndex === this.$refs.results.children.length - 1) {
|
||||
return;
|
||||
}
|
||||
this.selectedIndex++;
|
||||
break;
|
||||
case 'ArrowUp':
|
||||
$event.preventDefault();
|
||||
if (this.selectedIndex === -1 || this.selectedIndex === 0) {
|
||||
return;
|
||||
}
|
||||
this.selectedIndex--;
|
||||
break;
|
||||
case 'Enter':
|
||||
$event.preventDefault();
|
||||
if (this.selectedIndex === -1) {
|
||||
return;
|
||||
}
|
||||
this.selected = this.$refs.results.children[this.selectedIndex];
|
||||
window.location.href = this.selected.dataset.url;
|
||||
break;
|
||||
case 'Escape':
|
||||
this.open = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}"
|
||||
class="relative"
|
||||
@click.away="open = false"
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
wire:model.live.debounce.300ms="query"
|
||||
@focus="open = true"
|
||||
@keydown="onKeyDown($event)"
|
||||
placeholder="Search..."
|
||||
class="w-full px-4 py-2 border rounded-lg shadow-sm focus:ring-2 focus:ring-blue-500 focus:border-transparent dark:bg-gray-800 dark:border-gray-700 dark:text-white"
|
||||
>
|
||||
|
||||
<div
|
||||
x-show="open"
|
||||
x-ref="results"
|
||||
class="absolute z-50 w-full mt-1 bg-white rounded-md shadow-lg dark:bg-gray-800"
|
||||
x-cloak
|
||||
>
|
||||
@if(count($suggestions) > 0)
|
||||
@foreach($suggestions as $suggestion)
|
||||
<a
|
||||
href="{{ $suggestion['url'] }}"
|
||||
class="block px-4 py-2 text-sm hover:bg-gray-100 dark:hover:bg-gray-700 dark:text-gray-200"
|
||||
:class="{ 'bg-gray-100 dark:bg-gray-700': selectedIndex === {{ $loop->index }} }"
|
||||
data-url="{{ $suggestion['url'] }}"
|
||||
wire:key="{{ $suggestion['id'] }}"
|
||||
>
|
||||
{{ $suggestion['text'] }}
|
||||
</a>
|
||||
@endforeach
|
||||
@else
|
||||
@if(strlen($query) >= 2)
|
||||
<div class="px-4 py-2 text-sm text-gray-500 dark:text-gray-400">
|
||||
No results found
|
||||
</div>
|
||||
@endif
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
@@ -5,17 +5,16 @@
|
||||
<div class="bg-white dark:bg-gray-800 p-6 rounded-lg shadow">
|
||||
<h2 class="text-xl font-bold mb-4 dark:text-white">Filter Parks</h2>
|
||||
<div class="space-y-4">
|
||||
<!-- Search -->
|
||||
<!-- Search with Autocomplete -->
|
||||
<div class="flex flex-col">
|
||||
<label for="search" class="text-sm font-medium text-gray-700 dark:text-gray-300">
|
||||
<label class="text-sm font-medium text-gray-700 dark:text-gray-300">
|
||||
Search
|
||||
</label>
|
||||
<div class="mt-1">
|
||||
<input type="text"
|
||||
wire:model.live="search"
|
||||
id="search"
|
||||
class="w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:text-white"
|
||||
placeholder="Search parks...">
|
||||
<livewire:autocomplete-component
|
||||
type="park"
|
||||
wire:model="search"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user