Skip to content
Open
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
4 changes: 4 additions & 0 deletions packages/dnd-core/src/DragDropMonitorImpl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,10 @@ export default class DragDropMonitorImpl implements DragDropMonitor {
return this.store.getState().dragOperation.dropResult
}

public getCurrentDropEffect() {
return this.store.getState().dragOperation.dropEffect
}

public didDrop() {
return this.store.getState().dragOperation.didDrop
}
Expand Down
11 changes: 11 additions & 0 deletions packages/dnd-core/src/actions/dragDrop.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
BeginDragPayload,
BeginDragOptions,
SentinelAction,
DragPayload,
DropPayload,
HoverPayload,
HoverOptions,
Expand All @@ -15,6 +16,7 @@ import isObject from 'lodash/isObject'
import matchesType from '../utils/matchesType'

export const BEGIN_DRAG = 'dnd-core/BEGIN_DRAG'
export const DRAG = 'dnd-core/DRAG'
export const PUBLISH_DRAG_SOURCE = 'dnd-core/PUBLISH_DRAG_SOURCE'
export const HOVER = 'dnd-core/HOVER'
export const DROP = 'dnd-core/DROP'
Expand Down Expand Up @@ -90,6 +92,15 @@ export default function createDragDropActions<Context>(
return { type: PUBLISH_DRAG_SOURCE }
},

drag(dropEffect: string): Action<DragPayload> {
return {
type: DRAG,
payload: {
dropEffect,
},
}
},

hover(
targetIdsArg: string[],
{ clientOffset }: HoverOptions = {},
Expand Down
9 changes: 9 additions & 0 deletions packages/dnd-core/src/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ export interface DragDropMonitor {
* called outside endDrag().
*/
getDropResult(): any
/**
* Returns dropEffect for current drag operation.
*/
getCurrentDropEffect(): string | null
/**
* Returns true if some drop target has handled the drop event, false otherwise. Even if a target did not return a drop result,
* didDrop() returns true. Use it inside endDrag() to test whether any drop target has handled the drop. Returns false if called
Expand Down Expand Up @@ -154,6 +158,10 @@ export interface HoverOptions {
clientOffset?: XYCoord
}

export interface DragPayload {
dropEffect: string
}

export interface DropPayload {
dropResult: any
}
Expand All @@ -168,6 +176,7 @@ export interface SourceIdPayload {

export interface DragDropActions {
beginDrag(sourceIds: string[], options?: any): Action<BeginDragPayload>
drag(dropEffect: string): Action<DropPayload>
publishDragSource(): SentinelAction
hover(targetIds: string[], options?: any): Action<HoverPayload>
drop(options?: any): void
Expand Down
10 changes: 10 additions & 0 deletions packages/dnd-core/src/reducers/dragOperation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
PUBLISH_DRAG_SOURCE,
HOVER,
END_DRAG,
DRAG,
DROP,
} from '../actions/dragDrop'
import { REMOVE_TARGET } from '../actions/registry'
Expand All @@ -20,6 +21,7 @@ export interface State {
sourceId: string | null
targetIds: string[]
dropResult: any
dropEffect: string | null
didDrop: boolean
isSourcePublic: boolean | null
}
Expand All @@ -30,6 +32,7 @@ const initialState: State = {
sourceId: null,
targetIds: [],
dropResult: null,
dropEffect: null,
didDrop: false,
isSourcePublic: null,
}
Expand All @@ -44,6 +47,7 @@ export default function dragOperation(
targetIds: string[]
isSourcePublic: boolean
dropResult: any
dropEffect: string
}>,
) {
const { payload } = action
Expand All @@ -58,6 +62,11 @@ export default function dragOperation(
dropResult: null,
didDrop: false,
}
case DRAG:
return {
...state,
dropEffect: payload.dropEffect,
}
case PUBLISH_DRAG_SOURCE:
return {
...state,
Expand Down Expand Up @@ -90,6 +99,7 @@ export default function dragOperation(
item: null,
sourceId: null,
dropResult: null,
dropEffect: null,
didDrop: false,
isSourcePublic: null,
targetIds: [],
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import React from 'react'
import PropTypes from 'prop-types'
import {
DragSource,
ConnectDragSource,
DragSourceConnector,
DragSourceMonitor,
} from 'react-dnd'
import ItemTypes from '../Single Target/ItemTypes'
import { DragDropManager } from 'dnd-core'

const style: React.CSSProperties = {
border: '1px dashed gray',
backgroundColor: 'white',
padding: '0.5rem 1rem',
marginRight: '1.5rem',
marginBottom: '1.5rem',
float: 'left',
}

const boxSource = {
beginDrag(props: BoxProps) {
return {
name: props.name,
}
},

endDrag(props: BoxProps, monitor: DragSourceMonitor) {
const item = monitor.getItem()
const dropResult = monitor.getDropResult()

if (dropResult) {
const isCopyAction = dropResult.dropEffect === 'copy'
const actionName = isCopyAction ? 'copied' : 'moved'
alert(`You ${actionName} ${item.name} into ${dropResult.name}!`) // eslint-disable-line no-alert
}
},
}

export interface BoxProps {
name: string
isDragging?: boolean
connectDragSource?: ConnectDragSource
}

@DragSource(
ItemTypes.BOX,
boxSource,
(connect: DragSourceConnector, monitor: DragSourceMonitor) => ({
connectDragSource: connect.dragSource(),
isDragging: monitor.isDragging(),
}),
)
export default class Box extends React.Component<BoxProps> {
public static propTypes = {
connectDragSource: PropTypes.func.isRequired,
isDragging: PropTypes.bool.isRequired,
name: PropTypes.string.isRequired,
}

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

return (
connectDragSource &&
connectDragSource(<div style={{ ...style, opacity }}>{name}</div>)
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import React from 'react'
import { DragDropContextProvider } from 'react-dnd'
import HTML5Backend from 'react-dnd-html5-backend'
import Dustbin from './Dustbin'
import Box from './Box'

export default class Container extends React.Component {
public render() {
return (
<DragDropContextProvider backend={HTML5Backend}>
<div>
<div style={{ overflow: 'hidden', clear: 'both' }}>
<Dustbin allowedDropEffect="copy" />
<Dustbin allowedDropEffect="move" />
</div>
<div style={{ overflow: 'hidden', clear: 'both' }}>
<Box name="Glass" />
<Box name="Banana" />
<Box name="Paper" />
</div>
</div>
</DragDropContextProvider>
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import React from 'react'
import PropTypes from 'prop-types'
import {DropTarget, ConnectDropTarget, DropTargetMonitor} from 'react-dnd'
import ItemTypes from '../Single Target/ItemTypes'

const style: React.CSSProperties = {
height: '12rem',
width: '12rem',
marginRight: '1.5rem',
marginBottom: '1.5rem',
color: 'white',
padding: '1rem',
textAlign: 'center',
fontSize: '1rem',
lineHeight: 'normal',
float: 'left',
}

const boxTarget = {
drop({ allowedDropEffect }: DustbinProps) {
return {
name: `${allowedDropEffect} Dustbin`,
}
},
canDrop({ allowedDropEffect } : DustbinProps, monitor: any) {
return allowedDropEffect === monitor.getCurrentDropEffect()
}
}

export interface DustbinProps {
connectDropTarget?: ConnectDropTarget
canDrop?: boolean
isOver?: boolean
allowedDropEffect: string
}

@DropTarget(ItemTypes.BOX, boxTarget, (connect, monitor) => ({
connectDropTarget: connect.dropTarget(),
isOver: monitor.isOver(),
canDrop: monitor.canDrop(),
}))
export default class Dustbin extends React.Component<DustbinProps> {
public static propTypes = {
connectDropTarget: PropTypes.func.isRequired,
isOver: PropTypes.bool.isRequired,
canDrop: PropTypes.bool.isRequired,
allowedDropEffect: PropTypes.string.isRequired,
}

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

let backgroundColor = '#222'
if (isActive) {
backgroundColor = 'darkgreen'
} else if (canDrop) {
backgroundColor = 'darkkhaki'
}

return (
connectDropTarget &&
connectDropTarget(
<div style={{ ...style, backgroundColor }}>
{`You can only ${allowedDropEffect} items here`}
<br />
<br />
{isActive ? 'Release to drop' : 'Drag a box here'}
</div>,
)
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import React from 'react'
import Container from './Container'

export default class DustbinCopyOrMoveRestricted extends React.Component {
public render() {
return (
<div>
<p>
<b>
<a href="https://github.com/react-dnd/react-dnd/tree/master/packages/documentation/examples/01%20Dustbin/Copy%20or%20Move%20restricted">
Browse the Source
</a>
</b>
</p>
<p>
This example demonstrates drop targets that can be disabled/enabled
based on current drop effect, which users can switch between by
holding down or releasing the alt key as they drag.
</p>
<Container />
</div>
)
}
}
4 changes: 4 additions & 0 deletions packages/documentation/site/Constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,10 @@ export const ExamplePages = [
location: 'examples-dustbin-copy-or-move.html',
title: 'Copy or Move',
},
DUSTBIN_COPY_OR_MOVE_RESTRICTED: {
location: 'examples-dustbin-copy-or-move-restricted.html',
title: 'Copy or Move restricted',
},
DUSTBIN_MULTIPLE_TARGETS: {
location: 'examples-dustbin-multiple-targets.html',
title: 'Multiple Targets',
Expand Down
2 changes: 2 additions & 0 deletions packages/documentation/site/IndexPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ const Examples = {
DUSTBIN_IFRAME: require('../examples/01 Dustbin/Single Target in iframe')
.default,
DUSTBIN_COPY_OR_MOVE: require('../examples/01 Dustbin/Copy or Move').default,
DUSTBIN_COPY_OR_MOVE_RESTRICTED: require('../examples/01 Dustbin/Copy or Move restricted')
.default,
DUSTBIN_MULTIPLE_TARGETS: require('../examples/01 Dustbin/Multiple Targets')
.default,
DUSTBIN_STRESS_TEST: require('../examples/01 Dustbin/Stress Test').default,
Expand Down
12 changes: 12 additions & 0 deletions packages/react-dnd-html5-backend/src/HTML5Backend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ export default class HTML5Backend implements Backend {
if (!target.addEventListener) {
return
}
target.addEventListener('drag', this.handleDrag)
target.addEventListener('dragstart', this.handleTopDragStart)
target.addEventListener('dragstart', this.handleTopDragStartCapture, true)
target.addEventListener('dragend', this.handleTopDragEndCapture, true)
Expand All @@ -169,6 +170,7 @@ export default class HTML5Backend implements Backend {
if (!target.removeEventListener) {
return
}
target.removeEventListener('drag', this.handleDrag)
target.removeEventListener('dragstart', this.handleTopDragStart)
target.removeEventListener(
'dragstart',
Expand Down Expand Up @@ -483,6 +485,16 @@ export default class HTML5Backend implements Backend {
}
}

@autobind
private handleDrag(e: any) {
if (!this.monitor.isDragging()) {
return
}

this.altKeyPressed = e.altKey
this.actions.drag(this.getCurrentDropEffect())
}

@autobind
private handleTopDragEndCapture() {
if (this.clearCurrentDragSourceNode()) {
Expand Down
4 changes: 4 additions & 0 deletions packages/react-dnd/src/createSourceMonitor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,10 @@ class SourceMonitor implements DragSourceMonitor {
return this.internalMonitor.getDropResult()
}

public getCurrentDropEffect() {
return this.internalMonitor.getCurrentDropEffect()
}

public didDrop() {
return this.internalMonitor.didDrop()
}
Expand Down
4 changes: 4 additions & 0 deletions packages/react-dnd/src/createTargetMonitor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ export class TargetMonitor implements DropTargetMonitor {
return this.internalMonitor.getDropResult()
}

public getCurrentDropEffect() {
return this.internalMonitor.getCurrentDropEffect()
}

public didDrop() {
return this.internalMonitor.didDrop()
}
Expand Down
Loading