Skip to content

Commit 94226de

Browse files
authored
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
1 parent e35fbde commit 94226de

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+990
-955
lines changed

package-lock.json

+58-18
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+4
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@
1111
},
1212
"dependencies": {
1313
"@dbml/core": "^3.9.7-alpha.0",
14+
"@dnd-kit/core": "^6.3.1",
15+
"@dnd-kit/sortable": "^10.0.0",
16+
"@dnd-kit/utilities": "^3.2.2",
1417
"@douyinfe/semi-ui": "^2.77.1",
1518
"@lexical/react": "^0.12.5",
1619
"@monaco-editor/react": "^4.7.0",
@@ -29,6 +32,7 @@
2932
"jspdf": "^3.0.1",
3033
"jszip": "^3.10.1",
3134
"lexical": "^0.12.5",
35+
"nanoid": "^5.1.5",
3236
"node-sql-parser": "^5.3.9",
3337
"oracle-sql-parser": "^0.1.0",
3438
"react": "^18.2.0",

src/components/EditorCanvas/Canvas.jsx

+37-27
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ export default function Canvas() {
5555
} = useSelect();
5656
const [dragging, setDragging] = useState({
5757
element: ObjectType.NONE,
58-
id: -1,
58+
id: null,
5959
prevX: 0,
6060
prevY: 0,
6161
initialPositions: [],
@@ -73,8 +73,8 @@ export default function Canvas() {
7373
});
7474
const [grabOffset, setGrabOffset] = useState({ x: 0, y: 0 });
7575
const [hoveredTable, setHoveredTable] = useState({
76-
tableId: -1,
77-
field: -2,
76+
tableId: null,
77+
fieldId: null,
7878
});
7979
const [panning, setPanning] = useState({
8080
isPanning: false,
@@ -167,7 +167,7 @@ export default function Canvas() {
167167
const getElement = (element) => {
168168
switch (element.type) {
169169
case ObjectType.TABLE:
170-
return tables[element.id];
170+
return tables.find((t) => t.id === element.id);
171171
case ObjectType.AREA:
172172
return areas[element.id];
173173
case ObjectType.NOTE:
@@ -264,7 +264,7 @@ export default function Canvas() {
264264
});
265265
} else if (
266266
dragging.element !== ObjectType.NONE &&
267-
dragging.id >= 0 &&
267+
dragging.id !== null &&
268268
bulkSelectedElements.length
269269
) {
270270
const currentX = pointer.spaces.diagram.x + grabOffset.x;
@@ -274,9 +274,10 @@ export default function Canvas() {
274274

275275
for (const element of bulkSelectedElements) {
276276
if (element.type === ObjectType.TABLE) {
277+
const { x, y } = tables.find((e) => e.id === element.id);
277278
updateTable(element.id, {
278-
x: tables[element.id].x + deltaX,
279-
y: tables[element.id].y + deltaY,
279+
x: x + deltaX,
280+
y: y + deltaY,
280281
});
281282
}
282283
if (element.type === ObjectType.AREA) {
@@ -317,21 +318,21 @@ export default function Canvas() {
317318
(panning.cursorStart.y - pointer.spaces.screen.y) / transform.zoom,
318319
},
319320
}));
320-
} else if (dragging.element === ObjectType.TABLE && dragging.id >= 0) {
321+
} else if (dragging.element === ObjectType.TABLE && dragging.id !== null) {
321322
updateTable(dragging.id, {
322323
x: pointer.spaces.diagram.x + grabOffset.x,
323324
y: pointer.spaces.diagram.y + grabOffset.y,
324325
});
325326
} else if (
326327
dragging.element === ObjectType.AREA &&
327-
dragging.id >= 0 &&
328+
dragging.id !== null &&
328329
areaResize.id === -1
329330
) {
330331
updateArea(dragging.id, {
331332
x: pointer.spaces.diagram.x + grabOffset.x,
332333
y: pointer.spaces.diagram.y + grabOffset.y,
333334
});
334-
} else if (dragging.element === ObjectType.NOTE && dragging.id >= 0) {
335+
} else if (dragging.element === ObjectType.NOTE && dragging.id !== null) {
335336
updateNote(dragging.id, {
336337
x: pointer.spaces.diagram.x + grabOffset.x,
337338
y: pointer.spaces.diagram.y + grabOffset.y,
@@ -441,7 +442,10 @@ export default function Canvas() {
441442
};
442443

443444
const didPan = () =>
444-
!(transform.pan.x === panning.panStart.x && transform.pan.y === panning.panStart.y);
445+
!(
446+
transform.pan.x === panning.panStart.x &&
447+
transform.pan.y === panning.panStart.y
448+
);
445449

446450
/**
447451
* @param {PointerEvent} e
@@ -503,7 +507,7 @@ export default function Canvas() {
503507
}
504508
setDragging({
505509
element: ObjectType.NONE,
506-
id: -1,
510+
id: null,
507511
prevX: 0,
508512
prevY: 0,
509513
initialPositions: [],
@@ -585,7 +589,7 @@ export default function Canvas() {
585589
setPanning((old) => ({ ...old, isPanning: false }));
586590
setDragging({
587591
element: ObjectType.NONE,
588-
id: -1,
592+
id: null,
589593
prevX: 0,
590594
prevY: 0,
591595
initialPositions: [],
@@ -594,34 +598,40 @@ export default function Canvas() {
594598
};
595599

596600
const handleLinking = () => {
597-
if (hoveredTable.tableId < 0) return;
598-
if (hoveredTable.field < 0) return;
599-
if (
600-
!areFieldsCompatible(
601-
database,
602-
tables[linkingLine.startTableId].fields[linkingLine.startFieldId],
603-
tables[hoveredTable.tableId].fields[hoveredTable.field],
604-
)
605-
) {
601+
if (hoveredTable.tableId === null) return;
602+
if (hoveredTable.fieldId === null) return;
603+
604+
const { fields: startTableFields, name: startTableName } = tables.find(
605+
(t) => t.id === linkingLine.startTableId,
606+
);
607+
const { type: startType, name: startFieldName } = startTableFields.find(
608+
(f) => f.id === linkingLine.startFieldId,
609+
);
610+
const { fields: endTableFields, name: endTableName } = tables.find(
611+
(t) => t.id === hoveredTable.tableId,
612+
);
613+
const { type: endType } = endTableFields.find(
614+
(f) => f.id === hoveredTable.fieldId,
615+
);
616+
617+
if (!areFieldsCompatible(database, startType, endType)) {
606618
Toast.info(t("cannot_connect"));
607619
return;
608620
}
609621
if (
610622
linkingLine.startTableId === hoveredTable.tableId &&
611-
linkingLine.startFieldId === hoveredTable.field
623+
linkingLine.startFieldId === hoveredTable.fieldId
612624
)
613625
return;
614626

615627
const newRelationship = {
616628
...linkingLine,
617629
endTableId: hoveredTable.tableId,
618-
endFieldId: hoveredTable.field,
630+
endFieldId: hoveredTable.fieldId,
619631
cardinality: Cardinality.ONE_TO_ONE,
620632
updateConstraint: Constraint.NONE,
621633
deleteConstraint: Constraint.NONE,
622-
name: `fk_${tables[linkingLine.startTableId].name}_${
623-
tables[linkingLine.startTableId].fields[linkingLine.startFieldId].name
624-
}_${tables[hoveredTable.tableId].name}`,
634+
name: `fk_${startTableName}_${startFieldName}_${endTableName}`,
625635
id: relationships.length,
626636
};
627637
delete newRelationship.startX;

src/components/EditorCanvas/Relationship.jsx

+18-15
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useRef } from "react";
1+
import { useMemo, useRef } from "react";
22
import {
33
Cardinality,
44
darkBgTheme,
@@ -20,6 +20,22 @@ export default function Relationship({ data }) {
2020
const { selectedElement, setSelectedElement } = useSelect();
2121
const { t } = useTranslation();
2222

23+
const pathValues = useMemo(() => {
24+
const startTable = tables.find((t) => t.id === data.startTableId);
25+
const endTable = tables.find((t) => t.id === data.endTableId);
26+
27+
if (!startTable || !endTable) return null;
28+
29+
return {
30+
startFieldIndex: startTable.fields.findIndex(
31+
(f) => f.id === data.startFieldId,
32+
),
33+
endFieldIndex: endTable.fields.findIndex((f) => f.id === data.endFieldId),
34+
startTable: { x: startTable.x, y: startTable.y },
35+
endTable: { x: endTable.x, y: endTable.y },
36+
};
37+
}, [tables, data]);
38+
2339
const theme = localStorage.getItem("theme");
2440

2541
const pathRef = useRef();
@@ -106,20 +122,7 @@ export default function Relationship({ data }) {
106122
<g className="select-none group" onDoubleClick={edit}>
107123
<path
108124
ref={pathRef}
109-
d={calcPath(
110-
{
111-
...data,
112-
startTable: {
113-
x: tables[data.startTableId].x,
114-
y: tables[data.startTableId].y,
115-
},
116-
endTable: {
117-
x: tables[data.endTableId].x,
118-
y: tables[data.endTableId].y,
119-
},
120-
},
121-
settings.tableWidth,
122-
)}
125+
d={calcPath(pathValues, settings.tableWidth)}
123126
stroke="gray"
124127
className="group-hover:stroke-sky-700"
125128
fill="none"

0 commit comments

Comments
 (0)