83 lines
2.2 KiB
TypeScript
Executable File

import { FC, PropsWithChildren, ReactNode, useCallback, useState } from "react";
interface IProps {
expandOnHover?: boolean;
expanded?: boolean;
title?: ReactNode;
}
export const Accordion: FC<PropsWithChildren<IProps>> = (
{ children, expandOnHover, expanded, title },
) => {
const [open, setOpen] = useState(false);
return (
<div
data-expanded={open || expanded}
data-expandonhover={expandOnHover}
className={(expandOnHover ? "group/hover" : "group/controlled") +
" group"}
onClick={() => !title && !expandOnHover && setOpen(!open)}
>
{!!title && (
<div
className="flex justify-between cursor-pointer"
onClick={() => !expandOnHover && setOpen(!open)}
>
<div className="accordion-title">
{title}
</div>
<div
className={`
group-hover/hover:-rotate-180
group-data-[expanded]:-rotate-180
transition-transform
duration-500
grid
rounded-full
h-min
mr-2
mt-1
scale-y-50
`}
>
<span className="block w-2 h-2 rotate-45 border-r-2 border-b-2 place-self-center">
</span>
<span className="block w-2 h-2 rotate-45 border-r-2 border-b-2 place-self-center">
</span>
</div>
</div>
)}
{children}
</div>
);
};
export const AccordionContent: FC<PropsWithChildren> = ({ children }) => {
const [height, setHeight] = useState(0);
const updateRef = useCallback((node: HTMLDivElement | null) => {
if (node) {
setHeight(node.clientHeight);
} else {
setHeight(0);
}
}, []);
const Child = () => (
<div className="absolute bottom-0 w-full" ref={updateRef}>
{children}
</div>
);
return (
<div className="relative overflow-hidden">
{<Child />}
<span
style={{ ["--v-height" as never]: height + "px" }}
className="w-0 block h-0 group-hover/hover:h-variable group-data-[expanded]/controlled:h-variable transition-all duration-700"
/>
</div>
);
};