60 lines
1.7 KiB
TypeScript
60 lines
1.7 KiB
TypeScript
import { useState } from "react";
|
|
import ChevronDownIcon from "./icons/ChevronDownIcon";
|
|
import clsx from "clsx";
|
|
import { AnimatePresence, motion } from "motion/react";
|
|
import { useClickAway } from "@uidotdev/usehooks";
|
|
|
|
function Accordion({ text, title }: { title: string; text: string }) {
|
|
const [isOpen, setIsOpen] = useState(false);
|
|
|
|
const [initialHeight, setInitialHeight] = useState(0);
|
|
|
|
const [textHeight, setTextHeight] = useState(0);
|
|
|
|
const ref = useClickAway<HTMLDivElement>(() => setIsOpen(false));
|
|
|
|
return (
|
|
<motion.div
|
|
ref={(el) => {
|
|
if (el) {
|
|
ref.current = el;
|
|
setInitialHeight(el?.clientHeight || 0);
|
|
}
|
|
}}
|
|
animate={{
|
|
height: isOpen ? initialHeight + textHeight + 12 : initialHeight,
|
|
}}
|
|
className="p-[1.111vw] space-y-[0.833vw] bg-[#F6F6F6] rounded-[0.833vw] overflow-hidden cursor-pointer select-none"
|
|
onClick={() => setIsOpen(!isOpen)}
|
|
>
|
|
<div className="flex items-center justify-between">
|
|
<p className="button-m font-medium">{title}</p>
|
|
<div
|
|
className={clsx(
|
|
"text-[#7D7D7D] size-[1.389vw] transition-transform duration-300",
|
|
isOpen && "rotate-180"
|
|
)}
|
|
>
|
|
<ChevronDownIcon />
|
|
</div>
|
|
</div>
|
|
<AnimatePresence>
|
|
{isOpen && (
|
|
<motion.p
|
|
initial={{ opacity: 0 }}
|
|
animate={{ opacity: 1 }}
|
|
exit={{ opacity: 0 }}
|
|
transition={{ duration: 0.2 }}
|
|
ref={(el) => setTextHeight(el?.clientHeight || 0)}
|
|
className="text-s text-[#7D7D7D]"
|
|
>
|
|
{text}
|
|
</motion.p>
|
|
)}
|
|
</AnimatePresence>
|
|
</motion.div>
|
|
);
|
|
}
|
|
|
|
export default Accordion;
|