Participants popup; Actions popover

This commit is contained in:
2025-10-09 15:40:30 +05:00
parent 8ca825475e
commit 6f57fcf9f1
6 changed files with 176 additions and 13 deletions
@@ -0,0 +1,91 @@
import { useState, useRef, useEffect } from "react";
interface ActionsPopoverProps {
options: {
label: string;
onClick: () => void;
icon?: React.ReactNode;
disabled?: boolean;
}[];
}
export default function ActionsPopover({ options }: ActionsPopoverProps) {
const [isOpened, setIsOpened] = useState(false);
const [openUpwards, setOpenUpwards] = useState(false);
const popoverRef = useRef<HTMLDivElement>(null);
const buttonRef = useRef<HTMLButtonElement>(null);
const [menuHeight, setMenuHeight] = useState(0);
useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
if (
popoverRef.current &&
!popoverRef.current.contains(event.target as Node)
) {
setIsOpened(false);
}
};
document.addEventListener("mousedown", handleClickOutside);
return () => {
document.removeEventListener("mousedown", handleClickOutside);
};
}, []);
useEffect(() => {
if (isOpened && buttonRef.current) {
const buttonRect = buttonRef.current.getBoundingClientRect();
const spaceBelow = window.innerHeight - buttonRect.bottom;
const spaceAbove = buttonRect.top;
console.log(spaceBelow, menuHeight, spaceAbove);
// Если снизу недостаточно места, но сверху достаточно - открываем вверх
if (spaceBelow < menuHeight && spaceAbove > menuHeight) {
setOpenUpwards(true);
} else {
setOpenUpwards(false);
}
}
}, [isOpened, menuHeight, options.length]);
return (
<div className="relative" ref={popoverRef}>
<button
ref={buttonRef}
className="flex items-center justify-center gap-[0.139vw] size-[1.667vw]"
onClick={() => setIsOpened(!isOpened)}
>
<div className="size-[0.208vw] rounded-full bg-[#CCCCCC]" />
<div className="size-[0.208vw] rounded-full bg-[#CCCCCC]" />
<div className="size-[0.208vw] rounded-full bg-[#CCCCCC]" />
</button>
{isOpened && (
<div
ref={(el) => {
setMenuHeight(el?.offsetHeight || 0);
}}
className={`absolute z-10 right-0 w-[13.333vw] bg-white rounded-[1.111vw] shadow-[0_4px_40px_0_rgba(15,16,17,0.1)] ${
openUpwards ? "bottom-[100%]" : "top-[100%]"
}`}
>
{options.map((option) => (
<button
className="p-[0.833vw] button-s text-[#7D7D7D] w-full flex items-center gap-[0.556vw]"
key={option.label}
onClick={option.onClick}
disabled={option.disabled}
style={{
backgroundColor: option.disabled ? "#F6F6F6" : "transparent",
opacity: option.disabled ? 0.5 : 1,
}}
>
<div className="size-[1.111vw] ">{option.icon}</div>
{option.label}
</button>
))}
</div>
)}
</div>
);
}