@redocly/theme
Version:
Shared UI components lib
101 lines (85 loc) • 3.11 kB
text/typescript
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';
}