take into acount page and boundary scroll x/y

This commit is contained in:
Red Adaya
2024-09-23 11:28:02 +08:00
parent e991b616b8
commit 5d2a748330
3 changed files with 36 additions and 36 deletions
+1 -1
View File
@@ -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;
+13 -11
View File
@@ -46,17 +46,19 @@ export const Test: Story = {
className="boundary"
style={{ padding: "20px", height: "300px", border: "2px solid black" }}
>
<div
ref={anchorRef}
style={{
backgroundColor: "lightblue",
padding: "10px",
display: "inline-block",
cursor: "pointer",
}}
onClick={handleAnchorClick}
>
Anchor Element
<div style={{ height: "400px" }}>
<div
ref={anchorRef}
style={{
backgroundColor: "lightblue",
padding: "10px",
display: "inline-block",
cursor: "pointer",
}}
onClick={handleAnchorClick}
>
Anchor Element
</div>
</div>
{isDropdownVisible && <Dropdown {...args} anchorRef={anchorRef} boundaryRef={boundaryRef} />}
</div>
+22 -24
View File
@@ -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 (
<div
key={newKey}
@@ -111,31 +110,34 @@ const Dropdown = memo(
useLayoutEffect(() => {
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;