@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.
Files changed (44) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1238 -0
  3. package/documentation/web-docs/README.md +41 -0
  4. package/example-app/.expo/README.md +8 -0
  5. package/example-app/README.md +515 -0
  6. package/lib/components/Draggable.d.ts +5 -0
  7. package/lib/components/Draggable.js +1 -0
  8. package/lib/components/Droppable.d.ts +3 -0
  9. package/lib/components/Droppable.js +1 -0
  10. package/lib/components/Sortable.d.ts +3 -0
  11. package/lib/components/Sortable.js +1 -0
  12. package/lib/components/SortableItem.d.ts +6 -0
  13. package/lib/components/SortableItem.js +1 -0
  14. package/lib/components/sortableUtils.d.ts +33 -0
  15. package/lib/components/sortableUtils.js +1 -0
  16. package/lib/context/DropContext.d.ts +3 -0
  17. package/lib/context/DropContext.js +1 -0
  18. package/lib/hooks/index.d.ts +6 -0
  19. package/lib/hooks/index.js +1 -0
  20. package/lib/hooks/useDraggable.d.ts +2 -0
  21. package/lib/hooks/useDraggable.js +1 -0
  22. package/lib/hooks/useDroppable.d.ts +2 -0
  23. package/lib/hooks/useDroppable.js +1 -0
  24. package/lib/hooks/useHorizontalSortable.d.ts +2 -0
  25. package/lib/hooks/useHorizontalSortable.js +1 -0
  26. package/lib/hooks/useHorizontalSortableList.d.ts +4 -0
  27. package/lib/hooks/useHorizontalSortableList.js +1 -0
  28. package/lib/hooks/useSortable.d.ts +50 -0
  29. package/lib/hooks/useSortable.js +1 -0
  30. package/lib/hooks/useSortableList.d.ts +27 -0
  31. package/lib/hooks/useSortableList.js +1 -0
  32. package/lib/index.d.ts +13 -0
  33. package/lib/index.js +1 -0
  34. package/lib/types/context.d.ts +67 -0
  35. package/lib/types/context.js +1 -0
  36. package/lib/types/draggable.d.ts +54 -0
  37. package/lib/types/draggable.js +1 -0
  38. package/lib/types/droppable.d.ts +26 -0
  39. package/lib/types/droppable.js +1 -0
  40. package/lib/types/index.d.ts +4 -0
  41. package/lib/types/index.js +1 -0
  42. package/lib/types/sortable.d.ts +233 -0
  43. package/lib/types/sortable.js +1 -0
  44. 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
+ [![React Native](https://img.shields.io/badge/React%20Native-0.72+-blue.svg)](https://reactnative.dev/)
10
+ [![Expo](https://img.shields.io/badge/Expo-SDK%2049+-black.svg)](https://expo.dev/)
11
+ [![TypeScript](https://img.shields.io/badge/TypeScript-Ready-blue.svg)](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,3 @@
1
+ import React from "react";
2
+ import { SortableProps } from "../types/sortable";
3
+ export declare const Sortable: React.MemoExoticComponent<({ data, renderItem, ...props }: SortableProps<any>) => React.JSX.Element>;
@@ -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,3 @@
1
+ import React from "react";
2
+ import { DropProviderProps, DropProviderRef } from "../types/context";
3
+ export declare const DropProvider: React.ForwardRefExoticComponent<DropProviderProps & React.RefAttributes<DropProviderRef>>;
@@ -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";