From 701d84602fb4a235bf54e5c55fdcd38c78ddbd2e Mon Sep 17 00:00:00 2001 From: Tanish Manku Date: Wed, 26 Feb 2025 15:39:34 -0800 Subject: [PATCH] Add an eyedrop to all color pickers (#431) --- .../src/components/ui/HexEyedropPicker.tsx | 87 +++++++++++++++++++ .../components/ui/TextEditor/ColorPicker.tsx | 27 +++--- .../ui/ToolbarMain/BackgroundMenu.tsx | 28 +++--- .../ui/ToolbarMain/PaintModeMenu.tsx | 27 +++--- 4 files changed, 136 insertions(+), 33 deletions(-) create mode 100644 frontend/realtime/src/components/ui/HexEyedropPicker.tsx diff --git a/frontend/realtime/src/components/ui/HexEyedropPicker.tsx b/frontend/realtime/src/components/ui/HexEyedropPicker.tsx new file mode 100644 index 0000000000..d2e1791b0b --- /dev/null +++ b/frontend/realtime/src/components/ui/HexEyedropPicker.tsx @@ -0,0 +1,87 @@ +import { MouseEvent, MouseEventHandler, useState } from "react"; +import { HexColorPicker } from "react-colorful"; +import { EyeDropper } from "react-eyedrop"; +import { Button } from "./Button"; +import { faEyedropper } from "@fortawesome/pro-solid-svg-icons"; +import { twMerge } from "tailwind-merge"; + +type ColorPickerHTMLAttributes = Omit, "color" | "onChange" | "onChangeCapture">; +interface ColorPickerBaseProps extends ColorPickerHTMLAttributes { + color: T; + onChange: (newColor: T) => void; +} + +export const HexEyedropPicker = ({ + color, + onDropperChange, + dropperClassName, + pickerClassName, + onPickerChange, + Picker +}: { + color: string; + onDropperChange?: (newColor: string) => void; + dropperClassName?: string; + pickerClassName?: string; + onPickerChange: (newColor: string) => void; + Picker: (props: Partial>) => JSX.Element; +}) => { + + const handleDropperClick = () => { + if (!('EyeDropper' in window)) { + // NOTE: This shouldn't happen cuz we're using Chromium + console.error("EyeDropper not found in window"); + return; + } + + const startEyeDrop = async (): Promise => { + // @ts-ignore-next-line + const dropper = new window.EyeDropper(); + + try { + const result = await dropper.open(); + console.log(result); + return result.sRGBHex; + } catch (err) { + // User cancelled the picker + return null; + } + } + + startEyeDrop().then((newColor) => { + if (newColor) { + onDropperChange?.(newColor); + } + }); + } + + const [isDragging, setIsDragging] = useState(false); + const handleOnMouseDown = () => { + setIsDragging(true); + } + const handleOnMouseUp = () => { + setIsDragging(false); + } + + return ( +
+ +
+ ) +} diff --git a/frontend/realtime/src/components/ui/TextEditor/ColorPicker.tsx b/frontend/realtime/src/components/ui/TextEditor/ColorPicker.tsx index 197f5079d7..fc55f775b5 100644 --- a/frontend/realtime/src/components/ui/TextEditor/ColorPicker.tsx +++ b/frontend/realtime/src/components/ui/TextEditor/ColorPicker.tsx @@ -12,6 +12,7 @@ import { twMerge } from "tailwind-merge"; import { paperWrapperStyles } from "~/components/styles"; import { Button } from "../Button"; import { Input } from "../Input"; +import { HexEyedropPicker } from "../HexEyedropPicker"; export const ColorPicker = ({ color: prevColor, @@ -82,6 +83,16 @@ export const ColorPicker = ({ return hexColorRegex.test(color); }; + const handleColorChange = (color: string) => { + setStates({ + currColor: color, + textInput: color.substring(1), + }); + if (streamChanges) { + onChange(color); + } + } + return ( {({ open, close }) => ( @@ -122,18 +133,12 @@ export const ColorPicker = ({ } static > - { - setStates({ - currColor: color, - textInput: color.substring(1), - }); - if (streamChanges) { - onChange(color); - } - }} + onPickerChange={handleColorChange} + onDropperChange={handleColorChange} + Picker={HexAlphaColorPicker} /> { + setStates({ + currColor: color, + textInput: color.substring(1), + }); + if (streamChanges) { + onChange(color); + } + } + // Rerender the component when these signals change: useSignals(); @@ -130,18 +142,12 @@ export const BackgroundMenu = ({ } static > - { - setStates({ - currColor: color, - textInput: color.substring(1), - }); - if (streamChanges) { - onChange(color); - } - }} + onPickerChange={handleColorChange} + onDropperChange={handleColorChange} + Picker={HexColorPicker} /> { + setStates({ + currColor: color, + textInput: color.substring(1), + }); + if (streamChanges) { + onChange(color); + } + } + // Rerender the component when these signals change: useSignals(); const brushSize = paintBrushSize.value; @@ -126,18 +137,12 @@ export const PaintModeMenu = ({ } static > - { - setStates({ - currColor: color, - textInput: color.substring(1), - }); - if (streamChanges) { - onChange(color); - } - }} + onPickerChange={handleColorChange} + onDropperChange={handleColorChange} + Picker={HexAlphaColorPicker} />