I have a code of my ToolBar and there are groups that opening when clicked on button that opens it.
There are 3 of them: isToolGroupOpen, isColorGroupOpen and isWidthGroupOpen.
And I want to add transistion like here Animated Sliding Window
import { useEffect, useState, useCallback } from "react";
import "./ToolBar.css";
import { colorList, widthList } from "../constants.js";
import { FaPaintBrush, FaSquare, FaCircle, FaArrowRight } from "react-icons/fa";
import { AiOutlineLine } from "react-icons/ai";
import { IoFlashlight } from "react-icons/io5";
import { GiLaserBurst } from "react-icons/gi";
import { MdOutlineCancel } from "react-icons/md";
const ToolBar = ({
activeTool,
activeColorIndex,
activeWidthIndex,
handleReset,
handleChangeColor,
handleChangeWidth,
handleChangeTool,
}) => {
const [position, setPosition] = useState({ x: 0, y: 0 });
const [dragging, setDragging] = useState(false);
const [offset, setOffset] = useState({ x: 0, y: 0 });
const [isToolGroupOpen, setIsToolGroupOpen] = useState(false);
const [isColorGroupOpen, setIsColorGroupOpen] = useState(false);
const [isWidthGroupOpen, setIsWidthGroupOpen] = useState(false);
const [lastActiveFigure, setLastActiveFigure] = useState()
const onMouseDown = useCallback((e) => {
setDragging(true);
setOffset({
x: e.clientX - position.x,
y: e.clientY - position.y,
});
}, [position]);
const onMouseMove = useCallback((e) => {
if (!dragging) return;
setPosition({
x: e.clientX - offset.x,
y: e.clientY - offset.y,
});
}, [dragging, offset]);
const onMouseUp = useCallback(() => {
setDragging(false);
}, []);
useEffect(() => {
window.addEventListener("mousemove", onMouseMove);
window.addEventListener("mouseup", onMouseUp);
return () => {
window.removeEventListener("mousemove", onMouseMove);
window.removeEventListener("mouseup", onMouseUp);
};
}, [dragging, offset]);
const handleToolChange = (tool) => {
handleChangeTool(tool);
if (["rectangle", "oval", "line"].includes(tool)) {
setLastActiveFigure(tool);
}
setIsToolGroupOpen(false);
};
const handleColorChange = (index) => {
handleChangeColor(index);
setIsColorGroupOpen(false);
};
const handleWidthChange = (index) => {
handleChangeWidth(index);
setIsWidthGroupOpen(false);
};
const toggleGroup = (setGroupState) => () => {
setGroupState((prevState) => !prevState);
};
const renderGroupIcon = () => {
switch (activeTool) {
case "rectangle":
return <FaSquare />;
case "oval":
return <FaCircle />;
case "line":
return <AiOutlineLine />;
default:
switch (lastActiveFigure) {
case "rectangle":
return <FaSquare />;
case "oval":
return <FaCircle />;
case "line":
return <AiOutlineLine />;
default:
return <FaSquare />;
}
}
};
return (
<aside id="toolbar" style={{ left: position.x, top: position.y }}>
<div className="window__buttons">
<button>
<MdOutlineCancel size={15} />
</button>
</div>
<div className="toolbar__container">
<div className="toolbar__header">
<div className="draglines" onMouseDown={onMouseDown}>
<div />
<div />
<div />
</div>
</div>
<ul
className={`toolbar__items ${isColorGroupOpen ? "visible" : "hidden"}`}
>
{colorList.map((color, index) => (
<li key={index}>
<button
id="colorPicker"
onClick={() => handleColorChange(index)}
style={{ backgroundColor: color.color }}
/>
</li>
))}
</ul>
<ul
className={`toolbar__items ${isToolGroupOpen ? "visible" : "hidden"}`}
>
<li className={activeTool === "rectangle" ? "active" : ""}>
<button
name="rectangle"
onClick={() => handleToolChange("rectangle")}
>
<FaSquare />
</button>
</li>
<li className={activeTool === "oval" ? "active" : ""}>
<button name="oval" onClick={() => handleToolChange("oval")}>
<FaCircle />
</button>
</li>
<li className={activeTool === "line" ? "active" : ""}>
<button name="line" onClick={() => handleToolChange("line")}>
<AiOutlineLine />
</button>
</li>
</ul>
<ul
className={`toolbar__items ${isWidthGroupOpen ? "visible" : "hidden"}`}
>
{widthList.map((width, index) => (
<li key={index}>
<button className="toolbar__width-button" onClick={() => handleWidthChange(index)}>
<div
style={{
width: `${width.width / 3}px`,
}}
/>
</button>
</li>
))}
</ul>
<ul className={`toolbar__items ${
isToolGroupOpen || isColorGroupOpen || isWidthGroupOpen
? "hidden"
: "visible"
}`}>
<li className={activeTool === "arrow" ? "active" : ""}>
<button name="arrow" onClick={() => handleChangeTool("arrow")}>
<FaArrowRight />
</button>
</li>
<li className={activeTool === "pen" ? "active" : ""}>
<button name="pen" onClick={() => handleChangeTool("pen")}>
<FaPaintBrush />
</button>
</li>
<li className={["rectangle", "oval", "line"].includes(activeTool) ? "active" : ""}>
<button
name={activeTool}
onClick={toggleGroup(setIsToolGroupOpen)}
>
{renderGroupIcon()}
</button>
</li>
<li className={activeTool === "flashlight" ? "active" : ""}>
<button name="flashlight" onClick={() => handleChangeTool("flashlight")}>
<IoFlashlight />
</button>
</li>
<li className={activeTool === "laser" ? "active" : ""}>
<button name="laser" onClick={() => handleChangeTool("laser")}>
<GiLaserBurst />
</button>
</li>
<hr />
<li>
<button
id="colorPicker"
onClick={toggleGroup(setIsColorGroupOpen)}
style={{ backgroundColor: colorList[activeColorIndex].color }}
/>
</li>
<li>
<button className="toolbar__width-button" onClick={toggleGroup(setIsWidthGroupOpen)}>
<div
style={{
width: `${widthList[activeWidthIndex].width / 3}px`,
}}
/>
</button>
</li>
</ul>
<div className="toolbar__bottom">
<div className="draglines rotated" onMouseDown={onMouseDown}>
<div />
<div />
<div />
</div>
</div>
</div>
</aside>
);
};
export default ToolBar;
#toolbar {
position: absolute;
top: 0;
left: 0;
width: 70px;
height: 400px;
border-radius: 10px;
background-color: rgba(255, 255, 255, 0.2);
backdrop-filter: blur(17px);
border: 1px solid rgba(255, 255, 255, 0.2);
}
.toolbar__header,
.toolbar__bottom {
display: flex;
justify-content: center;
padding: 5px 0;
}
.toolbar__container {
display: flex;
flex-direction: column;
justify-content: space-between;
height: 380px;
}
.window__buttons button {
background: none;
border: none;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
padding: 2px;
width: 20px;
height: 20px;
}
.toolbar__items {
padding: 0;
display: flex;
justify-content: center;
flex-direction: column;
align-items: center;
height: 100%;
}
.toolbar__items.hidden {
height: 0;
overflow: hidden;
margin: 0;
opacity: 0;
}
.toolbar__items.visible {
margin-top: 5px;
/* opacity: 1; */
}
.toolbar__items li {
margin: 5px 0;
width: 30px;
height: 30px;
display: flex;
justify-content: center;
align-items: center;
}
.toolbar__items li.active {
background-color: #fff;
border-radius: 5px;
}
.toolbar__items li button {
background: none;
border: none;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
}
.toolbar__items li button:focus {
outline: none;
}
.toolbar__items li button svg {
width: 24px;
height: 24px;
pointer-events: none;
}
.toolbar__items hr {
border-color: #4c4c4c;
width: 80%;
}
.draglines {
width: 75%;
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: center;
height: 15px;
cursor: move;
}
.draglines div {
border: 1px solid #000;
border-radius: 15px;
}
.draglines div:nth-child(1) {
width: 75%;
}
.draglines div:nth-child(2) {
width: 85%;
}
.draglines div:nth-child(3) {
width: 95%;
}
.rotated {
transform: rotate(180deg);
}
#colorPicker {
position: relative;
width: 28px;
height: 28px;
border-radius: 50%;
border: none;
}
#colorPicker::before {
position: absolute;
content: "";
top: 50%;
left: 50%;
width: 20px;
height: 20px;
background: inherit;
border-radius: 50%;
border: 2px solid #fff;
transform: translate(-50%, -50%);
}
#toolbar .toolbar__width-button {
width: 28px;
height: 28px;
border: 2px solid #000;
border-radius: 50%;
}
.toolbar__width-button > div {
height: 18px;
background-color: #000;
transform: rotate(45deg);
border-radius: 5px;
}
==================================================