fix(ui): adjust submenu placement based on available viewport space
- add refs for submenu trigger and panel elements - compute available space above/below using getBoundingClientRect - dynamically set submenu side to avoid overflow outside viewport
This commit is contained in:
@@ -159,6 +159,26 @@ function BlockActionsMenu({
|
|||||||
const { side } = useDropdownPlacement(open, triggerRef);
|
const { side } = useDropdownPlacement(open, triggerRef);
|
||||||
const [submenuOpen, setSubmenuOpen] = useState(false);
|
const [submenuOpen, setSubmenuOpen] = useState(false);
|
||||||
const submenuTimerRef = useRef(null);
|
const submenuTimerRef = useRef(null);
|
||||||
|
const submenuTriggerRef = useRef(null);
|
||||||
|
const submenuPanelRef = useRef(null);
|
||||||
|
const [submenuSide, setSubmenuSide] = useState('below');
|
||||||
|
|
||||||
|
useLayoutEffect(() => {
|
||||||
|
if (!submenuOpen) return;
|
||||||
|
const trigger = submenuTriggerRef.current;
|
||||||
|
const panel = submenuPanelRef.current;
|
||||||
|
if (!trigger || !panel || typeof window === 'undefined') return;
|
||||||
|
const triggerRect = trigger.getBoundingClientRect();
|
||||||
|
const panelHeight = panel.getBoundingClientRect().height;
|
||||||
|
const vh = window.innerHeight;
|
||||||
|
const spaceBelow = vh - triggerRect.top - DROPDOWN_VIEWPORT_MARGIN;
|
||||||
|
const spaceAbove = triggerRect.bottom - DROPDOWN_VIEWPORT_MARGIN;
|
||||||
|
if (panelHeight > spaceBelow && spaceAbove > spaceBelow) {
|
||||||
|
setSubmenuSide('above');
|
||||||
|
} else {
|
||||||
|
setSubmenuSide('below');
|
||||||
|
}
|
||||||
|
}, [submenuOpen, transformOptions]);
|
||||||
|
|
||||||
function scheduleSubmenuClose() {
|
function scheduleSubmenuClose() {
|
||||||
if (submenuTimerRef.current) clearTimeout(submenuTimerRef.current);
|
if (submenuTimerRef.current) clearTimeout(submenuTimerRef.current);
|
||||||
@@ -236,6 +256,7 @@ function BlockActionsMenu({
|
|||||||
<div className="p-1.5 flex flex-col gap-0.5">
|
<div className="p-1.5 flex flex-col gap-0.5">
|
||||||
{transformOptions.length > 0 && (
|
{transformOptions.length > 0 && (
|
||||||
<div
|
<div
|
||||||
|
ref={submenuTriggerRef}
|
||||||
className="relative"
|
className="relative"
|
||||||
onMouseEnter={() => { cancelSubmenuClose(); setSubmenuOpen(true); }}
|
onMouseEnter={() => { cancelSubmenuClose(); setSubmenuOpen(true); }}
|
||||||
onMouseLeave={scheduleSubmenuClose}
|
onMouseLeave={scheduleSubmenuClose}
|
||||||
@@ -250,7 +271,8 @@ function BlockActionsMenu({
|
|||||||
</div>
|
</div>
|
||||||
{submenuOpen && (
|
{submenuOpen && (
|
||||||
<div
|
<div
|
||||||
className="absolute left-full top-0 ml-1 w-56 rounded-xl border border-black/8 dark:border-white/8 bg-white dark:bg-[#0B0B0B] shadow-lg p-1.5 flex flex-col gap-0.5 z-50"
|
ref={submenuPanelRef}
|
||||||
|
className={`absolute left-full ${submenuSide === 'above' ? 'bottom-0' : 'top-0'} ml-1 w-56 rounded-xl border border-black/8 dark:border-white/8 bg-white dark:bg-[#0B0B0B] shadow-lg p-1.5 flex flex-col gap-0.5 z-50`}
|
||||||
onMouseEnter={cancelSubmenuClose}
|
onMouseEnter={cancelSubmenuClose}
|
||||||
onMouseLeave={scheduleSubmenuClose}
|
onMouseLeave={scheduleSubmenuClose}
|
||||||
>
|
>
|
||||||
|
|||||||
Reference in New Issue
Block a user