Explorar el Código

fix(dashboard): Widgets layout on dashboard page after window resize (#3453)

David Höck hace 9 meses
padre
commit
d468ca3960

+ 47 - 47
package-lock.json

@@ -45509,7 +45509,7 @@
     },
     "packages/admin-ui": {
       "name": "@vendure/admin-ui",
-      "version": "3.1.7",
+      "version": "3.1.8",
       "license": "GPL-3.0-or-later",
       "dependencies": {
         "@angular/animations": "^19.2.4",
@@ -45603,7 +45603,7 @@
     },
     "packages/admin-ui-plugin": {
       "name": "@vendure/admin-ui-plugin",
-      "version": "3.1.7",
+      "version": "3.1.8",
       "license": "GPL-3.0-or-later",
       "dependencies": {
         "date-fns": "^2.30.0",
@@ -45613,9 +45613,9 @@
       "devDependencies": {
         "@types/express": "^4.17.21",
         "@types/fs-extra": "^11.0.4",
-        "@vendure/admin-ui": "^3.1.7",
-        "@vendure/common": "^3.1.7",
-        "@vendure/core": "^3.1.7",
+        "@vendure/admin-ui": "^3.1.8",
+        "@vendure/common": "^3.1.8",
+        "@vendure/core": "^3.1.8",
         "express": "^4.18.3",
         "rimraf": "^5.0.5",
         "typescript": "5.8.2"
@@ -45662,7 +45662,7 @@
     },
     "packages/asset-server-plugin": {
       "name": "@vendure/asset-server-plugin",
-      "version": "3.1.7",
+      "version": "3.1.8",
       "license": "GPL-3.0-or-later",
       "dependencies": {
         "file-type": "^19.0.0",
@@ -45675,8 +45675,8 @@
         "@types/express": "^4.17.21",
         "@types/fs-extra": "^11.0.4",
         "@types/node-fetch": "^2.6.11",
-        "@vendure/common": "^3.1.7",
-        "@vendure/core": "^3.1.7",
+        "@vendure/common": "^3.1.8",
+        "@vendure/core": "^3.1.8",
         "express": "^4.18.3",
         "node-fetch": "^2.7.0",
         "rimraf": "^5.0.5",
@@ -45688,11 +45688,11 @@
     },
     "packages/cli": {
       "name": "@vendure/cli",
-      "version": "3.1.7",
+      "version": "3.1.8",
       "license": "GPL-3.0-or-later",
       "dependencies": {
         "@clack/prompts": "^0.7.0",
-        "@vendure/common": "^3.1.7",
+        "@vendure/common": "^3.1.8",
         "change-case": "^4.1.2",
         "commander": "^11.0.0",
         "dotenv": "^16.4.5",
@@ -45706,7 +45706,7 @@
         "vendure": "dist/cli.js"
       },
       "devDependencies": {
-        "@vendure/core": "^3.1.7",
+        "@vendure/core": "^3.1.8",
         "typescript": "5.8.2"
       },
       "funding": {
@@ -45741,7 +45741,7 @@
     },
     "packages/common": {
       "name": "@vendure/common",
-      "version": "3.1.7",
+      "version": "3.1.8",
       "license": "GPL-3.0-or-later",
       "devDependencies": {
         "rimraf": "^5.0.5",
@@ -45753,7 +45753,7 @@
     },
     "packages/core": {
       "name": "@vendure/core",
-      "version": "3.1.7",
+      "version": "3.1.8",
       "license": "GPL-3.0-or-later",
       "dependencies": {
         "@apollo/server": "^4.11.3",
@@ -45765,7 +45765,7 @@
         "@nestjs/platform-express": "~11.0.12",
         "@nestjs/terminus": "~11.0.0",
         "@nestjs/typeorm": "~11.0.0",
-        "@vendure/common": "^3.1.7",
+        "@vendure/common": "^3.1.8",
         "bcrypt": "^5.1.1",
         "body-parser": "^1.20.2",
         "cookie-session": "^2.1.0",
@@ -46140,11 +46140,11 @@
     },
     "packages/create": {
       "name": "@vendure/create",
-      "version": "3.1.7",
+      "version": "3.1.8",
       "license": "GPL-3.0-or-later",
       "dependencies": {
         "@clack/prompts": "^0.7.0",
-        "@vendure/common": "^3.1.7",
+        "@vendure/common": "^3.1.8",
         "commander": "^11.0.0",
         "cross-spawn": "^7.0.3",
         "fs-extra": "^11.2.0",
@@ -46162,7 +46162,7 @@
         "@types/fs-extra": "^11.0.4",
         "@types/handlebars": "^4.1.0",
         "@types/semver": "^7.5.8",
-        "@vendure/core": "^3.1.7",
+        "@vendure/core": "^3.1.8",
         "rimraf": "^5.0.5",
         "ts-node": "^10.9.2",
         "typescript": "5.8.2"
@@ -46581,7 +46581,7 @@
       "license": "MIT"
     },
     "packages/dev-server": {
-      "version": "3.1.7",
+      "version": "3.1.8",
       "license": "GPL-3.0-or-later",
       "dependencies": {
         "@nestjs/axios": "^4.0.0",
@@ -46594,8 +46594,8 @@
         "typescript": "5.8.2"
       },
       "devDependencies": {
-        "@vendure/testing": "^3.1.7",
-        "@vendure/ui-devkit": "^3.1.7",
+        "@vendure/testing": "^3.1.8",
+        "@vendure/ui-devkit": "^3.1.8",
         "commander": "^12.0.0",
         "concurrently": "^8.2.2",
         "csv-stringify": "^6.4.6",
@@ -46614,7 +46614,7 @@
     },
     "packages/elasticsearch-plugin": {
       "name": "@vendure/elasticsearch-plugin",
-      "version": "3.1.7",
+      "version": "3.1.8",
       "license": "GPL-3.0-or-later",
       "dependencies": {
         "@elastic/elasticsearch": "~7.9.1",
@@ -46622,8 +46622,8 @@
         "fast-deep-equal": "^3.1.3"
       },
       "devDependencies": {
-        "@vendure/common": "^3.1.7",
-        "@vendure/core": "^3.1.7",
+        "@vendure/common": "^3.1.8",
+        "@vendure/core": "^3.1.8",
         "rimraf": "^5.0.5",
         "typescript": "5.8.2"
       },
@@ -46633,7 +46633,7 @@
     },
     "packages/email-plugin": {
       "name": "@vendure/email-plugin",
-      "version": "3.1.7",
+      "version": "3.1.8",
       "license": "GPL-3.0-or-later",
       "dependencies": {
         "@types/nodemailer": "^6.4.9",
@@ -46649,8 +46649,8 @@
         "@types/express": "^4.17.21",
         "@types/fs-extra": "^11.0.4",
         "@types/mjml": "^4.7.4",
-        "@vendure/common": "^3.1.7",
-        "@vendure/core": "^3.1.7",
+        "@vendure/common": "^3.1.8",
+        "@vendure/core": "^3.1.8",
         "rimraf": "^5.0.5",
         "typescript": "5.8.2"
       },
@@ -46660,14 +46660,14 @@
     },
     "packages/harden-plugin": {
       "name": "@vendure/harden-plugin",
-      "version": "3.1.7",
+      "version": "3.1.8",
       "license": "GPL-3.0-or-later",
       "dependencies": {
         "graphql-query-complexity": "^0.12.0"
       },
       "devDependencies": {
-        "@vendure/common": "^3.1.7",
-        "@vendure/core": "^3.1.7"
+        "@vendure/common": "^3.1.8",
+        "@vendure/core": "^3.1.8"
       },
       "funding": {
         "url": "https://github.com/sponsors/michaelbromley"
@@ -46675,12 +46675,12 @@
     },
     "packages/job-queue-plugin": {
       "name": "@vendure/job-queue-plugin",
-      "version": "3.1.7",
+      "version": "3.1.8",
       "license": "GPL-3.0-or-later",
       "devDependencies": {
         "@google-cloud/pubsub": "^2.8.0",
-        "@vendure/common": "^3.1.7",
-        "@vendure/core": "^3.1.7",
+        "@vendure/common": "^3.1.8",
+        "@vendure/core": "^3.1.8",
         "bullmq": "^5.4.2",
         "ioredis": "^5.3.2",
         "rimraf": "^5.0.5",
@@ -46692,7 +46692,7 @@
     },
     "packages/payments-plugin": {
       "name": "@vendure/payments-plugin",
-      "version": "3.1.7",
+      "version": "3.1.8",
       "license": "GPL-3.0-or-later",
       "dependencies": {
         "currency.js": "2.0.4"
@@ -46701,9 +46701,9 @@
         "@mollie/api-client": "^3.7.0",
         "@types/braintree": "^3.3.11",
         "@types/localtunnel": "2.0.4",
-        "@vendure/common": "^3.1.7",
-        "@vendure/core": "^3.1.7",
-        "@vendure/testing": "^3.1.7",
+        "@vendure/common": "^3.1.8",
+        "@vendure/core": "^3.1.8",
+        "@vendure/testing": "^3.1.8",
         "braintree": "^3.22.0",
         "localtunnel": "2.0.2",
         "nock": "^13.1.4",
@@ -46733,12 +46733,12 @@
     },
     "packages/sentry-plugin": {
       "name": "@vendure/sentry-plugin",
-      "version": "3.1.7",
+      "version": "3.1.8",
       "license": "GPL-3.0-or-later",
       "devDependencies": {
         "@sentry/node": "^7.106.1",
-        "@vendure/common": "^3.1.7",
-        "@vendure/core": "^3.1.7"
+        "@vendure/common": "^3.1.8",
+        "@vendure/core": "^3.1.8"
       },
       "funding": {
         "url": "https://github.com/sponsors/michaelbromley"
@@ -46749,14 +46749,14 @@
     },
     "packages/stellate-plugin": {
       "name": "@vendure/stellate-plugin",
-      "version": "3.1.7",
+      "version": "3.1.8",
       "license": "GPL-3.0-or-later",
       "dependencies": {
         "node-fetch": "^2.7.0"
       },
       "devDependencies": {
-        "@vendure/common": "^3.1.7",
-        "@vendure/core": "^3.1.7"
+        "@vendure/common": "^3.1.8",
+        "@vendure/core": "^3.1.8"
       },
       "funding": {
         "url": "https://github.com/sponsors/michaelbromley"
@@ -46764,11 +46764,11 @@
     },
     "packages/testing": {
       "name": "@vendure/testing",
-      "version": "3.1.7",
+      "version": "3.1.8",
       "license": "GPL-3.0-or-later",
       "dependencies": {
         "@graphql-typed-document-node/core": "^3.2.0",
-        "@vendure/common": "^3.1.7",
+        "@vendure/common": "^3.1.8",
         "faker": "^4.1.0",
         "form-data": "^4.0.0",
         "graphql": "^16.10.0",
@@ -46781,7 +46781,7 @@
         "@types/mysql": "^2.15.26",
         "@types/node-fetch": "^2.6.4",
         "@types/pg": "^8.11.2",
-        "@vendure/core": "^3.1.7",
+        "@vendure/core": "^3.1.8",
         "mysql": "^2.18.1",
         "pg": "^8.11.3",
         "rimraf": "^5.0.5",
@@ -46797,7 +46797,7 @@
     },
     "packages/ui-devkit": {
       "name": "@vendure/ui-devkit",
-      "version": "3.1.7",
+      "version": "3.1.8",
       "license": "GPL-3.0-or-later",
       "dependencies": {
         "@angular-devkit/build-angular": "^19.2.5",
@@ -46816,7 +46816,7 @@
         "@rollup/plugin-node-resolve": "^15.2.3",
         "@rollup/plugin-terser": "^0.4.4",
         "@types/fs-extra": "^11.0.4",
-        "@vendure/core": "^3.1.7",
+        "@vendure/core": "^3.1.8",
         "react": "^19.0.0",
         "react-dom": "^19.0.0",
         "rimraf": "^5.0.5",

+ 65 - 26
packages/dashboard/src/app/routes/_authenticated/index.tsx

@@ -1,6 +1,9 @@
 import { Button } from '@/components/ui/button.js';
-import { getDashboardWidget, getDashboardWidgetRegistry } from '@/framework/dashboard-widget/widget-extensions.js';
 import { DashboardWidgetInstance } from '@/framework/dashboard-widget/types.js';
+import {
+    getDashboardWidget,
+    getDashboardWidgetRegistry,
+} from '@/framework/dashboard-widget/widget-extensions.js';
 import {
     FullWidthPageBlock,
     Page,
@@ -10,8 +13,9 @@ import {
     PageTitle,
 } from '@/framework/layout-engine/page-layout.js';
 import { createFileRoute } from '@tanstack/react-router';
-import { useEffect, useMemo, useState } from 'react';
-import { Responsive as ResponsiveGridLayout, WidthProvider } from 'react-grid-layout';
+import { useEffect, useMemo, useRef, useState } from 'react';
+import { Responsive as ResponsiveGridLayout } from 'react-grid-layout';
+
 import 'react-grid-layout/css/styles.css';
 import 'react-resizable/css/styles.css';
 
@@ -68,6 +72,24 @@ const findNextPosition = (
 function DashboardPage() {
     const [widgets, setWidgets] = useState<DashboardWidgetInstance[]>([]);
     const [editMode, setEditMode] = useState(false);
+    const [layoutWidth, setLayoutWidth] = useState<number | undefined>(undefined);
+    const layoutRef = useRef<HTMLDivElement>(null);
+
+    useEffect(() => {
+        if (!layoutRef.current) return;
+
+        const resizeObserver = new ResizeObserver(entries => {
+            for (const entry of entries) {
+                setLayoutWidth(entry.contentRect.width);
+            }
+        });
+
+        resizeObserver.observe(layoutRef.current);
+
+        return () => {
+            resizeObserver.disconnect();
+        };
+    }, []);
 
     useEffect(() => {
         const initialWidgets = Array.from(getDashboardWidgetRegistry().entries()).reduce(
@@ -100,19 +122,55 @@ function DashboardPage() {
             [],
         );
 
+        console.log('initialWidgets', initialWidgets);
+
         setWidgets(initialWidgets);
     }, []);
 
     const handleLayoutChange = (layout: ReactGridLayout.Layout[]) => {
+        console.log('handleLayoutChange', layout);
         setWidgets(prev =>
             prev.map((widget, i) => ({
                 ...widget,
                 layout: layout[i],
             })),
         );
+        console.log('handleLayoutChange->widgets', widgets);
     };
 
-    const ResponsiveReactGridLayout = useMemo(() => WidthProvider(ResponsiveGridLayout), []);
+    const memoizedLayoutGrid = useMemo(() => {
+        console.log('memoizedLayoutGrid', layoutWidth, widgets, editMode);
+        return (
+            layoutWidth && (
+                <ResponsiveGridLayout
+                    className="overflow-hidden"
+                    key={layoutWidth}
+                    width={layoutWidth}
+                    layouts={{ lg: widgets.map(w => ({ ...w.layout, i: w.id })) }}
+                    onLayoutChange={handleLayoutChange}
+                    cols={{ lg: 12, md: 12, sm: 6, xs: 4, xxs: 2 }}
+                    rowHeight={100}
+                    isDraggable={editMode}
+                    isResizable={editMode}
+                    autoSize={true}
+                    innerRef={layoutRef}
+                    transformScale={0.9}
+                >
+                    {widgets.map(widget => {
+                        const definition = getDashboardWidget(widget.widgetId);
+                        if (!definition) return null;
+                        const WidgetComponent = definition.component;
+
+                        return (
+                            <div key={widget.id}>
+                                <WidgetComponent id={widget.id} config={widget.config} />
+                            </div>
+                        );
+                    })}
+                </ResponsiveGridLayout>
+            )
+        );
+    }, [layoutWidth, editMode, widgets]);
 
     return (
         <Page pageId="dashboard">
@@ -131,28 +189,9 @@ function DashboardPage() {
             </PageActionBar>
             <PageLayout>
                 <FullWidthPageBlock blockId="widgets">
-                    <ResponsiveReactGridLayout
-                        className="h-full w-full"
-                        layouts={{ lg: widgets.map(w => ({ ...w.layout, i: w.id })) }}
-                        onLayoutChange={handleLayoutChange}
-                        cols={{ lg: 12, md: 10, sm: 6, xs: 4, xxs: 2 }}
-                        rowHeight={100}
-                        isDraggable={editMode}
-                        isResizable={editMode}
-                        autoSize={true}
-                    >
-                        {widgets.map(widget => {
-                            const definition = getDashboardWidget(widget.widgetId);
-                            if (!definition) return null;
-                            const WidgetComponent = definition.component;
-
-                            return (
-                                <div key={widget.id}>
-                                    <WidgetComponent id={widget.id} config={widget.config} />
-                                </div>
-                            );
-                        })}
-                    </ResponsiveReactGridLayout>
+                    <div ref={layoutRef} className="h-full w-full">
+                        {memoizedLayoutGrid}
+                    </div>
                 </FullWidthPageBlock>
             </PageLayout>
         </Page>

+ 5 - 0
packages/dashboard/src/app/styles.css

@@ -74,3 +74,8 @@
 @utility col-side {
     grid-column: span 2 / span 2;
 }
+
+/* Overrides for the react-grid-layout library */
+.react-grid-item {
+    transition: none !important;
+}