hide if click outside the menus

This commit is contained in:
Red Adaya
2024-09-24 13:05:26 +08:00
parent 785a241759
commit 3c60757c13
3 changed files with 43 additions and 3 deletions
+1
View File
@@ -26,6 +26,7 @@
line-height: normal;
letter-spacing: -0.12px;
width: 100%;
border-radius: 2px;
&:hover,
&.active {
+13 -1
View File
@@ -1,4 +1,5 @@
import type { Meta, StoryObj } from "@storybook/react";
import { fn } from "@storybook/test";
import { useRef, useState } from "react";
import { Dropdown } from "./dropdown";
@@ -10,6 +11,7 @@ const meta = {
anchorRef: undefined,
boundaryRef: undefined,
className: "",
setVisibility: fn(),
},
argTypes: {
items: {
@@ -18,6 +20,9 @@ const meta = {
anchorRef: {
description: "Element to attach the dropdown",
},
setVisibility: {
description: "Visibility event handler",
},
boundaryRef: {
description: "Component that defines the boundaries of the dropdown",
},
@@ -82,7 +87,14 @@ export const Test: Story = {
Anchor Element
</div>
</div>
{isDropdownVisible && <Dropdown {...modifiedArgs} anchorRef={anchorRef} boundaryRef={boundaryRef} />}
{isDropdownVisible && (
<Dropdown
{...modifiedArgs}
setVisibility={(visible) => setIsDropdownVisible(visible)}
anchorRef={anchorRef}
boundaryRef={boundaryRef}
/>
)}
</div>
);
},
+29 -2
View File
@@ -1,7 +1,7 @@
import { useHeight } from "@/app/hook/useHeight";
import { useWidth } from "@/app/hook/useWidth";
import clsx from "clsx";
import React, { memo, useLayoutEffect, useRef, useState } from "react";
import React, { memo, useEffect, useLayoutEffect, useRef, useState } from "react";
import ReactDOM from "react-dom";
import "./dropdown.less";
@@ -92,8 +92,8 @@ const SubMenu = memo(
type DropdownItem = {
label: string;
onClick?: (e) => void;
subItems?: DropdownItem[];
onClick?: (e) => void;
};
const Dropdown = memo(
@@ -102,11 +102,13 @@ const Dropdown = memo(
anchorRef,
boundaryRef,
className,
setVisibility,
}: {
items: DropdownItem[];
anchorRef: React.RefObject<HTMLElement>;
boundaryRef?: React.RefObject<HTMLElement>;
className?: string;
setVisibility: (_: boolean) => void;
}) => {
const [visibleSubMenus, setVisibleSubMenus] = useState<{ [key: string]: any }>({});
const [hoveredItems, setHoveredItems] = useState<string[]>([]); // Track hovered items and ancestors
@@ -159,6 +161,31 @@ const Dropdown = memo(
}
}, [width, height]);
useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
const isClickInsideDropdown = dropdownRef.current && dropdownRef.current.contains(event.target as Node);
const isClickInsideAnchor = anchorRef.current && anchorRef.current.contains(event.target as Node);
// Check if the click is inside any of the submenus
const isClickInsideSubMenus = Object.keys(subMenuRefs.current).some(
(key) =>
subMenuRefs.current[key]?.current &&
subMenuRefs.current[key]?.current.contains(event.target as Node)
);
// If the click is outside the dropdown, anchor, and all submenus, hide the dropdown
if (!isClickInsideDropdown && !isClickInsideAnchor && !isClickInsideSubMenus) {
setVisibility(false);
}
};
document.addEventListener("mousedown", handleClickOutside);
return () => {
document.removeEventListener("mousedown", handleClickOutside);
};
}, [dropdownRef, anchorRef, subMenuRefs]);
// Position submenus based on available space and scroll position
const handleSubMenuPosition = (
key: string,