Skip to content

Fix issue with drag n drop over lazy loaded nodes #227

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

Closed
wants to merge 3 commits into from
Closed
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
18 changes: 18 additions & 0 deletions examples/basic-example/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,11 @@ class App extends Component {
},
],
},
{
title: 'Children will be added dynamically',
subtitle: 'If you specify children with a function',
children: params => this.generateNewChildren(params),
},
],
};

Expand Down Expand Up @@ -171,6 +176,19 @@ class App extends Component {
this.expand(false);
}

generateNewChildren(params) {
setTimeout(() => {
const childNodes = Array(5)
.fill()
.map(() => ({
title: 'Dynamic child node',
subtitle: `parent path: ${params.path.join('->')}`,
children: x => this.generateNewChildren(x),
}));
params.done(childNodes);
}, 1000);
}

render() {
const projectName = 'React Sortable Tree';
const authorName = 'Chris Fritz';
Expand Down
32 changes: 21 additions & 11 deletions src/react-sortable-tree.js
Original file line number Diff line number Diff line change
Expand Up @@ -202,15 +202,19 @@ class ReactSortableTree extends Component {
depth,
minimumTreeIndex,
}) {
const { treeData, treeIndex, path } = insertNode({
const insertedNode = insertNode({
treeData: this.state.draggingTreeData,
newNode: node,
depth,
minimumTreeIndex,
expandParent: true,
getNodeKey: this.props.getNodeKey,
});
if (!insertedNode) {
return;
}

const { treeData, treeIndex, path } = insertedNode;
this.props.onChange(treeData);

this.props.onMoveNode({
Expand Down Expand Up @@ -339,6 +343,9 @@ class ReactSortableTree extends Component {
expandParent: true,
getNodeKey: this.props.getNodeKey,
});
if (!addedResult) {
return;
}

const rows = this.getRows(addedResult.treeData);
const expandedParentPath = rows[addedResult.treeIndex].path;
Expand Down Expand Up @@ -553,16 +560,19 @@ class ReactSortableTree extends Component {
expandParent: true,
getNodeKey,
});

const swapTo = draggedMinimumTreeIndex;
swapFrom = addedResult.treeIndex;
swapLength = 1 + memoizedGetDescendantCount({ node: draggedNode });
rows = slideRows(
this.getRows(addedResult.treeData),
swapFrom,
swapTo,
swapLength
);
if (addedResult) {
const swapTo = draggedMinimumTreeIndex;
swapFrom = addedResult.treeIndex;
swapLength = 1 + memoizedGetDescendantCount({ node: draggedNode });
rows = slideRows(
this.getRows(addedResult.treeData),
swapFrom,
swapTo,
swapLength
);
} else {
rows = this.getRows(treeData);
}
} else {
rows = this.getRows(treeData);
}
Expand Down
3 changes: 3 additions & 0 deletions src/utils/dnd-manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,9 @@ export default class DndManager {
minimumTreeIndex: dropTargetProps.listIndex,
expandParent: true,
});
if (!addedResult) {
return false;
}

return this.customCanDrop({
node,
Expand Down
96 changes: 53 additions & 43 deletions src/utils/tree-data-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,21 +36,24 @@ function getNodeDataAtTreeIndexOrNextIndex({
let childIndex = currentIndex + 1;
const childCount = node.children.length;
for (let i = 0; i < childCount; i += 1) {
const result = getNodeDataAtTreeIndexOrNextIndex({
ignoreCollapsed,
getNodeKey,
targetIndex,
node: node.children[i],
currentIndex: childIndex,
lowerSiblingCounts: [...lowerSiblingCounts, childCount - i - 1],
path: selfPath,
});
const childNode = node.children[i];
if (childNode) {
const result = getNodeDataAtTreeIndexOrNextIndex({
ignoreCollapsed,
getNodeKey,
targetIndex,
node: node.children[i],
currentIndex: childIndex,
lowerSiblingCounts: [...lowerSiblingCounts, childCount - i - 1],
path: selfPath,
});

if (result.node) {
return result;
}
if (result.node) {
return result;
}

childIndex = result.nextIndex;
childIndex = result.nextIndex;
}
}

// If the target node is not found, return the farthest traversed index
Expand Down Expand Up @@ -135,20 +138,23 @@ function walkDescendants({
const childCount = node.children.length;
if (typeof node.children !== 'function') {
for (let i = 0; i < childCount; i += 1) {
childIndex = walkDescendants({
callback,
getNodeKey,
ignoreCollapsed,
node: node.children[i],
parentNode: isPseudoRoot ? null : node,
currentIndex: childIndex + 1,
lowerSiblingCounts: [...lowerSiblingCounts, childCount - i - 1],
path: selfPath,
});
const childNode = node.children[i];
if (childNode) {
childIndex = walkDescendants({
callback,
getNodeKey,
ignoreCollapsed,
node: childNode,
parentNode: isPseudoRoot ? null : node,
currentIndex: childIndex + 1,
lowerSiblingCounts: [...lowerSiblingCounts, childCount - i - 1],
path: selfPath,
});

// Cut walk short if the callback returned false
if (childIndex === false) {
return false;
// Cut walk short if the callback returned false
if (childIndex === false) {
return false;
}
}
}
}
Expand Down Expand Up @@ -710,24 +716,24 @@ function addNodeAtDepthAndIndex({
(isLastChild && !(node.children && node.children.length))
) {
if (typeof node.children === 'function') {
throw new Error('Cannot add to children defined by a function');
} else {
const extraNodeProps = expandParent ? { expanded: true } : {};
const nextNode = {
...node,
// Cannot add to children defined by a function
return undefined;
}
const extraNodeProps = expandParent ? { expanded: true } : {};
const nextNode = {
...node,

...extraNodeProps,
children: node.children ? [newNode, ...node.children] : [newNode],
};
...extraNodeProps,
children: node.children ? [newNode, ...node.children] : [newNode],
};

return {
node: nextNode,
nextIndex: currentIndex + 2,
insertedTreeIndex: currentIndex + 1,
parentPath: selfPath(nextNode),
parentNode: isPseudoRoot ? null : nextNode,
};
}
return {
node: nextNode,
nextIndex: currentIndex + 2,
insertedTreeIndex: currentIndex + 1,
parentPath: selfPath(nextNode),
parentNode: isPseudoRoot ? null : nextNode,
};
}

// If this is the target depth for the insertion,
Expand Down Expand Up @@ -828,6 +834,9 @@ function addNodeAtDepthAndIndex({
getNodeKey,
path: [], // Cannot determine the parent path until the children have been processed
});
if (!mapResult) {
return undefined;
}

if ('insertedTreeIndex' in mapResult) {
({
Expand Down Expand Up @@ -908,7 +917,8 @@ export function insertNode({
});

if (!('insertedTreeIndex' in insertResult)) {
throw new Error('No suitable position found to insert.');
// No suitable position found to insert
return undefined;
}

const treeIndex = insertResult.insertedTreeIndex;
Expand Down