Skip to content

Commit fac12cc

Browse files
committed
added process node modal for adding process details
1 parent cb37fd1 commit fac12cc

File tree

6 files changed

+282
-13
lines changed

6 files changed

+282
-13
lines changed

app/index.css

+1-1
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@
9292
border-radius: var(--xy-node-border-radius-default);
9393
background-color: var(--xy-node-background-color-default);
9494
text-align: center;
95-
padding: 15px 20px;
95+
/*padding: 15px 20px;*/
9696
font-size: 12px;
9797
border: var(--xy-node-border-default);
9898
border-color: var(--xy-theme-selected);

app/page.jsx

+2
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,8 @@ export default function Page() {
216216
<div className="dndflow">
217217
<main className="reactflow-wrapper" ref={reactFlowWrapper}>
218218
<ReactFlow
219+
elevateEdgesOnSelect={true}
220+
elevateNodesOnSelect={false}
219221
nodes={nodes}
220222
edges={edges}
221223
onNodesChange={onNodesChange}

components/custom-child-node.tsx

+117-11
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,124 @@
1-
import { Handle, Position } from "@xyflow/react";
1+
import { Handle, Position, useReactFlow } from "@xyflow/react";
22
import { BaseNode } from "@/components/base-node";
3+
import { useState } from "react";
4+
import { Button } from "@/components/ui/button";
5+
import {
6+
Dialog,
7+
DialogContent,
8+
DialogFooter,
9+
DialogHeader,
10+
DialogTitle,
11+
} from "@/components/ui/dialog";
12+
import { Input } from "@/components/ui/input";
13+
import { Label } from "@/components/ui/label";
14+
import {
15+
Select,
16+
SelectContent,
17+
SelectItem,
18+
SelectTrigger,
19+
SelectValue,
20+
} from "@/components/ui/select";
321

4-
export function CustomChildNode({ data, selected }: any) {
22+
export function CustomChildNode({ id, data, selected }: any) {
523
const { label } = data;
24+
const [isModalOpen, setIsModalOpen] = useState(true);
25+
const [childName, setChildName] = useState(label || "");
26+
const { updateNodeData } = useReactFlow();
27+
28+
const openModal = () => setIsModalOpen(true);
29+
const closeModal = () => setIsModalOpen(false);
30+
31+
const saveDetails = () => {
32+
// Update the node label
33+
if (childName.trim() !== "") {
34+
updateNodeData(id, { label: childName.trim() }); // Update node data
35+
}
36+
closeModal();
37+
};
38+
639
return (
7-
<BaseNode selected={selected}>
8-
{label && (
9-
<div>
10-
<span className="text-xs font-medium">{label}</span>
11-
</div>
12-
)}
13-
<Handle type="target" position={Position.Left} />
14-
<Handle type="source" position={Position.Right} />
15-
</BaseNode>
40+
<>
41+
<BaseNode
42+
selected={selected}
43+
onDoubleClick={openModal} // Open modal on double-click
44+
>
45+
{label && (
46+
<div
47+
style={{
48+
paddingInline: 20,
49+
paddingBlock: 15,
50+
}}
51+
>
52+
<span className="text-xs font-medium">{label}</span>
53+
</div>
54+
)}
55+
<Handle type="target" position={Position.Left} />
56+
<Handle type="source" position={Position.Right} />
57+
</BaseNode>
58+
59+
{/* Modal for editing node details */}
60+
<Dialog open={isModalOpen} onOpenChange={setIsModalOpen}>
61+
<DialogContent className="sm:max-w-[425px]">
62+
<DialogHeader>
63+
<DialogTitle>Enter Process Details</DialogTitle>
64+
</DialogHeader>
65+
<div className="space-y-4">
66+
<div className="flex flex-col gap-1">
67+
<Label
68+
htmlFor="childName"
69+
className="text-sm font-medium"
70+
>
71+
Process Name
72+
<span className="ms-0.5 text-destructive">
73+
*
74+
</span>
75+
</Label>
76+
<Input
77+
id="childName"
78+
value={childName}
79+
onChange={(e) => setChildName(e.target.value)}
80+
placeholder="Enter child name"
81+
required={true}
82+
/>
83+
</div>
84+
<div className="flex flex-col gap-1">
85+
<Label
86+
htmlFor="child-details-2"
87+
className="text-sm font-medium"
88+
>
89+
Process Admins
90+
<span className="ms-0.5 text-destructive">
91+
*
92+
</span>
93+
</Label>
94+
<Select>
95+
<SelectTrigger id="child-details-2">
96+
<SelectValue placeholder="Select Process admins" />
97+
</SelectTrigger>
98+
<SelectContent>
99+
<SelectItem value="admin1">
100+
Admin 1
101+
</SelectItem>
102+
<SelectItem value="admin2">
103+
Admin 2
104+
</SelectItem>
105+
<SelectItem value="admin3">
106+
Admin 3
107+
</SelectItem>
108+
</SelectContent>
109+
</Select>
110+
</div>
111+
</div>
112+
113+
<DialogFooter>
114+
<Button variant="secondary" onClick={closeModal}>
115+
Cancel
116+
</Button>
117+
<Button onClick={saveDetails}>Save</Button>
118+
</DialogFooter>
119+
</DialogContent>
120+
</Dialog>
121+
</>
16122
);
17123
}
18124

components/labeled-group-node.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ export function LabeledGroupNode({ id, data, selected }: any) {
7272
setIsEditing(true); // Enable editing on click
7373
}
7474
}}
75-
className="max-w-60 cursor-pointer truncate px-2 py-1 text-sm font-bold"
75+
className="max-w-60 cursor-pointer truncate px-2 py-1 text-sm font-bold transition hover:text-primary"
7676
>
7777
{label}
7878
</span>

components/ui/select.tsx

+160
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
"use client";
2+
3+
import * as React from "react";
4+
import * as SelectPrimitive from "@radix-ui/react-select";
5+
import { Check, ChevronDown, ChevronUp } from "lucide-react";
6+
7+
import { cn } from "@/lib/utils";
8+
9+
const Select = SelectPrimitive.Root;
10+
11+
const SelectGroup = SelectPrimitive.Group;
12+
13+
const SelectValue = SelectPrimitive.Value;
14+
15+
const SelectTrigger = React.forwardRef<
16+
React.ElementRef<typeof SelectPrimitive.Trigger>,
17+
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger>
18+
>(({ className, children, ...props }, ref) => (
19+
<SelectPrimitive.Trigger
20+
ref={ref}
21+
className={cn(
22+
"flex h-10 w-full items-center justify-between rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1",
23+
className,
24+
)}
25+
{...props}
26+
>
27+
{children}
28+
<SelectPrimitive.Icon asChild>
29+
<ChevronDown className="h-4 w-4 opacity-50" />
30+
</SelectPrimitive.Icon>
31+
</SelectPrimitive.Trigger>
32+
));
33+
SelectTrigger.displayName = SelectPrimitive.Trigger.displayName;
34+
35+
const SelectScrollUpButton = React.forwardRef<
36+
React.ElementRef<typeof SelectPrimitive.ScrollUpButton>,
37+
React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollUpButton>
38+
>(({ className, ...props }, ref) => (
39+
<SelectPrimitive.ScrollUpButton
40+
ref={ref}
41+
className={cn(
42+
"flex cursor-default items-center justify-center py-1",
43+
className,
44+
)}
45+
{...props}
46+
>
47+
<ChevronUp className="h-4 w-4" />
48+
</SelectPrimitive.ScrollUpButton>
49+
));
50+
SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName;
51+
52+
const SelectScrollDownButton = React.forwardRef<
53+
React.ElementRef<typeof SelectPrimitive.ScrollDownButton>,
54+
React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollDownButton>
55+
>(({ className, ...props }, ref) => (
56+
<SelectPrimitive.ScrollDownButton
57+
ref={ref}
58+
className={cn(
59+
"flex cursor-default items-center justify-center py-1",
60+
className,
61+
)}
62+
{...props}
63+
>
64+
<ChevronDown className="h-4 w-4" />
65+
</SelectPrimitive.ScrollDownButton>
66+
));
67+
SelectScrollDownButton.displayName =
68+
SelectPrimitive.ScrollDownButton.displayName;
69+
70+
const SelectContent = React.forwardRef<
71+
React.ElementRef<typeof SelectPrimitive.Content>,
72+
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content>
73+
>(({ className, children, position = "popper", ...props }, ref) => (
74+
<SelectPrimitive.Portal>
75+
<SelectPrimitive.Content
76+
ref={ref}
77+
className={cn(
78+
"relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
79+
position === "popper" &&
80+
"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
81+
className,
82+
)}
83+
position={position}
84+
{...props}
85+
>
86+
<SelectScrollUpButton />
87+
<SelectPrimitive.Viewport
88+
className={cn(
89+
"p-1",
90+
position === "popper" &&
91+
"h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]",
92+
)}
93+
>
94+
{children}
95+
</SelectPrimitive.Viewport>
96+
<SelectScrollDownButton />
97+
</SelectPrimitive.Content>
98+
</SelectPrimitive.Portal>
99+
));
100+
SelectContent.displayName = SelectPrimitive.Content.displayName;
101+
102+
const SelectLabel = React.forwardRef<
103+
React.ElementRef<typeof SelectPrimitive.Label>,
104+
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Label>
105+
>(({ className, ...props }, ref) => (
106+
<SelectPrimitive.Label
107+
ref={ref}
108+
className={cn("py-1.5 pl-8 pr-2 text-sm font-semibold", className)}
109+
{...props}
110+
/>
111+
));
112+
SelectLabel.displayName = SelectPrimitive.Label.displayName;
113+
114+
const SelectItem = React.forwardRef<
115+
React.ElementRef<typeof SelectPrimitive.Item>,
116+
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item>
117+
>(({ className, children, ...props }, ref) => (
118+
<SelectPrimitive.Item
119+
ref={ref}
120+
className={cn(
121+
"relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
122+
className,
123+
)}
124+
{...props}
125+
>
126+
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
127+
<SelectPrimitive.ItemIndicator>
128+
<Check className="h-4 w-4" />
129+
</SelectPrimitive.ItemIndicator>
130+
</span>
131+
132+
<SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
133+
</SelectPrimitive.Item>
134+
));
135+
SelectItem.displayName = SelectPrimitive.Item.displayName;
136+
137+
const SelectSeparator = React.forwardRef<
138+
React.ElementRef<typeof SelectPrimitive.Separator>,
139+
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Separator>
140+
>(({ className, ...props }, ref) => (
141+
<SelectPrimitive.Separator
142+
ref={ref}
143+
className={cn("-mx-1 my-1 h-px bg-muted", className)}
144+
{...props}
145+
/>
146+
));
147+
SelectSeparator.displayName = SelectPrimitive.Separator.displayName;
148+
149+
export {
150+
Select,
151+
SelectGroup,
152+
SelectValue,
153+
SelectTrigger,
154+
SelectContent,
155+
SelectLabel,
156+
SelectItem,
157+
SelectSeparator,
158+
SelectScrollUpButton,
159+
SelectScrollDownButton,
160+
};

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
"dependencies": {
1313
"@radix-ui/react-dialog": "^1.1.2",
1414
"@radix-ui/react-label": "^2.1.0",
15+
"@radix-ui/react-select": "^2.1.2",
1516
"@radix-ui/react-slot": "^1.1.0",
1617
"@xyflow/react": "^12.3.5",
1718
"class-variance-authority": "^0.7.0",

0 commit comments

Comments
 (0)