Command Palette

Search for a command to run...

Drag Items

The Drag Items component allows users to rearrange items by dragging them.

Drag & Drop Items

Drag to reorder

  • Follow me on X

    Get more update follow me on X

  • Star the repo

    If you like star the repo

  • Share it with your friend

    if you like it, please Share it with your friend

Installation

Install the following dependencies:

pnpm add motion

Create a file at components/motion/drag-items.tsx and paste the given code.

"use client";
import React, { useEffect } from "react";
import {
    useMotionValue,
    Reorder,
    useDragControls,
    motion,
    MotionValue,
    animate,
    DragControls,
} from "motion/react";
import {
    Card,
} from "@/components/ui/card";
import { Grip } from "lucide-react";
 
/*eslint-disable @typescript-eslint/no-explicit-any*/
export const DragItems = ({
    items,
    onReorder,
    children,
}: {
    items: any[];
    onReorder: (newItems: any[]) => void;
    children: React.ReactNode;
}) => {
    return (
        <Reorder.Group
            axis="y"
            values={items}
            onReorder={onReorder}
            className="mx-auto w-full max-w-2xl space-y-3"
        >
            {children}
        </Reorder.Group>
    );
};
 
export const Item = ({
    children,
    item,
}: {
    children: React.ReactNode;
    item: any;
}) => {
    const y = useMotionValue(0);
    const boxShadow = useRaisedShadow(y);
    const dragControls = useDragControls();
 
    return (
        <Reorder.Item
            value={item}
            style={{ boxShadow, y }}
            dragListener={false}
            dragControls={dragControls}
            className="w-full"
        >
            <Card className="overflow-hidden border transition-all duration-300">
                <div className="flex items-center justify-center">
                    <div className="flex-1">{children}</div>
                    <ReorderIcon dragControls={dragControls} />
                </div>
            </Card>
        </Reorder.Item>
    );
};
 
const ReorderIcon = ({ dragControls }: { dragControls: DragControls }) => {
    return (
        <motion.div
            // whileTap={{ scale: 0.85 }}
            onPointerDown={(e) => {
                e.preventDefault();
                dragControls.start(e);
            }}
            className="mr-3 cursor-grab active:cursor-grabbing"
        >
            <Grip size={20} />
        </motion.div>
    );
};
 
const inactiveShadow = "0px 0px 0px rgba(0,0,0,0)";
 
const useRaisedShadow = (value: MotionValue<number>) => {
    const boxShadow = useMotionValue(inactiveShadow);
 
    useEffect(() => {
        let isActive = false;
        value.onChange((latest) => {
            const wasActive = isActive;
            if (latest !== 0) {
                isActive = true;
                if (isActive !== wasActive) {
                    animate(boxShadow, "0px 0px 0px rgba(0,0,0,0)");
                }
            } else {
                isActive = false;
                if (isActive !== wasActive) {
                    animate(boxShadow, inactiveShadow);
                }
            }
        });
    }, [value, boxShadow]);
 
    return boxShadow;
};
 
/*eslint-enable @typescript-eslint/no-explicit-any*/

Update the import paths to match your project setup.