more undo redo for resize and panning
This commit is contained in:
parent
be66cf6e84
commit
16bc0edd36
1
package-lock.json
generated
1
package-lock.json
generated
@ -22,6 +22,7 @@
|
|||||||
"html-to-image": "^1.11.11",
|
"html-to-image": "^1.11.11",
|
||||||
"jsonschema": "^1.4.1",
|
"jsonschema": "^1.4.1",
|
||||||
"jspdf": "^2.5.1",
|
"jspdf": "^2.5.1",
|
||||||
|
"lodash": "^4.17.21",
|
||||||
"node-sql-parser": "^4.7.0",
|
"node-sql-parser": "^4.7.0",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dnd": "^16.0.1",
|
"react-dnd": "^16.0.1",
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
"html-to-image": "^1.11.11",
|
"html-to-image": "^1.11.11",
|
||||||
"jsonschema": "^1.4.1",
|
"jsonschema": "^1.4.1",
|
||||||
"jspdf": "^2.5.1",
|
"jspdf": "^2.5.1",
|
||||||
|
"lodash": "^4.17.21",
|
||||||
"node-sql-parser": "^4.7.0",
|
"node-sql-parser": "^4.7.0",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dnd": "^16.0.1",
|
"react-dnd": "^16.0.1",
|
||||||
|
@ -46,9 +46,12 @@ export default function Canvas(props) {
|
|||||||
tableId: -1,
|
tableId: -1,
|
||||||
field: -2,
|
field: -2,
|
||||||
});
|
});
|
||||||
const [panning, setPanning] = useState(false);
|
const [panning, setPanning] = useState({ state: false, x: 0, y: 0 });
|
||||||
const [panOffset, setPanOffset] = useState({ x: 0, y: 0 });
|
const [panOffset, setPanOffset] = useState({ x: 0, y: 0 });
|
||||||
const [areaResize, setAreaResize] = useState({ id: -1, dir: "none" });
|
const [areaResize, setAreaResize] = useState({
|
||||||
|
id: -1,
|
||||||
|
dir: "none",
|
||||||
|
});
|
||||||
const [initCoords, setInitCoords] = useState({
|
const [initCoords, setInitCoords] = useState({
|
||||||
x: 0,
|
x: 0,
|
||||||
y: 0,
|
y: 0,
|
||||||
@ -114,7 +117,7 @@ export default function Canvas(props) {
|
|||||||
endY: (e.clientY - offsetY) / settings.zoom - settings.pan.y,
|
endY: (e.clientY - offsetY) / settings.zoom - settings.pan.y,
|
||||||
});
|
});
|
||||||
} else if (
|
} else if (
|
||||||
panning &&
|
panning.state &&
|
||||||
dragging.element === ObjectType.NONE &&
|
dragging.element === ObjectType.NONE &&
|
||||||
areaResize.id === -1
|
areaResize.id === -1
|
||||||
) {
|
) {
|
||||||
@ -150,7 +153,7 @@ export default function Canvas(props) {
|
|||||||
let newHeight = initCoords.height;
|
let newHeight = initCoords.height;
|
||||||
const mouseX = e.clientX / settings.zoom;
|
const mouseX = e.clientX / settings.zoom;
|
||||||
const mouseY = e.clientY / settings.zoom;
|
const mouseY = e.clientY / settings.zoom;
|
||||||
setPanning(false);
|
setPanning({ state: false, x: 0, y: 0 });
|
||||||
if (areaResize.dir === "br") {
|
if (areaResize.dir === "br") {
|
||||||
newWidth = initCoords.width + (mouseX - initCoords.mouseX);
|
newWidth = initCoords.width + (mouseX - initCoords.mouseX);
|
||||||
newHeight = initCoords.height + (mouseY - initCoords.mouseY);
|
newHeight = initCoords.height + (mouseY - initCoords.mouseY);
|
||||||
@ -187,7 +190,7 @@ export default function Canvas(props) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleMouseDown = (e) => {
|
const handleMouseDown = (e) => {
|
||||||
setPanning(true);
|
setPanning({ state: true, ...settings.pan });
|
||||||
setPanOffset({ x: e.clientX, y: e.clientY });
|
setPanOffset({ x: e.clientX, y: e.clientY });
|
||||||
setCursor("grabbing");
|
setCursor("grabbing");
|
||||||
};
|
};
|
||||||
@ -214,6 +217,18 @@ export default function Canvas(props) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const didResize = (id) => {
|
||||||
|
return !(
|
||||||
|
areas[id].x === initCoords.x &&
|
||||||
|
areas[id].y === initCoords.y &&
|
||||||
|
areas[id].width === initCoords.width &&
|
||||||
|
areas[id].height === initCoords.height
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const didPan = () =>
|
||||||
|
!(settings.pan.x === panning.x && settings.pan.y === panning.y);
|
||||||
|
|
||||||
const handleMouseUp = (e) => {
|
const handleMouseUp = (e) => {
|
||||||
if (coordsDidUpdate(dragging.element)) {
|
if (coordsDidUpdate(dragging.element)) {
|
||||||
setUndoStack((prev) => [
|
setUndoStack((prev) => [
|
||||||
@ -229,10 +244,44 @@ export default function Canvas(props) {
|
|||||||
setRedoStack([]);
|
setRedoStack([]);
|
||||||
}
|
}
|
||||||
setDragging({ element: ObjectType.NONE, id: -1, prevX: 0, prevY: 0 });
|
setDragging({ element: ObjectType.NONE, id: -1, prevX: 0, prevY: 0 });
|
||||||
setPanning(false);
|
// NOTE: consider just saving the offset to sub and add in undo redo
|
||||||
|
if (panning.state && didPan()) {
|
||||||
|
setUndoStack((prev) => [
|
||||||
|
...prev,
|
||||||
|
{
|
||||||
|
action: Action.PAN,
|
||||||
|
data: {
|
||||||
|
undo: { x: panning.x, y: panning.y },
|
||||||
|
redo: settings.pan,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
setRedoStack([]);
|
||||||
|
}
|
||||||
|
setPanning({ state: false, x: 0, y: 0 });
|
||||||
setCursor("default");
|
setCursor("default");
|
||||||
if (linking) handleLinking();
|
if (linking) handleLinking();
|
||||||
setLinking(false);
|
setLinking(false);
|
||||||
|
if (areaResize.id !== -1 && didResize(areaResize.id)) {
|
||||||
|
setUndoStack((prev) => [
|
||||||
|
...prev,
|
||||||
|
{
|
||||||
|
action: Action.EDIT,
|
||||||
|
element: ObjectType.AREA,
|
||||||
|
data: {
|
||||||
|
undo: {
|
||||||
|
...areas[areaResize.id],
|
||||||
|
x: initCoords.x,
|
||||||
|
y: initCoords.y,
|
||||||
|
width: initCoords.width,
|
||||||
|
height: initCoords.height,
|
||||||
|
},
|
||||||
|
redo: areas[areaResize.id],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
setRedoStack([]);
|
||||||
|
}
|
||||||
setAreaResize({ id: -1, dir: "none" });
|
setAreaResize({ id: -1, dir: "none" });
|
||||||
setInitCoords({
|
setInitCoords({
|
||||||
x: 0,
|
x: 0,
|
||||||
|
@ -158,6 +158,25 @@ export default function ControlPanel(props) {
|
|||||||
addArea(false, a.data);
|
addArea(false, a.data);
|
||||||
}
|
}
|
||||||
setRedoStack((prev) => [...prev, a]);
|
setRedoStack((prev) => [...prev, a]);
|
||||||
|
} else if (a.action === Action.EDIT) {
|
||||||
|
if (a.element === ObjectType.AREA) {
|
||||||
|
setAreas((prev) =>
|
||||||
|
prev.map((n) => {
|
||||||
|
if (n.id === a.data.undo.id) {
|
||||||
|
return a.data.undo;
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
setRedoStack((prev) => [...prev, a]);
|
||||||
|
} else if (a.action === Action.PAN) {
|
||||||
|
console.log(a)
|
||||||
|
setSettings((prev) => ({
|
||||||
|
...prev,
|
||||||
|
pan: a.data.undo
|
||||||
|
}));
|
||||||
|
setRedoStack((prev) => [...prev, a]);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -206,6 +225,24 @@ export default function ControlPanel(props) {
|
|||||||
deleteArea(a.data.id, false);
|
deleteArea(a.data.id, false);
|
||||||
}
|
}
|
||||||
setUndoStack((prev) => [...prev, a]);
|
setUndoStack((prev) => [...prev, a]);
|
||||||
|
} else if (a.action === Action.EDIT) {
|
||||||
|
if (a.element === ObjectType.AREA) {
|
||||||
|
setAreas((prev) =>
|
||||||
|
prev.map((n) => {
|
||||||
|
if (n.id === a.data.redo.id) {
|
||||||
|
return a.data.redo;
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
setUndoStack((prev) => [...prev, a]);
|
||||||
|
} else if (a.action === Action.PAN) {
|
||||||
|
setSettings((prev) => ({
|
||||||
|
...prev,
|
||||||
|
pan: a.data.redo
|
||||||
|
}));
|
||||||
|
setUndoStack((prev) => [...prev, a]);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ export default function Note(props) {
|
|||||||
const textarea = document.getElementById(`note_${props.data.id}`);
|
const textarea = document.getElementById(`note_${props.data.id}`);
|
||||||
textarea.style.height = "0";
|
textarea.style.height = "0";
|
||||||
textarea.style.height = textarea.scrollHeight + "px";
|
textarea.style.height = textarea.scrollHeight + "px";
|
||||||
const newHeight = textarea.scrollHeight + 16 + 20 + 4;
|
const newHeight = textarea.scrollHeight + 41;
|
||||||
setNotes((prev) =>
|
setNotes((prev) =>
|
||||||
prev.map((n) => {
|
prev.map((n) => {
|
||||||
if (n.id === props.data.id) {
|
if (n.id === props.data.id) {
|
||||||
@ -25,17 +25,17 @@ export default function Note(props) {
|
|||||||
return (
|
return (
|
||||||
<g>
|
<g>
|
||||||
<path
|
<path
|
||||||
d={`M${props.data.x + fold} ${props.data.y} L${
|
d={`M${props.data.x + fold} ${props.data.y} L${props.data.x + w - r} ${
|
||||||
|
props.data.y
|
||||||
|
} A${r} ${r} 0 0 1 ${props.data.x + w} ${props.data.y + r} L${
|
||||||
|
props.data.x + w
|
||||||
|
} ${props.data.y + props.data.height - r} A${r} ${r} 0 0 1 ${
|
||||||
props.data.x + w - r
|
props.data.x + w - r
|
||||||
} ${props.data.y} A${r} ${r} 0 0 1 ${props.data.x + w} ${
|
} ${props.data.y + props.data.height} L${props.data.x + r} ${
|
||||||
props.data.y + r
|
|
||||||
} L${props.data.x + w} ${
|
|
||||||
props.data.y + props.data.height - r
|
|
||||||
} A${r} ${r} 0 0 1 ${props.data.x + w - r} ${
|
|
||||||
props.data.y + props.data.height
|
props.data.y + props.data.height
|
||||||
} L${props.data.x + r} ${props.data.y + props.data.height} A${r} ${r} 0 0 1 ${
|
} A${r} ${r} 0 0 1 ${props.data.x} ${
|
||||||
props.data.x
|
props.data.y + props.data.height - r
|
||||||
} ${props.data.y + props.data.height - r} L${props.data.x} ${props.data.y + fold}`}
|
} L${props.data.x} ${props.data.y + fold}`}
|
||||||
fill={props.data.color}
|
fill={props.data.color}
|
||||||
stroke="#665b25"
|
stroke="#665b25"
|
||||||
strokeLinejoin="round"
|
strokeLinejoin="round"
|
||||||
@ -62,12 +62,14 @@ export default function Note(props) {
|
|||||||
onMouseDown={props.onMouseDown}
|
onMouseDown={props.onMouseDown}
|
||||||
>
|
>
|
||||||
<div className="text-gray-900 select-none w-full h-full cursor-move px-3 py-2">
|
<div className="text-gray-900 select-none w-full h-full cursor-move px-3 py-2">
|
||||||
<label htmlFor={`note_${props.data.id}`} className="ms-5">{props.data.title}</label>
|
<label htmlFor={`note_${props.data.id}`} className="ms-5">
|
||||||
|
{props.data.title}
|
||||||
|
</label>
|
||||||
<textarea
|
<textarea
|
||||||
id={`note_${props.data.id}`}
|
id={`note_${props.data.id}`}
|
||||||
value={props.data.content}
|
value={props.data.content}
|
||||||
onInput={handleChange}
|
onChange={handleChange}
|
||||||
className="mt-1 w-full resize-none outline-none overflow-y-hidden border-none select-none"
|
className="w-full resize-none outline-none overflow-y-hidden border-none select-none"
|
||||||
style={{ backgroundColor: props.data.color }}
|
style={{ backgroundColor: props.data.color }}
|
||||||
></textarea>
|
></textarea>
|
||||||
</div>
|
</div>
|
||||||
|
@ -102,13 +102,7 @@ export default function TableOverview(props) {
|
|||||||
/>
|
/>
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={8}>
|
<Col span={8}>
|
||||||
<Button
|
<Button icon={<IconPlus />} block onClick={() => addTable(true)}>
|
||||||
icon={<IconPlus />}
|
|
||||||
block
|
|
||||||
onClick={() => {
|
|
||||||
addTable(true);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Add table
|
Add table
|
||||||
</Button>
|
</Button>
|
||||||
</Col>
|
</Col>
|
||||||
|
@ -80,6 +80,8 @@ const Action = {
|
|||||||
ADD: 0,
|
ADD: 0,
|
||||||
MOVE: 1,
|
MOVE: 1,
|
||||||
DELETE: 2,
|
DELETE: 2,
|
||||||
|
EDIT: 3,
|
||||||
|
PAN: 4,
|
||||||
};
|
};
|
||||||
|
|
||||||
export {
|
export {
|
||||||
|
@ -325,6 +325,20 @@ export default function Editor(props) {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const editNote = (id, values, addToHistory=true) => {
|
||||||
|
setNotes((prev) =>
|
||||||
|
prev.map((t) => {
|
||||||
|
if (t.id === id) {
|
||||||
|
return {
|
||||||
|
...t,
|
||||||
|
...values,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return t;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
document.title = "Editor - drawDB";
|
document.title = "Editor - drawDB";
|
||||||
}, []);
|
}, []);
|
||||||
@ -348,7 +362,7 @@ export default function Editor(props) {
|
|||||||
value={{ areas, setAreas, moveArea, addArea, deleteArea }}
|
value={{ areas, setAreas, moveArea, addArea, deleteArea }}
|
||||||
>
|
>
|
||||||
<NoteContext.Provider
|
<NoteContext.Provider
|
||||||
value={{ notes, setNotes, moveNote, addNote, deleteNote }}
|
value={{ notes, setNotes, moveNote, addNote, deleteNote, editNote }}
|
||||||
>
|
>
|
||||||
<TabContext.Provider value={{ tab, setTab }}>
|
<TabContext.Provider value={{ tab, setTab }}>
|
||||||
<SettingsContext.Provider value={{ settings, setSettings }}>
|
<SettingsContext.Provider value={{ settings, setSettings }}>
|
||||||
|
Loading…
Reference in New Issue
Block a user