Skip to content

Commit b16cc86

Browse files
jameskerrEvgeni Dimov
andauthored
Drop as Sibling of Empty, Open Folder (#202)
Based off #199 Thanks to the understanding and work of @edimov, we can now drop a node as a sibling (or child) of an open, but childless node. This should unlock people who want to create trees where every single node is an open, internal node. Co-authored-by: Evgeni Dimov <[email protected]>
1 parent c525451 commit b16cc86

File tree

2 files changed

+50
-29
lines changed

2 files changed

+50
-29
lines changed

packages/react-arborist/src/dnd/compute-drop.ts

Lines changed: 46 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
import { XYCoord } from "react-dnd";
22
import { NodeApi } from "../interfaces/node-api";
3-
import { bound, indexOf, isClosed, isItem } from "../utils";
3+
import {
4+
bound,
5+
indexOf,
6+
isClosed,
7+
isItem,
8+
isOpenWithEmptyChildren,
9+
} from "../utils";
410
import { DropResult } from "./drop-hook";
511

612
function measureHover(el: HTMLElement, offset: XYCoord) {
@@ -56,28 +62,6 @@ type Args = {
5662
nextNode: NodeApi | null;
5763
};
5864

59-
function getDropLevel(
60-
hovering: HoverData,
61-
aboveCursor: NodeApi | null,
62-
belowCursor: NodeApi | null,
63-
indent: number
64-
) {
65-
const hoverLevel = Math.round(Math.max(0, hovering.x - indent) / indent);
66-
let min, max;
67-
if (!aboveCursor) {
68-
max = 0;
69-
min = 0;
70-
} else if (!belowCursor) {
71-
max = aboveCursor.level;
72-
min = 0;
73-
} else {
74-
max = aboveCursor.level;
75-
min = belowCursor.level;
76-
}
77-
78-
return bound(hoverLevel, min, max);
79-
}
80-
8165
export type ComputedDrop = {
8266
drop: DropResult | null;
8367
cursor: Cursor | null;
@@ -128,10 +112,11 @@ export type Cursor = LineCursor | NoCursor | HighlightCursor;
128112

129113
/**
130114
* This is the most complex, tricky function in the whole repo.
131-
* It could be simplified and made more understandable.
132115
*/
133116
export function computeDrop(args: Args): ComputedDrop {
134117
const hover = measureHover(args.element, args.offset);
118+
const indent = args.indent;
119+
const hoverLevel = Math.round(Math.max(0, hover.x - indent) / indent);
135120
const { node, nextNode, prevNode } = args;
136121
const [above, below] = getNodesAroundCursor(node, prevNode, nextNode, hover);
137122

@@ -143,24 +128,56 @@ export function computeDrop(args: Args): ComputedDrop {
143128
};
144129
}
145130

146-
/* At the top of the list */
131+
/*
132+
* Now we only need to care about the node above the cursor
133+
* ----------- -------
134+
*/
135+
136+
/* There is no node above the cursor line */
147137
if (!above) {
148138
return {
149139
drop: dropAt(below?.parent?.id, 0),
150140
cursor: lineCursor(0, 0),
151141
};
152142
}
153143

154-
/* The above node is an item or a closed folder */
155-
if (isItem(above) || isClosed(above)) {
156-
const level = getDropLevel(hover, above, below, args.indent);
144+
/* The node above the cursor line is an item */
145+
if (isItem(above)) {
146+
const level = bound(hoverLevel, below?.level || 0, above.level);
147+
return {
148+
drop: walkUpFrom(above, level),
149+
cursor: lineCursor(above.rowIndex! + 1, level),
150+
};
151+
}
152+
153+
/* The node above the cursor line is a closed folder */
154+
if (isClosed(above)) {
155+
const level = bound(hoverLevel, below?.level || 0, above.level);
157156
return {
158157
drop: walkUpFrom(above, level),
159158
cursor: lineCursor(above.rowIndex! + 1, level),
160159
};
161160
}
162161

163-
/* The above node is an open folder */
162+
/* The node above the cursor line is an open folder with no children */
163+
if (isOpenWithEmptyChildren(above)) {
164+
const level = bound(hoverLevel, 0, above.level + 1);
165+
if (level > above.level) {
166+
/* Will be the first child of the empty folder */
167+
return {
168+
drop: dropAt(above.id, 0),
169+
cursor: lineCursor(above.rowIndex! + 1, level),
170+
};
171+
} else {
172+
/* Will be a sibling or grandsibling of the empty folder */
173+
return {
174+
drop: walkUpFrom(above, level),
175+
cursor: lineCursor(above.rowIndex! + 1, level),
176+
};
177+
}
178+
}
179+
180+
/* The node above the cursor is a an open folder with children */
164181
return {
165182
drop: dropAt(above?.id, 0),
166183
cursor: lineCursor(above.rowIndex! + 1, above.level + 1),

packages/react-arborist/src/utils.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ export function isClosed(node: NodeApi<any> | null) {
1414
return node && node.isInternal && !node.isOpen;
1515
}
1616

17+
export function isOpenWithEmptyChildren(node: NodeApi<any> | null) {
18+
return node && node.isOpen && !node.children?.length;
19+
}
20+
1721
/**
1822
* Is first param a descendant of the second param
1923
*/

0 commit comments

Comments
 (0)