cmdk icon indicating copy to clipboard operation
cmdk copied to clipboard

Create New

Open emersonlaurentino opened this issue 3 years ago • 10 comments

It would be really cool if there were no result, just as it is possible to render Command.Empty there was also a Command.Create that could render a selected Item with the message Create new "${seach}".

I tried to do this, however, the Command.Item is rendered null when there are no filtered results.

https://github.com/pacocoursey/cmdk/blob/a7c974083d8b201f39dc5a340a65ae8472781574/cmdk/src/index.tsx#L603

Como seria legal que fosse

Proposal 1

<Command>
  <Command.Input />
  <Command.List>
    <Command.Item>Apple</Command.Item>
    <Command.Item create>Create New</Command.Item>
  </Command.List>
</Command>

Command.Item create would only be displayed when the filter count was 0 (like Command.Empty).

Proposal 2

<Command>
  <Command.Input />
  <Command.List>
    <Command.Item>Apple</Command.Item>
    <Command.Create>Create New</Command.Create>
  </Command.List>
</Command>

Command.Create would only be displayed when the filter count was 0 (like Command.Empty).

emersonlaurentino avatar Jan 23 '23 23:01 emersonlaurentino

Hi.

Have you tried rendering a Command.Item where the value is the current input value?

<Command>
  <Command.Input value={inputValue} onChange={e => setInputValue(e.target.value)} />
  <Command.List>
    <Command.Item value={inputValue} onSelect={createNew}>
       Create new {inputValue}
    </Command.Item>
  </Command.List>
</Command>

This way it should always match the input query and even show up when no other results are matching.

raunofreiberg avatar Jan 24 '23 03:01 raunofreiberg

You may also wish to use the (advanced) useCommandState hook documented here:

const search = useCommandState((state) => state.search)
const isEmpty = useCommandState((state) => state.filtered.count === 0)

pacocoursey avatar Jan 24 '23 05:01 pacocoursey

You may also wish to use the (advanced) useCommandState hook documented here:

const search = useCommandState((state) => state.search)
const isEmpty = useCommandState((state) => state.filtered.count === 0)

I tried that way, but I couldn't render the Command.Item anyway.

emersonlaurentino avatar Jan 24 '23 14:01 emersonlaurentino

Have you tried rendering a Command.Item where the value is the current input value?

I'll test this approach.

emersonlaurentino avatar Jan 24 '23 14:01 emersonlaurentino

@raunofreiberg it worked, however when I search for something that ends with a space, the count goes to 0, thus removing the create new button.

So a strange behavior is when I search for a compound name it keeps blinking every space. Example:

"rauno" = true
"rauno " = false
"rauno f" = true

emersonlaurentino avatar Jan 24 '23 15:01 emersonlaurentino

I'm currently in a similar situation, where I have to implement several selectable items that should be displayed once there are no other matching items.

I have tried using the current search value from the state hook, managed to solve the problem mentioned here before but there was another flaw I couldn't figure out yet.

const search = useCommandState((state) => state.search);

return <Command.Item value={`create:${search}:`}>...</Command.Item>;

As you can see I added a character at the end of the current search value when using it as a value for the Item. This solves the issues described by @emersonlaurentino and makes the Item match even with spaces at the end.

With this solution (and any of the other I've tried so far too be honest) however, two problems remain:

  • While entering new characters in the input, filtered jumps randomly between { count: 1, items: {}, groups: {} } and { count: 0, items: {}, groups: {} }. When count is 0 it will show the Command.Empty even though it still renders the dynamic item just fine. When removing characters, the count stays 1 the whole time.

  • If (for some reason) I repeat the same character a bunch of times, the whole tab eventually crashes. For my 16GB RAM M1 Pro and the newest Firefox version, entering a ~30 times did the trick, no matter how much I waited between the keypresses. No errors in the console or anything and I don't encounter this issue without a "dynamic" item.

vainamov avatar Jan 25 '23 08:01 vainamov

Any updates on this? @vainamov even with your suggested solution, the empty screen keeps popping up with a space

gijsmin avatar Jun 13 '23 18:06 gijsmin

It's the third time this week I've encountered this problem and I still have no good solution in mind. Sometimes it just works, sometimes it doesn't.

TmLev avatar Nov 14 '23 17:11 TmLev

Got this working using:

<Command
   filter={(item, query) => {
      if (item === "+create+") {
         return 1;
      }
      if (item.toLowerCase().includes(query.trim().toLowerCase())) {
         return 1;
      }
      return 0;
   }}
>

and

const CommandCreateItem = ({ onSelect }: { onSelect: (value: string) => void }) => {
   const query = useCommandState((state) => state.search);
   if (!query || !onCreate) return null;
   return (
      <CommandItem
         forceMount
         value="+create+"
         onSelect={() => onSelect(query)}
      >
         <MdAdd className="mr-2" /> {query}
      </CommandItem>
   );
});

This definitely should be part of the lib.

cah4a avatar Feb 29 '24 10:02 cah4a

I'm also having this issue. Passing search as the value causes React 18 to freak out with too many state changes. Ironically, that only happens inside of <Command.Empty> - the one inside List doesn't seem to mind.

If I create a new component without using Command.Item then keyboard navigation breaks.

Has anyone managed to get this working?

cah4a's resolution overrides the filtering - and I don't want that.

andrioidApacta avatar Apr 18 '24 13:04 andrioidApacta