@app-studio/web 0.9.30 → 0.9.32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/Text/Text/Text.view.d.ts +1 -0
- package/dist/web.cjs.development.js +3 -3
- package/dist/web.cjs.development.js.map +1 -1
- package/dist/web.cjs.production.min.js +1 -1
- package/dist/web.cjs.production.min.js.map +1 -1
- package/dist/web.esm.js +3 -3
- package/dist/web.esm.js.map +1 -1
- package/dist/web.umd.development.js +3 -3
- package/dist/web.umd.development.js.map +1 -1
- package/dist/web.umd.production.min.js +1 -1
- package/dist/web.umd.production.min.js.map +1 -1
- package/docs/components/Accordion.mdx +158 -0
- package/docs/components/Alert.mdx +123 -0
- package/docs/components/AspectRatio.mdx +55 -0
- package/docs/components/Avatar.mdx +85 -0
- package/docs/components/Background.mdx +522 -0
- package/docs/components/Badge.mdx +220 -0
- package/docs/components/Button.mdx +272 -0
- package/docs/components/Calendar.mdx +274 -0
- package/docs/components/Card.mdx +341 -0
- package/docs/components/Carousel.mdx +411 -0
- package/docs/components/Center.mdx +474 -0
- package/docs/components/Chart.mdx +232 -0
- package/docs/components/ChatInput.mdx +373 -0
- package/docs/components/Checkbox.mdx +66 -0
- package/docs/components/ColorInput.mdx +209 -0
- package/docs/components/ComboBox.mdx +364 -0
- package/docs/components/Command.mdx +252 -0
- package/docs/components/ContextMenu.mdx +219 -0
- package/docs/components/CountryPicker.mdx +123 -0
- package/docs/components/DatePicker.mdx +77 -0
- package/docs/components/DragAndDrop.mdx +539 -0
- package/docs/components/DropdownMenu.mdx +205 -0
- package/docs/components/File.mdx +8 -0
- package/docs/components/Flow.mdx +257 -0
- package/docs/components/Form.mdx +681 -0
- package/docs/components/Formik.mdx +621 -0
- package/docs/components/Gradient.mdx +271 -0
- package/docs/components/Horizontal.mdx +40 -0
- package/docs/components/HoverCard.mdx +140 -0
- package/docs/components/Icon.mdx +438 -0
- package/docs/components/Label.mdx +438 -0
- package/docs/components/Link.mdx +83 -0
- package/docs/components/Loader.mdx +527 -0
- package/docs/components/Menubar.mdx +124 -0
- package/docs/components/Message.mdx +571 -0
- package/docs/components/Modal.mdx +533 -0
- package/docs/components/NavigationMenu.mdx +165 -0
- package/docs/components/Pagination.mdx +150 -0
- package/docs/components/Password.mdx +121 -0
- package/docs/components/Resizable.mdx +148 -0
- package/docs/components/Select.mdx +126 -0
- package/docs/components/Separator.mdx +121 -0
- package/docs/components/Sidebar.mdx +147 -0
- package/docs/components/Slider.mdx +232 -0
- package/docs/components/Switch.mdx +62 -0
- package/docs/components/Table.mdx +409 -0
- package/docs/components/Tabs.mdx +215 -0
- package/docs/components/TagInput.mdx +528 -0
- package/docs/components/Text.mdx +163 -0
- package/docs/components/TextArea.mdx +136 -0
- package/docs/components/TextField.mdx +225 -0
- package/docs/components/Title.mdx +535 -0
- package/docs/components/Toast.mdx +165 -0
- package/docs/components/Toggle.mdx +141 -0
- package/docs/components/ToggleGroup.mdx +165 -0
- package/docs/components/Tooltip.mdx +191 -0
- package/docs/components/Tree.mdx +340 -0
- package/docs/components/Uploader.mdx +426 -0
- package/docs/components/Vertical.mdx +566 -0
- package/package.json +1 -1
|
@@ -0,0 +1,539 @@
|
|
|
1
|
+
# DragAndDrop
|
|
2
|
+
|
|
3
|
+
A powerful drag and drop component for reordering lists and items with smooth animations, touch support, and customizable rendering. Perfect for creating sortable lists, kanban boards, and interactive interfaces.
|
|
4
|
+
|
|
5
|
+
### **Import**
|
|
6
|
+
```tsx
|
|
7
|
+
import { DragAndDrop } from '@app-studio/web';
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
### **Basic Usage**
|
|
11
|
+
```tsx
|
|
12
|
+
import React, { useState } from 'react';
|
|
13
|
+
import { DragAndDrop } from '@app-studio/web';
|
|
14
|
+
import { View, Text } from 'app-studio';
|
|
15
|
+
|
|
16
|
+
export const BasicDragAndDrop = () => {
|
|
17
|
+
const [items, setItems] = useState([
|
|
18
|
+
'Item 1',
|
|
19
|
+
'Item 2',
|
|
20
|
+
'Item 3',
|
|
21
|
+
'Item 4'
|
|
22
|
+
]);
|
|
23
|
+
|
|
24
|
+
const handleItemsChange = (newItems: string[]) => {
|
|
25
|
+
console.log('Items reordered:', newItems);
|
|
26
|
+
setItems(newItems);
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<DragAndDrop
|
|
31
|
+
items={items}
|
|
32
|
+
onChange={handleItemsChange}
|
|
33
|
+
containerProps={{
|
|
34
|
+
padding: 16,
|
|
35
|
+
backgroundColor: 'color.gray.50',
|
|
36
|
+
borderRadius: 8,
|
|
37
|
+
}}
|
|
38
|
+
itemProps={{
|
|
39
|
+
marginBottom: 8,
|
|
40
|
+
padding: 12,
|
|
41
|
+
backgroundColor: 'color.white',
|
|
42
|
+
borderRadius: 4,
|
|
43
|
+
border: '1px solid',
|
|
44
|
+
borderColor: 'color.gray.200',
|
|
45
|
+
}}
|
|
46
|
+
/>
|
|
47
|
+
);
|
|
48
|
+
};
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### **Custom Item Rendering**
|
|
52
|
+
```tsx
|
|
53
|
+
import React, { useState } from 'react';
|
|
54
|
+
import { DragAndDrop } from '@app-studio/web';
|
|
55
|
+
import { View, Text, Horizontal } from 'app-studio';
|
|
56
|
+
import { DragHandleIcon, EditIcon, DeleteIcon } from '@app-studio/web';
|
|
57
|
+
|
|
58
|
+
interface Task {
|
|
59
|
+
id: string;
|
|
60
|
+
title: string;
|
|
61
|
+
description: string;
|
|
62
|
+
priority: 'low' | 'medium' | 'high';
|
|
63
|
+
completed: boolean;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export const TaskDragAndDrop = () => {
|
|
67
|
+
const [tasks, setTasks] = useState<Task[]>([
|
|
68
|
+
{
|
|
69
|
+
id: '1',
|
|
70
|
+
title: 'Design Homepage',
|
|
71
|
+
description: 'Create wireframes and mockups',
|
|
72
|
+
priority: 'high',
|
|
73
|
+
completed: false,
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
id: '2',
|
|
77
|
+
title: 'Implement Authentication',
|
|
78
|
+
description: 'Set up user login and registration',
|
|
79
|
+
priority: 'medium',
|
|
80
|
+
completed: false,
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
id: '3',
|
|
84
|
+
title: 'Write Documentation',
|
|
85
|
+
description: 'Document API endpoints',
|
|
86
|
+
priority: 'low',
|
|
87
|
+
completed: true,
|
|
88
|
+
},
|
|
89
|
+
]);
|
|
90
|
+
|
|
91
|
+
const handleTasksChange = (newTasks: Task[]) => {
|
|
92
|
+
setTasks(newTasks);
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
const renderTask = (task: Task, index: number) => {
|
|
96
|
+
const priorityColors = {
|
|
97
|
+
low: 'color.green.500',
|
|
98
|
+
medium: 'color.yellow.500',
|
|
99
|
+
high: 'color.red.500',
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
return (
|
|
103
|
+
<View
|
|
104
|
+
padding={16}
|
|
105
|
+
backgroundColor={task.completed ? 'color.gray.50' : 'color.white'}
|
|
106
|
+
border="1px solid"
|
|
107
|
+
borderColor="color.gray.200"
|
|
108
|
+
borderRadius={8}
|
|
109
|
+
marginBottom={8}
|
|
110
|
+
>
|
|
111
|
+
<Horizontal justifyContent="space-between" alignItems="flex-start">
|
|
112
|
+
<View flex={1}>
|
|
113
|
+
<Horizontal alignItems="center" marginBottom={8}>
|
|
114
|
+
<View
|
|
115
|
+
width={8}
|
|
116
|
+
height={8}
|
|
117
|
+
borderRadius="50%"
|
|
118
|
+
backgroundColor={priorityColors[task.priority]}
|
|
119
|
+
marginRight={8}
|
|
120
|
+
/>
|
|
121
|
+
<Text
|
|
122
|
+
fontWeight="semibold"
|
|
123
|
+
textDecoration={task.completed ? 'line-through' : 'none'}
|
|
124
|
+
color={task.completed ? 'color.gray.500' : 'color.gray.900'}
|
|
125
|
+
>
|
|
126
|
+
{task.title}
|
|
127
|
+
</Text>
|
|
128
|
+
</Horizontal>
|
|
129
|
+
<Text
|
|
130
|
+
fontSize={14}
|
|
131
|
+
color="color.gray.600"
|
|
132
|
+
textDecoration={task.completed ? 'line-through' : 'none'}
|
|
133
|
+
>
|
|
134
|
+
{task.description}
|
|
135
|
+
</Text>
|
|
136
|
+
</View>
|
|
137
|
+
|
|
138
|
+
<Horizontal gap={8} alignItems="center">
|
|
139
|
+
<EditIcon widthHeight={16} color="color.gray.400" />
|
|
140
|
+
<DeleteIcon widthHeight={16} color="color.gray.400" />
|
|
141
|
+
<DragHandleIcon widthHeight={16} color="color.gray.400" />
|
|
142
|
+
</Horizontal>
|
|
143
|
+
</Horizontal>
|
|
144
|
+
</View>
|
|
145
|
+
);
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
return (
|
|
149
|
+
<View width="100%" maxWidth={500}>
|
|
150
|
+
<Text fontSize={18} fontWeight="bold" marginBottom={16}>
|
|
151
|
+
Task List (Drag to Reorder)
|
|
152
|
+
</Text>
|
|
153
|
+
<DragAndDrop
|
|
154
|
+
items={tasks}
|
|
155
|
+
onChange={handleTasksChange}
|
|
156
|
+
renderItem={renderTask}
|
|
157
|
+
containerProps={{
|
|
158
|
+
padding: 16,
|
|
159
|
+
backgroundColor: 'color.gray.50',
|
|
160
|
+
borderRadius: 12,
|
|
161
|
+
minHeight: 200,
|
|
162
|
+
}}
|
|
163
|
+
/>
|
|
164
|
+
</View>
|
|
165
|
+
);
|
|
166
|
+
};
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### **Kanban Board Example**
|
|
170
|
+
```tsx
|
|
171
|
+
import React, { useState } from 'react';
|
|
172
|
+
import { DragAndDrop } from '@app-studio/web';
|
|
173
|
+
import { View, Text, Horizontal, Vertical } from 'app-studio';
|
|
174
|
+
|
|
175
|
+
interface KanbanItem {
|
|
176
|
+
id: string;
|
|
177
|
+
title: string;
|
|
178
|
+
assignee: string;
|
|
179
|
+
tags: string[];
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
interface KanbanColumn {
|
|
183
|
+
id: string;
|
|
184
|
+
title: string;
|
|
185
|
+
items: KanbanItem[];
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
export const KanbanBoard = () => {
|
|
189
|
+
const [columns, setColumns] = useState<KanbanColumn[]>([
|
|
190
|
+
{
|
|
191
|
+
id: 'todo',
|
|
192
|
+
title: 'To Do',
|
|
193
|
+
items: [
|
|
194
|
+
{ id: '1', title: 'Design System', assignee: 'John', tags: ['Design'] },
|
|
195
|
+
{ id: '2', title: 'API Integration', assignee: 'Jane', tags: ['Backend'] },
|
|
196
|
+
],
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
id: 'inprogress',
|
|
200
|
+
title: 'In Progress',
|
|
201
|
+
items: [
|
|
202
|
+
{ id: '3', title: 'User Dashboard', assignee: 'Bob', tags: ['Frontend'] },
|
|
203
|
+
],
|
|
204
|
+
},
|
|
205
|
+
{
|
|
206
|
+
id: 'done',
|
|
207
|
+
title: 'Done',
|
|
208
|
+
items: [
|
|
209
|
+
{ id: '4', title: 'Project Setup', assignee: 'Alice', tags: ['DevOps'] },
|
|
210
|
+
],
|
|
211
|
+
},
|
|
212
|
+
]);
|
|
213
|
+
|
|
214
|
+
const handleColumnItemsChange = (columnId: string, newItems: KanbanItem[]) => {
|
|
215
|
+
setColumns(prev =>
|
|
216
|
+
prev.map(col =>
|
|
217
|
+
col.id === columnId
|
|
218
|
+
? { ...col, items: newItems }
|
|
219
|
+
: col
|
|
220
|
+
)
|
|
221
|
+
);
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
const renderKanbanItem = (item: KanbanItem) => (
|
|
225
|
+
<View
|
|
226
|
+
padding={12}
|
|
227
|
+
backgroundColor="color.white"
|
|
228
|
+
border="1px solid"
|
|
229
|
+
borderColor="color.gray.200"
|
|
230
|
+
borderRadius={6}
|
|
231
|
+
marginBottom={8}
|
|
232
|
+
boxShadow="0 1px 3px rgba(0,0,0,0.1)"
|
|
233
|
+
>
|
|
234
|
+
<Text fontWeight="medium" marginBottom={8}>
|
|
235
|
+
{item.title}
|
|
236
|
+
</Text>
|
|
237
|
+
<Horizontal justifyContent="space-between" alignItems="center">
|
|
238
|
+
<Text fontSize={12} color="color.gray.600">
|
|
239
|
+
{item.assignee}
|
|
240
|
+
</Text>
|
|
241
|
+
<Horizontal gap={4}>
|
|
242
|
+
{item.tags.map(tag => (
|
|
243
|
+
<View
|
|
244
|
+
key={tag}
|
|
245
|
+
padding="2px 6px"
|
|
246
|
+
backgroundColor="color.blue.100"
|
|
247
|
+
borderRadius={4}
|
|
248
|
+
>
|
|
249
|
+
<Text fontSize={10} color="color.blue.700">
|
|
250
|
+
{tag}
|
|
251
|
+
</Text>
|
|
252
|
+
</View>
|
|
253
|
+
))}
|
|
254
|
+
</Horizontal>
|
|
255
|
+
</Horizontal>
|
|
256
|
+
</View>
|
|
257
|
+
);
|
|
258
|
+
|
|
259
|
+
return (
|
|
260
|
+
<Horizontal gap={16} alignItems="flex-start">
|
|
261
|
+
{columns.map(column => (
|
|
262
|
+
<View
|
|
263
|
+
key={column.id}
|
|
264
|
+
width={250}
|
|
265
|
+
backgroundColor="color.gray.100"
|
|
266
|
+
borderRadius={8}
|
|
267
|
+
padding={16}
|
|
268
|
+
>
|
|
269
|
+
<Text fontWeight="bold" marginBottom={16}>
|
|
270
|
+
{column.title} ({column.items.length})
|
|
271
|
+
</Text>
|
|
272
|
+
<DragAndDrop
|
|
273
|
+
items={column.items}
|
|
274
|
+
onChange={(newItems) => handleColumnItemsChange(column.id, newItems)}
|
|
275
|
+
renderItem={renderKanbanItem}
|
|
276
|
+
containerProps={{
|
|
277
|
+
minHeight: 300,
|
|
278
|
+
}}
|
|
279
|
+
/>
|
|
280
|
+
</View>
|
|
281
|
+
))}
|
|
282
|
+
</Horizontal>
|
|
283
|
+
);
|
|
284
|
+
};
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
### **Props**
|
|
288
|
+
|
|
289
|
+
| Prop | Type | Default | Description |
|
|
290
|
+
| ---- | ---- | ------- | ----------- |
|
|
291
|
+
| items | any[] | required | Array of items to be rendered and reordered |
|
|
292
|
+
| onChange | `(items: any[]) => void` | undefined | Callback function called when items are reordered |
|
|
293
|
+
| renderItem | `(item: any, index: number) => React.ReactNode` | undefined | Custom render function for each item |
|
|
294
|
+
| containerProps | ViewProps | undefined | Props to apply to the container element |
|
|
295
|
+
| itemProps | ViewProps | undefined | Props to apply to each item wrapper |
|
|
296
|
+
|
|
297
|
+
### **Advanced Features**
|
|
298
|
+
|
|
299
|
+
**Touch Support:**
|
|
300
|
+
The component automatically supports touch devices for mobile drag and drop:
|
|
301
|
+
|
|
302
|
+
```tsx
|
|
303
|
+
// Touch events are handled automatically
|
|
304
|
+
<DragAndDrop
|
|
305
|
+
items={items}
|
|
306
|
+
onChange={handleChange}
|
|
307
|
+
// Works on both desktop and mobile
|
|
308
|
+
/>
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
**Visual Feedback:**
|
|
312
|
+
Items provide visual feedback during drag operations:
|
|
313
|
+
|
|
314
|
+
```tsx
|
|
315
|
+
<DragAndDrop
|
|
316
|
+
items={items}
|
|
317
|
+
onChange={handleChange}
|
|
318
|
+
itemProps={{
|
|
319
|
+
// Styling applied to all items
|
|
320
|
+
transition: 'transform 0.2s ease',
|
|
321
|
+
cursor: 'grab',
|
|
322
|
+
}}
|
|
323
|
+
views={{
|
|
324
|
+
item: {
|
|
325
|
+
// Additional styling for dragged items
|
|
326
|
+
'&:hover': {
|
|
327
|
+
backgroundColor: 'color.gray.50',
|
|
328
|
+
},
|
|
329
|
+
},
|
|
330
|
+
}}
|
|
331
|
+
/>
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
### **Integration with Other Components**
|
|
335
|
+
|
|
336
|
+
**With Tree Component:**
|
|
337
|
+
```tsx
|
|
338
|
+
import { Tree } from '@app-studio/web';
|
|
339
|
+
|
|
340
|
+
// Tree component has built-in drag and drop
|
|
341
|
+
<Tree
|
|
342
|
+
items={treeItems}
|
|
343
|
+
allowDragAndDrop={true}
|
|
344
|
+
onItemsReorder={handleTreeReorder}
|
|
345
|
+
dragHandleIcon={<DragHandleIcon />}
|
|
346
|
+
/>
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
**With Flow Component:**
|
|
350
|
+
```tsx
|
|
351
|
+
import { Flow } from '@app-studio/web';
|
|
352
|
+
|
|
353
|
+
// Flow component supports node dragging
|
|
354
|
+
<Flow
|
|
355
|
+
nodes={flowNodes}
|
|
356
|
+
allowDraggingNodes={true}
|
|
357
|
+
onNodeDragEnd={handleNodeDragEnd}
|
|
358
|
+
/>
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
### **Styling and Customization**
|
|
362
|
+
|
|
363
|
+
**Custom Drag Handle:**
|
|
364
|
+
```tsx
|
|
365
|
+
import { DragHandleIcon } from '@app-studio/web';
|
|
366
|
+
|
|
367
|
+
const renderItemWithHandle = (item, index) => (
|
|
368
|
+
<Horizontal alignItems="center" padding={12}>
|
|
369
|
+
<DragHandleIcon
|
|
370
|
+
widthHeight={16}
|
|
371
|
+
color="color.gray.400"
|
|
372
|
+
marginRight={8}
|
|
373
|
+
/>
|
|
374
|
+
<Text>{item.name}</Text>
|
|
375
|
+
</Horizontal>
|
|
376
|
+
);
|
|
377
|
+
|
|
378
|
+
<DragAndDrop
|
|
379
|
+
items={items}
|
|
380
|
+
onChange={handleChange}
|
|
381
|
+
renderItem={renderItemWithHandle}
|
|
382
|
+
/>
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
**Conditional Dragging:**
|
|
386
|
+
```tsx
|
|
387
|
+
const renderConditionalItem = (item, index) => (
|
|
388
|
+
<View
|
|
389
|
+
padding={12}
|
|
390
|
+
backgroundColor={item.locked ? 'color.gray.100' : 'color.white'}
|
|
391
|
+
cursor={item.locked ? 'not-allowed' : 'grab'}
|
|
392
|
+
opacity={item.locked ? 0.6 : 1}
|
|
393
|
+
>
|
|
394
|
+
<Text>{item.name}</Text>
|
|
395
|
+
{item.locked && <Text fontSize={12} color="color.gray.500">Locked</Text>}
|
|
396
|
+
</View>
|
|
397
|
+
);
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
### **Performance Considerations**
|
|
401
|
+
|
|
402
|
+
**Large Lists:**
|
|
403
|
+
For large lists, consider implementing virtualization:
|
|
404
|
+
|
|
405
|
+
```tsx
|
|
406
|
+
import { FixedSizeList as List } from 'react-window';
|
|
407
|
+
|
|
408
|
+
// For very large datasets, combine with virtualization
|
|
409
|
+
const VirtualizedDragAndDrop = ({ items }) => {
|
|
410
|
+
return (
|
|
411
|
+
<List
|
|
412
|
+
height={400}
|
|
413
|
+
itemCount={items.length}
|
|
414
|
+
itemSize={60}
|
|
415
|
+
>
|
|
416
|
+
{({ index, style }) => (
|
|
417
|
+
<div style={style}>
|
|
418
|
+
<DragAndDrop
|
|
419
|
+
items={[items[index]]}
|
|
420
|
+
// Handle single item drag
|
|
421
|
+
/>
|
|
422
|
+
</div>
|
|
423
|
+
)}
|
|
424
|
+
</List>
|
|
425
|
+
);
|
|
426
|
+
};
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
**Memory Management:**
|
|
430
|
+
```tsx
|
|
431
|
+
// Clean up event listeners and references
|
|
432
|
+
useEffect(() => {
|
|
433
|
+
return () => {
|
|
434
|
+
// Component handles cleanup automatically
|
|
435
|
+
};
|
|
436
|
+
}, []);
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
### **Accessibility**
|
|
440
|
+
|
|
441
|
+
The DragAndDrop component includes accessibility features:
|
|
442
|
+
|
|
443
|
+
- **Keyboard Navigation**: Support for keyboard-based reordering
|
|
444
|
+
- **Screen Reader Support**: ARIA attributes for assistive technologies
|
|
445
|
+
- **Focus Management**: Proper focus handling during drag operations
|
|
446
|
+
|
|
447
|
+
```tsx
|
|
448
|
+
<DragAndDrop
|
|
449
|
+
items={items}
|
|
450
|
+
onChange={handleChange}
|
|
451
|
+
containerProps={{
|
|
452
|
+
role: 'list',
|
|
453
|
+
'aria-label': 'Reorderable task list',
|
|
454
|
+
}}
|
|
455
|
+
itemProps={{
|
|
456
|
+
role: 'listitem',
|
|
457
|
+
tabIndex: 0,
|
|
458
|
+
'aria-grabbed': false, // Dynamically updated during drag
|
|
459
|
+
}}
|
|
460
|
+
/>
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
### **Best Practices**
|
|
464
|
+
|
|
465
|
+
**State Management:**
|
|
466
|
+
- Keep the items state in a parent component
|
|
467
|
+
- Use unique IDs for items when possible
|
|
468
|
+
- Handle state updates immutably
|
|
469
|
+
|
|
470
|
+
**User Experience:**
|
|
471
|
+
- Provide visual feedback during drag operations
|
|
472
|
+
- Use appropriate cursor styles
|
|
473
|
+
- Consider adding drag handles for complex items
|
|
474
|
+
- Test on both desktop and mobile devices
|
|
475
|
+
|
|
476
|
+
**Performance:**
|
|
477
|
+
- Avoid heavy computations in renderItem
|
|
478
|
+
- Use React.memo for complex item components
|
|
479
|
+
- Consider virtualization for large lists
|
|
480
|
+
|
|
481
|
+
**Accessibility:**
|
|
482
|
+
- Provide keyboard alternatives
|
|
483
|
+
- Use semantic HTML structure
|
|
484
|
+
- Include proper ARIA labels
|
|
485
|
+
- Test with screen readers
|
|
486
|
+
|
|
487
|
+
### **Common Patterns**
|
|
488
|
+
|
|
489
|
+
**Sortable Table Rows:**
|
|
490
|
+
```tsx
|
|
491
|
+
const SortableTable = ({ data }) => (
|
|
492
|
+
<table>
|
|
493
|
+
<thead>
|
|
494
|
+
<tr>
|
|
495
|
+
<th>Name</th>
|
|
496
|
+
<th>Status</th>
|
|
497
|
+
<th>Actions</th>
|
|
498
|
+
</tr>
|
|
499
|
+
</thead>
|
|
500
|
+
<tbody>
|
|
501
|
+
<DragAndDrop
|
|
502
|
+
items={data}
|
|
503
|
+
onChange={setData}
|
|
504
|
+
renderItem={(item) => (
|
|
505
|
+
<tr>
|
|
506
|
+
<td>{item.name}</td>
|
|
507
|
+
<td>{item.status}</td>
|
|
508
|
+
<td>
|
|
509
|
+
<DragHandleIcon widthHeight={16} />
|
|
510
|
+
</td>
|
|
511
|
+
</tr>
|
|
512
|
+
)}
|
|
513
|
+
/>
|
|
514
|
+
</tbody>
|
|
515
|
+
</table>
|
|
516
|
+
);
|
|
517
|
+
```
|
|
518
|
+
|
|
519
|
+
**Nested Drag and Drop:**
|
|
520
|
+
```tsx
|
|
521
|
+
const NestedDragAndDrop = ({ categories }) => (
|
|
522
|
+
<DragAndDrop
|
|
523
|
+
items={categories}
|
|
524
|
+
onChange={setCategories}
|
|
525
|
+
renderItem={(category) => (
|
|
526
|
+
<View>
|
|
527
|
+
<Text fontWeight="bold">{category.name}</Text>
|
|
528
|
+
<DragAndDrop
|
|
529
|
+
items={category.items}
|
|
530
|
+
onChange={(newItems) =>
|
|
531
|
+
updateCategoryItems(category.id, newItems)
|
|
532
|
+
}
|
|
533
|
+
renderItem={(item) => <Text>{item.name}</Text>}
|
|
534
|
+
/>
|
|
535
|
+
</View>
|
|
536
|
+
)}
|
|
537
|
+
/>
|
|
538
|
+
);
|
|
539
|
+
```
|