hide dropdown when an item is clicked

This commit is contained in:
Red Adaya
2024-09-24 12:18:19 +08:00
parent b99b30eb51
commit 785a241759
2 changed files with 83 additions and 36 deletions
+56 -27
View File
@@ -40,6 +40,28 @@ export const Test: Story = {
setIsDropdownVisible((prev) => !prev);
};
const mapItemsWithClick = (items: any[]) => {
return items.map((item) => ({
...item,
onClick: () => {
// Call the original onClick if it exists
if (item.onClick) {
item.onClick();
}
// Close the dropdown after an item is clicked
setIsDropdownVisible(false);
},
// Recursively update subItems' onClick handlers
subItems: item.subItems ? mapItemsWithClick(item.subItems) : undefined,
}));
};
// Modify args to include updated items with the new onClick behavior
const modifiedArgs = {
...args,
items: mapItemsWithClick(args.items),
};
return (
<div
ref={boundaryRef}
@@ -60,39 +82,50 @@ export const Test: Story = {
Anchor Element
</div>
</div>
{isDropdownVisible && <Dropdown {...args} anchorRef={anchorRef} boundaryRef={boundaryRef} />}
{isDropdownVisible && <Dropdown {...modifiedArgs} anchorRef={anchorRef} boundaryRef={boundaryRef} />}
</div>
);
},
args: {
items: [
{ label: "Option 1", onClick: () => null },
{ label: "Option 1", onClick: (e) => console.log("Clicked Option 1") },
{
label: "Option 2",
onClick: () => console.log("Clicked Option 2"),
onClick: (e) => console.log("Clicked Option 2"),
subItems: [
{ label: "Option 2 -> 1", onClick: () => null },
{ label: "Option 2 -> 2", onClick: () => null },
{ label: "Option 2 -> 1", onClick: (e) => console.log("Clicked Option 2 -> 1") },
{ label: "Option 2 -> 2", onClick: (e) => console.log("Clicked Option 2 -> 2") },
],
},
{
label: "Option 3",
onClick: () => console.log("Clicked Option 3"),
onClick: (e) => console.log("Clicked Option 3"),
subItems: [
{ label: "Option 3 -> 1", onClick: () => null },
{ label: "Option 3 -> 2", onClick: () => null },
{ label: "Option 3 -> 1", onClick: (e) => console.log("Clicked Option 3 -> 1") },
{ label: "Option 3 -> 2", onClick: (e) => console.log("Clicked Option 3 -> 2") },
{
label: "Option 3 -> 3",
onClick: () => console.log("Clicked Option 3"),
onClick: (e) => console.log("Clicked Option 3 -> 3"),
subItems: [
{ label: "Option 3 -> 3 -> 1", onClick: () => null },
{ label: "Option 3 -> 3 -> 2", onClick: () => null },
{ label: "Option 3 -> 3 -> 1", onClick: (e) => console.log("Clicked Option 3 -> 3 -> 1") },
{ label: "Option 3 -> 3 -> 2", onClick: (e) => console.log("Clicked Option 3 -> 3 -> 2") },
{ label: "Option 3 -> 3 -> 3", onClick: (e) => console.log("Clicked Option 3 -> 3 -> 3") },
{
label: "Option 3 -> 3",
onClick: () => console.log("Clicked Option 3"),
label: "Option 3 -> 3 -> 4",
onClick: (e) => console.log("Clicked Option 3 -> 3 -> 4"),
subItems: [
{ label: "Option 3 -> 3 -> 1", onClick: () => null },
{ label: "Option 3 -> 3 -> 2", onClick: () => null },
{
label: "Option 3 -> 3 -> 4 -> 1",
onClick: (e) => console.log("Clicked Option 3 -> 3 -> 4 -> 1"),
},
{
label: "Option 3 -> 3 -> 4 -> 2",
onClick: (e) => console.log("Clicked Option 3 -> 3 -> 4 -> 2"),
},
{
label: "Option 3 -> 3 -> 4 -> 3",
onClick: (e) => console.log("Clicked Option 3 -> 3 -> 4 -> 3"),
},
],
},
],
@@ -101,19 +134,15 @@ export const Test: Story = {
},
{
label: "Option 4",
onClick: () => console.log("Clicked Option 3"),
onClick: (e) => console.log("Clicked Option 4"),
subItems: [
{ label: "Option 4 -> 1", onClick: () => null },
{ label: "Option 4 -> 2", onClick: () => null },
{ label: "Option 4 -> 4", onClick: () => null },
{ label: "Option 4 -> 4", onClick: () => null },
{ label: "Option 4 -> 5", onClick: () => null },
{ label: "Option 4 -> 6", onClick: () => null },
{ label: "Option 4 -> 7", onClick: () => null },
{ label: "Option 4 -> 8", onClick: () => null },
{ label: "Option 4 -> 9", onClick: () => null },
{ label: "Option 4 -> 10", onClick: () => null },
{ label: "Option 4 -> 11", onClick: () => null },
{ label: "Option 4 -> 1", onClick: (e) => console.log("Clicked Option 4 -> 1") },
{ label: "Option 4 -> 2", onClick: (e) => console.log("Clicked Option 4 -> 2") },
{ label: "Option 4 -> 3", onClick: (e) => console.log("Clicked Option 4 -> 3") },
{ label: "Option 4 -> 4", onClick: (e) => console.log("Clicked Option 4 -> 4") },
{ label: "Option 4 -> 5", onClick: (e) => console.log("Clicked Option 4 -> 5") },
{ label: "Option 4 -> 6", onClick: (e) => console.log("Clicked Option 4 -> 6") },
{ label: "Option 4 -> 7", onClick: (e) => console.log("Clicked Option 4 -> 7") },
],
},
],
+27 -9
View File
@@ -12,16 +12,25 @@ const SubMenu = memo(
subMenuPosition,
visibleSubMenus,
hoveredItems,
handleMouseEnterItem,
subMenuRefs,
handleMouseEnterItem,
handleOnClick,
}: {
subItems: DropdownItem[];
parentKey: string;
subMenuPosition: any;
visibleSubMenus: any;
subMenuPosition: {
[key: string]: { top: number; left: number; label: string };
};
visibleSubMenus: { [key: string]: any };
hoveredItems: string[];
handleMouseEnterItem: any;
subMenuRefs: any;
subMenuRefs: React.MutableRefObject<{ [key: string]: React.RefObject<HTMLDivElement> }>;
handleMouseEnterItem: (
event: React.MouseEvent<HTMLDivElement, MouseEvent>,
parentKey: string | null,
index: number,
item: DropdownItem
) => void;
handleOnClick: (e: React.MouseEvent<HTMLDivElement>, item: DropdownItem) => void;
}) => {
// Ensure a ref exists for each submenu
subItems.forEach((_, idx) => {
@@ -54,8 +63,9 @@ const SubMenu = memo(
return (
<div
key={newKey}
className={clsx("dropdown-item", { active: isActive })} // Add "active" class if the item or ancestor is hovered
className={clsx("dropdown-item", { active: isActive })}
onMouseEnter={(event) => handleMouseEnterItem(event, parentKey, idx, item)}
onClick={(e) => handleOnClick(e, item)}
>
{item.label}
{item.subItems && <span className="arrow"></span>}
@@ -65,8 +75,9 @@ const SubMenu = memo(
parentKey={newKey}
subMenuPosition={subMenuPosition}
visibleSubMenus={visibleSubMenus}
hoveredItems={hoveredItems} // Pass hoveredItems to submenus
hoveredItems={hoveredItems}
handleMouseEnterItem={handleMouseEnterItem}
handleOnClick={handleOnClick}
subMenuRefs={subMenuRefs}
/>
)}
@@ -81,7 +92,7 @@ const SubMenu = memo(
type DropdownItem = {
label: string;
onClick?: () => void;
onClick?: (e) => void;
subItems?: DropdownItem[];
};
@@ -238,6 +249,11 @@ const Dropdown = memo(
handleSubMenuPosition(key, itemRect, dropdownRef, item.label);
};
const handleOnClick = (e: React.MouseEvent<HTMLDivElement>, item: DropdownItem) => {
e.stopPropagation();
item.onClick && item.onClick(e);
};
return ReactDOM.createPortal(
<div
className={clsx("dropdown", className)}
@@ -253,6 +269,7 @@ const Dropdown = memo(
key={key}
className={clsx("dropdown-item", { active: isActive })} // Highlight hovered items and ancestors
onMouseEnter={(event) => handleMouseEnterItem(event, null, index, item)}
onClick={(e) => handleOnClick(e, item)}
>
{item.label}
{item.subItems && <span className="arrow"></span>}
@@ -262,8 +279,9 @@ const Dropdown = memo(
parentKey={key}
subMenuPosition={subMenuPosition}
visibleSubMenus={visibleSubMenus}
hoveredItems={hoveredItems} // Pass hoveredItems to submenus
hoveredItems={hoveredItems}
handleMouseEnterItem={handleMouseEnterItem}
handleOnClick={handleOnClick}
subMenuRefs={subMenuRefs}
/>
)}