feat(network): add align/distribute/group sections to context menu

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
chihlasm
2026-04-13 20:09:32 +00:00
parent bdaea68dd3
commit f90e2c956f
2 changed files with 106 additions and 2 deletions

View File

@@ -1,5 +1,10 @@
import { useEffect, useRef } from 'react'
import { Copy, CopyPlus, Trash2, ClipboardPaste, BoxSelect, Maximize2, BringToFront, SendToBack } from 'lucide-react'
import {
Copy, CopyPlus, Trash2, ClipboardPaste, BoxSelect, Maximize2, BringToFront, SendToBack,
AlignStartVertical, AlignCenterHorizontal, AlignEndVertical,
AlignStartHorizontal, AlignCenterVertical, AlignEndHorizontal,
AlignHorizontalSpaceAround, AlignVerticalSpaceAround,
} from 'lucide-react'
import { cn } from '@/lib/utils'
interface MenuAction {
@@ -15,9 +20,41 @@ interface ContextMenuProps {
position: { x: number; y: number }
actions: MenuAction[]
onClose: () => void
onAlignLeft?: () => void
onAlignRight?: () => void
onAlignCenterH?: () => void
onAlignTop?: () => void
onAlignBottom?: () => void
onAlignCenterV?: () => void
onDistributeH?: () => void
onDistributeV?: () => void
canAlign?: boolean
canDistribute?: boolean
onGroupSelection?: () => void
onUngroupSelection?: () => void
canGroup?: boolean
canUngroup?: boolean
}
export function ContextMenu({ position, actions, onClose }: ContextMenuProps) {
export function ContextMenu({
position,
actions,
onClose,
onAlignLeft,
onAlignRight,
onAlignCenterH,
onAlignTop,
onAlignBottom,
onAlignCenterV,
onDistributeH,
onDistributeV,
canAlign,
canDistribute,
onGroupSelection,
onUngroupSelection,
canGroup,
canUngroup,
}: ContextMenuProps) {
const menuRef = useRef<HTMLDivElement>(null)
const clampedPosition = { ...position }
@@ -83,6 +120,59 @@ export function ContextMenu({ position, actions, onClose }: ContextMenuProps) {
</button>
</div>
))}
{canAlign && (
<>
<div className="border-t border-default my-1" />
<div className="px-3 py-0.5 text-[10px] font-medium text-muted uppercase tracking-wider">Align</div>
<button onClick={() => { onAlignLeft?.(); onClose() }} className="flex w-full items-center gap-2 px-3 py-2 text-xs text-primary hover:bg-elevated">
<AlignStartVertical size={13} /> <span>Align Left</span>
</button>
<button onClick={() => { onAlignCenterH?.(); onClose() }} className="flex w-full items-center gap-2 px-3 py-2 text-xs text-primary hover:bg-elevated">
<AlignCenterHorizontal size={13} /> <span>Align Center</span>
</button>
<button onClick={() => { onAlignRight?.(); onClose() }} className="flex w-full items-center gap-2 px-3 py-2 text-xs text-primary hover:bg-elevated">
<AlignEndVertical size={13} /> <span>Align Right</span>
</button>
<button onClick={() => { onAlignTop?.(); onClose() }} className="flex w-full items-center gap-2 px-3 py-2 text-xs text-primary hover:bg-elevated">
<AlignStartHorizontal size={13} /> <span>Align Top</span>
</button>
<button onClick={() => { onAlignCenterV?.(); onClose() }} className="flex w-full items-center gap-2 px-3 py-2 text-xs text-primary hover:bg-elevated">
<AlignCenterVertical size={13} /> <span>Align Middle</span>
</button>
<button onClick={() => { onAlignBottom?.(); onClose() }} className="flex w-full items-center gap-2 px-3 py-2 text-xs text-primary hover:bg-elevated">
<AlignEndHorizontal size={13} /> <span>Align Bottom</span>
</button>
{canDistribute && (
<>
<div className="border-t border-default my-1" />
<div className="px-3 py-0.5 text-[10px] font-medium text-muted uppercase tracking-wider">Distribute</div>
<button onClick={() => { onDistributeH?.(); onClose() }} className="flex w-full items-center gap-2 px-3 py-2 text-xs text-primary hover:bg-elevated">
<AlignHorizontalSpaceAround size={13} /> <span>Space Horizontally</span>
</button>
<button onClick={() => { onDistributeV?.(); onClose() }} className="flex w-full items-center gap-2 px-3 py-2 text-xs text-primary hover:bg-elevated">
<AlignVerticalSpaceAround size={13} /> <span>Space Vertically</span>
</button>
</>
)}
</>
)}
{(canGroup || canUngroup) && (
<>
<div className="border-t border-default my-1" />
{canGroup && (
<button onClick={() => { onGroupSelection?.(); onClose() }} className="flex w-full items-center gap-2 px-3 py-2 text-xs text-primary hover:bg-elevated">
<BoxSelect size={13} /> <span>Group Selection</span>
</button>
)}
{canUngroup && (
<button onClick={() => { onUngroupSelection?.(); onClose() }} className="flex w-full items-center gap-2 px-3 py-2 text-xs text-primary hover:bg-elevated">
<BoxSelect size={13} /> <span>Ungroup</span>
</button>
)}
</>
)}
</div>
)
}

View File

@@ -736,6 +736,20 @@ function DiagramEditorInner() {
})
}
onClose={closeContextMenu}
onAlignLeft={diagramCommands.alignLeft}
onAlignRight={diagramCommands.alignRight}
onAlignCenterH={diagramCommands.alignCenterH}
onAlignTop={diagramCommands.alignTop}
onAlignBottom={diagramCommands.alignBottom}
onAlignCenterV={diagramCommands.alignCenterV}
onDistributeH={diagramCommands.distributeHorizontally}
onDistributeV={diagramCommands.distributeVertically}
canAlign={contextMenu.type === 'node' ? diagramCommands.canAlign : false}
canDistribute={contextMenu.type === 'node' ? diagramCommands.canDistribute : false}
onGroupSelection={() => {}}
onUngroupSelection={() => {}}
canGroup={false}
canUngroup={false}
/>
)}
{pendingDeleteNodeId && (