@btfash/react-native-reanimated-dnd 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1238 -0
  3. package/documentation/web-docs/README.md +41 -0
  4. package/example-app/.expo/README.md +8 -0
  5. package/example-app/README.md +515 -0
  6. package/lib/components/Draggable.d.ts +5 -0
  7. package/lib/components/Draggable.js +1 -0
  8. package/lib/components/Droppable.d.ts +3 -0
  9. package/lib/components/Droppable.js +1 -0
  10. package/lib/components/Sortable.d.ts +3 -0
  11. package/lib/components/Sortable.js +1 -0
  12. package/lib/components/SortableItem.d.ts +6 -0
  13. package/lib/components/SortableItem.js +1 -0
  14. package/lib/components/sortableUtils.d.ts +33 -0
  15. package/lib/components/sortableUtils.js +1 -0
  16. package/lib/context/DropContext.d.ts +3 -0
  17. package/lib/context/DropContext.js +1 -0
  18. package/lib/hooks/index.d.ts +6 -0
  19. package/lib/hooks/index.js +1 -0
  20. package/lib/hooks/useDraggable.d.ts +2 -0
  21. package/lib/hooks/useDraggable.js +1 -0
  22. package/lib/hooks/useDroppable.d.ts +2 -0
  23. package/lib/hooks/useDroppable.js +1 -0
  24. package/lib/hooks/useHorizontalSortable.d.ts +2 -0
  25. package/lib/hooks/useHorizontalSortable.js +1 -0
  26. package/lib/hooks/useHorizontalSortableList.d.ts +4 -0
  27. package/lib/hooks/useHorizontalSortableList.js +1 -0
  28. package/lib/hooks/useSortable.d.ts +50 -0
  29. package/lib/hooks/useSortable.js +1 -0
  30. package/lib/hooks/useSortableList.d.ts +27 -0
  31. package/lib/hooks/useSortableList.js +1 -0
  32. package/lib/index.d.ts +13 -0
  33. package/lib/index.js +1 -0
  34. package/lib/types/context.d.ts +67 -0
  35. package/lib/types/context.js +1 -0
  36. package/lib/types/draggable.d.ts +54 -0
  37. package/lib/types/draggable.js +1 -0
  38. package/lib/types/droppable.d.ts +26 -0
  39. package/lib/types/droppable.js +1 -0
  40. package/lib/types/index.d.ts +4 -0
  41. package/lib/types/index.js +1 -0
  42. package/lib/types/sortable.d.ts +233 -0
  43. package/lib/types/sortable.js +1 -0
  44. package/package.json +66 -0
@@ -0,0 +1 @@
1
+ export{useSortable}from"./useSortable";export{useSortableList}from"./useSortableList";export{useHorizontalSortable}from"./useHorizontalSortable";export{useHorizontalSortableList}from"./useHorizontalSortableList";export{useDraggable}from"./useDraggable";export{useDroppable}from"./useDroppable";
@@ -0,0 +1,2 @@
1
+ import { UseDraggableOptions, UseDraggableReturn } from "../types/draggable";
2
+ export declare const useDraggable: <TData = unknown>(options: UseDraggableOptions<TData>) => UseDraggableReturn;
@@ -0,0 +1 @@
1
+ import React,{useRef,useCallback,useContext,useEffect,useState}from"react";import{useSharedValue,useAnimatedStyle,withSpring,runOnJS,runOnUI,useAnimatedReaction,useAnimatedRef,measure}from"react-native-reanimated";import{Gesture}from"react-native-gesture-handler";import{SlotsContext}from"../types/context";import{DraggableState}from"../types/draggable";export const useDraggable=options=>{const{data,draggableId,dragDisabled=false,preDragDelay=0,onDragStart,onDragEnd,onDragging,onStateChange,animationFunction,dragBoundsRef,dragAxis="both",collisionAlgorithm="intersect",children,handleComponent}=options;const animatedViewRef=useAnimatedRef();const[state,setState]=useState(DraggableState.IDLE);const[hasHandle,setHasHandle]=useState(false);useEffect((()=>{if(!children||!handleComponent){setHasHandle(false);return}const checkForHandle=child=>{if(React.isValidElement(child)){if(child.type===handleComponent){return true}const props=child.props;if(child.props&&props.children){if(React.Children.toArray(props.children).some(checkForHandle)){return true}}}return false};setHasHandle(React.Children.toArray(children).some(checkForHandle))}),[children,handleComponent]);useEffect((()=>{onStateChange===null||onStateChange===void 0?void 0:onStateChange(state)}),[state,onStateChange]);const tx=useSharedValue(0);const ty=useSharedValue(0);const offsetX=useSharedValue(0);const offsetY=useSharedValue(0);const dragDisabledShared=useSharedValue(dragDisabled);const dragAxisShared=useSharedValue(dragAxis);const preDragDelayShared=useSharedValue(preDragDelay);const originX=useSharedValue(0);const originY=useSharedValue(0);const itemW=useSharedValue(0);const itemH=useSharedValue(0);const isOriginSet=useRef(false);const internalDraggableId=useRef(draggableId||`draggable-${Math.random().toString(36).substr(2,9)}`).current;const boundsX=useSharedValue(0);const boundsY=useSharedValue(0);const boundsWidth=useSharedValue(0);const boundsHeight=useSharedValue(0);const boundsAreSet=useSharedValue(false);const{getSlots,setActiveHoverSlot,activeHoverSlotId,registerPositionUpdateListener,unregisterPositionUpdateListener,registerDroppedItem,unregisterDroppedItem,hasAvailableCapacity,onDragging:contextOnDragging,onDragStart:contextOnDragStart,onDragEnd:contextOnDragEnd}=useContext(SlotsContext);useEffect((()=>{preDragDelayShared.value=preDragDelay}),[preDragDelay,preDragDelayShared]);useEffect((()=>{dragDisabledShared.value=dragDisabled}),[dragDisabled,dragDisabledShared]);useEffect((()=>{dragAxisShared.value=dragAxis}),[dragAxis,dragAxisShared]);const updateDraggablePosition=useCallback((()=>{runOnUI((()=>{"worklet";const measurement=measure(animatedViewRef);if(measurement===null){return}const currentTx=tx.value;const currentTy=ty.value;if(currentTx===0&&currentTy===0){const newOriginX=measurement.pageX-currentTx;const newOriginY=measurement.pageY-currentTy;originX.value=newOriginX;originY.value=newOriginY}itemW.value=measurement.width;itemH.value=measurement.height;if(!isOriginSet.current){isOriginSet.current=true}}))()}),[animatedViewRef,originX,originY,itemW,itemH,tx,ty]);const updateDraggablePositionWorklet=useCallback((()=>{"worklet";const measurement=measure(animatedViewRef);if(measurement===null){return}const currentTx=tx.value;const currentTy=ty.value;if(currentTx===0&&currentTy===0){const newOriginX=measurement.pageX-currentTx;const newOriginY=measurement.pageY-currentTy;originX.value=newOriginX;originY.value=newOriginY}itemW.value=measurement.width;itemH.value=measurement.height;if(!isOriginSet.current){isOriginSet.current=true}}),[animatedViewRef,originX,originY,itemW,itemH,tx,ty]);const updateBounds=useCallback((()=>{const currentBoundsView=dragBoundsRef===null||dragBoundsRef===void 0?void 0:dragBoundsRef.current;if(currentBoundsView){currentBoundsView.measure(((_x,_y,width,height,pageX,pageY)=>{if(typeof pageX==="number"&&typeof pageY==="number"&&width>0&&height>0){runOnUI((()=>{"worklet";boundsX.value=pageX;boundsY.value=pageY;boundsWidth.value=width;boundsHeight.value=height;if(!boundsAreSet.value){boundsAreSet.value=true}}))()}else{console.warn("useDraggable: dragBoundsRef measurement failed or returned invalid dimensions. Bounds may be stale or item unbounded.")}}))}else{runOnUI((()=>{"worklet";if(boundsAreSet.value){boundsAreSet.value=false}}))()}}),[dragBoundsRef,boundsX,boundsY,boundsWidth,boundsHeight,boundsAreSet]);useEffect((()=>{const handlePositionUpdate=()=>{updateDraggablePosition();updateBounds()};registerPositionUpdateListener(internalDraggableId,handlePositionUpdate);return()=>{unregisterPositionUpdateListener(internalDraggableId)}}),[internalDraggableId,registerPositionUpdateListener,unregisterPositionUpdateListener,updateDraggablePosition,updateBounds]);useEffect((()=>{updateBounds()}),[updateBounds]);const handleLayoutHandler=useCallback((event=>{updateDraggablePosition()}),[updateDraggablePosition]);const animateDragEndPosition=useCallback(((targetXValue,targetYValue)=>{"worklet";if(animationFunction){tx.value=animationFunction(targetXValue);ty.value=animationFunction(targetYValue)}else{tx.value=withSpring(targetXValue);ty.value=withSpring(targetYValue)}}),[animationFunction,tx,ty]);const performCollisionCheck=useCallback(((draggableX,draggableY,draggableW,draggableH,slot,algo)=>{if(algo==="intersect"){return draggableX<slot.x+slot.width&&draggableX+draggableW>slot.x&&draggableY<slot.y+slot.height&&draggableY+draggableH>slot.y}else if(algo==="contain"){return draggableX>=slot.x&&draggableX+draggableW<=slot.x+slot.width&&draggableY>=slot.y&&draggableY+draggableH<=slot.y+slot.height}else{const draggableCenterX=draggableX+draggableW/2;const draggableCenterY=draggableY+draggableH/2;return draggableCenterX>=slot.x&&draggableCenterX<=slot.x+slot.width&&draggableCenterY>=slot.y&&draggableCenterY<=slot.y+slot.height}}),[]);const processDropAndAnimate=useCallback(((currentTxVal,currentTyVal,draggableData,currentOriginX,currentOriginY,currentItemW,currentItemH)=>{const slots=getSlots();const currentDraggableX=currentOriginX+currentTxVal;const currentDraggableY=currentOriginY+currentTyVal;let hitSlotData=null;let hitSlotId=null;for(const key in slots){const slotId=parseInt(key,10);const s=slots[slotId];const isCollision=performCollisionCheck(currentDraggableX,currentDraggableY,currentItemW,currentItemH,s,collisionAlgorithm);if(isCollision){const hasCapacity=hasAvailableCapacity(s.id);if(hasCapacity){hitSlotData=s;hitSlotId=slotId;break}}}let finalTxValue;let finalTyValue;if(hitSlotData&&hitSlotId!==null){if(hitSlotData.onDrop){hitSlotData.onDrop(draggableData)}runOnJS(registerDroppedItem)(internalDraggableId,hitSlotData.id,draggableData);runOnJS(setState)(DraggableState.DROPPED);const alignment=hitSlotData.dropAlignment||"center";const offset=hitSlotData.dropOffset||{x:0,y:0};let targetX=0;let targetY=0;switch(alignment){case"top-left":targetX=hitSlotData.x;targetY=hitSlotData.y;break;case"top-center":targetX=hitSlotData.x+hitSlotData.width/2-currentItemW/2;targetY=hitSlotData.y;break;case"top-right":targetX=hitSlotData.x+hitSlotData.width-currentItemW;targetY=hitSlotData.y;break;case"center-left":targetX=hitSlotData.x;targetY=hitSlotData.y+hitSlotData.height/2-currentItemH/2;break;case"center":targetX=hitSlotData.x+hitSlotData.width/2-currentItemW/2;targetY=hitSlotData.y+hitSlotData.height/2-currentItemH/2;break;case"center-right":targetX=hitSlotData.x+hitSlotData.width-currentItemW;targetY=hitSlotData.y+hitSlotData.height/2-currentItemH/2;break;case"bottom-left":targetX=hitSlotData.x;targetY=hitSlotData.y+hitSlotData.height-currentItemH;break;case"bottom-center":targetX=hitSlotData.x+hitSlotData.width/2-currentItemW/2;targetY=hitSlotData.y+hitSlotData.height-currentItemH;break;case"bottom-right":targetX=hitSlotData.x+hitSlotData.width-currentItemW;targetY=hitSlotData.y+hitSlotData.height-currentItemH;break;default:targetX=hitSlotData.x+hitSlotData.width/2-currentItemW/2;targetY=hitSlotData.y+hitSlotData.height/2-currentItemH/2}const draggableTargetX=targetX+offset.x;const draggableTargetY=targetY+offset.y;finalTxValue=draggableTargetX-currentOriginX;finalTyValue=draggableTargetY-currentOriginY}else{finalTxValue=0;finalTyValue=0;runOnJS(setState)(DraggableState.IDLE);runOnJS(unregisterDroppedItem)(internalDraggableId)}runOnUI(animateDragEndPosition)(finalTxValue,finalTyValue)}),[getSlots,animateDragEndPosition,collisionAlgorithm,performCollisionCheck,setState,internalDraggableId,registerDroppedItem,unregisterDroppedItem,hasAvailableCapacity]);const updateHoverState=useCallback(((currentTxVal,currentTyVal,currentOriginX,currentOriginY,currentItemW,currentItemH)=>{const slots=getSlots();const currentDraggableX=currentOriginX+currentTxVal;const currentDraggableY=currentOriginY+currentTyVal;let newHoveredSlotId=null;for(const key in slots){const slotId=parseInt(key,10);const s=slots[slotId];const isCollision=performCollisionCheck(currentDraggableX,currentDraggableY,currentItemW,currentItemH,s,collisionAlgorithm);if(isCollision){newHoveredSlotId=slotId;break}}if(activeHoverSlotId!==newHoveredSlotId){setActiveHoverSlot(newHoveredSlotId)}}),[getSlots,setActiveHoverSlot,activeHoverSlotId,collisionAlgorithm,performCollisionCheck]);const gesture=React.useMemo((()=>Gesture.Pan().activateAfterLongPress(preDragDelay).onStart((()=>{"worklet";updateDraggablePositionWorklet();if(dragDisabledShared.value){return}offsetX.value=tx.value;offsetY.value=ty.value;runOnJS(setState)(DraggableState.DRAGGING);if(onDragStart){runOnJS(onDragStart)(data)}if(contextOnDragStart){runOnJS(contextOnDragStart)(data)}})).onUpdate((event=>{"worklet";if(dragDisabledShared.value){return}let newTx=offsetX.value+event.translationX;let newTy=offsetY.value+event.translationY;if(boundsAreSet.value){const currentItemW=itemW.value;const currentItemH=itemH.value;const minTx=boundsX.value-originX.value;const maxTx=boundsX.value+boundsWidth.value-originX.value-currentItemW;const minTy=boundsY.value-originY.value;const maxTy=boundsY.value+boundsHeight.value-originY.value-currentItemH;newTx=Math.max(minTx,Math.min(newTx,maxTx));newTy=Math.max(minTy,Math.min(newTy,maxTy))}if(dragAxisShared.value==="x"){tx.value=newTx}else if(dragAxisShared.value==="y"){ty.value=newTy}else{tx.value=newTx;ty.value=newTy}if(onDragging){runOnJS(onDragging)({x:originX.value,y:originY.value,tx:tx.value,ty:ty.value,itemData:data})}if(contextOnDragging){runOnJS(contextOnDragging)({x:originX.value,y:originY.value,tx:tx.value,ty:ty.value,itemData:data})}runOnJS(updateHoverState)(tx.value,ty.value,originX.value,originY.value,itemW.value,itemH.value)})).onEnd((()=>{"worklet";if(dragDisabledShared.value){return}if(onDragEnd){runOnJS(onDragEnd)(data)}if(contextOnDragEnd){runOnJS(contextOnDragEnd)(data)}runOnJS(processDropAndAnimate)(tx.value,ty.value,data,originX.value,originY.value,itemW.value,itemH.value);runOnJS(setActiveHoverSlot)(null)}))),[dragDisabledShared,offsetX,offsetY,tx,ty,originX,originY,itemW,itemH,onDragStart,onDragEnd,data,processDropAndAnimate,updateHoverState,setActiveHoverSlot,animationFunction,onDragging,boundsAreSet,boundsX,boundsY,boundsWidth,boundsHeight,dragAxisShared,setState,updateDraggablePositionWorklet,contextOnDragging,contextOnDragStart,contextOnDragEnd,preDragDelay]);const animatedStyleProp=useAnimatedStyle((()=>{"worklet";return{transform:[{translateX:tx.value},{translateY:ty.value}]}}),[tx,ty]);useAnimatedReaction((()=>({txValue:tx.value,tyValue:ty.value,isZero:tx.value===0&&ty.value===0})),((result,previous)=>{if(result.isZero&&previous&&!previous.isZero){runOnJS(setState)(DraggableState.IDLE);runOnJS(unregisterDroppedItem)(internalDraggableId)}}),[setState,unregisterDroppedItem,internalDraggableId]);useEffect((()=>()=>{unregisterDroppedItem(internalDraggableId)}),[internalDraggableId,unregisterDroppedItem]);return{animatedViewProps:{style:animatedStyleProp,onLayout:handleLayoutHandler},gesture,state,animatedViewRef,hasHandle}};
@@ -0,0 +1,2 @@
1
+ import { UseDroppableOptions, UseDroppableReturn } from "../types/droppable";
2
+ export declare const useDroppable: <TData = unknown>(options: UseDroppableOptions<TData>) => UseDroppableReturn;
@@ -0,0 +1 @@
1
+ import{useRef,useEffect,useContext,useCallback,useMemo}from"react";import{StyleSheet}from"react-native";import{useAnimatedRef,measure,runOnUI,runOnJS}from"react-native-reanimated";import{SlotsContext}from"../types/context";let _nextDroppableId=1;const _getUniqueDroppableId=()=>_nextDroppableId++;export const useDroppable=options=>{const{onDrop,dropDisabled,onActiveChange,dropAlignment,dropOffset,activeStyle,droppableId,capacity}=options;const animatedViewRef=useAnimatedRef();const id=useRef(_getUniqueDroppableId()).current;const stringId=useRef(droppableId||`droppable-${id}`).current;const instanceId=useRef(`droppable-${id}-${Math.random().toString(36).substr(2,9)}`).current;const{register,unregister,isRegistered,activeHoverSlotId:contextActiveHoverSlotId,registerPositionUpdateListener,unregisterPositionUpdateListener}=useContext(SlotsContext);const isActive=contextActiveHoverSlotId===id;const{processedActiveStyle,activeTransforms}=useMemo((()=>{if(!isActive||!activeStyle){return{processedActiveStyle:null,activeTransforms:[]}}const flattenedStyle=StyleSheet.flatten(activeStyle);let processedStyle={...flattenedStyle};let transforms=[];if(flattenedStyle.transform){if(Array.isArray(flattenedStyle.transform)){transforms=[...flattenedStyle.transform]}delete processedStyle.transform}return{processedActiveStyle:processedStyle,activeTransforms:transforms}}),[isActive,activeStyle]);const combinedActiveStyle=useMemo((()=>{if(!isActive||!activeStyle){return undefined}if(activeTransforms.length===0){return processedActiveStyle}return{...processedActiveStyle,transform:activeTransforms}}),[isActive,activeStyle,processedActiveStyle,activeTransforms]);useEffect((()=>{onActiveChange===null||onActiveChange===void 0?void 0:onActiveChange(isActive)}),[isActive,onActiveChange]);const registerWithMeasurement=useCallback(((pageX,pageY,width,height)=>{register(id,{id:droppableId||`droppable-${id}`,x:pageX,y:pageY,width,height,onDrop,dropAlignment:dropAlignment||"center",dropOffset:dropOffset||{x:0,y:0},capacity})}),[id,droppableId,onDrop,register,dropAlignment,dropOffset,capacity]);const updateDroppablePosition=useCallback((()=>{runOnUI((()=>{"worklet";const measurement=measure(animatedViewRef);if(measurement===null){return}if(measurement.width>0&&measurement.height>0){runOnJS(registerWithMeasurement)(measurement.pageX,measurement.pageY,measurement.width,measurement.height)}}))()}),[animatedViewRef,registerWithMeasurement]);const handleLayoutHandler=useCallback((_event=>{updateDroppablePosition()}),[updateDroppablePosition]);useEffect((()=>{registerPositionUpdateListener(instanceId,updateDroppablePosition);return()=>{unregisterPositionUpdateListener(instanceId)}}),[instanceId,registerPositionUpdateListener,unregisterPositionUpdateListener,updateDroppablePosition]);useEffect((()=>{if(dropDisabled){unregister(id)}else{updateDroppablePosition()}}),[dropDisabled,id,unregister,updateDroppablePosition]);useEffect((()=>()=>{unregister(id)}),[id,unregister]);return{viewProps:{onLayout:handleLayoutHandler,style:combinedActiveStyle},isActive,activeStyle,animatedViewRef}};
@@ -0,0 +1,2 @@
1
+ import { UseHorizontalSortableOptions, UseHorizontalSortableReturn } from "../types/sortable";
2
+ export declare function useHorizontalSortable<T>(options: UseHorizontalSortableOptions<T>): UseHorizontalSortableReturn;
@@ -0,0 +1 @@
1
+ import{useState,useRef,useEffect,useMemo}from"react";import React from"react";import{runOnJS,useAnimatedReaction,useAnimatedStyle,useDerivedValue,useSharedValue,withSpring,withTiming}from"react-native-reanimated";import{Gesture}from"react-native-gesture-handler";import{setHorizontalPosition,setHorizontalAutoScroll,getItemXPosition,getContentWidth}from"../components/sortableUtils";import{HorizontalScrollDirection}from"../types/sortable";export function useHorizontalSortable(options){const{id,positions,leftBound,autoScrollDirection,itemsCount,itemWidth,gap=0,paddingHorizontal=0,containerWidth=500,onMove,onDragStart,onDrop,onDragging,children,handleComponent}=options;const[isMoving,setIsMoving]=useState(false);const[hasHandle,setHasHandle]=useState(false);const movingSV=useSharedValue(false);const currentOverItemId=useSharedValue(null);const onDraggingLastCallTimestamp=useSharedValue(0);const THROTTLE_INTERVAL=50;const initialLeftVal=useMemo((()=>{const posArr=positions.get();const pos=posArr===null||posArr===void 0?void 0:posArr[id];return getItemXPosition(pos,itemWidth,gap,paddingHorizontal)}),[]);const initialLeftBoundVal=useMemo((()=>leftBound.get()),[]);const positionX=useSharedValue(initialLeftVal);const left=useSharedValue(initialLeftVal);const targetLeftBound=useSharedValue(initialLeftBoundVal);const calculatedContainerWidth=useRef(containerWidth).current;const rightBound=useDerivedValue((()=>leftBound.value+calculatedContainerWidth));useEffect((()=>{if(!children||!handleComponent){setHasHandle(false);return}const checkForHandle=child=>{if(React.isValidElement(child)){if(child.type===handleComponent){return true}if(child.props&&child.props.children){if(React.Children.toArray(child.props.children).some(checkForHandle)){return true}}}return false};setHasHandle(React.Children.toArray(children).some(checkForHandle))}),[children,handleComponent]);useAnimatedReaction((()=>positionX.value),((currentX,previousX)=>{if(currentX===null||!movingSV.value){return}if(previousX!==null&&currentX===previousX){return}const adjustedX=currentX-paddingHorizontal;const itemWithGapWidth=itemWidth+gap;const clampedPosition=Math.min(Math.max(0,Math.ceil(adjustedX/itemWithGapWidth)),itemsCount-1);let newOverItemId=null;for(const[itemIdIter,itemPosIter]of Object.entries(positions.value)){if(itemPosIter===clampedPosition&&itemIdIter!==id){newOverItemId=itemIdIter;break}}if(currentOverItemId.value!==newOverItemId){currentOverItemId.value=newOverItemId}if(onDragging){const now=Date.now();if(now-onDraggingLastCallTimestamp.value>THROTTLE_INTERVAL){runOnJS(onDragging)(id,newOverItemId,Math.round(currentX));onDraggingLastCallTimestamp.value=now}}left.value=currentX;setHorizontalPosition(currentX,itemsCount,positions,id,itemWidth,gap,paddingHorizontal);setHorizontalAutoScroll(currentX,leftBound.value,rightBound.value,itemWidth,autoScrollDirection)}),[movingSV,itemWidth,gap,paddingHorizontal,itemsCount,positions,id,onDragging,leftBound,rightBound,autoScrollDirection,currentOverItemId,left,onDraggingLastCallTimestamp]);useAnimatedReaction((()=>positions.value[id]),((currentPosition,previousPosition)=>{if(currentPosition!==null&&previousPosition!==null&&currentPosition!==previousPosition){if(!movingSV.value){const newLeft=getItemXPosition(currentPosition,itemWidth,gap,paddingHorizontal);left.value=withSpring(newLeft);if(onMove){runOnJS(onMove)(id,previousPosition,currentPosition)}}}}),[movingSV,itemWidth,gap,paddingHorizontal]);useAnimatedReaction((()=>autoScrollDirection.value),((scrollDirection,previousValue)=>{if(scrollDirection!==null&&previousValue!==null&&scrollDirection!==previousValue){switch(scrollDirection){case HorizontalScrollDirection.Left:{targetLeftBound.value=leftBound.value;targetLeftBound.value=withTiming(0,{duration:1500});break}case HorizontalScrollDirection.Right:{const contentWidth=getContentWidth(itemsCount,itemWidth,gap,paddingHorizontal);const maxScroll=Math.max(0,contentWidth-calculatedContainerWidth);targetLeftBound.value=leftBound.value;targetLeftBound.value=withTiming(maxScroll,{duration:1500});break}case HorizontalScrollDirection.None:{targetLeftBound.value=leftBound.value;break}}}}));useAnimatedReaction((()=>targetLeftBound.value),((targetLeftBoundValue,previousValue)=>{if(targetLeftBoundValue!==null&&previousValue!==null&&targetLeftBoundValue!==previousValue){if(movingSV.value){leftBound.value=targetLeftBoundValue}}}),[movingSV]);const initialItemContentX=useSharedValue(0);const initialFingerAbsoluteX=useSharedValue(0);const initialLeftBound=useSharedValue(0);const panGestureHandler=Gesture.Pan().activateAfterLongPress(200).onStart((event=>{"worklet";initialItemContentX.value=getItemXPosition(positions.value[id],itemWidth,gap,paddingHorizontal);initialFingerAbsoluteX.value=event.absoluteX;initialLeftBound.value=leftBound.value;positionX.value=initialItemContentX.value;movingSV.value=true;runOnJS(setIsMoving)(true);if(onDragStart){runOnJS(onDragStart)(id,positions.value[id])}})).onUpdate((event=>{"worklet";const fingerDxScreen=event.absoluteX-initialFingerAbsoluteX.value;const scrollDeltaSinceStart=leftBound.value-initialLeftBound.value;positionX.value=initialItemContentX.value+fingerDxScreen+scrollDeltaSinceStart})).onEnd((()=>{"worklet";const finishPosition=getItemXPosition(positions.value[id],itemWidth,gap,paddingHorizontal);left.value=withTiming(finishPosition);movingSV.value=false;runOnJS(setIsMoving)(false);if(onDrop){const positionsCopy={...positions.value};runOnJS(onDrop)(id,positions.value[id],positionsCopy)}currentOverItemId.value=null}));const animatedStyle=useAnimatedStyle((()=>{"worklet";return{position:"absolute",top:0,bottom:0,left:left.value,width:itemWidth,zIndex:movingSV.value?1:0,backgroundColor:"transparent",shadowColor:"black",shadowOpacity:withSpring(movingSV.value?.2:0),shadowRadius:10}}),[movingSV,itemWidth]);return{animatedStyle,panGestureHandler,isMoving,hasHandle}}
@@ -0,0 +1,4 @@
1
+ import { UseHorizontalSortableListOptions, UseHorizontalSortableListReturn } from "../types/sortable";
2
+ export declare function useHorizontalSortableList<TData extends {
3
+ id: string;
4
+ }>(options: UseHorizontalSortableListOptions<TData>): UseHorizontalSortableListReturn<TData>;
@@ -0,0 +1 @@
1
+ import{useRef,useCallback}from"react";import{scrollTo,useAnimatedReaction,useAnimatedRef,useAnimatedScrollHandler,useSharedValue}from"react-native-reanimated";import{listToObject,getContentWidth}from"../components/sortableUtils";import{HorizontalScrollDirection}from"../types/sortable";export function useHorizontalSortableList(options){const{data,itemWidth,gap=0,paddingHorizontal=0,itemKeyExtractor=item=>item.id}=options;if(__DEV__){data.forEach(((item,index)=>{const id=itemKeyExtractor(item,index);if(typeof id!=="string"||!id){console.error(`[react-native-reanimated-dnd] Item at index ${index} has invalid id: ${id}. `+`Each item must have a unique string id property.`)}}))}const positions=useSharedValue(listToObject(data));const scrollX=useSharedValue(0);const autoScroll=useSharedValue(HorizontalScrollDirection.None);const scrollViewRef=useAnimatedRef();const dropProviderRef=useRef(null);useAnimatedReaction((()=>scrollX.value),(scrolling=>{scrollTo(scrollViewRef,scrolling,0,false)}));const handleScroll=useAnimatedScrollHandler((event=>{scrollX.value=event.contentOffset.x}));const handleScrollEnd=useCallback((()=>{let localScrollTimeout=null;if(localScrollTimeout){clearTimeout(localScrollTimeout)}localScrollTimeout=setTimeout((()=>{var _a;(_a=dropProviderRef.current)===null||_a===void 0?void 0:_a.requestPositionUpdate()}),50)}),[]);const contentWidth=getContentWidth(data.length,itemWidth,gap,paddingHorizontal);const getItemProps=useCallback(((item,index)=>{const id=itemKeyExtractor(item,index);return{id,positions,leftBound:scrollX,autoScrollDirection:autoScroll,itemsCount:data.length,itemWidth,gap,paddingHorizontal}}),[data.length,itemWidth,gap,paddingHorizontal,itemKeyExtractor,positions,scrollX,autoScroll]);return{positions,scrollX,autoScroll,scrollViewRef,dropProviderRef,handleScroll,handleScrollEnd,contentWidth,getItemProps}}
@@ -0,0 +1,50 @@
1
+ import { StyleProp, ViewStyle } from "react-native";
2
+ import React from "react";
3
+ import { SharedValue } from "react-native-reanimated";
4
+ import { GestureType } from "react-native-gesture-handler";
5
+ export declare enum ScrollDirection {
6
+ None = "none",
7
+ Up = "up",
8
+ Down = "down"
9
+ }
10
+ export declare function clamp(value: number, lowerBound: number, upperBound: number): number;
11
+ export declare function objectMove(object: {
12
+ [id: string]: number;
13
+ }, from: number, to: number): {
14
+ [id: string]: number;
15
+ };
16
+ export declare function listToObject<T extends {
17
+ id: string;
18
+ }>(list: T[]): {
19
+ [id: string]: number;
20
+ };
21
+ export declare function setPosition(positionY: number, itemsCount: number, positions: SharedValue<{
22
+ [id: string]: number;
23
+ }>, id: string, itemHeight: number): void;
24
+ export declare function setAutoScroll(positionY: number, lowerBound: number, upperBound: number, scrollThreshold: number, autoScroll: SharedValue<ScrollDirection>): void;
25
+ export interface UseSortableOptions<T> {
26
+ id: string;
27
+ positions: SharedValue<{
28
+ [id: string]: number;
29
+ }>;
30
+ lowerBound: SharedValue<number>;
31
+ autoScrollDirection: SharedValue<ScrollDirection>;
32
+ itemsCount: number;
33
+ itemHeight: number;
34
+ containerHeight?: number;
35
+ onMove?: (id: string, from: number, to: number) => void;
36
+ onDragStart?: (id: string, position: number) => void;
37
+ onDrop?: (id: string, position: number, allPositions?: {
38
+ [id: string]: number;
39
+ }) => void;
40
+ onDragging?: (id: string, overItemId: string | null, yPosition: number) => void;
41
+ children?: React.ReactNode;
42
+ handleComponent?: React.ComponentType<any>;
43
+ }
44
+ export interface UseSortableReturn {
45
+ animatedStyle: StyleProp<ViewStyle>;
46
+ panGestureHandler: GestureType;
47
+ isMoving: boolean;
48
+ hasHandle: boolean;
49
+ }
50
+ export declare function useSortable<T>(options: UseSortableOptions<T>): UseSortableReturn;
@@ -0,0 +1 @@
1
+ import{useState,useRef,useEffect,useMemo}from"react";import React from"react";import{runOnJS,useAnimatedReaction,useAnimatedStyle,useDerivedValue,useSharedValue,withSpring,withTiming}from"react-native-reanimated";import{Gesture}from"react-native-gesture-handler";export var ScrollDirection;(function(ScrollDirection){ScrollDirection["None"]="none";ScrollDirection["Up"]="up";ScrollDirection["Down"]="down"})(ScrollDirection||(ScrollDirection={}));export function clamp(value,lowerBound,upperBound){"worklet";return Math.max(lowerBound,Math.min(value,upperBound))}export function objectMove(object,from,to){"worklet";const newObject=Object.assign({},object);const movedUp=to<from;for(const id in object){if(object[id]===from){newObject[id]=to;continue}const currentPosition=object[id];if(movedUp&&currentPosition>=to&&currentPosition<from){newObject[id]++}else if(currentPosition<=to&&currentPosition>from){newObject[id]--}}return newObject}export function listToObject(list){const values=Object.values(list);const object={};for(let i=0;i<values.length;i++){object[values[i].id]=i}return object}export function setPosition(positionY,itemsCount,positions,id,itemHeight){"worklet";const newPosition=clamp(Math.floor(positionY/itemHeight),0,itemsCount-1);if(newPosition!==positions.value[id]){positions.value=objectMove(positions.value,positions.value[id],newPosition)}}export function setAutoScroll(positionY,lowerBound,upperBound,scrollThreshold,autoScroll){"worklet";if(positionY<=lowerBound+scrollThreshold){autoScroll.value=ScrollDirection.Up}else if(positionY>=upperBound-scrollThreshold){autoScroll.value=ScrollDirection.Down}else{autoScroll.value=ScrollDirection.None}}export function useSortable(options){const{id,positions,lowerBound,autoScrollDirection,itemsCount,itemHeight,containerHeight=500,onMove,onDragStart,onDrop,onDragging,children,handleComponent}=options;const[isMoving,setIsMoving]=useState(false);const[hasHandle,setHasHandle]=useState(false);const movingSV=useSharedValue(false);const currentOverItemId=useSharedValue(null);const onDraggingLastCallTimestamp=useSharedValue(0);const THROTTLE_INTERVAL=50;const initialTopVal=useMemo((()=>{const posArr=positions.get();const pos=posArr===null||posArr===void 0?void 0:posArr[id];return pos*itemHeight}),[]);const initialLowerBoundVal=useMemo((()=>lowerBound.get()),[]);const positionY=useSharedValue(initialTopVal);const top=useSharedValue(initialTopVal);const targetLowerBound=useSharedValue(initialLowerBoundVal);const calculatedContainerHeight=useRef(containerHeight).current;const upperBound=useDerivedValue((()=>lowerBound.value+calculatedContainerHeight));useEffect((()=>{if(!children||!handleComponent){setHasHandle(false);return}const checkForHandle=child=>{if(React.isValidElement(child)){if(child.type===handleComponent){return true}const props=child.props;if(child.props&&props.children){if(React.Children.toArray(props.children).some(checkForHandle)){return true}}}return false};setHasHandle(React.Children.toArray(children).some(checkForHandle))}),[children,handleComponent]);useAnimatedReaction((()=>positionY.value),((currentY,previousY)=>{if(currentY===null||!movingSV.value){return}if(previousY!==null&&currentY===previousY){return}const clampedPosition=Math.min(Math.max(0,Math.ceil(currentY/itemHeight)),itemsCount-1);let newOverItemId=null;for(const[itemIdIter,itemPosIter]of Object.entries(positions.value)){if(itemPosIter===clampedPosition&&itemIdIter!==id){newOverItemId=itemIdIter;break}}if(currentOverItemId.value!==newOverItemId){currentOverItemId.value=newOverItemId}if(onDragging){const now=Date.now();if(now-onDraggingLastCallTimestamp.value>THROTTLE_INTERVAL){runOnJS(onDragging)(id,newOverItemId,Math.round(currentY));onDraggingLastCallTimestamp.value=now}}top.value=currentY;setPosition(currentY,itemsCount,positions,id,itemHeight);setAutoScroll(currentY,lowerBound.value,upperBound.value,itemHeight,autoScrollDirection)}),[movingSV,itemHeight,itemsCount,positions,id,onDragging,lowerBound,upperBound,autoScrollDirection,currentOverItemId,top,onDraggingLastCallTimestamp]);useAnimatedReaction((()=>positions.value[id]),((currentPosition,previousPosition)=>{if(currentPosition!==null&&previousPosition!==null&&currentPosition!==previousPosition){if(!movingSV.value){top.value=withSpring(currentPosition*itemHeight);if(onMove){runOnJS(onMove)(id,previousPosition,currentPosition)}}}}),[movingSV]);useAnimatedReaction((()=>autoScrollDirection.value),((scrollDirection,previousValue)=>{if(scrollDirection!==null&&previousValue!==null&&scrollDirection!==previousValue){switch(scrollDirection){case ScrollDirection.Up:{targetLowerBound.value=lowerBound.value;targetLowerBound.value=withTiming(0,{duration:1500});break}case ScrollDirection.Down:{const contentHeight=itemsCount*itemHeight;const maxScroll=contentHeight-calculatedContainerHeight;targetLowerBound.value=lowerBound.value;targetLowerBound.value=withTiming(maxScroll,{duration:1500});break}case ScrollDirection.None:{targetLowerBound.value=lowerBound.value;break}}}}));useAnimatedReaction((()=>targetLowerBound.value),((targetLowerBoundValue,previousValue)=>{if(targetLowerBoundValue!==null&&previousValue!==null&&targetLowerBoundValue!==previousValue){if(movingSV.value){lowerBound.value=targetLowerBoundValue}}}),[movingSV]);const initialItemContentY=useSharedValue(0);const initialFingerAbsoluteY=useSharedValue(0);const initialLowerBound=useSharedValue(0);const panGestureHandler=Gesture.Pan().activateAfterLongPress(200).onStart((event=>{"worklet";initialItemContentY.value=positions.value[id]*itemHeight;initialFingerAbsoluteY.value=event.absoluteY;initialLowerBound.value=lowerBound.value;positionY.value=initialItemContentY.value;movingSV.value=true;runOnJS(setIsMoving)(true);if(onDragStart){runOnJS(onDragStart)(id,positions.value[id])}})).onUpdate((event=>{"worklet";const fingerDyScreen=event.absoluteY-initialFingerAbsoluteY.value;const scrollDeltaSinceStart=lowerBound.value-initialLowerBound.value;positionY.value=initialItemContentY.value+fingerDyScreen+scrollDeltaSinceStart})).onEnd((()=>{"worklet";const finishPosition=positions.value[id]*itemHeight;top.value=withTiming(finishPosition);movingSV.value=false;runOnJS(setIsMoving)(false);if(onDrop){const positionsCopy={...positions.value};runOnJS(onDrop)(id,positions.value[id],positionsCopy)}currentOverItemId.value=null}));const animatedStyle=useAnimatedStyle((()=>{"worklet";return{position:"absolute",left:0,right:0,top:top.value,zIndex:movingSV.value?1:0,backgroundColor:"#000000",shadowColor:"black",shadowOpacity:withSpring(movingSV.value?.2:0),shadowRadius:10}}),[movingSV]);return{animatedStyle,panGestureHandler,isMoving,hasHandle}}
@@ -0,0 +1,27 @@
1
+ import { DropProviderRef } from "../types/context";
2
+ export interface UseSortableListOptions<TData> {
3
+ data: TData[];
4
+ itemHeight: number;
5
+ itemKeyExtractor?: (item: TData, index: number) => string;
6
+ }
7
+ export interface UseSortableListReturn<TData> {
8
+ positions: any;
9
+ scrollY: any;
10
+ autoScroll: any;
11
+ scrollViewRef: any;
12
+ dropProviderRef: React.RefObject<DropProviderRef>;
13
+ handleScroll: any;
14
+ handleScrollEnd: () => void;
15
+ contentHeight: number;
16
+ getItemProps: (item: TData, index: number) => {
17
+ id: string;
18
+ positions: any;
19
+ lowerBound: any;
20
+ autoScrollDirection: any;
21
+ itemsCount: number;
22
+ itemHeight: number;
23
+ };
24
+ }
25
+ export declare function useSortableList<TData extends {
26
+ id: string;
27
+ }>(options: UseSortableListOptions<TData>): UseSortableListReturn<TData>;
@@ -0,0 +1 @@
1
+ import{useRef,useCallback}from"react";import{scrollTo,useAnimatedReaction,useAnimatedRef,useAnimatedScrollHandler,useSharedValue}from"react-native-reanimated";import{listToObject}from"../components/sortableUtils";import{ScrollDirection}from"../types/sortable";export function useSortableList(options){const{data,itemHeight,itemKeyExtractor=item=>item.id}=options;if(__DEV__){data.forEach(((item,index)=>{const id=itemKeyExtractor(item,index);if(typeof id!=="string"||!id){console.error(`[react-native-reanimated-dnd] Item at index ${index} has invalid id: ${id}. `+`Each item must have a unique string id property.`)}}))}const positions=useSharedValue(listToObject(data));const scrollY=useSharedValue(0);const autoScroll=useSharedValue(ScrollDirection.None);const scrollViewRef=useAnimatedRef();const dropProviderRef=useRef(null);useAnimatedReaction((()=>scrollY.value),(scrolling=>{scrollTo(scrollViewRef,0,scrolling,false)}));const handleScroll=useAnimatedScrollHandler((event=>{scrollY.value=event.contentOffset.y}));const handleScrollEnd=useCallback((()=>{let localScrollTimeout=null;if(localScrollTimeout){clearTimeout(localScrollTimeout)}localScrollTimeout=setTimeout((()=>{var _a;(_a=dropProviderRef.current)===null||_a===void 0?void 0:_a.requestPositionUpdate()}),50)}),[]);const contentHeight=data.length*itemHeight;const getItemProps=useCallback(((item,index)=>{const id=itemKeyExtractor(item,index);return{id,positions,lowerBound:scrollY,autoScrollDirection:autoScroll,itemsCount:data.length,itemHeight}}),[data.length,itemHeight,itemKeyExtractor,positions,scrollY,autoScroll]);return{positions,scrollY,autoScroll,scrollViewRef,dropProviderRef,handleScroll,handleScrollEnd,contentHeight,getItemProps}}
package/lib/index.d.ts ADDED
@@ -0,0 +1,13 @@
1
+ export { Draggable } from "./components/Draggable";
2
+ export { Droppable } from "./components/Droppable";
3
+ export { Sortable } from "./components/Sortable";
4
+ export { SortableItem } from "./components/SortableItem";
5
+ export { DropProvider } from "./context/DropContext";
6
+ export * from "./types";
7
+ export { listToObject, setAutoScroll, setPosition, clamp, objectMove, ScrollDirection, } from "./components/sortableUtils";
8
+ export { useDraggable } from "./hooks/useDraggable";
9
+ export { useDroppable } from "./hooks/useDroppable";
10
+ export { useSortable } from "./hooks/useSortable";
11
+ export { useSortableList } from "./hooks/useSortableList";
12
+ export { useHorizontalSortable } from "./hooks/useHorizontalSortable";
13
+ export { useHorizontalSortableList } from "./hooks/useHorizontalSortableList";
package/lib/index.js ADDED
@@ -0,0 +1 @@
1
+ export{Draggable}from"./components/Draggable";export{Droppable}from"./components/Droppable";export{Sortable}from"./components/Sortable";export{SortableItem}from"./components/SortableItem";export{DropProvider}from"./context/DropContext";export*from"./types";export{listToObject,setAutoScroll,setPosition,clamp,objectMove,ScrollDirection}from"./components/sortableUtils";export{useDraggable}from"./hooks/useDraggable";export{useDroppable}from"./hooks/useDroppable";export{useSortable}from"./hooks/useSortable";export{useSortableList}from"./hooks/useSortableList";export{useHorizontalSortable}from"./hooks/useHorizontalSortable";export{useHorizontalSortableList}from"./hooks/useHorizontalSortableList";
@@ -0,0 +1,67 @@
1
+ import { ReactNode } from "react";
2
+ export type DropAlignment = "center" | "top-left" | "top-center" | "top-right" | "center-left" | "center-right" | "bottom-left" | "bottom-center" | "bottom-right";
3
+ export interface DropOffset {
4
+ x: number;
5
+ y: number;
6
+ }
7
+ export interface DroppedItemsMap<TData = unknown> {
8
+ [draggableId: string]: {
9
+ droppableId: string;
10
+ data: TData;
11
+ };
12
+ }
13
+ export interface DropSlot<TData = unknown> {
14
+ id: string;
15
+ x: number;
16
+ y: number;
17
+ width: number;
18
+ height: number;
19
+ onDrop: (data: TData) => void;
20
+ dropAlignment?: DropAlignment;
21
+ dropOffset?: DropOffset;
22
+ capacity?: number;
23
+ }
24
+ export type PositionUpdateListener = () => void;
25
+ export interface SlotsContextValue<TData = unknown> {
26
+ register: (id: number, slot: DropSlot<TData>) => void;
27
+ unregister: (id: number) => void;
28
+ getSlots: () => Record<number, DropSlot<TData>>;
29
+ isRegistered: (id: number) => boolean;
30
+ setActiveHoverSlot: (id: number | null) => void;
31
+ activeHoverSlotId: number | null;
32
+ registerPositionUpdateListener: (id: string, listener: PositionUpdateListener) => void;
33
+ unregisterPositionUpdateListener: (id: string) => void;
34
+ requestPositionUpdate: () => void;
35
+ registerDroppedItem: (draggableId: string, droppableId: string, itemData: any) => void;
36
+ unregisterDroppedItem: (draggableId: string) => void;
37
+ getDroppedItems: () => DroppedItemsMap<any>;
38
+ hasAvailableCapacity: (droppableId: string) => boolean;
39
+ onDragging?: (payload: {
40
+ x: number;
41
+ y: number;
42
+ tx: number;
43
+ ty: number;
44
+ itemData: any;
45
+ }) => void;
46
+ onDragStart?: (data: any) => void;
47
+ onDragEnd?: (data: any) => void;
48
+ }
49
+ export declare const SlotsContext: import("react").Context<SlotsContextValue<any>>;
50
+ export interface DropProviderProps {
51
+ children: ReactNode;
52
+ onLayoutUpdateComplete?: () => void;
53
+ onDroppedItemsUpdate?: (droppedItems: DroppedItemsMap) => void;
54
+ onDragging?: (payload: {
55
+ x: number;
56
+ y: number;
57
+ tx: number;
58
+ ty: number;
59
+ itemData: any;
60
+ }) => void;
61
+ onDragStart?: (data: any) => void;
62
+ onDragEnd?: (data: any) => void;
63
+ }
64
+ export interface DropProviderRef {
65
+ requestPositionUpdate: () => void;
66
+ getDroppedItems: () => DroppedItemsMap;
67
+ }
@@ -0,0 +1 @@
1
+ import{createContext}from"react";const defaultSlotsContextValue={register:(_id,_slot)=>{if(process.env.NODE_ENV!=="production"){console.warn("SlotsContext: register called without a Provider.")}},unregister:_id=>{if(process.env.NODE_ENV!=="production"){console.warn("SlotsContext: unregister called without a Provider.")}},getSlots:()=>{if(process.env.NODE_ENV!=="production"){console.warn("SlotsContext: getSlots called without a Provider.")}return{}},isRegistered:_id=>{if(process.env.NODE_ENV!=="production"){console.warn("SlotsContext: isRegistered called without a Provider.")}return false},setActiveHoverSlot:_id=>{if(process.env.NODE_ENV!=="production"){console.warn("SlotsContext: setActiveHoverSlot called without a Provider.")}},activeHoverSlotId:null,registerPositionUpdateListener:(_id,_listener)=>{if(process.env.NODE_ENV!=="production"){console.warn("SlotsContext: registerPositionUpdateListener called without a Provider.")}},unregisterPositionUpdateListener:_id=>{if(process.env.NODE_ENV!=="production"){console.warn("SlotsContext: unregisterPositionUpdateListener called without a Provider.")}},requestPositionUpdate:()=>{if(process.env.NODE_ENV!=="production"){console.warn("SlotsContext: requestPositionUpdate called without a Provider (internally).")}},registerDroppedItem:(_draggableId,_droppableId,_itemData)=>{if(process.env.NODE_ENV!=="production"){console.warn("SlotsContext: registerDroppedItem called without a Provider.")}},unregisterDroppedItem:_draggableId=>{if(process.env.NODE_ENV!=="production"){console.warn("SlotsContext: unregisterDroppedItem called without a Provider.")}},getDroppedItems:()=>{if(process.env.NODE_ENV!=="production"){console.warn("SlotsContext: getDroppedItems called without a Provider.")}return{}},hasAvailableCapacity:_droppableId=>{if(process.env.NODE_ENV!=="production"){console.warn("SlotsContext: hasAvailableCapacity called without a Provider.")}return false},onDragging:payload=>{if(process.env.NODE_ENV!=="production"){console.warn("SlotsContext: onDragging called without a Provider.")}},onDragStart:undefined,onDragEnd:undefined};export const SlotsContext=createContext(defaultSlotsContextValue);
@@ -0,0 +1,54 @@
1
+ import { ViewStyle, View, StyleProp } from "react-native";
2
+ import Animated, { AnimatedStyle, useAnimatedRef } from "react-native-reanimated";
3
+ import { GestureType } from "react-native-gesture-handler";
4
+ import { LayoutChangeEvent } from "react-native";
5
+ export declare enum DraggableState {
6
+ IDLE = "IDLE",
7
+ DRAGGING = "DRAGGING",
8
+ DROPPED = "DROPPED"
9
+ }
10
+ export type AnimationFunction = (toValue: number) => number;
11
+ export type CollisionAlgorithm = "center" | "intersect" | "contain";
12
+ export interface UseDraggableOptions<TData = unknown> {
13
+ data: TData;
14
+ draggableId?: string;
15
+ dragDisabled?: boolean;
16
+ preDragDelay?: number;
17
+ onDragStart?: (data: TData) => void;
18
+ onDragEnd?: (data: TData) => void;
19
+ onDragging?: (payload: {
20
+ x: number;
21
+ y: number;
22
+ tx: number;
23
+ ty: number;
24
+ itemData: TData;
25
+ }) => void;
26
+ onStateChange?: (state: DraggableState) => void;
27
+ animationFunction?: AnimationFunction;
28
+ dragBoundsRef?: React.RefObject<Animated.View | View>;
29
+ dragAxis?: "x" | "y" | "both";
30
+ collisionAlgorithm?: CollisionAlgorithm;
31
+ }
32
+ export interface UseDraggableReturn {
33
+ animatedViewProps: {
34
+ style: AnimatedStyle<ViewStyle>;
35
+ onLayout: (event: LayoutChangeEvent) => void;
36
+ };
37
+ gesture: GestureType;
38
+ state: DraggableState;
39
+ animatedViewRef: ReturnType<typeof useAnimatedRef<Animated.View>>;
40
+ hasHandle: boolean;
41
+ }
42
+ export interface DraggableContextValue {
43
+ gesture: any;
44
+ state: DraggableState;
45
+ }
46
+ export interface DraggableProps<TData = unknown> extends UseDraggableOptions<TData> {
47
+ style?: StyleProp<ViewStyle>;
48
+ children: React.ReactNode;
49
+ onStateChange?: (state: DraggableState) => void;
50
+ }
51
+ export interface DraggableHandleProps {
52
+ children: React.ReactNode;
53
+ style?: StyleProp<ViewStyle>;
54
+ }
@@ -0,0 +1 @@
1
+ export var DraggableState;(function(DraggableState){DraggableState["IDLE"]="IDLE";DraggableState["DRAGGING"]="DRAGGING";DraggableState["DROPPED"]="DROPPED"})(DraggableState||(DraggableState={}));
@@ -0,0 +1,26 @@
1
+ import { LayoutChangeEvent, StyleProp, ViewStyle } from "react-native";
2
+ import Animated, { useAnimatedRef } from "react-native-reanimated";
3
+ import { DropAlignment, DropOffset } from "./context";
4
+ export interface UseDroppableOptions<TData = unknown> {
5
+ onDrop: (data: TData) => void;
6
+ dropDisabled?: boolean;
7
+ onActiveChange?: (isActive: boolean) => void;
8
+ dropAlignment?: DropAlignment;
9
+ dropOffset?: DropOffset;
10
+ activeStyle?: StyleProp<ViewStyle>;
11
+ droppableId?: string;
12
+ capacity?: number;
13
+ }
14
+ export interface UseDroppableReturn {
15
+ viewProps: {
16
+ onLayout: (event: LayoutChangeEvent) => void;
17
+ style?: StyleProp<ViewStyle>;
18
+ };
19
+ isActive: boolean;
20
+ activeStyle?: StyleProp<ViewStyle>;
21
+ animatedViewRef: ReturnType<typeof useAnimatedRef<Animated.View>>;
22
+ }
23
+ export interface DroppableProps<TData = unknown> extends UseDroppableOptions<TData> {
24
+ style?: StyleProp<ViewStyle>;
25
+ children: React.ReactNode;
26
+ }
@@ -0,0 +1 @@
1
+ export{};
@@ -0,0 +1,4 @@
1
+ export * from "./draggable";
2
+ export * from "./droppable";
3
+ export * from "./sortable";
4
+ export * from "./context";
@@ -0,0 +1 @@
1
+ export*from"./draggable";export*from"./droppable";export*from"./sortable";export*from"./context";
@@ -0,0 +1,233 @@
1
+ import { StyleProp, ViewStyle } from "react-native";
2
+ import { SharedValue } from "react-native-reanimated";
3
+ import { DropProviderRef } from "../types/context";
4
+ import { ReactNode } from "react";
5
+ export interface SortableData {
6
+ id: string;
7
+ }
8
+ export declare enum ScrollDirection {
9
+ None = "none",
10
+ Up = "up",
11
+ Down = "down"
12
+ }
13
+ export declare enum HorizontalScrollDirection {
14
+ None = "none",
15
+ Left = "left",
16
+ Right = "right"
17
+ }
18
+ export declare enum SortableDirection {
19
+ Vertical = "vertical",
20
+ Horizontal = "horizontal"
21
+ }
22
+ export interface UseSortableOptions<T> {
23
+ id: string;
24
+ positions: SharedValue<{
25
+ [id: string]: number;
26
+ }>;
27
+ lowerBound: SharedValue<number>;
28
+ autoScrollDirection: SharedValue<ScrollDirection>;
29
+ itemsCount: number;
30
+ itemHeight: number;
31
+ containerHeight?: number;
32
+ onMove?: (id: string, from: number, to: number) => void;
33
+ onDragStart?: (id: string, position: number) => void;
34
+ onDrop?: (id: string, position: number, allPositions?: {
35
+ [id: string]: number;
36
+ }) => void;
37
+ onDragging?: (id: string, overItemId: string | null, yPosition: number) => void;
38
+ }
39
+ export interface UseSortableReturn {
40
+ animatedStyle: StyleProp<ViewStyle>;
41
+ panGestureHandler: any;
42
+ isMoving: boolean;
43
+ hasHandle: boolean;
44
+ }
45
+ export interface UseSortableListOptions<TData extends SortableData> {
46
+ data: TData[];
47
+ itemHeight: number;
48
+ itemKeyExtractor?: (item: TData, index: number) => string;
49
+ }
50
+ export interface UseSortableListReturn<TData extends SortableData> {
51
+ positions: any;
52
+ scrollY: any;
53
+ autoScroll: any;
54
+ scrollViewRef: any;
55
+ dropProviderRef: React.RefObject<DropProviderRef>;
56
+ handleScroll: any;
57
+ handleScrollEnd: () => void;
58
+ contentHeight: number;
59
+ getItemProps: (item: TData, index: number) => {
60
+ id: string;
61
+ positions: any;
62
+ lowerBound: any;
63
+ autoScrollDirection: any;
64
+ itemsCount: number;
65
+ itemHeight: number;
66
+ };
67
+ }
68
+ export interface SortableItemProps<T> {
69
+ id: string;
70
+ data: T;
71
+ positions: SharedValue<{
72
+ [id: string]: number;
73
+ }>;
74
+ lowerBound?: SharedValue<number>;
75
+ leftBound?: SharedValue<number>;
76
+ autoScrollDirection?: SharedValue<ScrollDirection>;
77
+ autoScrollHorizontalDirection?: SharedValue<HorizontalScrollDirection>;
78
+ itemsCount: number;
79
+ direction?: SortableDirection;
80
+ itemHeight?: number;
81
+ itemWidth?: number;
82
+ gap?: number;
83
+ paddingHorizontal?: number;
84
+ containerHeight?: number;
85
+ containerWidth?: number;
86
+ children: ReactNode;
87
+ style?: StyleProp<ViewStyle>;
88
+ animatedStyle?: StyleProp<ViewStyle>;
89
+ onMove?: (id: string, from: number, to: number) => void;
90
+ onDragStart?: (id: string, position: number) => void;
91
+ onDrop?: (id: string, position: number, allPositions?: {
92
+ [id: string]: number;
93
+ }) => void;
94
+ onDragging?: (id: string, overItemId: string | null, yPosition: number) => void;
95
+ onDraggingHorizontal?: (id: string, overItemId: string | null, xPosition: number) => void;
96
+ }
97
+ export interface SortableProps<TData extends SortableData> {
98
+ data: TData[];
99
+ renderItem: (props: SortableRenderItemProps<TData>) => ReactNode;
100
+ direction?: SortableDirection;
101
+ itemHeight?: number;
102
+ itemWidth?: number;
103
+ gap?: number;
104
+ paddingHorizontal?: number;
105
+ style?: StyleProp<ViewStyle>;
106
+ contentContainerStyle?: StyleProp<ViewStyle>;
107
+ itemKeyExtractor?: (item: TData, index: number) => string;
108
+ useFlatList?: boolean;
109
+ }
110
+ export interface SortableRenderItemProps<TData extends SortableData> {
111
+ item: TData;
112
+ index: number;
113
+ id: string;
114
+ positions: SharedValue<{
115
+ [id: string]: number;
116
+ }>;
117
+ direction?: SortableDirection;
118
+ lowerBound?: SharedValue<number>;
119
+ leftBound?: SharedValue<number>;
120
+ autoScrollDirection?: SharedValue<ScrollDirection>;
121
+ autoScrollHorizontalDirection?: SharedValue<HorizontalScrollDirection>;
122
+ itemsCount: number;
123
+ itemHeight?: number;
124
+ itemWidth?: number;
125
+ gap?: number;
126
+ paddingHorizontal?: number;
127
+ }
128
+ export interface SortableContextValue {
129
+ panGestureHandler: any;
130
+ }
131
+ export interface SortableHandleProps {
132
+ children: React.ReactNode;
133
+ style?: StyleProp<ViewStyle>;
134
+ }
135
+ export interface UseHorizontalSortableOptions<T> {
136
+ id: string;
137
+ positions: SharedValue<{
138
+ [id: string]: number;
139
+ }>;
140
+ leftBound: SharedValue<number>;
141
+ autoScrollDirection: SharedValue<HorizontalScrollDirection>;
142
+ itemsCount: number;
143
+ itemWidth: number;
144
+ gap?: number;
145
+ paddingHorizontal?: number;
146
+ containerWidth?: number;
147
+ onMove?: (id: string, from: number, to: number) => void;
148
+ onDragStart?: (id: string, position: number) => void;
149
+ onDrop?: (id: string, position: number, allPositions?: {
150
+ [id: string]: number;
151
+ }) => void;
152
+ onDragging?: (id: string, overItemId: string | null, xPosition: number) => void;
153
+ }
154
+ export interface UseHorizontalSortableReturn {
155
+ animatedStyle: StyleProp<ViewStyle>;
156
+ panGestureHandler: any;
157
+ isMoving: boolean;
158
+ hasHandle: boolean;
159
+ }
160
+ export interface UseHorizontalSortableListOptions<TData extends SortableData> {
161
+ data: TData[];
162
+ itemWidth: number;
163
+ gap?: number;
164
+ paddingHorizontal?: number;
165
+ itemKeyExtractor?: (item: TData, index: number) => string;
166
+ }
167
+ export interface UseHorizontalSortableListReturn<TData extends SortableData> {
168
+ positions: any;
169
+ scrollX: any;
170
+ autoScroll: any;
171
+ scrollViewRef: any;
172
+ dropProviderRef: React.RefObject<DropProviderRef>;
173
+ handleScroll: any;
174
+ handleScrollEnd: () => void;
175
+ contentWidth: number;
176
+ getItemProps: (item: TData, index: number) => {
177
+ id: string;
178
+ positions: any;
179
+ leftBound: any;
180
+ autoScrollDirection: any;
181
+ itemsCount: number;
182
+ itemWidth: number;
183
+ gap: number;
184
+ paddingHorizontal: number;
185
+ };
186
+ }
187
+ export interface HorizontalSortableItemProps<T> {
188
+ id: string;
189
+ data: T;
190
+ positions: SharedValue<{
191
+ [id: string]: number;
192
+ }>;
193
+ leftBound: SharedValue<number>;
194
+ autoScrollDirection: SharedValue<HorizontalScrollDirection>;
195
+ itemsCount: number;
196
+ itemWidth: number;
197
+ gap?: number;
198
+ paddingHorizontal?: number;
199
+ containerWidth?: number;
200
+ children: ReactNode;
201
+ style?: StyleProp<ViewStyle>;
202
+ animatedStyle?: StyleProp<ViewStyle>;
203
+ onMove?: (id: string, from: number, to: number) => void;
204
+ onDragStart?: (id: string, position: number) => void;
205
+ onDrop?: (id: string, position: number, allPositions?: {
206
+ [id: string]: number;
207
+ }) => void;
208
+ onDragging?: (id: string, overItemId: string | null, xPosition: number) => void;
209
+ }
210
+ export interface HorizontalSortableProps<TData extends SortableData> {
211
+ data: TData[];
212
+ renderItem: (props: HorizontalSortableRenderItemProps<TData>) => ReactNode;
213
+ itemWidth: number;
214
+ gap?: number;
215
+ paddingHorizontal?: number;
216
+ style?: StyleProp<ViewStyle>;
217
+ contentContainerStyle?: StyleProp<ViewStyle>;
218
+ itemKeyExtractor?: (item: TData, index: number) => string;
219
+ }
220
+ export interface HorizontalSortableRenderItemProps<TData extends SortableData> {
221
+ item: TData;
222
+ index: number;
223
+ id: string;
224
+ positions: SharedValue<{
225
+ [id: string]: number;
226
+ }>;
227
+ leftBound: SharedValue<number>;
228
+ autoScrollDirection: SharedValue<HorizontalScrollDirection>;
229
+ itemsCount: number;
230
+ itemWidth: number;
231
+ gap: number;
232
+ paddingHorizontal: number;
233
+ }
@@ -0,0 +1 @@
1
+ export var ScrollDirection;(function(ScrollDirection){ScrollDirection["None"]="none";ScrollDirection["Up"]="up";ScrollDirection["Down"]="down"})(ScrollDirection||(ScrollDirection={}));export var HorizontalScrollDirection;(function(HorizontalScrollDirection){HorizontalScrollDirection["None"]="none";HorizontalScrollDirection["Left"]="left";HorizontalScrollDirection["Right"]="right"})(HorizontalScrollDirection||(HorizontalScrollDirection={}));export var SortableDirection;(function(SortableDirection){SortableDirection["Vertical"]="vertical";SortableDirection["Horizontal"]="horizontal"})(SortableDirection||(SortableDirection={}));