mirror of
https://github.com/drawdb-io/drawdb.git
synced 2025-08-29 02:25:26 +00:00
Table and field drag and drop ordering (#444)
* Add dnd for tables and fields * Fix inputs * Decouple ids and indecies in the editor * Decouple ids and indecies in utils * Fix field indexes * Use nanoid instead of numberic ids for fields and tables * Fix review comments
This commit is contained in:
76
package-lock.json
generated
76
package-lock.json
generated
@@ -9,6 +9,9 @@
|
||||
"version": "0.0.0",
|
||||
"dependencies": {
|
||||
"@dbml/core": "^3.9.7-alpha.0",
|
||||
"@dnd-kit/core": "^6.3.1",
|
||||
"@dnd-kit/sortable": "^10.0.0",
|
||||
"@dnd-kit/utilities": "^3.2.2",
|
||||
"@douyinfe/semi-ui": "^2.77.1",
|
||||
"@lexical/react": "^0.12.5",
|
||||
"@monaco-editor/react": "^4.7.0",
|
||||
@@ -27,6 +30,7 @@
|
||||
"jspdf": "^3.0.1",
|
||||
"jszip": "^3.10.1",
|
||||
"lexical": "^0.12.5",
|
||||
"nanoid": "^5.1.5",
|
||||
"node-sql-parser": "^5.3.9",
|
||||
"oracle-sql-parser": "^0.1.0",
|
||||
"react": "^18.2.0",
|
||||
@@ -402,9 +406,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@dnd-kit/accessibility": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@dnd-kit/accessibility/-/accessibility-3.1.0.tgz",
|
||||
"integrity": "sha512-ea7IkhKvlJUv9iSHJOnxinBcoOI3ppGnnL+VDJ75O45Nss6HtZd8IdN8touXPDtASfeI2T2LImb8VOZcL47wjQ==",
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@dnd-kit/accessibility/-/accessibility-3.1.1.tgz",
|
||||
"integrity": "sha512-2P+YgaXF+gRsIihwwY1gCsQSYnu9Zyj2py8kY5fFvUM1qm2WA2u639R6YNVfU4GWr+ZM5mqEsfHZZLoRONbemw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tslib": "^2.0.0"
|
||||
},
|
||||
@@ -413,11 +418,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@dnd-kit/core": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@dnd-kit/core/-/core-6.1.0.tgz",
|
||||
"integrity": "sha512-J3cQBClB4TVxwGo3KEjssGEXNJqGVWx17aRTZ1ob0FliR5IjYgTxl5YJbKTzA6IzrtelotH19v6y7uoIRUZPSg==",
|
||||
"version": "6.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@dnd-kit/core/-/core-6.3.1.tgz",
|
||||
"integrity": "sha512-xkGBRQQab4RLwgXxoqETICr6S5JlogafbhNsidmrkVv2YRs5MLwpjoF2qpiGjQt8S9AoxtIV603s0GIUpY5eYQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@dnd-kit/accessibility": "^3.1.0",
|
||||
"@dnd-kit/accessibility": "^3.1.1",
|
||||
"@dnd-kit/utilities": "^3.2.2",
|
||||
"tslib": "^2.0.0"
|
||||
},
|
||||
@@ -427,15 +433,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@dnd-kit/sortable": {
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@dnd-kit/sortable/-/sortable-7.0.2.tgz",
|
||||
"integrity": "sha512-wDkBHHf9iCi1veM834Gbk1429bd4lHX4RpAwT0y2cHLf246GAvU2sVw/oxWNpPKQNQRQaeGXhAVgrOl1IT+iyA==",
|
||||
"version": "10.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@dnd-kit/sortable/-/sortable-10.0.0.tgz",
|
||||
"integrity": "sha512-+xqhmIIzvAYMGfBYYnbKuNicfSsk4RksY2XdmJhT+HAC01nix6fHCztU68jooFiMUB01Ky3F0FyOvhG/BZrWkg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@dnd-kit/utilities": "^3.2.0",
|
||||
"@dnd-kit/utilities": "^3.2.2",
|
||||
"tslib": "^2.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@dnd-kit/core": "^6.0.7",
|
||||
"@dnd-kit/core": "^6.3.0",
|
||||
"react": ">=16.8.0"
|
||||
}
|
||||
},
|
||||
@@ -443,6 +450,7 @@
|
||||
"version": "3.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@dnd-kit/utilities/-/utilities-3.2.2.tgz",
|
||||
"integrity": "sha512-+MKAJEOfaBe5SmV6t34p80MMKhjvUz0vRrvVJbPT0WElzaOJ/1xs+D+KDv+tD/NE5ujfrChEcshd4fLn0wpiqg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tslib": "^2.0.0"
|
||||
},
|
||||
@@ -568,6 +576,20 @@
|
||||
"react-dom": ">=16.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@douyinfe/semi-ui/node_modules/@dnd-kit/sortable": {
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@dnd-kit/sortable/-/sortable-7.0.2.tgz",
|
||||
"integrity": "sha512-wDkBHHf9iCi1veM834Gbk1429bd4lHX4RpAwT0y2cHLf246GAvU2sVw/oxWNpPKQNQRQaeGXhAVgrOl1IT+iyA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@dnd-kit/utilities": "^3.2.0",
|
||||
"tslib": "^2.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@dnd-kit/core": "^6.0.7",
|
||||
"react": ">=16.8.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",
|
||||
@@ -6214,10 +6236,9 @@
|
||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||
},
|
||||
"node_modules/nanoid": {
|
||||
"version": "3.3.11",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
|
||||
"integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
|
||||
"dev": true,
|
||||
"version": "5.1.5",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.1.5.tgz",
|
||||
"integrity": "sha512-Ir/+ZpE9fDsNH0hQ3C68uyThDXzYcim2EqcZ8zn8Chtt1iylPT9xXJB0kPCnqzgcEGikO9RxSrh63MsmVCU7Fw==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
@@ -6226,10 +6247,10 @@
|
||||
],
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"nanoid": "bin/nanoid.cjs"
|
||||
"nanoid": "bin/nanoid.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
|
||||
"node": "^18 || >=20"
|
||||
}
|
||||
},
|
||||
"node_modules/natural-compare": {
|
||||
@@ -6571,6 +6592,25 @@
|
||||
"node": "^10 || ^12 || >=14"
|
||||
}
|
||||
},
|
||||
"node_modules/postcss/node_modules/nanoid": {
|
||||
"version": "3.3.11",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
|
||||
"integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ai"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"nanoid": "bin/nanoid.cjs"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/prelude-ls": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
|
||||
|
@@ -11,6 +11,9 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@dbml/core": "^3.9.7-alpha.0",
|
||||
"@dnd-kit/core": "^6.3.1",
|
||||
"@dnd-kit/sortable": "^10.0.0",
|
||||
"@dnd-kit/utilities": "^3.2.2",
|
||||
"@douyinfe/semi-ui": "^2.77.1",
|
||||
"@lexical/react": "^0.12.5",
|
||||
"@monaco-editor/react": "^4.7.0",
|
||||
@@ -29,6 +32,7 @@
|
||||
"jspdf": "^3.0.1",
|
||||
"jszip": "^3.10.1",
|
||||
"lexical": "^0.12.5",
|
||||
"nanoid": "^5.1.5",
|
||||
"node-sql-parser": "^5.3.9",
|
||||
"oracle-sql-parser": "^0.1.0",
|
||||
"react": "^18.2.0",
|
||||
|
@@ -55,7 +55,7 @@ export default function Canvas() {
|
||||
} = useSelect();
|
||||
const [dragging, setDragging] = useState({
|
||||
element: ObjectType.NONE,
|
||||
id: -1,
|
||||
id: null,
|
||||
prevX: 0,
|
||||
prevY: 0,
|
||||
initialPositions: [],
|
||||
@@ -73,8 +73,8 @@ export default function Canvas() {
|
||||
});
|
||||
const [grabOffset, setGrabOffset] = useState({ x: 0, y: 0 });
|
||||
const [hoveredTable, setHoveredTable] = useState({
|
||||
tableId: -1,
|
||||
field: -2,
|
||||
tableId: null,
|
||||
fieldId: null,
|
||||
});
|
||||
const [panning, setPanning] = useState({
|
||||
isPanning: false,
|
||||
@@ -167,7 +167,7 @@ export default function Canvas() {
|
||||
const getElement = (element) => {
|
||||
switch (element.type) {
|
||||
case ObjectType.TABLE:
|
||||
return tables[element.id];
|
||||
return tables.find((t) => t.id === element.id);
|
||||
case ObjectType.AREA:
|
||||
return areas[element.id];
|
||||
case ObjectType.NOTE:
|
||||
@@ -264,7 +264,7 @@ export default function Canvas() {
|
||||
});
|
||||
} else if (
|
||||
dragging.element !== ObjectType.NONE &&
|
||||
dragging.id >= 0 &&
|
||||
dragging.id !== null &&
|
||||
bulkSelectedElements.length
|
||||
) {
|
||||
const currentX = pointer.spaces.diagram.x + grabOffset.x;
|
||||
@@ -274,9 +274,10 @@ export default function Canvas() {
|
||||
|
||||
for (const element of bulkSelectedElements) {
|
||||
if (element.type === ObjectType.TABLE) {
|
||||
const { x, y } = tables.find((e) => e.id === element.id);
|
||||
updateTable(element.id, {
|
||||
x: tables[element.id].x + deltaX,
|
||||
y: tables[element.id].y + deltaY,
|
||||
x: x + deltaX,
|
||||
y: y + deltaY,
|
||||
});
|
||||
}
|
||||
if (element.type === ObjectType.AREA) {
|
||||
@@ -317,21 +318,21 @@ export default function Canvas() {
|
||||
(panning.cursorStart.y - pointer.spaces.screen.y) / transform.zoom,
|
||||
},
|
||||
}));
|
||||
} else if (dragging.element === ObjectType.TABLE && dragging.id >= 0) {
|
||||
} else if (dragging.element === ObjectType.TABLE && dragging.id !== null) {
|
||||
updateTable(dragging.id, {
|
||||
x: pointer.spaces.diagram.x + grabOffset.x,
|
||||
y: pointer.spaces.diagram.y + grabOffset.y,
|
||||
});
|
||||
} else if (
|
||||
dragging.element === ObjectType.AREA &&
|
||||
dragging.id >= 0 &&
|
||||
dragging.id !== null &&
|
||||
areaResize.id === -1
|
||||
) {
|
||||
updateArea(dragging.id, {
|
||||
x: pointer.spaces.diagram.x + grabOffset.x,
|
||||
y: pointer.spaces.diagram.y + grabOffset.y,
|
||||
});
|
||||
} else if (dragging.element === ObjectType.NOTE && dragging.id >= 0) {
|
||||
} else if (dragging.element === ObjectType.NOTE && dragging.id !== null) {
|
||||
updateNote(dragging.id, {
|
||||
x: pointer.spaces.diagram.x + grabOffset.x,
|
||||
y: pointer.spaces.diagram.y + grabOffset.y,
|
||||
@@ -441,7 +442,10 @@ export default function Canvas() {
|
||||
};
|
||||
|
||||
const didPan = () =>
|
||||
!(transform.pan.x === panning.panStart.x && transform.pan.y === panning.panStart.y);
|
||||
!(
|
||||
transform.pan.x === panning.panStart.x &&
|
||||
transform.pan.y === panning.panStart.y
|
||||
);
|
||||
|
||||
/**
|
||||
* @param {PointerEvent} e
|
||||
@@ -503,7 +507,7 @@ export default function Canvas() {
|
||||
}
|
||||
setDragging({
|
||||
element: ObjectType.NONE,
|
||||
id: -1,
|
||||
id: null,
|
||||
prevX: 0,
|
||||
prevY: 0,
|
||||
initialPositions: [],
|
||||
@@ -585,7 +589,7 @@ export default function Canvas() {
|
||||
setPanning((old) => ({ ...old, isPanning: false }));
|
||||
setDragging({
|
||||
element: ObjectType.NONE,
|
||||
id: -1,
|
||||
id: null,
|
||||
prevX: 0,
|
||||
prevY: 0,
|
||||
initialPositions: [],
|
||||
@@ -594,34 +598,40 @@ export default function Canvas() {
|
||||
};
|
||||
|
||||
const handleLinking = () => {
|
||||
if (hoveredTable.tableId < 0) return;
|
||||
if (hoveredTable.field < 0) return;
|
||||
if (
|
||||
!areFieldsCompatible(
|
||||
database,
|
||||
tables[linkingLine.startTableId].fields[linkingLine.startFieldId],
|
||||
tables[hoveredTable.tableId].fields[hoveredTable.field],
|
||||
)
|
||||
) {
|
||||
if (hoveredTable.tableId === null) return;
|
||||
if (hoveredTable.fieldId === null) return;
|
||||
|
||||
const { fields: startTableFields, name: startTableName } = tables.find(
|
||||
(t) => t.id === linkingLine.startTableId,
|
||||
);
|
||||
const { type: startType, name: startFieldName } = startTableFields.find(
|
||||
(f) => f.id === linkingLine.startFieldId,
|
||||
);
|
||||
const { fields: endTableFields, name: endTableName } = tables.find(
|
||||
(t) => t.id === hoveredTable.tableId,
|
||||
);
|
||||
const { type: endType } = endTableFields.find(
|
||||
(f) => f.id === hoveredTable.fieldId,
|
||||
);
|
||||
|
||||
if (!areFieldsCompatible(database, startType, endType)) {
|
||||
Toast.info(t("cannot_connect"));
|
||||
return;
|
||||
}
|
||||
if (
|
||||
linkingLine.startTableId === hoveredTable.tableId &&
|
||||
linkingLine.startFieldId === hoveredTable.field
|
||||
linkingLine.startFieldId === hoveredTable.fieldId
|
||||
)
|
||||
return;
|
||||
|
||||
const newRelationship = {
|
||||
...linkingLine,
|
||||
endTableId: hoveredTable.tableId,
|
||||
endFieldId: hoveredTable.field,
|
||||
endFieldId: hoveredTable.fieldId,
|
||||
cardinality: Cardinality.ONE_TO_ONE,
|
||||
updateConstraint: Constraint.NONE,
|
||||
deleteConstraint: Constraint.NONE,
|
||||
name: `fk_${tables[linkingLine.startTableId].name}_${
|
||||
tables[linkingLine.startTableId].fields[linkingLine.startFieldId].name
|
||||
}_${tables[hoveredTable.tableId].name}`,
|
||||
name: `fk_${startTableName}_${startFieldName}_${endTableName}`,
|
||||
id: relationships.length,
|
||||
};
|
||||
delete newRelationship.startX;
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { useRef } from "react";
|
||||
import { useMemo, useRef } from "react";
|
||||
import {
|
||||
Cardinality,
|
||||
darkBgTheme,
|
||||
@@ -20,6 +20,22 @@ export default function Relationship({ data }) {
|
||||
const { selectedElement, setSelectedElement } = useSelect();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const pathValues = useMemo(() => {
|
||||
const startTable = tables.find((t) => t.id === data.startTableId);
|
||||
const endTable = tables.find((t) => t.id === data.endTableId);
|
||||
|
||||
if (!startTable || !endTable) return null;
|
||||
|
||||
return {
|
||||
startFieldIndex: startTable.fields.findIndex(
|
||||
(f) => f.id === data.startFieldId,
|
||||
),
|
||||
endFieldIndex: endTable.fields.findIndex((f) => f.id === data.endFieldId),
|
||||
startTable: { x: startTable.x, y: startTable.y },
|
||||
endTable: { x: endTable.x, y: endTable.y },
|
||||
};
|
||||
}, [tables, data]);
|
||||
|
||||
const theme = localStorage.getItem("theme");
|
||||
|
||||
const pathRef = useRef();
|
||||
@@ -106,20 +122,7 @@ export default function Relationship({ data }) {
|
||||
<g className="select-none group" onDoubleClick={edit}>
|
||||
<path
|
||||
ref={pathRef}
|
||||
d={calcPath(
|
||||
{
|
||||
...data,
|
||||
startTable: {
|
||||
x: tables[data.startTableId].x,
|
||||
y: tables[data.startTableId].y,
|
||||
},
|
||||
endTable: {
|
||||
x: tables[data.endTableId].x,
|
||||
y: tables[data.endTableId].y,
|
||||
},
|
||||
},
|
||||
settings.tableWidth,
|
||||
)}
|
||||
d={calcPath(pathValues, settings.tableWidth)}
|
||||
stroke="gray"
|
||||
className="group-hover:stroke-sky-700"
|
||||
fill="none"
|
||||
|
@@ -22,7 +22,7 @@ import { isRtl } from "../../i18n/utils/rtl";
|
||||
import i18n from "../../i18n/i18n";
|
||||
|
||||
export default function Table(props) {
|
||||
const [hoveredField, setHoveredField] = useState(-1);
|
||||
const [hoveredField, setHoveredField] = useState(null);
|
||||
const { database } = useDiagram();
|
||||
const {
|
||||
tableData,
|
||||
@@ -45,9 +45,10 @@ export default function Table(props) {
|
||||
|
||||
const height =
|
||||
tableData.fields.length * tableFieldHeight + tableHeaderHeight + 7;
|
||||
|
||||
const isSelected = useMemo(() => {
|
||||
return (
|
||||
(selectedElement.id === tableData.id &&
|
||||
(selectedElement.id == tableData.id &&
|
||||
selectedElement.element === ObjectType.TABLE) ||
|
||||
bulkSelectedElements.some(
|
||||
(e) => e.type === ObjectType.TABLE && e.id === tableData.id,
|
||||
@@ -124,7 +125,7 @@ export default function Table(props) {
|
||||
onClick={openEditor}
|
||||
/>
|
||||
<Popover
|
||||
key={tableData.key}
|
||||
key={tableData.id}
|
||||
content={
|
||||
<div className="popover-theme">
|
||||
<div className="mb-2">
|
||||
@@ -304,13 +305,17 @@ export default function Table(props) {
|
||||
setHoveredField(index);
|
||||
setHoveredTable({
|
||||
tableId: tableData.id,
|
||||
field: index,
|
||||
fieldId: fieldData.id,
|
||||
});
|
||||
}}
|
||||
onPointerLeave={(e) => {
|
||||
if (!e.isPrimary) return;
|
||||
|
||||
setHoveredField(-1);
|
||||
setHoveredField(null);
|
||||
setHoveredTable({
|
||||
tableId: null,
|
||||
fieldId: null,
|
||||
});
|
||||
}}
|
||||
onPointerDown={(e) => {
|
||||
// Required for onPointerLeave to trigger when a touch pointer leaves
|
||||
@@ -328,10 +333,10 @@ export default function Table(props) {
|
||||
onPointerDown={(e) => {
|
||||
if (!e.isPrimary) return;
|
||||
|
||||
handleGripField(index);
|
||||
handleGripField();
|
||||
setLinkingLine((prev) => ({
|
||||
...prev,
|
||||
startFieldId: index,
|
||||
startFieldId: fieldData.id,
|
||||
startTableId: tableData.id,
|
||||
startX: tableData.x + 15,
|
||||
startY:
|
||||
|
@@ -78,6 +78,7 @@ import { IdContext } from "../Workspace";
|
||||
import { socials } from "../../data/socials";
|
||||
import { toDBML } from "../../utils/exportAs/dbml";
|
||||
import { exportSavedData } from "../../utils/exportSavedData";
|
||||
import { nanoid } from "nanoid";
|
||||
|
||||
export default function ControlPanel({
|
||||
diagramId,
|
||||
@@ -144,13 +145,12 @@ export default function ControlPanel({
|
||||
}
|
||||
}
|
||||
setRedoStack((prev) => [...prev, a]);
|
||||
console.log(a);
|
||||
return;
|
||||
}
|
||||
|
||||
if (a.action === Action.ADD) {
|
||||
if (a.element === ObjectType.TABLE) {
|
||||
deleteTable(tables[tables.length - 1].id, false);
|
||||
deleteTable(a.id, false);
|
||||
} else if (a.element === ObjectType.AREA) {
|
||||
deleteArea(areas[areas.length - 1].id, false);
|
||||
} else if (a.element === ObjectType.NOTE) {
|
||||
@@ -165,10 +165,8 @@ export default function ControlPanel({
|
||||
setRedoStack((prev) => [...prev, a]);
|
||||
} else if (a.action === Action.MOVE) {
|
||||
if (a.element === ObjectType.TABLE) {
|
||||
setRedoStack((prev) => [
|
||||
...prev,
|
||||
{ ...a, x: tables[a.id].x, y: tables[a.id].y },
|
||||
]);
|
||||
const { x, y } = tables.find((t) => t.id === a.id);
|
||||
setRedoStack((prev) => [...prev, { ...a, x, y }]);
|
||||
updateTable(a.id, { x: a.x, y: a.y });
|
||||
} else if (a.element === ObjectType.AREA) {
|
||||
setRedoStack((prev) => [
|
||||
@@ -205,6 +203,7 @@ export default function ControlPanel({
|
||||
} else if (a.element === ObjectType.NOTE) {
|
||||
updateNote(a.nid, a.undo);
|
||||
} else if (a.element === ObjectType.TABLE) {
|
||||
const table = tables.find((t) => t.id === a.tid);
|
||||
if (a.component === "field") {
|
||||
updateField(a.tid, a.fid, a.undo);
|
||||
} else if (a.component === "field_delete") {
|
||||
@@ -213,65 +212,24 @@ export default function ControlPanel({
|
||||
a.data.relationship.forEach((r) => {
|
||||
temp.splice(r.id, 0, r);
|
||||
});
|
||||
temp = temp.map((e, i) => {
|
||||
const recoveredRel = a.data.relationship.find(
|
||||
(x) =>
|
||||
(x.startTableId === e.startTableId &&
|
||||
x.startFieldId === e.startFieldId) ||
|
||||
(x.endTableId === e.endTableId &&
|
||||
x.endFieldId === a.endFieldId),
|
||||
);
|
||||
if (
|
||||
e.startTableId === a.tid &&
|
||||
e.startFieldId >= a.data.field.id &&
|
||||
!recoveredRel
|
||||
) {
|
||||
return {
|
||||
...e,
|
||||
id: i,
|
||||
startFieldId: e.startFieldId + 1,
|
||||
};
|
||||
}
|
||||
if (
|
||||
e.endTableId === a.tid &&
|
||||
e.endFieldId >= a.data.field.id &&
|
||||
!recoveredRel
|
||||
) {
|
||||
return {
|
||||
...e,
|
||||
id: i,
|
||||
endFieldId: e.endFieldId + 1,
|
||||
};
|
||||
}
|
||||
return { ...e, id: i };
|
||||
});
|
||||
return temp;
|
||||
});
|
||||
setTables((prev) =>
|
||||
prev.map((t) => {
|
||||
if (t.id === a.tid) {
|
||||
const temp = t.fields.slice();
|
||||
temp.splice(a.data.field.id, 0, a.data.field);
|
||||
return { ...t, fields: temp.map((t, i) => ({ ...t, id: i })) };
|
||||
}
|
||||
return t;
|
||||
}),
|
||||
);
|
||||
const updatedFields = table.fields.slice();
|
||||
updatedFields.splice(a.data.index, 0, a.data.field);
|
||||
updateTable(a.tid, { fields: updatedFields });
|
||||
} else if (a.component === "field_add") {
|
||||
updateTable(a.tid, {
|
||||
fields: tables[a.tid].fields
|
||||
.filter((e) => e.id !== tables[a.tid].fields.length - 1)
|
||||
.map((t, i) => ({ ...t, id: i })),
|
||||
fields: table.fields.filter((e) => e.id !== a.fid),
|
||||
});
|
||||
} else if (a.component === "index_add") {
|
||||
updateTable(a.tid, {
|
||||
indices: tables[a.tid].indices
|
||||
.filter((e) => e.id !== tables[a.tid].indices.length - 1)
|
||||
indices: table.indices
|
||||
.filter((e) => e.id !== table.indices.length - 1)
|
||||
.map((t, i) => ({ ...t, id: i })),
|
||||
});
|
||||
} else if (a.component === "index") {
|
||||
updateTable(a.tid, {
|
||||
indices: tables[a.tid].indices.map((index) =>
|
||||
indices: table.indices.map((index) =>
|
||||
index.id === a.iid
|
||||
? {
|
||||
...index,
|
||||
@@ -281,19 +239,11 @@ export default function ControlPanel({
|
||||
),
|
||||
});
|
||||
} else if (a.component === "index_delete") {
|
||||
setTables((prev) =>
|
||||
prev.map((table) => {
|
||||
if (table.id === a.tid) {
|
||||
const temp = table.indices.slice();
|
||||
temp.splice(a.data.id, 0, a.data);
|
||||
return {
|
||||
...table,
|
||||
indices: temp.map((t, i) => ({ ...t, id: i })),
|
||||
};
|
||||
}
|
||||
return table;
|
||||
}),
|
||||
);
|
||||
const updatedIndices = table.indices.slice();
|
||||
updatedIndices.splice(a.data.id, 0, a.data);
|
||||
updateTable(a.tid, {
|
||||
indices: updatedIndices.map((t, i) => ({ ...t, id: i })),
|
||||
});
|
||||
} else if (a.component === "self") {
|
||||
updateTable(a.tid, a.undo);
|
||||
}
|
||||
@@ -390,10 +340,8 @@ export default function ControlPanel({
|
||||
setUndoStack((prev) => [...prev, a]);
|
||||
} else if (a.action === Action.MOVE) {
|
||||
if (a.element === ObjectType.TABLE) {
|
||||
setUndoStack((prev) => [
|
||||
...prev,
|
||||
{ ...a, x: tables[a.id].x, y: tables[a.id].y },
|
||||
]);
|
||||
const { x, y } = tables.find((t) => t.id == a.id);
|
||||
setUndoStack((prev) => [...prev, { ...a, x, y }]);
|
||||
updateTable(a.id, { x: a.x, y: a.y });
|
||||
} else if (a.element === ObjectType.AREA) {
|
||||
setUndoStack((prev) => [
|
||||
@@ -429,6 +377,7 @@ export default function ControlPanel({
|
||||
} else if (a.element === ObjectType.NOTE) {
|
||||
updateNote(a.nid, a.redo);
|
||||
} else if (a.element === ObjectType.TABLE) {
|
||||
const table = tables.find((t) => t.id === a.tid);
|
||||
if (a.component === "field") {
|
||||
updateField(a.tid, a.fid, a.redo);
|
||||
} else if (a.component === "field_delete") {
|
||||
@@ -436,7 +385,7 @@ export default function ControlPanel({
|
||||
} else if (a.component === "field_add") {
|
||||
updateTable(a.tid, {
|
||||
fields: [
|
||||
...tables[a.tid].fields,
|
||||
...table.fields,
|
||||
{
|
||||
name: "",
|
||||
type: "",
|
||||
@@ -447,32 +396,24 @@ export default function ControlPanel({
|
||||
notNull: false,
|
||||
increment: false,
|
||||
comment: "",
|
||||
id: tables[a.tid].fields.length,
|
||||
id: nanoid(),
|
||||
},
|
||||
],
|
||||
});
|
||||
} else if (a.component === "index_add") {
|
||||
setTables((prev) =>
|
||||
prev.map((table) => {
|
||||
if (table.id === a.tid) {
|
||||
return {
|
||||
...table,
|
||||
indices: [
|
||||
...table.indices,
|
||||
{
|
||||
id: table.indices.length,
|
||||
name: `index_${table.indices.length}`,
|
||||
fields: [],
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
return table;
|
||||
}),
|
||||
);
|
||||
updateTable(a.tid, {
|
||||
indices: [
|
||||
...table.indices,
|
||||
{
|
||||
id: table.indices.length,
|
||||
name: `index_${table.indices.length}`,
|
||||
fields: [],
|
||||
},
|
||||
],
|
||||
});
|
||||
} else if (a.component === "index") {
|
||||
updateTable(a.tid, {
|
||||
indices: tables[a.tid].indices.map((index) =>
|
||||
indices: table.indices.map((index) =>
|
||||
index.id === a.iid
|
||||
? {
|
||||
...index,
|
||||
@@ -483,7 +424,7 @@ export default function ControlPanel({
|
||||
});
|
||||
} else if (a.component === "index_delete") {
|
||||
updateTable(a.tid, {
|
||||
indices: tables[a.tid].indices
|
||||
indices: table.indices
|
||||
.filter((e) => e.id !== a.data.id)
|
||||
.map((t, i) => ({ ...t, id: i })),
|
||||
});
|
||||
@@ -662,14 +603,16 @@ export default function ControlPanel({
|
||||
};
|
||||
const duplicate = () => {
|
||||
switch (selectedElement.element) {
|
||||
case ObjectType.TABLE:
|
||||
case ObjectType.TABLE: {
|
||||
const copiedTable = tables.find((t) => t.id === selectedElement.id);
|
||||
addTable({
|
||||
...tables[selectedElement.id],
|
||||
x: tables[selectedElement.id].x + 20,
|
||||
y: tables[selectedElement.id].y + 20,
|
||||
...copiedTable,
|
||||
x: copiedTable.x + 20,
|
||||
y: copiedTable.y + 20,
|
||||
id: tables.length,
|
||||
});
|
||||
break;
|
||||
}
|
||||
case ObjectType.NOTE:
|
||||
addNote({
|
||||
...notes[selectedElement.id],
|
||||
@@ -694,7 +637,9 @@ export default function ControlPanel({
|
||||
switch (selectedElement.element) {
|
||||
case ObjectType.TABLE:
|
||||
navigator.clipboard
|
||||
.writeText(JSON.stringify({ ...tables[selectedElement.id] }))
|
||||
.writeText(
|
||||
JSON.stringify(tables.find((t) => t.id === selectedElement.id)),
|
||||
)
|
||||
.catch(() => Toast.error(t("oops_smth_went_wrong")));
|
||||
break;
|
||||
case ObjectType.NOTE:
|
||||
@@ -1201,17 +1146,18 @@ export default function ControlPanel({
|
||||
setRedoStack([]);
|
||||
|
||||
if (!diagramId) {
|
||||
console.error("Something went wrong.");
|
||||
Toast.error(t("oops_smth_went_wrong"));
|
||||
return;
|
||||
}
|
||||
|
||||
db.table("diagrams")
|
||||
.delete(diagramId)
|
||||
.then(() => {
|
||||
console.info('Deleted diagram successfully.')
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(`Error deleting records with gistId '${diagramId}':`, error);
|
||||
Toast.error(t("oops_smth_went_wrong"));
|
||||
console.error(
|
||||
`Error deleting records with gistId '${diagramId}':`,
|
||||
error,
|
||||
);
|
||||
});
|
||||
},
|
||||
},
|
||||
|
@@ -83,10 +83,12 @@ export default function ImportDiagram({
|
||||
|
||||
let ok = true;
|
||||
jsonObject.relationships.forEach((rel) => {
|
||||
if (
|
||||
!jsonObject.tables[rel.startTableId] ||
|
||||
!jsonObject.tables[rel.endTableId]
|
||||
) {
|
||||
const startTable = jsonObject.tables.find(
|
||||
(t) => t.id === rel.startTableId,
|
||||
);
|
||||
const endTable = jsonObject.tables.find((t) => t.id === rel.endTableId);
|
||||
|
||||
if (!startTable || !endTable) {
|
||||
setError({
|
||||
type: STATUS.ERROR,
|
||||
message: `Relationship ${rel.name} references a table that does not exist.`,
|
||||
@@ -96,8 +98,8 @@ export default function ImportDiagram({
|
||||
}
|
||||
|
||||
if (
|
||||
!jsonObject.tables[rel.startTableId].fields[rel.startFieldId] ||
|
||||
!jsonObject.tables[rel.endTableId].fields[rel.endFieldId]
|
||||
!startTable.fields.find((f) => f.id === rel.startFieldId) ||
|
||||
!endTable.fields.find((f) => f.id === rel.endFieldId)
|
||||
) {
|
||||
setError({
|
||||
type: STATUS.ERROR,
|
||||
|
@@ -59,8 +59,7 @@ export default function Modal({
|
||||
importFrom,
|
||||
}) {
|
||||
const { t, i18n } = useTranslation();
|
||||
const { tables, setTables, setRelationships, database, setDatabase } =
|
||||
useDiagram();
|
||||
const { setTables, setRelationships, database, setDatabase } = useDiagram();
|
||||
const { setNotes } = useNotes();
|
||||
const { setAreas } = useAreas();
|
||||
const { setTypes } = useTypes();
|
||||
@@ -182,15 +181,10 @@ export default function Modal({
|
||||
setUndoStack([]);
|
||||
setRedoStack([]);
|
||||
} else {
|
||||
const initialTablesLength = tables.length;
|
||||
setTables((prev) =>
|
||||
[...prev, ...diagramData.tables].map((t, i) => ({ ...t, id: i })),
|
||||
);
|
||||
setTables((prev) => [...prev, ...diagramData.tables]);
|
||||
setRelationships((prev) =>
|
||||
[...prev, ...diagramData.relationships].map((r, i) => ({
|
||||
...r,
|
||||
startTableId: initialTablesLength + r.startTableId,
|
||||
endTableId: initialTablesLength + r.endTableId,
|
||||
id: i,
|
||||
})),
|
||||
);
|
||||
|
@@ -31,6 +31,7 @@ export default function Share({ title, setModal }) {
|
||||
|
||||
const diagramToString = useCallback(() => {
|
||||
return JSON.stringify({
|
||||
title,
|
||||
tables: tables,
|
||||
relationships: relationships,
|
||||
notes: notes,
|
||||
@@ -38,7 +39,6 @@ export default function Share({ title, setModal }) {
|
||||
database: database,
|
||||
...(databases[database].hasTypes && { types: types }),
|
||||
...(databases[database].hasEnums && { enums: enums }),
|
||||
title: title,
|
||||
transform: transform,
|
||||
});
|
||||
}, [
|
||||
|
@@ -22,10 +22,12 @@ export default function EnumDetails({ data, i }) {
|
||||
validateStatus={data.name.trim() === "" ? "error" : "default"}
|
||||
onChange={(value) => {
|
||||
updateEnum(i, { name: value });
|
||||
tables.forEach((table, i) => {
|
||||
table.fields.forEach((field, j) => {
|
||||
tables.forEach((table) => {
|
||||
table.fields.forEach((field) => {
|
||||
if (field.type.toLowerCase() === data.name.toLowerCase()) {
|
||||
updateField(i, j, { type: value.toUpperCase() });
|
||||
updateField(table.id, field.id, {
|
||||
type: value.toUpperCase(),
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
@@ -21,7 +21,7 @@ import {
|
||||
import { useDiagram, useUndoRedo } from "../../../hooks";
|
||||
import i18n from "../../../i18n/i18n";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useState } from "react";
|
||||
import { useMemo, useState } from "react";
|
||||
|
||||
const columns = [
|
||||
{
|
||||
@@ -36,11 +36,31 @@ const columns = [
|
||||
|
||||
export default function RelationshipInfo({ data }) {
|
||||
const { setUndoStack, setRedoStack } = useUndoRedo();
|
||||
const { tables, setRelationships, deleteRelationship, updateRelationship } =
|
||||
useDiagram();
|
||||
const { tables, deleteRelationship, updateRelationship } = useDiagram();
|
||||
const { t } = useTranslation();
|
||||
const [editField, setEditField] = useState({});
|
||||
|
||||
const relValues = useMemo(() => {
|
||||
const { fields: startTableFields, name: startTableName } = tables.find(
|
||||
(t) => t.id === data.startTableId,
|
||||
);
|
||||
const { name: startFieldName } = startTableFields.find(
|
||||
(f) => f.id === data.startFieldId,
|
||||
);
|
||||
const { fields: endTableFields, name: endTableName } = tables.find(
|
||||
(t) => t.id === data.endTableId,
|
||||
);
|
||||
const { name: endFieldName } = endTableFields.find(
|
||||
(f) => f.id === data.endFieldId,
|
||||
);
|
||||
return {
|
||||
startTableName,
|
||||
startFieldName,
|
||||
endTableName,
|
||||
endFieldName,
|
||||
};
|
||||
}, [tables, data]);
|
||||
|
||||
const swapKeys = () => {
|
||||
setUndoStack((prev) => [
|
||||
...prev,
|
||||
@@ -67,22 +87,14 @@ export default function RelationshipInfo({ data }) {
|
||||
},
|
||||
]);
|
||||
setRedoStack([]);
|
||||
setRelationships((prev) =>
|
||||
prev.map((e, idx) =>
|
||||
idx === data.id
|
||||
? {
|
||||
...e,
|
||||
name: `fk_${tables[e.endTableId].name}_${
|
||||
tables[e.endTableId].fields[e.endFieldId].name
|
||||
}_${tables[e.startTableId].name}`,
|
||||
startTableId: e.endTableId,
|
||||
startFieldId: e.endFieldId,
|
||||
endTableId: e.startTableId,
|
||||
endFieldId: e.startFieldId,
|
||||
}
|
||||
: e,
|
||||
),
|
||||
);
|
||||
|
||||
updateRelationship(data.id, {
|
||||
name: `fk_${relValues.endTableName}_${relValues.endFieldName}_${relValues.startTableName}`,
|
||||
startTableId: data.endTableId,
|
||||
startFieldId: data.endFieldId,
|
||||
endTableId: data.startTableId,
|
||||
endFieldId: data.startFieldId,
|
||||
});
|
||||
};
|
||||
|
||||
const changeCardinality = (value) => {
|
||||
@@ -101,11 +113,7 @@ export default function RelationshipInfo({ data }) {
|
||||
},
|
||||
]);
|
||||
setRedoStack([]);
|
||||
setRelationships((prev) =>
|
||||
prev.map((e, idx) =>
|
||||
idx === data.id ? { ...e, cardinality: value } : e,
|
||||
),
|
||||
);
|
||||
updateRelationship(data.id, { cardinality: value });
|
||||
};
|
||||
|
||||
const changeConstraint = (key, value) => {
|
||||
@@ -125,9 +133,7 @@ export default function RelationshipInfo({ data }) {
|
||||
},
|
||||
]);
|
||||
setRedoStack([]);
|
||||
setRelationships((prev) =>
|
||||
prev.map((e, idx) => (idx === data.id ? { ...e, [undoKey]: value } : e)),
|
||||
);
|
||||
updateRelationship(data.id, { [undoKey]: value });
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -165,11 +171,11 @@ export default function RelationshipInfo({ data }) {
|
||||
<div className="flex justify-between items-center mb-3">
|
||||
<div className="me-3">
|
||||
<span className="font-semibold">{t("primary")}: </span>
|
||||
{tables[data.endTableId].name}
|
||||
{relValues.endTableName}
|
||||
</div>
|
||||
<div className="mx-1">
|
||||
<span className="font-semibold">{t("foreign")}: </span>
|
||||
{tables[data.startTableId].name}
|
||||
{relValues.startTableName}
|
||||
</div>
|
||||
<div className="ms-1">
|
||||
<Popover
|
||||
@@ -180,13 +186,8 @@ export default function RelationshipInfo({ data }) {
|
||||
dataSource={[
|
||||
{
|
||||
key: "1",
|
||||
foreign: `${tables[data.startTableId]?.name}(${
|
||||
tables[data.startTableId].fields[data.startFieldId]
|
||||
?.name
|
||||
})`,
|
||||
primary: `${tables[data.endTableId]?.name}(${
|
||||
tables[data.endTableId].fields[data.endFieldId]?.name
|
||||
})`,
|
||||
foreign: `${relValues.startTableName}(${relValues.startFieldName})`,
|
||||
primary: `${relValues.endTableName}(${relValues.endFieldName})`,
|
||||
},
|
||||
]}
|
||||
pagination={false}
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { useState } from "react";
|
||||
import { useMemo, useState } from "react";
|
||||
import {
|
||||
Input,
|
||||
TextArea,
|
||||
@@ -14,12 +14,13 @@ import { useTranslation } from "react-i18next";
|
||||
import { dbToTypes } from "../../../data/datatypes";
|
||||
import { databases } from "../../../data/databases";
|
||||
|
||||
export default function FieldDetails({ data, tid, index }) {
|
||||
export default function FieldDetails({ data, tid }) {
|
||||
const { t } = useTranslation();
|
||||
const { tables, database } = useDiagram();
|
||||
const { setUndoStack, setRedoStack } = useUndoRedo();
|
||||
const { updateField, deleteField } = useDiagram();
|
||||
const [editField, setEditField] = useState({});
|
||||
const table = useMemo(() => tables.find((t) => t.id === tid), [tables, tid]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
@@ -29,7 +30,7 @@ export default function FieldDetails({ data, tid, index }) {
|
||||
placeholder={t("default_value")}
|
||||
value={data.default}
|
||||
disabled={dbToTypes[database][data.type].noDefault || data.increment}
|
||||
onChange={(value) => updateField(tid, index, { default: value })}
|
||||
onChange={(value) => updateField(tid, data.id, { default: value })}
|
||||
onFocus={(e) => setEditField({ default: e.target.value })}
|
||||
onBlur={(e) => {
|
||||
if (e.target.value === editField.default) return;
|
||||
@@ -40,11 +41,11 @@ export default function FieldDetails({ data, tid, index }) {
|
||||
element: ObjectType.TABLE,
|
||||
component: "field",
|
||||
tid: tid,
|
||||
fid: index,
|
||||
fid: data.id,
|
||||
undo: editField,
|
||||
redo: { default: e.target.value },
|
||||
message: t("edit_table", {
|
||||
tableName: tables[tid].name,
|
||||
tableName: table.name,
|
||||
extra: "[field]",
|
||||
}),
|
||||
},
|
||||
@@ -66,7 +67,7 @@ export default function FieldDetails({ data, tid, index }) {
|
||||
addOnBlur
|
||||
className="my-2"
|
||||
placeholder={t("use_for_batch_input")}
|
||||
onChange={(v) => updateField(tid, index, { values: v })}
|
||||
onChange={(v) => updateField(tid, data.id, { values: v })}
|
||||
onFocus={() => setEditField({ values: data.values })}
|
||||
onBlur={() => {
|
||||
if (
|
||||
@@ -80,11 +81,11 @@ export default function FieldDetails({ data, tid, index }) {
|
||||
element: ObjectType.TABLE,
|
||||
component: "field",
|
||||
tid: tid,
|
||||
fid: index,
|
||||
fid: data.id,
|
||||
undo: editField,
|
||||
redo: { values: data.values },
|
||||
message: t("edit_table", {
|
||||
tableName: tables[tid].name,
|
||||
tableName: table.name,
|
||||
extra: "[field]",
|
||||
}),
|
||||
},
|
||||
@@ -101,7 +102,7 @@ export default function FieldDetails({ data, tid, index }) {
|
||||
className="my-2 w-full"
|
||||
placeholder={t("size")}
|
||||
value={data.size}
|
||||
onChange={(value) => updateField(tid, index, { size: value })}
|
||||
onChange={(value) => updateField(tid, data.id, { size: value })}
|
||||
onFocus={(e) => setEditField({ size: e.target.value })}
|
||||
onBlur={(e) => {
|
||||
if (e.target.value === editField.size) return;
|
||||
@@ -112,11 +113,11 @@ export default function FieldDetails({ data, tid, index }) {
|
||||
element: ObjectType.TABLE,
|
||||
component: "field",
|
||||
tid: tid,
|
||||
fid: index,
|
||||
fid: data.id,
|
||||
undo: editField,
|
||||
redo: { size: e.target.value },
|
||||
message: t("edit_table", {
|
||||
tableName: tables[tid].name,
|
||||
tableName: table.name,
|
||||
extra: "[field]",
|
||||
}),
|
||||
},
|
||||
@@ -138,7 +139,7 @@ export default function FieldDetails({ data, tid, index }) {
|
||||
: "error"
|
||||
}
|
||||
value={data.size}
|
||||
onChange={(value) => updateField(tid, index, { size: value })}
|
||||
onChange={(value) => updateField(tid, data.id, { size: value })}
|
||||
onFocus={(e) => setEditField({ size: e.target.value })}
|
||||
onBlur={(e) => {
|
||||
if (e.target.value === editField.size) return;
|
||||
@@ -149,11 +150,11 @@ export default function FieldDetails({ data, tid, index }) {
|
||||
element: ObjectType.TABLE,
|
||||
component: "field",
|
||||
tid: tid,
|
||||
fid: index,
|
||||
fid: data.id,
|
||||
undo: editField,
|
||||
redo: { size: e.target.value },
|
||||
message: t("edit_table", {
|
||||
tableName: tables[tid].name,
|
||||
tableName: table.name,
|
||||
extra: "[field]",
|
||||
}),
|
||||
},
|
||||
@@ -171,7 +172,7 @@ export default function FieldDetails({ data, tid, index }) {
|
||||
placeholder={t("check")}
|
||||
value={data.check}
|
||||
disabled={data.increment}
|
||||
onChange={(value) => updateField(tid, index, { check: value })}
|
||||
onChange={(value) => updateField(tid, data.id, { check: value })}
|
||||
onFocus={(e) => setEditField({ check: e.target.value })}
|
||||
onBlur={(e) => {
|
||||
if (e.target.value === editField.check) return;
|
||||
@@ -182,11 +183,11 @@ export default function FieldDetails({ data, tid, index }) {
|
||||
element: ObjectType.TABLE,
|
||||
component: "field",
|
||||
tid: tid,
|
||||
fid: index,
|
||||
fid: data.id,
|
||||
undo: editField,
|
||||
redo: { check: e.target.value },
|
||||
message: t("edit_table", {
|
||||
tableName: tables[tid].name,
|
||||
tableName: table.name,
|
||||
extra: "[field]",
|
||||
}),
|
||||
},
|
||||
@@ -210,7 +211,7 @@ export default function FieldDetails({ data, tid, index }) {
|
||||
element: ObjectType.TABLE,
|
||||
component: "field",
|
||||
tid: tid,
|
||||
fid: index,
|
||||
fid: data.id,
|
||||
undo: {
|
||||
[checkedValues.target.value]: !checkedValues.target.checked,
|
||||
},
|
||||
@@ -220,7 +221,7 @@ export default function FieldDetails({ data, tid, index }) {
|
||||
},
|
||||
]);
|
||||
setRedoStack([]);
|
||||
updateField(tid, index, {
|
||||
updateField(tid, data.id, {
|
||||
[checkedValues.target.value]: checkedValues.target.checked,
|
||||
});
|
||||
}}
|
||||
@@ -242,7 +243,7 @@ export default function FieldDetails({ data, tid, index }) {
|
||||
element: ObjectType.TABLE,
|
||||
component: "field",
|
||||
tid: tid,
|
||||
fid: index,
|
||||
fid: data.id,
|
||||
undo: {
|
||||
[checkedValues.target.value]: !checkedValues.target.checked,
|
||||
},
|
||||
@@ -250,13 +251,13 @@ export default function FieldDetails({ data, tid, index }) {
|
||||
[checkedValues.target.value]: checkedValues.target.checked,
|
||||
},
|
||||
message: t("edit_table", {
|
||||
tableName: tables[tid].name,
|
||||
tableName: table.name,
|
||||
extra: "[field]",
|
||||
}),
|
||||
},
|
||||
]);
|
||||
setRedoStack([]);
|
||||
updateField(tid, index, {
|
||||
updateField(tid, data.id, {
|
||||
increment: !data.increment,
|
||||
check: data.increment ? data.check : "",
|
||||
});
|
||||
@@ -277,7 +278,7 @@ export default function FieldDetails({ data, tid, index }) {
|
||||
element: ObjectType.TABLE,
|
||||
component: "field",
|
||||
tid: tid,
|
||||
fid: index,
|
||||
fid: data.id,
|
||||
undo: {
|
||||
[checkedValues.target.value]: !checkedValues.target.checked,
|
||||
},
|
||||
@@ -285,13 +286,13 @@ export default function FieldDetails({ data, tid, index }) {
|
||||
[checkedValues.target.value]: checkedValues.target.checked,
|
||||
},
|
||||
message: t("edit_table", {
|
||||
tableName: tables[tid].name,
|
||||
tableName: table.name,
|
||||
extra: "[field]",
|
||||
}),
|
||||
},
|
||||
]);
|
||||
setRedoStack([]);
|
||||
updateField(tid, index, {
|
||||
updateField(tid, data.id, {
|
||||
isArray: checkedValues.target.checked,
|
||||
increment: data.isArray ? data.increment : false,
|
||||
});
|
||||
@@ -314,7 +315,7 @@ export default function FieldDetails({ data, tid, index }) {
|
||||
element: ObjectType.TABLE,
|
||||
component: "field",
|
||||
tid: tid,
|
||||
fid: index,
|
||||
fid: data.id,
|
||||
undo: {
|
||||
[checkedValues.target.value]:
|
||||
!checkedValues.target.checked,
|
||||
@@ -324,13 +325,13 @@ export default function FieldDetails({ data, tid, index }) {
|
||||
checkedValues.target.checked,
|
||||
},
|
||||
message: t("edit_table", {
|
||||
tableName: tables[tid].name,
|
||||
tableName: table.name,
|
||||
extra: "[field]",
|
||||
}),
|
||||
},
|
||||
]);
|
||||
setRedoStack([]);
|
||||
updateField(tid, index, {
|
||||
updateField(tid, data.id, {
|
||||
unsigned: checkedValues.target.checked,
|
||||
});
|
||||
}}
|
||||
@@ -344,7 +345,7 @@ export default function FieldDetails({ data, tid, index }) {
|
||||
value={data.comment}
|
||||
autosize
|
||||
rows={2}
|
||||
onChange={(value) => updateField(tid, index, { comment: value })}
|
||||
onChange={(value) => updateField(tid, data.id, { comment: value })}
|
||||
onFocus={(e) => setEditField({ comment: e.target.value })}
|
||||
onBlur={(e) => {
|
||||
if (e.target.value === editField.comment) return;
|
||||
@@ -355,11 +356,11 @@ export default function FieldDetails({ data, tid, index }) {
|
||||
element: ObjectType.TABLE,
|
||||
component: "field",
|
||||
tid: tid,
|
||||
fid: index,
|
||||
fid: data.id,
|
||||
undo: editField,
|
||||
redo: { comment: e.target.value },
|
||||
message: t("edit_table", {
|
||||
tableName: tables[tid].name,
|
||||
tableName: table.name,
|
||||
extra: "[field]",
|
||||
}),
|
||||
},
|
||||
|
@@ -3,13 +3,14 @@ import { Input, Button, Popover, Checkbox, Select } from "@douyinfe/semi-ui";
|
||||
import { IconMore, IconDeleteStroked } from "@douyinfe/semi-icons";
|
||||
import { useDiagram, useUndoRedo } from "../../../hooks";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useState } from "react";
|
||||
import { useMemo, useState } from "react";
|
||||
|
||||
export default function IndexDetails({ data, fields, iid, tid }) {
|
||||
const { t } = useTranslation();
|
||||
const { tables, updateTable } = useDiagram();
|
||||
const { setUndoStack, setRedoStack } = useUndoRedo();
|
||||
const [editField, setEditField] = useState({});
|
||||
const table = useMemo(() => tables.find((t) => t.id === tid), [tables, tid]);
|
||||
|
||||
return (
|
||||
<div className="flex justify-between items-center mb-2">
|
||||
@@ -36,14 +37,14 @@ export default function IndexDetails({ data, fields, iid, tid }) {
|
||||
fields: [...value],
|
||||
},
|
||||
message: t("edit_table", {
|
||||
tableName: tables[tid].name,
|
||||
tableName: table.name,
|
||||
extra: "[index field]",
|
||||
}),
|
||||
},
|
||||
]);
|
||||
setRedoStack([]);
|
||||
updateTable(tid, {
|
||||
indices: tables[tid].indices.map((index) =>
|
||||
indices: table.indices.map((index) =>
|
||||
index.id === iid
|
||||
? {
|
||||
...index,
|
||||
@@ -69,7 +70,7 @@ export default function IndexDetails({ data, fields, iid, tid }) {
|
||||
}
|
||||
onChange={(value) =>
|
||||
updateTable(tid, {
|
||||
indices: tables[tid].indices.map((index) =>
|
||||
indices: table.indices.map((index) =>
|
||||
index.id === iid
|
||||
? {
|
||||
...index,
|
||||
@@ -92,7 +93,7 @@ export default function IndexDetails({ data, fields, iid, tid }) {
|
||||
undo: editField,
|
||||
redo: { name: e.target.value },
|
||||
message: t("edit_table", {
|
||||
tableName: tables[tid].name,
|
||||
tableName: table.name,
|
||||
extra: "[index]",
|
||||
}),
|
||||
},
|
||||
@@ -123,14 +124,14 @@ export default function IndexDetails({ data, fields, iid, tid }) {
|
||||
checkedValues.target.checked,
|
||||
},
|
||||
message: t("edit_table", {
|
||||
tableName: tables[tid].name,
|
||||
tableName: table.name,
|
||||
extra: "[index field]",
|
||||
}),
|
||||
},
|
||||
]);
|
||||
setRedoStack([]);
|
||||
updateTable(tid, {
|
||||
indices: tables[tid].indices.map((index) =>
|
||||
indices: table.indices.map((index) =>
|
||||
index.id === iid
|
||||
? {
|
||||
...index,
|
||||
@@ -157,14 +158,14 @@ export default function IndexDetails({ data, fields, iid, tid }) {
|
||||
tid: tid,
|
||||
data: data,
|
||||
message: t("edit_table", {
|
||||
tableName: tables[tid].name,
|
||||
tableName: table.name,
|
||||
extra: "[delete index]",
|
||||
}),
|
||||
},
|
||||
]);
|
||||
setRedoStack([]);
|
||||
updateTable(tid, {
|
||||
indices: tables[tid].indices
|
||||
indices: table.indices
|
||||
.filter((e) => e.id !== iid)
|
||||
.map((e, j) => ({
|
||||
...e,
|
||||
|
@@ -11,7 +11,7 @@ export default function SearchBar({ tables }) {
|
||||
|
||||
const treeData = useMemo(() => {
|
||||
return tables.map(({ id, name: parentName, fields }, i) => {
|
||||
const children = fields.map(({ name }, j) => ({
|
||||
const children = fields?.map(({ name }, j) => ({
|
||||
tableId: id,
|
||||
id: `${j}`,
|
||||
label: name,
|
||||
|
@@ -1,11 +1,12 @@
|
||||
import { useMemo, useState } from "react";
|
||||
import { Action, ObjectType } from "../../../data/constants";
|
||||
import { Row, Col, Input, Button, Popover, Select } from "@douyinfe/semi-ui";
|
||||
import { Input, Button, Popover, Select } from "@douyinfe/semi-ui";
|
||||
import { IconMore, IconKeyStroked } from "@douyinfe/semi-icons";
|
||||
import { useEnums, useDiagram, useTypes, useUndoRedo } from "../../../hooks";
|
||||
import { useState } from "react";
|
||||
import FieldDetails from "./FieldDetails";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { dbToTypes } from "../../../data/datatypes";
|
||||
import { DragHandle } from "../../SortableList/DragHandle";
|
||||
import FieldDetails from "./FieldDetails";
|
||||
|
||||
export default function TableField({ data, tid, index }) {
|
||||
const { updateField } = useDiagram();
|
||||
@@ -15,16 +16,18 @@ export default function TableField({ data, tid, index }) {
|
||||
const { t } = useTranslation();
|
||||
const { setUndoStack, setRedoStack } = useUndoRedo();
|
||||
const [editField, setEditField] = useState({});
|
||||
const table = useMemo(() => tables.find((t) => t.id === tid), [tables, tid]);
|
||||
|
||||
return (
|
||||
<Row gutter={6} className="hover-1 my-2">
|
||||
<Col span={7}>
|
||||
<div className="hover-1 my-2 flex gap-2 items-center">
|
||||
<DragHandle id={data.id} />
|
||||
<div className="min-w-20 flex-1/3">
|
||||
<Input
|
||||
id={`scroll_table_${tid}_input_${index}`}
|
||||
value={data.name}
|
||||
id={`scroll_table_${tid}_input_${index}`}
|
||||
validateStatus={data.name.trim() === "" ? "error" : "default"}
|
||||
placeholder="Name"
|
||||
onChange={(value) => updateField(tid, index, { name: value })}
|
||||
onChange={(value) => updateField(tid, data.id, { name: value })}
|
||||
onFocus={(e) => setEditField({ name: e.target.value })}
|
||||
onBlur={(e) => {
|
||||
if (e.target.value === editField.name) return;
|
||||
@@ -35,11 +38,11 @@ export default function TableField({ data, tid, index }) {
|
||||
element: ObjectType.TABLE,
|
||||
component: "field",
|
||||
tid: tid,
|
||||
fid: index,
|
||||
fid: data.id,
|
||||
undo: editField,
|
||||
redo: { name: e.target.value },
|
||||
message: t("edit_table", {
|
||||
tableName: tables[tid].name,
|
||||
tableName: table.name,
|
||||
extra: "[field]",
|
||||
}),
|
||||
},
|
||||
@@ -47,8 +50,8 @@ export default function TableField({ data, tid, index }) {
|
||||
setRedoStack([]);
|
||||
}}
|
||||
/>
|
||||
</Col>
|
||||
<Col span={8}>
|
||||
</div>
|
||||
<div className="min-w-24 flex-1/3">
|
||||
<Select
|
||||
className="w-full"
|
||||
optionList={[
|
||||
@@ -78,11 +81,11 @@ export default function TableField({ data, tid, index }) {
|
||||
element: ObjectType.TABLE,
|
||||
component: "field",
|
||||
tid: tid,
|
||||
fid: index,
|
||||
fid: data.id,
|
||||
undo: { type: data.type },
|
||||
redo: { type: value },
|
||||
message: t("edit_table", {
|
||||
tableName: tables[tid].name,
|
||||
tableName: table.name,
|
||||
extra: "[field]",
|
||||
}),
|
||||
},
|
||||
@@ -92,7 +95,7 @@ export default function TableField({ data, tid, index }) {
|
||||
data.increment && !!dbToTypes[database][value].canIncrement;
|
||||
|
||||
if (value === "ENUM" || value === "SET") {
|
||||
updateField(tid, index, {
|
||||
updateField(tid, data.id, {
|
||||
type: value,
|
||||
default: "",
|
||||
values: data.values ? [...data.values] : [],
|
||||
@@ -102,13 +105,13 @@ export default function TableField({ data, tid, index }) {
|
||||
dbToTypes[database][value].isSized ||
|
||||
dbToTypes[database][value].hasPrecision
|
||||
) {
|
||||
updateField(tid, index, {
|
||||
updateField(tid, data.id, {
|
||||
type: value,
|
||||
size: dbToTypes[database][value].defaultSize,
|
||||
increment: incr,
|
||||
});
|
||||
} else if (!dbToTypes[database][value].hasDefault || incr) {
|
||||
updateField(tid, index, {
|
||||
updateField(tid, data.id, {
|
||||
type: value,
|
||||
increment: incr,
|
||||
default: "",
|
||||
@@ -116,13 +119,13 @@ export default function TableField({ data, tid, index }) {
|
||||
values: [],
|
||||
});
|
||||
} else if (dbToTypes[database][value].hasCheck) {
|
||||
updateField(tid, index, {
|
||||
updateField(tid, data.id, {
|
||||
type: value,
|
||||
check: "",
|
||||
increment: incr,
|
||||
});
|
||||
} else {
|
||||
updateField(tid, index, {
|
||||
updateField(tid, data.id, {
|
||||
type: value,
|
||||
increment: incr,
|
||||
size: "",
|
||||
@@ -131,10 +134,9 @@ export default function TableField({ data, tid, index }) {
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</Col>
|
||||
<Col span={3}>
|
||||
</div>
|
||||
<div>
|
||||
<Button
|
||||
|
||||
type={data.notNull ? "tertiary" : "primary"}
|
||||
title={t("nullable")}
|
||||
theme={data.notNull ? "light" : "solid"}
|
||||
@@ -146,23 +148,23 @@ export default function TableField({ data, tid, index }) {
|
||||
element: ObjectType.TABLE,
|
||||
component: "field",
|
||||
tid: tid,
|
||||
fid: index,
|
||||
fid: data.id,
|
||||
undo: { notNull: data.notNull },
|
||||
redo: { notNull: !data.notNull },
|
||||
message: t("edit_table", {
|
||||
tableName: tables[tid].name,
|
||||
tableName: table.name,
|
||||
extra: "[field]",
|
||||
}),
|
||||
},
|
||||
]);
|
||||
setRedoStack([]);
|
||||
updateField(tid, index, { notNull: !data.notNull });
|
||||
updateField(tid, data.id, { notNull: !data.notNull });
|
||||
}}
|
||||
>
|
||||
?
|
||||
</Button>
|
||||
</Col>
|
||||
<Col span={3}>
|
||||
</div>
|
||||
<div>
|
||||
<Button
|
||||
type={data.primary ? "primary" : "tertiary"}
|
||||
title={t("primary")}
|
||||
@@ -175,26 +177,26 @@ export default function TableField({ data, tid, index }) {
|
||||
element: ObjectType.TABLE,
|
||||
component: "field",
|
||||
tid: tid,
|
||||
fid: index,
|
||||
fid: data.id,
|
||||
undo: { primary: data.primary },
|
||||
redo: { primary: !data.primary },
|
||||
message: t("edit_table", {
|
||||
tableName: tables[tid].name,
|
||||
tableName: table.name,
|
||||
extra: "[field]",
|
||||
}),
|
||||
},
|
||||
]);
|
||||
setRedoStack([]);
|
||||
updateField(tid, index, { primary: !data.primary });
|
||||
updateField(tid, data.id, { primary: !data.primary });
|
||||
}}
|
||||
icon={<IconKeyStroked />}
|
||||
/>
|
||||
</Col>
|
||||
<Col span={3}>
|
||||
</div>
|
||||
<div>
|
||||
<Popover
|
||||
content={
|
||||
<div className="px-1 w-[240px] popover-theme">
|
||||
<FieldDetails data={data} index={index} tid={tid} />
|
||||
<FieldDetails data={data} tid={tid} />
|
||||
</div>
|
||||
}
|
||||
trigger="click"
|
||||
@@ -203,7 +205,7 @@ export default function TableField({ data, tid, index }) {
|
||||
>
|
||||
<Button type="tertiary" icon={<IconMore />} />
|
||||
</Popover>
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@@ -8,24 +8,21 @@ import {
|
||||
ColorPicker,
|
||||
} from "@douyinfe/semi-ui";
|
||||
import { IconDeleteStroked } from "@douyinfe/semi-icons";
|
||||
import { useDiagram, useUndoRedo } from "../../../hooks";
|
||||
import { Action, ObjectType } from "../../../data/constants";
|
||||
import { useDiagram, useSaveState, useUndoRedo } from "../../../hooks";
|
||||
import { Action, ObjectType, State } from "../../../data/constants";
|
||||
import TableField from "./TableField";
|
||||
import IndexDetails from "./IndexDetails";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { dbToTypes } from "../../../data/datatypes";
|
||||
import { SortableList } from "../../SortableList/SortableList";
|
||||
import { nanoid } from "nanoid";
|
||||
|
||||
export default function TableInfo({ data }) {
|
||||
const { t } = useTranslation();
|
||||
const [indexActiveKey, setIndexActiveKey] = useState("");
|
||||
const { deleteTable, updateTable, updateField, setRelationships, database } =
|
||||
useDiagram();
|
||||
const { deleteTable, updateTable, setTables } = useDiagram();
|
||||
const { setUndoStack, setRedoStack } = useUndoRedo();
|
||||
const { setSaveState } = useSaveState();
|
||||
const [editField, setEditField] = useState({});
|
||||
const [drag, setDrag] = useState({
|
||||
draggingElementIndex: null,
|
||||
draggingOverIndexList: [],
|
||||
});
|
||||
|
||||
return (
|
||||
<div>
|
||||
@@ -59,98 +56,21 @@ export default function TableInfo({ data }) {
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{data.fields.map((f, j) => (
|
||||
<div
|
||||
key={"field_" + j}
|
||||
className={`cursor-pointer ${drag.draggingOverIndexList.includes(j) ? "opacity-25" : ""}`}
|
||||
style={{ direction: "ltr" }}
|
||||
draggable
|
||||
onDragStart={() => {
|
||||
setDrag((prev) => ({ ...prev, draggingElementIndex: j }));
|
||||
}}
|
||||
onDragLeave={() => {
|
||||
setDrag((prev) => ({
|
||||
...prev,
|
||||
draggingOverIndexList: prev.draggingOverIndexList.filter(
|
||||
(index) => index !== j,
|
||||
),
|
||||
}));
|
||||
}}
|
||||
onDragOver={(e) => {
|
||||
e.preventDefault();
|
||||
if (drag.draggingElementIndex != null) {
|
||||
if (j !== drag.draggingElementIndex) {
|
||||
setDrag((prev) => {
|
||||
if (prev.draggingOverIndexList.includes(j)) {
|
||||
return prev;
|
||||
}
|
||||
|
||||
return {
|
||||
...prev,
|
||||
draggingOverIndexList: prev.draggingOverIndexList.concat(j),
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}}
|
||||
onDrop={(e) => {
|
||||
e.preventDefault();
|
||||
const index = drag.draggingElementIndex;
|
||||
setDrag({ draggingElementIndex: null, draggingOverIndexList: [] });
|
||||
if (index == null || index === j) {
|
||||
return;
|
||||
}
|
||||
|
||||
const a = data.fields[index];
|
||||
const b = data.fields[j];
|
||||
|
||||
updateField(data.id, index, {
|
||||
...b,
|
||||
...(!dbToTypes[database][b.type].isSized && { size: "" }),
|
||||
...(!dbToTypes[database][b.type].hasCheck && { check: "" }),
|
||||
...(dbToTypes[database][b.type].noDefault && { default: "" }),
|
||||
id: index,
|
||||
});
|
||||
updateField(data.id, j, {
|
||||
...a,
|
||||
...(!dbToTypes[database][a.type].isSized && { size: "" }),
|
||||
...(!dbToTypes[database][a.type].hasCheck && { check: "" }),
|
||||
...(!dbToTypes[database][a.type].noDefault && { default: "" }),
|
||||
id: j,
|
||||
});
|
||||
|
||||
setRelationships((prev) =>
|
||||
prev.map((e) => {
|
||||
if (e.startTableId === data.id) {
|
||||
if (e.startFieldId === index) {
|
||||
return { ...e, startFieldId: j };
|
||||
}
|
||||
if (e.startFieldId === j) {
|
||||
return { ...e, startFieldId: index };
|
||||
}
|
||||
}
|
||||
if (e.endTableId === data.id) {
|
||||
if (e.endFieldId === index) {
|
||||
return { ...e, endFieldId: j };
|
||||
}
|
||||
if (e.endFieldId === j) {
|
||||
return { ...e, endFieldId: index };
|
||||
}
|
||||
}
|
||||
return e;
|
||||
}),
|
||||
<SortableList
|
||||
items={data.fields}
|
||||
keyPrefix={`table-${data.id}`}
|
||||
onChange={(newFields) => {
|
||||
setTables((prev) => {
|
||||
return prev.map((t) =>
|
||||
t.id === data.id ? { ...t, fields: newFields } : t,
|
||||
);
|
||||
}}
|
||||
onDragEnd={(e) => {
|
||||
e.preventDefault();
|
||||
setDrag({ draggingElementIndex: null, draggingOverIndexList: [] });
|
||||
}}
|
||||
>
|
||||
<TableField data={f} tid={data.id} index={j} />
|
||||
</div>
|
||||
))}
|
||||
});
|
||||
}}
|
||||
afterChange={() => setSaveState(State.SAVING)}
|
||||
renderItem={(item, i) => (
|
||||
<TableField data={item} tid={data.id} index={i} />
|
||||
)}
|
||||
/>
|
||||
{data.indices.length > 0 && (
|
||||
<Card
|
||||
bodyStyle={{ padding: "4px" }}
|
||||
@@ -281,6 +201,7 @@ export default function TableInfo({ data }) {
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => {
|
||||
const id = nanoid();
|
||||
setUndoStack((prev) => [
|
||||
...prev,
|
||||
{
|
||||
@@ -288,6 +209,7 @@ export default function TableInfo({ data }) {
|
||||
element: ObjectType.TABLE,
|
||||
component: "field_add",
|
||||
tid: data.id,
|
||||
fid: id,
|
||||
message: t("edit_table", {
|
||||
tableName: data.name,
|
||||
extra: "[add field]",
|
||||
@@ -299,6 +221,7 @@ export default function TableInfo({ data }) {
|
||||
fields: [
|
||||
...data.fields,
|
||||
{
|
||||
id,
|
||||
name: "",
|
||||
type: "",
|
||||
default: "",
|
||||
@@ -308,7 +231,6 @@ export default function TableInfo({ data }) {
|
||||
notNull: false,
|
||||
increment: false,
|
||||
comment: "",
|
||||
id: data.fields.length,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
@@ -1,16 +1,19 @@
|
||||
import { Collapse, Button } from "@douyinfe/semi-ui";
|
||||
import { IconPlus } from "@douyinfe/semi-icons";
|
||||
import { useSelect, useDiagram } from "../../../hooks";
|
||||
import { ObjectType } from "../../../data/constants";
|
||||
import { useSelect, useDiagram, useSaveState } from "../../../hooks";
|
||||
import { ObjectType, State } from "../../../data/constants";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { DragHandle } from "../../SortableList/DragHandle";
|
||||
import { SortableList } from "../../SortableList/SortableList";
|
||||
import SearchBar from "./SearchBar";
|
||||
import Empty from "../Empty";
|
||||
import TableInfo from "./TableInfo";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
export default function TablesTab() {
|
||||
const { tables, addTable } = useDiagram();
|
||||
const { tables, addTable, setTables } = useDiagram();
|
||||
const { selectedElement, setSelectedElement } = useSelect();
|
||||
const { t } = useTranslation();
|
||||
const { setSaveState } = useSaveState();
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -37,35 +40,48 @@ export default function TablesTab() {
|
||||
setSelectedElement((prev) => ({
|
||||
...prev,
|
||||
open: true,
|
||||
id: parseInt(k),
|
||||
id: k[0],
|
||||
element: ObjectType.TABLE,
|
||||
}))
|
||||
}
|
||||
accordion
|
||||
>
|
||||
{tables.map((t) => (
|
||||
<div id={`scroll_table_${t.id}`} key={t.id}>
|
||||
<Collapse.Panel
|
||||
className="relative"
|
||||
header={
|
||||
<>
|
||||
<div className="overflow-hidden text-ellipsis whitespace-nowrap">
|
||||
{t.name}
|
||||
</div>
|
||||
<div
|
||||
className="w-1 h-full absolute top-0 left-0 bottom-0"
|
||||
style={{ backgroundColor: t.color }}
|
||||
/>
|
||||
</>
|
||||
}
|
||||
itemKey={`${t.id}`}
|
||||
>
|
||||
<TableInfo data={t} />
|
||||
</Collapse.Panel>
|
||||
</div>
|
||||
))}
|
||||
<SortableList
|
||||
keyPrefix="tables-tab"
|
||||
items={tables}
|
||||
onChange={(newTables) => setTables(newTables)}
|
||||
afterChange={() => setSaveState(State.SAVING)}
|
||||
renderItem={(item) => <TableListItem table={item} />}
|
||||
/>
|
||||
</Collapse>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function TableListItem({ table }) {
|
||||
return (
|
||||
<div id={`scroll_table_${table.id}`}>
|
||||
<Collapse.Panel
|
||||
className="relative"
|
||||
header={
|
||||
<>
|
||||
<div className="flex items-center gap-2">
|
||||
<DragHandle id={table.id} />
|
||||
<div className="overflow-hidden text-ellipsis whitespace-nowrap">
|
||||
{table.name}
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="w-1 h-full absolute top-0 left-0 bottom-0"
|
||||
style={{ backgroundColor: table.color }}
|
||||
/>
|
||||
</>
|
||||
}
|
||||
itemKey={`${table.id}`}
|
||||
>
|
||||
<TableInfo data={table} />
|
||||
</Collapse.Panel>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@@ -40,10 +40,12 @@ export default function TypeInfo({ index, data }) {
|
||||
className="ms-2"
|
||||
onChange={(value) => {
|
||||
updateType(index, { name: value });
|
||||
tables.forEach((table, i) => {
|
||||
table.fields.forEach((field, j) => {
|
||||
tables.forEach((table) => {
|
||||
table.fields.forEach((field) => {
|
||||
if (field.type.toLowerCase() === data.name.toLowerCase()) {
|
||||
updateField(i, j, { type: value.toUpperCase() });
|
||||
updateField(table.id, field.id, {
|
||||
type: value.toUpperCase(),
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
@@ -121,7 +121,8 @@ function Relationship({ relationship, tables }) {
|
||||
<path
|
||||
ref={pathRef}
|
||||
d={calcPath({
|
||||
...relationship,
|
||||
startFieldIndex: relationship.startFieldId,
|
||||
endFieldIndex: relationship.endFieldId,
|
||||
startTable: {
|
||||
x: tables[relationship.startTableId].x,
|
||||
y: tables[relationship.startTableId].y,
|
||||
|
14
src/components/SortableList/DragHandle.jsx
Normal file
14
src/components/SortableList/DragHandle.jsx
Normal file
@@ -0,0 +1,14 @@
|
||||
import { IconHandle } from "@douyinfe/semi-icons";
|
||||
import { useSortable } from "@dnd-kit/sortable";
|
||||
|
||||
export function DragHandle({ id }) {
|
||||
const { listeners } = useSortable({ id });
|
||||
return (
|
||||
<div
|
||||
className="flex cursor-move items-center justify-center opacity-50 mt-0.5"
|
||||
{...listeners}
|
||||
>
|
||||
<IconHandle />
|
||||
</div>
|
||||
);
|
||||
}
|
18
src/components/SortableList/SortableItem.jsx
Normal file
18
src/components/SortableList/SortableItem.jsx
Normal file
@@ -0,0 +1,18 @@
|
||||
import { useSortable } from "@dnd-kit/sortable";
|
||||
import { CSS } from "@dnd-kit/utilities";
|
||||
|
||||
export function SortableItem({ children, id }) {
|
||||
const { attributes, setNodeRef, transform, transition } = useSortable({
|
||||
id,
|
||||
});
|
||||
const style = {
|
||||
transform: CSS.Translate.toString(transform),
|
||||
transition,
|
||||
};
|
||||
|
||||
return (
|
||||
<div ref={setNodeRef} style={style} {...attributes}>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
54
src/components/SortableList/SortableList.jsx
Normal file
54
src/components/SortableList/SortableList.jsx
Normal file
@@ -0,0 +1,54 @@
|
||||
import {
|
||||
closestCenter,
|
||||
DndContext,
|
||||
PointerSensor,
|
||||
useSensor,
|
||||
useSensors,
|
||||
} from "@dnd-kit/core";
|
||||
import {
|
||||
SortableContext,
|
||||
arrayMove,
|
||||
verticalListSortingStrategy,
|
||||
} from "@dnd-kit/sortable";
|
||||
import { SortableItem } from "./SortableItem";
|
||||
|
||||
export function SortableList({
|
||||
items,
|
||||
onChange,
|
||||
afterChange,
|
||||
renderItem,
|
||||
keyPrefix,
|
||||
}) {
|
||||
const sensors = useSensors(useSensor(PointerSensor));
|
||||
|
||||
const handleDragEnd = (event) => {
|
||||
const { active, over } = event;
|
||||
|
||||
if (active.id !== over.id) {
|
||||
const oldIndex = items.findIndex((item) => item.id === active.id);
|
||||
const newIndex = items.findIndex((item) => item.id === over.id);
|
||||
const newItems = arrayMove(items, oldIndex, newIndex);
|
||||
onChange(newItems);
|
||||
afterChange();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<DndContext
|
||||
sensors={sensors}
|
||||
collisionDetection={closestCenter}
|
||||
onDragEnd={handleDragEnd}
|
||||
>
|
||||
<SortableContext items={items} strategy={verticalListSortingStrategy}>
|
||||
{items.map((item, i) => (
|
||||
<SortableItem
|
||||
id={item.id}
|
||||
key={`${keyPrefix}-sortable-item-${item.id}`}
|
||||
>
|
||||
{renderItem(item, i)}
|
||||
</SortableItem>
|
||||
))}
|
||||
</SortableContext>
|
||||
</DndContext>
|
||||
);
|
||||
}
|
@@ -1,5 +1,4 @@
|
||||
import { tableFieldHeight, tableHeaderHeight } from "../data/constants";
|
||||
import { calcPath } from "../utils/calcPath";
|
||||
|
||||
export default function Thumbnail({ diagram, i, zoom, theme }) {
|
||||
return (
|
||||
@@ -58,25 +57,6 @@ export default function Thumbnail({ diagram, i, zoom, theme }) {
|
||||
</div>
|
||||
</foreignObject>
|
||||
))}
|
||||
{diagram.relationships?.map((r, i) => (
|
||||
<path
|
||||
key={i}
|
||||
d={calcPath({
|
||||
...r,
|
||||
startTable: {
|
||||
x: diagram.tables[r.startTableId].x,
|
||||
y: diagram.tables[r.startTableId].y - tableFieldHeight / 2,
|
||||
},
|
||||
endTable: {
|
||||
x: diagram.tables[r.endTableId].x,
|
||||
y: diagram.tables[r.endTableId].y - tableFieldHeight / 2,
|
||||
},
|
||||
})}
|
||||
fill="none"
|
||||
strokeWidth={2}
|
||||
stroke="gray"
|
||||
/>
|
||||
))}
|
||||
{diagram.tables?.map((table, i) => {
|
||||
const height =
|
||||
table.fields.length * tableFieldHeight + tableHeaderHeight + 7;
|
||||
|
@@ -28,13 +28,15 @@ import { get } from "../api/gists";
|
||||
|
||||
export const IdContext = createContext({ gistId: "", setGistId: () => {} });
|
||||
|
||||
const SIDEPANEL_MIN_WIDTH = 384;
|
||||
|
||||
export default function WorkSpace() {
|
||||
const [id, setId] = useState(0);
|
||||
const [gistId, setGistId] = useState("");
|
||||
const [loadedFromGistId, setLoadedFromGistId] = useState("");
|
||||
const [title, setTitle] = useState("Untitled Diagram");
|
||||
const [resize, setResize] = useState(false);
|
||||
const [width, setWidth] = useState(340);
|
||||
const [width, setWidth] = useState(SIDEPANEL_MIN_WIDTH);
|
||||
const [lastSaved, setLastSaved] = useState("");
|
||||
const [showSelectDbModal, setShowSelectDbModal] = useState(false);
|
||||
const [selectedDb, setSelectedDb] = useState("");
|
||||
@@ -61,7 +63,7 @@ export default function WorkSpace() {
|
||||
const handleResize = (e) => {
|
||||
if (!resize) return;
|
||||
const w = isRtl(i18n.language) ? window.innerWidth - e.clientX : e.clientX;
|
||||
if (w > 340) setWidth(w);
|
||||
if (w > SIDEPANEL_MIN_WIDTH) setWidth(w);
|
||||
};
|
||||
|
||||
const save = useCallback(async () => {
|
||||
|
@@ -3,6 +3,7 @@ import { Action, DB, ObjectType, defaultBlue } from "../data/constants";
|
||||
import { useTransform, useUndoRedo, useSelect } from "../hooks";
|
||||
import { Toast } from "@douyinfe/semi-ui";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { nanoid } from "nanoid";
|
||||
|
||||
export const DiagramContext = createContext(null);
|
||||
|
||||
@@ -16,17 +17,18 @@ export default function DiagramContextProvider({ children }) {
|
||||
const { selectedElement, setSelectedElement } = useSelect();
|
||||
|
||||
const addTable = (data, addToHistory = true) => {
|
||||
const id = nanoid();
|
||||
if (data) {
|
||||
setTables((prev) => {
|
||||
const temp = prev.slice();
|
||||
temp.splice(data.id, 0, data);
|
||||
return temp.map((t, i) => ({ ...t, id: i }));
|
||||
temp.splice(data.index, 0, data);
|
||||
return temp;
|
||||
});
|
||||
} else {
|
||||
setTables((prev) => [
|
||||
...prev,
|
||||
{
|
||||
id: prev.length,
|
||||
id,
|
||||
name: `table_${prev.length}`,
|
||||
x: transform.pan.x,
|
||||
y: transform.pan.y,
|
||||
@@ -41,13 +43,12 @@ export default function DiagramContextProvider({ children }) {
|
||||
notNull: true,
|
||||
increment: true,
|
||||
comment: "",
|
||||
id: 0,
|
||||
id: nanoid(),
|
||||
},
|
||||
],
|
||||
comment: "",
|
||||
indices: [],
|
||||
color: defaultBlue,
|
||||
key: Date.now(),
|
||||
},
|
||||
]);
|
||||
}
|
||||
@@ -55,6 +56,7 @@ export default function DiagramContextProvider({ children }) {
|
||||
setUndoStack((prev) => [
|
||||
...prev,
|
||||
{
|
||||
id: data ? data.id : id,
|
||||
action: Action.ADD,
|
||||
element: ObjectType.TABLE,
|
||||
message: t("add_table"),
|
||||
@@ -66,48 +68,39 @@ export default function DiagramContextProvider({ children }) {
|
||||
|
||||
const deleteTable = (id, addToHistory = true) => {
|
||||
if (addToHistory) {
|
||||
Toast.success(t("table_deleted"));
|
||||
const rels = relationships.reduce((acc, r) => {
|
||||
if (r.startTableId === id || r.endTableId === id) {
|
||||
acc.push(r);
|
||||
}
|
||||
return acc;
|
||||
}, []);
|
||||
const deletedTable = tables.find((t) => t.id === id);
|
||||
const deletedTableIndex = tables.findIndex((t) => t.id === id);
|
||||
setUndoStack((prev) => [
|
||||
...prev,
|
||||
{
|
||||
action: Action.DELETE,
|
||||
element: ObjectType.TABLE,
|
||||
data: { table: tables[id], relationship: rels },
|
||||
message: t("delete_table", { tableName: tables[id].name }),
|
||||
data: {
|
||||
table: deletedTable,
|
||||
relationship: rels,
|
||||
index: deletedTableIndex,
|
||||
},
|
||||
message: t("delete_table", { tableName: deletedTable.name }),
|
||||
},
|
||||
]);
|
||||
setRedoStack([]);
|
||||
Toast.success(t("table_deleted"));
|
||||
}
|
||||
setRelationships((prevR) => {
|
||||
return prevR
|
||||
.filter((e) => !(e.startTableId === id || e.endTableId === id))
|
||||
.map((e, i) => {
|
||||
const newR = { ...e };
|
||||
|
||||
if (e.startTableId > id) {
|
||||
newR.startTableId = e.startTableId - 1;
|
||||
}
|
||||
if (e.endTableId > id) {
|
||||
newR.endTableId = e.endTableId - 1;
|
||||
}
|
||||
|
||||
return { ...newR, id: i };
|
||||
});
|
||||
});
|
||||
setTables((prev) => {
|
||||
return prev.filter((e) => e.id !== id).map((e, i) => ({ ...e, id: i }));
|
||||
});
|
||||
setRelationships((prevR) =>
|
||||
prevR.filter((e) => !(e.startTableId === id || e.endTableId === id)),
|
||||
);
|
||||
setTables((prev) => prev.filter((e) => e.id !== id));
|
||||
if (id === selectedElement.id) {
|
||||
setSelectedElement((prev) => ({
|
||||
...prev,
|
||||
element: ObjectType.NONE,
|
||||
id: -1,
|
||||
id: null,
|
||||
open: false,
|
||||
}));
|
||||
}
|
||||
@@ -121,12 +114,12 @@ export default function DiagramContextProvider({ children }) {
|
||||
|
||||
const updateField = (tid, fid, updatedValues) => {
|
||||
setTables((prev) =>
|
||||
prev.map((table, i) => {
|
||||
if (tid === i) {
|
||||
prev.map((table) => {
|
||||
if (tid === table.id) {
|
||||
return {
|
||||
...table,
|
||||
fields: table.fields.map((field, j) =>
|
||||
fid === j ? { ...field, ...updatedValues } : field,
|
||||
fields: table.fields.map((field) =>
|
||||
fid === field.id ? { ...field, ...updatedValues } : field,
|
||||
),
|
||||
};
|
||||
}
|
||||
@@ -136,6 +129,7 @@ export default function DiagramContextProvider({ children }) {
|
||||
};
|
||||
|
||||
const deleteField = (field, tid, addToHistory = true) => {
|
||||
const { fields, name } = tables.find((t) => t.id === tid);
|
||||
if (addToHistory) {
|
||||
const rels = relationships.reduce((acc, r) => {
|
||||
if (
|
||||
@@ -155,50 +149,28 @@ export default function DiagramContextProvider({ children }) {
|
||||
tid: tid,
|
||||
data: {
|
||||
field: field,
|
||||
index: fields.findIndex((f) => f.id === field.id),
|
||||
relationship: rels,
|
||||
},
|
||||
message: t("edit_table", {
|
||||
tableName: tables[tid].name,
|
||||
tableName: name,
|
||||
extra: "[delete field]",
|
||||
}),
|
||||
},
|
||||
]);
|
||||
setRedoStack([]);
|
||||
}
|
||||
setRelationships((prev) => {
|
||||
const temp = prev
|
||||
.filter(
|
||||
(e) =>
|
||||
!(
|
||||
(e.startTableId === tid && e.startFieldId === field.id) ||
|
||||
(e.endTableId === tid && e.endFieldId === field.id)
|
||||
),
|
||||
)
|
||||
.map((e, i) => {
|
||||
if (e.startTableId === tid && e.startFieldId > field.id) {
|
||||
return {
|
||||
...e,
|
||||
startFieldId: e.startFieldId - 1,
|
||||
id: i,
|
||||
};
|
||||
}
|
||||
if (e.endTableId === tid && e.endFieldId > field.id) {
|
||||
return {
|
||||
...e,
|
||||
endFieldId: e.endFieldId - 1,
|
||||
id: i,
|
||||
};
|
||||
}
|
||||
return { ...e, id: i };
|
||||
});
|
||||
return temp;
|
||||
});
|
||||
setRelationships((prev) =>
|
||||
prev.filter(
|
||||
(e) =>
|
||||
!(
|
||||
(e.startTableId === tid && e.startFieldId === field.id) ||
|
||||
(e.endTableId === tid && e.endFieldId === field.id)
|
||||
),
|
||||
),
|
||||
);
|
||||
updateTable(tid, {
|
||||
fields: tables[tid].fields
|
||||
.filter((e) => e.id !== field.id)
|
||||
.map((t, i) => {
|
||||
return { ...t, id: i };
|
||||
}),
|
||||
fields: fields.filter((e) => e.id !== field.id),
|
||||
});
|
||||
};
|
||||
|
||||
|
@@ -1,17 +1,34 @@
|
||||
import { tableFieldHeight, tableHeaderHeight } from "../data/constants";
|
||||
|
||||
/**
|
||||
* Generates an SVG path string to visually represent a relationship between two fields.
|
||||
*
|
||||
* @param {{
|
||||
* startTable: { x: number, y: number },
|
||||
* endTable: { x: number, y: number },
|
||||
* startFieldIndex: number,
|
||||
* endFieldIndex: number
|
||||
* }} r - Relationship data.
|
||||
* @param {number} tableWidth - Width of each table (used to calculate horizontal offsets).
|
||||
* @param {number} zoom - Zoom level (used to scale vertical spacing).
|
||||
* @returns {string} SVG path "d" attribute string.
|
||||
*/
|
||||
export function calcPath(r, tableWidth = 200, zoom = 1) {
|
||||
if (!r) {
|
||||
return "";
|
||||
}
|
||||
|
||||
const width = tableWidth * zoom;
|
||||
let x1 = r.startTable.x;
|
||||
let y1 =
|
||||
r.startTable.y +
|
||||
r.startFieldId * tableFieldHeight +
|
||||
r.startFieldIndex * tableFieldHeight +
|
||||
tableHeaderHeight +
|
||||
tableFieldHeight / 2;
|
||||
let x2 = r.endTable.x;
|
||||
let y2 =
|
||||
r.endTable.y +
|
||||
r.endFieldId * tableFieldHeight +
|
||||
r.endFieldIndex * tableFieldHeight +
|
||||
tableHeaderHeight +
|
||||
tableFieldHeight / 2;
|
||||
|
||||
|
@@ -52,6 +52,22 @@ function cardinality(rel) {
|
||||
}
|
||||
|
||||
export function toDBML(diagram) {
|
||||
const generateRelString = (rel) => {
|
||||
const { fields: startTableFields, name: startTableName } =
|
||||
diagram.tables.find((t) => t.id === rel.startTableId);
|
||||
const { name: startFieldName } = startTableFields.find(
|
||||
(f) => f.id === rel.startFieldId,
|
||||
);
|
||||
const { fields: endTableFields, name: endTableName } = diagram.tables.find(
|
||||
(t) => t.id === rel.endTableId,
|
||||
);
|
||||
const { name: endFieldName } = endTableFields.find(
|
||||
(f) => f.id === rel.endFieldId,
|
||||
);
|
||||
|
||||
return `Ref ${rel.name} {\n\t${startTableName}.${startFieldName} ${cardinality(rel)} ${endTableName}.${endFieldName} [ delete: ${rel.deleteConstraint.toLowerCase()}, update: ${rel.updateConstraint.toLowerCase()} ]\n}`;
|
||||
};
|
||||
|
||||
return `${diagram.enums
|
||||
.map(
|
||||
(en) =>
|
||||
@@ -88,15 +104,6 @@ export function toDBML(diagram) {
|
||||
}\n}`,
|
||||
)
|
||||
.join("\n\n")}\n\n${diagram.relationships
|
||||
.map(
|
||||
(rel) =>
|
||||
`Ref ${rel.name} {\n\t${
|
||||
diagram.tables[rel.startTableId].name
|
||||
}.${diagram.tables[rel.startTableId].fields[rel.startFieldId].name} ${cardinality(
|
||||
rel,
|
||||
)} ${diagram.tables[rel.endTableId].name}.${
|
||||
diagram.tables[rel.endTableId].fields[rel.endFieldId].name
|
||||
} [ delete: ${rel.deleteConstraint.toLowerCase()}, update: ${rel.updateConstraint.toLowerCase()} ]\n}`,
|
||||
)
|
||||
.map((rel) => generateRelString(rel))
|
||||
.join("\n\n")}`;
|
||||
}
|
||||
|
@@ -62,8 +62,10 @@ export function jsonToDocumentation(obj) {
|
||||
const documentationRelationships = obj.relationships?.length
|
||||
? obj.relationships
|
||||
.map((r) => {
|
||||
const startTable = obj.tables[r.startTableId].name;
|
||||
const endTable = obj.tables[r.endTableId].name;
|
||||
const startTable = obj.tables.find(
|
||||
(t) => t.id === r.startTableId,
|
||||
).name;
|
||||
const endTable = obj.tables.find((t) => t.id === r.endTableId).name;
|
||||
return `- **${startTable} to ${endTable}**: ${r.cardinality}\n`;
|
||||
})
|
||||
.join("")
|
||||
|
@@ -41,8 +41,10 @@ export function jsonToMermaid(obj) {
|
||||
const mermaidRelationships = obj.relationships?.length
|
||||
? obj.relationships
|
||||
.map((r) => {
|
||||
const startTable = obj.tables[r.startTableId].name;
|
||||
const endTable = obj.tables[r.endTableId].name;
|
||||
const startTable = obj.tables.find(
|
||||
(t) => t.id === r.startTableId,
|
||||
).name;
|
||||
const endTable = obj.tables.find((t) => t.id === r.endTableId).name;
|
||||
return `\t${startTable} ${getMermaidRelationship(r.cardinality)} ${endTable} : references`;
|
||||
})
|
||||
.join("\n")
|
||||
|
@@ -224,16 +224,20 @@ export function jsonToMySQL(obj) {
|
||||
.join("\n")}`}`,
|
||||
)
|
||||
.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()};`,
|
||||
)
|
||||
.map((r) => {
|
||||
const { name: startName, fields: startFields } = obj.tables.find(
|
||||
(t) => t.id === r.startTableId,
|
||||
);
|
||||
|
||||
const { name: endName, fields: endFields } = obj.tables.find(
|
||||
(t) => t.id === r.endTableId,
|
||||
);
|
||||
return `ALTER TABLE \`${startName}\`\nADD FOREIGN KEY(\`${
|
||||
startFields.find((f) => f.id === r.startFieldId).name
|
||||
}\`) REFERENCES \`${endName}\`(\`${
|
||||
endFields.find((f) => f.id === r.endFieldId).name
|
||||
}\`)\nON UPDATE ${r.updateConstraint.toUpperCase()} ON DELETE ${r.deleteConstraint.toUpperCase()};`;
|
||||
})
|
||||
.join("\n")}`;
|
||||
}
|
||||
|
||||
@@ -327,14 +331,20 @@ export function jsonToPostgreSQL(obj) {
|
||||
.join("\n")}`,
|
||||
)
|
||||
.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()};`,
|
||||
)
|
||||
.map((r) => {
|
||||
const { name: startName, fields: startFields } = obj.tables.find(
|
||||
(t) => t.id === r.startTableId,
|
||||
);
|
||||
|
||||
const { name: endName, fields: endFields } = obj.tables.find(
|
||||
(t) => t.id === r.endTableId,
|
||||
);
|
||||
return `ALTER TABLE "${startName}"\nADD FOREIGN KEY("${
|
||||
startFields.find((f) => f.id === r.startFieldId).name
|
||||
}") REFERENCES "${endName}"("${
|
||||
endFields.find((f) => f.id === r.endFieldId).name
|
||||
}")\nON UPDATE ${r.updateConstraint.toUpperCase()} ON DELETE ${r.deleteConstraint.toUpperCase()};`;
|
||||
})
|
||||
.join("\n")}`;
|
||||
}
|
||||
|
||||
@@ -459,16 +469,20 @@ export function jsonToMariaDB(obj) {
|
||||
.join("\n")}`}`,
|
||||
)
|
||||
.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()};`,
|
||||
)
|
||||
.map((r) => {
|
||||
const { name: startName, fields: startFields } = obj.tables.find(
|
||||
(t) => t.id === r.startTableId,
|
||||
);
|
||||
|
||||
const { name: endName, fields: endFields } = obj.tables.find(
|
||||
(t) => t.id === r.endTableId,
|
||||
);
|
||||
return `ALTER TABLE \`${startName}\`\nADD FOREIGN KEY(\`${
|
||||
startFields.find((f) => f.id === r.startFieldId).name
|
||||
}\`) REFERENCES \`${endName}\`(\`${
|
||||
endFields.find((f) => f.id === r.endFieldId).name
|
||||
}\`)\nON UPDATE ${r.updateConstraint.toUpperCase()} ON DELETE ${r.deleteConstraint.toUpperCase()};`;
|
||||
})
|
||||
.join("\n")}`;
|
||||
}
|
||||
|
||||
@@ -527,14 +541,20 @@ export function jsonToSQLServer(obj) {
|
||||
.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()};\nGO`,
|
||||
)
|
||||
.map((r) => {
|
||||
const { name: startName, fields: startFields } = obj.tables.find(
|
||||
(t) => t.id === r.startTableId,
|
||||
);
|
||||
|
||||
const { name: endName, fields: endFields } = obj.tables.find(
|
||||
(t) => t.id === r.endTableId,
|
||||
);
|
||||
return `ALTER TABLE [${startName}]\nADD FOREIGN KEY([${
|
||||
startFields.find((f) => f.id === r.startFieldId).name
|
||||
}]) REFERENCES [${endName}]([${
|
||||
endFields.find((f) => f.id === r.endFieldId).name
|
||||
}])\nON UPDATE ${r.updateConstraint.toUpperCase()} ON DELETE ${r.deleteConstraint.toUpperCase()};\nGO`;
|
||||
})
|
||||
.join("\n")}`;
|
||||
}
|
||||
|
||||
@@ -594,13 +614,19 @@ export function jsonToOracleSQL(obj) {
|
||||
.join("\n")}`,
|
||||
)
|
||||
.join("\n\n")}\n${obj.references
|
||||
.map(
|
||||
(r) =>
|
||||
`ALTER TABLE "${obj.tables[r.startTableId].name}"\nADD CONSTRAINT "${r.name}" FOREIGN KEY ("${
|
||||
obj.tables[r.startTableId].fields[r.startFieldId].name
|
||||
}") REFERENCES "${obj.tables[r.endTableId].name}"("${
|
||||
obj.tables[r.endTableId].fields[r.endFieldId].name
|
||||
}");`,
|
||||
)
|
||||
.map((r) => {
|
||||
const { name: startName, fields: startFields } = obj.tables.find(
|
||||
(t) => t.id === r.startTableId,
|
||||
);
|
||||
|
||||
const { name: endName, fields: endFields } = obj.tables.find(
|
||||
(t) => t.id === r.endTableId,
|
||||
);
|
||||
return `ALTER TABLE "${startName}"\nADD CONSTRAINT "${r.name}" FOREIGN KEY ("${
|
||||
startFields.find((f) => f.id === r.startFieldId).name
|
||||
}") REFERENCES "${endName}"("${
|
||||
endFields.find((f) => f.id === r.endFieldId).name
|
||||
}");`;
|
||||
})
|
||||
.join("\n")}`;
|
||||
}
|
||||
|
@@ -56,15 +56,19 @@ export function toMariaDB(diagram) {
|
||||
.join("")}`}`,
|
||||
)
|
||||
.join("\n")}\n${diagram.references
|
||||
.map(
|
||||
(r) =>
|
||||
`ALTER TABLE \`${
|
||||
diagram.tables[r.startTableId].name
|
||||
}\`\nADD FOREIGN KEY(\`${
|
||||
diagram.tables[r.startTableId].fields[r.startFieldId].name
|
||||
}\`) REFERENCES \`${diagram.tables[r.endTableId].name}\`(\`${
|
||||
diagram.tables[r.endTableId].fields[r.endFieldId].name
|
||||
}\`)\nON UPDATE ${r.updateConstraint.toUpperCase()} ON DELETE ${r.deleteConstraint.toUpperCase()};`,
|
||||
)
|
||||
.map((r) => {
|
||||
const { name: startName, fields: startFields } = diagram.tables.find(
|
||||
(t) => t.id === r.startTableId,
|
||||
);
|
||||
|
||||
const { name: endName, fields: endFields } = diagram.tables.find(
|
||||
(t) => t.id === r.endTableId,
|
||||
);
|
||||
return `ALTER TABLE \`${startName}\`\nADD FOREIGN KEY(\`${
|
||||
startFields.find((f) => f.id === r.startFieldId).name
|
||||
}\`) REFERENCES \`${endName}\`(\`${
|
||||
endFields.find((f) => f.id === r.endFieldId).name
|
||||
}\`)\nON UPDATE ${r.updateConstraint.toUpperCase()} ON DELETE ${r.deleteConstraint.toUpperCase()};`;
|
||||
})
|
||||
.join("\n")}`;
|
||||
}
|
||||
|
@@ -47,13 +47,19 @@ export function toMSSQL(diagram) {
|
||||
.join("")}`}`,
|
||||
)
|
||||
.join("\n")}\n${diagram.references
|
||||
.map(
|
||||
(r) =>
|
||||
`ALTER TABLE [${diagram.tables[r.startTableId].name}]\nADD FOREIGN KEY([${
|
||||
diagram.tables[r.startTableId].fields[r.startFieldId].name
|
||||
}]) REFERENCES [${diagram.tables[r.endTableId].name}]([${
|
||||
diagram.tables[r.endTableId].fields[r.endFieldId].name
|
||||
}])\nON UPDATE ${r.updateConstraint.toUpperCase()} ON DELETE ${r.deleteConstraint.toUpperCase()};\nGO`,
|
||||
)
|
||||
.map((r) => {
|
||||
const { name: startName, fields: startFields } = diagram.tables.find(
|
||||
(t) => t.id === r.startTableId,
|
||||
);
|
||||
|
||||
const { name: endName, fields: endFields } = diagram.tables.find(
|
||||
(t) => t.id === r.endTableId,
|
||||
);
|
||||
return `ALTER TABLE [${startName}]\nADD FOREIGN KEY([${
|
||||
startFields.find((f) => f.id === r.startFieldId).name
|
||||
}]) REFERENCES [${endName}]([${
|
||||
endFields.find((f) => f.id === r.endFieldId).name
|
||||
}])\nON UPDATE ${r.updateConstraint.toUpperCase()} ON DELETE ${r.deleteConstraint.toUpperCase()};\nGO`;
|
||||
})
|
||||
.join("\n")}`;
|
||||
}
|
||||
|
@@ -58,15 +58,19 @@ export function toMySQL(diagram) {
|
||||
.join("")}`}`,
|
||||
)
|
||||
.join("\n")}\n${diagram.references
|
||||
.map(
|
||||
(r) =>
|
||||
`ALTER TABLE \`${
|
||||
diagram.tables[r.startTableId].name
|
||||
}\`\nADD FOREIGN KEY(\`${
|
||||
diagram.tables[r.startTableId].fields[r.startFieldId].name
|
||||
}\`) REFERENCES \`${diagram.tables[r.endTableId].name}\`(\`${
|
||||
diagram.tables[r.endTableId].fields[r.endFieldId].name
|
||||
}\`)\nON UPDATE ${r.updateConstraint.toUpperCase()} ON DELETE ${r.deleteConstraint.toUpperCase()};`,
|
||||
)
|
||||
.map((r) => {
|
||||
const { name: startName, fields: startFields } = diagram.tables.find(
|
||||
(t) => t.id === r.startTableId,
|
||||
);
|
||||
|
||||
const { name: endName, fields: endFields } = diagram.tables.find(
|
||||
(t) => t.id === r.endTableId,
|
||||
);
|
||||
return `ALTER TABLE \`${startName}\`\nADD FOREIGN KEY(\`${
|
||||
startFields.find((f) => f.id === r.startFieldId).name
|
||||
}\`) REFERENCES \`${endName}\`(\`${
|
||||
endFields.find((f) => f.id === r.endFieldId).name
|
||||
}\`)\nON UPDATE ${r.updateConstraint.toUpperCase()} ON DELETE ${r.deleteConstraint.toUpperCase()};`;
|
||||
})
|
||||
.join("\n")}`;
|
||||
}
|
||||
|
@@ -2,7 +2,6 @@ import { dbToTypes } from "../../data/datatypes";
|
||||
import { parseDefault } from "./shared";
|
||||
|
||||
export function toOracleSQL(diagram) {
|
||||
console.log(diagram);
|
||||
return `${diagram.tables
|
||||
.map(
|
||||
(table) =>
|
||||
@@ -47,13 +46,18 @@ export function toOracleSQL(diagram) {
|
||||
.join("")}`}`,
|
||||
)
|
||||
.join("\n")}\n${diagram.references
|
||||
.map(
|
||||
(r) =>
|
||||
`ALTER TABLE "${diagram.tables[r.startTableId].name}"\nADD CONSTRAINT "${r.name}" FOREIGN KEY ("${
|
||||
diagram.tables[r.startTableId].fields[r.startFieldId].name
|
||||
}") REFERENCES "${diagram.tables[r.endTableId].name}" ("${
|
||||
diagram.tables[r.endTableId].fields[r.endFieldId].name
|
||||
}")\nON UPDATE ${r.updateConstraint.toUpperCase()} ON DELETE ${r.deleteConstraint.toUpperCase()};`,
|
||||
)
|
||||
.map((r) => {
|
||||
const { name: startName, fields: startFields } = diagram.tables.find(
|
||||
(t) => t.id === r.startTableId,
|
||||
);
|
||||
const { name: endName, fields: endFields } = diagram.tables.find(
|
||||
(t) => t.id === r.endTableId,
|
||||
);
|
||||
return `ALTER TABLE "${startName}"\nADD CONSTRAINT "${r.name}" FOREIGN KEY ("${
|
||||
startFields.find((f) => f.id === r.startFieldId).name
|
||||
}") REFERENCES "${endName}" ("${
|
||||
endFields.find((f) => f.id === r.endFieldId).name
|
||||
}")\nON UPDATE ${r.updateConstraint.toUpperCase()} ON DELETE ${r.deleteConstraint.toUpperCase()};`;
|
||||
})
|
||||
.join("\n")}`;
|
||||
}
|
||||
|
@@ -77,13 +77,20 @@ export function toPostgres(diagram) {
|
||||
.join("\n")}\n`,
|
||||
)
|
||||
.join("\n")}${diagram.references
|
||||
.map(
|
||||
(r) =>
|
||||
`\nALTER TABLE "${diagram.tables[r.startTableId].name}"\nADD FOREIGN KEY("${
|
||||
diagram.tables[r.startTableId].fields[r.startFieldId].name
|
||||
}") REFERENCES "${diagram.tables[r.endTableId].name}"("${
|
||||
diagram.tables[r.endTableId].fields[r.endFieldId].name
|
||||
}")\nON UPDATE ${r.updateConstraint.toUpperCase()} ON DELETE ${r.deleteConstraint.toUpperCase()};`,
|
||||
)
|
||||
.map((r) => {
|
||||
const { name: startName, fields: startFields } = diagram.tables.find(
|
||||
(t) => t.id === r.startTableId,
|
||||
);
|
||||
|
||||
const { name: endName, fields: endFields } = diagram.tables.find(
|
||||
(t) => t.id === r.endTableId,
|
||||
);
|
||||
|
||||
return `\nALTER TABLE "${startName}"\nADD FOREIGN KEY("${
|
||||
startFields.find((f) => f.id === r.startFieldId)?.name
|
||||
}") REFERENCES "${endName}"("${
|
||||
endFields.find((f) => f.id === r.endFieldId)?.name
|
||||
}")\nON UPDATE ${r.updateConstraint.toUpperCase()} ON DELETE ${r.deleteConstraint.toUpperCase()};`;
|
||||
})
|
||||
.join("\n")}`;
|
||||
}
|
||||
|
@@ -32,10 +32,12 @@ export function getInlineFK(table, obj) {
|
||||
obj.references.forEach((r) => {
|
||||
if (r.startTableId === table.id) {
|
||||
fks.push(
|
||||
`\tFOREIGN KEY ("${table.fields[r.startFieldId].name}") REFERENCES "${
|
||||
obj.tables[r.endTableId].name
|
||||
`\tFOREIGN KEY ("${table.fields.find((f) => f.id === r.startFieldId)?.name}") REFERENCES "${
|
||||
obj.tables.find((t) => t.id === r.endTableId)?.name
|
||||
}"("${
|
||||
obj.tables[r.endTableId].fields[r.endFieldId].name
|
||||
obj.tables
|
||||
.find((t) => t.id === r.endTableId)
|
||||
.fields.find((f) => f.id === r.endFieldId)?.name
|
||||
}")\n\tON UPDATE ${r.updateConstraint.toUpperCase()} ON DELETE ${r.deleteConstraint.toUpperCase()}`,
|
||||
);
|
||||
}
|
||||
|
@@ -54,35 +54,33 @@ export function fromDBML(src) {
|
||||
}
|
||||
|
||||
for (const ref of schema.refs) {
|
||||
const startTable = ref.endpoints[0].tableName;
|
||||
const endTable = ref.endpoints[1].tableName;
|
||||
const startField = ref.endpoints[0].fieldNames[0];
|
||||
const endField = ref.endpoints[1].fieldNames[0];
|
||||
const startTableName = ref.endpoints[0].tableName;
|
||||
const endTableName = ref.endpoints[1].tableName;
|
||||
const startFieldName = ref.endpoints[0].fieldNames[0];
|
||||
const endFieldName = ref.endpoints[1].fieldNames[0];
|
||||
|
||||
const startTableId = tables.findIndex((t) => t.name === startTable);
|
||||
if (startTableId === -1) continue;
|
||||
const startTable = tables.find((t) => t.name === startTableName);
|
||||
if (!startTable) continue;
|
||||
|
||||
const endTableId = tables.findIndex((t) => t.name === endTable);
|
||||
if (endTableId === -1) continue;
|
||||
const endTable = tables.find((t) => t.name === endTableName);
|
||||
if (!endTable) continue;
|
||||
|
||||
const endFieldId = tables[endTableId].fields.findIndex(
|
||||
(f) => f.name === endField,
|
||||
const endField = endTable.fields.find((f) => f.name === endFieldName);
|
||||
if (!endField) continue;
|
||||
|
||||
const startField = startTable.fields.find(
|
||||
(f) => f.name === startFieldName,
|
||||
);
|
||||
if (endFieldId === -1) continue;
|
||||
|
||||
const startFieldId = tables[startTableId].fields.findIndex(
|
||||
(f) => f.name === startField,
|
||||
);
|
||||
if (startFieldId === -1) continue;
|
||||
if (!startField) continue;
|
||||
|
||||
const relationship = {};
|
||||
|
||||
relationship.name =
|
||||
"fk_" + startTable + "_" + startField + "_" + endTable;
|
||||
relationship.startTableId = startTableId;
|
||||
relationship.endTableId = endTableId;
|
||||
relationship.endFieldId = endFieldId;
|
||||
relationship.startFieldId = startFieldId;
|
||||
relationship.startTableId = startTable.id;
|
||||
relationship.endTableId = endTable.id;
|
||||
relationship.endFieldId = endField.id;
|
||||
relationship.startFieldId = startField.id;
|
||||
relationship.id = relationships.length;
|
||||
|
||||
relationship.updateConstraint = ref.onDelete
|
||||
|
@@ -1,3 +1,4 @@
|
||||
import { nanoid } from "nanoid";
|
||||
import { Cardinality, DB } from "../../data/constants";
|
||||
import { dbToTypes } from "../../data/datatypes";
|
||||
import { buildSQLFromAST } from "./shared";
|
||||
@@ -32,10 +33,11 @@ export function fromMariaDB(ast, diagramDb = DB.GENERIC) {
|
||||
table.color = "#175e7a";
|
||||
table.fields = [];
|
||||
table.indices = [];
|
||||
table.id = tables.length;
|
||||
table.id = nanoid();
|
||||
e.create_definitions.forEach((d) => {
|
||||
if (d.resource === "column") {
|
||||
const field = {};
|
||||
field.id = nanoid();
|
||||
field.name = d.column.column;
|
||||
|
||||
let type = d.definition.dataType;
|
||||
@@ -108,30 +110,29 @@ export function fromMariaDB(ast, diagramDb = DB.GENERIC) {
|
||||
} else if (d.constraint_type.toLowerCase() === "foreign key") {
|
||||
const relationship = {};
|
||||
const startTableId = table.id;
|
||||
const startTable = e.table[0].table;
|
||||
const startField = d.definition[0].column;
|
||||
const endTable = d.reference_definition.table[0].table;
|
||||
const endField = d.reference_definition.definition[0].column;
|
||||
const startTableName = e.table[0].table;
|
||||
const startFieldName = d.definition[0].column;
|
||||
const endTableName = d.reference_definition.table[0].table;
|
||||
const endFieldName = d.reference_definition.definition[0].column;
|
||||
|
||||
const endTableId = tables.findIndex((t) => t.name === endTable);
|
||||
if (endTableId === -1) return;
|
||||
const endTable = tables.find((t) => t.name === endTableName);
|
||||
if (!endTable) return;
|
||||
|
||||
const endFieldId = tables[endTableId].fields.findIndex(
|
||||
(f) => f.name === endField,
|
||||
const endField = endTable.fields.find(
|
||||
(f) => f.name === endFieldName,
|
||||
);
|
||||
if (endFieldId === -1) return;
|
||||
if (endField) return;
|
||||
|
||||
const startFieldId = table.fields.findIndex(
|
||||
(f) => f.name === startField,
|
||||
const startField = table.fields.find(
|
||||
(f) => f.name === startFieldName,
|
||||
);
|
||||
if (startFieldId === -1) return;
|
||||
if (!startField) return;
|
||||
|
||||
relationship.name =
|
||||
"fk_" + startTable + "_" + startField + "_" + endTable;
|
||||
relationship.name = `fk_${startTableName}_${startFieldName}_${endTableName}`;
|
||||
relationship.startTableId = startTableId;
|
||||
relationship.endTableId = endTableId;
|
||||
relationship.endFieldId = endFieldId;
|
||||
relationship.startFieldId = startFieldId;
|
||||
relationship.endTableId = endTable.id;
|
||||
relationship.endFieldId = endField.id;
|
||||
relationship.startFieldId = startField.id;
|
||||
let updateConstraint = "No action";
|
||||
let deleteConstraint = "No action";
|
||||
d.reference_definition.on_action.forEach((c) => {
|
||||
@@ -151,7 +152,7 @@ export function fromMariaDB(ast, diagramDb = DB.GENERIC) {
|
||||
relationship.updateConstraint = updateConstraint;
|
||||
relationship.deleteConstraint = deleteConstraint;
|
||||
|
||||
if (table.fields[startFieldId].unique) {
|
||||
if (startField.unique) {
|
||||
relationship.cardinality = Cardinality.ONE_TO_ONE;
|
||||
} else {
|
||||
relationship.cardinality = Cardinality.MANY_TO_ONE;
|
||||
@@ -168,28 +169,22 @@ export function fromMariaDB(ast, diagramDb = DB.GENERIC) {
|
||||
}
|
||||
});
|
||||
|
||||
table.fields.forEach((f, j) => {
|
||||
f.id = j;
|
||||
});
|
||||
tables.push(table);
|
||||
} else if (e.keyword === "index") {
|
||||
const index = {};
|
||||
index.name = e.index;
|
||||
index.unique = false;
|
||||
if (e.index_type === "unique") index.unique = true;
|
||||
index.fields = [];
|
||||
e.index_columns.forEach((f) => index.fields.push(f.column));
|
||||
const index = {
|
||||
name: e.index,
|
||||
unique: e.index_type === "unique",
|
||||
fields: e.index_columns.map((f) => f.column),
|
||||
};
|
||||
|
||||
let found = -1;
|
||||
tables.forEach((t, i) => {
|
||||
if (found !== -1) return;
|
||||
if (t.name === e.table.table) {
|
||||
t.indices.push(index);
|
||||
found = i;
|
||||
}
|
||||
});
|
||||
const table = tables.find((t) => t.name === e.table.table);
|
||||
|
||||
if (found !== -1) tables[found].indices.forEach((i, j) => (i.id = j));
|
||||
if (table) {
|
||||
table.indices.push(index);
|
||||
table.indices.forEach((i, j) => {
|
||||
i.id = j;
|
||||
});
|
||||
}
|
||||
}
|
||||
} else if (e.type === "alter") {
|
||||
e.expr.forEach((expr) => {
|
||||
@@ -199,11 +194,11 @@ export function fromMariaDB(ast, diagramDb = DB.GENERIC) {
|
||||
"foreign key"
|
||||
) {
|
||||
const relationship = {};
|
||||
const startTable = e.table[0].table;
|
||||
const startField = expr.create_definitions.definition[0].column;
|
||||
const endTable =
|
||||
const startTableName = e.table[0].table;
|
||||
const startFieldName = expr.create_definitions.definition[0].column;
|
||||
const endTableName =
|
||||
expr.create_definitions.reference_definition.table[0].table;
|
||||
const endField =
|
||||
const endFieldName =
|
||||
expr.create_definitions.reference_definition.definition[0].column;
|
||||
let updateConstraint = "No action";
|
||||
let deleteConstraint = "No action";
|
||||
@@ -223,32 +218,30 @@ export function fromMariaDB(ast, diagramDb = DB.GENERIC) {
|
||||
},
|
||||
);
|
||||
|
||||
const startTableId = tables.findIndex((t) => t.name === startTable);
|
||||
if (startTable === -1) return;
|
||||
const startTable = tables.find((t) => t.name === startTableName);
|
||||
if (!startTable) return;
|
||||
|
||||
const endTableId = tables.findIndex((t) => t.name === endTable);
|
||||
if (endTableId === -1) return;
|
||||
const endTable = tables.find((t) => t.name === endTableName);
|
||||
if (!endTable) return;
|
||||
|
||||
const endFieldId = tables[endTableId].fields.findIndex(
|
||||
(f) => f.name === endField,
|
||||
const endField = endTable.fields.find((f) => f.name === endFieldName);
|
||||
if (!endField) return;
|
||||
|
||||
const startField = startTable.fields.find(
|
||||
(f) => f.name === startFieldName,
|
||||
);
|
||||
if (endFieldId === -1) return;
|
||||
|
||||
const startFieldId = tables[startTableId].fields.findIndex(
|
||||
(f) => f.name === startField,
|
||||
);
|
||||
if (startFieldId === -1) return;
|
||||
if (!startField) return;
|
||||
|
||||
relationship.name =
|
||||
"fk_" + startTable + "_" + startField + "_" + endTable;
|
||||
relationship.startTableId = startTableId;
|
||||
relationship.startFieldId = startFieldId;
|
||||
relationship.endTableId = endTableId;
|
||||
relationship.endFieldId = endFieldId;
|
||||
"fk_" + startTableName + "_" + startFieldName + "_" + endTableName;
|
||||
relationship.startTableId = startTable.id;
|
||||
relationship.startFieldId = startField.id;
|
||||
relationship.endTableId = endTable.id;
|
||||
relationship.endFieldId = endField.id;
|
||||
relationship.updateConstraint = updateConstraint;
|
||||
relationship.deleteConstraint = deleteConstraint;
|
||||
|
||||
if (tables[startTableId].fields[startFieldId].unique) {
|
||||
if (startField.unique) {
|
||||
relationship.cardinality = Cardinality.ONE_TO_ONE;
|
||||
} else {
|
||||
relationship.cardinality = Cardinality.MANY_TO_ONE;
|
||||
|
@@ -1,3 +1,4 @@
|
||||
import { nanoid } from "nanoid";
|
||||
import { Cardinality, DB } from "../../data/constants";
|
||||
import { dbToTypes } from "../../data/datatypes";
|
||||
import { buildSQLFromAST } from "./shared";
|
||||
@@ -44,10 +45,11 @@ export function fromMSSQL(ast, diagramDb = DB.GENERIC) {
|
||||
table.color = "#175e7a";
|
||||
table.fields = [];
|
||||
table.indices = [];
|
||||
table.id = tables.length;
|
||||
table.id = nanoid();
|
||||
e.create_definitions.forEach((d) => {
|
||||
if (d.resource === "column") {
|
||||
const field = {};
|
||||
field.id = nanoid();
|
||||
field.name = d.column.column;
|
||||
|
||||
let type = d.definition.dataType;
|
||||
@@ -120,30 +122,29 @@ export function fromMSSQL(ast, diagramDb = DB.GENERIC) {
|
||||
} else if (d.constraint_type.toLowerCase() === "foreign key") {
|
||||
const relationship = {};
|
||||
const startTableId = table.id;
|
||||
const startTable = e.table[0].table;
|
||||
const startField = d.definition[0].column;
|
||||
const endTable = d.reference_definition.table[0].table;
|
||||
const endField = d.reference_definition.definition[0].column;
|
||||
const startTableName = e.table[0].table;
|
||||
const startFieldName = d.definition[0].column;
|
||||
const endTableName = d.reference_definition.table[0].table;
|
||||
const endFieldName = d.reference_definition.definition[0].column;
|
||||
|
||||
const endTableId = tables.findIndex((t) => t.name === endTable);
|
||||
if (endTableId === -1) return;
|
||||
const endTable = tables.find((t) => t.name === endTableName);
|
||||
if (!endTable) return;
|
||||
|
||||
const endFieldId = tables[endTableId].fields.findIndex(
|
||||
(f) => f.name === endField,
|
||||
const endField = endTable.fields.find(
|
||||
(f) => f.name === endFieldName,
|
||||
);
|
||||
if (endFieldId === -1) return;
|
||||
if (!endField) return;
|
||||
|
||||
const startFieldId = table.fields.findIndex(
|
||||
(f) => f.name === startField,
|
||||
const startField = table.fields.find(
|
||||
(f) => f.name === startFieldName,
|
||||
);
|
||||
if (startFieldId === -1) return;
|
||||
if (!startField) return;
|
||||
|
||||
relationship.name =
|
||||
"fk_" + startTable + "_" + startField + "_" + endTable;
|
||||
relationship.name = `fk_${startTableName}_${startFieldName}_${endTableName}`;
|
||||
relationship.startTableId = startTableId;
|
||||
relationship.endTableId = endTableId;
|
||||
relationship.endFieldId = endFieldId;
|
||||
relationship.startFieldId = startFieldId;
|
||||
relationship.endTableId = endTable.id;
|
||||
relationship.endFieldId = endField.id;
|
||||
relationship.startFieldId = startField.id;
|
||||
let updateConstraint = "No action";
|
||||
let deleteConstraint = "No action";
|
||||
d.reference_definition.on_action.forEach((c) => {
|
||||
@@ -163,7 +164,7 @@ export function fromMSSQL(ast, diagramDb = DB.GENERIC) {
|
||||
relationship.updateConstraint = updateConstraint;
|
||||
relationship.deleteConstraint = deleteConstraint;
|
||||
|
||||
if (table.fields[startFieldId].unique) {
|
||||
if (startField.unique) {
|
||||
relationship.cardinality = Cardinality.ONE_TO_ONE;
|
||||
} else {
|
||||
relationship.cardinality = Cardinality.MANY_TO_ONE;
|
||||
@@ -173,28 +174,22 @@ export function fromMSSQL(ast, diagramDb = DB.GENERIC) {
|
||||
}
|
||||
}
|
||||
});
|
||||
table.fields.forEach((f, j) => {
|
||||
f.id = j;
|
||||
});
|
||||
tables.push(table);
|
||||
} else if (e.keyword === "index") {
|
||||
const index = {};
|
||||
index.name = e.index;
|
||||
index.unique = false;
|
||||
if (e.index_type === "unique") index.unique = true;
|
||||
index.fields = [];
|
||||
e.index_columns.forEach((f) => index.fields.push(f.column));
|
||||
const index = {
|
||||
name: e.index,
|
||||
unique: e.index_type === "unique",
|
||||
fields: e.index_columns.map((f) => f.column),
|
||||
};
|
||||
|
||||
let found = -1;
|
||||
tables.forEach((t, i) => {
|
||||
if (found !== -1) return;
|
||||
if (t.name === e.table.table) {
|
||||
t.indices.push(index);
|
||||
found = i;
|
||||
}
|
||||
});
|
||||
const table = tables.find((t) => t.name === e.table.table);
|
||||
|
||||
if (found !== -1) tables[found].indices.forEach((i, j) => (i.id = j));
|
||||
if (table) {
|
||||
table.indices.push(index);
|
||||
table.indices.forEach((i, j) => {
|
||||
i.id = j;
|
||||
});
|
||||
}
|
||||
}
|
||||
} else if (e.type === "alter") {
|
||||
e.expr.forEach((expr) => {
|
||||
@@ -204,11 +199,11 @@ export function fromMSSQL(ast, diagramDb = DB.GENERIC) {
|
||||
"foreign key"
|
||||
) {
|
||||
const relationship = {};
|
||||
const startTable = e.table[0].table;
|
||||
const startField = expr.create_definitions.definition[0].column;
|
||||
const endTable =
|
||||
const startTableName = e.table[0].table;
|
||||
const startFieldName = expr.create_definitions.definition[0].column;
|
||||
const endTableName =
|
||||
expr.create_definitions.reference_definition.table[0].table;
|
||||
const endField =
|
||||
const endFieldName =
|
||||
expr.create_definitions.reference_definition.definition[0].column;
|
||||
let updateConstraint = "No action";
|
||||
let deleteConstraint = "No action";
|
||||
@@ -228,32 +223,29 @@ export function fromMSSQL(ast, diagramDb = DB.GENERIC) {
|
||||
},
|
||||
);
|
||||
|
||||
const startTableId = tables.findIndex((t) => t.name === startTable);
|
||||
if (startTable === -1) return;
|
||||
const startTable = tables.find((t) => t.name === startTableName);
|
||||
if (!startTable) return;
|
||||
|
||||
const endTableId = tables.findIndex((t) => t.name === endTable);
|
||||
if (endTableId === -1) return;
|
||||
const endTable = tables.find((t) => t.name === endTableName);
|
||||
if (!endTable) return;
|
||||
|
||||
const endFieldId = tables[endTableId].fields.findIndex(
|
||||
(f) => f.name === endField,
|
||||
const endField = endTable.fields.find((f) => f.name === endFieldName);
|
||||
if (!endField) return;
|
||||
|
||||
const startField = startTable.fields.find(
|
||||
(f) => f.name === startFieldName,
|
||||
);
|
||||
if (endFieldId === -1) return;
|
||||
if (!startField) return;
|
||||
|
||||
const startFieldId = tables[startTableId].fields.findIndex(
|
||||
(f) => f.name === startField,
|
||||
);
|
||||
if (startFieldId === -1) return;
|
||||
|
||||
relationship.name =
|
||||
"fk_" + startTable + "_" + startField + "_" + endTable;
|
||||
relationship.startTableId = startTableId;
|
||||
relationship.startFieldId = startFieldId;
|
||||
relationship.endTableId = endTableId;
|
||||
relationship.endFieldId = endFieldId;
|
||||
relationship.name = `fk_${startTable}_${startField}_${endTable}`;
|
||||
relationship.startTableId = startTable.id;
|
||||
relationship.startFieldId = startField.id;
|
||||
relationship.endTableId = endTable.id;
|
||||
relationship.endFieldId = endField.id;
|
||||
relationship.updateConstraint = updateConstraint;
|
||||
relationship.deleteConstraint = deleteConstraint;
|
||||
|
||||
if (tables[startTableId].fields[startFieldId].unique) {
|
||||
if (startField.unique) {
|
||||
relationship.cardinality = Cardinality.ONE_TO_ONE;
|
||||
} else {
|
||||
relationship.cardinality = Cardinality.MANY_TO_ONE;
|
||||
|
@@ -1,3 +1,4 @@
|
||||
import { nanoid } from "nanoid";
|
||||
import { Cardinality, DB } from "../../data/constants";
|
||||
import { dbToTypes } from "../../data/datatypes";
|
||||
import { buildSQLFromAST } from "./shared";
|
||||
@@ -32,10 +33,11 @@ export function fromMySQL(ast, diagramDb = DB.GENERIC) {
|
||||
table.color = "#175e7a";
|
||||
table.fields = [];
|
||||
table.indices = [];
|
||||
table.id = tables.length;
|
||||
table.id = nanoid();
|
||||
e.create_definitions.forEach((d) => {
|
||||
if (d.resource === "column") {
|
||||
const field = {};
|
||||
field.id = nanoid();
|
||||
field.name = d.column.column;
|
||||
|
||||
let type = d.definition.dataType;
|
||||
@@ -107,31 +109,29 @@ export function fromMySQL(ast, diagramDb = DB.GENERIC) {
|
||||
});
|
||||
} else if (d.constraint_type.toLowerCase() === "foreign key") {
|
||||
const relationship = {};
|
||||
const startTableId = table.id;
|
||||
const startTable = e.table[0].table;
|
||||
const startField = d.definition[0].column;
|
||||
const endTable = d.reference_definition.table[0].table;
|
||||
const endField = d.reference_definition.definition[0].column;
|
||||
const startTableName = e.table[0].table;
|
||||
const startFieldName = d.definition[0].column;
|
||||
const endTableName = d.reference_definition.table[0].table;
|
||||
const endFieldName = d.reference_definition.definition[0].column;
|
||||
|
||||
const endTableId = tables.findIndex((t) => t.name === endTable);
|
||||
if (endTableId === -1) return;
|
||||
const endTable = tables.find((t) => t.name === endTableName);
|
||||
if (!endTable) return;
|
||||
|
||||
const endFieldId = tables[endTableId].fields.findIndex(
|
||||
(f) => f.name === endField,
|
||||
const endField = endTable.fields.find(
|
||||
(f) => f.name === endFieldName,
|
||||
);
|
||||
if (endFieldId === -1) return;
|
||||
if (!endField) return;
|
||||
|
||||
const startFieldId = table.fields.findIndex(
|
||||
(f) => f.name === startField,
|
||||
const startField = table.fields.find(
|
||||
(f) => f.name === startFieldName,
|
||||
);
|
||||
if (startFieldId === -1) return;
|
||||
if (!startField) return;
|
||||
|
||||
relationship.name =
|
||||
"fk_" + startTable + "_" + startField + "_" + endTable;
|
||||
relationship.startTableId = startTableId;
|
||||
relationship.endTableId = endTableId;
|
||||
relationship.endFieldId = endFieldId;
|
||||
relationship.startFieldId = startFieldId;
|
||||
relationship.name = `fk_${startTableName}_${startFieldName}_${endTableName}`;
|
||||
relationship.startTableId = table.id;
|
||||
relationship.endTableId = endTable.id;
|
||||
relationship.endFieldId = endField.id;
|
||||
relationship.startFieldId = startField.id;
|
||||
let updateConstraint = "No action";
|
||||
let deleteConstraint = "No action";
|
||||
d.reference_definition.on_action.forEach((c) => {
|
||||
@@ -151,7 +151,7 @@ export function fromMySQL(ast, diagramDb = DB.GENERIC) {
|
||||
relationship.updateConstraint = updateConstraint;
|
||||
relationship.deleteConstraint = deleteConstraint;
|
||||
|
||||
if (table.fields[startFieldId].unique) {
|
||||
if (startField.unique) {
|
||||
relationship.cardinality = Cardinality.ONE_TO_ONE;
|
||||
} else {
|
||||
relationship.cardinality = Cardinality.MANY_TO_ONE;
|
||||
@@ -164,7 +164,7 @@ export function fromMySQL(ast, diagramDb = DB.GENERIC) {
|
||||
|
||||
e.table_options.forEach((opt) => {
|
||||
if (opt.keyword === "comment") {
|
||||
table.comment = opt.value.replace(/^["']|["']$/g, '');
|
||||
table.comment = opt.value.replace(/^["']|["']$/g, "");
|
||||
}
|
||||
});
|
||||
|
||||
@@ -173,23 +173,20 @@ export function fromMySQL(ast, diagramDb = DB.GENERIC) {
|
||||
});
|
||||
tables.push(table);
|
||||
} else if (e.keyword === "index") {
|
||||
const index = {};
|
||||
index.name = e.index;
|
||||
index.unique = false;
|
||||
if (e.index_type === "unique") index.unique = true;
|
||||
index.fields = [];
|
||||
e.index_columns.forEach((f) => index.fields.push(f.column));
|
||||
const index = {
|
||||
name: e.index,
|
||||
unique: e.index_type === "unique",
|
||||
fields: e.index_columns.map((f) => f.column),
|
||||
};
|
||||
|
||||
let found = -1;
|
||||
tables.forEach((t, i) => {
|
||||
if (found !== -1) return;
|
||||
if (t.name === e.table.table) {
|
||||
t.indices.push(index);
|
||||
found = i;
|
||||
}
|
||||
});
|
||||
const table = tables.find((t) => t.name === e.table.table);
|
||||
|
||||
if (found !== -1) tables[found].indices.forEach((i, j) => (i.id = j));
|
||||
if (table) {
|
||||
table.indices.push(index);
|
||||
table.indices.forEach((i, j) => {
|
||||
i.id = j;
|
||||
});
|
||||
}
|
||||
}
|
||||
} else if (e.type === "alter") {
|
||||
e.expr.forEach((expr) => {
|
||||
@@ -199,11 +196,11 @@ export function fromMySQL(ast, diagramDb = DB.GENERIC) {
|
||||
"foreign key"
|
||||
) {
|
||||
const relationship = {};
|
||||
const startTable = e.table[0].table;
|
||||
const startField = expr.create_definitions.definition[0].column;
|
||||
const endTable =
|
||||
const startTableName = e.table[0].table;
|
||||
const startFieldName = expr.create_definitions.definition[0].column;
|
||||
const endTableName =
|
||||
expr.create_definitions.reference_definition.table[0].table;
|
||||
const endField =
|
||||
const endFieldName =
|
||||
expr.create_definitions.reference_definition.definition[0].column;
|
||||
let updateConstraint = "No action";
|
||||
let deleteConstraint = "No action";
|
||||
@@ -223,32 +220,29 @@ export function fromMySQL(ast, diagramDb = DB.GENERIC) {
|
||||
},
|
||||
);
|
||||
|
||||
const startTableId = tables.findIndex((t) => t.name === startTable);
|
||||
if (startTable === -1) return;
|
||||
const startTable = tables.find((t) => t.name === startTableName);
|
||||
if (!startTable) return;
|
||||
|
||||
const endTableId = tables.findIndex((t) => t.name === endTable);
|
||||
if (endTableId === -1) return;
|
||||
const endTable = tables.find((t) => t.name === endTableName);
|
||||
if (!endTable) return;
|
||||
|
||||
const endFieldId = tables[endTableId].fields.findIndex(
|
||||
(f) => f.name === endField,
|
||||
const endField = endTable.fields.find((f) => f.name === endFieldName);
|
||||
if (!endField) return;
|
||||
|
||||
const startField = startTable.fields.find(
|
||||
(f) => f.name === startFieldName,
|
||||
);
|
||||
if (endFieldId === -1) return;
|
||||
if (!startField) return;
|
||||
|
||||
const startFieldId = tables[startTableId].fields.findIndex(
|
||||
(f) => f.name === startField,
|
||||
);
|
||||
if (startFieldId === -1) return;
|
||||
|
||||
relationship.name =
|
||||
"fk_" + startTable + "_" + startField + "_" + endTable;
|
||||
relationship.startTableId = startTableId;
|
||||
relationship.startFieldId = startFieldId;
|
||||
relationship.endTableId = endTableId;
|
||||
relationship.endFieldId = endFieldId;
|
||||
relationship.name = `fk_${startTableName}_${startFieldName}_${endTableName}`;
|
||||
relationship.startTableId = startTable.id;
|
||||
relationship.startFieldId = startField.id;
|
||||
relationship.endTableId = endTable.id;
|
||||
relationship.endFieldId = endField.id;
|
||||
relationship.updateConstraint = updateConstraint;
|
||||
relationship.deleteConstraint = deleteConstraint;
|
||||
|
||||
if (tables[startTableId].fields[startFieldId].unique) {
|
||||
if (startField.unique) {
|
||||
relationship.cardinality = Cardinality.ONE_TO_ONE;
|
||||
} else {
|
||||
relationship.cardinality = Cardinality.MANY_TO_ONE;
|
||||
|
@@ -1,3 +1,4 @@
|
||||
import { nanoid } from "nanoid";
|
||||
import { Cardinality, Constraint, DB } from "../../data/constants";
|
||||
import { dbToTypes } from "../../data/datatypes";
|
||||
|
||||
@@ -33,10 +34,11 @@ export function fromOracleSQL(ast, diagramDb = DB.GENERIC) {
|
||||
table.color = "#175e7a";
|
||||
table.fields = [];
|
||||
table.indices = [];
|
||||
table.id = tables.length;
|
||||
table.id = nanoid();
|
||||
e.table.relational_properties.forEach((d) => {
|
||||
if (d.resource === "column") {
|
||||
const field = {};
|
||||
field.id = nanoid();
|
||||
field.name = d.name;
|
||||
|
||||
let type = d.type.type.toUpperCase();
|
||||
@@ -78,33 +80,32 @@ export function fromOracleSQL(ast, diagramDb = DB.GENERIC) {
|
||||
table.fields.push(field);
|
||||
} else if (d.resource === "constraint") {
|
||||
const relationship = {};
|
||||
const startTableId = table.id;
|
||||
const startField = d.constraint.columns[0];
|
||||
const endField = d.constraint.reference.columns[0];
|
||||
const endTable = d.constraint.reference.object.name;
|
||||
const startFieldName = d.constraint.columns[0];
|
||||
const endFieldName = d.constraint.reference.columns[0];
|
||||
const endTableName = d.constraint.reference.object.name;
|
||||
|
||||
const endTableId = tables.findIndex((t) => t.name === endTable);
|
||||
if (endTableId === -1) return;
|
||||
const endTable = tables.find((t) => t.name === endTableName);
|
||||
if (!endTable) return;
|
||||
|
||||
const endFieldId = tables[endTableId].fields.findIndex(
|
||||
(f) => f.name === endField,
|
||||
const endField = endTable.fields.find(
|
||||
(f) => f.name === endFieldName,
|
||||
);
|
||||
if (endFieldId === -1) return;
|
||||
if (!endField) return;
|
||||
|
||||
const startFieldId = table.fields.findIndex(
|
||||
(f) => f.name === startField,
|
||||
const startField = table.fields.find(
|
||||
(f) => f.name === startFieldName,
|
||||
);
|
||||
if (startFieldId === -1) return;
|
||||
if (!startField) return;
|
||||
|
||||
relationship.startTableId = startTableId;
|
||||
relationship.startFieldId = startFieldId;
|
||||
relationship.endTableId = endTableId;
|
||||
relationship.endFieldId = endFieldId;
|
||||
relationship.startTableId = table.id;
|
||||
relationship.startFieldId = startField.id;
|
||||
relationship.endTableId = endTable.id;
|
||||
relationship.endFieldId = endField.id;
|
||||
relationship.updateConstraint = Constraint.NONE;
|
||||
relationship.name =
|
||||
d.name && Boolean(d.name.trim())
|
||||
? d.name
|
||||
: "fk_" + table.name + "_" + startField + "_" + endTable;
|
||||
: `fk_${table.name}_${startFieldName}_${endTableName}`;
|
||||
relationship.deleteConstraint =
|
||||
d.constraint.reference.on_delete &&
|
||||
Boolean(d.constraint.reference.on_delete.trim())
|
||||
@@ -112,7 +113,7 @@ export function fromOracleSQL(ast, diagramDb = DB.GENERIC) {
|
||||
d.constraint.reference.on_delete.substring(1)
|
||||
: Constraint.NONE;
|
||||
|
||||
if (table.fields[startFieldId].unique) {
|
||||
if (startField.unique) {
|
||||
relationship.cardinality = Cardinality.ONE_TO_ONE;
|
||||
} else {
|
||||
relationship.cardinality = Cardinality.MANY_TO_ONE;
|
||||
|
@@ -1,3 +1,4 @@
|
||||
import { nanoid } from "nanoid";
|
||||
import { Cardinality, DB } from "../../data/constants";
|
||||
import { dbToTypes } from "../../data/datatypes";
|
||||
import { buildSQLFromAST } from "./shared";
|
||||
@@ -32,10 +33,11 @@ export function fromPostgres(ast, diagramDb = DB.GENERIC) {
|
||||
table.color = "#175e7a";
|
||||
table.fields = [];
|
||||
table.indices = [];
|
||||
table.id = tables.length;
|
||||
table.id = nanoid();
|
||||
e.create_definitions.forEach((d) => {
|
||||
const field = {};
|
||||
if (d.resource === "column") {
|
||||
field.id = nanoid();
|
||||
field.name = d.column.column.expr.value;
|
||||
|
||||
let type = types.find((t) =>
|
||||
@@ -118,31 +120,28 @@ export function fromPostgres(ast, diagramDb = DB.GENERIC) {
|
||||
} else if (d.constraint_type.toLowerCase() === "foreign key") {
|
||||
const relationship = {};
|
||||
const startTableId = table.id;
|
||||
const startTable = e.table[0].table;
|
||||
const startField = d.definition[0].column.expr.value;
|
||||
const endTable = d.reference_definition.table[0].table;
|
||||
const endField =
|
||||
const startTableName = e.table[0].table;
|
||||
const startFieldName = d.definition[0].column.expr.value;
|
||||
const endTableName = d.reference_definition.table[0].table;
|
||||
const endFieldName =
|
||||
d.reference_definition.definition[0].column.expr.value;
|
||||
|
||||
const endTableId = tables.findIndex((t) => t.name === endTable);
|
||||
if (endTableId === -1) return;
|
||||
const endTable = tables.find((t) => t.name === endTableName);
|
||||
if (!endTable) return;
|
||||
|
||||
const endFieldId = tables[endTableId].fields.findIndex(
|
||||
(f) => f.name === endField,
|
||||
const endField = endTable.fields.find(
|
||||
(f) => f.name === endFieldName,
|
||||
);
|
||||
if (endFieldId === -1) return;
|
||||
if (!endField) return;
|
||||
|
||||
const startFieldId = table.fields.findIndex(
|
||||
(f) => f.name === startField,
|
||||
);
|
||||
if (startFieldId === -1) return;
|
||||
const startField = table.find((f) => f.name === startFieldName);
|
||||
if (!startField) return;
|
||||
|
||||
relationship.name =
|
||||
"fk_" + startTable + "_" + startField + "_" + endTable;
|
||||
relationship.name = `fk_${startTableName}_${startFieldName}_${endTableName}`;
|
||||
relationship.startTableId = startTableId;
|
||||
relationship.endTableId = endTableId;
|
||||
relationship.endFieldId = endFieldId;
|
||||
relationship.startFieldId = startFieldId;
|
||||
relationship.endTableId = endTable.id;
|
||||
relationship.endFieldId = endField.id;
|
||||
relationship.startFieldId = startField.id;
|
||||
let updateConstraint = "No action";
|
||||
let deleteConstraint = "No action";
|
||||
d.reference_definition.on_action.forEach((c) => {
|
||||
@@ -161,7 +160,7 @@ export function fromPostgres(ast, diagramDb = DB.GENERIC) {
|
||||
|
||||
relationship.updateConstraint = updateConstraint;
|
||||
relationship.deleteConstraint = deleteConstraint;
|
||||
if (table.fields[startFieldId].unique) {
|
||||
if (startField.unique) {
|
||||
relationship.cardinality = Cardinality.ONE_TO_ONE;
|
||||
} else {
|
||||
relationship.cardinality = Cardinality.MANY_TO_ONE;
|
||||
@@ -172,10 +171,10 @@ export function fromPostgres(ast, diagramDb = DB.GENERIC) {
|
||||
|
||||
if (d.reference_definition) {
|
||||
const relationship = {};
|
||||
const startTable = table.name;
|
||||
const startField = field.name;
|
||||
const endTable = d.reference_definition.table[0].table;
|
||||
const endField =
|
||||
const startTableName = table.name;
|
||||
const startFieldName = field.name;
|
||||
const endTableName = d.reference_definition.table[0].table;
|
||||
const endFieldName =
|
||||
d.reference_definition.definition[0].column.expr.value;
|
||||
let updateConstraint = "No action";
|
||||
let deleteConstraint = "No action";
|
||||
@@ -195,29 +194,28 @@ export function fromPostgres(ast, diagramDb = DB.GENERIC) {
|
||||
|
||||
const startTableId = tables.length;
|
||||
|
||||
const endTableId = tables.findIndex((t) => t.name === endTable);
|
||||
if (endTableId === -1) return;
|
||||
const endTable = tables.find((t) => t.name === endTableName);
|
||||
if (!endTable) return;
|
||||
|
||||
const endFieldId = tables[endTableId].fields.findIndex(
|
||||
(f) => f.name === endField,
|
||||
const endField = endTable.fields.findIndex(
|
||||
(f) => f.name === endFieldName,
|
||||
);
|
||||
if (endFieldId === -1) return;
|
||||
if (!endField) return;
|
||||
|
||||
const startFieldId = table.fields.findIndex(
|
||||
(f) => f.name === startField,
|
||||
const startField = table.fields.find(
|
||||
(f) => f.name === startFieldName,
|
||||
);
|
||||
if (startFieldId === -1) return;
|
||||
if (!startField) return;
|
||||
|
||||
relationship.name =
|
||||
"fk_" + startTable + "_" + startField + "_" + endTable;
|
||||
relationship.name = `fk_${startTableName}_${startFieldName}_${endTableName}`;
|
||||
relationship.startTableId = startTableId;
|
||||
relationship.startFieldId = startFieldId;
|
||||
relationship.endTableId = endTableId;
|
||||
relationship.endFieldId = endFieldId;
|
||||
relationship.startFieldId = startField.id;
|
||||
relationship.endTableId = endTable.id;
|
||||
relationship.endFieldId = endField.id;
|
||||
relationship.updateConstraint = updateConstraint;
|
||||
relationship.deleteConstraint = deleteConstraint;
|
||||
|
||||
if (table.fields[startFieldId].unique) {
|
||||
if (startField.unique) {
|
||||
relationship.cardinality = Cardinality.ONE_TO_ONE;
|
||||
} else {
|
||||
relationship.cardinality = Cardinality.MANY_TO_ONE;
|
||||
@@ -233,23 +231,20 @@ export function fromPostgres(ast, diagramDb = DB.GENERIC) {
|
||||
});
|
||||
tables.push(table);
|
||||
} else if (e.keyword === "index") {
|
||||
const index = {};
|
||||
index.name = e.index;
|
||||
index.unique = false;
|
||||
if (e.index_type === "unique") index.unique = true;
|
||||
index.fields = [];
|
||||
e.index_columns.forEach((f) => index.fields.push(f.column.expr.value));
|
||||
const index = {
|
||||
name: e.index,
|
||||
unique: e.index_type === "unique",
|
||||
fields: e.index_columns.map((f) => f.column),
|
||||
};
|
||||
|
||||
let found = -1;
|
||||
tables.forEach((t, i) => {
|
||||
if (found !== -1) return;
|
||||
if (t.name === e.table.table) {
|
||||
t.indices.push(index);
|
||||
found = i;
|
||||
}
|
||||
});
|
||||
const table = tables.find((t) => t.name === e.table.table);
|
||||
|
||||
if (found !== -1) tables[found].indices.forEach((i, j) => (i.id = j));
|
||||
if (table) {
|
||||
table.indices.push(index);
|
||||
table.indices.forEach((i, j) => {
|
||||
i.id = j;
|
||||
});
|
||||
}
|
||||
} else if (e.keyword === "type") {
|
||||
if (e.resource === "enum") {
|
||||
const newEnum = {
|
||||
@@ -294,12 +289,12 @@ export function fromPostgres(ast, diagramDb = DB.GENERIC) {
|
||||
"foreign key"
|
||||
) {
|
||||
const relationship = {};
|
||||
const startTable = e.table[0].table;
|
||||
const startField =
|
||||
const startTableName = e.table[0].table;
|
||||
const startFieldName =
|
||||
expr.create_definitions.definition[0].column.expr.value;
|
||||
const endTable =
|
||||
const endTableName =
|
||||
expr.create_definitions.reference_definition.table[0].table;
|
||||
const endField =
|
||||
const endFieldName =
|
||||
expr.create_definitions.reference_definition.definition[0].column
|
||||
.expr.value;
|
||||
let updateConstraint = "No action";
|
||||
@@ -320,33 +315,30 @@ export function fromPostgres(ast, diagramDb = DB.GENERIC) {
|
||||
},
|
||||
);
|
||||
|
||||
const startTableId = tables.findIndex((t) => t.name === startTable);
|
||||
if (startTable === -1) return;
|
||||
const startTable = tables.find((t) => t.name === startTableName);
|
||||
if (!startTable) return;
|
||||
|
||||
const endTableId = tables.findIndex((t) => t.name === endTable);
|
||||
if (endTableId === -1) return;
|
||||
const endTable = tables.find((t) => t.name === endTableName);
|
||||
if (!endTable) return;
|
||||
|
||||
const endFieldId = tables[endTableId].fields.findIndex(
|
||||
(f) => f.name === endField,
|
||||
const endField = endTable.fields.find((f) => f.name === endFieldName);
|
||||
if (!endField) return;
|
||||
|
||||
const startField = startTable.fields.find(
|
||||
(f) => f.name === startFieldName,
|
||||
);
|
||||
if (endFieldId === -1) return;
|
||||
if (!startField) return;
|
||||
|
||||
const startFieldId = tables[startTableId].fields.findIndex(
|
||||
(f) => f.name === startField,
|
||||
);
|
||||
if (startFieldId === -1) return;
|
||||
|
||||
relationship.name =
|
||||
"fk_" + startTable + "_" + startField + "_" + endTable;
|
||||
relationship.startTableId = startTableId;
|
||||
relationship.startFieldId = startFieldId;
|
||||
relationship.endTableId = endTableId;
|
||||
relationship.endFieldId = endFieldId;
|
||||
relationship.name = `fk_${startTableName}_${startFieldName}_${endTableName}`;
|
||||
relationship.startTableId = startTable.id;
|
||||
relationship.startFieldId = startField.id;
|
||||
relationship.endTableId = endTable.id;
|
||||
relationship.endFieldId = endField.id;
|
||||
relationship.updateConstraint = updateConstraint;
|
||||
relationship.deleteConstraint = deleteConstraint;
|
||||
relationship.cardinality = Cardinality.ONE_TO_ONE;
|
||||
|
||||
if (tables[startTableId].fields[startFieldId].unique) {
|
||||
if (startField.unique) {
|
||||
relationship.cardinality = Cardinality.ONE_TO_ONE;
|
||||
} else {
|
||||
relationship.cardinality = Cardinality.MANY_TO_ONE;
|
||||
|
@@ -1,3 +1,4 @@
|
||||
import { nanoid } from "nanoid";
|
||||
import { Cardinality, DB } from "../../data/constants";
|
||||
import { dbToTypes } from "../../data/datatypes";
|
||||
import { buildSQLFromAST } from "./shared";
|
||||
@@ -47,27 +48,23 @@ export function fromSQLite(ast, diagramDb = DB.GENERIC) {
|
||||
) => {
|
||||
const relationship = {};
|
||||
const endTableName = referenceDefinition.table[0].table;
|
||||
const endField = referenceDefinition.definition[0].column;
|
||||
const endFieldName = referenceDefinition.definition[0].column;
|
||||
|
||||
const endTableId = tables.findIndex((t) => t.name === endTableName);
|
||||
if (endTableId === -1) return;
|
||||
const endTable = tables.find((t) => t.name === endTableName);
|
||||
if (!endTable) return;
|
||||
|
||||
const endFieldId = tables[endTableId].fields.findIndex(
|
||||
(f) => f.name === endField,
|
||||
);
|
||||
if (endFieldId === -1) return;
|
||||
const endField = endTable.fields.findIndex((f) => f.name === endFieldName);
|
||||
if (!endField) return;
|
||||
|
||||
const startFieldId = startTable.fields.findIndex(
|
||||
(f) => f.name === startFieldName,
|
||||
);
|
||||
if (startFieldId === -1) return;
|
||||
const startField = startTable.fields.find((f) => f.name === startFieldName);
|
||||
if (!startField) return;
|
||||
|
||||
relationship.name =
|
||||
"fk_" + startTable.name + "_" + startFieldName + "_" + endTableName;
|
||||
relationship.startTableId = startTable.id;
|
||||
relationship.endTableId = endTableId;
|
||||
relationship.endFieldId = endFieldId;
|
||||
relationship.startFieldId = startFieldId;
|
||||
relationship.endTableId = endTable.id;
|
||||
relationship.endFieldId = endField.id;
|
||||
relationship.startFieldId = startField.id;
|
||||
let updateConstraint = "No action";
|
||||
let deleteConstraint = "No action";
|
||||
referenceDefinition.on_action.forEach((c) => {
|
||||
@@ -85,7 +82,7 @@ export function fromSQLite(ast, diagramDb = DB.GENERIC) {
|
||||
relationship.updateConstraint = updateConstraint;
|
||||
relationship.deleteConstraint = deleteConstraint;
|
||||
|
||||
if (startTable.fields[startFieldId].unique) {
|
||||
if (startField.unique) {
|
||||
relationship.cardinality = Cardinality.ONE_TO_ONE;
|
||||
} else {
|
||||
relationship.cardinality = Cardinality.MANY_TO_ONE;
|
||||
@@ -102,10 +99,11 @@ export function fromSQLite(ast, diagramDb = DB.GENERIC) {
|
||||
table.color = "#175e7a";
|
||||
table.fields = [];
|
||||
table.indices = [];
|
||||
table.id = tables.length;
|
||||
table.id = nanoid();
|
||||
e.create_definitions.forEach((d) => {
|
||||
if (d.resource === "column") {
|
||||
const field = {};
|
||||
field.id = nanoid();
|
||||
field.name = d.column.column;
|
||||
|
||||
let type = d.definition.dataType;
|
||||
@@ -191,28 +189,22 @@ export function fromSQLite(ast, diagramDb = DB.GENERIC) {
|
||||
}
|
||||
}
|
||||
});
|
||||
table.fields.forEach((f, j) => {
|
||||
f.id = j;
|
||||
});
|
||||
tables.push(table);
|
||||
} else if (e.keyword === "index") {
|
||||
const index = {};
|
||||
index.name = e.index;
|
||||
index.unique = false;
|
||||
if (e.index_type === "unique") index.unique = true;
|
||||
index.fields = [];
|
||||
e.index_columns.forEach((f) => index.fields.push(f.column));
|
||||
const index = {
|
||||
name: e.index,
|
||||
unique: e.index_type === "unique",
|
||||
fields: e.index_columns.map((f) => f.column),
|
||||
};
|
||||
|
||||
let found = -1;
|
||||
tables.forEach((t, i) => {
|
||||
if (found !== -1) return;
|
||||
if (t.name === e.table.table) {
|
||||
t.indices.push(index);
|
||||
found = i;
|
||||
}
|
||||
});
|
||||
const table = tables.find((t) => t.name === e.table.table);
|
||||
|
||||
if (found !== -1) tables[found].indices.forEach((i, j) => (i.id = j));
|
||||
if (table) {
|
||||
table.indices.push(index);
|
||||
table.indices.forEach((i, j) => {
|
||||
i.id = j;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@@ -213,7 +213,7 @@ export function getIssues(diagram) {
|
||||
if (visited.includes(tableId)) {
|
||||
issues.push(
|
||||
i18n.t("circular_dependency", {
|
||||
refName: diagram.tables[tableId].name,
|
||||
refName: diagram.tables.find((t) => t.id === tableId)?.name,
|
||||
}),
|
||||
);
|
||||
return;
|
||||
|
Reference in New Issue
Block a user