From 5d2a748330fb9848d6f57358f8b0995981c1b4d6 Mon Sep 17 00:00:00 2001 From: Red Adaya Date: Mon, 23 Sep 2024 11:28:02 +0800 Subject: [PATCH] take into acount page and boundary scroll x/y --- frontend/app/element/dropdown.less | 2 +- frontend/app/element/dropdown.stories.tsx | 24 ++++++------ frontend/app/element/dropdown.tsx | 46 +++++++++++------------ 3 files changed, 36 insertions(+), 36 deletions(-) diff --git a/frontend/app/element/dropdown.less b/frontend/app/element/dropdown.less index 9918916f..5d67ce8b 100644 --- a/frontend/app/element/dropdown.less +++ b/frontend/app/element/dropdown.less @@ -5,7 +5,7 @@ position: absolute; z-index: 1000; // TODO: put this in theme.less display: flex; - width: 104px; + width: 125px; padding: 2px; flex-direction: column; justify-content: flex-end; diff --git a/frontend/app/element/dropdown.stories.tsx b/frontend/app/element/dropdown.stories.tsx index aeb07568..34385ef3 100644 --- a/frontend/app/element/dropdown.stories.tsx +++ b/frontend/app/element/dropdown.stories.tsx @@ -46,17 +46,19 @@ export const Test: Story = { className="boundary" style={{ padding: "20px", height: "300px", border: "2px solid black" }} > -
- Anchor Element +
+
+ Anchor Element +
{isDropdownVisible && }
diff --git a/frontend/app/element/dropdown.tsx b/frontend/app/element/dropdown.tsx index 89d20142..545a1323 100644 --- a/frontend/app/element/dropdown.tsx +++ b/frontend/app/element/dropdown.tsx @@ -21,7 +21,6 @@ const SubMenu = memo( handleMouseEnterItem: any; subMenuRefs: any; }) => { - // Ensure a ref exists for each submenu subItems.forEach((_, idx) => { const newKey = `${parentKey}-${idx}`; if (!subMenuRefs.current[newKey]) { @@ -42,7 +41,7 @@ const SubMenu = memo( }} > {subItems.map((item, idx) => { - const newKey = `${parentKey}-${idx}`; // Full hierarchical key + const newKey = `${parentKey}-${idx}`; return (
{ if (anchorRef.current && dropdownRef.current) { const anchorRect = anchorRef.current.getBoundingClientRect(); - let boundaryRect = effectiveBoundaryRef.current?.getBoundingClientRect() || { + const scrollTop = window.scrollY || document.documentElement.scrollTop; + const scrollLeft = window.scrollX || document.documentElement.scrollLeft; + + let top = anchorRect.bottom + scrollTop; // Adjust top for scroll position + let left = anchorRect.left + scrollLeft; // Adjust left for scroll position + + const boundaryRect = effectiveBoundaryRef.current?.getBoundingClientRect() || { top: 0, left: 0, bottom: window.innerHeight, right: window.innerWidth, }; - let top = anchorRect.bottom; - let left = anchorRect.left; - // Adjust if overflowing the right boundary - if (left + dropdownRef.current.offsetWidth > boundaryRect.right) { - left = boundaryRect.right - dropdownRef.current.offsetWidth; + if (left + dropdownRef.current.offsetWidth > boundaryRect.right + scrollLeft) { + left = boundaryRect.right + scrollLeft - dropdownRef.current.offsetWidth; } // Adjust if overflowing the bottom boundary - if (top + dropdownRef.current.offsetHeight > boundaryRect.bottom) { - top = boundaryRect.bottom - dropdownRef.current.offsetHeight; + if (top + dropdownRef.current.offsetHeight > boundaryRect.bottom + scrollTop) { + top = boundaryRect.bottom + scrollTop - dropdownRef.current.offsetHeight; } setPosition({ top, left }); } }, [width, height]); - // Position submenus based on available space + // Position submenus based on available space and scroll position const handleSubMenuPosition = ( key: string, itemRect: DOMRect, @@ -146,6 +148,9 @@ const Dropdown = memo( const subMenuRef = subMenuRefs.current[key]?.current; if (!subMenuRef) return; + const scrollTop = window.scrollY || document.documentElement.scrollTop; + const scrollLeft = window.scrollX || document.documentElement.scrollLeft; + const boundaryRect = effectiveBoundaryRef.current?.getBoundingClientRect() || { top: 0, left: 0, @@ -156,17 +161,17 @@ const Dropdown = memo( const submenuWidth = subMenuRef.offsetWidth; const submenuHeight = subMenuRef.offsetHeight; - let left = itemRect.right; - let top = itemRect.top; + let left = itemRect.right + scrollLeft - 2; // Adjust for horizontal scroll + let top = itemRect.top + scrollTop; // Adjust for vertical scroll // Adjust to the left if overflowing the right boundary - if (left + submenuWidth > window.innerWidth) { - left = itemRect.left - submenuWidth; + if (left + submenuWidth > window.innerWidth + scrollLeft) { + left = itemRect.left + scrollLeft - submenuWidth; } // Adjust if the submenu overflows the bottom boundary - if (top + submenuHeight > window.innerHeight) { - top = window.innerHeight - submenuHeight - 10; + if (top + submenuHeight > window.innerHeight + scrollTop) { + top = window.innerHeight + scrollTop - submenuHeight - 10; } setSubMenuPosition((prev) => ({ @@ -184,30 +189,23 @@ const Dropdown = memo( ) => { event.stopPropagation(); - // Build the full key for the current item const key = parentKey ? `${parentKey}-${index}` : `${index}`; setVisibleSubMenus((prev) => { - // Create a copy of the previous state const updatedState = { ...prev }; - - // Ensure the current submenu and its ancestors are visible updatedState[key] = { visible: true, label: item.label }; - // Extract ancestors of the key (e.g., "2-2-1" -> "2-2" -> "2") const ancestors = key.split("-").reduce((acc, part, idx) => { if (idx === 0) return [part]; return [...acc, `${acc[idx - 1]}-${part}`]; }, [] as string[]); - // Mark ancestors visible ancestors.forEach((ancestorKey) => { if (updatedState[ancestorKey]) { updatedState[ancestorKey].visible = true; } }); - // Hide any submenu that is not part of the current hierarchy for (const pkey in updatedState) { if (!ancestors.includes(pkey) && pkey !== key) { updatedState[pkey].visible = false;