drawDB/src/components/EditorHeader/Modal/Modal.jsx

404 lines
11 KiB
React
Raw Normal View History

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";
import { useState } from "react";
import { db } from "../../../data/db";
import {
useAreas,
2024-06-30 20:38:25 +08:00
useEnums,
useNotes,
useSettings,
2024-07-05 04:18:20 +08:00
useDiagram,
useTransform,
useTypes,
useUndoRedo,
useTasks,
} 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";
import Rename from "./Rename";
import Open from "./Open";
import New from "./New";
import ImportDiagram from "./ImportDiagram";
import ImportSource from "./ImportSource";
import SetTableWidth from "./SetTableWidth";
import Language from "./Language";
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";
import { useTranslation } from "react-i18next";
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-08-28 03:39:14 +08:00
import GithubToken from "./GithubToken";
2024-04-19 22:28:32 +08:00
const languageExtension = {
sql: [sql()],
json: [json()],
};
export default function Modal({
modal,
setModal,
title,
setTitle,
setDiagramId,
exportData,
setExportData,
2024-06-14 06:00:47 +08:00
importDb,
}) {
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();
const { setNotes } = useNotes();
const { setAreas } = useAreas();
const { setTypes } = useTypes();
const { settings } = useSettings();
2024-06-30 20:38:25 +08:00
const { setEnums } = useEnums();
const { setTasks } = useTasks();
const { setTransform } = useTransform();
const { setUndoStack, setRedoStack } = useUndoRedo();
2024-07-21 15:26:29 +08:00
const [uncontrolledTitle, setUncontrolledTitle] = useState(title);
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);
2024-08-28 03:39:14 +08:00
const [token, setToken] = useState(
localStorage.getItem("github_token") ?? "",
);
const overwriteDiagram = () => {
setTables(importData.tables);
setRelationships(importData.relationships);
setAreas(importData.subjectAreas);
setNotes(importData.notes);
if (importData.title) {
setTitle(importData.title);
}
if (databases[database].hasEnums && importData.enums) {
setEnums(importData.enums);
}
if (databases[database].hasTypes && importData.types) {
setTypes(importData.types);
}
};
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);
}
setDiagramId(diagram.id);
setTitle(diagram.name);
setTables(diagram.tables);
setRelationships(diagram.references);
setAreas(diagram.areas);
setNotes(diagram.notes);
setTasks(diagram.todos ?? []);
setTransform({
pan: diagram.pan,
zoom: diagram.zoom,
});
setUndoStack([]);
setRedoStack([]);
if (databases[database].hasTypes) {
setTypes(diagram.types ?? []);
}
if (databases[database].hasEnums) {
setEnums(diagram.enums ?? []);
}
window.name = `d ${diagram.id}`;
} else {
window.name = "";
Toast.error(t("didnt_find_diagram"));
}
})
.catch((error) => {
console.log(error);
Toast.error(t("didnt_find_diagram"));
});
};
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,
});
} catch (err) {
setError({
type: STATUS.ERROR,
message:
err.name +
" [Ln " +
err.location.start.line +
", Col " +
err.location.start.column +
"]: " +
err.message,
});
return;
}
2024-06-27 16:12:09 +08:00
const d = importSQL(
ast,
database === DB.GENERIC ? importDb : database,
database,
);
2024-04-06 13:22:23 +08:00
if (importSource.overwrite) {
setTables(d.tables);
setRelationships(d.relationships);
setTransform((prev) => ({ ...prev, pan: { x: 0, y: 0 } }));
setNotes([]);
setAreas([]);
2024-06-30 20:38:25 +08:00
if (databases[database].hasTypes) setTypes(d.types ?? []);
if (databases[database].hasEnums) setEnums(d.enums ?? []);
setUndoStack([]);
setRedoStack([]);
} else {
setTables((prev) =>
[...prev, ...d.tables].map((t, i) => ({ ...t, id: i })),
);
setRelationships((prev) =>
[...prev, ...d.relationships].map((r, i) => ({ ...r, id: i })),
);
}
setModal(MODAL.NONE);
};
const createNewDiagram = (id) => {
const newWindow = window.open("/editor");
newWindow.name = "lt " + id;
};
const getModalOnOk = async () => {
switch (modal) {
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) {
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);
setModal(MODAL.NONE);
return;
case MODAL.SAVEAS:
setTitle(saveAsTitle);
setModal(MODAL.NONE);
return;
case MODAL.NEW:
setModal(MODAL.NONE);
createNewDiagram(selectedTemplateId);
return;
2024-08-28 03:39:14 +08:00
case MODAL.GITHUB_TOKEN:
setModal(MODAL.NONE);
if (token !== "") {
localStorage.setItem("github_token", token);
} else {
localStorage.removeItem("github_token");
}
return;
default:
setModal(MODAL.NONE);
return;
}
};
const getModalBody = () => {
2024-08-30 15:09:50 +08:00
if (modal === MODAL.SHARE) return;
switch (modal) {
case MODAL.IMPORT:
return (
<ImportDiagram
setImportData={setImportData}
error={error}
setError={setError}
/>
);
case MODAL.IMPORT_SRC:
return (
<ImportSource
importData={importSource}
setImportData={setImportSource}
error={error}
setError={setError}
/>
);
case MODAL.NEW:
return (
<New
selectedTemplateId={selectedTemplateId}
setSelectedTemplateId={setSelectedTemplateId}
/>
);
case MODAL.RENAME:
2024-07-21 15:26:29 +08:00
return (
<Rename key={title} title={title} setTitle={setUncontrolledTitle} />
2024-07-21 15:26:29 +08:00
);
case MODAL.OPEN:
return (
<Open
selectedDiagramId={selectedDiagramId}
setSelectedDiagramId={setSelectedDiagramId}
/>
);
case MODAL.SAVEAS:
return (
<Input
placeholder={t("name")}
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
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}
/>
)}
<div className="text-sm font-semibold mt-2">{t("filename")}:</div>
<Input
value={exportData.filename}
placeholder={t("filename")}
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">
<Spin tip={t("loading")} size="large" />
</div>
);
}
case MODAL.TABLE_WIDTH:
return <SetTableWidth />;
case MODAL.LANGUAGE:
return <Language />;
2024-08-30 15:09:50 +08:00
// case MODAL.SHARE:
// return <Share setModal={setModal} />;
2024-08-28 03:39:14 +08:00
case MODAL.GITHUB_TOKEN:
return <GithubToken token={token} setToken={setToken} />;
default:
return <></>;
}
};
return (
<SemiUIModal
2024-08-04 20:27:31 +08:00
style={isRtl(i18n.language) ? { direction: "rtl" } : {}}
title={getModalTitle(modal)}
2024-08-30 15:09:50 +08:00
visible={modal !== MODAL.NONE && modal !== MODAL.SHARE}
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);
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 === ""),
}}
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",
}}
>
{getModalBody()}
</SemiUIModal>
);
}