diff --git a/client/public/img/popups/MessageUserPfp.png b/client/public/img/popups/MessageUserPfp.png index e481b8b..8d9c793 100644 Binary files a/client/public/img/popups/MessageUserPfp.png and b/client/public/img/popups/MessageUserPfp.png differ diff --git a/client/src/components/PopupWrapper.tsx b/client/src/components/PopupWrapper.tsx index e2e633a..c6549ec 100644 --- a/client/src/components/PopupWrapper.tsx +++ b/client/src/components/PopupWrapper.tsx @@ -75,7 +75,7 @@ function PopupWrapper({
diff --git a/client/src/components/popups/ChatPopup.tsx b/client/src/components/popups/ChatPopup.tsx index db4230d..eadbed8 100644 --- a/client/src/components/popups/ChatPopup.tsx +++ b/client/src/components/popups/ChatPopup.tsx @@ -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([ @@ -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 ( -
- - -
+ +
+ + +
+
); } diff --git a/client/src/components/popups/ParticipantsPopup.tsx b/client/src/components/popups/ParticipantsPopup.tsx new file mode 100644 index 0000000..eeaac15 --- /dev/null +++ b/client/src/components/popups/ParticipantsPopup.tsx @@ -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 ( + +
+
    + {participants.map((participant, index) => ( + <> + + {index !== participants.length - 1 && ( +
    + )} + + ))} +
+
+
+ ); +} + +function ParticipantItem({ id }: { id: string }) { + return ( +
+
+
+ user +
+
+ Иван Иванович {id} + Роль +
+
+ +
+ , + label: "Выключить микрофон", + onClick: () => {}, + }, + { + icon: , + label: "Выключить камеру", + onClick: () => {}, + disabled: true, + }, + { + icon: , + label: "Передать управление", + onClick: () => {}, + }, + { + icon: , + label: "Удалить со встречи", + onClick: () => {}, + }, + ]} + /> +
+
+ ); +} diff --git a/client/src/components/ui/ActionsPopover.tsx b/client/src/components/ui/ActionsPopover.tsx new file mode 100644 index 0000000..044a449 --- /dev/null +++ b/client/src/components/ui/ActionsPopover.tsx @@ -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(null); + const buttonRef = useRef(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 ( +
+ + + {isOpened && ( +
{ + 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) => ( + + ))} +
+ )} +
+ ); +} diff --git a/client/src/pages/HomePage.tsx b/client/src/pages/HomePage.tsx index 57bb2b9..ad51101 100644 --- a/client/src/pages/HomePage.tsx +++ b/client/src/pages/HomePage.tsx @@ -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() { setModal()} - onClick={() => setPopup()} + onClick={() => setPopup()} >