mirror of
https://github.com/drawdb-io/drawdb.git
synced 2025-05-24 18:39:12 +00:00
Import tables from dbml
This commit is contained in:
parent
5bd6f936a1
commit
d76a9e9ff8
@ -1444,7 +1444,7 @@ export default function ControlPanel({
|
|||||||
function toolbar() {
|
function toolbar() {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className="py-1.5 px-5 flex justify-between items-center rounded-xl my-1 sm:mx-1 xl:mx-6 select-none overflow-hidden toolbar-theme"
|
className="py-1.5 px-5 flex justify-between items-center rounded-lg my-1 sm:mx-1 xl:mx-6 select-none overflow-hidden toolbar-theme"
|
||||||
style={isRtl(i18n.language) ? { direction: "rtl" } : {}}
|
style={isRtl(i18n.language) ? { direction: "rtl" } : {}}
|
||||||
>
|
>
|
||||||
<div className="flex justify-start items-center">
|
<div className="flex justify-start items-center">
|
||||||
|
@ -2,28 +2,28 @@ import { useEffect, useState } from "react";
|
|||||||
import CodeMirror from "@uiw/react-codemirror";
|
import CodeMirror from "@uiw/react-codemirror";
|
||||||
import { vscodeDark, vscodeLight } from "@uiw/codemirror-theme-vscode";
|
import { vscodeDark, vscodeLight } from "@uiw/codemirror-theme-vscode";
|
||||||
import { languageExtension } from "../../../data/editorExtensions";
|
import { languageExtension } from "../../../data/editorExtensions";
|
||||||
import { useSettings } from "../../../hooks";
|
import { useDiagram, useSettings } from "../../../hooks";
|
||||||
import { useDebounceValue } from "usehooks-ts";
|
import { useDebounceValue } from "usehooks-ts";
|
||||||
import { Parser } from "@dbml/core";
|
|
||||||
import "./styles.css";
|
import "./styles.css";
|
||||||
|
import { fromDBML } from "../../../utils/dbml/fromDBML";
|
||||||
const parser = new Parser();
|
|
||||||
|
|
||||||
export default function DBMLEditor() {
|
export default function DBMLEditor() {
|
||||||
const { settings } = useSettings();
|
const { settings } = useSettings();
|
||||||
|
const { setTables } = useDiagram();
|
||||||
const [value, setValue] = useState("");
|
const [value, setValue] = useState("");
|
||||||
const [debouncedValue] = useDebounceValue(value, 1000);
|
const [debouncedValue] = useDebounceValue(value, 1000);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (debouncedValue) {
|
if (debouncedValue) {
|
||||||
try {
|
try {
|
||||||
const database = parser.parse(debouncedValue, "dbml");
|
const { tables } = fromDBML(debouncedValue);
|
||||||
console.log(database);
|
console.log(tables);
|
||||||
|
setTables(tables);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log(e);
|
console.log("error: ", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [debouncedValue]);
|
}, [debouncedValue, setTables]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
|
@ -9,3 +9,15 @@
|
|||||||
.ͼ1o .cm-gutters {
|
.ͼ1o .cm-gutters {
|
||||||
background-color: var(--semi-color-bg-0);
|
background-color: var(--semi-color-bg-0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ͼ1.cm-focused {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ͼ16 {
|
||||||
|
background-color: #1e1e1e00;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ͼ16 .cm-gutters {
|
||||||
|
background-color: rgba(var(--semi-grey-1), 0.3);
|
||||||
|
}
|
@ -92,31 +92,12 @@ export default function SidePanel({ width, resize, setResize }) {
|
|||||||
style={{ width: `${width}px` }}
|
style={{ width: `${width}px` }}
|
||||||
>
|
>
|
||||||
<div className="h-full flex-1 overflow-y-auto">
|
<div className="h-full flex-1 overflow-y-auto">
|
||||||
<<<<<<< HEAD
|
|
||||||
<Tabs
|
|
||||||
type="card"
|
|
||||||
activeKey={selectedElement.currentTab}
|
|
||||||
lazyRender
|
|
||||||
keepDOM={false}
|
|
||||||
onChange={(key) =>
|
|
||||||
setSelectedElement((prev) => ({ ...prev, currentTab: key }))
|
|
||||||
}
|
|
||||||
collapsible
|
|
||||||
tabBarStyle={{ direction: "ltr" }}
|
|
||||||
>
|
|
||||||
{tabList.length &&
|
|
||||||
tabList.map((tab) => (
|
|
||||||
<TabPane tab={tab.tab} itemKey={tab.itemKey} key={tab.itemKey}>
|
|
||||||
<div className="p-2">{tab.component}</div>
|
|
||||||
</TabPane>
|
|
||||||
))}
|
|
||||||
</Tabs>
|
|
||||||
=======
|
|
||||||
{layout.dbmlEditor ? (
|
{layout.dbmlEditor ? (
|
||||||
<Tabs
|
<Tabs
|
||||||
type="card"
|
type="card"
|
||||||
activeKey={selectedElement.currentTab}
|
activeKey={selectedElement.currentTab}
|
||||||
lazyRender
|
lazyRender
|
||||||
|
keepDOM={false}
|
||||||
onChange={(key) =>
|
onChange={(key) =>
|
||||||
setSelectedElement((prev) => ({ ...prev, currentTab: key }))
|
setSelectedElement((prev) => ({ ...prev, currentTab: key }))
|
||||||
}
|
}
|
||||||
@ -137,7 +118,6 @@ export default function SidePanel({ width, resize, setResize }) {
|
|||||||
) : (
|
) : (
|
||||||
<DBMLEditor />
|
<DBMLEditor />
|
||||||
)}
|
)}
|
||||||
>>>>>>> feb41e8 (Add dbml editor to sidepanel)
|
|
||||||
</div>
|
</div>
|
||||||
{layout.issues && (
|
{layout.issues && (
|
||||||
<div className="mt-auto border-t-2 border-color shadow-inner">
|
<div className="mt-auto border-t-2 border-color shadow-inner">
|
||||||
|
@ -243,11 +243,8 @@ const en = {
|
|||||||
failed_to_load: "Failed to load. Make sure the link is correct.",
|
failed_to_load: "Failed to load. Make sure the link is correct.",
|
||||||
share_info:
|
share_info:
|
||||||
"* Sharing this link will not create a live real-time collaboration session.",
|
"* Sharing this link will not create a live real-time collaboration session.",
|
||||||
<<<<<<< HEAD
|
|
||||||
show_relationship_labels: "Show relationship labels",
|
show_relationship_labels: "Show relationship labels",
|
||||||
=======
|
|
||||||
dbml_editor: "DBML editor",
|
dbml_editor: "DBML editor",
|
||||||
>>>>>>> feb41e8 (Add dbml editor to sidepanel)
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -105,7 +105,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.toolbar-theme {
|
.toolbar-theme {
|
||||||
background-color: rgba(var(--semi-grey-1), 1);
|
background-color: rgba(var(--semi-grey-1), 0.7);
|
||||||
}
|
}
|
||||||
|
|
||||||
.hover-1:hover {
|
.hover-1:hover {
|
||||||
|
27
src/utils/arrangeTables.js
Normal file
27
src/utils/arrangeTables.js
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import {
|
||||||
|
tableColorStripHeight,
|
||||||
|
tableFieldHeight,
|
||||||
|
tableHeaderHeight,
|
||||||
|
} from "../data/constants";
|
||||||
|
|
||||||
|
export function arrangeTables(diagram) {
|
||||||
|
let maxHeight = -1;
|
||||||
|
const tableWidth = 200;
|
||||||
|
const gapX = 54;
|
||||||
|
const gapY = 40;
|
||||||
|
diagram.tables.forEach((table, i) => {
|
||||||
|
if (i < diagram.tables.length / 2) {
|
||||||
|
table.x = i * tableWidth + (i + 1) * gapX;
|
||||||
|
table.y = gapY;
|
||||||
|
const height =
|
||||||
|
table.fields.length * tableFieldHeight +
|
||||||
|
tableHeaderHeight +
|
||||||
|
tableColorStripHeight;
|
||||||
|
maxHeight = Math.max(height, maxHeight);
|
||||||
|
} else {
|
||||||
|
const index = diagram.tables.length - i - 1;
|
||||||
|
table.x = index * tableWidth + (index + 1) * gapX;
|
||||||
|
table.y = maxHeight + 2 * gapY;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
78
src/utils/dbml/fromDBML.js
Normal file
78
src/utils/dbml/fromDBML.js
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
import { Parser } from "@dbml/core";
|
||||||
|
import { arrangeTables } from "../arrangeTables";
|
||||||
|
|
||||||
|
const parser = new Parser();
|
||||||
|
|
||||||
|
/**
|
||||||
|
|
||||||
|
{
|
||||||
|
"id": 0,
|
||||||
|
"name": "some_table",
|
||||||
|
"x": 812.9083754222163,
|
||||||
|
"y": 400.3451698134321,
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"name": "id",
|
||||||
|
"type": "INT",
|
||||||
|
"default": "",
|
||||||
|
"check": "",
|
||||||
|
"primary": true,
|
||||||
|
"unique": true,
|
||||||
|
"notNull": true,
|
||||||
|
"increment": true,
|
||||||
|
"comment": "",
|
||||||
|
"id": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"comment": "",
|
||||||
|
"indices": [],
|
||||||
|
"color": "#175e7a",
|
||||||
|
"key": 1737222753837
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
export function fromDBML(src) {
|
||||||
|
const ast = parser.parse(src, "dbml");
|
||||||
|
|
||||||
|
const tables = [];
|
||||||
|
|
||||||
|
for (const schema of ast.schemas) {
|
||||||
|
for (const table of schema.tables) {
|
||||||
|
let parsedTable = {};
|
||||||
|
parsedTable.id = tables.length;
|
||||||
|
parsedTable.name = table.name;
|
||||||
|
parsedTable.comment = "";
|
||||||
|
parsedTable.color = "#175e7a";
|
||||||
|
parsedTable.fields = [];
|
||||||
|
parsedTable.indices = [];
|
||||||
|
|
||||||
|
for (const column of table.fields) {
|
||||||
|
const field = {};
|
||||||
|
field.id = parsedTable.fields.length;
|
||||||
|
field.name = column.name;
|
||||||
|
field.type = column.type.type_name.toUpperCase();
|
||||||
|
field.default = column.dbdefault ?? "";
|
||||||
|
field.check = "";
|
||||||
|
field.primary = !!column.pk;
|
||||||
|
field.unique = true;
|
||||||
|
field.notNull = !!column.not_null;
|
||||||
|
field.increment = !!column.increment;
|
||||||
|
field.comment = column.note ?? "";
|
||||||
|
|
||||||
|
parsedTable.fields.push(field);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(table);
|
||||||
|
|
||||||
|
tables.push(parsedTable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(ast);
|
||||||
|
|
||||||
|
const diagram = { tables };
|
||||||
|
|
||||||
|
arrangeTables(diagram);
|
||||||
|
|
||||||
|
return diagram;
|
||||||
|
}
|
@ -1,9 +1,5 @@
|
|||||||
import {
|
import { DB } from "../../data/constants";
|
||||||
DB,
|
import { arrangeTables } from "../arrangeTables";
|
||||||
tableColorStripHeight,
|
|
||||||
tableFieldHeight,
|
|
||||||
tableHeaderHeight,
|
|
||||||
} from "../../data/constants";
|
|
||||||
import { fromMariaDB } from "./mariadb";
|
import { fromMariaDB } from "./mariadb";
|
||||||
import { fromMSSQL } from "./mssql";
|
import { fromMSSQL } from "./mssql";
|
||||||
import { fromMySQL } from "./mysql";
|
import { fromMySQL } from "./mysql";
|
||||||
@ -33,25 +29,7 @@ export function importSQL(ast, toDb = DB.MYSQL, diagramDb = DB.GENERIC) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
let maxHeight = -1;
|
arrangeTables(diagram);
|
||||||
const tableWidth = 200;
|
|
||||||
const gapX = 54;
|
|
||||||
const gapY = 40;
|
|
||||||
diagram.tables.forEach((table, i) => {
|
|
||||||
if (i < diagram.tables.length / 2) {
|
|
||||||
table.x = i * tableWidth + (i + 1) * gapX;
|
|
||||||
table.y = gapY;
|
|
||||||
const height =
|
|
||||||
table.fields.length * tableFieldHeight +
|
|
||||||
tableHeaderHeight +
|
|
||||||
tableColorStripHeight;
|
|
||||||
maxHeight = Math.max(height, maxHeight);
|
|
||||||
} else {
|
|
||||||
const index = diagram.tables.length - i - 1;
|
|
||||||
table.x = index * tableWidth + (index + 1) * gapX;
|
|
||||||
table.y = maxHeight + 2 * gapY;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return diagram;
|
return diagram;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user