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

177 lines
4.6 KiB
TypeScript

import React, { useState, useRef, useEffect } from "react";
import { ArrowIcon } from "../../icons/ExportCommonIcons";
// Dropdown Item Component
const DropdownItem = ({
label,
onClick,
disabled = false,
}: {
label: string;
onClick: () => void;
disabled?: boolean;
}) => (
<div
className={`dropdown-item ${disabled ? "disabled" : ""}`}
onClick={() => {
if (!disabled) onClick();
}}
style={{
cursor: disabled ? "not-allowed" : "default",
opacity: disabled ? 0.5 : 1,
}}
>
{label}
</div>
);
// Nested Dropdown Component
const NestedDropdown = ({
label,
fields,
onSelect,
disabledFields = [],
}: {
label: string;
fields: string[];
onSelect: (selectedData: { name: string; fields: string }) => void;
disabledFields?: string[];
}) => {
const [open, setOpen] = useState(false);
return (
<div className="nested-dropdown">
<div
className={`dropdown-trigger ${open ? "open" : ""}`}
onClick={() => setOpen(!open)}
>
{label}
<div
className="arrow-container"
style={{ rotate: open ? "" : "-90deg" }}
>
<ArrowIcon />
</div>
</div>
{open && (
<div className="submenu">
{fields.map((field) => {
const isDisabled = disabledFields.includes(`${label}-${field}`);
return (
<DropdownItem
key={field}
label={field}
onClick={() => onSelect({ name: label, fields: field })}
disabled={isDisabled}
/>
);
})}
</div>
)}
</div>
);
};
// Props type for MultiLevelDropdown
interface MultiLevelDropdownProps {
data: Record<string, any>;
onSelect: (selectedData: { name: string; fields: string }) => void;
onUnselect: () => void;
selectedValue?: { name: string; fields: string };
allSelections?: Record<string, { name: string; fields: string }>;
isLoading?: boolean;
}
// Main Multi-Level Dropdown Component
const MultiLevelDropdown = ({
data,
onSelect,
onUnselect,
selectedValue,
allSelections = {},
isLoading = false,
}: MultiLevelDropdownProps) => {
const [open, setOpen] = useState(false);
const dropdownRef = useRef<HTMLDivElement>(null);
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 item selection
const handleItemSelect = (selectedData: { name: string; fields: string }) => {
onSelect(selectedData);
setOpen(false);
};
// Handle unselect
const handleItemUnselect = () => {
onUnselect();
setOpen(false);
};
// Determine the display label
const displayLabel = selectedValue
? `${selectedValue.name} - ${selectedValue.fields}`
: "Dropdown trigger";
// Build list of disabled selections
const disabledFieldsList = Object.values(allSelections)
.filter(
(sel) =>
!(
sel.name === selectedValue?.name &&
sel.fields === selectedValue?.fields
)
)
.map((sel) => `${sel.name}-${sel.fields}`);
return (
<div className="multi-level-dropdown" ref={dropdownRef}>
<button
id="multi-level-drop"
className={`dropdown-button ${open ? "open" : ""}`}
onClick={() => setOpen(!open)}
>
<div className="label">{displayLabel}</div>
<span className="icon"></span>
</button>
{open && (
<div className="dropdown-menu">
<div className="dropdown-content">
{isLoading ? (
<div className="loading" />
) : (
<>
<DropdownItem label="Unselect" onClick={handleItemUnselect} />
{Object.entries(data).map(([key, value]) => (
<NestedDropdown
key={key}
label={key}
fields={Object.keys(value)}
onSelect={handleItemSelect}
disabledFields={disabledFieldsList}
/>
))}
</>
)}
</div>
</div>
)}
</div>
);
};
export default MultiLevelDropdown;