Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Connector Ref Updates #1278

Merged
merged 7 commits into from
Mar 19, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,13 @@ _New to React DnD? [Read the overview](/docs/overview) before jumping into the d

# DragSourceConnector

`DragSourceConnector` is an object passed to a collecting function of the [`DragSource`](/docs/api/drag-source). Its methods return functions that let you assign the roles to your component's DOM nodes.
`DragSourceConnector` is an object passed to the _collecting function_ of the [`DragSource`](/docs/api/drag-source). It provides the ability to bind your React component to the Drag Source role.

### Methods
## Properties

Call the `DragSourceConnector` methods inside your _collecting function_. This will pass the returned functions to your component as props. You can then use them inside `render` or `componentDidMount` to indicate the DOM node roles. Internally they work by attaching a [callback ref](https://facebook.github.io/react/docs/more-about-refs.html#the-ref-callback-attribute) to the React elements you give them. These callbacks connect the DOM nodes of your component to the chosen DnD backend.
- **`dragSource() => (Element | Node | Ref, options?)`**: Returns a function that must be prop-injected into your component and used in that component's `render()` method. You may pass this function a react component, an DOM element, or a ref object to this method.

- **`dragSource() => (elementOrNode, options?)`**: Returns a function that must be used inside the component to assign the drag source role to a node. By returning `{ connectDragSource: connect.dragSource() }` from your collecting function, you can mark any React element as the draggable node. To do that, replace any `element` with `this.props.connectDragSource(element)` inside the `render` function.

- **`dragPreview() => (elementOrNode, options?)`**: Optional. Returns a function that may be used inside the component to assign the drag preview role to a node. By returning `{ connectDragPreview: connect.dragPreview() }` from your collecting function, you can mark any React element as the drag preview node. To do that, replace any `element` with `this.props.connectDragPreview(element)` inside the `render` function. The drag preview is the node that will be screenshotted by the [HTML5 backend](/docs/backends/html5) when the drag begins. For example, if you want to make something draggable by a small custom handle, you can mark this handle as the `dragSource()`, but also mark an outer, larger component node as the `dragPreview()`. Thus the larger drag preview appears on the screenshot, but only the smaller drag source is actually draggable. Another possible customization is passing an [`Image`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/Image) instance to `dragPreview` from a lifecycle method like `componentDidMount`. This lets you use the actual images for drag previews. (Note that IE does not support this customization). See the example code below for the different usage examples.
- **`dragPreview() => (Element | Node | Ref, options?)`**_(optional)_: Returns a function that may be used inside the component to assign the drag preview role to a node. By returning `{ connectDragPreview: connect.dragPreview() }` from your collecting function, you can mark any React element as the drag preview node. To do that, replace any `element` with `this.props.connectDragPreview(element)` inside the `render` function. The drag preview is the node that will be screenshotted by the [HTML5 backend](/docs/backends/html5) when the drag begins. For example, if you want to make something draggable by a small custom handle, you can mark this handle as the `dragSource()`, but also mark an outer, larger component node as the `dragPreview()`. Thus the larger drag preview appears on the screenshot, but only the smaller drag source is actually draggable. Another possible customization is passing an [`Image`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/Image) instance to `dragPreview` from a lifecycle method like `componentDidMount`. This lets you use the actual images for drag previews. (Note that IE does not support this customization). See the example code below for the different usage examples.

### Method Options

Expand All @@ -37,9 +35,7 @@ The functions returned by the connector methods also accept options. They need t

- `offsetY`: Optional. A number or null if not needed. By default, null. Specifies the vertical offset between the cursor and the drag preview element. If offsetY has a value, anchorY won't be used.

### Example

Check out [the tutorial](/docs/tutorial) for more real examples!
## Usage

```js
import React from 'react';
Expand All @@ -66,7 +62,7 @@ class ComponentWithCopyEffect {
);
}
});
ComponentWithCopyEffect = DragSource(/* ... */)(ComponentWithCopyEffect);
ComponentWithCopyEffect = DragSource(type, {/* ... */}, collect)(ComponentWithCopyEffect);

class ComponentWithHandle {
render() {
Expand All @@ -82,7 +78,7 @@ class ComponentWithHandle {
);
}
}
ComponentWithHandle = DragSource(/* ... */)(ComponentWithHandle);
ComponentWithHandle = DragSource(type, {/* ... */}, collect)(ComponentWithHandle);

class ComponentWithImagePreview {
componentDidMount() {
Expand All @@ -103,5 +99,9 @@ class ComponentWithImagePreview {
);
}
}
ComponentWithImagePreview = DragSource(/* ... */)(ComponentWithImagePreview);
ComponentWithImagePreview = DragSource(type, {/* ... */}, collect)(ComponentWithImagePreview);
```

### Example

Check out [the tutorial](/docs/tutorial) for more real examples!
Original file line number Diff line number Diff line change
Expand Up @@ -7,37 +7,36 @@ _New to React DnD? [Read the overview](/docs/overview) before jumping into the d

# DropTargetConnector

`DropTargetConnector` is an object passed to a collecting function of the [`DropTarget`](/docs/api/drop-target). Its only method `dropTarget()` returns a function that lets you assign the drop target role to one of your component's DOM nodes.
`DropTargetConnector` is an object passed to the _collecting function_ of the [`DropTarget`](/docs/api/drop-target). It provides the ability to bind your React component to the Drop Target role.

### Methods
## Properties

Call the `DropTargetConnector`'s `dropTarget()` method inside your _collecting function_. This will pass the returned function to your component as a prop. You can then use it inside `render` or `componentDidMount` to indicate a DOM node should react to drop target events. Internally it works by attaching a [callback ref](https://facebook.github.io/react/docs/more-about-refs.html#the-ref-callback-attribute) to the React element you give it. This callback connects the DOM node of your component to the chosen DnD backend.
- **`dropTarget() => (Element | Node | Ref)`**: Returns a function that must be prop-injected into your component and used in that component's `render()` method. You may pass this function a react component, an DOM element, or a ref object to this method.

- **`dropTarget() => (elementOrNode)`**: Returns a function that must be used inside the component to assign the drop target role to a node. By returning `{ connectDropTarget: connect.dropTarget() }` from your collecting function, you can mark any React element as the droppable node. To do that, replace any `element` with `this.props.connectDropTarget(element)` inside the `render` function.

### Example

Check out [the tutorial](/docs/tutorial) for more real examples!
## Usage

```js
import React from 'react'
import { DropTarget } from 'react-dnd'

/* ... */

function collect(connect, monitor) {
return {
connectDropTarget: connect.dropTarget(),
}
}

class DropZone {
render() {
const { connectDropTarget } = this.props

return connectDropTarget(<div>You can drop here!</div>)
}
}

export default DropTarget(/* ... */)(DropZone)
export default DropTarget(
ItemTypes.Item,
{
/*...*/
},
(connect, monitor) => ({
connectDropTarget: connect.dropTarget(),
}),
)(DropZone)
```

### Examples

Check out [the tutorial](/docs/tutorial) for more real examples!
19 changes: 13 additions & 6 deletions packages/examples/src/01 Dustbin/Single Target/Box.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import React from 'react'
import { DragSource, DragSourceConnector, DragSourceMonitor } from 'react-dnd'
import {
DragSource,
DragSourceMonitor,
ConnectDragSource,
DragSourceConnector,
} from 'react-dnd'
import ItemTypes from './ItemTypes'

const style: React.CSSProperties = {
Expand All @@ -18,7 +23,7 @@ interface BoxProps {

interface BoxCollectedProps {
isDragging: boolean
dragSource: React.RefObject<any>
connectDragSource: ConnectDragSource
}

const boxSource = {
Expand All @@ -39,13 +44,15 @@ const boxSource = {
}

class Box extends React.Component<BoxProps & BoxCollectedProps> {
private dragSource: React.RefObject<HTMLDivElement> = React.createRef()

public render() {
const { isDragging, dragSource } = this.props
const { name } = this.props
const { name, isDragging, connectDragSource } = this.props
const opacity = isDragging ? 0.4 : 1
connectDragSource(this.dragSource)

return (
<div ref={dragSource} style={{ ...style, opacity }}>
<div ref={this.dragSource} style={{ ...style, opacity }}>
{name}
</div>
)
Expand All @@ -56,7 +63,7 @@ export default DragSource(
ItemTypes.BOX,
boxSource,
(connect: DragSourceConnector, monitor: DragSourceMonitor) => ({
dragSource: connect.dragSourceRef,
connectDragSource: connect.dragSource(),
isDragging: monitor.isDragging(),
}),
)(Box)
18 changes: 13 additions & 5 deletions packages/examples/src/01 Dustbin/Single Target/Dustbin.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import React from 'react'
import { DropTarget, DropTargetConnector, DropTargetMonitor } from 'react-dnd'
import {
DropTarget,
ConnectDropTarget,
DropTargetMonitor,
DropTargetConnector,
} from 'react-dnd'
import ItemTypes from './ItemTypes'

const style: React.CSSProperties = {
Expand All @@ -22,13 +27,16 @@ const boxTarget = {
export interface DustbinProps {
canDrop: boolean
isOver: boolean
dropTarget: React.RefObject<any>
connectDropTarget: ConnectDropTarget
}

class Dustbin extends React.Component<DustbinProps> {
private dropTarget: React.RefObject<HTMLDivElement> = React.createRef()

public render() {
const { canDrop, isOver, dropTarget } = this.props
const { canDrop, isOver, connectDropTarget } = this.props
const isActive = canDrop && isOver
connectDropTarget(this.dropTarget)

let backgroundColor = '#222'
if (isActive) {
Expand All @@ -38,7 +46,7 @@ class Dustbin extends React.Component<DustbinProps> {
}

return (
<div ref={dropTarget} style={{ ...style, backgroundColor }}>
<div ref={this.dropTarget} style={{ ...style, backgroundColor }}>
{isActive ? 'Release to drop' : 'Drag a box here'}
</div>
)
Expand All @@ -49,7 +57,7 @@ export default DropTarget(
ItemTypes.BOX,
boxTarget,
(connect: DropTargetConnector, monitor: DropTargetMonitor) => ({
dropTarget: connect.dropTargetRef,
connectDropTarget: connect.dropTarget(),
isOver: monitor.isOver(),
canDrop: monitor.canDrop(),
}),
Expand Down
23 changes: 13 additions & 10 deletions packages/react-dnd/src/createSourceConnector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,20 @@ import * as React from 'react'
import wrapConnectorHooks from './wrapConnectorHooks'
import { Backend, Unsubscribe, Identifier } from 'dnd-core'
import { isRef } from './hooks/util'
import { DragSourceOptions, DragPreviewOptions } from './interfaces'
const shallowEqual = require('shallowequal')

export default function createSourceConnector(backend: Backend) {
let handlerId: Identifier

// The drop target may either be attached via ref or connect function
let dragSourceRef = React.createRef<any>()
let dragSourceRef: React.RefObject<any> | null = null
let dragSourceNode: any
let dragSourceOptions: any
let disconnectDragSource: Unsubscribe | undefined

// The drag preview may either be attached via ref or connect function
let dragPreviewRef = React.createRef<any>()
let dragPreviewRef: React.RefObject<any> | null = null
let dragPreviewNode: any
let dragPreviewOptions: any
let disconnectDragPreview: Unsubscribe | undefined
Expand All @@ -27,7 +28,8 @@ export default function createSourceConnector(backend: Backend) {
let lastConnectedDragPreviewOptions: any = null

function reconnectDragSource() {
const dragSource = dragSourceNode || dragSourceRef.current
const dragSource =
dragSourceNode || (dragSourceRef && dragSourceRef.current)
if (!handlerId || !dragSource) {
return
}
Expand Down Expand Up @@ -55,7 +57,8 @@ export default function createSourceConnector(backend: Backend) {
}

function reconnectDragPreview() {
const dragPreview = dragPreviewNode || dragPreviewRef.current
const dragPreview =
dragPreviewNode || (dragPreviewRef && dragPreviewRef.current)
if (!handlerId || !dragPreview) {
return
}
Expand Down Expand Up @@ -94,18 +97,18 @@ export default function createSourceConnector(backend: Backend) {
return {
receiveHandlerId,
hooks: wrapConnectorHooks({
dragSourceRef,
dragPreviewRef,
dragSource: function connectDragSource(node: any, options?: any) {
dragSource: (
node: Element | React.ReactElement | React.Ref<any>,
options?: DragSourceOptions,
) => {
dragSourceOptions = options
if (isRef(node)) {
dragSourceRef = node
dragSourceRef = node as React.RefObject<any>
} else {
dragSourceNode = node
}
},

dragPreview: function connectDragPreview(node: any, options?: any) {
dragPreview: (node: any, options?: DragPreviewOptions) => {
dragPreviewOptions = options
if (isRef(node)) {
dragPreviewRef = node
Expand Down
8 changes: 4 additions & 4 deletions packages/react-dnd/src/createTargetConnector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ const shallowEqual = require('shallowequal')
export default function createTargetConnector(backend: Backend) {
let handlerId: Identifier
// The drop target may either be attached via ref or connect function
let dropTargetRef = React.createRef<any>()
let dropTargetRef: React.RefObject<any> | null = null
let dropTargetNode: any
let dropTargetOptions: any
let disconnectDropTarget: Unsubscribe | undefined
Expand All @@ -18,7 +18,8 @@ export default function createTargetConnector(backend: Backend) {
let lastConnectedDropTargetOptions: any = null

function reconnectDropTarget() {
const dropTarget = dropTargetNode || dropTargetRef.current
const dropTarget =
dropTargetNode || (dropTargetRef && dropTargetRef.current)
if (!handlerId || !dropTarget) {
return
}
Expand Down Expand Up @@ -56,8 +57,7 @@ export default function createTargetConnector(backend: Backend) {
return {
receiveHandlerId,
hooks: wrapConnectorHooks({
dropTargetRef,
dropTarget: function connectDropTarget(node: any, options: any) {
dropTarget: (node: any, options: any) => {
dropTargetOptions = options
if (isRef(node)) {
dropTargetRef = node
Expand Down
19 changes: 1 addition & 18 deletions packages/react-dnd/src/interfaces/classApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ export type ConnectedElement =
| React.ReactElement
| Element
| null

export type DragElementWrapper<Options> = <Props>(
elementOrNode: ConnectedElement,
options?: Options,
Expand All @@ -134,24 +135,11 @@ export type ConnectDragPreview = DragElementWrapper<DragPreviewOptions>
* Its methods return functions that let you assign the roles to your component's DOM nodes.
*/
export interface DragSourceConnector {
/**
* A React ref object to attach to the drag source. This replaces the dragSource() function described below.
*/
dragSourceRef: React.RefObject<any>

/**
* A React ref object to attach to the drag preview. This replaces the dragPreview() function described below.
*/
dragPreviewRef: React.RefObject<any>

/**
* Returns a function that must be used inside the component to assign the drag source role to a node. By
* returning { connectDragSource: connect.dragSource() } from your collecting function, you can mark any React
* element as the draggable node. To do that, replace any element with this.props.connectDragSource(element) inside
* the render function.
*
* @param elementOrNode
* @param options
*/
dragSource(): ConnectDragSource

Expand All @@ -174,11 +162,6 @@ export interface DragSourceConnector {
* that lets you assign the drop target role to one of your component's DOM nodes.
*/
export interface DropTargetConnector {
/**
* A React ref object to attach to the drop target. This replaces the dropTarget() function described below.
*/
dropTargetRef: React.RefObject<any>

/**
* Returns a function that must be used inside the component to assign the drop target role to a node.
* By returning { connectDropTarget: connect.dropTarget() } from your collecting function, you can mark any React element
Expand Down