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

170 lines
4.5 KiB
TypeScript
Raw Normal View History

2025-03-25 12:04:20 +00:00
import React, { useState, useRef, useEffect } from "react";
import { ArrowIcon } from "../../icons/ExportCommonIcons";
2025-03-25 12:04:20 +00:00
// Dropdown Item Component
const DropdownItem = ({
label,
onClick,
2025-04-16 12:34:29 +00:00
disabled = false,
2025-03-25 12:04:20 +00:00
}: {
label: string;
2025-03-26 12:57:58 +00:00
onClick: () => void;
2025-04-16 12:34:29 +00:00
disabled?: boolean;
2025-03-25 12:04:20 +00:00
}) => (
2025-04-16 12:34:29 +00:00
<div
className={`dropdown-item ${disabled ? "disabled" : ""}`}
onClick={() => {
if (!disabled) onClick();
}}
style={{ cursor: disabled ? "not-allowed": "default", opacity: disabled ? 0.5 : 1 }}
>
2025-03-25 12:04:20 +00:00
{label}
2025-03-26 12:57:58 +00:00
</div>
2025-03-25 12:04:20 +00:00
);
// Nested Dropdown Component
const NestedDropdown = ({
label,
2025-03-26 12:57:58 +00:00
fields,
2025-03-25 12:04:20 +00:00
onSelect,
2025-04-16 12:34:29 +00:00
disabledFields = [],
2025-03-25 12:04:20 +00:00
}: {
label: string;
2025-03-26 12:57:58 +00:00
fields: string[];
onSelect: (selectedData: { name: string; fields: string }) => void;
2025-04-16 12:34:29 +00:00
disabledFields?: string[];
2025-03-25 12:04:20 +00:00
}) => {
const [open, setOpen] = useState(false);
return (
<div className="nested-dropdown">
<div
className={`dropdown-trigger ${open ? "open" : ""}`}
2025-03-26 12:57:58 +00:00
onClick={() => setOpen(!open)}
2025-03-25 12:04:20 +00:00
>
{label}
<div
className="arrow-container"
style={{ rotate: open ? "" : "-90deg" }}
>
<ArrowIcon />
</div>
2025-03-25 12:04:20 +00:00
</div>
{open && (
<div className="submenu">
2025-04-16 12:34:29 +00:00
{fields.map((field) => {
const isDisabled = disabledFields.includes(`${label}-${field}`);
return (
<DropdownItem
key={field}
label={field}
onClick={() => onSelect({ name: label, fields: field })}
disabled={isDisabled}
/>
);
})}
2025-03-25 12:04:20 +00:00
</div>
)}
</div>
);
};
2025-03-26 12:57:58 +00:00
// Props type for MultiLevelDropdown
interface MultiLevelDropdownProps {
data: Record<string, any>;
onSelect: (selectedData: { name: string; fields: string }) => void;
onUnselect: () => void;
selectedValue?: { name: string; fields: string };
2025-04-16 12:34:29 +00:00
allSelections?: Record<string, { name: string; fields: string }>;
2025-04-15 12:35:01 +00:00
isLoading?: boolean;
2025-03-26 12:57:58 +00:00
}
2025-03-25 12:04:20 +00:00
// Main Multi-Level Dropdown Component
const MultiLevelDropdown = ({
data,
onSelect,
2025-03-26 12:57:58 +00:00
onUnselect,
selectedValue,
2025-04-16 12:34:29 +00:00
allSelections = {},
2025-04-15 12:35:01 +00:00
isLoading = false,
2025-03-26 12:57:58 +00:00
}: MultiLevelDropdownProps) => {
2025-03-25 12:04:20 +00:00
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);
};
}, []);
2025-03-26 12:57:58 +00:00
// Handle item selection
const handleItemSelect = (selectedData: { name: string; fields: string }) => {
onSelect(selectedData);
setOpen(false);
2025-03-25 12:04:20 +00:00
};
2025-03-26 12:57:58 +00:00
// Handle unselect
const handleItemUnselect = () => {
onUnselect();
setOpen(false);
2025-03-25 12:04:20 +00:00
};
2025-03-26 12:57:58 +00:00
// Determine the display label
const displayLabel = selectedValue
2025-03-26 12:57:58 +00:00
? `${selectedValue.name} - ${selectedValue.fields}`
: "Dropdown trigger";
2025-04-16 12:34:29 +00:00
// 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}`);
2025-03-25 12:04:20 +00:00
return (
<div className="multi-level-dropdown" ref={dropdownRef}>
<button
className={`dropdown-button ${open ? "open" : ""}`}
2025-03-26 12:57:58 +00:00
onClick={() => setOpen(!open)}
2025-03-25 12:04:20 +00:00
>
<div className="label">{displayLabel}</div>
<span className="icon"></span>
2025-03-25 12:04:20 +00:00
</button>
{open && (
<div className="dropdown-menu">
2025-04-16 12:34:29 +00:00
<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}
/>
))}
</>
)}
2025-03-25 12:04:20 +00:00
</div>
</div>
)}
</div>
);
};
2025-03-26 12:57:58 +00:00
export default MultiLevelDropdown;