I want the active link to remain as the hovered style. This current styling was done using Chakra UI.
SideNave.jsx
import React, { useState, useEffect } from "react";
import axios from "axios";
import {
Box,
CloseButton,
Flex,
Hide,
Image,
Icon,
useColorModeValue,
Drawer,
DrawerContent,
useDisclosure
} from "@chakra-ui/react";
import { FiCompass, FiSettings } from "react-icons/fi";
import { NavLink, useLocation } from "react-router-dom";
import {
AiFillClockCircle,
AiOutlineTransaction,
AiOutlineDashboard
} from "react-icons/ai";
import { IoApps, IoLockOpen, IoAnalytics } from "react-icons/io5";
import {
BsFillPeopleFill,
BsFillPersonFill,
BsBookHalf,
BsCurrencyBitcoin
} from "react-icons/bs";
const LinkItems = [
{
name: "Overview",
icon: AiOutlineDashboard
},
{ name: "Apps", icon: IoApps },
{ name: "Customers", icon: BsFillPeopleFill },
{ name: "Financial data", icon: IoAnalytics, hasDropdown: true },
{ name: "More", icon: FiSettings, hasDropdown: true }
];
function SideNav() {
const token = sessionStorage.getItem("token");
const { isOpen, onOpen, onClose } = useDisclosure();
return (
<Box bg={useColorModeValue("gray.100", "gray.900")}>
<SidebarContent
onClose={() => onClose}
display={{ base: "none", md: "block" }}
/>
<Drawer
isOpen={isOpen}
placement="left"
onClose={onClose}
returnFocusOnClose={false}
onOverlayClick={onClose}
size="full"
>
<DrawerContent>
<SidebarContent onClose={onClose} />
</DrawerContent>
</Drawer>
{/* mobilenav <MobileNav display={{ base: "flex", md: "none" }} onOpen={onOpen} /> */}
<Box ml={{ base: 0, md: 60 }} p={{ base: 0, md: "4" }}>
{/* Content */}
</Box>
</Box>
);
}
function SidebarContent({ onClose, post, ...rest }) {
return (
<Box
bg="whiteAlpha.800"
borderRight="1px"
borderRightColor={useColorModeValue("gray.200", "gray.700")}
w={{ base: "full", md: 48, lg: 44, xl: 48, "2xl": 60 }}
pos="fixed"
overflow="auto"
sx={{
"&::-webkit-scrollbar": {
width: "6px",
backgroundColor: "#f1f1f1",
borderRadius: "68px"
},
"&::-webkit-scrollbar-thumb": {
backgroundColor: "#c1c1c1",
borderRadius: "68px"
}
}}
h="full"
{...rest}
>
<Flex
h="20"
mb={12}
alignItems="center"
mx="8"
justifyContent="space-between"
>
<CloseButton display={{ base: "flex", md: "none" }} onClick={onClose} />
</Flex>
{LinkItems.map((link) => (
<React.Fragment key={link.name}>
{link.name === "Financial data" || link.name === "More" ? (
<NavItem icon={link.icon} hasDropdown={link.hasDropdown}>
{link.name}
</NavItem>
) : (
<div className="nav">
<NavLink
to={`/dashboard/${link.name.toLowerCase().replace(" ", "-")}`}
>
<NavItem icon={link.icon} hasDropdown={link.hasDropdown}>
{link.name}
</NavItem>
</NavLink>
</div>
)}
</React.Fragment>
))}
</Box>
);
}
function NavItem({ icon, children, hasDropdown, ...rest }) {
const [isDropdownOpen, setIsDropdownOpen] = useState(false);
const handleDropdownToggle = () => {
setIsDropdownOpen(!isDropdownOpen);
};
return (
<Box
as="div"
style={{ textDecoration: "none" }}
_focus={{ boxShadow: "none" }}
mt={4}
>
<Flex
align="center"
p="2"
mx="2"
borderRadius="lg"
role="group"
fontSize="15px"
cursor="pointer"
_hover={{
bg: "#002c8a",
color: "white",
borderColor: "white"
}}
onClick={hasDropdown ? handleDropdownToggle : undefined}
{...rest}
>
{icon && (
<Icon
mr="4"
p={1}
border="1px"
borderRadius="full"
borderColor="gray.400"
fontSize="24"
as={icon}
/>
)}
{children}
</Flex>
{hasDropdown && isDropdownOpen && <DropdownOptions name={children} />}
</Box>
);
}
function DropdownOptions({ name }) {
const options = getDropdownOptions(name);
const location = useLocation();
return (
<Flex
flexDirection="column"
ml="4"
boxShadow="lg"
w="150px"
p={2}
borderRadius="lg"
>
{options.map((option) => (
<NavLink
key={option.text}
to={`/dashboard/${option.text.toLowerCase()}`}
>
<Box
key={option}
className={`dropdown-option ${
location.pathname.includes(option.text.toLowerCase())
? "active-dot"
: ""
}`}
as="div"
py="2"
px="1"
borderRadius="lg"
w="130px"
fontSize="14px"
_hover={{
bg: "#002c8a",
color: "white"
}}
>
{option.icon && (
<Icon
mr="2"
fontSize="14"
verticalAlign="middle"
as={option.icon}
/>
)}
{option.text}
</Box>
</NavLink>
))}
</Flex>
);
}
function getDropdownOptions(name) {
if (name === "Financial data") {
return [
{
text: "Records",
icon: AiFillClockCircle
},
{
text: "Transactions",
icon: AiOutlineTransaction
},
{ text: "Balance", icon: BsBookHalf },
{
text: "Auth",
icon: IoLockOpen
}
];
} else if (name === "More") {
return [
{ text: "Logs", icon: IoAnalytics },
{ text: "Billings", icon: BsCurrencyBitcoin },
{ text: "Teams", icon: FiCompass },
{ text: "Profile", icon: BsFillPersonFill }
];
}
return [];
}
export default SideNav;
I created a minimal recreation here: https://codesandbox.io/s/navlink-project-n4d6cs?file=/SideNav.jsx
Use the NavLink
component's children
function to pass through the link's isActive
state to the NavItem
component, then conditionally apply the "hover" style.
<NavLink
to={`/dashboard/${link.name.toLowerCase().replace(" ", "-")}`}
>
{({ isActive }) => ( // <-- isActive prop
<NavItem
icon={link.icon}
hasDropdown={link.hasDropdown}
isActive={isActive} // <-- pass through
>
{link.name}
</NavItem>
)}
</NavLink>
NavItem
function NavItem({ icon, children, hasDropdown, isActive, ...rest }) { // <-- consume
const [isDropdownOpen, setIsDropdownOpen] = useState(false);
const handleDropdownToggle = () => {
setIsDropdownOpen(!isDropdownOpen);
};
return (
<Box
as="div"
style={{ textDecoration: "none" }}
_focus={{ boxShadow: "none" }}
mt={4}
>
<Flex
align="center"
p="2"
mx="2"
borderRadius="lg"
role="group"
fontSize="15px"
cursor="pointer"
_hover={{
bg: "#002c8a",
color: "white",
borderColor: "white"
}}
onClick={hasDropdown ? handleDropdownToggle : undefined}
{...(isActive // <-- conditional application
? {
bg: "#002c8a",
color: "white",
borderColor: "white"
}
: {})}
{...rest}
>
{icon && (
<Icon
mr="4"
p={1}
border="1px"
borderRadius="full"
borderColor="gray.400"
fontSize="24"
as={icon}
/>
)}
{children}
</Flex>
{hasDropdown && isDropdownOpen && <DropdownOptions name={children} />}
</Box>
);
}