Filtro funcionando primera versión

This commit is contained in:
2025-12-25 18:52:37 -06:00
parent 2f4f445ff0
commit d0b80b77f5
14 changed files with 1007 additions and 134 deletions

View File

@@ -2,28 +2,38 @@ import { SearchIcon } from 'lucide-react'
import { useId } from 'react'
import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
const InputSearchIconDemo = () => {
type Props = {
value: string
onChange: (value: string) => void
placeholder?: string
className?: string
}
const BarraBusqueda: React.FC<Props> = ({
value,
onChange,
placeholder = 'Buscar…',
className,
}) => {
const id = useId()
return (
<div className="w-full max-w-xs space-y-2">
<Label htmlFor={id}>Search input with icon and button</Label>
<div className="relative">
<div className="text-muted-foreground pointer-events-none absolute inset-y-0 left-0 flex items-center justify-center pl-3 peer-disabled:opacity-50">
<SearchIcon className="size-4" />
<span className="sr-only">Search</span>
</div>
<Input
id={id}
type="search"
placeholder="Search..."
className="peer px-9 [&::-webkit-search-cancel-button]:appearance-none [&::-webkit-search-decoration]:appearance-none [&::-webkit-search-results-button]:appearance-none [&::-webkit-search-results-decoration]:appearance-none"
/>
<div className={['relative', className].filter(Boolean).join(' ')}>
<div className="text-muted-foreground pointer-events-none absolute inset-y-0 left-0 flex items-center justify-center pl-3 peer-disabled:opacity-50">
<SearchIcon className="size-4" />
<span className="sr-only">Buscar</span>
</div>
<Input
id={id}
type="search"
value={value}
onChange={(e) => onChange(e.target.value)}
placeholder={placeholder}
className="peer px-9 [&::-webkit-search-cancel-button]:appearance-none [&::-webkit-search-decoration]:appearance-none [&::-webkit-search-results-button]:appearance-none [&::-webkit-search-results-decoration]:appearance-none"
/>
</div>
)
}
export default InputSearchIconDemo
export default BarraBusqueda

View File

@@ -0,0 +1,93 @@
'use client'
import { CheckIcon, ChevronDown } from 'lucide-react'
import { useState } from 'react'
import { Button } from '@/components/ui/button'
import {
Command,
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
CommandList,
} from '@/components/ui/command'
import {
Popover,
PopoverContent,
PopoverTrigger,
} from '@/components/ui/popover'
import { cn } from '@/lib/utils'
export type Option = { value: string; label: string }
type Props = {
options: Array<Option>
value: string | null
onChange: (value: string) => void
placeholder?: string
className?: string
ariaLabel?: string
}
const Filtro: React.FC<Props> = ({
options,
value,
onChange,
placeholder = 'Seleccionar…',
className,
ariaLabel,
}) => {
const [open, setOpen] = useState(false)
const label = value
? (options.find((o) => o.value === value)?.label ?? placeholder)
: placeholder
return (
<Popover open={open} onOpenChange={setOpen}>
<PopoverTrigger asChild>
<Button
variant="outline"
role="combobox"
aria-expanded={open}
className={cn('w-full justify-between', className)}
aria-label={ariaLabel ?? 'Filtro combobox'}
>
{label}
<ChevronDown className="opacity-50" />
</Button>
</PopoverTrigger>
<PopoverContent className="p-0">
<Command>
<CommandInput placeholder="Buscar…" className="h-9" />
<CommandList>
<CommandEmpty>Sin resultados.</CommandEmpty>
<CommandGroup>
{options.map((opt) => (
<CommandItem
key={opt.value}
value={opt.value}
onSelect={(currentValue) => {
onChange(currentValue)
setOpen(false)
}}
>
{opt.label}
<CheckIcon
className={cn(
'ml-auto',
value === opt.value ? 'opacity-100' : 'opacity-0',
)}
/>
</CommandItem>
))}
</CommandGroup>
</CommandList>
</Command>
</PopoverContent>
</Popover>
)
}
export default Filtro