feat(web): migrate dashboard to WidgetGrid with layout persistence (#497)
All checks were successful
ci/woodpecker/push/web Pipeline was successful
All checks were successful
ci/woodpecker/push/web Pipeline was successful
Co-authored-by: Jason Woltje <jason@diversecanvas.com> Co-committed-by: Jason Woltje <jason@diversecanvas.com>
This commit was merged in pull request #497.
This commit is contained in:
@@ -5,7 +5,7 @@
|
||||
|
||||
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
||||
|
||||
import { useCallback, useMemo } from "react";
|
||||
import { useCallback, useMemo, useRef, useState, useEffect } from "react";
|
||||
import GridLayout from "react-grid-layout";
|
||||
import type { Layout, LayoutItem } from "react-grid-layout";
|
||||
import type { WidgetPlacement } from "@mosaic/shared";
|
||||
@@ -33,6 +33,30 @@ export function WidgetGrid({
|
||||
isEditing = false,
|
||||
className,
|
||||
}: WidgetGridProps): React.JSX.Element {
|
||||
// Measure container width for responsive grid
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const [containerWidth, setContainerWidth] = useState(1200);
|
||||
|
||||
useEffect(() => {
|
||||
const el = containerRef.current;
|
||||
if (!el) return;
|
||||
|
||||
const observer = new ResizeObserver((entries): void => {
|
||||
const entry = entries[0];
|
||||
if (entry) {
|
||||
setContainerWidth(entry.contentRect.width);
|
||||
}
|
||||
});
|
||||
observer.observe(el);
|
||||
|
||||
// Set initial width
|
||||
setContainerWidth(el.clientWidth);
|
||||
|
||||
return (): void => {
|
||||
observer.disconnect();
|
||||
};
|
||||
}, []);
|
||||
|
||||
// Convert WidgetPlacement to react-grid-layout Layout format
|
||||
const gridLayout: Layout = useMemo(
|
||||
() =>
|
||||
@@ -96,22 +120,34 @@ export function WidgetGrid({
|
||||
// Empty state
|
||||
if (layout.length === 0) {
|
||||
return (
|
||||
<div className="flex items-center justify-center h-full min-h-[400px] bg-gray-50 rounded-lg border-2 border-dashed border-gray-300">
|
||||
<div
|
||||
ref={containerRef}
|
||||
className="flex items-center justify-center h-full min-h-[400px]"
|
||||
style={{
|
||||
background: "var(--surface-2)",
|
||||
borderRadius: "var(--r-lg)",
|
||||
border: "2px dashed var(--border)",
|
||||
}}
|
||||
>
|
||||
<div className="text-center">
|
||||
<p className="text-gray-500 text-lg font-medium">No widgets yet</p>
|
||||
<p className="text-gray-400 text-sm mt-1">Add widgets to customize your dashboard</p>
|
||||
<p className="text-lg font-medium" style={{ color: "var(--muted)" }}>
|
||||
No widgets yet
|
||||
</p>
|
||||
<p className="text-sm mt-1" style={{ color: "var(--muted)", opacity: 0.7 }}>
|
||||
Add widgets to customize your dashboard
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={cn("widget-grid-container", className)}>
|
||||
<div ref={containerRef} className={cn("widget-grid-container", className)}>
|
||||
<GridLayout
|
||||
className="layout"
|
||||
layout={gridLayout}
|
||||
onLayoutChange={handleLayoutChange}
|
||||
width={1200}
|
||||
width={containerWidth}
|
||||
gridConfig={{
|
||||
cols: 12,
|
||||
rowHeight: 100,
|
||||
|
||||
Reference in New Issue
Block a user