SearchBox.svelte 2.43 KB
<script>
  import { onMount } from 'svelte';
  import { goto } from '$app/navigation';
  import SearchResults from './SearchResults.svelte';
  import {
    query,
    results,
    isLoading,
    indexLoaded,
    error,
    selectedIndex,
    showResults,
    initIndex,
    performSearch,
    clearSearch,
    navigateResults
  } from '$lib/stores/searchStore';

  let inputRef;
  let debounceTimer;
  let isFocused = $state(false);

  onMount(() => {
    initIndex();
  });

  function handleInput(e) {
    const value = e.target.value;
    clearTimeout(debounceTimer);
    debounceTimer = setTimeout(() => {
      performSearch(value);
    }, 250);
  }

  function handleKeydown(e) {
    if (!$showResults) return;

    switch (e.key) {
      case 'ArrowDown':
        e.preventDefault();
        navigateResults('down', $results.length);
        break;
      case 'ArrowUp':
        e.preventDefault();
        navigateResults('up', $results.length);
        break;
      case 'Enter':
        e.preventDefault();
        if ($selectedIndex >= 0 && $results[$selectedIndex]) {
          const item = $results[$selectedIndex];
          if (item.tipo === 'entidad') {
            goto(`/entidad/${item.codigo}`);
          } else if (item.tipo === 'objeto_gasto') {
            goto(`/objeto/${item.codigo}`);
          } else if (item.tipo === 'rubro') {
            goto(`/rubro/${item.codigo}`);
          }
          clearSearch();
          inputRef.value = '';
        }
        break;
      case 'Escape':
        clearSearch();
        inputRef.value = '';
        inputRef.blur();
        break;
    }
  }

  function handleClickOutside(e) {
    if (!e.target.closest('.search-container')) {
      isFocused = false;
    }
  }
</script>

<svelte:window onclick={handleClickOutside} />

<div class="search-container relative">
  <input
    bind:this={inputRef}
    type="text"
    placeholder={$indexLoaded ? "Buscar entidad, objeto de gasto o rubro..." : "Cargando..."}
    disabled={!$indexLoaded}
    class="w-full p-2 border rounded"
    oninput={handleInput}
    onkeydown={handleKeydown}
    onfocus={() => isFocused = true}
  />

  {#if $isLoading}
    <span class="absolute right-2 top-2 text-sm text-gray-400">...</span>
  {/if}

  {#if isFocused && $showResults}
    <SearchResults
      results={$results}
      query={$query}
      selectedIndex={$selectedIndex}
    />
  {/if}

  {#if $error}
    <p class="text-red-500 text-sm mt-1">{$error}</p>
  {/if}
</div>