Load types for selected db

This commit is contained in:
1ilit 2024-06-06 20:24:05 +03:00
parent 1283f66a86
commit be6ecb066c
15 changed files with 402 additions and 39 deletions

BIN
src/assets/mariadb-icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
src/assets/mssql-icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

BIN
src/assets/mysql-icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 53 KiB

After

Width:  |  Height:  |  Size: 207 KiB

BIN
src/assets/sqlite-icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

View File

@ -1,4 +1,4 @@
import { Action, ObjectType, sqlDataTypes } from "../../../data/constants"; import { Action, ObjectType } from "../../../data/constants";
import { Row, Col, Input, Button, Popover, Select } from "@douyinfe/semi-ui"; import { Row, Col, Input, Button, Popover, Select } from "@douyinfe/semi-ui";
import { IconMore, IconKeyStroked } from "@douyinfe/semi-icons"; import { IconMore, IconKeyStroked } from "@douyinfe/semi-icons";
import { getSize, hasCheck, hasPrecision, isSized } from "../../../utils/toSQL"; import { getSize, hasCheck, hasPrecision, isSized } from "../../../utils/toSQL";
@ -6,11 +6,12 @@ import { useTables, useTypes, useUndoRedo } from "../../../hooks";
import { useState } from "react"; import { useState } from "react";
import FieldDetails from "./FieldDetails"; import FieldDetails from "./FieldDetails";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { dbToTypes } from "../../../data/datatypes";
export default function TableField({ data, tid, index }) { export default function TableField({ data, tid, index }) {
const { updateField } = useTables(); const { updateField } = useTables();
const { types } = useTypes(); const { types } = useTypes();
const { tables } = useTables(); const { tables, database } = useTables();
const { t } = useTranslation(); const { t } = useTranslation();
const { setUndoStack, setRedoStack } = useUndoRedo(); const { setUndoStack, setRedoStack } = useUndoRedo();
const [editField, setEditField] = useState({}); const [editField, setEditField] = useState({});
@ -50,7 +51,7 @@ export default function TableField({ data, tid, index }) {
<Select <Select
className="w-full" className="w-full"
optionList={[ optionList={[
...sqlDataTypes.map((value) => ({ ...dbToTypes[database].map((value) => ({
label: value, label: value,
value: value, value: value,
})), })),

View File

@ -1,5 +1,5 @@
import { useState } from "react"; import { useState } from "react";
import { Action, ObjectType, sqlDataTypes } from "../../../data/constants"; import { Action, ObjectType } from "../../../data/constants";
import { import {
Row, Row,
Col, Col,
@ -12,11 +12,13 @@ import {
} from "@douyinfe/semi-ui"; } from "@douyinfe/semi-ui";
import { IconDeleteStroked, IconMore } from "@douyinfe/semi-icons"; import { IconDeleteStroked, IconMore } from "@douyinfe/semi-icons";
import { isSized, hasPrecision, getSize } from "../../../utils/toSQL"; import { isSized, hasPrecision, getSize } from "../../../utils/toSQL";
import { useUndoRedo, useTypes } from "../../../hooks"; import { useUndoRedo, useTypes, useTables } from "../../../hooks";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { dbToTypes } from "../../../data/datatypes";
export default function TypeField({ data, tid, fid }) { export default function TypeField({ data, tid, fid }) {
const { types, updateType } = useTypes(); const { types, updateType } = useTypes();
const { database } = useTables();
const { setUndoStack, setRedoStack } = useUndoRedo(); const { setUndoStack, setRedoStack } = useUndoRedo();
const [editField, setEditField] = useState({}); const [editField, setEditField] = useState({});
const { t } = useTranslation(); const { t } = useTranslation();
@ -62,7 +64,7 @@ export default function TypeField({ data, tid, fid }) {
<Select <Select
className="w-full" className="w-full"
optionList={[ optionList={[
...sqlDataTypes.map((value) => ({ ...dbToTypes[database].map((value) => ({
label: value, label: value,
value: value, value: value,
})), })),

View File

@ -17,6 +17,9 @@ import {
useSaveState, useSaveState,
} from "../hooks"; } from "../hooks";
import FloatingControls from "./FloatingControls"; import FloatingControls from "./FloatingControls";
import { Modal } from "@douyinfe/semi-ui";
import { useTranslation } from "react-i18next";
import { databases } from "../data/databases";
export default function WorkSpace() { export default function WorkSpace() {
const [id, setId] = useState(0); const [id, setId] = useState(0);
@ -24,6 +27,8 @@ export default function WorkSpace() {
const [resize, setResize] = useState(false); const [resize, setResize] = useState(false);
const [width, setWidth] = useState(340); const [width, setWidth] = useState(340);
const [lastSaved, setLastSaved] = useState(""); const [lastSaved, setLastSaved] = useState("");
const [showSelectDbModal, setShowSelectDbModal] = useState(false);
const [selectedDb, setSelectedDb] = useState("");
const { layout } = useLayout(); const { layout } = useLayout();
const { settings } = useSettings(); const { settings } = useSettings();
const { types, setTypes } = useTypes(); const { types, setTypes } = useTypes();
@ -32,8 +37,10 @@ export default function WorkSpace() {
const { notes, setNotes } = useNotes(); const { notes, setNotes } = useNotes();
const { saveState, setSaveState } = useSaveState(); const { saveState, setSaveState } = useSaveState();
const { transform, setTransform } = useTransform(); const { transform, setTransform } = useTransform();
const { tables, relationships, setTables, setRelationships } = useTables(); const { tables, relationships, setTables, setRelationships, setDatabase } =
useTables();
const { undoStack, redoStack, setUndoStack, setRedoStack } = useUndoRedo(); const { undoStack, redoStack, setUndoStack, setRedoStack } = useUndoRedo();
const { t } = useTranslation();
const handleResize = (e) => { const handleResize = (e) => {
if (!resize) return; if (!resize) return;
@ -130,6 +137,9 @@ export default function WorkSpace() {
.last() .last()
.then((d) => { .then((d) => {
if (d) { if (d) {
if (!d.database) {
setShowSelectDbModal(true);
}
setId(d.id); setId(d.id);
setTitle(d.name); setTitle(d.name);
setTables(d.tables); setTables(d.tables);
@ -154,6 +164,9 @@ export default function WorkSpace() {
.get(id) .get(id)
.then((diagram) => { .then((diagram) => {
if (diagram) { if (diagram) {
if (!diagram.database) {
setShowSelectDbModal(true);
}
setId(diagram.id); setId(diagram.id);
setTitle(diagram.name); setTitle(diagram.name);
setTables(diagram.tables); setTables(diagram.tables);
@ -304,6 +317,46 @@ export default function WorkSpace() {
)} )}
</div> </div>
</div> </div>
<Modal
centered
size="medium"
closable={false}
hasCancel={false}
title={t("pick_db")}
okText={t("confirm")}
onOk={() => {
setDatabase(selectedDb);
setShowSelectDbModal(false);
}}
visible={showSelectDbModal}
>
<div className="grid grid-cols-3 gap-4 place-content-center">
{databases.map((x) => (
<div
key={x.name}
onClick={() => setSelectedDb(x.label)}
className={`space-y-3 py-3 px-4 rounded-md border-2 ${
settings.mode === "dark"
? "bg-zinc-700 hover:bg-zinc-600"
: "bg-zinc-100 hover:bg-zinc-200"
} ${selectedDb === x.label ? "border-zinc-400" : "border-transparent"}`}
>
<div className="font-semibold">{x.name}</div>
{x.image && (
<img
src={x.image}
className="h-10"
style={{
filter:
"opacity(0.4) drop-shadow(0 0 0 white) drop-shadow(0 0 0 white)",
}}
/>
)}
<div className="text-xs">{x.description}</div>
</div>
))}
</div>
</Modal>
</div> </div>
); );
} }

View File

@ -10,6 +10,7 @@ export const TablesContext = createContext(null);
export default function TablesContextProvider({ children }) { export default function TablesContextProvider({ children }) {
const { t } = useTranslation(); const { t } = useTranslation();
const [database, setDatabase] = useState("");
const [tables, setTables] = useState([]); const [tables, setTables] = useState([]);
const [relationships, setRelationships] = useState([]); const [relationships, setRelationships] = useState([]);
const { transform } = useTransform(); const { transform } = useTransform();
@ -261,6 +262,8 @@ export default function TablesContextProvider({ children }) {
setRelationships, setRelationships,
addRelationship, addRelationship,
deleteRelationship, deleteRelationship,
database,
setDatabase,
}} }}
> >
{children} {children}

View File

@ -1,31 +1,5 @@
import i18n from "../i18n/i18n"; import i18n from "../i18n/i18n";
export const sqlDataTypes = [
"INT",
"SMALLINT",
"BIGINT",
"DECIMAL",
"NUMERIC",
"FLOAT",
"DOUBLE",
"REAL",
"CHAR",
"VARCHAR",
"TEXT",
"DATE",
"TIME",
"TIMESTAMP",
"DATETIME",
"BOOLEAN",
"BINARY",
"VARBINARY",
"BLOB",
"JSON",
"UUID",
"ENUM",
"SET",
];
export const tableThemes = [ export const tableThemes = [
"#f03c3c", "#f03c3c",
"#ff4f81", "#ff4f81",

40
src/data/databases.js Normal file
View File

@ -0,0 +1,40 @@
import mysqlImage from "../assets/mysql-icon.png";
import postgresImage from "../assets/postgres-icon.png";
import sqliteImage from "../assets/sqlite-icon.png";
import mariadbImage from "../assets/mariadb-icon.png";
import mssqlImage from "../assets/mssql-icon.png";
export const databases = [
{
name: "MySQL",
label: "mysql",
image: mysqlImage,
},
{
name: "PostgreSQL",
label: "postgresql",
image: postgresImage,
},
{
name: "SQLite",
label: "sqlite",
image: sqliteImage,
},
{
name: "MariaDB",
label: "mariadb",
image: mariadbImage,
},
{
name: "MSSQL",
label: "mssql",
image: mssqlImage,
},
{
name: "Generic",
label: "generic",
image: null,
description:
"Generic diagrams can be exported to any SQL flavor but support few data types.",
},
];

290
src/data/datatypes.js Normal file
View File

@ -0,0 +1,290 @@
export const defaultTypes = [
"INT",
"SMALLINT",
"BIGINT",
"DECIMAL",
"NUMERIC",
"FLOAT",
"DOUBLE",
"REAL",
"CHAR",
"VARCHAR",
"TEXT",
"DATE",
"TIME",
"TIMESTAMP",
"DATETIME",
"BOOLEAN",
"BINARY",
"VARBINARY",
"BLOB",
"JSON",
"UUID",
"ENUM",
"SET",
];
export const mysqlTypes = [
// Numeric Data Types
"TINYINT",
"SMALLINT",
"MEDIUMINT",
"INT",
"INTEGER",
"BIGINT",
"DECIMAL",
"NUMERIC",
"FLOAT",
"DOUBLE",
"BIT",
"BOOLEAN",
// Date and Time Data Types
"DATE",
"DATETIME",
"TIMESTAMP",
"TIME",
"YEAR",
// String Data Types
"CHAR",
"VARCHAR",
"BINARY",
"VARBINARY",
"TINYBLOB",
"BLOB",
"MEDIUMBLOB",
"LONGBLOB",
"TINYTEXT",
"TEXT",
"MEDIUMTEXT",
"LONGTEXT",
"ENUM",
"SET",
// Spatial Data Types
"GEOMETRY",
"POINT",
"LINESTRING",
"POLYGON",
"MULTIPOINT",
"MULTILINESTRING",
"MULTIPOLYGON",
"GEOMETRYCOLLECTION",
// JSON Data Type
"JSON",
];
export const postgresTypes = [
// Numeric Data Types
"SMALLINT",
"INTEGER",
"BIGINT",
"DECIMAL",
"NUMERIC",
"REAL",
"DOUBLE PRECISION",
"SMALLSERIAL",
"SERIAL",
"BIGSERIAL",
"MONEY",
// Character Types
"CHARACTER",
"CHAR",
"VARCHAR",
"TEXT",
// Binary Data Types
"BYTEA",
// Date and Time Types
"DATE",
"TIME",
"TIMESTAMP",
"TIMESTAMPTZ",
"INTERVAL",
// Boolean Type
"BOOLEAN",
// Enumerated Types
"ENUM",
// Geometric Types
"POINT",
"LINE",
"LSEG",
"BOX",
"PATH",
"POLYGON",
"CIRCLE",
// Network Address Types
"CIDR",
"INET",
"MACADDR",
"MACADDR8",
// Bit String Types
"BIT",
"VARBIT",
// Text Search Types
"TSVECTOR",
"TSQUERY",
// JSON Types
"JSON",
"JSONB",
// UUID Type
"UUID",
// XML Type
"XML",
// Arrays
"ARRAY",
];
export const sqliteTypes = [
// Numeric Data Types
"INTEGER",
"REAL",
// Text Data Types
"TEXT",
// Blob Data Type
"BLOB",
// Affinity Types
"NUMERIC",
// Boolean Type (Alias of INTEGER)
"BOOLEAN",
// Date and Time Types (Recommended to store as TEXT)
"DATE",
"DATETIME",
"TIME",
"TIMESTAMP",
];
export const mariadbTypes = [
// Numeric Data Types
"TINYINT",
"SMALLINT",
"MEDIUMINT",
"INT",
"INTEGER",
"BIGINT",
"DECIMAL",
"NUMERIC",
"FLOAT",
"DOUBLE",
"BIT",
"BOOLEAN",
// Date and Time Data Types
"DATE",
"DATETIME",
"TIMESTAMP",
"TIME",
"YEAR",
// String Data Types
"CHAR",
"VARCHAR",
"BINARY",
"VARBINARY",
"TINYBLOB",
"BLOB",
"MEDIUMBLOB",
"LONGBLOB",
"TINYTEXT",
"TEXT",
"MEDIUMTEXT",
"LONGTEXT",
"ENUM",
"SET",
// Spatial Data Types
"GEOMETRY",
"POINT",
"LINESTRING",
"POLYGON",
"MULTIPOINT",
"MULTILINESTRING",
"MULTIPOLYGON",
"GEOMETRYCOLLECTION",
// JSON Data Type
"JSON",
];
export const mssqlTypes = [
// Exact Numeric Data Types
"BIGINT",
"INT",
"SMALLINT",
"TINYINT",
"BIT",
"DECIMAL",
"NUMERIC",
"MONEY",
"SMALLMONEY",
// Approximate Numeric Data Types
"FLOAT",
"REAL",
// Date and Time Data Types
"DATE",
"TIME",
"DATETIME",
"DATETIME2",
"DATETIMEOFFSET",
"SMALLDATETIME",
"TIMESTAMP",
// Character Strings
"CHAR",
"VARCHAR",
"TEXT",
// Unicode Character Strings
"NCHAR",
"NVARCHAR",
"NTEXT",
// Binary Data Types
"BINARY",
"VARBINARY",
"IMAGE",
// Other Data Types
"UNIQUEIDENTIFIER",
"XML",
"CURSOR",
"TABLE",
"SQL_VARIANT",
// JSON Data Type
"JSON", // Note: JSON is not a native type in MSSQL; it uses NVARCHAR to store JSON data
];
const dbToTypesBase = {
generic: defaultTypes,
mysql: mysqlTypes,
postgresql: postgresTypes,
sqlite: sqliteTypes,
mssql: mssqlTypes,
mariadb: mariadbTypes,
};
export const dbToTypes = new Proxy(dbToTypesBase, {
get: (target, prop) => (prop in target ? target[prop] : []),
});

View File

@ -197,7 +197,7 @@ export default function LandingPage() {
/> />
<img <img
src={sql_server_icon} src={sql_server_icon}
className="opacity-70 hover:opacity-100 transition-all duration-300 h-24" className="opacity-70 hover:opacity-100 transition-all duration-300 h-16"
/> />
</div> </div>
<div className="mt-16 mb-2 text-2xl font-bold text-center"> <div className="mt-16 mb-2 text-2xl font-bold text-center">

View File

@ -1,8 +1,8 @@
import { sqlDataTypes } from "../data/constants"; import { defaultTypes } from "../data/datatypes";
import { isFunction, isKeyword, strHasQuotes } from "./utils"; import { isFunction, isKeyword, strHasQuotes } from "./utils";
export function getJsonType(f) { export function getJsonType(f) {
if (!sqlDataTypes.includes(f.type)) { if (!defaultTypes.includes(f.type)) {
return '{ "type" : "object", additionalProperties : true }'; return '{ "type" : "object", additionalProperties : true }';
} }
switch (f.type) { switch (f.type) {
@ -50,7 +50,7 @@ export function getTypeString(field, dbms = "mysql", baseType = false) {
if (field.type === "SET" || field.type === "ENUM") { if (field.type === "SET" || field.type === "ENUM") {
return `${field.type}(${field.values.map((v) => `"${v}"`).join(", ")})`; return `${field.type}(${field.values.map((v) => `"${v}"`).join(", ")})`;
} }
if (!sqlDataTypes.includes(field.type)) { if (!defaultTypes.includes(field.type)) {
return "JSON"; return "JSON";
} }
return field.type; return field.type;
@ -173,7 +173,7 @@ export function jsonToMySQL(obj) {
field.default !== "" ? ` DEFAULT ${parseDefault(field)}` : "" field.default !== "" ? ` DEFAULT ${parseDefault(field)}` : ""
}${ }${
field.check === "" || !hasCheck(field.type) field.check === "" || !hasCheck(field.type)
? !sqlDataTypes.includes(field.type) ? !defaultTypes.includes(field.type)
? ` CHECK(\n\t\tJSON_SCHEMA_VALID("${generateSchema( ? ` CHECK(\n\t\tJSON_SCHEMA_VALID("${generateSchema(
obj.types.find( obj.types.find(
(t) => t.name === field.type.toLowerCase(), (t) => t.name === field.type.toLowerCase(),
@ -414,7 +414,7 @@ export function jsonToMariaDB(obj) {
field.default !== "" ? ` DEFAULT ${parseDefault(field)}` : "" field.default !== "" ? ` DEFAULT ${parseDefault(field)}` : ""
}${ }${
field.check === "" || !hasCheck(field.type) field.check === "" || !hasCheck(field.type)
? !sqlDataTypes.includes(field.type) ? !defaultTypes.includes(field.type)
? ` CHECK(\n\t\tJSON_SCHEMA_VALID('${generateSchema( ? ` CHECK(\n\t\tJSON_SCHEMA_VALID('${generateSchema(
obj.types.find( obj.types.find(
(t) => t.name === field.type.toLowerCase(), (t) => t.name === field.type.toLowerCase(),