@ -332,32 +332,43 @@ function GroupControls({ groupId, zoom }: GroupControlsProps) {
) }
< / div >
{ /* Expanded controls - shown when menu is open */ }
{ /* Three-dot menu toggle - always visible */ }
< div className = "relative" >
< button
onClick = { ( e ) = > { e . stopPropagation ( ) ; setShowMenu ( ! showMenu ) ; } }
className = "w-6 h-6 rounded-md flex flex-col items-center justify-center gap-[2px] hover:bg-white/20 transition-colors"
title = "Group options"
>
< div className = "w-[3px] h-[3px] rounded-full bg-white/70" / >
< div className = "w-[3px] h-[3px] rounded-full bg-white/70" / >
< div className = "w-[3px] h-[3px] rounded-full bg-white/70" / >
< / button >
{ /* Vertical context menu - appears above the three-dot button */ }
{ showMenu && (
< div className = "flex items-center gap-px ml-1" >
{ /* Color Picker */ }
< div className = "relative flex items-center" ref = { colorPickerRef } >
< div className = "absolute bottom-full left-1/2 -translate-x-1/2 mb-1 bg-neutral-800/90 backdrop-blur rounded-lg py-1 min-w-[130px] shadow-lg shadow-black/30 " >
{ /* Background color row */ }
< div className = "relative" ref = { colorPickerRef } >
< button
onClick = { ( e ) = > { e . stopPropagation ( ) ; setShowColorPicker ( ! showColorPicker ) ; } }
className = "w-6 h-6 rounded-md flex items-center justify-center hover:bg-white/20 transition-colors"
title = "Change color"
className = "flex items-center gap-2 px-3 py-1.5 w-full hover:bg-white/10 text-xs text-white/80 transition-colors"
>
< div
className = "w-3.5 h-3.5 rounded border border-white/30 hover:border-white/60 transition-colors"
className = "w-3 h-3 rounded-full border border-white/30"
style = { { backgroundColor : bgColor } }
/ >
< span > Background < / span >
< / button >
{ showColorPicker && (
< >
{ /* Invisible backdrop to catch clicks outside */ }
< div
className = "fixed inset-0 z-40"
onClick = { ( ) = > setShowColorPicker ( false ) }
/ >
< div className = "absolute bottom-full left-1/2 mb-2 z-50 pointer-events-auto" style = { { transform : "translateX(-50%)" } } >
< div className = "absolute bottom-full left-0 mb-1 z-50 pointer-events-auto" >
{ COLOR_OPTIONS . map ( ( { color , label } , index ) = > {
const totalItems = COLOR_OPTIONS . length ;
const arcSpread = 18 0 ;
const arcSpread = 15 0 ;
const startAngle = - 90 - arcSpread / 2 ;
const angleStep = arcSpread / ( totalItems - 1 ) ;
const angle = startAngle + index * angleStep ;
@ -365,7 +376,7 @@ function GroupControls({ groupId, zoom }: GroupControlsProps) {
const rad = ( angle * Math . PI ) / 180 ;
const x = Math . cos ( rad ) * radius ;
const y = Math . sin ( rad ) * radius ;
const finalX = x - 12 ;
const finalX = x ;
const finalY = y - 12 ;
return (
@ -383,8 +394,6 @@ function GroupControls({ groupId, zoom }: GroupControlsProps) {
animation : ` colorFanIn- ${ index } 0.25s cubic-bezier(0.34, 1.56, 0.64, 1) forwards ` ,
animationDelay : ` ${ index * 0.025 } s ` ,
opacity : 0 ,
[ "--final-x" as string ] : ` ${ finalX } px ` ,
[ "--final-y" as string ] : ` ${ finalY } px ` ,
} }
title = { label }
>
@ -392,7 +401,7 @@ function GroupControls({ groupId, zoom }: GroupControlsProps) {
@keyframes colorFanIn - $ { index } {
0 % {
opacity : 0 ;
transform : translate ( - 12 px , 0 px ) scale ( 0.3 ) ;
transform : translate ( 0 px , 0 px ) scale ( 0.3 ) ;
}
100 % {
opacity : 1 ;
@ -408,11 +417,10 @@ function GroupControls({ groupId, zoom }: GroupControlsProps) {
) }
< / div >
{ /* Lock/Unlock Button */ }
{ /* Lock/Unlock row */ }
< button
onClick = { ( e ) = > { e . stopPropagation ( ) ; handleToggleLock ( ) ; } }
className = "w-6 h-6 rounded-md flex items-center justify-center hover:bg-white/20 text-white/70 hover:text-white transition-colors"
title = { group . locked ? "Unlock group" : "Lock group" }
onClick = { ( e ) = > { e . stopPropagation ( ) ; handleToggleLock ( ) ; setShowMenu ( false ) ; } }
className = "flex items-center gap-2 px-3 py-1.5 w-full hover:bg-white/10 text-xs text-white/80 transition-colors"
>
{ group . locked ? (
< svg className = "w-3.5 h-3.5" fill = "none" viewBox = "0 0 24 24" stroke = "currentColor" strokeWidth = { 2 } >
@ -423,33 +431,22 @@ function GroupControls({ groupId, zoom }: GroupControlsProps) {
< path strokeLinecap = "round" strokeLinejoin = "round" d = "M8 11V7a4 4 0 118 0m-4 8v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2z" / >
< / svg >
) }
< span > { group . locked ? "Unlock" : "Lock" } < / span >
< / button >
{ /* Delete Button */ }
{ /* Delete row */ }
< button
onClick = { ( e ) = > { e . stopPropagation ( ) ; handleDelete ( ) ; } }
className = "w-6 h-6 rounded-md flex items-center justify-center hover:bg-white/20 text-white/70 hover:text-white transition-colors"
title = "Delete group"
className = "flex items-center gap-2 px-3 py-1.5 w-full hover:bg-white/10 text-xs text-white/80 transition-colors"
>
< svg className = "w-3.5 h-3.5" fill = "none" viewBox = "0 0 24 24" stroke = "currentColor" strokeWidth = { 2 } >
< path strokeLinecap = "round" strokeLinejoin = "round" d = "M6 18L18 6M6 6l12 12" / >
< / svg >
< span > Delete < / span >
< / button >
< / div >
) }
{ /* Three-dot menu toggle - hidden when menu is open */ }
{ ! showMenu && (
< button
onClick = { ( e ) = > { e . stopPropagation ( ) ; setShowMenu ( true ) ; } }
className = "w-6 h-6 rounded-md flex flex-col items-center justify-center gap-[2px] hover:bg-white/20 transition-colors"
title = "Group options"
>
< div className = "w-[3px] h-[3px] rounded-full bg-white/70" / >
< div className = "w-[3px] h-[3px] rounded-full bg-white/70" / >
< div className = "w-[3px] h-[3px] rounded-full bg-white/70" / >
< / button >
) }
< / div >
< / div >
< / div >
< / div >