in packages/dashboard-app/src/components/ui/keystrokeInput.tsx [117:222]
export default function KeystrokeGroup({
id,
defaults,
}: {
id: string;
defaults: string[];
}) {
// `id` has the `autocomplete.${command}` format. We just want the command.
const command = id.split(".")[1];
const keybindings = useKeybindings(command, defaults);
const { listening, setListening } = useContext(ListenerContext);
const [inputValue, setInputValue] = useState<string[] | null>(null);
const [isInvalid, setIsInvalid] = useState(false);
const inputOpen = listening === id;
type keypressEvent = {
key: string;
keyCode: number;
metaKey: boolean;
ctrlKey: boolean;
shiftKey: boolean;
altKey: boolean;
preventDefault: () => void;
stopPropagation: () => void;
};
const handleKeyPress = useCallback((e: keypressEvent) => {
const keys = new Set<string>();
if (e.metaKey) keys.add("command");
if (e.ctrlKey) keys.add("control");
if (e.shiftKey) keys.add("shift");
if (e.altKey) keys.add("option");
const key = getKeyName(e.keyCode);
const isInvalidCombination =
keys.has("command") ||
(keys.has("control") &&
key !== "control" &&
!VALID_CONTROL_KEYS.includes(key));
setIsInvalid(isInvalidCombination);
if (key) keys.add(key);
setInputValue(Array.from(keys));
e.preventDefault();
e.stopPropagation();
}, []);
useEffect(() => {
if (inputOpen) return;
setIsInvalid(false);
setInputValue(null);
}, [inputOpen]);
useEffect(() => {
if (!inputOpen) return;
// attach the event listener
document.addEventListener("keydown", handleKeyPress);
// remove the event listener
return () => {
document.removeEventListener("keydown", handleKeyPress);
};
}, [handleKeyPress, inputOpen]);
function cancelKeystroke() {
setInputValue(null);
setListening(null);
}
function openInput() {
setListening(id);
}
return (
<div className="flex flex-col gap-1">
<div className="flex gap-2 flex-wrap">
{keybindings.map((k: string, i: number) => (
<Keystroke keybinding={k} key={i} />
))}
{inputOpen ? (
<Input
command={command}
value={inputValue as string[]}
invalid={isInvalid}
cancel={cancelKeystroke}
/>
) : (
<button
onClick={openInput}
className="p-1 px-[6px] hover:bg-black/5 rounded-lg"
>
<Plus className="h-3 w-3" />
</button>
)}
</div>
{isInvalid && (
<span className="text-xs font-medium text-red-500 pl-8">
Sorry, that combination is invalid.
</span>
)}
</div>
);
}