useDragAction: activation-click grace + curve fence commit sfx

The legacy CurveFenceTool ignored grid:click for 150ms after mount —
otherwise the very click that activates a tool (e.g. the floating menu
"curve" button) cascades through the R3F drei <Html> portal into the
grid, fires grid:click on the just-mounted tool, and commits the drag
before any preview move runs. The new useDragAction was missing this
guard, so the Stage D fence curve port "click → place sfx → exit"
without ever letting the user adjust.

Adds `activationGraceMs` (default 150) on useDragAction; ports the
sfx:item-place commit emission into FenceCurveTool so the kind-owned
tool matches legacy UX. Same guard will cover the upcoming Stage D
ports (endpoint move, whole-fence move, placement, plus
slab/ceiling/wall D).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Wassim SAMAD
2026-05-15 17:29:04 -04:00
co-authored by Claude Opus 4.7
parent 8ca9686b27
commit 1082b62552
2 changed files with 23 additions and 5 deletions
+18 -1
View File
@@ -42,6 +42,14 @@ export type UseDragActionArgs<Ctx, Draft> = {
onCommit?: () => void
/** Fires once after `action.cancel` (Esc, unmount, or commit-returns-false). */
onCancel?: () => void
/**
* Milliseconds after activation during which `grid:click` is swallowed.
* Stops the very click that mounted this tool (a DOM button or 3D
* handle elsewhere) from cascading into the grid and immediately
* committing the drag. Defaults to 150ms — matches the legacy guard
* used by every kind-owned tool entered via a click.
*/
activationGraceMs?: number
}
/**
@@ -72,12 +80,21 @@ export function useDragAction<Ctx, Draft>(args: UseDragActionArgs<Ctx, Draft>) {
session.start(argsRef.current.initial)
const activatedAt = Date.now()
const graceMs = argsRef.current.activationGraceMs ?? 150
const onMove = (event: GridEvent) => {
const point: readonly [number, number] = [event.localPosition[0], event.localPosition[2]]
session.move(point, modifiersFromGridEvent(event))
}
const onClick = (_event: GridEvent) => {
const onClick = (event: GridEvent) => {
// Swallow the click that mounted this tool — otherwise the very
// first grid:click cascades into commit() before any move().
if (Date.now() - activatedAt < graceMs) {
event.nativeEvent?.stopPropagation?.()
return
}
session.commit()
}