feat: refactor ManagerSelect and ProjectSelector for improved positioning logic and integrate new utility function

This commit is contained in:
2025-06-27 17:59:06 +05:00
parent e8060c594d
commit fc1858a6ed
4 changed files with 58 additions and 35 deletions
+5 -8
View File
@@ -5,6 +5,7 @@ import clsx from "clsx";
import CheckIcon from "./icons/CheckIcon";
import { useClickAway } from "@uidotdev/usehooks";
import { AnimatePresence, motion } from "motion/react";
import { getPositionAbove } from "../utils/getPositionAbove";
function ManagerSelect({
placeholder,
@@ -21,19 +22,15 @@ function ManagerSelect({
const selectRef = useClickAway<HTMLDivElement>(() => setIsOpen(false));
useEffect(() => {
const rect = selectRef.current?.getBoundingClientRect();
if (rect) {
setPosition(rect.top > window.innerHeight / 2 ? "top" : "bottom");
}
if (!isOpen || !selectRef.current) return;
setPosition(getPositionAbove(selectRef) ? "top" : "bottom");
}, [isOpen, selectRef]);
useEffect(() => {
const handleScroll = () => {
if (isOpen) {
const rect = selectRef.current?.getBoundingClientRect();
if (rect) {
setPosition(rect.top > window.innerHeight / 2 ? "top" : "bottom");
}
setPosition(getPositionAbove(selectRef) ? "top" : "bottom");
}
};
+20 -15
View File
@@ -1,10 +1,12 @@
import { useEffect, useState } from "react";
import { App } from "../types/App";
import ChevronDownIcon from "./icons/ChevronDownIcon";
import CheckIcon from "./icons/CheckIcon";
import { useClickAway } from "@uidotdev/usehooks";
import { AnimatePresence, motion } from "motion/react";
import clsx from "clsx";
import FlashIcon from "./icons/FlashIcon";
import SortIcon from "./icons/SortIcon";
import { getPositionAbove } from "../utils/getPositionAbove";
interface Props {
projects: App[];
@@ -24,19 +26,13 @@ function ProjectSelector({
const selectRef = useClickAway<HTMLDivElement>(() => setIsOpen(false));
useEffect(() => {
const rect = selectRef.current?.getBoundingClientRect();
if (rect) {
setPosition(rect.top > window.innerHeight / 2 ? "top" : "bottom");
}
setPosition(getPositionAbove(selectRef) ? "top" : "bottom");
}, [isOpen, selectRef]);
useEffect(() => {
const handleScroll = () => {
if (isOpen) {
const rect = selectRef.current?.getBoundingClientRect();
if (rect) {
setPosition(rect.top > window.innerHeight / 2 ? "top" : "bottom");
}
setPosition(getPositionAbove(selectRef) ? "top" : "bottom");
}
};
@@ -61,13 +57,20 @@ function ProjectSelector({
<div className="flex flex-col gap-[0.278vw]">
<div className="caption-s font-medium text-[#7D7D7D]">Проект</div>
<div className="flex items-center gap-[0.556vw]">
<img src="/images/app_image.png" className="size-[1.111vw]" alt="" />
<div className="text-s">{selectedProject?.name}</div>
</div>
</div>
<span className="size-[1.389vw] text-[#7D7D7D]">
<ChevronDownIcon />
</span>
<div className="flex items-center gap-[0.556vw]">
<img
src="/images/app_image.png"
className="size-[2.222vw]"
alt="app_image"
/>
<span className="size-[1.389vw] text-[#7D7D7D]">
<SortIcon />
</span>
</div>
<AnimatePresence>
{isOpen && (
<motion.div
@@ -100,13 +103,15 @@ function ProjectSelector({
</div>
<img
src="/images/app_image.png"
className="size-[1.111vw]"
className="size-[1.389vw]"
alt=""
/>
<div className="flex items-center gap-[0.278vw]">
<div className="text-s">{project.name}</div>
{activeProject && project.name === activeProject.name && (
<span className="size-[0.972vw] text-[#7B60F3]"></span>
<span className="size-[0.972vw] text-[#7B60F3]">
<FlashIcon />
</span>
)}
</div>
</div>
+22 -12
View File
@@ -39,7 +39,7 @@ export default function CreateSessionModal({ targetServerId, client }: Props) {
const { data: managers } = useQuery({
queryKey: ["managers"],
queryFn: () => api.get("managers").json<Manager[]>(),
queryFn: () => api.get("users").json<Manager[]>(),
});
const targetServer = targetServerId
@@ -176,6 +176,10 @@ export default function CreateSessionModal({ targetServerId, client }: Props) {
selectedServer,
]);
useEffect(() => {
console.log(managers);
}, [managers]);
const ref = useRef<HTMLFormElement>(null);
return (
@@ -246,20 +250,26 @@ export default function CreateSessionModal({ targetServerId, client }: Props) {
</div>
<div className="flex flex-col gap-y-[0.833vw]">
<p className="title-s font-medium">Выберите параметры сеанса</p>
<ManagerSelect placeholder="Менеджер сеанса" data={managers || []} />
{selectedServer &&
selectedServer?.appsToServers &&
selectedServer.appsToServers?.length > 0 && (
<ProjectSelector
activeProject={
selectedServer?.sessions?.[0]?.status === "started"
? selectedApp
: null
}
projects={selectedServer?.appsToServers.map(({ app }) => app)}
selectedProject={selectedApp}
setSelectedProject={setSelectedApp}
/>
<>
<ManagerSelect
placeholder="Менеджер сеанса"
data={managers || []}
/>
<ProjectSelector
activeProject={
selectedServer?.sessions?.[0]?.status === "started"
? selectedApp
: null
}
projects={selectedServer?.appsToServers.map(({ app }) => app)}
selectedProject={selectedApp}
setSelectedProject={setSelectedApp}
/>
</>
)}
</div>
+11
View File
@@ -0,0 +1,11 @@
export function getPositionAbove(selectRef: React.RefObject<HTMLDivElement>) {
const rect = selectRef.current.getBoundingClientRect();
const dropdownHeight = 200;
const margin = 8;
const spaceBelow = window.innerHeight - rect.bottom - margin;
const spaceAbove = rect.top - margin;
return spaceBelow < dropdownHeight && spaceAbove >= dropdownHeight;
}