DEV Community

Cover image for Let's create a React File Manager Chapter X: Active Sidebar Node
Hasan Zohdy
Hasan Zohdy

Posted on

Let's create a React File Manager Chapter X: Active Sidebar Node

Now let's get the active node from the file manager and compare it to current node in SidebarNode, it will be active if they are the same.

// SidebarNode.tsx
...
import useFileManager from "../../../hooks/useFileManager";

export default function SidebarNode({ icon, node }: SidebarNodeProps) {
  const fileManager = useFileManager();

  const [isActiveNode, setIsActiveNode] = useState(
    node === fileManager.currentDirectoryNode,
  );

  useEffect(() => {
    setIsActiveNode(node === fileManager.currentDirectoryNode);
  }, [fileManager.currentDirectoryNode, node]);

...
}
Enter fullscreen mode Exit fullscreen mode

So now we can now if the current node is the active node if it matches the current directory node in the file manager.

Now let's highlight the node if it is the same, NavLink has active prop so we can use it to highlight the node, but we need to move it first inside sidebar node.

// SidebarNode.tsx
import { NavLink } from "@mantine/core";
import { IconFolder } from "@tabler/icons";
import { useEffect, useState } from "react";
import useFileManager from "../../../hooks/useFileManager";
import { Node } from "../../../types/FileManager.types";
import { IconWrapper } from "./SidebarNode.styles";

export type SidebarNodeProps = {
  node: Node;
  icon?: React.ReactNode;
};

export default function SidebarNode({ icon, node }: SidebarNodeProps) {
  const fileManager = useFileManager();

  const [isActiveNode, setIsActiveNode] = useState(
    node === fileManager.currentDirectoryNode,
  );

  useEffect(() => {
    setIsActiveNode(node === fileManager.currentDirectoryNode);
  }, [fileManager.currentDirectoryNode, node]);

  return (
    <>
      <NavLink
        label={
          <>
            <IconWrapper>{icon}</IconWrapper>
            <span>{node.name}</span>
          </>
        }
      />
    </>
  );
}

SidebarNode.defaultProps = {
  icon: <IconFolder fill="#31caf9" />,
};
Enter fullscreen mode Exit fullscreen mode

Now let's update Sidebar component and remove nav links from it.

// Sidebar.tsx
...

  return (
    <>
      <Card shadow="sm">
        <SidebarNode
          node={rootDirectory}
          icon={<IconHome2 size={16} color="#78a136" />}
        />
        {rootChildren?.map(child => (
          <SidebarNode
            key={child.path}
            icon={<IconFolder size={16} fill="#31caf9" />}
            node={child}
          />
        ))}
      </Card>
    </>
  );
Enter fullscreen mode Exit fullscreen mode

But we need to make more customization to NavLink for styling, so let's create navProps prop so it accepts any NavLink props.

// SidebarNode.tsx
import { NavLink, NavLinkProps } from "@mantine/core";
import { IconFolder } from "@tabler/icons";
import { useEffect, useState } from "react";
import useFileManager from "../../../hooks/useFileManager";
import { Node } from "../../../types/FileManager.types";
import { IconWrapper } from "./SidebarNode.styles";

export type SidebarNodeProps = {
  node: Node;
  icon?: React.ReactNode;
  navProps?: Partial<NavLinkProps>;
};

export default function SidebarNode({
  icon,
  node,
  navProps = {},
}: SidebarNodeProps) {
  const fileManager = useFileManager();

  const [isActiveNode, setIsActiveNode] = useState(
    node === fileManager.currentDirectoryNode,
  );

  useEffect(() => {
    setIsActiveNode(node === fileManager.currentDirectoryNode);
  }, [fileManager.currentDirectoryNode, node]);

  return (
    <>
      <NavLink
        {...navProps}
        label={
          <>
            <IconWrapper>{icon}</IconWrapper>
            <span>{node.name}</span>
          </>
        }
      />
    </>
  );
}

SidebarNode.defaultProps = {
  icon: <IconFolder fill="#31caf9" />,
};
Enter fullscreen mode Exit fullscreen mode

Now we can set the same props for the root and the children for paddings.

// Sidebar.tsx
...

  return (
    <>
      <Card shadow="sm">
        <SidebarNode
          node={rootDirectory}
          navProps={{
            p: 0,
          }}
          icon={<IconHome2 size={16} color="#78a136" />}
        />
        {rootChildren?.map(child => (
          <SidebarNode
            navProps={{
              p: 0,
              pl: 10,
            }}
            key={child.path}
            icon={<IconFolder size={16} fill="#31caf9" />}
            node={child}
          />
        ))}
      </Card>
    </>
  );
Enter fullscreen mode Exit fullscreen mode

Now let's mark the nav link as active if it is the same node.

// SidebarNode.tsx

import { NavLink, NavLinkProps } from "@mantine/core";
import { IconFolder } from "@tabler/icons";
import { useEffect, useState } from "react";
import useFileManager from "../../../hooks/useFileManager";
import { Node } from "../../../types/FileManager.types";
import { IconWrapper } from "./SidebarNode.styles";

export type SidebarNodeProps = {
  node: Node;
  icon?: React.ReactNode;
  navProps?: Partial<NavLinkProps>;
};

export default function SidebarNode({
  icon,
  node,
  navProps = {},
}: SidebarNodeProps) {
  const fileManager = useFileManager();

  const [isActiveNode, setIsActiveNode] = useState(
    node === fileManager.currentDirectoryNode,
  );

  useEffect(() => {
    setIsActiveNode(node === fileManager.currentDirectoryNode);
  }, [fileManager.currentDirectoryNode, node]);

  return (
    <>
      <NavLink
        {...navProps}
        active={isActiveNode}
        label={
          <>
            <IconWrapper>{icon}</IconWrapper>
            <span>{node.name}</span>
          </>
        }
      />
    </>
  );
}
...
Enter fullscreen mode Exit fullscreen mode

Now we can see something like this:

IFile Manager

In the next part, we will add the ability to navigate between directories.

Article Repository

You can see chapter files in Github Repository

Don't forget the main branch has the latest updated code.

Tell me where you are now

If you're following up with me this series, tell me where are you now and what you're struggling with, i'll try to help you as much as i can.

Salam.

Oldest comments (0)