Skip to content

Commit b80ea00

Browse files
committed
Add API for effectsAllowed and getDropEffect with sane defaults
1 parent e213023 commit b80ea00

File tree

7 files changed

+128
-46
lines changed

7 files changed

+128
-46
lines changed

modules/actions/DragDropActionCreators.js

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,19 @@ var DragDropDispatcher = require('../dispatcher/DragDropDispatcher'),
44
DragDropActionTypes = require('../constants/DragDropActionTypes');
55

66
var DragDropActionCreators = {
7-
startDragging(itemType, item) {
7+
startDragging(itemType, item, effectsAllowed) {
88
DragDropDispatcher.handleAction({
99
type: DragDropActionTypes.DRAG_START,
1010
itemType: itemType,
11-
item: item
11+
item: item,
12+
effectsAllowed: effectsAllowed
1213
});
1314
},
1415

15-
recordDrop() {
16+
recordDrop(dropEffect) {
1617
DragDropDispatcher.handleAction({
17-
type: DragDropActionTypes.DROP
18+
type: DragDropActionTypes.DROP,
19+
dropEffect: dropEffect
1820
});
1921
},
2022

modules/constants/DropEffects.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,7 @@
33
var DropEffects = {
44
COPY: 'copy',
55
MOVE: 'move',
6-
LINK: 'link',
7-
NONE: 'none'
6+
LINK: 'link'
87
};
98

109
module.exports = DropEffects;

modules/mixins/DragDropMixin.js

Lines changed: 60 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ var DragDropActionCreators = require('../actions/DragDropActionCreators'),
55
NativeDragDropSupport = require('../utils/NativeDragDropSupport'),
66
EnterLeaveMonitor = require('../utils/EnterLeaveMonitor'),
77
MemoizeBindMixin = require('./MemoizeBindMixin'),
8+
DropEffects = require('../constants/DropEffects'),
89
configureDataTransfer = require('../utils/configureDataTransfer'),
910
isFileDragDropEvent = require('../utils/isFileDragDropEvent'),
1011
bindAll = require('../utils/bindAll'),
@@ -13,6 +14,7 @@ var DragDropActionCreators = require('../actions/DragDropActionCreators'),
1314
defaults = require('lodash-node/modern/objects/defaults'),
1415
union = require('lodash-node/modern/arrays/union'),
1516
without = require('lodash-node/modern/arrays/without'),
17+
isArray = require('lodash-node/modern/objects/isArray'),
1618
isObject = require('lodash-node/modern/objects/isObject'),
1719
noop = require('lodash-node/modern/utilities/noop');
1820

@@ -75,6 +77,10 @@ var DefaultDropTarget = {
7577
return true;
7678
},
7779

80+
getDropEffect(allowedEffects) {
81+
return allowedEffects[0];
82+
},
83+
7884
enter: noop,
7985
over: noop,
8086
leave: noop,
@@ -90,7 +96,7 @@ var DragDropMixin = {
9096
getInitialState() {
9197
var state = {
9298
ownDraggedItemType: null,
93-
hasDragEntered: false
99+
currentDropEffect: null
94100
};
95101

96102
return merge(state, this.getStateFromDragDropStore());
@@ -137,11 +143,11 @@ var DragDropMixin = {
137143
checkDropTargetDefined(this, type);
138144

139145
var isDragging = this.getActiveDropTargetType() === type,
140-
hasDragEntered = this.state.hasDragEntered;
146+
isHovering = !!this.state.currentDropEffect;
141147

142148
return {
143149
isDragging: isDragging,
144-
isHovering: isDragging && hasDragEntered
150+
isHovering: isDragging && isHovering
145151
};
146152
},
147153

@@ -191,7 +197,9 @@ var DragDropMixin = {
191197
},
192198

193199
handleDragDropStoreChange() {
194-
this.setState(this.getStateFromDragDropStore());
200+
if (this.isMounted()) {
201+
this.setState(this.getStateFromDragDropStore());
202+
}
195203
},
196204

197205
dragSourceFor(type) {
@@ -221,12 +229,17 @@ var DragDropMixin = {
221229
);
222230

223231
var dragOptions = beginDrag(e),
224-
{ item } = dragOptions;
232+
{ item, dragPreview, dragAnchors, effectsAllowed } = dragOptions;
233+
234+
if (!effectsAllowed) {
235+
effectsAllowed = [DropEffects.MOVE];
236+
}
225237

226-
configureDataTransfer(this.getDOMNode(), e.nativeEvent, dragOptions);
238+
invariant(isArray(effectsAllowed) && effectsAllowed.length > 0, 'Expected effectsAllowed to be non-empty array');
227239
invariant(isObject(item), 'Expected return value of beginDrag to contain "item" object');
228240

229-
DragDropActionCreators.startDragging(type, item);
241+
configureDataTransfer(this.getDOMNode(), e.nativeEvent, dragPreview, dragAnchors, effectsAllowed);
242+
DragDropActionCreators.startDragging(type, item, effectsAllowed);
230243

231244
// Delay setting own state by a tick so `getDragState(type).isDragging`
232245
// doesn't return `true` yet. Otherwise browser will capture dragged state
@@ -245,7 +258,7 @@ var DragDropMixin = {
245258
NativeDragDropSupport.handleDragEnd();
246259

247260
var { endDrag } = this._dragSources[type],
248-
didDrop = DragDropStore.didDrop();
261+
recordedDropEffect = DragDropStore.getDropEffect();
249262

250263
DragDropActionCreators.endDragging();
251264

@@ -261,7 +274,7 @@ var DragDropMixin = {
261274
ownDraggedItemType: null
262275
});
263276

264-
endDrag(didDrop, e);
277+
endDrag(recordedDropEffect, e);
265278
},
266279

267280
dropTargetFor(...types) {
@@ -278,17 +291,6 @@ var DragDropMixin = {
278291
};
279292
},
280293

281-
handleDragOver(types, e) {
282-
if (!this.isAnyDropTargetActive(types)) {
283-
return;
284-
}
285-
286-
e.preventDefault();
287-
288-
var { over } = this._dropTargets[this.state.draggedItemType];
289-
over(this.state.draggedItem, e);
290-
},
291-
292294
handleDragEnter(types, e) {
293295
if (!this.isAnyDropTargetActive(types)) {
294296
return;
@@ -298,14 +300,40 @@ var DragDropMixin = {
298300
return;
299301
}
300302

303+
var { enter, getDropEffect } = this._dropTargets[this.state.draggedItemType],
304+
effectsAllowed = DragDropStore.getEffectsAllowed(),
305+
dropEffect = getDropEffect(effectsAllowed);
306+
307+
if (dropEffect && !isFileDragDropEvent(e)) {
308+
invariant(
309+
effectsAllowed.indexOf(dropEffect) > -1,
310+
'Effect %s supplied by drop target is not one of the effects allowed by drag source: %s',
311+
dropEffect,
312+
effectsAllowed.join(', ')
313+
);
314+
}
315+
301316
this.setState({
302-
hasDragEntered: true
317+
currentDropEffect: dropEffect
303318
});
304319

305-
var { enter } = this._dropTargets[this.state.draggedItemType];
306320
enter(this.state.draggedItem, e);
307321
},
308322

323+
handleDragOver(types, e) {
324+
if (!this.isAnyDropTargetActive(types)) {
325+
return;
326+
}
327+
328+
e.preventDefault();
329+
330+
var { over, getDropEffect } = this._dropTargets[this.state.draggedItemType];
331+
over(this.state.draggedItem, e);
332+
333+
// Don't use `none` because this will prevent browser from firing `dragend`
334+
NativeDragDropSupport.handleDragOver(e, this.state.currentDropEffect || 'move');
335+
},
336+
309337
handleDragLeave(types, e) {
310338
if (!this.isAnyDropTargetActive(types)) {
311339
return;
@@ -316,7 +344,7 @@ var DragDropMixin = {
316344
}
317345

318346
this.setState({
319-
hasDragEntered: false
347+
currentDropEffect: null
320348
});
321349

322350
var { leave } = this._dropTargets[this.state.draggedItemType];
@@ -331,7 +359,9 @@ var DragDropMixin = {
331359
e.preventDefault();
332360

333361
var item = this.state.draggedItem,
334-
{ acceptDrop } = this._dropTargets[this.state.draggedItemType];
362+
{ acceptDrop } = this._dropTargets[this.state.draggedItemType],
363+
{ currentDropEffect } = this.state,
364+
recordedDropEffect = DragDropStore.getDropEffect();
335365

336366
if (isFileDragDropEvent(e)) {
337367
// We don't know file list until the `drop` event,
@@ -343,13 +373,15 @@ var DragDropMixin = {
343373

344374
this._monitor.reset();
345375

376+
if (!recordedDropEffect && currentDropEffect) {
377+
DragDropActionCreators.recordDrop(currentDropEffect);
378+
}
379+
346380
this.setState({
347-
hasDragEntered: false
381+
currentDropEffect: null
348382
});
349383

350-
if (acceptDrop(item, e) !== false) {
351-
DragDropActionCreators.recordDrop();
352-
}
384+
acceptDrop(item, e, recordedDropEffect);
353385
}
354386
};
355387

modules/stores/DragDropStore.js

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,20 @@ var DragDropDispatcher = require('../dispatcher/DragDropDispatcher'),
66

77
var _draggedItem = null,
88
_draggedItemType = null,
9-
_didDrop = false;
9+
_effectsAllowed = null,
10+
_dropEffect = null;
1011

1112
var DragDropStore = createStore({
1213
isDragging() {
1314
return !!_draggedItem;
1415
},
1516

16-
didDrop() {
17-
return _didDrop;
17+
getEffectsAllowed() {
18+
return _effectsAllowed;
19+
},
20+
21+
getDropEffect() {
22+
return _dropEffect;
1823
},
1924

2025
getDraggedItem() {
@@ -31,21 +36,23 @@ DragDropDispatcher.register(function (payload) {
3136

3237
switch (action.type) {
3338
case DragDropActionTypes.DRAG_START:
34-
_didDrop = false;
39+
_dropEffect = null;
3540
_draggedItem = action.item;
3641
_draggedItemType = action.itemType;
42+
_effectsAllowed = action.effectsAllowed;
3743
DragDropStore.emitChange();
3844
break;
3945

4046
case DragDropActionTypes.DROP:
41-
_didDrop = true;
47+
_dropEffect = action.dropEffect;
4248
DragDropStore.emitChange();
4349
break;
4450

4551
case DragDropActionTypes.DRAG_END:
46-
_didDrop = false;
4752
_draggedItem = null;
4853
_draggedItemType = null;
54+
_effectsAllowed = null;
55+
_dropEffect = null;
4956
DragDropStore.emitChange();
5057
break;
5158
}

modules/utils/NativeDragDropSupport.js

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
var DragDropActionCreators = require('../actions/DragDropActionCreators'),
44
NativeDragItemTypes = require('../constants/NativeDragItemTypes'),
5+
DropEffects = require('../constants/DropEffects'),
56
EnterLeaveMonitor = require('../utils/EnterLeaveMonitor'),
67
isFileDragDropEvent = require('./isFileDragDropEvent'),
78
shallowEqual = require('react/lib/shallowEqual'),
@@ -16,7 +17,8 @@ var _monitor = new EnterLeaveMonitor(),
1617
_initialDragTargetRect,
1718
_imitateCurrentDragEnd,
1819
_dragTargetRectDidChange,
19-
_lastDragSourceCheckTimeout;
20+
_lastDragSourceCheckTimeout,
21+
_currentDropEffect;
2022

2123
function getElementRect(el) {
2224
var rect = el.getBoundingClientRect();
@@ -52,6 +54,10 @@ if (typeof window !== 'undefined') {
5254
});
5355

5456
window.addEventListener('dragover', function (e) {
57+
// At the top level of event bubbling, use previously set drop effect and reset it.
58+
e.dataTransfer.dropEffect = _currentDropEffect;
59+
_currentDropEffect = null;
60+
5561
if (!_currentDragTarget) {
5662
return;
5763
}
@@ -106,6 +112,13 @@ var NativeDragDropSupport = {
106112
_initialDragTargetRect = null;
107113
_dragTargetRectDidChange = false;
108114
_imitateCurrentDragEnd = null;
115+
},
116+
117+
handleDragOver(e, dropEffect) {
118+
// As event bubbles top-down, first specified effect will be used
119+
if (!_currentDropEffect) {
120+
_currentDropEffect = dropEffect;
121+
}
109122
}
110123
};
111124

modules/utils/configureDataTransfer.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
'use strict';
22

33
var shouldUseDragPreview = require('./shouldUseDragPreview'),
4-
getDragImageOffset = require('./getDragImageOffset');
4+
getDragImageOffset = require('./getDragImageOffset'),
5+
getBrowserEffectAllowed = require('./getBrowserEffectAllowed');
56

6-
function configureDataTransfer(containerNode, nativeEvent, dragOptions) {
7-
var { dataTransfer } = nativeEvent,
8-
{ dragPreview, effectAllowed, dragAnchors } = dragOptions;
7+
function configureDataTransfer(containerNode, nativeEvent, dragPreview, dragAnchors, effectsAllowed) {
8+
var { dataTransfer } = nativeEvent;
99

1010
try {
1111
// Firefox won't drag without setting data
@@ -19,7 +19,7 @@ function configureDataTransfer(containerNode, nativeEvent, dragOptions) {
1919
dataTransfer.setDragImage(dragPreview, dragOffset.x, dragOffset.y);
2020
}
2121

22-
dataTransfer.effectAllowed = effectAllowed;
22+
dataTransfer.effectAllowed = getBrowserEffectAllowed(effectsAllowed);
2323
}
2424

2525
module.exports = configureDataTransfer;
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
'use strict';
2+
3+
var DropEffects = require('../constants/DropEffects');
4+
5+
function getBrowserEffectAllowed(effectsAllowed) {
6+
var allowCopy = effectsAllowed.indexOf(DropEffects.COPY) > -1,
7+
allowMove = effectsAllowed.indexOf(DropEffects.MOVE) > -1,
8+
allowLink = effectsAllowed.indexOf(DropEffects.LINK) > -1;
9+
10+
if (allowCopy && allowMove && allowLink) {
11+
return 'all';
12+
} else if (allowCopy && allowMove) {
13+
return 'copyMove';
14+
} else if (allowLink && allowMove) {
15+
return 'linkMove';
16+
} else if (allowCopy && allowLink) {
17+
return 'copyLink';
18+
} else if (allowCopy) {
19+
return 'copy';
20+
} else if (allowMove) {
21+
return 'move';
22+
} else if (allowLink) {
23+
return 'link';
24+
} else {
25+
return 'none';
26+
}
27+
}
28+
29+
module.exports = getBrowserEffectAllowed;

0 commit comments

Comments
 (0)