mirror of
https://github.com/AykutSarac/jsoncrack.com.git
synced 2025-01-27 15:22:56 +08:00
performance improvements & refactor
This commit is contained in:
parent
3d3de397c4
commit
7f8bd9d1f7
@ -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<SVGElement>, 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 (
|
||||
<StyledEditorWrapper isWidget={isWidget}>
|
||||
<TransformWrapper
|
||||
@ -145,49 +129,4 @@ const MemoizedGraph = React.memo(function Layout({
|
||||
</TransformWrapper>
|
||||
</StyledEditorWrapper>
|
||||
);
|
||||
});
|
||||
|
||||
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 (
|
||||
<>
|
||||
<MemoizedGraph
|
||||
openModal={openModal}
|
||||
setSelectedNode={setSelectedNode}
|
||||
isWidget={isWidget}
|
||||
/>
|
||||
{!isWidget && (
|
||||
<NodeModal
|
||||
selectedNode={selectedNode}
|
||||
visible={isModalVisible}
|
||||
closeModal={() => setModalVisible(false)}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
85
src/components/MonacoEditor/index.tsx
Normal file
85
src/components/MonacoEditor/index.tsx
Normal file
@ -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<string | undefined>("");
|
||||
|
||||
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 (
|
||||
<StyledWrapper>
|
||||
<Editor
|
||||
height="100%"
|
||||
defaultLanguage="json"
|
||||
value={value}
|
||||
theme={lightmode}
|
||||
options={editorOptions}
|
||||
onChange={setValue}
|
||||
loading={<Loading message="Loading Editor..." />}
|
||||
/>
|
||||
</StyledWrapper>
|
||||
);
|
||||
};
|
@ -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<string | undefined>("");
|
||||
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 (
|
||||
<StyledEditorWrapper>
|
||||
<ErrorContainer hasError={hasError} />
|
||||
<StyledWrapper>
|
||||
<MonacoEditor
|
||||
height="100%"
|
||||
defaultLanguage="json"
|
||||
value={value}
|
||||
theme={editorTheme}
|
||||
options={editorOptions}
|
||||
onChange={setValue}
|
||||
loading={<Loading message="Loading Editor..." />}
|
||||
/>
|
||||
</StyledWrapper>
|
||||
<MonacoEditor setHasError={setHasError} />
|
||||
</StyledEditorWrapper>
|
||||
);
|
||||
};
|
||||
|
49
src/containers/Editor/LiveEditor/GraphCanvas.tsx
Normal file
49
src/containers/Editor/LiveEditor/GraphCanvas.tsx
Normal file
@ -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 (
|
||||
<>
|
||||
<Graph
|
||||
openModal={openModal}
|
||||
setSelectedNode={setSelectedNode}
|
||||
isWidget={isWidget}
|
||||
/>
|
||||
{!isWidget && (
|
||||
<NodeModal
|
||||
selectedNode={selectedNode}
|
||||
visible={isModalVisible}
|
||||
closeModal={() => setModalVisible(false)}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
@ -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 (
|
||||
<StyledLiveEditor>
|
||||
<Tools />
|
||||
<Graph />
|
||||
<GraphCanvas />
|
||||
</StyledLiveEditor>
|
||||
);
|
||||
};
|
||||
|
@ -51,11 +51,13 @@ export const ShareModal: React.FC<ModalProps> = ({ 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);
|
||||
|
@ -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";
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user