diff --git a/.gitignore b/.gitignore index 22a4e1c..887f834 100644 --- a/.gitignore +++ b/.gitignore @@ -25,4 +25,5 @@ dist-ssr .env /dist -/docs \ No newline at end of file +/docs + diff --git a/package.json b/package.json index 49c12bf..581a8ca 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "private": true, "version": "0.0.0", "type": "module", - "homepage": "index.html#", + "homepage": "https://stella0320.github.io/drawdb-extention/#", "scripts": { "dev": "vite", "build": "vite build", diff --git a/src/components/EditorCanvas/Canvas.jsx b/src/components/EditorCanvas/Canvas.jsx index 2a67516..87dab2c 100644 --- a/src/components/EditorCanvas/Canvas.jsx +++ b/src/components/EditorCanvas/Canvas.jsx @@ -21,7 +21,7 @@ import { useNotes, useLayout, } from "../../hooks"; -import { useTranslation } from "react-i18next"; +import { useTranslation, withSSR } from "react-i18next"; import { useEventListener } from "usehooks-ts"; import { areFieldsCompatible } from "../../utils/utils"; @@ -80,17 +80,18 @@ export default function Canvas() { pointerX: 0, pointerY: 0, }); - + + const [noteResize, setNoteResize] = useState({ id: -1, dir: "none" }); /** * @param {PointerEvent} e * @param {*} id * @param {ObjectType[keyof ObjectType]} type */ const handlePointerDownOnElement = (e, id, type) => { + if (selectedElement.open && !layout.sidebar) return; if (!e.isPrimary) return; - if (type === ObjectType.TABLE) { const table = tables.find((t) => t.id === id); setGrabOffset({ @@ -141,20 +142,21 @@ export default function Canvas() { */ const handlePointerMove = (e) => { if (selectedElement.open && !layout.sidebar) return; - if (!e.isPrimary) return; - + if (linking) { setLinkingLine({ ...linkingLine, endX: pointer.spaces.diagram.x, endY: pointer.spaces.diagram.y, }); + } else if ( panning.isPanning && dragging.element === ObjectType.NONE && - areaResize.id === -1 + areaResize.id === -1 && noteResize.id === -1 ) { + // 滑鼠抓住桌布 if (!settings.panning) { return; } @@ -174,21 +176,73 @@ export default function Canvas() { x: pointer.spaces.diagram.x + grabOffset.x, y: pointer.spaces.diagram.y + grabOffset.y, }); + } else if ( dragging.element === ObjectType.AREA && dragging.id >= 0 && areaResize.id === -1 ) { + // 滑鼠抓住Area物件 updateArea(dragging.id, { x: pointer.spaces.diagram.x + grabOffset.x, y: pointer.spaces.diagram.y + grabOffset.y, }); - } else if (dragging.element === ObjectType.NOTE && dragging.id >= 0) { + + } else if ( + dragging.element === ObjectType.NOTE && + dragging.id >= 0 && + noteResize.id === -1 + ) { + // 滑鼠抓住Note物件 updateNote(dragging.id, { x: pointer.spaces.diagram.x + grabOffset.x, y: pointer.spaces.diagram.y + grabOffset.y, }); + + } else if (noteResize.id !== -1) { + // 滑鼠調整Note物件大小 + if (noteResize.dir === "none") return; + let newDims = { ...initCoords }; + delete newDims.pointerX; + delete newDims.pointerY; + setPanning((old) => ({ ...old, isPanning: false })); + switch (noteResize.dir) { + case "br": + newDims.width = pointer.spaces.diagram.x - initCoords.x; + newDims.height = pointer.spaces.diagram.y - initCoords.y; + break; + case "tl": + newDims.x = pointer.spaces.diagram.x; + newDims.y = pointer.spaces.diagram.y; + newDims.width = + initCoords.x + initCoords.width - pointer.spaces.diagram.x; + newDims.height = + initCoords.y + initCoords.height - pointer.spaces.diagram.y; + break; + case "tr": + newDims.y = pointer.spaces.diagram.y; + newDims.width = pointer.spaces.diagram.x - initCoords.x; + newDims.height = + initCoords.y + initCoords.height - pointer.spaces.diagram.y; + break; + case "bl": + newDims.x = pointer.spaces.diagram.x; + newDims.width = + initCoords.x + initCoords.width - pointer.spaces.diagram.x; + newDims.height = pointer.spaces.diagram.y - initCoords.y; + break; + } + + // Note寬度最大500px,最想寬度50px + if (newDims.width > 500) { + newDims.width = 500; + } else if (newDims.width < 50) { + newDims.width = 50; + } + + updateNote(noteResize.id, {... newDims }); } else if (areaResize.id !== -1) { + // 滑鼠調整Area物件大小 if (areaResize.dir === "none") return; let newDims = { ...initCoords }; delete newDims.pointerX; @@ -221,7 +275,8 @@ export default function Canvas() { newDims.height = pointer.spaces.diagram.y - initCoords.y; break; } - + + updateArea(areaResize.id, { ...newDims }); } }; @@ -283,6 +338,15 @@ export default function Canvas() { ); }; + const didNoteResize = (id) => { + return !( + notes[id].x === initCoords.x && + notes[id].y === initCoords.y && + notes[id].width === initCoords.width && + notes[id].height === initCoords.height + ); + }; + const didPan = () => !(transform.pan.x === panning.x && transform.pan.y === panning.y); @@ -387,8 +451,31 @@ export default function Canvas() { }, ]); setRedoStack([]); + } else if (noteResize.id !== -1 && didNoteResize(noteResize.id)) { + setUndoStack((prev) => [ + ...prev, + { + action: Action.EDIT, + element: ObjectType.NOTE, + aid: noteResize.id, + undo: { + ...notes[noteResize.id], + x: initCoords.x, + y: initCoords.y, + width: initCoords.width, + height: initCoords.height, + }, + redo: notes[noteResize.id], + message: t("edit_note", { + areaName: notes[noteResize.id].name, + extra: "[resize]", + }), + }, + ]); + setRedoStack([]); } setAreaResize({ id: -1, dir: "none" }); + setNoteResize({ id: -1, dir: "none" }); setInitCoords({ x: 0, y: 0, @@ -581,6 +668,8 @@ export default function Canvas() { onPointerDown={(e) => handlePointerDownOnElement(e, n.id, ObjectType.NOTE) } + setResize={setNoteResize} + setInitCoords={setInitCoords} /> ))} diff --git a/src/components/EditorCanvas/Note.jsx b/src/components/EditorCanvas/Note.jsx index cff89ba..fd1b3f6 100644 --- a/src/components/EditorCanvas/Note.jsx +++ b/src/components/EditorCanvas/Note.jsx @@ -13,27 +13,44 @@ import { IconCheckboxTick, } from "@douyinfe/semi-icons"; import { + useCanvas, useLayout, useUndoRedo, useSelect, useNotes, useSaveState, + useSettings, } from "../../hooks"; import { useTranslation } from "react-i18next"; -export default function Note({ data, onPointerDown }) { - const w = 180; +export default function Note({data, onPointerDown, setResize, setInitCoords}) { const r = 3; const fold = 24; const [editField, setEditField] = useState({}); const [hovered, setHovered] = useState(false); + const { settings } = useSettings(); const { layout } = useLayout(); const { t } = useTranslation(); const { setSaveState } = useSaveState(); const { updateNote, deleteNote } = useNotes(); const { setUndoStack, setRedoStack } = useUndoRedo(); const { selectedElement, setSelectedElement } = useSelect(); - + const { + pointer: { + spaces: { diagram: pointer }, + }, + } = useCanvas(); + const handleResize = (e, dir) => { + setResize({ id: data.id, dir: dir }); + setInitCoords({ + x: data.x, + y: data.y, + width: data.width, + height: data.height, + pointerX: pointer.x, + pointerY: pointer.y, + }); + }; const handleChange = (e) => { const textarea = document.getElementById(`note_${data.id}`); textarea.style.height = "0"; @@ -92,15 +109,16 @@ export default function Note({ data, onPointerDown }) { }} > 0 ? data.width : 0} + height={data.height > 0 ? data.height : 0} onPointerDown={onPointerDown} >
@@ -307,6 +325,50 @@ export default function Note({ data, onPointerDown }) { />
+ {hovered && ( + <> + e.isPrimary && handleResize(e, "tl")} + /> + e.isPrimary && handleResize(e, "tr")} + /> + e.isPrimary && handleResize(e, "bl")} + /> + e.isPrimary && handleResize(e, "br")} + /> + + )} ); } diff --git a/src/context/NotesContext.jsx b/src/context/NotesContext.jsx index 712b0d1..1ecfddf 100644 --- a/src/context/NotesContext.jsx +++ b/src/context/NotesContext.jsx @@ -22,6 +22,7 @@ export default function NotesContextProvider({ children }) { }); } else { const height = 88; + const width = 180; setNotes((prev) => [ ...prev, { @@ -32,6 +33,7 @@ export default function NotesContextProvider({ children }) { content: "", color: defaultNoteTheme, height, + width }, ]); } diff --git a/src/manifest b/src/manifest deleted file mode 100644 index 8beea78..0000000 --- a/src/manifest +++ /dev/null @@ -1,11 +0,0 @@ -{ - "manifest_version": 3, - "name": "Drawdb.io Extension", - "version": "1.0", - "content_security_policy": { - "extension_pages": "script-src 'self' 'wasm-unsafe-eval'; object-src 'self';" - }, - "action": { - "default_popup": "index.html" - } -} \ No newline at end of file