Skip to content
Open
Show file tree
Hide file tree
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
34 changes: 32 additions & 2 deletions src/features/editor/views/GraphView/CustomNode/ObjectNode.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import React from "react";
import { ActionIcon } from "@mantine/core";
import { LuPencil } from "react-icons/lu";
import type { CustomNodeProps } from ".";
import { NODE_DIMENSIONS } from "../../../../../constants/graph";
import type { NodeData } from "../../../../../types/graph";
import useGraph from "../stores/useGraph";
import { useModal } from "../../../../../store/useModal";
import { TextRenderer } from "./TextRenderer";
import * as Styled from "./styles";

Expand All @@ -10,10 +14,21 @@ type RowProps = {
x: number;
y: number;
index: number;
nodeData: NodeData;
};

const Row = ({ row, x, y, index }: RowProps) => {
const Row = ({ row, x, y, index, nodeData }: RowProps) => {
const rowPosition = index * NODE_DIMENSIONS.ROW_HEIGHT;
const setSelectedNode = useGraph(state => state.setSelectedNode);
const setVisible = useModal(state => state.setVisible);
const [isHovered, setIsHovered] = React.useState(false);
const isPrimitive = row.type !== "object" && row.type !== "array";

const handleEdit = (e: React.MouseEvent) => {
e.stopPropagation();
setSelectedNode(nodeData);
setVisible("EditNodeModal", true);
};

const getRowText = () => {
if (row.type === "object") return `{${row.childrenCount ?? 0} keys}`;
Expand All @@ -27,9 +42,24 @@ const Row = ({ row, x, y, index }: RowProps) => {
data-key={`${row.key}: ${row.value}`}
data-x={x}
data-y={y + rowPosition}
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}
>
<Styled.StyledKey $type="object">{row.key}: </Styled.StyledKey>
<TextRenderer>{getRowText()}</TextRenderer>
{isPrimitive && isHovered && (
<Styled.StyledEditButton>
<ActionIcon
size="xs"
variant="subtle"
color="blue"
onClick={handleEdit}
aria-label="Edit value"
>
<LuPencil size={12} />
</ActionIcon>
</Styled.StyledEditButton>
)}
</Styled.StyledRow>
);
};
Expand All @@ -44,7 +74,7 @@ const Node = ({ node, x, y }: CustomNodeProps) => (
$isObject
>
{node.text.map((row, index) => (
<Row key={`${node.id}-${index}`} row={row} x={x} y={y} index={index} />
<Row key={`${node.id}-${index}`} row={row} x={x} y={y} index={index} nodeData={node} />
))}
</Styled.StyledForeignObject>
);
Expand Down
50 changes: 40 additions & 10 deletions src/features/editor/views/GraphView/CustomNode/TextNode.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import React from "react";
import styled from "styled-components";
import { ActionIcon } from "@mantine/core";
import { LuPencil } from "react-icons/lu";
import type { CustomNodeProps } from ".";
import useConfig from "../../../../../store/useConfig";
import useGraph from "../stores/useGraph";
import { useModal } from "../../../../../store/useModal";
import { isContentImage } from "../lib/utils/calculateNodeSize";
import { TextRenderer } from "./TextRenderer";
import * as Styled from "./styles";
Expand Down Expand Up @@ -29,32 +33,58 @@ const StyledImage = styled.img`
const Node = ({ node, x, y }: CustomNodeProps) => {
const { text, width, height } = node;
const imagePreviewEnabled = useConfig(state => state.imagePreviewEnabled);
const setSelectedNode = useGraph(state => state.setSelectedNode);
const setVisible = useModal(state => state.setVisible);
const [isHovered, setIsHovered] = React.useState(false);
const isImage = imagePreviewEnabled && isContentImage(JSON.stringify(text[0].value));
const value = text[0].value;

const handleEdit = (e: React.MouseEvent) => {
e.stopPropagation();
setSelectedNode(node);
setVisible("EditNodeModal", true);
};

return (
<Styled.StyledForeignObject
data-id={`node-${node.id}`}
width={width}
height={height}
x={0}
y={0}
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}
>
{isImage ? (
<StyledImageWrapper>
<StyledImage src={JSON.stringify(text[0].value)} width="70" height="70" loading="lazy" />
</StyledImageWrapper>
) : (
<StyledTextNodeWrapper
data-x={x}
data-y={y}
data-key={JSON.stringify(text)}
$isParent={false}
>
<Styled.StyledKey $value={value} $type={typeof text[0].value}>
<TextRenderer>{value}</TextRenderer>
</Styled.StyledKey>
</StyledTextNodeWrapper>
<>
<StyledTextNodeWrapper
data-x={x}
data-y={y}
data-key={JSON.stringify(text)}
$isParent={false}
>
<Styled.StyledKey $value={value} $type={typeof text[0].value}>
<TextRenderer>{value}</TextRenderer>
</Styled.StyledKey>
</StyledTextNodeWrapper>
{isHovered && (
<Styled.StyledEditButton>
<ActionIcon
size="xs"
variant="subtle"
color="blue"
onClick={handleEdit}
aria-label="Edit value"
>
<LuPencil size={12} />
</ActionIcon>
</Styled.StyledEditButton>
)}
</>
)}
</Styled.StyledForeignObject>
);
Expand Down
10 changes: 10 additions & 0 deletions src/features/editor/views/GraphView/CustomNode/styles.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export const StyledForeignObject = styled.foreignObject<{ $isObject?: boolean }>
font-weight: 500;
overflow: hidden;
pointer-events: none;
position: relative;

&.searched {
background: rgba(27, 255, 0, 0.1);
Expand Down Expand Up @@ -84,6 +85,7 @@ export const StyledRow = styled.span<{ $value: TextColorFn["$value"] }>`
white-space: nowrap;
border-bottom: 1px solid ${({ theme }) => theme.NODE_COLORS.DIVIDER};
box-sizing: border-box;
position: relative;

&:last-of-type {
border-bottom: none;
Expand All @@ -99,3 +101,11 @@ export const StyledChildrenCount = styled.span`
padding: 10px;
margin-left: -15px;
`;

export const StyledEditButton = styled.span`
position: absolute;
top: 2px;
right: 4px;
pointer-events: all;
z-index: 10;
`;
Loading