mirror of
https://github.com/drawdb-io/drawdb.git
synced 2025-07-18 10:11:24 +00:00
Lock notes and subject areas (#480)
This commit is contained in:
@@ -1,6 +1,11 @@
|
|||||||
import { useMemo, useRef, useState } from "react";
|
import { useMemo, useRef, useState } from "react";
|
||||||
import { Button, Popover, Input, ColorPicker } from "@douyinfe/semi-ui";
|
import { Button, Popover, Input, ColorPicker } from "@douyinfe/semi-ui";
|
||||||
import { IconEdit, IconDeleteStroked } from "@douyinfe/semi-icons";
|
import {
|
||||||
|
IconEdit,
|
||||||
|
IconDeleteStroked,
|
||||||
|
IconLock,
|
||||||
|
IconUnlock,
|
||||||
|
} from "@douyinfe/semi-icons";
|
||||||
import { Tab, Action, ObjectType, State } from "../../data/constants";
|
import { Tab, Action, ObjectType, State } from "../../data/constants";
|
||||||
import {
|
import {
|
||||||
useCanvas,
|
useCanvas,
|
||||||
@@ -30,6 +35,7 @@ export default function Area({
|
|||||||
const { layout } = useLayout();
|
const { layout } = useLayout();
|
||||||
const { settings } = useSettings();
|
const { settings } = useSettings();
|
||||||
const { setSaveState } = useSaveState();
|
const { setSaveState } = useSaveState();
|
||||||
|
const { updateArea } = useAreas();
|
||||||
const { selectedElement, setSelectedElement, bulkSelectedElements } =
|
const { selectedElement, setSelectedElement, bulkSelectedElements } =
|
||||||
useSelect();
|
useSelect();
|
||||||
|
|
||||||
@@ -45,6 +51,10 @@ export default function Area({
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const lockUnlockArea = () => {
|
||||||
|
updateArea(data.id, { locked: !data.locked });
|
||||||
|
};
|
||||||
|
|
||||||
const edit = () => {
|
const edit = () => {
|
||||||
if (layout.sidebar) {
|
if (layout.sidebar) {
|
||||||
setSelectedElement((prev) => ({
|
setSelectedElement((prev) => ({
|
||||||
@@ -123,25 +133,36 @@ export default function Area({
|
|||||||
{data.name}
|
{data.name}
|
||||||
</div>
|
</div>
|
||||||
{(isHovered || (areaIsOpen() && !layout.sidebar)) && (
|
{(isHovered || (areaIsOpen() && !layout.sidebar)) && (
|
||||||
<Popover
|
<div className="flex items-center gap-1.5">
|
||||||
visible={areaIsOpen() && !layout.sidebar}
|
|
||||||
onClickOutSide={onClickOutSide}
|
|
||||||
stopPropagation
|
|
||||||
content={<EditPopoverContent data={data} />}
|
|
||||||
trigger="custom"
|
|
||||||
position="rightTop"
|
|
||||||
showArrow
|
|
||||||
>
|
|
||||||
<Button
|
<Button
|
||||||
icon={<IconEdit />}
|
icon={data.locked ? <IconLock /> : <IconUnlock />}
|
||||||
size="small"
|
size="small"
|
||||||
theme="solid"
|
theme="solid"
|
||||||
style={{
|
style={{
|
||||||
backgroundColor: "#2F68ADB3",
|
backgroundColor: "#2F68ADB3",
|
||||||
}}
|
}}
|
||||||
onClick={edit}
|
onClick={lockUnlockArea}
|
||||||
/>
|
/>
|
||||||
</Popover>
|
<Popover
|
||||||
|
visible={areaIsOpen() && !layout.sidebar}
|
||||||
|
onClickOutSide={onClickOutSide}
|
||||||
|
stopPropagation
|
||||||
|
content={<EditPopoverContent data={data} />}
|
||||||
|
trigger="custom"
|
||||||
|
position="rightTop"
|
||||||
|
showArrow
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
icon={<IconEdit />}
|
||||||
|
size="small"
|
||||||
|
theme="solid"
|
||||||
|
style={{
|
||||||
|
backgroundColor: "#2F68ADB3",
|
||||||
|
}}
|
||||||
|
onClick={edit}
|
||||||
|
/>
|
||||||
|
</Popover>
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -126,6 +126,8 @@ export default function Canvas() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
areas.forEach((area) => {
|
areas.forEach((area) => {
|
||||||
|
if (area.locked) return;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
isInsideRect(
|
isInsideRect(
|
||||||
{
|
{
|
||||||
@@ -145,6 +147,8 @@ export default function Canvas() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
notes.forEach((note) => {
|
notes.forEach((note) => {
|
||||||
|
if (note.locked) return;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
isInsideRect(
|
isInsideRect(
|
||||||
{
|
{
|
||||||
@@ -333,11 +337,17 @@ export default function Canvas() {
|
|||||||
dragging.id !== null &&
|
dragging.id !== null &&
|
||||||
areaResize.id === -1
|
areaResize.id === -1
|
||||||
) {
|
) {
|
||||||
|
const area = areas.find((t) => t.id === dragging.id);
|
||||||
|
if (area.locked) return;
|
||||||
|
|
||||||
updateArea(dragging.id, {
|
updateArea(dragging.id, {
|
||||||
x: pointer.spaces.diagram.x + grabOffset.x,
|
x: pointer.spaces.diagram.x + grabOffset.x,
|
||||||
y: pointer.spaces.diagram.y + grabOffset.y,
|
y: pointer.spaces.diagram.y + grabOffset.y,
|
||||||
});
|
});
|
||||||
} else if (dragging.element === ObjectType.NOTE && dragging.id !== null) {
|
} else if (dragging.element === ObjectType.NOTE && dragging.id !== null) {
|
||||||
|
const note = notes.find((t) => t.id === dragging.id);
|
||||||
|
if (note.locked) return;
|
||||||
|
|
||||||
updateNote(dragging.id, {
|
updateNote(dragging.id, {
|
||||||
x: pointer.spaces.diagram.x + grabOffset.x,
|
x: pointer.spaces.diagram.x + grabOffset.x,
|
||||||
y: pointer.spaces.diagram.y + grabOffset.y,
|
y: pointer.spaces.diagram.y + grabOffset.y,
|
||||||
|
@@ -1,7 +1,12 @@
|
|||||||
import { useMemo, useState } from "react";
|
import { useMemo, useState } from "react";
|
||||||
import { Action, ObjectType, Tab, State } from "../../data/constants";
|
import { Action, ObjectType, Tab, State } from "../../data/constants";
|
||||||
import { Input, Button, Popover, ColorPicker } from "@douyinfe/semi-ui";
|
import { Input, Button, Popover, ColorPicker } from "@douyinfe/semi-ui";
|
||||||
import { IconEdit, IconDeleteStroked } from "@douyinfe/semi-icons";
|
import {
|
||||||
|
IconEdit,
|
||||||
|
IconDeleteStroked,
|
||||||
|
IconLock,
|
||||||
|
IconUnlock,
|
||||||
|
} from "@douyinfe/semi-icons";
|
||||||
import {
|
import {
|
||||||
useLayout,
|
useLayout,
|
||||||
useUndoRedo,
|
useUndoRedo,
|
||||||
@@ -56,6 +61,10 @@ export default function Note({ data, onPointerDown }) {
|
|||||||
setRedoStack([]);
|
setRedoStack([]);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const lockUnlockNote = () => {
|
||||||
|
updateNote(data.id, { locked: !data.locked });
|
||||||
|
};
|
||||||
|
|
||||||
const edit = () => {
|
const edit = () => {
|
||||||
setSelectedElement((prev) => ({
|
setSelectedElement((prev) => ({
|
||||||
...prev,
|
...prev,
|
||||||
@@ -152,7 +161,16 @@ export default function Note({ data, onPointerDown }) {
|
|||||||
selectedElement.id === data.id &&
|
selectedElement.id === data.id &&
|
||||||
selectedElement.open &&
|
selectedElement.open &&
|
||||||
!layout.sidebar)) && (
|
!layout.sidebar)) && (
|
||||||
<div>
|
<div className="flex items-center gap-1.5">
|
||||||
|
<Button
|
||||||
|
icon={data.locked ? <IconLock /> : <IconUnlock />}
|
||||||
|
size="small"
|
||||||
|
theme="solid"
|
||||||
|
style={{
|
||||||
|
backgroundColor: "#2F68ADB3",
|
||||||
|
}}
|
||||||
|
onClick={lockUnlockNote}
|
||||||
|
/>
|
||||||
<Popover
|
<Popover
|
||||||
visible={
|
visible={
|
||||||
selectedElement.element === ObjectType.NOTE &&
|
selectedElement.element === ObjectType.NOTE &&
|
||||||
|
@@ -33,6 +33,7 @@ export default function AreasContextProvider({ children }) {
|
|||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
color: defaultBlue,
|
color: defaultBlue,
|
||||||
|
locked: false,
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
@@ -30,6 +30,7 @@ export default function NotesContextProvider({ children }) {
|
|||||||
y: transform.pan.y - height / 2,
|
y: transform.pan.y - height / 2,
|
||||||
title: `note_${prev.length}`,
|
title: `note_${prev.length}`,
|
||||||
content: "",
|
content: "",
|
||||||
|
locked: false,
|
||||||
color: defaultNoteTheme,
|
color: defaultNoteTheme,
|
||||||
height,
|
height,
|
||||||
},
|
},
|
||||||
|
@@ -68,6 +68,7 @@ export const areaSchema = {
|
|||||||
y: { type: "number" },
|
y: { type: "number" },
|
||||||
width: { type: "number" },
|
width: { type: "number" },
|
||||||
height: { type: "number" },
|
height: { type: "number" },
|
||||||
|
locked: { type: "boolean" },
|
||||||
color: { type: "string", pattern: "^#[0-9a-fA-F]{6}$" },
|
color: { type: "string", pattern: "^#[0-9a-fA-F]{6}$" },
|
||||||
},
|
},
|
||||||
required: ["id", "name", "x", "y", "width", "height", "color"],
|
required: ["id", "name", "x", "y", "width", "height", "color"],
|
||||||
@@ -83,6 +84,7 @@ export const noteSchema = {
|
|||||||
content: { type: "string" },
|
content: { type: "string" },
|
||||||
color: { type: "string", pattern: "^#[0-9a-fA-F]{6}$" },
|
color: { type: "string", pattern: "^#[0-9a-fA-F]{6}$" },
|
||||||
height: { type: "number" },
|
height: { type: "number" },
|
||||||
|
locked: { type: "boolean" },
|
||||||
},
|
},
|
||||||
required: ["id", "x", "y", "title", "content", "color", "height"],
|
required: ["id", "x", "y", "title", "content", "color", "height"],
|
||||||
};
|
};
|
||||||
|
Reference in New Issue
Block a user