componente · @solu30/ui-kit/data-display
SearchInput
Campo de búsqueda con lupa y botón de limpiar, con demora opcional al avisar.
84 sitios de import en 8 de 15 verticales — un archivo que la importa cuenta una vez, monte una instancia o veinte.
- TheCharterPanel
- carlosmartinpavon
- cmp-packages
- dietaly
- elliot
- gestorsalon
- luigi
- raquetea
El componente, montado
Cargando el espécimen…
export function BuscadoresMatrix() {
const [search, setSearch] = useState('')
const [debounced, setDebounced] = useState('')
const [consultas, setConsultas] = useState(0)
const locales = RESERVAS_BUSCABLES.filter((r) => r.toLowerCase().includes(search.trim().toLowerCase()))
const remotas = RESERVAS_BUSCABLES.filter((r) => r.toLowerCase().includes(debounced.trim().toLowerCase()))
return (
<Stage label="SearchInput y DebouncedSearchInput · las dos líneas que escribe GestorSalon" tall wide>
<div className="grid w-full max-w-[620px] grid-cols-1 gap-5 px-1 py-1 sm:grid-cols-2">
<div className="flex flex-col gap-2">
<span className="text-xs text-content/50">
SearchInput · filtrado local, `debounce={'{'}0{'}'}` — la barra de la pantalla de servicios
</span>
<SearchInput
value={search}
onChange={setSearch}
placeholder="Buscar"
debounce={0}
rootClassName="w-full sm:w-60"
/>
<ul className="flex flex-col gap-1">
{locales.map((r) => (
<li key={r} className="rounded-md border border-content/10 bg-container px-2.5 py-1.5 text-[11px] text-content/70">
{r}
</li>
))}
{locales.length === 0 && <li className="px-2.5 py-1.5 text-[11px] text-content/60">Sin coincidencias.</li>}
</ul>
</div>
<div className="flex flex-col gap-2">
<span className="text-xs text-content/50">
DebouncedSearchInput · el buscador de contactos del diálogo de plantillas
</span>
<DebouncedSearchInput
value={debounced}
onDebouncedChange={(next) => {
setDebounced(next)
setConsultas((n) => n + 1)
}}
placeholder="Buscar por nombre o teléfono..."
isGlobalSearchTarget={false}
/>
<ul className="flex flex-col gap-1">
{remotas.map((r) => (
<li key={r} className="rounded-md border border-content/10 bg-container px-2.5 py-1.5 text-[11px] text-content/70">
{r}
</li>
))}
{remotas.length === 0 && <li className="px-2.5 py-1.5 text-[11px] text-content/60">Sin coincidencias.</li>}
</ul>
<span className="text-[11px] text-content/60">
Avisó {consultas} {consultas === 1 ? 'vez' : 'veces'} — una por pausa, no una por tecla.
Eso es lo que se ahorra el backend.
</span>
</div>
</div>
</Stage>
)
}De specimens/data-display.tsx · BuscadoresMatrix: el espécimen del Muestrario, no un ejemplo oficial del paquete. Muestra cómo se usa la pieza de verdad, montada contra los tipos instalados —eso es lo que vale— pero no es documentación del kit.
Cómo se usa
import { SearchInput } from '@solu30/ui-kit/data-display'Props
| Prop | Requerida | Tipo | Qué hace |
|---|---|---|---|
| value | sí | string | — |
| onChange | sí | (value: string) => void | — |
| placeholder | opcional | string | — |
| debounce | opcional | number | — |
| autoFocus | opcional | boolean | — |
| className | opcional | string | Clases para el `<input>`. OJO — es el input, NO el contenedor: el elemento que participa del layout del padre es el `<div>` raíz, y a ese hay que hablarle con `rootClassName`. Un `flex-1` o un `min-w-0` acá no hacen nada, porque el input ya es `w-full` dentro de una raíz que nunca los recibió. (Medido en gestorsalon el 2026-08-18: 12 call sites pasando layout por acá; ninguno llegaba al flex item.) |
| rootClassName | opcional | string | Clases para el `<div>` raíz — ancho, `flex-1`, `min-w-0`, todo lo que define cómo el buscador se comporta como hijo de su contenedor. La raíz también es el bloque contenedor de la lupa y de la X de limpiar (las dos son `absolute` contra ella): si el input queda más angosto que la raíz, la X deja de alinearse con el borde del campo. |
Ejemplo del paquete
// `onChange` recibe el STRING, no el evento. `debounce` retrasa el aviso, no el tipeo.
<SearchInput value={q} onChange={setQ} debounce={300} placeholder="Buscar cliente…" />Del @example del JSDoc de @solu30/ui-kit, sin editar: son 65 de los 541 símbolos. La sangría y los renglones en blanco son los del paquete.