Every placement and move path now publishes its ghost pose to a single
`useFacingPose` store, drawn by one editor-side `<FacingPoseIndicator>` overlay,
instead of each path drawing its own triangle (which left the nodes-package and
PlacementBox paths invisible):
- column/shelf presets + all moves (PlacementBox via move-registry, and
DragBoundingBox) now publish the pose, so the triangle finally shows
- stair create + move use a declarative `facingIndicator: { reversed: true }`
(new registry resolver) so the triangle sits before the entry pointing out —
resolved in one place, so create and move match automatically
- stair placement defaults to single and respects the shared `point`
continuation (C) toggle, like the other placement tools
Checkpoint on the placement-interaction epic: also carries the in-flight
continuation-profile extraction (lib/continuation), grid surface (item #8), and
HUD work. Door/window still render their own legacy inline triangle and are
migrated to the overlay next.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
109 lines
3.4 KiB
TypeScript
109 lines
3.4 KiB
TypeScript
'use client'
|
|
|
|
import { Icon } from '@iconify/react'
|
|
import { Copy, Move, Search, Spline, Trash2 } from 'lucide-react'
|
|
import type { MouseEventHandler, PointerEventHandler } from 'react'
|
|
|
|
type NodeActionMenuProps = {
|
|
onFind?: MouseEventHandler<HTMLButtonElement>
|
|
onAddHole?: MouseEventHandler<HTMLButtonElement>
|
|
onDelete?: MouseEventHandler<HTMLButtonElement>
|
|
onDuplicate?: MouseEventHandler<HTMLButtonElement>
|
|
onMove?: MouseEventHandler<HTMLButtonElement>
|
|
onCurve?: MouseEventHandler<HTMLButtonElement>
|
|
onPointerDown?: PointerEventHandler<HTMLDivElement>
|
|
onPointerUp?: PointerEventHandler<HTMLDivElement>
|
|
onPointerEnter?: PointerEventHandler<HTMLDivElement>
|
|
onPointerLeave?: PointerEventHandler<HTMLDivElement>
|
|
}
|
|
|
|
export function NodeActionMenu({
|
|
onFind,
|
|
onAddHole,
|
|
onDelete,
|
|
onDuplicate,
|
|
onMove,
|
|
onCurve,
|
|
onPointerDown,
|
|
onPointerUp,
|
|
onPointerEnter,
|
|
onPointerLeave,
|
|
}: NodeActionMenuProps) {
|
|
return (
|
|
<div
|
|
className="pointer-events-auto flex items-center gap-1 rounded-lg border border-border bg-background/95 p-1 shadow-xl backdrop-blur-md"
|
|
onPointerDown={onPointerDown}
|
|
onPointerEnter={onPointerEnter}
|
|
onPointerLeave={onPointerLeave}
|
|
onPointerUp={onPointerUp}
|
|
>
|
|
{onFind && (
|
|
<button
|
|
aria-label="Find in catalog"
|
|
className="tooltip-trigger rounded-md p-1.5 text-muted-foreground transition-colors hover:bg-accent hover:text-foreground"
|
|
onClick={onFind}
|
|
title="Find in catalog"
|
|
type="button"
|
|
>
|
|
<Search className="h-4 w-4" />
|
|
</button>
|
|
)}
|
|
{onMove && (
|
|
<button
|
|
aria-label="Move"
|
|
className="tooltip-trigger rounded-md p-1.5 text-muted-foreground transition-colors hover:bg-accent hover:text-foreground"
|
|
onClick={onMove}
|
|
title="Move"
|
|
type="button"
|
|
>
|
|
<Move className="h-4 w-4" />
|
|
</button>
|
|
)}
|
|
{onCurve && (
|
|
<button
|
|
aria-label="Curve"
|
|
className="tooltip-trigger rounded-md p-1.5 text-muted-foreground transition-colors hover:bg-accent hover:text-foreground"
|
|
onClick={onCurve}
|
|
title="Curve"
|
|
type="button"
|
|
>
|
|
<Spline className="h-4 w-4" />
|
|
</button>
|
|
)}
|
|
{onDuplicate && (
|
|
<button
|
|
aria-label="Duplicate"
|
|
className="tooltip-trigger rounded-md p-1.5 text-muted-foreground transition-colors hover:bg-accent hover:text-foreground"
|
|
onClick={onDuplicate}
|
|
title="Duplicate"
|
|
type="button"
|
|
>
|
|
<Copy className="h-4 w-4" />
|
|
</button>
|
|
)}
|
|
{onAddHole && (
|
|
<button
|
|
aria-label="Cut Out"
|
|
className="tooltip-trigger rounded-md p-1.5 text-muted-foreground transition-colors hover:bg-accent hover:text-foreground"
|
|
onClick={onAddHole}
|
|
title="Cut Out"
|
|
type="button"
|
|
>
|
|
<Icon height={16} icon="carbon:cut-out" width={16} />
|
|
</button>
|
|
)}
|
|
{onDelete && (
|
|
<button
|
|
aria-label="Delete"
|
|
className="tooltip-trigger rounded-md p-1.5 text-muted-foreground transition-colors hover:bg-destructive/10 hover:text-destructive"
|
|
onClick={onDelete}
|
|
title="Delete"
|
|
type="button"
|
|
>
|
|
<Trash2 className="h-4 w-4" />
|
|
</button>
|
|
)}
|
|
</div>
|
|
)
|
|
}
|