Skip to content

Commit 4a5da8c

Browse files
1ilitewqazxc
authored andcommitted
Table and field drag and drop ordering (drawdb-io#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 188aa41 commit 4a5da8c

Some content is hidden

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

45 files changed

+1013
-952
lines changed

package-lock.json

Lines changed: 58 additions & 18 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 4 additions & 0 deletions
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",
@@ -27,6 +30,7 @@
2730
"jspdf": "^3.0.1",
2831
"jszip": "^3.10.1",
2932
"lexical": "^0.12.5",
33+
"nanoid": "^5.1.5",
3034
"node-sql-parser": "^5.3.9",
3135
"octokit": "^4.0.2",
3236
"oracle-sql-parser": "^0.1.0",

src/components/EditorCanvas/Canvas.jsx

Lines changed: 36 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ export default function Canvas() {
5656
} = useSelect();
5757
const [dragging, setDragging] = useState({
5858
element: ObjectType.NONE,
59-
id: -1,
59+
id: null,
6060
prevX: 0,
6161
prevY: 0,
6262
initialPositions: [],
@@ -74,8 +74,8 @@ export default function Canvas() {
7474
});
7575
const [grabOffset, setGrabOffset] = useState({ x: 0, y: 0 });
7676
const [hoveredTable, setHoveredTable] = useState({
77-
tableId: -1,
78-
field: -2,
77+
tableId: null,
78+
fieldId: null,
7979
});
8080
const [panning, setPanning] = useState({
8181
isPanning: false,
@@ -168,7 +168,7 @@ export default function Canvas() {
168168
const getElement = (element) => {
169169
switch (element.type) {
170170
case ObjectType.TABLE:
171-
return tables[element.id];
171+
return tables.find((t) => t.id === element.id);
172172
case ObjectType.AREA:
173173
return areas[element.id];
174174
case ObjectType.NOTE:
@@ -265,7 +265,7 @@ export default function Canvas() {
265265
});
266266
} else if (
267267
dragging.element !== ObjectType.NONE &&
268-
dragging.id >= 0 &&
268+
dragging.id !== null &&
269269
bulkSelectedElements.length
270270
) {
271271
const currentX = pointer.spaces.diagram.x + grabOffset.x;
@@ -275,9 +275,10 @@ export default function Canvas() {
275275

276276
for (const element of bulkSelectedElements) {
277277
if (element.type === ObjectType.TABLE) {
278+
const { x, y } = tables.find((e) => e.id === element.id);
278279
updateTable(element.id, {
279-
x: tables[element.id].x + deltaX,
280-
y: tables[element.id].y + deltaY,
280+
x: x + deltaX,
281+
y: y + deltaY,
281282
});
282283
}
283284
if (element.type === ObjectType.AREA) {
@@ -318,21 +319,21 @@ export default function Canvas() {
318319
(panning.cursorStart.y - pointer.spaces.screen.y) / transform.zoom,
319320
},
320321
}));
321-
} else if (dragging.element === ObjectType.TABLE && dragging.id >= 0) {
322+
} else if (dragging.element === ObjectType.TABLE && dragging.id !== null) {
322323
updateTable(dragging.id, {
323324
x: pointer.spaces.diagram.x + grabOffset.x,
324325
y: pointer.spaces.diagram.y + grabOffset.y,
325326
});
326327
} else if (
327328
dragging.element === ObjectType.AREA &&
328-
dragging.id >= 0 &&
329+
dragging.id !== null &&
329330
areaResize.id === -1
330331
) {
331332
updateArea(dragging.id, {
332333
x: pointer.spaces.diagram.x + grabOffset.x,
333334
y: pointer.spaces.diagram.y + grabOffset.y,
334335
});
335-
} else if (dragging.element === ObjectType.NOTE && dragging.id >= 0) {
336+
} else if (dragging.element === ObjectType.NOTE && dragging.id !== null) {
336337
updateNote(dragging.id, {
337338
x: pointer.spaces.diagram.x + grabOffset.x,
338339
y: pointer.spaces.diagram.y + grabOffset.y,
@@ -442,7 +443,10 @@ export default function Canvas() {
442443
};
443444

444445
const didPan = () =>
445-
!(transform.pan.x === panning.panStart.x && transform.pan.y === panning.panStart.y);
446+
!(
447+
transform.pan.x === panning.panStart.x &&
448+
transform.pan.y === panning.panStart.y
449+
);
446450

447451
/**
448452
* @param {PointerEvent} e
@@ -504,7 +508,7 @@ export default function Canvas() {
504508
}
505509
setDragging({
506510
element: ObjectType.NONE,
507-
id: -1,
511+
id: null,
508512
prevX: 0,
509513
prevY: 0,
510514
initialPositions: [],
@@ -586,7 +590,7 @@ export default function Canvas() {
586590
setPanning((old) => ({ ...old, isPanning: false }));
587591
setDragging({
588592
element: ObjectType.NONE,
589-
id: -1,
593+
id: null,
590594
prevX: 0,
591595
prevY: 0,
592596
initialPositions: [],
@@ -595,28 +599,36 @@ export default function Canvas() {
595599
};
596600

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

616628
const newRelationship = {
617629
...linkingLine,
618630
endTableId: hoveredTable.tableId,
619-
endFieldId: hoveredTable.field,
631+
endFieldId: hoveredTable.fieldId,
620632
cardinality: Cardinality.ONE_TO_ONE,
621633
updateConstraint: Constraint.NONE,
622634
deleteConstraint: Constraint.NONE,

src/components/EditorCanvas/Relationship.jsx

Lines changed: 18 additions & 15 deletions
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)