Create a new diagram
This commit is contained in:
parent
50d82fb435
commit
9588d7c918
@ -11,6 +11,7 @@ import {
|
|||||||
IconRedo,
|
IconRedo,
|
||||||
IconRowsStroked,
|
IconRowsStroked,
|
||||||
IconEdit,
|
IconEdit,
|
||||||
|
IconPlus,
|
||||||
} 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";
|
||||||
@ -71,6 +72,7 @@ export default function ControlPanel({ diagramId, setDiagramId }) {
|
|||||||
RENAME: 4,
|
RENAME: 4,
|
||||||
OPEN: 5,
|
OPEN: 5,
|
||||||
SAVEAS: 6,
|
SAVEAS: 6,
|
||||||
|
NEW: 7,
|
||||||
};
|
};
|
||||||
const STATUS = {
|
const STATUS = {
|
||||||
NONE: 0,
|
NONE: 0,
|
||||||
@ -84,6 +86,7 @@ export default function ControlPanel({ diagramId, setDiagramId }) {
|
|||||||
const [prevTitle, setPrevTitle] = useState(title);
|
const [prevTitle, setPrevTitle] = useState(title);
|
||||||
const [saveAsTitle, setSaveAsTitle] = useState(title);
|
const [saveAsTitle, setSaveAsTitle] = useState(title);
|
||||||
const [selectedDiagramId, setSelectedDiagramId] = useState(0);
|
const [selectedDiagramId, setSelectedDiagramId] = useState(0);
|
||||||
|
const [selectedTemplateId, setSelectedTemplateId] = useState(-1);
|
||||||
const [showEditName, setShowEditName] = useState(false);
|
const [showEditName, setShowEditName] = useState(false);
|
||||||
const [exportData, setExportData] = useState({
|
const [exportData, setExportData] = useState({
|
||||||
data: null,
|
data: null,
|
||||||
@ -713,11 +716,15 @@ export default function ControlPanel({ diagramId, setDiagramId }) {
|
|||||||
Toast.error("Oops! Couldn't load diagram.");
|
Toast.error("Oops! Couldn't load diagram.");
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
const createNewDiagram = (id) => {
|
||||||
|
localStorage.setItem("args", `${id}`);
|
||||||
|
window.open("/editor", "_blank");
|
||||||
|
};
|
||||||
|
|
||||||
const menu = {
|
const menu = {
|
||||||
File: {
|
File: {
|
||||||
New: {
|
New: {
|
||||||
function: () => {},
|
function: () => setVisible(MODAL.NEW),
|
||||||
},
|
},
|
||||||
"New window": {
|
"New window": {
|
||||||
function: () => {},
|
function: () => {},
|
||||||
@ -1114,6 +1121,8 @@ export default function ControlPanel({ diagramId, setDiagramId }) {
|
|||||||
return "Open diagram";
|
return "Open diagram";
|
||||||
case MODAL.SAVEAS:
|
case MODAL.SAVEAS:
|
||||||
return "Save as";
|
return "Save as";
|
||||||
|
case MODAL.NEW:
|
||||||
|
return "New diagram";
|
||||||
default:
|
default:
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
@ -1132,6 +1141,8 @@ export default function ControlPanel({ diagramId, setDiagramId }) {
|
|||||||
return "Open";
|
return "Open";
|
||||||
case MODAL.SAVEAS:
|
case MODAL.SAVEAS:
|
||||||
return "Save as";
|
return "Save as";
|
||||||
|
case MODAL.NEW:
|
||||||
|
return "Create";
|
||||||
default:
|
default:
|
||||||
return "Confirm";
|
return "Confirm";
|
||||||
}
|
}
|
||||||
@ -1166,6 +1177,10 @@ export default function ControlPanel({ diagramId, setDiagramId }) {
|
|||||||
loadDiagram(selectedDiagramId);
|
loadDiagram(selectedDiagramId);
|
||||||
setVisible(MODAL.NONE);
|
setVisible(MODAL.NONE);
|
||||||
return;
|
return;
|
||||||
|
case MODAL.RENAME:
|
||||||
|
setPrevTitle(title);
|
||||||
|
setVisible(MODAL.NONE);
|
||||||
|
return;
|
||||||
case MODAL.SAVEAS:
|
case MODAL.SAVEAS:
|
||||||
db.diagrams.add({
|
db.diagrams.add({
|
||||||
name: saveAsTitle,
|
name: saveAsTitle,
|
||||||
@ -1178,6 +1193,10 @@ export default function ControlPanel({ diagramId, setDiagramId }) {
|
|||||||
});
|
});
|
||||||
setVisible(MODAL.NONE);
|
setVisible(MODAL.NONE);
|
||||||
return;
|
return;
|
||||||
|
case MODAL.NEW:
|
||||||
|
setVisible(MODAL.NONE);
|
||||||
|
createNewDiagram(selectedTemplateId);
|
||||||
|
return;
|
||||||
default:
|
default:
|
||||||
setVisible(MODAL.NONE);
|
setVisible(MODAL.NONE);
|
||||||
return;
|
return;
|
||||||
@ -1291,127 +1310,164 @@ export default function ControlPanel({ diagramId, setDiagramId }) {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const newModalBody = () => (
|
||||||
|
<div className="h-[360px] grid grid-cols-3 gap-2 overflow-auto px-1">
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
className={`h-[180px] w-full bg-blue-400 bg-opacity-30 flex justify-center items-center rounded hover:bg-opacity-40 hover:border-2 hover:border-dashed ${
|
||||||
|
settings.mode === "light"
|
||||||
|
? "hover:border-blue-500"
|
||||||
|
: "hover:border-white"
|
||||||
|
} ${selectedTemplateId === 0 && "border-2 border-blue-500"}`}
|
||||||
|
onClick={() => setSelectedTemplateId(0)}
|
||||||
|
>
|
||||||
|
<IconPlus style={{ color: "#fff" }} size="extra-large" />
|
||||||
|
</div>
|
||||||
|
<div className="text-center mt-1">Blank</div>
|
||||||
|
</div>
|
||||||
|
{[1, 2, 3, 4, 5, 6, 7, 8].map((i) => (
|
||||||
|
<div key={i}>
|
||||||
|
<div
|
||||||
|
className={`h-[180px] w-full bg-blue-400 bg-opacity-30 flex justify-center items-center rounded hover:bg-opacity-40 hover:border-2 hover:border-dashed ${
|
||||||
|
settings.mode === "light"
|
||||||
|
? "hover:border-blue-500"
|
||||||
|
: "hover:border-white"
|
||||||
|
} ${selectedTemplateId === i && "border-2 border-blue-500"}`}
|
||||||
|
onClick={() => setSelectedTemplateId(i)}
|
||||||
|
>
|
||||||
|
+
|
||||||
|
</div>
|
||||||
|
<div className="text-center mt-1">Template {i}</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
const getModalBody = () => {
|
const getModalBody = () => {
|
||||||
if (visible === MODAL.IMPORT) {
|
switch (visible) {
|
||||||
return importModalBody();
|
case MODAL.IMPORT:
|
||||||
}
|
return importModalBody();
|
||||||
if (visible === MODAL.RENAME) {
|
case MODAL.NEW:
|
||||||
return (
|
return newModalBody();
|
||||||
<Input
|
case MODAL.RENAME:
|
||||||
placeholder="Diagram name"
|
return (
|
||||||
value={title}
|
|
||||||
onChange={(v) => setTitle(v)}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (visible === MODAL.SAVEAS) {
|
|
||||||
return (
|
|
||||||
<Input
|
|
||||||
placeholder="Diagram name"
|
|
||||||
value={saveAsTitle}
|
|
||||||
onChange={(v) => setSaveAsTitle(v)}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (visible === MODAL.OPEN) {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
{diagrams.length === 0 ? (
|
|
||||||
<Banner
|
|
||||||
fullMode={false}
|
|
||||||
type="info"
|
|
||||||
bordered
|
|
||||||
icon={null}
|
|
||||||
closeIcon={null}
|
|
||||||
description={<div>You have no saved diagrams.</div>}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
<table className="w-full text-left border-separate border-spacing-x-0">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Name</th>
|
|
||||||
<th>Last Modified</th>
|
|
||||||
<th>Size</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{diagrams?.map((d) => {
|
|
||||||
const size = JSON.stringify(d).length;
|
|
||||||
let sizeStr;
|
|
||||||
if (size >= 1024 && size < 1024 * 1024)
|
|
||||||
sizeStr = (size / 1024).toFixed(1) + "KB";
|
|
||||||
else if (size >= 1024 * 1024)
|
|
||||||
sizeStr = (size / (1024 * 1024)).toFixed(1) + "MB";
|
|
||||||
else sizeStr = size + "B";
|
|
||||||
return (
|
|
||||||
<tr
|
|
||||||
key={d.id}
|
|
||||||
className={`${
|
|
||||||
selectedDiagramId === d.id
|
|
||||||
? "bg-sky-400 bg-opacity-20"
|
|
||||||
: "hover-1"
|
|
||||||
}`}
|
|
||||||
onClick={() => {
|
|
||||||
setSelectedDiagramId(d.id);
|
|
||||||
}}
|
|
||||||
onDoubleClick={() => {
|
|
||||||
loadDiagram(d.id);
|
|
||||||
setVisible(MODAL.NONE);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<td className="py-1">
|
|
||||||
<i className="bi bi-file-earmark-text text-[16px] me-1 opacity-60"></i>
|
|
||||||
{d.name}
|
|
||||||
</td>
|
|
||||||
<td className="py-1">
|
|
||||||
{d.lastModified.toLocaleDateString() +
|
|
||||||
" " +
|
|
||||||
d.lastModified.toLocaleTimeString()}
|
|
||||||
</td>
|
|
||||||
<td className="py-1">{sizeStr}</td>
|
|
||||||
</tr>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
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
|
<Input
|
||||||
value={exportData.filename}
|
placeholder="Diagram name"
|
||||||
placeholder="Filename"
|
value={title}
|
||||||
suffix={<div className="p-2">{`.${exportData.extension}`}</div>}
|
onChange={(v) => setTitle(v)}
|
||||||
onChange={(value) =>
|
|
||||||
setExportData((prev) => ({ ...prev, filename: value }))
|
|
||||||
}
|
|
||||||
field="filename"
|
|
||||||
/>
|
/>
|
||||||
</>
|
);
|
||||||
);
|
case MODAL.OPEN:
|
||||||
} else {
|
return (
|
||||||
return (
|
<div>
|
||||||
<div className="text-center my-3">
|
{diagrams.length === 0 ? (
|
||||||
<Spin tip="Loading..." size="large" />
|
<Banner
|
||||||
</div>
|
fullMode={false}
|
||||||
);
|
type="info"
|
||||||
|
bordered
|
||||||
|
icon={null}
|
||||||
|
closeIcon={null}
|
||||||
|
description={<div>You have no saved diagrams.</div>}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<table className="w-full text-left border-separate border-spacing-x-0">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Name</th>
|
||||||
|
<th>Last Modified</th>
|
||||||
|
<th>Size</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{diagrams?.map((d) => {
|
||||||
|
const size = JSON.stringify(d).length;
|
||||||
|
let sizeStr;
|
||||||
|
if (size >= 1024 && size < 1024 * 1024)
|
||||||
|
sizeStr = (size / 1024).toFixed(1) + "KB";
|
||||||
|
else if (size >= 1024 * 1024)
|
||||||
|
sizeStr = (size / (1024 * 1024)).toFixed(1) + "MB";
|
||||||
|
else sizeStr = size + "B";
|
||||||
|
return (
|
||||||
|
<tr
|
||||||
|
key={d.id}
|
||||||
|
className={`${
|
||||||
|
selectedDiagramId === d.id
|
||||||
|
? "bg-sky-400 bg-opacity-20"
|
||||||
|
: "hover-1"
|
||||||
|
}`}
|
||||||
|
onClick={() => {
|
||||||
|
setSelectedDiagramId(d.id);
|
||||||
|
}}
|
||||||
|
onDoubleClick={() => {
|
||||||
|
loadDiagram(d.id);
|
||||||
|
setVisible(MODAL.NONE);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<td className="py-1">
|
||||||
|
<i className="bi bi-file-earmark-text text-[16px] me-1 opacity-60"></i>
|
||||||
|
{d.name}
|
||||||
|
</td>
|
||||||
|
<td className="py-1">
|
||||||
|
{d.lastModified.toLocaleDateString() +
|
||||||
|
" " +
|
||||||
|
d.lastModified.toLocaleTimeString()}
|
||||||
|
</td>
|
||||||
|
<td className="py-1">{sizeStr}</td>
|
||||||
|
</tr>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
case MODAL.SAVEAS:
|
||||||
|
return (
|
||||||
|
<Input
|
||||||
|
placeholder="Diagram name"
|
||||||
|
value={saveAsTitle}
|
||||||
|
onChange={(v) => setSaveAsTitle(v)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
case MODAL.CODE:
|
||||||
|
case MODAL.IMG:
|
||||||
|
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>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return <></>;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -8,11 +8,11 @@ import en_US from "@douyinfe/semi-ui/lib/es/locale/source/en_US";
|
|||||||
|
|
||||||
const root = ReactDOM.createRoot(document.getElementById("root"));
|
const root = ReactDOM.createRoot(document.getElementById("root"));
|
||||||
root.render(
|
root.render(
|
||||||
<React.StrictMode>
|
// <React.StrictMode>
|
||||||
<LocaleProvider locale={en_US}>
|
<LocaleProvider locale={en_US}>
|
||||||
<App />
|
<App />
|
||||||
</LocaleProvider>
|
</LocaleProvider>
|
||||||
</React.StrictMode>
|
// </React.StrictMode>
|
||||||
);
|
);
|
||||||
|
|
||||||
// If you want to start measuring performance in your app, pass a function
|
// If you want to start measuring performance in your app, pass a function
|
||||||
|
@ -480,7 +480,13 @@ export default function Editor(props) {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
loadLatestDiagram();
|
const args = localStorage.getItem("args");
|
||||||
|
if (!args || args === "-1") {
|
||||||
|
loadLatestDiagram();
|
||||||
|
} else {
|
||||||
|
console.log("Loading template with id", args);
|
||||||
|
localStorage.setItem("args", "-1");
|
||||||
|
}
|
||||||
|
|
||||||
socket.connect();
|
socket.connect();
|
||||||
|
|
||||||
@ -583,7 +589,7 @@ export default function Editor(props) {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className="h-[100vh] overflow-hidden theme">
|
<div className="h-[100vh] overflow-hidden theme">
|
||||||
<ControlPanel diagramId={id} setDiagramId={setId}/>
|
<ControlPanel diagramId={id} setDiagramId={setId} />
|
||||||
<div
|
<div
|
||||||
className={
|
className={
|
||||||
layout.header
|
layout.header
|
||||||
|
Loading…
Reference in New Issue
Block a user