Skip to content

Commit cf7e254

Browse files
committed
add edit button to node visualization panel
1 parent eb409d2 commit cf7e254

File tree

6 files changed

+350
-13
lines changed

6 files changed

+350
-13
lines changed

src/features/editor/views/GraphView/CustomNode/ObjectNode.tsx

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
import React from "react";
2+
import { ActionIcon } from "@mantine/core";
3+
import { LuPencil } from "react-icons/lu";
24
import type { CustomNodeProps } from ".";
35
import { NODE_DIMENSIONS } from "../../../../../constants/graph";
46
import type { NodeData } from "../../../../../types/graph";
7+
import useGraph from "../stores/useGraph";
8+
import { useModal } from "../../../../../store/useModal";
59
import { TextRenderer } from "./TextRenderer";
610
import * as Styled from "./styles";
711

@@ -10,10 +14,21 @@ type RowProps = {
1014
x: number;
1115
y: number;
1216
index: number;
17+
nodeData: NodeData;
1318
};
1419

15-
const Row = ({ row, x, y, index }: RowProps) => {
20+
const Row = ({ row, x, y, index, nodeData }: RowProps) => {
1621
const rowPosition = index * NODE_DIMENSIONS.ROW_HEIGHT;
22+
const setSelectedNode = useGraph(state => state.setSelectedNode);
23+
const setVisible = useModal(state => state.setVisible);
24+
const [isHovered, setIsHovered] = React.useState(false);
25+
const isPrimitive = row.type !== "object" && row.type !== "array";
26+
27+
const handleEdit = (e: React.MouseEvent) => {
28+
e.stopPropagation();
29+
setSelectedNode(nodeData);
30+
setVisible("EditNodeModal", true);
31+
};
1732

1833
const getRowText = () => {
1934
if (row.type === "object") return `{${row.childrenCount ?? 0} keys}`;
@@ -27,9 +42,24 @@ const Row = ({ row, x, y, index }: RowProps) => {
2742
data-key={`${row.key}: ${row.value}`}
2843
data-x={x}
2944
data-y={y + rowPosition}
45+
onMouseEnter={() => setIsHovered(true)}
46+
onMouseLeave={() => setIsHovered(false)}
3047
>
3148
<Styled.StyledKey $type="object">{row.key}: </Styled.StyledKey>
3249
<TextRenderer>{getRowText()}</TextRenderer>
50+
{isPrimitive && isHovered && (
51+
<Styled.StyledEditButton>
52+
<ActionIcon
53+
size="xs"
54+
variant="subtle"
55+
color="blue"
56+
onClick={handleEdit}
57+
aria-label="Edit value"
58+
>
59+
<LuPencil size={12} />
60+
</ActionIcon>
61+
</Styled.StyledEditButton>
62+
)}
3363
</Styled.StyledRow>
3464
);
3565
};
@@ -44,7 +74,7 @@ const Node = ({ node, x, y }: CustomNodeProps) => (
4474
$isObject
4575
>
4676
{node.text.map((row, index) => (
47-
<Row key={`${node.id}-${index}`} row={row} x={x} y={y} index={index} />
77+
<Row key={`${node.id}-${index}`} row={row} x={x} y={y} index={index} nodeData={node} />
4878
))}
4979
</Styled.StyledForeignObject>
5080
);

src/features/editor/views/GraphView/CustomNode/TextNode.tsx

Lines changed: 40 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
import React from "react";
22
import styled from "styled-components";
3+
import { ActionIcon } from "@mantine/core";
4+
import { LuPencil } from "react-icons/lu";
35
import type { CustomNodeProps } from ".";
46
import useConfig from "../../../../../store/useConfig";
7+
import useGraph from "../stores/useGraph";
8+
import { useModal } from "../../../../../store/useModal";
59
import { isContentImage } from "../lib/utils/calculateNodeSize";
610
import { TextRenderer } from "./TextRenderer";
711
import * as Styled from "./styles";
@@ -29,32 +33,58 @@ const StyledImage = styled.img`
2933
const Node = ({ node, x, y }: CustomNodeProps) => {
3034
const { text, width, height } = node;
3135
const imagePreviewEnabled = useConfig(state => state.imagePreviewEnabled);
36+
const setSelectedNode = useGraph(state => state.setSelectedNode);
37+
const setVisible = useModal(state => state.setVisible);
38+
const [isHovered, setIsHovered] = React.useState(false);
3239
const isImage = imagePreviewEnabled && isContentImage(JSON.stringify(text[0].value));
3340
const value = text[0].value;
3441

42+
const handleEdit = (e: React.MouseEvent) => {
43+
e.stopPropagation();
44+
setSelectedNode(node);
45+
setVisible("EditNodeModal", true);
46+
};
47+
3548
return (
3649
<Styled.StyledForeignObject
3750
data-id={`node-${node.id}`}
3851
width={width}
3952
height={height}
4053
x={0}
4154
y={0}
55+
onMouseEnter={() => setIsHovered(true)}
56+
onMouseLeave={() => setIsHovered(false)}
4257
>
4358
{isImage ? (
4459
<StyledImageWrapper>
4560
<StyledImage src={JSON.stringify(text[0].value)} width="70" height="70" loading="lazy" />
4661
</StyledImageWrapper>
4762
) : (
48-
<StyledTextNodeWrapper
49-
data-x={x}
50-
data-y={y}
51-
data-key={JSON.stringify(text)}
52-
$isParent={false}
53-
>
54-
<Styled.StyledKey $value={value} $type={typeof text[0].value}>
55-
<TextRenderer>{value}</TextRenderer>
56-
</Styled.StyledKey>
57-
</StyledTextNodeWrapper>
63+
<>
64+
<StyledTextNodeWrapper
65+
data-x={x}
66+
data-y={y}
67+
data-key={JSON.stringify(text)}
68+
$isParent={false}
69+
>
70+
<Styled.StyledKey $value={value} $type={typeof text[0].value}>
71+
<TextRenderer>{value}</TextRenderer>
72+
</Styled.StyledKey>
73+
</StyledTextNodeWrapper>
74+
{isHovered && (
75+
<Styled.StyledEditButton>
76+
<ActionIcon
77+
size="xs"
78+
variant="subtle"
79+
color="blue"
80+
onClick={handleEdit}
81+
aria-label="Edit value"
82+
>
83+
<LuPencil size={12} />
84+
</ActionIcon>
85+
</Styled.StyledEditButton>
86+
)}
87+
</>
5888
)}
5989
</Styled.StyledForeignObject>
6090
);

src/features/editor/views/GraphView/CustomNode/styles.tsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ export const StyledForeignObject = styled.foreignObject<{ $isObject?: boolean }>
3131
font-weight: 500;
3232
overflow: hidden;
3333
pointer-events: none;
34+
position: relative;
3435
3536
&.searched {
3637
background: rgba(27, 255, 0, 0.1);
@@ -84,6 +85,7 @@ export const StyledRow = styled.span<{ $value: TextColorFn["$value"] }>`
8485
white-space: nowrap;
8586
border-bottom: 1px solid ${({ theme }) => theme.NODE_COLORS.DIVIDER};
8687
box-sizing: border-box;
88+
position: relative;
8789
8890
&:last-of-type {
8991
border-bottom: none;
@@ -99,3 +101,11 @@ export const StyledChildrenCount = styled.span`
99101
padding: 10px;
100102
margin-left: -15px;
101103
`;
104+
105+
export const StyledEditButton = styled.span`
106+
position: absolute;
107+
top: 2px;
108+
right: 4px;
109+
pointer-events: all;
110+
z-index: 10;
111+
`;

0 commit comments

Comments
 (0)