Skip to content

Commit 06c5f5a

Browse files
committed
added edit button and functionality on nodes
1 parent eb409d2 commit 06c5f5a

File tree

1 file changed

+108
-10
lines changed

1 file changed

+108
-10
lines changed

src/features/modals/NodeModal/index.tsx

Lines changed: 108 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,17 @@
11
import React from "react";
22
import type { ModalProps } from "@mantine/core";
3-
import { Modal, Stack, Text, ScrollArea, Flex, CloseButton } from "@mantine/core";
3+
import { Modal, Stack, Text, ScrollArea, Flex, CloseButton, Button, Textarea } from "@mantine/core";
44
import { CodeHighlight } from "@mantine/code-highlight";
55
import type { NodeData } from "../../../types/graph";
66
import useGraph from "../../editor/views/GraphView/stores/useGraph";
7+
import useJson from "../../../store/useJson";
78

89
// return object from json removing array and object fields
910
const normalizeNodeData = (nodeRows: NodeData["text"]) => {
1011
if (!nodeRows || nodeRows.length === 0) return "{}";
1112
if (nodeRows.length === 1 && !nodeRows[0].key) return `${nodeRows[0].value}`;
1213

13-
const obj = {};
14+
const obj: Record<string, any> = {};
1415
nodeRows?.forEach(row => {
1516
if (row.type !== "array" && row.type !== "object") {
1617
if (row.key) obj[row.key] = row.value;
@@ -28,6 +29,73 @@ const jsonPathToString = (path?: NodeData["path"]) => {
2829

2930
export const NodeModal = ({ opened, onClose }: ModalProps) => {
3031
const nodeData = useGraph(state => state.selectedNode);
32+
const getJson = useJson.getState().getJson;
33+
const setJson = useJson.getState().setJson;
34+
35+
const [isEditing, setIsEditing] = React.useState(false);
36+
const [editedContent, setEditedContent] = React.useState("");
37+
const [error, setError] = React.useState<string | null>(null);
38+
39+
React.useEffect(() => {
40+
// reset editing state whenever node changes or modal opened/closed
41+
setIsEditing(false);
42+
setError(null);
43+
setEditedContent("");
44+
}, [nodeData, opened]);
45+
46+
const enterEditMode = () => {
47+
setError(null);
48+
setEditedContent(normalizeNodeData(nodeData?.text ?? []));
49+
setIsEditing(true);
50+
};
51+
52+
// helper to set value at path inside an object (mutates obj)
53+
const setValueAtPath = (obj: any, path: NodeData["path"] | undefined, value: any) => {
54+
if (!path || path.length === 0) {
55+
return value;
56+
}
57+
58+
let curr: any = obj;
59+
for (let i = 0; i < path.length - 1; i++) {
60+
const seg = path[i] as string | number;
61+
if (typeof seg === "number") {
62+
if (!Array.isArray(curr[seg])) curr[seg] = [];
63+
} else {
64+
if (curr[seg] === undefined) curr[seg] = {};
65+
}
66+
curr = curr[seg];
67+
}
68+
69+
const last = path[path.length - 1] as string | number;
70+
curr[last] = value;
71+
return obj;
72+
};
73+
74+
const handleSave = () => {
75+
setError(null);
76+
try {
77+
const newValue = JSON.parse(editedContent);
78+
79+
// get current app JSON
80+
const raw = getJson();
81+
const parsed = JSON.parse(raw);
82+
83+
const updated = setValueAtPath(parsed, nodeData?.path, newValue);
84+
85+
setJson(JSON.stringify(updated, null, 2));
86+
87+
setIsEditing(false);
88+
onClose?.();
89+
} catch (err: any) {
90+
setError(err?.message ?? String(err));
91+
}
92+
};
93+
94+
const handleCancel = () => {
95+
setIsEditing(false);
96+
setError(null);
97+
setEditedContent("");
98+
};
3199

32100
return (
33101
<Modal size="auto" opened={opened} onClose={onClose} centered withCloseButton={false}>
@@ -37,16 +105,46 @@ export const NodeModal = ({ opened, onClose }: ModalProps) => {
37105
<Text fz="xs" fw={500}>
38106
Content
39107
</Text>
40-
<CloseButton onClick={onClose} />
108+
<Flex gap="xs" align="center">
109+
{!isEditing ? (
110+
<Button size="xs" variant="outline" onClick={enterEditMode}>
111+
Edit
112+
</Button>
113+
) : (
114+
<>
115+
<Button size="xs" color="blue" onClick={handleSave}>
116+
Save
117+
</Button>
118+
<Button size="xs" variant="outline" onClick={handleCancel}>
119+
Cancel
120+
</Button>
121+
</>
122+
)}
123+
<CloseButton onClick={onClose} />
124+
</Flex>
41125
</Flex>
42126
<ScrollArea.Autosize mah={250} maw={600}>
43-
<CodeHighlight
44-
code={normalizeNodeData(nodeData?.text ?? [])}
45-
miw={350}
46-
maw={600}
47-
language="json"
48-
withCopyButton
49-
/>
127+
{!isEditing ? (
128+
<CodeHighlight
129+
code={normalizeNodeData(nodeData?.text ?? [])}
130+
miw={350}
131+
maw={600}
132+
language="json"
133+
withCopyButton
134+
/>
135+
) : (
136+
<Textarea
137+
minRows={6}
138+
value={editedContent}
139+
onChange={e => setEditedContent(e.currentTarget.value)}
140+
styles={{ input: { fontFamily: "monospace" } }}
141+
/>
142+
)}
143+
{error && (
144+
<Text fz="xs" color="red" mt="xs">
145+
{error}
146+
</Text>
147+
)}
50148
</ScrollArea.Autosize>
51149
</Stack>
52150
<Text fz="xs" fw={500}>

0 commit comments

Comments
 (0)