Skip to content

Commit 4a604ca

Browse files
committed
feat: update graph UI
1 parent d58d090 commit 4a604ca

File tree

9 files changed

+69
-32
lines changed

9 files changed

+69
-32
lines changed

src/constants/graph.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export const NODE_DIMENSIONS = {
2+
ROW_HEIGHT: 24, // Regular row height
3+
PARENT_HEIGHT: 36, // Height for parent nodes
4+
} as const;

src/constants/theme.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ const nodeColors = {
4747
PARENT_ARR: "#FC9A40",
4848
PARENT_OBJ: "#59b8ff",
4949
CHILD_COUNT: "white",
50+
DIVIDER: "#383838",
5051
},
5152
},
5253
light: {
@@ -63,6 +64,7 @@ const nodeColors = {
6364
PARENT_ARR: "#FF6B00",
6465
PARENT_OBJ: "#761CEA",
6566
CHILD_COUNT: "#535353",
67+
DIVIDER: "#e6e6e6",
6668
},
6769
},
6870
};
@@ -89,9 +91,9 @@ export const darkTheme = {
8991
MODAL_BACKGROUND: "#36393E",
9092
TEXT_NORMAL: "#dcddde",
9193
TEXT_POSITIVE: "hsl(139,calc(var(--saturation-factor, 1)*51.6%),52.2%)",
92-
GRID_BG_COLOR: "#1E1E1E",
93-
GRID_COLOR_PRIMARY: "#272626",
94-
GRID_COLOR_SECONDARY: "#232323",
94+
GRID_BG_COLOR: "#141414",
95+
GRID_COLOR_PRIMARY: "#1c1b1b",
96+
GRID_COLOR_SECONDARY: "#191919",
9597
};
9698

9799
export const lightTheme = {
@@ -116,9 +118,9 @@ export const lightTheme = {
116118
MODAL_BACKGROUND: "#FFFFFF",
117119
TEXT_NORMAL: "#2e3338",
118120
TEXT_POSITIVE: "#008736",
119-
GRID_BG_COLOR: "#f3f3f3",
120-
GRID_COLOR_PRIMARY: "#E0E0E0",
121-
GRID_COLOR_SECONDARY: "#E4E4E4",
121+
GRID_BG_COLOR: "#f7f7f7",
122+
GRID_COLOR_PRIMARY: "#ebe8e8",
123+
GRID_COLOR_SECONDARY: "#f2eeee",
122124
};
123125

124126
const themeDs = {
Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,21 @@
11
import React from "react";
2+
import { useComputedColorScheme } from "@mantine/core";
23
import type { EdgeProps } from "reaflow";
34
import { Edge } from "reaflow";
45

56
const CustomEdgeWrapper = (props: EdgeProps) => {
6-
return <Edge containerClassName={`edge-${props.id}`} {...props} />;
7+
const colorScheme = useComputedColorScheme();
8+
9+
return (
10+
<Edge
11+
containerClassName={`edge-${props.id}`}
12+
style={{
13+
stroke: colorScheme === "dark" ? "#444444" : "#BCBEC0",
14+
strokeWidth: 1.5,
15+
}}
16+
{...props}
17+
/>
18+
);
719
};
820

921
export const CustomEdge = React.memo(CustomEdgeWrapper);

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import React from "react";
2+
import { NODE_DIMENSIONS } from "src/constants/graph";
23
import type { CustomNodeProps } from "src/features/editor/views/GraphView/CustomNode";
34
import { TextRenderer } from "./TextRenderer";
45
import * as Styled from "./styles";
@@ -17,8 +18,10 @@ const Row = ({ val, x, y, index }: RowProps) => {
1718
const rowKey = JSON.stringify(val[0]).replaceAll('"', "");
1819
const rowValue = JSON.stringify(val[1]);
1920

21+
const rowPosition = index * NODE_DIMENSIONS.ROW_HEIGHT;
22+
2023
return (
21-
<Styled.StyledRow $value={rowValue} data-key={key} data-x={x} data-y={y + index * 17.8}>
24+
<Styled.StyledRow $value={rowValue} data-key={key} data-x={x} data-y={y + rowPosition}>
2225
<Styled.StyledKey $type="object">{rowKey}: </Styled.StyledKey>
2326
<TextRenderer>{rowValue}</TextRenderer>
2427
</Styled.StyledRow>

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

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,20 +17,23 @@ const StyledExpand = styled.button`
1717
color: ${({ theme }) => theme.TEXT_NORMAL};
1818
background: rgba(0, 0, 0, 0.1);
1919
height: 100%;
20-
width: 40px;
20+
width: 36px;
2121
border-left: 1px solid ${({ theme }) => theme.BACKGROUND_MODIFIER_ACCENT};
2222
2323
&:hover {
2424
background-image: linear-gradient(rgba(0, 0, 0, 0.1) 0 0);
2525
}
2626
`;
2727

28-
const StyledTextNodeWrapper = styled.span<{ $hasCollapse: boolean }>`
28+
const StyledTextNodeWrapper = styled.span<{ $hasCollapse: boolean; $isParent: boolean }>`
2929
display: flex;
30-
justify-content: ${({ $hasCollapse }) => ($hasCollapse ? "space-between" : "center")};
30+
justify-content: ${({ $hasCollapse, $isParent }) =>
31+
$hasCollapse ? "space-between" : $isParent ? "center" : "flex-start"};
3132
align-items: center;
3233
height: 100%;
3334
width: 100%;
35+
overflow: hidden;
36+
padding: ${({ $hasCollapse }) => ($hasCollapse ? "0" : "0 10px")};
3437
`;
3538

3639
const StyledImageWrapper = styled.div`
@@ -81,14 +84,14 @@ const Node = ({ node, x, y, hasCollapse = false }: CustomNodeProps) => {
8184
data-y={y}
8285
data-key={JSON.stringify(text)}
8386
$hasCollapse={isParent && collapseButtonVisible}
87+
$isParent={isParent}
8488
>
8589
<Styled.StyledKey $value={value} $parent={isParent} $type={type}>
8690
<TextRenderer>{value}</TextRenderer>
8791
</Styled.StyledKey>
8892
{isParent && childrenCount > 0 && childrenCountVisible && (
89-
<Styled.StyledChildrenCount>({childrenCount})</Styled.StyledChildrenCount>
93+
<Styled.StyledChildrenCount>[{childrenCount}]</Styled.StyledChildrenCount>
9094
)}
91-
9295
{isParent && hasCollapse && collapseButtonVisible && (
9396
<StyledExpand aria-label="Expand" onClick={handleExpand}>
9497
{isExpanded ? <MdLinkOff size={18} /> : <MdLink size={18} />}

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import React from "react";
2+
import { useComputedColorScheme } from "@mantine/core";
23
import type { NodeProps } from "reaflow";
34
import { Node } from "reaflow";
45
import useGraph from "src/features/editor/views/GraphView/stores/useGraph";
@@ -23,6 +24,7 @@ const CustomNodeWrapper = (nodeProps: NodeProps<NodeData["data"]>) => {
2324
const data = nodeProps.properties.data;
2425
const setSelectedNode = useGraph(state => state.setSelectedNode);
2526
const setVisible = useModal(state => state.setVisible);
27+
const colorScheme = useComputedColorScheme();
2628

2729
const handleNodeClick = React.useCallback(
2830
(_: React.MouseEvent<SVGGElement, MouseEvent>, data: NodeData) => {
@@ -39,6 +41,11 @@ const CustomNodeWrapper = (nodeProps: NodeProps<NodeData["data"]>) => {
3941
onClick={handleNodeClick as any}
4042
animated={false}
4143
label={null as any}
44+
style={{
45+
fill: colorScheme === "dark" ? "#292929" : "#ffffff",
46+
stroke: colorScheme === "dark" ? "#424242" : "#BCBEC0",
47+
strokeWidth: 1.5,
48+
}}
4249
>
4350
{({ node, x, y }) => {
4451
if (Array.isArray(nodeProps.properties.text)) {

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

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import type { DefaultTheme } from "styled-components";
22
import styled from "styled-components";
33
import { LinkItUrl } from "react-linkify-it";
4+
import { NODE_DIMENSIONS } from "src/constants/graph";
45

56
type TextColorFn = {
67
theme: DefaultTheme;
@@ -64,31 +65,35 @@ export const StyledForeignObject = styled.foreignObject<{ $isObject?: boolean }>
6465
`;
6566

6667
export const StyledKey = styled.span<{ $parent?: boolean; $type: string; $value?: string }>`
67-
display: inline;
68+
display: ${({ $parent }) => ($parent ? "flex" : "inline")};
69+
align-items: center;
70+
justify-content: center; // Always center for parent nodes
6871
flex: 1;
72+
min-width: 0;
73+
height: ${({ $parent }) => ($parent ? `${NODE_DIMENSIONS.PARENT_HEIGHT}px` : "auto")};
74+
line-height: ${({ $parent }) => ($parent ? `${NODE_DIMENSIONS.PARENT_HEIGHT}px` : "inherit")};
75+
padding: 0; // Remove padding
6976
color: ${({ theme, $type, $parent = false, $value = "" }) =>
7077
getTextColor({ $parent, $type, $value, theme })};
71-
font-size: ${({ $parent }) => $parent && "14px"};
7278
overflow: hidden;
7379
text-overflow: ellipsis;
74-
padding: ${({ $type }) => $type !== "object" && "10px"};
7580
white-space: nowrap;
7681
`;
7782

7883
export const StyledRow = styled.span<{ $value: string }>`
79-
padding: 0 10px;
84+
padding: 3px 10px;
85+
height: ${NODE_DIMENSIONS.ROW_HEIGHT}px;
86+
line-height: 18px;
8087
color: ${({ theme, $value }) => getTextColor({ $value, theme })};
8188
display: block;
8289
overflow: hidden;
8390
text-overflow: ellipsis;
8491
white-space: nowrap;
85-
86-
&:first-of-type {
87-
padding-top: 10px;
88-
}
92+
border-bottom: 1px solid ${({ theme }) => theme.NODE_COLORS.DIVIDER};
93+
box-sizing: border-box;
8994
9095
&:last-of-type {
91-
padding-bottom: 10px;
96+
border-bottom: none;
9297
}
9398
`;
9499

src/features/editor/views/GraphView/index.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import React from "react";
2-
import { LoadingOverlay } from "@mantine/core";
2+
import { LoadingOverlay, useComputedColorScheme } from "@mantine/core";
33
import styled from "styled-components";
44
import debounce from "lodash.debounce";
55
import { Space } from "react-zoomable-ui";
@@ -83,6 +83,7 @@ const GraphCanvas = ({ isWidget }: GraphProps) => {
8383
const centerView = useGraph(state => state.centerView);
8484
const direction = useGraph(state => state.direction);
8585
const nodes = useGraph(state => state.nodes);
86+
const colorScheme = useComputedColorScheme();
8687
const edges = useGraph(state => state.edges);
8788
const [paneWidth, setPaneWidth] = React.useState(2000);
8889
const [paneHeight, setPaneHeight] = React.useState(2000);
@@ -116,13 +117,14 @@ const GraphCanvas = ({ isWidget }: GraphProps) => {
116117
edge={p => <CustomEdge {...p} />}
117118
nodes={nodes}
118119
edges={edges}
120+
arrow={null}
119121
maxHeight={paneHeight}
120122
maxWidth={paneWidth}
121123
height={paneHeight}
122124
width={paneWidth}
123125
direction={direction}
124126
layoutOptions={layoutOptions}
125-
key={direction}
127+
key={[direction, colorScheme].join("-")}
126128
pannable={false}
127129
zoomable={false}
128130
animated={false}

src/features/editor/views/GraphView/lib/utils/calculateNodeSize.ts

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { NODE_DIMENSIONS } from "src/constants/graph";
12
import useConfig from "src/store/useConfig";
23

34
type Text = string | [string, string][];
@@ -24,24 +25,23 @@ const calculateWidthAndHeight = (str: string, single = false) => {
2425
if (!str) return { width: 45, height: 45 };
2526

2627
const dummyElement = document.createElement("div");
27-
2828
dummyElement.style.whiteSpace = single ? "nowrap" : "pre-wrap";
2929
dummyElement.innerHTML = str;
3030
dummyElement.style.fontSize = "12px";
3131
dummyElement.style.width = "fit-content";
32-
dummyElement.style.height = "fit-content";
33-
dummyElement.style.padding = "10px";
32+
dummyElement.style.padding = "0 10px";
3433
dummyElement.style.fontWeight = "500";
35-
dummyElement.style.overflowWrap = "break-word";
3634
dummyElement.style.fontFamily = "monospace";
3735
document.body.appendChild(dummyElement);
3836

3937
const clientRect = dummyElement.getBoundingClientRect();
38+
const lines = str.split("\n").length;
39+
4040
const width = clientRect.width + 4;
41-
const height = clientRect.height;
41+
// Use parent height for single line nodes that are parents
42+
const height = single ? NODE_DIMENSIONS.PARENT_HEIGHT : lines * NODE_DIMENSIONS.ROW_HEIGHT;
4243

4344
document.body.removeChild(dummyElement);
44-
4545
return { width, height };
4646
};
4747

@@ -59,7 +59,6 @@ export const calculateNodeSize = (text: Text, isParent = false) => {
5959
// check cache if data already exists
6060
if (sizeCache.has(cacheKey)) {
6161
const size = sizeCache.get(cacheKey);
62-
6362
if (size) return size;
6463
}
6564

@@ -71,7 +70,7 @@ export const calculateNodeSize = (text: Text, isParent = false) => {
7170
sizes.height = 80;
7271
}
7372

74-
if (isParent) sizes.width += 100;
73+
if (isParent) sizes.width += 80;
7574
if (sizes.width > 700) sizes.width = 700;
7675

7776
sizeCache.set(cacheKey, sizes);

0 commit comments

Comments
 (0)