diff --git a/apps/client/src/components/NCloud/NetworksBar/SubnetSelect.tsx b/apps/client/src/components/NCloud/NetworksBar/SubnetSelect.tsx index be0c913b..defe6cd4 100644 --- a/apps/client/src/components/NCloud/NetworksBar/SubnetSelect.tsx +++ b/apps/client/src/components/NCloud/NetworksBar/SubnetSelect.tsx @@ -83,6 +83,7 @@ export default ({ variant="text" disabled={disabled} disableRipple + sx={{ whiteSpace: 'nowrap' }} > {subnet ? subnet.value : } diff --git a/apps/client/src/components/NCloud/NetworksBar/VpcSelect.tsx b/apps/client/src/components/NCloud/NetworksBar/VpcSelect.tsx index a352bda7..fcafbb00 100644 --- a/apps/client/src/components/NCloud/NetworksBar/VpcSelect.tsx +++ b/apps/client/src/components/NCloud/NetworksBar/VpcSelect.tsx @@ -88,6 +88,7 @@ export default ({ type="submit" disabled={disabled} disableRipple + sx={{ whiteSpace: 'nowrap' }} > {vpc ? vpc.value : } diff --git a/apps/client/src/components/SaveDialog.tsx b/apps/client/src/components/SaveDialog.tsx index ed928d53..6d28d251 100644 --- a/apps/client/src/components/SaveDialog.tsx +++ b/apps/client/src/components/SaveDialog.tsx @@ -12,6 +12,8 @@ import TextField from '@mui/material/TextField'; import { useNavigate, useParams } from 'react-router-dom'; import { urls } from '../apis'; import { useCloudGraph } from './CloudGraphProvider'; +import { useDimensionContext } from '@contexts/DimensionContext'; +import useGraph from '@hooks/useGraph'; type Props = { open: boolean; onClose: () => void; @@ -27,7 +29,9 @@ export default ({ open, onClose }: Props) => { const { state: { edges }, } = useEdgeContext(); + const { updatePointForDimension } = useGraph(); const { data: graphData, setData: setGraphData } = useCloudGraph(); + const { dimension } = useDimensionContext(); const navigate = useNavigate(); const params = useParams(); @@ -47,12 +51,22 @@ export default ({ open, onClose }: Props) => { const title = (e.target as any).title.value; + let updatedNodes = nodes; + let updatedEdges = edges; + // 서버에 저장시 2d로 변환 + if (dimension === '3d') { + const { updatedNodes: _updatedNodes, updatedEdges: _updatedEdges } = + updatePointForDimension(nodes, edges, '2d'); + + updatedNodes = _updatedNodes; + updatedEdges = _updatedEdges; + } const resp = await saveArchitecture({ cost: 0, architecture: { - nodes, + nodes: updatedNodes, + edges: updatedEdges, groups, - edges, }, title, }); diff --git a/apps/client/src/components/ShareDialog.tsx b/apps/client/src/components/ShareDialog.tsx index ef5b0c4c..30302a24 100644 --- a/apps/client/src/components/ShareDialog.tsx +++ b/apps/client/src/components/ShareDialog.tsx @@ -1,26 +1,92 @@ +import { useDimensionContext } from '@contexts/DimensionContext'; +import { useEdgeContext } from '@contexts/EdgeContext'; +import { useGroupContext } from '@contexts/GroupContext'; +import { useNodeContext } from '@contexts/NodeContext'; +import { useSvgContext } from '@contexts/SvgContext'; +import { calculateNodeBoundingBox } from '@helpers/node'; +import useFetch from '@hooks/useFetch'; +import { FormControl, Stack, useColorScheme, useTheme } from '@mui/material'; import Button from '@mui/material/Button'; -import TextField from '@mui/material/TextField'; import Dialog from '@mui/material/Dialog'; import DialogActions from '@mui/material/DialogActions'; import DialogContent from '@mui/material/DialogContent'; import DialogTitle from '@mui/material/DialogTitle'; -import Select, { OnChangeValue } from 'react-select'; +import TextField from '@mui/material/TextField'; import { useState } from 'react'; -import Creatable, { useCreatable } from 'react-select/creatable'; -import { Stack, FormControl } from '@mui/material'; -import { useEdgeContext } from '@contexts/EdgeContext'; -import { useGroupContext } from '@contexts/GroupContext'; -import { useNodeContext } from '@contexts/NodeContext'; -import useFetch from '@hooks/useFetch'; +import { OnChangeValue } from 'react-select'; +import Creatable from 'react-select/creatable'; import { urls } from '../apis'; -import { useSvgContext } from '@contexts/SvgContext'; -import { calculateNodeBoundingBox } from '@helpers/node'; -import { useDimensionContext } from '@contexts/DimensionContext'; +import useGraph from '@hooks/useGraph'; + type Props = { open: boolean; onClose: () => void; }; +const reactSelectStyles = (mode: string, theme: any) => ({ + control: (provided: any) => ({ + ...provided, + backgroundColor: + mode === 'dark' ? '#393939' : theme.palette.background.paper, + borderColor: + mode === 'dark' ? theme.palette.grey[700] : theme.palette.divider, + minHeight: '40px', + height: '40px', + boxShadow: 'none', + '&:hover': { + borderColor: mode === 'dark' ? '#fff' : theme.palette.text.primary, + }, + '&:focus-within': { + borderColor: + mode === 'dark' + ? theme.palette.primary.main + : theme.palette.text.primary, + }, + }), + input: (provided: any) => ({ + ...provided, + color: + mode === 'dark' + ? theme.palette.text.primary + : theme.palette.text.secondary, + }), + menu: (provided: any) => ({ + ...provided, + display: 'none', + }), + dropdownIndicator: () => ({ display: 'none' }), + indicatorSeparator: () => ({ display: 'none' }), + multiValue: (provided: any) => ({ + ...provided, + backgroundColor: + mode === 'dark' ? theme.palette.grey[700] : theme.palette.grey[300], + }), + multiValueLabel: (provided: any) => ({ + ...provided, + color: + mode === 'dark' + ? theme.palette.common.white + : theme.palette.text.primary, + }), + multiValueRemove: (provided: any) => ({ + ...provided, + color: + mode === 'dark' + ? theme.palette.common.white + : theme.palette.text.primary, + ':hover': { + backgroundColor: + mode === 'dark' + ? theme.palette.grey[600] + : theme.palette.grey[400], + color: + mode === 'dark' + ? theme.palette.common.white + : theme.palette.text.primary, + }, + }), +}); + export default ({ open, onClose }: Props) => { const { state: { nodes }, @@ -31,8 +97,12 @@ export default ({ open, onClose }: Props) => { const { state: { edges }, } = useEdgeContext(); + const { updatePointForDimension } = useGraph(); + const { svgRef } = useSvgContext(); const { dimension } = useDimensionContext(); + const { mode } = useColorScheme(); + const theme = useTheme(); const [tags, setTags] = useState<{ label: string; value: string }[]>([]); @@ -40,6 +110,25 @@ export default ({ open, onClose }: Props) => { method: 'POST', }); + const getCloneSvg = (svg: SVGSVGElement) => { + const allNodeBoundsBox = calculateNodeBoundingBox( + Object.values(nodes), + dimension, + ); + const padding = 90 * 8; + + const viewBox = `${allNodeBoundsBox.minX - padding} ${ + allNodeBoundsBox.minY - padding + } ${allNodeBoundsBox.width + padding * 2} ${ + allNodeBoundsBox.height + padding * 2 + }`; + + const svgClone = svg.cloneNode(true) as SVGSVGElement; + svgClone.setAttribute('viewBox', viewBox); + + return svgClone; + }; + const handleClose = () => { onClose(); }; @@ -62,27 +151,26 @@ export default ({ open, onClose }: Props) => { return; } - const allNodeBoundsBox = calculateNodeBoundingBox( - Object.values(nodes), - dimension, - ); - const padding = 90 * 4; + const svgClone = getCloneSvg(svgRef.current); - const viewBox = `${allNodeBoundsBox.minX - padding} ${ - allNodeBoundsBox.minY - padding - } ${allNodeBoundsBox.width + padding * 2} ${ - allNodeBoundsBox.height + padding * 2 - }`; + let updatedNodes = nodes; + let updatedEdges = edges; + // 서버에 저장시 2d로 변환 + if (dimension === '3d') { + const { updatedNodes: _updatedNodes, updatedEdges: _updatedEdges } = + updatePointForDimension(nodes, edges, '2d'); + + updatedNodes = _updatedNodes; + updatedEdges = _updatedEdges; + } - const svgClone = svgRef.current.cloneNode(true) as SVGSVGElement; - svgClone.setAttribute('viewBox', viewBox); await shareArchitecture({ cost: 0, tags: cloudTags, architecture: { - nodes, + nodes: updatedNodes, + edges: updatedEdges, groups, - edges, svg: svgClone.outerHTML, }, title, @@ -133,14 +221,7 @@ export default ({ open, onClose }: Props) => { name="tags" value={tags} onChange={handleChange} - styles={{ - menu: (provided) => ({ - ...provided, - display: 'none', - }), - dropdownIndicator: () => ({ display: 'none' }), - indicatorSeparator: () => ({ display: 'none' }), - }} + styles={reactSelectStyles(mode!, theme)} /> diff --git a/apps/client/src/hooks/useGraph.ts b/apps/client/src/hooks/useGraph.ts index 31f0855a..da76218d 100644 --- a/apps/client/src/hooks/useGraph.ts +++ b/apps/client/src/hooks/useGraph.ts @@ -125,9 +125,15 @@ export default () => { clearSelection(); }; - //TODO: Refactoring필요 - const updateNodePointForDimension = (dimension: Dimension) => { - //INFO: update node + const updatePointForDimension = ( + nodes: Record, + edges: Record, + dimension: Dimension, + ): { + updatedNodes: Record; + updatedEdges: Record; + } => { + // INFO: Update nodes const updatedNodes: Record = Object.entries(nodes).reduce( (acc, [id, node]) => { const adjustedPoint = adjustNodePointForDimension( @@ -153,26 +159,96 @@ export default () => { {}, ); - //INFO:update edge - let updatedEdges = Object.entries(edges).reduce((acc, [id, edge]) => { - const adjustedBendingPoints = edge.bendingPoints.map((point) => { - if (dimension === '3d') { - const grid = screenToGrid2d(point); - return gridToScreen3d({ col: grid.col + 1, row: grid.row }); - } else { - const grid = screenToGrid3d(point); - return gridToScreen2d({ col: grid.col - 1, row: grid.row }); - } - }); + // INFO: Update edges + const updatedEdges: Record = Object.entries(edges).reduce( + (acc, [id, edge]) => { + const adjustedBendingPoints = edge.bendingPoints.map( + (point) => { + if (dimension === '3d') { + const grid = screenToGrid2d(point); + return gridToScreen3d({ + col: grid.col + 1, + row: grid.row, + }); + } else { + const grid = screenToGrid3d(point); + return gridToScreen2d({ + col: grid.col - 1, + row: grid.row, + }); + } + }, + ); - return { - ...acc, - [id]: { - ...edge, - bendingPoints: adjustedBendingPoints, - }, - }; - }, {}); + return { + ...acc, + [id]: { + ...edge, + bendingPoints: adjustedBendingPoints, + }, + }; + }, + {}, + ); + + // Return the updated nodes and edges + return { updatedNodes, updatedEdges }; + }; + + //TODO: Refactoring필요 + const updateNodePointForDimension = (dimension: Dimension) => { + //INFO: update node + // const updatedNodes: Record = Object.entries(nodes).reduce( + // (acc, [id, node]) => { + // const adjustedPoint = adjustNodePointForDimension( + // node.point, + // node.size['3d'], + // dimension, + // ); + // + // const connectors = getConnectorPoints( + // { ...node, point: adjustedPoint }, + // dimension, + // ); + // + // return { + // ...acc, + // [id]: { + // ...node, + // point: adjustedPoint, + // connectors, + // }, + // }; + // }, + // {}, + // ); + // + // //INFO:update edge + // let updatedEdges = Object.entries(edges).reduce((acc, [id, edge]) => { + // const adjustedBendingPoints = edge.bendingPoints.map((point) => { + // if (dimension === '3d') { + // const grid = screenToGrid2d(point); + // return gridToScreen3d({ col: grid.col + 1, row: grid.row }); + // } else { + // const grid = screenToGrid3d(point); + // return gridToScreen2d({ col: grid.col - 1, row: grid.row }); + // } + // }); + // + // return { + // ...acc, + // [id]: { + // ...edge, + // bendingPoints: adjustedBendingPoints, + // }, + // }; + // }, {}); + + const { updatedNodes, updatedEdges } = updatePointForDimension( + nodes, + edges, + dimension, + ); //INFO: update ViewBox if (Object.keys(updatedNodes).length === 0 || !svgRef.current) return; @@ -457,6 +533,7 @@ export default () => { addEdge, removeEdge, splitEdge, + updatePointForDimension, updateNodePointForDimension, moveBendingPointer, addGroup,