Android_Retail/src/Pages/Product/Utils/CellSelect.jsx

203 lines
6.7 KiB
React
Raw Normal View History

2026-01-27 18:27:29 +05:30
import { memo, useCallback, useEffect, useRef, useState } from "react";
import { IoIosArrowDown, IoIosArrowUp, IoIosCloseCircle } from "react-icons/io";
const CellSelect = memo(({
value,
onChange,
onKeyDown,
dataCell,
options,
hasError,
placeholder = "Select option",
onClick
}) => {
const [isOpen, setIsOpen] = useState(false);
const [dropdownDirection, setDropdownDirection] = useState("down");
const [searchTerm, setSearchTerm] = useState("");
const selectRef = useRef(null);
const dropdownRef = useRef(null);
const searchInputRef = useRef(null);
// Filter options based on search term
const filteredOptions = options?.filter(option =>
option.label.toLowerCase().includes(searchTerm.toLowerCase())
) || [];
// Calculate dropdown position
const calculateDropdownPosition = useCallback(() => {
if (!selectRef.current) return "down";
const rect = selectRef.current.getBoundingClientRect();
const spaceBelow = window.innerHeight - rect.bottom;
const spaceAbove = rect.top;
return (spaceBelow < 200 && spaceAbove > spaceBelow) ? "up" : "down";
}, []);
// Close dropdown on outside click or scroll
useEffect(() => {
if (!isOpen) return;
const handler = e => {
if (
!dropdownRef.current?.contains(e.target) &&
!selectRef.current?.contains(e.target)
) {
setIsOpen(false);
// Reset search term when closing dropdown
setSearchTerm("");
}
};
const scrollHandler = e => {
if (
dropdownRef.current &&
!dropdownRef.current.contains(e.target) &&
selectRef.current &&
!selectRef.current.contains(e.target)
) {
setIsOpen(false);
setSearchTerm("");
}
};
document.addEventListener("mousedown", handler);
window.addEventListener("scroll", scrollHandler, true);
return () => {
document.removeEventListener("mousedown", handler);
window.removeEventListener("scroll", scrollHandler, true);
};
}, [isOpen]);
// Focus search input when dropdown opens
useEffect(() => {
if (isOpen && searchInputRef.current) {
searchInputRef.current.focus();
}
}, [isOpen]);
// Update dropdown direction
useEffect(() => {
if (isOpen) setDropdownDirection(calculateDropdownPosition());
}, [isOpen, calculateDropdownPosition]);
// Keyboard navigation
const handleKeyDown = e => {
if (!isOpen && ["ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight"].includes(e.key)) {
e.preventDefault();
onKeyDown(e);
return;
}
if (isOpen && ["ArrowLeft", "ArrowRight"].includes(e.key)) {
e.preventDefault();
return;
}
if (e.key === "Enter") {
e.preventDefault();
if (isOpen && filteredOptions.length > 0) {
// Select the first option when pressing Enter
handleSelectChange(filteredOptions[0].value);
} else {
setIsOpen(v => !v);
}
return;
}
if (e.key === "Escape") {
e.preventDefault();
setIsOpen(false);
setSearchTerm("");
return;
}
// Allow typing to search when dropdown is open
if (isOpen && e.key.length === 1 && !e.ctrlKey && !e.metaKey) {
setSearchTerm(prev => prev + e.key);
e.preventDefault();
return;
}
onKeyDown(e);
};
const handleSelectChange = v => {
onChange({ target: { value: v } });
setIsOpen(false);
setSearchTerm(""); // Clear search term after selection
};
const handleSearchChange = e => {
setSearchTerm(e.target.value);
};
const clearSearch = () => {
setSearchTerm("");
if (searchInputRef.current) {
searchInputRef.current.focus();
}
};
const selectedOption = options?.find(opt => opt.value === value);
const displayValue = selectedOption ? selectedOption.label : placeholder;
return (
<div className="custom-select-container" ref={selectRef}>
<div
className={`custom-select-trigger${hasError ? " error" : ""}${isOpen ? " open" : ""}`}
onClick={e => {
setIsOpen(v => !v);
onClick?.(e);
}}
onKeyDown={handleKeyDown}
data-cell={dataCell}
tabIndex={0}
>
<span className={!value ? "placeholder-text" : ""}>
{isOpen ? (
<input
ref={searchInputRef}
className="search-input"
value={searchTerm}
onChange={handleSearchChange}
onClick={e => e.stopPropagation()}
placeholder={selectedOption?.label || placeholder}
/>
) : (
displayValue
)}
</span>
<span className="dropdown-arrow">
{isOpen ? <IoIosArrowUp /> : <IoIosArrowDown />}
</span>
</div>
{isOpen && (
<div
className={`custom-select-dropdown ${dropdownDirection}`}
ref={dropdownRef}
style={dropdownDirection === "up" ? { bottom: "100%", top: "auto" } : {}}
>
<div className="dropdown-content">
{filteredOptions.length === 0 ? (
<div className="dropdown-option disabled">
{searchTerm ? "No matches found" : "No data"}
</div>
) : (
filteredOptions.map(option => (
<div
key={option.value}
className={`dropdown-option${value === option.value ? " selected" : ""}`}
onClick={() => handleSelectChange(option.value)}
>
{option.label}
</div>
))
)}
</div>
</div>
)}
</div>
);
});
export default CellSelect;