UNPKG

nextuiq

Version:

NextUIQ is a modern, lightweight, and developer-friendly UI component library for React and Next.js. Built with TypeScript and Tailwind CSS, it offers customizable, accessible, and performance-optimized components with built-in dark mode, theme customizat

140 lines (137 loc) 4.78 kB
import { j as jsxRuntimeExports } from './index46.mjs'; import { useState, useRef, useId, useEffect } from 'react'; import { cn } from './index38.mjs'; const Dropdown = ({ trigger, items = [], align = "end", className, onSelect, isOpen: controlledIsOpen, onClose, children, id, role = "menu", "aria-modal": ariaModal, "aria-label": ariaLabel }) => { const [internalIsOpen, setInternalIsOpen] = useState(false); const [activeIndex, setActiveIndex] = useState(-1); const dropdownRef = useRef(null); const menuId = useId(); const isOpen = controlledIsOpen ?? internalIsOpen; const setIsOpen = (value) => { setInternalIsOpen(value); if (!value && onClose) { onClose(); } }; const handleItemClick = (item) => { if (!item.disabled) { item.onClick?.(); onSelect?.(item.value); setIsOpen(false); setActiveIndex(-1); } }; useEffect(() => { const handleClickOutside = (event) => { if (dropdownRef.current && !dropdownRef.current.contains(event.target)) { setIsOpen(false); } }; const handleKeyDown = (event) => { if (!isOpen) return; switch (event.key) { case "ArrowDown": event.preventDefault(); setActiveIndex((prev) => prev < items.length - 1 ? prev + 1 : 0); break; case "ArrowUp": event.preventDefault(); setActiveIndex((prev) => prev > 0 ? prev - 1 : items.length - 1); break; case "Enter": case "Space": event.preventDefault(); if (activeIndex >= 0) { handleItemClick(items[activeIndex]); } break; case "Escape": event.preventDefault(); setIsOpen(false); break; } }; document.addEventListener("mousedown", handleClickOutside); document.addEventListener("keydown", handleKeyDown); return () => { document.removeEventListener("mousedown", handleClickOutside); document.removeEventListener("keydown", handleKeyDown); }; }, [isOpen, items, activeIndex]); return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative inline-block", ref: dropdownRef, children: [ trigger && /* @__PURE__ */ jsxRuntimeExports.jsx( "div", { onClick: () => setIsOpen(!isOpen), onKeyDown: (e) => { if (e.key === "Enter" || e.key === " ") { e.preventDefault(); setIsOpen(!isOpen); } }, role: "button", tabIndex: 0, "aria-haspopup": "true", "aria-expanded": isOpen, "aria-controls": id || menuId, children: trigger } ), isOpen && /* @__PURE__ */ jsxRuntimeExports.jsx( "div", { id: id || menuId, className: cn( "absolute z-50 mt-2 min-w-[8rem] overflow-hidden rounded-md border", "border-[oklch(var(--theme-border))] bg-[oklch(var(--theme-background))]", "shadow-md animate-in fade-in-0 zoom-in-95", align === "end" ? "right-0" : "left-0", className ), role, "aria-modal": ariaModal, "aria-label": ariaLabel, children: children || /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "py-1", role: "menu", "aria-orientation": "vertical", children: items?.map((item, index) => /* @__PURE__ */ jsxRuntimeExports.jsxs( "button", { onClick: () => handleItemClick(item), onMouseEnter: () => setActiveIndex(index), onFocus: () => setActiveIndex(index), className: cn( "relative flex w-full cursor-pointer select-none items-center px-3 py-2 text-sm outline-none transition-colors", "text-[oklch(var(--theme-foreground))] hover:bg-[oklch(var(--theme-muted))] hover:text-[oklch(var(--theme-foreground))]", "focus:bg-[oklch(var(--theme-muted))] focus:text-[oklch(var(--theme-foreground))]", item.disabled && "cursor-not-allowed opacity-50", item.selected && "bg-[oklch(var(--theme-muted))] text-[oklch(var(--theme-foreground))]", activeIndex === index && "bg-[oklch(var(--theme-muted))] text-[oklch(var(--theme-foreground))]" ), disabled: item.disabled, role: "menuitem", tabIndex: -1, "aria-selected": item.selected, "aria-disabled": item.disabled, children: [ item.icon && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "mr-2 h-4 w-4", "aria-hidden": "true", children: item.icon }), item.label ] }, index )) }) } ) ] }); }; export { Dropdown };