fix: several eyeDropper fixes (#7002)

This commit is contained in:
David Luzar 2023-09-17 13:24:50 +02:00 committed by GitHub
parent 1f94f204dd
commit c1952fd6cc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 51 additions and 27 deletions

View File

@ -15,9 +15,15 @@ interface ColorInputProps {
color: string; color: string;
onChange: (color: string) => void; onChange: (color: string) => void;
label: string; label: string;
eyeDropperType: "strokeColor" | "backgroundColor";
} }
export const ColorInput = ({ color, onChange, label }: ColorInputProps) => { export const ColorInput = ({
color,
onChange,
label,
eyeDropperType,
}: ColorInputProps) => {
const device = useDevice(); const device = useDevice();
const [innerValue, setInnerValue] = useState(color); const [innerValue, setInnerValue] = useState(color);
const [activeSection, setActiveColorPickerSection] = useAtom( const [activeSection, setActiveColorPickerSection] = useAtom(
@ -110,6 +116,7 @@ export const ColorInput = ({ color, onChange, label }: ColorInputProps) => {
: { : {
keepOpenOnAlt: false, keepOpenOnAlt: false,
onSelect: (color) => onChange(color), onSelect: (color) => onChange(color),
previewType: eyeDropperType,
}, },
) )
} }

View File

@ -82,7 +82,14 @@ const ColorPickerPopupContent = ({
const { container } = useExcalidrawContainer(); const { container } = useExcalidrawContainer();
const { isMobile, isLandscape } = useDevice(); const { isMobile, isLandscape } = useDevice();
const colorInputJSX = ( const eyeDropperType =
type === "canvasBackground"
? undefined
: type === "elementBackground"
? "backgroundColor"
: "strokeColor";
const colorInputJSX = eyeDropperType && (
<div> <div>
<PickerHeading>{t("colorPicker.hexCode")}</PickerHeading> <PickerHeading>{t("colorPicker.hexCode")}</PickerHeading>
<ColorInput <ColorInput
@ -91,6 +98,7 @@ const ColorPickerPopupContent = ({
onChange={(color) => { onChange={(color) => {
onChange(color); onChange(color);
}} }}
eyeDropperType={eyeDropperType}
/> />
</div> </div>
); );
@ -140,7 +148,7 @@ const ColorPickerPopupContent = ({
alignOffset={-16} alignOffset={-16}
sideOffset={20} sideOffset={20}
style={{ style={{
zIndex: 9999, zIndex: "var(--zIndex-layerUI)",
backgroundColor: "var(--popup-bg-color)", backgroundColor: "var(--popup-bg-color)",
maxWidth: "208px", maxWidth: "208px",
maxHeight: window.innerHeight, maxHeight: window.innerHeight,
@ -152,7 +160,7 @@ const ColorPickerPopupContent = ({
"0px 7px 14px rgba(0, 0, 0, 0.05), 0px 0px 3.12708px rgba(0, 0, 0, 0.0798), 0px 0px 0.931014px rgba(0, 0, 0, 0.1702)", "0px 7px 14px rgba(0, 0, 0, 0.05), 0px 0px 3.12708px rgba(0, 0, 0, 0.0798), 0px 0px 0.931014px rgba(0, 0, 0, 0.1702)",
}} }}
> >
{palette ? ( {palette && eyeDropperType ? (
<Picker <Picker
palette={palette} palette={palette}
color={color} color={color}
@ -165,6 +173,7 @@ const ColorPickerPopupContent = ({
state = state || { state = state || {
keepOpenOnAlt: true, keepOpenOnAlt: true,
onSelect: onChange, onSelect: onChange,
previewType: eyeDropperType,
}; };
state.keepOpenOnAlt = true; state.keepOpenOnAlt = true;
return state; return state;
@ -175,6 +184,7 @@ const ColorPickerPopupContent = ({
: { : {
keepOpenOnAlt: false, keepOpenOnAlt: false,
onSelect: onChange, onSelect: onChange,
previewType: eyeDropperType,
}; };
}); });
}} }}

View File

@ -4,7 +4,7 @@
position: absolute; position: absolute;
width: 100%; width: 100%;
height: 100%; height: 100%;
z-index: 2; z-index: var(--zIndex-eyeDropperBackdrop);
touch-action: none; touch-action: none;
} }
@ -21,7 +21,7 @@
width: 3rem; width: 3rem;
height: 3rem; height: 3rem;
position: fixed; position: fixed;
z-index: 999999; z-index: var(--zIndex-eyeDropperPreview);
border-radius: 1rem; border-radius: 1rem;
border: 1px solid var(--default-border-color); border: 1px solid var(--default-border-color);
filter: var(--theme-filter); filter: var(--theme-filter);

View File

@ -1,7 +1,7 @@
import { atom } from "jotai"; import { atom } from "jotai";
import { useEffect, useRef } from "react"; import { useEffect, useRef } from "react";
import { createPortal } from "react-dom"; import { createPortal } from "react-dom";
import { COLOR_PALETTE, rgbToHex } from "../colors"; import { rgbToHex } from "../colors";
import { EVENT } from "../constants"; import { EVENT } from "../constants";
import { useUIAppState } from "../context/ui-appState"; import { useUIAppState } from "../context/ui-appState";
import { mutateElement } from "../element/mutateElement"; import { mutateElement } from "../element/mutateElement";
@ -18,8 +18,8 @@ import "./EyeDropper.scss";
type EyeDropperProperties = { type EyeDropperProperties = {
keepOpenOnAlt: boolean; keepOpenOnAlt: boolean;
swapPreviewOnAlt?: boolean; swapPreviewOnAlt?: boolean;
onSelect?: (color: string, event: PointerEvent) => void; onSelect: (color: string, event: PointerEvent) => void;
previewType?: "strokeColor" | "backgroundColor"; previewType: "strokeColor" | "backgroundColor";
}; };
export const activeEyeDropperAtom = atom<null | EyeDropperProperties>(null); export const activeEyeDropperAtom = atom<null | EyeDropperProperties>(null);
@ -28,13 +28,8 @@ export const EyeDropper: React.FC<{
onCancel: () => void; onCancel: () => void;
onSelect: Required<EyeDropperProperties>["onSelect"]; onSelect: Required<EyeDropperProperties>["onSelect"];
swapPreviewOnAlt?: EyeDropperProperties["swapPreviewOnAlt"]; swapPreviewOnAlt?: EyeDropperProperties["swapPreviewOnAlt"];
previewType?: EyeDropperProperties["previewType"]; previewType: EyeDropperProperties["previewType"];
}> = ({ }> = ({ onCancel, onSelect, swapPreviewOnAlt, previewType }) => {
onCancel,
onSelect,
swapPreviewOnAlt,
previewType = "backgroundColor",
}) => {
const eyeDropperContainer = useCreatePortalContainer({ const eyeDropperContainer = useCreatePortalContainer({
className: "excalidraw-eye-dropper-backdrop", className: "excalidraw-eye-dropper-backdrop",
parentSelector: ".excalidraw-eye-dropper-container", parentSelector: ".excalidraw-eye-dropper-container",
@ -58,11 +53,27 @@ export const EyeDropper: React.FC<{
return; return;
} }
let currentColor: string = COLOR_PALETTE.black;
let isHoldingPointerDown = false; let isHoldingPointerDown = false;
const ctx = app.canvas.getContext("2d")!; const ctx = app.canvas.getContext("2d")!;
const getCurrentColor = ({
clientX,
clientY,
}: {
clientX: number;
clientY: number;
}) => {
const pixel = ctx.getImageData(
(clientX - appState.offsetLeft) * window.devicePixelRatio,
(clientY - appState.offsetTop) * window.devicePixelRatio,
1,
1,
).data;
return rgbToHex(pixel[0], pixel[1], pixel[2]);
};
const mouseMoveListener = ({ const mouseMoveListener = ({
clientX, clientX,
clientY, clientY,
@ -76,14 +87,7 @@ export const EyeDropper: React.FC<{
colorPreviewDiv.style.top = `${clientY + 20}px`; colorPreviewDiv.style.top = `${clientY + 20}px`;
colorPreviewDiv.style.left = `${clientX + 20}px`; colorPreviewDiv.style.left = `${clientX + 20}px`;
const pixel = ctx.getImageData( const currentColor = getCurrentColor({ clientX, clientY });
(clientX - appState.offsetLeft) * window.devicePixelRatio,
(clientY - appState.offsetTop) * window.devicePixelRatio,
1,
1,
).data;
currentColor = rgbToHex(pixel[0], pixel[1], pixel[2]);
if (isHoldingPointerDown) { if (isHoldingPointerDown) {
for (const element of metaStuffRef.current.selectedElements) { for (const element of metaStuffRef.current.selectedElements) {
@ -125,7 +129,7 @@ export const EyeDropper: React.FC<{
event.stopImmediatePropagation(); event.stopImmediatePropagation();
event.preventDefault(); event.preventDefault();
onSelect(currentColor, event); onSelect(getCurrentColor(event), event);
}; };
const keyDownListener = (event: KeyboardEvent) => { const keyDownListener = (event: KeyboardEvent) => {

View File

@ -6,6 +6,8 @@
--zIndex-interactiveCanvas: 2; --zIndex-interactiveCanvas: 2;
--zIndex-wysiwyg: 3; --zIndex-wysiwyg: 3;
--zIndex-layerUI: 4; --zIndex-layerUI: 4;
--zIndex-eyeDropperBackdrop: 5;
--zIndex-eyeDropperPreview: 6;
--zIndex-modal: 1000; --zIndex-modal: 1000;
--zIndex-popup: 1001; --zIndex-popup: 1001;

View File

@ -507,8 +507,9 @@ export type AppProps = Merge<
* in the app, eg Manager. Factored out into a separate type to keep DRY. */ * in the app, eg Manager. Factored out into a separate type to keep DRY. */
export type AppClassProperties = { export type AppClassProperties = {
props: AppProps; props: AppProps;
canvas: HTMLCanvasElement;
interactiveCanvas: HTMLCanvasElement | null; interactiveCanvas: HTMLCanvasElement | null;
/** static canvas */
canvas: HTMLCanvasElement;
focusContainer(): void; focusContainer(): void;
library: Library; library: Library;
imageCache: Map< imageCache: Map<