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 // for 'Add Widget', 'Edit Widgets', and 'Remove Widgets'. This component will
// control settings related to the Widgets on the main canvas. // 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"; import { FiSettings } from "react-icons/fi";
export function SettingsButton(props) { export function SettingsButton(props) {
@@ -25,9 +25,9 @@ export function SettingsButton(props) {
// This uses Chakra's built in menu component // This uses Chakra's built in menu component
<Menu.Root> <Menu.Root>
<Menu.Trigger asChild> <Menu.Trigger asChild>
<Button variant="ghost" size="md"> <IconButton variant="ghost" size="md">
<FiSettings /> <FiSettings />
</Button> </IconButton>
</Menu.Trigger> </Menu.Trigger>
<Portal> <Portal>
<Menu.Positioner> <Menu.Positioner>
+316 -77
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 { FaVolcano } from "react-icons/fa6";
import { RxHamburgerMenu } from "react-icons/rx"; import { RxHamburgerMenu } from "react-icons/rx";
import { useState } from "react"; 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 [isOpen, setIsOpen] = useState(true);
const [activeButton, setActiveButton] = useState("Dashboard") const [activeButton, setActiveButton] = useState("Dashboard");
return ( return (
<> <Box
{isOpen && ( w={isOpen ? "250px" : "60px"}
<Box w="100%" h="100%" h="100vh"
borderRight="1px" transition="width 0.4s ease"
borderColor="black" borderRight="1px solid black"
borderStyle="solid" display="flex"
display="flex" flexDirection="column"
flexDirection="column" alignItems="start"
justifyContent="start" bg="white"
alignItems="start" p={4}
gap={4} paddingTop={4} gap={4}
paddingLeft={14} paddingRight={3} >
> <IconButton
{/* <CloseButton color="secondary" _hover={{bg: "#EDF1F1"}} size="sm" variant="ghost" left="-10" onClick={() => setIsOpen(!isOpen)}></CloseButton> */} aria-label="Toggle Sidebar"
{/* <RxHamburgerMenu color="secondary" _hover={{bg: "#EDF1F1"}} size="sm" variant="ghost" left="-10" onClick={() => setIsOpen(!isOpen)}></RxHamburgerMenu> */} variant="ghost"
<Button size="md"
aria-lable="Toggle Sidebar" // ml="auto"
color="secondary" // left="0"
_hover={{bg: "#EDF1F1"}} onClick={() => setIsOpen(!isOpen)}
size="md" variant="ghost" // icon={<RxHamburgerMenu />}
// left="-10" >
ml="auto" // Margin-left: auto, pushes hamburger menu to right side <RxHamburgerMenu />
onClick={() => setIsOpen(!isOpen)} </IconButton>
> {/* Dashboard */}
<RxHamburgerMenu /> <Button
</Button> color="#19332D"
<Button color="#19332D" _hover={activeButton === "Dashboard" ? {bg: "#A4B6B6"}:{bg:"#EDF1F1"}} fontSize="xl" variant="ghost" onClick={() => setActiveButton("Dashboard")} bg={activeButton === "Dashboard" ? "#A4B6B6" : "transparent"}> fontSize="lg"
<PushPin fill="white" /> Dashboard variant="ghost"
</Button> justifyContent={isOpen ? "flex-start" : "center"}
<Button color="#19332D" _hover={activeButton === "Global" ? {bg: "#A4B6B6"}:{bg:"#EDF1F1"}} fontSize="xl" variant="ghost" onClick={() => setActiveButton("Global")} bg={activeButton === "Global" ? "#A4B6B6" : "transparent"}> w="100%"
<Globe fill="white" stroke="black" /> Global Map onClick={() => setActiveButton("Dashboard")}
</Button> bg={activeButton === "Dashboard" ? "#A4B6B6" : "transparent"}
<Button color="#19332D" _hover={activeButton === "Regional" ? {bg: "#A4B6B6"}:{bg:"#EDF1F1"}} fontSize="xl" variant="ghost" onClick={() => setActiveButton("Regional")} bg={activeButton === "Regional" ? "#A4B6B6" : "transparent"}> _hover={{
<MapMarker fill="white" stroke="black"/> Regional Map bg: activeButton === "Dashboard" ? "#A4B6B6" : "#EDF1F1",
</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 {isOpen ? (
</Button> <>
<Button color="#19332D" _hover={activeButton === "Admin" ? {bg: "#A4B6B6"}:{bg:"#EDF1F1"}} fontSize="xl" variant="ghost" onClick={() => setActiveButton("Admin")} bg={activeButton === "Admin" ? "#A4B6B6" : "transparent"}> {/* <PushPin boxSize={5} style={{ marginRight: "8px" }} /> */}
<Users fill="white" stroke="black" /> Admin <PushPin fill="white" />
</Button> {/* Make text appear gradually instead of popping in */}
</Box> <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>
{!isOpen && ( {/* Global Map */}
<Box w="100%" h="100%" <Button
borderRight="1px" color="#19332D"
borderColor="black" fontSize="lg"
borderStyle="solid" variant="ghost"
display="flex" justifyContent={isOpen ? "flex-start" : "center"}
flexDirection="column" w="100%"
justifyContent="start" onClick={() => setActiveButton("Global")}
alignItems="start" bg={activeButton === "Global" ? "#A4B6B6" : "transparent"}
gap={4} padding={4} _hover={{
> bg: activeButton === "Global" ? "#A4B6B6" : "#EDF1F1",
<IconButton color="#19332D" _hover={{bg: "#EDF1F1"}} size="md" variant="ghost" onClick={() => setIsOpen(!isOpen)}> }}
<RxHamburgerMenu /> >
</IconButton> {isOpen ? (
<IconButton color="#19332D" _hover={activeButton === "Dashboard" ? {bg: "#A4B6B6"}:{bg:"#EDF1F1"}} variant="ghost" onClick={() => setActiveButton("Dashboard")} bg={activeButton === "Dashboard" ? "#A4B6B6" : "transparent"}> <>
<PushPin fill="white"/> {/* <Globe boxSize={5} style={{ marginRight: "8px" }} /> */}
</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" /> <Globe fill="white" stroke="black" />
</IconButton> {/* Make text appear gradually instead of popping in */}
<IconButton color="#19332D" _hover={activeButton === "Regional" ? {bg: "#A4B6B6"}:{bg:"#EDF1F1"}} variant="ghost" onClick={() => setActiveButton("Regional")} bg={activeButton === "Regional" ? "#A4B6B6" : "transparent"}> <span
<MapMarker fill="white" stroke="black" /> style={{
</IconButton> marginLeft: "8px",
<IconButton color="#19332D" _hover={activeButton === "Volcano" ? {bg: "#A4B6B6"}:{bg:"#EDF1F1"}} variant="ghost" onClick={() => setActiveButton("Volcano")} bg={activeButton === "Volcano" ? "#A4B6B6" : "transparent"}> opacity: isOpen ? 1 : 0,
<FaVolcano /> maxWidth: isOpen ? "200px" : "0px", // prevent layout from expanding before fade-in
</IconButton> overflow: "hidden",
<IconButton color="#19332D" _hover={activeButton === "Admin" ? {bg: "#A4B6B6"}:{bg:"#EDF1F1"}} variant="ghost" onClick={() => setActiveButton("Admin")} bg={activeButton === "Admin" ? "#A4B6B6" : "transparent"}> 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" /> <Users fill="white" stroke="black" />
</IconButton> {/* Make text appear gradually instead of popping in */}
</Box> <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>
// )}
// </>
// );
// }