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
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

+1 -1
View File
@@ -75,7 +75,7 @@ function PopupWrapper({
<div
ref={wrapperRef}
className={clsx(
"2xl:rounded-[2.222vw] rounded-[32px] relative bg-white shadow-[0_4px_40px_0_rgba(15,16,17,0.1)]",
"2xl:rounded-[2.222vw] rounded-[32px] relative bg-white shadow-[0_4px_40px_0_rgba(15,16,17,0.1)] overflow-hid den",
className
)}
>
+8 -5
View File
@@ -3,6 +3,7 @@ import SendIcon from "../icons/SendIcon";
import Button from "../ui/Button";
import { useMe } from "../../hooks/useAuth";
import clsx from "clsx";
import PopupWrapper from "../PopupWrapper";
export default function ChatPopup() {
const [messages, setMessages] = useState<MessageItemProps[]>([
@@ -23,7 +24,7 @@ export default function ChatPopup() {
setMessages([
...messages,
{
senderId: "1",
senderId: "2",
timestamp: "12:22",
content: message,
},
@@ -31,10 +32,12 @@ export default function ChatPopup() {
}
return (
<div className="flex flex-col w-full h-full relative">
<MessageFeed messages={messages} />
<MessageInput onMessageSend={onMessageSend} />
</div>
<PopupWrapper title="Чат" draggable className="max-h-[40]">
<div className="flex flex-col h-[27.778vw] relative -m-[1.389vw]">
<MessageFeed messages={messages} />
<MessageInput onMessageSend={onMessageSend} />
</div>
</PopupWrapper>
);
}
@@ -0,0 +1,74 @@
import PopupWrapper from "../PopupWrapper";
import ActionsPopover from "../ui/ActionsPopover";
import MicrophoneFilledIcon from "../icons/MicrophoneFilledIcon";
import VideoOffFilledIcon from "../icons/VideoOffFilledIcon";
import HandRaisedFilledIcon from "../icons/HandRaisedFilledIcon";
import XMarkFilledIcon from "../icons/XMarkFilledIcon";
export default function ParticipantsPopup() {
const participants = [1, 2, 3];
return (
<PopupWrapper title="Чат" draggable className="h-max">
<div className="flex flex-col w-[21.667vw] relative">
<ul className="flex flex-col gap-[1.111vw]">
{participants.map((participant, index) => (
<>
<ParticipantItem key={participant} id={participant.toString()} />
{index !== participants.length - 1 && (
<div className="w-full h-[1px] bg-[#F6F6F6]" />
)}
</>
))}
</ul>
</div>
</PopupWrapper>
);
}
function ParticipantItem({ id }: { id: string }) {
return (
<div className="flex items-center justify-between w-full h-[2.5vw]">
<div className="flex items-center gap-[0.833vw]">
<div className="size-[2.5vw] rounded-full bg-[#F6F6F6]">
<img
src="/img/popups/MessageUserPfp.png"
className="size-full object-cover"
alt="user"
/>
</div>
<div className="flex flex-col gap-[0.278vw]">
<span className="button-m">Иван Иванович {id}</span>
<span className="caption-s text-[#CCCCCC]">Роль</span>
</div>
</div>
<div>
<ActionsPopover
options={[
{
icon: <MicrophoneFilledIcon />,
label: "Выключить микрофон",
onClick: () => {},
},
{
icon: <VideoOffFilledIcon />,
label: "Выключить камеру",
onClick: () => {},
disabled: true,
},
{
icon: <HandRaisedFilledIcon />,
label: "Передать управление",
onClick: () => {},
},
{
icon: <XMarkFilledIcon />,
label: "Удалить со встречи",
onClick: () => {},
},
]}
/>
</div>
</div>
);
}
@@ -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>
);
}
+2 -7
View File
@@ -1,14 +1,10 @@
<<<<<<< HEAD
import ChatPopup from "../components/popups/ChatPopup";
=======
>>>>>>> 8aef8a530bcdd53af4add911e773f2691e0027e4
import Button from "../components/ui/Button";
import FloatingActionButton from "../components/ui/FloatingActionButton";
import { useMe, useLogout } from "../hooks/useAuth";
import { useNavigate } from "react-router";
import ShareFilledIcon from "../components/icons/ShareFilledIcon";
import SharePopup from "../components/popups/SharePopup";
import usePopupStore from "../store/popupStore";
import ParticipantsPopup from "../components/popups/ParticipantsPopup";
function HomePage() {
const { data: user } = useMe();
@@ -32,8 +28,7 @@ function HomePage() {
<FloatingActionButton
variant="default"
// onClick={() => setModal(<ShareModal />)}
onClick={() => setPopup(<SharePopup />)}
onClick={() => setPopup(<ParticipantsPopup />)}
>
<div className="2xl:size-[1.111vw] size-4 text-white">
<ShareFilledIcon />