Skip to content

Commit 9c3524f

Browse files
committed
chore: get basic swapping working, but preview still weird
1 parent c888203 commit 9c3524f

File tree

3 files changed

+174
-96
lines changed

3 files changed

+174
-96
lines changed

src/react-sortable-tree.js

+102-59
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,16 @@ import TreePlaceholder from './tree-placeholder';
1313
import PlaceholderRendererDefault from './placeholder-renderer-default';
1414
import {
1515
walk,
16-
getFlatDataFromTree,
1716
changeNodeAtPath,
18-
removeNodeAtPath,
17+
removeNode,
1918
insertNode,
20-
getDescendantCount,
2119
find,
2220
} from './utils/tree-data-utils';
23-
import { memoizedInsertNode } from './utils/memoized-tree-data-utils';
21+
import {
22+
memoizedInsertNode,
23+
memoizedGetFlatDataFromTree,
24+
memoizedGetDescendantCount,
25+
} from './utils/memoized-tree-data-utils';
2426
import { slideRows } from './utils/generic-utils';
2527
import {
2628
defaultGetNodeKey,
@@ -75,7 +77,6 @@ class ReactSortableTree extends Component {
7577
treeNodeRenderer,
7678
isVirtualized,
7779
slideRegionSize,
78-
treeData,
7980
} = mergeTheme(props);
8081

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

100101
this.state = {
101102
draggingTreeData: null,
102-
swapFrom: null,
103-
swapLength: null,
104-
swapDepth: null,
105-
rows: this.getRows(treeData),
103+
draggedNode: null,
104+
draggedMinimumTreeIndex: null,
105+
draggedDepth: null,
106106
searchMatches: [],
107107
searchFocusTreeIndex: null,
108108
};
@@ -141,13 +141,12 @@ class ReactSortableTree extends Component {
141141
this.search(nextProps, false, false);
142142
}
143143

144-
// Calculate the rows to be shown from the new tree data
144+
// Reset the drag state
145145
this.setState({
146146
draggingTreeData: null,
147-
swapFrom: null,
148-
swapLength: null,
149-
swapDepth: null,
150-
rows: this.getRows(nextProps.treeData),
147+
draggedNode: null,
148+
draggedMinimumTreeIndex: null,
149+
draggedDepth: null,
151150
});
152151
} else if (!isEqual(this.props.searchQuery, nextProps.searchQuery)) {
153152
this.search(nextProps);
@@ -161,7 +160,7 @@ class ReactSortableTree extends Component {
161160
}
162161

163162
getRows(treeData) {
164-
return getFlatDataFromTree({
163+
return memoizedGetFlatDataFromTree({
165164
ignoreCollapsed: true,
166165
getNodeKey: this.props.getNodeKey,
167166
treeData,
@@ -295,42 +294,51 @@ class ReactSortableTree extends Component {
295294
}
296295

297296
startDrag({ path }) {
298-
const draggingTreeData = removeNodeAtPath({
299-
treeData: this.props.treeData,
300-
path,
301-
getNodeKey: this.props.getNodeKey,
302-
});
297+
this.setState(() => {
298+
const {
299+
treeData: draggingTreeData,
300+
node: draggedNode,
301+
treeIndex: draggedMinimumTreeIndex,
302+
} = removeNode({
303+
treeData: this.props.treeData,
304+
path,
305+
getNodeKey: this.props.getNodeKey,
306+
});
303307

304-
this.setState({
305-
draggingTreeData,
308+
return {
309+
draggingTreeData,
310+
draggedNode,
311+
draggedDepth: path.length - 1,
312+
draggedMinimumTreeIndex,
313+
};
306314
});
307315
}
308316

309-
dragHover({ node: draggedNode, depth, minimumTreeIndex }) {
317+
dragHover({
318+
node: draggedNode,
319+
depth: draggedDepth,
320+
minimumTreeIndex: draggedMinimumTreeIndex,
321+
}) {
310322
// Fall back to the tree data if something is being dragged in from
311323
// an external element
312324
const draggingTreeData = this.state.draggingTreeData || this.props.treeData;
313325

314326
const addedResult = memoizedInsertNode({
315327
treeData: draggingTreeData,
316328
newNode: draggedNode,
317-
depth,
318-
minimumTreeIndex,
329+
depth: draggedDepth,
330+
minimumTreeIndex: draggedMinimumTreeIndex,
319331
expandParent: true,
320332
getNodeKey: this.props.getNodeKey,
321333
});
322334

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

326-
const swapFrom = addedResult.treeIndex;
327-
const swapTo = minimumTreeIndex;
328-
const swapLength = 1 + getDescendantCount({ node: draggedNode });
329338
this.setState({
330-
rows: slideRows(rows, swapFrom, swapTo, swapLength),
331-
swapFrom,
332-
swapLength,
333-
swapDepth: depth,
339+
draggedNode,
340+
draggedDepth,
341+
draggedMinimumTreeIndex,
334342
draggingTreeData: changeNodeAtPath({
335343
treeData: draggingTreeData,
336344
path: expandedParentPath.slice(0, -1),
@@ -344,10 +352,9 @@ class ReactSortableTree extends Component {
344352
const resetTree = () =>
345353
this.setState({
346354
draggingTreeData: null,
347-
swapFrom: null,
348-
swapLength: null,
349-
swapDepth: null,
350-
rows: this.getRows(this.props.treeData),
355+
draggedNode: null,
356+
draggedMinimumTreeIndex: null,
357+
draggedDepth: null,
351358
});
352359

353360
// Drop was cancelled
@@ -443,10 +450,7 @@ class ReactSortableTree extends Component {
443450

444451
renderRow(
445452
{ node, parentNode, path, lowerSiblingCounts, treeIndex },
446-
listIndex,
447-
style,
448-
getPrevRow,
449-
matchKeys
453+
{ listIndex, style, getPrevRow, matchKeys, swapFrom, swapDepth, swapLength }
450454
) {
451455
const {
452456
canDrag,
@@ -490,9 +494,9 @@ class ReactSortableTree extends Component {
490494
listIndex={listIndex}
491495
getPrevRow={getPrevRow}
492496
lowerSiblingCounts={lowerSiblingCounts}
493-
swapFrom={this.state.swapFrom}
494-
swapLength={this.state.swapLength}
495-
swapDepth={this.state.swapDepth}
497+
swapFrom={swapFrom}
498+
swapLength={swapLength}
499+
swapDepth={swapDepth}
496500
{...sharedProps}
497501
>
498502
<NodeContentRenderer
@@ -517,8 +521,43 @@ class ReactSortableTree extends Component {
517521
isVirtualized,
518522
placeholderRenderer,
519523
reactVirtualizedListProps,
524+
getNodeKey,
520525
} = mergeTheme(this.props);
521-
const { rows, searchMatches, searchFocusTreeIndex } = this.state;
526+
const {
527+
searchMatches,
528+
searchFocusTreeIndex,
529+
draggedNode,
530+
draggedDepth,
531+
draggedMinimumTreeIndex,
532+
} = this.state;
533+
534+
const treeData = this.state.draggingTreeData || this.props.treeData;
535+
536+
let rows;
537+
let swapFrom = null;
538+
let swapLength = null;
539+
if (draggedNode && draggedMinimumTreeIndex !== null) {
540+
const addedResult = memoizedInsertNode({
541+
treeData,
542+
newNode: draggedNode,
543+
depth: draggedDepth,
544+
minimumTreeIndex: draggedMinimumTreeIndex,
545+
expandParent: true,
546+
getNodeKey,
547+
});
548+
549+
const swapTo = draggedMinimumTreeIndex;
550+
swapFrom = addedResult.treeIndex;
551+
swapLength = 1 + memoizedGetDescendantCount({ node: draggedNode });
552+
rows = slideRows(
553+
this.getRows(addedResult.treeData),
554+
swapFrom,
555+
swapTo,
556+
swapLength
557+
);
558+
} else {
559+
rows = this.getRows(treeData);
560+
}
522561

523562
// Get indices for rows that match the search conditions
524563
const matchKeys = {};
@@ -569,13 +608,15 @@ class ReactSortableTree extends Component {
569608
}
570609
rowHeight={rowHeight}
571610
rowRenderer={({ index, style: rowStyle }) =>
572-
this.renderRow(
573-
rows[index],
574-
index,
575-
rowStyle,
576-
() => rows[index - 1] || null,
577-
matchKeys
578-
)
611+
this.renderRow(rows[index], {
612+
listIndex: index,
613+
style: rowStyle,
614+
getPrevRow: () => rows[index - 1] || null,
615+
matchKeys,
616+
swapFrom,
617+
swapDepth: draggedDepth,
618+
swapLength,
619+
})
579620
}
580621
{...reactVirtualizedListProps}
581622
/>
@@ -585,18 +626,20 @@ class ReactSortableTree extends Component {
585626
} else {
586627
// Render list without react-virtualized
587628
list = rows.map((row, index) =>
588-
this.renderRow(
589-
row,
590-
index,
591-
{
629+
this.renderRow(row, {
630+
listIndex: index,
631+
style: {
592632
height:
593633
typeof rowHeight !== 'function'
594634
? rowHeight
595-
: rowHeight({ ...row, index }),
635+
: rowHeight({ index }),
596636
},
597-
() => rows[index - 1] || null,
598-
matchKeys
599-
)
637+
getPrevRow: () => rows[index - 1] || null,
638+
matchKeys,
639+
swapFrom,
640+
swapDepth: draggedDepth,
641+
swapLength,
642+
})
600643
);
601644
}
602645

src/utils/memoized-tree-data-utils.js

+30-37
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,34 @@
1-
/* eslint-disable import/prefer-default-export */
2-
import { insertNode } from './tree-data-utils';
1+
import {
2+
insertNode,
3+
getDescendantCount,
4+
getFlatDataFromTree,
5+
} from './tree-data-utils';
36

4-
let memoizedInsertArgsArray = [];
5-
let memoizedInsertKeysArray = [];
6-
let memoizedInsertResult = null;
7+
const memoize = f => {
8+
let savedArgsArray = [];
9+
let savedKeysArray = [];
10+
let savedResult = null;
711

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

28-
// If the arguments for the last insert operation are different than this time,
29-
// recalculate the result
30-
if (
31-
argsArray.length !== memoizedInsertArgsArray.length ||
32-
argsArray.some((arg, index) => arg !== memoizedInsertArgsArray[index]) ||
33-
keysArray.some((key, index) => key !== memoizedInsertKeysArray[index])
34-
) {
35-
memoizedInsertArgsArray = argsArray;
36-
memoizedInsertKeysArray = keysArray;
37-
memoizedInsertResult = insertNode(args);
38-
}
16+
// If the arguments for the last insert operation are different than this time,
17+
// recalculate the result
18+
if (
19+
argsArray.length !== savedArgsArray.length ||
20+
argsArray.some((arg, index) => arg !== savedArgsArray[index]) ||
21+
keysArray.some((key, index) => key !== savedKeysArray[index])
22+
) {
23+
savedArgsArray = argsArray;
24+
savedKeysArray = keysArray;
25+
savedResult = f(args);
26+
}
3927

40-
return memoizedInsertResult;
41-
}
28+
return savedResult;
29+
};
30+
};
31+
32+
export const memoizedInsertNode = memoize(insertNode);
33+
export const memoizedGetFlatDataFromTree = memoize(getFlatDataFromTree);
34+
export const memoizedGetDescendantCount = memoize(getDescendantCount);

src/utils/tree-data-utils.js

+42
Original file line numberDiff line numberDiff line change
@@ -517,6 +517,48 @@ export function removeNodeAtPath({
517517
});
518518
}
519519

520+
/**
521+
* Removes the node at the specified path and returns the resulting treeData.
522+
*
523+
* @param {!Object[]} treeData
524+
* @param {number[]|string[]} path - Array of keys leading up to node to be deleted
525+
* @param {!function} getNodeKey - Function to get the key from the nodeData and tree index
526+
* @param {boolean=} ignoreCollapsed - Ignore children of nodes without `expanded` set to `true`
527+
*
528+
* @return {Object} result
529+
* @return {Object[]} result.treeData - The tree data with the node removed
530+
* @return {Object} result.node - The node that was removed
531+
* @return {number} result.treeIndex - The previous treeIndex of the removed node
532+
*/
533+
export function removeNode({
534+
treeData,
535+
path,
536+
getNodeKey,
537+
ignoreCollapsed = true,
538+
}) {
539+
let removedNode = null;
540+
let removedTreeIndex = null;
541+
const nextTreeData = changeNodeAtPath({
542+
treeData,
543+
path,
544+
getNodeKey,
545+
ignoreCollapsed,
546+
newNode: ({ node, treeIndex }) => {
547+
// Store the target node and delete it from the tree
548+
removedNode = node;
549+
removedTreeIndex = treeIndex;
550+
551+
return null;
552+
},
553+
});
554+
555+
return {
556+
treeData: nextTreeData,
557+
node: removedNode,
558+
treeIndex: removedTreeIndex,
559+
};
560+
}
561+
520562
/**
521563
* Gets the node at the specified path
522564
*

0 commit comments

Comments
 (0)