Spaces:
Running
Running
import { Square, RectangleVertical, RectangleHorizontal, ChevronDown } from 'lucide-react'; | |
import { useState, useEffect, useRef } from 'react'; | |
const dimensions = [ | |
{ | |
id: 'landscape', | |
label: '3:2', | |
width: 1500, | |
height: 1000, | |
icon: RectangleHorizontal | |
}, | |
{ | |
id: 'square', | |
label: '1:1', | |
width: 1000, | |
height: 1000, | |
icon: Square | |
}, | |
{ | |
id: 'portrait', | |
label: '4:5', | |
width: 1000, | |
height: 1250, | |
icon: RectangleVertical | |
} | |
]; | |
const DimensionSelector = ({ currentDimension = dimensions[0], onDimensionChange }) => { | |
const [isOpen, setIsOpen] = useState(false); | |
const dropdownRef = useRef(null); | |
// Handle both click and hover for better mobile and desktop experience | |
const handleToggle = () => setIsOpen(!isOpen); | |
// We don't want to close on mouse leave immediately | |
const timeoutRef = useRef(null); | |
const handleMouseEnter = () => { | |
if (timeoutRef.current) { | |
clearTimeout(timeoutRef.current); | |
timeoutRef.current = null; | |
} | |
setIsOpen(true); | |
}; | |
const handleMouseLeave = () => { | |
timeoutRef.current = setTimeout(() => { | |
setIsOpen(false); | |
}, 100); | |
}; | |
// Close dropdown when clicking outside | |
useEffect(() => { | |
const handleClickOutside = (event) => { | |
if (dropdownRef.current && !dropdownRef.current.contains(event.target)) { | |
setIsOpen(false); | |
} | |
}; | |
document.addEventListener('mousedown', handleClickOutside); | |
return () => { | |
document.removeEventListener('mousedown', handleClickOutside); | |
if (timeoutRef.current) clearTimeout(timeoutRef.current); | |
}; | |
}, []); | |
// Find current dimension | |
const current = dimensions.find(d => d.id === currentDimension.id) || dimensions[0]; | |
const Icon = current.icon; | |
return ( | |
<div | |
className="relative z-50" | |
ref={dropdownRef} | |
onMouseEnter={handleMouseEnter} | |
onMouseLeave={handleMouseLeave} | |
> | |
{/* Current selection */} | |
<button | |
type="button" | |
className="w-full flex flex-row md:flex-col items-center justify-center md:p-2 md:py-4 px-2 py-2 rounded-lg transition-colors hover:bg-gray-50" | |
onClick={handleToggle} | |
style={{ opacity: isOpen ? 1 : 0.7 }} | |
> | |
<Icon className="w-5 h-5 text-gray-900 mr-2 md:mr-0 md:mb-1" /> | |
<span className="text-sm text-gray-900">{current.label}</span> | |
</button> | |
{/* Dropdown */} | |
{isOpen && ( | |
<> | |
{/* Invisible bridge element that extends to the dropdown */} | |
<div className="hidden md:block absolute left-[calc(100%-4px)] top-0 h-full w-8" | |
onMouseEnter={handleMouseEnter} | |
style={{ pointerEvents: 'auto' }} /> | |
<div className="absolute left-0 md:left-[calc(100%+4px)] top-0 bg-white rounded-xl shadow-soft | |
border border-gray-200 z-10 min-w-[80px]" | |
onMouseEnter={handleMouseEnter} | |
onMouseLeave={handleMouseLeave}> | |
<div className="py-1"> | |
{dimensions.map((dim) => { | |
const Icon = dim.icon; | |
return ( | |
<button | |
type="button" | |
key={dim.id} | |
onClick={() => { | |
onDimensionChange(dim); | |
setIsOpen(false); | |
}} | |
className={`w-full p-2 flex items-center gap-2 hover:bg-gray-50 transition-colors whitespace-nowrap ${ | |
currentDimension.id === dim.id ? 'bg-gray-100 text-gray-900' : 'text-gray-600' | |
} ${dim.id === dimensions[0].id ? 'rounded-t-lg' : ''} ${dim.id === dimensions[dimensions.length-1].id ? 'rounded-b-lg' : ''}`} | |
> | |
<Icon className="w-4 h-4" /> | |
<span className="text-sm">{dim.label}</span> | |
</button> | |
); | |
})} | |
</div> | |
</div> | |
</> | |
)} | |
</div> | |
); | |
}; | |
export default DimensionSelector; |