react-native-reanimated-dnd 1.0.2 → 1.0.4

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.
@@ -1,265 +1 @@
1
- // Node Modules
2
- import React, { createContext, useContext } from "react";
3
- import Animated from "react-native-reanimated";
4
- import { GestureDetector } from "react-native-gesture-handler";
5
- import { useDraggable } from "../hooks/useDraggable";
6
- // Create a context to share gesture and state between Draggable and Handle
7
- const DraggableContext = createContext(null);
8
- /**
9
- * A handle component that can be used within Draggable to create a specific
10
- * draggable area. When a Handle is present, only the handle area can initiate
11
- * dragging, while the rest of the draggable remains non-interactive for dragging.
12
- *
13
- * @param props - Props for the handle component
14
- *
15
- * @example
16
- * Basic drag handle:
17
- * ```typescript
18
- * <Draggable data={{ id: '1', name: 'Item 1' }}>
19
- * <View style={styles.itemContent}>
20
- * <Text>Item content (not draggable)</Text>
21
- *
22
- * <Draggable.Handle style={styles.dragHandle}>
23
- * <Icon name="drag-handle" size={20} />
24
- * </Draggable.Handle>
25
- * </View>
26
- * </Draggable>
27
- * ```
28
- *
29
- * @example
30
- * Custom styled handle:
31
- * ```typescript
32
- * <Draggable data={{ id: '2', type: 'card' }}>
33
- * <View style={styles.card}>
34
- * <Text style={styles.title}>Card Title</Text>
35
- * <Text style={styles.content}>Card content...</Text>
36
- *
37
- * <Draggable.Handle style={styles.customHandle}>
38
- * <View style={styles.handleDots}>
39
- * <View style={styles.dot} />
40
- * <View style={styles.dot} />
41
- * <View style={styles.dot} />
42
- * </View>
43
- * </Draggable.Handle>
44
- * </View>
45
- * </Draggable>
46
- * ```
47
- */
48
- const Handle = ({ children, style }) => {
49
- const draggableContext = useContext(DraggableContext);
50
- if (!draggableContext) {
51
- console.warn("Draggable.Handle must be used within a Draggable component");
52
- return <>{children}</>;
53
- }
54
- return (<GestureDetector gesture={draggableContext.gesture}>
55
- <Animated.View style={style}>{children}</Animated.View>
56
- </GestureDetector>);
57
- };
58
- /**
59
- * A versatile draggable component with advanced features like collision detection,
60
- * bounded dragging, axis constraints, and custom animations.
61
- *
62
- * The Draggable component provides a complete drag-and-drop solution that can be
63
- * used standalone or within a DropProvider context for drop zone interactions.
64
- * It supports both full-item dragging and handle-based dragging patterns.
65
- *
66
- * @template TData - The type of data associated with the draggable item
67
- * @param props - Configuration props for the draggable component
68
- *
69
- * @example
70
- * Basic draggable item:
71
- * ```typescript
72
- * import { Draggable } from './components/Draggable';
73
- *
74
- * function BasicDraggable() {
75
- * return (
76
- * <Draggable
77
- * data={{ id: '1', name: 'Draggable Item', type: 'task' }}
78
- * onDragStart={(data) => console.log('Started dragging:', data.name)}
79
- * onDragEnd={(data) => console.log('Finished dragging:', data.name)}
80
- * >
81
- * <View style={styles.item}>
82
- * <Text>Drag me anywhere!</Text>
83
- * </View>
84
- * </Draggable>
85
- * );
86
- * }
87
- * ```
88
- *
89
- * @example
90
- * Draggable with custom animation and constraints:
91
- * ```typescript
92
- * function ConstrainedDraggable() {
93
- * const boundsRef = useRef<View>(null);
94
- *
95
- * return (
96
- * <View ref={boundsRef} style={styles.container}>
97
- * <Draggable
98
- * data={{ id: '2', category: 'bounded' }}
99
- * dragBoundsRef={boundsRef}
100
- * dragAxis="x" // Only horizontal movement
101
- * animationFunction={(toValue) => {
102
- * 'worklet';
103
- * return withTiming(toValue, { duration: 300 });
104
- * }}
105
- * collisionAlgorithm="center"
106
- * style={styles.draggableItem}
107
- * >
108
- * <View style={styles.slider}>
109
- * <Text>Horizontal Slider</Text>
110
- * </View>
111
- * </Draggable>
112
- * </View>
113
- * );
114
- * }
115
- * ```
116
- *
117
- * @example
118
- * Draggable with handle and state tracking:
119
- * ```typescript
120
- * function HandleDraggable() {
121
- * const [dragState, setDragState] = useState(DraggableState.IDLE);
122
- *
123
- * return (
124
- * <Draggable
125
- * data={{ id: '3', title: 'Card with Handle' }}
126
- * onStateChange={setDragState}
127
- * onDragging={({ x, y, tx, ty }) => {
128
- * console.log(`Position: (${x + tx}, ${y + ty})`);
129
- * }}
130
- * style={[
131
- * styles.card,
132
- * dragState === DraggableState.DRAGGING && styles.dragging
133
- * ]}
134
- * >
135
- * <View style={styles.cardContent}>
136
- * <Text style={styles.cardTitle}>Card Title</Text>
137
- * <Text style={styles.cardBody}>
138
- * This card can only be dragged by its handle.
139
- * </Text>
140
- *
141
- * <Draggable.Handle style={styles.dragHandle}>
142
- * <View style={styles.handleIcon}>
143
- * <Text>⋮⋮</Text>
144
- * </View>
145
- * </Draggable.Handle>
146
- * </View>
147
- * </Draggable>
148
- * );
149
- * }
150
- * ```
151
- *
152
- * @example
153
- * Draggable with collision detection and drop zones:
154
- * ```typescript
155
- * function DragDropExample() {
156
- * return (
157
- * <DropProvider>
158
- * <View style={styles.container}>
159
- * <Draggable
160
- * data={{ id: '4', type: 'file', name: 'document.pdf' }}
161
- * collisionAlgorithm="intersect"
162
- * onDragStart={(data) => {
163
- * hapticFeedback();
164
- * setDraggedItem(data);
165
- * }}
166
- * onDragEnd={(data) => {
167
- * setDraggedItem(null);
168
- * }}
169
- * >
170
- * <View style={styles.fileItem}>
171
- * <Icon name="file-pdf" size={24} />
172
- * <Text>{data.name}</Text>
173
- * </View>
174
- * </Draggable>
175
- *
176
- * <Droppable
177
- * onDrop={(data) => {
178
- * console.log('File dropped:', data.name);
179
- * moveToTrash(data.id);
180
- * }}
181
- * >
182
- * <View style={styles.trashZone}>
183
- * <Icon name="trash" size={32} />
184
- * <Text>Drop files here to delete</Text>
185
- * </View>
186
- * </Droppable>
187
- * </View>
188
- * </DropProvider>
189
- * );
190
- * }
191
- * ```
192
- *
193
- * @example
194
- * Draggable with disabled state and conditional behavior:
195
- * ```typescript
196
- * function ConditionalDraggable({ item, canDrag }) {
197
- * return (
198
- * <Draggable
199
- * data={item}
200
- * dragDisabled={!canDrag}
201
- * onDragStart={(data) => {
202
- * if (data.locked) {
203
- * showError('This item is locked');
204
- * return;
205
- * }
206
- * analytics.track('drag_start', { itemId: data.id });
207
- * }}
208
- * style={[
209
- * styles.item,
210
- * !canDrag && styles.disabled
211
- * ]}
212
- * >
213
- * <View style={styles.itemContent}>
214
- * <Text style={styles.itemTitle}>{item.title}</Text>
215
- * {item.locked && <Icon name="lock" size={16} />}
216
- * {!canDrag && <Text style={styles.disabledText}>Drag disabled</Text>}
217
- * </View>
218
- * </Draggable>
219
- * );
220
- * }
221
- * ```
222
- *
223
- * @see {@link Draggable.Handle} for creating drag handles
224
- * @see {@link useDraggable} for the underlying hook
225
- * @see {@link Droppable} for drop zone components
226
- * @see {@link DraggableState} for state management
227
- * @see {@link CollisionAlgorithm} for collision detection options
228
- * @see {@link UseDraggableOptions} for configuration options
229
- * @see {@link UseDraggableReturn} for hook return details
230
- * @see {@link DropProvider} for drag-and-drop context setup
231
- */
232
- const DraggableComponent = ({
233
- // Destructure component-specific props first
234
- style: componentStyle, children,
235
- // Collect all other props (which are now the modified UseDraggableOptions)
236
- ...useDraggableHookOptions }) => {
237
- // Pass the collected useDraggableHookOptions object directly to the hook
238
- // Also pass children and Handle component reference for handle detection
239
- const { animatedViewProps, gesture, state, hasHandle, animatedViewRef } = useDraggable({
240
- ...useDraggableHookOptions,
241
- children,
242
- handleComponent: Handle,
243
- });
244
- // Create the context value
245
- const contextValue = {
246
- gesture,
247
- state,
248
- };
249
- // Render the component
250
- const content = (<Animated.View ref={animatedViewRef} {...animatedViewProps} style={[componentStyle, animatedViewProps.style]} collapsable={false}>
251
- <DraggableContext.Provider value={contextValue}>
252
- {children}
253
- </DraggableContext.Provider>
254
- </Animated.View>);
255
- // If a handle is found, let the handle control the dragging
256
- // Otherwise, the entire component is draggable
257
- if (hasHandle) {
258
- return content;
259
- }
260
- else {
261
- return <GestureDetector gesture={gesture}>{content}</GestureDetector>;
262
- }
263
- };
264
- // Attach the Handle as a static property
265
- export const Draggable = Object.assign(DraggableComponent, { Handle });
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),Handle=({children:e,style:t})=>{const a=useContext(DraggableContext);return a?React.createElement(GestureDetector,{gesture:a.gesture},React.createElement(Animated.View,{style:t},e)):React.createElement(React.Fragment,null,e)},DraggableComponent=({style:e,children:t,...a})=>{const{animatedViewProps:r,gesture:n,state:l,hasHandle:o,animatedViewRef:c}=useDraggable({...a,children:t,handleComponent:Handle}),s={gesture:n,state:l},g=React.createElement(Animated.View,{ref:c,...r,style:[e,r.style],collapsable:!1},React.createElement(DraggableContext.Provider,{value:s},t));return o?g:React.createElement(GestureDetector,{gesture:n},g)};export const Draggable=Object.assign(DraggableComponent,{Handle});
@@ -1,264 +1,4 @@
1
1
  import React from "react";
2
2
  import { DroppableProps } from "../types/droppable";
3
3
  export declare const _getUniqueDroppableId: () => number;
4
- /**
5
- * A component that creates drop zones for receiving draggable items.
6
- *
7
- * The Droppable component provides visual feedback when draggable items hover over it
8
- * and handles the drop logic when items are released. It integrates seamlessly with
9
- * the drag-and-drop context to provide collision detection and proper positioning
10
- * of dropped items.
11
- *
12
- * @template TData - The type of data that can be dropped on this droppable
13
- * @param props - Configuration props for the droppable component
14
- *
15
- * @example
16
- * Basic drop zone:
17
- * ```typescript
18
- * import { Droppable } from './components/Droppable';
19
- *
20
- * function BasicDropZone() {
21
- * const handleDrop = (data) => {
22
- * console.log('Item dropped:', data);
23
- * // Handle the dropped item
24
- * addItemToList(data);
25
- * };
26
- *
27
- * return (
28
- * <Droppable onDrop={handleDrop}>
29
- * <View style={styles.dropZone}>
30
- * <Text>Drop items here</Text>
31
- * </View>
32
- * </Droppable>
33
- * );
34
- * }
35
- * ```
36
- *
37
- * @example
38
- * Drop zone with visual feedback:
39
- * ```typescript
40
- * function VisualDropZone() {
41
- * const [isHovered, setIsHovered] = useState(false);
42
- *
43
- * return (
44
- * <Droppable
45
- * onDrop={(data) => {
46
- * console.log('Dropped:', data.name);
47
- * processDroppedItem(data);
48
- * }}
49
- * onActiveChange={setIsHovered}
50
- * activeStyle={{
51
- * backgroundColor: 'rgba(0, 255, 0, 0.2)',
52
- * borderColor: '#00ff00',
53
- * borderWidth: 2,
54
- * transform: [{ scale: 1.05 }]
55
- * }}
56
- * style={styles.dropZone}
57
- * >
58
- * <View style={[
59
- * styles.dropContent,
60
- * isHovered && styles.hoveredContent
61
- * ]}>
62
- * <Icon
63
- * name="cloud-upload"
64
- * size={32}
65
- * color={isHovered ? '#00ff00' : '#666'}
66
- * />
67
- * <Text style={styles.dropText}>
68
- * {isHovered ? 'Release to drop' : 'Drag files here'}
69
- * </Text>
70
- * </View>
71
- * </Droppable>
72
- * );
73
- * }
74
- * ```
75
- *
76
- * @example
77
- * Drop zone with custom alignment and capacity:
78
- * ```typescript
79
- * function TaskColumn() {
80
- * const [tasks, setTasks] = useState([]);
81
- * const maxTasks = 5;
82
- *
83
- * return (
84
- * <Droppable
85
- * droppableId="todo-column"
86
- * onDrop={(task) => {
87
- * if (tasks.length < maxTasks) {
88
- * setTasks(prev => [...prev, task]);
89
- * updateTaskStatus(task.id, 'todo');
90
- * }
91
- * }}
92
- * dropAlignment="top-center"
93
- * dropOffset={{ x: 0, y: 10 }}
94
- * capacity={maxTasks}
95
- * activeStyle={{
96
- * backgroundColor: 'rgba(59, 130, 246, 0.1)',
97
- * borderColor: '#3b82f6',
98
- * borderWidth: 2,
99
- * borderStyle: 'dashed'
100
- * }}
101
- * style={styles.column}
102
- * >
103
- * <Text style={styles.columnTitle}>
104
- * To Do ({tasks.length}/{maxTasks})
105
- * </Text>
106
- *
107
- * {tasks.map(task => (
108
- * <TaskCard key={task.id} task={task} />
109
- * ))}
110
- *
111
- * {tasks.length === 0 && (
112
- * <Text style={styles.emptyText}>
113
- * Drop tasks here
114
- * </Text>
115
- * )}
116
- * </Droppable>
117
- * );
118
- * }
119
- * ```
120
- *
121
- * @example
122
- * Conditional drop zone with validation:
123
- * ```typescript
124
- * function RestrictedDropZone() {
125
- * const [canAcceptFiles, setCanAcceptFiles] = useState(true);
126
- * const [uploadProgress, setUploadProgress] = useState(0);
127
- *
128
- * const handleDrop = (fileData) => {
129
- * // Validate file type and size
130
- * if (fileData.type !== 'image') {
131
- * showError('Only image files are allowed');
132
- * return;
133
- * }
134
- *
135
- * if (fileData.size > 5000000) { // 5MB limit
136
- * showError('File size must be under 5MB');
137
- * return;
138
- * }
139
- *
140
- * // Start upload
141
- * setCanAcceptFiles(false);
142
- * uploadFile(fileData, setUploadProgress)
143
- * .then(() => {
144
- * showSuccess('File uploaded successfully');
145
- * setCanAcceptFiles(true);
146
- * setUploadProgress(0);
147
- * })
148
- * .catch(() => {
149
- * showError('Upload failed');
150
- * setCanAcceptFiles(true);
151
- * setUploadProgress(0);
152
- * });
153
- * };
154
- *
155
- * return (
156
- * <Droppable
157
- * onDrop={handleDrop}
158
- * dropDisabled={!canAcceptFiles}
159
- * onActiveChange={(active) => {
160
- * if (active && !canAcceptFiles) {
161
- * showTooltip('Upload in progress...');
162
- * }
163
- * }}
164
- * activeStyle={{
165
- * backgroundColor: canAcceptFiles
166
- * ? 'rgba(34, 197, 94, 0.1)'
167
- * : 'rgba(239, 68, 68, 0.1)',
168
- * borderColor: canAcceptFiles ? '#22c55e' : '#ef4444'
169
- * }}
170
- * style={[
171
- * styles.uploadZone,
172
- * !canAcceptFiles && styles.disabled
173
- * ]}
174
- * >
175
- * <View style={styles.uploadContent}>
176
- * {uploadProgress > 0 ? (
177
- * <>
178
- * <ProgressBar progress={uploadProgress} />
179
- * <Text>Uploading... {Math.round(uploadProgress * 100)}%</Text>
180
- * </>
181
- * ) : (
182
- * <>
183
- * <Icon
184
- * name="image"
185
- * size={48}
186
- * color={canAcceptFiles ? '#22c55e' : '#ef4444'}
187
- * />
188
- * <Text style={styles.uploadText}>
189
- * {canAcceptFiles
190
- * ? 'Drop images here (max 5MB)'
191
- * : 'Upload in progress...'}
192
- * </Text>
193
- * </>
194
- * )}
195
- * </View>
196
- * </Droppable>
197
- * );
198
- * }
199
- * ```
200
- *
201
- * @example
202
- * Multiple drop zones with different behaviors:
203
- * ```typescript
204
- * function MultiDropZoneExample() {
205
- * const [items, setItems] = useState([]);
206
- * const [trash, setTrash] = useState([]);
207
- *
208
- * return (
209
- * <DropProvider>
210
- * <View style={styles.container}>
211
- * {/* Source items *\/}
212
- * {items.map(item => (
213
- * <Draggable key={item.id} data={item}>
214
- * <ItemCard item={item} />
215
- * </Draggable>
216
- * ))}
217
- *
218
- * {/* Archive drop zone *\/}
219
- * <Droppable
220
- * droppableId="archive"
221
- * onDrop={(item) => {
222
- * archiveItem(item.id);
223
- * setItems(prev => prev.filter(i => i.id !== item.id));
224
- * }}
225
- * dropAlignment="center"
226
- * activeStyle={styles.archiveActive}
227
- * >
228
- * <View style={styles.archiveZone}>
229
- * <Icon name="archive" size={24} />
230
- * <Text>Archive</Text>
231
- * </View>
232
- * </Droppable>
233
- *
234
- * {/* Trash drop zone *\/}
235
- * <Droppable
236
- * droppableId="trash"
237
- * onDrop={(item) => {
238
- * setTrash(prev => [...prev, item]);
239
- * setItems(prev => prev.filter(i => i.id !== item.id));
240
- * }}
241
- * dropAlignment="center"
242
- * activeStyle={styles.trashActive}
243
- * capacity={10} // Max 10 items in trash
244
- * >
245
- * <View style={styles.trashZone}>
246
- * <Icon name="trash" size={24} />
247
- * <Text>Trash ({trash.length}/10)</Text>
248
- * </View>
249
- * </Droppable>
250
- * </View>
251
- * </DropProvider>
252
- * );
253
- * }
254
- * ```
255
- *
256
- * @see {@link useDroppable} for the underlying hook
257
- * @see {@link Draggable} for draggable components
258
- * @see {@link DropAlignment} for alignment options
259
- * @see {@link DropOffset} for offset configuration
260
- * @see {@link UseDroppableOptions} for configuration options
261
- * @see {@link UseDroppableReturn} for hook return details
262
- * @see {@link DropProvider} for drag-and-drop context setup
263
- */
264
4
  export declare const Droppable: <TData = unknown>({ onDrop, dropDisabled, onActiveChange, dropAlignment, dropOffset, activeStyle, droppableId, capacity, style, children, }: DroppableProps<TData>) => React.ReactElement;