@btfash/react-native-reanimated-dnd 1.1.0
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/LICENSE +21 -0
- package/README.md +1238 -0
- package/documentation/web-docs/README.md +41 -0
- package/example-app/.expo/README.md +8 -0
- package/example-app/README.md +515 -0
- package/lib/components/Draggable.d.ts +5 -0
- package/lib/components/Draggable.js +1 -0
- package/lib/components/Droppable.d.ts +3 -0
- package/lib/components/Droppable.js +1 -0
- package/lib/components/Sortable.d.ts +3 -0
- package/lib/components/Sortable.js +1 -0
- package/lib/components/SortableItem.d.ts +6 -0
- package/lib/components/SortableItem.js +1 -0
- package/lib/components/sortableUtils.d.ts +33 -0
- package/lib/components/sortableUtils.js +1 -0
- package/lib/context/DropContext.d.ts +3 -0
- package/lib/context/DropContext.js +1 -0
- package/lib/hooks/index.d.ts +6 -0
- package/lib/hooks/index.js +1 -0
- package/lib/hooks/useDraggable.d.ts +2 -0
- package/lib/hooks/useDraggable.js +1 -0
- package/lib/hooks/useDroppable.d.ts +2 -0
- package/lib/hooks/useDroppable.js +1 -0
- package/lib/hooks/useHorizontalSortable.d.ts +2 -0
- package/lib/hooks/useHorizontalSortable.js +1 -0
- package/lib/hooks/useHorizontalSortableList.d.ts +4 -0
- package/lib/hooks/useHorizontalSortableList.js +1 -0
- package/lib/hooks/useSortable.d.ts +50 -0
- package/lib/hooks/useSortable.js +1 -0
- package/lib/hooks/useSortableList.d.ts +27 -0
- package/lib/hooks/useSortableList.js +1 -0
- package/lib/index.d.ts +13 -0
- package/lib/index.js +1 -0
- package/lib/types/context.d.ts +67 -0
- package/lib/types/context.js +1 -0
- package/lib/types/draggable.d.ts +54 -0
- package/lib/types/draggable.js +1 -0
- package/lib/types/droppable.d.ts +26 -0
- package/lib/types/droppable.js +1 -0
- package/lib/types/index.d.ts +4 -0
- package/lib/types/index.js +1 -0
- package/lib/types/sortable.d.ts +233 -0
- package/lib/types/sortable.js +1 -0
- package/package.json +66 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Documentation Website
|
|
2
|
+
|
|
3
|
+
This is the documentation website for React Native Reanimated DnD.
|
|
4
|
+
|
|
5
|
+
### Installation
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
$ yarn
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
### Local Development
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
$ yarn start
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server.
|
|
18
|
+
|
|
19
|
+
### Build
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
$ yarn build
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
This command generates static content into the `build` directory and can be served using any static contents hosting service.
|
|
26
|
+
|
|
27
|
+
### Deployment
|
|
28
|
+
|
|
29
|
+
Using SSH:
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
$ USE_SSH=true yarn deploy
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Not using SSH:
|
|
36
|
+
|
|
37
|
+
```
|
|
38
|
+
$ GIT_USER=<Your GitHub username> yarn deploy
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch.
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
> Why do I have a folder named ".expo" in my project?
|
|
2
|
+
The ".expo" folder is created when an Expo project is started using "expo start" command.
|
|
3
|
+
> What do the files contain?
|
|
4
|
+
- "devices.json": contains information about devices that have recently opened this project. This is used to populate the "Development sessions" list in your development builds.
|
|
5
|
+
- "settings.json": contains the server configuration that is used to serve the application manifest.
|
|
6
|
+
> Should I commit the ".expo" folder?
|
|
7
|
+
No, you should not share the ".expo" folder. It does not contain any information that is relevant for other developers working on the project, it is specific to your machine.
|
|
8
|
+
Upon project creation, the ".expo" folder is already added to your ".gitignore" file.
|
|
@@ -0,0 +1,515 @@
|
|
|
1
|
+
# React Native Reanimated DnD - Example App 📱
|
|
2
|
+
|
|
3
|
+
<div align="center">
|
|
4
|
+
|
|
5
|
+
**Interactive Examples Showcase**
|
|
6
|
+
|
|
7
|
+
A comprehensive collection of 15 interactive examples demonstrating every feature of the React Native Reanimated DnD library.
|
|
8
|
+
|
|
9
|
+
[](https://reactnative.dev/)
|
|
10
|
+
[](https://expo.dev/)
|
|
11
|
+
[](https://www.typescriptlang.org/)
|
|
12
|
+
|
|
13
|
+
</div>
|
|
14
|
+
|
|
15
|
+
## 🎯 Overview
|
|
16
|
+
|
|
17
|
+
This example app provides a hands-on demonstration of all the features available in the `react-native-reanimated-dnd` library. Each example is carefully crafted to showcase specific functionality, from basic drag-and-drop to advanced collision detection and custom animations.
|
|
18
|
+
|
|
19
|
+
## 🚀 Quick Start
|
|
20
|
+
|
|
21
|
+
### Prerequisites
|
|
22
|
+
|
|
23
|
+
- Node.js 16+
|
|
24
|
+
- React Native development environment
|
|
25
|
+
- iOS Simulator or Android Emulator (or physical device)
|
|
26
|
+
|
|
27
|
+
### Installation & Setup
|
|
28
|
+
|
|
29
|
+
1. **Clone the repository:**
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
git clone https://github.com/entropyconquers/react-native-reanimated-dnd.git
|
|
33
|
+
cd react-native-reanimated-dnd/example-app
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
2. **Install dependencies:**
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
npm install
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
3. **Run the app:**
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
# iOS
|
|
46
|
+
npx expo run:ios
|
|
47
|
+
|
|
48
|
+
# Android
|
|
49
|
+
npx expo run:android
|
|
50
|
+
|
|
51
|
+
# Development build
|
|
52
|
+
npx expo start
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## 📚 Examples Catalog
|
|
56
|
+
|
|
57
|
+
### 🎵 Core Examples
|
|
58
|
+
|
|
59
|
+
#### **Sortable Music Queue** ♫
|
|
60
|
+
|
|
61
|
+
_Perfect introduction to sortable lists_
|
|
62
|
+
|
|
63
|
+
**Features Demonstrated:**
|
|
64
|
+
|
|
65
|
+
- Reorderable vertical list with smooth animations
|
|
66
|
+
- Drag handles for precise control
|
|
67
|
+
- Auto-scrolling when dragging near edges
|
|
68
|
+
- Real-time position updates and data synchronization
|
|
69
|
+
- Long-press activation with haptic feedback
|
|
70
|
+
|
|
71
|
+
**Key Components:** `Sortable`, `SortableItem`, `SortableItem.Handle`
|
|
72
|
+
|
|
73
|
+
**Use Cases:** Music playlists, task lists, priority queues, menu reordering
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
#### **Basic Drag & Drop** 👆
|
|
78
|
+
|
|
79
|
+
_Your first step into drag-and-drop_
|
|
80
|
+
|
|
81
|
+
**Features Demonstrated:**
|
|
82
|
+
|
|
83
|
+
- Simple draggable items with multiple drop zones
|
|
84
|
+
- Visual feedback when hovering over drop zones
|
|
85
|
+
- Basic data transfer between components
|
|
86
|
+
- Clean, minimal implementation pattern
|
|
87
|
+
|
|
88
|
+
**Key Components:** `Draggable`, `Droppable`, `DropProvider`
|
|
89
|
+
|
|
90
|
+
**Use Cases:** File organization, category sorting, simple workflows
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
### 🔧 Interaction Examples
|
|
95
|
+
|
|
96
|
+
#### **Drag Handles** 🔧
|
|
97
|
+
|
|
98
|
+
_Precise control over drag initiation_
|
|
99
|
+
|
|
100
|
+
**Features Demonstrated:**
|
|
101
|
+
|
|
102
|
+
- Handle-only dragging (rest of item non-draggable)
|
|
103
|
+
- Mixed interaction patterns within single items
|
|
104
|
+
- Custom handle styling and positioning
|
|
105
|
+
- Accessibility considerations for touch targets
|
|
106
|
+
|
|
107
|
+
**Key Components:** `Draggable.Handle`
|
|
108
|
+
|
|
109
|
+
**Use Cases:** Complex UI cards, mixed-interaction lists, accessibility-focused designs
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
#### **Drag State Management** ⚡
|
|
114
|
+
|
|
115
|
+
_Complete lifecycle state tracking_
|
|
116
|
+
|
|
117
|
+
**Features Demonstrated:**
|
|
118
|
+
|
|
119
|
+
- `DraggableState` enum usage (`IDLE`, `DRAGGING`, `ANIMATING`)
|
|
120
|
+
- `onStateChange` callback implementation
|
|
121
|
+
- Visual state indicators and conditional styling
|
|
122
|
+
- State-based UI updates and animations
|
|
123
|
+
|
|
124
|
+
**Key Components:** State management hooks and callbacks
|
|
125
|
+
|
|
126
|
+
**Use Cases:** Complex UI feedback, analytics tracking, conditional behaviors
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
#### **Dropped Items Map** 📍
|
|
131
|
+
|
|
132
|
+
_Real-time tracking of drop relationships_
|
|
133
|
+
|
|
134
|
+
**Features Demonstrated:**
|
|
135
|
+
|
|
136
|
+
- Track which draggables are on which droppables
|
|
137
|
+
- Real-time mapping updates
|
|
138
|
+
- Multi-zone state management
|
|
139
|
+
- Persistent state across interactions
|
|
140
|
+
|
|
141
|
+
**Key Components:** Context state management
|
|
142
|
+
|
|
143
|
+
**Use Cases:** Kanban boards, inventory management, workspace organization
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
### 🎨 Visual & Animation Examples
|
|
148
|
+
|
|
149
|
+
#### **Custom Animations** 🎬
|
|
150
|
+
|
|
151
|
+
_Bring your drag interactions to life_
|
|
152
|
+
|
|
153
|
+
**Features Demonstrated:**
|
|
154
|
+
|
|
155
|
+
- Custom timing functions with `withTiming`
|
|
156
|
+
- Spring animations with `withSpring`
|
|
157
|
+
- Easing variations (bezier, bounce, elastic)
|
|
158
|
+
- Duration and damping controls
|
|
159
|
+
- Chained and sequential animations
|
|
160
|
+
|
|
161
|
+
**Key Components:** Animation functions, `animationFunction` prop
|
|
162
|
+
|
|
163
|
+
**Use Cases:** Premium app experiences, game-like interactions, brand-specific animations
|
|
164
|
+
|
|
165
|
+
---
|
|
166
|
+
|
|
167
|
+
#### **Active Drop Styles** ✨
|
|
168
|
+
|
|
169
|
+
_Beautiful visual feedback_
|
|
170
|
+
|
|
171
|
+
**Features Demonstrated:**
|
|
172
|
+
|
|
173
|
+
- Hover state styling with smooth transitions
|
|
174
|
+
- Color changes and border effects
|
|
175
|
+
- Scale transformations and shadow effects
|
|
176
|
+
- Conditional styling based on drop state
|
|
177
|
+
- CSS-like transition animations
|
|
178
|
+
|
|
179
|
+
**Key Components:** `activeStyle` prop, conditional styling
|
|
180
|
+
|
|
181
|
+
**Use Cases:** File upload zones, shopping carts, interactive dashboards
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
|
|
185
|
+
#### **Alignment & Offset** 📐
|
|
186
|
+
|
|
187
|
+
_Pixel-perfect drop positioning_
|
|
188
|
+
|
|
189
|
+
**Features Demonstrated:**
|
|
190
|
+
|
|
191
|
+
- 9-point alignment system (top-left, center, bottom-right, etc.)
|
|
192
|
+
- Custom offset values for fine-tuning
|
|
193
|
+
- Visual alignment guides and indicators
|
|
194
|
+
- Precise positioning control
|
|
195
|
+
|
|
196
|
+
**Key Components:** `dropAlignment`, `dropOffset` props
|
|
197
|
+
|
|
198
|
+
**Use Cases:** Layout builders, design tools, precise positioning requirements
|
|
199
|
+
|
|
200
|
+
---
|
|
201
|
+
|
|
202
|
+
### 🎯 Advanced Collision Examples
|
|
203
|
+
|
|
204
|
+
#### **Collision Detection** 🎯
|
|
205
|
+
|
|
206
|
+
_Different strategies for drop detection_
|
|
207
|
+
|
|
208
|
+
**Features Demonstrated:**
|
|
209
|
+
|
|
210
|
+
- **Center Algorithm**: Precise center-point collision detection
|
|
211
|
+
- **Intersect Algorithm**: Forgiving overlap detection (default)
|
|
212
|
+
- **Contain Algorithm**: Strict containment requirement
|
|
213
|
+
- Visual collision indicators and feedback
|
|
214
|
+
- Performance implications of each algorithm
|
|
215
|
+
|
|
216
|
+
**Key Components:** `collisionAlgorithm` prop
|
|
217
|
+
|
|
218
|
+
**Use Cases:** Games, precise interactions, different UX requirements
|
|
219
|
+
|
|
220
|
+
---
|
|
221
|
+
|
|
222
|
+
#### **Drop Zone Capacity** 🗂️
|
|
223
|
+
|
|
224
|
+
_Smart capacity management_
|
|
225
|
+
|
|
226
|
+
**Features Demonstrated:**
|
|
227
|
+
|
|
228
|
+
- Maximum item limits per drop zone
|
|
229
|
+
- Visual capacity indicators (3/5 items)
|
|
230
|
+
- Overflow handling and rejection
|
|
231
|
+
- Dynamic capacity updates
|
|
232
|
+
- Full/available state styling
|
|
233
|
+
|
|
234
|
+
**Key Components:** `capacity` prop, conditional logic
|
|
235
|
+
|
|
236
|
+
**Use Cases:** Limited slots, inventory systems, resource management
|
|
237
|
+
|
|
238
|
+
---
|
|
239
|
+
|
|
240
|
+
### 📦 Constraint Examples
|
|
241
|
+
|
|
242
|
+
#### **Bounded Dragging** 📦
|
|
243
|
+
|
|
244
|
+
_Keep items within boundaries_
|
|
245
|
+
|
|
246
|
+
**Features Demonstrated:**
|
|
247
|
+
|
|
248
|
+
- Container-based boundary constraints
|
|
249
|
+
- Visual boundary indicators
|
|
250
|
+
- Smooth edge collision handling
|
|
251
|
+
- Responsive boundary updates
|
|
252
|
+
- Ref-based boundary definition
|
|
253
|
+
|
|
254
|
+
**Key Components:** `dragBoundsRef` prop
|
|
255
|
+
|
|
256
|
+
**Use Cases:** Canvas editors, constrained workspaces, mobile-friendly interactions
|
|
257
|
+
|
|
258
|
+
---
|
|
259
|
+
|
|
260
|
+
#### **X-Axis Constraints** ↔️
|
|
261
|
+
|
|
262
|
+
_Horizontal-only movement_
|
|
263
|
+
|
|
264
|
+
**Features Demonstrated:**
|
|
265
|
+
|
|
266
|
+
- Horizontal slider implementations
|
|
267
|
+
- Axis-locked dragging behavior
|
|
268
|
+
- Custom constraint handling
|
|
269
|
+
- Smooth horizontal motion
|
|
270
|
+
- Value-based positioning
|
|
271
|
+
|
|
272
|
+
**Key Components:** `dragAxis="x"` prop
|
|
273
|
+
|
|
274
|
+
**Use Cases:** Sliders, horizontal timelines, progress controls
|
|
275
|
+
|
|
276
|
+
---
|
|
277
|
+
|
|
278
|
+
#### **Y-Axis Constraints** ↕️
|
|
279
|
+
|
|
280
|
+
_Vertical-only movement_
|
|
281
|
+
|
|
282
|
+
**Features Demonstrated:**
|
|
283
|
+
|
|
284
|
+
- Vertical slider controls
|
|
285
|
+
- Column-based movement patterns
|
|
286
|
+
- Vertical constraint handling
|
|
287
|
+
- Smooth vertical motion
|
|
288
|
+
- List reordering patterns
|
|
289
|
+
|
|
290
|
+
**Key Components:** `dragAxis="y"` prop
|
|
291
|
+
|
|
292
|
+
**Use Cases:** Volume controls, vertical lists, priority ordering
|
|
293
|
+
|
|
294
|
+
---
|
|
295
|
+
|
|
296
|
+
#### **Bounded Y-Axis** 📏
|
|
297
|
+
|
|
298
|
+
_Vertical movement with limits_
|
|
299
|
+
|
|
300
|
+
**Features Demonstrated:**
|
|
301
|
+
|
|
302
|
+
- Vertical movement within specific boundaries
|
|
303
|
+
- Combined axis and boundary constraints
|
|
304
|
+
- Boundary visualization
|
|
305
|
+
- Smooth constraint handling
|
|
306
|
+
- Range-based interactions
|
|
307
|
+
|
|
308
|
+
**Key Components:** `dragAxis="y"` + `dragBoundsRef`
|
|
309
|
+
|
|
310
|
+
**Use Cases:** Range selectors, bounded lists, constrained vertical controls
|
|
311
|
+
|
|
312
|
+
---
|
|
313
|
+
|
|
314
|
+
### ⚙️ Custom Implementation
|
|
315
|
+
|
|
316
|
+
#### **Custom Draggable** ⚙️
|
|
317
|
+
|
|
318
|
+
_Advanced implementation patterns_
|
|
319
|
+
|
|
320
|
+
**Features Demonstrated:**
|
|
321
|
+
|
|
322
|
+
- Direct hook usage (`useDraggable`, `useDroppable`)
|
|
323
|
+
- Advanced gesture handling patterns
|
|
324
|
+
- Custom animation systems
|
|
325
|
+
- Performance optimization techniques
|
|
326
|
+
- Low-level API usage
|
|
327
|
+
|
|
328
|
+
**Key Components:** Core hooks and custom implementations
|
|
329
|
+
|
|
330
|
+
**Use Cases:** Highly customized interactions, performance-critical apps, unique UX patterns
|
|
331
|
+
|
|
332
|
+
---
|
|
333
|
+
|
|
334
|
+
## 🏗️ App Architecture
|
|
335
|
+
|
|
336
|
+
### Navigation Structure
|
|
337
|
+
|
|
338
|
+
```
|
|
339
|
+
📱 App
|
|
340
|
+
├── 🏠 Home (Examples Navigation)
|
|
341
|
+
├── 🎵 Sortable Music Queue
|
|
342
|
+
├── 👆 Basic Drag & Drop
|
|
343
|
+
├── 🔧 Drag Handles
|
|
344
|
+
├── ⚡ Drag State Management
|
|
345
|
+
├── 📍 Dropped Items Map
|
|
346
|
+
├── 🎬 Custom Animations
|
|
347
|
+
├── ✨ Active Drop Styles
|
|
348
|
+
├── 📐 Alignment & Offset
|
|
349
|
+
├── 🎯 Collision Detection
|
|
350
|
+
├── 🗂️ Drop Zone Capacity
|
|
351
|
+
├── 📦 Bounded Dragging
|
|
352
|
+
├── ↔️ X-Axis Constraints
|
|
353
|
+
├── ↕️ Y-Axis Constraints
|
|
354
|
+
├── 📏 Bounded Y-Axis
|
|
355
|
+
└── ⚙️ Custom Draggable
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
### Key Components
|
|
359
|
+
|
|
360
|
+
#### **ExamplesNavigationPage**
|
|
361
|
+
|
|
362
|
+
- Beautiful home screen with example cards
|
|
363
|
+
- Categorized example organization
|
|
364
|
+
- Smooth navigation transitions
|
|
365
|
+
- Custom fonts and styling
|
|
366
|
+
|
|
367
|
+
#### **Example Components**
|
|
368
|
+
|
|
369
|
+
- Consistent header with back navigation
|
|
370
|
+
- Interactive demonstrations
|
|
371
|
+
- Real-time feedback and state display
|
|
372
|
+
- Clean, educational code patterns
|
|
373
|
+
|
|
374
|
+
#### **Shared Components**
|
|
375
|
+
|
|
376
|
+
- `ExampleHeader` - Consistent navigation
|
|
377
|
+
- `Footer` - App information and links
|
|
378
|
+
- `BottomSheet` - Settings and options
|
|
379
|
+
|
|
380
|
+
## 🎨 Design System
|
|
381
|
+
|
|
382
|
+
### Typography
|
|
383
|
+
|
|
384
|
+
- **Primary Font**: Major Mono Display (headers)
|
|
385
|
+
- **Body Font**: Kumbh Sans (content)
|
|
386
|
+
- **Weights**: Regular, Medium, SemiBold, Bold
|
|
387
|
+
|
|
388
|
+
### Color Palette
|
|
389
|
+
|
|
390
|
+
- **Background**: Deep black (#000000)
|
|
391
|
+
- **Cards**: Dark gray with subtle borders
|
|
392
|
+
- **Accent**: Red highlights for branding
|
|
393
|
+
- **Text**: High contrast white/gray
|
|
394
|
+
- **Interactive**: Blue and green for actions
|
|
395
|
+
|
|
396
|
+
### Animations
|
|
397
|
+
|
|
398
|
+
- **Duration**: 300ms standard, 200ms quick
|
|
399
|
+
- **Easing**: Smooth bezier curves
|
|
400
|
+
- **Spring**: Natural physics-based motion
|
|
401
|
+
- **Feedback**: Haptic feedback on interactions
|
|
402
|
+
|
|
403
|
+
## 🔧 Technical Implementation
|
|
404
|
+
|
|
405
|
+
### Dependencies
|
|
406
|
+
|
|
407
|
+
```json
|
|
408
|
+
{
|
|
409
|
+
"react-native-reanimated": "^3.5.0",
|
|
410
|
+
"react-native-gesture-handler": "^2.13.0",
|
|
411
|
+
"@react-navigation/native": "^6.1.0",
|
|
412
|
+
"@react-navigation/stack": "^6.3.0",
|
|
413
|
+
"expo": "~49.0.0"
|
|
414
|
+
}
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
### Performance Optimizations
|
|
418
|
+
|
|
419
|
+
- **Worklet Functions**: All animations run on UI thread
|
|
420
|
+
- **Gesture Handler**: Native gesture recognition
|
|
421
|
+
- **Reanimated 3**: Latest performance improvements
|
|
422
|
+
- **Memoization**: Optimized re-renders
|
|
423
|
+
- **Lazy Loading**: Efficient navigation
|
|
424
|
+
|
|
425
|
+
### Code Organization
|
|
426
|
+
|
|
427
|
+
```
|
|
428
|
+
📁 example-app/
|
|
429
|
+
├── 📁 components/
|
|
430
|
+
│ ├── 📁 examples/ # All example implementations
|
|
431
|
+
│ ├── ExamplesNavigationPage.tsx
|
|
432
|
+
│ ├── ExampleHeader.tsx
|
|
433
|
+
│ └── Footer.tsx
|
|
434
|
+
├── 📁 navigation/
|
|
435
|
+
│ └── AppNavigator.tsx # React Navigation setup
|
|
436
|
+
├── 📁 assets/ # Images and fonts
|
|
437
|
+
└── App.tsx # Root component
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
## 🎯 Learning Path
|
|
441
|
+
|
|
442
|
+
### Beginner
|
|
443
|
+
|
|
444
|
+
1. **Basic Drag & Drop** - Understand core concepts
|
|
445
|
+
2. **Sortable Music Queue** - Learn list reordering
|
|
446
|
+
3. **Active Drop Styles** - Add visual feedback
|
|
447
|
+
|
|
448
|
+
### Intermediate
|
|
449
|
+
|
|
450
|
+
4. **Drag Handles** - Implement precise controls
|
|
451
|
+
5. **Collision Detection** - Understand algorithms
|
|
452
|
+
6. **Bounded Dragging** - Add constraints
|
|
453
|
+
7. **Custom Animations** - Enhance user experience
|
|
454
|
+
|
|
455
|
+
### Advanced
|
|
456
|
+
|
|
457
|
+
8. **Drag State Management** - Master state tracking
|
|
458
|
+
9. **Dropped Items Map** - Complex state management
|
|
459
|
+
10. **Alignment & Offset** - Precise positioning
|
|
460
|
+
11. **Capacity Management** - Smart limitations
|
|
461
|
+
12. **Axis Constraints** - Specialized interactions
|
|
462
|
+
13. **Custom Implementation** - Build from scratch
|
|
463
|
+
|
|
464
|
+
## 🤝 Contributing to Examples
|
|
465
|
+
|
|
466
|
+
We welcome contributions to improve and expand the example collection!
|
|
467
|
+
|
|
468
|
+
### Adding New Examples
|
|
469
|
+
|
|
470
|
+
1. Create new component in `components/examples/`
|
|
471
|
+
2. Add to navigation in `AppNavigator.tsx`
|
|
472
|
+
3. Update `ExamplesNavigationPage.tsx`
|
|
473
|
+
4. Follow existing patterns and styling
|
|
474
|
+
|
|
475
|
+
### Improving Existing Examples
|
|
476
|
+
|
|
477
|
+
1. Enhance visual design
|
|
478
|
+
2. Add more interactive features
|
|
479
|
+
3. Improve code comments and documentation
|
|
480
|
+
4. Optimize performance
|
|
481
|
+
|
|
482
|
+
## 📱 Device Testing
|
|
483
|
+
|
|
484
|
+
### Recommended Testing
|
|
485
|
+
|
|
486
|
+
- **iOS**: iPhone 12+ (iOS 14+)
|
|
487
|
+
- **Android**: API 21+ (Android 5.0+)
|
|
488
|
+
- **Tablets**: iPad, Android tablets
|
|
489
|
+
- **Physical Devices**: Best performance testing
|
|
490
|
+
|
|
491
|
+
### Performance Notes
|
|
492
|
+
|
|
493
|
+
- Animations run at 60fps on modern devices
|
|
494
|
+
- Gesture recognition is native and responsive
|
|
495
|
+
- Memory usage is optimized for mobile
|
|
496
|
+
- Works well on both platforms
|
|
497
|
+
|
|
498
|
+
## 🔗 Related Resources
|
|
499
|
+
|
|
500
|
+
- **Main Library**: [react-native-reanimated-dnd](https://github.com/entropyconquers/react-native-reanimated-dnd)
|
|
501
|
+
- **Documentation**: [API Reference](https://github.com/entropyconquers/react-native-reanimated-dnd#readme)
|
|
502
|
+
- **React Native Reanimated**: [Official Docs](https://docs.swmansion.com/react-native-reanimated/)
|
|
503
|
+
- **Gesture Handler**: [Official Docs](https://docs.swmansion.com/react-native-gesture-handler/)
|
|
504
|
+
|
|
505
|
+
---
|
|
506
|
+
|
|
507
|
+
<div align="center">
|
|
508
|
+
|
|
509
|
+
**🎯 Ready to explore drag-and-drop interactions?**
|
|
510
|
+
|
|
511
|
+
Run the app and start with the **Basic Drag & Drop** example!
|
|
512
|
+
|
|
513
|
+
[📱 Run the App](#quick-start) • [📖 View Source](https://github.com/entropyconquers/react-native-reanimated-dnd) • [⭐ Star on GitHub](https://github.com/entropyconquers/react-native-reanimated-dnd)
|
|
514
|
+
|
|
515
|
+
</div>
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { DraggableHandleProps, DraggableProps } from "../types/draggable";
|
|
3
|
+
export declare const Draggable: (<TData = unknown>({ style: componentStyle, children, ...useDraggableHookOptions }: DraggableProps<TData>) => React.JSX.Element) & {
|
|
4
|
+
Handle: ({ children, style }: DraggableHandleProps) => React.JSX.Element;
|
|
5
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import React,{createContext,useContext}from"react";import Animated from"react-native-reanimated";import{GestureDetector}from"react-native-gesture-handler";import{useDraggable}from"../hooks/useDraggable";const DraggableContext=createContext(null);const Handle=({children,style})=>{const draggableContext=useContext(DraggableContext);if(!draggableContext){console.warn("Draggable.Handle must be used within a Draggable component");return React.createElement(React.Fragment,null,children)}return React.createElement(GestureDetector,{gesture:draggableContext.gesture},React.createElement(Animated.View,{style},children))};const DraggableComponent=({style:componentStyle,children,...useDraggableHookOptions})=>{const{animatedViewProps,gesture,state,hasHandle,animatedViewRef}=useDraggable({...useDraggableHookOptions,children,handleComponent:Handle});const contextValue={gesture,state};const content=React.createElement(Animated.View,{ref:animatedViewRef,...animatedViewProps,style:[componentStyle,animatedViewProps.style],collapsable:false},React.createElement(DraggableContext.Provider,{value:contextValue},children));if(hasHandle){return content}else{return React.createElement(GestureDetector,{gesture},content)}};export const Draggable=Object.assign(DraggableComponent,{Handle});
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { DroppableProps } from "../types/droppable";
|
|
3
|
+
export declare const Droppable: <TData = unknown>({ onDrop, dropDisabled, onActiveChange, dropAlignment, dropOffset, activeStyle, droppableId, capacity, style, children, }: DroppableProps<TData>) => React.ReactElement;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import React from"react";import Animated from"react-native-reanimated";import{useDroppable}from"../hooks/useDroppable";export const Droppable=({onDrop,dropDisabled,onActiveChange,dropAlignment,dropOffset,activeStyle,droppableId,capacity,style,children})=>{const{viewProps,animatedViewRef}=useDroppable({onDrop,dropDisabled,onActiveChange,dropAlignment,dropOffset,activeStyle,droppableId,capacity});return React.createElement(Animated.View,{ref:animatedViewRef,...viewProps,style:[style,viewProps.style],collapsable:false},children)};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import React,{memo,useCallback}from"react";import{StyleSheet}from"react-native";import Animated from"react-native-reanimated";import{GestureHandlerRootView,FlatList,ScrollView}from"react-native-gesture-handler";import{DropProvider}from"../context/DropContext";import{SortableDirection}from"../types/sortable";import{useSortableList}from"../hooks/useSortableList";import{useHorizontalSortableList}from"../hooks/useHorizontalSortableList";import{dataHash}from"./sortableUtils";const AnimatedFlatList=Animated.createAnimatedComponent(FlatList);const AnimatedScrollView=Animated.createAnimatedComponent(ScrollView);function SortableComponent({data,renderItem,direction=SortableDirection.Vertical,itemHeight,itemWidth,gap=0,paddingHorizontal=0,style,contentContainerStyle,itemKeyExtractor=item=>item.id,useFlatList=true}){if(direction===SortableDirection.Vertical&&!itemHeight){throw new Error("itemHeight is required when direction is vertical")}if(direction===SortableDirection.Horizontal&&!itemWidth){throw new Error("itemWidth is required when direction is horizontal")}if(direction===SortableDirection.Horizontal){const horizontalOptions={data,itemWidth,gap,paddingHorizontal,itemKeyExtractor};const{scrollViewRef:horizontalScrollViewRef,dropProviderRef:horizontalDropProviderRef,handleScroll:horizontalHandleScroll,handleScrollEnd:horizontalHandleScrollEnd,contentWidth,getItemProps:getHorizontalItemProps}=useHorizontalSortableList(horizontalOptions);const memoizedHorizontalRenderItem=useCallback((({item,index})=>{const itemProps=getHorizontalItemProps(item,index);const sortableItemProps={item,index,direction:SortableDirection.Horizontal,autoScrollHorizontalDirection:itemProps.autoScrollDirection,...itemProps};return renderItem(sortableItemProps)}),[getHorizontalItemProps,renderItem]);return React.createElement(GestureHandlerRootView,{style:styles.flex},React.createElement(DropProvider,{ref:horizontalDropProviderRef},useFlatList?React.createElement(AnimatedFlatList,{ref:horizontalScrollViewRef,data,keyExtractor:itemKeyExtractor,horizontal:true,renderItem:memoizedHorizontalRenderItem,onScroll:horizontalHandleScroll,scrollEventThrottle:16,style:[styles.scrollView,style],contentContainerStyle:[{width:contentWidth},contentContainerStyle],onScrollEndDrag:horizontalHandleScrollEnd,onMomentumScrollEnd:horizontalHandleScrollEnd,simultaneousHandlers:horizontalDropProviderRef,showsHorizontalScrollIndicator:false}):React.createElement(AnimatedScrollView,{ref:horizontalScrollViewRef,onScroll:horizontalHandleScroll,scrollEventThrottle:16,horizontal:true,style:[styles.scrollView,style],contentContainerStyle:[{width:contentWidth},contentContainerStyle],onScrollEndDrag:horizontalHandleScrollEnd,onMomentumScrollEnd:horizontalHandleScrollEnd,simultaneousHandlers:horizontalDropProviderRef,showsHorizontalScrollIndicator:false},data.map(((item,index)=>{const itemProps=getHorizontalItemProps(item,index);const sortableItemProps={item,index,direction:SortableDirection.Horizontal,autoScrollHorizontalDirection:itemProps.autoScrollDirection,...itemProps};return renderItem(sortableItemProps)})))))}const verticalOptions={data,itemHeight,itemKeyExtractor};const{scrollViewRef,dropProviderRef,handleScroll,handleScrollEnd,contentHeight,getItemProps}=useSortableList(verticalOptions);const memoizedVerticalRenderItem=useCallback((({item,index})=>{const itemProps=getItemProps(item,index);const sortableItemProps={item,index,direction:SortableDirection.Vertical,...itemProps};return renderItem(sortableItemProps)}),[getItemProps,renderItem]);return React.createElement(GestureHandlerRootView,{style:styles.flex},React.createElement(DropProvider,{ref:dropProviderRef},useFlatList?React.createElement(AnimatedFlatList,{ref:scrollViewRef,data,keyExtractor:itemKeyExtractor,renderItem:memoizedVerticalRenderItem,onScroll:handleScroll,scrollEventThrottle:16,style:[styles.scrollView,style],contentContainerStyle:[{height:contentHeight},contentContainerStyle],onScrollEndDrag:handleScrollEnd,onMomentumScrollEnd:handleScrollEnd,simultaneousHandlers:dropProviderRef,showsVerticalScrollIndicator:false}):React.createElement(AnimatedScrollView,{ref:scrollViewRef,onScroll:handleScroll,scrollEventThrottle:16,style:[styles.scrollView,style],contentContainerStyle:[{height:contentHeight},contentContainerStyle],onScrollEndDrag:handleScrollEnd,onMomentumScrollEnd:handleScrollEnd,simultaneousHandlers:dropProviderRef},data.map(((item,index)=>{const itemProps=getItemProps(item,index);const sortableItemProps={item,index,direction:SortableDirection.Vertical,...itemProps};return renderItem(sortableItemProps)})))))}export const Sortable=memo((({data,renderItem,...props})=>{const dataHashKey=dataHash(data);return React.createElement(SortableComponent,{data,renderItem,...props,key:dataHashKey})}));const styles=StyleSheet.create({flex:{flex:1},scrollView:{flex:1,position:"relative",backgroundColor:"white"}});
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { SortableItemProps, SortableHandleProps } from "../types/sortable";
|
|
3
|
+
export declare function SortableItem<T>({ id, data, positions, direction, lowerBound, leftBound, autoScrollDirection, autoScrollHorizontalDirection, itemsCount, itemHeight, itemWidth, gap, paddingHorizontal, containerHeight, containerWidth, children, style, animatedStyle: customAnimatedStyle, onMove, onDragStart, onDrop, onDragging, onDraggingHorizontal, }: SortableItemProps<T>): React.JSX.Element;
|
|
4
|
+
export declare namespace SortableItem {
|
|
5
|
+
var Handle: ({ children, style }: SortableHandleProps) => React.JSX.Element;
|
|
6
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import React,{createContext,useContext}from"react";import Animated from"react-native-reanimated";import{GestureDetector}from"react-native-gesture-handler";import{useSortable}from"../hooks/useSortable";import{useHorizontalSortable}from"../hooks/useHorizontalSortable";import{SortableDirection}from"../types/sortable";const SortableContext=createContext(null);const SortableHandle=({children,style})=>{const sortableContext=useContext(SortableContext);if(!sortableContext){console.warn("SortableHandle must be used within a SortableItem component");return React.createElement(React.Fragment,null,children)}return React.createElement(GestureDetector,{gesture:sortableContext.panGestureHandler},React.createElement(Animated.View,{style},children))};export function SortableItem({id,data,positions,direction=SortableDirection.Vertical,lowerBound,leftBound,autoScrollDirection,autoScrollHorizontalDirection,itemsCount,itemHeight,itemWidth,gap=0,paddingHorizontal=0,containerHeight,containerWidth,children,style,animatedStyle:customAnimatedStyle,onMove,onDragStart,onDrop,onDragging,onDraggingHorizontal}){if(direction===SortableDirection.Vertical&&(!itemHeight||!lowerBound||!autoScrollDirection)){throw new Error("itemHeight, lowerBound, and autoScrollDirection are required for vertical direction")}if(direction===SortableDirection.Horizontal&&(!itemWidth||!leftBound||!autoScrollHorizontalDirection)){throw new Error("itemWidth, leftBound, and autoScrollHorizontalDirection are required for horizontal direction")}if(direction===SortableDirection.Horizontal){const horizontalOptions={id,positions,leftBound,autoScrollDirection:autoScrollHorizontalDirection,itemsCount,itemWidth,gap,paddingHorizontal,containerWidth,onMove,onDragStart,onDrop,onDragging:onDraggingHorizontal,children,handleComponent:SortableHandle};const{animatedStyle:horizontalAnimatedStyle,panGestureHandler:horizontalPanGestureHandler,isMoving:horizontalIsMoving,hasHandle:horizontalHasHandle}=useHorizontalSortable(horizontalOptions);const combinedAnimatedStyle=[horizontalAnimatedStyle,customAnimatedStyle];const contextValue={panGestureHandler:horizontalPanGestureHandler};const content=React.createElement(Animated.View,{style:combinedAnimatedStyle},React.createElement(SortableContext.Provider,{value:contextValue},React.createElement(Animated.View,{style},children)));if(horizontalHasHandle){return content}else{return React.createElement(GestureDetector,{gesture:horizontalPanGestureHandler},content)}}const verticalOptions={id,positions,lowerBound,autoScrollDirection,itemsCount,itemHeight,containerHeight,onMove,onDragStart,onDrop,onDragging,children,handleComponent:SortableHandle};const{animatedStyle:verticalAnimatedStyle,panGestureHandler:verticalPanGestureHandler,isMoving:verticalIsMoving,hasHandle:verticalHasHandle}=useSortable(verticalOptions);const combinedAnimatedStyle=[verticalAnimatedStyle,customAnimatedStyle];const contextValue={panGestureHandler:verticalPanGestureHandler};const content=React.createElement(Animated.View,{style:combinedAnimatedStyle},React.createElement(SortableContext.Provider,{value:contextValue},React.createElement(Animated.View,{style},children)));if(verticalHasHandle){return content}else{return React.createElement(GestureDetector,{gesture:verticalPanGestureHandler},content)}}SortableItem.Handle=SortableHandle;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { SharedValue } from "react-native-reanimated";
|
|
2
|
+
export declare enum ScrollDirection {
|
|
3
|
+
None = "none",
|
|
4
|
+
Up = "up",
|
|
5
|
+
Down = "down"
|
|
6
|
+
}
|
|
7
|
+
export declare enum HorizontalScrollDirection {
|
|
8
|
+
None = "none",
|
|
9
|
+
Left = "left",
|
|
10
|
+
Right = "right"
|
|
11
|
+
}
|
|
12
|
+
export declare function clamp(value: number, lowerBound: number, upperBound: number): number;
|
|
13
|
+
export declare function objectMove(object: {
|
|
14
|
+
[id: string]: number;
|
|
15
|
+
}, from: number, to: number): {
|
|
16
|
+
[id: string]: number;
|
|
17
|
+
};
|
|
18
|
+
export declare function listToObject<T extends {
|
|
19
|
+
id: string;
|
|
20
|
+
}>(list: T[]): {
|
|
21
|
+
[id: string]: number;
|
|
22
|
+
};
|
|
23
|
+
export declare function setPosition(positionY: number, itemsCount: number, positions: SharedValue<{
|
|
24
|
+
[id: string]: number;
|
|
25
|
+
}>, id: string, itemHeight: number): void;
|
|
26
|
+
export declare function setAutoScroll(positionY: number, lowerBound: number, upperBound: number, scrollThreshold: number, autoScroll: SharedValue<ScrollDirection>): void;
|
|
27
|
+
export declare function getItemXPosition(position: number, itemWidth: number, gap?: number, paddingHorizontal?: number): number;
|
|
28
|
+
export declare function getContentWidth(itemsCount: number, itemWidth: number, gap?: number, paddingHorizontal?: number): number;
|
|
29
|
+
export declare function setHorizontalPosition(positionX: number, itemsCount: number, positions: SharedValue<{
|
|
30
|
+
[id: string]: number;
|
|
31
|
+
}>, id: string, itemWidth: number, gap?: number, paddingHorizontal?: number): void;
|
|
32
|
+
export declare function setHorizontalAutoScroll(positionX: number, leftBound: number, rightBound: number, scrollThreshold: number, autoScrollDirection: SharedValue<HorizontalScrollDirection>): void;
|
|
33
|
+
export declare const dataHash: (data: any[]) => string;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export var ScrollDirection;(function(ScrollDirection){ScrollDirection["None"]="none";ScrollDirection["Up"]="up";ScrollDirection["Down"]="down"})(ScrollDirection||(ScrollDirection={}));export var HorizontalScrollDirection;(function(HorizontalScrollDirection){HorizontalScrollDirection["None"]="none";HorizontalScrollDirection["Left"]="left";HorizontalScrollDirection["Right"]="right"})(HorizontalScrollDirection||(HorizontalScrollDirection={}));export function clamp(value,lowerBound,upperBound){"worklet";return Math.max(lowerBound,Math.min(value,upperBound))}export function objectMove(object,from,to){"worklet";const newObject=Object.assign({},object);for(const id in object){if(object[id]===from){newObject[id]=to}if(object[id]===to){newObject[id]=from}}return newObject}export function listToObject(list){const values=Object.values(list);const object={};for(let i=0;i<values.length;i++){object[values[i].id]=i}return object}export function setPosition(positionY,itemsCount,positions,id,itemHeight){"worklet";const newPosition=clamp(Math.floor(positionY/itemHeight),0,itemsCount-1);if(newPosition!==positions.value[id]){positions.value=objectMove(positions.value,positions.value[id],newPosition)}}export function setAutoScroll(positionY,lowerBound,upperBound,scrollThreshold,autoScroll){"worklet";if(positionY<=lowerBound+scrollThreshold){autoScroll.value=ScrollDirection.Up}else if(positionY>=upperBound-scrollThreshold){autoScroll.value=ScrollDirection.Down}else{autoScroll.value=ScrollDirection.None}}export function getItemXPosition(position,itemWidth,gap=0,paddingHorizontal=0){"worklet";return paddingHorizontal+position*(itemWidth+gap)}export function getContentWidth(itemsCount,itemWidth,gap=0,paddingHorizontal=0){"worklet";if(itemsCount===0){return paddingHorizontal*2}const totalItemsWidth=itemsCount*itemWidth;const totalGaps=Math.max(0,itemsCount-1)*gap;return totalItemsWidth+totalGaps+paddingHorizontal*2}export function setHorizontalPosition(positionX,itemsCount,positions,id,itemWidth,gap=0,paddingHorizontal=0){"worklet";const adjustedX=positionX-paddingHorizontal;const itemWithGapWidth=itemWidth+gap;const newPosition=clamp(Math.floor(adjustedX/itemWithGapWidth),0,itemsCount-1);if(newPosition!==positions.value[id]){positions.value=objectMove(positions.value,positions.value[id],newPosition)}}export function setHorizontalAutoScroll(positionX,leftBound,rightBound,scrollThreshold,autoScrollDirection){"worklet";const effectiveThreshold=Math.max(scrollThreshold,60);const leftEdge=leftBound+effectiveThreshold;const rightEdge=rightBound-effectiveThreshold;if(positionX<leftEdge){autoScrollDirection.value=HorizontalScrollDirection.Left}else if(positionX>rightEdge){autoScrollDirection.value=HorizontalScrollDirection.Right}else{autoScrollDirection.value=HorizontalScrollDirection.None}}export const dataHash=data=>{const str=data.reduce(((acc,item)=>acc+item.id),"");let hash=0;for(let i=0,len=str.length;i<len;i++){let chr=str.charCodeAt(i);hash=(hash<<5)-hash+chr;hash|=0}return hash.toString()};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import React,{useRef,useState,useMemo,useCallback,forwardRef,useImperativeHandle,useEffect}from"react";import{SlotsContext}from"../types/context";export const DropProvider=forwardRef((({children,onLayoutUpdateComplete,onDroppedItemsUpdate,onDragging,onDragStart,onDragEnd},ref)=>{const slotsRef=useRef({});const[activeHoverSlotId,setActiveHoverSlotIdState]=useState(null);const[droppedItems,setDroppedItems]=useState({});const positionUpdateListenersRef=useRef({});const registerPositionUpdateListener=useCallback(((id,listener)=>{positionUpdateListenersRef.current[id]=listener}),[]);const unregisterPositionUpdateListener=useCallback((id=>{delete positionUpdateListenersRef.current[id]}),[]);useEffect((()=>{if(onDroppedItemsUpdate){onDroppedItemsUpdate(droppedItems)}}),[droppedItems,onDroppedItemsUpdate]);const registerDroppedItem=useCallback(((draggableId,droppableId,itemData)=>{setDroppedItems((prev=>({...prev,[draggableId]:{droppableId,data:itemData}})))}),[]);const unregisterDroppedItem=useCallback((draggableId=>{setDroppedItems((prev=>{const newItems={...prev};delete newItems[draggableId];return newItems}))}),[]);const getDroppedItems=useCallback((()=>droppedItems),[droppedItems]);const internalRequestPositionUpdate=useCallback((()=>{const listeners=positionUpdateListenersRef.current;Object.values(listeners).forEach((listener=>{listener()}));onLayoutUpdateComplete===null||onLayoutUpdateComplete===void 0?void 0:onLayoutUpdateComplete()}),[onLayoutUpdateComplete]);useImperativeHandle(ref,(()=>({requestPositionUpdate:internalRequestPositionUpdate,getDroppedItems})));const hasAvailableCapacity=useCallback((droppableId=>{const droppedCount=Object.values(droppedItems).filter((item=>item.droppableId===droppableId)).length;const droppableSlot=Object.values(slotsRef.current).find((slot=>slot.id===droppableId));if(!droppableSlot){return false}const capacity=droppableSlot.capacity!==undefined?droppableSlot.capacity:1;return droppedCount<capacity}),[droppedItems]);const handleDragStart=useCallback((data=>{if(onDragStart){onDragStart(data)}internalRequestPositionUpdate()}),[onDragStart,internalRequestPositionUpdate]);const contextValue=useMemo((()=>({register:(id,slot)=>{slotsRef.current[id]=slot},unregister:id=>{delete slotsRef.current[id]},isRegistered:id=>slotsRef.current[id]!==undefined,getSlots:()=>slotsRef.current,setActiveHoverSlot:id=>setActiveHoverSlotIdState(id),activeHoverSlotId,registerPositionUpdateListener,unregisterPositionUpdateListener,requestPositionUpdate:internalRequestPositionUpdate,registerDroppedItem,unregisterDroppedItem,getDroppedItems,hasAvailableCapacity,onDragging,onDragStart:handleDragStart,onDragEnd})),[activeHoverSlotId,registerPositionUpdateListener,unregisterPositionUpdateListener,internalRequestPositionUpdate,registerDroppedItem,unregisterDroppedItem,getDroppedItems,hasAvailableCapacity,onDragging,handleDragStart,onDragEnd]);return React.createElement(SlotsContext.Provider,{value:contextValue},children)}));DropProvider.displayName="DropProvider";
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { useSortable } from "./useSortable";
|
|
2
|
+
export { useSortableList } from "./useSortableList";
|
|
3
|
+
export { useHorizontalSortable } from "./useHorizontalSortable";
|
|
4
|
+
export { useHorizontalSortableList } from "./useHorizontalSortableList";
|
|
5
|
+
export { useDraggable } from "./useDraggable";
|
|
6
|
+
export { useDroppable } from "./useDroppable";
|