From b1a4a93e739ebc9b866ff2e35159113d1ebd01fc Mon Sep 17 00:00:00 2001 From: rektdeckard Date: Wed, 29 Jul 2020 12:23:53 -0400 Subject: [PATCH] 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. --- src/components/ColorInput/ColorInput.css | 46 ++++++++++++++++++++++++ src/components/ColorInput/ColorInput.tsx | 18 +++++++--- src/hooks/useThrottled.ts | 31 ++++++++++++++++ 3 files changed, 90 insertions(+), 5 deletions(-) create mode 100644 src/components/ColorInput/ColorInput.css create mode 100644 src/hooks/useThrottled.ts diff --git a/src/components/ColorInput/ColorInput.css b/src/components/ColorInput/ColorInput.css new file mode 100644 index 0000000..02f40d8 --- /dev/null +++ b/src/components/ColorInput/ColorInput.css @@ -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; +} \ No newline at end of file diff --git a/src/components/ColorInput/ColorInput.tsx b/src/components/ColorInput/ColorInput.tsx index 25d4e65..f1e06db 100644 --- a/src/components/ColorInput/ColorInput.tsx +++ b/src/components/ColorInput/ColorInput.tsx @@ -1,29 +1,37 @@ import React from "react"; import { useRecoilState } from "recoil"; +import TinyColor from "tinycolor2"; import { iconColorAtom } from "../../state/atoms"; +import useThrottled from "../../hooks/useThrottled"; +import "./ColorInput.css"; type ColorInputProps = {}; const ColorInput: React.FC = () => { const [color, setColor] = useRecoilState(iconColorAtom); - + const isDark = TinyColor(color).isDark(); + const handleColorChange = (event: React.ChangeEvent) => { const { target: { value: color }, } = event; if (color[0] === "#") setColor(color); }; - + + const throttledColorChange = useThrottled(handleColorChange, 100, [handleColorChange]) + return ( -
+
+ {color}
); }; diff --git a/src/hooks/useThrottled.ts b/src/hooks/useThrottled.ts new file mode 100644 index 0000000..beb40c1 --- /dev/null +++ b/src/hooks/useThrottled.ts @@ -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(false); + const callbackRef = useRef(callback); + + // Update the callback to be used, if it ever changes + useEffect(() => { + callbackRef.current = callback; + }, [callback]); + + return useCallback( + (...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] + ); +};