From dcab29735504e685a03f355f41df72ede986d456 Mon Sep 17 00:00:00 2001 From: Miguel Ortega Date: Sun, 13 Sep 2026 22:18:09 -0500 Subject: [PATCH] refactor(ui): migrar componente Dropdown a HeroUI --- src/components/ui/Dropdown.tsx | 288 ++++++++++++++++++--------------- 1 file changed, 162 insertions(+), 126 deletions(-) diff --git a/src/components/ui/Dropdown.tsx b/src/components/ui/Dropdown.tsx index 6b06707..f71a7f2 100644 --- a/src/components/ui/Dropdown.tsx +++ b/src/components/ui/Dropdown.tsx @@ -1,6 +1,7 @@ "use client"; import React, { useState, useRef, useEffect } from "react"; import { FiCheck, FiChevronDown, FiSearch, FiX } from "react-icons/fi"; +import { Popover as HeroUIPopover } from "@heroui/react"; import { useTheme } from "../../context/ThemeContext"; export interface DropdownOption { @@ -27,7 +28,6 @@ export const Dropdown: React.FC = ({ const { isDark } = useTheme(); const [open, setOpen] = useState(false); const [search, setSearch] = useState(""); - const ref = useRef(null); const searchRef = useRef(null); const filtered = options.filter((o) => @@ -35,24 +35,18 @@ export const Dropdown: React.FC = ({ ); useEffect(() => { - const handler = (e: MouseEvent) => { - if (ref.current && !ref.current.contains(e.target as Node)) { - setOpen(false); - setSearch(""); - } - }; - document.addEventListener("mousedown", handler); - return () => document.removeEventListener("mousedown", handler); - }, []); - - useEffect(() => { - if (open) setTimeout(() => searchRef.current?.focus(), 50); + if (!open) { + setSearch(""); + return; + } + const timer = setTimeout(() => searchRef.current?.focus(), 50); + return () => clearTimeout(timer); }, [open]); const toggle = (val: string) => onChange(value.includes(val) ? value.filter((v) => v !== val) : [...value, val]); - const removeTag = (val: string, e: React.MouseEvent) => { + const removeTag = (val: string, e: React.MouseEvent) => { e.stopPropagation(); onChange(value.filter((v) => v !== val)); }; @@ -71,128 +65,170 @@ export const Dropdown: React.FC = ({ const divider = isDark ? "bg-slate-700" : "bg-slate-100"; return ( -
- - {/* Trigger */} -
setOpen((p) => !p)} - className={`flex items-center gap-2 min-h-[46px] w-full px-4 py-2 cursor-pointer border transition-colors duration-150 ${bg} ${borderColor} ${hoverBorder} ${ - open ? "rounded-t-xl border-b-0" : "rounded-xl" - }`} - > -
- {value.length === 0 ? ( - - {placeholder} - - ) : ( - <> - {value.slice(0, 1).map((v) => ( - - {labelOf(v)} - - - ))} - {value.length > 1 && ( - - +{value.length - 2} más +
+ + +
- - {/* Menu — absolute, sin border-top para no doblar la línea */} - {open && ( -
- - {/* Search */} -
-
- - setSearch(e.target.value)} - placeholder="Buscar..." - className={`flex-1 text-sm bg-transparent outline-none ${ - isDark ? "text-slate-200 placeholder:text-slate-600" : "text-slate-700 placeholder:text-slate-400" - }`} +
+ - {search && ( - - )} -
+ + -
+ + +
+ +
+ + setSearch(e.target.value)} + placeholder="Buscar..." + className={`flex-1 text-sm bg-transparent outline-none ${ + isDark ? "text-slate-200 placeholder:text-slate-600" : "text-slate-700 placeholder:text-slate-400" + }`} + /> + {search && ( + + )} +
- {/* Options */} -
- {filtered.length === 0 ? ( -

- Sin resultados -

- ) : ( - filtered.map((opt) => { - const isActive = value.includes(opt.value); - return ( +
+ +
+ {filtered.length === 0 ? ( +

+ Sin resultados +

+ ) : ( + filtered.map((opt) => { + const isActive = value.includes(opt.value); + return ( + + ); + }) + )} +
+ + {value.length > 0 && ( + <> +
+
- ); - }) +
+ )} -
- - {value.length > 0 && ( - <> -
-
- -
- - )} -
- )} + + +
); -}; +}; \ No newline at end of file