ColorInput: add throttling and update style to match spec

This patch adds throttling to events emitted by the native color-picker,
preventing noticeable lag caused by rapid state updates. It is currently
throttled to one event per 100ms, but we may want to play with this
value in the future! For not it feels smooth.
This commit is contained in:
rektdeckard
2020-07-29 12:23:53 -04:00
parent 2ad1e01641
commit b1a4a93e73
3 changed files with 90 additions and 5 deletions

View File

@@ -0,0 +1,46 @@
.color-picker {
position: relative;
overflow: hidden;
height: 48px;
min-width: 112px;
flex: 1;
border: none;
outline: none;
-webkit-appearance: none;
}
.color-picker span {
position: absolute;
margin: 0;
top: 50%;
left: 50%;
-ms-transform: translate(-50%, -50%);
transform: translate(-50%, -50%);
user-select: none;
pointer-events: none;
}
input.color-input {
position: relative;
height: 100%;
width: 96%;
box-sizing: border-box;
border: none;
border-radius: 8px;
margin: 0 0 0 4px;
padding: 0;
}
input.color-input::-webkit-color-swatch-wrapper {
padding: 0;
}
input.color-input::-webkit-color-swatch {
border: none;
border-radius: 100%;
}
input.color-input:focus {
outline: none;
border: 2px solid white;
}

View File

@@ -1,12 +1,16 @@
import React from "react"; import React from "react";
import { useRecoilState } from "recoil"; import { useRecoilState } from "recoil";
import TinyColor from "tinycolor2";
import { iconColorAtom } from "../../state/atoms"; import { iconColorAtom } from "../../state/atoms";
import useThrottled from "../../hooks/useThrottled";
import "./ColorInput.css";
type ColorInputProps = {}; type ColorInputProps = {};
const ColorInput: React.FC<ColorInputProps> = () => { const ColorInput: React.FC<ColorInputProps> = () => {
const [color, setColor] = useRecoilState(iconColorAtom); const [color, setColor] = useRecoilState(iconColorAtom);
const isDark = TinyColor(color).isDark();
const handleColorChange = (event: React.ChangeEvent<HTMLInputElement>) => { const handleColorChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const { const {
@@ -15,15 +19,19 @@ const ColorInput: React.FC<ColorInputProps> = () => {
if (color[0] === "#") setColor(color); if (color[0] === "#") setColor(color);
}; };
const throttledColorChange = useThrottled(handleColorChange, 100, [handleColorChange])
return ( return (
<div> <div className="color-picker">
<input <input
id="color-picker" className="color-input"
aria-label="Icon Color" aria-label="Icon Color"
style={{ backgroundColor: color }}
type="color" type="color"
onChange={handleColorChange} onChange={throttledColorChange}
value={color} value={color}
/> />
<span style={{ color: isDark ? "white" : "black" }}>{color}</span>
</div> </div>
); );
}; };

31
src/hooks/useThrottled.ts Normal file
View File

@@ -0,0 +1,31 @@
import { useRef, useEffect, useCallback } from "react";
type Callback = (...args: any) => void;
export default (
callback: Callback,
delay: number,
dependencies: any[] = []
) => {
const throttleRef = useRef<Boolean>(false);
const callbackRef = useRef<Callback>(callback);
// Update the callback to be used, if it ever changes
useEffect(() => {
callbackRef.current = callback;
}, [callback]);
return useCallback<Callback>(
(...args) => {
if (throttleRef.current) return;
throttleRef.current = true;
callbackRef.current(...args);
setTimeout(() => {
throttleRef.current = false;
}, delay);
},
// eslint-disable-next-line react-hooks/exhaustive-deps
[delay, ...dependencies]
);
};