2025-03-25 12:04:20 +00:00
|
|
|
import React, { useState, useRef, useEffect } from "react";
|
2025-03-27 10:46:31 +00:00
|
|
|
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
|
|
|
>
|
2025-03-27 10:46:31 +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
|
2025-03-27 10:46:31 +00:00
|
|
|
const MultiLevelDropdown = ({
|
|
|
|
data,
|
|
|
|
onSelect,
|
2025-03-26 12:57:58 +00:00
|
|
|
onUnselect,
|
2025-03-27 10:46:31 +00:00
|
|
|
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
|
2025-03-27 10:46:31 +00:00
|
|
|
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
|
|
|
>
|
2025-03-29 12:26:50 +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;
|