diff --git a/src/components/ErrorContainer/ErrorContainer.tsx b/src/components/ErrorContainer/index.tsx similarity index 100% rename from src/components/ErrorContainer/ErrorContainer.tsx rename to src/components/ErrorContainer/index.tsx diff --git a/src/components/Graph/index.tsx b/src/components/Graph/index.tsx index 195fe98..7555b46 100644 --- a/src/components/Graph/index.tsx +++ b/src/components/Graph/index.tsx @@ -6,12 +6,9 @@ import { } from "react-zoom-pan-pinch"; import { Canvas, Edge, ElkRoot } from "reaflow"; import { CustomNode } from "src/components/CustomNode"; -import { NodeModal } from "src/containers/Modals/NodeModal"; import useConfig from "src/hooks/store/useConfig"; -import styled from "styled-components"; -import shallow from "zustand/shallow"; import useGraph from "src/hooks/store/useGraph"; -import { parser } from "src/utils/jsonParser"; +import styled from "styled-components"; interface LayoutProps { isWidget: boolean; @@ -41,27 +38,21 @@ const StyledEditorWrapper = styled.div<{ isWidget: boolean }>` } `; -const MemoizedGraph = React.memo(function Layout({ +export const Graph = ({ isWidget, openModal, setSelectedNode, -}: LayoutProps) { - const json = useConfig((state) => state.json); +}: LayoutProps) => { const setConfig = useConfig((state) => state.setConfig); + const layout = useConfig((state) => state.layout); const nodes = useGraph((state) => state.nodes); const edges = useGraph((state) => state.edges); - const setGraphValue = useGraph((state) => state.setGraphValue); const [size, setSize] = React.useState({ width: 2000, height: 2000, }); - const [expand, layout] = useConfig( - (state) => [state.expand, state.layout], - shallow - ); - const handleNodeClick = React.useCallback( (e: React.MouseEvent, data: NodeData) => { setSelectedNode(data.text); @@ -88,13 +79,6 @@ const MemoizedGraph = React.memo(function Layout({ if (input) input.blur(); }, []); - React.useEffect(() => { - const { nodes, edges } = parser(json, expand); - - setGraphValue("nodes", nodes); - setGraphValue("edges", edges); - }, [expand, json, setGraphValue]); - return ( ); -}); - -export const Graph = ({ isWidget = false }: { isWidget?: boolean }) => { - const [isModalVisible, setModalVisible] = React.useState(false); - const [selectedNode, setSelectedNode] = React.useState<[string, string][]>( - [] - ); - - const openModal = React.useCallback(() => setModalVisible(true), []); - - const collapsedNodes = useGraph((state) => state.collapsedNodes); - const collapsedEdges = useGraph((state) => state.collapsedEdges); - - React.useEffect(() => { - const nodeList = collapsedNodes.map((id) => `[id$="node-${id}"]`); - const edgeList = collapsedEdges.map((id) => `[class$="edge-${id}"]`); - - const hiddenItems = document.querySelectorAll(".hide"); - hiddenItems.forEach((item) => item.classList.remove("hide")); - - if (nodeList.length) { - const selectedNodes = document.querySelectorAll(nodeList.join(",")); - const selectedEdges = document.querySelectorAll(edgeList.join(",")); - - selectedNodes.forEach((node) => node.classList.add("hide")); - selectedEdges.forEach((edge) => edge.classList.add("hide")); - } - }, [collapsedNodes, collapsedEdges]); - - return ( - <> - - {!isWidget && ( - setModalVisible(false)} - /> - )} - - ); }; diff --git a/src/components/MonacoEditor/index.tsx b/src/components/MonacoEditor/index.tsx new file mode 100644 index 0000000..cbfbd3c --- /dev/null +++ b/src/components/MonacoEditor/index.tsx @@ -0,0 +1,85 @@ +import React from "react"; +import styled from "styled-components"; +import Editor, { loader } from "@monaco-editor/react"; +import { Loading } from "src/components/Loading"; +import { parser } from "src/utils/jsonParser"; +import useConfig from "src/hooks/store/useConfig"; +import useStored from "src/hooks/store/useStored"; +import useGraph from "src/hooks/store/useGraph"; + +loader.config({ + paths: { + vs: "https://microsoft.github.io/monaco-editor/node_modules/monaco-editor/min/vs", + }, +}); + +const editorOptions = { + formatOnPaste: true, + minimap: { + enabled: false, + }, +}; + +const StyledWrapper = styled.div` + display: grid; + height: calc(100vh - 36px); + grid-template-columns: 100%; + grid-template-rows: minmax(0, 1fr); +`; + +export const MonacoEditor = ({ + setHasError, +}: { + setHasError: (value: boolean) => void; +}) => { + const json = useConfig((state) => state.json); + const expand = useConfig((state) => state.expand); + const setJson = useConfig((state) => state.setJson); + const setGraphValue = useGraph((state) => state.setGraphValue); + const [value, setValue] = React.useState(""); + + const lightmode = useStored((state) => + state.lightmode ? "light" : "vs-dark" + ); + + React.useEffect(() => { + const { nodes, edges } = parser(json, expand); + + setGraphValue("nodes", nodes); + setGraphValue("edges", edges); + setValue(json); + }, [expand, json, setGraphValue]); + + React.useEffect(() => { + const formatTimer = setTimeout(() => { + try { + if (!value) { + setHasError(false); + return setJson("{}"); + } + + const parsedJSON = JSON.stringify(JSON.parse(value), null, 2); + setJson(parsedJSON); + setHasError(false); + } catch (jsonError: any) { + setHasError(true); + } + }, 1200); + + return () => clearTimeout(formatTimer); + }, [value, setJson, setHasError]); + + return ( + + } + /> + + ); +}; diff --git a/src/containers/Editor/JsonEditor/index.tsx b/src/containers/Editor/JsonEditor/index.tsx index 59f0406..c923a82 100644 --- a/src/containers/Editor/JsonEditor/index.tsx +++ b/src/containers/Editor/JsonEditor/index.tsx @@ -1,16 +1,7 @@ import React from "react"; import styled from "styled-components"; -import MonacoEditor, { loader } from "@monaco-editor/react"; -import { ErrorContainer } from "src/components/ErrorContainer/ErrorContainer"; -import useConfig from "src/hooks/store/useConfig"; -import { Loading } from "src/components/Loading"; -import useStored from "src/hooks/store/useStored"; - -loader.config({ - paths: { - vs: "https://microsoft.github.io/monaco-editor/node_modules/monaco-editor/min/vs", - }, -}); +import { ErrorContainer } from "src/components/ErrorContainer"; +import { MonacoEditor } from "src/components/MonacoEditor"; const StyledEditorWrapper = styled.div` display: flex; @@ -19,71 +10,13 @@ const StyledEditorWrapper = styled.div` overflow: auto; user-select: none; `; - -const editorOptions = { - formatOnPaste: true, - minimap: { - enabled: false, - }, -}; - -const StyledWrapper = styled.div` - display: grid; - height: calc(100vh - 36px); - grid-template-columns: 100%; - grid-template-rows: minmax(0, 1fr); -`; - export const JsonEditor: React.FC = () => { - const [value, setValue] = React.useState(""); const [hasError, setHasError] = React.useState(false); - const json = useConfig((state) => state.json); - const lightmode = useStored((state) => state.lightmode); - const setJson = useConfig((state) => state.setJson); - - const editorTheme = React.useMemo( - () => (lightmode ? "light" : "vs-dark"), - [lightmode] - ); - - React.useEffect(() => { - setValue(JSON.stringify(JSON.parse(json), null, 2)); - }, [json]); - - React.useEffect(() => { - const formatTimer = setTimeout(() => { - try { - if (!value) { - setHasError(false); - return setJson("{}"); - } - - JSON.parse(value); - setJson(value); - setHasError(false); - } catch (jsonError: any) { - setHasError(true); - } - }, 1500); - - return () => clearTimeout(formatTimer); - }, [value, setJson]); - return ( - - } - /> - + ); }; diff --git a/src/containers/Editor/LiveEditor/GraphCanvas.tsx b/src/containers/Editor/LiveEditor/GraphCanvas.tsx new file mode 100644 index 0000000..fbfc21f --- /dev/null +++ b/src/containers/Editor/LiveEditor/GraphCanvas.tsx @@ -0,0 +1,49 @@ +import React from "react"; +import { NodeModal } from "src/containers/Modals/NodeModal"; +import useGraph from "src/hooks/store/useGraph"; +import { Graph } from "src/components/Graph"; + +export const GraphCanvas = ({ isWidget = false }: { isWidget?: boolean }) => { + const [isModalVisible, setModalVisible] = React.useState(false); + const [selectedNode, setSelectedNode] = React.useState<[string, string][]>( + [] + ); + + const openModal = React.useCallback(() => setModalVisible(true), []); + + const collapsedNodes = useGraph((state) => state.collapsedNodes); + const collapsedEdges = useGraph((state) => state.collapsedEdges); + + React.useEffect(() => { + const nodeList = collapsedNodes.map((id) => `[id$="node-${id}"]`); + const edgeList = collapsedEdges.map((id) => `[class$="edge-${id}"]`); + + const hiddenItems = document.querySelectorAll(".hide"); + hiddenItems.forEach((item) => item.classList.remove("hide")); + + if (nodeList.length) { + const selectedNodes = document.querySelectorAll(nodeList.join(",")); + const selectedEdges = document.querySelectorAll(edgeList.join(",")); + + selectedNodes.forEach((node) => node.classList.add("hide")); + selectedEdges.forEach((edge) => edge.classList.add("hide")); + } + }, [collapsedNodes, collapsedEdges]); + + return ( + <> + + {!isWidget && ( + setModalVisible(false)} + /> + )} + + ); +}; diff --git a/src/containers/Editor/LiveEditor/index.tsx b/src/containers/Editor/LiveEditor/index.tsx index 6a6e850..da78be1 100644 --- a/src/containers/Editor/LiveEditor/index.tsx +++ b/src/containers/Editor/LiveEditor/index.tsx @@ -1,7 +1,7 @@ import React from "react"; import styled from "styled-components"; import { Tools } from "src/containers/Editor/Tools"; -import { Graph } from "src/components/Graph"; +import { GraphCanvas } from "src/containers/Editor/LiveEditor/GraphCanvas"; const StyledLiveEditor = styled.div` position: relative; @@ -11,7 +11,7 @@ const LiveEditor: React.FC = () => { return ( - + ); }; diff --git a/src/containers/Modals/ShareModal/index.tsx b/src/containers/Modals/ShareModal/index.tsx index 9eac92a..bfdd041 100644 --- a/src/containers/Modals/ShareModal/index.tsx +++ b/src/containers/Modals/ShareModal/index.tsx @@ -51,11 +51,13 @@ export const ShareModal: React.FC = ({ visible, setVisible }) => { const shareURL = `${baseURL}/editor?json=${encodedJson}`; React.useEffect(() => { - const jsonEncode = compress(JSON.parse(json)); - const jsonString = JSON.stringify(jsonEncode); - - setEncodedJson(encodeURIComponent(jsonString)); - }, [json]); + if (visible) { + const jsonEncode = compress(JSON.parse(json)); + const jsonString = JSON.stringify(jsonEncode); + + setEncodedJson(encodeURIComponent(jsonString)); + } + }, [json, visible]); const handleShare = (value: string) => { navigator.clipboard.writeText(value); diff --git a/src/hooks/store/useGraph.tsx b/src/hooks/store/useGraph.tsx index fef926a..c443068 100644 --- a/src/hooks/store/useGraph.tsx +++ b/src/hooks/store/useGraph.tsx @@ -1,5 +1,4 @@ import create from "zustand"; -import { Graph } from "src/components/Graph"; import { getChildrenEdges } from "src/utils/getChildrenEdges"; import { getOutgoers } from "src/utils/getOutgoers";