Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 111 additions & 10 deletions src/features/modals/NodeModal/index.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import React from "react";
import type { ModalProps } from "@mantine/core";
import { Modal, Stack, Text, ScrollArea, Flex, CloseButton } from "@mantine/core";
import { Modal, Stack, Text, ScrollArea, Flex, CloseButton, Button, Group, TextInput } from "@mantine/core";
import { CodeHighlight } from "@mantine/code-highlight";
import { MdEdit } from "react-icons/md";
import type { NodeData } from "../../../types/graph";
import useGraph from "../../editor/views/GraphView/stores/useGraph";
import useJson from "../../../store/useJson";

// return object from json removing array and object fields
const normalizeNodeData = (nodeRows: NodeData["text"]) => {
Expand All @@ -28,6 +30,64 @@ const jsonPathToString = (path?: NodeData["path"]) => {

export const NodeModal = ({ opened, onClose }: ModalProps) => {
const nodeData = useGraph(state => state.selectedNode);
const [isEditing, setIsEditing] = React.useState(false);
const [editingIndex, setEditingIndex] = React.useState(-1);
const [editValue, setEditValue] = React.useState("");

const json = useJson(state => state.json);
const setJson = useJson(state => state.setJson);

React.useEffect(() => {
if (editingIndex >= 0 && nodeData?.text?.[editingIndex]?.value !== undefined) {
setEditValue(String(nodeData.text[editingIndex].value));
}
}, [nodeData, editingIndex]);

const handleEditClick = (index: number) => {
setEditingIndex(index);
setIsEditing(true);
};

const handleSave = () => {
if (!nodeData?.path || editingIndex < 0) return;

try {
const jsonObj = JSON.parse(json);
let current = jsonObj;

// Navigate to the parent object/array
for (let i = 0; i < nodeData.path.length - 1; i++) {
current = current[nodeData.path[i]];
}

// Update the value
const lastKey = nodeData.path[nodeData.path.length - 1];

// Try to parse as number, boolean, or null first
let newValue: any = editValue;
if (editValue === "null") {
newValue = null;
} else if (editValue === "true") {
newValue = true;
} else if (editValue === "false") {
newValue = false;
} else if (!isNaN(Number(editValue)) && editValue !== "") {
newValue = Number(editValue);
}

current[lastKey] = newValue;
setJson(JSON.stringify(jsonObj));
setIsEditing(false);
setEditingIndex(-1);
} catch (error) {
console.error("Failed to update value:", error);
}
};

const handleCancel = () => {
setIsEditing(false);
setEditingIndex(-1);
};

return (
<Modal size="auto" opened={opened} onClose={onClose} centered withCloseButton={false}>
Expand All @@ -39,15 +99,56 @@ export const NodeModal = ({ opened, onClose }: ModalProps) => {
</Text>
<CloseButton onClick={onClose} />
</Flex>
<ScrollArea.Autosize mah={250} maw={600}>
<CodeHighlight
code={normalizeNodeData(nodeData?.text ?? [])}
miw={350}
maw={600}
language="json"
withCopyButton
/>
</ScrollArea.Autosize>
{!isEditing ? (
<>
<ScrollArea.Autosize mah={250} maw={600}>
<CodeHighlight
code={normalizeNodeData(nodeData?.text ?? [])}
miw={350}
maw={600}
language="json"
withCopyButton
/>
</ScrollArea.Autosize>
{nodeData?.text && nodeData.text.length > 0 && (
<Stack gap="xs">
{nodeData.text.map((row, index) => {
if (row.type === "array" || row.type === "object") return null;
return (
<Button
key={index}
leftSection={<MdEdit />}
variant="light"
size="xs"
onClick={() => handleEditClick(index)}
fullWidth
>
Edit {row.key ? `${row.key}` : "Value"}
</Button>
);
})}
</Stack>
)}
</>
) : (
<>
<TextInput
label="New Value"
placeholder="Enter new value"
value={editValue}
onChange={e => setEditValue(e.currentTarget.value)}
autoFocus
/>
<Group grow>
<Button size="xs" onClick={handleSave}>
Save
</Button>
<Button size="xs" variant="default" onClick={handleCancel}>
Cancel
</Button>
</Group>
</>
)}
</Stack>
<Text fz="xs" fw={500}>
JSON Path
Expand Down