mirror of
https://github.com/drawdb-io/drawdb.git
synced 2025-07-18 10:11:24 +00:00
Fix ColorPicker undo stack additions (#494)
* Fix ColorPicker undo stack additions * make custom component ColorPicker
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { useMemo, useRef, useState } from "react";
|
||||
import { Button, Popover, Input, ColorPicker } from "@douyinfe/semi-ui";
|
||||
import { Button, Popover, Input } from "@douyinfe/semi-ui";
|
||||
import ColorPicker from "../EditorSidePanel/ColorPicker";
|
||||
import {
|
||||
IconEdit,
|
||||
IconDeleteStroked,
|
||||
@@ -220,6 +221,42 @@ function EditPopoverContent({ data }) {
|
||||
const { updateArea, deleteArea } = useAreas();
|
||||
const { setUndoStack, setRedoStack } = useUndoRedo();
|
||||
const { t } = useTranslation();
|
||||
const initialColorRef = useRef(data.color);
|
||||
|
||||
const handleColorPick = (color) => {
|
||||
setUndoStack((prev) => {
|
||||
let undoColor = initialColorRef.current;
|
||||
const lastColorChange = prev.findLast(
|
||||
(e) =>
|
||||
e.element === ObjectType.AREA &&
|
||||
e.aid === data.id &&
|
||||
e.action === Action.EDIT &&
|
||||
e.redo.color,
|
||||
);
|
||||
if (lastColorChange) {
|
||||
undoColor = lastColorChange.redo.color;
|
||||
}
|
||||
|
||||
if (color === undoColor) return prev;
|
||||
|
||||
const newStack = [
|
||||
...prev,
|
||||
{
|
||||
action: Action.EDIT,
|
||||
element: ObjectType.AREA,
|
||||
aid: data.id,
|
||||
undo: { color: undoColor },
|
||||
redo: { color: color },
|
||||
message: t("edit_area", {
|
||||
areaName: data.name,
|
||||
extra: "[color]",
|
||||
}),
|
||||
},
|
||||
];
|
||||
return newStack;
|
||||
});
|
||||
setRedoStack([]);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="popover-theme">
|
||||
@@ -251,26 +288,10 @@ function EditPopoverContent({ data }) {
|
||||
}}
|
||||
/>
|
||||
<ColorPicker
|
||||
onChange={({ hex: color }) => {
|
||||
setUndoStack((prev) => [
|
||||
...prev,
|
||||
{
|
||||
action: Action.EDIT,
|
||||
element: ObjectType.AREA,
|
||||
aid: data.id,
|
||||
undo: { color: data.color },
|
||||
redo: { color },
|
||||
message: t("edit_area", {
|
||||
areaName: data.name,
|
||||
extra: "[color]",
|
||||
}),
|
||||
},
|
||||
]);
|
||||
setRedoStack([]);
|
||||
updateArea(data.id, { color });
|
||||
}}
|
||||
usePopover={true}
|
||||
value={ColorPicker.colorStringToValue(data.color)}
|
||||
value={data.color}
|
||||
onChange={(color) => updateArea(data.id, { color })}
|
||||
onColorPick={(color) => handleColorPick(color)}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex">
|
||||
|
@@ -1,6 +1,7 @@
|
||||
import { useMemo, useState } from "react";
|
||||
import { useMemo, useState, useRef } from "react";
|
||||
import { Action, ObjectType, Tab, State } from "../../data/constants";
|
||||
import { Input, Button, Popover, ColorPicker } from "@douyinfe/semi-ui";
|
||||
import { Input, Button, Popover } from "@douyinfe/semi-ui";
|
||||
import ColorPicker from "../EditorSidePanel/ColorPicker";
|
||||
import {
|
||||
IconEdit,
|
||||
IconDeleteStroked,
|
||||
@@ -27,6 +28,42 @@ export default function Note({ data, onPointerDown }) {
|
||||
const { setUndoStack, setRedoStack } = useUndoRedo();
|
||||
const { selectedElement, setSelectedElement, bulkSelectedElements } =
|
||||
useSelect();
|
||||
const initialColorRef = useRef(data.color);
|
||||
|
||||
const handleColorPick = (color) => {
|
||||
setUndoStack((prev) => {
|
||||
let undoColor = initialColorRef.current;
|
||||
const lastColorChange = prev.findLast(
|
||||
(e) =>
|
||||
e.element === ObjectType.NOTE &&
|
||||
e.nid === data.id &&
|
||||
e.action === Action.EDIT &&
|
||||
e.redo.color,
|
||||
);
|
||||
if (lastColorChange) {
|
||||
undoColor = lastColorChange.redo.color;
|
||||
}
|
||||
|
||||
if (color === undoColor) return prev;
|
||||
|
||||
const newStack = [
|
||||
...prev,
|
||||
{
|
||||
action: Action.EDIT,
|
||||
element: ObjectType.NOTE,
|
||||
nid: data.id,
|
||||
undo: { color: undoColor },
|
||||
redo: { color: color },
|
||||
message: t("edit_note", {
|
||||
noteTitle: data.title,
|
||||
extra: "[color]",
|
||||
}),
|
||||
},
|
||||
];
|
||||
return newStack;
|
||||
});
|
||||
setRedoStack([]);
|
||||
};
|
||||
|
||||
const handleChange = (e) => {
|
||||
const textarea = document.getElementById(`note_${data.id}`);
|
||||
@@ -225,32 +262,11 @@ export default function Note({ data, onPointerDown }) {
|
||||
}}
|
||||
/>
|
||||
<ColorPicker
|
||||
onChange={({ hex: color }) => {
|
||||
setUndoStack((prev) => [
|
||||
...prev,
|
||||
{
|
||||
action: Action.EDIT,
|
||||
element: ObjectType.NOTE,
|
||||
nid: data.id,
|
||||
undo: { color: data.color },
|
||||
redo: { color },
|
||||
message: t("edit_note", {
|
||||
noteTitle: data.title,
|
||||
extra: "[color]",
|
||||
}),
|
||||
},
|
||||
]);
|
||||
setRedoStack([]);
|
||||
updateNote(data.id, { color });
|
||||
}}
|
||||
usePopover={true}
|
||||
value={ColorPicker.colorStringToValue(data.color)}
|
||||
>
|
||||
<div
|
||||
className="h-[32px] w-[32px] rounded-sm"
|
||||
style={{ backgroundColor: data.color }}
|
||||
/>
|
||||
</ColorPicker>
|
||||
value={data.color}
|
||||
onChange={(color) => updateNote(data.id, { color })}
|
||||
onColorPick={(color) => handleColorPick(color)}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex">
|
||||
<Button
|
||||
|
@@ -1,5 +1,6 @@
|
||||
import { useState } from "react";
|
||||
import { Button, Input, ColorPicker } from "@douyinfe/semi-ui";
|
||||
import { useState, useRef } from "react";
|
||||
import { Button, Input } from "@douyinfe/semi-ui";
|
||||
import ColorPicker from "../ColorPicker";
|
||||
import { IconDeleteStroked } from "@douyinfe/semi-icons";
|
||||
import { useAreas, useUndoRedo } from "../../../hooks";
|
||||
import { Action, ObjectType } from "../../../data/constants";
|
||||
@@ -10,6 +11,42 @@ export default function AreaInfo({ data, i }) {
|
||||
const { deleteArea, updateArea } = useAreas();
|
||||
const { setUndoStack, setRedoStack } = useUndoRedo();
|
||||
const [editField, setEditField] = useState({});
|
||||
const initialColorRef = useRef(data.color);
|
||||
|
||||
const handleColorPick = (color) => {
|
||||
setUndoStack((prev) => {
|
||||
let undoColor = initialColorRef.current;
|
||||
const lastColorChange = prev.findLast(
|
||||
(e) =>
|
||||
e.element === ObjectType.AREA &&
|
||||
e.aid === data.id &&
|
||||
e.action === Action.EDIT &&
|
||||
e.redo.color,
|
||||
);
|
||||
if (lastColorChange) {
|
||||
undoColor = lastColorChange.redo.color;
|
||||
}
|
||||
|
||||
if (color === undoColor) return prev;
|
||||
|
||||
const newStack = [
|
||||
...prev,
|
||||
{
|
||||
action: Action.EDIT,
|
||||
element: ObjectType.AREA,
|
||||
aid: i,
|
||||
undo: { color: undoColor },
|
||||
redo: { color: color },
|
||||
message: t("edit_area", {
|
||||
areaName: data.name,
|
||||
extra: "[color]",
|
||||
}),
|
||||
},
|
||||
];
|
||||
return newStack;
|
||||
});
|
||||
setRedoStack([]);
|
||||
};
|
||||
|
||||
return (
|
||||
<div id={`scroll_area_${data.id}`} className="my-3 flex gap-2 items-center">
|
||||
@@ -38,32 +75,11 @@ export default function AreaInfo({ data, i }) {
|
||||
}}
|
||||
/>
|
||||
<ColorPicker
|
||||
onChange={({ hex: color }) => {
|
||||
setUndoStack((prev) => [
|
||||
...prev,
|
||||
{
|
||||
action: Action.EDIT,
|
||||
element: ObjectType.AREA,
|
||||
aid: i,
|
||||
undo: { color: data.color },
|
||||
redo: { color },
|
||||
message: t("edit_area", {
|
||||
areaName: data.name,
|
||||
extra: "[color]",
|
||||
}),
|
||||
},
|
||||
]);
|
||||
setRedoStack([]);
|
||||
updateArea(i, { color });
|
||||
}}
|
||||
usePopover={true}
|
||||
value={ColorPicker.colorStringToValue(data.color)}
|
||||
>
|
||||
<div
|
||||
className="h-[32px] w-[32px] rounded-sm shrink-0"
|
||||
style={{ backgroundColor: data.color }}
|
||||
/>
|
||||
</ColorPicker>
|
||||
value={data.color}
|
||||
onChange={(color) => updateArea(i, { color })}
|
||||
onColorPick={(color) => handleColorPick(color)}
|
||||
/>
|
||||
<Button
|
||||
icon={<IconDeleteStroked />}
|
||||
type="danger"
|
||||
|
41
src/components/EditorSidePanel/ColorPicker.jsx
Normal file
41
src/components/EditorSidePanel/ColorPicker.jsx
Normal file
@@ -0,0 +1,41 @@
|
||||
import { ColorPicker as SemiColorPicker } from "@douyinfe/semi-ui";
|
||||
import { useState } from "react";
|
||||
|
||||
export default function ColorPicker({
|
||||
children,
|
||||
value,
|
||||
onChange,
|
||||
onColorPick,
|
||||
...props
|
||||
}) {
|
||||
const [pickedColor, setPickedColor] = useState(null);
|
||||
|
||||
const handleColorPick = () => {
|
||||
if (pickedColor) onColorPick(pickedColor);
|
||||
setPickedColor(null);
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
onPointerUp={handleColorPick}
|
||||
onBlur={handleColorPick}
|
||||
onMouseLeave={handleColorPick}
|
||||
>
|
||||
<SemiColorPicker
|
||||
{...props}
|
||||
value={SemiColorPicker.colorStringToValue(value)}
|
||||
onChange={({ hex: color }) => {
|
||||
setPickedColor(color);
|
||||
onChange(color);
|
||||
}}
|
||||
>
|
||||
{children || (
|
||||
<div
|
||||
className="h-8 w-8 rounded-md"
|
||||
style={{ backgroundColor: value }}
|
||||
/>
|
||||
)}
|
||||
</SemiColorPicker>
|
||||
</div>
|
||||
);
|
||||
}
|
@@ -1,11 +1,6 @@
|
||||
import { useState } from "react";
|
||||
import {
|
||||
Button,
|
||||
Collapse,
|
||||
TextArea,
|
||||
Input,
|
||||
ColorPicker,
|
||||
} from "@douyinfe/semi-ui";
|
||||
import { useState, useRef } from "react";
|
||||
import { Button, Collapse, TextArea, Input } from "@douyinfe/semi-ui";
|
||||
import ColorPicker from "../ColorPicker";
|
||||
import { IconDeleteStroked } from "@douyinfe/semi-icons";
|
||||
import { Action, ObjectType } from "../../../data/constants";
|
||||
import { useNotes, useUndoRedo } from "../../../hooks";
|
||||
@@ -16,6 +11,42 @@ export default function NoteInfo({ data, nid }) {
|
||||
const { setUndoStack, setRedoStack } = useUndoRedo();
|
||||
const [editField, setEditField] = useState({});
|
||||
const { t } = useTranslation();
|
||||
const initialColorRef = useRef(data.color);
|
||||
|
||||
const handleColorPick = (color) => {
|
||||
setUndoStack((prev) => {
|
||||
let undoColor = initialColorRef.current;
|
||||
const lastColorChange = prev.findLast(
|
||||
(e) =>
|
||||
e.element === ObjectType.NOTE &&
|
||||
e.nid === data.id &&
|
||||
e.action === Action.EDIT &&
|
||||
e.redo.color,
|
||||
);
|
||||
if (lastColorChange) {
|
||||
undoColor = lastColorChange.redo.color;
|
||||
}
|
||||
|
||||
if (color === undoColor) return prev;
|
||||
|
||||
const newStack = [
|
||||
...prev,
|
||||
{
|
||||
action: Action.EDIT,
|
||||
element: ObjectType.NOTE,
|
||||
nid: data.id,
|
||||
undo: { color: undoColor },
|
||||
redo: { color: color },
|
||||
message: t("edit_note", {
|
||||
noteTitle: data.title,
|
||||
extra: "[color]",
|
||||
}),
|
||||
},
|
||||
];
|
||||
return newStack;
|
||||
});
|
||||
setRedoStack([]);
|
||||
};
|
||||
|
||||
return (
|
||||
<Collapse.Panel
|
||||
@@ -93,34 +124,13 @@ export default function NoteInfo({ data, nid }) {
|
||||
}}
|
||||
rows={3}
|
||||
/>
|
||||
<div className="ms-2">
|
||||
<div className="ms-2 flex flex-col gap-2">
|
||||
<ColorPicker
|
||||
onChange={({ hex: color }) => {
|
||||
setUndoStack((prev) => [
|
||||
...prev,
|
||||
{
|
||||
action: Action.EDIT,
|
||||
element: ObjectType.NOTE,
|
||||
nid: nid,
|
||||
undo: { color: data.color },
|
||||
redo: { color },
|
||||
message: t("edit_note", {
|
||||
noteTitle: data.title,
|
||||
extra: "[color]",
|
||||
}),
|
||||
},
|
||||
]);
|
||||
setRedoStack([]);
|
||||
updateNote(nid, { color });
|
||||
}}
|
||||
usePopover={true}
|
||||
value={ColorPicker.colorStringToValue(data.color)}
|
||||
>
|
||||
<div
|
||||
className="h-[32px] w-[32px] rounded-sm shrink-0 mb-2"
|
||||
style={{ backgroundColor: data.color }}
|
||||
/>
|
||||
</ColorPicker>
|
||||
value={data.color}
|
||||
onChange={(color) => updateNote(data.id, { color })}
|
||||
onColorPick={(color) => handleColorPick(color)}
|
||||
/>
|
||||
<Button
|
||||
icon={<IconDeleteStroked />}
|
||||
type="danger"
|
||||
|
@@ -1,12 +1,6 @@
|
||||
import { useState } from "react";
|
||||
import {
|
||||
Collapse,
|
||||
Input,
|
||||
TextArea,
|
||||
Button,
|
||||
Card,
|
||||
ColorPicker,
|
||||
} from "@douyinfe/semi-ui";
|
||||
import { useState, useRef } from "react";
|
||||
import { Collapse, Input, TextArea, Button, Card } from "@douyinfe/semi-ui";
|
||||
import ColorPicker from "../ColorPicker";
|
||||
import { IconDeleteStroked } from "@douyinfe/semi-icons";
|
||||
import { useDiagram, useSaveState, useUndoRedo } from "../../../hooks";
|
||||
import { Action, ObjectType, State } from "../../../data/constants";
|
||||
@@ -23,6 +17,44 @@ export default function TableInfo({ data }) {
|
||||
const { setUndoStack, setRedoStack } = useUndoRedo();
|
||||
const { setSaveState } = useSaveState();
|
||||
const [editField, setEditField] = useState({});
|
||||
const initialColorRef = useRef(data.color);
|
||||
|
||||
const handleColorPick = (color) => {
|
||||
setUndoStack((prev) => {
|
||||
let undoColor = initialColorRef.current;
|
||||
const lastColorChange = prev.findLast(
|
||||
(e) =>
|
||||
e.element === ObjectType.TABLE &&
|
||||
e.tid === data.id &&
|
||||
e.action === Action.EDIT &&
|
||||
e.redo.color,
|
||||
);
|
||||
if (lastColorChange) {
|
||||
undoColor = lastColorChange.redo.color;
|
||||
}
|
||||
|
||||
if (color === undoColor) return prev;
|
||||
|
||||
const newStack = [
|
||||
...prev,
|
||||
{
|
||||
action: Action.EDIT,
|
||||
element: ObjectType.TABLE,
|
||||
component: "self",
|
||||
tid: data.id,
|
||||
undo: { color: undoColor },
|
||||
redo: { color: color },
|
||||
message: t("edit_table", {
|
||||
tableName: data.name,
|
||||
extra: "[color]",
|
||||
}),
|
||||
},
|
||||
];
|
||||
return newStack;
|
||||
});
|
||||
setRedoStack([]);
|
||||
};
|
||||
undefined;
|
||||
|
||||
return (
|
||||
<div>
|
||||
@@ -143,27 +175,10 @@ export default function TableInfo({ data }) {
|
||||
</Card>
|
||||
<div className="flex justify-between items-center gap-1 mb-2">
|
||||
<ColorPicker
|
||||
onChange={({ hex: color }) => {
|
||||
setUndoStack((prev) => [
|
||||
...prev,
|
||||
{
|
||||
action: Action.EDIT,
|
||||
element: ObjectType.TABLE,
|
||||
component: "self",
|
||||
tid: data.id,
|
||||
undo: { color: data.color },
|
||||
redo: { color },
|
||||
message: t("edit_table", {
|
||||
tableName: data.name,
|
||||
extra: "[color]",
|
||||
}),
|
||||
},
|
||||
]);
|
||||
setRedoStack([]);
|
||||
updateTable(data.id, { color });
|
||||
}}
|
||||
usePopover={true}
|
||||
value={ColorPicker.colorStringToValue(data.color)}
|
||||
value={data.color}
|
||||
onChange={(color) => updateTable(data.id, { color })}
|
||||
onColorPick={(color) => handleColorPick(color)}
|
||||
/>
|
||||
<div className="flex gap-1">
|
||||
<Button
|
||||
|
Reference in New Issue
Block a user