Skip to content

Commit

Permalink
chore: get basic swapping working, but preview still weird
Browse files Browse the repository at this point in the history
  • Loading branch information
fritz-c committed Dec 9, 2017
1 parent c888203 commit 9c3524f
Show file tree
Hide file tree
Showing 3 changed files with 174 additions and 96 deletions.
161 changes: 102 additions & 59 deletions src/react-sortable-tree.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,16 @@ import TreePlaceholder from './tree-placeholder';
import PlaceholderRendererDefault from './placeholder-renderer-default';
import {
walk,
getFlatDataFromTree,
changeNodeAtPath,
removeNodeAtPath,
removeNode,
insertNode,
getDescendantCount,
find,
} from './utils/tree-data-utils';
import { memoizedInsertNode } from './utils/memoized-tree-data-utils';
import {
memoizedInsertNode,
memoizedGetFlatDataFromTree,
memoizedGetDescendantCount,
} from './utils/memoized-tree-data-utils';
import { slideRows } from './utils/generic-utils';
import {
defaultGetNodeKey,
Expand Down Expand Up @@ -75,7 +77,6 @@ class ReactSortableTree extends Component {
treeNodeRenderer,
isVirtualized,
slideRegionSize,
treeData,
} = mergeTheme(props);

this.dndManager = new DndManager(this);
Expand All @@ -99,10 +100,9 @@ class ReactSortableTree extends Component {

this.state = {
draggingTreeData: null,
swapFrom: null,
swapLength: null,
swapDepth: null,
rows: this.getRows(treeData),
draggedNode: null,
draggedMinimumTreeIndex: null,
draggedDepth: null,
searchMatches: [],
searchFocusTreeIndex: null,
};
Expand Down Expand Up @@ -141,13 +141,12 @@ class ReactSortableTree extends Component {
this.search(nextProps, false, false);
}

// Calculate the rows to be shown from the new tree data
// Reset the drag state
this.setState({
draggingTreeData: null,
swapFrom: null,
swapLength: null,
swapDepth: null,
rows: this.getRows(nextProps.treeData),
draggedNode: null,
draggedMinimumTreeIndex: null,
draggedDepth: null,
});
} else if (!isEqual(this.props.searchQuery, nextProps.searchQuery)) {
this.search(nextProps);
Expand All @@ -161,7 +160,7 @@ class ReactSortableTree extends Component {
}

getRows(treeData) {
return getFlatDataFromTree({
return memoizedGetFlatDataFromTree({
ignoreCollapsed: true,
getNodeKey: this.props.getNodeKey,
treeData,
Expand Down Expand Up @@ -295,42 +294,51 @@ class ReactSortableTree extends Component {
}

startDrag({ path }) {
const draggingTreeData = removeNodeAtPath({
treeData: this.props.treeData,
path,
getNodeKey: this.props.getNodeKey,
});
this.setState(() => {
const {
treeData: draggingTreeData,
node: draggedNode,
treeIndex: draggedMinimumTreeIndex,
} = removeNode({
treeData: this.props.treeData,
path,
getNodeKey: this.props.getNodeKey,
});

this.setState({
draggingTreeData,
return {
draggingTreeData,
draggedNode,
draggedDepth: path.length - 1,
draggedMinimumTreeIndex,
};
});
}

dragHover({ node: draggedNode, depth, minimumTreeIndex }) {
dragHover({
node: draggedNode,
depth: draggedDepth,
minimumTreeIndex: draggedMinimumTreeIndex,
}) {
// Fall back to the tree data if something is being dragged in from
// an external element
const draggingTreeData = this.state.draggingTreeData || this.props.treeData;

const addedResult = memoizedInsertNode({
treeData: draggingTreeData,
newNode: draggedNode,
depth,
minimumTreeIndex,
depth: draggedDepth,
minimumTreeIndex: draggedMinimumTreeIndex,
expandParent: true,
getNodeKey: this.props.getNodeKey,
});

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

const swapFrom = addedResult.treeIndex;
const swapTo = minimumTreeIndex;
const swapLength = 1 + getDescendantCount({ node: draggedNode });
this.setState({
rows: slideRows(rows, swapFrom, swapTo, swapLength),
swapFrom,
swapLength,
swapDepth: depth,
draggedNode,
draggedDepth,
draggedMinimumTreeIndex,
draggingTreeData: changeNodeAtPath({
treeData: draggingTreeData,
path: expandedParentPath.slice(0, -1),
Expand All @@ -344,10 +352,9 @@ class ReactSortableTree extends Component {
const resetTree = () =>
this.setState({
draggingTreeData: null,
swapFrom: null,
swapLength: null,
swapDepth: null,
rows: this.getRows(this.props.treeData),
draggedNode: null,
draggedMinimumTreeIndex: null,
draggedDepth: null,
});

// Drop was cancelled
Expand Down Expand Up @@ -443,10 +450,7 @@ class ReactSortableTree extends Component {

renderRow(
{ node, parentNode, path, lowerSiblingCounts, treeIndex },
listIndex,
style,
getPrevRow,
matchKeys
{ listIndex, style, getPrevRow, matchKeys, swapFrom, swapDepth, swapLength }
) {
const {
canDrag,
Expand Down Expand Up @@ -490,9 +494,9 @@ class ReactSortableTree extends Component {
listIndex={listIndex}
getPrevRow={getPrevRow}
lowerSiblingCounts={lowerSiblingCounts}
swapFrom={this.state.swapFrom}
swapLength={this.state.swapLength}
swapDepth={this.state.swapDepth}
swapFrom={swapFrom}
swapLength={swapLength}
swapDepth={swapDepth}
{...sharedProps}
>
<NodeContentRenderer
Expand All @@ -517,8 +521,43 @@ class ReactSortableTree extends Component {
isVirtualized,
placeholderRenderer,
reactVirtualizedListProps,
getNodeKey,
} = mergeTheme(this.props);
const { rows, searchMatches, searchFocusTreeIndex } = this.state;
const {
searchMatches,
searchFocusTreeIndex,
draggedNode,
draggedDepth,
draggedMinimumTreeIndex,
} = this.state;

const treeData = this.state.draggingTreeData || this.props.treeData;

let rows;
let swapFrom = null;
let swapLength = null;
if (draggedNode && draggedMinimumTreeIndex !== null) {
const addedResult = memoizedInsertNode({
treeData,
newNode: draggedNode,
depth: draggedDepth,
minimumTreeIndex: draggedMinimumTreeIndex,
expandParent: true,
getNodeKey,
});

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);
}

// Get indices for rows that match the search conditions
const matchKeys = {};
Expand Down Expand Up @@ -569,13 +608,15 @@ class ReactSortableTree extends Component {
}
rowHeight={rowHeight}
rowRenderer={({ index, style: rowStyle }) =>
this.renderRow(
rows[index],
index,
rowStyle,
() => rows[index - 1] || null,
matchKeys
)
this.renderRow(rows[index], {
listIndex: index,
style: rowStyle,
getPrevRow: () => rows[index - 1] || null,
matchKeys,
swapFrom,
swapDepth: draggedDepth,
swapLength,
})
}
{...reactVirtualizedListProps}
/>
Expand All @@ -585,18 +626,20 @@ class ReactSortableTree extends Component {
} else {
// Render list without react-virtualized
list = rows.map((row, index) =>
this.renderRow(
row,
index,
{
this.renderRow(row, {
listIndex: index,
style: {
height:
typeof rowHeight !== 'function'
? rowHeight
: rowHeight({ ...row, index }),
: rowHeight({ index }),
},
() => rows[index - 1] || null,
matchKeys
)
getPrevRow: () => rows[index - 1] || null,
matchKeys,
swapFrom,
swapDepth: draggedDepth,
swapLength,
})
);
}

Expand Down
67 changes: 30 additions & 37 deletions src/utils/memoized-tree-data-utils.js
Original file line number Diff line number Diff line change
@@ -1,41 +1,34 @@
/* eslint-disable import/prefer-default-export */
import { insertNode } from './tree-data-utils';
import {
insertNode,
getDescendantCount,
getFlatDataFromTree,
} from './tree-data-utils';

let memoizedInsertArgsArray = [];
let memoizedInsertKeysArray = [];
let memoizedInsertResult = null;
const memoize = f => {
let savedArgsArray = [];
let savedKeysArray = [];
let savedResult = null;

/**
* Insert a node into the tree at the given depth, after the minimum index
*
* @param {!Object[]} treeData - Tree data
* @param {!number} depth - The depth to insert the node at (the first level of the array being depth 0)
* @param {!number} minimumTreeIndex - The lowest possible treeIndex to insert the node at
* @param {!Object} newNode - The node to insert into the tree
* @param {boolean=} ignoreCollapsed - Ignore children of nodes without `expanded` set to `true`
* @param {boolean=} expandParent - If true, expands the parent of the inserted node
* @param {!function} getNodeKey - Function to get the key from the nodeData and tree index
*
* @return {Object} result
* @return {Object[]} result.treeData - The tree data with the node added
* @return {number} result.treeIndex - The tree index at which the node was inserted
* @return {number[]|string[]} result.path - Array of keys leading to the node location after insertion
*/
export function memoizedInsertNode(args) {
const keysArray = Object.keys(args).sort();
const argsArray = keysArray.map(key => args[key]);
return args => {
const keysArray = Object.keys(args).sort();
const argsArray = keysArray.map(key => args[key]);

// If the arguments for the last insert operation are different than this time,
// recalculate the result
if (
argsArray.length !== memoizedInsertArgsArray.length ||
argsArray.some((arg, index) => arg !== memoizedInsertArgsArray[index]) ||
keysArray.some((key, index) => key !== memoizedInsertKeysArray[index])
) {
memoizedInsertArgsArray = argsArray;
memoizedInsertKeysArray = keysArray;
memoizedInsertResult = insertNode(args);
}
// If the arguments for the last insert operation are different than this time,
// recalculate the result
if (
argsArray.length !== savedArgsArray.length ||
argsArray.some((arg, index) => arg !== savedArgsArray[index]) ||
keysArray.some((key, index) => key !== savedKeysArray[index])
) {
savedArgsArray = argsArray;
savedKeysArray = keysArray;
savedResult = f(args);
}

return memoizedInsertResult;
}
return savedResult;
};
};

export const memoizedInsertNode = memoize(insertNode);
export const memoizedGetFlatDataFromTree = memoize(getFlatDataFromTree);
export const memoizedGetDescendantCount = memoize(getDescendantCount);
42 changes: 42 additions & 0 deletions src/utils/tree-data-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -517,6 +517,48 @@ export function removeNodeAtPath({
});
}

/**
* Removes the node at the specified path and returns the resulting treeData.
*
* @param {!Object[]} treeData
* @param {number[]|string[]} path - Array of keys leading up to node to be deleted
* @param {!function} getNodeKey - Function to get the key from the nodeData and tree index
* @param {boolean=} ignoreCollapsed - Ignore children of nodes without `expanded` set to `true`
*
* @return {Object} result
* @return {Object[]} result.treeData - The tree data with the node removed
* @return {Object} result.node - The node that was removed
* @return {number} result.treeIndex - The previous treeIndex of the removed node
*/
export function removeNode({
treeData,
path,
getNodeKey,
ignoreCollapsed = true,
}) {
let removedNode = null;
let removedTreeIndex = null;
const nextTreeData = changeNodeAtPath({
treeData,
path,
getNodeKey,
ignoreCollapsed,
newNode: ({ node, treeIndex }) => {
// Store the target node and delete it from the tree
removedNode = node;
removedTreeIndex = treeIndex;

return null;
},
});

return {
treeData: nextTreeData,
node: removedNode,
treeIndex: removedTreeIndex,
};
}

/**
* Gets the node at the specified path
*
Expand Down

0 comments on commit 9c3524f

Please sign in to comment.