DEV Community

TThroo-Dev
TThroo-Dev

Posted on

JSONCrack Codebase Analysis — Part 4.3 — LiveEditor

jsoncrack is a popular opensource tool used to visualise json into a mindmap. It is built using Next.js.

We, at TThroo, love open source and perform codebase analysis on popular repositories, document, and provide a detailed explanation of the codebase. This enables OSS enthusiasts to learn and contribute to open source projects, and we also apply these learnings in our projects.

Part 4.1 - Editor - Panes Component has 2 main components.

  • JsonEditor Component

Part 4.2.1 — Editor — JsonEditor Component
and

Part 4.2.1.1 — JsonEditor — debouncedUpdateJson

  • LiveEditor Component

In this article, let's understand how LiveEditor works.

const LiveEditor: React.FC = () => {
  const [contextOpened, setContextOpened] = React.useState(false);
  const [contextPosition, setContextPosition] = React.useState({
    x: 0,
    y: 0,
  });

  return (
    <StyledLiveEditor
      onContextMenuCapture={e => {
        e.preventDefault();
        setContextOpened(true);
        setContextPosition({ x: e.pageX, y: e.pageY });
      }}
      onClick={() => setContextOpened(false)}
    >
      <div
        style={{
          position: "fixed",
          top: contextPosition.y,
          left: contextPosition.x,
          zIndex: 100,
        }}
      >
        <Menu opened={false} shadow="sm">
          <Menu.Dropdown>
            <Menu.Item>
              <Text size="xs">Download as Image</Text>
            </Menu.Item>
            <Menu.Item>
              <Text size="xs">Zoom to Fit</Text>
            </Menu.Item>
            <Menu.Item>
              <Text size="xs">Rotate</Text>
            </Menu.Item>
          </Menu.Dropdown>
        </Menu>
      </div>

      <View />
    </StyledLiveEditor>
  );
};
Enter fullscreen mode Exit fullscreen mode

Hang on minute? where is the LiveEditor? On the first look, it might be apparent but what are interested in is here: View

const View = () => {
  const viewMode = useConfig(state => state.viewMode);

  if (viewMode === ViewMode.Graph) return <Graph />;
  if (viewMode === ViewMode.Tree) return <TreeView />;
  return null;
};
Enter fullscreen mode Exit fullscreen mode

By this time, you must have understood the power off zustand to simplify the state management. useConfig is a zustand store.

View mode

A view mode that is header aka Toolbar is used to set the view type. You guessed it. We are now moving away from LiveEditor to Graph.

A rookie mistake here would be dump all the Graph and TreeView code in the same file, this only makes your life hard when it comes to maintenance.

Graph is in src/containers/Views/GraphView. Can the containers folder have Views? It it makes sense, so be it. Because this Views contains Graph and TreeView are in a way containers.

There is no strict rule to follow to place your files, you be the best judge and place them where it makes sense. You can move the files around as your project grows in size.

Graph

Graphs File Placement

No wonder Graph is placed in containers/Views. It just has more modularity to it.

return (
    <>
      <Loading loading={loading} message="Painting graph..." />
      <StyledEditorWrapper
        $widget={isWidget}
        onContextMenu={e => e.preventDefault()}
        onClick={blurOnClick}
        key={String(gesturesEnabled)}
        $showRulers={rulersEnabled}
        {...bindLongPress()}
      >
        <Space
          onCreate={setViewPort}
          onContextMenu={e => e.preventDefault()}
          treatTwoFingerTrackPadGesturesLikeTouch={gesturesEnabled}
          pollForElementResizing
        >
          <GraphCanvas isWidget={isWidget} />
        </Space>
      </StyledEditorWrapper>
    </>
  );
Enter fullscreen mode Exit fullscreen mode

Graph to GraphCanvas, Notice how the component we are interested is now changed GraphCanvas.

But why GraphCanvas? It is because Reaflow uses Canvas to render visualisations. Context matters here. Hence the name Canvas appended to GraphCanvas

const GraphCanvas = ({ isWidget }: GraphProps) => {
  const { validateHiddenNodes } = useToggleHide();
  const setLoading = useGraph(state => state.setLoading);
  const centerView = useGraph(state => state.centerView);
  const direction = useGraph(state => state.direction);
  const nodes = useGraph(state => state.nodes);
  const edges = useGraph(state => state.edges);

  const [paneWidth, setPaneWidth] = React.useState(2000);
  const [paneHeight, setPaneHeight] = React.useState(2000);

  const onLayoutChange = React.useCallback(
    (layout: ElkRoot) => {
      if (layout.width && layout.height) {
        const areaSize = layout.width * layout.height;
        const changeRatio = Math.abs((areaSize * 100) / (paneWidth * paneHeight) - 100);

        setPaneWidth(layout.width + 50);
        setPaneHeight((layout.height as number) + 50);

        setTimeout(() => {
          validateHiddenNodes();
          window.requestAnimationFrame(() => {
            if (changeRatio > 70 || isWidget) centerView();
            setLoading(false);
          });
        });
      }
    },
    [isWidget, paneHeight, paneWidth, centerView, setLoading, validateHiddenNodes]
  );

  return (
    <Canvas
      className="jsoncrack-canvas"
      onLayoutChange={onLayoutChange}
      node={p => <CustomNode {...p} />}
      edge={p => <CustomEdge {...p} />}
      nodes={nodes}
      edges={edges}
      maxHeight={paneHeight}
      maxWidth={paneWidth}
      height={paneHeight}
      width={paneWidth}
      direction={direction}
      layoutOptions={layoutOptions}
      key={direction}
      pannable={false}
      zoomable={false}
      animated={false}
      readonly={true}
      dragEdge={null}
      dragNode={null}
      fit={true}
    />
  );
};
Enter fullscreen mode Exit fullscreen mode

Remember when we were looking to understanding what was responsible for generating nodes and edges in Part 4.2.1.1 — JsonEditor — debouncedUpdateJson

GraphCanvas uses nodes and edges to render the visualisation.

This is how what you entered as a json in code editor visualised using Reaflow in the LiveEditor.

I just love how efficiently props management is done using zustand.

Conclusion

We navigated between few files to understand the workings for LiveEditor and saw how powerful zustand can be when it comes to state management.

Part 5 would be about Toolbar and Bottombar.

Part 6 - How the payments and subscription is integrated into this product using lemonsqueezy.

Thank you for reading till the end. If you have any questions or need help with a project, feel free to reach out to me at ram@tthroo.com

Top comments (0)