From 6f57fcf9f144810aa15ef7c7717eb9460328762e Mon Sep 17 00:00:00 2001 From: C4rnivore Date: Thu, 9 Oct 2025 15:40:30 +0500 Subject: [PATCH] Participants popup; Actions popover --- client/public/img/popups/MessageUserPfp.png | Bin 1205 -> 2278 bytes client/src/components/PopupWrapper.tsx | 2 +- client/src/components/popups/ChatPopup.tsx | 13 ++- .../components/popups/ParticipantsPopup.tsx | 74 ++++++++++++++ client/src/components/ui/ActionsPopover.tsx | 91 ++++++++++++++++++ client/src/pages/HomePage.tsx | 9 +- 6 files changed, 176 insertions(+), 13 deletions(-) create mode 100644 client/src/components/popups/ParticipantsPopup.tsx create mode 100644 client/src/components/ui/ActionsPopover.tsx diff --git a/client/public/img/popups/MessageUserPfp.png b/client/public/img/popups/MessageUserPfp.png index e481b8b7ac6430bdcf73b9fc88d65ff8df52f77c..8d9c79383a4569b4751021ba8221f791d135fe73 100644 GIT binary patch delta 2273 zcmV<72p;#f3FZ+tiBL{Q4GJ0x0000DNk~Le0000s0000s2nGNE0E|Uy-2eap32;bR za{vGkoB#k2oB<}yIy8|sAAbmYNklv z=J%=IFb3CC2leTaNe&St$r_vY6z)ZuNQ+p5j|r*%>=V)>eS)@#`F$gmVTAY4<5E0U zh@cZWwM~J7pw$)*tA7<)OA&E_3ifhKY;jj1Z7 zB-Y^T53t9>69o}y4iwgz)-ZedWRlWgPA=&ji<;k)N{XTSVpGHrh8kl>K@GPfYcj}x z(E+m(68r}R%2zFc;#Ph6lwsJVv-itrjiO#hhg9@GP~plID1UxRpkNx2qfSS-{Jx5b z3Bcp!av{qzK*$N_BK=a})Yl3lasugwMMUV%-wMi}ap1j=@=*1L3!V}IhtA-c$b)+d zE}aYGw}a!j*Q20fmLpw)-q_k&J4u$rADevrJ_=6vz$RGmzPnLEU-;c9pBWW0mDy=^ zZg(sAY3m5?-G5f_RE2<|z&7+IIcaE`Tb;ZZ!Ul43fUcVg>YLqIRO6IFIV>;xQD_7& zotwZ9-wb2U{2V9|*hOdv;d!lrk`Vuhi@@5lOG^9NyN(v2`VFTP&@L&hA8z~NVi$HE z@yI^Y=*R1ZvtIiVZ%#A8R%GGGiO#28$2`b)3n^SC9)C}3Ur;EpoBI6cFGX5|tv-Nw z$R&wI_fyuj#b2}ghJx(}i{x`US~BZ9hpyINiqLfv4I|{!3-VJx5 z$SNrfg8JlT;LB}pa!PhxWt58)i+8@(+W}9L_gnay6OS4%)wobLUD(`QMY)KW>P}c~ zcITFYEq^~3Vmi5W@d&h)2$ekSU0MP*uN_4B3|oWMIymuSwKh*~9^jeT#-vnoZF~(6 z56j<*$%4f1#>U3t&#J-d%wts02P~WJu;rMjrz#e2snrI<6b>!>AkQ>HxN_wRHf`F3 z;^JcT_VyYI%w;`2J=n5k3vzRFarNrer2TR8cYi*}#mKPrxkf&^+F>Uq9H2Z>70s<= zUr*4Bb?eq)_3G7dyWQ~n{j$uc-i8euuyW-}%$hX|_4W0J^)yt!?v!z-RdCvmh*@{@ zxh?^3nYm4PJRYoBvj!Cv6(}euNLp{soHW}RX{qb5<8aX8#+T+qj4(1X z60bKjG?cPmUf1lv-eQz8T36B1(t?_r8n|389654Cmg(mYmo+prV8Ma~Qgf$IpEj&# zW9TWcN6fx2>&ziNe(#=aRFI?7IiSRh$Atg&@a^bay7%m$E3dbc!~nxtPo?gx3|m4 zgXh=&{rlq~Q(s?SbU+fmzrSA+W~XuV=uv5c*REYdXJ@Br2xb#)9C=(s7Qg~>B!A(E z?PHD~KQ4vm@>gDdIX;i77A}-^1_lOj;J^V{cY;z@wY9bJwwXc|xZvbNwr_nLEM*&C%pMQ>bckYxQCVv~Is0tC_ zdwlUAb9|7>9P?-$-MqUHrX8~bh~?8nEJJgX0^R5D-vJ~pji+X9|>{2cF%#A$^MB)a!R2#z>Ndwge7Drw?bBEWa9Klk!wNnB9K z#iXPV2Bgw)|3Ww#wdZ(`E`Nk~@X0+af(N$Zo|&YBwlb0|5p~Xc|iGOM8iSc|K9UV>C zj@(cq%d9Uq^s#J`WEhPwfwluqgpn4lLf@AAPDPAm+!iR>Ve~HP;z13XVhgt+css8r zW~9Yr={G&}ZBwNTXkG=PG8)5lRcsD;e96ceLk}N5Tu)=x ze%56DFW)i-eR&@Z-bt6*(b1uHcXz9L1cO0KWnEoevX2-ZkoNqPYDjzYo6+eVrP%^> z3y>gh)7SRV2@4;(d3EHcv@_BoAw+%fCNV%7I*AOCm1D`6Abiy^NxE0lwUoZPm!gqM vdal$p3DUa|Wg?3?ktDCVbLVDECK3Mvkb^LMN7%Q6#iztZW_l9Hl_5!j|)}LNe{h3 zZ1Zd2#A@qj5qP@f`IXXgq$#dq<;kByeEK)4U`1L z2K{r_lw+%zoTvn`D~1!-lPy?R1Z3JJ3T9`0Taw(i9q`J=xB4=D*jXC5@EPwpYPG>z5GjRQ{aVGzt&>I*p$j@gPafNt z|1I`XDR0z?S=#tw?QHRYr{SnfA`;?)-ZzVpY zT{+>m*Px^J$$yulJ&_w^b!}}8$z;-*4_jwk`r;^>4XNR$M@?vs-riH`95t`^sZ-NT*Rw{iT$2?T`@#$7WE>rV^jQjk7aTwKJ>Tet8#9XT93hStM}oOgt&1fp2P zPt()5dVl#cf}$pyt4@P{pRO*EKhx^wlmBe|{P(z1U!mSc%DvpUf1h5m7DmpVg{0Au zqcV8$RjP{D-|RgZbP46=bK{>o$BtAQLR?H`|4w(dyeb% zI{b9oHm!?p>U|wxWOC6B_7a^NQZ=GR 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()} >