ev.stopPropagation()}
+ className="flex items-center justify-between w-full relative py-[2.222vw] 2xl:p-0"
+ >
+
Иван Иванович {id}
@@ -63,7 +75,18 @@ function ParticipantItem({ id }: { id: string }) {
-
+
+ {isNotControlling && (
+
+
+
+ )}
+ {isMuted && (
+
+
+
+ )}
+
{},
},
]}
+ parentRef={isMobile ? parentRef : undefined}
+ className={isMobile ? "left-0" : undefined}
/>
diff --git a/client/src/components/ui/ActionsPopover.tsx b/client/src/components/ui/ActionsPopover.tsx
index 831a101..bdd8627 100644
--- a/client/src/components/ui/ActionsPopover.tsx
+++ b/client/src/components/ui/ActionsPopover.tsx
@@ -2,6 +2,7 @@ import { useState, useRef, useEffect } from "react";
import Button from "./Button";
import MoreIcon from "../icons/MoreIcon";
import PopoverWrapper from "./PopoverWrapper";
+import clsx from "clsx";
interface ActionsPopoverProps {
options: {
@@ -10,47 +11,79 @@ interface ActionsPopoverProps {
icon?: React.ReactNode;
disabled?: boolean;
}[];
+ isOpened?: boolean;
+ parentRef?: React.RefObject
;
+ className?: string;
}
-export default function ActionsPopover({ options }: ActionsPopoverProps) {
- const [isOpened, setIsOpened] = useState(false);
-
+/**
+ * @param parentRef - Если передан, то кнопка активации опций не будет отображаться, а сам Popover будет отображаться по клику на parentRef.
+ */
+export default function ActionsPopover({
+ options,
+ isOpened = false,
+ parentRef,
+ className,
+}: ActionsPopoverProps) {
+ const [opened, setOpened] = useState(isOpened);
const popoverRef = useRef(null);
const buttonRef = useRef(null);
useEffect(() => {
+ let isHandling = false;
+
const handleClickOutside = (event: MouseEvent | TouchEvent) => {
- if (
- popoverRef.current &&
- !popoverRef.current.contains(event.target as Node)
- ) {
- setIsOpened(false);
+ // Предотвращаем двойное срабатывание mousedown и touchstart
+ if (isHandling) return;
+ isHandling = true;
+ setTimeout(() => {
+ isHandling = false;
+ }, 200);
+
+ const tgt = event.target as Node;
+ const clickedInsideParentElement = parentRef?.current?.contains(tgt);
+ const clickedInsidePopover = popoverRef.current?.contains(tgt);
+ if (clickedInsideParentElement && !clickedInsidePopover) {
+ //
+ } else {
+ // Если нет parentRef - закрытие только по клику вне popover и вне кнопки
+ const clickedInsideButton = buttonRef.current?.contains(tgt);
+ if (!clickedInsidePopover && !clickedInsideButton) {
+ setOpened(false);
+ }
}
};
document.addEventListener("mousedown", handleClickOutside);
document.addEventListener("touchstart", handleClickOutside);
+ parentRef?.current?.addEventListener("touchend", () => setOpened(!opened));
return () => {
document.removeEventListener("mousedown", handleClickOutside);
document.removeEventListener("touchstart", handleClickOutside);
+ parentRef?.current?.addEventListener("touchend", () =>
+ setOpened(!opened)
+ );
};
- }, []);
+ }, [parentRef]);
return (
-
-
+
+ {!parentRef && (
+
+ )}
+
{options.map((option) => (
))}
diff --git a/client/src/components/ui/Avatar.tsx b/client/src/components/ui/Avatar.tsx
index 96d3e94..2a7df1c 100644
--- a/client/src/components/ui/Avatar.tsx
+++ b/client/src/components/ui/Avatar.tsx
@@ -15,18 +15,21 @@ export default function Avatar({ size, status, src, name }: AvatarProps) {
{GetAvatarImage(src, name)}
{status === "caution" && (
diff --git a/client/src/components/ui/ControlsPopover.tsx b/client/src/components/ui/ControlsPopover.tsx
index 6c56c1a..1c3b19b 100644
--- a/client/src/components/ui/ControlsPopover.tsx
+++ b/client/src/components/ui/ControlsPopover.tsx
@@ -54,7 +54,7 @@ function ControlsPopover() {