Skip to content

Commit b41e1d2

Browse files
committed
fix: reset drag line on drop (#132)
1 parent 4450393 commit b41e1d2

File tree

8 files changed

+30
-8
lines changed

8 files changed

+30
-8
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@headless-tree/core": patch
3+
---
4+
5+
fixed a bug where ending drag without successful drop doesn't properly reset drag line (#132)

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ describe("core-feature/drag-and-drop", () => {
182182
});
183183
});
184184

185-
it("updates dnd state", () => {
185+
it("updates dnd state", async () => {
186186
const setDndState = tree.mockedHandler("setDndState");
187187
tree.do.startDrag("x111");
188188
expect(setDndState).toBeCalledWith({
@@ -198,7 +198,7 @@ describe("core-feature/drag-and-drop", () => {
198198
},
199199
});
200200
tree.do.drop("x22");
201-
expect(setDndState).toBeCalledWith(null);
201+
await vi.waitFor(() => expect(setDndState).toBeCalledWith(null));
202202
});
203203
});
204204

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

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,19 @@ export const dragAndDropFeature: FeatureImplementation = {
2424
dnd: "setDndState",
2525
},
2626

27+
onTreeMount: (tree) => {
28+
const listener = () => {
29+
tree.applySubStateUpdate("dnd", null);
30+
};
31+
tree.getDataRef<DndDataRef>().current.windowDragEndListener = listener;
32+
window.addEventListener("dragend", listener);
33+
},
34+
onTreeUnmount: (tree) => {
35+
const { windowDragEndListener } = tree.getDataRef<DndDataRef>().current;
36+
if (!windowDragEndListener) return;
37+
window.removeEventListener("dragend", windowDragEndListener);
38+
},
39+
2740
treeInstance: {
2841
getDragTarget: ({ tree }) => {
2942
return tree.getState().dnd?.dragTarget ?? null;
@@ -106,7 +119,6 @@ export const dragAndDropFeature: FeatureImplementation = {
106119
const draggedItems = tree.getState().dnd?.draggedItems;
107120

108121
dataRef.current.lastDragCode = undefined;
109-
tree.applySubStateUpdate("dnd", null);
110122

111123
if (draggedItems) {
112124
await config.onDrop?.(draggedItems, target);
@@ -211,7 +223,6 @@ export const dragAndDropFeature: FeatureImplementation = {
211223

212224
onDragEnd: (e: DragEvent) => {
213225
const draggedItems = tree.getState().dnd?.draggedItems;
214-
tree.applySubStateUpdate("dnd", null);
215226

216227
if (e.dataTransfer?.dropEffect === "none" || !draggedItems) {
217228
return;
@@ -234,7 +245,6 @@ export const dragAndDropFeature: FeatureImplementation = {
234245
const draggedItems = tree.getState().dnd?.draggedItems;
235246

236247
dataRef.current.lastDragCode = undefined;
237-
tree.applySubStateUpdate("dnd", null);
238248

239249
if (draggedItems) {
240250
await config.onDrop?.(draggedItems, target);

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { ItemInstance, SetStateFn } from "../../types/core";
33
export interface DndDataRef {
44
lastDragCode?: string;
55
lastAllowDrop?: boolean;
6+
windowDragEndListener?: () => void;
67
}
78

89
export interface DndState<T> {

packages/core/src/test-utils/test-tree-do.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,12 +105,14 @@ export class TestTreeDo<T> {
105105
dragEnd(itemId: string, event?: DragEvent) {
106106
const e = event ?? TestTree.dragEvent();
107107
this.itemProps(itemId).onDragEnd(e);
108+
window.dispatchEvent(new CustomEvent("dragend"));
108109
return e;
109110
}
110111

111112
async drop(itemId: string, event?: DragEvent) {
112113
const e = event ?? TestTree.dragEvent();
113114
await this.itemProps(itemId).onDrop(e);
115+
window.dispatchEvent(new CustomEvent("dragend"));
114116
return e;
115117
}
116118

packages/core/src/test-utils/test-tree.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ export class TestTree<T = string> {
137137
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
138138
this.instance;
139139
await this.resolveAsyncVisibleItems();
140+
this.instance.registerElement({ getBoundingClientRect: () => null } as any);
140141
return this;
141142
}
142143

packages/core/vitest.config.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,7 @@
22
import { defineConfig } from "vitest/config";
33

44
export default defineConfig({
5-
test: {},
5+
test: {
6+
environment: "jsdom",
7+
},
68
});

packages/sb-react/src/dnd/drag-inside.stories.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,8 @@ export const DragInside = () => {
5050
)}" on ${JSON.stringify(target)}`,
5151
);
5252
},
53-
canDropForeignDragObject: (_, target) => target.item.isFolder(),
53+
canDropForeignDragObject: (_, target) =>
54+
target.item.isFolder() && target.item.getId() !== "drinks",
5455
indent: 20,
5556
dataLoader: syncDataLoader,
5657
features: [
@@ -94,7 +95,7 @@ export const DragInside = () => {
9495
e.dataTransfer.setData("text/plain", "hello world");
9596
}}
9697
>
97-
Drag me into the tree!
98+
Drag me into the tree! (but not in Drinks)
9899
</div>
99100
</>
100101
);

0 commit comments

Comments
 (0)