or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

context-provider.mddrag-layer.mddrag-sources.mddrop-targets.mdindex.mdutilities.md

drag-sources.mddocs/

0

# Drag Sources

1

2

The useDrag hook enables components to act as drag sources, allowing users to drag elements and initiate drag and drop operations.

3

4

## Capabilities

5

6

### useDrag Hook

7

8

Creates a draggable element with comprehensive configuration options for drag behavior.

9

10

```typescript { .api }

11

/**

12

* Hook for making components draggable

13

* @param specArg - Drag source specification (object or function)

14

* @param deps - Optional dependency array for memoization

15

* @returns Tuple of [collected props, drag ref connector, preview ref connector]

16

*/

17

function useDrag<DragObject = unknown, DropResult = unknown, CollectedProps = unknown>(

18

specArg: FactoryOrInstance<DragSourceHookSpec<DragObject, DropResult, CollectedProps>>,

19

deps?: unknown[]

20

): [CollectedProps, ConnectDragSource, ConnectDragPreview];

21

22

interface DragSourceHookSpec<DragObject, DropResult, CollectedProps> {

23

/** The type of item being dragged - required */

24

type: SourceType;

25

/** Item data or factory function - defines what data is available to drop targets */

26

item?: DragObject | DragObjectFactory<DragObject>;

27

/** Drag source options for visual effects */

28

options?: DragSourceOptions;

29

/** Preview rendering options */

30

previewOptions?: DragPreviewOptions;

31

/** Called when drag operation ends */

32

end?: (draggedItem: DragObject, monitor: DragSourceMonitor<DragObject, DropResult>) => void;

33

/** Determines if dragging is allowed */

34

canDrag?: boolean | ((monitor: DragSourceMonitor<DragObject, DropResult>) => boolean);

35

/** Custom logic for determining drag state */

36

isDragging?: (monitor: DragSourceMonitor<DragObject, DropResult>) => boolean;

37

/** Function to collect properties from monitor */

38

collect?: (monitor: DragSourceMonitor<DragObject, DropResult>) => CollectedProps;

39

}

40

41

type DragObjectFactory<T> = (monitor: DragSourceMonitor<T>) => T | null;

42

type FactoryOrInstance<T> = T | (() => T);

43

```

44

45

**Basic Usage:**

46

47

```typescript

48

import React from "react";

49

import { useDrag } from "react-dnd";

50

51

interface DragItem {

52

id: string;

53

name: string;

54

}

55

56

function DraggableCard({ id, name }: DragItem) {

57

const [{ isDragging }, drag] = useDrag({

58

type: "card",

59

item: { id, name },

60

collect: (monitor) => ({

61

isDragging: monitor.isDragging(),

62

}),

63

});

64

65

return (

66

<div

67

ref={drag}

68

style={{

69

opacity: isDragging ? 0.5 : 1,

70

cursor: 'move'

71

}}

72

>

73

{name}

74

</div>

75

);

76

}

77

```

78

79

**Advanced Usage with All Options:**

80

81

```typescript

82

import React, { useState } from "react";

83

import { useDrag } from "react-dnd";

84

85

function AdvancedDraggableItem({ id, data, disabled }) {

86

const [dragCount, setDragCount] = useState(0);

87

88

const [collected, drag, preview] = useDrag({

89

type: "advanced-item",

90

91

// Dynamic item factory

92

item: (monitor) => {

93

console.log("Drag started");

94

return { id, data, timestamp: Date.now() };

95

},

96

97

// Conditional dragging

98

canDrag: !disabled,

99

100

// Custom drag state logic

101

isDragging: (monitor) => {

102

return monitor.getItem()?.id === id;

103

},

104

105

// Drag end handler

106

end: (item, monitor) => {

107

setDragCount(prev => prev + 1);

108

109

if (monitor.didDrop()) {

110

const dropResult = monitor.getDropResult();

111

console.log("Item dropped:", dropResult);

112

} else {

113

console.log("Drag cancelled");

114

}

115

},

116

117

// Visual options

118

options: {

119

dropEffect: "move"

120

},

121

122

// Preview options

123

previewOptions: {

124

captureDraggingState: false,

125

anchorX: 0.5,

126

anchorY: 0.5

127

},

128

129

// Collect function

130

collect: (monitor) => ({

131

isDragging: monitor.isDragging(),

132

canDrag: monitor.canDrag(),

133

itemType: monitor.getItemType(),

134

dragOffset: monitor.getClientOffset(),

135

}),

136

});

137

138

return (

139

<div>

140

<div ref={drag} style={{ opacity: collected.isDragging ? 0.5 : 1 }}>

141

Draggable Item (dragged {dragCount} times)

142

</div>

143

<div ref={preview}>

144

Custom Preview Content

145

</div>

146

</div>

147

);

148

}

149

```

150

151

### Connector Functions

152

153

The useDrag hook returns connector functions for attaching drag functionality to DOM elements.

154

155

```typescript { .api }

156

/** Function to connect DOM elements as drag sources */

157

type ConnectDragSource = DragElementWrapper<DragSourceOptions>;

158

159

/** Function to connect custom drag preview elements */

160

type ConnectDragPreview = DragElementWrapper<DragPreviewOptions>;

161

162

type DragElementWrapper<Options> = (

163

elementOrNode: ConnectableElement,

164

options?: Options

165

) => React.ReactElement | null;

166

167

type ConnectableElement = React.RefObject<any> | React.ReactElement | Element | null;

168

```

169

170

**Usage Examples:**

171

172

```typescript

173

function CustomConnectorExample() {

174

const [{ isDragging }, drag, preview] = useDrag({

175

type: "item",

176

item: { id: "example" },

177

collect: (monitor) => ({ isDragging: monitor.isDragging() })

178

});

179

180

return (

181

<div>

182

{/* Basic drag connector */}

183

<button ref={drag}>Drag Handle</button>

184

185

{/* Separate preview element */}

186

<div ref={preview}>

187

This will be shown during drag

188

</div>

189

190

{/* Connector with options */}

191

{drag(

192

<div style={{ padding: 10 }}>

193

Custom draggable area

194

</div>,

195

{ dropEffect: "copy" }

196

)}

197

</div>

198

);

199

}

200

```

201

202

### Drag Source Monitor

203

204

Monitor interface providing information about the current drag operation.

205

206

```typescript { .api }

207

interface DragSourceMonitor<DragObject = unknown, DropResult = unknown> {

208

/** Returns true if dragging is allowed */

209

canDrag(): boolean;

210

/** Returns true if this component is being dragged */

211

isDragging(): boolean;

212

/** Returns the type of item being dragged */

213

getItemType(): Identifier | null;

214

/** Returns the dragged item data */

215

getItem<T = DragObject>(): T;

216

/** Returns drop result after drop completes */

217

getDropResult<T = DropResult>(): T | null;

218

/** Returns true if drop was handled by a target */

219

didDrop(): boolean;

220

/** Returns initial pointer coordinates when drag started */

221

getInitialClientOffset(): XYCoord | null;

222

/** Returns initial drag source coordinates */

223

getInitialSourceClientOffset(): XYCoord | null;

224

/** Returns current pointer coordinates */

225

getClientOffset(): XYCoord | null;

226

/** Returns pointer movement since drag start */

227

getDifferenceFromInitialOffset(): XYCoord | null;

228

/** Returns projected source coordinates */

229

getSourceClientOffset(): XYCoord | null;

230

/** Returns IDs of potential drop targets */

231

getTargetIds(): Identifier[];

232

}

233

```

234

235

## Configuration Options

236

237

### Drag Source Options

238

239

```typescript { .api }

240

interface DragSourceOptions {

241

/** Visual drop effect hint ('move', 'copy', etc.) */

242

dropEffect?: string;

243

}

244

```

245

246

### Drag Preview Options

247

248

```typescript { .api }

249

interface DragPreviewOptions {

250

/** Whether to immediately capture dragging state */

251

captureDraggingState?: boolean;

252

/** Horizontal anchor point (0-1) */

253

anchorX?: number;

254

/** Vertical anchor point (0-1) */

255

anchorY?: number;

256

/** Horizontal offset from cursor */

257

offsetX?: number;

258

/** Vertical offset from cursor */

259

offsetY?: number;

260

}

261

```

262

263

## Common Patterns

264

265

### Conditional Dragging

266

267

```typescript

268

function ConditionalDrag({ item, canEdit }) {

269

const [{ isDragging }, drag] = useDrag({

270

type: "item",

271

item,

272

canDrag: canEdit && item.status !== "locked",

273

collect: (monitor) => ({

274

isDragging: monitor.isDragging(),

275

}),

276

});

277

278

return (

279

<div

280

ref={canEdit ? drag : null}

281

style={{

282

opacity: isDragging ? 0.5 : 1,

283

cursor: canEdit ? 'move' : 'default'

284

}}

285

>

286

{item.name}

287

</div>

288

);

289

}

290

```

291

292

### Multi-type Dragging

293

294

```typescript

295

function MultiTypeDragItem({ item, mode }) {

296

const [{ isDragging }, drag] = useDrag({

297

type: mode === "copy" ? "copyable-item" : "movable-item",

298

item: { ...item, mode },

299

collect: (monitor) => ({

300

isDragging: monitor.isDragging(),

301

}),

302

});

303

304

return <div ref={drag}>{item.name}</div>;

305

}

306

```