/** * WidgetGrid - Draggable grid layout for widgets * Uses react-grid-layout for drag-and-drop functionality */ import { useCallback, useMemo } from "react"; import GridLayout from "react-grid-layout"; import type { Layout } from "react-grid-layout"; import type { WidgetPlacement } from "@mosaic/shared"; import { cn } from "@mosaic/ui/lib/utils"; import { getWidgetByName } from "./WidgetRegistry"; import { BaseWidget } from "./BaseWidget"; import "react-grid-layout/css/styles.css"; export interface WidgetGridProps { layout: WidgetPlacement[]; onLayoutChange: (layout: WidgetPlacement[]) => void; onRemoveWidget?: (widgetId: string) => void; isEditing?: boolean; className?: string; } export function WidgetGrid({ layout, onLayoutChange, onRemoveWidget, isEditing = false, className, }: WidgetGridProps) { // Convert WidgetPlacement to react-grid-layout Layout format const gridLayout: Layout[] = useMemo( () => layout.map((item) => ({ i: item.i, x: item.x, y: item.y, w: item.w, h: item.h, minW: item.minW, maxW: item.maxW, minH: item.minH, maxH: item.maxH, static: !isEditing || item.static, isDraggable: isEditing && (item.isDraggable !== false), isResizable: isEditing && (item.isResizable !== false), })), [layout, isEditing] ); const handleLayoutChange = useCallback( (newLayout: Layout[]) => { const updatedLayout: WidgetPlacement[] = newLayout.map((item) => ({ i: item.i, x: item.x, y: item.y, w: item.w, h: item.h, minW: item.minW, maxW: item.maxW, minH: item.minH, maxH: item.maxH, static: item.static, isDraggable: item.isDraggable, isResizable: item.isResizable, })); onLayoutChange(updatedLayout); }, [onLayoutChange] ); const handleRemoveWidget = useCallback( (widgetId: string) => { if (onRemoveWidget) { onRemoveWidget(widgetId); } }, [onRemoveWidget] ); // Empty state if (layout.length === 0) { return (

No widgets yet

Add widgets to customize your dashboard

); } return (
{layout.map((item) => { // Extract widget type from widget ID (format: "WidgetType-uuid") const widgetType = item.i.split("-")[0]; const widgetDef = getWidgetByName(widgetType); if (!widgetDef) { return (
); } const WidgetComponent = widgetDef.component; return (
handleRemoveWidget(item.i) : undefined } >
); })}
); }