[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-73908":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":9,"htmlUrl":10,"language":11,"languages":10,"totalLinesOfCode":10,"stars":12,"forks":13,"watchers":14,"openIssues":15,"contributorsCount":16,"subscribersCount":16,"size":16,"stars1d":16,"stars7d":17,"stars30d":18,"stars90d":16,"forks30d":16,"starsTrendScore":16,"compositeScore":19,"rankGlobal":10,"rankLanguage":10,"license":20,"archived":21,"fork":21,"defaultBranch":22,"hasWiki":23,"hasPages":21,"topics":24,"createdAt":10,"pushedAt":10,"updatedAt":34,"readmeContent":35,"aiSummary":36,"trendingCount":16,"starSnapshotCount":16,"syncStatus":37,"lastSyncTime":38,"discoverSource":39},73908,"shadcn-multi-select-component","sersavan\u002Fshadcn-multi-select-component","sersavan","A multi-select component designed with shadcn\u002Fui","https:\u002F\u002Fshadcn-multi-select-component.vercel.app",null,"TypeScript",2191,93,6,26,0,3,12,27.92,"MIT License",false,"main",true,[25,26,27,28,29,30,31,32,33],"multi-select","multi-select-container","multi-select-dropdown","multi-selection","multi-selector","nextjs","radix-ui","reactjs","shadcn-ui","2026-06-12 02:03:19","# MultiSelect Component\n\nA powerful and flexible multi-select component built with **React**,\n**TypeScript**, **Tailwind CSS**, and **shadcn\u002Fui** components.\n\n**Compatible with:** Next.js, Vite, Create React App, and any React environment\nthat supports path aliases and shadcn\u002Fui components.\n\n![Multi Select Demo](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FReact-19+-blue?logo=react)\n![TypeScript](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FTypeScript-5+-blue?logo=typescript)\n![Tailwind CSS](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FTailwind-CSS-blue?logo=tailwindcss)\n\n## Star History\n\n[![Star History Chart](https:\u002F\u002Fapi.star-history.com\u002Fsvg?repos=sersavan\u002Fshadcn-multi-select-component&type=Date)](https:\u002F\u002Fstar-history.com\u002F#sersavan\u002Fshadcn-multi-select-component&Date)\n\n## 🚀 Features\n\n- ✨ **Multiple Variants**: Default, secondary, destructive, and inverted styles\n- 🌈 **Custom Styling**: Custom badge colors, icon colors, and gradient\n  backgrounds\n- 📁 **Grouped Options**: Organize options in groups with headings and\n  separators\n- 🚫 **Disabled Options**: Mark specific options as disabled and non-selectable\n- 🎨 **Advanced Animations**: Multiple animation types (bounce, pulse, wiggle,\n  fade, slide) for badges and popovers\n- 🔍 **Search & Filter**: Built-in search functionality with keyboard navigation\n- 📊 **Dashboard Integration**: Perfect for analytics dashboards and data\n  visualization\n- 📈 **Chart Filtering**: Real-time filtering for bar, pie, area, and line\n  charts\n- 🎯 **Multi-level Filtering**: Primary and secondary filter combinations\n- 📱 **Responsive Design**: Automatic adaptation to mobile, tablet, and desktop\n  screens\n- 📐 **Width Constraints**: Support for minimum and maximum width settings\n- 📲 **Mobile-Optimized**: Compact mode with touch-friendly interactions\n- 💻 **Desktop-Enhanced**: Full feature set with large displays\n- ♿ **Accessibility**: Full keyboard support, screen reader compatibility, and\n  ARIA live regions\n- ⌨️ **Keyboard Shortcuts**: Global hotkeys for navigation and quick actions\n- 🔧 **Imperative Methods**: Programmatic control via ref (reset, clear, focus,\n  get\u002Fset values)\n- 🔄 **Duplicate Handling**: Automatic detection and removal of duplicate\n  options\n- 📝 **Form Integration**: Seamless integration with React Hook Form and\n  validation\n- 🎛️ **Customizable Behavior**: Auto-close on select, width constraints, empty\n  indicators\n- 🎯 **TypeScript Support**: Fully typed with comprehensive TypeScript support\n- 📦 **Self-Contained**: All accessibility features built-in, no external\n  dependencies required\n\n## 📦 Installation\n\n### Prerequisites\n\nThis component is compatible with any React project but requires proper setup:\n\n- **React environment**: Next.js, Vite, Create React App, or any React setup\n- **Path aliases**: Configure `@\u002F` imports in your bundler\n- **shadcn\u002Fui**: Install and configure shadcn\u002Fui components\n- **Tailwind CSS**: Setup and configure Tailwind CSS\n\n### 1. Copy the Component\n\n```bash\ncp src\u002Fcomponents\u002Fmulti-select.tsx your-project\u002Fcomponents\u002F\n```\n\n### 2. Install Dependencies\n\n```bash\nnpm install react react-dom\nnpm install @radix-ui\u002Freact-popover @radix-ui\u002Freact-separator\nnpm install lucide-react class-variance-authority clsx tailwind-merge cmdk\n```\n\n### 3. Setup shadcn\u002Fui Components\n\nInstall the required shadcn\u002Fui components:\n\n```bash\nnpx shadcn@latest add button badge popover command separator\n```\n\n### 4. Configure Path Aliases\n\n#### For Next.js\n\nAdd to your `tsconfig.json` or `jsconfig.json`:\n\n```json\n{\n\t\"compilerOptions\": {\n\t\t\"baseUrl\": \".\",\n\t\t\"paths\": {\n\t\t\t\"@\u002F*\": [\".\u002Fsrc\u002F*\"]\n\t\t}\n\t}\n}\n```\n\n#### For Vite\n\nAdd to your `vite.config.ts`:\n\n```typescript\nimport { defineConfig } from \"vite\";\nimport path from \"path\";\n\nexport default defineConfig({\n\tresolve: {\n\t\talias: {\n\t\t\t\"@\": path.resolve(__dirname, \".\u002Fsrc\"),\n\t\t},\n\t},\n});\n```\n\n#### For Webpack\u002FCreate React App\n\nYou may need to eject or use CRACO to configure path aliases.\n\n### 5. Setup Utility Function\n\nEnsure you have the `cn` utility function in `src\u002Flib\u002Futils.ts`:\n\n```typescript\nimport { type ClassValue, clsx } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\nexport function cn(...inputs: ClassValue[]) {\n\treturn twMerge(clsx(inputs));\n}\n```\n\n## 🎯 Quick Start\n\n### Basic Usage\n\n```tsx\nimport { MultiSelect } from \"@\u002Fcomponents\u002Fmulti-select\";\nimport { useState } from \"react\";\n\nconst options = [\n\t{ value: \"react\", label: \"React\" },\n\t{ value: \"vue\", label: \"Vue.js\" },\n\t{ value: \"angular\", label: \"Angular\" },\n];\n\nfunction App() {\n\tconst [selectedValues, setSelectedValues] = useState\u003Cstring[]>([]);\n\n\treturn (\n\t\t\u003CMultiSelect\n\t\t\toptions={options}\n\t\t\tonValueChange={setSelectedValues}\n\t\t\tdefaultValue={selectedValues}\n\t\t\u002F>\n\t);\n}\n```\n\n### Custom Styling\n\n```tsx\nconst styledOptions = [\n\t{\n\t\tvalue: \"react\",\n\t\tlabel: \"React\",\n\t\ticon: ReactIcon,\n\t\tstyle: {\n\t\t\tbadgeColor: \"#61DAFB\",\n\t\t\ticonColor: \"#282C34\",\n\t\t},\n\t},\n\t{\n\t\tvalue: \"vue\",\n\t\tlabel: \"Vue.js\",\n\t\ticon: VueIcon,\n\t\tstyle: {\n\t\t\tgradient: \"linear-gradient(135deg, #4FC08D 0%, #42B883 100%)\",\n\t\t},\n\t},\n];\n\n\u003CMultiSelect\n\toptions={styledOptions}\n\tonValueChange={setSelected}\n\tplaceholder=\"Select with custom styles...\"\n\u002F>;\n```\n\n### Next.js Usage\n\nFor Next.js projects, you can use the component in client components with the\n\"use client\" directive:\n\n```tsx\n\"use client\";\n\nimport { MultiSelect } from \"@\u002Fcomponents\u002Fmulti-select\";\nimport { useState } from \"react\";\n\nconst options = [\n\t{ value: \"next\", label: \"Next.js\" },\n\t{ value: \"react\", label: \"React\" },\n\t{ value: \"typescript\", label: \"TypeScript\" },\n];\n\nexport default function MyPage() {\n\tconst [selected, setSelected] = useState\u003Cstring[]>([]);\n\n\treturn (\n\t\t\u003Cdiv className=\"container mx-auto p-4\">\n\t\t\t\u003Ch1 className=\"text-2xl font-bold mb-4\">Select Technologies\u003C\u002Fh1>\n\t\t\t\u003CMultiSelect\n\t\t\t\toptions={options}\n\t\t\t\tonValueChange={setSelected}\n\t\t\t\tplaceholder=\"Select technologies...\"\n\t\t\t\tvariant=\"secondary\"\n\t\t\t\u002F>\n\t\t\u003C\u002Fdiv>\n\t);\n}\n```\n\n### Grouped Options\n\n```tsx\nconst groupedOptions = [\n\t{\n\t\theading: \"Frontend Frameworks\",\n\t\toptions: [\n\t\t\t{ value: \"react\", label: \"React\" },\n\t\t\t{ value: \"vue\", label: \"Vue.js\" },\n\t\t\t{ value: \"angular\", label: \"Angular\", disabled: true },\n\t\t],\n\t},\n\t{\n\t\theading: \"Backend Technologies\",\n\t\toptions: [\n\t\t\t{ value: \"node\", label: \"Node.js\" },\n\t\t\t{ value: \"python\", label: \"Python\" },\n\t\t],\n\t},\n];\n\n\u003CMultiSelect\n\toptions={groupedOptions}\n\tonValueChange={setSelected}\n\tplaceholder=\"Select from groups...\"\n\u002F>;\n```\n\n## 📱 Responsive Design\n\nThe Multi-Select component includes comprehensive responsive design capabilities\nthat automatically adapt to different screen sizes.\n\n### Automatic Responsive Behavior\n\nEnable responsive design with default settings:\n\n```tsx\n\u003CMultiSelect\n\toptions={options}\n\tonValueChange={setSelected}\n\tresponsive={true} \u002F\u002F Enable automatic responsive behavior\n\tplaceholder=\"Responsive component\"\n\u002F>\n```\n\n**Default responsive settings:**\n\n- **Mobile** (\u003C 640px): 2 badges max, compact mode\n- **Tablet** (640px - 1024px): 4 badges max, normal mode\n- **Desktop** (> 1024px): 6 badges max, full features\n\n### Width Constraints\n\nControl component width with responsive adaptation:\n\n```tsx\n\u003CMultiSelect\n\toptions={options}\n\tonValueChange={setSelected}\n\tresponsive={true}\n\tminWidth=\"200px\"\n\tmaxWidth=\"400px\"\n\tplaceholder=\"Constrained width\"\n\u002F>\n```\n\n## 📊 Dashboard Integration\n\nThe Multi-Select component provides powerful integration with analytics\ndashboards and data visualization libraries.\n\n### Basic Dashboard Filtering\n\n```tsx\nimport { MultiSelect } from \"@\u002Fcomponents\u002Fmulti-select\";\nimport { BarChart, Bar, XAxis, YAxis, ResponsiveContainer } from \"recharts\";\n\nconst Dashboard = () => {\n\tconst [selectedCategories, setSelectedCategories] = useState([\"2024\"]);\n\n\tconst filteredData = data.filter((item) =>\n\t\tselectedCategories.includes(item.category)\n\t);\n\n\treturn (\n\t\t\u003Cdiv className=\"space-y-4\">\n\t\t\t\u003CMultiSelect\n\t\t\t\toptions={[\n\t\t\t\t\t{ value: \"2024\", label: \"2024\", icon: CalendarIcon },\n\t\t\t\t\t{ value: \"2023\", label: \"2023\", icon: CalendarIcon },\n\t\t\t\t]}\n\t\t\t\tonValueChange={setSelectedCategories}\n\t\t\t\tdefaultValue={selectedCategories}\n\t\t\t\tplaceholder=\"Select time period\"\n\t\t\t\tresponsive={true}\n\t\t\t\u002F>\n\n\t\t\t\u003CResponsiveContainer width=\"100%\" height={300}>\n\t\t\t\t\u003CBarChart data={filteredData}>\n\t\t\t\t\t\u003CXAxis dataKey=\"name\" \u002F>\n\t\t\t\t\t\u003CYAxis \u002F>\n\t\t\t\t\t\u003CBar dataKey=\"value\" fill=\"#8884d8\" \u002F>\n\t\t\t\t\u003C\u002FBarChart>\n\t\t\t\u003C\u002FResponsiveContainer>\n\t\t\u003C\u002Fdiv>\n\t);\n};\n```\n\n### Multi-level Filtering\n\n```tsx\nconst AdvancedDashboard = () => {\n\tconst [primaryFilters, setPrimaryFilters] = useState([\"Performance\"]);\n\tconst [secondaryFilters, setSecondaryFilters] = useState([\"Speed\"]);\n\n\treturn (\n\t\t\u003Cdiv className=\"space-y-4\">\n\t\t\t\u003Cdiv className=\"grid grid-cols-2 gap-4\">\n\t\t\t\t\u003CMultiSelect\n\t\t\t\t\toptions={primaryCategories}\n\t\t\t\t\tonValueChange={setPrimaryFilters}\n\t\t\t\t\tplaceholder=\"Primary category\"\n\t\t\t\t\u002F>\n\n\t\t\t\t\u003CMultiSelect\n\t\t\t\t\toptions={secondaryCategories}\n\t\t\t\t\tonValueChange={setSecondaryFilters}\n\t\t\t\t\tplaceholder=\"Secondary filters\"\n\t\t\t\t\tvariant=\"secondary\"\n\t\t\t\t\u002F>\n\t\t\t\u003C\u002Fdiv>\n\n\t\t\t\u003CComposedChart data={filteredData}>\n\t\t\t\t{\u002F* Multiple chart types combined *\u002F}\n\t\t\t\u003C\u002FComposedChart>\n\t\t\u003C\u002Fdiv>\n\t);\n};\n```\n\n## 📖 Examples\n\n### Basic Multi-Select\n\n```tsx\nconst [selectedValues, setSelectedValues] = useState\u003Cstring[]>([]);\n\n\u003CMultiSelect\n\toptions={frameworks}\n\tonValueChange={setSelectedValues}\n\tdefaultValue={selectedValues}\n\tplaceholder=\"Select frameworks\"\n\u002F>;\n```\n\n### With Icons and Custom Styling\n\n```tsx\nconst [selectedValues, setSelectedValues] = useState\u003Cstring[]>([]);\n\n\u003CMultiSelect\n\toptions={frameworksWithIcons}\n\tonValueChange={setSelectedValues}\n\tdefaultValue={selectedValues}\n\tplaceholder=\"Select technologies\"\n\tvariant=\"inverted\"\n\tmaxCount={3}\n\tmodalPopover={true}\n\u002F>;\n```\n\n### Async Data Loading\n\n```tsx\nconst [options, setOptions] = useState\u003COption[]>([]);\nconst [loading, setLoading] = useState(true);\n\nuseEffect(() => {\n\tloadData().then((data) => {\n\t\tsetOptions(data);\n\t\tsetLoading(false);\n\t});\n}, []);\n\n\u003CMultiSelect\n\toptions={options}\n\tonValueChange={setSelectedValues}\n\tdefaultValue={selectedValues}\n\tplaceholder={loading ? \"Loading...\" : \"Select items\"}\n\tdisabled={loading}\n\u002F>;\n```\n\n## 📚 API Reference\n\n## 🎯 Best Practices\n\n1. **Always provide meaningful labels**: Use descriptive placeholders and\n   aria-labels\n2. **Handle loading states**: Show loading indicators when fetching async data\n3. **Limit display count**: Use `maxCount` for large selections to maintain UI\n   clarity\n4. **Use modal on mobile**: Set `modalPopover={true}` for better mobile\n   experience\n5. **Implement search**: For large option lists, consider adding search\n   functionality\n6. **Provide clear feedback**: Use the built-in announcements for screen readers\n\n## �📚 API Reference\n\n### Props\n\n| Prop                        | Type                                                      | Default            | Description                                 |\n| --------------------------- | --------------------------------------------------------- | ------------------ | ------------------------------------------- |\n| `options`                   | `MultiSelectOption[] \\| MultiSelectGroup[]`               | -                  | Array of selectable options or groups       |\n| `onValueChange`             | `(value: string[]) => void`                               | -                  | Callback when selection changes             |\n| `defaultValue`              | `string[]`                                                | `[]`               | Initially selected values                   |\n| `placeholder`               | `string`                                                  | `\"Select options\"` | Placeholder text                            |\n| `variant`                   | `\"default\" \\| \"secondary\" \\| \"destructive\" \\| \"inverted\"` | `\"default\"`        | Visual variant                              |\n| `animation`                 | `number`                                                  | `0`                | Legacy animation duration in seconds        |\n| `animationConfig`           | `AnimationConfig`                                         | -                  | Advanced animation configuration            |\n| `maxCount`                  | `number`                                                  | `3`                | Maximum visible selected items              |\n| `modalPopover`              | `boolean`                                                 | `false`            | Modal behavior for popover                  |\n| `asChild`                   | `boolean`                                                 | `false`            | Render as child component                   |\n| `className`                 | `string`                                                  | -                  | Additional CSS classes                      |\n| `hideSelectAll`             | `boolean`                                                 | `false`            | Hide \"Select All\" option                    |\n| `searchable`                | `boolean`                                                 | `true`             | Enable search functionality                 |\n| `emptyIndicator`            | `ReactNode`                                               | -                  | Custom empty state component                |\n| `autoSize`                  | `boolean`                                                 | `false`            | Allow component to grow\u002Fshrink with content |\n| `singleLine`                | `boolean`                                                 | `false`            | Show badges in single line with scroll      |\n| `popoverClassName`          | `string`                                                  | -                  | Custom CSS class for popover content        |\n| `disabled`                  | `boolean`                                                 | `false`            | Disable the entire component                |\n| `responsive`                | `boolean \\| ResponsiveConfig`                             | `false`            | Enable responsive behavior                  |\n| `minWidth`                  | `string`                                                  | -                  | Minimum component width                     |\n| `maxWidth`                  | `string`                                                  | -                  | Maximum component width                     |\n| `deduplicateOptions`        | `boolean`                                                 | `false`            | Automatically remove duplicate options      |\n| `resetOnDefaultValueChange` | `boolean`                                                 | `true`             | Reset state when defaultValue changes       |\n| `closeOnSelect`             | `boolean`                                                 | `false`            | Close popover after selecting an option     |\n\n### Types\n\n#### AnimationConfig\n\n```tsx\ninterface AnimationConfig {\n\tbadgeAnimation?: \"bounce\" | \"pulse\" | \"wiggle\" | \"fade\" | \"slide\" | \"none\";\n\tpopoverAnimation?: \"scale\" | \"slide\" | \"fade\" | \"flip\" | \"none\";\n\toptionHoverAnimation?: \"highlight\" | \"scale\" | \"glow\" | \"none\";\n\tduration?: number; \u002F\u002F Animation duration in seconds\n\tdelay?: number; \u002F\u002F Animation delay in seconds\n}\n```\n\n#### MultiSelectRef\n\n```tsx\ninterface MultiSelectRef {\n\treset: () => void; \u002F\u002F Reset to default value\n\tgetSelectedValues: () => string[]; \u002F\u002F Get current selection\n\tsetSelectedValues: (values: string[]) => void; \u002F\u002F Set selection programmatically\n\tclear: () => void; \u002F\u002F Clear all selections\n\tfocus: () => void; \u002F\u002F Focus the component\n}\n```\n\n#### MultiSelectOption\n\n```tsx\ninterface MultiSelectOption {\n\tlabel: string; \u002F\u002F Display text\n\tvalue: string; \u002F\u002F Unique identifier\n\ticon?: React.ComponentType\u003C{\n\t\t\u002F\u002F Optional icon component\n\t\tclassName?: string;\n\t}>;\n\tdisabled?: boolean; \u002F\u002F Whether option is disabled\n\tstyle?: {\n\t\t\u002F\u002F Custom styling\n\t\tbadgeColor?: string; \u002F\u002F Custom badge background color\n\t\ticonColor?: string; \u002F\u002F Custom icon color\n\t\tgradient?: string; \u002F\u002F Gradient background (CSS gradient)\n\t};\n}\n```\n\n#### MultiSelectGroup\n\n```tsx\ninterface MultiSelectGroup {\n\theading: string; \u002F\u002F Group heading text\n\toptions: MultiSelectOption[]; \u002F\u002F Options in this group\n}\n```\n\n## ♿ Accessibility\n\nThe MultiSelect component includes comprehensive accessibility features\nbuilt-in:\n\n### ARIA Support\n\n- **ARIA Live Regions**: Automatic announcements for screen readers when\n  selections change\n- **Role Attributes**: Proper `combobox`, `listbox`, and `option` roles\n- **ARIA Labels**: Descriptive labels for all interactive elements\n- **ARIA States**: `aria-expanded`, `aria-selected`, `aria-disabled` states\n\n### Keyboard Navigation\n\n- **Tab**: Focus component and navigate between elements\n- **Enter\u002FSpace**: Open dropdown and select options\n- **Arrow Keys**: Navigate through options\n- **Escape**: Close dropdown\n- **Backspace**: Remove last selected item when search is empty\n\n### Screen Reader Announcements\n\nThe component automatically announces:\n\n- Number of options selected\n- When dropdown opens\u002Fcloses\n- Search results count\n- Individual option selection\u002Fdeselection\n\n### Example with Enhanced Accessibility\n\n```tsx\n\u003CMultiSelect\n\toptions={options}\n\tonValueChange={setSelected}\n\tplaceholder=\"Choose frameworks (accessible)\"\n\t\u002F\u002F Accessibility features are built-in and automatic\n\tsearchable={true}\n\taria-label=\"Framework selection\"\n\u002F>\n```\n\n### Manual Accessibility Testing\n\n```tsx\n\u002F\u002F The component provides imperative methods for testing\nconst multiSelectRef = useRef\u003CMultiSelectRef>(null);\n\n\u002F\u002F Programmatic focus for testing\nmultiSelectRef.current?.focus();\n\n\u002F\u002F Check current selection\nconst currentValues = multiSelectRef.current?.getSelectedValues();\n```\n\n## 🎨 Styling Examples\n\n### Custom Colors\n\n```tsx\n\u002F\u002F Single color badge with custom icon color\n{\n  value: \"react\",\n  label: \"React\",\n  style: {\n    badgeColor: \"#61DAFB\",\n    iconColor: \"#282C34\"\n  }\n}\n\n\u002F\u002F Gradient badge (icon will be white by default)\n{\n  value: \"vue\",\n  label: \"Vue.js\",\n  style: {\n    gradient: \"linear-gradient(135deg, #4FC08D 0%, #42B883 100%)\"\n  }\n}\n\n\u002F\u002F Multiple gradients\n{\n  value: \"angular\",\n  label: \"Angular\",\n  style: {\n    gradient: \"linear-gradient(45deg, #DD0031 0%, #C3002F 50%, #FF6B6B 100%)\"\n  }\n}\n```\n\n### Brand Colors\n\n```tsx\nconst brandColors = {\n\treact: { badgeColor: \"#61DAFB\", iconColor: \"#282C34\" },\n\tvue: { gradient: \"linear-gradient(135deg, #4FC08D 0%, #42B883 100%)\" },\n\tangular: { badgeColor: \"#DD0031\", iconColor: \"#ffffff\" },\n\tsvelte: { gradient: \"linear-gradient(135deg, #FF3E00 0%, #FF8A00 100%)\" },\n\tnode: { badgeColor: \"#339933\", iconColor: \"#ffffff\" },\n};\n```\n\n### Animation Examples\n\n```tsx\n\u002F\u002F Wiggle animation with custom timing\n\u003CMultiSelect\n  options={options}\n  onValueChange={setSelected}\n  animationConfig={{\n    badgeAnimation: \"wiggle\",\n    duration: 0.5,\n  }}\n\u002F>\n\n\u002F\u002F Pulse animation with delay\n\u003CMultiSelect\n  options={options}\n  onValueChange={setSelected}\n  animationConfig={{\n    badgeAnimation: \"pulse\",\n    popoverAnimation: \"fade\",\n    duration: 0.3,\n    delay: 0.1,\n  }}\n\u002F>\n\n\u002F\u002F Scale animation for popover\n\u003CMultiSelect\n  options={options}\n  onValueChange={setSelected}\n  animationConfig={{\n    badgeAnimation: \"slide\",\n    popoverAnimation: \"scale\",\n    duration: 0.4,\n  }}\n\u002F>\n```\n\n## 🎯 Advanced Examples\n\n### Advanced Animations\n\n```tsx\n\u003CMultiSelect\n\toptions={options}\n\tonValueChange={setSelected}\n\tanimationConfig={{\n\t\tbadgeAnimation: \"wiggle\",\n\t\tpopoverAnimation: \"scale\",\n\t\tduration: 0.3,\n\t\tdelay: 0.1,\n\t}}\n\tplaceholder=\"Animated component\"\n\u002F>\n```\n\n### Programmatic Control via Ref\n\n```tsx\nimport { useRef } from \"react\";\nimport type { MultiSelectRef } from \"@\u002Fcomponents\u002Fmulti-select\";\n\nfunction ControlledExample() {\n\tconst multiSelectRef = useRef\u003CMultiSelectRef>(null);\n\n\tconst handleReset = () => {\n\t\tmultiSelectRef.current?.reset();\n\t};\n\n\tconst handleClear = () => {\n\t\tmultiSelectRef.current?.clear();\n\t};\n\n\tconst handleSelectAll = () => {\n\t\tconst allValues = options.map((option) => option.value);\n\t\tmultiSelectRef.current?.setSelectedValues(allValues);\n\t};\n\n\tconst handleFocus = () => {\n\t\tmultiSelectRef.current?.focus();\n\t};\n\n\treturn (\n\t\t\u003Cdiv className=\"space-y-4\">\n\t\t\t\u003CMultiSelect\n\t\t\t\tref={multiSelectRef}\n\t\t\t\toptions={options}\n\t\t\t\tonValueChange={setSelected}\n\t\t\t\tplaceholder=\"Controlled component\"\n\t\t\t\u002F>\n\t\t\t\u003Cdiv className=\"flex gap-2\">\n\t\t\t\t\u003Cbutton onClick={handleReset}>Reset\u003C\u002Fbutton>\n\t\t\t\t\u003Cbutton onClick={handleClear}>Clear\u003C\u002Fbutton>\n\t\t\t\t\u003Cbutton onClick={handleSelectAll}>Select All\u003C\u002Fbutton>\n\t\t\t\t\u003Cbutton onClick={handleFocus}>Focus\u003C\u002Fbutton>\n\t\t\t\u003C\u002Fdiv>\n\t\t\u003C\u002Fdiv>\n\t);\n}\n```\n\n### Auto-close on Select\n\n```tsx\n\u003CMultiSelect\n\toptions={options}\n\tonValueChange={setSelected}\n\tcloseOnSelect={true}\n\tplaceholder=\"Closes after each selection\"\n\u002F>\n```\n\n### Duplicate Handling\n\n```tsx\nconst optionsWithDuplicates = [\n\t{ value: \"react\", label: \"React\" },\n\t{ value: \"react\", label: \"React Duplicate\" }, \u002F\u002F Will be handled\n\t{ value: \"vue\", label: \"Vue.js\" },\n];\n\n\u003CMultiSelect\n\toptions={optionsWithDuplicates}\n\tonValueChange={setSelected}\n\tdeduplicateOptions={true} \u002F\u002F Automatically removes duplicates\n\tplaceholder=\"Handles duplicates\"\n\u002F>;\n```\n\n### With Form Integration (React Hook Form)\n\n```tsx\nimport { useForm, Controller } from \"react-hook-form\";\nimport { zodResolver } from \"@hookform\u002Fresolvers\u002Fzod\";\nimport { z } from \"zod\";\n\nconst formSchema = z.object({\n\ttechnologies: z.array(z.string()).min(1, \"Select at least one technology\"),\n});\n\nfunction MyForm() {\n\tconst form = useForm({\n\t\tresolver: zodResolver(formSchema),\n\t\tdefaultValues: { technologies: [] },\n\t});\n\n\treturn (\n\t\t\u003Cform onSubmit={form.handleSubmit(onSubmit)}>\n\t\t\t\u003CController\n\t\t\t\tcontrol={form.control}\n\t\t\t\tname=\"technologies\"\n\t\t\t\trender={({ field }) => (\n\t\t\t\t\t\u003CMultiSelect\n\t\t\t\t\t\toptions={techOptions}\n\t\t\t\t\t\tonValueChange={field.onChange}\n\t\t\t\t\t\tdefaultValue={field.value}\n\t\t\t\t\t\tplaceholder=\"Select technologies...\"\n\t\t\t\t\t\u002F>\n\t\t\t\t)}\n\t\t\t\u002F>\n\t\t\u003C\u002Fform>\n\t);\n}\n```\n\n### Programmatic Control\n\n```tsx\nfunction ControlledExample() {\n\tconst [selected, setSelected] = useState\u003Cstring[]>([]);\n\n\tconst selectRandom = () => {\n\t\tconst randomItems = options\n\t\t\t.filter((item) => !item.disabled)\n\t\t\t.sort(() => 0.5 - Math.random())\n\t\t\t.slice(0, 3)\n\t\t\t.map((item) => item.value);\n\t\tsetSelected(randomItems);\n\t};\n\n\treturn (\n\t\t\u003Cdiv>\n\t\t\t\u003CMultiSelect\n\t\t\t\toptions={options}\n\t\t\t\tonValueChange={setSelected}\n\t\t\t\tdefaultValue={selected}\n\t\t\t\u002F>\n\t\t\t\u003Cbutton onClick={selectRandom}>Random Selection\u003C\u002Fbutton>\n\t\t\t\u003Cbutton onClick={() => setSelected([])}>Clear All\u003C\u002Fbutton>\n\t\t\u003C\u002Fdiv>\n\t);\n}\n```\n\n### Custom Empty State\n\n```tsx\n\u003CMultiSelect\n\toptions={options}\n\tonValueChange={setSelected}\n\tsearchable={true}\n\temptyIndicator={\n\t\t\u003Cdiv className=\"flex flex-col items-center p-4\">\n\t\t\t\u003CSearchIcon className=\"h-8 w-8 text-muted-foreground mb-2\" \u002F>\n\t\t\t\u003Cp className=\"text-muted-foreground\">No items found\u003C\u002Fp>\n\t\t\t\u003Cp className=\"text-xs text-muted-foreground\">\n\t\t\t\tTry a different search term\n\t\t\t\u003C\u002Fp>\n\t\t\u003C\u002Fdiv>\n\t}\n\u002F>\n```\n\n### Complex Grouped Structure\n\n```tsx\nconst complexStructure = [\n\t{\n\t\theading: \"Frontend Frameworks\",\n\t\toptions: [\n\t\t\t{\n\t\t\t\tvalue: \"react\",\n\t\t\t\tlabel: \"React\",\n\t\t\t\ticon: ReactIcon,\n\t\t\t\tstyle: { badgeColor: \"#61DAFB\", iconColor: \"#282C34\" },\n\t\t\t},\n\t\t\t{\n\t\t\t\tvalue: \"vue\",\n\t\t\t\tlabel: \"Vue.js\",\n\t\t\t\ticon: VueIcon,\n\t\t\t\tstyle: {\n\t\t\t\t\tgradient: \"linear-gradient(135deg, #4FC08D 0%, #42B883 100%)\",\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tvalue: \"angular\",\n\t\t\t\tlabel: \"Angular\",\n\t\t\t\ticon: AngularIcon,\n\t\t\t\tdisabled: true,\n\t\t\t\tstyle: { badgeColor: \"#DD0031\", iconColor: \"#ffffff\" },\n\t\t\t},\n\t\t],\n\t},\n\t{\n\t\theading: \"State Management\",\n\t\toptions: [\n\t\t\t{ value: \"redux\", label: \"Redux\" },\n\t\t\t{ value: \"zustand\", label: \"Zustand\" },\n\t\t\t{ value: \"recoil\", label: \"Recoil\", disabled: true },\n\t\t],\n\t},\n];\n```\n\n## 🎯 Use Cases\n\n- **Technology Stack Selection**: Choose programming languages, frameworks,\n  libraries\n- **Skill Assessment**: Multi-skill selection for profiles or job applications\n- **Category Filtering**: Filter content by multiple categories\n- **Tag Management**: Select multiple tags for articles or products\n- **Permission Management**: Assign multiple roles or permissions\n- **Geographic Selection**: Choose multiple countries, regions, or locations\n- **Product Configuration**: Select features, variants, or add-ons\n- **Team Assignment**: Assign multiple team members to projects\n\n## 🛠️ Customization\n\n### Style Customization\n\nThe component uses CSS classes that can be customized via Tailwind CSS. You can\noverride styles by passing custom classes:\n\n```tsx\n\u003CMultiSelect\n\tclassName=\"my-custom-class\"\n\toptions={options}\n\tonValueChange={setSelected}\n\u002F>\n```\n\n### Custom Variants\n\nCreate your own variants by extending the `multiSelectVariants`:\n\n```tsx\nconst customVariants = cva(\"base-classes\", {\n\tvariants: {\n\t\tvariant: {\n\t\t\t\u002F\u002F ... existing variants\n\t\t\tpremium: \"bg-gradient-to-r from-purple-500 to-pink-500 text-white\",\n\t\t\tminimal: \"bg-transparent border-dashed\",\n\t\t},\n\t},\n});\n```\n\n### Theme Integration\n\nThe component automatically adapts to your theme (light\u002Fdark mode) when using\nthe shadcn\u002Fui theme provider.\n\n## 📝 TypeScript Support\n\nThe component is fully typed and provides excellent TypeScript support:\n\n```tsx\n\u002F\u002F All types are exported for use\nimport type {\n\tMultiSelectOption,\n\tMultiSelectGroup,\n\tMultiSelectProps,\n\tMultiSelectRef,\n\tAnimationConfig,\n} from \"@\u002Fcomponents\u002Fmulti-select\";\n\n\u002F\u002F Type-safe option creation\nconst createOption = (\n\tvalue: string,\n\tlabel: string,\n\toptions?: Partial\u003CMultiSelectOption>\n): MultiSelectOption => ({\n\tvalue,\n\tlabel,\n\t...options,\n});\n\n\u002F\u002F Type-safe event handlers\nconst handleChange = (values: string[]) => {\n\t\u002F\u002F values is automatically typed as string[]\n\tconsole.log(\"Selected:\", values);\n};\n```\n\n## 🚀 Getting Started\n\n### Clone and Run\n\n```bash\n# Clone the repository\ngit clone https:\u002F\u002Fgithub.com\u002Fsersavan\u002Fshadcn-multi-select-component.git\n\n# Navigate to the project\ncd shadcn-multi-select-component\n\n# Install dependencies\nnpm install\n\n# Start the development server\nnpm run dev\n\n# Open your browser to http:\u002F\u002Flocalhost:3000\n```\n\n### Project Structure\n\n```text\n├── src\u002F\n│   ├── app\u002F\n│   │   ├── page.tsx           # Demo page with examples\n│   │   ├── layout.tsx         # Root layout\n│   │   └── globals.css        # Global styles\n│   ├── components\u002F\n│   │   ├── multi-select.tsx   # Main component\n│   │   ├── icons.tsx          # Icon components\n│   │   └── ui\u002F                # shadcn\u002Fui components\n│   └── lib\u002F\n│       └── utils.ts           # Utility functions\n├── components.json            # shadcn\u002Fui config\n├── tailwind.config.ts         # Tailwind configuration\n└── package.json              # Dependencies and scripts\n```\n\n## 🤝 Contributing\n\n1. Fork the repository\n2. Create your feature branch (`git checkout -b feature\u002Famazing-feature`)\n3. Commit your changes (`git commit -m 'Add some amazing feature'`)\n4. Push to the branch (`git push origin feature\u002Famazing-feature`)\n5. Open a Pull Request\n\n## 📄 License\n\nThis project is licensed under the MIT License - see the [LICENSE](LICENSE) file\nfor details.\n\n## 🙏 Acknowledgments\n\n- Built with [shadcn\u002Fui](https:\u002F\u002Fui.shadcn.com\u002F)\n- Icons from [Lucide React](https:\u002F\u002Flucide.dev\u002F)\n- Powered by [Radix UI](https:\u002F\u002Fwww.radix-ui.com\u002F)\n- Styled with [Tailwind CSS](https:\u002F\u002Ftailwindcss.com\u002F)\n\n## 🚀 Live Demo\n\nCheck out the live demo:\n[Multi-Select Component Demo](https:\u002F\u002Fshadcn-multi-select-component.vercel.app)\n\n---\n\nMade with ❤️ by [sersavan](https:\u002F\u002Fgithub.com\u002Fsersavan)\n","这是一个基于shadcn\u002Fui设计的多选组件。它使用React、TypeScript和Tailwind CSS构建，支持多种样式变体、自定义样式、选项分组、禁用选项以及高级动画效果等功能。该组件还具备内置搜索与过滤功能，适用于数据分析仪表板和数据可视化场景中的实时图表过滤。此外，它具有良好的响应式设计，能够适应移动设备和桌面环境，并且完全支持TypeScript，确保了开发过程中的类型安全。对于需要在React项目中实现复杂多选功能的应用来说，这是一个理想的选择。",2,"2026-06-11 03:47:51","high_star"]