Dwinzo_dev/app/src/components/ui/inputs/MultiLevelDropDown.tsx

141 lines
3.8 KiB
TypeScript
Raw Normal View History

2025-03-25 06:17:41 +00:00
import React, { useState, useRef, useEffect } from "react";
// Dropdown Item Component
const DropdownItem = ({
label,
href,
onClick,
}: {
label: string;
href?: string;
onClick?: () => void;
}) => (
<a
href={href || "#"}
className="dropdown-item"
onClick={(e) => {
e.preventDefault();
onClick?.();
}}
>
{label}
</a>
);
// Nested Dropdown Component
const NestedDropdown = ({
label,
children,
onSelect,
}: {
label: string;
children: React.ReactNode;
onSelect: (selectedLabel: string) => void;
}) => {
const [open, setOpen] = useState(false);
return (
<div className="nested-dropdown">
{/* Dropdown Trigger */}
<div
className={`dropdown-trigger ${open ? "open" : ""}`}
onClick={() => setOpen(!open)} // Toggle submenu on click
>
{label} <span className="icon">{open ? "▼" : "▶"}</span>
</div>
{/* Submenu */}
{open && (
<div className="submenu">
{React.Children.map(children, (child) => {
if (React.isValidElement(child)) {
// Clone the element and pass the `onSelect` prop only if it's expected
return React.cloneElement(child as React.ReactElement<any>, { onSelect });
}
return child; // Return non-element children as-is
})}
</div>
)}
</div>
);
};
// Recursive Function to Render Nested Data
const renderNestedData = (
data: Record<string, any>,
onSelect: (selectedLabel: string) => void
) => {
return Object.entries(data).map(([key, value]) => {
if (typeof value === "object" && !Array.isArray(value)) {
// If the value is an object, render it as a nested dropdown
return (
<NestedDropdown key={key} label={key} onSelect={onSelect}>
{renderNestedData(value, onSelect)}
</NestedDropdown>
);
} else if (Array.isArray(value)) {
// If the value is an array, render each item as a dropdown item
return value.map((item, index) => (
<DropdownItem key={index} label={item} onClick={() => onSelect(item)} />
));
} else {
// If the value is a simple string, render it as a dropdown item
return (
<DropdownItem key={key} label={value} onClick={() => onSelect(value)} />
);
}
});
};
// Main Multi-Level Dropdown Component
const MultiLevelDropdown = ({ data }: { data: Record<string, any> }) => {
const [open, setOpen] = useState(false);
const [selectedLabel, setSelectedLabel] = useState("Dropdown trigger");
const dropdownRef = useRef<HTMLDivElement>(null);
// Handle outer click to close the dropdown
useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
if (
dropdownRef.current &&
!dropdownRef.current.contains(event.target as Node)
) {
setOpen(false);
}
};
document.addEventListener("mousedown", handleClickOutside);
return () => {
document.removeEventListener("mousedown", handleClickOutside);
};
}, []);
// Handle selection of an item
const handleSelect = (selectedLabel: string) => {
setSelectedLabel(selectedLabel); // Update the dropdown trigger text
setOpen(false); // Close the dropdown
};
return (
<div className="multi-level-dropdown" ref={dropdownRef}>
{/* Dropdown Trigger Button */}
<button
className={`dropdown-button ${open ? "open" : ""}`}
onClick={() => setOpen(!open)} // Toggle main menu on click
>
{selectedLabel} <span className="icon"></span>
</button>
{/* Dropdown Menu */}
{open && (
<div className="dropdown-menu">
<div className="dropdown-content">
{renderNestedData(data, handleSelect)}
</div>
</div>
)}
</div>
);
};
export default MultiLevelDropdown;