Skip to content

Commit d103e9f

Browse files
committed
refactor: get rid of dndWrapExternalSource api
BREAKING CHANGE: `dndWrapExternalSource` api no longer exists. You can achieve the same functionality and more with react-dnd APIs, as demonstrated in the storybook example.
1 parent 49be6b4 commit d103e9f

File tree

7 files changed

+54
-180
lines changed

7 files changed

+54
-180
lines changed

Diff for: README.md

-82
Original file line numberDiff line numberDiff line change
@@ -83,88 +83,6 @@ Notable among the available functions:
8383
Documentation for each method is only available in the code at this time. You can also refer to the tests for simple usage examples.
8484
If your hobbies happen to include writing documentation, by all means submit a pull request. It would really help out.
8585

86-
## Adding External Nodes
87-
88-
### How to wrap your own component as an external node
89-
90-
To use your own components as external nodes, you can call `dndWrapExternalSource`, exported from [`drag-and-drop-utils.js`](https://github.com/fritz-c/react-sortable-tree/blob/master/src/utils/drag-and-drop-utils.js), like in this example below, as long as you also pass the exact same [react-dnd type](http://react-dnd.github.io/react-dnd/docs-overview.html) as set for your tree component, so your custom components can become valid react-dnd [DragSources](http://react-dnd.github.io/react-dnd/docs-drag-source.html), that can be dropped in to add nodes to your own tree component.
91-
92-
```jsx
93-
import React, { Component } from 'react'
94-
import { dndWrapExternalSource } from 'react-sortable-tree';
95-
96-
const YourExternalNodeComponent = ({ node }) =>
97-
<div>{node.title}</div>;
98-
99-
export const externalNodeType = 'yourNodeType';
100-
101-
// this will wrap your external node component as a valid react-dnd DragSource
102-
export default dndWrapExternalSource(YourExternalNodeComponent, externalNodeType);
103-
```
104-
105-
__NOTE:__ You need to implement a `dropCancelled` method and an `addNewItem` method, passed as props to your external node component from your parent component, so that your tree-component can effectively respond to your external node. Check out the external node demo for an example implementation. A simple example below:
106-
107-
```jsx
108-
import React, { Component } from 'react'
109-
import {
110-
SortableTreeWithoutDndContext as SortableTree,
111-
insertNode,
112-
} from 'react-sortable-tree'
113-
import YourExternalNodeComponent, {
114-
externalNodeType,
115-
} from './YourExternalNodeComponent.js'
116-
117-
class App extends Component {
118-
constructor(props) {
119-
super(props)
120-
121-
this.state = {
122-
treeData: [
123-
{ title: 'node1' },
124-
{ title: 'node2' },
125-
],
126-
}
127-
}
128-
129-
render() {
130-
return (
131-
<div>
132-
<div style={{ height: 200 }}>
133-
<SortableTree
134-
treeData={this.state.treeData}
135-
onChange={treeData => this.setState({ treeData })}
136-
dndType={externalNodeType}
137-
/>
138-
</div>
139-
140-
<YourExternalNodeComponent
141-
node={{ title: 'I am an external node' }}
142-
addNewItem={(newItem) => {
143-
const { treeData } = insertNode({
144-
treeData: this.state.treeData,
145-
newNode: newItem.node,
146-
depth: newItem.depth,
147-
minimumTreeIndex: newItem.minimumTreeIndex,
148-
expandParent: true,
149-
getNodeKey: ({ treeIndex }) => treeIndex,
150-
});
151-
this.setState({ treeData });
152-
}}
153-
// Update the tree appearance post-drag
154-
dropCancelled={() => this.setState(state => ({
155-
treeData: state.treeData.concat(),
156-
}))}
157-
/>
158-
</div>
159-
)
160-
}
161-
}
162-
```
163-
164-
165-
In addition, the external node wrapper assumes you are using the tree component as `SortableTreeWithoutDndContext`
166-
167-
16886
## Browser Compatibility
16987

17088
| Browser | Works? |

Diff for: examples/storybooks/__snapshots__/storyshots.test.js.snap

+14-17
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ exports[`Storyshots Advanced Drag from external source 1`] = `
66
<div
77
style={
88
Object {
9-
"height": 250,
9+
"height": 300,
1010
}
1111
}
1212
>
@@ -112,7 +112,7 @@ exports[`Storyshots Advanced Drag from external source 1`] = `
112112
<span
113113
className="rowTitle"
114114
>
115-
node1
115+
Mama Rabbit
116116
</span>
117117
</div>
118118
<div
@@ -182,7 +182,7 @@ exports[`Storyshots Advanced Drag from external source 1`] = `
182182
<span
183183
className="rowTitle"
184184
>
185-
node2
185+
Papa Rabbit
186186
</span>
187187
</div>
188188
<div
@@ -199,22 +199,19 @@ exports[`Storyshots Advanced Drag from external source 1`] = `
199199
</div>
200200
</div>
201201
</div>
202-
<div>
203-
<div
204-
style={
205-
Object {
206-
"background": "blue",
207-
"border": "solid black 1px",
208-
"color": "white",
209-
"display": "inline-block",
210-
"padding": "3px 5px",
211-
}
202+
<div
203+
style={
204+
Object {
205+
"background": "blue",
206+
"color": "white",
207+
"display": "inline-block",
208+
"padding": "3px 5px",
212209
}
213-
>
214-
External node
215-
</div>
210+
}
211+
>
212+
Baby Rabbit
216213
</div>
217-
drag this
214+
drag this
218215
</div>
219216
<br />
220217
<a

Diff for: examples/storybooks/external-node.js

+33-16
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,31 @@
11
import React, { Component } from 'react';
2-
import { DragDropContext } from 'react-dnd';
2+
import { DragDropContext, DragSource } from 'react-dnd';
33
import HTML5Backend from 'react-dnd-html5-backend';
4-
import {
5-
SortableTreeWithoutDndContext as SortableTree,
6-
dndWrapExternalSource,
7-
} from '../../src';
4+
import { SortableTreeWithoutDndContext as SortableTree } from '../../src';
85

6+
// -------------------------
7+
// Create an drag source component that can be dragged into the tree
8+
// https://react-dnd.github.io/react-dnd/docs-drag-source.html
9+
// -------------------------
10+
// This type must be assigned to the tree via the `dndType` prop as well
911
const externalNodeType = 'yourNodeType';
10-
11-
// this will wrap your external node component as a valid react-dnd DragSource
12-
const YourExternalNodeComponent = dndWrapExternalSource(
13-
({ node }) =>
12+
const externalNodeSpec = {
13+
// This needs to return an object with a property `node` in it.
14+
// Object rest spread is recommended to avoid side effects of
15+
// referencing the same object in different trees.
16+
beginDrag: componentProps => ({ node: { ...componentProps.node } }),
17+
};
18+
const externalNodeCollect = (connect /* , monitor */) => ({
19+
connectDragSource: connect.dragSource(),
20+
// Add props via react-dnd APIs to enable more visual
21+
// customization of your component
22+
// isDragging: monitor.isDragging(),
23+
// didDrop: monitor.didDrop(),
24+
});
25+
const externalNodeBaseComponent = ({ connectDragSource, node }) =>
26+
connectDragSource(
1427
<div
1528
style={{
16-
border: 'solid black 1px',
1729
display: 'inline-block',
1830
padding: '3px 5px',
1931
background: 'blue',
@@ -22,35 +34,40 @@ const YourExternalNodeComponent = dndWrapExternalSource(
2234
>
2335
{node.title}
2436
</div>,
25-
externalNodeType
26-
);
37+
{ dropEffect: 'copy' }
38+
);
39+
const YourExternalNodeComponent = DragSource(
40+
externalNodeType,
41+
externalNodeSpec,
42+
externalNodeCollect
43+
)(externalNodeBaseComponent);
2744

2845
class App extends Component {
2946
constructor(props) {
3047
super(props);
3148

3249
this.state = {
33-
treeData: [{ title: 'node1' }, { title: 'node2' }],
50+
treeData: [{ title: 'Mama Rabbit' }, { title: 'Papa Rabbit' }],
3451
};
3552
}
3653

3754
render() {
3855
return (
3956
<div>
40-
<div style={{ height: 250 }}>
57+
<div style={{ height: 300 }}>
4158
<SortableTree
4259
treeData={this.state.treeData}
4360
onChange={treeData => this.setState({ treeData })}
4461
dndType={externalNodeType}
4562
/>
4663
</div>
4764
<YourExternalNodeComponent
48-
node={{ title: 'External node' }}
65+
node={{ title: 'Baby Rabbit' }}
4966
addNewItem={() => {}}
5067
// Update the tree appearance post-drag
5168
dropCancelled={() => {}}
5269
/>
53-
drag this
70+
drag this
5471
</div>
5572
);
5673
}

Diff for: examples/storybooks/tree-to-tree.js

+3-6
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,6 @@
11
import React, { Component } from 'react';
2-
// import { DragDropContext } from 'react-dnd';
3-
// import HTML5Backend from 'react-dnd-html5-backend';
42
import SortableTree from '../../src';
5-
// SortableTreeWithoutDndContext as SortableTree,
6-
// insertNode,
7-
// dndWrapExternalSource,
83

9-
const externalNodeType = 'yourNodeType';
104

115
class App extends Component {
126
constructor(props) {
@@ -22,6 +16,9 @@ class App extends Component {
2216
}
2317

2418
render() {
19+
// Both trees need to share this same node type in their
20+
// `dndType` prop
21+
const externalNodeType = 'yourNodeType';
2522
return (
2623
<div>
2724
<div

Diff for: src/index.js

-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import SortableTree, {
44

55
export * from './utils/default-handlers';
66
export * from './utils/tree-data-utils';
7-
export { dndWrapExternalSource } from './utils/drag-and-drop-utils';
87
export default SortableTree;
98

109
// Export the tree component without the react-dnd DragDropContext,

Diff for: src/react-sortable-tree.js

+2-8
Original file line numberDiff line numberDiff line change
@@ -142,13 +142,7 @@ class ReactSortableTree extends Component {
142142
// it means that the drag was canceled or the dragSource dropped
143143
// elsewhere, and we should reset the state of this tree
144144
if (!monitor.isDragging() && this.state.draggingTreeData) {
145-
this.setState({
146-
draggingTreeData: null,
147-
swapFrom: null,
148-
swapLength: null,
149-
swapDepth: null,
150-
rows: this.getRows(this.props.treeData),
151-
});
145+
this.endDrag();
152146
}
153147
}
154148

@@ -328,8 +322,8 @@ class ReactSortableTree extends Component {
328322
rows: this.getRows(this.props.treeData),
329323
});
330324
} else if (dropResult.treeId !== this.treeId) {
331-
const { node, path: prevPath, treeIndex: prevTreeIndex } = dropResult;
332325
// The node was dropped in an external drop target or tree
326+
const { node, path: prevPath, treeIndex: prevTreeIndex } = dropResult;
333327
const treeData = this.state.draggingTreeData || this.props.treeData;
334328
this.props.onChange(treeData);
335329

Diff for: src/utils/drag-and-drop-utils.js

+2-50
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@ import {
44
DropTarget as dropTarget,
55
} from 'react-dnd';
66
import HTML5Backend from 'react-dnd-html5-backend';
7-
import React from 'react';
8-
import PropTypes from 'prop-types';
97
import { findDOMNode } from 'react-dom';
108
import { getDepth } from './tree-data-utils';
119
import { memoizedInsertNode } from './memoized-tree-data-utils';
@@ -35,23 +33,8 @@ const nodeDragSource = {
3533
},
3634
};
3735

38-
const externalSource = {
39-
beginDrag(props) {
40-
return {
41-
node: {
42-
...props.node,
43-
},
44-
path: [],
45-
treeId: null,
46-
parentNode: null,
47-
treeIndex: -1, // Use -1 to indicate external node
48-
};
49-
},
50-
};
51-
5236
function getTargetDepth(dropTargetProps, monitor, component) {
5337
let dropTargetDepth = 0;
54-
const draggedItem = monitor.getItem();
5538

5639
const rowAbove = dropTargetProps.getPrevRow();
5740
if (rowAbove) {
@@ -84,9 +67,10 @@ function getTargetDepth(dropTargetProps, monitor, component) {
8467
);
8568
}
8669

70+
const dragSourcePath = monitor.getItem().path || [];
8771
let targetDepth = Math.min(
8872
dropTargetDepth,
89-
Math.max(0, draggedItem.path.length + blocksOffset - 1)
73+
Math.max(0, dragSourcePath.length + blocksOffset - 1)
9074
);
9175

9276
// If a maxDepth is defined, constrain the target depth
@@ -190,13 +174,6 @@ const nodeDropTarget = {
190174
canDrop,
191175
};
192176

193-
function externalSourcePropInjection(connect, monitor) {
194-
return {
195-
connectDragSource: connect.dragSource(),
196-
isDragging: monitor.isDragging(),
197-
};
198-
}
199-
200177
function nodeDragSourcePropInjection(connect, monitor) {
201178
return {
202179
connectDragSource: connect.dragSource(),
@@ -216,31 +193,6 @@ function nodeDropTargetPropInjection(connect, monitor) {
216193
};
217194
}
218195

219-
export function dndWrapExternalSource(UserComponent, type) {
220-
class DndWrapExternalSource extends React.Component {
221-
render() {
222-
return this.props.connectDragSource(
223-
<div>
224-
<UserComponent {...this.props} />
225-
</div>,
226-
{ dropEffect: 'copy' }
227-
);
228-
}
229-
}
230-
231-
DndWrapExternalSource.propTypes = {
232-
connectDragSource: PropTypes.func.isRequired,
233-
/* eslint-disable react/no-unused-prop-types */
234-
// The following are used within the react-dnd lifecycle hooks
235-
node: PropTypes.shape({}).isRequired,
236-
/* eslint-enable react/no-unused-prop-types */
237-
};
238-
239-
return dragSource(type, externalSource, externalSourcePropInjection)(
240-
DndWrapExternalSource
241-
);
242-
}
243-
244196
export function dndWrapSource(el, type) {
245197
return dragSource(type, nodeDragSource, nodeDragSourcePropInjection)(el);
246198
}

0 commit comments

Comments
 (0)