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: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,9 @@ export default class Tree extends Component {
| rowHeight | number or func | Used by react-sortable-tree. Defaults to `62`. Either a fixed row height (number) or a function that returns the height of a row given its index: `({ treeIndex: number, node: object, path: number[] or string[] }): number` |
| slideRegionSize | number | Size in px of the region near the edges that initiates scrolling on dragover. Defaults to `100`. |
| scaffoldBlockPxWidth | number | The width of the blocks containing the lines representing the structure of the tree. Defaults to `44`. |
| isVirtualized | bool | Set to false to disable virtualization. Defaults to `true`. **NOTE**: Auto-scrolling while dragging, and scrolling to the `searchFocusOffset` will be disabled. |
| isVirtualized | bool | Set to false to disable virtualization. Defaults to `true`. **NOTE**: Auto-scrolling while dragging, and scrolling to the `searchFocusOffset` will be disabled.
| isNodeDepthFixed | bool | Set to true to restrict drag and drop operation of every node to its original depth without allowing auto expand on drag for any node. Defaults to `false`.
| isParentNodeFixed | bool | Set to true to restrict drag and drop operation of every node to its parent without allowing auto expand on drag for any node. Defaults to `false`. `false`. |
| nodeContentRenderer | any | Override the default component ([`NodeRendererDefault`](https://github.com/frontend-collective/react-sortable-tree/blob/master/src/node-renderer-default.js)) for rendering nodes (but keep the scaffolding generator). This is a last resort for customization - most custom styling should be able to be solved with `generateNodeProps`, a `theme` or CSS rules. If you must use it, is best to copy the component in `node-renderer-default.js` to use as a base, and customize as needed. |
| placeholderRenderer | any | Override the default placeholder component ([`PlaceholderRendererDefault`](https://github.com/frontend-collective/react-sortable-tree/blob/master/src/placeholder-renderer-default.js)) which is displayed when the tree is empty. This is an advanced option, and in most cases should probably be solved with a `theme` or custom CSS instead. |

Expand Down
43 changes: 41 additions & 2 deletions src/react-sortable-tree.js
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,8 @@ class ReactSortableTree extends Component {
searchMatches: [],
searchFocusTreeIndex: null,
dragging: false,
parentBeforeDrag: null,
sameParent: false,

// props that need to be used in gDSFP or static functions will be stored here
instanceProps: {
Expand Down Expand Up @@ -254,6 +256,8 @@ class ReactSortableTree extends Component {
depth,
minimumTreeIndex,
}) {
if(this.props.isParentNodeFixed && !this.sameParent)
return;
const {
treeData,
treeIndex,
Expand Down Expand Up @@ -361,6 +365,19 @@ class ReactSortableTree extends Component {
getNodeKey: this.props.getNodeKey,
});

if(this.props.isParentNodeFixed) {
const addedResult = memoizedInsertNode({
treeData: draggingTreeData,
newNode: draggedNode,
depth: path.length -1,
minimumTreeIndex: draggedMinimumTreeIndex,
expandParent: true,
getNodeKey: this.props.getNodeKey,
});

const rows = this.getRows(addedResult.treeData);
this.parentBeforeDrag = rows[addedResult.treeIndex].path;
}
return {
draggingTreeData,
draggedNode,
Expand Down Expand Up @@ -399,10 +416,23 @@ class ReactSortableTree extends Component {
expandParent: true,
getNodeKey: this.props.getNodeKey,
});

const rows = this.getRows(addedResult.treeData);
const expandedParentPath = rows[addedResult.treeIndex].path;

if(this.props.isParentNodeFixed) {
if(expandedParentPath.length === this.parentBeforeDrag.length) {
for(let i=0;i<this.parentBeforeDrag.length -1;i++)
{
if(expandedParentPath[i] != this.parentBeforeDrag[i]) {
this.sameParent = false;
return;
}
}
this.sameParent = true;
} else {
this.sameParent = false;
return;
}
}
this.setState({
draggedNode,
draggedDepth,
Expand Down Expand Up @@ -813,6 +843,13 @@ ReactSortableTree.propTypes = {

// Set to false to disable virtualization.
// NOTE: Auto-scrolling while dragging, and scrolling to the `searchFocusOffset` will be disabled.

// fixes the depth of the node such that the children do not expand on drag
isNodeDepthFixed: PropTypes.bool,

// restricts node to parent
isParentNodeFixed: PropTypes.bool,

isVirtualized: PropTypes.bool,

treeNodeRenderer: PropTypes.func,
Expand Down Expand Up @@ -889,6 +926,8 @@ ReactSortableTree.defaultProps = {
generateNodeProps: null,
getNodeKey: defaultGetNodeKey,
innerStyle: {},
isNodeDepthFixed: false,
isParentNodeFixed: false,
isVirtualized: true,
maxDepth: null,
treeNodeRenderer: null,
Expand Down
26 changes: 16 additions & 10 deletions src/utils/dnd-manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@ export default class DndManager {
return this.treeRef.props.maxDepth;
}

get isNodeDepthFixed(){
return this.treeRef.props.isNodeDepthFixed;
}

getTargetDepth(dropTargetProps, monitor, component) {
let dropTargetDepth = 0;

Expand Down Expand Up @@ -126,6 +130,9 @@ export default class DndManager {
const aboveNode = rowAbove ? rowAbove.node : {};
const targetDepth = this.getTargetDepth(dropTargetProps, monitor, null);

//Cannot drop if depth is fixed and destination is not of equal depth
if(monitor.getItem().path.length -1 !== targetDepth && this.isNodeDepthFixed)
return false;
// Cannot drop if we're adding to the children of the row above and
// the row above is a function
if (
Expand All @@ -134,7 +141,6 @@ export default class DndManager {
) {
return false;
}

if (typeof this.customCanDrop === 'function') {
const { node } = monitor.getItem();
const addedResult = memoizedInsertNode({
Expand All @@ -145,7 +151,6 @@ export default class DndManager {
minimumTreeIndex: dropTargetProps.listIndex,
expandParent: true,
});

return this.customCanDrop({
node,
prevPath: monitor.getItem().path,
Expand All @@ -164,7 +169,6 @@ export default class DndManager {
const nodeDragSource = {
beginDrag: props => {
this.startDrag(props);

return {
node: props.node,
parentNode: props.parentNode,
Expand Down Expand Up @@ -235,13 +239,15 @@ export default class DndManager {
if (!needsRedraw) {
return;
}

this.dragHover({
node: draggedNode,
path: monitor.getItem().path,
minimumTreeIndex: dropTargetProps.listIndex,
depth: targetDepth,
});

if((targetDepth === monitor.getItem().path.length-1 && this.isNodeDepthFixed) || !this.isNodeDepthFixed) {
this.dragHover({
node: draggedNode,
path: monitor.getItem().path,
minimumTreeIndex: dropTargetProps.listIndex,
depth: targetDepth,
});
}
},

canDrop: this.canDrop.bind(this),
Expand Down