Add side bar transition animation between open and closed

This commit is contained in:
Daniel S. Hansen
2025-07-15 13:51:56 -07:00
parent b6a3b3d9d8
commit 9f61ab2216
2 changed files with 319 additions and 80 deletions
+3 -3
View File
@@ -2,7 +2,7 @@
// for 'Add Widget', 'Edit Widgets', and 'Remove Widgets'. This component will
// control settings related to the Widgets on the main canvas.
import { Button, Menu, Portal } from "@chakra-ui/react";
import { Button, IconButton, Menu, Portal } from "@chakra-ui/react";
import { FiSettings } from "react-icons/fi";
export function SettingsButton(props) {
@@ -25,9 +25,9 @@ export function SettingsButton(props) {
// This uses Chakra's built in menu component
<Menu.Root>
<Menu.Trigger asChild>
<Button variant="ghost" size="md">
<IconButton variant="ghost" size="md">
<FiSettings />
</Button>
</IconButton>
</Menu.Trigger>
<Portal>
<Menu.Positioner>
+310 -71
View File
@@ -1,4 +1,4 @@
import { Box, Button, createIcon, CloseButton, IconButton, Stack } from "@chakra-ui/react";
import { Box, Button, createIcon, CloseButton, IconButton, Stack, useDisclosure, Tooltip } from "@chakra-ui/react";
import { FaVolcano } from "react-icons/fa6";
import { RxHamburgerMenu } from "react-icons/rx";
import { useState } from "react";
@@ -56,87 +56,326 @@ export const Users = createIcon({
),
});
export default function Sidebar () {
export default function Sidebar() {
const [isOpen, setIsOpen] = useState(true);
const [activeButton, setActiveButton] = useState("Dashboard")
const [activeButton, setActiveButton] = useState("Dashboard");
return (
<>
{isOpen && (
<Box w="100%" h="100%"
borderRight="1px"
borderColor="black"
borderStyle="solid"
<Box
w={isOpen ? "250px" : "60px"}
h="100vh"
transition="width 0.4s ease"
borderRight="1px solid black"
display="flex"
flexDirection="column"
justifyContent="start"
alignItems="start"
gap={4} paddingTop={4}
paddingLeft={14} paddingRight={3}
bg="white"
p={4}
gap={4}
>
{/* <CloseButton color="secondary" _hover={{bg: "#EDF1F1"}} size="sm" variant="ghost" left="-10" onClick={() => setIsOpen(!isOpen)}></CloseButton> */}
{/* <RxHamburgerMenu color="secondary" _hover={{bg: "#EDF1F1"}} size="sm" variant="ghost" left="-10" onClick={() => setIsOpen(!isOpen)}></RxHamburgerMenu> */}
<Button
aria-lable="Toggle Sidebar"
color="secondary"
_hover={{bg: "#EDF1F1"}}
size="md" variant="ghost"
// left="-10"
ml="auto" // Margin-left: auto, pushes hamburger menu to right side
<IconButton
aria-label="Toggle Sidebar"
variant="ghost"
size="md"
// ml="auto"
// left="0"
onClick={() => setIsOpen(!isOpen)}
// icon={<RxHamburgerMenu />}
>
<RxHamburgerMenu />
</Button>
<Button color="#19332D" _hover={activeButton === "Dashboard" ? {bg: "#A4B6B6"}:{bg:"#EDF1F1"}} fontSize="xl" variant="ghost" onClick={() => setActiveButton("Dashboard")} bg={activeButton === "Dashboard" ? "#A4B6B6" : "transparent"}>
<PushPin fill="white" /> Dashboard
</Button>
<Button color="#19332D" _hover={activeButton === "Global" ? {bg: "#A4B6B6"}:{bg:"#EDF1F1"}} fontSize="xl" variant="ghost" onClick={() => setActiveButton("Global")} bg={activeButton === "Global" ? "#A4B6B6" : "transparent"}>
<Globe fill="white" stroke="black" /> Global Map
</Button>
<Button color="#19332D" _hover={activeButton === "Regional" ? {bg: "#A4B6B6"}:{bg:"#EDF1F1"}} fontSize="xl" variant="ghost" onClick={() => setActiveButton("Regional")} bg={activeButton === "Regional" ? "#A4B6B6" : "transparent"}>
<MapMarker fill="white" stroke="black"/> Regional Map
</Button>
<Button color="#19332D" _hover={activeButton === "Volcano" ? {bg: "#A4B6B6"}:{bg:"#EDF1F1"}} fontSize="xl" variant="ghost" onClick={() => setActiveButton("Volcano")} bg={activeButton === "Volcano" ? "#A4B6B6" : "transparent"}>
<FaVolcano /> Volcano
</Button>
<Button color="#19332D" _hover={activeButton === "Admin" ? {bg: "#A4B6B6"}:{bg:"#EDF1F1"}} fontSize="xl" variant="ghost" onClick={() => setActiveButton("Admin")} bg={activeButton === "Admin" ? "#A4B6B6" : "transparent"}>
<Users fill="white" stroke="black" /> Admin
</Button>
</Box>
)}
{!isOpen && (
<Box w="100%" h="100%"
borderRight="1px"
borderColor="black"
borderStyle="solid"
display="flex"
flexDirection="column"
justifyContent="start"
alignItems="start"
gap={4} padding={4}
</IconButton>
{/* Dashboard */}
<Button
color="#19332D"
fontSize="lg"
variant="ghost"
justifyContent={isOpen ? "flex-start" : "center"}
w="100%"
onClick={() => setActiveButton("Dashboard")}
bg={activeButton === "Dashboard" ? "#A4B6B6" : "transparent"}
_hover={{
bg: activeButton === "Dashboard" ? "#A4B6B6" : "#EDF1F1",
}}
>
<IconButton color="#19332D" _hover={{bg: "#EDF1F1"}} size="md" variant="ghost" onClick={() => setIsOpen(!isOpen)}>
<RxHamburgerMenu />
</IconButton>
<IconButton color="#19332D" _hover={activeButton === "Dashboard" ? {bg: "#A4B6B6"}:{bg:"#EDF1F1"}} variant="ghost" onClick={() => setActiveButton("Dashboard")} bg={activeButton === "Dashboard" ? "#A4B6B6" : "transparent"}>
<PushPin fill="white"/>
</IconButton>
<IconButton color="#19332D" _hover={activeButton === "Global" ? {bg: "#A4B6B6"}:{bg:"#EDF1F1"}} variant="ghost" onClick={() => setActiveButton("Global")} bg={activeButton === "Global" ? "#A4B6B6" : "transparent"}>
<Globe fill="white" stroke="black" />
</IconButton>
<IconButton color="#19332D" _hover={activeButton === "Regional" ? {bg: "#A4B6B6"}:{bg:"#EDF1F1"}} variant="ghost" onClick={() => setActiveButton("Regional")} bg={activeButton === "Regional" ? "#A4B6B6" : "transparent"}>
<MapMarker fill="white" stroke="black" />
</IconButton>
<IconButton color="#19332D" _hover={activeButton === "Volcano" ? {bg: "#A4B6B6"}:{bg:"#EDF1F1"}} variant="ghost" onClick={() => setActiveButton("Volcano")} bg={activeButton === "Volcano" ? "#A4B6B6" : "transparent"}>
<FaVolcano />
</IconButton>
<IconButton color="#19332D" _hover={activeButton === "Admin" ? {bg: "#A4B6B6"}:{bg:"#EDF1F1"}} variant="ghost" onClick={() => setActiveButton("Admin")} bg={activeButton === "Admin" ? "#A4B6B6" : "transparent"}>
<Users fill="white" stroke="black" />
</IconButton>
</Box>
)}
{isOpen ? (
<>
{/* <PushPin boxSize={5} style={{ marginRight: "8px" }} /> */}
<PushPin fill="white" />
{/* Make text appear gradually instead of popping in */}
<span
style={{
marginLeft: "8px",
opacity: isOpen ? 1 : 0,
maxWidth: isOpen ? "200px" : "0px", // prevent layout from expanding before fade-in
overflow: "hidden",
whiteSpace: "nowrap",
display: "inline-block",
transitionProperty: "opacity, max-width",
transitionDuration: "0.3s",
transitionTimingFunction: "ease",
transitionDelay: isOpen ? "0.1s" : "0s",
}}
>
Dashboard
</span>
</>
) : (
// <PushPin boxSize={5} />
<PushPin fill="white" />
)}
</Button>
{/* Global Map */}
<Button
color="#19332D"
fontSize="lg"
variant="ghost"
justifyContent={isOpen ? "flex-start" : "center"}
w="100%"
onClick={() => setActiveButton("Global")}
bg={activeButton === "Global" ? "#A4B6B6" : "transparent"}
_hover={{
bg: activeButton === "Global" ? "#A4B6B6" : "#EDF1F1",
}}
>
{isOpen ? (
<>
{/* <Globe boxSize={5} style={{ marginRight: "8px" }} /> */}
<Globe fill="white" stroke="black" />
{/* Make text appear gradually instead of popping in */}
<span
style={{
marginLeft: "8px",
opacity: isOpen ? 1 : 0,
maxWidth: isOpen ? "200px" : "0px", // prevent layout from expanding before fade-in
overflow: "hidden",
whiteSpace: "nowrap",
display: "inline-block",
transitionProperty: "opacity, max-width",
transitionDuration: "0.3s",
transitionTimingFunction: "ease",
transitionDelay: isOpen ? "0.1s" : "0s",
}}
>
Global Map
</span>
</>
) : (
// <Globe boxSize={5} />
<Globe fill="white" stroke="black" />
)}
</Button>
{/* Regional Map */}
<Button
color="#19332D"
fontSize="lg"
variant="ghost"
justifyContent={isOpen ? "flex-start" : "center"}
w="100%"
onClick={() => setActiveButton("Regional")}
bg={activeButton === "Regional" ? "#A4B6B6" : "transparent"}
_hover={{
bg: activeButton === "Regional" ? "#A4B6B6" : "#EDF1F1",
}}
>
{isOpen ? (
<>
{/* <MapMarker boxSize={5} style={{ marginRight: "8px" }} /> */}
<MapMarker fill="white" stroke="black"/>
{/* Make text appear gradually instead of popping in */}
<span
style={{
marginLeft: "8px",
opacity: isOpen ? 1 : 0,
maxWidth: isOpen ? "200px" : "0px", // prevent layout from expanding before fade-in
overflow: "hidden",
whiteSpace: "nowrap",
display: "inline-block",
transitionProperty: "opacity, max-width",
transitionDuration: "0.3s",
transitionTimingFunction: "ease",
transitionDelay: isOpen ? "0.1s" : "0s",
}}
>
Regional Map
</span>
</>
) : (
// <MapMarker boxSize={5} />
<MapMarker fill="white" stroke="black" />
)}
</Button>
{/* Volcano */}
<Button
color="#19332D"
fontSize="lg"
variant="ghost"
justifyContent={isOpen ? "flex-start" : "center"}
w="100%"
onClick={() => setActiveButton("Volcano")}
bg={activeButton === "Volcano" ? "#A4B6B6" : "transparent"}
_hover={{
bg: activeButton === "Volcano" ? "#A4B6B6" : "#EDF1F1",
}}
>
{isOpen ? (
<>
<FaVolcano size={20} style={{ marginRight: "8px" }} />
{/* Make text appear gradually instead of popping in */}
<span
style={{
marginLeft: "8px",
opacity: isOpen ? 1 : 0,
maxWidth: isOpen ? "200px" : "0px", // prevent layout from expanding before fade-in
overflow: "hidden",
whiteSpace: "nowrap",
display: "inline-block",
transitionProperty: "opacity, max-width",
transitionDuration: "0.3s",
transitionTimingFunction: "ease",
transitionDelay: isOpen ? "0.1s" : "0s",
}}
>
Volcano
</span>
</>
) : (
// <FaVolcano size={20} />
<FaVolcano />
)}
</Button>
{/* Admin */}
<Button
color="#19332D"
fontSize="lg"
variant="ghost"
justifyContent={isOpen ? "flex-start" : "center"}
w="100%"
onClick={() => setActiveButton("Admin")}
bg={activeButton === "Admin" ? "#A4B6B6" : "transparent"}
_hover={{
bg: activeButton === "Admin" ? "#A4B6B6" : "#EDF1F1",
}}
>
{isOpen ? (
<>
{/* <Users boxSize={5} style={{ marginRight: "8px" }} /> */}
<Users fill="white" stroke="black" />
{/* Make text appear gradually instead of popping in */}
<span
style={{
marginLeft: "8px",
opacity: isOpen ? 1 : 0,
maxWidth: isOpen ? "200px" : "0px", // prevent layout from expanding before fade-in
overflow: "hidden",
whiteSpace: "nowrap",
display: "inline-block",
transitionProperty: "opacity, max-width",
transitionDuration: "0.3s",
transitionTimingFunction: "ease",
transitionDelay: isOpen ? "0.1s" : "0s",
}}
>
Admin
</span>
</>
) : (
// <Users boxSize={5} />
<Users fill="white" stroke="black" />
)}
</Button>
</Box>
);
}
// THIS CODE WORKS, SAVE IN CASE OF EMERGENCY
// Needed to refactor in order allow transitions to work properly
// export default function Sidebar () {
// const [isOpen, setIsOpen] = useState(true);
// const [activeButton, setActiveButton] = useState("Dashboard")
// return (
// <>
// {isOpen && (
// <Box w="100%" h="100%"
// borderRight="1px"
// borderColor="black"
// borderStyle="solid"
// display="flex"
// flexDirection="column"
// justifyContent="start"
// alignItems="start"
// gap={4} paddingTop={4}
// paddingLeft={14} paddingRight={3}
// >
// {/* <CloseButton color="secondary" _hover={{bg: "#EDF1F1"}} size="sm" variant="ghost" left="-10" onClick={() => setIsOpen(!isOpen)}></CloseButton> */}
// {/* <RxHamburgerMenu color="secondary" _hover={{bg: "#EDF1F1"}} size="sm" variant="ghost" left="-10" onClick={() => setIsOpen(!isOpen)}></RxHamburgerMenu> */}
// <Button
// aria-lable="Toggle Sidebar"
// color="secondary"
// _hover={{bg: "#EDF1F1"}}
// size="md" variant="ghost"
// left="-10"
// // ml="auto" // Margin-left: auto, pushes hamburger menu to right side
// onClick={() => setIsOpen(!isOpen)}
// >
// <RxHamburgerMenu />
// </Button>
// <Button color="#19332D" _hover={activeButton === "Dashboard" ? {bg: "#A4B6B6"}:{bg:"#EDF1F1"}} fontSize="xl" variant="ghost" onClick={() => setActiveButton("Dashboard")} bg={activeButton === "Dashboard" ? "#A4B6B6" : "transparent"}>
// <PushPin fill="white" /> Dashboard
// </Button>
// <Button color="#19332D" _hover={activeButton === "Global" ? {bg: "#A4B6B6"}:{bg:"#EDF1F1"}} fontSize="xl" variant="ghost" onClick={() => setActiveButton("Global")} bg={activeButton === "Global" ? "#A4B6B6" : "transparent"}>
// <Globe fill="white" stroke="black" /> Global Map
// </Button>
// <Button color="#19332D" _hover={activeButton === "Regional" ? {bg: "#A4B6B6"}:{bg:"#EDF1F1"}} fontSize="xl" variant="ghost" onClick={() => setActiveButton("Regional")} bg={activeButton === "Regional" ? "#A4B6B6" : "transparent"}>
// <MapMarker fill="white" stroke="black"/> Regional Map
// </Button>
// <Button color="#19332D" _hover={activeButton === "Volcano" ? {bg: "#A4B6B6"}:{bg:"#EDF1F1"}} fontSize="xl" variant="ghost" onClick={() => setActiveButton("Volcano")} bg={activeButton === "Volcano" ? "#A4B6B6" : "transparent"}>
// <FaVolcano /> Volcano
// </Button>
// <Button color="#19332D" _hover={activeButton === "Admin" ? {bg: "#A4B6B6"}:{bg:"#EDF1F1"}} fontSize="xl" variant="ghost" onClick={() => setActiveButton("Admin")} bg={activeButton === "Admin" ? "#A4B6B6" : "transparent"}>
// <Users fill="white" stroke="black" /> Admin
// </Button>
// </Box>
// )}
// {!isOpen && (
// <Box w="100%" h="100%"
// borderRight="1px"
// borderColor="black"
// borderStyle="solid"
// display="flex"
// flexDirection="column"
// justifyContent="start"
// alignItems="start"
// gap={4} padding={4}
// >
// <IconButton color="#19332D" _hover={{bg: "#EDF1F1"}} size="md" variant="ghost" onClick={() => setIsOpen(!isOpen)}>
// <RxHamburgerMenu />
// </IconButton>
// <IconButton color="#19332D" _hover={activeButton === "Dashboard" ? {bg: "#A4B6B6"}:{bg:"#EDF1F1"}} variant="ghost" onClick={() => setActiveButton("Dashboard")} bg={activeButton === "Dashboard" ? "#A4B6B6" : "transparent"}>
// <PushPin fill="white"/>
// </IconButton>
// <IconButton color="#19332D" _hover={activeButton === "Global" ? {bg: "#A4B6B6"}:{bg:"#EDF1F1"}} variant="ghost" onClick={() => setActiveButton("Global")} bg={activeButton === "Global" ? "#A4B6B6" : "transparent"}>
// <Globe fill="white" stroke="black" />
// </IconButton>
// <IconButton color="#19332D" _hover={activeButton === "Regional" ? {bg: "#A4B6B6"}:{bg:"#EDF1F1"}} variant="ghost" onClick={() => setActiveButton("Regional")} bg={activeButton === "Regional" ? "#A4B6B6" : "transparent"}>
// <MapMarker fill="white" stroke="black" />
// </IconButton>
// <IconButton color="#19332D" _hover={activeButton === "Volcano" ? {bg: "#A4B6B6"}:{bg:"#EDF1F1"}} variant="ghost" onClick={() => setActiveButton("Volcano")} bg={activeButton === "Volcano" ? "#A4B6B6" : "transparent"}>
// <FaVolcano />
// </IconButton>
// <IconButton color="#19332D" _hover={activeButton === "Admin" ? {bg: "#A4B6B6"}:{bg:"#EDF1F1"}} variant="ghost" onClick={() => setActiveButton("Admin")} bg={activeButton === "Admin" ? "#A4B6B6" : "transparent"}>
// <Users fill="white" stroke="black" />
// </IconButton>
// </Box>
// )}
// </>
// );
// }