const {
useState,
useRef,
useEffect
} = React;
const SortDropdown = ({
value,
onChange,
options
}) => {
const [isOpen, setIsOpen] = useState(false);
const dropdownRef = useRef(null);
useEffect(() => {
const handleClickOutside = (event) => {
if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
setIsOpen(false);
}
};
document.addEventListener('mousedown', handleClickOutside);
return () => document.removeEventListener('mousedown', handleClickOutside);
}, []);
const handleSelect = (optionValue) => {
onChange(optionValue);
setIsOpen(false);
};
const selectedOption = options.find(opt => opt.value === value);
return ( <
div className = "sort-dropdown"
ref = {
dropdownRef
} >
<
button className = "sort-dropdown__button"
onClick = {
() => setIsOpen(!isOpen)
} >
<
span > {
selectedOption?.label || 'Sort by'
} < /span> <
span className = {
`arrow ${isOpen ? 'open' : ''}`
} > ▼ < /span> <
/button>
{
isOpen && ( <
ul className = "sort-dropdown__list" > {
options.map((option) => ( <
li key = {
option.value
}
className = {
option.value === value ? 'selected' : ''
}
onClick = {
() => handleSelect(option.value)
} >
{
option.label
} <
/li>
))
} <
/ul>
)
} <
/div>
);
};
const App = () => {
const [sortValue, setSortValue] = useState('default');
const products = [{
id: 1,
name: 'Widget A',
price: 29.99
},
{
id: 2,
name: 'Gadget Z',
price: 49.99
},
{
id: 3,
name: 'Tool B',
price: 19.99
},
{
id: 4,
name: 'Device Y',
price: 39.99
},
];
const sortOptions = [{
value: 'default',
label: 'Default'
},
{
value: 'az',
label: 'A → Z'
},
{
value: 'za',
label: 'Z → A'
},
{
value: 'low-high',
label: 'Price: Low → High'
},
{
value: 'high-low',
label: 'Price: High → Low'
},
];
const sortedProducts = [...products].sort((a, b) => {
switch (sortValue) {
case 'az':
return a.name.localeCompare(b.name);
case 'za':
return b.name.localeCompare(a.name);
case 'low-high':
return a.price - b.price;
case 'high-low':
return b.price - a.price;
default:
return 0;
}
});
return ( <
div className = "container" >
<
h2 > Product List < /h2> <
SortDropdown value = {
sortValue
}
onChange = {
setSortValue
}
options = {
sortOptions
}
/> <
div className = "product-list" > {
sortedProducts.map(product => ( <
div key = {
product.id
}
className = "product-item" >
<
span > {
product.name
} < /span> <
span > $ {
product.price
} < /span> <
/div>
))
} <
/div> <
/div>
);
};
ReactDOM.render( < App / > , document.getElementById('root'));
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Arial, sans-serif;
padding: 40px;
background: #f5f5f5;
}
.container {
max-width: 800px;
margin: 0 auto;
background: white;
padding: 30px;
border-radius: 8px;
}
.sort-dropdown {
position: relative;
display: inline-block;
margin-bottom: 20px;
}
.sort-dropdown__button {
display: flex;
align-items: center;
gap: 8px;
padding: 10px 16px;
background: white;
border: 1px solid #ddd;
border-radius: 6px;
cursor: pointer;
font-size: 14px;
}
.sort-dropdown__button:hover {
border-color: #999;
background: #fafafa;
}
.arrow {
transition: transform 0.2s;
font-size: 10px;
}
.arrow.open {
transform: rotate(180deg);
}
.sort-dropdown__list {
position: absolute;
top: calc(100% + 4px);
left: 0;
min-width: 100%;
background: white;
border: 1px solid #ddd;
border-radius: 6px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
list-style: none;
padding: 4px;
margin: 0;
z-index: 100;
}
.sort-dropdown__list li {
padding: 8px 12px;
cursor: pointer;
border-radius: 4px;
white-space: nowrap;
}
.sort-dropdown__list li:hover {
background: #f5f5f5;
}
.sort-dropdown__list li.selected {
background: #e6f2ff;
font-weight: 500;
}
.product-list {
display: grid;
gap: 15px;
}
.product-item {
display: flex;
justify-content: space-between;
padding: 15px;
background: #fafafa;
border-radius: 6px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.0/umd/react-dom.production.min.js"></script>
<div id="root"></div>