Skip to content

Commit 7ef4864

Browse files
committed
feat: auto-expand closed items on drop
1 parent d609026 commit 7ef4864

File tree

4 files changed

+52
-8
lines changed

4 files changed

+52
-8
lines changed

.changeset/quick-foxes-dance.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@headless-tree/core": minor
3+
---
4+
5+
added feature where closed items are auto-expanded briefly after dragging onto them. set config option `openOnDropDelay` to zero to disable.

packages/core/src/features/drag-and-drop/feature.ts

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,47 @@
1-
import { FeatureImplementation } from "../../types/core";
1+
import {
2+
FeatureImplementation,
3+
type ItemInstance,
4+
type TreeInstance,
5+
} from "../../types/core";
26
import { DndDataRef, DragLineData, DragTarget } from "./types";
37
import {
8+
PlacementType,
9+
type TargetPlacement,
410
canDrop,
511
getDragCode,
612
getDragTarget,
13+
getTargetPlacement,
714
isOrderedDragTarget,
815
} from "./utils";
916
import { makeStateUpdater } from "../../utils";
1017

18+
const handleAutoOpenFolder = (
19+
dataRef: { current: DndDataRef },
20+
tree: TreeInstance<any>,
21+
item: ItemInstance<any>,
22+
placement: TargetPlacement,
23+
) => {
24+
const { openOnDropDelay } = tree.getConfig();
25+
const dragCode = dataRef.current.lastDragCode;
26+
27+
if (
28+
!openOnDropDelay ||
29+
!item.isFolder() ||
30+
item.isExpanded() ||
31+
placement.type !== PlacementType.MakeChild
32+
) {
33+
return;
34+
}
35+
setTimeout(() => {
36+
if (
37+
dragCode !== dataRef.current.lastDragCode ||
38+
!dataRef.current.lastAllowDrop
39+
)
40+
return;
41+
item.expand();
42+
}, openOnDropDelay);
43+
};
44+
1145
const defaultCanDropForeignDragObject = () => false;
1246
export const dragAndDropFeature: FeatureImplementation = {
1347
key: "drag-and-drop",
@@ -22,6 +56,7 @@ export const dragAndDropFeature: FeatureImplementation = {
2256
: () => false,
2357
setDndState: makeStateUpdater("dnd", tree),
2458
canReorder: true,
59+
openOnDropDelay: 800,
2560
...defaultConfig,
2661
}),
2762

@@ -185,7 +220,10 @@ export const dragAndDropFeature: FeatureImplementation = {
185220
onDragOver: (e: DragEvent) => {
186221
e.stopPropagation(); // don't bubble up to container dragover
187222
const dataRef = tree.getDataRef<DndDataRef>();
188-
const nextDragCode = getDragCode(e, item, tree);
223+
const placement = getTargetPlacement(e, item, tree, true);
224+
const nextDragCode = getDragCode(item, placement);
225+
handleAutoOpenFolder(dataRef, tree, item, placement);
226+
189227
if (nextDragCode === dataRef.current.lastDragCode) {
190228
if (dataRef.current.lastAllowDrop) {
191229
e.preventDefault();

packages/core/src/features/drag-and-drop/types.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,9 @@ export type DragAndDropFeatureDef<T> = {
9393
target: DragTarget<T>,
9494
) => void | Promise<void>;
9595
onCompleteForeignDrop?: (items: ItemInstance<T>[]) => void;
96+
97+
/** When dragging for this many ms on a closed folder, the folder will automatically open. Set to zero to disable. */
98+
openOnDropDelay?: number;
9699
};
97100
treeInstance: {
98101
getDragTarget: () => DragTarget<T> | null;

packages/core/src/features/drag-and-drop/utils.ts

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,14 @@ export enum ItemDropCategory {
77
LastInGroup,
88
}
99

10-
enum PlacementType {
10+
export enum PlacementType {
1111
ReorderAbove,
1212
ReorderBelow,
1313
MakeChild,
1414
Reparent,
1515
}
1616

17-
type TargetPlacement =
17+
export type TargetPlacement =
1818
| {
1919
type:
2020
| PlacementType.ReorderAbove
@@ -95,7 +95,7 @@ export const getInsertionIndex = <T>(
9595
return childIndex - numberOfDragItemsBeforeTarget;
9696
};
9797

98-
const getTargetPlacement = (
98+
export const getTargetPlacement = (
9999
e: any,
100100
item: ItemInstance<any>,
101101
tree: TreeInstance<any>,
@@ -153,11 +153,9 @@ const getTargetPlacement = (
153153
};
154154

155155
export const getDragCode = (
156-
e: any,
157156
item: ItemInstance<any>,
158-
tree: TreeInstance<any>,
157+
placement: TargetPlacement,
159158
) => {
160-
const placement = getTargetPlacement(e, item, tree, true);
161159
return [
162160
item.getId(),
163161
placement.type,

0 commit comments

Comments
 (0)