JSONCrack Codebase Analysis - Part 5 - Toolbar and Bottom bar
Tags: webdev, javascript, opensource, nextjs
Published: 2024-01-09 21:41:54

[jsoncrack](https://jsoncrack.com/editor) is a popular opensource tool used to visualise json into a mindmap. It is built using Next.js.

We, at [TThroo](https://tthroo.com/), 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.

In this part 5, we understand how `Toolbar` and `BottomBar` are configured.

## Interested to learn from opensource?
I am going to write a tutorial series where we use what we learnt from this jsoncrack codebase analysis, potentially involves usage of monaco editor, mantine, authentication and zustand configuration and modals, pretty much everything that is worth applying.

Before You ask, no, it is not going to be a jsoncrack clone but a unique project that uses similar codebase and similar approach and configuration to what we have seen in this series. It involves using supabase, zustand, mantine, monaco editor, nextjs.

[Join the waitlist](https://forms.gle/HECxzhUgH1JHxTnm6) and I will send you the link to the tutorials once they are ready.

## Where are they used?

They are used in [Editor](https://github.com/AykutSarac/jsoncrack.com/blob/main/src/pages/editor.tsx) component. [Part 4](https://dev.to/tthroo/jsoncrack-codebase-analysis-part-4-editor-56eh) explains about `Editor` component.

## Toolbar
You will find Toolbar inside [src/containers/Toolbar](https://github.com/AykutSarac/jsoncrack.com/blob/main/src/containers/Toolbar/index.tsx).

![Toolbar](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ogl1rjj03ph9nvq0gnb2.png)

```
export const Toolbar: React.FC<{ isWidget?: boolean }> = ({ isWidget = false }) => {
  const getJson = useJson(state => state.getJson);
  const setVisible = useModal(state => state.setVisible);

  const setFormat = useFile(state => state.setFormat);
  const format = useFile(state => state.format);
  const premium = useUser(state => state.premium);

  const handleSave = () => {
    const a = document.createElement("a");
    const file = new Blob([getJson()], { type: "text/plain" });

    a.href = window.URL.createObjectURL(file);
    a.download = "jsoncrack.json";
    a.click();
  };

  return (
    <Styles.StyledTools>
      {isWidget && <Logo />}
      {!isWidget && (
        <Group gap="xs" justify="left" w="100%" style={{ flexWrap: "nowrap" }}>
          <Styles.StyledToolElement title="JSON Crack">
            <Flex gap="xs" align="center" justify="center">
              <JSONCrackLogo fontSize="1.2em" />
            </Flex>
          </Styles.StyledToolElement>

          <Select
            defaultValue="json"
            size="xs"
            value={format}
            onChange={e => setFormat(e as FileFormat)}
            miw={80}
            w={120}
            data={[
              { value: FileFormat.JSON, label: "JSON" },
              { value: FileFormat.YAML, label: "YAML" },
              { value: FileFormat.XML, label: "XML" },
              { value: FileFormat.TOML, label: "TOML" },
              { value: FileFormat.CSV, label: "CSV" },
            ]}
          />

          <ViewModeMenu />
          <Styles.StyledToolElement title="Import File" onClick={() => setVisible("import")(true)}>
            Import
          </Styles.StyledToolElement>
          <ViewMenu />
          <ToolsMenu />
          <Styles.StyledToolElement title="Cloud" onClick={() => setVisible("cloud")(true)}>
            Cloud
          </Styles.StyledToolElement>
          <Styles.StyledToolElement title="Download as File" onClick={handleSave}>
            Download
          </Styles.StyledToolElement>
        </Group>
      )}
      <Group gap="xs" justify="right" w="100%" style={{ flexWrap: "nowrap" }}>
        {!premium && !isWidget && (
          <Styles.StyledToolElement onClick={() => setVisible("premium")(true)}>
            <Text display="flex" c="teal" fz="xs" fw="bold" style={{ textAlign: "center", gap: 4 }}>
              <MdWorkspacePremium size="18" />
              Get Premium
            </Text>
          </Styles.StyledToolElement>
        )}

        <SearchInput />
        {!isWidget && (
          <>
            <Styles.StyledToolElement
              title="Save as Image"
              onClick={() => setVisible("download")(true)}
            >
              <FiDownload size="18" />
            </Styles.StyledToolElement>
            <ZoomMenu />
            <AccountMenu />
            <OptionsMenu />
            <Styles.StyledToolElement
              title="Fullscreen"
              $hide={isWidget}
              onClick={fullscreenBrowser}
            >
              <AiOutlineFullscreen size="18" />
            </Styles.StyledToolElement>
          </>
        )}
      </Group>
    </Styles.StyledTools>
  );
};
```

`isWidget` flag is used to show either logo or the below optiions.

![Header Options](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ji0qxtkf2rsxr8l4e1fa.png)

## Select

```
<Select
  defaultValue="json"
  size="xs"
  value={format}
  onChange={e => setFormat(e as FileFormat)}
  miw={80}
  w={120}
  data={[
   { value: FileFormat.JSON, label: "JSON" },
   { value: FileFormat.YAML, label: "YAML" },
   { value: FileFormat.XML, label: "XML" },
   { value: FileFormat.TOML, label: "TOML" },
   { value: FileFormat.CSV, label: "CSV" },
  ]}
/>
```

It is a Select component from mantine

```
import { Flex, Group, Select, Text } from "@mantine/core";
```

`onChange` sets format in `useFile` store

## ViewMode

[ViewMode](https://github.com/AykutSarac/jsoncrack.com/blob/main/src/containers/Toolbar/ViewModeMenu.tsx) deserves a file with its own html and is a standalone that directly updates zustand store.

## Import

[Import](https://github.com/AykutSarac/jsoncrack.com/blob/main/src/containers/Toolbar/index.tsx#L79) sets `visible` prop in `useModal` store.

## ViewMenu

[ViewMenu](https://github.com/AykutSarac/jsoncrack.com/blob/main/src/containers/Toolbar/ViewMenu.tsx) has a lot of changes to update `useGraph` store that is directly correlated to the way visualisation is rendered using Reaflow.

And this is again in it own file under Toolbar folder because it has additional HTML that is not the right fit to be in `Toolbar/index.tsx`

The same approach above is applied to [ToolsMenu](https://github.com/AykutSarac/jsoncrack.com/blob/main/src/containers/Toolbar/ToolsMenu.tsx).

## Cloud

Cloud triggers a modal by setting visible prop in [useModal](https://github.com/AykutSarac/jsoncrack.com/blob/main/src/store/useModal.ts) store.

![Modals](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qj1karpmus19rrpv1b4l.png)

Modals are handled super neatly.
Took me a while to figure this one out. 

If you open [_app.tsx](https://github.com/AykutSarac/jsoncrack.com/blob/main/src/pages/_app.tsx#L62), You will find [ModalController](https://github.com/AykutSarac/jsoncrack.com/blob/main/src/layout/ModalController/index.tsx#L28)

```
const ModalController = () => {
  const setVisible = useModal(state => state.setVisible);
  const modalStates = useModal(state => modalComponents.map(modal => state[modal.key]));

  return (
    <EditorWrapper>
      {modalComponents.map(({ key, component }, index) => {
        const ModalComponent = component;
        const opened = modalStates[index];

        return <ModalComponent key={key} opened={opened} onClose={() => setVisible(key)(false)} />;
      })}
    </EditorWrapper>
  );
};
```

That is basically telling to render what is set to true in useModal store. All the Modals used are placed in a container specifically dedicated for Modals. How NEAT!.

This is one of the way cleanest ways to deal with Modals especially when you have a lot of them :)

You can follow through similarly to understand the other items available in Toolbar.

## Fullscreen Bar Icon

```
function fullscreenBrowser() {
  if (!document.fullscreenElement) {
    document.documentElement.requestFullscreen().catch(() => {
      toast.error("Unable to enter fullscreen mode.");
    });
  } else if (document.exitFullscreen) {
    document.exitFullscreen();
  }
}
```

Next time, You are looking for a piece of code to toggle in and out of a full screenmode, You now know how!

## BottomBar

`BottomBar` does not contain as many options as `Topbar`.

![Bottom Bar](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zw1ouk2l01lmshym8skr.png).

Interestingly, `Bottombar` is part of `Editor`, instead of it being a separate folder due to its limited number of options/features.

![Bottombar](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6553qkrl48uvo2pnw267.png)

```
<StyledBottomBar>
      {data?.name && (
        <Head>
          <title>{data.name} | JSON Crack</title>
        </Head>
      )}
      <StyledLeft>
        <StyledBottomBarItem onClick={toggleEditor}>
          <BiSolidDockLeft />
        </StyledBottomBarItem>

        {fileName && (
          <StyledBottomBarItem onClick={() => setVisible("cloud")(true)}>
            <VscSourceControl />
            {fileName}
          </StyledBottomBarItem>
        )}
        <StyledBottomBarItem>
          {error ? (
            <Popover width="auto" shadow="md" position="top" withArrow>
              <Popover.Target>
                <Flex align="center" gap={2}>
                  <VscError color="red" size={16} />
                  <Text c="red" fw={500} fz="xs">
                    Invalid
                  </Text>
                </Flex>
              </Popover.Target>
              <Popover.Dropdown
                style={{
                  pointerEvents: "none",
                }}
              >
                <Text size="xs">{error}</Text>
              </Popover.Dropdown>
            </Popover>
          ) : (
            <Flex align="center" gap={2}>
              <MdOutlineCheckCircleOutline />
              <Text size="xs">Valid</Text>
            </Flex>
          )}
        </StyledBottomBarItem>
        {(data?.owner_email === user?.email || (!data && user)) && (
          <StyledBottomBarItem onClick={handleSaveJson} disabled={isUpdating || error}>
            {hasChanges || !user ? <AiOutlineCloudUpload /> : <AiOutlineCloudSync />}
            {hasChanges || !user ? (query?.json ? "Unsaved Changes" : "Save to Cloud") : "Saved"}
          </StyledBottomBarItem>
        )}
        {data?.owner_email === user?.email && (
          <StyledBottomBarItem onClick={setPrivate} disabled={isUpdating}>
            {isPrivate ? <AiOutlineLock /> : <AiOutlineUnlock />}
            {isPrivate ? "Private" : "Public"}
          </StyledBottomBarItem>
        )}
        <StyledBottomBarItem
          onClick={() => setVisible("share")(true)}
          disabled={isPrivate || !data}
        >
          <AiOutlineLink />
          Share
        </StyledBottomBarItem>
        {liveTransformEnabled ? (
          <StyledBottomBarItem onClick={() => toggleLiveTransform(false)}>
            <VscSync />
            <Text fz="xs">Live Transform</Text>
          </StyledBottomBarItem>
        ) : (
          <StyledBottomBarItem onClick={() => toggleLiveTransform(true)}>
            <VscSyncIgnored />
            <Text fz="xs">Manual Transform</Text>
          </StyledBottomBarItem>
        )}
        {!liveTransformEnabled && (
          <StyledBottomBarItem onClick={() => setContents({})}>
            <TbTransform />
            Transform
          </StyledBottomBarItem>
        )}
      </StyledLeft>

      <StyledRight>
        <StyledBottomBarItem>Nodes: {nodeCount}</StyledBottomBarItem>
        <StyledBottomBarItem onClick={() => setVisible("review")(true)}>
          <VscFeedback />
          Feedback
        </StyledBottomBarItem>
      </StyledRight>
    </StyledBottomBar>
```
The code is pretty straigt forward, modifies what needs to be set in stores and few flags are to display what's needed.

If You ask me to refactor the above code, I would choose to move the following to separate component.

```
{liveTransformEnabled ? (
          <StyledBottomBarItem onClick={() => toggleLiveTransform(false)}>
            <VscSync />
            <Text fz="xs">Live Transform</Text>
          </StyledBottomBarItem>
        ) : (
          <StyledBottomBarItem onClick={() => toggleLiveTransform(true)}>
            <VscSyncIgnored />
            <Text fz="xs">Manual Transform</Text>
          </StyledBottomBarItem>
        )}
        {!liveTransformEnabled && (
          <StyledBottomBarItem onClick={() => setContents({})}>
            <TbTransform />
            Transform
          </StyledBottomBarItem>
        )}
```

In this new file, You could do the following:

```
const liveTransformEnabled = useConfig(state => state.liveTransformEnabled);

```
This could improve the readability, again, it is choice and not hard set rule, depends on dev teams and their preferences. You cannot over engineer at the same time, it is a fine balance. How you draw this fine balance is directly correlated to code maintainability and readability.

## Conclusion

We learnt how to use zustand stores and perform operations and do the state updates. 

`Modal` configuration was interesting, I have never seen it before. All the modals are placed inside a folder and a separate store for modals. You can leverage this pattern if your project is dealing with modals heavy operations to handle and maintain the code easily.

Up next, we have part 6 where we talk about how the payments and subscription is integrated into this product using [lemonsqueezy](https://www.lemonsqueezy.com/).

If you have any questions or need help with your project, feel free to reach out to me at ram@tthroo.com