pacocoursey / cmdk

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

Idea: `shouldFilter` per `Group`/`Item` #107

Open nandorojo opened 1 year ago

nandorojo commented 1 year ago

I have some items which I'm fetching from TypeSense and don't need filtering. Others are statically-defined. It would be useful to define shouldFilter at the Item/Group level, rather than only the parent. I believe cmdk is built in a way where this would be possible, but I haven't peaked at the source in a while, so I'm not totally sure. Figured I'd mention it!

joseph-mccombs commented 1 year ago

@nandorojo One way around this I found, is to use the searchFilter prop and assign something common to the <Command.Group> value prop to ensure that these items dont get filtered like so:

<Command
     filter={(value, search) => {
       if (value.includes(search) || value.includes("doNotFilter-")) return 1; // ensures items arent filtered
       return 0;
     }}
>
   <Command.Group>
      {unfilteredItem.map((item) => (
         <Command.Item value={`doNotFilter-${item.name}`} />
      )}
   </Command.Group>
   <Command.Group>
      {filtered.map((item) => (
         <Command.Item value={item.name} />
      )}
   </Command.Group>
</Command>
Jamess-Lucass commented 2 months ago

I've also ran into a very similar use case to you, where my command consists of static data, such as links to pages, but also has dynamic data which has been fetching and searched via an API.

For example:

const Page = () => {
  const [searchTerm, setSearchTerm] = React.useState<string>('');

  const { data: itemsA } = useSearchItemsA({
    query: {
      search: searchTerm,
    },
    enabled: !!searchTerm,
  });

  const { data: itemsB } = useSearchItemsB({
    query: {
      search: searchTerm,
    },
    enabled: !!searchTerm,
  });

  return (
    <div>
      <Command>
        <Command.Input placeholder="Search…" onValueChange={setSearchTerm} />
        <Command.List>
          <Command.Empty>No results.</Command.Empty>
          <Command.Group heading="Links">
            <Command.Item>Link A</Command.Item>
            <Command.Item>Link B</Command.Item>
            <Command.Item>Link C</Command.Item>
          </Command.Group>

          {/* This data has already been filtered by an external source, do not apply additional filtering */}
          <Command.Group heading="Items A">
            {itemsA.map((item) => (
              <Command.Item key={item.id}>{item.name}</Command.Item>
            ))}
          </Command.Group>

          {/* This data has already been filtered by an external source, do not apply additional filtering */}
          <Command.Group heading="Items B">
            {itemsB.map((item) => (
              <Command.Item key={item.id}>{item.name}</Command.Item>
            ))}
          </Command.Group>
        </Command.List>
      </Command>
    </div>
  );
};

I've opened a PR, which adds shouldFilter to the Command.Item component so these items can be skipped during any filtering.