UNPKG

@redocly/theme

Version:

Shared UI components lib

101 lines (85 loc) 3.11 kB
import { useCallback, useMemo } from 'react'; import { getUserAgent } from '../utils/get-user-agent'; type Action = 'selectAll' | 'escape' | 'clear' | 'enter' | 'paste'; type ActionHandlers = { [key in `on${Capitalize<Action>}`]?: (e: React.KeyboardEvent<HTMLInputElement>) => void; }; type KeyboardCommand = { match: (event: React.KeyboardEvent<HTMLInputElement>) => boolean; execute: (event: React.KeyboardEvent<HTMLInputElement>) => void; }; export function useInputKeyCommands(actionHandlers?: ActionHandlers) { // MacOS uses Command key instead of Ctrl const ctrlKey = useMemo(() => (getUserAgent().includes('Mac') ? 'metaKey' : 'ctrlKey'), []); const isSelectAll = useCallback( (event: React.KeyboardEvent<HTMLInputElement>) => { return event.key === 'a' && event[ctrlKey]; }, [ctrlKey], ); const isPaste = useCallback( (event: React.KeyboardEvent<HTMLInputElement>) => { return event.key === 'v' && event[ctrlKey]; }, [ctrlKey], ); const commands = useMemo<KeyboardCommand[]>( () => [ { match: (event) => event.key === 'Enter', execute: (event) => actionHandlers?.onEnter?.(event), }, { match: (event) => event.key === 'Escape', execute: (event) => { actionHandlers?.onEscape?.(event); if (event.currentTarget?.selectionStart !== event.currentTarget?.selectionEnd) { event.stopPropagation(); removeSelection(event); } }, }, { match: isSelectAll, execute: (event) => actionHandlers?.onSelectAll?.(event), }, { match: isPaste, execute: (event) => actionHandlers?.onPaste?.(event), }, { match: (event) => { if (!event.currentTarget?.value) return false; const selectionLength = (event.currentTarget?.selectionEnd ?? 0) - (event.currentTarget?.selectionStart ?? 0); const isFullValueSelected = event.currentTarget?.value.length === selectionLength; const isModifyAction = isPrintableCharacter(event) || isPaste(event) || isDelete(event); return isFullValueSelected && isModifyAction; }, execute: (event) => actionHandlers?.onClear?.(event), }, ], [actionHandlers, isPaste, isSelectAll], ); const onKeyDown = useCallback( (event: React.KeyboardEvent<HTMLInputElement>) => { for (const command of commands) { if (command.match(event)) { command.execute(event); } } }, [commands], ); return { onKeyDown }; } function removeSelection(event: React.KeyboardEvent<HTMLInputElement>) { const selectionEnd = event.currentTarget.selectionEnd ?? 0; event.currentTarget.setSelectionRange(selectionEnd, selectionEnd); } function isPrintableCharacter(event: React.KeyboardEvent<HTMLInputElement>) { return event.key.length === 1 && !event.ctrlKey && !event.metaKey; } function isDelete(event: React.KeyboardEvent<HTMLInputElement>) { return event.key === 'Backspace' || event.key === 'Delete'; }