react-native-swappable-grid 1.0.4 → 1.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -16,7 +16,7 @@ A powerful React Native component for creating draggable, swappable grid layouts
16
16
  ## 📱 Demo
17
17
 
18
18
  <h3 align="center">
19
- Drag and swap items
19
+ Drag and re-order items
20
20
  </h3>
21
21
 
22
22
  <div align="center">
@@ -27,18 +27,29 @@ A powerful React Native component for creating draggable, swappable grid layouts
27
27
  </br>
28
28
 
29
29
  <h3 align="center">
30
- Add new items. Grid adjustes its size dynamically
30
+ Add new items
31
31
  </h3>
32
32
 
33
33
  <div align="center">
34
- <img src="./assets/add-items-and-re-order.gif" width="300" height="auto" />
34
+ <img src="./assets/add-items.gif" width="300" height="auto" />
35
35
  </div>
36
36
 
37
37
  </br>
38
38
  </br>
39
39
 
40
40
  <h3 align="center">
41
- Hold still to Delete items
41
+ The grid can have handle any item size and adjustes itself dynamically
42
+ </h3>
43
+
44
+ <div align="center">
45
+ <img src="./assets/re-size.gif" width="300" height="auto" />
46
+ </div>
47
+
48
+ </br>
49
+ </br>
50
+
51
+ <h3 align="center">
52
+ Hold still to delete items
42
53
  </h3>
43
54
 
44
55
  <div align="center">
@@ -49,7 +60,7 @@ A powerful React Native component for creating draggable, swappable grid layouts
49
60
  </br>
50
61
 
51
62
  <h3 align="center">
52
- Drag to Delete items
63
+ Drag to delete items
53
64
  </h3>
54
65
 
55
66
  <div align="center">
@@ -14,6 +14,7 @@ function ChildWrapper({
14
14
  itemHeight,
15
15
  dragMode,
16
16
  anyItemInDeleteMode,
17
+ isPressingDeleteItem,
17
18
  children,
18
19
  wiggle,
19
20
  wiggleDeleteMode,
@@ -93,12 +94,18 @@ function ChildWrapper({
93
94
  // Reset release tracking when not in delete mode
94
95
  wasReleasedAfterDeleteMode.value = false;
95
96
 
96
- // If not in drag mode or not active, reset timer
97
- if (!isDragging || !isActive) {
97
+ // Timer runs when item is active (being held)
98
+ // Note: isActive (position.active.value) is set when gesture activates after long press
99
+ // isDragging (dragMode.value) is also set at that time, but we primarily check isActive
100
+ // to allow timer to work even in edge cases
101
+ if (!isActive) {
98
102
  stillTimer.value = 0;
99
103
  return;
100
104
  }
101
105
 
106
+ // Item is active - timer can run (dragMode should also be true at this point,
107
+ // but we don't require it to allow timer to work in all cases)
108
+
102
109
  // Item is active (being held down) - check if it's still
103
110
  // Check if position has changed significantly (more than 10px threshold)
104
111
  const moved = Math.abs(x - lastX.value) > 10 || Math.abs(y - lastY.value) > 10;
@@ -312,6 +319,16 @@ function ChildWrapper({
312
319
  style: animatedStyle,
313
320
  pointerEvents: "box-none"
314
321
  }, isInDeleteMode && /*#__PURE__*/_react.default.createElement(_reactNative.Pressable, {
322
+ onPressIn: () => {
323
+ // Mark that we're pressing an item to prevent ScrollView from canceling delete mode
324
+ isPressingDeleteItem.value = true;
325
+ },
326
+ onPressOut: () => {
327
+ // Clear the flag after a short delay to allow onPress to fire
328
+ setTimeout(() => {
329
+ isPressingDeleteItem.value = false;
330
+ }, 50);
331
+ },
315
332
  onPress: handleDelete,
316
333
  style: {
317
334
  position: "absolute",
@@ -1 +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","wiggleDeleteMode","holdStillToDeleteMs","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","targetMode","cancelAnimation","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 wiggleDeleteMode?: { duration: number; degrees: number };\n holdStillToDeleteMs?: 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 wiggleDeleteMode,\n holdStillToDeleteMs = 1000,\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 holdStillToDeleteMs of being held still\n if (stillTimer.value >= holdStillToDeleteMs) {\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 // Determine the target wiggle mode\n let targetMode: \"none\" | \"normal\" | \"delete\" = \"none\";\n if (inDeleteMode && (wiggleDeleteMode || wiggle)) {\n targetMode = \"delete\";\n } else if (anyInDeleteMode && !isActive && wiggle) {\n targetMode = \"normal\";\n } else if (isEditMode && !isActive && wiggle) {\n targetMode = \"normal\";\n }\n\n // If no wiggle is configured at all, stop animation\n if (!wiggle && !wiggleDeleteMode) {\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 // If in delete mode but no wiggleDeleteMode and no wiggle, stop animation\n if (targetMode === \"delete\" && !wiggleDeleteMode && !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 // If normal mode but no wiggle, stop animation\n if (targetMode === \"normal\" && !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 // 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, use wiggleDeleteMode if provided, otherwise use 2x degrees and 0.7x duration\n if (targetMode === \"delete\") {\n const deleteWiggleDegrees = wiggleDeleteMode\n ? wiggleDeleteMode.degrees\n : (wiggle?.degrees ?? 0) * 2;\n const deleteWiggleDuration = wiggleDeleteMode\n ? wiggleDeleteMode.duration\n : (wiggle?.duration ?? 200) * 0.7; // Faster wiggle\n\n // If transitioning from normal wiggle, preserve the phase by scaling\n if (previousMode === \"normal\" && wiggle) {\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 [\n dragMode,\n position.active,\n deleteModeActive,\n anyItemInDeleteMode,\n wiggle,\n wiggleDeleteMode,\n ]\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":[]}
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","isPressingDeleteItem","children","wiggle","wiggleDeleteMode","holdStillToDeleteMs","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","targetMode","cancelAnimation","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","onPressIn","onPressOut","setTimeout","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 isPressingDeleteItem: SharedValue<boolean>;\n children: React.ReactNode;\n wiggle?: { duration: number; degrees: number };\n wiggleDeleteMode?: { duration: number; degrees: number };\n holdStillToDeleteMs?: 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 isPressingDeleteItem,\n children,\n wiggle,\n wiggleDeleteMode,\n holdStillToDeleteMs = 1000,\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 // Timer runs when item is active (being held)\n // Note: isActive (position.active.value) is set when gesture activates after long press\n // isDragging (dragMode.value) is also set at that time, but we primarily check isActive\n // to allow timer to work even in edge cases\n if (!isActive) {\n stillTimer.value = 0;\n return;\n }\n\n // Item is active - timer can run (dragMode should also be true at this point,\n // but we don't require it to allow timer to work in all cases)\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 holdStillToDeleteMs of being held still\n if (stillTimer.value >= holdStillToDeleteMs) {\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 // Determine the target wiggle mode\n let targetMode: \"none\" | \"normal\" | \"delete\" = \"none\";\n if (inDeleteMode && (wiggleDeleteMode || wiggle)) {\n targetMode = \"delete\";\n } else if (anyInDeleteMode && !isActive && wiggle) {\n targetMode = \"normal\";\n } else if (isEditMode && !isActive && wiggle) {\n targetMode = \"normal\";\n }\n\n // If no wiggle is configured at all, stop animation\n if (!wiggle && !wiggleDeleteMode) {\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 // If in delete mode but no wiggleDeleteMode and no wiggle, stop animation\n if (targetMode === \"delete\" && !wiggleDeleteMode && !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 // If normal mode but no wiggle, stop animation\n if (targetMode === \"normal\" && !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 // 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, use wiggleDeleteMode if provided, otherwise use 2x degrees and 0.7x duration\n if (targetMode === \"delete\") {\n const deleteWiggleDegrees = wiggleDeleteMode\n ? wiggleDeleteMode.degrees\n : (wiggle?.degrees ?? 0) * 2;\n const deleteWiggleDuration = wiggleDeleteMode\n ? wiggleDeleteMode.duration\n : (wiggle?.duration ?? 200) * 0.7; // Faster wiggle\n\n // If transitioning from normal wiggle, preserve the phase by scaling\n if (previousMode === \"normal\" && wiggle) {\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 [\n dragMode,\n position.active,\n deleteModeActive,\n anyItemInDeleteMode,\n wiggle,\n wiggleDeleteMode,\n ]\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 onPressIn={() => {\n // Mark that we're pressing an item to prevent ScrollView from canceling delete mode\n isPressingDeleteItem.value = true;\n }}\n onPressOut={() => {\n // Clear the flag after a short delay to allow onPress to fire\n setTimeout(() => {\n isPressingDeleteItem.value = false;\n }, 50);\n }}\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":[]}
@@ -112,6 +112,7 @@ const SwappableGrid = /*#__PURE__*/(0, _react.forwardRef)(({
112
112
  positions,
113
113
  dragMode,
114
114
  anyItemInDeleteMode,
115
+ isPressingDeleteItem,
115
116
  order,
116
117
  deleteComponentPosition
117
118
  } = (0, _useGridLayout.useGridLayout)({
@@ -290,8 +291,9 @@ const SwappableGrid = /*#__PURE__*/(0, _react.forwardRef)(({
290
291
  }
291
292
  touchEndTimeoutRef.current = setTimeout(() => {
292
293
  // Only cancel if still in delete mode and not dragging
294
+ // Don't cancel if user is currently pressing an item (they might be deleting it)
293
295
  // This ensures we don't cancel when user is interacting with items
294
- if (anyItemInDeleteMode.value && !dragMode.value) {
296
+ if (anyItemInDeleteMode.value && !dragMode.value && !isPressingDeleteItem.value) {
295
297
  anyItemInDeleteMode.value = false;
296
298
  }
297
299
  }, 100); // Small delay to let item interactions complete
@@ -317,6 +319,7 @@ const SwappableGrid = /*#__PURE__*/(0, _react.forwardRef)(({
317
319
  itemHeight: itemHeight,
318
320
  dragMode: dragMode,
319
321
  anyItemInDeleteMode: anyItemInDeleteMode,
322
+ isPressingDeleteItem: isPressingDeleteItem,
320
323
  wiggle: wiggle,
321
324
  wiggleDeleteMode: wiggleDeleteMode,
322
325
  holdStillToDeleteMs: holdStillToDeleteMs,
@@ -1 +1 @@
1
- {"version":3,"names":["_react","_interopRequireWildcard","require","_reactNative","_reactNativeReanimated","_reactNativeGestureHandler","_computerMinHeight","_interopRequireDefault","_useGridLayout","_ChildWrapper","_indexCalculations","e","__esModule","default","t","WeakMap","r","n","o","i","f","__proto__","has","get","set","hasOwnProperty","call","Object","defineProperty","getOwnPropertyDescriptor","AnimatedScrollView","Animated","createAnimatedComponent","ScrollView","normalizeKey","k","String","replace","SwappableGrid","forwardRef","children","itemWidth","itemHeight","gap","containerPadding","holdToDragMs","numColumns","onDragEnd","onOrderChange","onDelete","wiggle","wiggleDeleteMode","holdStillToDeleteMs","style","dragSizeIncreaseFactor","scrollThreshold","scrollSpeed","trailingComponent","deleteComponent","deleteComponentStyle","reverse","ref","scrollViewRef","useAnimatedRef","showTrailingComponent","setShowTrailingComponent","useState","showDeleteComponent","setShowDeleteComponent","isDragging","setIsDragging","currentNumColumns","setCurrentNumColumns","touchEndTimeoutRef","React","useRef","deleteComponentRef","useEffect","setTimeout","handleOnOrderChange","order","map","key","paddingBottom","useMemo","styleObj","StyleSheet","flatten","orderState","composed","dynamicNumColumns","onLayoutContent","originalOnLayoutContent","onLayoutScrollView","onScroll","deleteItem","childArray","positions","dragMode","anyItemInDeleteMode","deleteComponentPosition","useGridLayout","undefined","contentPaddingBottom","possibleCols","Math","floor","nativeEvent","layout","width","max","useAnimatedReaction","value","isDraggingValue","runOnJS","useImperativeHandle","cancelDeleteMode","trailingX","useDerivedValue","x","indexToXY","index","length","trailingY","y","trailingStyle","useAnimatedStyle","left","top","deleteComponentX","cols","totalWidth","deleteComponentY","rows","ceil","baseY","deleteComponentStyleAnimated","baseStyle","position","height","showTrailing","showDelete","itemsCountForHeight","baseHeight","computeMinHeight","calculatedHeight","totalItems","deleteComponentBottom","createElement","onLayout","scrollEventThrottle","contentContainerStyle","onTouchEnd","current","clearTimeout","View","styles","container","padding","GestureDetector","gesture","pointerEvents","absoluteFill","child","find","c","disableHoldToDelete","collapsable","flex","create","_default","exports"],"sources":["SwappableGrid.tsx"],"sourcesContent":["import React, {\n ReactNode,\n useEffect,\n useState,\n useImperativeHandle,\n forwardRef,\n} from \"react\";\nimport {\n View,\n StyleSheet,\n ScrollView,\n StyleProp,\n ViewStyle,\n LayoutChangeEvent,\n} from \"react-native\";\nimport Animated, {\n useAnimatedRef,\n useAnimatedStyle,\n useDerivedValue,\n useAnimatedReaction,\n runOnJS,\n} from \"react-native-reanimated\";\nimport { GestureDetector } from \"react-native-gesture-handler\";\nimport computeMinHeight from \"./utils/helpers/computerMinHeight\";\nimport { useGridLayout } from \"./utils/useGridLayout\";\nimport ChildWrapper from \"./ChildWrapper\";\nimport { indexToXY } from \"./utils/helpers/indexCalculations\";\n\nconst AnimatedScrollView = Animated.createAnimatedComponent(ScrollView);\n\nconst normalizeKey = (k: React.Key) => String(k).replace(/^\\.\\$/, \"\");\n\n/**\n * Props for the SwappableGrid component\n */\ntype SwappableGridProps = {\n /** The child components to render in the grid. Each child should have a unique key. */\n children: ReactNode;\n /** Width of each grid item in pixels */\n itemWidth: number;\n /** Height of each grid item in pixels */\n itemHeight: number;\n /** Gap between grid items in pixels. Defaults to 8. */\n gap?: number;\n /** Padding around the container in pixels. Defaults to 8. */\n containerPadding?: number;\n /** Duration in milliseconds to hold before drag starts. Defaults to 300. */\n holdToDragMs?: number;\n /** Number of columns in the grid. If not provided, will be calculated automatically based on container width. */\n numColumns?: number;\n /** Wiggle animation configuration when items are in drag mode or delete mode */\n wiggle?: {\n /** Duration of one wiggle cycle in milliseconds */\n duration: number;\n /** Rotation degrees for the wiggle animation */\n degrees: number;\n };\n /** Wiggle animation configuration specifically for delete mode. If not provided, uses 2x degrees and 0.7x duration of wiggle prop. */\n wiggleDeleteMode?: {\n /** Duration of one wiggle cycle in milliseconds */\n duration: number;\n /** Rotation degrees for the wiggle animation */\n degrees: number;\n };\n /** Duration in milliseconds to hold an item still before entering delete mode. Defaults to 1000. */\n holdStillToDeleteMs?: number;\n /** Callback fired when drag ends, providing the ordered array of child nodes */\n onDragEnd?: (ordered: ChildNode[]) => void;\n /** Callback fired when the order changes, providing an array of keys in the new order */\n onOrderChange?: (keys: string[]) => void;\n /** Callback fired when an item is deleted, providing the key of the deleted item */\n onDelete?: (key: string) => void;\n /** Factor by which the dragged item scales up. Defaults to 1.06. */\n dragSizeIncreaseFactor?: number;\n /** Speed of auto-scrolling when dragging near edges. Defaults to 10. */\n scrollSpeed?: number;\n /** Distance from edge in pixels that triggers auto-scroll. Defaults to 100. */\n scrollThreshold?: number;\n /** Custom style for the ScrollView container */\n style?: StyleProp<ViewStyle>;\n /** Component to render after all grid items (e.g., an \"Add\" button) */\n trailingComponent?: ReactNode;\n /** Component to render as a delete target (shown when dragging). If provided, disables hold-to-delete feature. */\n deleteComponent?: ReactNode;\n /** Custom style for the delete component. If provided, allows custom positioning. */\n deleteComponentStyle?: StyleProp<ViewStyle>;\n /** If true, reverses the order of items (right-to-left, bottom-to-top). Defaults to false. */\n reverse?: boolean;\n};\n\n/**\n * Ref methods for SwappableGrid component\n */\nexport interface SwappableGridRef {\n /** Cancels the delete mode if any item is currently in delete mode */\n cancelDeleteMode: () => void;\n}\n\n/**\n * SwappableGrid - A React Native component for creating a draggable, swappable grid layout.\n *\n * Features:\n * - Drag and drop to reorder items\n * - Long press to enter drag mode\n * - Auto-scroll when dragging near edges\n * - Optional wiggle animation during drag mode\n * - Optional delete functionality with hold-to-delete or delete component\n * - Support for trailing components (e.g., \"Add\" button)\n * - Automatic column calculation based on container width\n *\n * @example\n * ```tsx\n * <SwappableGrid\n * itemWidth={100}\n * itemHeight={100}\n * numColumns={3}\n * onOrderChange={(keys) => console.log('New order:', keys)}\n * >\n * {items.map(item => (\n * <View key={item.id}>{item.content}</View>\n * ))}\n * </SwappableGrid>\n * ```\n */\nconst SwappableGrid = forwardRef<SwappableGridRef, SwappableGridProps>(\n (\n {\n children,\n itemWidth,\n itemHeight,\n gap = 8,\n containerPadding = 8,\n holdToDragMs = 300,\n numColumns,\n onDragEnd,\n onOrderChange,\n onDelete,\n wiggle,\n wiggleDeleteMode,\n holdStillToDeleteMs = 1000,\n style,\n dragSizeIncreaseFactor = 1.06,\n scrollThreshold = 100,\n scrollSpeed = 10,\n trailingComponent,\n deleteComponent,\n deleteComponentStyle,\n reverse = false,\n },\n ref\n ) => {\n // MUST be Animated ref for scrollTo\n const scrollViewRef = useAnimatedRef<Animated.ScrollView>();\n const [showTrailingComponent, setShowTrailingComponent] =\n useState<boolean>(false);\n const [showDeleteComponent, setShowDeleteComponent] =\n useState<boolean>(false);\n const [isDragging, setIsDragging] = useState<boolean>(false);\n const [currentNumColumns, setCurrentNumColumns] = useState<number>(\n numColumns || 1\n );\n const touchEndTimeoutRef = React.useRef<ReturnType<\n typeof setTimeout\n > | null>(null);\n const deleteComponentRef = React.useRef<View>(null);\n\n useEffect(() => {\n setTimeout(() => {\n setShowTrailingComponent(true);\n setShowDeleteComponent(true);\n }, 50); // This is kinda a hack that makes the trailing component render in the correct position because it lets the other components render first to make the position calculation correct.\n }, []);\n\n const handleOnOrderChange = (order: string[]) => {\n if (onOrderChange) onOrderChange(order.map((key) => normalizeKey(key)));\n };\n\n // Extract paddingBottom from style prop to allow dragging into padding area\n const paddingBottom = React.useMemo(() => {\n if (!style) return 0;\n const styleObj = StyleSheet.flatten(style);\n return (styleObj.paddingBottom as number) || 0;\n }, [style]);\n\n const {\n orderState,\n composed,\n dynamicNumColumns,\n onLayoutContent: originalOnLayoutContent, // layout of the inner content view (for width/cols)\n onLayoutScrollView, // layout of the scroll viewport (for height)\n onScroll,\n deleteItem,\n childArray,\n positions,\n dragMode,\n anyItemInDeleteMode,\n order,\n deleteComponentPosition,\n } = useGridLayout({\n reverse,\n children,\n holdToDragMs,\n itemWidth,\n itemHeight,\n gap,\n containerPadding,\n numColumns,\n onDragEnd,\n onOrderChange: handleOnOrderChange,\n onDelete: onDelete ? (key) => onDelete(normalizeKey(key)) : undefined,\n scrollViewRef,\n scrollSpeed,\n scrollThreshold,\n contentPaddingBottom: paddingBottom,\n });\n\n // Track numColumns changes for height calculation\n const onLayoutContent = (e: LayoutChangeEvent) => {\n originalOnLayoutContent(e);\n // Update currentNumColumns for height calculation\n if (numColumns) {\n setCurrentNumColumns(numColumns);\n } else {\n const possibleCols = Math.floor(\n (e.nativeEvent.layout.width - containerPadding * 2 + gap) /\n (itemWidth + gap)\n );\n setCurrentNumColumns(Math.max(1, possibleCols));\n }\n };\n\n // Track drag state to show/hide delete component\n useAnimatedReaction(\n () => dragMode.value,\n (isDraggingValue) => {\n runOnJS(setIsDragging)(isDraggingValue);\n }\n );\n\n // Expose cancel delete mode function to parent\n useImperativeHandle(\n ref,\n () => ({\n cancelDeleteMode: () => {\n if (anyItemInDeleteMode.value) {\n anyItemInDeleteMode.value = false;\n }\n },\n }),\n [anyItemInDeleteMode]\n );\n\n const trailingX = useDerivedValue(() => {\n const { x } = indexToXY({\n index: order.value.length, // AFTER last swappable\n itemWidth,\n itemHeight,\n dynamicNumColumns,\n containerPadding,\n gap,\n });\n return x;\n });\n\n const trailingY = useDerivedValue(() => {\n const { y } = indexToXY({\n index: order.value.length,\n itemWidth,\n itemHeight,\n dynamicNumColumns,\n containerPadding,\n gap,\n });\n return y;\n });\n\n const trailingStyle = useAnimatedStyle(() => ({\n left: trailingX.value,\n top: trailingY.value,\n }));\n\n // Calculate default delete component position (bottom center)\n const deleteComponentX = useDerivedValue(() => {\n if (deleteComponentStyle) {\n // If custom style provided, position will be set by user\n return 0;\n }\n // Default: center horizontally\n const cols = dynamicNumColumns.value;\n const totalWidth =\n cols * itemWidth + (cols - 1) * gap + containerPadding * 2;\n return (totalWidth - itemWidth) / 2;\n });\n\n const deleteComponentY = useDerivedValue(() => {\n if (deleteComponentStyle) {\n // If custom style provided, position will be set by user\n return 0;\n }\n // Default: bottom of grid (after all items)\n // Account for trailing component if it exists\n const rows = Math.ceil(order.value.length / dynamicNumColumns.value);\n const baseY = containerPadding + rows * (itemHeight + gap);\n\n // If trailing component exists, add extra space so delete component appears below it\n if (trailingComponent && showTrailingComponent) {\n return baseY + (itemHeight + gap);\n }\n\n return baseY + gap;\n });\n\n const deleteComponentStyleAnimated = useAnimatedStyle(() => {\n const baseStyle: any = {\n position: \"absolute\",\n width: itemWidth,\n height: itemHeight,\n };\n\n // Use custom position if provided, otherwise use default\n if (deleteComponentStyle) {\n return baseStyle;\n }\n\n return {\n ...baseStyle,\n left: deleteComponentX.value,\n top: deleteComponentY.value,\n };\n });\n\n // Update delete component position for drop detection when default position changes\n useDerivedValue(() => {\n if (deleteComponent && deleteComponentPosition && !deleteComponentStyle) {\n // Only update if using default position (custom style uses onLayout)\n deleteComponentPosition.value = {\n x: deleteComponentX.value,\n y: deleteComponentY.value,\n width: itemWidth,\n height: itemHeight,\n };\n }\n });\n\n const showTrailing = !!(trailingComponent && showTrailingComponent);\n const showDelete = !!(deleteComponent && showDeleteComponent);\n // Make sure we calculate room for both trailing and delete components\n // Trailing component is part of the grid, delete component is positioned below\n const itemsCountForHeight = orderState.length + (showTrailing ? 1 : 0);\n\n // Calculate minimum height needed (on JS thread since computeMinHeight is not a worklet)\n const baseHeight = computeMinHeight(\n itemsCountForHeight,\n currentNumColumns,\n itemHeight + gap,\n containerPadding\n );\n\n // If delete component is shown and using default position, add extra space for it\n let calculatedHeight = baseHeight;\n if (showDelete && !deleteComponentStyle) {\n // Account for trailing component when calculating rows (same logic as deleteComponentY)\n const totalItems = orderState.length + (showTrailing ? 1 : 0);\n const rows = Math.ceil(totalItems / currentNumColumns);\n const baseY = containerPadding + rows * (itemHeight + gap);\n\n // If trailing component exists, add extra space so delete component appears below it\n let deleteComponentY = baseY;\n if (showTrailing) {\n deleteComponentY = baseY + (itemHeight + gap);\n } else {\n deleteComponentY = baseY + gap;\n }\n\n const deleteComponentBottom = deleteComponentY + itemHeight;\n // Ensure container is tall enough to show the delete component\n calculatedHeight = Math.max(\n baseHeight,\n deleteComponentBottom + containerPadding\n );\n }\n\n return (\n <AnimatedScrollView\n ref={scrollViewRef}\n onScroll={onScroll}\n onLayout={onLayoutScrollView} // viewport height comes from the SCROLLVIEW\n scrollEventThrottle={16}\n contentContainerStyle={[style]}\n onTouchEnd={() => {\n // Cancel delete mode when user touches outside items\n // Add a small delay to avoid canceling when user taps on items\n // (items might briefly activate, which would prevent cancellation)\n if (touchEndTimeoutRef.current) {\n clearTimeout(touchEndTimeoutRef.current);\n }\n touchEndTimeoutRef.current = setTimeout(() => {\n // Only cancel if still in delete mode and not dragging\n // This ensures we don't cancel when user is interacting with items\n if (anyItemInDeleteMode.value && !dragMode.value) {\n anyItemInDeleteMode.value = false;\n }\n }, 100); // Small delay to let item interactions complete\n }}\n >\n <View\n style={[\n styles.container,\n {\n padding: containerPadding,\n height: calculatedHeight,\n },\n ]}\n onLayout={onLayoutContent}\n >\n <GestureDetector gesture={composed}>\n <View pointerEvents=\"box-none\" style={StyleSheet.absoluteFill}>\n {orderState.map((key) => {\n const child = childArray.find(\n (c) => c.key && normalizeKey(c.key) === normalizeKey(key)\n );\n if (!child) return null;\n return (\n <ChildWrapper\n key={key}\n position={positions[key]}\n itemWidth={itemWidth}\n itemHeight={itemHeight}\n dragMode={dragMode}\n anyItemInDeleteMode={anyItemInDeleteMode}\n wiggle={wiggle}\n wiggleDeleteMode={wiggleDeleteMode}\n holdStillToDeleteMs={holdStillToDeleteMs}\n dragSizeIncreaseFactor={dragSizeIncreaseFactor}\n disableHoldToDelete={!onDelete || !!deleteComponent}\n onDelete={() => {\n deleteItem(key);\n if (onDelete) {\n onDelete(normalizeKey(key));\n }\n }}\n >\n {child}\n </ChildWrapper>\n );\n })}\n </View>\n </GestureDetector>\n\n {/* Trailing rendered OUTSIDE the GestureDetector */}\n {trailingComponent && showTrailingComponent && (\n <Animated.View\n pointerEvents=\"box-none\"\n collapsable={false}\n style={[\n {\n position: \"absolute\",\n width: itemWidth,\n height: itemHeight,\n },\n trailingStyle, // 👈 left/top from UI thread\n ]}\n >\n <View pointerEvents=\"auto\" style={{ flex: 1 }}>\n {trailingComponent}\n </View>\n </Animated.View>\n )}\n\n {/* Delete component rendered OUTSIDE the GestureDetector - only show when dragging */}\n {deleteComponent && showDeleteComponent && isDragging && (\n <Animated.View\n ref={deleteComponentRef}\n pointerEvents=\"box-none\"\n collapsable={false}\n style={[\n deleteComponentStyleAnimated,\n deleteComponentStyle, // User can override position with custom style\n ]}\n onLayout={(e) => {\n // Update position for drop detection\n const { x, y, width, height } = e.nativeEvent.layout;\n if (deleteComponentPosition) {\n deleteComponentPosition.value = { x, y, width, height };\n }\n }}\n >\n <View pointerEvents=\"auto\" style={{ flex: 1 }}>\n {deleteComponent}\n </View>\n </Animated.View>\n )}\n </View>\n </AnimatedScrollView>\n );\n }\n);\n\nconst styles = StyleSheet.create({\n container: {\n width: \"100%\",\n },\n});\n\nexport default SwappableGrid;\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":["_react","_interopRequireWildcard","require","_reactNative","_reactNativeReanimated","_reactNativeGestureHandler","_computerMinHeight","_interopRequireDefault","_useGridLayout","_ChildWrapper","_indexCalculations","e","__esModule","default","t","WeakMap","r","n","o","i","f","__proto__","has","get","set","hasOwnProperty","call","Object","defineProperty","getOwnPropertyDescriptor","AnimatedScrollView","Animated","createAnimatedComponent","ScrollView","normalizeKey","k","String","replace","SwappableGrid","forwardRef","children","itemWidth","itemHeight","gap","containerPadding","holdToDragMs","numColumns","onDragEnd","onOrderChange","onDelete","wiggle","wiggleDeleteMode","holdStillToDeleteMs","style","dragSizeIncreaseFactor","scrollThreshold","scrollSpeed","trailingComponent","deleteComponent","deleteComponentStyle","reverse","ref","scrollViewRef","useAnimatedRef","showTrailingComponent","setShowTrailingComponent","useState","showDeleteComponent","setShowDeleteComponent","isDragging","setIsDragging","currentNumColumns","setCurrentNumColumns","touchEndTimeoutRef","React","useRef","deleteComponentRef","useEffect","setTimeout","handleOnOrderChange","order","map","key","paddingBottom","useMemo","styleObj","StyleSheet","flatten","orderState","composed","dynamicNumColumns","onLayoutContent","originalOnLayoutContent","onLayoutScrollView","onScroll","deleteItem","childArray","positions","dragMode","anyItemInDeleteMode","isPressingDeleteItem","deleteComponentPosition","useGridLayout","undefined","contentPaddingBottom","possibleCols","Math","floor","nativeEvent","layout","width","max","useAnimatedReaction","value","isDraggingValue","runOnJS","useImperativeHandle","cancelDeleteMode","trailingX","useDerivedValue","x","indexToXY","index","length","trailingY","y","trailingStyle","useAnimatedStyle","left","top","deleteComponentX","cols","totalWidth","deleteComponentY","rows","ceil","baseY","deleteComponentStyleAnimated","baseStyle","position","height","showTrailing","showDelete","itemsCountForHeight","baseHeight","computeMinHeight","calculatedHeight","totalItems","deleteComponentBottom","createElement","onLayout","scrollEventThrottle","contentContainerStyle","onTouchEnd","current","clearTimeout","View","styles","container","padding","GestureDetector","gesture","pointerEvents","absoluteFill","child","find","c","disableHoldToDelete","collapsable","flex","create","_default","exports"],"sources":["SwappableGrid.tsx"],"sourcesContent":["import React, {\n ReactNode,\n useEffect,\n useState,\n useImperativeHandle,\n forwardRef,\n} from \"react\";\nimport {\n View,\n StyleSheet,\n ScrollView,\n StyleProp,\n ViewStyle,\n LayoutChangeEvent,\n} from \"react-native\";\nimport Animated, {\n useAnimatedRef,\n useAnimatedStyle,\n useDerivedValue,\n useAnimatedReaction,\n runOnJS,\n} from \"react-native-reanimated\";\nimport { GestureDetector } from \"react-native-gesture-handler\";\nimport computeMinHeight from \"./utils/helpers/computerMinHeight\";\nimport { useGridLayout } from \"./utils/useGridLayout\";\nimport ChildWrapper from \"./ChildWrapper\";\nimport { indexToXY } from \"./utils/helpers/indexCalculations\";\n\nconst AnimatedScrollView = Animated.createAnimatedComponent(ScrollView);\n\nconst normalizeKey = (k: React.Key) => String(k).replace(/^\\.\\$/, \"\");\n\n/**\n * Props for the SwappableGrid component\n */\ntype SwappableGridProps = {\n /** The child components to render in the grid. Each child should have a unique key. */\n children: ReactNode;\n /** Width of each grid item in pixels */\n itemWidth: number;\n /** Height of each grid item in pixels */\n itemHeight: number;\n /** Gap between grid items in pixels. Defaults to 8. */\n gap?: number;\n /** Padding around the container in pixels. Defaults to 8. */\n containerPadding?: number;\n /** Duration in milliseconds to hold before drag starts. Defaults to 300. */\n holdToDragMs?: number;\n /** Number of columns in the grid. If not provided, will be calculated automatically based on container width. */\n numColumns?: number;\n /** Wiggle animation configuration when items are in drag mode or delete mode */\n wiggle?: {\n /** Duration of one wiggle cycle in milliseconds */\n duration: number;\n /** Rotation degrees for the wiggle animation */\n degrees: number;\n };\n /** Wiggle animation configuration specifically for delete mode. If not provided, uses 2x degrees and 0.7x duration of wiggle prop. */\n wiggleDeleteMode?: {\n /** Duration of one wiggle cycle in milliseconds */\n duration: number;\n /** Rotation degrees for the wiggle animation */\n degrees: number;\n };\n /** Duration in milliseconds to hold an item still before entering delete mode. Defaults to 1000. */\n holdStillToDeleteMs?: number;\n /** Callback fired when drag ends, providing the ordered array of child nodes */\n onDragEnd?: (ordered: ChildNode[]) => void;\n /** Callback fired when the order changes, providing an array of keys in the new order */\n onOrderChange?: (keys: string[]) => void;\n /** Callback fired when an item is deleted, providing the key of the deleted item */\n onDelete?: (key: string) => void;\n /** Factor by which the dragged item scales up. Defaults to 1.06. */\n dragSizeIncreaseFactor?: number;\n /** Speed of auto-scrolling when dragging near edges. Defaults to 10. */\n scrollSpeed?: number;\n /** Distance from edge in pixels that triggers auto-scroll. Defaults to 100. */\n scrollThreshold?: number;\n /** Custom style for the ScrollView container */\n style?: StyleProp<ViewStyle>;\n /** Component to render after all grid items (e.g., an \"Add\" button) */\n trailingComponent?: ReactNode;\n /** Component to render as a delete target (shown when dragging). If provided, disables hold-to-delete feature. */\n deleteComponent?: ReactNode;\n /** Custom style for the delete component. If provided, allows custom positioning. */\n deleteComponentStyle?: StyleProp<ViewStyle>;\n /** If true, reverses the order of items (right-to-left, bottom-to-top). Defaults to false. */\n reverse?: boolean;\n};\n\n/**\n * Ref methods for SwappableGrid component\n */\nexport interface SwappableGridRef {\n /** Cancels the delete mode if any item is currently in delete mode */\n cancelDeleteMode: () => void;\n}\n\n/**\n * SwappableGrid - A React Native component for creating a draggable, swappable grid layout.\n *\n * Features:\n * - Drag and drop to reorder items\n * - Long press to enter drag mode\n * - Auto-scroll when dragging near edges\n * - Optional wiggle animation during drag mode\n * - Optional delete functionality with hold-to-delete or delete component\n * - Support for trailing components (e.g., \"Add\" button)\n * - Automatic column calculation based on container width\n *\n * @example\n * ```tsx\n * <SwappableGrid\n * itemWidth={100}\n * itemHeight={100}\n * numColumns={3}\n * onOrderChange={(keys) => console.log('New order:', keys)}\n * >\n * {items.map(item => (\n * <View key={item.id}>{item.content}</View>\n * ))}\n * </SwappableGrid>\n * ```\n */\nconst SwappableGrid = forwardRef<SwappableGridRef, SwappableGridProps>(\n (\n {\n children,\n itemWidth,\n itemHeight,\n gap = 8,\n containerPadding = 8,\n holdToDragMs = 300,\n numColumns,\n onDragEnd,\n onOrderChange,\n onDelete,\n wiggle,\n wiggleDeleteMode,\n holdStillToDeleteMs = 1000,\n style,\n dragSizeIncreaseFactor = 1.06,\n scrollThreshold = 100,\n scrollSpeed = 10,\n trailingComponent,\n deleteComponent,\n deleteComponentStyle,\n reverse = false,\n },\n ref\n ) => {\n // MUST be Animated ref for scrollTo\n const scrollViewRef = useAnimatedRef<Animated.ScrollView>();\n const [showTrailingComponent, setShowTrailingComponent] =\n useState<boolean>(false);\n const [showDeleteComponent, setShowDeleteComponent] =\n useState<boolean>(false);\n const [isDragging, setIsDragging] = useState<boolean>(false);\n const [currentNumColumns, setCurrentNumColumns] = useState<number>(\n numColumns || 1\n );\n const touchEndTimeoutRef = React.useRef<ReturnType<\n typeof setTimeout\n > | null>(null);\n const deleteComponentRef = React.useRef<View>(null);\n\n useEffect(() => {\n setTimeout(() => {\n setShowTrailingComponent(true);\n setShowDeleteComponent(true);\n }, 50); // This is kinda a hack that makes the trailing component render in the correct position because it lets the other components render first to make the position calculation correct.\n }, []);\n\n const handleOnOrderChange = (order: string[]) => {\n if (onOrderChange) onOrderChange(order.map((key) => normalizeKey(key)));\n };\n\n // Extract paddingBottom from style prop to allow dragging into padding area\n const paddingBottom = React.useMemo(() => {\n if (!style) return 0;\n const styleObj = StyleSheet.flatten(style);\n return (styleObj.paddingBottom as number) || 0;\n }, [style]);\n\n const {\n orderState,\n composed,\n dynamicNumColumns,\n onLayoutContent: originalOnLayoutContent, // layout of the inner content view (for width/cols)\n onLayoutScrollView, // layout of the scroll viewport (for height)\n onScroll,\n deleteItem,\n childArray,\n positions,\n dragMode,\n anyItemInDeleteMode,\n isPressingDeleteItem,\n order,\n deleteComponentPosition,\n } = useGridLayout({\n reverse,\n children,\n holdToDragMs,\n itemWidth,\n itemHeight,\n gap,\n containerPadding,\n numColumns,\n onDragEnd,\n onOrderChange: handleOnOrderChange,\n onDelete: onDelete ? (key) => onDelete(normalizeKey(key)) : undefined,\n scrollViewRef,\n scrollSpeed,\n scrollThreshold,\n contentPaddingBottom: paddingBottom,\n });\n\n // Track numColumns changes for height calculation\n const onLayoutContent = (e: LayoutChangeEvent) => {\n originalOnLayoutContent(e);\n // Update currentNumColumns for height calculation\n if (numColumns) {\n setCurrentNumColumns(numColumns);\n } else {\n const possibleCols = Math.floor(\n (e.nativeEvent.layout.width - containerPadding * 2 + gap) /\n (itemWidth + gap)\n );\n setCurrentNumColumns(Math.max(1, possibleCols));\n }\n };\n\n // Track drag state to show/hide delete component\n useAnimatedReaction(\n () => dragMode.value,\n (isDraggingValue) => {\n runOnJS(setIsDragging)(isDraggingValue);\n }\n );\n\n // Expose cancel delete mode function to parent\n useImperativeHandle(\n ref,\n () => ({\n cancelDeleteMode: () => {\n if (anyItemInDeleteMode.value) {\n anyItemInDeleteMode.value = false;\n }\n },\n }),\n [anyItemInDeleteMode]\n );\n\n const trailingX = useDerivedValue(() => {\n const { x } = indexToXY({\n index: order.value.length, // AFTER last swappable\n itemWidth,\n itemHeight,\n dynamicNumColumns,\n containerPadding,\n gap,\n });\n return x;\n });\n\n const trailingY = useDerivedValue(() => {\n const { y } = indexToXY({\n index: order.value.length,\n itemWidth,\n itemHeight,\n dynamicNumColumns,\n containerPadding,\n gap,\n });\n return y;\n });\n\n const trailingStyle = useAnimatedStyle(() => ({\n left: trailingX.value,\n top: trailingY.value,\n }));\n\n // Calculate default delete component position (bottom center)\n const deleteComponentX = useDerivedValue(() => {\n if (deleteComponentStyle) {\n // If custom style provided, position will be set by user\n return 0;\n }\n // Default: center horizontally\n const cols = dynamicNumColumns.value;\n const totalWidth =\n cols * itemWidth + (cols - 1) * gap + containerPadding * 2;\n return (totalWidth - itemWidth) / 2;\n });\n\n const deleteComponentY = useDerivedValue(() => {\n if (deleteComponentStyle) {\n // If custom style provided, position will be set by user\n return 0;\n }\n // Default: bottom of grid (after all items)\n // Account for trailing component if it exists\n const rows = Math.ceil(order.value.length / dynamicNumColumns.value);\n const baseY = containerPadding + rows * (itemHeight + gap);\n\n // If trailing component exists, add extra space so delete component appears below it\n if (trailingComponent && showTrailingComponent) {\n return baseY + (itemHeight + gap);\n }\n\n return baseY + gap;\n });\n\n const deleteComponentStyleAnimated = useAnimatedStyle(() => {\n const baseStyle: any = {\n position: \"absolute\",\n width: itemWidth,\n height: itemHeight,\n };\n\n // Use custom position if provided, otherwise use default\n if (deleteComponentStyle) {\n return baseStyle;\n }\n\n return {\n ...baseStyle,\n left: deleteComponentX.value,\n top: deleteComponentY.value,\n };\n });\n\n // Update delete component position for drop detection when default position changes\n useDerivedValue(() => {\n if (deleteComponent && deleteComponentPosition && !deleteComponentStyle) {\n // Only update if using default position (custom style uses onLayout)\n deleteComponentPosition.value = {\n x: deleteComponentX.value,\n y: deleteComponentY.value,\n width: itemWidth,\n height: itemHeight,\n };\n }\n });\n\n const showTrailing = !!(trailingComponent && showTrailingComponent);\n const showDelete = !!(deleteComponent && showDeleteComponent);\n // Make sure we calculate room for both trailing and delete components\n // Trailing component is part of the grid, delete component is positioned below\n const itemsCountForHeight = orderState.length + (showTrailing ? 1 : 0);\n\n // Calculate minimum height needed (on JS thread since computeMinHeight is not a worklet)\n const baseHeight = computeMinHeight(\n itemsCountForHeight,\n currentNumColumns,\n itemHeight + gap,\n containerPadding\n );\n\n // If delete component is shown and using default position, add extra space for it\n let calculatedHeight = baseHeight;\n if (showDelete && !deleteComponentStyle) {\n // Account for trailing component when calculating rows (same logic as deleteComponentY)\n const totalItems = orderState.length + (showTrailing ? 1 : 0);\n const rows = Math.ceil(totalItems / currentNumColumns);\n const baseY = containerPadding + rows * (itemHeight + gap);\n\n // If trailing component exists, add extra space so delete component appears below it\n let deleteComponentY = baseY;\n if (showTrailing) {\n deleteComponentY = baseY + (itemHeight + gap);\n } else {\n deleteComponentY = baseY + gap;\n }\n\n const deleteComponentBottom = deleteComponentY + itemHeight;\n // Ensure container is tall enough to show the delete component\n calculatedHeight = Math.max(\n baseHeight,\n deleteComponentBottom + containerPadding\n );\n }\n\n return (\n <AnimatedScrollView\n ref={scrollViewRef}\n onScroll={onScroll}\n onLayout={onLayoutScrollView} // viewport height comes from the SCROLLVIEW\n scrollEventThrottle={16}\n contentContainerStyle={[style]}\n onTouchEnd={() => {\n // Cancel delete mode when user touches outside items\n // Add a small delay to avoid canceling when user taps on items\n // (items might briefly activate, which would prevent cancellation)\n if (touchEndTimeoutRef.current) {\n clearTimeout(touchEndTimeoutRef.current);\n }\n touchEndTimeoutRef.current = setTimeout(() => {\n // Only cancel if still in delete mode and not dragging\n // Don't cancel if user is currently pressing an item (they might be deleting it)\n // This ensures we don't cancel when user is interacting with items\n if (\n anyItemInDeleteMode.value &&\n !dragMode.value &&\n !isPressingDeleteItem.value\n ) {\n anyItemInDeleteMode.value = false;\n }\n }, 100); // Small delay to let item interactions complete\n }}\n >\n <View\n style={[\n styles.container,\n {\n padding: containerPadding,\n height: calculatedHeight,\n },\n ]}\n onLayout={onLayoutContent}\n >\n <GestureDetector gesture={composed}>\n <View pointerEvents=\"box-none\" style={StyleSheet.absoluteFill}>\n {orderState.map((key) => {\n const child = childArray.find(\n (c) => c.key && normalizeKey(c.key) === normalizeKey(key)\n );\n if (!child) return null;\n return (\n <ChildWrapper\n key={key}\n position={positions[key]}\n itemWidth={itemWidth}\n itemHeight={itemHeight}\n dragMode={dragMode}\n anyItemInDeleteMode={anyItemInDeleteMode}\n isPressingDeleteItem={isPressingDeleteItem}\n wiggle={wiggle}\n wiggleDeleteMode={wiggleDeleteMode}\n holdStillToDeleteMs={holdStillToDeleteMs}\n dragSizeIncreaseFactor={dragSizeIncreaseFactor}\n disableHoldToDelete={!onDelete || !!deleteComponent}\n onDelete={() => {\n deleteItem(key);\n if (onDelete) {\n onDelete(normalizeKey(key));\n }\n }}\n >\n {child}\n </ChildWrapper>\n );\n })}\n </View>\n </GestureDetector>\n\n {/* Trailing rendered OUTSIDE the GestureDetector */}\n {trailingComponent && showTrailingComponent && (\n <Animated.View\n pointerEvents=\"box-none\"\n collapsable={false}\n style={[\n {\n position: \"absolute\",\n width: itemWidth,\n height: itemHeight,\n },\n trailingStyle, // 👈 left/top from UI thread\n ]}\n >\n <View pointerEvents=\"auto\" style={{ flex: 1 }}>\n {trailingComponent}\n </View>\n </Animated.View>\n )}\n\n {/* Delete component rendered OUTSIDE the GestureDetector - only show when dragging */}\n {deleteComponent && showDeleteComponent && isDragging && (\n <Animated.View\n ref={deleteComponentRef}\n pointerEvents=\"box-none\"\n collapsable={false}\n style={[\n deleteComponentStyleAnimated,\n deleteComponentStyle, // User can override position with custom style\n ]}\n onLayout={(e) => {\n // Update position for drop detection\n const { x, y, width, height } = e.nativeEvent.layout;\n if (deleteComponentPosition) {\n deleteComponentPosition.value = { x, y, width, height };\n }\n }}\n >\n <View pointerEvents=\"auto\" style={{ flex: 1 }}>\n {deleteComponent}\n </View>\n </Animated.View>\n )}\n </View>\n </AnimatedScrollView>\n );\n }\n);\n\nconst styles = StyleSheet.create({\n container: {\n width: \"100%\",\n },\n});\n\nexport default SwappableGrid;\n"],"mappings":"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","ignoreList":[]}
@@ -52,6 +52,7 @@ function useGridLayout({
52
52
  const startY = (0, _reactNativeReanimated.useSharedValue)(0);
53
53
  const dragMode = (0, _reactNativeReanimated.useSharedValue)(false);
54
54
  const anyItemInDeleteMode = (0, _reactNativeReanimated.useSharedValue)(false); // Global delete mode state
55
+ const isPressingDeleteItem = (0, _reactNativeReanimated.useSharedValue)(false); // Track if user is pressing an item in delete mode
55
56
  const contentH = (0, _reactNativeReanimated.useSharedValue)(0);
56
57
  const scrollOffset = (0, _reactNativeReanimated.useSharedValue)(0);
57
58
  const onScroll = (0, _reactNativeReanimated.useAnimatedScrollHandler)({
@@ -190,6 +191,7 @@ function useGridLayout({
190
191
  orderState,
191
192
  dragMode,
192
193
  anyItemInDeleteMode,
194
+ isPressingDeleteItem,
193
195
  composed,
194
196
  dynamicNumColumns,
195
197
  onLayoutContent,
@@ -1 +1 @@
1
- {"version":3,"names":["_react","require","_reactNativeGestureHandler","_reactNativeReanimated","_indexCalculations","_PanWithLongPress","useGridLayout","reverse","children","itemWidth","itemHeight","gap","containerPadding","holdToDragMs","numColumns","onDragEnd","onOrderChange","onDelete","scrollViewRef","scrollSpeed","scrollThreshold","contentPaddingBottom","childArray","Children","toArray","filter","isValidElement","keys","map","child","key","Error","String","orderState","setOrderState","useState","itemsByKey","useMemo","forEach","dynamicNumColumns","useSharedValue","order","contentW","viewportH","activeKey","offsetX","offsetY","startX","startY","dragMode","anyItemInDeleteMode","contentH","scrollOffset","onScroll","useAnimatedScrollHandler","e","value","contentOffset","y","positionsArray","d","i","x","indexToXY","index","pos","active","positions","obj","Math","random","toString","slice","deleteItem","prev","k","useDerivedValue","p","displayIndex","length","scale","min","damping","stiffness","mass","max","withSpring","onLayoutScrollView","nativeEvent","layout","height","onLayoutContent","width","possibleCols","floor","deleteComponentPosition","composed","Gesture","Simultaneous","PanWithLongPress"],"sources":["useGridLayout.ts"],"sourcesContent":["import { Children, isValidElement, ReactNode, useMemo, useState } from \"react\";\nimport { LayoutChangeEvent } from \"react-native\";\nimport { Gesture } from \"react-native-gesture-handler\";\nimport {\n AnimatedRef,\n SharedValue,\n useAnimatedScrollHandler,\n useDerivedValue,\n useSharedValue,\n withSpring,\n} from \"react-native-reanimated\";\nimport { indexToXY } from \"./helpers/indexCalculations\";\nimport { PanWithLongPress } from \"./helpers/gestures/PanWithLongPress\";\n\ninterface useGridLayoutProps {\n reverse?: boolean;\n children: ReactNode;\n itemWidth: number;\n itemHeight: number;\n gap: number;\n containerPadding: number;\n holdToDragMs: number;\n numColumns?: number;\n onDragEnd?: (ordered: ChildNode[]) => void;\n onOrderChange?: (keys: string[]) => void;\n onDelete?: (key: string) => void;\n scrollViewRef: AnimatedRef<any>;\n scrollSpeed: number;\n scrollThreshold: number;\n contentPaddingBottom?: number;\n}\n\nexport function useGridLayout({\n reverse,\n children,\n itemWidth,\n itemHeight,\n gap,\n containerPadding,\n holdToDragMs,\n numColumns,\n onDragEnd,\n onOrderChange,\n onDelete,\n scrollViewRef,\n scrollSpeed,\n scrollThreshold,\n contentPaddingBottom = 0,\n}: useGridLayoutProps) {\n const childArray = Children.toArray(children).filter(isValidElement);\n const keys = childArray.map((child) => {\n if (!(\"key\" in child) || child.key == null) {\n throw new Error(\"All children must have a unique 'key' prop.\");\n }\n return String(child.key);\n });\n\n const [orderState, setOrderState] = useState(keys);\n\n const itemsByKey = useMemo(() => {\n const map: Record<string, ReactNode> = {};\n childArray.forEach((child) => {\n map[String(child.key)] = child;\n });\n return map;\n }, [children]);\n\n const dynamicNumColumns: SharedValue<number> = useSharedValue(\n numColumns ? numColumns : 1\n );\n const order = useSharedValue<string[]>(orderState);\n const contentW = useSharedValue(0);\n const viewportH = useSharedValue(0); // **visible height of ScrollView**\n const activeKey = useSharedValue<string | null>(null);\n const offsetX = useSharedValue(0);\n const offsetY = useSharedValue(0);\n const startX = useSharedValue(0);\n const startY = useSharedValue(0);\n const dragMode = useSharedValue(false);\n const anyItemInDeleteMode = useSharedValue(false); // Global delete mode state\n const contentH = useSharedValue(0);\n const scrollOffset = useSharedValue(0);\n\n const onScroll = useAnimatedScrollHandler({\n onScroll: (e) => {\n scrollOffset.value = e.contentOffset.y;\n },\n });\n\n // initial positions (create shared values consistently by data length)\n const positionsArray = childArray.map((d, i) => {\n const { x, y } = indexToXY({\n index: i,\n itemWidth,\n itemHeight,\n dynamicNumColumns,\n containerPadding,\n gap,\n });\n return {\n key: d.key,\n pos: {\n x: useSharedValue(x),\n y: useSharedValue(y),\n active: useSharedValue(0),\n },\n };\n });\n\n const positions = useMemo(() => {\n const obj: Record<string, (typeof positionsArray)[number][\"pos\"]> = {};\n\n positionsArray.forEach(({ key, pos }) => {\n obj[key ?? `key-${Math.random().toString(36).slice(2)}`] = pos;\n });\n\n return obj;\n }, [positionsArray]);\n\n const deleteItem = (key: string) => {\n setOrderState((prev) => prev.filter((k) => k !== key));\n order.value = order.value.filter((k) => k !== key);\n onOrderChange?.([...order.value]);\n // Call onDelete callback if provided (for both delete button and drop-to-delete)\n if (onDelete) {\n onDelete(key);\n }\n };\n\n useDerivedValue(() => {\n order.value.forEach((key, i) => {\n if (activeKey.value === key) return; // ⬅️ do not layout the active tile\n\n const p = positions[key];\n if (!p) return;\n\n const displayIndex = reverse ? order.value.length - 1 - i : i;\n\n const { x, y } = indexToXY({\n index: displayIndex,\n itemWidth,\n itemHeight,\n dynamicNumColumns,\n containerPadding,\n gap,\n });\n\n const scale = Math.min(itemWidth, itemHeight) / 100; // 100px baseline\n\n const damping = 18 * scale;\n const stiffness = 240 * scale;\n const mass = Math.max(0.1, scale); // helps stability for tiny items\n\n p.x.value = withSpring(x, { damping, stiffness, mass });\n p.y.value = withSpring(y, { damping, stiffness, mass });\n });\n });\n\n // Layout of the ScrollView (viewport) — height we compare against for edge-scrolling\n const onLayoutScrollView = (e: LayoutChangeEvent) => {\n viewportH.value = e.nativeEvent.layout.height;\n };\n\n // Layout of the content view — width used to compute columns\n const onLayoutContent = (e: LayoutChangeEvent) => {\n contentW.value = e.nativeEvent.layout.width;\n contentH.value = e.nativeEvent.layout.height;\n\n if (numColumns) {\n dynamicNumColumns.value = numColumns;\n } else {\n const possibleCols = Math.floor(\n (e.nativeEvent.layout.width - containerPadding * 2 + gap) /\n (itemWidth + gap)\n );\n dynamicNumColumns.value = Math.max(1, possibleCols);\n }\n };\n\n const deleteComponentPosition = useSharedValue<{\n x: number;\n y: number;\n width: number;\n height: number;\n } | null>(null);\n\n const composed = Gesture.Simultaneous(\n PanWithLongPress({\n contentH,\n order,\n dynamicNumColumns,\n activeKey,\n offsetX,\n offsetY,\n startX,\n startY,\n dragMode,\n positions,\n itemsByKey,\n itemWidth,\n itemHeight,\n containerPadding,\n gap,\n setOrderState,\n onDragEnd,\n onOrderChange,\n scrollViewRef,\n scrollOffset,\n viewportH,\n holdToDragMs,\n scrollSpeed,\n scrollThreshold,\n reverse,\n deleteComponentPosition,\n deleteItem,\n contentPaddingBottom,\n })\n );\n\n return {\n itemsByKey,\n orderState,\n dragMode,\n anyItemInDeleteMode,\n composed,\n dynamicNumColumns,\n onLayoutContent,\n onLayoutScrollView,\n positions,\n onScroll,\n childArray,\n order,\n deleteItem,\n deleteComponentPosition,\n };\n}\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":["_react","require","_reactNativeGestureHandler","_reactNativeReanimated","_indexCalculations","_PanWithLongPress","useGridLayout","reverse","children","itemWidth","itemHeight","gap","containerPadding","holdToDragMs","numColumns","onDragEnd","onOrderChange","onDelete","scrollViewRef","scrollSpeed","scrollThreshold","contentPaddingBottom","childArray","Children","toArray","filter","isValidElement","keys","map","child","key","Error","String","orderState","setOrderState","useState","itemsByKey","useMemo","forEach","dynamicNumColumns","useSharedValue","order","contentW","viewportH","activeKey","offsetX","offsetY","startX","startY","dragMode","anyItemInDeleteMode","isPressingDeleteItem","contentH","scrollOffset","onScroll","useAnimatedScrollHandler","e","value","contentOffset","y","positionsArray","d","i","x","indexToXY","index","pos","active","positions","obj","Math","random","toString","slice","deleteItem","prev","k","useDerivedValue","p","displayIndex","length","scale","min","damping","stiffness","mass","max","withSpring","onLayoutScrollView","nativeEvent","layout","height","onLayoutContent","width","possibleCols","floor","deleteComponentPosition","composed","Gesture","Simultaneous","PanWithLongPress"],"sources":["useGridLayout.ts"],"sourcesContent":["import { Children, isValidElement, ReactNode, useMemo, useState } from \"react\";\nimport { LayoutChangeEvent } from \"react-native\";\nimport { Gesture } from \"react-native-gesture-handler\";\nimport {\n AnimatedRef,\n SharedValue,\n useAnimatedScrollHandler,\n useDerivedValue,\n useSharedValue,\n withSpring,\n} from \"react-native-reanimated\";\nimport { indexToXY } from \"./helpers/indexCalculations\";\nimport { PanWithLongPress } from \"./helpers/gestures/PanWithLongPress\";\n\ninterface useGridLayoutProps {\n reverse?: boolean;\n children: ReactNode;\n itemWidth: number;\n itemHeight: number;\n gap: number;\n containerPadding: number;\n holdToDragMs: number;\n numColumns?: number;\n onDragEnd?: (ordered: ChildNode[]) => void;\n onOrderChange?: (keys: string[]) => void;\n onDelete?: (key: string) => void;\n scrollViewRef: AnimatedRef<any>;\n scrollSpeed: number;\n scrollThreshold: number;\n contentPaddingBottom?: number;\n}\n\nexport function useGridLayout({\n reverse,\n children,\n itemWidth,\n itemHeight,\n gap,\n containerPadding,\n holdToDragMs,\n numColumns,\n onDragEnd,\n onOrderChange,\n onDelete,\n scrollViewRef,\n scrollSpeed,\n scrollThreshold,\n contentPaddingBottom = 0,\n}: useGridLayoutProps) {\n const childArray = Children.toArray(children).filter(isValidElement);\n const keys = childArray.map((child) => {\n if (!(\"key\" in child) || child.key == null) {\n throw new Error(\"All children must have a unique 'key' prop.\");\n }\n return String(child.key);\n });\n\n const [orderState, setOrderState] = useState(keys);\n\n const itemsByKey = useMemo(() => {\n const map: Record<string, ReactNode> = {};\n childArray.forEach((child) => {\n map[String(child.key)] = child;\n });\n return map;\n }, [children]);\n\n const dynamicNumColumns: SharedValue<number> = useSharedValue(\n numColumns ? numColumns : 1\n );\n const order = useSharedValue<string[]>(orderState);\n const contentW = useSharedValue(0);\n const viewportH = useSharedValue(0); // **visible height of ScrollView**\n const activeKey = useSharedValue<string | null>(null);\n const offsetX = useSharedValue(0);\n const offsetY = useSharedValue(0);\n const startX = useSharedValue(0);\n const startY = useSharedValue(0);\n const dragMode = useSharedValue(false);\n const anyItemInDeleteMode = useSharedValue(false); // Global delete mode state\n const isPressingDeleteItem = useSharedValue(false); // Track if user is pressing an item in delete mode\n const contentH = useSharedValue(0);\n const scrollOffset = useSharedValue(0);\n\n const onScroll = useAnimatedScrollHandler({\n onScroll: (e) => {\n scrollOffset.value = e.contentOffset.y;\n },\n });\n\n // initial positions (create shared values consistently by data length)\n const positionsArray = childArray.map((d, i) => {\n const { x, y } = indexToXY({\n index: i,\n itemWidth,\n itemHeight,\n dynamicNumColumns,\n containerPadding,\n gap,\n });\n return {\n key: d.key,\n pos: {\n x: useSharedValue(x),\n y: useSharedValue(y),\n active: useSharedValue(0),\n },\n };\n });\n\n const positions = useMemo(() => {\n const obj: Record<string, (typeof positionsArray)[number][\"pos\"]> = {};\n\n positionsArray.forEach(({ key, pos }) => {\n obj[key ?? `key-${Math.random().toString(36).slice(2)}`] = pos;\n });\n\n return obj;\n }, [positionsArray]);\n\n const deleteItem = (key: string) => {\n setOrderState((prev) => prev.filter((k) => k !== key));\n order.value = order.value.filter((k) => k !== key);\n onOrderChange?.([...order.value]);\n // Call onDelete callback if provided (for both delete button and drop-to-delete)\n if (onDelete) {\n onDelete(key);\n }\n };\n\n useDerivedValue(() => {\n order.value.forEach((key, i) => {\n if (activeKey.value === key) return; // ⬅️ do not layout the active tile\n\n const p = positions[key];\n if (!p) return;\n\n const displayIndex = reverse ? order.value.length - 1 - i : i;\n\n const { x, y } = indexToXY({\n index: displayIndex,\n itemWidth,\n itemHeight,\n dynamicNumColumns,\n containerPadding,\n gap,\n });\n\n const scale = Math.min(itemWidth, itemHeight) / 100; // 100px baseline\n\n const damping = 18 * scale;\n const stiffness = 240 * scale;\n const mass = Math.max(0.1, scale); // helps stability for tiny items\n\n p.x.value = withSpring(x, { damping, stiffness, mass });\n p.y.value = withSpring(y, { damping, stiffness, mass });\n });\n });\n\n // Layout of the ScrollView (viewport) — height we compare against for edge-scrolling\n const onLayoutScrollView = (e: LayoutChangeEvent) => {\n viewportH.value = e.nativeEvent.layout.height;\n };\n\n // Layout of the content view — width used to compute columns\n const onLayoutContent = (e: LayoutChangeEvent) => {\n contentW.value = e.nativeEvent.layout.width;\n contentH.value = e.nativeEvent.layout.height;\n\n if (numColumns) {\n dynamicNumColumns.value = numColumns;\n } else {\n const possibleCols = Math.floor(\n (e.nativeEvent.layout.width - containerPadding * 2 + gap) /\n (itemWidth + gap)\n );\n dynamicNumColumns.value = Math.max(1, possibleCols);\n }\n };\n\n const deleteComponentPosition = useSharedValue<{\n x: number;\n y: number;\n width: number;\n height: number;\n } | null>(null);\n\n const composed = Gesture.Simultaneous(\n PanWithLongPress({\n contentH,\n order,\n dynamicNumColumns,\n activeKey,\n offsetX,\n offsetY,\n startX,\n startY,\n dragMode,\n positions,\n itemsByKey,\n itemWidth,\n itemHeight,\n containerPadding,\n gap,\n setOrderState,\n onDragEnd,\n onOrderChange,\n scrollViewRef,\n scrollOffset,\n viewportH,\n holdToDragMs,\n scrollSpeed,\n scrollThreshold,\n reverse,\n deleteComponentPosition,\n deleteItem,\n contentPaddingBottom,\n })\n );\n\n return {\n itemsByKey,\n orderState,\n dragMode,\n anyItemInDeleteMode,\n isPressingDeleteItem,\n composed,\n dynamicNumColumns,\n onLayoutContent,\n onLayoutScrollView,\n positions,\n onScroll,\n childArray,\n order,\n deleteItem,\n deleteComponentPosition,\n };\n}\n"],"mappings":"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","ignoreList":[]}
@@ -7,6 +7,7 @@ export default function ChildWrapper({
7
7
  itemHeight,
8
8
  dragMode,
9
9
  anyItemInDeleteMode,
10
+ isPressingDeleteItem,
10
11
  children,
11
12
  wiggle,
12
13
  wiggleDeleteMode,
@@ -86,12 +87,18 @@ export default function ChildWrapper({
86
87
  // Reset release tracking when not in delete mode
87
88
  wasReleasedAfterDeleteMode.value = false;
88
89
 
89
- // If not in drag mode or not active, reset timer
90
- if (!isDragging || !isActive) {
90
+ // Timer runs when item is active (being held)
91
+ // Note: isActive (position.active.value) is set when gesture activates after long press
92
+ // isDragging (dragMode.value) is also set at that time, but we primarily check isActive
93
+ // to allow timer to work even in edge cases
94
+ if (!isActive) {
91
95
  stillTimer.value = 0;
92
96
  return;
93
97
  }
94
98
 
99
+ // Item is active - timer can run (dragMode should also be true at this point,
100
+ // but we don't require it to allow timer to work in all cases)
101
+
95
102
  // Item is active (being held down) - check if it's still
96
103
  // Check if position has changed significantly (more than 10px threshold)
97
104
  const moved = Math.abs(x - lastX.value) > 10 || Math.abs(y - lastY.value) > 10;
@@ -305,6 +312,16 @@ export default function ChildWrapper({
305
312
  style: animatedStyle,
306
313
  pointerEvents: "box-none"
307
314
  }, isInDeleteMode && /*#__PURE__*/React.createElement(Pressable, {
315
+ onPressIn: () => {
316
+ // Mark that we're pressing an item to prevent ScrollView from canceling delete mode
317
+ isPressingDeleteItem.value = true;
318
+ },
319
+ onPressOut: () => {
320
+ // Clear the flag after a short delay to allow onPress to fire
321
+ setTimeout(() => {
322
+ isPressingDeleteItem.value = false;
323
+ }, 50);
324
+ },
308
325
  onPress: handleDelete,
309
326
  style: {
310
327
  position: "absolute",
@@ -1 +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","wiggleDeleteMode","holdStillToDeleteMs","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 wiggleDeleteMode?: { duration: number; degrees: number };\n holdStillToDeleteMs?: 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 wiggleDeleteMode,\n holdStillToDeleteMs = 1000,\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 holdStillToDeleteMs of being held still\n if (stillTimer.value >= holdStillToDeleteMs) {\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 // Determine the target wiggle mode\n let targetMode: \"none\" | \"normal\" | \"delete\" = \"none\";\n if (inDeleteMode && (wiggleDeleteMode || wiggle)) {\n targetMode = \"delete\";\n } else if (anyInDeleteMode && !isActive && wiggle) {\n targetMode = \"normal\";\n } else if (isEditMode && !isActive && wiggle) {\n targetMode = \"normal\";\n }\n\n // If no wiggle is configured at all, stop animation\n if (!wiggle && !wiggleDeleteMode) {\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 // If in delete mode but no wiggleDeleteMode and no wiggle, stop animation\n if (targetMode === \"delete\" && !wiggleDeleteMode && !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 // If normal mode but no wiggle, stop animation\n if (targetMode === \"normal\" && !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 // 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, use wiggleDeleteMode if provided, otherwise use 2x degrees and 0.7x duration\n if (targetMode === \"delete\") {\n const deleteWiggleDegrees = wiggleDeleteMode\n ? wiggleDeleteMode.degrees\n : (wiggle?.degrees ?? 0) * 2;\n const deleteWiggleDuration = wiggleDeleteMode\n ? wiggleDeleteMode.duration\n : (wiggle?.duration ?? 200) * 0.7; // Faster wiggle\n\n // If transitioning from normal wiggle, preserve the phase by scaling\n if (previousMode === \"normal\" && wiggle) {\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 [\n dragMode,\n position.active,\n deleteModeActive,\n anyItemInDeleteMode,\n wiggle,\n wiggleDeleteMode,\n ]\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":[]}
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","isPressingDeleteItem","children","wiggle","wiggleDeleteMode","holdStillToDeleteMs","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","onPressIn","onPressOut","setTimeout","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 isPressingDeleteItem: SharedValue<boolean>;\n children: React.ReactNode;\n wiggle?: { duration: number; degrees: number };\n wiggleDeleteMode?: { duration: number; degrees: number };\n holdStillToDeleteMs?: 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 isPressingDeleteItem,\n children,\n wiggle,\n wiggleDeleteMode,\n holdStillToDeleteMs = 1000,\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 // Timer runs when item is active (being held)\n // Note: isActive (position.active.value) is set when gesture activates after long press\n // isDragging (dragMode.value) is also set at that time, but we primarily check isActive\n // to allow timer to work even in edge cases\n if (!isActive) {\n stillTimer.value = 0;\n return;\n }\n\n // Item is active - timer can run (dragMode should also be true at this point,\n // but we don't require it to allow timer to work in all cases)\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 holdStillToDeleteMs of being held still\n if (stillTimer.value >= holdStillToDeleteMs) {\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 // Determine the target wiggle mode\n let targetMode: \"none\" | \"normal\" | \"delete\" = \"none\";\n if (inDeleteMode && (wiggleDeleteMode || wiggle)) {\n targetMode = \"delete\";\n } else if (anyInDeleteMode && !isActive && wiggle) {\n targetMode = \"normal\";\n } else if (isEditMode && !isActive && wiggle) {\n targetMode = \"normal\";\n }\n\n // If no wiggle is configured at all, stop animation\n if (!wiggle && !wiggleDeleteMode) {\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 // If in delete mode but no wiggleDeleteMode and no wiggle, stop animation\n if (targetMode === \"delete\" && !wiggleDeleteMode && !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 // If normal mode but no wiggle, stop animation\n if (targetMode === \"normal\" && !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 // 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, use wiggleDeleteMode if provided, otherwise use 2x degrees and 0.7x duration\n if (targetMode === \"delete\") {\n const deleteWiggleDegrees = wiggleDeleteMode\n ? wiggleDeleteMode.degrees\n : (wiggle?.degrees ?? 0) * 2;\n const deleteWiggleDuration = wiggleDeleteMode\n ? wiggleDeleteMode.duration\n : (wiggle?.duration ?? 200) * 0.7; // Faster wiggle\n\n // If transitioning from normal wiggle, preserve the phase by scaling\n if (previousMode === \"normal\" && wiggle) {\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 [\n dragMode,\n position.active,\n deleteModeActive,\n anyItemInDeleteMode,\n wiggle,\n wiggleDeleteMode,\n ]\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 onPressIn={() => {\n // Mark that we're pressing an item to prevent ScrollView from canceling delete mode\n isPressingDeleteItem.value = true;\n }}\n onPressOut={() => {\n // Clear the flag after a short delay to allow onPress to fire\n setTimeout(() => {\n isPressingDeleteItem.value = false;\n }, 50);\n }}\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":[]}
@@ -104,6 +104,7 @@ const SwappableGrid = /*#__PURE__*/forwardRef(({
104
104
  positions,
105
105
  dragMode,
106
106
  anyItemInDeleteMode,
107
+ isPressingDeleteItem,
107
108
  order,
108
109
  deleteComponentPosition
109
110
  } = useGridLayout({
@@ -282,8 +283,9 @@ const SwappableGrid = /*#__PURE__*/forwardRef(({
282
283
  }
283
284
  touchEndTimeoutRef.current = setTimeout(() => {
284
285
  // Only cancel if still in delete mode and not dragging
286
+ // Don't cancel if user is currently pressing an item (they might be deleting it)
285
287
  // This ensures we don't cancel when user is interacting with items
286
- if (anyItemInDeleteMode.value && !dragMode.value) {
288
+ if (anyItemInDeleteMode.value && !dragMode.value && !isPressingDeleteItem.value) {
287
289
  anyItemInDeleteMode.value = false;
288
290
  }
289
291
  }, 100); // Small delay to let item interactions complete
@@ -309,6 +311,7 @@ const SwappableGrid = /*#__PURE__*/forwardRef(({
309
311
  itemHeight: itemHeight,
310
312
  dragMode: dragMode,
311
313
  anyItemInDeleteMode: anyItemInDeleteMode,
314
+ isPressingDeleteItem: isPressingDeleteItem,
312
315
  wiggle: wiggle,
313
316
  wiggleDeleteMode: wiggleDeleteMode,
314
317
  holdStillToDeleteMs: holdStillToDeleteMs,
@@ -1 +1 @@
1
- {"version":3,"names":["React","useEffect","useState","useImperativeHandle","forwardRef","View","StyleSheet","ScrollView","Animated","useAnimatedRef","useAnimatedStyle","useDerivedValue","useAnimatedReaction","runOnJS","GestureDetector","computeMinHeight","useGridLayout","ChildWrapper","indexToXY","AnimatedScrollView","createAnimatedComponent","normalizeKey","k","String","replace","SwappableGrid","children","itemWidth","itemHeight","gap","containerPadding","holdToDragMs","numColumns","onDragEnd","onOrderChange","onDelete","wiggle","wiggleDeleteMode","holdStillToDeleteMs","style","dragSizeIncreaseFactor","scrollThreshold","scrollSpeed","trailingComponent","deleteComponent","deleteComponentStyle","reverse","ref","scrollViewRef","showTrailingComponent","setShowTrailingComponent","showDeleteComponent","setShowDeleteComponent","isDragging","setIsDragging","currentNumColumns","setCurrentNumColumns","touchEndTimeoutRef","useRef","deleteComponentRef","setTimeout","handleOnOrderChange","order","map","key","paddingBottom","useMemo","styleObj","flatten","orderState","composed","dynamicNumColumns","onLayoutContent","originalOnLayoutContent","onLayoutScrollView","onScroll","deleteItem","childArray","positions","dragMode","anyItemInDeleteMode","deleteComponentPosition","undefined","contentPaddingBottom","e","possibleCols","Math","floor","nativeEvent","layout","width","max","value","isDraggingValue","cancelDeleteMode","trailingX","x","index","length","trailingY","y","trailingStyle","left","top","deleteComponentX","cols","totalWidth","deleteComponentY","rows","ceil","baseY","deleteComponentStyleAnimated","baseStyle","position","height","showTrailing","showDelete","itemsCountForHeight","baseHeight","calculatedHeight","totalItems","deleteComponentBottom","createElement","onLayout","scrollEventThrottle","contentContainerStyle","onTouchEnd","current","clearTimeout","styles","container","padding","gesture","pointerEvents","absoluteFill","child","find","c","disableHoldToDelete","collapsable","flex","create"],"sources":["SwappableGrid.tsx"],"sourcesContent":["import React, {\n ReactNode,\n useEffect,\n useState,\n useImperativeHandle,\n forwardRef,\n} from \"react\";\nimport {\n View,\n StyleSheet,\n ScrollView,\n StyleProp,\n ViewStyle,\n LayoutChangeEvent,\n} from \"react-native\";\nimport Animated, {\n useAnimatedRef,\n useAnimatedStyle,\n useDerivedValue,\n useAnimatedReaction,\n runOnJS,\n} from \"react-native-reanimated\";\nimport { GestureDetector } from \"react-native-gesture-handler\";\nimport computeMinHeight from \"./utils/helpers/computerMinHeight\";\nimport { useGridLayout } from \"./utils/useGridLayout\";\nimport ChildWrapper from \"./ChildWrapper\";\nimport { indexToXY } from \"./utils/helpers/indexCalculations\";\n\nconst AnimatedScrollView = Animated.createAnimatedComponent(ScrollView);\n\nconst normalizeKey = (k: React.Key) => String(k).replace(/^\\.\\$/, \"\");\n\n/**\n * Props for the SwappableGrid component\n */\ntype SwappableGridProps = {\n /** The child components to render in the grid. Each child should have a unique key. */\n children: ReactNode;\n /** Width of each grid item in pixels */\n itemWidth: number;\n /** Height of each grid item in pixels */\n itemHeight: number;\n /** Gap between grid items in pixels. Defaults to 8. */\n gap?: number;\n /** Padding around the container in pixels. Defaults to 8. */\n containerPadding?: number;\n /** Duration in milliseconds to hold before drag starts. Defaults to 300. */\n holdToDragMs?: number;\n /** Number of columns in the grid. If not provided, will be calculated automatically based on container width. */\n numColumns?: number;\n /** Wiggle animation configuration when items are in drag mode or delete mode */\n wiggle?: {\n /** Duration of one wiggle cycle in milliseconds */\n duration: number;\n /** Rotation degrees for the wiggle animation */\n degrees: number;\n };\n /** Wiggle animation configuration specifically for delete mode. If not provided, uses 2x degrees and 0.7x duration of wiggle prop. */\n wiggleDeleteMode?: {\n /** Duration of one wiggle cycle in milliseconds */\n duration: number;\n /** Rotation degrees for the wiggle animation */\n degrees: number;\n };\n /** Duration in milliseconds to hold an item still before entering delete mode. Defaults to 1000. */\n holdStillToDeleteMs?: number;\n /** Callback fired when drag ends, providing the ordered array of child nodes */\n onDragEnd?: (ordered: ChildNode[]) => void;\n /** Callback fired when the order changes, providing an array of keys in the new order */\n onOrderChange?: (keys: string[]) => void;\n /** Callback fired when an item is deleted, providing the key of the deleted item */\n onDelete?: (key: string) => void;\n /** Factor by which the dragged item scales up. Defaults to 1.06. */\n dragSizeIncreaseFactor?: number;\n /** Speed of auto-scrolling when dragging near edges. Defaults to 10. */\n scrollSpeed?: number;\n /** Distance from edge in pixels that triggers auto-scroll. Defaults to 100. */\n scrollThreshold?: number;\n /** Custom style for the ScrollView container */\n style?: StyleProp<ViewStyle>;\n /** Component to render after all grid items (e.g., an \"Add\" button) */\n trailingComponent?: ReactNode;\n /** Component to render as a delete target (shown when dragging). If provided, disables hold-to-delete feature. */\n deleteComponent?: ReactNode;\n /** Custom style for the delete component. If provided, allows custom positioning. */\n deleteComponentStyle?: StyleProp<ViewStyle>;\n /** If true, reverses the order of items (right-to-left, bottom-to-top). Defaults to false. */\n reverse?: boolean;\n};\n\n/**\n * Ref methods for SwappableGrid component\n */\nexport interface SwappableGridRef {\n /** Cancels the delete mode if any item is currently in delete mode */\n cancelDeleteMode: () => void;\n}\n\n/**\n * SwappableGrid - A React Native component for creating a draggable, swappable grid layout.\n *\n * Features:\n * - Drag and drop to reorder items\n * - Long press to enter drag mode\n * - Auto-scroll when dragging near edges\n * - Optional wiggle animation during drag mode\n * - Optional delete functionality with hold-to-delete or delete component\n * - Support for trailing components (e.g., \"Add\" button)\n * - Automatic column calculation based on container width\n *\n * @example\n * ```tsx\n * <SwappableGrid\n * itemWidth={100}\n * itemHeight={100}\n * numColumns={3}\n * onOrderChange={(keys) => console.log('New order:', keys)}\n * >\n * {items.map(item => (\n * <View key={item.id}>{item.content}</View>\n * ))}\n * </SwappableGrid>\n * ```\n */\nconst SwappableGrid = forwardRef<SwappableGridRef, SwappableGridProps>(\n (\n {\n children,\n itemWidth,\n itemHeight,\n gap = 8,\n containerPadding = 8,\n holdToDragMs = 300,\n numColumns,\n onDragEnd,\n onOrderChange,\n onDelete,\n wiggle,\n wiggleDeleteMode,\n holdStillToDeleteMs = 1000,\n style,\n dragSizeIncreaseFactor = 1.06,\n scrollThreshold = 100,\n scrollSpeed = 10,\n trailingComponent,\n deleteComponent,\n deleteComponentStyle,\n reverse = false,\n },\n ref\n ) => {\n // MUST be Animated ref for scrollTo\n const scrollViewRef = useAnimatedRef<Animated.ScrollView>();\n const [showTrailingComponent, setShowTrailingComponent] =\n useState<boolean>(false);\n const [showDeleteComponent, setShowDeleteComponent] =\n useState<boolean>(false);\n const [isDragging, setIsDragging] = useState<boolean>(false);\n const [currentNumColumns, setCurrentNumColumns] = useState<number>(\n numColumns || 1\n );\n const touchEndTimeoutRef = React.useRef<ReturnType<\n typeof setTimeout\n > | null>(null);\n const deleteComponentRef = React.useRef<View>(null);\n\n useEffect(() => {\n setTimeout(() => {\n setShowTrailingComponent(true);\n setShowDeleteComponent(true);\n }, 50); // This is kinda a hack that makes the trailing component render in the correct position because it lets the other components render first to make the position calculation correct.\n }, []);\n\n const handleOnOrderChange = (order: string[]) => {\n if (onOrderChange) onOrderChange(order.map((key) => normalizeKey(key)));\n };\n\n // Extract paddingBottom from style prop to allow dragging into padding area\n const paddingBottom = React.useMemo(() => {\n if (!style) return 0;\n const styleObj = StyleSheet.flatten(style);\n return (styleObj.paddingBottom as number) || 0;\n }, [style]);\n\n const {\n orderState,\n composed,\n dynamicNumColumns,\n onLayoutContent: originalOnLayoutContent, // layout of the inner content view (for width/cols)\n onLayoutScrollView, // layout of the scroll viewport (for height)\n onScroll,\n deleteItem,\n childArray,\n positions,\n dragMode,\n anyItemInDeleteMode,\n order,\n deleteComponentPosition,\n } = useGridLayout({\n reverse,\n children,\n holdToDragMs,\n itemWidth,\n itemHeight,\n gap,\n containerPadding,\n numColumns,\n onDragEnd,\n onOrderChange: handleOnOrderChange,\n onDelete: onDelete ? (key) => onDelete(normalizeKey(key)) : undefined,\n scrollViewRef,\n scrollSpeed,\n scrollThreshold,\n contentPaddingBottom: paddingBottom,\n });\n\n // Track numColumns changes for height calculation\n const onLayoutContent = (e: LayoutChangeEvent) => {\n originalOnLayoutContent(e);\n // Update currentNumColumns for height calculation\n if (numColumns) {\n setCurrentNumColumns(numColumns);\n } else {\n const possibleCols = Math.floor(\n (e.nativeEvent.layout.width - containerPadding * 2 + gap) /\n (itemWidth + gap)\n );\n setCurrentNumColumns(Math.max(1, possibleCols));\n }\n };\n\n // Track drag state to show/hide delete component\n useAnimatedReaction(\n () => dragMode.value,\n (isDraggingValue) => {\n runOnJS(setIsDragging)(isDraggingValue);\n }\n );\n\n // Expose cancel delete mode function to parent\n useImperativeHandle(\n ref,\n () => ({\n cancelDeleteMode: () => {\n if (anyItemInDeleteMode.value) {\n anyItemInDeleteMode.value = false;\n }\n },\n }),\n [anyItemInDeleteMode]\n );\n\n const trailingX = useDerivedValue(() => {\n const { x } = indexToXY({\n index: order.value.length, // AFTER last swappable\n itemWidth,\n itemHeight,\n dynamicNumColumns,\n containerPadding,\n gap,\n });\n return x;\n });\n\n const trailingY = useDerivedValue(() => {\n const { y } = indexToXY({\n index: order.value.length,\n itemWidth,\n itemHeight,\n dynamicNumColumns,\n containerPadding,\n gap,\n });\n return y;\n });\n\n const trailingStyle = useAnimatedStyle(() => ({\n left: trailingX.value,\n top: trailingY.value,\n }));\n\n // Calculate default delete component position (bottom center)\n const deleteComponentX = useDerivedValue(() => {\n if (deleteComponentStyle) {\n // If custom style provided, position will be set by user\n return 0;\n }\n // Default: center horizontally\n const cols = dynamicNumColumns.value;\n const totalWidth =\n cols * itemWidth + (cols - 1) * gap + containerPadding * 2;\n return (totalWidth - itemWidth) / 2;\n });\n\n const deleteComponentY = useDerivedValue(() => {\n if (deleteComponentStyle) {\n // If custom style provided, position will be set by user\n return 0;\n }\n // Default: bottom of grid (after all items)\n // Account for trailing component if it exists\n const rows = Math.ceil(order.value.length / dynamicNumColumns.value);\n const baseY = containerPadding + rows * (itemHeight + gap);\n\n // If trailing component exists, add extra space so delete component appears below it\n if (trailingComponent && showTrailingComponent) {\n return baseY + (itemHeight + gap);\n }\n\n return baseY + gap;\n });\n\n const deleteComponentStyleAnimated = useAnimatedStyle(() => {\n const baseStyle: any = {\n position: \"absolute\",\n width: itemWidth,\n height: itemHeight,\n };\n\n // Use custom position if provided, otherwise use default\n if (deleteComponentStyle) {\n return baseStyle;\n }\n\n return {\n ...baseStyle,\n left: deleteComponentX.value,\n top: deleteComponentY.value,\n };\n });\n\n // Update delete component position for drop detection when default position changes\n useDerivedValue(() => {\n if (deleteComponent && deleteComponentPosition && !deleteComponentStyle) {\n // Only update if using default position (custom style uses onLayout)\n deleteComponentPosition.value = {\n x: deleteComponentX.value,\n y: deleteComponentY.value,\n width: itemWidth,\n height: itemHeight,\n };\n }\n });\n\n const showTrailing = !!(trailingComponent && showTrailingComponent);\n const showDelete = !!(deleteComponent && showDeleteComponent);\n // Make sure we calculate room for both trailing and delete components\n // Trailing component is part of the grid, delete component is positioned below\n const itemsCountForHeight = orderState.length + (showTrailing ? 1 : 0);\n\n // Calculate minimum height needed (on JS thread since computeMinHeight is not a worklet)\n const baseHeight = computeMinHeight(\n itemsCountForHeight,\n currentNumColumns,\n itemHeight + gap,\n containerPadding\n );\n\n // If delete component is shown and using default position, add extra space for it\n let calculatedHeight = baseHeight;\n if (showDelete && !deleteComponentStyle) {\n // Account for trailing component when calculating rows (same logic as deleteComponentY)\n const totalItems = orderState.length + (showTrailing ? 1 : 0);\n const rows = Math.ceil(totalItems / currentNumColumns);\n const baseY = containerPadding + rows * (itemHeight + gap);\n\n // If trailing component exists, add extra space so delete component appears below it\n let deleteComponentY = baseY;\n if (showTrailing) {\n deleteComponentY = baseY + (itemHeight + gap);\n } else {\n deleteComponentY = baseY + gap;\n }\n\n const deleteComponentBottom = deleteComponentY + itemHeight;\n // Ensure container is tall enough to show the delete component\n calculatedHeight = Math.max(\n baseHeight,\n deleteComponentBottom + containerPadding\n );\n }\n\n return (\n <AnimatedScrollView\n ref={scrollViewRef}\n onScroll={onScroll}\n onLayout={onLayoutScrollView} // viewport height comes from the SCROLLVIEW\n scrollEventThrottle={16}\n contentContainerStyle={[style]}\n onTouchEnd={() => {\n // Cancel delete mode when user touches outside items\n // Add a small delay to avoid canceling when user taps on items\n // (items might briefly activate, which would prevent cancellation)\n if (touchEndTimeoutRef.current) {\n clearTimeout(touchEndTimeoutRef.current);\n }\n touchEndTimeoutRef.current = setTimeout(() => {\n // Only cancel if still in delete mode and not dragging\n // This ensures we don't cancel when user is interacting with items\n if (anyItemInDeleteMode.value && !dragMode.value) {\n anyItemInDeleteMode.value = false;\n }\n }, 100); // Small delay to let item interactions complete\n }}\n >\n <View\n style={[\n styles.container,\n {\n padding: containerPadding,\n height: calculatedHeight,\n },\n ]}\n onLayout={onLayoutContent}\n >\n <GestureDetector gesture={composed}>\n <View pointerEvents=\"box-none\" style={StyleSheet.absoluteFill}>\n {orderState.map((key) => {\n const child = childArray.find(\n (c) => c.key && normalizeKey(c.key) === normalizeKey(key)\n );\n if (!child) return null;\n return (\n <ChildWrapper\n key={key}\n position={positions[key]}\n itemWidth={itemWidth}\n itemHeight={itemHeight}\n dragMode={dragMode}\n anyItemInDeleteMode={anyItemInDeleteMode}\n wiggle={wiggle}\n wiggleDeleteMode={wiggleDeleteMode}\n holdStillToDeleteMs={holdStillToDeleteMs}\n dragSizeIncreaseFactor={dragSizeIncreaseFactor}\n disableHoldToDelete={!onDelete || !!deleteComponent}\n onDelete={() => {\n deleteItem(key);\n if (onDelete) {\n onDelete(normalizeKey(key));\n }\n }}\n >\n {child}\n </ChildWrapper>\n );\n })}\n </View>\n </GestureDetector>\n\n {/* Trailing rendered OUTSIDE the GestureDetector */}\n {trailingComponent && showTrailingComponent && (\n <Animated.View\n pointerEvents=\"box-none\"\n collapsable={false}\n style={[\n {\n position: \"absolute\",\n width: itemWidth,\n height: itemHeight,\n },\n trailingStyle, // 👈 left/top from UI thread\n ]}\n >\n <View pointerEvents=\"auto\" style={{ flex: 1 }}>\n {trailingComponent}\n </View>\n </Animated.View>\n )}\n\n {/* Delete component rendered OUTSIDE the GestureDetector - only show when dragging */}\n {deleteComponent && showDeleteComponent && isDragging && (\n <Animated.View\n ref={deleteComponentRef}\n pointerEvents=\"box-none\"\n collapsable={false}\n style={[\n deleteComponentStyleAnimated,\n deleteComponentStyle, // User can override position with custom style\n ]}\n onLayout={(e) => {\n // Update position for drop detection\n const { x, y, width, height } = e.nativeEvent.layout;\n if (deleteComponentPosition) {\n deleteComponentPosition.value = { x, y, width, height };\n }\n }}\n >\n <View pointerEvents=\"auto\" style={{ flex: 1 }}>\n {deleteComponent}\n </View>\n </Animated.View>\n )}\n </View>\n </AnimatedScrollView>\n );\n }\n);\n\nconst styles = StyleSheet.create({\n container: {\n width: \"100%\",\n },\n});\n\nexport default SwappableGrid;\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":["React","useEffect","useState","useImperativeHandle","forwardRef","View","StyleSheet","ScrollView","Animated","useAnimatedRef","useAnimatedStyle","useDerivedValue","useAnimatedReaction","runOnJS","GestureDetector","computeMinHeight","useGridLayout","ChildWrapper","indexToXY","AnimatedScrollView","createAnimatedComponent","normalizeKey","k","String","replace","SwappableGrid","children","itemWidth","itemHeight","gap","containerPadding","holdToDragMs","numColumns","onDragEnd","onOrderChange","onDelete","wiggle","wiggleDeleteMode","holdStillToDeleteMs","style","dragSizeIncreaseFactor","scrollThreshold","scrollSpeed","trailingComponent","deleteComponent","deleteComponentStyle","reverse","ref","scrollViewRef","showTrailingComponent","setShowTrailingComponent","showDeleteComponent","setShowDeleteComponent","isDragging","setIsDragging","currentNumColumns","setCurrentNumColumns","touchEndTimeoutRef","useRef","deleteComponentRef","setTimeout","handleOnOrderChange","order","map","key","paddingBottom","useMemo","styleObj","flatten","orderState","composed","dynamicNumColumns","onLayoutContent","originalOnLayoutContent","onLayoutScrollView","onScroll","deleteItem","childArray","positions","dragMode","anyItemInDeleteMode","isPressingDeleteItem","deleteComponentPosition","undefined","contentPaddingBottom","e","possibleCols","Math","floor","nativeEvent","layout","width","max","value","isDraggingValue","cancelDeleteMode","trailingX","x","index","length","trailingY","y","trailingStyle","left","top","deleteComponentX","cols","totalWidth","deleteComponentY","rows","ceil","baseY","deleteComponentStyleAnimated","baseStyle","position","height","showTrailing","showDelete","itemsCountForHeight","baseHeight","calculatedHeight","totalItems","deleteComponentBottom","createElement","onLayout","scrollEventThrottle","contentContainerStyle","onTouchEnd","current","clearTimeout","styles","container","padding","gesture","pointerEvents","absoluteFill","child","find","c","disableHoldToDelete","collapsable","flex","create"],"sources":["SwappableGrid.tsx"],"sourcesContent":["import React, {\n ReactNode,\n useEffect,\n useState,\n useImperativeHandle,\n forwardRef,\n} from \"react\";\nimport {\n View,\n StyleSheet,\n ScrollView,\n StyleProp,\n ViewStyle,\n LayoutChangeEvent,\n} from \"react-native\";\nimport Animated, {\n useAnimatedRef,\n useAnimatedStyle,\n useDerivedValue,\n useAnimatedReaction,\n runOnJS,\n} from \"react-native-reanimated\";\nimport { GestureDetector } from \"react-native-gesture-handler\";\nimport computeMinHeight from \"./utils/helpers/computerMinHeight\";\nimport { useGridLayout } from \"./utils/useGridLayout\";\nimport ChildWrapper from \"./ChildWrapper\";\nimport { indexToXY } from \"./utils/helpers/indexCalculations\";\n\nconst AnimatedScrollView = Animated.createAnimatedComponent(ScrollView);\n\nconst normalizeKey = (k: React.Key) => String(k).replace(/^\\.\\$/, \"\");\n\n/**\n * Props for the SwappableGrid component\n */\ntype SwappableGridProps = {\n /** The child components to render in the grid. Each child should have a unique key. */\n children: ReactNode;\n /** Width of each grid item in pixels */\n itemWidth: number;\n /** Height of each grid item in pixels */\n itemHeight: number;\n /** Gap between grid items in pixels. Defaults to 8. */\n gap?: number;\n /** Padding around the container in pixels. Defaults to 8. */\n containerPadding?: number;\n /** Duration in milliseconds to hold before drag starts. Defaults to 300. */\n holdToDragMs?: number;\n /** Number of columns in the grid. If not provided, will be calculated automatically based on container width. */\n numColumns?: number;\n /** Wiggle animation configuration when items are in drag mode or delete mode */\n wiggle?: {\n /** Duration of one wiggle cycle in milliseconds */\n duration: number;\n /** Rotation degrees for the wiggle animation */\n degrees: number;\n };\n /** Wiggle animation configuration specifically for delete mode. If not provided, uses 2x degrees and 0.7x duration of wiggle prop. */\n wiggleDeleteMode?: {\n /** Duration of one wiggle cycle in milliseconds */\n duration: number;\n /** Rotation degrees for the wiggle animation */\n degrees: number;\n };\n /** Duration in milliseconds to hold an item still before entering delete mode. Defaults to 1000. */\n holdStillToDeleteMs?: number;\n /** Callback fired when drag ends, providing the ordered array of child nodes */\n onDragEnd?: (ordered: ChildNode[]) => void;\n /** Callback fired when the order changes, providing an array of keys in the new order */\n onOrderChange?: (keys: string[]) => void;\n /** Callback fired when an item is deleted, providing the key of the deleted item */\n onDelete?: (key: string) => void;\n /** Factor by which the dragged item scales up. Defaults to 1.06. */\n dragSizeIncreaseFactor?: number;\n /** Speed of auto-scrolling when dragging near edges. Defaults to 10. */\n scrollSpeed?: number;\n /** Distance from edge in pixels that triggers auto-scroll. Defaults to 100. */\n scrollThreshold?: number;\n /** Custom style for the ScrollView container */\n style?: StyleProp<ViewStyle>;\n /** Component to render after all grid items (e.g., an \"Add\" button) */\n trailingComponent?: ReactNode;\n /** Component to render as a delete target (shown when dragging). If provided, disables hold-to-delete feature. */\n deleteComponent?: ReactNode;\n /** Custom style for the delete component. If provided, allows custom positioning. */\n deleteComponentStyle?: StyleProp<ViewStyle>;\n /** If true, reverses the order of items (right-to-left, bottom-to-top). Defaults to false. */\n reverse?: boolean;\n};\n\n/**\n * Ref methods for SwappableGrid component\n */\nexport interface SwappableGridRef {\n /** Cancels the delete mode if any item is currently in delete mode */\n cancelDeleteMode: () => void;\n}\n\n/**\n * SwappableGrid - A React Native component for creating a draggable, swappable grid layout.\n *\n * Features:\n * - Drag and drop to reorder items\n * - Long press to enter drag mode\n * - Auto-scroll when dragging near edges\n * - Optional wiggle animation during drag mode\n * - Optional delete functionality with hold-to-delete or delete component\n * - Support for trailing components (e.g., \"Add\" button)\n * - Automatic column calculation based on container width\n *\n * @example\n * ```tsx\n * <SwappableGrid\n * itemWidth={100}\n * itemHeight={100}\n * numColumns={3}\n * onOrderChange={(keys) => console.log('New order:', keys)}\n * >\n * {items.map(item => (\n * <View key={item.id}>{item.content}</View>\n * ))}\n * </SwappableGrid>\n * ```\n */\nconst SwappableGrid = forwardRef<SwappableGridRef, SwappableGridProps>(\n (\n {\n children,\n itemWidth,\n itemHeight,\n gap = 8,\n containerPadding = 8,\n holdToDragMs = 300,\n numColumns,\n onDragEnd,\n onOrderChange,\n onDelete,\n wiggle,\n wiggleDeleteMode,\n holdStillToDeleteMs = 1000,\n style,\n dragSizeIncreaseFactor = 1.06,\n scrollThreshold = 100,\n scrollSpeed = 10,\n trailingComponent,\n deleteComponent,\n deleteComponentStyle,\n reverse = false,\n },\n ref\n ) => {\n // MUST be Animated ref for scrollTo\n const scrollViewRef = useAnimatedRef<Animated.ScrollView>();\n const [showTrailingComponent, setShowTrailingComponent] =\n useState<boolean>(false);\n const [showDeleteComponent, setShowDeleteComponent] =\n useState<boolean>(false);\n const [isDragging, setIsDragging] = useState<boolean>(false);\n const [currentNumColumns, setCurrentNumColumns] = useState<number>(\n numColumns || 1\n );\n const touchEndTimeoutRef = React.useRef<ReturnType<\n typeof setTimeout\n > | null>(null);\n const deleteComponentRef = React.useRef<View>(null);\n\n useEffect(() => {\n setTimeout(() => {\n setShowTrailingComponent(true);\n setShowDeleteComponent(true);\n }, 50); // This is kinda a hack that makes the trailing component render in the correct position because it lets the other components render first to make the position calculation correct.\n }, []);\n\n const handleOnOrderChange = (order: string[]) => {\n if (onOrderChange) onOrderChange(order.map((key) => normalizeKey(key)));\n };\n\n // Extract paddingBottom from style prop to allow dragging into padding area\n const paddingBottom = React.useMemo(() => {\n if (!style) return 0;\n const styleObj = StyleSheet.flatten(style);\n return (styleObj.paddingBottom as number) || 0;\n }, [style]);\n\n const {\n orderState,\n composed,\n dynamicNumColumns,\n onLayoutContent: originalOnLayoutContent, // layout of the inner content view (for width/cols)\n onLayoutScrollView, // layout of the scroll viewport (for height)\n onScroll,\n deleteItem,\n childArray,\n positions,\n dragMode,\n anyItemInDeleteMode,\n isPressingDeleteItem,\n order,\n deleteComponentPosition,\n } = useGridLayout({\n reverse,\n children,\n holdToDragMs,\n itemWidth,\n itemHeight,\n gap,\n containerPadding,\n numColumns,\n onDragEnd,\n onOrderChange: handleOnOrderChange,\n onDelete: onDelete ? (key) => onDelete(normalizeKey(key)) : undefined,\n scrollViewRef,\n scrollSpeed,\n scrollThreshold,\n contentPaddingBottom: paddingBottom,\n });\n\n // Track numColumns changes for height calculation\n const onLayoutContent = (e: LayoutChangeEvent) => {\n originalOnLayoutContent(e);\n // Update currentNumColumns for height calculation\n if (numColumns) {\n setCurrentNumColumns(numColumns);\n } else {\n const possibleCols = Math.floor(\n (e.nativeEvent.layout.width - containerPadding * 2 + gap) /\n (itemWidth + gap)\n );\n setCurrentNumColumns(Math.max(1, possibleCols));\n }\n };\n\n // Track drag state to show/hide delete component\n useAnimatedReaction(\n () => dragMode.value,\n (isDraggingValue) => {\n runOnJS(setIsDragging)(isDraggingValue);\n }\n );\n\n // Expose cancel delete mode function to parent\n useImperativeHandle(\n ref,\n () => ({\n cancelDeleteMode: () => {\n if (anyItemInDeleteMode.value) {\n anyItemInDeleteMode.value = false;\n }\n },\n }),\n [anyItemInDeleteMode]\n );\n\n const trailingX = useDerivedValue(() => {\n const { x } = indexToXY({\n index: order.value.length, // AFTER last swappable\n itemWidth,\n itemHeight,\n dynamicNumColumns,\n containerPadding,\n gap,\n });\n return x;\n });\n\n const trailingY = useDerivedValue(() => {\n const { y } = indexToXY({\n index: order.value.length,\n itemWidth,\n itemHeight,\n dynamicNumColumns,\n containerPadding,\n gap,\n });\n return y;\n });\n\n const trailingStyle = useAnimatedStyle(() => ({\n left: trailingX.value,\n top: trailingY.value,\n }));\n\n // Calculate default delete component position (bottom center)\n const deleteComponentX = useDerivedValue(() => {\n if (deleteComponentStyle) {\n // If custom style provided, position will be set by user\n return 0;\n }\n // Default: center horizontally\n const cols = dynamicNumColumns.value;\n const totalWidth =\n cols * itemWidth + (cols - 1) * gap + containerPadding * 2;\n return (totalWidth - itemWidth) / 2;\n });\n\n const deleteComponentY = useDerivedValue(() => {\n if (deleteComponentStyle) {\n // If custom style provided, position will be set by user\n return 0;\n }\n // Default: bottom of grid (after all items)\n // Account for trailing component if it exists\n const rows = Math.ceil(order.value.length / dynamicNumColumns.value);\n const baseY = containerPadding + rows * (itemHeight + gap);\n\n // If trailing component exists, add extra space so delete component appears below it\n if (trailingComponent && showTrailingComponent) {\n return baseY + (itemHeight + gap);\n }\n\n return baseY + gap;\n });\n\n const deleteComponentStyleAnimated = useAnimatedStyle(() => {\n const baseStyle: any = {\n position: \"absolute\",\n width: itemWidth,\n height: itemHeight,\n };\n\n // Use custom position if provided, otherwise use default\n if (deleteComponentStyle) {\n return baseStyle;\n }\n\n return {\n ...baseStyle,\n left: deleteComponentX.value,\n top: deleteComponentY.value,\n };\n });\n\n // Update delete component position for drop detection when default position changes\n useDerivedValue(() => {\n if (deleteComponent && deleteComponentPosition && !deleteComponentStyle) {\n // Only update if using default position (custom style uses onLayout)\n deleteComponentPosition.value = {\n x: deleteComponentX.value,\n y: deleteComponentY.value,\n width: itemWidth,\n height: itemHeight,\n };\n }\n });\n\n const showTrailing = !!(trailingComponent && showTrailingComponent);\n const showDelete = !!(deleteComponent && showDeleteComponent);\n // Make sure we calculate room for both trailing and delete components\n // Trailing component is part of the grid, delete component is positioned below\n const itemsCountForHeight = orderState.length + (showTrailing ? 1 : 0);\n\n // Calculate minimum height needed (on JS thread since computeMinHeight is not a worklet)\n const baseHeight = computeMinHeight(\n itemsCountForHeight,\n currentNumColumns,\n itemHeight + gap,\n containerPadding\n );\n\n // If delete component is shown and using default position, add extra space for it\n let calculatedHeight = baseHeight;\n if (showDelete && !deleteComponentStyle) {\n // Account for trailing component when calculating rows (same logic as deleteComponentY)\n const totalItems = orderState.length + (showTrailing ? 1 : 0);\n const rows = Math.ceil(totalItems / currentNumColumns);\n const baseY = containerPadding + rows * (itemHeight + gap);\n\n // If trailing component exists, add extra space so delete component appears below it\n let deleteComponentY = baseY;\n if (showTrailing) {\n deleteComponentY = baseY + (itemHeight + gap);\n } else {\n deleteComponentY = baseY + gap;\n }\n\n const deleteComponentBottom = deleteComponentY + itemHeight;\n // Ensure container is tall enough to show the delete component\n calculatedHeight = Math.max(\n baseHeight,\n deleteComponentBottom + containerPadding\n );\n }\n\n return (\n <AnimatedScrollView\n ref={scrollViewRef}\n onScroll={onScroll}\n onLayout={onLayoutScrollView} // viewport height comes from the SCROLLVIEW\n scrollEventThrottle={16}\n contentContainerStyle={[style]}\n onTouchEnd={() => {\n // Cancel delete mode when user touches outside items\n // Add a small delay to avoid canceling when user taps on items\n // (items might briefly activate, which would prevent cancellation)\n if (touchEndTimeoutRef.current) {\n clearTimeout(touchEndTimeoutRef.current);\n }\n touchEndTimeoutRef.current = setTimeout(() => {\n // Only cancel if still in delete mode and not dragging\n // Don't cancel if user is currently pressing an item (they might be deleting it)\n // This ensures we don't cancel when user is interacting with items\n if (\n anyItemInDeleteMode.value &&\n !dragMode.value &&\n !isPressingDeleteItem.value\n ) {\n anyItemInDeleteMode.value = false;\n }\n }, 100); // Small delay to let item interactions complete\n }}\n >\n <View\n style={[\n styles.container,\n {\n padding: containerPadding,\n height: calculatedHeight,\n },\n ]}\n onLayout={onLayoutContent}\n >\n <GestureDetector gesture={composed}>\n <View pointerEvents=\"box-none\" style={StyleSheet.absoluteFill}>\n {orderState.map((key) => {\n const child = childArray.find(\n (c) => c.key && normalizeKey(c.key) === normalizeKey(key)\n );\n if (!child) return null;\n return (\n <ChildWrapper\n key={key}\n position={positions[key]}\n itemWidth={itemWidth}\n itemHeight={itemHeight}\n dragMode={dragMode}\n anyItemInDeleteMode={anyItemInDeleteMode}\n isPressingDeleteItem={isPressingDeleteItem}\n wiggle={wiggle}\n wiggleDeleteMode={wiggleDeleteMode}\n holdStillToDeleteMs={holdStillToDeleteMs}\n dragSizeIncreaseFactor={dragSizeIncreaseFactor}\n disableHoldToDelete={!onDelete || !!deleteComponent}\n onDelete={() => {\n deleteItem(key);\n if (onDelete) {\n onDelete(normalizeKey(key));\n }\n }}\n >\n {child}\n </ChildWrapper>\n );\n })}\n </View>\n </GestureDetector>\n\n {/* Trailing rendered OUTSIDE the GestureDetector */}\n {trailingComponent && showTrailingComponent && (\n <Animated.View\n pointerEvents=\"box-none\"\n collapsable={false}\n style={[\n {\n position: \"absolute\",\n width: itemWidth,\n height: itemHeight,\n },\n trailingStyle, // 👈 left/top from UI thread\n ]}\n >\n <View pointerEvents=\"auto\" style={{ flex: 1 }}>\n {trailingComponent}\n </View>\n </Animated.View>\n )}\n\n {/* Delete component rendered OUTSIDE the GestureDetector - only show when dragging */}\n {deleteComponent && showDeleteComponent && isDragging && (\n <Animated.View\n ref={deleteComponentRef}\n pointerEvents=\"box-none\"\n collapsable={false}\n style={[\n deleteComponentStyleAnimated,\n deleteComponentStyle, // User can override position with custom style\n ]}\n onLayout={(e) => {\n // Update position for drop detection\n const { x, y, width, height } = e.nativeEvent.layout;\n if (deleteComponentPosition) {\n deleteComponentPosition.value = { x, y, width, height };\n }\n }}\n >\n <View pointerEvents=\"auto\" style={{ flex: 1 }}>\n {deleteComponent}\n </View>\n </Animated.View>\n )}\n </View>\n </AnimatedScrollView>\n );\n }\n);\n\nconst styles = StyleSheet.create({\n container: {\n width: \"100%\",\n },\n});\n\nexport default SwappableGrid;\n"],"mappings":"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","ignoreList":[]}
@@ -46,6 +46,7 @@ export function useGridLayout({
46
46
  const startY = useSharedValue(0);
47
47
  const dragMode = useSharedValue(false);
48
48
  const anyItemInDeleteMode = useSharedValue(false); // Global delete mode state
49
+ const isPressingDeleteItem = useSharedValue(false); // Track if user is pressing an item in delete mode
49
50
  const contentH = useSharedValue(0);
50
51
  const scrollOffset = useSharedValue(0);
51
52
  const onScroll = useAnimatedScrollHandler({
@@ -184,6 +185,7 @@ export function useGridLayout({
184
185
  orderState,
185
186
  dragMode,
186
187
  anyItemInDeleteMode,
188
+ isPressingDeleteItem,
187
189
  composed,
188
190
  dynamicNumColumns,
189
191
  onLayoutContent,
@@ -1 +1 @@
1
- {"version":3,"names":["Children","isValidElement","useMemo","useState","Gesture","useAnimatedScrollHandler","useDerivedValue","useSharedValue","withSpring","indexToXY","PanWithLongPress","useGridLayout","reverse","children","itemWidth","itemHeight","gap","containerPadding","holdToDragMs","numColumns","onDragEnd","onOrderChange","onDelete","scrollViewRef","scrollSpeed","scrollThreshold","contentPaddingBottom","childArray","toArray","filter","keys","map","child","key","Error","String","orderState","setOrderState","itemsByKey","forEach","dynamicNumColumns","order","contentW","viewportH","activeKey","offsetX","offsetY","startX","startY","dragMode","anyItemInDeleteMode","contentH","scrollOffset","onScroll","e","value","contentOffset","y","positionsArray","d","i","x","index","pos","active","positions","obj","Math","random","toString","slice","deleteItem","prev","k","p","displayIndex","length","scale","min","damping","stiffness","mass","max","onLayoutScrollView","nativeEvent","layout","height","onLayoutContent","width","possibleCols","floor","deleteComponentPosition","composed","Simultaneous"],"sources":["useGridLayout.ts"],"sourcesContent":["import { Children, isValidElement, ReactNode, useMemo, useState } from \"react\";\nimport { LayoutChangeEvent } from \"react-native\";\nimport { Gesture } from \"react-native-gesture-handler\";\nimport {\n AnimatedRef,\n SharedValue,\n useAnimatedScrollHandler,\n useDerivedValue,\n useSharedValue,\n withSpring,\n} from \"react-native-reanimated\";\nimport { indexToXY } from \"./helpers/indexCalculations\";\nimport { PanWithLongPress } from \"./helpers/gestures/PanWithLongPress\";\n\ninterface useGridLayoutProps {\n reverse?: boolean;\n children: ReactNode;\n itemWidth: number;\n itemHeight: number;\n gap: number;\n containerPadding: number;\n holdToDragMs: number;\n numColumns?: number;\n onDragEnd?: (ordered: ChildNode[]) => void;\n onOrderChange?: (keys: string[]) => void;\n onDelete?: (key: string) => void;\n scrollViewRef: AnimatedRef<any>;\n scrollSpeed: number;\n scrollThreshold: number;\n contentPaddingBottom?: number;\n}\n\nexport function useGridLayout({\n reverse,\n children,\n itemWidth,\n itemHeight,\n gap,\n containerPadding,\n holdToDragMs,\n numColumns,\n onDragEnd,\n onOrderChange,\n onDelete,\n scrollViewRef,\n scrollSpeed,\n scrollThreshold,\n contentPaddingBottom = 0,\n}: useGridLayoutProps) {\n const childArray = Children.toArray(children).filter(isValidElement);\n const keys = childArray.map((child) => {\n if (!(\"key\" in child) || child.key == null) {\n throw new Error(\"All children must have a unique 'key' prop.\");\n }\n return String(child.key);\n });\n\n const [orderState, setOrderState] = useState(keys);\n\n const itemsByKey = useMemo(() => {\n const map: Record<string, ReactNode> = {};\n childArray.forEach((child) => {\n map[String(child.key)] = child;\n });\n return map;\n }, [children]);\n\n const dynamicNumColumns: SharedValue<number> = useSharedValue(\n numColumns ? numColumns : 1\n );\n const order = useSharedValue<string[]>(orderState);\n const contentW = useSharedValue(0);\n const viewportH = useSharedValue(0); // **visible height of ScrollView**\n const activeKey = useSharedValue<string | null>(null);\n const offsetX = useSharedValue(0);\n const offsetY = useSharedValue(0);\n const startX = useSharedValue(0);\n const startY = useSharedValue(0);\n const dragMode = useSharedValue(false);\n const anyItemInDeleteMode = useSharedValue(false); // Global delete mode state\n const contentH = useSharedValue(0);\n const scrollOffset = useSharedValue(0);\n\n const onScroll = useAnimatedScrollHandler({\n onScroll: (e) => {\n scrollOffset.value = e.contentOffset.y;\n },\n });\n\n // initial positions (create shared values consistently by data length)\n const positionsArray = childArray.map((d, i) => {\n const { x, y } = indexToXY({\n index: i,\n itemWidth,\n itemHeight,\n dynamicNumColumns,\n containerPadding,\n gap,\n });\n return {\n key: d.key,\n pos: {\n x: useSharedValue(x),\n y: useSharedValue(y),\n active: useSharedValue(0),\n },\n };\n });\n\n const positions = useMemo(() => {\n const obj: Record<string, (typeof positionsArray)[number][\"pos\"]> = {};\n\n positionsArray.forEach(({ key, pos }) => {\n obj[key ?? `key-${Math.random().toString(36).slice(2)}`] = pos;\n });\n\n return obj;\n }, [positionsArray]);\n\n const deleteItem = (key: string) => {\n setOrderState((prev) => prev.filter((k) => k !== key));\n order.value = order.value.filter((k) => k !== key);\n onOrderChange?.([...order.value]);\n // Call onDelete callback if provided (for both delete button and drop-to-delete)\n if (onDelete) {\n onDelete(key);\n }\n };\n\n useDerivedValue(() => {\n order.value.forEach((key, i) => {\n if (activeKey.value === key) return; // ⬅️ do not layout the active tile\n\n const p = positions[key];\n if (!p) return;\n\n const displayIndex = reverse ? order.value.length - 1 - i : i;\n\n const { x, y } = indexToXY({\n index: displayIndex,\n itemWidth,\n itemHeight,\n dynamicNumColumns,\n containerPadding,\n gap,\n });\n\n const scale = Math.min(itemWidth, itemHeight) / 100; // 100px baseline\n\n const damping = 18 * scale;\n const stiffness = 240 * scale;\n const mass = Math.max(0.1, scale); // helps stability for tiny items\n\n p.x.value = withSpring(x, { damping, stiffness, mass });\n p.y.value = withSpring(y, { damping, stiffness, mass });\n });\n });\n\n // Layout of the ScrollView (viewport) — height we compare against for edge-scrolling\n const onLayoutScrollView = (e: LayoutChangeEvent) => {\n viewportH.value = e.nativeEvent.layout.height;\n };\n\n // Layout of the content view — width used to compute columns\n const onLayoutContent = (e: LayoutChangeEvent) => {\n contentW.value = e.nativeEvent.layout.width;\n contentH.value = e.nativeEvent.layout.height;\n\n if (numColumns) {\n dynamicNumColumns.value = numColumns;\n } else {\n const possibleCols = Math.floor(\n (e.nativeEvent.layout.width - containerPadding * 2 + gap) /\n (itemWidth + gap)\n );\n dynamicNumColumns.value = Math.max(1, possibleCols);\n }\n };\n\n const deleteComponentPosition = useSharedValue<{\n x: number;\n y: number;\n width: number;\n height: number;\n } | null>(null);\n\n const composed = Gesture.Simultaneous(\n PanWithLongPress({\n contentH,\n order,\n dynamicNumColumns,\n activeKey,\n offsetX,\n offsetY,\n startX,\n startY,\n dragMode,\n positions,\n itemsByKey,\n itemWidth,\n itemHeight,\n containerPadding,\n gap,\n setOrderState,\n onDragEnd,\n onOrderChange,\n scrollViewRef,\n scrollOffset,\n viewportH,\n holdToDragMs,\n scrollSpeed,\n scrollThreshold,\n reverse,\n deleteComponentPosition,\n deleteItem,\n contentPaddingBottom,\n })\n );\n\n return {\n itemsByKey,\n orderState,\n dragMode,\n anyItemInDeleteMode,\n composed,\n dynamicNumColumns,\n onLayoutContent,\n onLayoutScrollView,\n positions,\n onScroll,\n childArray,\n order,\n deleteItem,\n deleteComponentPosition,\n };\n}\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":["Children","isValidElement","useMemo","useState","Gesture","useAnimatedScrollHandler","useDerivedValue","useSharedValue","withSpring","indexToXY","PanWithLongPress","useGridLayout","reverse","children","itemWidth","itemHeight","gap","containerPadding","holdToDragMs","numColumns","onDragEnd","onOrderChange","onDelete","scrollViewRef","scrollSpeed","scrollThreshold","contentPaddingBottom","childArray","toArray","filter","keys","map","child","key","Error","String","orderState","setOrderState","itemsByKey","forEach","dynamicNumColumns","order","contentW","viewportH","activeKey","offsetX","offsetY","startX","startY","dragMode","anyItemInDeleteMode","isPressingDeleteItem","contentH","scrollOffset","onScroll","e","value","contentOffset","y","positionsArray","d","i","x","index","pos","active","positions","obj","Math","random","toString","slice","deleteItem","prev","k","p","displayIndex","length","scale","min","damping","stiffness","mass","max","onLayoutScrollView","nativeEvent","layout","height","onLayoutContent","width","possibleCols","floor","deleteComponentPosition","composed","Simultaneous"],"sources":["useGridLayout.ts"],"sourcesContent":["import { Children, isValidElement, ReactNode, useMemo, useState } from \"react\";\nimport { LayoutChangeEvent } from \"react-native\";\nimport { Gesture } from \"react-native-gesture-handler\";\nimport {\n AnimatedRef,\n SharedValue,\n useAnimatedScrollHandler,\n useDerivedValue,\n useSharedValue,\n withSpring,\n} from \"react-native-reanimated\";\nimport { indexToXY } from \"./helpers/indexCalculations\";\nimport { PanWithLongPress } from \"./helpers/gestures/PanWithLongPress\";\n\ninterface useGridLayoutProps {\n reverse?: boolean;\n children: ReactNode;\n itemWidth: number;\n itemHeight: number;\n gap: number;\n containerPadding: number;\n holdToDragMs: number;\n numColumns?: number;\n onDragEnd?: (ordered: ChildNode[]) => void;\n onOrderChange?: (keys: string[]) => void;\n onDelete?: (key: string) => void;\n scrollViewRef: AnimatedRef<any>;\n scrollSpeed: number;\n scrollThreshold: number;\n contentPaddingBottom?: number;\n}\n\nexport function useGridLayout({\n reverse,\n children,\n itemWidth,\n itemHeight,\n gap,\n containerPadding,\n holdToDragMs,\n numColumns,\n onDragEnd,\n onOrderChange,\n onDelete,\n scrollViewRef,\n scrollSpeed,\n scrollThreshold,\n contentPaddingBottom = 0,\n}: useGridLayoutProps) {\n const childArray = Children.toArray(children).filter(isValidElement);\n const keys = childArray.map((child) => {\n if (!(\"key\" in child) || child.key == null) {\n throw new Error(\"All children must have a unique 'key' prop.\");\n }\n return String(child.key);\n });\n\n const [orderState, setOrderState] = useState(keys);\n\n const itemsByKey = useMemo(() => {\n const map: Record<string, ReactNode> = {};\n childArray.forEach((child) => {\n map[String(child.key)] = child;\n });\n return map;\n }, [children]);\n\n const dynamicNumColumns: SharedValue<number> = useSharedValue(\n numColumns ? numColumns : 1\n );\n const order = useSharedValue<string[]>(orderState);\n const contentW = useSharedValue(0);\n const viewportH = useSharedValue(0); // **visible height of ScrollView**\n const activeKey = useSharedValue<string | null>(null);\n const offsetX = useSharedValue(0);\n const offsetY = useSharedValue(0);\n const startX = useSharedValue(0);\n const startY = useSharedValue(0);\n const dragMode = useSharedValue(false);\n const anyItemInDeleteMode = useSharedValue(false); // Global delete mode state\n const isPressingDeleteItem = useSharedValue(false); // Track if user is pressing an item in delete mode\n const contentH = useSharedValue(0);\n const scrollOffset = useSharedValue(0);\n\n const onScroll = useAnimatedScrollHandler({\n onScroll: (e) => {\n scrollOffset.value = e.contentOffset.y;\n },\n });\n\n // initial positions (create shared values consistently by data length)\n const positionsArray = childArray.map((d, i) => {\n const { x, y } = indexToXY({\n index: i,\n itemWidth,\n itemHeight,\n dynamicNumColumns,\n containerPadding,\n gap,\n });\n return {\n key: d.key,\n pos: {\n x: useSharedValue(x),\n y: useSharedValue(y),\n active: useSharedValue(0),\n },\n };\n });\n\n const positions = useMemo(() => {\n const obj: Record<string, (typeof positionsArray)[number][\"pos\"]> = {};\n\n positionsArray.forEach(({ key, pos }) => {\n obj[key ?? `key-${Math.random().toString(36).slice(2)}`] = pos;\n });\n\n return obj;\n }, [positionsArray]);\n\n const deleteItem = (key: string) => {\n setOrderState((prev) => prev.filter((k) => k !== key));\n order.value = order.value.filter((k) => k !== key);\n onOrderChange?.([...order.value]);\n // Call onDelete callback if provided (for both delete button and drop-to-delete)\n if (onDelete) {\n onDelete(key);\n }\n };\n\n useDerivedValue(() => {\n order.value.forEach((key, i) => {\n if (activeKey.value === key) return; // ⬅️ do not layout the active tile\n\n const p = positions[key];\n if (!p) return;\n\n const displayIndex = reverse ? order.value.length - 1 - i : i;\n\n const { x, y } = indexToXY({\n index: displayIndex,\n itemWidth,\n itemHeight,\n dynamicNumColumns,\n containerPadding,\n gap,\n });\n\n const scale = Math.min(itemWidth, itemHeight) / 100; // 100px baseline\n\n const damping = 18 * scale;\n const stiffness = 240 * scale;\n const mass = Math.max(0.1, scale); // helps stability for tiny items\n\n p.x.value = withSpring(x, { damping, stiffness, mass });\n p.y.value = withSpring(y, { damping, stiffness, mass });\n });\n });\n\n // Layout of the ScrollView (viewport) — height we compare against for edge-scrolling\n const onLayoutScrollView = (e: LayoutChangeEvent) => {\n viewportH.value = e.nativeEvent.layout.height;\n };\n\n // Layout of the content view — width used to compute columns\n const onLayoutContent = (e: LayoutChangeEvent) => {\n contentW.value = e.nativeEvent.layout.width;\n contentH.value = e.nativeEvent.layout.height;\n\n if (numColumns) {\n dynamicNumColumns.value = numColumns;\n } else {\n const possibleCols = Math.floor(\n (e.nativeEvent.layout.width - containerPadding * 2 + gap) /\n (itemWidth + gap)\n );\n dynamicNumColumns.value = Math.max(1, possibleCols);\n }\n };\n\n const deleteComponentPosition = useSharedValue<{\n x: number;\n y: number;\n width: number;\n height: number;\n } | null>(null);\n\n const composed = Gesture.Simultaneous(\n PanWithLongPress({\n contentH,\n order,\n dynamicNumColumns,\n activeKey,\n offsetX,\n offsetY,\n startX,\n startY,\n dragMode,\n positions,\n itemsByKey,\n itemWidth,\n itemHeight,\n containerPadding,\n gap,\n setOrderState,\n onDragEnd,\n onOrderChange,\n scrollViewRef,\n scrollOffset,\n viewportH,\n holdToDragMs,\n scrollSpeed,\n scrollThreshold,\n reverse,\n deleteComponentPosition,\n deleteItem,\n contentPaddingBottom,\n })\n );\n\n return {\n itemsByKey,\n orderState,\n dragMode,\n anyItemInDeleteMode,\n isPressingDeleteItem,\n composed,\n dynamicNumColumns,\n onLayoutContent,\n onLayoutScrollView,\n positions,\n onScroll,\n childArray,\n order,\n deleteItem,\n deleteComponentPosition,\n };\n}\n"],"mappings":"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","ignoreList":[]}
@@ -10,6 +10,7 @@ type Props = {
10
10
  itemHeight: number;
11
11
  dragMode: SharedValue<boolean>;
12
12
  anyItemInDeleteMode: SharedValue<boolean>;
13
+ isPressingDeleteItem: SharedValue<boolean>;
13
14
  children: React.ReactNode;
14
15
  wiggle?: {
15
16
  duration: number;
@@ -24,5 +25,5 @@ type Props = {
24
25
  onDelete?: () => void;
25
26
  disableHoldToDelete?: boolean;
26
27
  };
27
- export default function ChildWrapper({ position, itemWidth, itemHeight, dragMode, anyItemInDeleteMode, children, wiggle, wiggleDeleteMode, holdStillToDeleteMs, dragSizeIncreaseFactor, onDelete, disableHoldToDelete, }: Props): React.JSX.Element;
28
+ export default function ChildWrapper({ position, itemWidth, itemHeight, dragMode, anyItemInDeleteMode, isPressingDeleteItem, children, wiggle, wiggleDeleteMode, holdStillToDeleteMs, dragSizeIncreaseFactor, onDelete, disableHoldToDelete, }: Props): React.JSX.Element;
28
29
  export {};
@@ -23,6 +23,7 @@ export declare function useGridLayout({ reverse, children, itemWidth, itemHeight
23
23
  orderState: string[];
24
24
  dragMode: SharedValue<boolean>;
25
25
  anyItemInDeleteMode: SharedValue<boolean>;
26
+ isPressingDeleteItem: SharedValue<boolean>;
26
27
  composed: import("react-native-gesture-handler/lib/typescript/handlers/gestures/gestureComposition").SimultaneousGesture;
27
28
  dynamicNumColumns: SharedValue<number>;
28
29
  onLayoutContent: (e: LayoutChangeEvent) => void;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-swappable-grid",
3
- "version": "1.0.4",
3
+ "version": "1.0.6",
4
4
  "description": "A React Native component for creating draggable, swappable grid layouts with reordering, delete functionality, and smooth animations",
5
5
  "keywords": [
6
6
  "react-native",
@@ -24,6 +24,7 @@ type Props = {
24
24
  itemHeight: number;
25
25
  dragMode: SharedValue<boolean>;
26
26
  anyItemInDeleteMode: SharedValue<boolean>;
27
+ isPressingDeleteItem: SharedValue<boolean>;
27
28
  children: React.ReactNode;
28
29
  wiggle?: { duration: number; degrees: number };
29
30
  wiggleDeleteMode?: { duration: number; degrees: number };
@@ -39,6 +40,7 @@ export default function ChildWrapper({
39
40
  itemHeight,
40
41
  dragMode,
41
42
  anyItemInDeleteMode,
43
+ isPressingDeleteItem,
42
44
  children,
43
45
  wiggle,
44
46
  wiggleDeleteMode,
@@ -122,12 +124,18 @@ export default function ChildWrapper({
122
124
  // Reset release tracking when not in delete mode
123
125
  wasReleasedAfterDeleteMode.value = false;
124
126
 
125
- // If not in drag mode or not active, reset timer
126
- if (!isDragging || !isActive) {
127
+ // Timer runs when item is active (being held)
128
+ // Note: isActive (position.active.value) is set when gesture activates after long press
129
+ // isDragging (dragMode.value) is also set at that time, but we primarily check isActive
130
+ // to allow timer to work even in edge cases
131
+ if (!isActive) {
127
132
  stillTimer.value = 0;
128
133
  return;
129
134
  }
130
135
 
136
+ // Item is active - timer can run (dragMode should also be true at this point,
137
+ // but we don't require it to allow timer to work in all cases)
138
+
131
139
  // Item is active (being held down) - check if it's still
132
140
  // Check if position has changed significantly (more than 10px threshold)
133
141
  const moved =
@@ -363,6 +371,16 @@ export default function ChildWrapper({
363
371
  {/* Full-item Pressable for delete - only active when in delete mode */}
364
372
  {isInDeleteMode && (
365
373
  <Pressable
374
+ onPressIn={() => {
375
+ // Mark that we're pressing an item to prevent ScrollView from canceling delete mode
376
+ isPressingDeleteItem.value = true;
377
+ }}
378
+ onPressOut={() => {
379
+ // Clear the flag after a short delay to allow onPress to fire
380
+ setTimeout(() => {
381
+ isPressingDeleteItem.value = false;
382
+ }, 50);
383
+ }}
366
384
  onPress={handleDelete}
367
385
  style={{
368
386
  position: "absolute",
@@ -194,6 +194,7 @@ const SwappableGrid = forwardRef<SwappableGridRef, SwappableGridProps>(
194
194
  positions,
195
195
  dragMode,
196
196
  anyItemInDeleteMode,
197
+ isPressingDeleteItem,
197
198
  order,
198
199
  deleteComponentPosition,
199
200
  } = useGridLayout({
@@ -396,8 +397,13 @@ const SwappableGrid = forwardRef<SwappableGridRef, SwappableGridProps>(
396
397
  }
397
398
  touchEndTimeoutRef.current = setTimeout(() => {
398
399
  // Only cancel if still in delete mode and not dragging
400
+ // Don't cancel if user is currently pressing an item (they might be deleting it)
399
401
  // This ensures we don't cancel when user is interacting with items
400
- if (anyItemInDeleteMode.value && !dragMode.value) {
402
+ if (
403
+ anyItemInDeleteMode.value &&
404
+ !dragMode.value &&
405
+ !isPressingDeleteItem.value
406
+ ) {
401
407
  anyItemInDeleteMode.value = false;
402
408
  }
403
409
  }, 100); // Small delay to let item interactions complete
@@ -428,6 +434,7 @@ const SwappableGrid = forwardRef<SwappableGridRef, SwappableGridProps>(
428
434
  itemHeight={itemHeight}
429
435
  dragMode={dragMode}
430
436
  anyItemInDeleteMode={anyItemInDeleteMode}
437
+ isPressingDeleteItem={isPressingDeleteItem}
431
438
  wiggle={wiggle}
432
439
  wiggleDeleteMode={wiggleDeleteMode}
433
440
  holdStillToDeleteMs={holdStillToDeleteMs}
@@ -78,6 +78,7 @@ export function useGridLayout({
78
78
  const startY = useSharedValue(0);
79
79
  const dragMode = useSharedValue(false);
80
80
  const anyItemInDeleteMode = useSharedValue(false); // Global delete mode state
81
+ const isPressingDeleteItem = useSharedValue(false); // Track if user is pressing an item in delete mode
81
82
  const contentH = useSharedValue(0);
82
83
  const scrollOffset = useSharedValue(0);
83
84
 
@@ -222,6 +223,7 @@ export function useGridLayout({
222
223
  orderState,
223
224
  dragMode,
224
225
  anyItemInDeleteMode,
226
+ isPressingDeleteItem,
225
227
  composed,
226
228
  dynamicNumColumns,
227
229
  onLayoutContent,