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
30 changes: 26 additions & 4 deletions src/features/editor/views/GraphView/CustomNode/ObjectNode.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,16 +43,38 @@ const Node = ({ node, x, y }: CustomNodeProps) => (
y={0}
$isObject
>
{node.text.map((row, index) => (
<Row key={`${node.id}-${index}`} row={row} x={x} y={y} index={index} />
))}
<div style={{ position: "relative", width: "100%", height: "100%" }}>
{(() => {
const firstRow = node.text?.[0];
const firstPrimitiveValue = typeof firstRow?.value === "string" || typeof firstRow?.value === "number" ? String(firstRow?.value) : undefined;
const shouldShowLabel = Boolean(node.name && node.name !== firstPrimitiveValue);
return shouldShowLabel ? (
<div style={{ position: "absolute", top: 2, left: 6, fontSize: 10, fontWeight: 700, pointerEvents: "none" }}>
{node.name}
</div>
) : null;
})()}

<div style={{ paddingTop: (() => {
const firstRow = node.text?.[0];
const firstPrimitiveValue = typeof firstRow?.value === "string" || typeof firstRow?.value === "number" ? String(firstRow?.value) : undefined;
const shouldShowLabel = Boolean(node.name && node.name !== firstPrimitiveValue);
return shouldShowLabel ? 16 : 0;
})() }}>
{node.text.map((row, index) => (
<Row key={`${node.id}-${index}`} row={row} x={x} y={y} index={index} />
))}
</div>
</div>
</Styled.StyledForeignObject>
);

function propsAreEqual(prev: CustomNodeProps, next: CustomNodeProps) {
return (
JSON.stringify(prev.node.text) === JSON.stringify(next.node.text) &&
prev.node.width === next.node.width
prev.node.width === next.node.width &&
prev.node.name === next.node.name &&
prev.node.color === next.node.color
);
}

Expand Down
51 changes: 34 additions & 17 deletions src/features/editor/views/GraphView/CustomNode/TextNode.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ const Node = ({ node, x, y }: CustomNodeProps) => {
const isImage = imagePreviewEnabled && isContentImage(JSON.stringify(text[0].value));
const value = text[0].value;

const firstPrimitiveValue = typeof text[0]?.value === "string" || typeof text[0]?.value === "number" ? String(text[0]?.value) : undefined;
const shouldShowLabel = Boolean(node.name && node.name !== firstPrimitiveValue);

return (
<Styled.StyledForeignObject
data-id={`node-${node.id}`}
Expand All @@ -40,28 +43,42 @@ const Node = ({ node, x, y }: CustomNodeProps) => {
x={0}
y={0}
>
{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>
)}
<div style={{ position: "relative", width: "100%", height: "100%" }}>
{shouldShowLabel ? (
<div style={{ position: "absolute", top: 2, left: 6, fontSize: 10, fontWeight: 700, pointerEvents: "none" }}>
{node.name}
</div>
) : null}

{isImage ? (
<StyledImageWrapper style={{ paddingTop: shouldShowLabel ? 16 : 0 }}>
<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}
style={{ paddingTop: shouldShowLabel ? 16 : 0 }}
>
<Styled.StyledKey $value={value} $type={typeof text[0].value}>
<TextRenderer>{value}</TextRenderer>
</Styled.StyledKey>
</StyledTextNodeWrapper>
)}
</div>
</Styled.StyledForeignObject>
);
};

function propsAreEqual(prev: CustomNodeProps, next: CustomNodeProps) {
return prev.node.text === next.node.text && prev.node.width === next.node.width;
return (
prev.node.text === next.node.text &&
prev.node.width === next.node.width &&
prev.node.name === next.node.name &&
prev.node.color === next.node.color
);
}

export const TextNode = React.memo(Node, propsAreEqual);
8 changes: 5 additions & 3 deletions src/features/editor/views/GraphView/CustomNode/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,9 @@ const CustomNodeWrapper = (nodeProps: NodeProps<NodeData>) => {

const handleNodeClick = React.useCallback(
(_: React.MouseEvent<SVGGElement, MouseEvent>, data: NodeData) => {
if (setSelectedNode) setSelectedNode(data);
// show modal first for perceived responsiveness, then set selected node data
setVisible("NodeModal", true);
if (setSelectedNode) setSelectedNode(data);
},
[setSelectedNode, setVisible]
);
Expand All @@ -41,9 +42,10 @@ const CustomNodeWrapper = (nodeProps: NodeProps<NodeData>) => {
ev.currentTarget.style.stroke = colorScheme === "dark" ? "#424242" : "#BCBEC0";
}}
style={{
fill: colorScheme === "dark" ? "#292929" : "#ffffff",
stroke: colorScheme === "dark" ? "#424242" : "#BCBEC0",
fill: (nodeProps.properties as NodeData).color ?? (colorScheme === "dark" ? "#292929" : "#ffffff"),
stroke: (nodeProps.properties as NodeData).color ?? (colorScheme === "dark" ? "#424242" : "#BCBEC0"),
strokeWidth: 1,
pointerEvents: "auto",
}}
>
{({ node, x, y }) => {
Expand Down
7 changes: 7 additions & 0 deletions src/features/editor/views/GraphView/stores/useGraph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ interface GraphActions {
setDirection: (direction: CanvasDirection) => void;
setViewPort: (ref: ViewPort) => void;
setSelectedNode: (nodeData: NodeData) => void;
updateNode: (id: string, patch: Partial<NodeData>) => void;
focusFirstNode: () => void;
toggleFullscreen: (value: boolean) => void;
zoomIn: () => void;
Expand All @@ -49,6 +50,12 @@ const useGraph = create<Graph & GraphActions>((set, get) => ({
...initialStates,
clearGraph: () => set({ nodes: [], edges: [], loading: false }),
setSelectedNode: nodeData => set({ selectedNode: nodeData }),
updateNode: (id, patch) =>
set(state => {
const nodes = state.nodes.map(n => (n.id === id ? { ...n, ...patch } : n));
const selectedNode = state.selectedNode?.id === id ? { ...state.selectedNode, ...patch } : state.selectedNode;
return { nodes, selectedNode } as any;
}),
setGraph: (data, options) => {
const { nodes, edges } = parser(data ?? useJson.getState().json);

Expand Down
Loading