|
@@ -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<HTMLButtonElement>();
|
|
|
+ const activeItem = React.useRef<HTMLButtonElement>();
|
|
|
+ const gallery = React.useRef<HTMLDivElement>();
|
|
|
const colorInput = React.useRef<HTMLInputElement>();
|
|
|
|
|
|
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({
|
|
|
<div className="color-picker-triangle-shadow"></div>
|
|
|
<div className="color-picker-triangle"></div>
|
|
|
<div className="color-picker-content">
|
|
|
- <div className="colors-gallery">
|
|
|
- {colors.map((color, i) => (
|
|
|
+ <div
|
|
|
+ className="colors-gallery"
|
|
|
+ ref={el => {
|
|
|
+ if (el) gallery.current = el;
|
|
|
+ }}
|
|
|
+ >
|
|
|
+ {colors.map((_color, i) => (
|
|
|
<button
|
|
|
className="color-picker-swatch"
|
|
|
onClick={() => {
|
|
|
- onChange(color);
|
|
|
+ onChange(_color);
|
|
|
}}
|
|
|
- title={color}
|
|
|
- aria-label={color}
|
|
|
- style={{ backgroundColor: color }}
|
|
|
- key={color}
|
|
|
+ title={`${_color} — ${keyBindings[i].toUpperCase()}`}
|
|
|
+ aria-label={_color}
|
|
|
+ aria-keyshortcuts={keyBindings[i]}
|
|
|
+ style={{ backgroundColor: _color }}
|
|
|
+ key={_color}
|
|
|
ref={el => {
|
|
|
- if (i === 0 && el) firstItem.current = el;
|
|
|
+ if (el && i === 0) firstItem.current = el;
|
|
|
+ if (el && _color === color) activeItem.current = el;
|
|
|
+ }}
|
|
|
+ onFocus={() => {
|
|
|
+ onChange(_color);
|
|
|
}}
|
|
|
>
|
|
|
- {color === "transparent" ? (
|
|
|
+ {_color === "transparent" ? (
|
|
|
<div className="color-picker-transparent"></div>
|
|
|
) : (
|
|
|
undefined
|
|
|
)}
|
|
|
+ <span className="color-picker-keybinding">{keyBindings[i]}</span>
|
|
|
</button>
|
|
|
))}
|
|
|
</div>
|