import { Spin, Input, Image, Toast, Modal as SemiUIModal, } from "@douyinfe/semi-ui"; import { DB, MODAL, STATUS } from "../../../data/constants"; import { useState } from "react"; import { db } from "../../../data/db"; import { useAreas, useEnums, useNotes, useDiagram, useTransform, useTypes, useUndoRedo, useTasks, } from "../../../hooks"; import { saveAs } from "file-saver"; import { Parser } from "node-sql-parser"; 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"; import Share from "./Share"; import Code from "./Code"; import { useTranslation } from "react-i18next"; import { importSQL } from "../../../utils/importSQL"; import { databases } from "../../../data/databases"; import { isRtl } from "../../../i18n/utils/rtl"; export default function Modal({ modal, setModal, title, setTitle, setDiagramId, exportData, setExportData, importDb, }) { const { t, i18n } = useTranslation(); const { setTables, setRelationships, database, setDatabase } = useDiagram(); const { setNotes } = useNotes(); const { setAreas } = useAreas(); const { setTypes } = useTypes(); const { setEnums } = useEnums(); const { setTasks } = useTasks(); const { setTransform } = useTransform(); const { setUndoStack, setRedoStack } = useUndoRedo(); 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); 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) { 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 { ast = parser.astify(importSource.src, { database: database === DB.GENERIC ? importDb : database, }); } catch (error) { const message = error.location ? `${error.name} [Ln ${error.location.start.line}, Col ${error.location.start.column}]: ${error.message}` : error.message; setError({ type: STATUS.ERROR, message }); return; } try { const diagramData = importSQL( ast, database === DB.GENERIC ? importDb : database, database, ); 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); } catch { setError({ type: STATUS.ERROR, message: `Please check for syntax errors or let us know about the error.`, }); } }; 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: setTitle(uncontrolledTitle); 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 ( ); case MODAL.IMPORT_SRC: return ( ); case MODAL.NEW: return ( ); case MODAL.RENAME: return ( ); case MODAL.OPEN: return ( ); case MODAL.SAVEAS: return ( setSaveAsTitle(v)} /> ); case MODAL.CODE: case MODAL.IMG: if (exportData.data !== "" || exportData.data) { return ( <> {modal === MODAL.IMG ? ( Diagram ) : ( )}
{t("filename")}:
{`.${exportData.extension}`}} onChange={(value) => setExportData((prev) => ({ ...prev, filename: value })) } field="filename" /> ); } else { return (
); } case MODAL.TABLE_WIDTH: return ; case MODAL.LANGUAGE: return ; case MODAL.SHARE: return ; default: return <>; } }; return ( { setExportData(() => ({ data: "", extension: "", filename: `${title}_${new Date().toISOString()}`, })); setError({ type: STATUS.NONE, message: "", }); setImportData(null); setImportSource({ src: "", overwrite: true, }); }} onCancel={() => { 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 === ""), hidden: modal === MODAL.SHARE, }} hasCancel={modal !== MODAL.SHARE} cancelText={t("cancel")} width={getModalWidth(modal)} bodyStyle={{ maxHeight: window.innerHeight - 280, overflow: "auto", direction: "ltr", }} > {getModalBody()} ); }