-
-
Notifications
You must be signed in to change notification settings - Fork 871
Expand file tree
/
Copy pathDndListBox.tsx
More file actions
131 lines (118 loc) · 3.79 KB
/
Copy pathDndListBox.tsx
File metadata and controls
131 lines (118 loc) · 3.79 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
import * as React from 'react';
import { isTextDropItem, useDragAndDrop } from 'react-aria-components';
import { ListBox, ListBoxItem } from '../ListBox/ListBox';
import { useListData } from 'react-stately';
interface FileItem {
id: string;
name: string;
type: string;
}
interface DndListBoxProps {
initialItems: FileItem[];
'aria-label': string;
}
function DndListBox(props: DndListBoxProps) {
const list = useListData({
initialItems: props.initialItems,
});
const { dragAndDropHooks } = useDragAndDrop({
// Provide drag data in a custom format as well as plain text.
getItems(keys) {
return [...keys].map((key) => {
const item = list.getItem(key);
return {
'custom-app-type': JSON.stringify(item),
'text/plain': item.name,
};
});
},
// Accept drops with the custom format.
acceptedDragTypes: ['custom-app-type'],
// Ensure items are always moved rather than copied.
getDropOperation: () => 'move',
// Handle drops between items from other lists.
async onInsert(e) {
const processedItems = await Promise.all(
e.items
.filter(isTextDropItem)
.map(async (item) =>
JSON.parse(await item.getText('custom-app-type')),
),
);
if (e.target.dropPosition === 'before') {
list.insertBefore(e.target.key, ...processedItems);
} else if (e.target.dropPosition === 'after') {
list.insertAfter(e.target.key, ...processedItems);
}
},
// Handle drops on the collection when empty.
async onRootDrop(e) {
const processedItems = await Promise.all(
e.items
.filter(isTextDropItem)
.map(async (item) =>
JSON.parse(await item.getText('custom-app-type')),
),
);
list.append(...processedItems);
},
// Handle reordering items within the same list.
onReorder(e) {
if (e.target.dropPosition === 'before') {
list.moveBefore(e.target.key, e.keys);
} else if (e.target.dropPosition === 'after') {
list.moveAfter(e.target.key, e.keys);
}
},
// Remove the items from the source list on drop
// if they were moved to a different list.
onDragEnd(e) {
if (e.dropOperation === 'move' && !e.isInternal) {
list.remove(...e.keys);
}
},
});
return (
<ListBox
aria-label={props['aria-label']}
selectionMode="multiple"
selectionBehavior="replace"
selectedKeys={list.selectedKeys}
onSelectionChange={list.setSelectedKeys}
items={list.items}
dragAndDropHooks={dragAndDropHooks}
renderEmptyState={() => 'Drop items here'}
>
{(item) => <ListBoxItem>{item.name}</ListBoxItem>}
</ListBox>
);
}
const DndListBoxWidget = () => {
return (
<div style={{ display: 'flex', gap: 12, flexWrap: 'wrap' }}>
<DndListBox
initialItems={[
{ id: '1', type: 'file', name: 'Adobe Photoshop' },
{ id: '2', type: 'file', name: 'Adobe XD' },
{ id: '3', type: 'folder', name: 'Documents' },
{ id: '4', type: 'file', name: 'Adobe InDesign' },
{ id: '5', type: 'folder', name: 'Utilities' },
{ id: '6', type: 'file', name: 'Adobe AfterEffects' },
]}
aria-label="First ListBox"
/>
<DndListBox
initialItems={[
{ id: '7', type: 'folder', name: 'Pictures' },
{ id: '8', type: 'file', name: 'Adobe Fresco' },
{ id: '9', type: 'folder', name: 'Apps' },
{ id: '10', type: 'file', name: 'Adobe Illustrator' },
{ id: '11', type: 'file', name: 'Adobe Lightroom' },
{ id: '12', type: 'file', name: 'Adobe Dreamweaver' },
]}
aria-label="Second ListBox"
/>
</div>
);
};
export default DndListBoxWidget;