react-native-swappable-grid 1.0.1 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +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","longPressMs","numColumns","onDragEnd","onOrderChange","onDelete","wiggle","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 longPressMs?: 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 /** 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 longPressMs = 300,\n numColumns,\n onDragEnd,\n onOrderChange,\n onDelete,\n wiggle,\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 longPressMs,\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 dragSizeIncreaseFactor={dragSizeIncreaseFactor}\n disableHoldToDelete={!!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","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":[]}
@@ -25,7 +25,7 @@ export const PanWithLongPress = props => {
25
25
  scrollViewRef,
26
26
  scrollOffset,
27
27
  viewportH,
28
- longPressMs,
28
+ holdToDragMs,
29
29
  contentH,
30
30
  reverse = false,
31
31
  deleteComponentPosition,
@@ -69,7 +69,7 @@ export const PanWithLongPress = props => {
69
69
 
70
70
  return order.value.findIndex(x => x === key);
71
71
  };
72
- return Gesture.Pan().minDistance(10).activateAfterLongPress(longPressMs).onStart(({
72
+ return Gesture.Pan().minDistance(10).activateAfterLongPress(holdToDragMs).onStart(({
73
73
  x,
74
74
  y
75
75
  }) => {
@@ -1 +1 @@
1
- {"version":3,"names":["Gesture","runOnJS","withSpring","withTiming","scrollTo","useSharedValue","useDerivedValue","indexToXY","toIndex1ColFromLiveMidlines","xyToIndex","PanWithLongPress","props","order","dynamicNumColumns","activeKey","offsetX","offsetY","startX","startY","dragMode","positions","itemsByKey","itemWidth","itemHeight","containerPadding","gap","setOrderState","onDragEnd","onOrderChange","scrollSpeed","scrollThreshold","scrollViewRef","scrollOffset","viewportH","longPressMs","contentH","reverse","deleteComponentPosition","deleteItem","contentPaddingBottom","scrollDir","initialScrollOffset","value","key","p","maxScroll","newScroll","Math","max","min","scrollDelta","minY","maxY","proposedY","y","x","requestAnimationFrame","getIndexOfKey","findIndex","Pan","minDistance","activateAfterLongPress","onStart","bestKey","bestDist","Number","MAX_VALUE","forEach","cx","cy","dx","dy","dist2","active","duration","onUpdate","translationX","translationY","pointerYInViewport","centerY","fromIndex","toIndex","centerX","length","next","splice","onEnd","deletePos","tolerance","expandedDeleteX","expandedDeleteY","expandedDeleteWidth","width","expandedDeleteHeight","height","itemLeft","itemRight","itemTop","itemBottom","overlaps","idx","index","scale","damping","stiffness","mass","map"],"sources":["PanWithLongPress.ts"],"sourcesContent":["import { Gesture } from \"react-native-gesture-handler\";\nimport {\n runOnJS,\n SharedValue,\n withSpring,\n withTiming,\n scrollTo,\n AnimatedRef,\n useSharedValue,\n useDerivedValue,\n} from \"react-native-reanimated\";\nimport {\n indexToXY,\n toIndex1ColFromLiveMidlines,\n xyToIndex,\n} from \"../indexCalculations\";\n\ninterface PanProps {\n order: SharedValue<string[]>;\n dynamicNumColumns: SharedValue<number>;\n activeKey: SharedValue<string | null>;\n offsetX: SharedValue<number>;\n offsetY: SharedValue<number>;\n startX: SharedValue<number>;\n startY: SharedValue<number>;\n dragMode: SharedValue<boolean>;\n positions: any;\n itemsByKey: any;\n itemWidth: number;\n itemHeight: number;\n containerPadding: number;\n gap: number;\n setOrderState: React.Dispatch<React.SetStateAction<string[]>>;\n onDragEnd?: (ordered: ChildNode[]) => void;\n onOrderChange?: (keys: string[]) => void;\n\n // scrolling\n scrollSpeed: number;\n scrollThreshold: number;\n scrollViewRef: AnimatedRef<any>;\n scrollOffset: SharedValue<number>;\n viewportH: SharedValue<number>;\n longPressMs: number;\n contentH: SharedValue<number>;\n reverse?: boolean;\n deleteComponentPosition?: SharedValue<{\n x: number;\n y: number;\n width: number;\n height: number;\n } | null>;\n deleteItem?: (key: string) => void;\n contentPaddingBottom?: number; // Padding bottom from style prop to allow dragging into padding area\n}\n\nexport const PanWithLongPress = (props: PanProps & { longPressMs: number }) => {\n const {\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 scrollSpeed,\n scrollThreshold,\n scrollViewRef,\n scrollOffset,\n viewportH,\n longPressMs,\n contentH,\n reverse = false,\n deleteComponentPosition,\n deleteItem,\n contentPaddingBottom = 0,\n } = props;\n\n const scrollDir = useSharedValue(0); // -1 = up, 1 = down, 0 = none\n const initialScrollOffset = useSharedValue(0);\n\n useDerivedValue(() => {\n if (!dragMode.value || !activeKey.value) return;\n\n if (viewportH.value <= 0 || contentH.value <= 0) return;\n\n const key = activeKey.value;\n const p = positions[key];\n if (!p) return;\n\n // 1. Clamp scroll offset\n const maxScroll = contentH.value - viewportH.value;\n const newScroll = Math.max(\n 0,\n Math.min(scrollOffset.value + scrollDir.value * scrollSpeed, maxScroll)\n );\n\n scrollTo(scrollViewRef, 0, newScroll, false);\n const scrollDelta = newScroll - initialScrollOffset.value;\n scrollOffset.value = newScroll;\n\n // 2. Clamp item position\n // Allow dragging into padding area (paddingBottom from style prop)\n const minY = 0;\n // Add paddingBottom to maxY to allow dragging into the padding area\n const maxY = contentH.value - itemHeight + contentPaddingBottom;\n const proposedY = startY.value + offsetY.value + scrollDelta;\n p.y.value = Math.max(minY, Math.min(proposedY, maxY));\n\n // X stays normal\n p.x.value = startX.value + offsetX.value;\n\n // Keep loop alive\n requestAnimationFrame(() => {\n scrollDir.value = scrollDir.value;\n });\n });\n\n const getIndexOfKey = (key: string) => {\n \"worklet\";\n return order.value.findIndex((x) => x === key);\n };\n\n return Gesture.Pan()\n .minDistance(10)\n .activateAfterLongPress(longPressMs)\n .onStart(({ x, y }) => {\n initialScrollOffset.value = scrollOffset.value;\n dragMode.value = true;\n let bestKey: string | null = null;\n let bestDist = Number.MAX_VALUE;\n order.value.forEach((key) => {\n const p = positions[key];\n if (!p) return;\n const cx = p.x.value + itemWidth / 2;\n const cy = p.y.value + itemHeight / 2;\n const dx = cx - x;\n const dy = cy - y;\n const dist2 = dx * dx + dy * dy;\n if (dist2 < bestDist) {\n bestDist = dist2;\n bestKey = key;\n }\n });\n if (!bestKey) return;\n activeKey.value = bestKey;\n const p = positions[bestKey]!;\n p.active.value = withTiming(1, { duration: 120 });\n startX.value = p.x.value;\n startY.value = p.y.value;\n offsetX.value = 0;\n offsetY.value = 0;\n })\n .onUpdate(({ translationX, translationY }) => {\n if (!dragMode.value) return;\n const key = activeKey.value;\n if (!key) return;\n\n const p = positions[key]!;\n const scrollDelta = scrollOffset.value - initialScrollOffset.value;\n\n // Update active (top-left)\n offsetX.value = translationX;\n offsetY.value = translationY;\n p.x.value = startX.value + offsetX.value;\n p.y.value = startY.value + offsetY.value + scrollDelta;\n\n // Auto-scroll (unchanged)\n const pointerYInViewport = p.y.value - scrollOffset.value;\n if (pointerYInViewport > viewportH.value - scrollThreshold) {\n scrollDir.value = 1;\n } else if (pointerYInViewport < scrollThreshold) {\n scrollDir.value = -1;\n } else {\n scrollDir.value = 0;\n }\n\n // Compute target index from the active tile's **center**\n const centerY = p.y.value + itemHeight / 2;\n const fromIndex = getIndexOfKey(key);\n\n let toIndex: number;\n if (dynamicNumColumns.value === 1) {\n toIndex = toIndex1ColFromLiveMidlines(\n order,\n positions,\n activeKey,\n itemHeight,\n centerY,\n reverse // ← pass your prop\n );\n } else {\n // unchanged multi-column path\n const centerX = p.x.value + itemWidth / 2;\n toIndex = xyToIndex({\n order,\n x: centerX,\n y: centerY,\n itemWidth,\n itemHeight,\n dynamicNumColumns,\n containerPadding,\n gap,\n });\n }\n\n if (\n toIndex !== fromIndex &&\n toIndex >= 0 &&\n toIndex <= order.value.length - 1\n ) {\n const next = [...order.value];\n next.splice(fromIndex, 1);\n next.splice(toIndex, 0, key);\n order.value = next;\n }\n })\n .onEnd(() => {\n scrollDir.value = 0; // stop auto-scroll\n if (!dragMode.value) return;\n const key = activeKey.value;\n if (!key) {\n dragMode.value = false;\n return;\n }\n const p = positions[key]!;\n\n // Check if item was dropped into delete component\n if (deleteComponentPosition?.value && deleteItem) {\n const deletePos = deleteComponentPosition.value;\n\n // Add tolerance/padding to make it easier to hit (20% of item size)\n const tolerance = Math.min(itemWidth, itemHeight) * 0.2;\n const expandedDeleteX = deletePos.x - tolerance;\n const expandedDeleteY = deletePos.y - tolerance;\n const expandedDeleteWidth = deletePos.width + tolerance * 2;\n const expandedDeleteHeight = deletePos.height + tolerance * 2;\n\n // Check if item bounding box overlaps with expanded delete component bounds\n // This is more forgiving than checking just the center point\n const itemLeft = p.x.value;\n const itemRight = p.x.value + itemWidth;\n const itemTop = p.y.value;\n const itemBottom = p.y.value + itemHeight;\n\n // Bounding box intersection check\n const overlaps =\n itemLeft < expandedDeleteX + expandedDeleteWidth &&\n itemRight > expandedDeleteX &&\n itemTop < expandedDeleteY + expandedDeleteHeight &&\n itemBottom > expandedDeleteY;\n\n if (overlaps) {\n // Item was dropped into delete component - delete it\n runOnJS(deleteItem)(key);\n // Note: deleteItem will handle calling onDelete callback if provided\n p.active.value = withTiming(0, { duration: 120 });\n activeKey.value = null;\n dragMode.value = false;\n return;\n }\n }\n\n // Normal drop - return to grid position\n const idx = getIndexOfKey(key);\n const { x, y } = indexToXY({\n index: idx,\n itemWidth,\n itemHeight,\n dynamicNumColumns,\n containerPadding,\n gap,\n });\n const scale = Math.min(itemWidth, itemHeight) / 200; // 100px baseline\n\n const damping = 18 * scale;\n const stiffness = 240 * scale;\n const mass = Math.max(0.05, 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 p.active.value = withTiming(0, { duration: 120 });\n\n runOnJS(setOrderState)(order.value);\n if (onDragEnd) {\n runOnJS(onDragEnd)(order.value.map((key) => itemsByKey[key]));\n }\n if (onOrderChange) {\n runOnJS(onOrderChange)([...order.value]);\n }\n activeKey.value = null;\n dragMode.value = false;\n });\n};\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":["Gesture","runOnJS","withSpring","withTiming","scrollTo","useSharedValue","useDerivedValue","indexToXY","toIndex1ColFromLiveMidlines","xyToIndex","PanWithLongPress","props","order","dynamicNumColumns","activeKey","offsetX","offsetY","startX","startY","dragMode","positions","itemsByKey","itemWidth","itemHeight","containerPadding","gap","setOrderState","onDragEnd","onOrderChange","scrollSpeed","scrollThreshold","scrollViewRef","scrollOffset","viewportH","holdToDragMs","contentH","reverse","deleteComponentPosition","deleteItem","contentPaddingBottom","scrollDir","initialScrollOffset","value","key","p","maxScroll","newScroll","Math","max","min","scrollDelta","minY","maxY","proposedY","y","x","requestAnimationFrame","getIndexOfKey","findIndex","Pan","minDistance","activateAfterLongPress","onStart","bestKey","bestDist","Number","MAX_VALUE","forEach","cx","cy","dx","dy","dist2","active","duration","onUpdate","translationX","translationY","pointerYInViewport","centerY","fromIndex","toIndex","centerX","length","next","splice","onEnd","deletePos","tolerance","expandedDeleteX","expandedDeleteY","expandedDeleteWidth","width","expandedDeleteHeight","height","itemLeft","itemRight","itemTop","itemBottom","overlaps","idx","index","scale","damping","stiffness","mass","map"],"sources":["PanWithLongPress.ts"],"sourcesContent":["import { Gesture } from \"react-native-gesture-handler\";\nimport {\n runOnJS,\n SharedValue,\n withSpring,\n withTiming,\n scrollTo,\n AnimatedRef,\n useSharedValue,\n useDerivedValue,\n} from \"react-native-reanimated\";\nimport {\n indexToXY,\n toIndex1ColFromLiveMidlines,\n xyToIndex,\n} from \"../indexCalculations\";\n\ninterface PanProps {\n order: SharedValue<string[]>;\n dynamicNumColumns: SharedValue<number>;\n activeKey: SharedValue<string | null>;\n offsetX: SharedValue<number>;\n offsetY: SharedValue<number>;\n startX: SharedValue<number>;\n startY: SharedValue<number>;\n dragMode: SharedValue<boolean>;\n positions: any;\n itemsByKey: any;\n itemWidth: number;\n itemHeight: number;\n containerPadding: number;\n gap: number;\n setOrderState: React.Dispatch<React.SetStateAction<string[]>>;\n onDragEnd?: (ordered: ChildNode[]) => void;\n onOrderChange?: (keys: string[]) => void;\n\n // scrolling\n scrollSpeed: number;\n scrollThreshold: number;\n scrollViewRef: AnimatedRef<any>;\n scrollOffset: SharedValue<number>;\n viewportH: SharedValue<number>;\n holdToDragMs: number;\n contentH: SharedValue<number>;\n reverse?: boolean;\n deleteComponentPosition?: SharedValue<{\n x: number;\n y: number;\n width: number;\n height: number;\n } | null>;\n deleteItem?: (key: string) => void;\n contentPaddingBottom?: number; // Padding bottom from style prop to allow dragging into padding area\n}\n\nexport const PanWithLongPress = (\n props: PanProps & { holdToDragMs: number }\n) => {\n const {\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 scrollSpeed,\n scrollThreshold,\n scrollViewRef,\n scrollOffset,\n viewportH,\n holdToDragMs,\n contentH,\n reverse = false,\n deleteComponentPosition,\n deleteItem,\n contentPaddingBottom = 0,\n } = props;\n\n const scrollDir = useSharedValue(0); // -1 = up, 1 = down, 0 = none\n const initialScrollOffset = useSharedValue(0);\n\n useDerivedValue(() => {\n if (!dragMode.value || !activeKey.value) return;\n\n if (viewportH.value <= 0 || contentH.value <= 0) return;\n\n const key = activeKey.value;\n const p = positions[key];\n if (!p) return;\n\n // 1. Clamp scroll offset\n const maxScroll = contentH.value - viewportH.value;\n const newScroll = Math.max(\n 0,\n Math.min(scrollOffset.value + scrollDir.value * scrollSpeed, maxScroll)\n );\n\n scrollTo(scrollViewRef, 0, newScroll, false);\n const scrollDelta = newScroll - initialScrollOffset.value;\n scrollOffset.value = newScroll;\n\n // 2. Clamp item position\n // Allow dragging into padding area (paddingBottom from style prop)\n const minY = 0;\n // Add paddingBottom to maxY to allow dragging into the padding area\n const maxY = contentH.value - itemHeight + contentPaddingBottom;\n const proposedY = startY.value + offsetY.value + scrollDelta;\n p.y.value = Math.max(minY, Math.min(proposedY, maxY));\n\n // X stays normal\n p.x.value = startX.value + offsetX.value;\n\n // Keep loop alive\n requestAnimationFrame(() => {\n scrollDir.value = scrollDir.value;\n });\n });\n\n const getIndexOfKey = (key: string) => {\n \"worklet\";\n return order.value.findIndex((x) => x === key);\n };\n\n return Gesture.Pan()\n .minDistance(10)\n .activateAfterLongPress(holdToDragMs)\n .onStart(({ x, y }) => {\n initialScrollOffset.value = scrollOffset.value;\n dragMode.value = true;\n let bestKey: string | null = null;\n let bestDist = Number.MAX_VALUE;\n order.value.forEach((key) => {\n const p = positions[key];\n if (!p) return;\n const cx = p.x.value + itemWidth / 2;\n const cy = p.y.value + itemHeight / 2;\n const dx = cx - x;\n const dy = cy - y;\n const dist2 = dx * dx + dy * dy;\n if (dist2 < bestDist) {\n bestDist = dist2;\n bestKey = key;\n }\n });\n if (!bestKey) return;\n activeKey.value = bestKey;\n const p = positions[bestKey]!;\n p.active.value = withTiming(1, { duration: 120 });\n startX.value = p.x.value;\n startY.value = p.y.value;\n offsetX.value = 0;\n offsetY.value = 0;\n })\n .onUpdate(({ translationX, translationY }) => {\n if (!dragMode.value) return;\n const key = activeKey.value;\n if (!key) return;\n\n const p = positions[key]!;\n const scrollDelta = scrollOffset.value - initialScrollOffset.value;\n\n // Update active (top-left)\n offsetX.value = translationX;\n offsetY.value = translationY;\n p.x.value = startX.value + offsetX.value;\n p.y.value = startY.value + offsetY.value + scrollDelta;\n\n // Auto-scroll (unchanged)\n const pointerYInViewport = p.y.value - scrollOffset.value;\n if (pointerYInViewport > viewportH.value - scrollThreshold) {\n scrollDir.value = 1;\n } else if (pointerYInViewport < scrollThreshold) {\n scrollDir.value = -1;\n } else {\n scrollDir.value = 0;\n }\n\n // Compute target index from the active tile's **center**\n const centerY = p.y.value + itemHeight / 2;\n const fromIndex = getIndexOfKey(key);\n\n let toIndex: number;\n if (dynamicNumColumns.value === 1) {\n toIndex = toIndex1ColFromLiveMidlines(\n order,\n positions,\n activeKey,\n itemHeight,\n centerY,\n reverse // ← pass your prop\n );\n } else {\n // unchanged multi-column path\n const centerX = p.x.value + itemWidth / 2;\n toIndex = xyToIndex({\n order,\n x: centerX,\n y: centerY,\n itemWidth,\n itemHeight,\n dynamicNumColumns,\n containerPadding,\n gap,\n });\n }\n\n if (\n toIndex !== fromIndex &&\n toIndex >= 0 &&\n toIndex <= order.value.length - 1\n ) {\n const next = [...order.value];\n next.splice(fromIndex, 1);\n next.splice(toIndex, 0, key);\n order.value = next;\n }\n })\n .onEnd(() => {\n scrollDir.value = 0; // stop auto-scroll\n if (!dragMode.value) return;\n const key = activeKey.value;\n if (!key) {\n dragMode.value = false;\n return;\n }\n const p = positions[key]!;\n\n // Check if item was dropped into delete component\n if (deleteComponentPosition?.value && deleteItem) {\n const deletePos = deleteComponentPosition.value;\n\n // Add tolerance/padding to make it easier to hit (20% of item size)\n const tolerance = Math.min(itemWidth, itemHeight) * 0.2;\n const expandedDeleteX = deletePos.x - tolerance;\n const expandedDeleteY = deletePos.y - tolerance;\n const expandedDeleteWidth = deletePos.width + tolerance * 2;\n const expandedDeleteHeight = deletePos.height + tolerance * 2;\n\n // Check if item bounding box overlaps with expanded delete component bounds\n // This is more forgiving than checking just the center point\n const itemLeft = p.x.value;\n const itemRight = p.x.value + itemWidth;\n const itemTop = p.y.value;\n const itemBottom = p.y.value + itemHeight;\n\n // Bounding box intersection check\n const overlaps =\n itemLeft < expandedDeleteX + expandedDeleteWidth &&\n itemRight > expandedDeleteX &&\n itemTop < expandedDeleteY + expandedDeleteHeight &&\n itemBottom > expandedDeleteY;\n\n if (overlaps) {\n // Item was dropped into delete component - delete it\n runOnJS(deleteItem)(key);\n // Note: deleteItem will handle calling onDelete callback if provided\n p.active.value = withTiming(0, { duration: 120 });\n activeKey.value = null;\n dragMode.value = false;\n return;\n }\n }\n\n // Normal drop - return to grid position\n const idx = getIndexOfKey(key);\n const { x, y } = indexToXY({\n index: idx,\n itemWidth,\n itemHeight,\n dynamicNumColumns,\n containerPadding,\n gap,\n });\n const scale = Math.min(itemWidth, itemHeight) / 200; // 100px baseline\n\n const damping = 18 * scale;\n const stiffness = 240 * scale;\n const mass = Math.max(0.05, 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 p.active.value = withTiming(0, { duration: 120 });\n\n runOnJS(setOrderState)(order.value);\n if (onDragEnd) {\n runOnJS(onDragEnd)(order.value.map((key) => itemsByKey[key]));\n }\n if (onOrderChange) {\n runOnJS(onOrderChange)([...order.value]);\n }\n activeKey.value = null;\n dragMode.value = false;\n });\n};\n"],"mappings":"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","ignoreList":[]}
@@ -10,7 +10,7 @@ export function useGridLayout({
10
10
  itemHeight,
11
11
  gap,
12
12
  containerPadding,
13
- longPressMs,
13
+ holdToDragMs,
14
14
  numColumns,
15
15
  onDragEnd,
16
16
  onOrderChange,
@@ -171,7 +171,7 @@ export function useGridLayout({
171
171
  scrollViewRef,
172
172
  scrollOffset,
173
173
  viewportH,
174
- longPressMs,
174
+ holdToDragMs,
175
175
  scrollSpeed,
176
176
  scrollThreshold,
177
177
  reverse,
@@ -1 +1 @@
1
- {"version":3,"names":["Children","isValidElement","useMemo","useState","Gesture","useAnimatedScrollHandler","useDerivedValue","useSharedValue","withSpring","indexToXY","PanWithLongPress","useGridLayout","reverse","children","itemWidth","itemHeight","gap","containerPadding","longPressMs","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 longPressMs: 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 longPressMs,\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 longPressMs,\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","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":[]}
@@ -15,9 +15,14 @@ type Props = {
15
15
  duration: number;
16
16
  degrees: number;
17
17
  };
18
+ wiggleDeleteMode?: {
19
+ duration: number;
20
+ degrees: number;
21
+ };
22
+ holdStillToDeleteMs?: number;
18
23
  dragSizeIncreaseFactor: number;
19
24
  onDelete?: () => void;
20
25
  disableHoldToDelete?: boolean;
21
26
  };
22
- export default function ChildWrapper({ position, itemWidth, itemHeight, dragMode, anyItemInDeleteMode, children, wiggle, dragSizeIncreaseFactor, onDelete, disableHoldToDelete, }: Props): React.JSX.Element;
27
+ export default function ChildWrapper({ position, itemWidth, itemHeight, dragMode, anyItemInDeleteMode, children, wiggle, wiggleDeleteMode, holdStillToDeleteMs, dragSizeIncreaseFactor, onDelete, disableHoldToDelete, }: Props): React.JSX.Element;
23
28
  export {};
@@ -15,7 +15,7 @@ type SwappableGridProps = {
15
15
  /** Padding around the container in pixels. Defaults to 8. */
16
16
  containerPadding?: number;
17
17
  /** Duration in milliseconds to hold before drag starts. Defaults to 300. */
18
- longPressMs?: number;
18
+ holdToDragMs?: number;
19
19
  /** Number of columns in the grid. If not provided, will be calculated automatically based on container width. */
20
20
  numColumns?: number;
21
21
  /** Wiggle animation configuration when items are in drag mode or delete mode */
@@ -25,6 +25,15 @@ type SwappableGridProps = {
25
25
  /** Rotation degrees for the wiggle animation */
26
26
  degrees: number;
27
27
  };
28
+ /** Wiggle animation configuration specifically for delete mode. If not provided, uses 2x degrees and 0.7x duration of wiggle prop. */
29
+ wiggleDeleteMode?: {
30
+ /** Duration of one wiggle cycle in milliseconds */
31
+ duration: number;
32
+ /** Rotation degrees for the wiggle animation */
33
+ degrees: number;
34
+ };
35
+ /** Duration in milliseconds to hold an item still before entering delete mode. Defaults to 1000. */
36
+ holdStillToDeleteMs?: number;
28
37
  /** Callback fired when drag ends, providing the ordered array of child nodes */
29
38
  onDragEnd?: (ordered: ChildNode[]) => void;
30
39
  /** Callback fired when the order changes, providing an array of keys in the new order */
@@ -22,7 +22,7 @@ interface PanProps {
22
22
  scrollViewRef: AnimatedRef<any>;
23
23
  scrollOffset: SharedValue<number>;
24
24
  viewportH: SharedValue<number>;
25
- longPressMs: number;
25
+ holdToDragMs: number;
26
26
  contentH: SharedValue<number>;
27
27
  reverse?: boolean;
28
28
  deleteComponentPosition?: SharedValue<{
@@ -35,6 +35,6 @@ interface PanProps {
35
35
  contentPaddingBottom?: number;
36
36
  }
37
37
  export declare const PanWithLongPress: (props: PanProps & {
38
- longPressMs: number;
38
+ holdToDragMs: number;
39
39
  }) => import("react-native-gesture-handler/lib/typescript/handlers/gestures/panGesture").PanGesture;
40
40
  export {};
@@ -8,7 +8,7 @@ interface useGridLayoutProps {
8
8
  itemHeight: number;
9
9
  gap: number;
10
10
  containerPadding: number;
11
- longPressMs: number;
11
+ holdToDragMs: number;
12
12
  numColumns?: number;
13
13
  onDragEnd?: (ordered: ChildNode[]) => void;
14
14
  onOrderChange?: (keys: string[]) => void;
@@ -18,7 +18,7 @@ interface useGridLayoutProps {
18
18
  scrollThreshold: number;
19
19
  contentPaddingBottom?: number;
20
20
  }
21
- export declare function useGridLayout({ reverse, children, itemWidth, itemHeight, gap, containerPadding, longPressMs, numColumns, onDragEnd, onOrderChange, onDelete, scrollViewRef, scrollSpeed, scrollThreshold, contentPaddingBottom, }: useGridLayoutProps): {
21
+ export declare function useGridLayout({ reverse, children, itemWidth, itemHeight, gap, containerPadding, holdToDragMs, numColumns, onDragEnd, onOrderChange, onDelete, scrollViewRef, scrollSpeed, scrollThreshold, contentPaddingBottom, }: useGridLayoutProps): {
22
22
  itemsByKey: Record<string, ReactNode>;
23
23
  orderState: string[];
24
24
  dragMode: SharedValue<boolean>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-swappable-grid",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
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",
@@ -26,6 +26,8 @@ type Props = {
26
26
  anyItemInDeleteMode: SharedValue<boolean>;
27
27
  children: React.ReactNode;
28
28
  wiggle?: { duration: number; degrees: number };
29
+ wiggleDeleteMode?: { duration: number; degrees: number };
30
+ holdStillToDeleteMs?: number;
29
31
  dragSizeIncreaseFactor: number;
30
32
  onDelete?: () => void;
31
33
  disableHoldToDelete?: boolean; // If true, disable the hold-to-delete feature
@@ -39,6 +41,8 @@ export default function ChildWrapper({
39
41
  anyItemInDeleteMode,
40
42
  children,
41
43
  wiggle,
44
+ wiggleDeleteMode,
45
+ holdStillToDeleteMs = 1000,
42
46
  dragSizeIncreaseFactor,
43
47
  onDelete,
44
48
  disableHoldToDelete = false,
@@ -147,8 +151,8 @@ export default function ChildWrapper({
147
151
  // Increment by ~16ms per frame (assuming 60fps)
148
152
  stillTimer.value += 16;
149
153
 
150
- // Enter delete mode after 1 second (1000ms) of being held still
151
- if (stillTimer.value >= 1000) {
154
+ // Enter delete mode after holdStillToDeleteMs of being held still
155
+ if (stillTimer.value >= holdStillToDeleteMs) {
152
156
  deleteModeActive.value = true;
153
157
  anyItemInDeleteMode.value = true; // Set global delete mode
154
158
  showDelete.value = true;
@@ -192,7 +196,18 @@ export default function ChildWrapper({
192
196
  anyInDeleteMode: anyItemInDeleteMode.value,
193
197
  }),
194
198
  ({ isEditMode, isActive, inDeleteMode, anyInDeleteMode }) => {
195
- if (!wiggle) {
199
+ // Determine the target wiggle mode
200
+ let targetMode: "none" | "normal" | "delete" = "none";
201
+ if (inDeleteMode && (wiggleDeleteMode || wiggle)) {
202
+ targetMode = "delete";
203
+ } else if (anyInDeleteMode && !isActive && wiggle) {
204
+ targetMode = "normal";
205
+ } else if (isEditMode && !isActive && wiggle) {
206
+ targetMode = "normal";
207
+ }
208
+
209
+ // If no wiggle is configured at all, stop animation
210
+ if (!wiggle && !wiggleDeleteMode) {
196
211
  if (currentWiggleMode.value !== "none") {
197
212
  cancelAnimation(rotation);
198
213
  currentWiggleMode.value = "none";
@@ -201,14 +216,24 @@ export default function ChildWrapper({
201
216
  return;
202
217
  }
203
218
 
204
- // Determine the target wiggle mode
205
- let targetMode: "none" | "normal" | "delete" = "none";
206
- if (inDeleteMode) {
207
- targetMode = "delete";
208
- } else if (anyInDeleteMode && !isActive) {
209
- targetMode = "normal";
210
- } else if (isEditMode && !isActive) {
211
- targetMode = "normal";
219
+ // If in delete mode but no wiggleDeleteMode and no wiggle, stop animation
220
+ if (targetMode === "delete" && !wiggleDeleteMode && !wiggle) {
221
+ if (currentWiggleMode.value !== "none") {
222
+ cancelAnimation(rotation);
223
+ currentWiggleMode.value = "none";
224
+ }
225
+ rotation.value = withTiming(0, { duration: 150 });
226
+ return;
227
+ }
228
+
229
+ // If normal mode but no wiggle, stop animation
230
+ if (targetMode === "normal" && !wiggle) {
231
+ if (currentWiggleMode.value !== "none") {
232
+ cancelAnimation(rotation);
233
+ currentWiggleMode.value = "none";
234
+ }
235
+ rotation.value = withTiming(0, { duration: 150 });
236
+ return;
212
237
  }
213
238
 
214
239
  // Only restart animation if mode changed
@@ -222,13 +247,17 @@ export default function ChildWrapper({
222
247
  // Cancel current animation
223
248
  cancelAnimation(rotation);
224
249
 
225
- // If this item is in delete mode, wiggle more (2x degrees, faster)
250
+ // If this item is in delete mode, use wiggleDeleteMode if provided, otherwise use 2x degrees and 0.7x duration
226
251
  if (targetMode === "delete") {
227
- const deleteWiggleDegrees = wiggle.degrees * 2;
228
- const deleteWiggleDuration = wiggle.duration * 0.7; // Faster wiggle
252
+ const deleteWiggleDegrees = wiggleDeleteMode
253
+ ? wiggleDeleteMode.degrees
254
+ : (wiggle?.degrees ?? 0) * 2;
255
+ const deleteWiggleDuration = wiggleDeleteMode
256
+ ? wiggleDeleteMode.duration
257
+ : (wiggle?.duration ?? 200) * 0.7; // Faster wiggle
229
258
 
230
259
  // If transitioning from normal wiggle, preserve the phase by scaling
231
- if (previousMode === "normal") {
260
+ if (previousMode === "normal" && wiggle) {
232
261
  const currentRot = rotation.value;
233
262
  const scaleFactor = deleteWiggleDegrees / wiggle.degrees;
234
263
  rotation.value = currentRot * scaleFactor;
@@ -278,7 +307,14 @@ export default function ChildWrapper({
278
307
  rotation.value = withTiming(0, { duration: 150 });
279
308
  }
280
309
  },
281
- [dragMode, position.active, deleteModeActive, anyItemInDeleteMode]
310
+ [
311
+ dragMode,
312
+ position.active,
313
+ deleteModeActive,
314
+ anyItemInDeleteMode,
315
+ wiggle,
316
+ wiggleDeleteMode,
317
+ ]
282
318
  );
283
319
 
284
320
  const animatedStyle = useAnimatedStyle(() => {
@@ -45,7 +45,7 @@ type SwappableGridProps = {
45
45
  /** Padding around the container in pixels. Defaults to 8. */
46
46
  containerPadding?: number;
47
47
  /** Duration in milliseconds to hold before drag starts. Defaults to 300. */
48
- longPressMs?: number;
48
+ holdToDragMs?: number;
49
49
  /** Number of columns in the grid. If not provided, will be calculated automatically based on container width. */
50
50
  numColumns?: number;
51
51
  /** Wiggle animation configuration when items are in drag mode or delete mode */
@@ -55,6 +55,15 @@ type SwappableGridProps = {
55
55
  /** Rotation degrees for the wiggle animation */
56
56
  degrees: number;
57
57
  };
58
+ /** Wiggle animation configuration specifically for delete mode. If not provided, uses 2x degrees and 0.7x duration of wiggle prop. */
59
+ wiggleDeleteMode?: {
60
+ /** Duration of one wiggle cycle in milliseconds */
61
+ duration: number;
62
+ /** Rotation degrees for the wiggle animation */
63
+ degrees: number;
64
+ };
65
+ /** Duration in milliseconds to hold an item still before entering delete mode. Defaults to 1000. */
66
+ holdStillToDeleteMs?: number;
58
67
  /** Callback fired when drag ends, providing the ordered array of child nodes */
59
68
  onDragEnd?: (ordered: ChildNode[]) => void;
60
69
  /** Callback fired when the order changes, providing an array of keys in the new order */
@@ -121,12 +130,14 @@ const SwappableGrid = forwardRef<SwappableGridRef, SwappableGridProps>(
121
130
  itemHeight,
122
131
  gap = 8,
123
132
  containerPadding = 8,
124
- longPressMs = 300,
133
+ holdToDragMs = 300,
125
134
  numColumns,
126
135
  onDragEnd,
127
136
  onOrderChange,
128
137
  onDelete,
129
138
  wiggle,
139
+ wiggleDeleteMode,
140
+ holdStillToDeleteMs = 1000,
130
141
  style,
131
142
  dragSizeIncreaseFactor = 1.06,
132
143
  scrollThreshold = 100,
@@ -188,7 +199,7 @@ const SwappableGrid = forwardRef<SwappableGridRef, SwappableGridProps>(
188
199
  } = useGridLayout({
189
200
  reverse,
190
201
  children,
191
- longPressMs,
202
+ holdToDragMs,
192
203
  itemWidth,
193
204
  itemHeight,
194
205
  gap,
@@ -418,8 +429,10 @@ const SwappableGrid = forwardRef<SwappableGridRef, SwappableGridProps>(
418
429
  dragMode={dragMode}
419
430
  anyItemInDeleteMode={anyItemInDeleteMode}
420
431
  wiggle={wiggle}
432
+ wiggleDeleteMode={wiggleDeleteMode}
433
+ holdStillToDeleteMs={holdStillToDeleteMs}
421
434
  dragSizeIncreaseFactor={dragSizeIncreaseFactor}
422
- disableHoldToDelete={!!deleteComponent}
435
+ disableHoldToDelete={!onDelete || !!deleteComponent}
423
436
  onDelete={() => {
424
437
  deleteItem(key);
425
438
  if (onDelete) {