Skip to content
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

How to tell between Enter and Cmd+Enter #310

Open
donovan-fintool opened this issue Oct 1, 2024 · 2 comments
Open

How to tell between Enter and Cmd+Enter #310

donovan-fintool opened this issue Oct 1, 2024 · 2 comments

Comments

@donovan-fintool
Copy link

I have a use case where I want to perform different actions on enter and on cmd+enter. In my use case, I'm using cmdk as a multi select, so users can check / uncheck each item in the list, then hit cmd+enter to submit their selections.

onSelect triggers on both on enter and on cmd+enter, which causes issues for me. My workaround is to create a different React state that tracks if the cmd key is currently being pressed, which is kind of hacky and a lot of boilerplate.

My question is - is there a better way of doing it? If not, could we pass an event to onSelect so I can handle it myself?

Thanks!

import { Command } from 'cmdk';
import { useEffect, useState } from "react";

export const Demo = () => {
    const [isCmdKeyPressed, setIsCmdKeyPressed] = useState(false);

    const handleEnter = () => {
        // do something
    }

    const handleCmdEnter = () => {
        // do something else
    }

    useEffect(() => {
        const handleKeyDown = (event: KeyboardEvent) => {
            if (event.metaKey || event.ctrlKey) {
                setIsCmdKeyPressed(true);
            }

            if (event.key === 'Enter' && (event.metaKey || event.ctrlKey)) {
                handleCmdEnter();
            }
        };

        const handleKeyUp = (event: KeyboardEvent) => {
            if (event.key === 'Meta' || event.key === 'Control') {
                setIsCmdKeyPressed(false);
            }
        };

        document.addEventListener('keydown', handleKeyDown);
        document.addEventListener('keyup', handleKeyUp);

        return () => {
            document.removeEventListener('keydown', handleKeyDown);
            document.removeEventListener('keyup', handleKeyUp);
        };
    }, []);

    return (
        <Command label="Command Menu">
            <Command.Input />
            <Command.List>
                <Command.Empty>No results found.</Command.Empty>
                <Command.Item
                    onSelect={() => {
                        if (isCmdKeyPressed) {
                            // detected cmd+enter, don't do anything
                            return;
                        }

                        handleEnter();
                    }}
                >
                    Apple
                </Command.Item>
            </Command.List>
        </Command>
    );
};
@EdoAPP
Copy link

EdoAPP commented Oct 9, 2024

I had a similar situation where I wanted to perform a different action depending if an item was selected with "Enter". After looking at the implementation, I ended up with this solution:

<Command
  onKeyDown={(e) => {
    // You can also add another condition if (e.key === 'Enter' && e.metaKey) to tackle cmd + enter
    if (e.key === 'Enter') {
      // Prevents "onSelect" from being called"
      e.preventDefault();
      // item that is currently on focused (selected) either by mouse hover or keyboard navigation
      const item = getSelectedItem(listInnerRef);
      
      // you can do 
      
      if (item) {
      // this will dispatch an event that the cmdk library will listen to (then onSelect) will be executed
        const event = new Event(SELECT_EVENT);
        item.dispatchEvent(event);
      }
    }
  }}
>

Then your utility functions

export const ITEM_SELECTOR = `[cmdk-item=""]`;
export const SELECT_EVENT = `cmdk-item-select`;

export function getSelectedItem(listInnerRef: React.RefObject<HTMLDivElement>) {
  return listInnerRef.current?.querySelector(`${ITEM_SELECTOR}[aria-selected="true"]`);
}

listInnerRef is a reference to your list/group of items.

@donovan-fintool
Copy link
Author

I had a similar situation where I wanted to perform a different action depending if an item was selected with "Enter". After looking at the implementation, I ended up with this solution:

<Command
  onKeyDown={(e) => {
    // You can also add another condition if (e.key === 'Enter' && e.metaKey) to tackle cmd + enter
    if (e.key === 'Enter') {
      // Prevents "onSelect" from being called"
      e.preventDefault();
      // item that is currently on focused (selected) either by mouse hover or keyboard navigation
      const item = getSelectedItem(listInnerRef);
      
      // you can do 
      
      if (item) {
      // this will dispatch an event that the cmdk library will listen to (then onSelect) will be executed
        const event = new Event(SELECT_EVENT);
        item.dispatchEvent(event);
      }
    }
  }}
>

Then your utility functions

export const ITEM_SELECTOR = `[cmdk-item=""]`;
export const SELECT_EVENT = `cmdk-item-select`;

export function getSelectedItem(listInnerRef: React.RefObject<HTMLDivElement>) {
  return listInnerRef.current?.querySelector(`${ITEM_SELECTOR}[aria-selected="true"]`);
}

listInnerRef is a reference to your list/group of items.

Amazing, that's much cleaner than my method. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants