diff --git a/client/src/components/ActionsSidebarWrapper.tsx b/client/src/components/ActionsSidebarWrapper.tsx
new file mode 100644
index 0000000..eed1306
--- /dev/null
+++ b/client/src/components/ActionsSidebarWrapper.tsx
@@ -0,0 +1,34 @@
+import { AnimatePresence, motion } from "motion/react";
+import clsx from "clsx";
+
+interface ActionsSidebarWrapperProps {
+ children: React.ReactNode;
+ className?: string;
+ show?: boolean;
+}
+
+function ActionsSidebarWrapper({
+ children,
+ className,
+ show = true,
+}: ActionsSidebarWrapperProps) {
+ return (
+
+ {show && (
+
+ {children}
+
+ )}
+
+ );
+}
+
+export default ActionsSidebarWrapper;
diff --git a/client/src/components/ui/ActionsPopover.tsx b/client/src/components/ui/ActionsPopover.tsx
index 2fedfe7..203f8dd 100644
--- a/client/src/components/ui/ActionsPopover.tsx
+++ b/client/src/components/ui/ActionsPopover.tsx
@@ -1,7 +1,7 @@
import { useState, useRef, useEffect } from "react";
import Button from "./Button";
import MoreIcon from "../icons/MoreIcon";
-import { AnimatePresence, motion } from "motion/react";
+import Popover from "./Popover";
interface ActionsPopoverProps {
options: {
@@ -14,10 +14,9 @@ interface ActionsPopoverProps {
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 | TouchEvent) => {
@@ -37,15 +36,6 @@ export default function ActionsPopover({ options }: ActionsPopoverProps) {
};
}, []);
- useEffect(() => {
- if (isOpened && buttonRef.current) {
- const buttonRect = buttonRef.current.getBoundingClientRect();
- const spaceBelow = window.innerHeight - buttonRect.bottom;
- const spaceAbove = buttonRect.top;
- setOpenUpwards(spaceBelow < menuHeight && spaceAbove > menuHeight);
- }
- }, [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)] overflow-hidden ${
- openUpwards ? "bottom-[100%]" : "top-[100%]"
- }`}
+
+ {options.map((option) => (
+
- )}
-
+ {option.icon}
+ {option.label}
+
+ ))}
+
);
}
diff --git a/client/src/components/ui/ControlsPopover.tsx b/client/src/components/ui/ControlsPopover.tsx
new file mode 100644
index 0000000..a7b3e1d
--- /dev/null
+++ b/client/src/components/ui/ControlsPopover.tsx
@@ -0,0 +1,78 @@
+import FloatingActionButton from "./FloatingActionButton";
+import Popover from "./Popover";
+import MoreIcon from "../icons/MoreIcon";
+import { useEffect, useRef, useState } from "react";
+import Button from "./Button";
+import ChatFilledIcon from "../icons/ChatFilledIcon";
+import UsersFilledIcon from "../icons/UsersFilledIcon";
+import ShareFilledIcon from "../icons/ShareFilledIcon";
+import CogFilledIcon from "../icons/CogFilledIcon";
+
+function ControlsPopover() {
+ const [isOpened, setIsOpened] = useState(false);
+ const buttonRef = useRef(null);
+
+ useEffect(() => {
+ const handleClickOutside = (event: MouseEvent | TouchEvent) => {
+ if (
+ buttonRef.current &&
+ !buttonRef.current.contains(event.target as Node)
+ ) {
+ setIsOpened(false);
+ }
+ };
+
+ document.addEventListener("mousedown", handleClickOutside);
+ document.addEventListener("touchstart", handleClickOutside);
+ return () => {
+ document.removeEventListener("mousedown", handleClickOutside);
+ document.removeEventListener("touchstart", handleClickOutside);
+ };
+ }, []);
+
+ return (
+
+
setIsOpened(!isOpened)}
+ >
+
+
+
+
+
+
+
+
+
+ Чат
+
+
+
+
+
+ Участники
+
+
+
+
+
+ Пригласить
+
+
+
+
+
+ Настройки
+
+
+
+ );
+}
+
+export default ControlsPopover;
diff --git a/client/src/components/ui/FloatingActionButton.tsx b/client/src/components/ui/FloatingActionButton.tsx
index 75f9641..41f1d15 100644
--- a/client/src/components/ui/FloatingActionButton.tsx
+++ b/client/src/components/ui/FloatingActionButton.tsx
@@ -3,7 +3,8 @@ import clsx from "clsx";
interface FloatingActionButtonProps
extends React.ButtonHTMLAttributes {
children: React.ReactNode;
- variant: "default" | "critical";
+ variant?: "default" | "critical";
+ ref?: React.RefObject;
}
function FloatingActionButton({
@@ -11,11 +12,13 @@ function FloatingActionButton({
variant = "default",
className,
onClick,
+ ref,
...props
}: FloatingActionButtonProps) {
return (
;
+ children: React.ReactNode;
+ className?: string;
+}
+
+function Popover({ isOpened, buttonRef, children, className }: PopoverProps) {
+ const [openUpwards, setOpenUpwards] = useState(false);
+ const [menuHeight, setMenuHeight] = useState(0);
+
+ useEffect(() => {
+ if (isOpened && buttonRef.current) {
+ const buttonRect = buttonRef.current.getBoundingClientRect();
+ const spaceBelow = window.innerHeight - buttonRect.bottom;
+ const spaceAbove = buttonRect.top;
+ setOpenUpwards(spaceBelow < menuHeight && spaceAbove > menuHeight);
+ }
+ }, [buttonRef, isOpened, menuHeight]);
+
+ return (
+
+ {isOpened && (
+ {
+ setMenuHeight(el?.offsetHeight || 0);
+ }}
+ className={clsx(
+ "absolute z-10 right-0 bg-white 2xl:rounded-[1.111vw] shadow-[0_4px_40px_0_rgba(15,16,17,0.1)] overflow-hidden rounded-2xl 2xl:p-[0.278vw] p-1",
+ openUpwards ? "bottom-full" : "top-full",
+ className
+ )}
+ >
+ {children}
+
+ )}
+
+ );
+}
+
+export default Popover;
diff --git a/client/src/main.tsx b/client/src/main.tsx
index 3a89c24..953fd6f 100644
--- a/client/src/main.tsx
+++ b/client/src/main.tsx
@@ -5,13 +5,14 @@ import { createBrowserRouter, RouterProvider } from "react-router";
import SessionPage from "./pages/SessionPage";
import LoginPage from "./pages/LoginPage";
import RegisterPage from "./pages/RegisterPage";
-import TestPage from "./pages/TestPage";
+// import TestPage from "./pages/TestPage";
import { QueryClientProvider } from "@tanstack/react-query";
import { queryClient } from "./lib/queryClient";
import ProtectedRoute from "./components/ProtectedRoute";
import PublicRoute from "./components/PublicRoute";
import ModalContainer from "./components/ModalContainer";
import PopupContainer from "./components/PopupContainer";
+import NewSessionPage from "./pages/NewSessionPage";
const router = createBrowserRouter([
{
@@ -40,7 +41,7 @@ const router = createBrowserRouter([
},
{
path: "/test",
- element: ,
+ element: ,
},
{
path: "/sessions/:id",
diff --git a/client/src/pages/NewSessionPage.tsx b/client/src/pages/NewSessionPage.tsx
new file mode 100644
index 0000000..1411c4e
--- /dev/null
+++ b/client/src/pages/NewSessionPage.tsx
@@ -0,0 +1,70 @@
+import ActionsSidebarWrapper from "../components/ActionsSidebarWrapper";
+import ChatFilledIcon from "../components/icons/ChatFilledIcon";
+import CogFilledIcon from "../components/icons/CogFilledIcon";
+import ExitFilledIcon from "../components/icons/ExitFilledIcon";
+import FullscreenIcon from "../components/icons/FullscreenIcon";
+import MicrophoneFilledIcon from "../components/icons/MicrophoneFilledIcon";
+import ShareFilledIcon from "../components/icons/ShareFilledIcon";
+import UsersFilledIcon from "../components/icons/UsersFilledIcon";
+import VideoOffFilledIcon from "../components/icons/VideoOffFilledIcon";
+import FloatingActionButton from "../components/ui/FloatingActionButton";
+import ParticipantsPopup from "../components/popups/ParticipantsPopup";
+import usePopupStore from "../store/popupStore";
+import ControlsPopover from "../components/ui/ControlsPopover";
+
+function NewSessionPage() {
+ const { setPopup } = usePopupStore();
+
+ return (
+
+
+ setPopup()}
+ >
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
+
+export default NewSessionPage;
diff --git a/client/tailwind.config.js b/client/tailwind.config.js
index a350a14..656f6ff 100644
--- a/client/tailwind.config.js
+++ b/client/tailwind.config.js
@@ -4,7 +4,7 @@ export default {
theme: {
extend: {},
screens: {
- "2xl": { min: "1440px" },
+ "2xl": "1440px",
},
},
plugins: [],