pacocoursey / cmdk

Fast, unstyled command menu React component.
https://cmdk.paco.me
MIT License
9.03k stars 258 forks source link

is it possible to search by label not by value only? #240

Closed bizmich closed 3 months ago

IlirEdis commented 3 months ago

need this also!

najdic commented 3 months ago

@bizmich @IlirEdis have you tried the keywords feature? 🤔

<Command.Item keywords={['fruit', 'apple']}>Apple</Command.Item>

If not you can always write custom logic for filtering/searching

import { matchSorter } from 'match-sorter';

export function useMatchByLabelAndValue(data, query) {
  return useMemo(
    () => matchSorter(data, query, { keys: ['label', 'value'] }),
    [data, query],
  );
}

function CustomCmdk({ data }) {
    const [searchQuery, setSearchQuery] = useState('');
    const matches = useMatchByLabelAndValue(data, searchQuery);

    return (
      <Command shouldFilter={false}>
        <CommandInput
          value={searchQuery}
          onValueChange={(value) =>
            startTransition(() => setSearchQuery(value))
          }
        />

        <CommandList>
          <CommandEmpty>No results found.</CommandEmpty>

          {matches.map((item) => (
            <CommandItem
              key={item.value}
              onSelect={() => {
                startTransition(() => setSearchQuery(''));
              }}
            >
              <span>
                {item.label} {item.value}
              </span> 
            </CommandItem>
          ))}
        </CommandList>
      </Command>
)}
shomyx commented 3 months ago

Use the custom filter option and combine value and label as value for the CommandItem(s).

<Command
    filter={(value, search) => {
      if (value.includes(search)) return 1;
      return 0;
    }}
>
<CommandItem value={`${option.value} ${option.label}`}>

https://github.com/pacocoursey/cmdk/tree/v1.0.0?tab=readme-ov-file#parts-and-styling

Screenshot 2024-03-20 at 14 51 26

IlirEdis commented 3 months ago

I also noticed that if you don't provide a value prop it uses the "label" from inside the <CommandItem> tag.

Also @najdic & @shomyx thanks a lot for these valuable informations! Its my second time using cmdk and i will use these to make searching experience better.

bizmich commented 2 months ago

@bizmich @IlirEdis have you tried the keywords feature? 🤔

<Command.Item keywords={['fruit', 'apple']}>Apple</Command.Item>

If not you can always write custom logic for filtering/searching

import { matchSorter } from 'match-sorter';

export function useMatchByLabelAndValue(data, query) {
  return useMemo(
    () => matchSorter(data, query, { keys: ['label', 'value'] }),
    [data, query],
  );
}

function CustomCmdk({ data }) {
    const [searchQuery, setSearchQuery] = useState('');
    const matches = useMatchByLabelAndValue(data, searchQuery);

    return (
      <Command shouldFilter={false}>
        <CommandInput
          value={searchQuery}
          onValueChange={(value) =>
            startTransition(() => setSearchQuery(value))
          }
        />

        <CommandList>
          <CommandEmpty>No results found.</CommandEmpty>

          {matches.map((item) => (
            <CommandItem
              key={item.value}
              onSelect={() => {
                startTransition(() => setSearchQuery(''));
              }}
            >
              <span>
                {item.label} {item.value}
              </span> 
            </CommandItem>
          ))}
        </CommandList>
      </Command>
)}

Doesn't it seem illogical to have a filter based on a value that the user cannot see? What's the purpose behind this design?

bizmich commented 2 months ago

Use the custom filter option and combine value and label as value for the CommandItem(s).

<Command
    filter={(value, search) => {
      if (value.includes(search)) return 1;
      return 0;
    }}
>
<CommandItem value={`${option.value} ${option.label}`}>

https://github.com/pacocoursey/cmdk/tree/v1.0.0?tab=readme-ov-file#parts-and-styling

Screenshot 2024-03-20 at 14 51 26

After combining label and value, I get both results. This makes me filter the output again. Is there a more efficient way to do this?