124 lines
4.9 KiB
TypeScript
124 lines
4.9 KiB
TypeScript
import React, { useState, useMemo, useRef, useEffect } from "react";
|
|
import { EyeDroperIcon, SearchIcon } from "../../icons/ExportCommonIcons";
|
|
|
|
export type DropdownItem = {
|
|
id: string;
|
|
label: string;
|
|
icon?: React.ReactNode;
|
|
};
|
|
|
|
type DataDetailedDropdownProps = {
|
|
title: string;
|
|
placeholder?: string;
|
|
sections: {
|
|
title?: string;
|
|
items: DropdownItem[];
|
|
}[];
|
|
value?: DropdownItem | null;
|
|
onChange?: (item: DropdownItem) => void;
|
|
dropDownHeader?: string;
|
|
eyedroper?: boolean;
|
|
eyeDropperActive?: boolean;
|
|
onEyeDrop?: () => void;
|
|
};
|
|
|
|
const DataDetailedDropdown: React.FC<DataDetailedDropdownProps> = ({ title, placeholder = "Select value", sections, value, onChange, dropDownHeader, eyedroper, eyeDropperActive, onEyeDrop }) => {
|
|
const [open, setOpen] = useState(false);
|
|
const [search, setSearch] = useState("");
|
|
const [isEyeDroperActiveLocal, setIsEyeDroperActiveLocal] = useState(false);
|
|
const containerRef = useRef<HTMLDivElement>(null);
|
|
|
|
const isEyeActive = eyeDropperActive !== undefined ? eyeDropperActive : isEyeDroperActiveLocal;
|
|
|
|
const handleEyeClick = () => {
|
|
if (onEyeDrop) {
|
|
onEyeDrop();
|
|
} else {
|
|
setIsEyeDroperActiveLocal(!isEyeDroperActiveLocal);
|
|
}
|
|
};
|
|
|
|
// Close dropdown on outside click
|
|
useEffect(() => {
|
|
const handleClickOutside = (event: MouseEvent) => {
|
|
if (containerRef.current && !containerRef.current.contains(event.target as Node)) {
|
|
setOpen(false);
|
|
}
|
|
};
|
|
|
|
document.addEventListener("mousedown", handleClickOutside);
|
|
return () => document.removeEventListener("mousedown", handleClickOutside);
|
|
}, []);
|
|
|
|
const filteredSections = useMemo(() => {
|
|
if (!search) return sections;
|
|
|
|
return sections
|
|
.map((section) => ({
|
|
...section,
|
|
items: section.items.filter((item) => item.label.toLowerCase().includes(search.toLowerCase())),
|
|
}))
|
|
.filter((section) => section.items.length > 0);
|
|
}, [search, sections]);
|
|
|
|
return (
|
|
<div className="data-detailed-dropdown" ref={containerRef}>
|
|
<div className="title">{title}</div>
|
|
<div className="input-container">
|
|
<div className="input-wrapper">
|
|
<div className="input" onClick={() => setOpen((v) => !v)}>
|
|
<div className="key">{value?.label || placeholder}</div>
|
|
<div className="icon">▾</div>
|
|
</div>
|
|
|
|
{open && (
|
|
<div className="dropdown-panel">
|
|
{dropDownHeader && <div className="heading">{dropDownHeader}</div>}
|
|
|
|
<div className="search">
|
|
<div className="icon">
|
|
<SearchIcon />
|
|
</div>
|
|
<input placeholder="Search Assets" value={search} onChange={(e) => setSearch(e.target.value)} />
|
|
</div>
|
|
|
|
{filteredSections.map((section, index) => (
|
|
<div key={index} className="data-section">
|
|
{section.title && (
|
|
<div className="data-header">
|
|
<div className="data-section-title">{section.title}</div>
|
|
<div className="data-section-count">{section.items.length}</div>
|
|
</div>
|
|
)}
|
|
|
|
{section.items.map((item) => (
|
|
<div
|
|
key={item.id}
|
|
className="item"
|
|
onClick={() => {
|
|
onChange?.(item);
|
|
setOpen(false);
|
|
}}
|
|
>
|
|
{item.icon && <span className="icon">{item.icon}</span>}
|
|
<span>{item.label}</span>
|
|
</div>
|
|
))}
|
|
</div>
|
|
))}
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
{eyedroper && (
|
|
<div className={`add-icon ${isEyeActive ? "active" : ""}`} onClick={handleEyeClick}>
|
|
<EyeDroperIcon isActive={isEyeActive} />
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default DataDetailedDropdown;
|