lightswind
Version:
A collection of beautifully crafted React Components, Blocks & Templates for Modern Developers. Create stunning web applications effortlessly by using our 160+ professional and animated react components.
29 lines (28 loc) • 3.54 kB
JavaScript
// @ts-nocheck
"use client";
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import { useState } from "react";
import { motion, AnimatePresence, Reorder, useDragControls, } from "framer-motion";
import { GripVertical, X, Plus } from "lucide-react";
import { cn } from "../../lib/utils";
function Item({ item, onRemove, removable, }) {
const dragControls = useDragControls();
return (_jsx(Reorder.Item, { value: item, id: item.id, dragListener: false, dragControls: dragControls, className: "relative", children: _jsxs(motion.div, { layout: true, initial: { opacity: 0, y: -8, scale: 0.98 }, animate: { opacity: 1, y: 0, scale: 1 }, exit: { opacity: 0, scale: 0.95, transition: { duration: 0.15 } }, whileDrag: {
scale: 1.03,
boxShadow: "0 16px 40px rgba(0,0,0,0.15)",
zIndex: 50,
}, transition: { type: "spring", stiffness: 350, damping: 30 }, onPointerDown: (e) => e.preventDefault(), className: cn("flex items-center gap-3 rounded-xl border bg-background px-4 py-3", "shadow-sm hover:shadow-md transition-shadow cursor-default select-none"), children: [_jsx(motion.div, { onPointerDown: (e) => dragControls.start(e), className: "flex-shrink-0 cursor-grab active:cursor-grabbing touch-none text-muted-foreground/40 hover:text-muted-foreground transition-colors", whileHover: { scale: 1.1 }, children: _jsx(GripVertical, { className: "h-4 w-4" }) }), item.icon && (_jsx("div", { className: "flex-shrink-0 flex h-8 w-8 items-center justify-center rounded-lg bg-muted text-muted-foreground", children: item.icon })), _jsxs("div", { className: "flex-1 min-w-0 pointer-events-none", children: [_jsx("p", { className: "text-sm font-medium truncate select-none", children: item.label }), item.description && (_jsx("p", { className: "text-xs text-muted-foreground truncate mt-0.5 select-none", children: item.description }))] }), removable && (_jsx(motion.button, { type: "button", onClick: () => onRemove(item.id), "aria-label": `Remove ${item.label}`, className: "flex-shrink-0 flex h-6 w-6 items-center justify-center rounded-full text-muted-foreground/40 hover:text-destructive hover:bg-destructive/10 transition-colors focus:outline-none", whileHover: { scale: 1.15 }, whileTap: { scale: 0.9 }, children: _jsx(X, { className: "h-3.5 w-3.5" }) }))] }) }));
}
export function DraggableReorderList({ items: initialItems, onReorder, removable = true, className, }) {
const [items, setItems] = useState(initialItems);
const handleReorder = (newOrder) => {
setItems(newOrder);
onReorder?.(newOrder);
};
const handleRemove = (id) => {
const next = items.filter((item) => item.id !== id);
setItems(next);
onReorder?.(next);
};
return (_jsxs("div", { className: cn("w-full select-none", className), style: { userSelect: "none" }, children: [_jsx(Reorder.Group, { axis: "y", values: items, onReorder: handleReorder, className: "flex flex-col gap-2", as: "div", children: _jsx(AnimatePresence, { initial: false, children: items.map((item) => (_jsx(Item, { item: item, onRemove: handleRemove, removable: removable }, item.id))) }) }), items.length === 0 && (_jsxs(motion.div, { initial: { opacity: 0, y: 8 }, animate: { opacity: 1, y: 0 }, className: "flex flex-col items-center justify-center rounded-xl border border-dashed py-10 text-muted-foreground gap-2", children: [_jsx(Plus, { className: "h-5 w-5 opacity-40" }), _jsx("p", { className: "text-sm", children: "All items removed" })] }))] }));
}