react-native-swappable-grid 1.0.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/README.md +284 -0
  2. package/lib/commonjs/ChildWrapper.js +320 -0
  3. package/lib/commonjs/ChildWrapper.js.map +1 -0
  4. package/lib/commonjs/SwappableGrid.js +378 -0
  5. package/lib/commonjs/SwappableGrid.js.map +1 -0
  6. package/lib/commonjs/index.js +14 -0
  7. package/lib/commonjs/index.js.map +1 -0
  8. package/lib/commonjs/utils/helpers/computerMinHeight.js +11 -0
  9. package/lib/commonjs/utils/helpers/computerMinHeight.js.map +1 -0
  10. package/lib/commonjs/utils/helpers/gestures/PanWithLongPress.js +249 -0
  11. package/lib/commonjs/utils/helpers/gestures/PanWithLongPress.js.map +1 -0
  12. package/lib/commonjs/utils/helpers/indexCalculations.js +73 -0
  13. package/lib/commonjs/utils/helpers/indexCalculations.js.map +1 -0
  14. package/lib/commonjs/utils/useGridLayout.js +205 -0
  15. package/lib/commonjs/utils/useGridLayout.js.map +1 -0
  16. package/lib/module/ChildWrapper.js +313 -0
  17. package/lib/module/ChildWrapper.js.map +1 -0
  18. package/lib/module/SwappableGrid.js +370 -0
  19. package/lib/module/SwappableGrid.js.map +1 -0
  20. package/lib/module/index.js +2 -0
  21. package/lib/module/index.js.map +1 -0
  22. package/lib/module/utils/helpers/computerMinHeight.js +5 -0
  23. package/lib/module/utils/helpers/computerMinHeight.js.map +1 -0
  24. package/lib/module/utils/helpers/gestures/PanWithLongPress.js +242 -0
  25. package/lib/module/utils/helpers/gestures/PanWithLongPress.js.map +1 -0
  26. package/lib/module/utils/helpers/indexCalculations.js +64 -0
  27. package/lib/module/utils/helpers/indexCalculations.js.map +1 -0
  28. package/lib/module/utils/useGridLayout.js +199 -0
  29. package/lib/module/utils/useGridLayout.js.map +1 -0
  30. package/lib/typescript/ChildWrapper.d.ts +23 -0
  31. package/lib/typescript/SwappableGrid.d.ts +85 -0
  32. package/lib/typescript/index.d.ts +2 -0
  33. package/lib/typescript/utils/helpers/computerMinHeight.d.ts +1 -0
  34. package/lib/typescript/utils/helpers/gestures/PanWithLongPress.d.ts +40 -0
  35. package/lib/typescript/utils/helpers/indexCalculations.d.ts +28 -0
  36. package/lib/typescript/utils/useGridLayout.d.ts +46 -0
  37. package/package.json +68 -0
  38. package/src/ChildWrapper.tsx +376 -0
  39. package/src/SwappableGrid.tsx +492 -0
  40. package/src/index.ts +2 -0
  41. package/src/utils/helpers/computerMinHeight.ts +9 -0
  42. package/src/utils/helpers/gestures/PanWithLongPress.ts +304 -0
  43. package/src/utils/helpers/indexCalculations.ts +91 -0
  44. package/src/utils/useGridLayout.ts +236 -0
package/README.md ADDED
@@ -0,0 +1,284 @@
1
+ # react-native-swappable-grid
2
+
3
+ A powerful React Native component for creating draggable, swappable grid layouts with smooth animations, reordering, and delete functionality.
4
+
5
+ ## Features
6
+
7
+ - 🎯 **Drag & Drop**: Long press to drag and reorder items in a grid layout
8
+ - 📐 **Flexible Layout**: Automatic column calculation or fixed number of columns
9
+ - 🎨 **Smooth Animations**: Optional wiggle animation during drag mode
10
+ - 🗑️ **Delete Support**: Built-in hold-to-delete or custom delete component
11
+ - ➕ **Trailing Components**: Support for additional components (e.g., "Add" button)
12
+ - 📜 **Auto-scroll**: Automatic scrolling when dragging near edges
13
+ - 🔄 **Order Tracking**: Callbacks for order changes and drag end events
14
+ - ⚡ **Performance**: Built with React Native Reanimated and Gesture Handler for 60fps animations
15
+
16
+ ## Installation
17
+
18
+ ```bash
19
+ npm install react-native-swappable-grid
20
+ # or
21
+ yarn add react-native-swappable-grid
22
+ ```
23
+
24
+ ### Peer Dependencies
25
+
26
+ This package requires the following peer dependencies:
27
+
28
+ ```bash
29
+ npm install react react-native react-native-gesture-handler react-native-reanimated
30
+ # or
31
+ yarn add react react-native react-native-gesture-handler react-native-reanimated
32
+ ```
33
+
34
+ **Important**: Make sure to follow the setup instructions for:
35
+
36
+ - [react-native-gesture-handler](https://docs.swmansion.com/react-native-gesture-handler/docs/installation)
37
+ - [react-native-reanimated](https://docs.swmansion.com/react-native-reanimated/docs/installation)
38
+
39
+ ## Basic Usage
40
+
41
+ ```tsx
42
+ import React, { useState } from "react";
43
+ import { View, Text } from "react-native";
44
+ import { SwappableGrid } from "react-native-swappable-grid";
45
+
46
+ const MyComponent = () => {
47
+ const [items, setItems] = useState([
48
+ { id: "1", title: "Item 1" },
49
+ { id: "2", title: "Item 2" },
50
+ { id: "3", title: "Item 3" },
51
+ ]);
52
+
53
+ return (
54
+ <SwappableGrid
55
+ itemWidth={100}
56
+ itemHeight={100}
57
+ numColumns={3}
58
+ gap={8}
59
+ onOrderChange={(keys) => {
60
+ // Reorder items based on new key order
61
+ const newOrder = keys
62
+ .map((key) => items.find((item) => item.id === key))
63
+ .filter(Boolean);
64
+ setItems(newOrder);
65
+ }}
66
+ >
67
+ {items.map((item) => (
68
+ <View
69
+ key={item.id}
70
+ style={{ backgroundColor: "#ccc", borderRadius: 8 }}
71
+ >
72
+ <Text>{item.title}</Text>
73
+ </View>
74
+ ))}
75
+ </SwappableGrid>
76
+ );
77
+ };
78
+ ```
79
+
80
+ ## API Reference
81
+
82
+ ### SwappableGrid Props
83
+
84
+ | Prop | Type | Required | Default | Description |
85
+ | ------------------------ | --------------------------------------- | -------- | ------- | ---------------------------------------------------------------------------------------------------------- |
86
+ | `children` | `ReactNode` | ✅ | - | The child components to render in the grid. Each child should have a unique key. |
87
+ | `itemWidth` | `number` | ✅ | - | Width of each grid item in pixels |
88
+ | `itemHeight` | `number` | ✅ | - | Height of each grid item in pixels |
89
+ | `gap` | `number` | ❌ | `8` | Gap between grid items in pixels |
90
+ | `containerPadding` | `number` | ❌ | `8` | Padding around the container in pixels |
91
+ | `longPressMs` | `number` | ❌ | `300` | Duration in milliseconds to hold before drag starts |
92
+ | `numColumns` | `number` | ❌ | Auto | Number of columns in the grid. If not provided, will be calculated automatically based on container width |
93
+ | `wiggle` | `{ duration: number; degrees: number }` | ❌ | - | Wiggle animation configuration when items are in drag mode or delete mode |
94
+ | `onDragEnd` | `(ordered: ChildNode[]) => void` | ❌ | - | Callback fired when drag ends, providing the ordered array of child nodes |
95
+ | `onOrderChange` | `(keys: string[]) => void` | ❌ | - | Callback fired when the order changes, providing an array of keys in the new order |
96
+ | `onDelete` | `(key: string) => void` | ❌ | - | Callback fired when an item is deleted, providing the key of the deleted item |
97
+ | `dragSizeIncreaseFactor` | `number` | ❌ | `1.06` | Factor by which the dragged item scales up |
98
+ | `scrollSpeed` | `number` | ❌ | `10` | Speed of auto-scrolling when dragging near edges |
99
+ | `scrollThreshold` | `number` | ❌ | `100` | Distance from edge in pixels that triggers auto-scroll |
100
+ | `style` | `StyleProp<ViewStyle>` | ❌ | - | Custom style for the ScrollView container |
101
+ | `trailingComponent` | `ReactNode` | ❌ | - | Component to render after all grid items (e.g., an "Add" button) |
102
+ | `deleteComponent` | `ReactNode` | ❌ | - | Component to render as a delete target (shown when dragging). If provided, disables hold-to-delete feature |
103
+ | `deleteComponentStyle` | `StyleProp<ViewStyle>` | ❌ | - | Custom style for the delete component. If provided, allows custom positioning |
104
+ | `reverse` | `boolean` | ❌ | `false` | If true, reverses the order of items (right-to-left, bottom-to-top) |
105
+
106
+ ### SwappableGridRef
107
+
108
+ The component can be used with a ref to access imperative methods:
109
+
110
+ ```tsx
111
+ const gridRef = useRef<SwappableGridRef>(null);
112
+
113
+ <SwappableGrid ref={gridRef} ... />
114
+
115
+ // Cancel delete mode programmatically
116
+ gridRef.current?.cancelDeleteMode();
117
+ ```
118
+
119
+ | Method | Description |
120
+ | -------------------- | --------------------------------------------------------------- |
121
+ | `cancelDeleteMode()` | Cancels the delete mode if any item is currently in delete mode |
122
+
123
+ ## Examples
124
+
125
+ ### With Wiggle Animation
126
+
127
+ ```tsx
128
+ <SwappableGrid
129
+ itemWidth={100}
130
+ itemHeight={100}
131
+ numColumns={3}
132
+ wiggle={{ duration: 200, degrees: 3 }}
133
+ onOrderChange={(keys) => console.log("New order:", keys)}
134
+ >
135
+ {items.map((item) => (
136
+ <View key={item.id}>{item.content}</View>
137
+ ))}
138
+ </SwappableGrid>
139
+ ```
140
+
141
+ ### With Delete Functionality (Hold-to-Delete)
142
+
143
+ ```tsx
144
+ <SwappableGrid
145
+ itemWidth={100}
146
+ itemHeight={100}
147
+ numColumns={3}
148
+ onDelete={(key) => {
149
+ setItems(items.filter((item) => item.id !== key));
150
+ }}
151
+ >
152
+ {items.map((item) => (
153
+ <View key={item.id}>{item.content}</View>
154
+ ))}
155
+ </SwappableGrid>
156
+ ```
157
+
158
+ ### With Delete Component
159
+
160
+ ```tsx
161
+ <SwappableGrid
162
+ itemWidth={100}
163
+ itemHeight={100}
164
+ numColumns={3}
165
+ deleteComponent={
166
+ <View
167
+ style={{
168
+ backgroundColor: "red",
169
+ borderRadius: 8,
170
+ justifyContent: "center",
171
+ alignItems: "center",
172
+ }}
173
+ >
174
+ <Text style={{ color: "white" }}>Drop to Delete</Text>
175
+ </View>
176
+ }
177
+ onDelete={(key) => {
178
+ setItems(items.filter((item) => item.id !== key));
179
+ }}
180
+ >
181
+ {items.map((item) => (
182
+ <View key={item.id}>{item.content}</View>
183
+ ))}
184
+ </SwappableGrid>
185
+ ```
186
+
187
+ ### With Trailing Component (Add Button)
188
+
189
+ ```tsx
190
+ <SwappableGrid
191
+ itemWidth={100}
192
+ itemHeight={100}
193
+ numColumns={3}
194
+ trailingComponent={
195
+ <Pressable
196
+ onPress={() => addNewItem()}
197
+ style={{
198
+ backgroundColor: "#007AFF",
199
+ borderRadius: 8,
200
+ justifyContent: "center",
201
+ alignItems: "center",
202
+ }}
203
+ >
204
+ <Text style={{ color: "white", fontSize: 24 }}>+</Text>
205
+ </Pressable>
206
+ }
207
+ >
208
+ {items.map((item) => (
209
+ <View key={item.id}>{item.content}</View>
210
+ ))}
211
+ </SwappableGrid>
212
+ ```
213
+
214
+ ### Auto-calculated Columns
215
+
216
+ ```tsx
217
+ <SwappableGrid
218
+ itemWidth={100}
219
+ itemHeight={100}
220
+ gap={12}
221
+ containerPadding={16}
222
+ // numColumns not provided - will be calculated automatically
223
+ onOrderChange={(keys) => console.log("New order:", keys)}
224
+ >
225
+ {items.map((item) => (
226
+ <View key={item.id}>{item.content}</View>
227
+ ))}
228
+ </SwappableGrid>
229
+ ```
230
+
231
+ ### Custom Delete Component Position
232
+
233
+ ```tsx
234
+ <SwappableGrid
235
+ itemWidth={100}
236
+ itemHeight={100}
237
+ numColumns={3}
238
+ deleteComponent={
239
+ <View style={{ backgroundColor: "red", borderRadius: 8 }}>
240
+ <Text>Delete</Text>
241
+ </View>
242
+ }
243
+ deleteComponentStyle={{
244
+ position: "absolute",
245
+ top: 20,
246
+ right: 20,
247
+ width: 100,
248
+ height: 100,
249
+ }}
250
+ onDelete={(key) => {
251
+ setItems(items.filter((item) => item.id !== key));
252
+ }}
253
+ >
254
+ {items.map((item) => (
255
+ <View key={item.id}>{item.content}</View>
256
+ ))}
257
+ </SwappableGrid>
258
+ ```
259
+
260
+ ## How It Works
261
+
262
+ 1. **Long Press**: Hold an item for `longPressMs` milliseconds to enter drag mode
263
+ 2. **Drag**: Move the item to swap positions with other items
264
+ 3. **Auto-scroll**: When dragging near edges, the grid automatically scrolls
265
+ 4. **Delete**:
266
+ - **Hold-to-delete**: Hold an item still for 1 second to enter delete mode (shows delete button)
267
+ - **Delete component**: Drag an item to the delete component to delete it
268
+ 5. **Order Change**: The `onOrderChange` callback fires whenever items are reordered
269
+
270
+ ## Notes
271
+
272
+ - Each child component **must** have a unique `key` prop
273
+ - The component uses `react-native-reanimated` for smooth 60fps animations
274
+ - When `deleteComponent` is provided, the hold-to-delete feature is automatically disabled
275
+ - The trailing component is positioned after all grid items
276
+ - The delete component appears only when dragging (if provided)
277
+
278
+ ## License
279
+
280
+ ISC
281
+
282
+ ## Contributing
283
+
284
+ Contributions are welcome! Please feel free to submit a Pull Request.
@@ -0,0 +1,320 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = ChildWrapper;
7
+ var _react = _interopRequireWildcard(require("react"));
8
+ var _reactNative = require("react-native");
9
+ var _reactNativeReanimated = _interopRequireWildcard(require("react-native-reanimated"));
10
+ function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
11
+ function ChildWrapper({
12
+ position,
13
+ itemWidth,
14
+ itemHeight,
15
+ dragMode,
16
+ anyItemInDeleteMode,
17
+ children,
18
+ wiggle,
19
+ dragSizeIncreaseFactor,
20
+ onDelete,
21
+ disableHoldToDelete = false
22
+ }) {
23
+ const rotation = (0, _reactNativeReanimated.useSharedValue)(0);
24
+ const currentWiggleMode = (0, _reactNativeReanimated.useSharedValue)("none");
25
+ const previousDragMode = (0, _reactNativeReanimated.useSharedValue)(false);
26
+ const showDelete = (0, _reactNativeReanimated.useSharedValue)(false);
27
+ const deleteModeActive = (0, _reactNativeReanimated.useSharedValue)(false); // Persistent delete mode state
28
+ const stillTimer = (0, _reactNativeReanimated.useSharedValue)(0);
29
+ const lastX = (0, _reactNativeReanimated.useSharedValue)(position.x.value);
30
+ const lastY = (0, _reactNativeReanimated.useSharedValue)(position.y.value);
31
+ const frameCounter = (0, _reactNativeReanimated.useSharedValue)(0);
32
+ const wasReleasedAfterDeleteMode = (0, _reactNativeReanimated.useSharedValue)(false); // Track if item was released after entering delete mode
33
+
34
+ // Timer logic that runs every frame via useDerivedValue
35
+ (0, _reactNativeReanimated.useDerivedValue)(() => {
36
+ "worklet";
37
+
38
+ frameCounter.value = frameCounter.value + 1;
39
+
40
+ // If hold-to-delete is disabled, skip all delete mode logic
41
+ if (disableHoldToDelete) {
42
+ deleteModeActive.value = false;
43
+ showDelete.value = false;
44
+ stillTimer.value = 0;
45
+ anyItemInDeleteMode.value = false;
46
+ return;
47
+ }
48
+ const isDragging = dragMode.value;
49
+ const isActive = position.active.value > 0.5;
50
+ const x = position.x.value;
51
+ const y = position.y.value;
52
+
53
+ // Track dragMode changes for detecting touches outside
54
+ const dragModeJustEnded = previousDragMode.value && !isDragging;
55
+ previousDragMode.value = isDragging;
56
+
57
+ // If delete mode is active, keep it active unless:
58
+ // 1. Another item becomes active (dragMode true but this item not active)
59
+ // 2. This item becomes active again AFTER it was released (user starts dragging it again)
60
+ // 3. User touches outside (dragMode becomes false and no item is active)
61
+ if (deleteModeActive.value) {
62
+ // Check if item was released (became inactive)
63
+ if (!isActive && !wasReleasedAfterDeleteMode.value) {
64
+ wasReleasedAfterDeleteMode.value = true;
65
+ }
66
+ if (isDragging && !isActive) {
67
+ // Another item is being dragged, exit delete mode
68
+ deleteModeActive.value = false;
69
+ anyItemInDeleteMode.value = false; // Clear global delete mode
70
+ showDelete.value = false;
71
+ stillTimer.value = 0;
72
+ wasReleasedAfterDeleteMode.value = false;
73
+ } else if (isActive && wasReleasedAfterDeleteMode.value) {
74
+ // This item became active again AFTER it was released, exit delete mode
75
+ deleteModeActive.value = false;
76
+ anyItemInDeleteMode.value = false; // Clear global delete mode
77
+ showDelete.value = false;
78
+ stillTimer.value = 0;
79
+ wasReleasedAfterDeleteMode.value = false;
80
+ } else if (!isDragging && !isActive) {
81
+ // Keep delete mode active (waiting for user interaction)
82
+ // The tap gesture handler in SwappableGrid will cancel it when user taps outside
83
+ showDelete.value = true;
84
+ } else {
85
+ // Keep delete mode active (item can still be held or released)
86
+ showDelete.value = true;
87
+ }
88
+ return;
89
+ }
90
+
91
+ // Reset release tracking when not in delete mode
92
+ wasReleasedAfterDeleteMode.value = false;
93
+
94
+ // If not in drag mode or not active, reset timer
95
+ if (!isDragging || !isActive) {
96
+ stillTimer.value = 0;
97
+ return;
98
+ }
99
+
100
+ // Item is active (being held down) - check if it's still
101
+ // Check if position has changed significantly (more than 10px threshold)
102
+ const moved = Math.abs(x - lastX.value) > 10 || Math.abs(y - lastY.value) > 10;
103
+ if (moved) {
104
+ // Reset timer if item moved while being held
105
+ stillTimer.value = 0;
106
+ lastX.value = x;
107
+ lastY.value = y;
108
+ return;
109
+ }
110
+
111
+ // Initialize last position on first frame when active
112
+ if (stillTimer.value === 0) {
113
+ lastX.value = x;
114
+ lastY.value = y;
115
+ }
116
+
117
+ // If the tile hasn't moved significantly while being held → increment timer
118
+ // Increment by ~16ms per frame (assuming 60fps)
119
+ stillTimer.value += 16;
120
+
121
+ // Enter delete mode after 1 second (1000ms) of being held still
122
+ if (stillTimer.value >= 1000) {
123
+ deleteModeActive.value = true;
124
+ anyItemInDeleteMode.value = true; // Set global delete mode
125
+ showDelete.value = true;
126
+ wasReleasedAfterDeleteMode.value = false; // Reset on entry
127
+ }
128
+ });
129
+ const deleteButtonStyle = (0, _reactNativeReanimated.useAnimatedStyle)(() => {
130
+ // Show delete button when delete mode is active (persists after release)
131
+ const shouldShow = showDelete.value;
132
+ return {
133
+ opacity: shouldShow ? 1 : 0,
134
+ pointerEvents: shouldShow ? "auto" : "none",
135
+ transform: [{
136
+ scale: (0, _reactNativeReanimated.withTiming)(shouldShow ? 1 : 0.6, {
137
+ duration: 120
138
+ })
139
+ }]
140
+ };
141
+ });
142
+
143
+ // Watch for when global delete mode is cancelled (user tapped outside)
144
+ (0, _reactNativeReanimated.useAnimatedReaction)(() => anyItemInDeleteMode.value, (current, previous) => {
145
+ "worklet";
146
+
147
+ // If delete mode was cancelled globally (user tapped outside)
148
+ if (previous && !current && deleteModeActive.value) {
149
+ deleteModeActive.value = false;
150
+ showDelete.value = false;
151
+ stillTimer.value = 0;
152
+ wasReleasedAfterDeleteMode.value = false;
153
+ }
154
+ });
155
+
156
+ // Wiggle animation — triggers on editMode/active changes and delete mode
157
+ (0, _reactNativeReanimated.useAnimatedReaction)(() => ({
158
+ isEditMode: dragMode.value,
159
+ isActive: position.active.value > 0.5,
160
+ inDeleteMode: deleteModeActive.value,
161
+ anyInDeleteMode: anyItemInDeleteMode.value
162
+ }), ({
163
+ isEditMode,
164
+ isActive,
165
+ inDeleteMode,
166
+ anyInDeleteMode
167
+ }) => {
168
+ if (!wiggle) {
169
+ if (currentWiggleMode.value !== "none") {
170
+ (0, _reactNativeReanimated.cancelAnimation)(rotation);
171
+ currentWiggleMode.value = "none";
172
+ }
173
+ rotation.value = (0, _reactNativeReanimated.withTiming)(0, {
174
+ duration: 150
175
+ });
176
+ return;
177
+ }
178
+
179
+ // Determine the target wiggle mode
180
+ let targetMode = "none";
181
+ if (inDeleteMode) {
182
+ targetMode = "delete";
183
+ } else if (anyInDeleteMode && !isActive) {
184
+ targetMode = "normal";
185
+ } else if (isEditMode && !isActive) {
186
+ targetMode = "normal";
187
+ }
188
+
189
+ // Only restart animation if mode changed
190
+ if (currentWiggleMode.value === targetMode) {
191
+ return; // Already in the correct mode, don't restart
192
+ }
193
+ const previousMode = currentWiggleMode.value;
194
+ currentWiggleMode.value = targetMode;
195
+
196
+ // Cancel current animation
197
+ (0, _reactNativeReanimated.cancelAnimation)(rotation);
198
+
199
+ // If this item is in delete mode, wiggle more (2x degrees, faster)
200
+ if (targetMode === "delete") {
201
+ const deleteWiggleDegrees = wiggle.degrees * 2;
202
+ const deleteWiggleDuration = wiggle.duration * 0.7; // Faster wiggle
203
+
204
+ // If transitioning from normal wiggle, preserve the phase by scaling
205
+ if (previousMode === "normal") {
206
+ const currentRot = rotation.value;
207
+ const scaleFactor = deleteWiggleDegrees / wiggle.degrees;
208
+ rotation.value = currentRot * scaleFactor;
209
+ }
210
+ rotation.value = (0, _reactNativeReanimated.withRepeat)((0, _reactNativeReanimated.withSequence)((0, _reactNativeReanimated.withTiming)(deleteWiggleDegrees, {
211
+ duration: deleteWiggleDuration,
212
+ easing: _reactNativeReanimated.Easing.linear
213
+ }), (0, _reactNativeReanimated.withTiming)(-deleteWiggleDegrees, {
214
+ duration: deleteWiggleDuration,
215
+ easing: _reactNativeReanimated.Easing.linear
216
+ })), -1,
217
+ // infinite
218
+ true);
219
+ }
220
+ // Normal wiggle (when dragging but not this item, or any item in delete mode)
221
+ else if (targetMode === "normal") {
222
+ // If transitioning from delete wiggle, preserve the phase by scaling
223
+ if (previousMode === "delete") {
224
+ const currentRot = rotation.value;
225
+ const scaleFactor = wiggle.degrees / (wiggle.degrees * 2);
226
+ rotation.value = currentRot * scaleFactor;
227
+ }
228
+ rotation.value = (0, _reactNativeReanimated.withRepeat)((0, _reactNativeReanimated.withSequence)((0, _reactNativeReanimated.withTiming)(wiggle.degrees, {
229
+ duration: wiggle.duration,
230
+ easing: _reactNativeReanimated.Easing.linear
231
+ }), (0, _reactNativeReanimated.withTiming)(-wiggle.degrees, {
232
+ duration: wiggle.duration,
233
+ easing: _reactNativeReanimated.Easing.linear
234
+ })), -1,
235
+ // infinite
236
+ true);
237
+ }
238
+ // Stop wiggling
239
+ else {
240
+ rotation.value = (0, _reactNativeReanimated.withTiming)(0, {
241
+ duration: 150
242
+ });
243
+ }
244
+ }, [dragMode, position.active, deleteModeActive, anyItemInDeleteMode]);
245
+ const animatedStyle = (0, _reactNativeReanimated.useAnimatedStyle)(() => {
246
+ const scale = position.active.value ? (0, _reactNativeReanimated.withTiming)(dragSizeIncreaseFactor, {
247
+ duration: 120
248
+ }) : (0, _reactNativeReanimated.withTiming)(1, {
249
+ duration: 120
250
+ });
251
+ return {
252
+ position: "absolute",
253
+ width: itemWidth,
254
+ height: itemHeight,
255
+ transform: [{
256
+ translateX: position.x.value
257
+ }, {
258
+ translateY: position.y.value
259
+ }, {
260
+ scale: scale
261
+ }, {
262
+ rotate: `${rotation.value}deg`
263
+ }],
264
+ zIndex: position.active.value ? 2 : 0
265
+ };
266
+ });
267
+
268
+ // Track delete mode on JS thread for conditional rendering
269
+ const [isInDeleteMode, setIsInDeleteMode] = (0, _react.useState)(false);
270
+ (0, _reactNativeReanimated.useAnimatedReaction)(() => deleteModeActive.value, current => {
271
+ (0, _reactNativeReanimated.runOnJS)(setIsInDeleteMode)(current);
272
+ });
273
+ const handleDelete = () => {
274
+ // Exit delete mode when delete button is pressed
275
+ deleteModeActive.value = false;
276
+ anyItemInDeleteMode.value = false; // Clear global delete mode
277
+ showDelete.value = false;
278
+ stillTimer.value = 0;
279
+ wasReleasedAfterDeleteMode.value = false;
280
+ if (onDelete) {
281
+ onDelete();
282
+ }
283
+ };
284
+ return /*#__PURE__*/_react.default.createElement(_reactNativeReanimated.default.View, {
285
+ style: animatedStyle,
286
+ pointerEvents: "box-none"
287
+ }, isInDeleteMode && /*#__PURE__*/_react.default.createElement(_reactNative.Pressable, {
288
+ onPress: handleDelete,
289
+ style: {
290
+ position: "absolute",
291
+ top: 0,
292
+ left: 0,
293
+ right: 0,
294
+ bottom: 0,
295
+ width: itemWidth,
296
+ height: itemHeight,
297
+ zIndex: 2
298
+ }
299
+ }), /*#__PURE__*/_react.default.createElement(_reactNativeReanimated.default.View, {
300
+ style: [{
301
+ position: "absolute",
302
+ top: itemHeight * 0.01,
303
+ right: itemWidth * 0.04,
304
+ width: itemWidth * 0.2,
305
+ height: itemHeight * 0.2,
306
+ borderRadius: 12,
307
+ justifyContent: "center",
308
+ alignItems: "center",
309
+ zIndex: 3
310
+ }, deleteButtonStyle],
311
+ pointerEvents: "none"
312
+ }, /*#__PURE__*/_react.default.createElement(_reactNative.Text, {
313
+ style: {
314
+ fontSize: itemWidth * 0.2,
315
+ color: "black",
316
+ fontWeight: 500
317
+ }
318
+ }, "\xD7")), children);
319
+ }
320
+ //# sourceMappingURL=ChildWrapper.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["_react","_interopRequireWildcard","require","_reactNative","_reactNativeReanimated","e","t","WeakMap","r","n","__esModule","o","i","f","__proto__","default","has","get","set","hasOwnProperty","call","Object","defineProperty","getOwnPropertyDescriptor","ChildWrapper","position","itemWidth","itemHeight","dragMode","anyItemInDeleteMode","children","wiggle","dragSizeIncreaseFactor","onDelete","disableHoldToDelete","rotation","useSharedValue","currentWiggleMode","previousDragMode","showDelete","deleteModeActive","stillTimer","lastX","x","value","lastY","y","frameCounter","wasReleasedAfterDeleteMode","useDerivedValue","isDragging","isActive","active","dragModeJustEnded","moved","Math","abs","deleteButtonStyle","useAnimatedStyle","shouldShow","opacity","pointerEvents","transform","scale","withTiming","duration","useAnimatedReaction","current","previous","isEditMode","inDeleteMode","anyInDeleteMode","cancelAnimation","targetMode","previousMode","deleteWiggleDegrees","degrees","deleteWiggleDuration","currentRot","scaleFactor","withRepeat","withSequence","easing","Easing","linear","animatedStyle","width","height","translateX","translateY","rotate","zIndex","isInDeleteMode","setIsInDeleteMode","useState","runOnJS","handleDelete","createElement","View","style","Pressable","onPress","top","left","right","bottom","borderRadius","justifyContent","alignItems","Text","fontSize","color","fontWeight"],"sources":["ChildWrapper.tsx"],"sourcesContent":["import React, { useEffect, useState } from \"react\";\nimport { Text, View, Pressable } from \"react-native\";\nimport Animated, {\n Easing,\n useAnimatedStyle,\n useAnimatedReaction,\n useSharedValue,\n withRepeat,\n withSequence,\n withTiming,\n SharedValue,\n useDerivedValue,\n cancelAnimation,\n runOnJS,\n} from \"react-native-reanimated\";\n\ntype Props = {\n position: {\n x: SharedValue<number>;\n y: SharedValue<number>;\n active: SharedValue<number>;\n };\n itemWidth: number;\n itemHeight: number;\n dragMode: SharedValue<boolean>;\n anyItemInDeleteMode: SharedValue<boolean>;\n children: React.ReactNode;\n wiggle?: { duration: number; degrees: number };\n dragSizeIncreaseFactor: number;\n onDelete?: () => void;\n disableHoldToDelete?: boolean; // If true, disable the hold-to-delete feature\n};\n\nexport default function ChildWrapper({\n position,\n itemWidth,\n itemHeight,\n dragMode,\n anyItemInDeleteMode,\n children,\n wiggle,\n dragSizeIncreaseFactor,\n onDelete,\n disableHoldToDelete = false,\n}: Props) {\n const rotation = useSharedValue(0);\n const currentWiggleMode = useSharedValue<\"none\" | \"normal\" | \"delete\">(\n \"none\"\n );\n const previousDragMode = useSharedValue(false);\n\n const showDelete = useSharedValue(false);\n const deleteModeActive = useSharedValue(false); // Persistent delete mode state\n const stillTimer = useSharedValue(0);\n const lastX = useSharedValue(position.x.value);\n const lastY = useSharedValue(position.y.value);\n const frameCounter = useSharedValue(0);\n const wasReleasedAfterDeleteMode = useSharedValue(false); // Track if item was released after entering delete mode\n\n // Timer logic that runs every frame via useDerivedValue\n useDerivedValue(() => {\n \"worklet\";\n frameCounter.value = frameCounter.value + 1;\n\n // If hold-to-delete is disabled, skip all delete mode logic\n if (disableHoldToDelete) {\n deleteModeActive.value = false;\n showDelete.value = false;\n stillTimer.value = 0;\n anyItemInDeleteMode.value = false;\n return;\n }\n\n const isDragging = dragMode.value;\n const isActive = position.active.value > 0.5;\n const x = position.x.value;\n const y = position.y.value;\n\n // Track dragMode changes for detecting touches outside\n const dragModeJustEnded = previousDragMode.value && !isDragging;\n previousDragMode.value = isDragging;\n\n // If delete mode is active, keep it active unless:\n // 1. Another item becomes active (dragMode true but this item not active)\n // 2. This item becomes active again AFTER it was released (user starts dragging it again)\n // 3. User touches outside (dragMode becomes false and no item is active)\n if (deleteModeActive.value) {\n // Check if item was released (became inactive)\n if (!isActive && !wasReleasedAfterDeleteMode.value) {\n wasReleasedAfterDeleteMode.value = true;\n }\n\n if (isDragging && !isActive) {\n // Another item is being dragged, exit delete mode\n deleteModeActive.value = false;\n anyItemInDeleteMode.value = false; // Clear global delete mode\n showDelete.value = false;\n stillTimer.value = 0;\n wasReleasedAfterDeleteMode.value = false;\n } else if (isActive && wasReleasedAfterDeleteMode.value) {\n // This item became active again AFTER it was released, exit delete mode\n deleteModeActive.value = false;\n anyItemInDeleteMode.value = false; // Clear global delete mode\n showDelete.value = false;\n stillTimer.value = 0;\n wasReleasedAfterDeleteMode.value = false;\n } else if (!isDragging && !isActive) {\n // Keep delete mode active (waiting for user interaction)\n // The tap gesture handler in SwappableGrid will cancel it when user taps outside\n showDelete.value = true;\n } else {\n // Keep delete mode active (item can still be held or released)\n showDelete.value = true;\n }\n return;\n }\n\n // Reset release tracking when not in delete mode\n wasReleasedAfterDeleteMode.value = false;\n\n // If not in drag mode or not active, reset timer\n if (!isDragging || !isActive) {\n stillTimer.value = 0;\n return;\n }\n\n // Item is active (being held down) - check if it's still\n // Check if position has changed significantly (more than 10px threshold)\n const moved =\n Math.abs(x - lastX.value) > 10 || Math.abs(y - lastY.value) > 10;\n\n if (moved) {\n // Reset timer if item moved while being held\n stillTimer.value = 0;\n lastX.value = x;\n lastY.value = y;\n return;\n }\n\n // Initialize last position on first frame when active\n if (stillTimer.value === 0) {\n lastX.value = x;\n lastY.value = y;\n }\n\n // If the tile hasn't moved significantly while being held → increment timer\n // Increment by ~16ms per frame (assuming 60fps)\n stillTimer.value += 16;\n\n // Enter delete mode after 1 second (1000ms) of being held still\n if (stillTimer.value >= 1000) {\n deleteModeActive.value = true;\n anyItemInDeleteMode.value = true; // Set global delete mode\n showDelete.value = true;\n wasReleasedAfterDeleteMode.value = false; // Reset on entry\n }\n });\n\n const deleteButtonStyle = useAnimatedStyle(() => {\n // Show delete button when delete mode is active (persists after release)\n const shouldShow = showDelete.value;\n return {\n opacity: shouldShow ? 1 : 0,\n pointerEvents: shouldShow ? \"auto\" : \"none\",\n transform: [\n { scale: withTiming(shouldShow ? 1 : 0.6, { duration: 120 }) },\n ],\n };\n });\n\n // Watch for when global delete mode is cancelled (user tapped outside)\n useAnimatedReaction(\n () => anyItemInDeleteMode.value,\n (current, previous) => {\n \"worklet\";\n // If delete mode was cancelled globally (user tapped outside)\n if (previous && !current && deleteModeActive.value) {\n deleteModeActive.value = false;\n showDelete.value = false;\n stillTimer.value = 0;\n wasReleasedAfterDeleteMode.value = false;\n }\n }\n );\n\n // Wiggle animation — triggers on editMode/active changes and delete mode\n useAnimatedReaction(\n () => ({\n isEditMode: dragMode.value,\n isActive: position.active.value > 0.5,\n inDeleteMode: deleteModeActive.value,\n anyInDeleteMode: anyItemInDeleteMode.value,\n }),\n ({ isEditMode, isActive, inDeleteMode, anyInDeleteMode }) => {\n if (!wiggle) {\n if (currentWiggleMode.value !== \"none\") {\n cancelAnimation(rotation);\n currentWiggleMode.value = \"none\";\n }\n rotation.value = withTiming(0, { duration: 150 });\n return;\n }\n\n // Determine the target wiggle mode\n let targetMode: \"none\" | \"normal\" | \"delete\" = \"none\";\n if (inDeleteMode) {\n targetMode = \"delete\";\n } else if (anyInDeleteMode && !isActive) {\n targetMode = \"normal\";\n } else if (isEditMode && !isActive) {\n targetMode = \"normal\";\n }\n\n // Only restart animation if mode changed\n if (currentWiggleMode.value === targetMode) {\n return; // Already in the correct mode, don't restart\n }\n\n const previousMode = currentWiggleMode.value;\n currentWiggleMode.value = targetMode;\n\n // Cancel current animation\n cancelAnimation(rotation);\n\n // If this item is in delete mode, wiggle more (2x degrees, faster)\n if (targetMode === \"delete\") {\n const deleteWiggleDegrees = wiggle.degrees * 2;\n const deleteWiggleDuration = wiggle.duration * 0.7; // Faster wiggle\n\n // If transitioning from normal wiggle, preserve the phase by scaling\n if (previousMode === \"normal\") {\n const currentRot = rotation.value;\n const scaleFactor = deleteWiggleDegrees / wiggle.degrees;\n rotation.value = currentRot * scaleFactor;\n }\n\n rotation.value = withRepeat(\n withSequence(\n withTiming(deleteWiggleDegrees, {\n duration: deleteWiggleDuration,\n easing: Easing.linear,\n }),\n withTiming(-deleteWiggleDegrees, {\n duration: deleteWiggleDuration,\n easing: Easing.linear,\n })\n ),\n -1, // infinite\n true\n );\n }\n // Normal wiggle (when dragging but not this item, or any item in delete mode)\n else if (targetMode === \"normal\") {\n // If transitioning from delete wiggle, preserve the phase by scaling\n if (previousMode === \"delete\") {\n const currentRot = rotation.value;\n const scaleFactor = wiggle.degrees / (wiggle.degrees * 2);\n rotation.value = currentRot * scaleFactor;\n }\n\n rotation.value = withRepeat(\n withSequence(\n withTiming(wiggle.degrees, {\n duration: wiggle.duration,\n easing: Easing.linear,\n }),\n withTiming(-wiggle.degrees, {\n duration: wiggle.duration,\n easing: Easing.linear,\n })\n ),\n -1, // infinite\n true\n );\n }\n // Stop wiggling\n else {\n rotation.value = withTiming(0, { duration: 150 });\n }\n },\n [dragMode, position.active, deleteModeActive, anyItemInDeleteMode]\n );\n\n const animatedStyle = useAnimatedStyle(() => {\n const scale = position.active.value\n ? withTiming(dragSizeIncreaseFactor, { duration: 120 })\n : withTiming(1, { duration: 120 });\n\n return {\n position: \"absolute\",\n width: itemWidth,\n height: itemHeight,\n transform: [\n { translateX: position.x.value as any },\n { translateY: position.y.value as any },\n { scale: scale as any },\n { rotate: `${rotation.value}deg` as any },\n ],\n zIndex: position.active.value ? 2 : 0,\n } as any;\n });\n\n // Track delete mode on JS thread for conditional rendering\n const [isInDeleteMode, setIsInDeleteMode] = useState(false);\n\n useAnimatedReaction(\n () => deleteModeActive.value,\n (current) => {\n runOnJS(setIsInDeleteMode)(current);\n }\n );\n\n const handleDelete = () => {\n // Exit delete mode when delete button is pressed\n deleteModeActive.value = false;\n anyItemInDeleteMode.value = false; // Clear global delete mode\n showDelete.value = false;\n stillTimer.value = 0;\n wasReleasedAfterDeleteMode.value = false;\n if (onDelete) {\n onDelete();\n }\n };\n\n return (\n <Animated.View style={animatedStyle} pointerEvents=\"box-none\">\n {/* Full-item Pressable for delete - only active when in delete mode */}\n {isInDeleteMode && (\n <Pressable\n onPress={handleDelete}\n style={{\n position: \"absolute\",\n top: 0,\n left: 0,\n right: 0,\n bottom: 0,\n width: itemWidth,\n height: itemHeight,\n zIndex: 2,\n }}\n />\n )}\n\n {/* Delete button (×) - visual indicator only */}\n <Animated.View\n style={[\n {\n position: \"absolute\",\n top: itemHeight * 0.01,\n right: itemWidth * 0.04,\n width: itemWidth * 0.2,\n height: itemHeight * 0.2,\n borderRadius: 12,\n justifyContent: \"center\",\n alignItems: \"center\",\n zIndex: 3,\n },\n deleteButtonStyle,\n ]}\n pointerEvents=\"none\"\n >\n <Text\n style={{\n fontSize: itemWidth * 0.2,\n color: \"black\",\n fontWeight: 500,\n }}\n >\n ×\n </Text>\n </Animated.View>\n\n {children}\n </Animated.View>\n );\n}\n"],"mappings":"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","ignoreList":[]}