Spaces:
Paused
Paused
| import { ReactNode } from 'react'; | |
| import { useSortable } from '@dnd-kit/sortable'; | |
| import { CSS } from '@dnd-kit/utilities'; | |
| import { GripVertical, X, Maximize2, Minimize2, Square } from 'lucide-react'; | |
| import { cn } from '@/lib/utils'; | |
| import { DashboardWidget } from '@/pages/Dashboard'; | |
| import { Button } from '@/components/ui/button'; | |
| import { | |
| DropdownMenu, | |
| DropdownMenuContent, | |
| DropdownMenuItem, | |
| DropdownMenuTrigger, | |
| } from '@/components/ui/dropdown-menu'; | |
| interface DraggableWidgetProps { | |
| widget: DashboardWidget; | |
| children: ReactNode; | |
| onRemove: () => void; | |
| onResize: (size: 'small' | 'medium' | 'large') => void; | |
| isDragging?: boolean; | |
| } | |
| const DraggableWidget = ({ | |
| widget, | |
| children, | |
| onRemove, | |
| onResize, | |
| isDragging | |
| }: DraggableWidgetProps) => { | |
| const { | |
| attributes, | |
| listeners, | |
| setNodeRef, | |
| transform, | |
| transition, | |
| } = useSortable({ id: widget.id }); | |
| const style = { | |
| transform: CSS.Transform.toString(transform), | |
| transition, | |
| }; | |
| const sizeClasses = { | |
| small: '', | |
| medium: 'md:col-span-1', | |
| large: 'md:col-span-2', | |
| }; | |
| return ( | |
| <div | |
| ref={setNodeRef} | |
| style={style} | |
| className={cn( | |
| "relative bg-card/80 backdrop-blur-sm border border-border/50 overflow-hidden transition-all duration-200 group", | |
| sizeClasses[widget.size], | |
| isDragging && "opacity-50 scale-105 border-primary shadow-lg shadow-primary/20" | |
| )} | |
| > | |
| {/* Header with drag handle */} | |
| <div className="flex items-center gap-2 px-3 py-2 border-b border-border/50 bg-secondary/30"> | |
| <button | |
| {...attributes} | |
| {...listeners} | |
| className="cursor-grab active:cursor-grabbing p-1 hover:bg-secondary/50 rounded transition-colors" | |
| > | |
| <GripVertical className="w-4 h-4 text-muted-foreground" /> | |
| </button> | |
| <div className="w-2 h-2 rounded-full bg-primary animate-pulse" /> | |
| <span className="font-display text-xs uppercase tracking-wider text-primary flex-1"> | |
| {widget.name} | |
| </span> | |
| {/* Size controls */} | |
| <DropdownMenu> | |
| <DropdownMenuTrigger asChild> | |
| <Button variant="ghost" size="sm" className="h-6 w-6 p-0 opacity-0 group-hover:opacity-100 transition-opacity"> | |
| {widget.size === 'small' && <Minimize2 className="w-3 h-3" />} | |
| {widget.size === 'medium' && <Square className="w-3 h-3" />} | |
| {widget.size === 'large' && <Maximize2 className="w-3 h-3" />} | |
| </Button> | |
| </DropdownMenuTrigger> | |
| <DropdownMenuContent align="end" className="bg-card border-border z-50"> | |
| <DropdownMenuItem onClick={() => onResize('small')} className="cursor-pointer"> | |
| <Minimize2 className="w-4 h-4 mr-2" /> | |
| Lille | |
| </DropdownMenuItem> | |
| <DropdownMenuItem onClick={() => onResize('medium')} className="cursor-pointer"> | |
| <Square className="w-4 h-4 mr-2" /> | |
| Medium | |
| </DropdownMenuItem> | |
| <DropdownMenuItem onClick={() => onResize('large')} className="cursor-pointer"> | |
| <Maximize2 className="w-4 h-4 mr-2" /> | |
| Stor | |
| </DropdownMenuItem> | |
| </DropdownMenuContent> | |
| </DropdownMenu> | |
| {/* Remove button */} | |
| <Button | |
| variant="ghost" | |
| size="sm" | |
| className="h-6 w-6 p-0 opacity-0 group-hover:opacity-100 transition-opacity text-destructive hover:text-destructive hover:bg-destructive/10" | |
| onClick={onRemove} | |
| > | |
| <X className="w-3 h-3" /> | |
| </Button> | |
| </div> | |
| {/* Widget content */} | |
| <div className="p-4"> | |
| {children} | |
| </div> | |
| {/* Corner accents */} | |
| <div className="absolute top-0 left-0 w-3 h-3 border-l-2 border-t-2 border-primary/50" /> | |
| <div className="absolute top-0 right-0 w-3 h-3 border-r-2 border-t-2 border-primary/50" /> | |
| <div className="absolute bottom-0 left-0 w-3 h-3 border-l-2 border-b-2 border-primary/50" /> | |
| <div className="absolute bottom-0 right-0 w-3 h-3 border-r-2 border-b-2 border-primary/50" /> | |
| </div> | |
| ); | |
| }; | |
| export default DraggableWidget; | |