Drop Confirm

A drop target that names what will move and where, ignores flick drops and offers Undo, so files and folders don't land in the wrong place.

StopsFolder dropped into wrong place

Installation

pnpm dlx shadcn@latest add https://bollard-ui.vercel.app/r/drop-confirm.json

The files land in your project. They're yours to edit.

Usage

drop-confirm-usage.tsx
import { DropConfirm, type MoveItems } from "@/components/ui/drop-confirm"

<DropConfirm
  destination="Archive"
  items={items}
  onMove={() => setWhere("Archive")}
  onUndo={() => setWhere("My files")}
/>

What it catches

  • Drops that land in passing, before the pointer settles (settleTime)
  • Large moves above a threshold (confirmAbove)
  • Moves into a different space that change who can see the items
  • Regret, with a timed Undo

Props

DropConfirm props
PropTypeDefault
destination* (required)

Folder name, used in every message.

string—
space

Who can see this folder. Items arriving from a different space ask first. Defaults to `homeSpace`.

string—
items* (required)

What a drag or the Move here button would move: the current selection. Without it, dragged OS files are counted.

MoveItems | null—
onMove* (required)

Performs the move. Return a promise to show a pending state.

(items: MoveItems) => void | Promise<void>—
onUndo

Puts the items back. Without it there is no Undo.

(items: MoveItems) => void—
settleTime

Drops sooner than this (ms) after entering the zone are ignored as an accidental flick.

number350
confirmAbove

Moves of at least this many items ask first.

number20
undoTimeout

How long Undo stays offered, in ms.

number6000
dragText

Templates: `{count}` ("12 files"), `{destination}`, `{from}` and `{to}` are filled in.

string"Move {count} to {destination}"
flickText

Shown when a drop was ignored because it came too fast.

string"That drop was too quick, so nothing moved. Hold over the…
spaceText

Warning when the move changes who can see the items.

string"Moving {count} from {from} to {to} changes who can see t…
largeText

Warning for large moves.

string"Move all {count} to {destination}?"
movedText

Shown after a move.

string"Moved {count} to {destination}."
undoneText

Shown after Undo.

string"Moved {count} back."
moveHereLabel

Text for the Move here button.

string"Move here"
confirmLabel

Text for the confirm button.

string"Move {count}"
cancelLabel

Text for the cancel button.

string"Cancel"
undoLabel

Text for the Undo button.

string"Undo"
settlingText

Hint under the folder while the pointer is settling, and once it's armed.

string"Hold a moment…"
armedText

Shown once the pointer has rested long enough to drop.

string"Release to move"
moveHereAriaLabel

Screen reader name of Move here. `{label}`, `{count}` and `{destination}` are filled in.

string"{label}: {count} to {destination}"
formatItems

Turns items into words for `{count}`, e.g. with your i18n plural rules. Defaults to `formatCount`.

(items: MoveItems) => string(i) => formatCount(i)
homeSpace

Where items with no `space` are. Defaults to "Private"; set it when you translate the space names.

string"Private"

* required. Also accepts the native props of <div> (className, aria-*, data-*…), passed through to it.

Accessibility

  • Each target is a role="group" labelled with the destination and described by its status
  • A Move here button gives keyboard and touch users the same action, with a counted aria-label
  • The confirm step focuses Cancel; Escape closes it

Only what the source does. Test with your own screen reader and content before shipping.

Limitations

This is a UX guard rail. Enforce on the server: Re-check permissions for the destination on the server, and support undo for moves that change sharing.

  • Item counts and spaces come from your drag data and props
  • Undo calls your onUndo; it can't reverse a move your backend already finalised

What it stops

Folder dropped into wrong place

Also known as

drag and drop · dnd · move to folder · kanban · file manager · dnd-kit · drop zone

Field type

Dependencies

Pairs well with