108 lines
5.5 KiB
PHP
108 lines
5.5 KiB
PHP
<div class="relative" wire:ignore.self>
|
|||
|
|
<div class="relative w-full max-w-xl">
|
||
|
|
<!-- Search Input -->
|
||
|
|
<div class="relative">
|
||
|
|
<label for="global-search" class="sr-only">{{ __('Buscar globalmente') }}</label>
|
||
|
|
<div class="relative">
|
||
|
|
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
|
||
|
|
<svg class="h-5 w-5 text-base-content/40" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
|
||
|
|
</svg>
|
||
|
|
</div>
|
||
|
|
<input
|
||
|
|
type="text"
|
||
|
|
id="global-search"
|
||
|
|
wire:model.debounce.300ms="query"
|
||
|
|
wire:keydown.down="focusFirstResult"
|
||
|
|
wire:keydown.enter="selectFirstResult"
|
||
|
|
wire:keydown.escape="clearSearch"
|
||
|
|
placeholder="{{ __('Buscar en proyectos, elementos, tareas, incidencias...') }}"
|
||
|
|
class="block w-full pl-10 pr-4 py-2 border border-base-300 rounded-lg bg-base-100 text-base-content placeholder:text-base-content/40 focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent transition-all"
|
||
|
|
autocomplete="off"
|
||
|
|
@click="showResults = true"
|
||
|
|
>
|
||
|
|
@if($query)
|
||
|
|
<button
|
||
|
|
wire:click="clearSearch"
|
||
|
|
class="absolute inset-y-0 right-0 pr-3 flex items-center"
|
||
|
|
aria-label="{{ __('Limpiar búsqueda') }}"
|
||
|
|
>
|
||
|
|
<svg class="h-5 w-5 text-base-content/40 hover:text-base-content/60" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
|
||
|
|
</svg>
|
||
|
|
</button>
|
||
|
|
@endif
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- Results Dropdown -->
|
||
|
|
@if($showResults && count($results) > 0)
|
||
|
|
<div
|
||
|
|
class="absolute z-50 mt-1 w-full max-w-xl bg-base-100 border border-base-200 rounded-lg shadow-lg overflow-hidden"
|
||
|
|
style="max-height: 400px; overflow-y: auto;"
|
||
|
|
x-data="{ highlightedIndex: 0 }"
|
||
|
|
x-init="
|
||
|
|
document.addEventListener('keydown', (e) => {
|
||
|
|
if (e.key === 'ArrowDown') {
|
||
|
|
e.preventDefault();
|
||
|
|
@this.focusFirstResult();
|
||
|
|
} else if (e.key === 'ArrowUp') {
|
||
|
|
e.preventDefault();
|
||
|
|
highlightedIndex = Math.max(0, highlightedIndex - 1);
|
||
|
|
} else if (e.key === 'Enter') {
|
||
|
|
e.preventDefault();
|
||
|
|
@this.selectFirstResult();
|
||
|
|
} else if (e.key === 'Escape') {
|
||
|
|
@this.clearSearch();
|
||
|
|
}
|
||
|
|
})
|
||
|
|
"
|
||
|
|
>
|
||
|
|
@foreach($results as $index => $result)
|
||
|
|
<a
|
||
|
|
href="{{ $result['url'] }}"
|
||
|
|
wire:navigate
|
||
|
|
class="flex items-center gap-3 px-4 py-3 hover:bg-base-200 transition-colors {{ $index === 0 ? 'bg-base-200' : '' }}"
|
||
|
|
:class="{ 'bg-base-200': highlightedIndex === {{ $index }} }"
|
||
|
|
x-on:mouseenter="highlightedIndex = {{ $index }}"
|
||
|
|
>
|
||
|
|
<div class="flex-shrink-0">
|
||
|
|
<div class="w-8 h-8 rounded-lg flex items-center justify-center bg-{{ $result['color'] }}-100 text-{{ $result['color'] }}-600">
|
||
|
|
<x-heroicon-o-{{ $result['icon'] }} class="w-5 h-5" />
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<div class="flex-1 min-w-0">
|
||
|
|
<p class="text-sm font-medium text-base-content truncate">{{ $result['title'] }}</p>
|
||
|
|
<p class="text-xs text-base-content/50 truncate">{{ $result['subtitle'] }}</p>
|
||
|
|
</div>
|
||
|
|
<span class="text-xs font-medium text-base-content/40 bg-base-200 px-2 py-0.5 rounded">{{ $result['type_label'] }}</span>
|
||
|
|
</a>
|
||
|
|
@endforeach
|
||
|
|
</div>
|
||
|
|
@elseif($showResults && strlen($query) >= 2)
|
||
|
|
<div class="absolute z-50 mt-1 w-full max-w-xl bg-base-100 border border-base-200 rounded-lg shadow-lg overflow-hidden">
|
||
|
|
<div class="px-4 py-6 text-center text-base-content/50">
|
||
|
|
<x-heroicon-o-magnifying-glass class="w-8 h-8 mx-auto text-base-content/30 mb-2" />
|
||
|
|
<p class="text-sm">{{ __('No se encontraron resultados para') }} “{{ $query }}”</p>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
@endif
|
||
|
|
</div>
|
||
|
|
|
||
|
|
@push('scripts')
|
||
|
|
<script>
|
||
|
|
document.addEventListener('click', (e) => {
|
||
|
|
const searchComponent = document.querySelector('[wire\\:id="{{ $id }}"]');
|
||
|
|
if (searchComponent && !searchComponent.contains(e.target)) {
|
||
|
|
@this.hideResults();
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
document.addEventListener('keydown', (e) => {
|
||
|
|
if (e.key === 'Escape') {
|
||
|
|
@this.clearSearch();
|
||
|
|
}
|
||
|
|
});
|
||
|
|
</script>
|
||
|
|
@endpush
|
||
|
|
</div>
|