nextui-org / nextui

🚀 Beautiful, fast and modern React UI library.
https://nextui.org
MIT License
21.6k stars 1.41k forks source link

[Feature Request] Autocomplete with Multiple Selections Feature or Multiple Select with Autocomplete Filtering #2109

Open Dennisjoch opened 10 months ago

Dennisjoch commented 10 months ago

Is your feature request related to a problem? Please describe.

Users of my application need the ability to select multiple options from an autocomplete field. Currently, Next UI provides an Autocomplete component that allows for a single selection with suggestions and a Select component that allows multiple selections but without autocomplete functionality. Merging these features would greatly enhance user experience.

Describe the solution you'd like

I would like the Autocomplete component to include a multiple attribute, allowing multiple selections with autocomplete suggestions, similar to the current Select component's multiple selection feature. Alternatively, the Select component could be enhanced with an autocomplete input to filter options as the user types.

Describe alternatives you've considered

As a workaround, we could use third-party libraries to achieve this functionality, but a native solution from Next UI would be ideal for consistency and to avoid additional dependencies.

Screenshots or Videos

No response

CodeVisionEvgen commented 9 months ago

Screencast from 09.12.23 14:51:02.webm Code from the documentation

LubomirGeorgiev commented 9 months ago

That would be really useful

duyanhvo2503 commented 9 months ago

+1

Inaztm commented 9 months ago

+1

AlexisCeballos-Gauzz commented 7 months ago

+1

ElithPalomino commented 7 months ago

+1

yahorbarkouski commented 7 months ago

hey, perhaps anyone already created a custom multiselect autocomplete and would like share? 👀

DanielReyes03 commented 7 months ago

+1

Tomass673 commented 7 months ago

+1

Dennisjoch commented 7 months ago

hey, perhaps anyone already created a custom multiselect autocomplete and would like share? 👀

Check: ReactSelect

johnshift commented 7 months ago

+1

vasujhawar commented 7 months ago

+1

calrloco commented 6 months ago

Im having the same issue i think i will use the headless ui one with nextui componets https://headlessui.com/react/combobox#selecting-multiple-values

nikolay-gipp-sibe commented 6 months ago

+1

thepooyan commented 5 months ago

+1

Armandotrsg commented 5 months ago

+1

hericsantos commented 5 months ago

+1

peterondesign commented 5 months ago

+1

brycelund commented 4 months ago

It seems like passing these down to the listbox should work, but no luck.

<Autocomplete
listboxProps={{
      selectionMode: 'multiple',
      selectedKeys: selectedKeys,
      onSelectionChange: setSelectedKeys
}}
AlexMartin998 commented 3 months ago

+1

AprilPolubiec commented 3 months ago

+1

tsqqqqqq commented 3 months ago

+1 This feature is very useful to me

Jackmekiss commented 3 months ago

+1

HiteshKanwar100 commented 3 months ago

+1

qpenney commented 3 months ago

+1

manthankumaar commented 3 months ago

+1

oawebdev commented 2 months ago

+1

chitraksty1994 commented 2 months ago

Any updates on this one! it would be really usefull

HiteshKanwar100 commented 2 months ago

Here's an elegant approach that I came up with using NextUI's Dropdown component :

1. Structure:

Use NextUI's Dropdown component as the base.
Utilize the Dropdown's built-in multiple selection feature.
Implement two sections within the DropdownMenu:
a. A search input section (non-selectable)
b. A filterable list of selectable items

2. Implementation:

import { Dropdown, DropdownTrigger, DropdownMenu, DropdownSection, DropdownItem, Button, Input, Avatar } from "@nextui-org/react";
import { ChevronDownIcon } from "@your-icon-library";
import { useState, useMemo } from "react";

const MultiSelectAutocompleteDropdown = ({ users }) => {
  const [selectedKeys, setSelectedKeys] = useState(new Set([]));
  const [searchQuery, setSearchQuery] = useState("");

  // Filter users based on search query
  const filteredUsers = useMemo(() => {
    return users.filter((user) =>
      user.name.toLowerCase().includes(searchQuery.toLowerCase())
    );
  }, [users, searchQuery]);

  // Handle search input changes
  const handleSearchChange = (e) => {
    setSearchQuery(e.target.value);
  };

  // Generate display value for the button
  const selectedValue = useMemo(() => {
    const selected = Array.from(selectedKeys);
    return selected.length > 0
      ? `${selected.length} selected`
      : "Select member";
  }, [selectedKeys]);

  return (
    <div>
      <p className="text-sm pb-2">Assign to</p>
      <Dropdown>
        <DropdownTrigger>
          <Button 
            className="max-w-full" 
            endContent={<ChevronDownIcon className="text-small" />}
            variant="bordered"
          >
            {selectedValue}
          </Button>
        </DropdownTrigger>
        <DropdownMenu
          aria-label="Multiple selection with search"
          variant="flat"
          closeOnSelect={false}
          disallowEmptySelection
          selectionMode="multiple"
          selectedKeys={selectedKeys}
          onSelectionChange={setSelectedKeys}
        >
          <DropdownSection>
            <DropdownItem key="search" isReadOnly>
              <Input
                placeholder="Search..."
                value={searchQuery}
                onChange={handleSearchChange}
                fullWidth
              />
            </DropdownItem>
          </DropdownSection>
          <DropdownSection>
            {filteredUsers.map((user) => (
              <DropdownItem key={user.id}>
                <div className="flex gap-2 items-center">
                  <Avatar alt={user.name} className="flex-shrink-0" size="sm" src={user.avatar} />
                  <div className="flex flex-col">
                    <span className="text-small">{user.name}</span>
                    <span className="text-tiny text-default-400">{user.email}</span>
                  </div>
                </div>
              </DropdownItem>
            ))}
          </DropdownSection>
        </DropdownMenu>
      </Dropdown>
    </div>
  );
};
impact-ls commented 2 months ago

I created a simple version that worked for us

ali-shafi-hff commented 1 month ago

+1

riczudaniel commented 1 month ago

I have just implemented a new solution for us regarding multiselect with the option to search for the listed elements. An array is set in the props (it can also be set directly, but in our case it was not convenient.

Feel free to use and wish you a good coding 🤲🏼

import { useState } from 'react'
import { Autocomplete, AutocompleteItem, Chip } from "@nextui-org/react";
import { X, Check } from 'lucide-react';

const MultiselectSearch = ({array}) => {

    const [selectedItems, setSelectedItems] = useState([])

    const handleSelect = (item) => {
        if (!selectedItems.includes(item)) {
            setSelectedItems([...selectedItems, item])
        } else {
            setSelectedItems(selectedItems.filter(selection => selection !== item))

        }
    }

    const handleDeleteSelection = (item) => {
        setSelectedItems(selectedItems.filter(selection => selection !== item))
    }

    return (
        <div>
        <Autocomplete
            className="w-96"
            selectedKey={''}>
            {array.map((item, index) => (
                <AutocompleteItem 
                    key={index} 
                    value={item} 
                    onClick={() => handleSelect(item)}
                    endContent={
                        setSelectedItems.includes(item) && (
                            <Check size={16} className="mr-2 text-green-500" />
                        )
                    }
                    >
                    {item}
                </AutocompleteItem>
            ))}
        </Autocomplete>
        <div className="flex mt-2 w-96 flex-wrap">
            {setSelectedItems.map((item) => (
                <Chip 
                    color={"primary"} 
                    className="mr-2 mt-2" 
                    endContent={<X 
                        size={14} 
                        className="mr-1 cursor-pointer"
                        onClick={() => handleDeleteSelection(item)}
                    />}>
                {item}
                </Chip>
            ))} 
        </div> 
    </div>
    )
}

export default MultiselectSearch