swipeon-react 0.0.1-alpha.2 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -1,2 +1,2 @@
1
- var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},s=(n,r,a)=>(a=n==null?{}:e(i(n)),o(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n));let c=require(`react`);c=s(c);const l=(e,t)=>t===0?0:Math.abs(e/t),u=(e,t,n=50)=>{let r=Math.abs(e),i=Math.abs(t);return r<n&&i<n?null:r>i?e>0?`right`:`left`:t>0?`down`:`up`},d=(e,t=15,n=300)=>{let r=e/n*t;return Math.max(-t,Math.min(t,r))},f=(e,t,n=100)=>{let r=1-Math.sqrt(e*e+t*t)/(n*3);return Math.max(.5,Math.min(1,r))},p=e=>`touches`in e&&e.touches&&e.touches.length>0?{x:e.touches[0].clientX,y:e.touches[0].clientY}:`clientX`in e&&`clientY`in e?{x:e.clientX,y:e.clientY}:{x:0,y:0},m={threshold:100,velocityThreshold:.5,maxRotation:15,exitDuration:300,returnDuration:200,enableRotation:!0},h=(e={},t={})=>{let{threshold:n,velocityThreshold:r,maxRotation:i,exitDuration:a,returnDuration:o,enableRotation:s}={...m,...t},h=(0,c.useRef)(null),g=(0,c.useRef)(null),[_,v]=(0,c.useState)({isDragging:!1,startX:0,startY:0,currentX:0,currentY:0,deltaX:0,deltaY:0,velocity:0,direction:null,startTime:0}),[y,b]=(0,c.useState)(`translate3d(0px, 0px, 0px) rotate(0deg)`),[x,S]=(0,c.useState)(1),[C,w]=(0,c.useState)(`none`),T=(0,c.useCallback)(t=>{let n=p(t),r=Date.now();v({isDragging:!0,startX:n.x,startY:n.y,currentX:n.x,currentY:n.y,deltaX:0,deltaY:0,velocity:0,direction:null,startTime:r}),w(`none`),e.onSwipeStart&&e.onSwipeStart()},[e]),E=(0,c.useCallback)(e=>{if(!_.isDragging)return;let t=p(e),r=t.x-_.startX,a=t.y-_.startY;v(e=>({...e,currentX:t.x,currentY:t.y,deltaX:r,deltaY:a})),g.current&&cancelAnimationFrame(g.current),g.current=requestAnimationFrame(()=>{let e=s?d(r,i):0,t=f(r,a,n);b(`translate3d(${r}px, ${a}px, 0px) rotate(${e}deg)`),S(t)})},[_.isDragging,_.startX,_.startY,s,i,n]),D=(0,c.useCallback)(()=>{if(!_.isDragging)return;let{deltaX:t,deltaY:c,startTime:f}=_,p=Date.now()-f,m=Math.sqrt(t*t+c*c),h=l(m,p),g=u(t,c,n/2);if((m>=n||h>=r)&&g){w(`transform ${a}ms ease-out, opacity ${a}ms ease-out`);let n=t*2;b(`translate3d(${n}px, ${c*2}px, 0px) rotate(${s?d(n,i*2):0}deg)`),S(0),setTimeout(()=>{switch(g){case`left`:e.onSwipeLeft?.();break;case`right`:e.onSwipeRight?.();break;case`up`:e.onSwipeUp?.();break;case`down`:e.onSwipeDown?.();break}e.onSwipeEnd&&e.onSwipeEnd(),setTimeout(()=>{w(`none`),b(`translate3d(0px, 0px, 0px) rotate(0deg)`),S(1)},50)},a)}else w(`transform ${o}ms cubic-bezier(0.175, 0.885, 0.32, 1.275), opacity ${o}ms ease-out`),b(`translate3d(0px, 0px, 0px) rotate(0deg)`),S(1),e.onSwipeEnd&&e.onSwipeEnd();v(e=>({...e,isDragging:!1}))},[_,n,r,a,o,s,i,e]);return(0,c.useEffect)(()=>{let e=h.current;if(!e)return;let t=e=>{T(e)},n=e=>{_.isDragging&&E(e)},r=()=>{D()};return e.addEventListener(`pointerdown`,t),_.isDragging&&(document.addEventListener(`pointermove`,n),document.addEventListener(`pointerup`,r),document.addEventListener(`pointercancel`,r)),()=>{e.removeEventListener(`pointerdown`,t),document.removeEventListener(`pointermove`,n),document.removeEventListener(`pointerup`,r),document.removeEventListener(`pointercancel`,r),g.current&&cancelAnimationFrame(g.current)}},[_.isDragging,T,E,D]),{ref:h,transform:y,opacity:x,transition:C,isDragging:_.isDragging}},g=({children:e,className:t=``,style:n={},onSwipeLeft:r,onSwipeRight:i,onSwipeUp:a,onSwipeDown:o,onSwipeStart:s,onSwipeEnd:l,threshold:u=100,velocityThreshold:d=.5,maxRotation:f=15,exitDuration:p=300,returnDuration:m=200,enableRotation:g=!0})=>{let{ref:_,transform:v,opacity:y,transition:b,isDragging:x}=h({onSwipeLeft:r,onSwipeRight:i,onSwipeUp:a,onSwipeDown:o,onSwipeStart:s,onSwipeEnd:l},{threshold:u,velocityThreshold:d,maxRotation:f,exitDuration:p,returnDuration:m,enableRotation:g}),S={...n,transform:v,opacity:y,transition:b,touchAction:`none`,userSelect:`none`,WebkitUserSelect:`none`,cursor:x?`grabbing`:`grab`,willChange:x?`transform, opacity`:`auto`,position:`relative`};return c.default.createElement(`div`,{ref:_,className:`swipeon-card ${t}`,style:S},e)};exports.SwipeCard=g,exports.useSwipe=h;
1
+ var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},s=(n,r,a)=>(a=n==null?{}:e(i(n)),o(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n));let c=require(`react`);c=s(c);const l=(e,t)=>t===0?0:Math.abs(e/t),u=(e,t,n=50)=>{let r=Math.abs(e),i=Math.abs(t);return r<n&&i<n?null:r>i?e>0?`right`:`left`:t>0?`down`:`up`},d=(e,t=15,n=300)=>{let r=e/n*t;return Math.max(-t,Math.min(t,r))},f=(e,t,n=100)=>{let r=1-Math.sqrt(e*e+t*t)/(n*3);return Math.max(.5,Math.min(1,r))},p=e=>`touches`in e&&e.touches&&e.touches.length>0?{x:e.touches[0].clientX,y:e.touches[0].clientY}:`clientX`in e&&`clientY`in e?{x:e.clientX,y:e.clientY}:{x:0,y:0},m={threshold:100,velocityThreshold:.5,maxRotation:15,exitDuration:300,returnDuration:200,enableRotation:!0,preventSwipe:[]},h=(e={},t={})=>{let{threshold:n,velocityThreshold:r,maxRotation:i,exitDuration:a,returnDuration:o,enableRotation:s,preventSwipe:h}=(0,c.useMemo)(()=>({...m,...t}),[t.threshold,t.velocityThreshold,t.maxRotation,t.exitDuration,t.returnDuration,t.enableRotation,JSON.stringify(t.preventSwipe)]),g=(0,c.useRef)(null),_=(0,c.useRef)(null),v=(0,c.useRef)(new Set),y=(0,c.useRef)(e);y.current=e;let b=(0,c.useRef)(`translate3d(0px, 0px, 0px) rotate(0deg)`),x=(0,c.useRef)(1),S=(0,c.useRef)(`none`),[C,w]=(0,c.useState)({transform:`translate3d(0px, 0px, 0px) rotate(0deg)`,opacity:1,transition:`none`}),[T,E]=(0,c.useState)({isDragging:!1,startX:0,startY:0,currentX:0,currentY:0,deltaX:0,deltaY:0,velocity:0,direction:null,startTime:0}),D=(0,c.useCallback)(e=>{w(t=>{let n={...t,...e};return t.transform===n.transform&&t.opacity===n.opacity&&t.transition===n.transition?t:n})},[]),O=(0,c.useCallback)((e,t)=>{let n=setTimeout(()=>{v.current.delete(n),e()},t);return v.current.add(n),n},[]),k=(0,c.useCallback)(e=>{let t=p(e),n=Date.now();E({isDragging:!0,startX:t.x,startY:t.y,currentX:t.x,currentY:t.y,deltaX:0,deltaY:0,velocity:0,direction:null,startTime:n}),S.current=`none`,D({transition:`none`}),y.current.onSwipeStart?.()},[D]),A=(0,c.useRef)(T);A.current=T;let j=(0,c.useCallback)(e=>{let t=A.current;if(!t.isDragging)return;let r=p(e),a=r.x-t.startX,o=r.y-t.startY;h.includes(`left`)&&a<0&&(a=0),h.includes(`right`)&&a>0&&(a=0),h.includes(`up`)&&o<0&&(o=0),h.includes(`down`)&&o>0&&(o=0),E(e=>({...e,currentX:r.x,currentY:r.y,deltaX:a,deltaY:o})),_.current&&cancelAnimationFrame(_.current),_.current=requestAnimationFrame(()=>{let e=s?d(a,i):0,t=f(a,o,n),r=`translate3d(${a}px, ${o}px, 0px) rotate(${e}deg)`;b.current=r,x.current=t,D({transform:r,opacity:t})})},[s,i,n,h,D]),M=(0,c.useCallback)(()=>{let e=A.current;if(!e.isDragging)return;let{deltaX:t,deltaY:c,startTime:f}=e,p=Date.now()-f,m=Math.sqrt(t*t+c*c),g=l(m,p),_=u(t,c,n/2),v=m>=n||g>=r,b=_&&h.includes(_);if(v&&_&&!b){let e=`transform ${a}ms ease-out, opacity ${a}ms ease-out`,n=t*2;D({transition:e,transform:`translate3d(${n}px, ${c*2}px, 0px) rotate(${s?d(n,i*2):0}deg)`,opacity:0}),O(()=>{switch(_){case`left`:y.current.onSwipeLeft?.();break;case`right`:y.current.onSwipeRight?.();break;case`up`:y.current.onSwipeUp?.();break;case`down`:y.current.onSwipeDown?.();break}y.current.onSwipeEnd?.(),O(()=>{D({transition:`none`,transform:`translate3d(0px, 0px, 0px) rotate(0deg)`,opacity:1})},50)},a)}else D({transition:`transform ${o}ms cubic-bezier(0.175, 0.885, 0.32, 1.275), opacity ${o}ms ease-out`,transform:`translate3d(0px, 0px, 0px) rotate(0deg)`,opacity:1}),y.current.onSwipeEnd?.();E(e=>({...e,isDragging:!1}))},[n,r,a,o,s,i,h,D,O]),N=(0,c.useRef)({handleSwipeStart:k,handleSwipeMove:j,handleSwipeEnd:M});return N.current={handleSwipeStart:k,handleSwipeMove:j,handleSwipeEnd:M},(0,c.useEffect)(()=>{let e=g.current;if(!e)return;let t=e=>{N.current.handleSwipeStart(e)};return e.addEventListener(`pointerdown`,t),()=>{e.removeEventListener(`pointerdown`,t)}},[]),(0,c.useEffect)(()=>{if(!T.isDragging)return;let e=e=>{N.current.handleSwipeMove(e)},t=()=>{N.current.handleSwipeEnd()};return document.addEventListener(`pointermove`,e),document.addEventListener(`pointerup`,t),document.addEventListener(`pointercancel`,t),()=>{document.removeEventListener(`pointermove`,e),document.removeEventListener(`pointerup`,t),document.removeEventListener(`pointercancel`,t)}},[T.isDragging]),(0,c.useEffect)(()=>()=>{_.current&&cancelAnimationFrame(_.current),v.current.forEach(e=>clearTimeout(e)),v.current.clear()},[]),{ref:g,transform:C.transform,opacity:C.opacity,transition:C.transition,isDragging:T.isDragging,deltaX:T.deltaX,deltaY:T.deltaY}},g={fontSize:`2rem`,fontWeight:`bold`,color:`#fff`,textTransform:`uppercase`,letterSpacing:`2px`,textShadow:`0 2px 8px rgba(0, 0, 0, 0.3)`},_={right:{backgroundColor:`rgba(34, 197, 94, 0.6)`,label:`✓ Accept`,labelStyle:g},left:{backgroundColor:`rgba(239, 68, 68, 0.6)`,label:`✗ Reject`,labelStyle:g},up:{backgroundColor:`rgba(59, 130, 246, 0.6)`,label:`⭐ Super`,labelStyle:g},down:{backgroundColor:`rgba(168, 85, 247, 0.6)`,label:`⏭ Skip`,labelStyle:g}},v=[],y=({children:e,className:t=``,style:n,onSwipeLeft:r,onSwipeRight:i,onSwipeUp:a,onSwipeDown:o,onSwipeStart:s,onSwipeEnd:l,threshold:u=100,velocityThreshold:d=.5,maxRotation:f=15,exitDuration:p=300,returnDuration:m=200,enableRotation:g=!0,preventSwipe:y=v,swipeStyles:b,showOverlay:x=!0})=>{let{ref:S,transform:C,opacity:w,transition:T,isDragging:E,deltaX:D,deltaY:O}=h((0,c.useMemo)(()=>({onSwipeLeft:r,onSwipeRight:i,onSwipeUp:a,onSwipeDown:o,onSwipeStart:s,onSwipeEnd:l}),[r,i,a,o,s,l]),(0,c.useMemo)(()=>({threshold:u,velocityThreshold:d,maxRotation:f,exitDuration:p,returnDuration:m,enableRotation:g,preventSwipe:y}),[u,d,f,p,m,g,y])),k=(0,c.useMemo)(()=>({right:{..._.right,...b?.right,labelStyle:{..._.right.labelStyle,...b?.right?.labelStyle}},left:{..._.left,...b?.left,labelStyle:{..._.left.labelStyle,...b?.left?.labelStyle}},up:{..._.up,...b?.up,labelStyle:{..._.up.labelStyle,...b?.up?.labelStyle}},down:{..._.down,...b?.down,labelStyle:{..._.down.labelStyle,...b?.down?.labelStyle}}}),[b?.right,b?.left,b?.up,b?.down]),A=Math.abs(D),j=Math.abs(O),M=A>=j,N=M?A:j,P=Math.min(N/u,1),F=M&&D>20,I=M&&D<-20,L=!M&&O<-20,R=!M&&O>20,z=P>=.15,B=x&&E&&N>10,V=(0,c.useMemo)(()=>F?`right`:I?`left`:L?`up`:R?`down`:null,[F,I,L,R]),H=V?k[V]:null,U=H?.backgroundColor??`transparent`,W=H?{label:H.label,labelStyle:H.labelStyle}:null,G=(0,c.useMemo)(()=>({...n,transform:C,opacity:w,transition:T,touchAction:`none`,userSelect:`none`,WebkitUserSelect:`none`,cursor:E?`grabbing`:`grab`,willChange:E?`transform, opacity`:`auto`,position:`absolute`}),[n,C,w,T,E]),K=(0,c.useMemo)(()=>({position:`absolute`,top:0,left:0,right:0,bottom:0,display:`flex`,alignItems:`center`,justifyContent:`center`,borderRadius:`inherit`,pointerEvents:`none`,zIndex:10,opacity:B?P:0,transition:E?`none`:`opacity 200ms ease-out`,backgroundColor:B?U:`transparent`}),[B,P,E,U]),q=(0,c.useMemo)(()=>({...W?.labelStyle,transform:`scale(${.8+P*.4})`,opacity:P}),[W?.labelStyle,P]);return c.default.createElement(`div`,{ref:S,className:`swipeon-card ${t}`,style:G},e,c.default.createElement(`div`,{style:K},B&&z&&W?.label&&c.default.createElement(`span`,{style:q},W.label)))},b=(0,c.memo)(y);exports.SwipeCard=b,exports.useSwipe=h;
2
2
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":["DEFAULT_CONFIG: Required<SwipeConfig>","SwipeCard: React.FC<SwipeCardProps>","cardStyle: CSSProperties"],"sources":["../src/utils/helpers.ts","../src/hooks/useSwipe.ts","../src/components/SwipeCard.tsx"],"sourcesContent":["import { SwipeDirection } from '../types';\n\n/**\n * Calculate the distance between two points\n */\nexport const getDistance = (x1: number, y1: number, x2: number, y2: number): number => {\n const dx = x2 - x1;\n const dy = y2 - y1;\n return Math.sqrt(dx * dx + dy * dy);\n};\n\n/**\n * Calculate velocity based on distance and time\n */\nexport const calculateVelocity = (distance: number, time: number): number => {\n if (time === 0) return 0;\n return Math.abs(distance / time);\n};\n\n/**\n * Determine the swipe direction based on deltas\n */\nexport const getSwipeDirection = (\n deltaX: number,\n deltaY: number,\n threshold: number = 50\n): SwipeDirection | null => {\n const absDeltaX = Math.abs(deltaX);\n const absDeltaY = Math.abs(deltaY);\n\n // Need minimum movement\n if (absDeltaX < threshold && absDeltaY < threshold) {\n return null;\n }\n\n // Determine primary direction\n if (absDeltaX > absDeltaY) {\n return deltaX > 0 ? 'right' : 'left';\n } else {\n return deltaY > 0 ? 'down' : 'up';\n }\n};\n\n/**\n * Calculate rotation angle based on horizontal movement\n */\nexport const calculateRotation = (\n deltaX: number,\n maxRotation: number = 15,\n containerWidth: number = 300\n): number => {\n const rotation = (deltaX / containerWidth) * maxRotation;\n return Math.max(-maxRotation, Math.min(maxRotation, rotation));\n};\n\n/**\n * Calculate opacity based on drag distance\n */\nexport const calculateOpacity = (\n deltaX: number,\n deltaY: number,\n threshold: number = 100\n): number => {\n const distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);\n const opacity = 1 - distance / (threshold * 3);\n return Math.max(0.5, Math.min(1, opacity));\n};\n\n/**\n * Clamp a value between min and max\n */\nexport const clamp = (value: number, min: number, max: number): number => {\n return Math.max(min, Math.min(max, value));\n};\n\n/**\n * Get touch/pointer coordinates from event\n */\nexport const getEventCoordinates = (\n event: TouchEvent | PointerEvent | MouseEvent | any\n): { x: number; y: number } => {\n if ('touches' in event && event.touches && event.touches.length > 0) {\n return {\n x: event.touches[0].clientX,\n y: event.touches[0].clientY,\n };\n }\n \n if ('clientX' in event && 'clientY' in event) {\n return {\n x: event.clientX,\n y: event.clientY,\n };\n }\n \n return { x: 0, y: 0 };\n};\n\n","import { useRef, useState, useCallback, useEffect } from 'react';\nimport { SwipeConfig, SwipeCallbacks, GestureState, UseSwipeReturn } from '../types';\nimport {\n getSwipeDirection,\n calculateRotation,\n calculateOpacity,\n calculateVelocity,\n getEventCoordinates,\n} from '../utils/helpers';\n\nconst DEFAULT_CONFIG: Required<SwipeConfig> = {\n threshold: 100,\n velocityThreshold: 0.5,\n maxRotation: 15,\n exitDuration: 300,\n returnDuration: 200,\n enableRotation: true,\n};\n\nexport const useSwipe = (\n callbacks: SwipeCallbacks = {},\n config: SwipeConfig = {}\n): UseSwipeReturn => {\n const mergedConfig = { ...DEFAULT_CONFIG, ...config };\n const {\n threshold,\n velocityThreshold,\n maxRotation,\n exitDuration,\n returnDuration,\n enableRotation,\n } = mergedConfig;\n\n const ref = useRef<HTMLDivElement>(null);\n const animationFrameRef = useRef<number | null>(null);\n const [gestureState, setGestureState] = useState<GestureState>({\n isDragging: false,\n startX: 0,\n startY: 0,\n currentX: 0,\n currentY: 0,\n deltaX: 0,\n deltaY: 0,\n velocity: 0,\n direction: null,\n startTime: 0,\n });\n\n const [transform, setTransform] = useState('translate3d(0px, 0px, 0px) rotate(0deg)');\n const [opacity, setOpacity] = useState(1);\n const [transition, setTransition] = useState('none');\n\n // Handle swipe start\n const handleSwipeStart = useCallback(\n (event: PointerEvent | TouchEvent | MouseEvent) => {\n const coords = getEventCoordinates(event as any);\n const now = Date.now();\n\n setGestureState({\n isDragging: true,\n startX: coords.x,\n startY: coords.y,\n currentX: coords.x,\n currentY: coords.y,\n deltaX: 0,\n deltaY: 0,\n velocity: 0,\n direction: null,\n startTime: now,\n });\n\n setTransition('none');\n \n if (callbacks.onSwipeStart) {\n callbacks.onSwipeStart();\n }\n },\n [callbacks]\n );\n\n // Handle swipe move\n const handleSwipeMove = useCallback(\n (event: PointerEvent | TouchEvent | MouseEvent) => {\n if (!gestureState.isDragging) return;\n\n const coords = getEventCoordinates(event as any);\n const deltaX = coords.x - gestureState.startX;\n const deltaY = coords.y - gestureState.startY;\n\n // Update gesture state\n setGestureState((prev: GestureState) => ({\n ...prev,\n currentX: coords.x,\n currentY: coords.y,\n deltaX,\n deltaY,\n }));\n\n // Use RAF for smooth updates\n if (animationFrameRef.current) {\n cancelAnimationFrame(animationFrameRef.current);\n }\n\n animationFrameRef.current = requestAnimationFrame(() => {\n const rotation = enableRotation ? calculateRotation(deltaX, maxRotation) : 0;\n const newOpacity = calculateOpacity(deltaX, deltaY, threshold);\n\n setTransform(\n `translate3d(${deltaX}px, ${deltaY}px, 0px) rotate(${rotation}deg)`\n );\n setOpacity(newOpacity);\n });\n },\n [gestureState.isDragging, gestureState.startX, gestureState.startY, enableRotation, maxRotation, threshold]\n );\n\n // Handle swipe end\n const handleSwipeEnd = useCallback(() => {\n if (!gestureState.isDragging) return;\n\n const { deltaX, deltaY, startTime } = gestureState;\n const timeDelta = Date.now() - startTime;\n const distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);\n const velocity = calculateVelocity(distance, timeDelta);\n\n const direction = getSwipeDirection(deltaX, deltaY, threshold / 2);\n const shouldSwipe =\n distance >= threshold || velocity >= velocityThreshold;\n\n if (shouldSwipe && direction) {\n // Execute swipe\n setTransition(`transform ${exitDuration}ms ease-out, opacity ${exitDuration}ms ease-out`);\n \n // Calculate exit position (move far enough off screen)\n const multiplier = 2;\n const exitX = deltaX * multiplier;\n const exitY = deltaY * multiplier;\n const rotation = enableRotation ? calculateRotation(exitX, maxRotation * 2) : 0;\n\n setTransform(\n `translate3d(${exitX}px, ${exitY}px, 0px) rotate(${rotation}deg)`\n );\n setOpacity(0);\n\n // Trigger callback after animation\n setTimeout(() => {\n switch (direction) {\n case 'left':\n callbacks.onSwipeLeft?.();\n break;\n case 'right':\n callbacks.onSwipeRight?.();\n break;\n case 'up':\n callbacks.onSwipeUp?.();\n break;\n case 'down':\n callbacks.onSwipeDown?.();\n break;\n }\n \n if (callbacks.onSwipeEnd) {\n callbacks.onSwipeEnd();\n }\n\n // Reset after callback\n setTimeout(() => {\n setTransition('none');\n setTransform('translate3d(0px, 0px, 0px) rotate(0deg)');\n setOpacity(1);\n }, 50);\n }, exitDuration);\n } else {\n // Spring back\n setTransition(\n `transform ${returnDuration}ms cubic-bezier(0.175, 0.885, 0.32, 1.275), opacity ${returnDuration}ms ease-out`\n );\n setTransform('translate3d(0px, 0px, 0px) rotate(0deg)');\n setOpacity(1);\n\n if (callbacks.onSwipeEnd) {\n callbacks.onSwipeEnd();\n }\n }\n\n setGestureState((prev: GestureState) => ({ ...prev, isDragging: false }));\n }, [\n gestureState,\n threshold,\n velocityThreshold,\n exitDuration,\n returnDuration,\n enableRotation,\n maxRotation,\n callbacks,\n ]);\n\n // Attach event listeners\n useEffect(() => {\n const element = ref.current;\n if (!element) return;\n\n const handlePointerDown = (e: PointerEvent) => {\n handleSwipeStart(e);\n };\n\n const handlePointerMove = (e: PointerEvent) => {\n if (gestureState.isDragging) {\n handleSwipeMove(e);\n }\n };\n\n const handlePointerUp = () => {\n handleSwipeEnd();\n };\n\n element.addEventListener('pointerdown', handlePointerDown);\n\n if (gestureState.isDragging) {\n document.addEventListener('pointermove', handlePointerMove);\n document.addEventListener('pointerup', handlePointerUp);\n document.addEventListener('pointercancel', handlePointerUp);\n }\n\n return () => {\n element.removeEventListener('pointerdown', handlePointerDown);\n document.removeEventListener('pointermove', handlePointerMove);\n document.removeEventListener('pointerup', handlePointerUp);\n document.removeEventListener('pointercancel', handlePointerUp);\n \n if (animationFrameRef.current) {\n cancelAnimationFrame(animationFrameRef.current);\n }\n };\n }, [gestureState.isDragging, handleSwipeStart, handleSwipeMove, handleSwipeEnd]);\n\n return {\n ref,\n transform,\n opacity,\n transition,\n isDragging: gestureState.isDragging,\n };\n};\n\n","import React, { CSSProperties } from 'react';\nimport { useSwipe } from '../hooks/useSwipe';\nimport { SwipeCardProps } from '../types';\n\n/**\n * SwipeCard Component\n * \n * A performant swipeable card component with smooth animations.\n * Supports swipe in all four directions: left, right, up, and down.\n * \n * @example\n * ```tsx\n * <SwipeCard\n * onSwipeLeft={() => console.log('Swiped left')}\n * onSwipeRight={() => console.log('Swiped right')}\n * threshold={100}\n * >\n * <div>Your content here</div>\n * </SwipeCard>\n * ```\n */\nexport const SwipeCard: React.FC<SwipeCardProps> = ({\n children,\n className = '',\n style = {},\n onSwipeLeft,\n onSwipeRight,\n onSwipeUp,\n onSwipeDown,\n onSwipeStart,\n onSwipeEnd,\n threshold = 100,\n velocityThreshold = 0.5,\n maxRotation = 15,\n exitDuration = 300,\n returnDuration = 200,\n enableRotation = true,\n}) => {\n const callbacks = {\n onSwipeLeft,\n onSwipeRight,\n onSwipeUp,\n onSwipeDown,\n onSwipeStart,\n onSwipeEnd,\n };\n\n const config = {\n threshold,\n velocityThreshold,\n maxRotation,\n exitDuration,\n returnDuration,\n enableRotation,\n };\n\n const { ref, transform, opacity, transition, isDragging } = useSwipe(callbacks, config);\n\n const cardStyle: CSSProperties = {\n ...style,\n transform,\n opacity,\n transition,\n touchAction: 'none', // Prevent default touch behaviors\n userSelect: 'none', // Prevent text selection during drag\n WebkitUserSelect: 'none',\n cursor: isDragging ? 'grabbing' : 'grab',\n willChange: isDragging ? 'transform, opacity' : 'auto',\n position: 'relative',\n };\n\n return (\n <div\n ref={ref}\n className={`swipeon-card ${className}`}\n style={cardStyle}\n >\n {children}\n </div>\n );\n};\n\nexport default SwipeCard;\n\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","names":["DEFAULT_CONFIG: Required<SwipeConfig>","DEFAULT_LABEL_STYLE: CSSProperties","EMPTY_PREVENT_SWIPE: readonly string[]","SwipeCardInner: React.FC<SwipeCardProps>","cardStyle: CSSProperties","overlayStyle: CSSProperties","computedLabelStyle: CSSProperties"],"sources":["../src/utils/helpers.ts","../src/hooks/useSwipe.ts","../src/components/SwipeCard.tsx"],"sourcesContent":["import { SwipeDirection } from '../types';\n\n/**\n * Calculate the distance between two points\n */\nexport const getDistance = (x1: number, y1: number, x2: number, y2: number): number => {\n const dx = x2 - x1;\n const dy = y2 - y1;\n return Math.sqrt(dx * dx + dy * dy);\n};\n\n/**\n * Calculate velocity based on distance and time\n */\nexport const calculateVelocity = (distance: number, time: number): number => {\n if (time === 0) return 0;\n return Math.abs(distance / time);\n};\n\n/**\n * Determine the swipe direction based on deltas\n */\nexport const getSwipeDirection = (\n deltaX: number,\n deltaY: number,\n threshold: number = 50\n): SwipeDirection | null => {\n const absDeltaX = Math.abs(deltaX);\n const absDeltaY = Math.abs(deltaY);\n\n // Need minimum movement\n if (absDeltaX < threshold && absDeltaY < threshold) {\n return null;\n }\n\n // Determine primary direction\n if (absDeltaX > absDeltaY) {\n return deltaX > 0 ? 'right' : 'left';\n } else {\n return deltaY > 0 ? 'down' : 'up';\n }\n};\n\n/**\n * Calculate rotation angle based on horizontal movement\n */\nexport const calculateRotation = (\n deltaX: number,\n maxRotation: number = 15,\n containerWidth: number = 300\n): number => {\n const rotation = (deltaX / containerWidth) * maxRotation;\n return Math.max(-maxRotation, Math.min(maxRotation, rotation));\n};\n\n/**\n * Calculate opacity based on drag distance\n */\nexport const calculateOpacity = (\n deltaX: number,\n deltaY: number,\n threshold: number = 100\n): number => {\n const distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);\n const opacity = 1 - distance / (threshold * 3);\n return Math.max(0.5, Math.min(1, opacity));\n};\n\n/**\n * Clamp a value between min and max\n */\nexport const clamp = (value: number, min: number, max: number): number => {\n return Math.max(min, Math.min(max, value));\n};\n\n/**\n * Get touch/pointer coordinates from event\n */\nexport const getEventCoordinates = (\n event: TouchEvent | PointerEvent | MouseEvent | any\n): { x: number; y: number } => {\n if ('touches' in event && event.touches && event.touches.length > 0) {\n return {\n x: event.touches[0].clientX,\n y: event.touches[0].clientY,\n };\n }\n \n if ('clientX' in event && 'clientY' in event) {\n return {\n x: event.clientX,\n y: event.clientY,\n };\n }\n \n return { x: 0, y: 0 };\n};\n\n","import { useRef, useState, useCallback, useEffect, useMemo } from 'react';\nimport { SwipeConfig, SwipeCallbacks, GestureState, UseSwipeReturn } from '../types';\nimport {\n getSwipeDirection,\n calculateRotation,\n calculateOpacity,\n calculateVelocity,\n getEventCoordinates,\n} from '../utils/helpers';\n\nconst DEFAULT_CONFIG: Required<SwipeConfig> = {\n threshold: 100,\n velocityThreshold: 0.5,\n maxRotation: 15,\n exitDuration: 300,\n returnDuration: 200,\n enableRotation: true,\n preventSwipe: [],\n};\n\nexport const useSwipe = (\n callbacks: SwipeCallbacks = {},\n config: SwipeConfig = {}\n): UseSwipeReturn => {\n // Memoize config to prevent unnecessary recalculations\n const mergedConfig = useMemo(() => ({ ...DEFAULT_CONFIG, ...config }), [\n config.threshold,\n config.velocityThreshold,\n config.maxRotation,\n config.exitDuration,\n config.returnDuration,\n config.enableRotation,\n // Use JSON for array comparison (preventSwipe is typically small)\n JSON.stringify(config.preventSwipe),\n ]);\n\n const {\n threshold,\n velocityThreshold,\n maxRotation,\n exitDuration,\n returnDuration,\n enableRotation,\n preventSwipe,\n } = mergedConfig;\n\n const ref = useRef<HTMLDivElement>(null);\n const animationFrameRef = useRef<number | null>(null);\n const timeoutRefs = useRef<Set<ReturnType<typeof setTimeout>>>(new Set());\n \n // Store callbacks in ref to avoid dependency issues\n const callbacksRef = useRef(callbacks);\n callbacksRef.current = callbacks;\n\n // Use refs for animation values that don't need to trigger re-renders during drag\n const transformRef = useRef('translate3d(0px, 0px, 0px) rotate(0deg)');\n const opacityRef = useRef(1);\n const transitionRef = useRef('none');\n\n // Single state for values that need to trigger re-renders\n const [renderState, setRenderState] = useState({\n transform: 'translate3d(0px, 0px, 0px) rotate(0deg)',\n opacity: 1,\n transition: 'none',\n });\n\n const [gestureState, setGestureState] = useState<GestureState>({\n isDragging: false,\n startX: 0,\n startY: 0,\n currentX: 0,\n currentY: 0,\n deltaX: 0,\n deltaY: 0,\n velocity: 0,\n direction: null,\n startTime: 0,\n });\n\n // Batch update render state\n const updateRenderState = useCallback((updates: Partial<typeof renderState>) => {\n setRenderState(prev => {\n const newState = { ...prev, ...updates };\n // Only update if values actually changed\n if (\n prev.transform === newState.transform &&\n prev.opacity === newState.opacity &&\n prev.transition === newState.transition\n ) {\n return prev;\n }\n return newState;\n });\n }, []);\n\n // Safe setTimeout that tracks timeouts for cleanup\n const safeTimeout = useCallback((fn: () => void, delay: number) => {\n const id = setTimeout(() => {\n timeoutRefs.current.delete(id);\n fn();\n }, delay);\n timeoutRefs.current.add(id);\n return id;\n }, []);\n\n // Handle swipe start\n const handleSwipeStart = useCallback(\n (event: PointerEvent | TouchEvent | MouseEvent) => {\n const coords = getEventCoordinates(event as any);\n const now = Date.now();\n\n setGestureState({\n isDragging: true,\n startX: coords.x,\n startY: coords.y,\n currentX: coords.x,\n currentY: coords.y,\n deltaX: 0,\n deltaY: 0,\n velocity: 0,\n direction: null,\n startTime: now,\n });\n\n transitionRef.current = 'none';\n updateRenderState({ transition: 'none' });\n \n callbacksRef.current.onSwipeStart?.();\n },\n [updateRenderState]\n );\n\n // Handle swipe move - use refs to avoid closure issues\n const gestureStateRef = useRef(gestureState);\n gestureStateRef.current = gestureState;\n\n const handleSwipeMove = useCallback(\n (event: PointerEvent | TouchEvent | MouseEvent) => {\n const state = gestureStateRef.current;\n if (!state.isDragging) return;\n\n const coords = getEventCoordinates(event as any);\n let deltaX = coords.x - state.startX;\n let deltaY = coords.y - state.startY;\n\n // Constrain movement based on preventSwipe directions\n if (preventSwipe.includes('left') && deltaX < 0) deltaX = 0;\n if (preventSwipe.includes('right') && deltaX > 0) deltaX = 0;\n if (preventSwipe.includes('up') && deltaY < 0) deltaY = 0;\n if (preventSwipe.includes('down') && deltaY > 0) deltaY = 0;\n\n // Update gesture state\n setGestureState(prev => ({\n ...prev,\n currentX: coords.x,\n currentY: coords.y,\n deltaX,\n deltaY,\n }));\n\n // Use RAF for smooth updates - cancel previous frame\n if (animationFrameRef.current) {\n cancelAnimationFrame(animationFrameRef.current);\n }\n\n animationFrameRef.current = requestAnimationFrame(() => {\n const rotation = enableRotation ? calculateRotation(deltaX, maxRotation) : 0;\n const newOpacity = calculateOpacity(deltaX, deltaY, threshold);\n const newTransform = `translate3d(${deltaX}px, ${deltaY}px, 0px) rotate(${rotation}deg)`;\n\n // Update refs immediately for internal use\n transformRef.current = newTransform;\n opacityRef.current = newOpacity;\n\n // Batch update to React state\n updateRenderState({\n transform: newTransform,\n opacity: newOpacity,\n });\n });\n },\n [enableRotation, maxRotation, threshold, preventSwipe, updateRenderState]\n );\n\n // Handle swipe end\n const handleSwipeEnd = useCallback(() => {\n const state = gestureStateRef.current;\n if (!state.isDragging) return;\n\n const { deltaX, deltaY, startTime } = state;\n const timeDelta = Date.now() - startTime;\n const distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);\n const velocity = calculateVelocity(distance, timeDelta);\n\n const direction = getSwipeDirection(deltaX, deltaY, threshold / 2);\n const shouldSwipe = distance >= threshold || velocity >= velocityThreshold;\n\n // Check if direction is prevented\n const isDirectionPrevented = direction && preventSwipe.includes(direction);\n\n if (shouldSwipe && direction && !isDirectionPrevented) {\n // Execute swipe\n const exitTransition = `transform ${exitDuration}ms ease-out, opacity ${exitDuration}ms ease-out`;\n \n // Calculate exit position (move far enough off screen)\n const multiplier = 2;\n const exitX = deltaX * multiplier;\n const exitY = deltaY * multiplier;\n const rotation = enableRotation ? calculateRotation(exitX, maxRotation * 2) : 0;\n const exitTransform = `translate3d(${exitX}px, ${exitY}px, 0px) rotate(${rotation}deg)`;\n\n updateRenderState({\n transition: exitTransition,\n transform: exitTransform,\n opacity: 0,\n });\n\n // Trigger callback after animation\n safeTimeout(() => {\n switch (direction) {\n case 'left':\n callbacksRef.current.onSwipeLeft?.();\n break;\n case 'right':\n callbacksRef.current.onSwipeRight?.();\n break;\n case 'up':\n callbacksRef.current.onSwipeUp?.();\n break;\n case 'down':\n callbacksRef.current.onSwipeDown?.();\n break;\n }\n \n callbacksRef.current.onSwipeEnd?.();\n\n // Reset after callback\n safeTimeout(() => {\n updateRenderState({\n transition: 'none',\n transform: 'translate3d(0px, 0px, 0px) rotate(0deg)',\n opacity: 1,\n });\n }, 50);\n }, exitDuration);\n } else {\n // Spring back\n const returnTransition = `transform ${returnDuration}ms cubic-bezier(0.175, 0.885, 0.32, 1.275), opacity ${returnDuration}ms ease-out`;\n \n updateRenderState({\n transition: returnTransition,\n transform: 'translate3d(0px, 0px, 0px) rotate(0deg)',\n opacity: 1,\n });\n\n callbacksRef.current.onSwipeEnd?.();\n }\n\n setGestureState(prev => ({ ...prev, isDragging: false }));\n }, [\n threshold,\n velocityThreshold,\n exitDuration,\n returnDuration,\n enableRotation,\n maxRotation,\n preventSwipe,\n updateRenderState,\n safeTimeout,\n ]);\n\n // Stable event handler refs to avoid re-attaching listeners\n const handlersRef = useRef({\n handleSwipeStart,\n handleSwipeMove,\n handleSwipeEnd,\n });\n handlersRef.current = { handleSwipeStart, handleSwipeMove, handleSwipeEnd };\n\n // Attach event listeners with stable references\n useEffect(() => {\n const element = ref.current;\n if (!element) return;\n\n const onPointerDown = (e: PointerEvent) => {\n handlersRef.current.handleSwipeStart(e);\n };\n\n element.addEventListener('pointerdown', onPointerDown);\n\n return () => {\n element.removeEventListener('pointerdown', onPointerDown);\n };\n }, []); // Empty deps - handlers are accessed via ref\n\n // Attach document listeners when dragging\n useEffect(() => {\n if (!gestureState.isDragging) return;\n\n const onPointerMove = (e: PointerEvent) => {\n handlersRef.current.handleSwipeMove(e);\n };\n\n const onPointerUp = () => {\n handlersRef.current.handleSwipeEnd();\n };\n\n document.addEventListener('pointermove', onPointerMove);\n document.addEventListener('pointerup', onPointerUp);\n document.addEventListener('pointercancel', onPointerUp);\n\n return () => {\n document.removeEventListener('pointermove', onPointerMove);\n document.removeEventListener('pointerup', onPointerUp);\n document.removeEventListener('pointercancel', onPointerUp);\n };\n }, [gestureState.isDragging]);\n\n // Cleanup on unmount\n useEffect(() => {\n return () => {\n if (animationFrameRef.current) {\n cancelAnimationFrame(animationFrameRef.current);\n }\n // Clear all pending timeouts\n timeoutRefs.current.forEach(id => clearTimeout(id));\n timeoutRefs.current.clear();\n };\n }, []);\n\n return {\n ref,\n transform: renderState.transform,\n opacity: renderState.opacity,\n transition: renderState.transition,\n isDragging: gestureState.isDragging,\n deltaX: gestureState.deltaX,\n deltaY: gestureState.deltaY,\n };\n};\n","import React, { CSSProperties, useMemo, memo } from 'react';\nimport { useSwipe } from '../hooks/useSwipe';\nimport { SwipeCardProps, SwipeCallbacks, SwipeConfig } from '../types';\n\n/**\n * SwipeCard Component\n * \n * A performant swipeable card component with smooth animations.\n * Supports swipe in all four directions: left, right, up, and down.\n * \n * @example\n * ```tsx\n * <SwipeCard\n * onSwipeLeft={() => console.log('Swiped left')}\n * onSwipeRight={() => console.log('Swiped right')}\n * threshold={100}\n * swipeStyles={{\n * right: { \n * backgroundColor: 'rgba(34, 197, 94, 0.6)', \n * label: '👍 Like',\n * labelStyle: { position: 'absolute', top: 20, right: 20 }\n * },\n * left: { \n * backgroundColor: 'rgba(239, 68, 68, 0.6)', \n * label: '👎 Nope',\n * labelStyle: { position: 'absolute', top: 20, left: 20 }\n * },\n * }}\n * >\n * <div>Your content here</div>\n * </SwipeCard>\n * ```\n */\n\n// Default label style - constant, no need to recreate\nconst DEFAULT_LABEL_STYLE: CSSProperties = {\n fontSize: '2rem',\n fontWeight: 'bold',\n color: '#fff',\n textTransform: 'uppercase',\n letterSpacing: '2px',\n textShadow: '0 2px 8px rgba(0, 0, 0, 0.3)',\n};\n\n// Default styles for each direction - constant\nconst DEFAULT_SWIPE_STYLES = {\n right: { backgroundColor: 'rgba(34, 197, 94, 0.6)', label: '✓ Accept', labelStyle: DEFAULT_LABEL_STYLE },\n left: { backgroundColor: 'rgba(239, 68, 68, 0.6)', label: '✗ Reject', labelStyle: DEFAULT_LABEL_STYLE },\n up: { backgroundColor: 'rgba(59, 130, 246, 0.6)', label: '⭐ Super', labelStyle: DEFAULT_LABEL_STYLE },\n down: { backgroundColor: 'rgba(168, 85, 247, 0.6)', label: '⏭ Skip', labelStyle: DEFAULT_LABEL_STYLE },\n} as const;\n\n// Empty array constant to prevent recreating on each render\nconst EMPTY_PREVENT_SWIPE: readonly string[] = [];\n\nconst SwipeCardInner: React.FC<SwipeCardProps> = ({\n children,\n className = '',\n style,\n onSwipeLeft,\n onSwipeRight,\n onSwipeUp,\n onSwipeDown,\n onSwipeStart,\n onSwipeEnd,\n threshold = 100,\n velocityThreshold = 0.5,\n maxRotation = 15,\n exitDuration = 300,\n returnDuration = 200,\n enableRotation = true,\n preventSwipe = EMPTY_PREVENT_SWIPE as any,\n swipeStyles,\n showOverlay: showOverlayProp = true,\n}) => {\n // Memoize callbacks object to prevent unnecessary hook updates\n const callbacks: SwipeCallbacks = useMemo(() => ({\n onSwipeLeft,\n onSwipeRight,\n onSwipeUp,\n onSwipeDown,\n onSwipeStart,\n onSwipeEnd,\n }), [onSwipeLeft, onSwipeRight, onSwipeUp, onSwipeDown, onSwipeStart, onSwipeEnd]);\n\n // Memoize config object\n const config: SwipeConfig = useMemo(() => ({\n threshold,\n velocityThreshold,\n maxRotation,\n exitDuration,\n returnDuration,\n enableRotation,\n preventSwipe,\n }), [threshold, velocityThreshold, maxRotation, exitDuration, returnDuration, enableRotation, preventSwipe]);\n\n const { ref, transform, opacity, transition, isDragging, deltaX, deltaY } = useSwipe(callbacks, config);\n\n // Memoize merged swipe styles\n const mergedSwipeStyles = useMemo(() => ({\n right: { \n ...DEFAULT_SWIPE_STYLES.right, \n ...swipeStyles?.right,\n labelStyle: { ...DEFAULT_SWIPE_STYLES.right.labelStyle, ...swipeStyles?.right?.labelStyle }\n },\n left: { \n ...DEFAULT_SWIPE_STYLES.left, \n ...swipeStyles?.left,\n labelStyle: { ...DEFAULT_SWIPE_STYLES.left.labelStyle, ...swipeStyles?.left?.labelStyle }\n },\n up: { \n ...DEFAULT_SWIPE_STYLES.up, \n ...swipeStyles?.up,\n labelStyle: { ...DEFAULT_SWIPE_STYLES.up.labelStyle, ...swipeStyles?.up?.labelStyle }\n },\n down: { \n ...DEFAULT_SWIPE_STYLES.down, \n ...swipeStyles?.down,\n labelStyle: { ...DEFAULT_SWIPE_STYLES.down.labelStyle, ...swipeStyles?.down?.labelStyle }\n },\n }), [\n swipeStyles?.right, \n swipeStyles?.left, \n swipeStyles?.up, \n swipeStyles?.down,\n ]);\n\n // Calculate swipe state - these are cheap, no need to memoize\n const absX = Math.abs(deltaX);\n const absY = Math.abs(deltaY);\n const isPrimaryHorizontal = absX >= absY;\n const swipeDistance = isPrimaryHorizontal ? absX : absY;\n const swipeProgress = Math.min(swipeDistance / threshold, 1);\n \n const isSwipingRight = isPrimaryHorizontal && deltaX > 20;\n const isSwipingLeft = isPrimaryHorizontal && deltaX < -20;\n const isSwipingUp = !isPrimaryHorizontal && deltaY < -20;\n const isSwipingDown = !isPrimaryHorizontal && deltaY > 20;\n \n const isActionInProgress = swipeProgress >= 0.15;\n const showOverlay = showOverlayProp && isDragging && swipeDistance > 10;\n\n // Calculate current direction info - single calculation\n const currentDirection = useMemo(() => {\n if (isSwipingRight) return 'right' as const;\n if (isSwipingLeft) return 'left' as const;\n if (isSwipingUp) return 'up' as const;\n if (isSwipingDown) return 'down' as const;\n return null;\n }, [isSwipingRight, isSwipingLeft, isSwipingUp, isSwipingDown]);\n\n const currentStyle = currentDirection ? mergedSwipeStyles[currentDirection] : null;\n const backgroundColor = currentStyle?.backgroundColor ?? 'transparent';\n const labelConfig = currentStyle ? { label: currentStyle.label, labelStyle: currentStyle.labelStyle } : null;\n\n // Memoize card style - only recalculate when values change\n const cardStyle: CSSProperties = useMemo(() => ({\n ...style,\n transform,\n opacity,\n transition,\n touchAction: 'none',\n userSelect: 'none',\n WebkitUserSelect: 'none',\n cursor: isDragging ? 'grabbing' : 'grab',\n willChange: isDragging ? 'transform, opacity' : 'auto',\n position: 'absolute',\n }), [style, transform, opacity, transition, isDragging]);\n\n // Memoize overlay style\n const overlayStyle: CSSProperties = useMemo(() => ({\n position: 'absolute',\n top: 0,\n left: 0,\n right: 0,\n bottom: 0,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n borderRadius: 'inherit',\n pointerEvents: 'none',\n zIndex: 10,\n opacity: showOverlay ? swipeProgress : 0,\n transition: isDragging ? 'none' : 'opacity 200ms ease-out',\n backgroundColor: showOverlay ? backgroundColor : 'transparent',\n }), [showOverlay, swipeProgress, isDragging, backgroundColor]);\n\n // Memoize computed label style\n const computedLabelStyle: CSSProperties = useMemo(() => ({\n ...labelConfig?.labelStyle,\n transform: `scale(${0.8 + swipeProgress * 0.4})`,\n opacity: swipeProgress,\n }), [labelConfig?.labelStyle, swipeProgress]);\n\n return (\n <div\n ref={ref}\n className={`swipeon-card ${className}`}\n style={cardStyle}\n >\n {children}\n <div style={overlayStyle}>\n {showOverlay && isActionInProgress && labelConfig?.label && (\n <span style={computedLabelStyle}>\n {labelConfig.label}\n </span>\n )}\n </div>\n </div>\n );\n};\n\n// Memoize the entire component to prevent unnecessary re-renders from parent\nexport const SwipeCard = memo(SwipeCardInner);\n\nexport default SwipeCard;\n"],"mappings":"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"}
package/dist/index.d.cts CHANGED
@@ -33,11 +33,46 @@ interface SwipeConfig {
33
33
  returnDuration?: number;
34
34
  /** Enable/disable rotation effect during drag */
35
35
  enableRotation?: boolean;
36
+ /** Array of directions to prevent swiping in */
37
+ preventSwipe?: SwipeDirection[];
38
+ }
39
+ /**
40
+ * Style configuration for each swipe direction
41
+ */
42
+ interface SwipeDirectionStyle {
43
+ /** Background color for the overlay - supports rgba, hex, or color name */
44
+ backgroundColor?: string;
45
+ /** Label text to display when swiping in this direction */
46
+ label?: string;
47
+ /** Custom CSS styles for the label */
48
+ labelStyle?: CSSProperties;
49
+ }
50
+ /**
51
+ * Styles for different swipe directions
52
+ */
53
+ interface SwipeStyles {
54
+ /** Style applied when swiping right */
55
+ right?: SwipeDirectionStyle;
56
+ /** Style applied when swiping left */
57
+ left?: SwipeDirectionStyle;
58
+ /** Style applied when swiping up */
59
+ up?: SwipeDirectionStyle;
60
+ /** Style applied when swiping down */
61
+ down?: SwipeDirectionStyle;
62
+ }
63
+ /**
64
+ * Configuration for the swipe direction overlay
65
+ */
66
+ interface SwipeOverlayConfig {
67
+ /** Styles and labels for different swipe directions */
68
+ swipeStyles?: SwipeStyles;
69
+ /** Whether to show the swipe overlay */
70
+ showOverlay?: boolean;
36
71
  }
37
72
  /**
38
73
  * Props for the SwipeCard component
39
74
  */
40
- interface SwipeCardProps extends SwipeCallbacks, SwipeConfig {
75
+ interface SwipeCardProps extends SwipeCallbacks, SwipeConfig, SwipeOverlayConfig {
41
76
  /** Content to render inside the card */
42
77
  children: ReactNode;
43
78
  /** Additional CSS class name */
@@ -71,30 +106,15 @@ interface UseSwipeReturn {
71
106
  opacity: number;
72
107
  transition: string;
73
108
  isDragging: boolean;
109
+ deltaX: number;
110
+ deltaY: number;
74
111
  }
75
112
  //#endregion
76
113
  //#region src/components/SwipeCard.d.ts
77
- /**
78
- * SwipeCard Component
79
- *
80
- * A performant swipeable card component with smooth animations.
81
- * Supports swipe in all four directions: left, right, up, and down.
82
- *
83
- * @example
84
- * ```tsx
85
- * <SwipeCard
86
- * onSwipeLeft={() => console.log('Swiped left')}
87
- * onSwipeRight={() => console.log('Swiped right')}
88
- * threshold={100}
89
- * >
90
- * <div>Your content here</div>
91
- * </SwipeCard>
92
- * ```
93
- */
94
- declare const SwipeCard: React$1.FC<SwipeCardProps>;
114
+ declare const SwipeCard: React$1.NamedExoticComponent<SwipeCardProps>;
95
115
  //#endregion
96
116
  //#region src/hooks/useSwipe.d.ts
97
117
  declare const useSwipe: (callbacks?: SwipeCallbacks, config?: SwipeConfig) => UseSwipeReturn;
98
118
  //#endregion
99
- export { type GestureState, type SwipeCallbacks, SwipeCard, type SwipeCardProps, type SwipeConfig, type SwipeDirection, type UseSwipeReturn, useSwipe };
119
+ export { type GestureState, type SwipeCallbacks, SwipeCard, type SwipeCardProps, type SwipeConfig, type SwipeDirection, type SwipeDirectionStyle, type SwipeOverlayConfig, type SwipeStyles, type UseSwipeReturn, useSwipe };
100
120
  //# sourceMappingURL=index.d.cts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.cts","names":[],"sources":["../src/types/index.ts","../src/components/SwipeCard.tsx","../src/hooks/useSwipe.ts"],"sourcesContent":[],"mappings":";;;;;;AAKA;AAKiB,KALL,cAAA,GAKmB,MAAA,GAAA,OAAA,GAAA,IAAA,GAAA,MAAA;AAY/B;AAkBA;;AAMU,UApCO,cAAA,CAoCP;EAN8B,WAAA,CAAA,EAAA,GAAA,GAAA,IAAA;EAAgB,YAAA,CAAA,EAAA,GAAA,GAAA,IAAA;EAAW,SAAA,CAAA,EAAA,GAAA,GAAA,IAAA;EAclD,WAAA,CAAA,EAAA,GAAY,GAAA,IAAA;EAgBZ,YAAA,CAAA,EAAA,GAAc,GAAA,IAAA;;;;ACjD/B;;UDCiB,WAAA;;EEHJ,SAAA,CAAA,EAgOZ,MAAA;EA/NY;EACH,iBAAA,CAAA,EAAA,MAAA;EACP;EA6NF,WAAA,CAAA,EAAA,MAAA;;;;;;;;;;;UF3MgB,cAAA,SAAuB,gBAAgB;;YAE5C;;;;UAIF;;;;;;;UAQO,YAAA;;;;;;;;;aASJ;;;;;;UAOI,cAAA;OACV,KAAA,CAAM,UAAU;;;;;;;;;AAlEvB;AAKA;AAYA;AAkBA;;;;;;AAcA;AAgBA;;;;ACjDA;;cAAa,WAAW,OAAA,CAAM,GAAG;;;cCFpB,uBACA,yBACH,gBACP"}
1
+ {"version":3,"file":"index.d.cts","names":[],"sources":["../src/types/index.ts","../src/components/SwipeCard.tsx","../src/hooks/useSwipe.ts"],"sourcesContent":[],"mappings":";;;;;;AAKA;AAKiB,KALL,cAAA,GAKmB,MAAA,GAAA,OAAA,GAAA,IAAA,GAAA,MAAA;AAY/B;AAoBA;AAYA;AAEU,UA9CO,cAAA,CA8CP;EAED,WAAA,CAAA,EAAA,GAAA,GAAA,IAAA;EAEF,YAAA,CAAA,EAAA,GAAA,GAAA,IAAA;EAEE,SAAA,CAAA,EAAA,GAAA,GAAA,IAAA;EAAmB,WAAA,CAAA,EAAA,GAAA,GAAA,IAAA;EAMX,YAAA,CAAA,EAAA,GAAA,GAAkB,IAAA;EAUlB,UAAA,CAAA,EAAA,GAAA,GAAe,IAAA;;;;;AAAqC,UAxDpD,WAAA,CAwDoD;EAAkB;EActE,SAAA,CAAA,EAAA,MAAY;EAgBZ;;;;ECyGJ;;;;ECjMA;EACA,cAAA,CAAA,EAAA,OAAA;EACH;EACP,YAAA,CAAA,EFac,cEbd,EAAA;;;;;UFmBc,mBAAA;;;;;;eAMF;;;;;UAME,WAAA;;UAEP;;SAED;;OAEF;;SAEE;;;;;UAMQ,kBAAA;;gBAED;;;;;;;UAQC,cAAA,SAAuB,gBAAgB,aAAa;;YAEzD;;;;UAIF;;;;;;;UAQO,YAAA;;;;;;;;;aASJ;;;;;;UAOI,cAAA;OACV,KAAA,CAAM,UAAU;;;;;;;;;;cCwGV,WAAS,OAAA,CAAA,qBAAA;;;cCjMT,uBACA,yBACH,gBACP"}
package/dist/index.d.mts CHANGED
@@ -33,11 +33,46 @@ interface SwipeConfig {
33
33
  returnDuration?: number;
34
34
  /** Enable/disable rotation effect during drag */
35
35
  enableRotation?: boolean;
36
+ /** Array of directions to prevent swiping in */
37
+ preventSwipe?: SwipeDirection[];
38
+ }
39
+ /**
40
+ * Style configuration for each swipe direction
41
+ */
42
+ interface SwipeDirectionStyle {
43
+ /** Background color for the overlay - supports rgba, hex, or color name */
44
+ backgroundColor?: string;
45
+ /** Label text to display when swiping in this direction */
46
+ label?: string;
47
+ /** Custom CSS styles for the label */
48
+ labelStyle?: CSSProperties;
49
+ }
50
+ /**
51
+ * Styles for different swipe directions
52
+ */
53
+ interface SwipeStyles {
54
+ /** Style applied when swiping right */
55
+ right?: SwipeDirectionStyle;
56
+ /** Style applied when swiping left */
57
+ left?: SwipeDirectionStyle;
58
+ /** Style applied when swiping up */
59
+ up?: SwipeDirectionStyle;
60
+ /** Style applied when swiping down */
61
+ down?: SwipeDirectionStyle;
62
+ }
63
+ /**
64
+ * Configuration for the swipe direction overlay
65
+ */
66
+ interface SwipeOverlayConfig {
67
+ /** Styles and labels for different swipe directions */
68
+ swipeStyles?: SwipeStyles;
69
+ /** Whether to show the swipe overlay */
70
+ showOverlay?: boolean;
36
71
  }
37
72
  /**
38
73
  * Props for the SwipeCard component
39
74
  */
40
- interface SwipeCardProps extends SwipeCallbacks, SwipeConfig {
75
+ interface SwipeCardProps extends SwipeCallbacks, SwipeConfig, SwipeOverlayConfig {
41
76
  /** Content to render inside the card */
42
77
  children: ReactNode;
43
78
  /** Additional CSS class name */
@@ -71,30 +106,15 @@ interface UseSwipeReturn {
71
106
  opacity: number;
72
107
  transition: string;
73
108
  isDragging: boolean;
109
+ deltaX: number;
110
+ deltaY: number;
74
111
  }
75
112
  //#endregion
76
113
  //#region src/components/SwipeCard.d.ts
77
- /**
78
- * SwipeCard Component
79
- *
80
- * A performant swipeable card component with smooth animations.
81
- * Supports swipe in all four directions: left, right, up, and down.
82
- *
83
- * @example
84
- * ```tsx
85
- * <SwipeCard
86
- * onSwipeLeft={() => console.log('Swiped left')}
87
- * onSwipeRight={() => console.log('Swiped right')}
88
- * threshold={100}
89
- * >
90
- * <div>Your content here</div>
91
- * </SwipeCard>
92
- * ```
93
- */
94
- declare const SwipeCard: React$1.FC<SwipeCardProps>;
114
+ declare const SwipeCard: React$1.NamedExoticComponent<SwipeCardProps>;
95
115
  //#endregion
96
116
  //#region src/hooks/useSwipe.d.ts
97
117
  declare const useSwipe: (callbacks?: SwipeCallbacks, config?: SwipeConfig) => UseSwipeReturn;
98
118
  //#endregion
99
- export { type GestureState, type SwipeCallbacks, SwipeCard, type SwipeCardProps, type SwipeConfig, type SwipeDirection, type UseSwipeReturn, useSwipe };
119
+ export { type GestureState, type SwipeCallbacks, SwipeCard, type SwipeCardProps, type SwipeConfig, type SwipeDirection, type SwipeDirectionStyle, type SwipeOverlayConfig, type SwipeStyles, type UseSwipeReturn, useSwipe };
100
120
  //# sourceMappingURL=index.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../src/types/index.ts","../src/components/SwipeCard.tsx","../src/hooks/useSwipe.ts"],"sourcesContent":[],"mappings":";;;;;;AAKA;AAKiB,KALL,cAAA,GAKmB,MAAA,GAAA,OAAA,GAAA,IAAA,GAAA,MAAA;AAY/B;AAkBA;;AAMU,UApCO,cAAA,CAoCP;EAN8B,WAAA,CAAA,EAAA,GAAA,GAAA,IAAA;EAAgB,YAAA,CAAA,EAAA,GAAA,GAAA,IAAA;EAAW,SAAA,CAAA,EAAA,GAAA,GAAA,IAAA;EAclD,WAAA,CAAA,EAAA,GAAY,GAAA,IAAA;EAgBZ,YAAA,CAAA,EAAA,GAAc,GAAA,IAAA;;;;ACjD/B;;UDCiB,WAAA;;EEHJ,SAAA,CAAA,EAgOZ,MAAA;EA/NY;EACH,iBAAA,CAAA,EAAA,MAAA;EACP;EA6NF,WAAA,CAAA,EAAA,MAAA;;;;;;;;;;;UF3MgB,cAAA,SAAuB,gBAAgB;;YAE5C;;;;UAIF;;;;;;;UAQO,YAAA;;;;;;;;;aASJ;;;;;;UAOI,cAAA;OACV,KAAA,CAAM,UAAU;;;;;;;;;AAlEvB;AAKA;AAYA;AAkBA;;;;;;AAcA;AAgBA;;;;ACjDA;;cAAa,WAAW,OAAA,CAAM,GAAG;;;cCFpB,uBACA,yBACH,gBACP"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/types/index.ts","../src/components/SwipeCard.tsx","../src/hooks/useSwipe.ts"],"sourcesContent":[],"mappings":";;;;;;AAKA;AAKiB,KALL,cAAA,GAKmB,MAAA,GAAA,OAAA,GAAA,IAAA,GAAA,MAAA;AAY/B;AAoBA;AAYA;AAEU,UA9CO,cAAA,CA8CP;EAED,WAAA,CAAA,EAAA,GAAA,GAAA,IAAA;EAEF,YAAA,CAAA,EAAA,GAAA,GAAA,IAAA;EAEE,SAAA,CAAA,EAAA,GAAA,GAAA,IAAA;EAAmB,WAAA,CAAA,EAAA,GAAA,GAAA,IAAA;EAMX,YAAA,CAAA,EAAA,GAAA,GAAkB,IAAA;EAUlB,UAAA,CAAA,EAAA,GAAA,GAAe,IAAA;;;;;AAAqC,UAxDpD,WAAA,CAwDoD;EAAkB;EActE,SAAA,CAAA,EAAA,MAAY;EAgBZ;;;;ECyGJ;;;;ECjMA;EACA,cAAA,CAAA,EAAA,OAAA;EACH;EACP,YAAA,CAAA,EFac,cEbd,EAAA;;;;;UFmBc,mBAAA;;;;;;eAMF;;;;;UAME,WAAA;;UAEP;;SAED;;OAEF;;SAEE;;;;;UAMQ,kBAAA;;gBAED;;;;;;;UAQC,cAAA,SAAuB,gBAAgB,aAAa;;YAEzD;;;;UAIF;;;;;;;UAQO,YAAA;;;;;;;;;aASJ;;;;;;UAOI,cAAA;OACV,KAAA,CAAM,UAAU;;;;;;;;;;cCwGV,WAAS,OAAA,CAAA,qBAAA;;;cCjMT,uBACA,yBACH,gBACP"}
package/dist/index.mjs CHANGED
@@ -1,2 +1,2 @@
1
- import e,{useCallback as t,useEffect as n,useRef as r,useState as i}from"react";const a=(e,t)=>t===0?0:Math.abs(e/t),o=(e,t,n=50)=>{let r=Math.abs(e),i=Math.abs(t);return r<n&&i<n?null:r>i?e>0?`right`:`left`:t>0?`down`:`up`},s=(e,t=15,n=300)=>{let r=e/n*t;return Math.max(-t,Math.min(t,r))},c=(e,t,n=100)=>{let r=1-Math.sqrt(e*e+t*t)/(n*3);return Math.max(.5,Math.min(1,r))},l=e=>`touches`in e&&e.touches&&e.touches.length>0?{x:e.touches[0].clientX,y:e.touches[0].clientY}:`clientX`in e&&`clientY`in e?{x:e.clientX,y:e.clientY}:{x:0,y:0},u={threshold:100,velocityThreshold:.5,maxRotation:15,exitDuration:300,returnDuration:200,enableRotation:!0},d=(e={},d={})=>{let{threshold:f,velocityThreshold:p,maxRotation:m,exitDuration:h,returnDuration:g,enableRotation:_}={...u,...d},v=r(null),y=r(null),[b,x]=i({isDragging:!1,startX:0,startY:0,currentX:0,currentY:0,deltaX:0,deltaY:0,velocity:0,direction:null,startTime:0}),[S,C]=i(`translate3d(0px, 0px, 0px) rotate(0deg)`),[w,T]=i(1),[E,D]=i(`none`),O=t(t=>{let n=l(t),r=Date.now();x({isDragging:!0,startX:n.x,startY:n.y,currentX:n.x,currentY:n.y,deltaX:0,deltaY:0,velocity:0,direction:null,startTime:r}),D(`none`),e.onSwipeStart&&e.onSwipeStart()},[e]),k=t(e=>{if(!b.isDragging)return;let t=l(e),n=t.x-b.startX,r=t.y-b.startY;x(e=>({...e,currentX:t.x,currentY:t.y,deltaX:n,deltaY:r})),y.current&&cancelAnimationFrame(y.current),y.current=requestAnimationFrame(()=>{let e=_?s(n,m):0,t=c(n,r,f);C(`translate3d(${n}px, ${r}px, 0px) rotate(${e}deg)`),T(t)})},[b.isDragging,b.startX,b.startY,_,m,f]),A=t(()=>{if(!b.isDragging)return;let{deltaX:t,deltaY:n,startTime:r}=b,i=Date.now()-r,c=Math.sqrt(t*t+n*n),l=a(c,i),u=o(t,n,f/2);if((c>=f||l>=p)&&u){D(`transform ${h}ms ease-out, opacity ${h}ms ease-out`);let r=t*2;C(`translate3d(${r}px, ${n*2}px, 0px) rotate(${_?s(r,m*2):0}deg)`),T(0),setTimeout(()=>{switch(u){case`left`:e.onSwipeLeft?.();break;case`right`:e.onSwipeRight?.();break;case`up`:e.onSwipeUp?.();break;case`down`:e.onSwipeDown?.();break}e.onSwipeEnd&&e.onSwipeEnd(),setTimeout(()=>{D(`none`),C(`translate3d(0px, 0px, 0px) rotate(0deg)`),T(1)},50)},h)}else D(`transform ${g}ms cubic-bezier(0.175, 0.885, 0.32, 1.275), opacity ${g}ms ease-out`),C(`translate3d(0px, 0px, 0px) rotate(0deg)`),T(1),e.onSwipeEnd&&e.onSwipeEnd();x(e=>({...e,isDragging:!1}))},[b,f,p,h,g,_,m,e]);return n(()=>{let e=v.current;if(!e)return;let t=e=>{O(e)},n=e=>{b.isDragging&&k(e)},r=()=>{A()};return e.addEventListener(`pointerdown`,t),b.isDragging&&(document.addEventListener(`pointermove`,n),document.addEventListener(`pointerup`,r),document.addEventListener(`pointercancel`,r)),()=>{e.removeEventListener(`pointerdown`,t),document.removeEventListener(`pointermove`,n),document.removeEventListener(`pointerup`,r),document.removeEventListener(`pointercancel`,r),y.current&&cancelAnimationFrame(y.current)}},[b.isDragging,O,k,A]),{ref:v,transform:S,opacity:w,transition:E,isDragging:b.isDragging}},f=({children:t,className:n=``,style:r={},onSwipeLeft:i,onSwipeRight:a,onSwipeUp:o,onSwipeDown:s,onSwipeStart:c,onSwipeEnd:l,threshold:u=100,velocityThreshold:f=.5,maxRotation:p=15,exitDuration:m=300,returnDuration:h=200,enableRotation:g=!0})=>{let{ref:_,transform:v,opacity:y,transition:b,isDragging:x}=d({onSwipeLeft:i,onSwipeRight:a,onSwipeUp:o,onSwipeDown:s,onSwipeStart:c,onSwipeEnd:l},{threshold:u,velocityThreshold:f,maxRotation:p,exitDuration:m,returnDuration:h,enableRotation:g}),S={...r,transform:v,opacity:y,transition:b,touchAction:`none`,userSelect:`none`,WebkitUserSelect:`none`,cursor:x?`grabbing`:`grab`,willChange:x?`transform, opacity`:`auto`,position:`relative`};return e.createElement(`div`,{ref:_,className:`swipeon-card ${n}`,style:S},t)};export{f as SwipeCard,d as useSwipe};
1
+ import e,{memo as t,useCallback as n,useEffect as r,useMemo as i,useRef as a,useState as o}from"react";const s=(e,t)=>t===0?0:Math.abs(e/t),c=(e,t,n=50)=>{let r=Math.abs(e),i=Math.abs(t);return r<n&&i<n?null:r>i?e>0?`right`:`left`:t>0?`down`:`up`},l=(e,t=15,n=300)=>{let r=e/n*t;return Math.max(-t,Math.min(t,r))},u=(e,t,n=100)=>{let r=1-Math.sqrt(e*e+t*t)/(n*3);return Math.max(.5,Math.min(1,r))},d=e=>`touches`in e&&e.touches&&e.touches.length>0?{x:e.touches[0].clientX,y:e.touches[0].clientY}:`clientX`in e&&`clientY`in e?{x:e.clientX,y:e.clientY}:{x:0,y:0},f={threshold:100,velocityThreshold:.5,maxRotation:15,exitDuration:300,returnDuration:200,enableRotation:!0,preventSwipe:[]},p=(e={},t={})=>{let{threshold:p,velocityThreshold:m,maxRotation:h,exitDuration:g,returnDuration:_,enableRotation:v,preventSwipe:y}=i(()=>({...f,...t}),[t.threshold,t.velocityThreshold,t.maxRotation,t.exitDuration,t.returnDuration,t.enableRotation,JSON.stringify(t.preventSwipe)]),b=a(null),x=a(null),S=a(new Set),C=a(e);C.current=e;let w=a(`translate3d(0px, 0px, 0px) rotate(0deg)`),T=a(1),E=a(`none`),[D,O]=o({transform:`translate3d(0px, 0px, 0px) rotate(0deg)`,opacity:1,transition:`none`}),[k,A]=o({isDragging:!1,startX:0,startY:0,currentX:0,currentY:0,deltaX:0,deltaY:0,velocity:0,direction:null,startTime:0}),j=n(e=>{O(t=>{let n={...t,...e};return t.transform===n.transform&&t.opacity===n.opacity&&t.transition===n.transition?t:n})},[]),M=n((e,t)=>{let n=setTimeout(()=>{S.current.delete(n),e()},t);return S.current.add(n),n},[]),N=n(e=>{let t=d(e),n=Date.now();A({isDragging:!0,startX:t.x,startY:t.y,currentX:t.x,currentY:t.y,deltaX:0,deltaY:0,velocity:0,direction:null,startTime:n}),E.current=`none`,j({transition:`none`}),C.current.onSwipeStart?.()},[j]),P=a(k);P.current=k;let F=n(e=>{let t=P.current;if(!t.isDragging)return;let n=d(e),r=n.x-t.startX,i=n.y-t.startY;y.includes(`left`)&&r<0&&(r=0),y.includes(`right`)&&r>0&&(r=0),y.includes(`up`)&&i<0&&(i=0),y.includes(`down`)&&i>0&&(i=0),A(e=>({...e,currentX:n.x,currentY:n.y,deltaX:r,deltaY:i})),x.current&&cancelAnimationFrame(x.current),x.current=requestAnimationFrame(()=>{let e=v?l(r,h):0,t=u(r,i,p),n=`translate3d(${r}px, ${i}px, 0px) rotate(${e}deg)`;w.current=n,T.current=t,j({transform:n,opacity:t})})},[v,h,p,y,j]),I=n(()=>{let e=P.current;if(!e.isDragging)return;let{deltaX:t,deltaY:n,startTime:r}=e,i=Date.now()-r,a=Math.sqrt(t*t+n*n),o=s(a,i),u=c(t,n,p/2),d=a>=p||o>=m,f=u&&y.includes(u);if(d&&u&&!f){let e=`transform ${g}ms ease-out, opacity ${g}ms ease-out`,r=t*2;j({transition:e,transform:`translate3d(${r}px, ${n*2}px, 0px) rotate(${v?l(r,h*2):0}deg)`,opacity:0}),M(()=>{switch(u){case`left`:C.current.onSwipeLeft?.();break;case`right`:C.current.onSwipeRight?.();break;case`up`:C.current.onSwipeUp?.();break;case`down`:C.current.onSwipeDown?.();break}C.current.onSwipeEnd?.(),M(()=>{j({transition:`none`,transform:`translate3d(0px, 0px, 0px) rotate(0deg)`,opacity:1})},50)},g)}else j({transition:`transform ${_}ms cubic-bezier(0.175, 0.885, 0.32, 1.275), opacity ${_}ms ease-out`,transform:`translate3d(0px, 0px, 0px) rotate(0deg)`,opacity:1}),C.current.onSwipeEnd?.();A(e=>({...e,isDragging:!1}))},[p,m,g,_,v,h,y,j,M]),L=a({handleSwipeStart:N,handleSwipeMove:F,handleSwipeEnd:I});return L.current={handleSwipeStart:N,handleSwipeMove:F,handleSwipeEnd:I},r(()=>{let e=b.current;if(!e)return;let t=e=>{L.current.handleSwipeStart(e)};return e.addEventListener(`pointerdown`,t),()=>{e.removeEventListener(`pointerdown`,t)}},[]),r(()=>{if(!k.isDragging)return;let e=e=>{L.current.handleSwipeMove(e)},t=()=>{L.current.handleSwipeEnd()};return document.addEventListener(`pointermove`,e),document.addEventListener(`pointerup`,t),document.addEventListener(`pointercancel`,t),()=>{document.removeEventListener(`pointermove`,e),document.removeEventListener(`pointerup`,t),document.removeEventListener(`pointercancel`,t)}},[k.isDragging]),r(()=>()=>{x.current&&cancelAnimationFrame(x.current),S.current.forEach(e=>clearTimeout(e)),S.current.clear()},[]),{ref:b,transform:D.transform,opacity:D.opacity,transition:D.transition,isDragging:k.isDragging,deltaX:k.deltaX,deltaY:k.deltaY}},m={fontSize:`2rem`,fontWeight:`bold`,color:`#fff`,textTransform:`uppercase`,letterSpacing:`2px`,textShadow:`0 2px 8px rgba(0, 0, 0, 0.3)`},h={right:{backgroundColor:`rgba(34, 197, 94, 0.6)`,label:`✓ Accept`,labelStyle:m},left:{backgroundColor:`rgba(239, 68, 68, 0.6)`,label:`✗ Reject`,labelStyle:m},up:{backgroundColor:`rgba(59, 130, 246, 0.6)`,label:`⭐ Super`,labelStyle:m},down:{backgroundColor:`rgba(168, 85, 247, 0.6)`,label:`⏭ Skip`,labelStyle:m}},g=[],_=t(({children:t,className:n=``,style:r,onSwipeLeft:a,onSwipeRight:o,onSwipeUp:s,onSwipeDown:c,onSwipeStart:l,onSwipeEnd:u,threshold:d=100,velocityThreshold:f=.5,maxRotation:m=15,exitDuration:_=300,returnDuration:v=200,enableRotation:y=!0,preventSwipe:b=g,swipeStyles:x,showOverlay:S=!0})=>{let{ref:C,transform:w,opacity:T,transition:E,isDragging:D,deltaX:O,deltaY:k}=p(i(()=>({onSwipeLeft:a,onSwipeRight:o,onSwipeUp:s,onSwipeDown:c,onSwipeStart:l,onSwipeEnd:u}),[a,o,s,c,l,u]),i(()=>({threshold:d,velocityThreshold:f,maxRotation:m,exitDuration:_,returnDuration:v,enableRotation:y,preventSwipe:b}),[d,f,m,_,v,y,b])),A=i(()=>({right:{...h.right,...x?.right,labelStyle:{...h.right.labelStyle,...x?.right?.labelStyle}},left:{...h.left,...x?.left,labelStyle:{...h.left.labelStyle,...x?.left?.labelStyle}},up:{...h.up,...x?.up,labelStyle:{...h.up.labelStyle,...x?.up?.labelStyle}},down:{...h.down,...x?.down,labelStyle:{...h.down.labelStyle,...x?.down?.labelStyle}}}),[x?.right,x?.left,x?.up,x?.down]),j=Math.abs(O),M=Math.abs(k),N=j>=M,P=N?j:M,F=Math.min(P/d,1),I=N&&O>20,L=N&&O<-20,R=!N&&k<-20,z=!N&&k>20,B=F>=.15,V=S&&D&&P>10,H=i(()=>I?`right`:L?`left`:R?`up`:z?`down`:null,[I,L,R,z]),U=H?A[H]:null,W=U?.backgroundColor??`transparent`,G=U?{label:U.label,labelStyle:U.labelStyle}:null,K=i(()=>({...r,transform:w,opacity:T,transition:E,touchAction:`none`,userSelect:`none`,WebkitUserSelect:`none`,cursor:D?`grabbing`:`grab`,willChange:D?`transform, opacity`:`auto`,position:`absolute`}),[r,w,T,E,D]),q=i(()=>({position:`absolute`,top:0,left:0,right:0,bottom:0,display:`flex`,alignItems:`center`,justifyContent:`center`,borderRadius:`inherit`,pointerEvents:`none`,zIndex:10,opacity:V?F:0,transition:D?`none`:`opacity 200ms ease-out`,backgroundColor:V?W:`transparent`}),[V,F,D,W]),J=i(()=>({...G?.labelStyle,transform:`scale(${.8+F*.4})`,opacity:F}),[G?.labelStyle,F]);return e.createElement(`div`,{ref:C,className:`swipeon-card ${n}`,style:K},t,e.createElement(`div`,{style:q},V&&B&&G?.label&&e.createElement(`span`,{style:J},G.label)))});export{_ as SwipeCard,p as useSwipe};
2
2
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":["DEFAULT_CONFIG: Required<SwipeConfig>","SwipeCard: React.FC<SwipeCardProps>","cardStyle: CSSProperties"],"sources":["../src/utils/helpers.ts","../src/hooks/useSwipe.ts","../src/components/SwipeCard.tsx"],"sourcesContent":["import { SwipeDirection } from '../types';\n\n/**\n * Calculate the distance between two points\n */\nexport const getDistance = (x1: number, y1: number, x2: number, y2: number): number => {\n const dx = x2 - x1;\n const dy = y2 - y1;\n return Math.sqrt(dx * dx + dy * dy);\n};\n\n/**\n * Calculate velocity based on distance and time\n */\nexport const calculateVelocity = (distance: number, time: number): number => {\n if (time === 0) return 0;\n return Math.abs(distance / time);\n};\n\n/**\n * Determine the swipe direction based on deltas\n */\nexport const getSwipeDirection = (\n deltaX: number,\n deltaY: number,\n threshold: number = 50\n): SwipeDirection | null => {\n const absDeltaX = Math.abs(deltaX);\n const absDeltaY = Math.abs(deltaY);\n\n // Need minimum movement\n if (absDeltaX < threshold && absDeltaY < threshold) {\n return null;\n }\n\n // Determine primary direction\n if (absDeltaX > absDeltaY) {\n return deltaX > 0 ? 'right' : 'left';\n } else {\n return deltaY > 0 ? 'down' : 'up';\n }\n};\n\n/**\n * Calculate rotation angle based on horizontal movement\n */\nexport const calculateRotation = (\n deltaX: number,\n maxRotation: number = 15,\n containerWidth: number = 300\n): number => {\n const rotation = (deltaX / containerWidth) * maxRotation;\n return Math.max(-maxRotation, Math.min(maxRotation, rotation));\n};\n\n/**\n * Calculate opacity based on drag distance\n */\nexport const calculateOpacity = (\n deltaX: number,\n deltaY: number,\n threshold: number = 100\n): number => {\n const distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);\n const opacity = 1 - distance / (threshold * 3);\n return Math.max(0.5, Math.min(1, opacity));\n};\n\n/**\n * Clamp a value between min and max\n */\nexport const clamp = (value: number, min: number, max: number): number => {\n return Math.max(min, Math.min(max, value));\n};\n\n/**\n * Get touch/pointer coordinates from event\n */\nexport const getEventCoordinates = (\n event: TouchEvent | PointerEvent | MouseEvent | any\n): { x: number; y: number } => {\n if ('touches' in event && event.touches && event.touches.length > 0) {\n return {\n x: event.touches[0].clientX,\n y: event.touches[0].clientY,\n };\n }\n \n if ('clientX' in event && 'clientY' in event) {\n return {\n x: event.clientX,\n y: event.clientY,\n };\n }\n \n return { x: 0, y: 0 };\n};\n\n","import { useRef, useState, useCallback, useEffect } from 'react';\nimport { SwipeConfig, SwipeCallbacks, GestureState, UseSwipeReturn } from '../types';\nimport {\n getSwipeDirection,\n calculateRotation,\n calculateOpacity,\n calculateVelocity,\n getEventCoordinates,\n} from '../utils/helpers';\n\nconst DEFAULT_CONFIG: Required<SwipeConfig> = {\n threshold: 100,\n velocityThreshold: 0.5,\n maxRotation: 15,\n exitDuration: 300,\n returnDuration: 200,\n enableRotation: true,\n};\n\nexport const useSwipe = (\n callbacks: SwipeCallbacks = {},\n config: SwipeConfig = {}\n): UseSwipeReturn => {\n const mergedConfig = { ...DEFAULT_CONFIG, ...config };\n const {\n threshold,\n velocityThreshold,\n maxRotation,\n exitDuration,\n returnDuration,\n enableRotation,\n } = mergedConfig;\n\n const ref = useRef<HTMLDivElement>(null);\n const animationFrameRef = useRef<number | null>(null);\n const [gestureState, setGestureState] = useState<GestureState>({\n isDragging: false,\n startX: 0,\n startY: 0,\n currentX: 0,\n currentY: 0,\n deltaX: 0,\n deltaY: 0,\n velocity: 0,\n direction: null,\n startTime: 0,\n });\n\n const [transform, setTransform] = useState('translate3d(0px, 0px, 0px) rotate(0deg)');\n const [opacity, setOpacity] = useState(1);\n const [transition, setTransition] = useState('none');\n\n // Handle swipe start\n const handleSwipeStart = useCallback(\n (event: PointerEvent | TouchEvent | MouseEvent) => {\n const coords = getEventCoordinates(event as any);\n const now = Date.now();\n\n setGestureState({\n isDragging: true,\n startX: coords.x,\n startY: coords.y,\n currentX: coords.x,\n currentY: coords.y,\n deltaX: 0,\n deltaY: 0,\n velocity: 0,\n direction: null,\n startTime: now,\n });\n\n setTransition('none');\n \n if (callbacks.onSwipeStart) {\n callbacks.onSwipeStart();\n }\n },\n [callbacks]\n );\n\n // Handle swipe move\n const handleSwipeMove = useCallback(\n (event: PointerEvent | TouchEvent | MouseEvent) => {\n if (!gestureState.isDragging) return;\n\n const coords = getEventCoordinates(event as any);\n const deltaX = coords.x - gestureState.startX;\n const deltaY = coords.y - gestureState.startY;\n\n // Update gesture state\n setGestureState((prev: GestureState) => ({\n ...prev,\n currentX: coords.x,\n currentY: coords.y,\n deltaX,\n deltaY,\n }));\n\n // Use RAF for smooth updates\n if (animationFrameRef.current) {\n cancelAnimationFrame(animationFrameRef.current);\n }\n\n animationFrameRef.current = requestAnimationFrame(() => {\n const rotation = enableRotation ? calculateRotation(deltaX, maxRotation) : 0;\n const newOpacity = calculateOpacity(deltaX, deltaY, threshold);\n\n setTransform(\n `translate3d(${deltaX}px, ${deltaY}px, 0px) rotate(${rotation}deg)`\n );\n setOpacity(newOpacity);\n });\n },\n [gestureState.isDragging, gestureState.startX, gestureState.startY, enableRotation, maxRotation, threshold]\n );\n\n // Handle swipe end\n const handleSwipeEnd = useCallback(() => {\n if (!gestureState.isDragging) return;\n\n const { deltaX, deltaY, startTime } = gestureState;\n const timeDelta = Date.now() - startTime;\n const distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);\n const velocity = calculateVelocity(distance, timeDelta);\n\n const direction = getSwipeDirection(deltaX, deltaY, threshold / 2);\n const shouldSwipe =\n distance >= threshold || velocity >= velocityThreshold;\n\n if (shouldSwipe && direction) {\n // Execute swipe\n setTransition(`transform ${exitDuration}ms ease-out, opacity ${exitDuration}ms ease-out`);\n \n // Calculate exit position (move far enough off screen)\n const multiplier = 2;\n const exitX = deltaX * multiplier;\n const exitY = deltaY * multiplier;\n const rotation = enableRotation ? calculateRotation(exitX, maxRotation * 2) : 0;\n\n setTransform(\n `translate3d(${exitX}px, ${exitY}px, 0px) rotate(${rotation}deg)`\n );\n setOpacity(0);\n\n // Trigger callback after animation\n setTimeout(() => {\n switch (direction) {\n case 'left':\n callbacks.onSwipeLeft?.();\n break;\n case 'right':\n callbacks.onSwipeRight?.();\n break;\n case 'up':\n callbacks.onSwipeUp?.();\n break;\n case 'down':\n callbacks.onSwipeDown?.();\n break;\n }\n \n if (callbacks.onSwipeEnd) {\n callbacks.onSwipeEnd();\n }\n\n // Reset after callback\n setTimeout(() => {\n setTransition('none');\n setTransform('translate3d(0px, 0px, 0px) rotate(0deg)');\n setOpacity(1);\n }, 50);\n }, exitDuration);\n } else {\n // Spring back\n setTransition(\n `transform ${returnDuration}ms cubic-bezier(0.175, 0.885, 0.32, 1.275), opacity ${returnDuration}ms ease-out`\n );\n setTransform('translate3d(0px, 0px, 0px) rotate(0deg)');\n setOpacity(1);\n\n if (callbacks.onSwipeEnd) {\n callbacks.onSwipeEnd();\n }\n }\n\n setGestureState((prev: GestureState) => ({ ...prev, isDragging: false }));\n }, [\n gestureState,\n threshold,\n velocityThreshold,\n exitDuration,\n returnDuration,\n enableRotation,\n maxRotation,\n callbacks,\n ]);\n\n // Attach event listeners\n useEffect(() => {\n const element = ref.current;\n if (!element) return;\n\n const handlePointerDown = (e: PointerEvent) => {\n handleSwipeStart(e);\n };\n\n const handlePointerMove = (e: PointerEvent) => {\n if (gestureState.isDragging) {\n handleSwipeMove(e);\n }\n };\n\n const handlePointerUp = () => {\n handleSwipeEnd();\n };\n\n element.addEventListener('pointerdown', handlePointerDown);\n\n if (gestureState.isDragging) {\n document.addEventListener('pointermove', handlePointerMove);\n document.addEventListener('pointerup', handlePointerUp);\n document.addEventListener('pointercancel', handlePointerUp);\n }\n\n return () => {\n element.removeEventListener('pointerdown', handlePointerDown);\n document.removeEventListener('pointermove', handlePointerMove);\n document.removeEventListener('pointerup', handlePointerUp);\n document.removeEventListener('pointercancel', handlePointerUp);\n \n if (animationFrameRef.current) {\n cancelAnimationFrame(animationFrameRef.current);\n }\n };\n }, [gestureState.isDragging, handleSwipeStart, handleSwipeMove, handleSwipeEnd]);\n\n return {\n ref,\n transform,\n opacity,\n transition,\n isDragging: gestureState.isDragging,\n };\n};\n\n","import React, { CSSProperties } from 'react';\nimport { useSwipe } from '../hooks/useSwipe';\nimport { SwipeCardProps } from '../types';\n\n/**\n * SwipeCard Component\n * \n * A performant swipeable card component with smooth animations.\n * Supports swipe in all four directions: left, right, up, and down.\n * \n * @example\n * ```tsx\n * <SwipeCard\n * onSwipeLeft={() => console.log('Swiped left')}\n * onSwipeRight={() => console.log('Swiped right')}\n * threshold={100}\n * >\n * <div>Your content here</div>\n * </SwipeCard>\n * ```\n */\nexport const SwipeCard: React.FC<SwipeCardProps> = ({\n children,\n className = '',\n style = {},\n onSwipeLeft,\n onSwipeRight,\n onSwipeUp,\n onSwipeDown,\n onSwipeStart,\n onSwipeEnd,\n threshold = 100,\n velocityThreshold = 0.5,\n maxRotation = 15,\n exitDuration = 300,\n returnDuration = 200,\n enableRotation = true,\n}) => {\n const callbacks = {\n onSwipeLeft,\n onSwipeRight,\n onSwipeUp,\n onSwipeDown,\n onSwipeStart,\n onSwipeEnd,\n };\n\n const config = {\n threshold,\n velocityThreshold,\n maxRotation,\n exitDuration,\n returnDuration,\n enableRotation,\n };\n\n const { ref, transform, opacity, transition, isDragging } = useSwipe(callbacks, config);\n\n const cardStyle: CSSProperties = {\n ...style,\n transform,\n opacity,\n transition,\n touchAction: 'none', // Prevent default touch behaviors\n userSelect: 'none', // Prevent text selection during drag\n WebkitUserSelect: 'none',\n cursor: isDragging ? 'grabbing' : 'grab',\n willChange: isDragging ? 'transform, opacity' : 'auto',\n position: 'relative',\n };\n\n return (\n <div\n ref={ref}\n className={`swipeon-card ${className}`}\n style={cardStyle}\n >\n {children}\n </div>\n );\n};\n\nexport default SwipeCard;\n\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":["DEFAULT_CONFIG: Required<SwipeConfig>","DEFAULT_LABEL_STYLE: CSSProperties","EMPTY_PREVENT_SWIPE: readonly string[]","SwipeCardInner: React.FC<SwipeCardProps>","cardStyle: CSSProperties","overlayStyle: CSSProperties","computedLabelStyle: CSSProperties"],"sources":["../src/utils/helpers.ts","../src/hooks/useSwipe.ts","../src/components/SwipeCard.tsx"],"sourcesContent":["import { SwipeDirection } from '../types';\n\n/**\n * Calculate the distance between two points\n */\nexport const getDistance = (x1: number, y1: number, x2: number, y2: number): number => {\n const dx = x2 - x1;\n const dy = y2 - y1;\n return Math.sqrt(dx * dx + dy * dy);\n};\n\n/**\n * Calculate velocity based on distance and time\n */\nexport const calculateVelocity = (distance: number, time: number): number => {\n if (time === 0) return 0;\n return Math.abs(distance / time);\n};\n\n/**\n * Determine the swipe direction based on deltas\n */\nexport const getSwipeDirection = (\n deltaX: number,\n deltaY: number,\n threshold: number = 50\n): SwipeDirection | null => {\n const absDeltaX = Math.abs(deltaX);\n const absDeltaY = Math.abs(deltaY);\n\n // Need minimum movement\n if (absDeltaX < threshold && absDeltaY < threshold) {\n return null;\n }\n\n // Determine primary direction\n if (absDeltaX > absDeltaY) {\n return deltaX > 0 ? 'right' : 'left';\n } else {\n return deltaY > 0 ? 'down' : 'up';\n }\n};\n\n/**\n * Calculate rotation angle based on horizontal movement\n */\nexport const calculateRotation = (\n deltaX: number,\n maxRotation: number = 15,\n containerWidth: number = 300\n): number => {\n const rotation = (deltaX / containerWidth) * maxRotation;\n return Math.max(-maxRotation, Math.min(maxRotation, rotation));\n};\n\n/**\n * Calculate opacity based on drag distance\n */\nexport const calculateOpacity = (\n deltaX: number,\n deltaY: number,\n threshold: number = 100\n): number => {\n const distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);\n const opacity = 1 - distance / (threshold * 3);\n return Math.max(0.5, Math.min(1, opacity));\n};\n\n/**\n * Clamp a value between min and max\n */\nexport const clamp = (value: number, min: number, max: number): number => {\n return Math.max(min, Math.min(max, value));\n};\n\n/**\n * Get touch/pointer coordinates from event\n */\nexport const getEventCoordinates = (\n event: TouchEvent | PointerEvent | MouseEvent | any\n): { x: number; y: number } => {\n if ('touches' in event && event.touches && event.touches.length > 0) {\n return {\n x: event.touches[0].clientX,\n y: event.touches[0].clientY,\n };\n }\n \n if ('clientX' in event && 'clientY' in event) {\n return {\n x: event.clientX,\n y: event.clientY,\n };\n }\n \n return { x: 0, y: 0 };\n};\n\n","import { useRef, useState, useCallback, useEffect, useMemo } from 'react';\nimport { SwipeConfig, SwipeCallbacks, GestureState, UseSwipeReturn } from '../types';\nimport {\n getSwipeDirection,\n calculateRotation,\n calculateOpacity,\n calculateVelocity,\n getEventCoordinates,\n} from '../utils/helpers';\n\nconst DEFAULT_CONFIG: Required<SwipeConfig> = {\n threshold: 100,\n velocityThreshold: 0.5,\n maxRotation: 15,\n exitDuration: 300,\n returnDuration: 200,\n enableRotation: true,\n preventSwipe: [],\n};\n\nexport const useSwipe = (\n callbacks: SwipeCallbacks = {},\n config: SwipeConfig = {}\n): UseSwipeReturn => {\n // Memoize config to prevent unnecessary recalculations\n const mergedConfig = useMemo(() => ({ ...DEFAULT_CONFIG, ...config }), [\n config.threshold,\n config.velocityThreshold,\n config.maxRotation,\n config.exitDuration,\n config.returnDuration,\n config.enableRotation,\n // Use JSON for array comparison (preventSwipe is typically small)\n JSON.stringify(config.preventSwipe),\n ]);\n\n const {\n threshold,\n velocityThreshold,\n maxRotation,\n exitDuration,\n returnDuration,\n enableRotation,\n preventSwipe,\n } = mergedConfig;\n\n const ref = useRef<HTMLDivElement>(null);\n const animationFrameRef = useRef<number | null>(null);\n const timeoutRefs = useRef<Set<ReturnType<typeof setTimeout>>>(new Set());\n \n // Store callbacks in ref to avoid dependency issues\n const callbacksRef = useRef(callbacks);\n callbacksRef.current = callbacks;\n\n // Use refs for animation values that don't need to trigger re-renders during drag\n const transformRef = useRef('translate3d(0px, 0px, 0px) rotate(0deg)');\n const opacityRef = useRef(1);\n const transitionRef = useRef('none');\n\n // Single state for values that need to trigger re-renders\n const [renderState, setRenderState] = useState({\n transform: 'translate3d(0px, 0px, 0px) rotate(0deg)',\n opacity: 1,\n transition: 'none',\n });\n\n const [gestureState, setGestureState] = useState<GestureState>({\n isDragging: false,\n startX: 0,\n startY: 0,\n currentX: 0,\n currentY: 0,\n deltaX: 0,\n deltaY: 0,\n velocity: 0,\n direction: null,\n startTime: 0,\n });\n\n // Batch update render state\n const updateRenderState = useCallback((updates: Partial<typeof renderState>) => {\n setRenderState(prev => {\n const newState = { ...prev, ...updates };\n // Only update if values actually changed\n if (\n prev.transform === newState.transform &&\n prev.opacity === newState.opacity &&\n prev.transition === newState.transition\n ) {\n return prev;\n }\n return newState;\n });\n }, []);\n\n // Safe setTimeout that tracks timeouts for cleanup\n const safeTimeout = useCallback((fn: () => void, delay: number) => {\n const id = setTimeout(() => {\n timeoutRefs.current.delete(id);\n fn();\n }, delay);\n timeoutRefs.current.add(id);\n return id;\n }, []);\n\n // Handle swipe start\n const handleSwipeStart = useCallback(\n (event: PointerEvent | TouchEvent | MouseEvent) => {\n const coords = getEventCoordinates(event as any);\n const now = Date.now();\n\n setGestureState({\n isDragging: true,\n startX: coords.x,\n startY: coords.y,\n currentX: coords.x,\n currentY: coords.y,\n deltaX: 0,\n deltaY: 0,\n velocity: 0,\n direction: null,\n startTime: now,\n });\n\n transitionRef.current = 'none';\n updateRenderState({ transition: 'none' });\n \n callbacksRef.current.onSwipeStart?.();\n },\n [updateRenderState]\n );\n\n // Handle swipe move - use refs to avoid closure issues\n const gestureStateRef = useRef(gestureState);\n gestureStateRef.current = gestureState;\n\n const handleSwipeMove = useCallback(\n (event: PointerEvent | TouchEvent | MouseEvent) => {\n const state = gestureStateRef.current;\n if (!state.isDragging) return;\n\n const coords = getEventCoordinates(event as any);\n let deltaX = coords.x - state.startX;\n let deltaY = coords.y - state.startY;\n\n // Constrain movement based on preventSwipe directions\n if (preventSwipe.includes('left') && deltaX < 0) deltaX = 0;\n if (preventSwipe.includes('right') && deltaX > 0) deltaX = 0;\n if (preventSwipe.includes('up') && deltaY < 0) deltaY = 0;\n if (preventSwipe.includes('down') && deltaY > 0) deltaY = 0;\n\n // Update gesture state\n setGestureState(prev => ({\n ...prev,\n currentX: coords.x,\n currentY: coords.y,\n deltaX,\n deltaY,\n }));\n\n // Use RAF for smooth updates - cancel previous frame\n if (animationFrameRef.current) {\n cancelAnimationFrame(animationFrameRef.current);\n }\n\n animationFrameRef.current = requestAnimationFrame(() => {\n const rotation = enableRotation ? calculateRotation(deltaX, maxRotation) : 0;\n const newOpacity = calculateOpacity(deltaX, deltaY, threshold);\n const newTransform = `translate3d(${deltaX}px, ${deltaY}px, 0px) rotate(${rotation}deg)`;\n\n // Update refs immediately for internal use\n transformRef.current = newTransform;\n opacityRef.current = newOpacity;\n\n // Batch update to React state\n updateRenderState({\n transform: newTransform,\n opacity: newOpacity,\n });\n });\n },\n [enableRotation, maxRotation, threshold, preventSwipe, updateRenderState]\n );\n\n // Handle swipe end\n const handleSwipeEnd = useCallback(() => {\n const state = gestureStateRef.current;\n if (!state.isDragging) return;\n\n const { deltaX, deltaY, startTime } = state;\n const timeDelta = Date.now() - startTime;\n const distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);\n const velocity = calculateVelocity(distance, timeDelta);\n\n const direction = getSwipeDirection(deltaX, deltaY, threshold / 2);\n const shouldSwipe = distance >= threshold || velocity >= velocityThreshold;\n\n // Check if direction is prevented\n const isDirectionPrevented = direction && preventSwipe.includes(direction);\n\n if (shouldSwipe && direction && !isDirectionPrevented) {\n // Execute swipe\n const exitTransition = `transform ${exitDuration}ms ease-out, opacity ${exitDuration}ms ease-out`;\n \n // Calculate exit position (move far enough off screen)\n const multiplier = 2;\n const exitX = deltaX * multiplier;\n const exitY = deltaY * multiplier;\n const rotation = enableRotation ? calculateRotation(exitX, maxRotation * 2) : 0;\n const exitTransform = `translate3d(${exitX}px, ${exitY}px, 0px) rotate(${rotation}deg)`;\n\n updateRenderState({\n transition: exitTransition,\n transform: exitTransform,\n opacity: 0,\n });\n\n // Trigger callback after animation\n safeTimeout(() => {\n switch (direction) {\n case 'left':\n callbacksRef.current.onSwipeLeft?.();\n break;\n case 'right':\n callbacksRef.current.onSwipeRight?.();\n break;\n case 'up':\n callbacksRef.current.onSwipeUp?.();\n break;\n case 'down':\n callbacksRef.current.onSwipeDown?.();\n break;\n }\n \n callbacksRef.current.onSwipeEnd?.();\n\n // Reset after callback\n safeTimeout(() => {\n updateRenderState({\n transition: 'none',\n transform: 'translate3d(0px, 0px, 0px) rotate(0deg)',\n opacity: 1,\n });\n }, 50);\n }, exitDuration);\n } else {\n // Spring back\n const returnTransition = `transform ${returnDuration}ms cubic-bezier(0.175, 0.885, 0.32, 1.275), opacity ${returnDuration}ms ease-out`;\n \n updateRenderState({\n transition: returnTransition,\n transform: 'translate3d(0px, 0px, 0px) rotate(0deg)',\n opacity: 1,\n });\n\n callbacksRef.current.onSwipeEnd?.();\n }\n\n setGestureState(prev => ({ ...prev, isDragging: false }));\n }, [\n threshold,\n velocityThreshold,\n exitDuration,\n returnDuration,\n enableRotation,\n maxRotation,\n preventSwipe,\n updateRenderState,\n safeTimeout,\n ]);\n\n // Stable event handler refs to avoid re-attaching listeners\n const handlersRef = useRef({\n handleSwipeStart,\n handleSwipeMove,\n handleSwipeEnd,\n });\n handlersRef.current = { handleSwipeStart, handleSwipeMove, handleSwipeEnd };\n\n // Attach event listeners with stable references\n useEffect(() => {\n const element = ref.current;\n if (!element) return;\n\n const onPointerDown = (e: PointerEvent) => {\n handlersRef.current.handleSwipeStart(e);\n };\n\n element.addEventListener('pointerdown', onPointerDown);\n\n return () => {\n element.removeEventListener('pointerdown', onPointerDown);\n };\n }, []); // Empty deps - handlers are accessed via ref\n\n // Attach document listeners when dragging\n useEffect(() => {\n if (!gestureState.isDragging) return;\n\n const onPointerMove = (e: PointerEvent) => {\n handlersRef.current.handleSwipeMove(e);\n };\n\n const onPointerUp = () => {\n handlersRef.current.handleSwipeEnd();\n };\n\n document.addEventListener('pointermove', onPointerMove);\n document.addEventListener('pointerup', onPointerUp);\n document.addEventListener('pointercancel', onPointerUp);\n\n return () => {\n document.removeEventListener('pointermove', onPointerMove);\n document.removeEventListener('pointerup', onPointerUp);\n document.removeEventListener('pointercancel', onPointerUp);\n };\n }, [gestureState.isDragging]);\n\n // Cleanup on unmount\n useEffect(() => {\n return () => {\n if (animationFrameRef.current) {\n cancelAnimationFrame(animationFrameRef.current);\n }\n // Clear all pending timeouts\n timeoutRefs.current.forEach(id => clearTimeout(id));\n timeoutRefs.current.clear();\n };\n }, []);\n\n return {\n ref,\n transform: renderState.transform,\n opacity: renderState.opacity,\n transition: renderState.transition,\n isDragging: gestureState.isDragging,\n deltaX: gestureState.deltaX,\n deltaY: gestureState.deltaY,\n };\n};\n","import React, { CSSProperties, useMemo, memo } from 'react';\nimport { useSwipe } from '../hooks/useSwipe';\nimport { SwipeCardProps, SwipeCallbacks, SwipeConfig } from '../types';\n\n/**\n * SwipeCard Component\n * \n * A performant swipeable card component with smooth animations.\n * Supports swipe in all four directions: left, right, up, and down.\n * \n * @example\n * ```tsx\n * <SwipeCard\n * onSwipeLeft={() => console.log('Swiped left')}\n * onSwipeRight={() => console.log('Swiped right')}\n * threshold={100}\n * swipeStyles={{\n * right: { \n * backgroundColor: 'rgba(34, 197, 94, 0.6)', \n * label: '👍 Like',\n * labelStyle: { position: 'absolute', top: 20, right: 20 }\n * },\n * left: { \n * backgroundColor: 'rgba(239, 68, 68, 0.6)', \n * label: '👎 Nope',\n * labelStyle: { position: 'absolute', top: 20, left: 20 }\n * },\n * }}\n * >\n * <div>Your content here</div>\n * </SwipeCard>\n * ```\n */\n\n// Default label style - constant, no need to recreate\nconst DEFAULT_LABEL_STYLE: CSSProperties = {\n fontSize: '2rem',\n fontWeight: 'bold',\n color: '#fff',\n textTransform: 'uppercase',\n letterSpacing: '2px',\n textShadow: '0 2px 8px rgba(0, 0, 0, 0.3)',\n};\n\n// Default styles for each direction - constant\nconst DEFAULT_SWIPE_STYLES = {\n right: { backgroundColor: 'rgba(34, 197, 94, 0.6)', label: '✓ Accept', labelStyle: DEFAULT_LABEL_STYLE },\n left: { backgroundColor: 'rgba(239, 68, 68, 0.6)', label: '✗ Reject', labelStyle: DEFAULT_LABEL_STYLE },\n up: { backgroundColor: 'rgba(59, 130, 246, 0.6)', label: '⭐ Super', labelStyle: DEFAULT_LABEL_STYLE },\n down: { backgroundColor: 'rgba(168, 85, 247, 0.6)', label: '⏭ Skip', labelStyle: DEFAULT_LABEL_STYLE },\n} as const;\n\n// Empty array constant to prevent recreating on each render\nconst EMPTY_PREVENT_SWIPE: readonly string[] = [];\n\nconst SwipeCardInner: React.FC<SwipeCardProps> = ({\n children,\n className = '',\n style,\n onSwipeLeft,\n onSwipeRight,\n onSwipeUp,\n onSwipeDown,\n onSwipeStart,\n onSwipeEnd,\n threshold = 100,\n velocityThreshold = 0.5,\n maxRotation = 15,\n exitDuration = 300,\n returnDuration = 200,\n enableRotation = true,\n preventSwipe = EMPTY_PREVENT_SWIPE as any,\n swipeStyles,\n showOverlay: showOverlayProp = true,\n}) => {\n // Memoize callbacks object to prevent unnecessary hook updates\n const callbacks: SwipeCallbacks = useMemo(() => ({\n onSwipeLeft,\n onSwipeRight,\n onSwipeUp,\n onSwipeDown,\n onSwipeStart,\n onSwipeEnd,\n }), [onSwipeLeft, onSwipeRight, onSwipeUp, onSwipeDown, onSwipeStart, onSwipeEnd]);\n\n // Memoize config object\n const config: SwipeConfig = useMemo(() => ({\n threshold,\n velocityThreshold,\n maxRotation,\n exitDuration,\n returnDuration,\n enableRotation,\n preventSwipe,\n }), [threshold, velocityThreshold, maxRotation, exitDuration, returnDuration, enableRotation, preventSwipe]);\n\n const { ref, transform, opacity, transition, isDragging, deltaX, deltaY } = useSwipe(callbacks, config);\n\n // Memoize merged swipe styles\n const mergedSwipeStyles = useMemo(() => ({\n right: { \n ...DEFAULT_SWIPE_STYLES.right, \n ...swipeStyles?.right,\n labelStyle: { ...DEFAULT_SWIPE_STYLES.right.labelStyle, ...swipeStyles?.right?.labelStyle }\n },\n left: { \n ...DEFAULT_SWIPE_STYLES.left, \n ...swipeStyles?.left,\n labelStyle: { ...DEFAULT_SWIPE_STYLES.left.labelStyle, ...swipeStyles?.left?.labelStyle }\n },\n up: { \n ...DEFAULT_SWIPE_STYLES.up, \n ...swipeStyles?.up,\n labelStyle: { ...DEFAULT_SWIPE_STYLES.up.labelStyle, ...swipeStyles?.up?.labelStyle }\n },\n down: { \n ...DEFAULT_SWIPE_STYLES.down, \n ...swipeStyles?.down,\n labelStyle: { ...DEFAULT_SWIPE_STYLES.down.labelStyle, ...swipeStyles?.down?.labelStyle }\n },\n }), [\n swipeStyles?.right, \n swipeStyles?.left, \n swipeStyles?.up, \n swipeStyles?.down,\n ]);\n\n // Calculate swipe state - these are cheap, no need to memoize\n const absX = Math.abs(deltaX);\n const absY = Math.abs(deltaY);\n const isPrimaryHorizontal = absX >= absY;\n const swipeDistance = isPrimaryHorizontal ? absX : absY;\n const swipeProgress = Math.min(swipeDistance / threshold, 1);\n \n const isSwipingRight = isPrimaryHorizontal && deltaX > 20;\n const isSwipingLeft = isPrimaryHorizontal && deltaX < -20;\n const isSwipingUp = !isPrimaryHorizontal && deltaY < -20;\n const isSwipingDown = !isPrimaryHorizontal && deltaY > 20;\n \n const isActionInProgress = swipeProgress >= 0.15;\n const showOverlay = showOverlayProp && isDragging && swipeDistance > 10;\n\n // Calculate current direction info - single calculation\n const currentDirection = useMemo(() => {\n if (isSwipingRight) return 'right' as const;\n if (isSwipingLeft) return 'left' as const;\n if (isSwipingUp) return 'up' as const;\n if (isSwipingDown) return 'down' as const;\n return null;\n }, [isSwipingRight, isSwipingLeft, isSwipingUp, isSwipingDown]);\n\n const currentStyle = currentDirection ? mergedSwipeStyles[currentDirection] : null;\n const backgroundColor = currentStyle?.backgroundColor ?? 'transparent';\n const labelConfig = currentStyle ? { label: currentStyle.label, labelStyle: currentStyle.labelStyle } : null;\n\n // Memoize card style - only recalculate when values change\n const cardStyle: CSSProperties = useMemo(() => ({\n ...style,\n transform,\n opacity,\n transition,\n touchAction: 'none',\n userSelect: 'none',\n WebkitUserSelect: 'none',\n cursor: isDragging ? 'grabbing' : 'grab',\n willChange: isDragging ? 'transform, opacity' : 'auto',\n position: 'absolute',\n }), [style, transform, opacity, transition, isDragging]);\n\n // Memoize overlay style\n const overlayStyle: CSSProperties = useMemo(() => ({\n position: 'absolute',\n top: 0,\n left: 0,\n right: 0,\n bottom: 0,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n borderRadius: 'inherit',\n pointerEvents: 'none',\n zIndex: 10,\n opacity: showOverlay ? swipeProgress : 0,\n transition: isDragging ? 'none' : 'opacity 200ms ease-out',\n backgroundColor: showOverlay ? backgroundColor : 'transparent',\n }), [showOverlay, swipeProgress, isDragging, backgroundColor]);\n\n // Memoize computed label style\n const computedLabelStyle: CSSProperties = useMemo(() => ({\n ...labelConfig?.labelStyle,\n transform: `scale(${0.8 + swipeProgress * 0.4})`,\n opacity: swipeProgress,\n }), [labelConfig?.labelStyle, swipeProgress]);\n\n return (\n <div\n ref={ref}\n className={`swipeon-card ${className}`}\n style={cardStyle}\n >\n {children}\n <div style={overlayStyle}>\n {showOverlay && isActionInProgress && labelConfig?.label && (\n <span style={computedLabelStyle}>\n {labelConfig.label}\n </span>\n )}\n </div>\n </div>\n );\n};\n\n// Memoize the entire component to prevent unnecessary re-renders from parent\nexport const SwipeCard = memo(SwipeCardInner);\n\nexport default SwipeCard;\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "swipeon-react",
3
- "version": "0.0.1-alpha.2",
3
+ "version": "1.0.1",
4
4
  "type": "module",
5
5
  "description": "A high-performance React swipe card library with smooth animations and multi-directional swipe support",
6
6
  "main": "dist/index.js",
@@ -26,7 +26,9 @@
26
26
  "prebuild": "npm run clean",
27
27
  "publish:alpha": "npm version prerelease --preid=alpha && npm run build && npm publish --tag alpha",
28
28
  "publish:rc": "npm version prerelease --preid=rc && npm run build && npm publish --tag next",
29
- "publish:stable": "npm version patch && npm run build && npm publish --tag latest"
29
+ "publish:stable": "npm version patch && npm run build && npm publish --tag latest",
30
+ "storybook": "storybook dev -p 6006",
31
+ "build-storybook": "storybook build"
30
32
  },
31
33
  "keywords": [
32
34
  "react",
@@ -44,17 +46,29 @@
44
46
  "type": "git",
45
47
  "url": "https://github.com/react-forge/swipeon-react"
46
48
  },
49
+ "homepage": "https://react-forge.github.io/swipeon-react",
47
50
  "peerDependencies": {
48
51
  "react": ">=16.8.0",
49
52
  "react-dom": ">=16.8.0"
50
53
  },
51
54
  "devDependencies": {
55
+ "@storybook/addon-a11y": "^8.6.0",
56
+ "@storybook/addon-essentials": "^8.6.0",
57
+ "@storybook/addon-interactions": "^8.6.0",
58
+ "@storybook/blocks": "^8.6.0",
59
+ "@storybook/manager-api": "^8.6.0",
60
+ "@storybook/react": "^8.6.0",
61
+ "@storybook/react-vite": "^8.6.0",
62
+ "@storybook/theming": "^8.6.0",
52
63
  "@types/react": "^18.2.45",
53
64
  "@types/react-dom": "^18.2.18",
54
65
  "react": "^18.2.0",
55
66
  "react-dom": "^18.2.0",
67
+ "storybook": "^8.6.0",
56
68
  "tsdown": "0.18.2",
57
69
  "tslib": "^2.6.2",
58
- "typescript": "^5.3.3"
59
- }
70
+ "typescript": "^5.3.3",
71
+ "vite": "^5.0.0"
72
+ },
73
+ "packageManager": "yarn@3.6.0+sha512.418e45c2268c4d6b69a28f3939084b5853d5f392c43c0b5588bd1995a96e328414ae4b7777a8980c64bad4328c52586ff879b289f98ae65372a55fa4d0ff70dd"
60
74
  }