import React from 'react'; export interface UseDocSearchKeyboardEventsProps { isOpen: boolean; onOpen: () => void; onClose: () => void; onInput?: (event: KeyboardEvent) => void; searchButtonRef?: React.RefObject; } function isEditingContent(event: KeyboardEvent): boolean { const element = event.target as HTMLElement; const tagName = element.tagName; return element.isContentEditable || tagName === 'INPUT' || tagName === 'SELECT' || tagName === 'TEXTAREA'; } export function useDocSearchKeyboardEvents({ isOpen, onOpen, onClose, onInput, searchButtonRef, }: UseDocSearchKeyboardEventsProps): void { React.useEffect(() => { function onKeyDown(event: KeyboardEvent): void { if ( (event.code === 'Escape' && isOpen) || // The `Cmd+K` shortcut both opens and closes the modal. // We need to check for `event.key` because it can be `undefined` with // Chrome's autofill feature. // See https://github.com/paperjs/paper.js/issues/1398 (event.key?.toLowerCase() === 'k' && (event.metaKey || event.ctrlKey)) || // The `/` shortcut opens but doesn't close the modal because it's // a character. (!isEditingContent(event) && event.key === '/' && !isOpen) ) { event.preventDefault(); if (isOpen) { onClose(); } else if (!document.body.classList.contains('DocSearch--active')) { // We check that no other DocSearch modal is showing before opening // another one. onOpen(); } return; } if (searchButtonRef && searchButtonRef.current === document.activeElement && onInput) { if (/[a-zA-Z0-9]/.test(String.fromCharCode(event.keyCode))) { onInput(event); } } } window.addEventListener('keydown', onKeyDown); return (): void => { window.removeEventListener('keydown', onKeyDown); }; }, [isOpen, onOpen, onClose, onInput, searchButtonRef]); }