Skip to content

Commit 856d7d7

Browse files
authored
Graph UI fix (#9)
* fix nodes do not update bug * use right-click menu instead of dnd to add node * fix dispatch position when dropping into a scope
1 parent 63d8ed8 commit 856d7d7

File tree

1 file changed

+87
-86
lines changed

1 file changed

+87
-86
lines changed

ui/src/components/repo/graph.js

Lines changed: 87 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ import CircularProgress from "@mui/material/CircularProgress";
1414
import Tooltip from "@mui/material/Tooltip";
1515
import IconButton from "@mui/material/IconButton";
1616
import PlayArrowIcon from "@mui/icons-material/PlayArrow";
17+
import Stack from "@mui/material/Stack";
18+
import Button from "@mui/material/Button";
1719

1820
import { useDispatch, useSelector } from "react-redux";
1921

@@ -32,7 +34,7 @@ import { MyMonaco } from "../MyMonaco";
3234

3335
const nanoid = customAlphabet(nolookalikes, 10);
3436

35-
const ScopeNode = ({ data, id, isConnectable, selected }) => {
37+
const ScopeNode = memo(({ data, id, isConnectable, selected }) => {
3638
// add resize to the node
3739
const dispatch = useDispatch();
3840
const ref = useRef(null);
@@ -98,7 +100,7 @@ const ScopeNode = ({ data, id, isConnectable, selected }) => {
98100
)}
99101
</Box>
100102
);
101-
};
103+
});
102104

103105
const CodeNode = memo(({ data, id, isConnectable, selected }) => {
104106
const pod = useSelector((state) => state.repo.pods[id]);
@@ -306,44 +308,6 @@ const CodeNode = memo(({ data, id, isConnectable, selected }) => {
306308

307309
const nodeTypes = { scope: ScopeNode, code: CodeNode };
308310

309-
const NodeBar = () => {
310-
const onDragStart = (event, nodeType) => {
311-
event.dataTransfer.setData("application/reactflow", nodeType);
312-
event.dataTransfer.effectAllowed = "move";
313-
};
314-
315-
return (
316-
<Box
317-
sx={{
318-
zIndex: 4,
319-
display: "flex",
320-
flexDirection: "row",
321-
}}
322-
>
323-
<Box
324-
className="dndnode input"
325-
sx={{
326-
cursor: "grab",
327-
zIndex: 4,
328-
border: "1px solid #aaa",
329-
}}
330-
onDragStart={(event) => onDragStart(event, "code")}
331-
draggable
332-
>
333-
Code
334-
</Box>
335-
<Box
336-
className="dndnode output"
337-
sx={{ cursor: "grab", ml: 2, border: "1px solid #aaa" }}
338-
onDragStart={(event) => onDragStart(event, "scope")}
339-
draggable
340-
>
341-
Scope
342-
</Box>
343-
</Box>
344-
);
345-
};
346-
347311
const level2color = {
348312
0: "rgba(255, 0, 0, 0.2)",
349313
1: "rgba(255, 0, 255, 0.2)",
@@ -419,7 +383,8 @@ export function Deck({ props }) {
419383
useEffect(() => {
420384
let nodes = getRealNodes("ROOT", -1);
421385
setNodes(nodes);
422-
}, [getRealNodes]);
386+
// eslint-disable-next-line react-hooks/exhaustive-deps
387+
}, []);
423388

424389
const onNodesChange = useCallback(
425390
(changes) => {
@@ -436,27 +401,13 @@ export function Deck({ props }) {
436401
[setEdges]
437402
);
438403

439-
const onDragOver = useCallback((event) => {
440-
event.preventDefault();
441-
event.dataTransfer.dropEffect = "move";
442-
}, []);
443-
444404
const [reactFlowInstance, setReactFlowInstance] = useState(null);
445405
const reactFlowWrapper = useRef(null);
446406

447407
const dispatch = useDispatch();
448-
const onDrop = useCallback(
449-
(event) => {
450-
event.preventDefault();
451-
408+
const addNode = useCallback(
409+
(x, y, type) => {
452410
const reactFlowBounds = reactFlowWrapper.current.getBoundingClientRect();
453-
let type = event.dataTransfer.getData("application/reactflow");
454-
455-
// check if the dropped element is valid
456-
if (typeof type === "undefined" || !type) {
457-
return;
458-
}
459-
460411
let style;
461412

462413
// if (type === "code") type = "default";
@@ -474,8 +425,8 @@ export function Deck({ props }) {
474425
}
475426

476427
const position = reactFlowInstance.project({
477-
x: event.clientX - reactFlowBounds.left,
478-
y: event.clientY - reactFlowBounds.top,
428+
x: x - reactFlowBounds.left,
429+
y: y - reactFlowBounds.top,
479430
});
480431
let id = "CP" + nanoid();
481432
const newNode = {
@@ -510,7 +461,7 @@ export function Deck({ props }) {
510461
})
511462
);
512463
},
513-
[reactFlowInstance, dispatch, onResize]
464+
[dispatch, onResize, reactFlowInstance, setNodes]
514465
);
515466

516467
const getAbsPos = useCallback(
@@ -581,14 +532,6 @@ export function Deck({ props }) {
581532
x: event.clientX - reactFlowBounds.left,
582533
y: event.clientY - reactFlowBounds.top,
583534
});
584-
// first, dispatch this to the store
585-
dispatch(
586-
repoSlice.actions.setPodPosition({
587-
id: node.id,
588-
x: node.position.x,
589-
y: node.position.y,
590-
})
591-
);
592535
// check if this position is inside parent scope
593536
if (
594537
mousePos.x < node.positionAbsolute.x ||
@@ -601,8 +544,30 @@ export function Deck({ props }) {
601544
}
602545
// Check which group is at this position.
603546
const scope = getScopeAt(mousePos.x, mousePos.y, node.id);
547+
let absX = node.position.x;
548+
let absY = node.position.y;
604549
if (scope) {
605550
console.log("dropped into scope:", scope);
551+
// compute the actual position
552+
let { x: _absX, y: _absY } = getAbsolutePos(
553+
node.positionAbsolute.x,
554+
node.positionAbsolute.y,
555+
scope,
556+
nodes
557+
);
558+
absX = _absX;
559+
absY = _absY;
560+
}
561+
// first, dispatch this to the store
562+
dispatch(
563+
repoSlice.actions.setPodPosition({
564+
id: node.id,
565+
x: absX,
566+
y: absY,
567+
})
568+
);
569+
570+
if (scope) {
606571
dispatch(
607572
repoSlice.actions.setPodParent({
608573
id: node.id,
@@ -628,20 +593,10 @@ export function Deck({ props }) {
628593
...nd.style,
629594
backgroundColor: level2color[scope.level + 1],
630595
},
631-
position: scope.positionAbsolute
632-
? {
633-
x: node.positionAbsolute.x - scope.positionAbsolute.x,
634-
y: node.positionAbsolute.y - scope.positionAbsolute.y,
635-
}
636-
: // I need to adjust for all the ancestor nodes' position.
637-
// But there's no positionAbsolute field in the nodes.
638-
// So, I need to calculate it.
639-
getAbsolutePos(
640-
node.positionAbsolute.x,
641-
node.positionAbsolute.y,
642-
scope,
643-
nds
644-
),
596+
position: {
597+
x: absX,
598+
y: absY,
599+
},
645600
};
646601
}
647602
return nd;
@@ -665,6 +620,23 @@ export function Deck({ props }) {
665620
[dispatch]
666621
);
667622

623+
const [showContextMenu, setShowContextMenu] = useState(false);
624+
const [points, setPoints] = useState({ x: 0, y: 0 });
625+
const [client, setClient] = useState({ x: 0, y: 0 });
626+
627+
const onPaneContextMenu = (event) => {
628+
event.preventDefault();
629+
setShowContextMenu(true);
630+
setPoints({ x: event.pageX, y: event.pageY });
631+
setClient({ x: event.clientX, y: event.clientY });
632+
};
633+
634+
useEffect(() => {
635+
const handleClick = () => setShowContextMenu(false);
636+
document.addEventListener("click", handleClick);
637+
return () => document.removeEventListener("click", handleClick);
638+
}, []);
639+
668640
return (
669641
<Box
670642
style={{
@@ -673,7 +645,6 @@ export function Deck({ props }) {
673645
flexDirection: "column",
674646
}}
675647
>
676-
<NodeBar />
677648
<Box sx={{ height: "100%" }} ref={reactFlowWrapper}>
678649
<ReactFlow
679650
nodes={nodes}
@@ -682,14 +653,12 @@ export function Deck({ props }) {
682653
onEdgesChange={onEdgesChange}
683654
onConnect={onConnect}
684655
onInit={setReactFlowInstance}
685-
// onNodeDrag={onNodeDrag}
686656
onNodeDragStop={onNodeDragStop}
687657
onNodesDelete={onNodesDelete}
688658
fitView
689659
attributionPosition="top-right"
690660
maxZoom={5}
691-
onDrop={onDrop}
692-
onDragOver={onDragOver}
661+
onPaneContextMenu={onPaneContextMenu}
693662
nodeTypes={nodeTypes}
694663
zoomOnScroll={false}
695664
panOnScroll={true}
@@ -715,6 +684,38 @@ export function Deck({ props }) {
715684
<Background />
716685
</Box>
717686
</ReactFlow>
687+
{showContextMenu && (
688+
<Box
689+
sx={{
690+
left: `${points.x}px`,
691+
top: `${points.y}px`,
692+
zIndex: 100,
693+
position: "absolute",
694+
boxShadow: "0px 1px 8px 0px rgba(0, 0, 0, 0.1)",
695+
// width: '200px',
696+
backgroundColor: "#fff",
697+
borderRadius: "5px",
698+
boxSizing: "border-box",
699+
}}
700+
>
701+
<Stack>
702+
<Button
703+
onClick={() => {
704+
addNode(client.x, client.y, "code");
705+
}}
706+
>
707+
New Code{" "}
708+
</Button>
709+
<Button
710+
onClick={() => {
711+
addNode(client.x, client.y, "scope");
712+
}}
713+
>
714+
New Scope{" "}
715+
</Button>
716+
</Stack>
717+
</Box>
718+
)}
718719
</Box>
719720
</Box>
720721
);

0 commit comments

Comments
 (0)