diff --git a/src/components/ColorPicker.css b/src/components/ColorPicker.css index d652e076e..25194298e 100644 --- a/src/components/ColorPicker.css +++ b/src/components/ColorPicker.css @@ -107,3 +107,11 @@ margin-right: 0.25rem; border: 1px solid #dee2e6; } + +.color-picker-keybinding { + position: absolute; + bottom: 2px; + right: 2px; + font-size: 0.7em; + color: #ccc; +} diff --git a/src/components/ColorPicker.tsx b/src/components/ColorPicker.tsx index 765f7f780..7e670e8d0 100644 --- a/src/components/ColorPicker.tsx +++ b/src/components/ColorPicker.tsx @@ -8,6 +8,14 @@ import { t } from "../i18n"; // This is a narrow reimplementation of the awesome react-color Twitter component // https://github.com/casesandberg/react-color/blob/master/src/components/twitter/Twitter.js +// Unfortunately, we can't detect keyboard layout in the browser. So this will +// only work well for QWERTY but not AZERTY or others... +const keyBindings = [ + ["1", "2", "3", "4", "5"], + ["q", "w", "e", "r", "t"], + ["a", "s", "d", "f", "g"], +].flat(); + const Picker = function({ colors, color, @@ -22,12 +30,18 @@ const Picker = function({ label: string; }) { const firstItem = React.useRef(); + const activeItem = React.useRef(); + const gallery = React.useRef(); const colorInput = React.useRef(); React.useEffect(() => { // After the component is first mounted // focus on first input - if (firstItem.current) firstItem.current.focus(); + if (activeItem.current) { + activeItem.current.focus(); + } else if (firstItem.current) { + firstItem.current.focus(); + } }, []); const handleKeyDown = (e: React.KeyboardEvent) => { @@ -44,10 +58,41 @@ const Picker = function({ e.preventDefault(); } } - } else if (e.key === KEYS.ESCAPE) { + } else if ( + e.key === KEYS.ARROW_RIGHT || + e.key === KEYS.ARROW_LEFT || + e.key === KEYS.ARROW_UP || + e.key === KEYS.ARROW_DOWN + ) { + const { activeElement } = document; + const index = Array.prototype.indexOf.call( + gallery!.current!.children, + activeElement, + ); + if (index !== -1) { + const length = gallery!.current!.children.length; + const nextIndex = + e.key === KEYS.ARROW_RIGHT + ? (index + 1) % length + : e.key === KEYS.ARROW_LEFT + ? (length + index - 1) % length + : e.key === KEYS.ARROW_DOWN + ? (index + 5) % length + : e.key === KEYS.ARROW_UP + ? (length + index - 5) % length + : index; + (gallery!.current!.children![nextIndex] as any).focus(); + } + e.preventDefault(); + } else if (keyBindings.includes(e.key.toLowerCase())) { + const index = keyBindings.indexOf(e.key.toLowerCase()); + (gallery!.current!.children![index] as any).focus(); + e.preventDefault(); + } else if (e.key === KEYS.ESCAPE || e.key === KEYS.ENTER) { + e.preventDefault(); onClose(); - e.nativeEvent.stopImmediatePropagation(); } + e.nativeEvent.stopImmediatePropagation(); }; return ( @@ -61,26 +106,37 @@ const Picker = function({
-
- {colors.map((color, i) => ( +
{ + if (el) gallery.current = el; + }} + > + {colors.map((_color, i) => ( ))}