Skip to content

Commit 5090f8d

Browse files
committed
added code
1 parent a4ffeec commit 5090f8d

File tree

7 files changed

+176
-126
lines changed

7 files changed

+176
-126
lines changed

app/_components/add-stage-modal.jsx

Lines changed: 58 additions & 32 deletions
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

Lines changed: 41 additions & 5 deletions
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

Lines changed: 1 addition & 7 deletions
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

Lines changed: 1 addition & 76 deletions
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

Lines changed: 13 additions & 6 deletions
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

Lines changed: 19 additions & 0 deletions
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

Lines changed: 43 additions & 0 deletions
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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy