mirror of
https://github.com/storytold/artcraft.git
synced 2026-10-21 09:22:41 -04:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8b824db1e5 |
@@ -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<React.HTMLAttributes<HTMLDivElement>, "color" | "onChange" | "onChangeCapture">;
|
||||
interface ColorPickerBaseProps<T extends string> 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<ColorPickerBaseProps<string>>) => 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<string | null> => {
|
||||
// @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 (
|
||||
<div className={"relative"}>
|
||||
<Picker
|
||||
className={pickerClassName}
|
||||
color={color}
|
||||
onChange={onPickerChange}
|
||||
onMouseDown={handleOnMouseDown}
|
||||
onMouseUp={handleOnMouseUp}
|
||||
/>
|
||||
<Button
|
||||
className={twMerge([
|
||||
"rounded-full m-1 size-8 text-ui-controls bg-transparent hover:bg-black/70 hover:text-white z-20 shadow-none hover:shadow-xl duration-200",
|
||||
dropperClassName,
|
||||
"absolute left-0 top-0",
|
||||
isDragging ? "opacity-0 -z-10" : ""
|
||||
])}
|
||||
icon={faEyedropper}
|
||||
onClick={handleDropperClick}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -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 (
|
||||
<Popover className="relative">
|
||||
{({ open, close }) => (
|
||||
@@ -122,18 +133,12 @@ export const ColorPicker = ({
|
||||
}
|
||||
static
|
||||
>
|
||||
<HexAlphaColorPicker
|
||||
className="overflow-hidden"
|
||||
<HexEyedropPicker
|
||||
pickerClassName="overflow-hidden"
|
||||
color={currColor}
|
||||
onChange={(color) => {
|
||||
setStates({
|
||||
currColor: color,
|
||||
textInput: color.substring(1),
|
||||
});
|
||||
if (streamChanges) {
|
||||
onChange(color);
|
||||
}
|
||||
}}
|
||||
onPickerChange={handleColorChange}
|
||||
onDropperChange={handleColorChange}
|
||||
Picker={HexAlphaColorPicker}
|
||||
/>
|
||||
<Input
|
||||
style={{ width: "198px" }}
|
||||
|
||||
@@ -15,6 +15,8 @@ import { BRUSH_MAX_SIZE, BRUSH_MIN_SIZE, paintBrushSize, setPaintBrushSize } fro
|
||||
import { Button } from "../Button";
|
||||
import { Input } from "../Input";
|
||||
import { Slider } from "../Slider";
|
||||
import { EyeDropper } from "react-eyedrop";
|
||||
import { HexEyedropPicker } from "../HexEyedropPicker";
|
||||
|
||||
export const BackgroundMenu = ({
|
||||
color: prevColor,
|
||||
@@ -83,6 +85,16 @@ export const BackgroundMenu = ({
|
||||
return hexColorRegex.test(color);
|
||||
};
|
||||
|
||||
const handleColorChange = (color: string) => {
|
||||
setStates({
|
||||
currColor: color,
|
||||
textInput: color.substring(1),
|
||||
});
|
||||
if (streamChanges) {
|
||||
onChange(color);
|
||||
}
|
||||
}
|
||||
|
||||
// Rerender the component when these signals change:
|
||||
useSignals();
|
||||
|
||||
@@ -125,18 +137,12 @@ export const BackgroundMenu = ({
|
||||
}
|
||||
static
|
||||
>
|
||||
<HexColorPicker
|
||||
className="overflow-hidden"
|
||||
<HexEyedropPicker
|
||||
pickerClassName="overflow-hidden"
|
||||
color={currColor}
|
||||
onChange={(color) => {
|
||||
setStates({
|
||||
currColor: color,
|
||||
textInput: color.substring(1),
|
||||
});
|
||||
if (streamChanges) {
|
||||
onChange(color);
|
||||
}
|
||||
}}
|
||||
onPickerChange={handleColorChange}
|
||||
onDropperChange={handleColorChange}
|
||||
Picker={HexColorPicker}
|
||||
/>
|
||||
<Input
|
||||
style={{ width: "198px" }}
|
||||
|
||||
@@ -15,6 +15,7 @@ import { BRUSH_MAX_SIZE, BRUSH_MIN_SIZE, paintBrushSize, setPaintBrushSize } fro
|
||||
import { Button } from "../Button";
|
||||
import { Input } from "../Input";
|
||||
import { Slider } from "../Slider";
|
||||
import { HexEyedropPicker } from "../HexEyedropPicker";
|
||||
|
||||
export const PaintModeMenu = ({
|
||||
color: prevColor,
|
||||
@@ -83,6 +84,16 @@ export const PaintModeMenu = ({
|
||||
return hexColorRegex.test(color);
|
||||
};
|
||||
|
||||
const handleColorChange = (color: string) => {
|
||||
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
|
||||
>
|
||||
<HexAlphaColorPicker
|
||||
className="overflow-hidden"
|
||||
<HexEyedropPicker
|
||||
pickerClassName="overflow-hidden"
|
||||
color={currColor}
|
||||
onChange={(color) => {
|
||||
setStates({
|
||||
currColor: color,
|
||||
textInput: color.substring(1),
|
||||
});
|
||||
if (streamChanges) {
|
||||
onChange(color);
|
||||
}
|
||||
}}
|
||||
onPickerChange={handleColorChange}
|
||||
onDropperChange={handleColorChange}
|
||||
Picker={HexAlphaColorPicker}
|
||||
/>
|
||||
<Input
|
||||
style={{ width: "198px" }}
|
||||
|
||||
Reference in New Issue
Block a user