diff --git a/src/components/EditorHeader/ControlPanel.jsx b/src/components/EditorHeader/ControlPanel.jsx index 1e067918..eea63c17 100644 --- a/src/components/EditorHeader/ControlPanel.jsx +++ b/src/components/EditorHeader/ControlPanel.jsx @@ -21,15 +21,8 @@ import { Toast, Popconfirm, } from "@douyinfe/semi-ui"; -import { toPng, toJpeg, toSvg } from "html-to-image"; +import { toPng, toJpeg } from "html-to-image"; import { saveAs } from "file-saver"; -import { - jsonToMySQL, - jsonToPostgreSQL, - jsonToSQLite, - jsonToMariaDB, - jsonToSQLServer, -} from "../../utils/toSQL"; import { ObjectType, Action, @@ -60,8 +53,8 @@ import useSaveState from "../../hooks/useSaveState"; import { IconAddArea, IconAddNote, IconAddTable } from "../../icons"; import LayoutDropdown from "./LayoutDropdown"; import Sidesheet from "./SideSheet/Sidesheet"; -import Modal from "./Modal/Modal"; import { useTranslation } from "react-i18next"; +import ModalManager from "./ModalManager"; export default function ControlPanel({ diagramId, @@ -72,13 +65,7 @@ export default function ControlPanel({ }) { const [modal, setModal] = useState(MODAL.NONE); const [sidesheet, setSidesheet] = useState(SIDESHEET.NONE); - const [prevTitle, setPrevTitle] = useState(title); const [showEditName, setShowEditName] = useState(false); - const [exportData, setExportData] = useState({ - data: null, - filename: `${title}_${new Date().toISOString()}`, - extension: "", - }); const { saveState, setSaveState } = useSaveState(); const { layout, setLayout } = useLayout(); const { settings, setSettings } = useSettings(); @@ -452,7 +439,7 @@ export default function ControlPanel({ } }; - const fileImport = () => setModal(MODAL.IMPORT); + const importDiagram = () => setModal(MODAL.IMPORT_DIAGRAM); const viewGrid = () => setSettings((prev) => ({ ...prev, showGrid: !prev.showGrid })); const zoomIn = () => @@ -704,7 +691,6 @@ export default function ControlPanel({ rename: { function: () => { setModal(MODAL.RENAME); - setPrevTitle(title); }, }, delete_diagram: { @@ -730,7 +716,7 @@ export default function ControlPanel({ }, }, import_diagram: { - function: fileImport, + function: importDiagram, shortcut: "Ctrl+I", }, import_from_source: { @@ -739,66 +725,8 @@ export default function ControlPanel({ export_as: { children: [ { - PNG: () => { - toPng(document.getElementById("canvas")).then(function (dataUrl) { - setExportData((prev) => ({ - ...prev, - data: dataUrl, - extension: "png", - })); - }); - setModal(MODAL.IMG); - }, - }, - { - JPEG: () => { - toJpeg(document.getElementById("canvas"), { quality: 0.95 }).then( - function (dataUrl) { - setExportData((prev) => ({ - ...prev, - data: dataUrl, - extension: "jpeg", - })); - }, - ); - setModal(MODAL.IMG); - }, - }, - { - JSON: () => { - setModal(MODAL.CODE); - const result = JSON.stringify( - { - tables: tables, - relationships: relationships, - notes: notes, - subjectAreas: areas, - types: types, - title: title, - }, - null, - 2, - ); - setExportData((prev) => ({ - ...prev, - data: result, - extension: "json", - })); - }, - }, - { - SVG: () => { - const filter = (node) => node.tagName !== "i"; - toSvg(document.getElementById("canvas"), { filter: filter }).then( - function (dataUrl) { - setExportData((prev) => ({ - ...prev, - data: dataUrl, - extension: "svg", - })); - }, - ); - setModal(MODAL.IMG); + IMAGE: () => { + setModal(MODAL.EXPORT_IMG); }, }, { @@ -817,10 +745,20 @@ export default function ControlPanel({ canvas.offsetWidth, canvas.offsetHeight, ); - doc.save(`${exportData.filename}.pdf`); + doc.save(`${title}_${new Date().toISOString()}.pdf`); }); }, }, + { + SQL: () => { + setModal(MODAL.EXPORT_SQL); + }, + }, + { + JSON: () => { + setModal(MODAL.EXPORT_JSON); + }, + }, { DRAWDB: () => { const result = JSON.stringify( @@ -840,88 +778,8 @@ export default function ControlPanel({ const blob = new Blob([result], { type: "text/plain;charset=utf-8", }); - saveAs(blob, `${exportData.filename}.ddb`); - }, - }, - ], - function: () => {}, - }, - export_source: { - children: [ - { - MySQL: () => { - setModal(MODAL.CODE); - const src = jsonToMySQL({ - tables: tables, - references: relationships, - types: types, - }); - setExportData((prev) => ({ - ...prev, - data: src, - extension: "sql", - })); - }, - }, - { - PostgreSQL: () => { - setModal(MODAL.CODE); - const src = jsonToPostgreSQL({ - tables: tables, - references: relationships, - types: types, - }); - setExportData((prev) => ({ - ...prev, - data: src, - extension: "sql", - })); - }, - }, - { - SQLite: () => { - setModal(MODAL.CODE); - const src = jsonToSQLite({ - tables: tables, - references: relationships, - types: types, - }); - setExportData((prev) => ({ - ...prev, - data: src, - extension: "sql", - })); - }, - }, - { - MariaDB: () => { - setModal(MODAL.CODE); - const src = jsonToMariaDB({ - tables: tables, - references: relationships, - types: types, - }); - setExportData((prev) => ({ - ...prev, - data: src, - extension: "sql", - })); - }, - }, - { - MSSQL: () => { - setModal(MODAL.CODE); - const src = jsonToSQLServer({ - tables: tables, - references: relationships, - types: types, - }); - setExportData((prev) => ({ - ...prev, - data: src, - extension: "sql", - })); - }, + saveAs(blob, `${title}_${new Date().toISOString()}.ddb`); + } }, ], function: () => {}, @@ -1166,7 +1024,7 @@ export default function ControlPanel({ }, }; - useHotkeys("ctrl+i, meta+i", fileImport, { preventDefault: true }); + useHotkeys("ctrl+i, meta+i", importDiagram, { preventDefault: true }); useHotkeys("ctrl+z, meta+z", undo, { preventDefault: true }); useHotkeys("ctrl+y, meta+y", redo, { preventDefault: true }); useHotkeys("ctrl+s, meta+s", save, { preventDefault: true }); @@ -1196,20 +1054,21 @@ export default function ControlPanel({ }); useHotkeys("ctrl+alt+w, meta+alt+w", fitWindow, { preventDefault: true }); + const hideModal = () => { + setModal(MODAL.NONE); + }; + return ( <> {layout.header && header()} {layout.toolbar && toolbar()} - + {children} + + ); +} diff --git a/src/components/EditorHeader/Modal/ExportImage.jsx b/src/components/EditorHeader/Modal/ExportImage.jsx new file mode 100644 index 00000000..77153c6e --- /dev/null +++ b/src/components/EditorHeader/Modal/ExportImage.jsx @@ -0,0 +1,80 @@ +import { useEffect, useState } from "react"; +import { toPng, toJpeg, toSvg } from "html-to-image"; +import { Image, Select, Spin } from "@douyinfe/semi-ui"; +import { useTranslation } from "react-i18next"; +import { IMAGE_TYPES } from "../../../data/constants"; +import ExportModal from "./ExportModal"; + +export default function ExportImage({ title, hideModal }) { + const { t } = useTranslation(); + const [exportData, setExportData] = useState({ + data: null, + filename: `${title}_${new Date().toISOString()}`, + extension: IMAGE_TYPES[0], + }); + + const changeType = async (type) => { + setExportData((prev) => ({ + ...prev, + data: null, + extension: type, + })); + + const canvasElm = document.getElementById("canvas"); + + let dataUrl; + switch (type) { + case "png": + dataUrl = await toPng(canvasElm); + break; + case "jpeg": + dataUrl = await toJpeg(canvasElm, { quality: 0.95 }); + break; + case "svg": + dataUrl = await toSvg(canvasElm, { + filter: (node) => node.tagName !== "i", + }); + break; + } + + setExportData((prev) => ({ + ...prev, + data: dataUrl, + })); + }; + + useEffect(() => { + changeType(IMAGE_TYPES[0]); + }, []); + + return ( + +
{t("format")}:
+ {`.${exportData.extension}`}} + onChange={(value) => + setExportData((prev) => ({ ...prev, filename: value })) + } + field="filename" + /> + {children} + + ); +} diff --git a/src/components/EditorHeader/Modal/ExportSql.jsx b/src/components/EditorHeader/Modal/ExportSql.jsx new file mode 100644 index 00000000..a139e196 --- /dev/null +++ b/src/components/EditorHeader/Modal/ExportSql.jsx @@ -0,0 +1,93 @@ +import { useCallback, useEffect, useState } from "react"; +import { useTranslation } from "react-i18next"; +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 { Select } from "@douyinfe/semi-ui"; +import { DATABASE_TYPES } from "../../../data/constants"; +import toSQL from "../../../utils/toSQL"; +import { useTables, useSettings, useTypes } from "../../../hooks"; +import ExportModal from "./ExportModal"; + +const languageExtension = { + sql: [sql()], + json: [json()], +}; + +export default function ExportSql({ title, hideModal }) { + const { t } = useTranslation(); + const { settings } = useSettings(); + const { tables, relationships } = useTables(); + const { types } = useTypes(); + + const [options, setOptions] = useState({ + format: DATABASE_TYPES[0], + }); + + const [exportData, setExportData] = useState({ + data: null, + rawData: "", + filename: `${title}_${new Date().toISOString()}`, + extension: "sql", + }); + + const changeOptions = (options) => { + setOptions((prev) => ({ + ...prev, + ...options, + })); + }; + + const changeData = useCallback( + (options) => { + const rawData = toSQL[`jsonTo${options.format}`]({ + tables: tables, + references: relationships, + types: types, + }); + + setExportData((prev) => ({ + ...prev, + data: new Blob([rawData], { + type: "application/json", + }), + rawData: rawData, + })); + }, + [tables, relationships, types], + ); + + useEffect(() => { + changeData(options); + }, [options, changeData]); + + return ( + +
{t("format")}:
+ setSaveAsTitle(v)} - /> - ); - case MODAL.CODE: - case MODAL.IMG: - if (exportData.data !== "" || exportData.data) { - return ( - <> - {modal === MODAL.IMG ? ( - Diagram - ) : ( - {}} - editable={false} - theme={settings.mode === "dark" ? vscodeDark : githubLight} - /> - )} -
{t("filename")}:
- {`.${exportData.extension}`}} - onChange={(value) => - setExportData((prev) => ({ ...prev, filename: value })) - } - field="filename" - /> - - ); - } else { - return ( -
- -
- ); - } - case MODAL.TABLE_WIDTH: - return ; - case MODAL.LANGUAGE: - return ; - default: - return <>; - } - }; - - return ( - { - setExportData(() => ({ - data: "", - extension: "", - filename: `${title}_${new Date().toISOString()}`, - })); - setError({ - type: STATUS.NONE, - message: "", - }); - setImportData(null); - setImportSource({ - src: "", - overwrite: true, - dbms: "MySQL", - }); - }} - onCancel={() => { - if (modal === MODAL.RENAME) setTitle(prevTitle); - 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")} - width={modal === MODAL.NEW ? 740 : 600} - bodyStyle={{ maxHeight: window.innerHeight - 280, overflow: "auto" }} - > - {getModalBody()} - - ); -} diff --git a/src/components/EditorHeader/Modal/New.jsx b/src/components/EditorHeader/Modal/New.jsx index 81ffcdb2..c0d25589 100644 --- a/src/components/EditorHeader/Modal/New.jsx +++ b/src/components/EditorHeader/Modal/New.jsx @@ -1,45 +1,67 @@ -import { db } from "../../../data/db"; -import { useSettings } from "../../../hooks"; import { useLiveQuery } from "dexie-react-hooks"; -import Thumbnail from "../../Thumbnail"; +import { useState } from "react"; import { useTranslation } from "react-i18next"; +import { db } from "../../../data/db"; +import Thumbnail from "../../Thumbnail"; +import { useSettings } from "../../../hooks"; +import BaseModal from "./BaseModal"; -export default function New({ selectedTemplateId, setSelectedTemplateId }) { - const { settings } = useSettings(); +export default function New({ hideModal }) { const { t } = useTranslation(); + const { settings } = useSettings(); + const [selectedTemplateId, setSelectedTemplateId] = useState(-1); + const templates = useLiveQuery(() => db.templates.toArray()); + const createNewDiagram = (id) => { + const newWindow = window.open("/editor"); + newWindow.name = "lt " + id; + }; + + const onOk = () => { + hideModal(); + createNewDiagram(selectedTemplateId); + }; + return ( -
-
setSelectedTemplateId(0)}> -
- -
-
{t("blank")}
-
- {templates?.map((temp, i) => ( -
setSelectedTemplateId(temp.id)}> + +
+
setSelectedTemplateId(0)}>
- +
-
{temp.title}
+
{t("blank")}
- ))} -
+ {templates?.map((temp, i) => ( +
setSelectedTemplateId(temp.id)}> +
+ +
+
{temp.title}
+
+ ))} +
+ ); } diff --git a/src/components/EditorHeader/Modal/Open.jsx b/src/components/EditorHeader/Modal/Open.jsx index cf7a5266..b9e22603 100644 --- a/src/components/EditorHeader/Modal/Open.jsx +++ b/src/components/EditorHeader/Modal/Open.jsx @@ -1,11 +1,29 @@ -import { db } from "../../../data/db"; -import { Banner } from "@douyinfe/semi-ui"; +import { Banner, Toast } from "@douyinfe/semi-ui"; import { useLiveQuery } from "dexie-react-hooks"; +import { useState } from "react"; import { useTranslation } from "react-i18next"; +import { db } from "../../../data/db"; +import { + useAreas, + useNotes, + useTables, + useTransform, + useTypes, + useUndoRedo, +} from "../../../hooks"; +import BaseModal from "./BaseModal"; -export default function Open({ selectedDiagramId, setSelectedDiagramId }) { - const diagrams = useLiveQuery(() => db.diagrams.toArray()); +export default function Open({ hideModal, setDiagramId, setTitle }) { const { t } = useTranslation(); + const { setTables, setRelationships } = useTables(); + const { setNotes } = useNotes(); + const { setAreas } = useAreas(); + const { setTypes } = useTypes(); + const { setTransform } = useTransform(); + const { setUndoStack, setRedoStack } = useUndoRedo(); + + const diagrams = useLiveQuery(() => db.diagrams.toArray()); + const [selectedDiagramId, setSelectedDiagramId] = useState(0); const getDiagramSize = (d) => { const size = JSON.stringify(d).length; @@ -18,8 +36,48 @@ export default function Open({ selectedDiagramId, setSelectedDiagramId }) { return sizeStr; }; + + const loadDiagram = async (id) => { + await db.diagrams + .get(id) + .then((diagram) => { + if (diagram) { + setDiagramId(diagram.id); + setTitle(diagram.name); + setTables(diagram.tables); + setTypes(diagram.types); + setRelationships(diagram.references); + setAreas(diagram.areas); + setNotes(diagram.notes); + setTransform({ + pan: diagram.pan, + zoom: diagram.zoom, + }); + setUndoStack([]); + setRedoStack([]); + window.name = `d ${diagram.id}`; + } else { + Toast.error("Oops! Something went wrong."); + } + }) + .catch(() => { + Toast.error("Oops! Couldn't load diagram."); + }); + }; + + const onOk = () => { + if (selectedDiagramId === 0) return; + loadDiagram(selectedDiagramId); + hideModal(); + }; + return ( -
+ {diagrams?.length === 0 ? (
)} -
+ ); } diff --git a/src/components/EditorHeader/Modal/Rename.jsx b/src/components/EditorHeader/Modal/Rename.jsx index d8767468..b37aeff1 100644 --- a/src/components/EditorHeader/Modal/Rename.jsx +++ b/src/components/EditorHeader/Modal/Rename.jsx @@ -1,14 +1,34 @@ import { Input } from "@douyinfe/semi-ui"; +import { useRef } from "react"; import { useTranslation } from "react-i18next"; +import BaseModal from "./BaseModal"; -export default function Rename({ title, setTitle }) { +export default function Rename({ hideModal, title, setTitle }) { const { t } = useTranslation(); + const originalTitle = useRef(title); + + const onCancel = () => { + setTitle(originalTitle.current); + hideModal(); + }; + + const onOk = () => { + hideModal(); + }; return ( - setTitle(v)} - /> + + setTitle(v)} + /> + ); } diff --git a/src/components/EditorHeader/Modal/SaveAs.jsx b/src/components/EditorHeader/Modal/SaveAs.jsx new file mode 100644 index 00000000..9296d6d7 --- /dev/null +++ b/src/components/EditorHeader/Modal/SaveAs.jsx @@ -0,0 +1,30 @@ +import { Input } from "@douyinfe/semi-ui"; +import { useState } from "react"; +import { useTranslation } from "react-i18next"; +import BaseModal from "./BaseModal"; + +export default function SaveAs({ hideModal, title, setTitle }) { + const { t } = useTranslation(); + const [saveAsTitle, setSaveAsTitle] = useState(title); + + const onOk = () => { + setTitle(saveAsTitle); + hideModal(); + }; + + return ( + + setSaveAsTitle(v)} + /> + + ); +} diff --git a/src/components/EditorHeader/Modal/SetTableWidth.jsx b/src/components/EditorHeader/Modal/SetTableWidth.jsx index 93482e68..c9d9998c 100644 --- a/src/components/EditorHeader/Modal/SetTableWidth.jsx +++ b/src/components/EditorHeader/Modal/SetTableWidth.jsx @@ -1,17 +1,26 @@ import { InputNumber } from "@douyinfe/semi-ui"; +import { useTranslation } from "react-i18next"; import { useSettings } from "../../../hooks"; +import BaseModal from "./BaseModal"; -export default function SetTableWidth() { +export default function SetTableWidth({ hideModal }) { + const { t } = useTranslation(); const { settings, setSettings } = useSettings(); return ( - { - if (c < 180) return; - setSettings((prev) => ({ ...prev, tableWidth: c })); - }} - /> + + { + if (c < 180) return; + setSettings((prev) => ({ ...prev, tableWidth: c })); + }} + /> + ); } diff --git a/src/components/EditorHeader/ModalManager.jsx b/src/components/EditorHeader/ModalManager.jsx new file mode 100644 index 00000000..4fec954c --- /dev/null +++ b/src/components/EditorHeader/ModalManager.jsx @@ -0,0 +1,53 @@ +import { MODAL } from "../../data/constants"; +import Rename from "./Modal/Rename"; +import Open from "./Modal/Open"; +import New from "./Modal/New"; +import ImportDiagram from "./Modal/ImportDiagram"; +import ImportSource from "./Modal/ImportSource"; +import SetTableWidth from "./Modal/SetTableWidth"; +import Language from "./Modal/Language"; +import ExportImage from "./Modal/ExportImage"; +import ExportJson from "./Modal/ExportJson"; +import ExportSql from "./Modal/ExportSql"; +import SaveAs from "./Modal/SaveAs"; + +export default function ModalManager({ + modal, + hideModal, + title, + setTitle, + setDiagramId, +}) { + switch (modal) { + case MODAL.EXPORT_IMG: + return ; + case MODAL.EXPORT_SQL: + return ; + case MODAL.EXPORT_JSON: + return ; + case MODAL.IMPORT_DIAGRAM: + return ; + case MODAL.IMPORT_SRC: + return ; + case MODAL.NEW: + return ; + case MODAL.OPEN: + return ( + + ); + case MODAL.SAVEAS: + return ; + case MODAL.RENAME: + return ; + case MODAL.TABLE_WIDTH: + return ; + case MODAL.LANGUAGE: + return ; + default: + return <>; + } +} diff --git a/src/data/constants.js b/src/data/constants.js index 480b1ba4..0052fd5b 100644 --- a/src/data/constants.js +++ b/src/data/constants.js @@ -105,16 +105,17 @@ export const State = { export const MODAL = { NONE: 0, - IMG: 1, - CODE: 2, - IMPORT: 3, + NEW: 1, + OPEN: 2, + SAVEAS: 3, RENAME: 4, - OPEN: 5, - SAVEAS: 6, - NEW: 7, - IMPORT_SRC: 8, - TABLE_WIDTH: 9, - LANGUAGE: 10, + IMPORT_SRC: 5, + IMPORT_DIAGRAM: 6, + TABLE_WIDTH: 7, + LANGUAGE: 8, + EXPORT_IMG: 9, + EXPORT_SQL: 10, + EXPORT_JSON: 11, }; export const STATUS = { @@ -129,3 +130,13 @@ export const SIDESHEET = { TODO: 1, TIMELINE: 2, }; + +export const DATABASE_TYPES = [ + "MySQL", + "MariaDB", + "PostgreSQL", + "SQLite", + "MSSQL", +]; + +export const IMAGE_TYPES = ["png", "jpeg", "svg"]; diff --git a/src/i18n/locales/da.js b/src/i18n/locales/da.js index baca4f5b..3f775dcb 100644 --- a/src/i18n/locales/da.js +++ b/src/i18n/locales/da.js @@ -165,7 +165,7 @@ const da = { no_values_for_field: "'{{fieldName}}' felt fra tabellen '{{tableName}}' er af type `{{type}}` men ingen værdi er blevet specificeret", default_doesnt_match_type: - "Standardværdien for feltet '{{fieldName}}' i tabellen '{{table.name}}' stemmer ikke overens med dens type", + "Standardværdien for feltet '{{fieldName}}' i tabellen '{{tableName}}' stemmer ikke overens med dens type", not_null_is_null: "'{{fieldName}}' felt fra tabellen '{{tableName}}' er IKKE NUL, men har standardværdien NUL", duplicate_fields: diff --git a/src/i18n/locales/de.js b/src/i18n/locales/de.js index a1c2d776..973507f2 100644 --- a/src/i18n/locales/de.js +++ b/src/i18n/locales/de.js @@ -168,7 +168,7 @@ const de = { no_values_for_field: "Das Feld '{{fieldName}}' der Tabelle '{{tableName}}' ist vom Typ `{{type}}`, aber es wurden keine Werte angegeben", default_doesnt_match_type: - "Der Standardwert für das Feld '{{fieldName}}' in der Tabelle '{{table.name}}' entspricht nicht seinem Typ", + "Der Standardwert für das Feld '{{fieldName}}' in der Tabelle '{{tableName}}' entspricht nicht seinem Typ", not_null_is_null: "Das Feld '{{fieldName}}' der Tabelle '{{tableName}}' ist NOT NULL, hat aber standardmäßig NULL", duplicate_fields: diff --git a/src/i18n/locales/en.js b/src/i18n/locales/en.js index 2bce8950..b3e78f0c 100644 --- a/src/i18n/locales/en.js +++ b/src/i18n/locales/en.js @@ -165,7 +165,7 @@ const en = { no_values_for_field: "'{{fieldName}}' field of table '{{tableName}}' is of type `{{type}}` but no values have been specified", default_doesnt_match_type: - "Default value for field '{{fieldName}}' in table '{{table.name}}' does not match its type", + "Default value for field '{{fieldName}}' in table '{{tableName}}' does not match its type", not_null_is_null: "'{{fieldName}}' field of table '{{tableName}}' is NOT NULL but has default NULL", duplicate_fields: @@ -212,6 +212,8 @@ const en = { edit_relationship: "{{extra}} Edit relationship {{refName}}", delete_relationship: "Delete relationship {{refName}}", not_found: "Not found", + export_json: "Export json", + format: "Format", }, }; diff --git a/src/i18n/locales/es.js b/src/i18n/locales/es.js index 39c00c68..173b2975 100644 --- a/src/i18n/locales/es.js +++ b/src/i18n/locales/es.js @@ -165,7 +165,7 @@ const es = { no_values_for_field: "El campo '{{fieldName}}' de la tabla '{{tableName}}' es de tipo `{{type}}` pero no se han especificado valores", default_doesnt_match_type: - "El valor predeterminado para el campo '{{fieldName}}' en la tabla '{{table.name}}' no coincide con su tipo", + "El valor predeterminado para el campo '{{fieldName}}' en la tabla '{{tableName}}' no coincide con su tipo", not_null_is_null: "El campo '{{fieldName}}' de la tabla '{{tableName}}' es NOT NULL pero tiene NULL por defecto", duplicate_fields: diff --git a/src/i18n/locales/fa.js b/src/i18n/locales/fa.js index 4e4c4414..b7214458 100644 --- a/src/i18n/locales/fa.js +++ b/src/i18n/locales/fa.js @@ -156,7 +156,7 @@ const fa = { empty_field_name: "فیلد خالی name در جدول '{{tableName}}'", empty_field_type: "فیلد خالی type در جدول '{{tableName}}'", no_values_for_field: "فیلد '{{fieldName}}' جدول '{{tableName}}' از نوع {{type}} است اما هیچ مقداری مشخص نشده است", - default_doesnt_match_type: "مقدار پیش‌فرض برای فیلد '{{fieldName}}' در جدول '{{table.name}}' با نوع آن مطابقت ندارد", + default_doesnt_match_type: "مقدار پیش‌فرض برای فیلد '{{fieldName}}' در جدول '{{tableName}}' با نوع آن مطابقت ندارد", not_null_is_null: "فیلد '{{fieldName}}' جدول '{{tableName}}' غیر خالی است اما پیش‌فرض آن خالی است", duplicate_fields: "فیلدهای تکراری جدول به نام '{{fieldName}}' در جدول '{{tableName}}'", duplicate_index: "شاخص تکراری به نام '{{indexName}}' در جدول '{{tableName}}'", @@ -198,9 +198,8 @@ const fa = { edit_relationship: "{{extra}} ویرایش ارتباط {{refName}}", delete_relationship: "حذف ارتباط {{refName}}", not_found: "یافت نشد" - + }, }; - + export { fa,persian }; - \ No newline at end of file diff --git a/src/i18n/locales/pt-br.js b/src/i18n/locales/pt-br.js index 9368201a..008bdff8 100644 --- a/src/i18n/locales/pt-br.js +++ b/src/i18n/locales/pt-br.js @@ -165,7 +165,7 @@ const pt = { no_values_for_field: "O campo '{{fieldName}}' da tabela '{{tableName}}' é do tipo `{{type}}`, mas nenhum valor foi especificado", default_doesnt_match_type: - "O valor padrão para o campo '{{fieldName}}' na tabela '{{table.name}}' não corresponde ao seu tipo", + "O valor padrão para o campo '{{fieldName}}' na tabela '{{tableName}}' não corresponde ao seu tipo", not_null_is_null: "O campo '{{fieldName}}' da tabela '{{tableName}}' é NOT NULL mas tem o valor padrão NULL", duplicate_fields: diff --git a/src/i18n/locales/vi.js b/src/i18n/locales/vi.js index 42662e47..063f5ca6 100644 --- a/src/i18n/locales/vi.js +++ b/src/i18n/locales/vi.js @@ -112,7 +112,7 @@ const vi = { size: "Kích cỡ", precision: "Độ chính xác", set_precision: "Đặt độ chính xác: (kích thước, chữ số)", - use_for_batch_input: "Sử dụng , để nhập hàng loạt", + use_for_batch_input: "Sử dụng để nhập hàng loạt", indices: "Chỉ số", add_index: "Thêm chỉ mục", select_fields: "Chọn các trường", @@ -123,9 +123,9 @@ const vi = { on_update: "Khi cập nhật", on_delete: "Khi xóa", swap: "Tráo đổi", - one_to_one: "Một đối một", - one_to_many: "Một đến nhiều", - many_to_one: "Nhiều thành một", + one_to_one: "Một với một", + one_to_many: "Một với nhiều", + many_to_one: "Nhiều với một", content: "Nội dung", types_info: "Tính năng này dành cho các DBMS quan hệ đối tượng như PostgreSQL.\nNếu được sử dụng cho MySQL hoặc MariaDB, loại JSON sẽ được tạo bằng kiểm tra xác thực json tương ứng.\nNếu được sử dụng cho SQLite, nó sẽ được dịch sang BLOB.\nNếu được sử dụng cho MSSQL một bí danh loại cho trường đầu tiên sẽ được tạo.", table_deleted: "Đã xóa bảng", @@ -156,7 +156,7 @@ const vi = { empty_field_name: "Trường trống `name` trong bảng '{{tableName}}'", empty_field_type: "Trường trống `loại` trong bảng '{{tableName}}'", no_values_for_field: "Trường '{{fieldName}}' của bảng '{{tableName}}' thuộc loại `{{type}}` nhưng không có giá trị nào được chỉ định", - default_doesnt_match_type: "Giá trị mặc định cho trường '{{fieldName}}' trong bảng '{{table.name}}' không khớp với loại của nó", + default_doesnt_match_type: "Giá trị mặc định cho trường '{{fieldName}}' trong bảng '{{tableName}}' không khớp với loại của nó", not_null_is_null: "Trường '{{fieldName}}' của bảng '{{tableName}}' là NOT NULL nhưng đang là NULL", duplicate_fields: "Các trường bảng trùng lặp theo tên '{{fieldName}}' trong bảng '{{tableName}}'", duplicate_index: "Chỉ mục trùng lặp theo tên '{{indexName}}' trong bảng '{{tableName}}'", @@ -198,6 +198,8 @@ const vi = { edit_relationship: "{{extra}} Chỉnh sửa quan hệ {{refName}}", delete_relationship: "Xóa quan hệ {{refName}}", not_found: "Không tìm thấy", + export_json: "Xuất json", + format: "Định dạng", }, }; diff --git a/src/utils/modalTitles.js b/src/utils/modalTitles.js deleted file mode 100644 index 6501aa08..00000000 --- a/src/utils/modalTitles.js +++ /dev/null @@ -1,49 +0,0 @@ -import { MODAL } from "../data/constants"; -import i18n from "../i18n/i18n"; - -export const getModalTitle = (modal) => { - switch (modal) { - case MODAL.IMPORT: - case MODAL.IMPORT_SRC: - return i18n.t("import_diagram"); - case MODAL.CODE: - return i18n.t("export_source"); - case MODAL.IMG: - return i18n.t("export_image"); - case MODAL.RENAME: - return i18n.t("rename_diagram"); - case MODAL.OPEN: - return i18n.t("open_diagram"); - case MODAL.SAVEAS: - return i18n.t("save_as"); - case MODAL.NEW: - return i18n.t("create_new_diagram"); - case MODAL.TABLE_WIDTH: - return i18n.t("table_width"); - case MODAL.LANGUAGE: - return i18n.t("language"); - default: - return ""; - } -}; - -export const getOkText = (modal) => { - switch (modal) { - case MODAL.IMPORT: - case MODAL.IMPORT_SRC: - return i18n.t("import"); - case MODAL.CODE: - case MODAL.IMG: - return i18n.t("export"); - case MODAL.RENAME: - return i18n.t("rename"); - case MODAL.OPEN: - return i18n.t("open"); - case MODAL.SAVEAS: - return i18n.t("save_as"); - case MODAL.NEW: - return i18n.t("create"); - default: - return i18n.t("confirm"); - } -}; diff --git a/src/utils/toSQL.js b/src/utils/toSQL.js index 2ad49616..edae5bc3 100644 --- a/src/utils/toSQL.js +++ b/src/utils/toSQL.js @@ -39,58 +39,61 @@ export function generateSchema(type) { )}\n\t\t\t},\n\t\t\t"additionalProperties": false\n\t\t}`; } -export function getTypeString(field, dbms = "mysql", baseType = false) { - if (dbms === "mysql") { - if (field.type === "UUID") { - return `VARCHAR(36)`; - } - if (hasPrecision(field.type) || isSized(field.type)) { - return `${field.type}${field.size ? `(${field.size})` : ""}`; - } - if (field.type === "SET" || field.type === "ENUM") { - return `${field.type}(${field.values.map((v) => `"${v}"`).join(", ")})`; - } - if (!sqlDataTypes.includes(field.type)) { - return "JSON"; - } - return field.type; - } else if (dbms === "postgres") { - if (field.type === "SMALLINT" && field.increment) { - return "smallserial"; - } - if (field.type === "INT" && field.increment) { - return "serial"; - } - if (field.type === "BIGINT" && field.increment) { - return "bigserial"; - } - if (field.type === "ENUM") { - return `${field.name}_t`; - } - if (field.type === "SET") { - return `${field.name}_t[]`; - } - if (field.type === "TIMESTAMP") { - return "TIMESTAMPTZ"; - } - if (field.type === "DATETIME") { - return `timestamp`; - } - if (isSized(field.type)) { - const type = - field.type === "BINARY" - ? "bit" - : field.type === "VARBINARY" - ? "bit varying" - : field.type.toLowerCase(); - return `${type}(${field.size})`; - } - if (hasPrecision(field.type) && field.size !== "") { - return `${field.type}${field.size}`; - } - return field.type.toLowerCase(); - } else if (dbms === "mssql") { - let type = field.type; +export function getMysqlType(field) { + if (field.type === "UUID") { + return `VARCHAR(36)`; + } + if (hasPrecision(field.type) || isSized(field.type)) { + return `${field.type}${field.size ? `(${field.size})` : ""}`; + } + if (field.type === "SET" || field.type === "ENUM") { + return `${field.type}(${field.values.map((v) => `"${v}"`).join(", ")})`; + } + if (!sqlDataTypes.includes(field.type)) { + return "JSON"; + } + return field.type; +} + +export function getPostgreSQLType(field) { + if (field.type === "SMALLINT" && field.increment) { + return "smallserial"; + } + if (field.type === "INT" && field.increment) { + return "serial"; + } + if (field.type === "BIGINT" && field.increment) { + return "bigserial"; + } + if (field.type === "ENUM") { + return `${field.name}_t`; + } + if (field.type === "SET") { + return `${field.name}_t[]`; + } + if (field.type === "TIMESTAMP") { + return "TIMESTAMPTZ"; + } + if (field.type === "DATETIME") { + return `timestamp`; + } + if (isSized(field.type)) { + const type = + field.type === "BINARY" + ? "bit" + : field.type === "VARBINARY" + ? "bit varying" + : field.type.toLowerCase(); + return `${type}(${field.size})`; + } + if (hasPrecision(field.type) && field.size !== "") { + return `${field.type}${field.size}`; + } + return field.type.toLowerCase(); +} + +export function getMSSQLType(field, baseType = false) { + let type = field.type; switch (field.type) { case "ENUM": return baseType @@ -126,9 +129,55 @@ export function getTypeString(field, dbms = "mysql", baseType = false) { } return type; +} + +export function getSQLiteType(field) { + switch (field.type) { + case "INT": + case "SMALLINT": + case "BIGINT": + case "BOOLEAN": + return "INTEGER"; + case "DECIMAL": + case "NUMERIC": + case "FLOAT": + case "DOUBLE": + case "REAL": + return "REAL"; + case "CHAR": + case "VARCHAR": + case "UUID": + case "TEXT": + case "DATE": + case "TIME": + case "TIMESTAMP": + case "DATETIME": + case "BINARY": + case "VARBINARY": + return "TEXT"; + case "ENUM": + return `TEXT CHECK("${field.name}" in (${field.values + .map((v) => `'${v}'`) + .join(", ")}))`; + default: + return "BLOB"; } } +export function getTypeString(field, dbms = "mysql", baseType = false) { + if (dbms === "mysql" || dbms === "mariadb") { + return getMysqlType(field); + } else if (dbms === "postgressql") { + return getPostgreSQLType(field); + } else if (dbms === "mssql") { + return getMSSQLType(field, baseType) + } else if (dbms === "sqlite") { + return getSQLiteType(field); + } + + return field.type; +} + export function hasQuotes(type) { return [ "CHAR", @@ -233,14 +282,14 @@ export function jsonToPostgreSQL(obj) { `${ type.comment === "" ? "" : `/**\n${type.comment}\n*/\n` }CREATE TYPE ${type.name} AS (\n${type.fields - .map((f) => `\t${f.name} ${getTypeString(f, "postgres")}`) + .map((f) => `\t${f.name} ${getTypeString(f, "postgressql")}`) .join("\n")}\n);` ); } else { return `${ type.comment === "" ? "" : `/**\n${type.comment}\n*/\n` }CREATE TYPE ${type.name} AS (\n${type.fields - .map((f) => `\t${f.name} ${getTypeString(f, "postgres")}`) + .map((f) => `\t${f.name} ${getTypeString(f, "postgressql")}`) .join("\n")}\n);`; } })}\n${obj.tables @@ -263,7 +312,7 @@ export function jsonToPostgreSQL(obj) { (field) => `${field.comment === "" ? "" : `\t-- ${field.comment}\n`}\t"${ field.name - }" ${getTypeString(field, "postgres")}${ + }" ${getTypeString(field, "postgressql")}${ field.notNull ? " NOT NULL" : "" }${ field.default !== "" ? ` DEFAULT ${parseDefault(field)}` : "" @@ -305,39 +354,6 @@ export function jsonToPostgreSQL(obj) { .join("\n")}`; } -export function getSQLiteType(field) { - switch (field.type) { - case "INT": - case "SMALLINT": - case "BIGINT": - case "BOOLEAN": - return "INTEGER"; - case "DECIMAL": - case "NUMERIC": - case "FLOAT": - case "DOUBLE": - case "REAL": - return "REAL"; - case "CHAR": - case "VARCHAR": - case "UUID": - case "TEXT": - case "DATE": - case "TIME": - case "TIMESTAMP": - case "DATETIME": - case "BINARY": - case "VARBINARY": - return "TEXT"; - case "ENUM": - return `TEXT CHECK("${field.name}" in (${field.values - .map((v) => `'${v}'`) - .join(", ")}))`; - default: - return "BLOB"; - } -} - export function getInlineFK(table, obj) { let fk = ""; obj.references.forEach((r) => { @@ -364,7 +380,7 @@ export function jsonToSQLite(obj) { (field) => `${field.comment === "" ? "" : `\t-- ${field.comment}\n`}\t"${ field.name - }" ${getSQLiteType(field)}${field.notNull ? " NOT NULL" : ""}${ + }" ${getTypeString(field, "sqlite")}${field.notNull ? " NOT NULL" : ""}${ field.unique ? " UNIQUE" : "" }${field.default !== "" ? ` DEFAULT ${parseDefault(field)}` : ""}${ field.check === "" || !hasCheck(field.type) @@ -458,7 +474,7 @@ export function jsonToMariaDB(obj) { .join("\n")}`; } -export function jsonToSQLServer(obj) { +export function jsonToMSSQL(obj) { return `${obj.types .map((type) => { return `${ @@ -560,3 +576,11 @@ export function getSize(type) { return ""; } } + +export default { + jsonToMySQL, + jsonToMariaDB, + jsonToPostgreSQL, + jsonToSQLite, + jsonToMSSQL, +}