Select Menus
Source FreeCustom styled select dropdowns with multi-select support.
src/components/mtverse/ui-elements/index.tsx
tsx
1'use client';
2import { useState, useEffect, type ReactNode } from 'react';
3import Link from 'next/link';
4import { X, Settings, Users, Check, Code2, ChevronDown } from 'lucide-react';
5
6function SectionCard({ title, sourceSlug, children }: { title: string; sourceSlug?: string; children: ReactNode }) {
7 return (
8 <div>
9 <div className="mb-3 flex flex-wrap items-center justify-between gap-2">
10 <h3 className="text-theme-xl font-semibold text-gray-800 dark:text-white/90">{title}</h3>
11 {sourceSlug && (
12 <Link
13 href={sourceSlug}
14 className="inline-flex items-center gap-1.5 rounded-lg px-3 py-1.5 text-xs font-medium text-brand-500 transition-colors hover:bg-brand-50 dark:text-brand-400 dark:hover:bg-brand-500/10"
15 >
16 <Code2 className="size-3.5" />
17 View Source
18 </Link>
19 )}
20 </div>
21 <div className="rounded-xl border border-gray-200 bg-white p-6 dark:border-white/5 dark:bg-gray-dark">
22 {children}
23 </div>
24 </div>
25 );
26}
27
28export function SelectMenusSection() {
29 const [selected, setSelected] = useState('');
30 const [simpleSelected, setSimpleSelected] = useState('Analytics workspace');
31 const [simpleOpen, setSimpleOpen] = useState(false);
32 const [open, setOpen] = useState(false);
33 const options = ['Dashboard', 'Analytics', 'Reports', 'Settings', 'Users'];
34 const [multiSelected, setMultiSelected] = useState<string[]>(['Dashboard', 'Reports']);
35
36 useEffect(() => {
37 const handler = () => { setOpen(false); setSimpleOpen(false); };
38 if (open || simpleOpen) document.addEventListener('click', handler);
39 return () => document.removeEventListener('click', handler);
40 }, [open, simpleOpen]);
41
42 return (
43 <SectionCard title="Select Menus" sourceSlug="/ui/source/ui-elements/select-menus">
44 <div className="grid grid-cols-1 md:grid-cols-2 gap-4 max-w-2xl">`r`n {/* Single Select */}
45 <div>
46 <label className="mb-1.5 block text-sm font-medium text-gray-700 dark:text-gray-300">Single Select</label>
47 <div className="relative">
48 <button onClick={(e) => { e.stopPropagation(); setSimpleOpen(!simpleOpen); setOpen(false); }} className="flex w-full items-center justify-between rounded-xl border border-gray-300 bg-white px-4 py-2.5 text-sm text-gray-800 dark:border-white/10 dark:bg-gray-800 dark:text-white/90 transition-colors">
49 {simpleSelected}
50 <ChevronDown className={`size-4 text-gray-400 transition-transform ${simpleOpen ? 'rotate-180' : ''}`} />
51 </button>
52 {simpleOpen && (
53 <div className="absolute left-0 right-0 top-full z-50 mt-1 rounded-xl border border-gray-200 bg-white py-1 shadow-theme-lg dark:border-white/5 dark:bg-gray-800">
54 {options.map((opt) => (
55 <button key={opt} onClick={(e) => { e.stopPropagation(); setSimpleSelected(opt); setSimpleOpen(false); }} className={`flex w-full items-center justify-between px-4 py-2 text-sm transition-colors ${simpleSelected === opt ? 'bg-brand-50 text-brand-600 dark:bg-brand-500/10 dark:text-brand-400' : 'text-gray-700 hover:bg-gray-50 dark:text-gray-300 dark:hover:bg-white/5'}`}>
56 {opt}
57 {simpleSelected === opt && <Check className="size-4" />}
58 </button>
59 ))}
60 </div>
61 )}
62 </div>
63 </div>
64 {/* Custom Select */}
65 <div>
66 <label className="mb-1.5 block text-sm font-medium text-gray-700 dark:text-gray-300">Custom Select</label>
67 <div className="relative">
68 <button onClick={(e) => { e.stopPropagation(); setOpen(!open); }} className="flex w-full items-center justify-between rounded-xl border border-gray-300 bg-white px-4 py-2.5 text-sm text-gray-800 dark:border-white/10 dark:bg-gray-800 dark:text-white/90 transition-colors">
69 {selected || 'Choose an option'}
70 <ChevronDown className={`size-4 text-gray-400 transition-transform ${open ? 'rotate-180' : ''}`} />
71 </button>
72 {open && (
73 <div className="absolute left-0 right-0 top-full z-50 mt-1 rounded-xl border border-gray-200 bg-white py-1 shadow-theme-lg dark:border-white/5 dark:bg-gray-800">
74 {options.map((opt) => (
75 <button key={opt} onClick={() => { setSelected(opt); setOpen(false); }} className={`flex w-full items-center px-4 py-2 text-sm transition-colors ${selected === opt ? 'bg-brand-50 text-brand-600 dark:bg-brand-500/10 dark:text-brand-400' : 'text-gray-700 hover:bg-gray-50 dark:text-gray-300 dark:hover:bg-white/5'}`}>
76 {opt}
77 </button>
78 ))}
79 </div>
80 )}
81 </div>
82 </div>
83 {/* Multi-Select Tags */}
84 <div className="md:col-span-2">
85 <label className="mb-1.5 block text-sm font-medium text-gray-700 dark:text-gray-300">Multi-Select</label>
86 <div className="flex flex-wrap items-center gap-2 rounded-xl border border-gray-300 bg-white px-3 py-2 dark:border-white/10 dark:bg-gray-800">
87 {multiSelected.map((item) => (
88 <span key={item} className="inline-flex items-center gap-1 rounded-lg bg-brand-50 px-2.5 py-1 text-xs font-medium text-brand-600 dark:bg-brand-500/15 dark:text-brand-400">
89 {item}
90 <button onClick={() => setMultiSelected((p) => p.filter((x) => x !== item))} className="hover:text-brand-800 dark:hover:text-brand-300"><X className="size-3" /></button>
91 </span>
92 ))}
93 <input placeholder="Add..." className="flex-1 min-w-[80px] border-0 bg-transparent py-1 text-sm text-gray-800 placeholder-gray-400 outline-none dark:text-white/90 dark:placeholder-gray-500" onKeyDown={(e) => { if (e.key === 'Enter') { const v = e.currentTarget.value.trim(); if (v && !multiSelected.includes(v)) { setMultiSelected((p) => [...p, v]); e.currentTarget.value = ''; } e.preventDefault(); } }} />
94 </div>
95 </div>
96 </div>
97 </SectionCard>
98 );
99}More ui elements Components
ButtonsAlertsBadgesCardsDropdownsModalsTabsAccordionsTooltipsProgressSpinnersSkeletonsAvatarsPaginationPopoversToastsTimelinesTypographyBreadcrumbEmpty StatesTag InputCode BlockDividersChipsSwitchesRadio GroupsCheckboxesText InputsTextareasSelect MenusRange SlidersFile UploadColor SwatchesIcon ShowcaseData TagsNotification BadgeStatus IndicatorCountdown TimerGradient TextAnimated UnderlineKeyboard KeysMetric CardsComparison ToggleScroll IndicatorResizable PanelCollapsible SectionsDrag Handle ListTabs with IconsVertical NavBreadcrumb with Dropdown