-
Notifications
You must be signed in to change notification settings - Fork 3.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weβll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Multi select ? #66
Comments
Unfortunately no. The |
π Would love to have a Multi Select |
@its-monotype Listbox from HeadlessUI (same creators as TailwindCSS) has support for multi-select |
Hey @Flo-Slv, I also wanted a multi-select, one of my colleague suggested to use a dropdown-menu with checkboxes. It worked well for my use-case. Checked it out here. |
Hey ! It can be a workaround until they implement a native solution ! Thanks ! |
https://github.com/colepeters/multiselect What about this? |
I'd love see a multi-select with labels (instead of saying something like "4 items selected"), which is incredibly valuable for adding "tags" to things - a fairly common use case Here's the one I'm currently using (not based on tailwind-css) Here's a version using tailwind: https://demo-react-tailwindcss-select.vercel.app/ |
There is a headless multi-select combobox in Base UI ( ![]() https://mui.com/material-ui/react-autocomplete/#customized-hook and small: ![]() https://bundlephobia.com/package/@mui/base@5.0.0-beta.4 maybe to consider as a base to style on top of, it could be a temporary solution for radix-ui/primitives#1342. |
In case anyone else comes to this issue looking for a solution, @mxkaske just dropped a mutli-select component built with cmdk and shadcn components. Demo here: https://craft.mxkaske.dev/post/fancy-multi-select Source here: https://github.com/mxkaske/mxkaske.dev/blob/main/components/craft/fancy-multi-select.tsx |
This isn't accessible |
@zachrip you can drop an issue in the repo here: https://github.com/mxkaske/mxkaske.dev/issues |
I tweaked Headless UI Listbox component to achieve the desired UI. Here is the example code: import { Listbox, Transition } from '@headlessui/react';
import { CaretSortIcon, CheckIcon } from '@radix-ui/react-icons';
import React from 'react';
export default function MultiSelect() {
const [selected, setSelected] = React.useState(['None']);
const [options, setOptions] = React.useState<string[]>([]);
React.useEffect(() => {
setOptions(['None', 'Apple', 'Orange', 'Banana', 'Grapes']);
}, []);
return (
<Listbox
value={selected}
onChange={setSelected}
multiple>
<div className='relative'>
<Listbox.Button className='flex h-9 w-full items-center justify-between rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-ring disabled:cursor-not-allowed disabled:opacity-50'>
<span className='block truncate'> {selected.map(option => option).join(', ')}</span>
<CaretSortIcon className='h-4 w-4 opacity-50' />
</Listbox.Button>
<Transition
as={React.Fragment}
leave='transition ease-in duration-100'
leaveFrom='opacity-100'
leaveTo='opacity-0'>
<Listbox.Options className='absolute z-50 mt-1 max-h-60 w-full overflow-auto rounded-md bg-popover py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm'>
{options.map((option, optionIdx) => (
<Listbox.Option
key={optionIdx}
className='relative cursor-default select-none py-1.5 pl-10 pr-4 text-sm rounded-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50'
value={option}>
{({ selected }) => (
<>
{option}
{selected ? (
<span className='absolute inset-y-0 right-2 flex items-center pl-3'>
<CheckIcon className='h-4 w-4' />
</span>
) : null}
</>
)}
</Listbox.Option>
))}
</Listbox.Options>
</Transition>
</div>
</Listbox>
);
} Hope this works for you. Cheers! |
Hi all, I have created component, I hope somebody will find it helpful: import * as React from 'react'
import { cn } from "@/lib/utils"
import { Check, X, ChevronsUpDown } from "lucide-react"
import { Button } from "@/components/ui/button"
import {
Command,
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
} from "@/components/ui/command"
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover"
import { Badge } from "@/components/ui/badge";
export type OptionType = {
label: string;
value: string;
}
interface MultiSelectProps {
options: OptionType[];
selected: string[];
onChange: React.Dispatch<React.SetStateAction<string[]>>;
className?: string;
}
function MultiSelect({ options, selected, onChange, className, ...props }: MultiSelectProps) {
const [open, setOpen] = React.useState(false)
const handleUnselect = (item: string) => {
onChange(selected.filter((i) => i !== item))
}
return (
<Popover open={open} onOpenChange={setOpen} {...props}>
<PopoverTrigger asChild>
<Button
variant="outline"
role="combobox"
aria-expanded={open}
className={`w-full justify-between ${selected.length > 1 ? "h-full" : "h-10"}`}
onClick={() => setOpen(!open)}
>
<div className="flex gap-1 flex-wrap">
{selected.map((item) => (
<Badge
variant="secondary"
key={item}
className="mr-1 mb-1"
onClick={() => handleUnselect(item)}
>
{item}
<button
className="ml-1 ring-offset-background rounded-full outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2"
onKeyDown={(e) => {
if (e.key === "Enter") {
handleUnselect(item);
}
}}
onMouseDown={(e) => {
e.preventDefault();
e.stopPropagation();
}}
onClick={() => handleUnselect(item)}
>
<X className="h-3 w-3 text-muted-foreground hover:text-foreground" />
</button>
</Badge>
))}
</div>
<ChevronsUpDown className="h-4 w-4 shrink-0 opacity-50" />
</Button>
</PopoverTrigger>
<PopoverContent className="w-full p-0">
<Command className={className}>
<CommandInput placeholder="Search ..." />
<CommandEmpty>No item found.</CommandEmpty>
<CommandGroup className='max-h-64 overflow-auto'>
{options.map((option) => (
<CommandItem
key={option.value}
onSelect={() => {
onChange(
selected.includes(option.value)
? selected.filter((item) => item !== option.value)
: [...selected, option.value]
)
setOpen(true)
}}
>
<Check
className={cn(
"mr-2 h-4 w-4",
selected.includes(option.value) ?
"opacity-100" : "opacity-0"
)}
/>
{option.label}
</CommandItem>
))}
</CommandGroup>
</Command>
</PopoverContent>
</Popover>
)
}
export { MultiSelect } Use it like standalone component : import * as React from 'react'
import { MultiSelect } from from "@/components/ui/multi-select"
function Demo() {
const [selected, setSelected] = useState<string[]>([]);
return (
<MultiSelect
options={[
{
value: "next.js",
label: "Next.js",
},
{
value: "sveltekit",
label: "SvelteKit",
},
{
value: "nuxt.js",
label: "Nuxt.js",
},
{
value: "remix",
label: "Remix",
},
{
value: "astro",
label: "Astro",
},
{
value: "wordpress",
label: "WordPress",
},
{
value: "express.js",
label: "Express.js",
},
]}
selected={selected}
onChange={setSelected}
className="w-[560px]"
/>
)
} or part of React Hook Form: <FormField
control={form.control}
name="industry"
render={({ field }) => (
<FormItem>
<FormLabel>Select Frameworks</FormLabel>
<MultiSelect
selected={field.value}
options={[
{
value: "next.js",
label: "Next.js",
},
{
value: "sveltekit",
label: "SvelteKit",
},
{
value: "nuxt.js",
label: "Nuxt.js",
},
{
value: "remix",
label: "Remix",
},
{
value: "astro",
label: "Astro",
},
{
value: "wordpress",
label: "WordPress",
},
{
value: "express.js",
label: "Express.js",
}
]}
{...field}
className="sm:w-[510px]"
/>
<FormMessage />
</FormItem>
)}
/> |
You can create a PR for this to be supported officially? |
thank you, this is great. also wondering did anyone successfully make a form collect inputs correctly? |
Is there any component that has multi-select except the dropdown? @shadcn |
I created a multi input/select component but for tags that the user inputs rather than using a pre-defined list of options https://gist.github.com/enesien/03ba5340f628c6c812b306da5fedd1a4 |
I am getting an error through the selected item, I am using react hook form
|
@johnLamberts PR is still in progress, so until this is done, copy code from here, fix some imports and let me know does it work. |
I am still getting the same error, I have already check the code.
|
import { z } from "zod";
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import {
Form,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form";
import { MultiSelect } from "@/components/ui/multi-select" const AuthorsSchema = z.array(
z.record(
z.string().trim()
)
) const form = useForm<z.infer<typeof AuthorsSchema>>({
resolver: zodResolver(AuthorsSchema),
defaultValues: {
authors: [],
},
}); const onHandleSubmit = (values: z.infer<typeof AuthorsSchema>) => {
console.log({ values })
};
const authorsData = [
{
value: "author1",
label: "Author 1",
}, {
value: "author2",
label: "Author 2",
},
{
value: "author3",
label: "Author 3",
},
{
value: "author4",
label: "Author 4",
}
] <Form {...form}>
<form
onSubmit={form.handleSubmit(onHandleSubmit)}
className="space-y-4"
>
<FormField
control={form.control}
name="authors"
render={({ field: { ...field } }) => (
<FormItem className="mb-5">
<FormLabel>Author</FormLabel>
<MultiSelect
selected={field.value}
options={authorsData}
{...field} />
</FormItem>
)}
/>
<Button type="submit" className="w-full">
Continue
</Button>
</form>
</Form> |
to @timwehrle Hi,
Try it here: https://shadcn-multi-select-component.vercel.app/ |
@sersavan I can't get your component to work. Options are disabled for some reason for me even though I have identical multi-select component as yours π€ |
to @snufkind I've fixed issue with cmdk, now you can use latest version as well |
Really awesome work with this component @sersavan ! I was looking into the aria-selected of each of the CommandItems and seems they are not following what's really selected, as we are controlling in the component the state of each item ourselves. ![]() Did you also noticed that ? |
@joaopedrodcf |
Thanks @sersavan. I really like the UI, but I'd prefer to use it with numerical values. Could you assist me with that? |
@cbptamtom |
and in my form. I just use warehouse_id is a number array [number] |
@cbptamtom |
Thanks @sersavan. I've fixed |
@sersavan You're doing god's work here π Only thing that would make this absolutely perfect is replacing the breadcrumb's with a text. For example "6 options selected" so that the element doesn't overflow or grow horizontally. |
@snufkind |
@sersavan First thanks. I am unable to add my own style to the component. Can you help me to address this issue. |
Hi, I've added a few props, including |
Where does it updated? |
@zekageri |
Hello any accessible solution for app-index.js:33 Warning: In HTML, button cannot be a descendant of button. |
not working !! Unexpected Application Error! |
@AhmedBelkadi |
dinogit Evango None of the components worked in my Next.js application, it renders normally on the screen but when clicked it opens an Error in both: After a bit of searching I discovered that the problem is in <"CommandItem">, this code renders perfectly, but is not functional: <CommandGroup className="h-full overflow-auto">
{selectables.map((framework) => {
return (
<div key={framework.value}>
{framework.label}
</div>
// <CommandItem
// key={framework.value}
// onMouseDown={(e) => {
// e.preventDefault();
// e.stopPropagation();
// }}
// onSelect={(value) => {
// setInputValue("")
// setSelected(prev => [...prev, framework])
// }}
// className={"cursor-pointer"}
// >
// {framework.label}
// </CommandItem>
);
})}
</CommandGroup>
any suggestion?π
resolved in this [issues](https://github.com/shadcn-ui/ui/issues/2944) |
You need to check the version of the dependencies u have for cmdk as they had a major release, also check if you are importing the components from the right npm package shadcn abstraction instead of directly from radix, it's a really common mistake |
@sersavan Why have you made the Popover component a controlled one. I've noticed an issue with your deployment that clicking on the trigger-button when the dropdown is opened, causes the dropdown to close and then immediately reopen. The cause to that is the But since the trigger-button itself toggles the dropdown state, we see the dropdown being opened again. To solve this removing the |
@mshahzebraza |
@Marco-Antonio-Rodrigues you have to wrap 'CommandItem' inside 'CommandList' to make it work in latest version.
|
@shadcn , will we expect to see this component in the next releases? I'm using this MultiSelect component from Tremor, but I would keep my components consistent using only Shadcn UI. |
Thanks a lot for multi select! One addition that would be great is the ability to turn off searching via an https://shadcnui-expansions.typeart.cc/docs/multiple-selector Edit: Ah... nvm! I can kinda achieve this by passing the following props: inputProps={{
inputMode: 'none',
className: 'caret-transparent', // tailwindcss class, could also use 'style' attribute
}} |
Hello Guys this code multiple select for shadcn-vue with vue3 composition API
// Data
// Method
Summary The provided code is a Vue 3 Composition API component that implements a custom multiple select input using a popover for the dropdown. The component displays selected items as badges that can be removed. Users can search and select options from the dropdown. The component uses several custom UI components and utility functions to manage its state and display. |
Hi there βπΌ
thanks for this amazing components !
Is there a way to select multiple data with the Select component ?
Thanks !
The text was updated successfully, but these errors were encountered: