Parse diagram to json
This commit is contained in:
parent
70df6dd2a9
commit
974c254e48
19
package-lock.json
generated
19
package-lock.json
generated
@ -8,6 +8,7 @@
|
|||||||
"name": "frontend",
|
"name": "frontend",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@codemirror/lang-json": "^6.0.1",
|
||||||
"@codemirror/lang-sql": "^6.5.0",
|
"@codemirror/lang-sql": "^6.5.0",
|
||||||
"@douyinfe/semi-ui": "^2.36.0",
|
"@douyinfe/semi-ui": "^2.36.0",
|
||||||
"@lezer/highlight": "^1.1.5",
|
"@lezer/highlight": "^1.1.5",
|
||||||
@ -2152,6 +2153,15 @@
|
|||||||
"@lezer/common": "^1.0.0"
|
"@lezer/common": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@codemirror/lang-json": {
|
||||||
|
"version": "6.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@codemirror/lang-json/-/lang-json-6.0.1.tgz",
|
||||||
|
"integrity": "sha512-+T1flHdgpqDDlJZ2Lkil/rLiRy684WMLc74xUnjJH48GQdfJo/pudlTRreZmKwzP8/tGdKf83wlbAdOCzlJOGQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"@codemirror/language": "^6.0.0",
|
||||||
|
"@lezer/json": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@codemirror/lang-sql": {
|
"node_modules/@codemirror/lang-sql": {
|
||||||
"version": "6.5.2",
|
"version": "6.5.2",
|
||||||
"resolved": "https://registry.npmjs.org/@codemirror/lang-sql/-/lang-sql-6.5.2.tgz",
|
"resolved": "https://registry.npmjs.org/@codemirror/lang-sql/-/lang-sql-6.5.2.tgz",
|
||||||
@ -3721,6 +3731,15 @@
|
|||||||
"@lezer/common": "^1.0.0"
|
"@lezer/common": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@lezer/json": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@lezer/json/-/json-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-nkVC27qiEZEjySbi6gQRuMwa2sDu2PtfjSgz0A4QF81QyRGm3kb2YRzLcOPcTEtmcwvrX/cej7mlhbwViA4WJw==",
|
||||||
|
"dependencies": {
|
||||||
|
"@lezer/highlight": "^1.0.0",
|
||||||
|
"@lezer/lr": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@lezer/lr": {
|
"node_modules/@lezer/lr": {
|
||||||
"version": "1.3.7",
|
"version": "1.3.7",
|
||||||
"resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.3.7.tgz",
|
"resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.3.7.tgz",
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@codemirror/lang-json": "^6.0.1",
|
||||||
"@codemirror/lang-sql": "^6.5.0",
|
"@codemirror/lang-sql": "^6.5.0",
|
||||||
"@douyinfe/semi-ui": "^2.36.0",
|
"@douyinfe/semi-ui": "^2.36.0",
|
||||||
"@lezer/highlight": "^1.1.5",
|
"@lezer/highlight": "^1.1.5",
|
||||||
|
@ -22,22 +22,40 @@ import {
|
|||||||
Image,
|
Image,
|
||||||
Modal,
|
Modal,
|
||||||
Spin,
|
Spin,
|
||||||
|
Input,
|
||||||
} from "@douyinfe/semi-ui";
|
} from "@douyinfe/semi-ui";
|
||||||
import { toPng, toJpeg, toSvg } from "html-to-image";
|
import { toPng, toJpeg, toSvg } from "html-to-image";
|
||||||
import { saveAs } from "file-saver";
|
import { saveAs } from "file-saver";
|
||||||
import { enterFullscreen, exitFullscreen } from "../utils";
|
import { enterFullscreen, exitFullscreen } from "../utils";
|
||||||
import { LayoutContext, SettingsContext } from "../pages/editor";
|
import {
|
||||||
|
AreaContext,
|
||||||
|
LayoutContext,
|
||||||
|
NoteContext,
|
||||||
|
SettingsContext,
|
||||||
|
TableContext,
|
||||||
|
} from "../pages/editor";
|
||||||
import { AddTable, AddArea, AddNote } from "./custom_icons";
|
import { AddTable, AddArea, AddNote } from "./custom_icons";
|
||||||
|
import { defaultTableTheme, defaultNoteTheme } from "../data/data";
|
||||||
|
import CodeMirror from "@uiw/react-codemirror";
|
||||||
|
import { json } from "@codemirror/lang-json";
|
||||||
|
|
||||||
export default function ControlPanel(props) {
|
export default function ControlPanel(props) {
|
||||||
const [visible, setVisible] = useState(false);
|
const MODAL = {
|
||||||
const [dataUrl, setDataUrl] = useState("");
|
NONE: 0,
|
||||||
const [filename, setFilename] = useState(
|
IMG: 1,
|
||||||
`diagram_${new Date().toISOString()}`
|
CODE: 2,
|
||||||
);
|
};
|
||||||
const [extension, setExtension] = useState("");
|
const [visible, setVisible] = useState(MODAL.NONE);
|
||||||
|
const [exportData, setExportData] = useState({
|
||||||
|
data: "",
|
||||||
|
filename: `diagram_${new Date().toISOString()}`,
|
||||||
|
extension: "",
|
||||||
|
});
|
||||||
const { layout, setLayout } = useContext(LayoutContext);
|
const { layout, setLayout } = useContext(LayoutContext);
|
||||||
const { setSettings } = useContext(SettingsContext);
|
const { setSettings } = useContext(SettingsContext);
|
||||||
|
const { relationships, tables, setTables } = useContext(TableContext);
|
||||||
|
const { notes, setNotes } = useContext(NoteContext);
|
||||||
|
const { areas, setAreas } = useContext(AreaContext);
|
||||||
|
|
||||||
const menu = {
|
const menu = {
|
||||||
File: {
|
File: {
|
||||||
@ -74,37 +92,66 @@ export default function ControlPanel(props) {
|
|||||||
{
|
{
|
||||||
PNG: () => {
|
PNG: () => {
|
||||||
toPng(document.getElementById("canvas")).then(function (dataUrl) {
|
toPng(document.getElementById("canvas")).then(function (dataUrl) {
|
||||||
setDataUrl(dataUrl);
|
setExportData((prev) => ({
|
||||||
|
...prev,
|
||||||
|
data: dataUrl,
|
||||||
|
extension: "png",
|
||||||
|
}));
|
||||||
});
|
});
|
||||||
setVisible(true);
|
setVisible(MODAL.IMG);
|
||||||
setExtension("png");
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
JPEG: () => {
|
JPEG: () => {
|
||||||
toJpeg(document.getElementById("canvas"), { quality: 0.95 }).then(
|
toJpeg(document.getElementById("canvas"), { quality: 0.95 }).then(
|
||||||
function (dataUrl) {
|
function (dataUrl) {
|
||||||
setDataUrl(dataUrl);
|
setExportData((prev) => ({
|
||||||
|
...prev,
|
||||||
|
data: dataUrl,
|
||||||
|
extension: "jpeg",
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
setVisible(true);
|
setVisible(MODAL.IMG);
|
||||||
setExtension("jpeg");
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
JSON: () => {
|
||||||
|
setVisible(MODAL.CODE);
|
||||||
|
|
||||||
|
const result = JSON.stringify(
|
||||||
|
{
|
||||||
|
tables: tables,
|
||||||
|
relationships: relationships,
|
||||||
|
notes: notes,
|
||||||
|
"subject areas": areas,
|
||||||
|
},
|
||||||
|
null,
|
||||||
|
2
|
||||||
|
);
|
||||||
|
setExportData((prev) => ({
|
||||||
|
...prev,
|
||||||
|
data: result,
|
||||||
|
extension: "json",
|
||||||
|
}));
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{ XML: () => {} },
|
|
||||||
{
|
{
|
||||||
SVG: () => {
|
SVG: () => {
|
||||||
const filter = (node) => node.tagName !== "i";
|
const filter = (node) => node.tagName !== "i";
|
||||||
toSvg(document.getElementById("canvas"), { filter: filter }).then(
|
toSvg(document.getElementById("canvas"), { filter: filter }).then(
|
||||||
function (dataUrl) {
|
function (dataUrl) {
|
||||||
setDataUrl(dataUrl);
|
setExportData((prev) => ({
|
||||||
|
...prev,
|
||||||
|
data: dataUrl,
|
||||||
|
extension: "svg",
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
setVisible(true);
|
setVisible(MODAL.IMG);
|
||||||
setExtension("svg");
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{ PDF: () => {} },
|
{ IFRAME: () => {} },
|
||||||
],
|
],
|
||||||
function: () => {},
|
function: () => {},
|
||||||
},
|
},
|
||||||
@ -316,39 +363,94 @@ export default function ControlPanel(props) {
|
|||||||
</button>
|
</button>
|
||||||
<Divider layout="vertical" margin="8px" />
|
<Divider layout="vertical" margin="8px" />
|
||||||
<button
|
<button
|
||||||
className="py-1 px-2 hover:bg-slate-200 rounded flex items-center justify-center"
|
className="py-1 px-2 hover:bg-slate-200 rounded flex items-center"
|
||||||
title="Undo"
|
title="Undo"
|
||||||
>
|
>
|
||||||
<IconUndo size="large" />
|
<IconUndo size="large" />
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
className="py-1 px-2 hover:bg-slate-200 rounded flex items-center justify-center"
|
className="py-1 px-2 hover:bg-slate-200 rounded flex items-center"
|
||||||
title="Redo"
|
title="Redo"
|
||||||
>
|
>
|
||||||
<IconRedo size="large" />
|
<IconRedo size="large" />
|
||||||
</button>
|
</button>
|
||||||
<Divider layout="vertical" margin="8px" />
|
<Divider layout="vertical" margin="8px" />
|
||||||
<div
|
<button
|
||||||
className="py-1 px-2 hover:bg-slate-200 rounded"
|
className="flex items-center py-1 px-2 hover:bg-slate-200 rounded"
|
||||||
title="Add new table"
|
title="Add new table"
|
||||||
|
onClick={() =>
|
||||||
|
setTables((prev) => [
|
||||||
|
...prev,
|
||||||
|
{
|
||||||
|
id: prev.length,
|
||||||
|
name: `table_${prev.length}`,
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: "id",
|
||||||
|
type: "UUID",
|
||||||
|
default: "",
|
||||||
|
check: "",
|
||||||
|
primary: true,
|
||||||
|
unique: true,
|
||||||
|
notNull: true,
|
||||||
|
increment: true,
|
||||||
|
comment: "",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
comment: "",
|
||||||
|
indices: [],
|
||||||
|
color: defaultTableTheme,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<AddTable />
|
<AddTable />
|
||||||
</div>
|
</button>
|
||||||
<div
|
<button
|
||||||
className="py-1 px-2 hover:bg-slate-200 rounded"
|
className="py-1 px-2 hover:bg-slate-200 rounded flex items-center"
|
||||||
title="Add subject area"
|
title="Add subject area"
|
||||||
|
onClick={() =>
|
||||||
|
setAreas((prev) => [
|
||||||
|
...prev,
|
||||||
|
{
|
||||||
|
id: prev.length,
|
||||||
|
name: `area_${prev.length}`,
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
width: 200,
|
||||||
|
height: 200,
|
||||||
|
color: defaultTableTheme,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<AddArea />
|
<AddArea />
|
||||||
</div>
|
</button>
|
||||||
<div
|
<button
|
||||||
className="py-1 px-2 hover:bg-slate-200 rounded"
|
className="py-1 px-2 hover:bg-slate-200 rounded flex items-center"
|
||||||
title="Add new note"
|
title="Add new note"
|
||||||
|
onClick={() =>
|
||||||
|
setNotes((prev) => [
|
||||||
|
...prev,
|
||||||
|
{
|
||||||
|
id: prev.length,
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
title: `note_${prev.length}`,
|
||||||
|
content: "",
|
||||||
|
color: defaultNoteTheme,
|
||||||
|
height: 88,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<AddNote />
|
<AddNote />
|
||||||
</div>
|
</button>
|
||||||
<Divider layout="vertical" margin="8px" />
|
<Divider layout="vertical" margin="8px" />
|
||||||
<button
|
<button
|
||||||
className="py-1 px-2 hover:bg-slate-200 rounded flex items-center justify-center"
|
className="py-1 px-2 hover:bg-slate-200 rounded flex items-center"
|
||||||
title="Save"
|
title="Save"
|
||||||
>
|
>
|
||||||
<IconSaveStroked size="extra-large" />
|
<IconSaveStroked size="extra-large" />
|
||||||
@ -364,50 +466,71 @@ export default function ControlPanel(props) {
|
|||||||
onClick={(e) =>
|
onClick={(e) =>
|
||||||
setLayout((prev) => ({ ...prev, header: !prev.header }))
|
setLayout((prev) => ({ ...prev, header: !prev.header }))
|
||||||
}
|
}
|
||||||
|
className="flex items-center"
|
||||||
>
|
>
|
||||||
{layout.header ? <IconChevronUp /> : <IconChevronDown />}
|
{layout.header ? <IconChevronUp /> : <IconChevronDown />}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<Modal
|
<Modal
|
||||||
title="Export diagram"
|
title="Export diagram"
|
||||||
visible={visible}
|
visible={visible !== MODAL.NONE}
|
||||||
onOk={() => saveAs(dataUrl, `${filename}.${extension}`)}
|
onOk={() => {
|
||||||
afterClose={() => {
|
if (visible === MODAL.IMG) {
|
||||||
setFilename(`diagram_${new Date().toISOString()}`);
|
saveAs(
|
||||||
setDataUrl("");
|
exportData.data,
|
||||||
|
`${exportData.filename}.${exportData.extension}`
|
||||||
|
);
|
||||||
|
} else if (visible === MODAL.CODE) {
|
||||||
|
const blob = new Blob([exportData.data], {
|
||||||
|
type: "text/plain;charset=utf-8",
|
||||||
|
});
|
||||||
|
saveAs(blob, `${exportData.filename}.${exportData.extension}`);
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
onCancel={() => setVisible(false)}
|
afterClose={() => {
|
||||||
|
setExportData((prev) => ({
|
||||||
|
data: "",
|
||||||
|
extension: "",
|
||||||
|
filename: `diagram_${new Date().toISOString()}`,
|
||||||
|
}));
|
||||||
|
}}
|
||||||
|
onCancel={() => setVisible(MODAL.NONE)}
|
||||||
centered
|
centered
|
||||||
closeOnEsc={true}
|
closeOnEsc={true}
|
||||||
okText="Export"
|
okText="Export"
|
||||||
cancelText="Cancel"
|
cancelText="Cancel"
|
||||||
width={470}
|
width={520}
|
||||||
>
|
>
|
||||||
{dataUrl !== "" || dataUrl ? (
|
{exportData.data !== "" || exportData.data ? (
|
||||||
<Image src={dataUrl} alt="Diagram" width={420}></Image>
|
visible === MODAL.IMG ? (
|
||||||
|
<Image src={exportData.data} alt="Diagram" height={220} />
|
||||||
|
) : (
|
||||||
|
<div className="max-h-[400px] overflow-auto border border-gray-200">
|
||||||
|
<CodeMirror
|
||||||
|
value={exportData.data}
|
||||||
|
extensions={[json()]}
|
||||||
|
style={{
|
||||||
|
width: "100%",
|
||||||
|
height: "100%",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
) : (
|
) : (
|
||||||
<div className="text-center my-3">
|
<div className="text-center my-3">
|
||||||
<Spin tip="Loading..." size="large"></Spin>
|
<Spin tip="Loading..." size="large" />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<Form
|
<div className="text-sm font-semibold mt-2">Filename : </div>
|
||||||
labelPosition="left"
|
<Input
|
||||||
labelAlign="right"
|
value={exportData.filename}
|
||||||
onChange={(value) => {
|
placeholder="Filename"
|
||||||
setFilename((prev) =>
|
suffix={<div className="p-2">{`.${exportData.extension}`}</div>}
|
||||||
value.values["filename"] !== undefined
|
onChange={(value) =>
|
||||||
? value.values["filename"]
|
setExportData((prev) => ({ ...prev, filename: value }))
|
||||||
: prev
|
}
|
||||||
);
|
field="filename"
|
||||||
}}
|
/>
|
||||||
>
|
|
||||||
<Form.Input
|
|
||||||
field="filename"
|
|
||||||
label="Filename"
|
|
||||||
suffix={<div className="p-2">{`.${extension}`}</div>}
|
|
||||||
initValue={filename}
|
|
||||||
/>
|
|
||||||
</Form>
|
|
||||||
</Modal>
|
</Modal>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
Loading…
Reference in New Issue
Block a user