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
@@ -0,0 +1,313 @@
1
+ import React, { useState } from "react";
2
+ import { Text, Pressable } from "react-native";
3
+ import Animated, { Easing, useAnimatedStyle, useAnimatedReaction, useSharedValue, withRepeat, withSequence, withTiming, useDerivedValue, cancelAnimation, runOnJS } from "react-native-reanimated";
4
+ export default function ChildWrapper({
5
+ position,
6
+ itemWidth,
7
+ itemHeight,
8
+ dragMode,
9
+ anyItemInDeleteMode,
10
+ children,
11
+ wiggle,
12
+ dragSizeIncreaseFactor,
13
+ onDelete,
14
+ disableHoldToDelete = false
15
+ }) {
16
+ const rotation = useSharedValue(0);
17
+ const currentWiggleMode = useSharedValue("none");
18
+ const previousDragMode = useSharedValue(false);
19
+ const showDelete = useSharedValue(false);
20
+ const deleteModeActive = useSharedValue(false); // Persistent delete mode state
21
+ const stillTimer = useSharedValue(0);
22
+ const lastX = useSharedValue(position.x.value);
23
+ const lastY = useSharedValue(position.y.value);
24
+ const frameCounter = useSharedValue(0);
25
+ const wasReleasedAfterDeleteMode = useSharedValue(false); // Track if item was released after entering delete mode
26
+
27
+ // Timer logic that runs every frame via useDerivedValue
28
+ useDerivedValue(() => {
29
+ "worklet";
30
+
31
+ frameCounter.value = frameCounter.value + 1;
32
+
33
+ // If hold-to-delete is disabled, skip all delete mode logic
34
+ if (disableHoldToDelete) {
35
+ deleteModeActive.value = false;
36
+ showDelete.value = false;
37
+ stillTimer.value = 0;
38
+ anyItemInDeleteMode.value = false;
39
+ return;
40
+ }
41
+ const isDragging = dragMode.value;
42
+ const isActive = position.active.value > 0.5;
43
+ const x = position.x.value;
44
+ const y = position.y.value;
45
+
46
+ // Track dragMode changes for detecting touches outside
47
+ const dragModeJustEnded = previousDragMode.value && !isDragging;
48
+ previousDragMode.value = isDragging;
49
+
50
+ // If delete mode is active, keep it active unless:
51
+ // 1. Another item becomes active (dragMode true but this item not active)
52
+ // 2. This item becomes active again AFTER it was released (user starts dragging it again)
53
+ // 3. User touches outside (dragMode becomes false and no item is active)
54
+ if (deleteModeActive.value) {
55
+ // Check if item was released (became inactive)
56
+ if (!isActive && !wasReleasedAfterDeleteMode.value) {
57
+ wasReleasedAfterDeleteMode.value = true;
58
+ }
59
+ if (isDragging && !isActive) {
60
+ // Another item is being dragged, exit delete mode
61
+ deleteModeActive.value = false;
62
+ anyItemInDeleteMode.value = false; // Clear global delete mode
63
+ showDelete.value = false;
64
+ stillTimer.value = 0;
65
+ wasReleasedAfterDeleteMode.value = false;
66
+ } else if (isActive && wasReleasedAfterDeleteMode.value) {
67
+ // This item became active again AFTER it was released, 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 (!isDragging && !isActive) {
74
+ // Keep delete mode active (waiting for user interaction)
75
+ // The tap gesture handler in SwappableGrid will cancel it when user taps outside
76
+ showDelete.value = true;
77
+ } else {
78
+ // Keep delete mode active (item can still be held or released)
79
+ showDelete.value = true;
80
+ }
81
+ return;
82
+ }
83
+
84
+ // Reset release tracking when not in delete mode
85
+ wasReleasedAfterDeleteMode.value = false;
86
+
87
+ // If not in drag mode or not active, reset timer
88
+ if (!isDragging || !isActive) {
89
+ stillTimer.value = 0;
90
+ return;
91
+ }
92
+
93
+ // Item is active (being held down) - check if it's still
94
+ // Check if position has changed significantly (more than 10px threshold)
95
+ const moved = Math.abs(x - lastX.value) > 10 || Math.abs(y - lastY.value) > 10;
96
+ if (moved) {
97
+ // Reset timer if item moved while being held
98
+ stillTimer.value = 0;
99
+ lastX.value = x;
100
+ lastY.value = y;
101
+ return;
102
+ }
103
+
104
+ // Initialize last position on first frame when active
105
+ if (stillTimer.value === 0) {
106
+ lastX.value = x;
107
+ lastY.value = y;
108
+ }
109
+
110
+ // If the tile hasn't moved significantly while being held → increment timer
111
+ // Increment by ~16ms per frame (assuming 60fps)
112
+ stillTimer.value += 16;
113
+
114
+ // Enter delete mode after 1 second (1000ms) of being held still
115
+ if (stillTimer.value >= 1000) {
116
+ deleteModeActive.value = true;
117
+ anyItemInDeleteMode.value = true; // Set global delete mode
118
+ showDelete.value = true;
119
+ wasReleasedAfterDeleteMode.value = false; // Reset on entry
120
+ }
121
+ });
122
+ const deleteButtonStyle = useAnimatedStyle(() => {
123
+ // Show delete button when delete mode is active (persists after release)
124
+ const shouldShow = showDelete.value;
125
+ return {
126
+ opacity: shouldShow ? 1 : 0,
127
+ pointerEvents: shouldShow ? "auto" : "none",
128
+ transform: [{
129
+ scale: withTiming(shouldShow ? 1 : 0.6, {
130
+ duration: 120
131
+ })
132
+ }]
133
+ };
134
+ });
135
+
136
+ // Watch for when global delete mode is cancelled (user tapped outside)
137
+ useAnimatedReaction(() => anyItemInDeleteMode.value, (current, previous) => {
138
+ "worklet";
139
+
140
+ // If delete mode was cancelled globally (user tapped outside)
141
+ if (previous && !current && deleteModeActive.value) {
142
+ deleteModeActive.value = false;
143
+ showDelete.value = false;
144
+ stillTimer.value = 0;
145
+ wasReleasedAfterDeleteMode.value = false;
146
+ }
147
+ });
148
+
149
+ // Wiggle animation — triggers on editMode/active changes and delete mode
150
+ useAnimatedReaction(() => ({
151
+ isEditMode: dragMode.value,
152
+ isActive: position.active.value > 0.5,
153
+ inDeleteMode: deleteModeActive.value,
154
+ anyInDeleteMode: anyItemInDeleteMode.value
155
+ }), ({
156
+ isEditMode,
157
+ isActive,
158
+ inDeleteMode,
159
+ anyInDeleteMode
160
+ }) => {
161
+ if (!wiggle) {
162
+ if (currentWiggleMode.value !== "none") {
163
+ cancelAnimation(rotation);
164
+ currentWiggleMode.value = "none";
165
+ }
166
+ rotation.value = withTiming(0, {
167
+ duration: 150
168
+ });
169
+ return;
170
+ }
171
+
172
+ // Determine the target wiggle mode
173
+ let targetMode = "none";
174
+ if (inDeleteMode) {
175
+ targetMode = "delete";
176
+ } else if (anyInDeleteMode && !isActive) {
177
+ targetMode = "normal";
178
+ } else if (isEditMode && !isActive) {
179
+ targetMode = "normal";
180
+ }
181
+
182
+ // Only restart animation if mode changed
183
+ if (currentWiggleMode.value === targetMode) {
184
+ return; // Already in the correct mode, don't restart
185
+ }
186
+ const previousMode = currentWiggleMode.value;
187
+ currentWiggleMode.value = targetMode;
188
+
189
+ // Cancel current animation
190
+ cancelAnimation(rotation);
191
+
192
+ // If this item is in delete mode, wiggle more (2x degrees, faster)
193
+ if (targetMode === "delete") {
194
+ const deleteWiggleDegrees = wiggle.degrees * 2;
195
+ const deleteWiggleDuration = wiggle.duration * 0.7; // Faster wiggle
196
+
197
+ // If transitioning from normal wiggle, preserve the phase by scaling
198
+ if (previousMode === "normal") {
199
+ const currentRot = rotation.value;
200
+ const scaleFactor = deleteWiggleDegrees / wiggle.degrees;
201
+ rotation.value = currentRot * scaleFactor;
202
+ }
203
+ rotation.value = withRepeat(withSequence(withTiming(deleteWiggleDegrees, {
204
+ duration: deleteWiggleDuration,
205
+ easing: Easing.linear
206
+ }), withTiming(-deleteWiggleDegrees, {
207
+ duration: deleteWiggleDuration,
208
+ easing: Easing.linear
209
+ })), -1,
210
+ // infinite
211
+ true);
212
+ }
213
+ // Normal wiggle (when dragging but not this item, or any item in delete mode)
214
+ else if (targetMode === "normal") {
215
+ // If transitioning from delete wiggle, preserve the phase by scaling
216
+ if (previousMode === "delete") {
217
+ const currentRot = rotation.value;
218
+ const scaleFactor = wiggle.degrees / (wiggle.degrees * 2);
219
+ rotation.value = currentRot * scaleFactor;
220
+ }
221
+ rotation.value = withRepeat(withSequence(withTiming(wiggle.degrees, {
222
+ duration: wiggle.duration,
223
+ easing: Easing.linear
224
+ }), withTiming(-wiggle.degrees, {
225
+ duration: wiggle.duration,
226
+ easing: Easing.linear
227
+ })), -1,
228
+ // infinite
229
+ true);
230
+ }
231
+ // Stop wiggling
232
+ else {
233
+ rotation.value = withTiming(0, {
234
+ duration: 150
235
+ });
236
+ }
237
+ }, [dragMode, position.active, deleteModeActive, anyItemInDeleteMode]);
238
+ const animatedStyle = useAnimatedStyle(() => {
239
+ const scale = position.active.value ? withTiming(dragSizeIncreaseFactor, {
240
+ duration: 120
241
+ }) : withTiming(1, {
242
+ duration: 120
243
+ });
244
+ return {
245
+ position: "absolute",
246
+ width: itemWidth,
247
+ height: itemHeight,
248
+ transform: [{
249
+ translateX: position.x.value
250
+ }, {
251
+ translateY: position.y.value
252
+ }, {
253
+ scale: scale
254
+ }, {
255
+ rotate: `${rotation.value}deg`
256
+ }],
257
+ zIndex: position.active.value ? 2 : 0
258
+ };
259
+ });
260
+
261
+ // Track delete mode on JS thread for conditional rendering
262
+ const [isInDeleteMode, setIsInDeleteMode] = useState(false);
263
+ useAnimatedReaction(() => deleteModeActive.value, current => {
264
+ runOnJS(setIsInDeleteMode)(current);
265
+ });
266
+ const handleDelete = () => {
267
+ // Exit delete mode when delete button is pressed
268
+ deleteModeActive.value = false;
269
+ anyItemInDeleteMode.value = false; // Clear global delete mode
270
+ showDelete.value = false;
271
+ stillTimer.value = 0;
272
+ wasReleasedAfterDeleteMode.value = false;
273
+ if (onDelete) {
274
+ onDelete();
275
+ }
276
+ };
277
+ return /*#__PURE__*/React.createElement(Animated.View, {
278
+ style: animatedStyle,
279
+ pointerEvents: "box-none"
280
+ }, isInDeleteMode && /*#__PURE__*/React.createElement(Pressable, {
281
+ onPress: handleDelete,
282
+ style: {
283
+ position: "absolute",
284
+ top: 0,
285
+ left: 0,
286
+ right: 0,
287
+ bottom: 0,
288
+ width: itemWidth,
289
+ height: itemHeight,
290
+ zIndex: 2
291
+ }
292
+ }), /*#__PURE__*/React.createElement(Animated.View, {
293
+ style: [{
294
+ position: "absolute",
295
+ top: itemHeight * 0.01,
296
+ right: itemWidth * 0.04,
297
+ width: itemWidth * 0.2,
298
+ height: itemHeight * 0.2,
299
+ borderRadius: 12,
300
+ justifyContent: "center",
301
+ alignItems: "center",
302
+ zIndex: 3
303
+ }, deleteButtonStyle],
304
+ pointerEvents: "none"
305
+ }, /*#__PURE__*/React.createElement(Text, {
306
+ style: {
307
+ fontSize: itemWidth * 0.2,
308
+ color: "black",
309
+ fontWeight: 500
310
+ }
311
+ }, "\xD7")), children);
312
+ }
313
+ //# sourceMappingURL=ChildWrapper.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["React","useState","Text","Pressable","Animated","Easing","useAnimatedStyle","useAnimatedReaction","useSharedValue","withRepeat","withSequence","withTiming","useDerivedValue","cancelAnimation","runOnJS","ChildWrapper","position","itemWidth","itemHeight","dragMode","anyItemInDeleteMode","children","wiggle","dragSizeIncreaseFactor","onDelete","disableHoldToDelete","rotation","currentWiggleMode","previousDragMode","showDelete","deleteModeActive","stillTimer","lastX","x","value","lastY","y","frameCounter","wasReleasedAfterDeleteMode","isDragging","isActive","active","dragModeJustEnded","moved","Math","abs","deleteButtonStyle","shouldShow","opacity","pointerEvents","transform","scale","duration","current","previous","isEditMode","inDeleteMode","anyInDeleteMode","targetMode","previousMode","deleteWiggleDegrees","degrees","deleteWiggleDuration","currentRot","scaleFactor","easing","linear","animatedStyle","width","height","translateX","translateY","rotate","zIndex","isInDeleteMode","setIsInDeleteMode","handleDelete","createElement","View","style","onPress","top","left","right","bottom","borderRadius","justifyContent","alignItems","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":[]}