Rename diagram

This commit is contained in:
1ilit 2023-10-21 19:45:13 +03:00
parent 69dd120c0d
commit b41942c794
3 changed files with 245 additions and 174 deletions

View File

@ -1,21 +1,11 @@
{ {
"short_name": "React App", "short_name": "drawDB",
"name": "Create React App Sample", "name": "drawDB",
"icons": [ "icons": [
{ {
"src": "favicon.ico", "src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16", "sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon" "type": "image/x-icon"
},
{
"src": "logo192.png",
"type": "image/png",
"sizes": "192x192"
},
{
"src": "logo512.png",
"type": "image/png",
"sizes": "512x512"
} }
], ],
"start_url": ".", "start_url": ".",

View File

@ -12,7 +12,7 @@ function App() {
<Router> <Router>
<Routes> <Routes>
<Route path="/" element={<LandingPage />} /> <Route path="/" element={<LandingPage />} />
<Route path="/editor" element={<Editor name="Untitled" />} /> <Route path="/editor" element={<Editor />} />
<Route path="/survey" element={<Survey />} /> <Route path="/survey" element={<Survey />} />
<Route path="/shortcuts" element={<Shortcuts />} /> <Route path="/shortcuts" element={<Shortcuts />} />
<Route path="/bug_report" element={<BugReport />} /> <Route path="/bug_report" element={<BugReport />} />

View File

@ -10,6 +10,7 @@ import {
IconUndo, IconUndo,
IconRedo, IconRedo,
IconRowsStroked, IconRowsStroked,
IconEdit,
} from "@douyinfe/semi-icons"; } from "@douyinfe/semi-icons";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import icon from "../assets/icon_dark_64.png"; import icon from "../assets/icon_dark_64.png";
@ -65,6 +66,7 @@ export default function ControlPanel(props) {
IMG: 1, IMG: 1,
CODE: 2, CODE: 2,
IMPORT: 3, IMPORT: 3,
RENAME: 4,
}; };
const STATUS = { const STATUS = {
NONE: 0, NONE: 0,
@ -73,6 +75,9 @@ export default function ControlPanel(props) {
OK: 3, OK: 3,
}; };
const [visible, setVisible] = useState(MODAL.NONE); const [visible, setVisible] = useState(MODAL.NONE);
const [title, setTitle] = useState("Untitled Diagram");
const [prevTitle, setPrevTitle] = useState(title);
const [showEditName, setShowEditName] = useState(false);
const [exportData, setExportData] = useState({ const [exportData, setExportData] = useState({
data: null, data: null,
filename: `diagram_${new Date().toISOString()}`, filename: `diagram_${new Date().toISOString()}`,
@ -672,7 +677,10 @@ export default function ControlPanel(props) {
function: () => {}, function: () => {},
}, },
Rename: { Rename: {
function: () => {}, function: () => {
setVisible(MODAL.RENAME);
setPrevTitle(title);
},
}, },
Import: { Import: {
function: fileImport, function: fileImport,
@ -1009,35 +1017,228 @@ export default function ControlPanel(props) {
}); });
useHotkeys("ctrl+alt+w, meta+alt+w", fitWindow, { preventDefault: true }); useHotkeys("ctrl+alt+w, meta+alt+w", fitWindow, { preventDefault: true });
const getModalTitle = () => {
switch (visible) {
case MODAL.IMPORT:
return "Import diagram";
case MODAL.CODE:
return "Export diagram";
case MODAL.IMG:
return "Export image";
case MODAL.RENAME:
return "Rename diagram";
default:
return "";
}
};
const getOkText = () => {
switch (visible) {
case MODAL.IMPORT:
return "Import";
case MODAL.CODE:
case MODAL.IMG:
return "Export";
case MODAL.RENAME:
return "Rename";
default:
return "";
}
};
const getModalOnOk = () => {
switch (visible) {
case MODAL.IMG:
saveAs(
exportData.data,
`${exportData.filename}.${exportData.extension}`
);
return;
case MODAL.CODE:
const blob = new Blob([exportData.data], {
type: "application/json",
});
saveAs(blob, `${exportData.filename}.${exportData.extension}`);
return;
case MODAL.IMPORT:
if (error.type !== STATUS.ERROR) {
setSettings((prev) => ({ ...prev, pan: { x: 0, y: 0 } }));
overwriteDiagram();
setData(null);
setVisible(MODAL.NONE);
setUndoStack([]);
setRedoStack([]);
}
return;
default:
setVisible(MODAL.NONE);
return;
}
};
const importModalBody = () => {
return (
<>
<Upload
action="#"
beforeUpload={({ file, fileList }) => {
const f = fileList[0].fileInstance;
if (!f) {
return;
}
const reader = new FileReader();
reader.onload = function (event) {
let jsonObject = null;
try {
jsonObject = JSON.parse(event.target.result);
} catch (error) {
setError({
type: STATUS.ERROR,
message: "The file contains an error.",
});
return;
}
if (f.type === "application/json") {
if (!jsonDiagramIsValid(jsonObject)) {
setError({
type: STATUS.ERROR,
message:
"The file is missing necessary properties for a diagram.",
});
return;
}
} else if (f.name.split(".").pop() === "ddb") {
if (!ddbDiagramIsValid(jsonObject)) {
setError({
type: STATUS.ERROR,
message:
"The file is missing necessary properties for a diagram.",
});
return;
}
}
setData(jsonObject);
if (diagramIsEmpty()) {
setError({
type: STATUS.OK,
message: "Everything looks good. You can now import.",
});
} else {
setError({
type: STATUS.WARNING,
message:
"The current diagram is not empty. Importing a new diagram will overwrite the current changes.",
});
}
};
reader.readAsText(f);
return {
autoRemove: false,
fileInstance: file.fileInstance,
status: "success",
shouldUpload: false,
};
}}
draggable={true}
dragMainText="Drag and drop the file here or click to upload."
dragSubText="Support json and ddb"
accept="application/json,.ddb"
onRemove={() =>
setError({
type: STATUS.NONE,
message: "",
})
}
onFileChange={() =>
setError({
type: STATUS.NONE,
message: "",
})
}
limit={1}
></Upload>
{error.type === STATUS.ERROR ? (
<Banner
type="danger"
fullMode={false}
description={<div className="text-red-800">{error.message}</div>}
/>
) : error.type === STATUS.OK ? (
<Banner
type="info"
fullMode={false}
description={<div>{error.message}</div>}
/>
) : (
error.type === STATUS.WARNING && (
<Banner
type="warning"
fullMode={false}
description={<div>{error.message}</div>}
/>
)
)}
</>
);
};
const getModalBody = () => {
if (visible === MODAL.IMPORT) {
return importModalBody();
}
if (visible === MODAL.RENAME) {
return (
<Input
placeholder="Diagram name"
value={title}
onChange={(v) => setTitle(v)}
/>
);
}
if (exportData.data !== "" || exportData.data) {
return (
<>
{visible === MODAL.IMG ? (
<Image src={exportData.data} alt="Diagram" height={280} />
) : (
<Editor
height="360px"
value={exportData.data}
language={exportData.extension}
options={{ readOnly: true }}
theme={settings.mode === "light" ? "light" : "vs-dark"}
/>
)}
<div className="text-sm font-semibold mt-2">Filename:</div>
<Input
value={exportData.filename}
placeholder="Filename"
suffix={<div className="p-2">{`.${exportData.extension}`}</div>}
onChange={(value) =>
setExportData((prev) => ({ ...prev, filename: value }))
}
field="filename"
/>
</>
);
} else {
return (
<div className="text-center my-3">
<Spin tip="Loading..." size="large" />
</div>
);
}
};
return ( return (
<> <>
{layout.header && header()} {layout.header && header()}
{toolbar()} {toolbar()}
<Modal <Modal
title={`${visible === MODAL.IMPORT ? "Import" : "Export"} diagram`} title={getModalTitle()}
visible={visible !== MODAL.NONE} visible={visible !== MODAL.NONE}
onOk={() => { onOk={getModalOnOk}
if (visible === MODAL.IMG) {
saveAs(
exportData.data,
`${exportData.filename}.${exportData.extension}`
);
} else if (visible === MODAL.CODE) {
const blob = new Blob([exportData.data], {
type: "application/json",
});
saveAs(blob, `${exportData.filename}.${exportData.extension}`);
} else if (visible === MODAL.IMPORT) {
if (error.type !== STATUS.ERROR) {
setSettings((prev) => ({ ...prev, pan: { x: 0, y: 0 } }));
overwriteDiagram();
setData(null);
setVisible(MODAL.NONE);
setUndoStack([]);
setRedoStack([]);
}
}
}}
afterClose={() => { afterClose={() => {
setExportData((prev) => ({ setExportData((prev) => ({
data: "", data: "",
@ -1050,10 +1251,13 @@ export default function ControlPanel(props) {
}); });
setData(null); setData(null);
}} }}
onCancel={() => setVisible(MODAL.NONE)} onCancel={() => {
if (visible === MODAL.RENAME) setTitle(prevTitle);
setVisible(MODAL.NONE);
}}
centered centered
closeOnEsc={true} closeOnEsc={true}
okText={`${visible === MODAL.IMPORT ? "Import" : "Export"}`} okText={getOkText()}
okButtonProps={{ okButtonProps={{
disabled: disabled:
(visible === MODAL.IMPORT && (visible === MODAL.IMPORT &&
@ -1064,140 +1268,7 @@ export default function ControlPanel(props) {
cancelText="Cancel" cancelText="Cancel"
width={600} width={600}
> >
{visible === MODAL.IMPORT ? ( {getModalBody()}
<div>
<Upload
action="#"
beforeUpload={({ file, fileList }) => {
const f = fileList[0].fileInstance;
if (!f) {
return;
}
const reader = new FileReader();
reader.onload = function (event) {
let jsonObject = null;
try {
jsonObject = JSON.parse(event.target.result);
} catch (error) {
setError({
type: STATUS.ERROR,
message: "The file contains an error.",
});
return;
}
if (f.type === "application/json") {
if (!jsonDiagramIsValid(jsonObject)) {
setError({
type: STATUS.ERROR,
message:
"The file is missing necessary properties for a diagram.",
});
return;
}
} else if (f.name.split(".").pop() === "ddb") {
if (!ddbDiagramIsValid(jsonObject)) {
setError({
type: STATUS.ERROR,
message:
"The file is missing necessary properties for a diagram.",
});
return;
}
}
setData(jsonObject);
if (diagramIsEmpty()) {
setError({
type: STATUS.OK,
message: "Everything looks good. You can now import.",
});
} else {
setError({
type: STATUS.WARNING,
message:
"The current diagram is not empty. Importing a new diagram will overwrite the current changes.",
});
}
};
reader.readAsText(f);
return {
autoRemove: false,
fileInstance: file.fileInstance,
status: "success",
shouldUpload: false,
};
}}
draggable={true}
dragMainText="Click to upload the file or drag and drop the file here"
dragSubText="Support json"
accept="application/json,.ddb"
onRemove={() =>
setError({
type: STATUS.NONE,
message: "",
})
}
onFileChange={() =>
setError({
type: STATUS.NONE,
message: "",
})
}
limit={1}
></Upload>
{error.type === STATUS.ERROR ? (
<Banner
type="danger"
fullMode={false}
description={
<div className="text-red-800">{error.message}</div>
}
/>
) : error.type === STATUS.OK ? (
<Banner
type="info"
fullMode={false}
description={<div>{error.message}</div>}
/>
) : (
error.type === STATUS.WARNING && (
<Banner
type="warning"
fullMode={false}
description={<div>{error.message}</div>}
/>
)
)}
</div>
) : exportData.data !== "" || exportData.data ? (
<>
{visible === MODAL.IMG ? (
<Image src={exportData.data} alt="Diagram" height={280} />
) : (
<Editor
height="360px"
value={exportData.data}
language={exportData.extension}
options={{ readOnly: true }}
theme={settings.mode === "light" ? "light" : "vs-dark"}
/>
)}
<div className="text-sm font-semibold mt-2">Filename:</div>
<Input
value={exportData.filename}
placeholder="Filename"
suffix={<div className="p-2">{`.${exportData.extension}`}</div>}
onChange={(value) =>
setExportData((prev) => ({ ...prev, filename: value }))
}
field="filename"
/>
</>
) : (
<div className="text-center my-3">
<Spin tip="Loading..." size="large" />
</div>
)}
</Modal> </Modal>
</> </>
); );
@ -1360,7 +1431,17 @@ export default function ControlPanel(props) {
/> />
</Link> </Link>
<div className="ms-1 mt-1"> <div className="ms-1 mt-1">
<div className="text-xl ms-3">Project1 / Untitled</div> <div className="flex items-center">
<div
className="text-xl ms-3 me-1"
onMouseEnter={() => setShowEditName(true)}
onMouseLeave={() => setShowEditName(false)}
onClick={() => setVisible(MODAL.RENAME)}
>
{title}
</div>
{(showEditName || visible === MODAL.RENAME) && <IconEdit />}
</div>
<div className="flex justify-between items-center"> <div className="flex justify-between items-center">
<div className="flex justify-start text-md select-none me-2"> <div className="flex justify-start text-md select-none me-2">
{Object.keys(menu).map((category) => ( {Object.keys(menu).map((category) => (