Skip to content

Commit 5090f8d

Browse files
committed
added code
1 parent a4ffeec commit 5090f8d

7 files changed

+176
-126
lines changed

app/_components/add-stage-modal.jsx

+58-32
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Button } from "@/components/ui/button";
1+
import { Button } from "../../components/ui/button";
22
import {
33
Dialog,
44
DialogContent,
@@ -7,48 +7,74 @@ import {
77
DialogHeader,
88
DialogTitle,
99
DialogTrigger,
10-
} from "@/components/ui/dialog";
11-
import { Input } from "@/components/ui/input";
12-
import { Label } from "@/components/ui/label";
10+
} from "../../components/ui/dialog";
11+
import { Input } from "../../components/ui/input";
12+
import { Label } from "../../components/ui/label";
1313
import { Plus } from "lucide-react";
14+
import { useState } from "react";
15+
16+
export function AddStageModal({ setNodes }) {
17+
const [open, setOpen] = useState(false);
1418

15-
export function AddStageModal() {
1619
return (
17-
<Dialog>
20+
<Dialog open={open} onOpenChange={setOpen}>
1821
<DialogTrigger asChild>
1922
<Button size="sm">
2023
<Plus />
2124
Add New Stage
2225
</Button>
2326
</DialogTrigger>
2427
<DialogContent className="sm:max-w-[425px]">
25-
<DialogHeader>
26-
<DialogTitle>Add new Stage</DialogTitle>
27-
<DialogDescription>
28-
A stage is basically compilation of admins
29-
</DialogDescription>
30-
</DialogHeader>
31-
<div className="grid gap-6">
32-
<div className="grid items-center gap-1">
33-
<Label htmlFor="name">Name</Label>
34-
<Input
35-
id="name"
36-
className="w-full"
37-
placeholder="Enter name of the stage"
38-
/>
39-
</div>
40-
<div className="grid items-center gap-1">
41-
<Label htmlFor="username">Username</Label>
42-
<Input
43-
id="username"
44-
className="w-full"
45-
placeholder="Enter admins for the stage"
46-
/>
28+
<form
29+
className="space-y-4"
30+
onSubmit={(e) => {
31+
e.preventDefault();
32+
33+
const formData = new FormData(e.target);
34+
const name = formData.get("name");
35+
36+
if (!name) {
37+
alert("Name is required!");
38+
return;
39+
}
40+
41+
const newNode = {
42+
id: `node-${Date.now()}`,
43+
type: "LabeledGroupNode",
44+
style: {
45+
width: 400,
46+
height: 250,
47+
backgroundColor: "rgba(240,240,240,0.25)",
48+
},
49+
position: { x: 100, y: 100 },
50+
data: { label: name, setNodes }, // Pass setNodes through the data property
51+
};
52+
53+
setNodes((prevNodes) => [...prevNodes, newNode]);
54+
setOpen(false);
55+
}}
56+
>
57+
<DialogHeader>
58+
<DialogTitle>Add new Stage</DialogTitle>
59+
<DialogDescription>
60+
A stage is basically a compilation of processes.
61+
</DialogDescription>
62+
</DialogHeader>
63+
<div className="grid gap-6">
64+
<div className="grid items-center gap-1">
65+
<Label htmlFor="name">Name</Label>
66+
<Input
67+
id="name"
68+
name="name"
69+
className="w-full"
70+
placeholder="Enter name of the stage"
71+
/>
72+
</div>
4773
</div>
48-
</div>
49-
<DialogFooter>
50-
<Button type="submit">Create Stage</Button>
51-
</DialogFooter>
74+
<DialogFooter>
75+
<Button type="submit">Create Stage</Button>
76+
</DialogFooter>
77+
</form>
5278
</DialogContent>
5379
</Dialog>
5480
);

app/_components/custom-edge.jsx

+41-5
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,48 @@
1-
import { BaseEdge, getStraightPath } from "@xyflow/react";
1+
import { BaseEdge, EdgeLabelRenderer, getBezierPath } from "@xyflow/react";
2+
import { SquarePlus } from "lucide-react";
23

3-
export function CustomEdge({ sourceX, sourceY, targetX, targetY, ...props }) {
4-
const [edgePath] = getStraightPath({
4+
const CustomEdge = ({
5+
id,
6+
sourceX,
7+
sourceY,
8+
targetX,
9+
targetY,
10+
sourcePosition,
11+
targetPosition,
12+
data,
13+
...props
14+
}) => {
15+
const [edgePath, labelX, labelY] = getBezierPath({
516
sourceX,
617
sourceY,
18+
sourcePosition,
719
targetX,
820
targetY,
21+
targetPosition,
922
});
1023

11-
return <BaseEdge path={edgePath} {...props} />;
12-
}
24+
return (
25+
<>
26+
<BaseEdge id={id} path={edgePath} {...props} />
27+
<EdgeLabelRenderer>
28+
<div
29+
style={{
30+
position: "absolute",
31+
transform: `translate(-50%, -50%) translate(${labelX}px,${labelY}px)`,
32+
background: "#ffcc00",
33+
paddingInline: 8,
34+
paddingBlock: 4,
35+
borderRadius: 2,
36+
fontWeight: 700,
37+
}}
38+
className="nodrag nopan flex items-center gap-1 text-xs"
39+
>
40+
<SquarePlus size={14} />
41+
{data.label}
42+
</div>
43+
</EdgeLabelRenderer>
44+
</>
45+
);
46+
};
47+
48+
export default CustomEdge;

app/_components/initial-edges.jsx

+1-7
Original file line numberDiff line numberDiff line change
@@ -1,7 +1 @@
1-
export const initialEdges = [
2-
{ id: "a1-a2", source: "A-1", target: "A-2" },
3-
{ id: "a2-b", source: "A-2", target: "B" },
4-
{ id: "a2-c", source: "A-2", target: "C" },
5-
{ id: "b1-b2", source: "B-1", target: "B-2" },
6-
{ id: "b1-b3", source: "B-1", target: "B-3" },
7-
];
1+
export const initialEdges = [];

app/_components/initial-nodes.jsx

+1-76
Original file line numberDiff line numberDiff line change
@@ -1,76 +1 @@
1-
export const initialNodes = [
2-
{
3-
id: "A",
4-
type: "group",
5-
position: { x: 0, y: 0 },
6-
style: {
7-
width: 170,
8-
height: 140,
9-
},
10-
},
11-
{
12-
id: "A-1",
13-
type: "input",
14-
data: { label: "Child Node 1" },
15-
position: { x: 10, y: 10 },
16-
parentId: "A",
17-
extent: "parent",
18-
},
19-
{
20-
id: "A-2",
21-
data: { label: "Child Node 2" },
22-
position: { x: 10, y: 90 },
23-
parentId: "A",
24-
extent: "parent",
25-
},
26-
{
27-
id: "B",
28-
type: "output",
29-
position: { x: -100, y: 200 },
30-
data: null,
31-
style: {
32-
width: 170,
33-
height: 140,
34-
backgroundColor: "rgba(240,240,240,0.25)",
35-
},
36-
},
37-
{
38-
id: "B-1",
39-
data: { label: "Child 1" },
40-
position: { x: 50, y: 10 },
41-
parentId: "B",
42-
extent: "parent",
43-
draggable: false,
44-
style: {
45-
width: 60,
46-
},
47-
},
48-
{
49-
id: "B-2",
50-
data: { label: "Child 2" },
51-
position: { x: 10, y: 90 },
52-
parentId: "B",
53-
extent: "parent",
54-
draggable: false,
55-
style: {
56-
width: 60,
57-
},
58-
},
59-
{
60-
id: "B-3",
61-
data: { label: "Child 3" },
62-
position: { x: 100, y: 90 },
63-
parentId: "B",
64-
extent: "parent",
65-
draggable: false,
66-
style: {
67-
width: 60,
68-
},
69-
},
70-
{
71-
id: "C",
72-
type: "output",
73-
position: { x: 100, y: 200 },
74-
data: { label: "Node C" },
75-
},
76-
];
1+
export const initialNodes = [];

app/page.jsx

+13-6
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,10 @@ import "@xyflow/react/dist/style.css";
2020
import { AddStageModal } from "./_components/add-stage-modal";
2121
import { initialNodes } from "./_components/initial-nodes";
2222
import { initialEdges } from "./_components/initial-edges";
23-
import { CustomEdge } from "./_components/custom-edge";
23+
import CustomEdge from "./_components/custom-edge";
2424
import ResizableNodeSelected from "./_components/resizable-node-selected";
2525
import ResizableNode from "./_components/resizable-node";
26+
import { LabeledGroupNode } from "../components/labeled-group-node";
2627

2728
// Additional Styling
2829
/*
@@ -43,10 +44,17 @@ const applyNodeStyles = (node) => ({
4344
const nodeTypes = {
4445
ResizableNode,
4546
ResizableNodeSelected,
47+
LabeledGroupNode,
4648
};
49+
const edgeTypes = {
50+
CustomEdge,
51+
};
52+
4753
export default function Page() {
4854
const [nodes, setNodes] = useNodesState(initialNodes);
4955
const [edges, setEdges] = useEdgesState(initialEdges);
56+
console.log(edges);
57+
console.log(edges);
5058

5159
const defaultEdgeOptions = {
5260
animated: true,
@@ -55,11 +63,10 @@ export default function Page() {
5563
width: 20,
5664
height: 20,
5765
},
66+
type: "CustomEdge",
67+
data: { label: "Dependent" },
5868
};
5969
const panOnDrag = [1, 2];
60-
const edgeTypes = {
61-
"custom-edge": CustomEdge,
62-
};
6370

6471
const onConnect = useCallback(
6572
(params) => setEdges((eds) => addEdge(params, eds)),
@@ -77,7 +84,7 @@ export default function Page() {
7784
return (
7885
<div className="h-screen w-screen">
7986
<nav className="flex h-[60px] items-center border bg-background px-4 shadow">
80-
<AddStageModal />
87+
<AddStageModal setNodes={setNodes} />
8188
</nav>
8289
<main className="h-[calc(100vh-60px)]">
8390
<ReactFlow
@@ -91,7 +98,7 @@ export default function Page() {
9198
panOnDrag={panOnDrag}
9299
selectionOnDrag
93100
selectionMode={SelectionMode.Partial}
94-
fitView
101+
// fitView
95102
edgeTypes={edgeTypes}
96103
nodeTypes={nodeTypes}
97104
>

components/base-node.tsx

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import React from "react";
2+
import { cn } from "@/lib/utils";
3+
4+
export const BaseNode = React.forwardRef<
5+
HTMLDivElement,
6+
React.HTMLAttributes<HTMLDivElement> & { selected?: boolean }
7+
>(({ className, selected, ...props }, ref) => (
8+
<div
9+
ref={ref}
10+
className={cn(
11+
"rounded-md border bg-card p-5 text-card-foreground",
12+
className,
13+
selected ? "border-muted-foreground shadow-lg" : "",
14+
"hover:ring-1",
15+
)}
16+
{...props}
17+
/>
18+
));
19+
BaseNode.displayName = "BaseNode";

components/labeled-group-node.tsx

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { Handle, NodeResizer, Position } from "@xyflow/react";
2+
import { BaseNode } from "@/components/base-node";
3+
4+
export function LabeledGroupNode({ id, data, selected }: any) {
5+
const { label, setNodes } = data;
6+
7+
const handleAddProcess = () => {
8+
const newChildNode = {
9+
id: `child-${Date.now()}`,
10+
type: "default",
11+
data: { label: `Process of ${label}` },
12+
position: { x: 50, y: 50 },
13+
parentId: id,
14+
extent: "parent",
15+
};
16+
17+
setNodes((prevNodes: any) => [...prevNodes, newChildNode]);
18+
};
19+
20+
return (
21+
<BaseNode
22+
selected={selected}
23+
className="h-full overflow-hidden rounded-sm bg-white bg-opacity-50 p-0"
24+
>
25+
<Handle type="target" position={Position.Left} />
26+
<NodeResizer minWidth={300} minHeight={250} />
27+
{label && (
28+
<div className="absolute -top-8 flex w-full items-end justify-between text-card-foreground">
29+
<span className="text-sm font-bold">{label}</span>
30+
<button
31+
onClick={handleAddProcess}
32+
className="rounded bg-primary px-2 py-1 text-xs text-white"
33+
>
34+
Add process
35+
</button>
36+
</div>
37+
)}
38+
<Handle type="source" position={Position.Right} />
39+
</BaseNode>
40+
);
41+
}
42+
43+
LabeledGroupNode.displayName = "LabeledGroupNode";

0 commit comments

Comments
 (0)