fix: tools popover overflowing screen on small viewports (#5549)
* fix tools popover from expanding beyond screen height limits * fix max height clamp | remove unneeded useLayoutEffect dependency --------- Co-authored-by: Timothy Carambat <rambat1010@gmail.com>
This commit is contained in:
parent
2c82e9df5f
commit
ce900cb517
@ -1,4 +1,11 @@
|
|||||||
import { useState, useEffect, useCallback, useRef, useMemo } from "react";
|
import {
|
||||||
|
useState,
|
||||||
|
useEffect,
|
||||||
|
useLayoutEffect,
|
||||||
|
useCallback,
|
||||||
|
useRef,
|
||||||
|
useMemo,
|
||||||
|
} from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import useUser from "@/hooks/useUser";
|
import useUser from "@/hooks/useUser";
|
||||||
import AgentSkillsTab from "./Tabs/AgentSkills";
|
import AgentSkillsTab from "./Tabs/AgentSkills";
|
||||||
@ -50,7 +57,9 @@ export default function ToolsMenu({
|
|||||||
const TABS = useMemo(() => getTabs(t, user), [t, user]);
|
const TABS = useMemo(() => getTabs(t, user), [t, user]);
|
||||||
const [activeTab, setActiveTab] = useState(TABS[0].key);
|
const [activeTab, setActiveTab] = useState(TABS[0].key);
|
||||||
const [highlightedIndex, setHighlightedIndex] = useState(-1);
|
const [highlightedIndex, setHighlightedIndex] = useState(-1);
|
||||||
|
const [maxHeight, setMaxHeight] = useState(360);
|
||||||
const itemCountRef = useRef(0);
|
const itemCountRef = useRef(0);
|
||||||
|
const popoverRef = useRef(null);
|
||||||
|
|
||||||
// Always open to the slash commands
|
// Always open to the slash commands
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -62,6 +71,24 @@ export default function ToolsMenu({
|
|||||||
setHighlightedIndex(-1);
|
setHighlightedIndex(-1);
|
||||||
}, [activeTab, showing]);
|
}, [activeTab, showing]);
|
||||||
|
|
||||||
|
// Constrain popover height to the space available in the viewport so it
|
||||||
|
// never overflows off-screen on shorter windows (e.g. centered home view).
|
||||||
|
useLayoutEffect(() => {
|
||||||
|
if (!showing) return;
|
||||||
|
const update = () => {
|
||||||
|
const el = popoverRef.current;
|
||||||
|
if (!el) return;
|
||||||
|
const rect = el.getBoundingClientRect();
|
||||||
|
const available = centered
|
||||||
|
? window.innerHeight - rect.top - 16
|
||||||
|
: rect.bottom - 16;
|
||||||
|
setMaxHeight(Math.max(0, Math.min(360, available)));
|
||||||
|
};
|
||||||
|
update();
|
||||||
|
window.addEventListener("resize", update);
|
||||||
|
return () => window.removeEventListener("resize", update);
|
||||||
|
}, [showing, centered]);
|
||||||
|
|
||||||
// Keep the parent ref in sync so PromptInput can check it on Enter
|
// Keep the parent ref in sync so PromptInput can check it on Enter
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (highlightedIndexRef) highlightedIndexRef.current = highlightedIndex;
|
if (highlightedIndexRef) highlightedIndexRef.current = highlightedIndex;
|
||||||
@ -119,15 +146,15 @@ export default function ToolsMenu({
|
|||||||
onClick={() => setShowing(false)}
|
onClick={() => setShowing(false)}
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
|
ref={popoverRef}
|
||||||
onMouseDown={(e) => {
|
onMouseDown={(e) => {
|
||||||
// Prevents prompt textarea from losing focus when clicking inside the menu.
|
// Prevents prompt textarea from losing focus when clicking inside the menu.
|
||||||
// Skip for portaled modals so their inputs can still receive focus.
|
// Skip for portaled modals so their inputs can still receive focus.
|
||||||
if (e.currentTarget.contains(e.target)) e.preventDefault();
|
if (e.currentTarget.contains(e.target)) e.preventDefault();
|
||||||
}}
|
}}
|
||||||
|
style={{ maxHeight }}
|
||||||
className={`absolute left-2 right-2 md:left-14 md:right-auto md:w-[400px] z-50 bg-zinc-800 light:bg-white border border-zinc-700 light:border-slate-300 rounded-lg p-3 flex flex-col gap-2.5 shadow-lg overflow-hidden ${
|
className={`absolute left-2 right-2 md:left-14 md:right-auto md:w-[400px] z-50 bg-zinc-800 light:bg-white border border-zinc-700 light:border-slate-300 rounded-lg p-3 flex flex-col gap-2.5 shadow-lg overflow-hidden ${
|
||||||
centered
|
centered ? "top-full mt-2" : "bottom-full mb-2"
|
||||||
? "top-full mt-2 max-h-[min(360px,calc(100dvh-25rem))]"
|
|
||||||
: "bottom-full mb-2 max-h-[min(360px,calc(100dvh-11rem))]"
|
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<div className="flex shrink-0 gap-2.5 items-center">
|
<div className="flex shrink-0 gap-2.5 items-center">
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user