From 58bafcfcaf31b91d9f1afaa5df41cbbc6f387260 Mon Sep 17 00:00:00 2001 From: 1ilit Date: Thu, 18 Jan 2024 04:24:38 +0200 Subject: [PATCH 1/4] Add hero --- src/components/SimpleCanvas.jsx | 29 ++++++----- src/data/{loginDiagram.js => heroDiagram.js} | 2 +- src/pages/LandingPage.jsx | 51 +++++++++++++++----- 3 files changed, 56 insertions(+), 26 deletions(-) rename src/data/{loginDiagram.js => heroDiagram.js} (98%) diff --git a/src/components/SimpleCanvas.jsx b/src/components/SimpleCanvas.jsx index d1965fb..ad6587e 100644 --- a/src/components/SimpleCanvas.jsx +++ b/src/components/SimpleCanvas.jsx @@ -19,9 +19,8 @@ function Table({ table, grab }) { onMouseLeave={() => setIsHovered(false)} >
(
setHoveredField(i)} onMouseLeave={() => setHoveredField(-1)} > @@ -139,7 +137,7 @@ function Relationship({ relationship }) { ); } -export default function Canvas({ diagram }) { +export default function SimpleCanvas({ diagram, zoom }) { const [tables, setTables] = useState(diagram.tables); const [relationships, setRelationships] = useState(diagram.relationships); const [dragging, setDragging] = useState(-1); @@ -233,12 +231,17 @@ export default function Canvas({ diagram }) { height="100%" fill="url(#pattern-circles)" > - {tables.map((t, i) => ( - grabTable(e, i)} /> - ))} - {relationships.map((r, i) => ( - - ))} + + {tables.map((t, i) => ( +
grabTable(e, i)} /> + ))} + {relationships.map((r, i) => ( + + ))} + ); } diff --git a/src/data/loginDiagram.js b/src/data/heroDiagram.js similarity index 98% rename from src/data/loginDiagram.js rename to src/data/heroDiagram.js index 9c02131..8d2268a 100644 --- a/src/data/loginDiagram.js +++ b/src/data/heroDiagram.js @@ -1,4 +1,4 @@ -const xOffset = window.innerWidth * 0.57 * 0.09; +const xOffset = window.innerWidth * 0.65; export const diagram = { tables: [ { diff --git a/src/pages/LandingPage.jsx b/src/pages/LandingPage.jsx index 6dcb0eb..0586f73 100644 --- a/src/pages/LandingPage.jsx +++ b/src/pages/LandingPage.jsx @@ -1,8 +1,9 @@ import { useState, useEffect } from "react"; import { Link } from "react-router-dom"; import { IconCrossStroked } from "@douyinfe/semi-icons"; +import SimpleCanvas from "../components/SimpleCanvas" import Navbar from "../components/Navbar"; - +import { diagram } from "../data/heroDiagram" export default function LandingPage() { const [showSurvey, setShowSurvey] = useState(true); @@ -14,19 +15,45 @@ export default function LandingPage() { return (
- {showSurvey && ( -
- - Help us improve! Share your feedback. - -
- +
+ {showSurvey && ( +
+ + Help us improve! Share your feedback. + +
+ +
+
+ )} + +
+
+ +
+
+
+

+ Draw, Copy, and Paste +

+
+
+ Free, simple, and intuitive database design tool and SQL generator. +
+
+ + + Try it for yourself + +
- )} - +
+
hi
); } From df9a3f8089e94217168046e72207a18b5c734884 Mon Sep 17 00:00:00 2001 From: 1ilit Date: Thu, 18 Jan 2024 09:21:30 +0200 Subject: [PATCH 2/4] Add framer motion --- package-lock.json | 39 +++++++++++++++++++++++++++++++++++++++ package.json | 1 + src/animations/Reveal.jsx | 30 ++++++++++++++++++++++++++++++ src/pages/LandingPage.jsx | 30 +++++++++++++++++++----------- 4 files changed, 89 insertions(+), 11 deletions(-) create mode 100644 src/animations/Reveal.jsx diff --git a/package-lock.json b/package-lock.json index ec9886b..0449a40 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,6 +15,7 @@ "dexie": "^3.2.4", "dexie-react-hooks": "^1.1.7", "file-saver": "^2.0.5", + "framer-motion": "^10.18.0", "html-to-image": "^1.11.11", "jsonschema": "^1.4.1", "jspdf": "^2.5.1", @@ -563,6 +564,21 @@ "react-dom": ">=16.0.0" } }, + "node_modules/@emotion/is-prop-valid": { + "version": "0.8.8", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz", + "integrity": "sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==", + "optional": true, + "dependencies": { + "@emotion/memoize": "0.7.4" + } + }, + "node_modules/@emotion/memoize": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.4.tgz", + "integrity": "sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==", + "optional": true + }, "node_modules/@esbuild/android-arm": { "version": "0.19.9", "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.9.tgz", @@ -3103,6 +3119,29 @@ "url": "https://github.com/sponsors/rawify" } }, + "node_modules/framer-motion": { + "version": "10.18.0", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-10.18.0.tgz", + "integrity": "sha512-oGlDh1Q1XqYPksuTD/usb0I70hq95OUzmL9+6Zd+Hs4XV0oaISBa/UUMSjYiq6m8EUF32132mOJ8xVZS+I0S6w==", + "dependencies": { + "tslib": "^2.4.0" + }, + "optionalDependencies": { + "@emotion/is-prop-valid": "^0.8.2" + }, + "peerDependencies": { + "react": "^18.0.0", + "react-dom": "^18.0.0" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", diff --git a/package.json b/package.json index 4c3508e..92bc695 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ "dexie": "^3.2.4", "dexie-react-hooks": "^1.1.7", "file-saver": "^2.0.5", + "framer-motion": "^10.18.0", "html-to-image": "^1.11.11", "jsonschema": "^1.4.1", "jspdf": "^2.5.1", diff --git a/src/animations/Reveal.jsx b/src/animations/Reveal.jsx new file mode 100644 index 0000000..5ef2a40 --- /dev/null +++ b/src/animations/Reveal.jsx @@ -0,0 +1,30 @@ +import { useRef, useEffect } from "react"; +import { motion, useInView, useAnimation } from "framer-motion"; + +export default function Reveal({ children }) { + const ref = useRef(null); + const isInView = useInView(ref, { once: true }); + const mainControls = useAnimation(); + + useEffect(() => { + if (isInView) { + mainControls.start("visible"); + } + }, [isInView, mainControls]); + + return ( +
+ + {children} + +
+ ); +} \ No newline at end of file diff --git a/src/pages/LandingPage.jsx b/src/pages/LandingPage.jsx index 0586f73..fa53b03 100644 --- a/src/pages/LandingPage.jsx +++ b/src/pages/LandingPage.jsx @@ -4,6 +4,8 @@ import { IconCrossStroked } from "@douyinfe/semi-icons"; import SimpleCanvas from "../components/SimpleCanvas" import Navbar from "../components/Navbar"; import { diagram } from "../data/heroDiagram" +import Reveal from "../animations/Reveal"; + export default function LandingPage() { const [showSurvey, setShowSurvey] = useState(true); @@ -34,26 +36,32 @@ export default function LandingPage() {
-
-

- Draw, Copy, and Paste -

-
-
- Free, simple, and intuitive database design tool and SQL generator. -
+ +
+

+ Draw, Copy, and Paste +

+
+
+ Free, simple, and intuitive database design tool and SQL generator. +
+
- - + Try it for yourself
-
hi
+
+ more stuff +
); } From 6c65134c933ead5e9d4aeb9728ac49734ed557e1 Mon Sep 17 00:00:00 2001 From: 1ilit Date: Thu, 18 Jan 2024 12:45:58 +0200 Subject: [PATCH 3/4] Fix precison for float bug --- package-lock.json | 20 ++ package.json | 1 + src/components/ControlPanel.jsx | 84 ++++++++- src/utils/index.js | 314 +++++++++++++------------------- 4 files changed, 235 insertions(+), 184 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0449a40..66c6f1b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,6 +20,7 @@ "jsonschema": "^1.4.1", "jspdf": "^2.5.1", "lexical": "^0.12.5", + "node-sql-parser": "^4.17.0", "react": "^18.2.0", "react-cookie": "^7.0.1", "react-dom": "^18.2.0", @@ -1975,6 +1976,14 @@ "resolved": "https://registry.npmjs.org/bezier-easing/-/bezier-easing-2.1.0.tgz", "integrity": "sha512-gbIqZ/eslnUFC1tjEvtz0sgx+xTK20wDnYMIA27VA04R7w6xxXQPZDbibjA9DTWZRA2CXtwHykkVzlCaAJAZig==" }, + "node_modules/big-integer": { + "version": "1.6.52", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.52.tgz", + "integrity": "sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==", + "engines": { + "node": ">=0.6" + } + }, "node_modules/binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", @@ -4122,6 +4131,17 @@ "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", "dev": true }, + "node_modules/node-sql-parser": { + "version": "4.17.0", + "resolved": "https://registry.npmjs.org/node-sql-parser/-/node-sql-parser-4.17.0.tgz", + "integrity": "sha512-3IhovpmUBpcETnoKK/KBdkz2mz53kVG5E1dnqz1QuYvtzdxYZW5xaGGEvW9u6Yyy2ivwR3eUZrn9inmEVef02w==", + "dependencies": { + "big-integer": "^1.6.48" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", diff --git a/package.json b/package.json index 92bc695..1edc03a 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "jsonschema": "^1.4.1", "jspdf": "^2.5.1", "lexical": "^0.12.5", + "node-sql-parser": "^4.17.0", "react": "^18.2.0", "react-cookie": "^7.0.1", "react-dom": "^18.2.0", diff --git a/src/components/ControlPanel.jsx b/src/components/ControlPanel.jsx index 86d647a..b203faf 100644 --- a/src/components/ControlPanel.jsx +++ b/src/components/ControlPanel.jsx @@ -64,6 +64,7 @@ import { areaSchema, noteSchema, tableSchema } from "../data/schemas"; import { Editor } from "@monaco-editor/react"; import { db } from "../data/db"; import { useLiveQuery } from "dexie-react-hooks"; +import { Parser } from "node-sql-parser"; import Todo from "./Todo"; export default function ControlPanel({ @@ -84,6 +85,7 @@ export default function ControlPanel({ OPEN: 5, SAVEAS: 6, NEW: 7, + IMPORT_SRC: 8, }; const STATUS = { NONE: 0, @@ -823,10 +825,13 @@ export default function ControlPanel({ .catch(() => Toast.error("Oops! Something went wrong.")); }, }, - Import: { + "Import diagram": { function: fileImport, shortcut: "Ctrl+I", }, + "Import from source": { + function: () => setVisible(MODAL.IMPORT_SRC) + }, "Export as": { children: [ { @@ -1172,6 +1177,7 @@ export default function ControlPanel({ const getModalTitle = () => { switch (visible) { case MODAL.IMPORT: + case MODAL.IMPORT_SRC: return "Import diagram"; case MODAL.CODE: return "Export source"; @@ -1193,6 +1199,7 @@ export default function ControlPanel({ const getOkText = () => { switch (visible) { case MODAL.IMPORT: + case MODAL.IMPORT_SRC: return "Import"; case MODAL.CODE: case MODAL.IMG: @@ -1235,6 +1242,9 @@ export default function ControlPanel({ setRedoStack([]); } return; + case MODAL.IMPORT_SRC: + setVisible(MODAL.NONE) + return; case MODAL.OPEN: if (selectedDiagramId === 0) return; loadDiagram(selectedDiagramId); @@ -1373,6 +1383,76 @@ export default function ControlPanel({ ); }; + const importSrcModalBody = () => { + return ( + <> + { + const f = fileList[0].fileInstance; + if (!f) { + return; + } + const reader = new FileReader(); + reader.onload = async (e) => { + console.log(e.target.result); + const parser = new Parser(); + const ast = parser.astify(e.target.result); + console.log(ast); + }; + reader.readAsText(f); + + return { + autoRemove: false, + fileInstance: file.fileInstance, + status: "success", + shouldUpload: false, + }; + }} + draggable={true} + dragMainText="Drag and drop the file here or click to upload." + dragSubText="Upload an sql file to autogenerate your tables and columns." + accept=".sql" + onRemove={() => + setError({ + type: STATUS.NONE, + message: "", + }) + } + onFileChange={() => + setError({ + type: STATUS.NONE, + message: "", + }) + } + limit={1} + > + + {error.type === STATUS.ERROR ? ( + {error.message}} + /> + ) : error.type === STATUS.OK ? ( + {error.message}} + /> + ) : ( + error.type === STATUS.WARNING && ( + {error.message}} + /> + ) + )} + + ); + }; + const newModalBody = () => (
@@ -1408,6 +1488,8 @@ export default function ControlPanel({ switch (visible) { case MODAL.IMPORT: return importModalBody(); + case MODAL.IMPORT_SRC: + return importSrcModalBody(); case MODAL.NEW: return newModalBody(); case MODAL.RENAME: diff --git a/src/utils/index.js b/src/utils/index.js index 8a152ce..243cfb6 100644 --- a/src/utils/index.js +++ b/src/utils/index.js @@ -95,8 +95,8 @@ function getTypeString(field, dbms = "mysql") { if (isSized(field.type)) { return `${field.type}(${field.size})`; } - if (hasPrecision(field.type) && field.size !== "") { - return `${field.type}${field.size}`; + if (hasPrecision(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(", ")})`; @@ -132,8 +132,8 @@ function getTypeString(field, dbms = "mysql") { field.type === "BINARY" ? "bit" : field.type === "VARBINARY" - ? "bit varying" - : field.type.toLowerCase(); + ? "bit varying" + : field.type.toLowerCase(); return `${type}(${field.size})`; } if (hasPrecision(field.type) && field.size !== "") { @@ -161,68 +161,56 @@ function jsonToMySQL(obj) { return `${obj.tables .map( (table) => - `${ - table.comment === "" ? "" : `/* ${table.comment} */\n` + `${table.comment === "" ? "" : `/* ${table.comment} */\n` }CREATE TABLE \`${table.name}\` (\n${table.fields .map( (field) => - `${field.comment === "" ? "" : `\t-- ${field.comment}\n`}\t\`${ - field.name - }\` ${getTypeString(field)}${field.notNull ? " NOT NULL" : ""}${ - field.increment ? " AUTO_INCREMENT" : "" - }${field.unique ? " UNIQUE" : ""}${ - field.default !== "" - ? ` DEFAULT ${ - hasQuotes(field.type) && - field.default.toLowerCase() !== "null" - ? `"${field.default}"` - : `${field.default}` - }` + `${field.comment === "" ? "" : `\t-- ${field.comment}\n`}\t\`${field.name + }\` ${getTypeString(field)}${field.notNull ? " NOT NULL" : ""}${field.increment ? " AUTO_INCREMENT" : "" + }${field.unique ? " UNIQUE" : ""}${field.default !== "" + ? ` DEFAULT ${hasQuotes(field.type) && + field.default.toLowerCase() !== "null" + ? `"${field.default}"` + : `${field.default}` + }` + : "" + }${field.check === "" || !hasCheck(field.type) + ? !sqlDataTypes.includes(field.type) + ? ` CHECK(\n\t\tJSON_SCHEMA_VALID("${generateSchema( + obj.types.find( + (t) => t.name === field.type.toLowerCase() + ) + )}", \`${field.name}\`))` : "" - }${ - field.check === "" || !hasCheck(field.type) - ? !sqlDataTypes.includes(field.type) - ? ` CHECK(\n\t\tJSON_SCHEMA_VALID("${generateSchema( - obj.types.find( - (t) => t.name === field.type.toLowerCase() - ) - )}", \`${field.name}\`))` - : "" - : ` CHECK(${field.check})` + : ` CHECK(${field.check})` }` ) - .join(",\n")}${ - table.fields.filter((f) => f.primary).length > 0 + .join(",\n")}${table.fields.filter((f) => f.primary).length > 0 ? `,\n\tPRIMARY KEY(${table.fields - .filter((f) => f.primary) - .map((f) => `\`${f.name}\``) - .join(", ")})` - : "" - }\n);\n${ - table.indices.length > 0 - ? `\n${table.indices.map( - (i) => - `\nCREATE ${i.unique ? "UNIQUE " : ""}INDEX \`${ - i.name - }\`\nON \`${table.name}\` (${i.fields - .map((f) => `\`${f}\``) - .join(", ")});` - )}` + .filter((f) => f.primary) + .map((f) => `\`${f.name}\``) + .join(", ")})` : "" + }\n);\n${table.indices.length > 0 + ? `\n${table.indices.map( + (i) => + `\nCREATE ${i.unique ? "UNIQUE " : ""}INDEX \`${i.name + }\`\nON \`${table.name}\` (${i.fields + .map((f) => `\`${f}\``) + .join(", ")});` + )}` + : "" }` ) .join("\n")}\n${obj.references - .map( - (r) => - `ALTER TABLE \`${ - obj.tables[r.startTableId].name - }\`\nADD FOREIGN KEY(\`${ - obj.tables[r.startTableId].fields[r.startFieldId].name - }\`) REFERENCES \`${obj.tables[r.endTableId].name}\`(\`${ - obj.tables[r.endTableId].fields[r.endFieldId].name - }\`)\nON UPDATE ${r.updateConstraint.toUpperCase()} ON DELETE ${r.deleteConstraint.toUpperCase()};` - ) - .join("\n")}`; + .map( + (r) => + `ALTER TABLE \`${obj.tables[r.startTableId].name + }\`\nADD FOREIGN KEY(\`${obj.tables[r.startTableId].fields[r.startFieldId].name + }\`) REFERENCES \`${obj.tables[r.endTableId].name}\`(\`${obj.tables[r.endTableId].fields[r.endFieldId].name + }\`)\nON UPDATE ${r.updateConstraint.toUpperCase()} ON DELETE ${r.deleteConstraint.toUpperCase()};` + ) + .join("\n")}`; } function jsonToPostgreSQL(obj) { @@ -238,86 +226,73 @@ function jsonToPostgreSQL(obj) { if (typeStatements.length > 0) { return ( typeStatements.join("") + - `${ - type.comment === "" ? "" : `/**\n${type.comment}\n*/\n` + `${type.comment === "" ? "" : `/**\n${type.comment}\n*/\n` }CREATE TYPE ${type.name} AS (\n${type.fields .map((f) => `\t${f.name} ${getTypeString(f, "postgres")}`) .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")}`) - .join("\n")}\n);`; + return `${type.comment === "" ? "" : `/**\n${type.comment}\n*/\n` + }CREATE TYPE ${type.name} AS (\n${type.fields + .map((f) => `\t${f.name} ${getTypeString(f, "postgres")}`) + .join("\n")}\n);`; } })}\n${obj.tables .map( (table) => - `${table.comment === "" ? "" : `/**\n${table.comment}\n*/\n`}${ - table.fields.filter((f) => f.type === "ENUM" || f.type === "SET") - .length > 0 - ? `${table.fields - .filter((f) => f.type === "ENUM" || f.type === "SET") - .map( - (f) => - `CREATE TYPE "${f.name}_t" AS ENUM (${f.values - .map((v) => `'${v}'`) - .join(", ")});\n\n` - )}` - : "" + `${table.comment === "" ? "" : `/**\n${table.comment}\n*/\n`}${table.fields.filter((f) => f.type === "ENUM" || f.type === "SET") + .length > 0 + ? `${table.fields + .filter((f) => f.type === "ENUM" || f.type === "SET") + .map( + (f) => + `CREATE TYPE "${f.name}_t" AS ENUM (${f.values + .map((v) => `'${v}'`) + .join(", ")});\n\n` + )}` + : "" }CREATE TABLE "${table.name}" (\n${table.fields .map( (field) => - `${field.comment === "" ? "" : `\t-- ${field.comment}\n`}\t"${ - field.name - }" ${getTypeString(field, "postgres")}${ - field.notNull ? " NOT NULL" : "" - }${ - field.default !== "" - ? ` DEFAULT ${ - hasQuotes(field.type) && - field.default.toLowerCase() !== "null" - ? `'${field.default}'` - : `${field.default}` - }` - : "" - }${ - field.check === "" || !hasCheck(field.type) - ? "" - : ` CHECK(${field.check})` + `${field.comment === "" ? "" : `\t-- ${field.comment}\n`}\t"${field.name + }" ${getTypeString(field, "postgres")}${field.notNull ? " NOT NULL" : "" + }${field.default !== "" + ? ` DEFAULT ${hasQuotes(field.type) && + field.default.toLowerCase() !== "null" + ? `'${field.default}'` + : `${field.default}` + }` + : "" + }${field.check === "" || !hasCheck(field.type) + ? "" + : ` CHECK(${field.check})` }` ) - .join(",\n")}${ - table.fields.filter((f) => f.primary).length > 0 + .join(",\n")}${table.fields.filter((f) => f.primary).length > 0 ? `,\n\tPRIMARY KEY(${table.fields - .filter((f) => f.primary) - .map((f) => `"${f.name}"`) - .join(", ")})` - : "" - }\n);\n${ - table.indices.length > 0 - ? `${table.indices.map( - (i) => - `\nCREATE ${i.unique ? "UNIQUE " : ""}INDEX "${ - i.name - }"\nON "${table.name}" (${i.fields - .map((f) => `"${f}"`) - .join(", ")});` - )}` + .filter((f) => f.primary) + .map((f) => `"${f.name}"`) + .join(", ")})` : "" + }\n);\n${table.indices.length > 0 + ? `${table.indices.map( + (i) => + `\nCREATE ${i.unique ? "UNIQUE " : ""}INDEX "${i.name + }"\nON "${table.name}" (${i.fields + .map((f) => `"${f}"`) + .join(", ")});` + )}` + : "" }` ) .join("\n")}\n${obj.references - .map( - (r) => - `ALTER TABLE "${obj.tables[r.startTableId].name}"\nADD FOREIGN KEY("${ - obj.tables[r.startTableId].fields[r.startFieldId].name - }") REFERENCES "${obj.tables[r.endTableId].name}"("${ - obj.tables[r.endTableId].fields[r.endFieldId].name - }")\nON UPDATE ${r.updateConstraint.toUpperCase()} ON DELETE ${r.deleteConstraint.toUpperCase()};` - ) - .join("\n")}`; + .map( + (r) => + `ALTER TABLE "${obj.tables[r.startTableId].name}"\nADD FOREIGN KEY("${obj.tables[r.startTableId].fields[r.startFieldId].name + }") REFERENCES "${obj.tables[r.endTableId].name}"("${obj.tables[r.endTableId].fields[r.endFieldId].name + }")\nON UPDATE ${r.updateConstraint.toUpperCase()} ON DELETE ${r.deleteConstraint.toUpperCase()};` + ) + .join("\n")}`; } function arrayIsEqual(arr1, arr2) { @@ -325,13 +300,13 @@ function arrayIsEqual(arr1, arr2) { } function isSized(type) { - return ["CHAR", "VARCHAR", "BINARY", "VARBINARY", "TEXT", "FLOAT"].includes( + return ["CHAR", "VARCHAR", "BINARY", "VARBINARY", "TEXT"].includes( type ); } function hasPrecision(type) { - return ["DOUBLE", "NUMERIC", "DECIMAL"].includes(type); + return ["DOUBLE", "NUMERIC", "DECIMAL", "FLOAT"].includes(type); } function hasCheck(type) { @@ -569,11 +544,9 @@ function validateDiagram(diagram) { diagram.tables[r.startTableId].fields[r.startFieldId].type !== diagram.tables[r.endTableId].fields[r.endFieldId].type ) { - issues.push(`Referencing column "${ - diagram.tables[r.endTableId].fields[r.endFieldId].name - }" and referenced column "${ - diagram.tables[r.startTableId].fields[r.startFieldId].name - }" are incompatible. + issues.push(`Referencing column "${diagram.tables[r.endTableId].fields[r.endFieldId].name + }" and referenced column "${diagram.tables[r.startTableId].fields[r.startFieldId].name + }" are incompatible. `); } }); @@ -632,81 +605,56 @@ const calcPath = (x1, x2, y1, y2, startFieldId, endFieldId, zoom = 1) => { if (y1 <= y2) { if (x1 + tableWidth <= x2) { - return `M ${x1 + tableWidth - offsetX * 2} ${y1} L ${ - midX - r - } ${y1} A ${r} ${r} 0 0 1 ${midX} ${y1 + r} L ${midX} ${ - y2 - r - } A ${r} ${r} 0 0 0 ${midX + r} ${y2} L ${endX} ${y2}`; + return `M ${x1 + tableWidth - offsetX * 2} ${y1} L ${midX - r + } ${y1} A ${r} ${r} 0 0 1 ${midX} ${y1 + r} L ${midX} ${y2 - r + } A ${r} ${r} 0 0 0 ${midX + r} ${y2} L ${endX} ${y2}`; } else if (x2 <= x1 + tableWidth && x1 <= x2) { - return `M ${x1 + tableWidth - 2 * offsetX} ${y1} L ${ - x2 + tableWidth - } ${y1} A ${r} ${r} 0 0 1 ${x2 + tableWidth + r} ${y1 + r} L ${ - x2 + tableWidth + r - } ${y2 - r} A ${r} ${r} 0 0 1 ${x2 + tableWidth} ${y2} L ${ - x2 + tableWidth - 2 * offsetX - } ${y2}`; + return `M ${x1 + tableWidth - 2 * offsetX} ${y1} L ${x2 + tableWidth + } ${y1} A ${r} ${r} 0 0 1 ${x2 + tableWidth + r} ${y1 + r} L ${x2 + tableWidth + r + } ${y2 - r} A ${r} ${r} 0 0 1 ${x2 + tableWidth} ${y2} L ${x2 + tableWidth - 2 * offsetX + } ${y2}`; } else if (x2 + tableWidth >= x1 && x2 + tableWidth <= x1 + tableWidth) { - return `M ${x1} ${y1} L ${x2 - r} ${y1} A ${r} ${r} 0 0 0 ${x2 - r - r} ${ - y1 + r - } L ${x2 - r - r} ${y2 - r} A ${r} ${r} 0 0 0 ${ - x2 - r - } ${y2} L ${x2} ${y2}`; + return `M ${x1} ${y1} L ${x2 - r} ${y1} A ${r} ${r} 0 0 0 ${x2 - r - r} ${y1 + r + } L ${x2 - r - r} ${y2 - r} A ${r} ${r} 0 0 0 ${x2 - r + } ${y2} L ${x2} ${y2}`; } else { - return `M ${x1} ${y1} L ${midX + r} ${y1} A ${r} ${r} 0 0 0 ${midX} ${ - y1 + r - } L ${midX} ${y2 - r} A ${r} ${r} 0 0 1 ${ - midX - r - } ${y2} L ${endX} ${y2}`; + return `M ${x1} ${y1} L ${midX + r} ${y1} A ${r} ${r} 0 0 0 ${midX} ${y1 + r + } L ${midX} ${y2 - r} A ${r} ${r} 0 0 1 ${midX - r + } ${y2} L ${endX} ${y2}`; } } else { if (x1 + tableWidth <= x2) { - return `M ${x1 + tableWidth - offsetX * 2} ${y1} L ${ - midX - r - } ${y1} A ${r} ${r} 0 0 0 ${midX} ${y1 - r} L ${midX} ${ - y2 + r - } A ${r} ${r} 0 0 1 ${midX + r} ${y2} L ${endX} ${y2}`; + return `M ${x1 + tableWidth - offsetX * 2} ${y1} L ${midX - r + } ${y1} A ${r} ${r} 0 0 0 ${midX} ${y1 - r} L ${midX} ${y2 + r + } A ${r} ${r} 0 0 1 ${midX + r} ${y2} L ${endX} ${y2}`; } else if (x1 + tableWidth >= x2 && x1 + tableWidth <= x2 + tableWidth) { // this for the overlap remember if (startTableY < y2) { - return `M ${x1} ${y1} L ${x1 - r - r} ${y1} A ${r} ${r} 0 0 1 ${ - x1 - r - r - r - } ${y1 - r} L ${x1 - r - r - r} ${y2 + r} A ${r} ${r} 0 0 1 ${ - x1 - r - r - } ${y2} L ${x1 - r - 4} ${y2}`; + return `M ${x1} ${y1} L ${x1 - r - r} ${y1} A ${r} ${r} 0 0 1 ${x1 - r - r - r + } ${y1 - r} L ${x1 - r - r - r} ${y2 + r} A ${r} ${r} 0 0 1 ${x1 - r - r + } ${y2} L ${x1 - r - 4} ${y2}`; } - return `M ${x1} ${y1} L ${x1 - r - r} ${y1} A ${r} ${r} 0 0 1 ${ - x1 - r - r - r - } ${y1 - r} L ${x1 - r - r - r} ${y2 + r} A ${r} ${r} 0 0 1 ${ - x1 - r - r - } ${y2} L ${endX} ${y2}`; + return `M ${x1} ${y1} L ${x1 - r - r} ${y1} A ${r} ${r} 0 0 1 ${x1 - r - r - r + } ${y1 - r} L ${x1 - r - r - r} ${y2 + r} A ${r} ${r} 0 0 1 ${x1 - r - r + } ${y2} L ${endX} ${y2}`; } else if (x1 >= x2 && x1 <= x2 + tableWidth) { // this for the overlap remember if (startTableY < y2) { - return `M ${x1 + tableWidth - 2 * offsetX} ${y1} L ${ - x1 + tableWidth - 2 * offsetX + r - } ${y1} A ${r} ${r} 0 0 0 ${x1 + tableWidth - 2 * offsetX + r + r} ${ - y1 - r - } L ${x1 + tableWidth - 2 * offsetX + r + r} ${ - y2 + r - } A ${r} ${r} 0 0 0 ${x1 + tableWidth - 2 * offsetX + r} ${y2} L ${ - x1 + tableWidth - 16 - } ${y2}`; + return `M ${x1 + tableWidth - 2 * offsetX} ${y1} L ${x1 + tableWidth - 2 * offsetX + r + } ${y1} A ${r} ${r} 0 0 0 ${x1 + tableWidth - 2 * offsetX + r + r} ${y1 - r + } L ${x1 + tableWidth - 2 * offsetX + r + r} ${y2 + r + } A ${r} ${r} 0 0 0 ${x1 + tableWidth - 2 * offsetX + r} ${y2} L ${x1 + tableWidth - 16 + } ${y2}`; } - return `M ${x1 + tableWidth - 2 * offsetX} ${y1} L ${ - x1 + tableWidth - 2 * offsetX + r - } ${y1} A ${r} ${r} 0 0 0 ${x1 + tableWidth - 2 * offsetX + r + r} ${ - y1 - r - } L ${x1 + tableWidth - 2 * offsetX + r + r} ${ - y2 + r - } A ${r} ${r} 0 0 0 ${x1 + tableWidth - 2 * offsetX + r} ${y2} L ${ - x2 + tableWidth - 2 * offsetX - } ${y2}`; + return `M ${x1 + tableWidth - 2 * offsetX} ${y1} L ${x1 + tableWidth - 2 * offsetX + r + } ${y1} A ${r} ${r} 0 0 0 ${x1 + tableWidth - 2 * offsetX + r + r} ${y1 - r + } L ${x1 + tableWidth - 2 * offsetX + r + r} ${y2 + r + } A ${r} ${r} 0 0 0 ${x1 + tableWidth - 2 * offsetX + r} ${y2} L ${x2 + tableWidth - 2 * offsetX + } ${y2}`; } else { - return `M ${x1} ${y1} L ${midX + r} ${y1} A ${r} ${r} 0 0 1 ${midX} ${ - y1 - r - } L ${midX} ${y2 + r} A ${r} ${r} 0 0 0 ${ - midX - r - } ${y2} L ${endX} ${y2}`; + return `M ${x1} ${y1} L ${midX + r} ${y1} A ${r} ${r} 0 0 1 ${midX} ${y1 - r + } L ${midX} ${y2 + r} A ${r} ${r} 0 0 0 ${midX - r + } ${y2} L ${endX} ${y2}`; } } }; From b0d262e2a0272496f72cc03b8bcb6f6664397881 Mon Sep 17 00:00:00 2001 From: 1ilit Date: Sun, 21 Jan 2024 09:05:43 +0200 Subject: [PATCH 4/4] Handle simple sql table parsing --- src/components/ControlPanel.jsx | 222 +++++++++++++++++++++++++++----- 1 file changed, 191 insertions(+), 31 deletions(-) diff --git a/src/components/ControlPanel.jsx b/src/components/ControlPanel.jsx index b203faf..e67c57f 100644 --- a/src/components/ControlPanel.jsx +++ b/src/components/ControlPanel.jsx @@ -29,6 +29,8 @@ import { Toast, SideSheet, List, + Select, + Checkbox, } from "@douyinfe/semi-ui"; import timeLine from "../assets/process.png"; import timeLineDark from "../assets/process_dark.png"; @@ -830,7 +832,10 @@ export default function ControlPanel({ shortcut: "Ctrl+I", }, "Import from source": { - function: () => setVisible(MODAL.IMPORT_SRC) + function: () => { + setData({ src: "", overwrite: true, dbms: "MySQL" }); + setVisible(MODAL.IMPORT_SRC) + } }, "Export as": { children: [ @@ -1217,6 +1222,165 @@ export default function ControlPanel({ } }; + /** + * + * { + "id": 0, + "name": "table_4", + "x": 50, + "y": 83, + "fields": [ + { + "name": "id", + "type": "INT", + "default": "", + "check": "", + "primary": true, + "unique": true, + "notNull": true, + "increment": true, + "comment": "", + "id": 0 + }, + { + "name": "name", + "type": "NUMERIC", + "default": "", + "check": "", + "primary": false, + "unique": false, + "notNull": false, + "increment": false, + "comment": "", + "id": 1, + "size": "" + } + ], + "comment": "", + "indices": [], + "color": "#175e7a" + }, + { + "id": 1, + "name": "table_1", + "x": 360, + "y": 181, + "fields": [ + { + "name": "id", + "type": "INT", + "default": "", + "check": "", + "primary": true, + "unique": true, + "notNull": true, + "increment": true, + "comment": "", + "id": 0 + }, + { + "name": "kk", + "type": "INT", + "default": "", + "check": "", + "primary": false, + "unique": false, + "notNull": false, + "increment": false, + "comment": "", + "id": 1 + }, + { + "id": 2, + "size": "12" + } + ], + "comment": "", + "indices": [], + "color": "#175e7a" + } + */ + + const parseSQLAndLoadDiagram = () => { + const parser = new Parser(); + let ast = null; + try { + console.log(data.dbms) + ast = parser.astify(data.src, { database: data.dbms }); + } catch (err) { + Toast.error("Could not parse the sql file. Make sure there are no syntax errors."); + console.log(err); + return; + } + const tables = []; + + ast.forEach(((e) => { + console.log(JSON.stringify(e)) + if (e.type === "create" && e.keyword === "table") { + const table = {}; + table.name = e.table[0].table; + table.color = "#175e7a"; + table.fields = []; + table.indices = []; + table.x = 0; + table.y = 0; + e.create_definitions.forEach((d) => { + if (d.resource === "column") { + const field = {}; + field.name = d.column.column; + field.type = d.definition.dataType; + field.comment = ""; + field.unique = false; + if (d.unique) field.unique = true; + field.auto_increment = false; + if (d.auto_increment) field.auto_increment = true; + field.notNull = false; + if (d.nullable) field.notNull = true; + field.primary = false; + if (d.primary_key) field.primary = true; + field.default = ""; + if (d.default_val) field.default = d.default_val.value.value; + if (d.definition["length"]) field.size = d.definition["length"]; + + if (d.check) { + let check = ""; + if (d.check.definition[0].left.column) { + check = d.check.definition[0].left.column + " " + d.check.definition[0].operator + " " + d.check.definition[0].right.value; + } else { + check = d.check.definition[0].left.value + " " + d.check.definition[0].operator + " " + d.check.definition[0].right.column; + } + field.check = check; + } + + table.fields.push(field); + } else if (d.resource === "constraint") { + if (d.constraint_type === "primary key") { + d.definition.forEach(c => { + table.fields.forEach((f) => { + if (f.name === c.column && !f.primary) { + f.primary = true; + } + }) + }); + } + } + }); + + tables.push(table); + } + })) + + tables.forEach((e, i) => { + e.id = i; + e.fields.forEach((f, j) => { + f.id = j; + }) + }) + + setTables(tables) + console.log(tables); + } + const getModalOnOk = async () => { switch (visible) { case MODAL.IMG: @@ -1243,6 +1407,7 @@ export default function ControlPanel({ } return; case MODAL.IMPORT_SRC: + parseSQLAndLoadDiagram(); setVisible(MODAL.NONE) return; case MODAL.OPEN: @@ -1395,10 +1560,7 @@ export default function ControlPanel({ } const reader = new FileReader(); reader.onload = async (e) => { - console.log(e.target.result); - const parser = new Parser(); - const ast = parser.astify(e.target.result); - console.log(ast); + setData(prev => ({ ...prev, src: e.target.result })) }; reader.readAsText(f); @@ -1413,11 +1575,13 @@ export default function ControlPanel({ dragMainText="Drag and drop the file here or click to upload." dragSubText="Upload an sql file to autogenerate your tables and columns." accept=".sql" - onRemove={() => + onRemove={() => { setError({ type: STATUS.NONE, message: "", - }) + }); + setData(prev => ({ ...prev, src: "" })); + } } onFileChange={() => setError({ @@ -1427,28 +1591,23 @@ export default function ControlPanel({ } limit={1} > - - {error.type === STATUS.ERROR ? ( - {error.message}
} - /> - ) : error.type === STATUS.OK ? ( - {error.message}
} - /> - ) : ( - error.type === STATUS.WARNING && ( - {error.message}} - /> - ) - )} +
+
Select DBMS
+ + setData(prev => ({ ...prev, overwrite: e.target.checked }))} + className="my-2"> + Overwrite existing diagram + +
); }; @@ -1642,13 +1801,14 @@ export default function ControlPanel({ closeOnEsc={true} okText={getOkText()} okButtonProps={{ - disabled: + disabled: (error && error.type && error.type === STATUS.ERROR) || (visible === MODAL.IMPORT && (error.type === STATUS.ERROR || !data)) || ((visible === MODAL.IMG || visible === MODAL.CODE) && !exportData.data) || (visible === MODAL.RENAME && title === "") || - (visible === MODAL.SAVEAS && saveAsTitle === ""), + (visible === MODAL.SAVEAS && saveAsTitle === "") || + (visible === MODAL.IMPORT_SRC && data.src === ""), }} cancelText="Cancel" width={600}