From 4a604ca70e79efaf038526fe7456bba490e1cf1d Mon Sep 17 00:00:00 2001 From: AykutSarac Date: Thu, 23 Jan 2025 00:18:40 +0300 Subject: [PATCH] feat: update graph UI --- src/constants/graph.ts | 4 ++++ src/constants/theme.ts | 14 ++++++----- .../views/GraphView/CustomEdge/index.tsx | 14 ++++++++++- .../views/GraphView/CustomNode/ObjectNode.tsx | 5 +++- .../views/GraphView/CustomNode/TextNode.tsx | 13 +++++++---- .../views/GraphView/CustomNode/index.tsx | 7 ++++++ .../views/GraphView/CustomNode/styles.tsx | 23 +++++++++++-------- src/features/editor/views/GraphView/index.tsx | 6 +++-- .../GraphView/lib/utils/calculateNodeSize.ts | 15 ++++++------ 9 files changed, 69 insertions(+), 32 deletions(-) create mode 100644 src/constants/graph.ts diff --git a/src/constants/graph.ts b/src/constants/graph.ts new file mode 100644 index 00000000000..ee566a585ed --- /dev/null +++ b/src/constants/graph.ts @@ -0,0 +1,4 @@ +export const NODE_DIMENSIONS = { + ROW_HEIGHT: 24, // Regular row height + PARENT_HEIGHT: 36, // Height for parent nodes +} as const; diff --git a/src/constants/theme.ts b/src/constants/theme.ts index 76a82a045ec..dd0b235b134 100644 --- a/src/constants/theme.ts +++ b/src/constants/theme.ts @@ -47,6 +47,7 @@ const nodeColors = { PARENT_ARR: "#FC9A40", PARENT_OBJ: "#59b8ff", CHILD_COUNT: "white", + DIVIDER: "#383838", }, }, light: { @@ -63,6 +64,7 @@ const nodeColors = { PARENT_ARR: "#FF6B00", PARENT_OBJ: "#761CEA", CHILD_COUNT: "#535353", + DIVIDER: "#e6e6e6", }, }, }; @@ -89,9 +91,9 @@ export const darkTheme = { MODAL_BACKGROUND: "#36393E", TEXT_NORMAL: "#dcddde", TEXT_POSITIVE: "hsl(139,calc(var(--saturation-factor, 1)*51.6%),52.2%)", - GRID_BG_COLOR: "#1E1E1E", - GRID_COLOR_PRIMARY: "#272626", - GRID_COLOR_SECONDARY: "#232323", + GRID_BG_COLOR: "#141414", + GRID_COLOR_PRIMARY: "#1c1b1b", + GRID_COLOR_SECONDARY: "#191919", }; export const lightTheme = { @@ -116,9 +118,9 @@ export const lightTheme = { MODAL_BACKGROUND: "#FFFFFF", TEXT_NORMAL: "#2e3338", TEXT_POSITIVE: "#008736", - GRID_BG_COLOR: "#f3f3f3", - GRID_COLOR_PRIMARY: "#E0E0E0", - GRID_COLOR_SECONDARY: "#E4E4E4", + GRID_BG_COLOR: "#f7f7f7", + GRID_COLOR_PRIMARY: "#ebe8e8", + GRID_COLOR_SECONDARY: "#f2eeee", }; const themeDs = { diff --git a/src/features/editor/views/GraphView/CustomEdge/index.tsx b/src/features/editor/views/GraphView/CustomEdge/index.tsx index 371446e58e4..f691d2944a5 100644 --- a/src/features/editor/views/GraphView/CustomEdge/index.tsx +++ b/src/features/editor/views/GraphView/CustomEdge/index.tsx @@ -1,9 +1,21 @@ import React from "react"; +import { useComputedColorScheme } from "@mantine/core"; import type { EdgeProps } from "reaflow"; import { Edge } from "reaflow"; const CustomEdgeWrapper = (props: EdgeProps) => { - return ; + const colorScheme = useComputedColorScheme(); + + return ( + + ); }; export const CustomEdge = React.memo(CustomEdgeWrapper); diff --git a/src/features/editor/views/GraphView/CustomNode/ObjectNode.tsx b/src/features/editor/views/GraphView/CustomNode/ObjectNode.tsx index 4d502722884..3a96d5aec18 100644 --- a/src/features/editor/views/GraphView/CustomNode/ObjectNode.tsx +++ b/src/features/editor/views/GraphView/CustomNode/ObjectNode.tsx @@ -1,4 +1,5 @@ import React from "react"; +import { NODE_DIMENSIONS } from "src/constants/graph"; import type { CustomNodeProps } from "src/features/editor/views/GraphView/CustomNode"; import { TextRenderer } from "./TextRenderer"; import * as Styled from "./styles"; @@ -17,8 +18,10 @@ const Row = ({ val, x, y, index }: RowProps) => { const rowKey = JSON.stringify(val[0]).replaceAll('"', ""); const rowValue = JSON.stringify(val[1]); + const rowPosition = index * NODE_DIMENSIONS.ROW_HEIGHT; + return ( - + {rowKey}: {rowValue} diff --git a/src/features/editor/views/GraphView/CustomNode/TextNode.tsx b/src/features/editor/views/GraphView/CustomNode/TextNode.tsx index f223f03b799..dc23dc6d92e 100644 --- a/src/features/editor/views/GraphView/CustomNode/TextNode.tsx +++ b/src/features/editor/views/GraphView/CustomNode/TextNode.tsx @@ -17,7 +17,7 @@ const StyledExpand = styled.button` color: ${({ theme }) => theme.TEXT_NORMAL}; background: rgba(0, 0, 0, 0.1); height: 100%; - width: 40px; + width: 36px; border-left: 1px solid ${({ theme }) => theme.BACKGROUND_MODIFIER_ACCENT}; &:hover { @@ -25,12 +25,15 @@ const StyledExpand = styled.button` } `; -const StyledTextNodeWrapper = styled.span<{ $hasCollapse: boolean }>` +const StyledTextNodeWrapper = styled.span<{ $hasCollapse: boolean; $isParent: boolean }>` display: flex; - justify-content: ${({ $hasCollapse }) => ($hasCollapse ? "space-between" : "center")}; + justify-content: ${({ $hasCollapse, $isParent }) => + $hasCollapse ? "space-between" : $isParent ? "center" : "flex-start"}; align-items: center; height: 100%; width: 100%; + overflow: hidden; + padding: ${({ $hasCollapse }) => ($hasCollapse ? "0" : "0 10px")}; `; const StyledImageWrapper = styled.div` @@ -81,14 +84,14 @@ const Node = ({ node, x, y, hasCollapse = false }: CustomNodeProps) => { data-y={y} data-key={JSON.stringify(text)} $hasCollapse={isParent && collapseButtonVisible} + $isParent={isParent} > {value} {isParent && childrenCount > 0 && childrenCountVisible && ( - ({childrenCount}) + [{childrenCount}] )} - {isParent && hasCollapse && collapseButtonVisible && ( {isExpanded ? : } diff --git a/src/features/editor/views/GraphView/CustomNode/index.tsx b/src/features/editor/views/GraphView/CustomNode/index.tsx index d62b78ee88f..dc04adf21da 100644 --- a/src/features/editor/views/GraphView/CustomNode/index.tsx +++ b/src/features/editor/views/GraphView/CustomNode/index.tsx @@ -1,4 +1,5 @@ import React from "react"; +import { useComputedColorScheme } from "@mantine/core"; import type { NodeProps } from "reaflow"; import { Node } from "reaflow"; import useGraph from "src/features/editor/views/GraphView/stores/useGraph"; @@ -23,6 +24,7 @@ const CustomNodeWrapper = (nodeProps: NodeProps) => { const data = nodeProps.properties.data; const setSelectedNode = useGraph(state => state.setSelectedNode); const setVisible = useModal(state => state.setVisible); + const colorScheme = useComputedColorScheme(); const handleNodeClick = React.useCallback( (_: React.MouseEvent, data: NodeData) => { @@ -39,6 +41,11 @@ const CustomNodeWrapper = (nodeProps: NodeProps) => { onClick={handleNodeClick as any} animated={false} label={null as any} + style={{ + fill: colorScheme === "dark" ? "#292929" : "#ffffff", + stroke: colorScheme === "dark" ? "#424242" : "#BCBEC0", + strokeWidth: 1.5, + }} > {({ node, x, y }) => { if (Array.isArray(nodeProps.properties.text)) { diff --git a/src/features/editor/views/GraphView/CustomNode/styles.tsx b/src/features/editor/views/GraphView/CustomNode/styles.tsx index 2e8bfcc37c3..fe9162a579f 100644 --- a/src/features/editor/views/GraphView/CustomNode/styles.tsx +++ b/src/features/editor/views/GraphView/CustomNode/styles.tsx @@ -1,6 +1,7 @@ import type { DefaultTheme } from "styled-components"; import styled from "styled-components"; import { LinkItUrl } from "react-linkify-it"; +import { NODE_DIMENSIONS } from "src/constants/graph"; type TextColorFn = { theme: DefaultTheme; @@ -64,31 +65,35 @@ export const StyledForeignObject = styled.foreignObject<{ $isObject?: boolean }> `; export const StyledKey = styled.span<{ $parent?: boolean; $type: string; $value?: string }>` - display: inline; + display: ${({ $parent }) => ($parent ? "flex" : "inline")}; + align-items: center; + justify-content: center; // Always center for parent nodes flex: 1; + min-width: 0; + height: ${({ $parent }) => ($parent ? `${NODE_DIMENSIONS.PARENT_HEIGHT}px` : "auto")}; + line-height: ${({ $parent }) => ($parent ? `${NODE_DIMENSIONS.PARENT_HEIGHT}px` : "inherit")}; + padding: 0; // Remove padding color: ${({ theme, $type, $parent = false, $value = "" }) => getTextColor({ $parent, $type, $value, theme })}; - font-size: ${({ $parent }) => $parent && "14px"}; overflow: hidden; text-overflow: ellipsis; - padding: ${({ $type }) => $type !== "object" && "10px"}; white-space: nowrap; `; export const StyledRow = styled.span<{ $value: string }>` - padding: 0 10px; + padding: 3px 10px; + height: ${NODE_DIMENSIONS.ROW_HEIGHT}px; + line-height: 18px; color: ${({ theme, $value }) => getTextColor({ $value, theme })}; display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; - - &:first-of-type { - padding-top: 10px; - } + border-bottom: 1px solid ${({ theme }) => theme.NODE_COLORS.DIVIDER}; + box-sizing: border-box; &:last-of-type { - padding-bottom: 10px; + border-bottom: none; } `; diff --git a/src/features/editor/views/GraphView/index.tsx b/src/features/editor/views/GraphView/index.tsx index 4db97f60760..2a8ccfb5e03 100644 --- a/src/features/editor/views/GraphView/index.tsx +++ b/src/features/editor/views/GraphView/index.tsx @@ -1,5 +1,5 @@ import React from "react"; -import { LoadingOverlay } from "@mantine/core"; +import { LoadingOverlay, useComputedColorScheme } from "@mantine/core"; import styled from "styled-components"; import debounce from "lodash.debounce"; import { Space } from "react-zoomable-ui"; @@ -83,6 +83,7 @@ const GraphCanvas = ({ isWidget }: GraphProps) => { const centerView = useGraph(state => state.centerView); const direction = useGraph(state => state.direction); const nodes = useGraph(state => state.nodes); + const colorScheme = useComputedColorScheme(); const edges = useGraph(state => state.edges); const [paneWidth, setPaneWidth] = React.useState(2000); const [paneHeight, setPaneHeight] = React.useState(2000); @@ -116,13 +117,14 @@ const GraphCanvas = ({ isWidget }: GraphProps) => { edge={p => } nodes={nodes} edges={edges} + arrow={null} maxHeight={paneHeight} maxWidth={paneWidth} height={paneHeight} width={paneWidth} direction={direction} layoutOptions={layoutOptions} - key={direction} + key={[direction, colorScheme].join("-")} pannable={false} zoomable={false} animated={false} diff --git a/src/features/editor/views/GraphView/lib/utils/calculateNodeSize.ts b/src/features/editor/views/GraphView/lib/utils/calculateNodeSize.ts index 5a2671750ae..0a516ea5d09 100644 --- a/src/features/editor/views/GraphView/lib/utils/calculateNodeSize.ts +++ b/src/features/editor/views/GraphView/lib/utils/calculateNodeSize.ts @@ -1,3 +1,4 @@ +import { NODE_DIMENSIONS } from "src/constants/graph"; import useConfig from "src/store/useConfig"; type Text = string | [string, string][]; @@ -24,24 +25,23 @@ const calculateWidthAndHeight = (str: string, single = false) => { if (!str) return { width: 45, height: 45 }; const dummyElement = document.createElement("div"); - dummyElement.style.whiteSpace = single ? "nowrap" : "pre-wrap"; dummyElement.innerHTML = str; dummyElement.style.fontSize = "12px"; dummyElement.style.width = "fit-content"; - dummyElement.style.height = "fit-content"; - dummyElement.style.padding = "10px"; + dummyElement.style.padding = "0 10px"; dummyElement.style.fontWeight = "500"; - dummyElement.style.overflowWrap = "break-word"; dummyElement.style.fontFamily = "monospace"; document.body.appendChild(dummyElement); const clientRect = dummyElement.getBoundingClientRect(); + const lines = str.split("\n").length; + const width = clientRect.width + 4; - const height = clientRect.height; + // Use parent height for single line nodes that are parents + const height = single ? NODE_DIMENSIONS.PARENT_HEIGHT : lines * NODE_DIMENSIONS.ROW_HEIGHT; document.body.removeChild(dummyElement); - return { width, height }; }; @@ -59,7 +59,6 @@ export const calculateNodeSize = (text: Text, isParent = false) => { // check cache if data already exists if (sizeCache.has(cacheKey)) { const size = sizeCache.get(cacheKey); - if (size) return size; } @@ -71,7 +70,7 @@ export const calculateNodeSize = (text: Text, isParent = false) => { sizes.height = 80; } - if (isParent) sizes.width += 100; + if (isParent) sizes.width += 80; if (sizes.width > 700) sizes.width = 700; sizeCache.set(cacheKey, sizes);