Restore Logo Drag (translate #logo-root only)

Goal

Click-drag inside the preview (empty canvas area or a “Move Logo” handle) should translate the whole logo group (#logo-root) smoothly on mouse + touch.

Drag must compose with rotation/scale (don’t overwrite them) and stay within bounds.

Do (exactly):

Single transform surface

Keep logo state: tx, ty, scale, rotationDeg.

Compose on #logo-root in this exact order:

transform="translate(tx, ty) rotate(rotationDeg) scale(scale)"


Drag changes tx/ty only. Size slider changes scale only. Rotation changes rotationDeg only.

Pointer wiring (rAF throttled)

Attach drag start to the preview interaction layer (not the SVG element that’s CSS-scaled).

EITHER: add a “Move Logo” sub-tool button that, when active, sets the preview to logo-drag mode;

OR: start drag when pointerdown occurs on the preview but not on draggable text/shapes (hit-test target).

On pointerdown:

setPointerCapture(e.pointerId)

record {originX, originY, startTx, startTy, previewRect}

e.preventDefault()

On pointermove (if dragging):

dx = e.clientX - originX, dy = e.clientY - originY

candidate tx = startTx + dx, ty = startTy + dy

Clamp using transformed bbox (see step 4)

requestAnimationFrame(() => setTxTy(clampedTx, clampedTy))

On pointerup/cancel: clear drag state.

Interaction hygiene

Ensure preview has touch-action: none; user-select: none;

Ensure any decorative overlays/ghosts have pointer-events: none;

The panel/toolbar stays outside the preview so it doesn’t intercept.

Bounds clamp (works with rotation/scale)

Build a temporary transform string with candidate tx/ty and current rotation/scale, apply to #logo-root, get transformed bbox (getBBox() or getBoundingClientRect()).

Keep the bbox fully inside the preview:

left ≥ 0, top ≥ 0, right ≤ previewWidth, bottom ≤ previewHeight

Adjust tx/ty accordingly.

Respect the existing 200% max scale rule when user scales; drag should never push the logo outside.

Don’t regress per-piece drag

Text and shape overlays must keep their own drag handlers.

Logo drag should not fire when the target is a text/shape overlay (check e.target.closest('[data-draggable-text],[data-shape-id]') to bail).

Cursors + UX

When Move Logo mode is active (or when mousing empty canvas), show cursor: grab/grabbing.

Acceptance

In both Replit preview and a normal Chrome tab:

Dragging moves the entire logo smoothly.

Rotation/scale remain intact during/after drag.

Elements cannot be dragged outside the preview bounds.

Per-piece drag (text/shapes) still works as before.

If it still doesn’t move after they wire this, the usual culprits are:

Listener attached to the wrong node (measure/attach on the inner preview, not the card).

An overlay with pointer-events:auto covering the canvas.

Transform order wrong (rotate/scale before translate).