2024-04-06 13:19:12 +08:00
|
|
|
import {
|
|
|
|
Spin,
|
|
|
|
Input,
|
|
|
|
Image,
|
|
|
|
Toast,
|
|
|
|
Modal as SemiUIModal,
|
|
|
|
} from "@douyinfe/semi-ui";
|
2024-06-10 01:18:12 +08:00
|
|
|
import { DB, MODAL, STATUS } from "../../../data/constants";
|
2024-07-21 18:22:23 +08:00
|
|
|
import { useState } from "react";
|
2024-04-06 13:19:12 +08:00
|
|
|
import { db } from "../../../data/db";
|
|
|
|
import {
|
|
|
|
useAreas,
|
2024-06-30 20:38:25 +08:00
|
|
|
useEnums,
|
2024-04-06 13:19:12 +08:00
|
|
|
useNotes,
|
|
|
|
useSettings,
|
2024-07-05 04:18:20 +08:00
|
|
|
useDiagram,
|
2024-04-06 13:19:12 +08:00
|
|
|
useTransform,
|
|
|
|
useTypes,
|
|
|
|
useUndoRedo,
|
2024-07-08 07:17:46 +08:00
|
|
|
useTasks,
|
2024-04-06 13:19:12 +08:00
|
|
|
} from "../../../hooks";
|
|
|
|
import { saveAs } from "file-saver";
|
|
|
|
import { Parser } from "node-sql-parser";
|
2024-08-08 04:00:19 +08:00
|
|
|
import {
|
|
|
|
getModalTitle,
|
|
|
|
getModalWidth,
|
|
|
|
getOkText,
|
|
|
|
} from "../../../utils/modalData";
|
2024-04-06 13:19:12 +08:00
|
|
|
import Rename from "./Rename";
|
|
|
|
import Open from "./Open";
|
|
|
|
import New from "./New";
|
|
|
|
import ImportDiagram from "./ImportDiagram";
|
|
|
|
import ImportSource from "./ImportSource";
|
2024-04-10 11:47:06 +08:00
|
|
|
import SetTableWidth from "./SetTableWidth";
|
2024-05-16 11:44:39 +08:00
|
|
|
import Language from "./Language";
|
2024-08-31 23:49:43 +08:00
|
|
|
import Share from "./Share";
|
2024-04-19 22:28:32 +08:00
|
|
|
import CodeMirror from "@uiw/react-codemirror";
|
|
|
|
import { sql } from "@codemirror/lang-sql";
|
|
|
|
import { vscodeDark } from "@uiw/codemirror-theme-vscode";
|
|
|
|
import { json } from "@codemirror/lang-json";
|
|
|
|
import { githubLight } from "@uiw/codemirror-theme-github";
|
2024-05-16 11:44:39 +08:00
|
|
|
import { useTranslation } from "react-i18next";
|
2024-06-11 03:57:21 +08:00
|
|
|
import { importSQL } from "../../../utils/importSQL";
|
2024-06-30 20:38:25 +08:00
|
|
|
import { databases } from "../../../data/databases";
|
2024-08-04 20:27:31 +08:00
|
|
|
import { isRtl } from "../../../i18n/utils/rtl";
|
2024-04-19 22:28:32 +08:00
|
|
|
|
|
|
|
const languageExtension = {
|
|
|
|
sql: [sql()],
|
|
|
|
json: [json()],
|
|
|
|
};
|
2024-04-06 13:19:12 +08:00
|
|
|
|
|
|
|
export default function Modal({
|
|
|
|
modal,
|
|
|
|
setModal,
|
|
|
|
title,
|
|
|
|
setTitle,
|
|
|
|
setDiagramId,
|
|
|
|
exportData,
|
|
|
|
setExportData,
|
2024-06-14 06:00:47 +08:00
|
|
|
importDb,
|
2024-04-06 13:19:12 +08:00
|
|
|
}) {
|
2024-08-04 20:27:31 +08:00
|
|
|
const { t, i18n } = useTranslation();
|
2024-07-05 04:18:20 +08:00
|
|
|
const { setTables, setRelationships, database, setDatabase } = useDiagram();
|
2024-04-06 13:19:12 +08:00
|
|
|
const { setNotes } = useNotes();
|
|
|
|
const { setAreas } = useAreas();
|
|
|
|
const { setTypes } = useTypes();
|
|
|
|
const { settings } = useSettings();
|
2024-06-30 20:38:25 +08:00
|
|
|
const { setEnums } = useEnums();
|
2024-07-08 07:17:46 +08:00
|
|
|
const { setTasks } = useTasks();
|
2024-04-06 13:19:12 +08:00
|
|
|
const { setTransform } = useTransform();
|
|
|
|
const { setUndoStack, setRedoStack } = useUndoRedo();
|
2024-07-21 15:26:29 +08:00
|
|
|
const [uncontrolledTitle, setUncontrolledTitle] = useState(title);
|
2024-04-06 13:19:12 +08:00
|
|
|
const [importSource, setImportSource] = useState({
|
|
|
|
src: "",
|
|
|
|
overwrite: true,
|
|
|
|
});
|
|
|
|
const [importData, setImportData] = useState(null);
|
|
|
|
const [error, setError] = useState({
|
|
|
|
type: STATUS.NONE,
|
|
|
|
message: "",
|
|
|
|
});
|
|
|
|
const [selectedTemplateId, setSelectedTemplateId] = useState(-1);
|
|
|
|
const [selectedDiagramId, setSelectedDiagramId] = useState(0);
|
|
|
|
const [saveAsTitle, setSaveAsTitle] = useState(title);
|
|
|
|
|
|
|
|
const overwriteDiagram = () => {
|
|
|
|
setTables(importData.tables);
|
|
|
|
setRelationships(importData.relationships);
|
|
|
|
setAreas(importData.subjectAreas);
|
|
|
|
setNotes(importData.notes);
|
|
|
|
if (importData.title) {
|
|
|
|
setTitle(importData.title);
|
|
|
|
}
|
2024-08-24 18:46:45 +08:00
|
|
|
if (databases[database].hasEnums && importData.enums) {
|
|
|
|
setEnums(importData.enums);
|
|
|
|
}
|
|
|
|
if (databases[database].hasTypes && importData.types) {
|
|
|
|
setTypes(importData.types);
|
|
|
|
}
|
2024-04-06 13:19:12 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
const loadDiagram = async (id) => {
|
|
|
|
await db.diagrams
|
|
|
|
.get(id)
|
|
|
|
.then((diagram) => {
|
|
|
|
if (diagram) {
|
2024-06-10 01:18:12 +08:00
|
|
|
if (diagram.database) {
|
|
|
|
setDatabase(diagram.database);
|
|
|
|
} else {
|
|
|
|
setDatabase(DB.GENERIC);
|
|
|
|
}
|
2024-04-06 13:19:12 +08:00
|
|
|
setDiagramId(diagram.id);
|
|
|
|
setTitle(diagram.name);
|
|
|
|
setTables(diagram.tables);
|
|
|
|
setRelationships(diagram.references);
|
|
|
|
setAreas(diagram.areas);
|
|
|
|
setNotes(diagram.notes);
|
2024-07-08 07:17:46 +08:00
|
|
|
setTasks(diagram.todos ?? []);
|
2024-04-06 13:19:12 +08:00
|
|
|
setTransform({
|
|
|
|
pan: diagram.pan,
|
|
|
|
zoom: diagram.zoom,
|
|
|
|
});
|
|
|
|
setUndoStack([]);
|
|
|
|
setRedoStack([]);
|
2024-07-08 07:17:46 +08:00
|
|
|
if (databases[database].hasTypes) {
|
|
|
|
setTypes(diagram.types ?? []);
|
|
|
|
}
|
|
|
|
if (databases[database].hasEnums) {
|
|
|
|
setEnums(diagram.enums ?? []);
|
|
|
|
}
|
2024-04-06 13:19:12 +08:00
|
|
|
window.name = `d ${diagram.id}`;
|
|
|
|
} else {
|
2024-07-08 07:17:46 +08:00
|
|
|
window.name = "";
|
|
|
|
Toast.error(t("didnt_find_diagram"));
|
2024-04-06 13:19:12 +08:00
|
|
|
}
|
|
|
|
})
|
2024-07-08 07:17:46 +08:00
|
|
|
.catch((error) => {
|
|
|
|
console.log(error);
|
|
|
|
Toast.error(t("didnt_find_diagram"));
|
2024-04-06 13:19:12 +08:00
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
const parseSQLAndLoadDiagram = () => {
|
|
|
|
const parser = new Parser();
|
|
|
|
let ast = null;
|
|
|
|
try {
|
2024-06-14 06:00:47 +08:00
|
|
|
ast = parser.astify(importSource.src, {
|
|
|
|
database: database === DB.GENERIC ? importDb : database,
|
|
|
|
});
|
2024-11-03 17:48:25 +08:00
|
|
|
} catch (error) {
|
|
|
|
const message = error.location
|
|
|
|
? `${error.name} [Ln ${error.location.start.line}, Col ${error.location.start.column}]: ${error.message}`
|
|
|
|
: error.message;
|
2024-10-24 22:57:02 +08:00
|
|
|
|
2024-11-03 17:48:25 +08:00
|
|
|
setError({ type: STATUS.ERROR, message });
|
2024-04-06 13:19:12 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2024-11-03 17:48:25 +08:00
|
|
|
try {
|
|
|
|
const diagramData = importSQL(
|
|
|
|
ast,
|
|
|
|
database === DB.GENERIC ? importDb : database,
|
|
|
|
database,
|
2024-08-02 15:06:55 +08:00
|
|
|
);
|
2024-11-03 17:48:25 +08:00
|
|
|
|
|
|
|
if (importSource.overwrite) {
|
|
|
|
setTables(diagramData.tables);
|
|
|
|
setRelationships(diagramData.relationships);
|
|
|
|
setTransform((prev) => ({ ...prev, pan: { x: 0, y: 0 } }));
|
|
|
|
setNotes([]);
|
|
|
|
setAreas([]);
|
|
|
|
if (databases[database].hasTypes) setTypes(diagramData.types ?? []);
|
|
|
|
if (databases[database].hasEnums) setEnums(diagramData.enums ?? []);
|
|
|
|
setUndoStack([]);
|
|
|
|
setRedoStack([]);
|
|
|
|
} else {
|
|
|
|
setTables((prev) =>
|
|
|
|
[...prev, ...diagramData.tables].map((t, i) => ({ ...t, id: i })),
|
|
|
|
);
|
|
|
|
setRelationships((prev) =>
|
|
|
|
[...prev, ...diagramData.relationships].map((r, i) => ({
|
|
|
|
...r,
|
|
|
|
id: i,
|
|
|
|
})),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
setModal(MODAL.NONE);
|
2024-11-04 23:43:02 +08:00
|
|
|
} catch {
|
2024-11-03 17:48:25 +08:00
|
|
|
setError({
|
|
|
|
type: STATUS.ERROR,
|
|
|
|
message: `Please check for syntax errors or let us know about the error.`,
|
|
|
|
});
|
2024-04-06 13:19:12 +08:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
const createNewDiagram = (id) => {
|
|
|
|
const newWindow = window.open("/editor");
|
|
|
|
newWindow.name = "lt " + id;
|
|
|
|
};
|
|
|
|
|
|
|
|
const getModalOnOk = async () => {
|
|
|
|
switch (modal) {
|
|
|
|
case MODAL.IMG:
|
|
|
|
saveAs(
|
|
|
|
exportData.data,
|
2024-04-10 11:47:06 +08:00
|
|
|
`${exportData.filename}.${exportData.extension}`,
|
2024-04-06 13:19:12 +08:00
|
|
|
);
|
|
|
|
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) {
|
|
|
|
setTransform((prev) => ({ ...prev, pan: { x: 0, y: 0 } }));
|
|
|
|
overwriteDiagram();
|
|
|
|
setImportData(null);
|
|
|
|
setModal(MODAL.NONE);
|
|
|
|
setUndoStack([]);
|
|
|
|
setRedoStack([]);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
case MODAL.IMPORT_SRC:
|
|
|
|
parseSQLAndLoadDiagram();
|
|
|
|
return;
|
|
|
|
case MODAL.OPEN:
|
|
|
|
if (selectedDiagramId === 0) return;
|
|
|
|
loadDiagram(selectedDiagramId);
|
|
|
|
setModal(MODAL.NONE);
|
|
|
|
return;
|
|
|
|
case MODAL.RENAME:
|
2024-07-21 15:26:29 +08:00
|
|
|
setTitle(uncontrolledTitle);
|
2024-04-06 13:19:12 +08:00
|
|
|
setModal(MODAL.NONE);
|
|
|
|
return;
|
|
|
|
case MODAL.SAVEAS:
|
|
|
|
setTitle(saveAsTitle);
|
|
|
|
setModal(MODAL.NONE);
|
|
|
|
return;
|
|
|
|
case MODAL.NEW:
|
|
|
|
setModal(MODAL.NONE);
|
|
|
|
createNewDiagram(selectedTemplateId);
|
|
|
|
return;
|
|
|
|
default:
|
|
|
|
setModal(MODAL.NONE);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
const getModalBody = () => {
|
|
|
|
switch (modal) {
|
|
|
|
case MODAL.IMPORT:
|
|
|
|
return (
|
|
|
|
<ImportDiagram
|
|
|
|
setImportData={setImportData}
|
|
|
|
error={error}
|
|
|
|
setError={setError}
|
|
|
|
/>
|
|
|
|
);
|
|
|
|
case MODAL.IMPORT_SRC:
|
|
|
|
return (
|
|
|
|
<ImportSource
|
|
|
|
importData={importSource}
|
|
|
|
setImportData={setImportSource}
|
2024-04-20 23:16:41 +08:00
|
|
|
error={error}
|
2024-04-06 13:19:12 +08:00
|
|
|
setError={setError}
|
|
|
|
/>
|
|
|
|
);
|
|
|
|
case MODAL.NEW:
|
|
|
|
return (
|
|
|
|
<New
|
|
|
|
selectedTemplateId={selectedTemplateId}
|
|
|
|
setSelectedTemplateId={setSelectedTemplateId}
|
|
|
|
/>
|
|
|
|
);
|
|
|
|
case MODAL.RENAME:
|
2024-07-21 15:26:29 +08:00
|
|
|
return (
|
2024-07-21 18:22:23 +08:00
|
|
|
<Rename key={title} title={title} setTitle={setUncontrolledTitle} />
|
2024-07-21 15:26:29 +08:00
|
|
|
);
|
2024-04-06 13:19:12 +08:00
|
|
|
case MODAL.OPEN:
|
|
|
|
return (
|
|
|
|
<Open
|
|
|
|
selectedDiagramId={selectedDiagramId}
|
|
|
|
setSelectedDiagramId={setSelectedDiagramId}
|
|
|
|
/>
|
|
|
|
);
|
|
|
|
case MODAL.SAVEAS:
|
|
|
|
return (
|
|
|
|
<Input
|
2024-05-16 11:44:39 +08:00
|
|
|
placeholder={t("name")}
|
2024-04-06 13:19:12 +08:00
|
|
|
value={saveAsTitle}
|
|
|
|
onChange={(v) => setSaveAsTitle(v)}
|
|
|
|
/>
|
|
|
|
);
|
|
|
|
case MODAL.CODE:
|
|
|
|
case MODAL.IMG:
|
|
|
|
if (exportData.data !== "" || exportData.data) {
|
|
|
|
return (
|
|
|
|
<>
|
|
|
|
{modal === MODAL.IMG ? (
|
|
|
|
<Image src={exportData.data} alt="Diagram" height={280} />
|
|
|
|
) : (
|
2024-04-19 22:28:32 +08:00
|
|
|
<CodeMirror
|
2024-04-06 13:19:12 +08:00
|
|
|
value={exportData.data}
|
2024-04-19 22:28:32 +08:00
|
|
|
height="360px"
|
|
|
|
extensions={languageExtension[exportData.extension]}
|
|
|
|
onChange={() => {}}
|
|
|
|
editable={false}
|
|
|
|
theme={settings.mode === "dark" ? vscodeDark : githubLight}
|
2024-04-06 13:19:12 +08:00
|
|
|
/>
|
|
|
|
)}
|
2024-05-16 11:44:39 +08:00
|
|
|
<div className="text-sm font-semibold mt-2">{t("filename")}:</div>
|
2024-04-06 13:19:12 +08:00
|
|
|
<Input
|
|
|
|
value={exportData.filename}
|
2024-05-16 11:44:39 +08:00
|
|
|
placeholder={t("filename")}
|
2024-04-06 13:19:12 +08:00
|
|
|
suffix={<div className="p-2">{`.${exportData.extension}`}</div>}
|
|
|
|
onChange={(value) =>
|
|
|
|
setExportData((prev) => ({ ...prev, filename: value }))
|
|
|
|
}
|
|
|
|
field="filename"
|
|
|
|
/>
|
|
|
|
</>
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
return (
|
2024-08-28 03:39:14 +08:00
|
|
|
<div className="text-center my-3 text-sky-600">
|
2024-05-16 11:44:39 +08:00
|
|
|
<Spin tip={t("loading")} size="large" />
|
2024-04-06 13:19:12 +08:00
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
2024-04-10 11:47:06 +08:00
|
|
|
case MODAL.TABLE_WIDTH:
|
|
|
|
return <SetTableWidth />;
|
2024-05-16 11:44:39 +08:00
|
|
|
case MODAL.LANGUAGE:
|
|
|
|
return <Language />;
|
2024-08-31 23:49:43 +08:00
|
|
|
case MODAL.SHARE:
|
|
|
|
return <Share title={title} />;
|
2024-04-06 13:19:12 +08:00
|
|
|
default:
|
|
|
|
return <></>;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
return (
|
|
|
|
<SemiUIModal
|
2024-08-04 20:27:31 +08:00
|
|
|
style={isRtl(i18n.language) ? { direction: "rtl" } : {}}
|
2024-04-06 13:19:12 +08:00
|
|
|
title={getModalTitle(modal)}
|
2024-08-31 23:49:43 +08:00
|
|
|
visible={modal !== MODAL.NONE}
|
2024-04-06 13:19:12 +08:00
|
|
|
onOk={getModalOnOk}
|
|
|
|
afterClose={() => {
|
|
|
|
setExportData(() => ({
|
|
|
|
data: "",
|
|
|
|
extension: "",
|
|
|
|
filename: `${title}_${new Date().toISOString()}`,
|
|
|
|
}));
|
|
|
|
setError({
|
|
|
|
type: STATUS.NONE,
|
|
|
|
message: "",
|
|
|
|
});
|
|
|
|
setImportData(null);
|
|
|
|
setImportSource({
|
|
|
|
src: "",
|
|
|
|
overwrite: true,
|
|
|
|
});
|
|
|
|
}}
|
|
|
|
onCancel={() => {
|
2024-07-21 15:26:29 +08:00
|
|
|
if (modal === MODAL.RENAME) setUncontrolledTitle(title);
|
2024-04-06 13:19:12 +08:00
|
|
|
setModal(MODAL.NONE);
|
|
|
|
}}
|
|
|
|
centered
|
|
|
|
closeOnEsc={true}
|
|
|
|
okText={getOkText(modal)}
|
|
|
|
okButtonProps={{
|
|
|
|
disabled:
|
|
|
|
(error && error?.type === STATUS.ERROR) ||
|
|
|
|
(modal === MODAL.IMPORT &&
|
|
|
|
(error.type === STATUS.ERROR || !importData)) ||
|
|
|
|
(modal === MODAL.RENAME && title === "") ||
|
|
|
|
((modal === MODAL.IMG || modal === MODAL.CODE) && !exportData.data) ||
|
|
|
|
(modal === MODAL.SAVEAS && saveAsTitle === "") ||
|
|
|
|
(modal === MODAL.IMPORT_SRC && importSource.src === ""),
|
2024-09-01 23:15:58 +08:00
|
|
|
hidden: modal === MODAL.SHARE,
|
2024-04-06 13:19:12 +08:00
|
|
|
}}
|
2024-09-01 23:15:58 +08:00
|
|
|
hasCancel={modal !== MODAL.SHARE}
|
2024-05-16 11:44:39 +08:00
|
|
|
cancelText={t("cancel")}
|
2024-08-08 04:00:19 +08:00
|
|
|
width={getModalWidth(modal)}
|
2024-08-04 20:27:31 +08:00
|
|
|
bodyStyle={{
|
|
|
|
maxHeight: window.innerHeight - 280,
|
|
|
|
overflow: "auto",
|
|
|
|
direction: "ltr",
|
|
|
|
}}
|
2024-04-06 13:19:12 +08:00
|
|
|
>
|
|
|
|
{getModalBody()}
|
|
|
|
</SemiUIModal>
|
|
|
|
);
|
|
|
|
}
|