Merge remote-tracking branch 'origin/v3-refactor' into v3
This commit is contained in:
commit
8cf12c75df
|
@ -31,7 +31,7 @@
|
||||||
"chartjs-plugin-annotation": "^3.1.0",
|
"chartjs-plugin-annotation": "^3.1.0",
|
||||||
"dxf-parser": "^1.1.2",
|
"dxf-parser": "^1.1.2",
|
||||||
"glob": "^11.0.0",
|
"glob": "^11.0.0",
|
||||||
"gsap": "^3.12.5",
|
"gsap": "^3.13.0",
|
||||||
"html2canvas": "^1.4.1",
|
"html2canvas": "^1.4.1",
|
||||||
"immer": "^9.0.21",
|
"immer": "^9.0.21",
|
||||||
"leva": "^0.10.0",
|
"leva": "^0.10.0",
|
||||||
|
@ -2026,7 +2026,7 @@
|
||||||
"version": "0.8.1",
|
"version": "0.8.1",
|
||||||
"resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
|
"resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
|
||||||
"integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==",
|
"integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@jridgewell/trace-mapping": "0.3.9"
|
"@jridgewell/trace-mapping": "0.3.9"
|
||||||
},
|
},
|
||||||
|
@ -2038,7 +2038,7 @@
|
||||||
"version": "0.3.9",
|
"version": "0.3.9",
|
||||||
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz",
|
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz",
|
||||||
"integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==",
|
"integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@jridgewell/resolve-uri": "^3.0.3",
|
"@jridgewell/resolve-uri": "^3.0.3",
|
||||||
"@jridgewell/sourcemap-codec": "^1.4.10"
|
"@jridgewell/sourcemap-codec": "^1.4.10"
|
||||||
|
@ -4180,6 +4180,26 @@
|
||||||
"url": "https://github.com/sponsors/gregberge"
|
"url": "https://github.com/sponsors/gregberge"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@testing-library/dom": {
|
||||||
|
"version": "10.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.0.tgz",
|
||||||
|
"integrity": "sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/code-frame": "^7.10.4",
|
||||||
|
"@babel/runtime": "^7.12.5",
|
||||||
|
"@types/aria-query": "^5.0.1",
|
||||||
|
"aria-query": "5.3.0",
|
||||||
|
"chalk": "^4.1.0",
|
||||||
|
"dom-accessibility-api": "^0.5.9",
|
||||||
|
"lz-string": "^1.5.0",
|
||||||
|
"pretty-format": "^27.0.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@testing-library/jest-dom": {
|
"node_modules/@testing-library/jest-dom": {
|
||||||
"version": "5.17.0",
|
"version": "5.17.0",
|
||||||
"resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-5.17.0.tgz",
|
"resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-5.17.0.tgz",
|
||||||
|
@ -4291,25 +4311,25 @@
|
||||||
"version": "1.0.11",
|
"version": "1.0.11",
|
||||||
"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz",
|
"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz",
|
||||||
"integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==",
|
"integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==",
|
||||||
"dev": true
|
"devOptional": true
|
||||||
},
|
},
|
||||||
"node_modules/@tsconfig/node12": {
|
"node_modules/@tsconfig/node12": {
|
||||||
"version": "1.0.11",
|
"version": "1.0.11",
|
||||||
"resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz",
|
"resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz",
|
||||||
"integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==",
|
"integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==",
|
||||||
"dev": true
|
"devOptional": true
|
||||||
},
|
},
|
||||||
"node_modules/@tsconfig/node14": {
|
"node_modules/@tsconfig/node14": {
|
||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz",
|
||||||
"integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==",
|
"integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==",
|
||||||
"dev": true
|
"devOptional": true
|
||||||
},
|
},
|
||||||
"node_modules/@tsconfig/node16": {
|
"node_modules/@tsconfig/node16": {
|
||||||
"version": "1.0.4",
|
"version": "1.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz",
|
||||||
"integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==",
|
"integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==",
|
||||||
"dev": true
|
"devOptional": true
|
||||||
},
|
},
|
||||||
"node_modules/@turf/along": {
|
"node_modules/@turf/along": {
|
||||||
"version": "7.2.0",
|
"version": "7.2.0",
|
||||||
|
@ -9063,7 +9083,7 @@
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
|
||||||
"integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
|
"integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
|
||||||
"dev": true
|
"devOptional": true
|
||||||
},
|
},
|
||||||
"node_modules/cross-env": {
|
"node_modules/cross-env": {
|
||||||
"version": "7.0.3",
|
"version": "7.0.3",
|
||||||
|
@ -9940,7 +9960,7 @@
|
||||||
"version": "4.0.2",
|
"version": "4.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
|
||||||
"integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
|
"integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.3.1"
|
"node": ">=0.3.1"
|
||||||
}
|
}
|
||||||
|
@ -12291,9 +12311,10 @@
|
||||||
"integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag=="
|
"integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag=="
|
||||||
},
|
},
|
||||||
"node_modules/gsap": {
|
"node_modules/gsap": {
|
||||||
"version": "3.12.5",
|
"version": "3.13.0",
|
||||||
"resolved": "https://registry.npmjs.org/gsap/-/gsap-3.12.5.tgz",
|
"resolved": "https://registry.npmjs.org/gsap/-/gsap-3.13.0.tgz",
|
||||||
"integrity": "sha512-srBfnk4n+Oe/ZnMIOXt3gT605BX9x5+rh/prT2F1SsNJsU1XuMiP0E2aptW481OnonOGACZWBqseH5Z7csHxhQ=="
|
"integrity": "sha512-QL7MJ2WMjm1PHWsoFrAQH/J8wUeqZvMtHO58qdekHpCfhvhSL4gSiz6vJf5EeMP0LOn3ZCprL2ki/gjED8ghVw==",
|
||||||
|
"license": "Standard 'no charge' license: https://gsap.com/standard-license."
|
||||||
},
|
},
|
||||||
"node_modules/gzip-size": {
|
"node_modules/gzip-size": {
|
||||||
"version": "6.0.0",
|
"version": "6.0.0",
|
||||||
|
@ -15324,7 +15345,7 @@
|
||||||
"version": "1.3.6",
|
"version": "1.3.6",
|
||||||
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
|
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
|
||||||
"integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
|
"integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
|
||||||
"dev": true
|
"devOptional": true
|
||||||
},
|
},
|
||||||
"node_modules/makeerror": {
|
"node_modules/makeerror": {
|
||||||
"version": "1.0.12",
|
"version": "1.0.12",
|
||||||
|
@ -20801,7 +20822,7 @@
|
||||||
"version": "10.9.2",
|
"version": "10.9.2",
|
||||||
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz",
|
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz",
|
||||||
"integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==",
|
"integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@cspotcode/source-map-support": "^0.8.0",
|
"@cspotcode/source-map-support": "^0.8.0",
|
||||||
"@tsconfig/node10": "^1.0.7",
|
"@tsconfig/node10": "^1.0.7",
|
||||||
|
@ -20844,7 +20865,7 @@
|
||||||
"version": "8.3.4",
|
"version": "8.3.4",
|
||||||
"resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz",
|
"resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz",
|
||||||
"integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==",
|
"integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"acorn": "^8.11.0"
|
"acorn": "^8.11.0"
|
||||||
},
|
},
|
||||||
|
@ -20856,7 +20877,7 @@
|
||||||
"version": "4.1.3",
|
"version": "4.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
|
||||||
"integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
|
"integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
|
||||||
"dev": true
|
"devOptional": true
|
||||||
},
|
},
|
||||||
"node_modules/tsconfig-paths": {
|
"node_modules/tsconfig-paths": {
|
||||||
"version": "3.15.0",
|
"version": "3.15.0",
|
||||||
|
@ -21352,7 +21373,7 @@
|
||||||
"version": "3.0.1",
|
"version": "3.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
|
||||||
"integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==",
|
"integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==",
|
||||||
"dev": true
|
"devOptional": true
|
||||||
},
|
},
|
||||||
"node_modules/v8-to-istanbul": {
|
"node_modules/v8-to-istanbul": {
|
||||||
"version": "8.1.1",
|
"version": "8.1.1",
|
||||||
|
@ -22411,7 +22432,7 @@
|
||||||
"version": "3.1.1",
|
"version": "3.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
|
||||||
"integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
|
"integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,7 +26,7 @@
|
||||||
"chartjs-plugin-annotation": "^3.1.0",
|
"chartjs-plugin-annotation": "^3.1.0",
|
||||||
"dxf-parser": "^1.1.2",
|
"dxf-parser": "^1.1.2",
|
||||||
"glob": "^11.0.0",
|
"glob": "^11.0.0",
|
||||||
"gsap": "^3.12.5",
|
"gsap": "^3.13.0",
|
||||||
"html2canvas": "^1.4.1",
|
"html2canvas": "^1.4.1",
|
||||||
"immer": "^9.0.21",
|
"immer": "^9.0.21",
|
||||||
"leva": "^0.10.0",
|
"leva": "^0.10.0",
|
||||||
|
|
|
@ -1,5 +1,10 @@
|
||||||
|
import { forwardRef, useEffect, useImperativeHandle, useRef, useState } from "react";
|
||||||
|
import { gsap } from "gsap";
|
||||||
|
import { Draggable } from "gsap/Draggable";
|
||||||
|
|
||||||
import { useWidgetStore } from "../../../../store/useWidgetStore";
|
import { useWidgetStore } from "../../../../store/useWidgetStore";
|
||||||
import ProgressCard from "../2d/charts/ProgressCard";
|
import { usePlayButtonStore } from "../../../../store/usePlayButtonStore";
|
||||||
|
import { useSelectedZoneStore } from "../../../../store/visualization/useZoneStore";
|
||||||
import PieGraphComponent from "../2d/charts/PieGraphComponent";
|
import PieGraphComponent from "../2d/charts/PieGraphComponent";
|
||||||
import BarGraphComponent from "../2d/charts/BarGraphComponent";
|
import BarGraphComponent from "../2d/charts/BarGraphComponent";
|
||||||
import LineGraphComponent from "../2d/charts/LineGraphComponent";
|
import LineGraphComponent from "../2d/charts/LineGraphComponent";
|
||||||
|
@ -12,13 +17,11 @@ import {
|
||||||
DublicateIcon,
|
DublicateIcon,
|
||||||
KebabIcon,
|
KebabIcon,
|
||||||
} from "../../../../components/icons/ExportCommonIcons";
|
} from "../../../../components/icons/ExportCommonIcons";
|
||||||
import { useEffect, useRef, useState } from "react";
|
|
||||||
import { useClickOutside } from "../../functions/handleWidgetsOuterClick";
|
|
||||||
import { useSocketStore } from "../../../../store/builder/store";
|
|
||||||
import { usePlayButtonStore } from "../../../../store/usePlayButtonStore";
|
|
||||||
import OuterClick from "../../../../utils/outerClick";
|
|
||||||
import useChartStore from "../../../../store/visualization/useChartStore";
|
|
||||||
import { useParams } from "react-router-dom";
|
import { useParams } from "react-router-dom";
|
||||||
|
import { useSocketStore } from "../../../../store/builder/store";
|
||||||
|
import useChartStore from "../../../../store/visualization/useChartStore";
|
||||||
|
|
||||||
|
gsap.registerPlugin(Draggable);
|
||||||
import { getUserData } from "../../../../functions/getUserData";
|
import { getUserData } from "../../../../functions/getUserData";
|
||||||
|
|
||||||
type Side = "top" | "bottom" | "left" | "right";
|
type Side = "top" | "bottom" | "left" | "right";
|
||||||
|
@ -31,73 +34,74 @@ interface Widget {
|
||||||
data: any;
|
data: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DraggableWidget = ({
|
type DraggableWidgetProps = {
|
||||||
widget,
|
|
||||||
hiddenPanels,
|
|
||||||
index,
|
|
||||||
onReorder,
|
|
||||||
openKebabId,
|
|
||||||
setOpenKebabId,
|
|
||||||
selectedZone,
|
|
||||||
setSelectedZone,
|
|
||||||
}: {
|
|
||||||
selectedZone: {
|
|
||||||
zoneName: string;
|
|
||||||
zoneUuid: string;
|
|
||||||
activeSides: Side[];
|
|
||||||
points: [];
|
|
||||||
panelOrder: Side[];
|
|
||||||
lockedPanels: Side[];
|
|
||||||
widgets: Widget[];
|
|
||||||
};
|
|
||||||
setSelectedZone: React.Dispatch<
|
|
||||||
React.SetStateAction<{
|
|
||||||
zoneName: string;
|
|
||||||
activeSides: Side[];
|
|
||||||
panelOrder: Side[];
|
|
||||||
points: [];
|
|
||||||
lockedPanels: Side[];
|
|
||||||
zoneUuid: string;
|
|
||||||
zoneViewPortTarget: number[];
|
|
||||||
zoneViewPortPosition: number[];
|
|
||||||
widgets: {
|
|
||||||
id: string;
|
|
||||||
type: string;
|
|
||||||
title: string;
|
|
||||||
panel: Side;
|
|
||||||
data: any;
|
|
||||||
}[];
|
|
||||||
}>
|
|
||||||
>;
|
|
||||||
|
|
||||||
widget: any;
|
widget: any;
|
||||||
hiddenPanels: string[];
|
index: any
|
||||||
index: number;
|
|
||||||
onReorder: (fromIndex: number, toIndex: number) => void;
|
|
||||||
openKebabId: string | null;
|
openKebabId: string | null;
|
||||||
setOpenKebabId: (id: string | null) => void;
|
setOpenKebabId: (id: string | null) => void;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
export const DraggableWidget = forwardRef<HTMLDivElement, DraggableWidgetProps>(({
|
||||||
|
widget,
|
||||||
|
index,
|
||||||
|
openKebabId,
|
||||||
|
setOpenKebabId
|
||||||
}) => {
|
}) => {
|
||||||
const { visualizationSocket } = useSocketStore();
|
|
||||||
const { selectedChartId, setSelectedChartId } = useWidgetStore();
|
const { selectedZone, setSelectedZone } = useSelectedZoneStore();
|
||||||
|
const widgetRef = useRef<HTMLDivElement | null>(null);
|
||||||
const [panelDimensions, setPanelDimensions] = useState<{
|
const [panelDimensions, setPanelDimensions] = useState<{
|
||||||
[side in Side]?: { width: number; height: number };
|
[side in Side]?: { width: number; height: number };
|
||||||
}>({});
|
}>({});
|
||||||
const { measurements, duration, name } = useChartStore();
|
|
||||||
const { isPlaying } = usePlayButtonStore();
|
|
||||||
const { userId, organization, email } = getUserData();
|
|
||||||
const [canvasDimensions, setCanvasDimensions] = useState({
|
const [canvasDimensions, setCanvasDimensions] = useState({
|
||||||
width: 0,
|
width: 0,
|
||||||
height: 0,
|
height: 0,
|
||||||
});
|
});
|
||||||
const { projectId } = useParams();
|
const { projectId } = useParams();
|
||||||
useEffect(() => { }, [measurements, duration, name]);
|
const { visualizationSocket } = useSocketStore();
|
||||||
const handlePointerDown = () => {
|
const { selectedChartId, setSelectedChartId } = useWidgetStore();
|
||||||
if (selectedChartId?.id !== widget.id) {
|
const { measurements, duration, name } = useChartStore();
|
||||||
setSelectedChartId(widget);
|
const { userId, organization, email } = getUserData();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const handleKebabClick = (event: React.MouseEvent<HTMLDivElement>) => {
|
||||||
|
event.stopPropagation();
|
||||||
|
if (openKebabId === widget.id) {
|
||||||
|
setOpenKebabId(null);
|
||||||
|
} else {
|
||||||
|
setOpenKebabId(widget.id);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const chartWidget = useRef<HTMLDivElement>(null);
|
const isPanelFull = (panel: Side) => {
|
||||||
|
const currentWidgetCount = getCurrentWidgetCount(panel);
|
||||||
|
const panelCapacity = calculatePanelCapacity(panel);
|
||||||
|
|
||||||
|
return currentWidgetCount > panelCapacity;
|
||||||
|
};
|
||||||
|
const panelSize = Math.max(
|
||||||
|
Math.min(canvasDimensions.width * 0.25, canvasDimensions.height * 0.25),
|
||||||
|
170 // Min 170px
|
||||||
|
);
|
||||||
|
const getCurrentWidgetCount = (panel: Side) =>
|
||||||
|
selectedZone.widgets.filter((w) => w.panel === panel).length;
|
||||||
|
// Calculate panel capacity
|
||||||
|
|
||||||
|
const calculatePanelCapacity = (panel: Side) => {
|
||||||
|
const CHART_WIDTH = panelSize;
|
||||||
|
const CHART_HEIGHT = panelSize;
|
||||||
|
|
||||||
|
const dimensions = panelDimensions[panel];
|
||||||
|
if (!dimensions) {
|
||||||
|
return panel === "top" || panel === "bottom" ? 5 : 3; // Fallback capacities
|
||||||
|
}
|
||||||
|
|
||||||
|
return panel === "top" || panel === "bottom"
|
||||||
|
? Math.max(1, Math.floor(dimensions.width / CHART_WIDTH))
|
||||||
|
: Math.max(1, Math.floor(dimensions.height / CHART_HEIGHT));
|
||||||
|
};
|
||||||
|
|
||||||
const deleteSelectedChart = async () => {
|
const deleteSelectedChart = async () => {
|
||||||
try {
|
try {
|
||||||
|
@ -105,9 +109,10 @@ export const DraggableWidget = ({
|
||||||
let deleteWidget = {
|
let deleteWidget = {
|
||||||
zoneUuid: selectedZone.zoneUuid,
|
zoneUuid: selectedZone.zoneUuid,
|
||||||
widgetID: widget.id,
|
widgetID: widget.id,
|
||||||
organization,
|
organization: organization,
|
||||||
projectId, userId
|
projectId, userId
|
||||||
};
|
};
|
||||||
|
console.log('deleteWidget: ', deleteWidget);
|
||||||
|
|
||||||
if (visualizationSocket) {
|
if (visualizationSocket) {
|
||||||
setSelectedChartId(null);
|
setSelectedChartId(null);
|
||||||
|
@ -138,38 +143,6 @@ export const DraggableWidget = ({
|
||||||
setOpenKebabId(null);
|
setOpenKebabId(null);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Calculate panel size
|
|
||||||
const panelSize = Math.max(
|
|
||||||
Math.min(canvasDimensions.width * 0.25, canvasDimensions.height * 0.25),
|
|
||||||
170 // Min 170px
|
|
||||||
);
|
|
||||||
|
|
||||||
const getCurrentWidgetCount = (panel: Side) =>
|
|
||||||
selectedZone.widgets.filter((w) => w.panel === panel).length;
|
|
||||||
// Calculate panel capacity
|
|
||||||
|
|
||||||
const calculatePanelCapacity = (panel: Side) => {
|
|
||||||
const CHART_WIDTH = panelSize;
|
|
||||||
const CHART_HEIGHT = panelSize;
|
|
||||||
|
|
||||||
const dimensions = panelDimensions[panel];
|
|
||||||
if (!dimensions) {
|
|
||||||
return panel === "top" || panel === "bottom" ? 5 : 3; // Fallback capacities
|
|
||||||
}
|
|
||||||
|
|
||||||
return panel === "top" || panel === "bottom"
|
|
||||||
? Math.max(1, Math.floor(dimensions.width / CHART_WIDTH))
|
|
||||||
: Math.max(1, Math.floor(dimensions.height / CHART_HEIGHT));
|
|
||||||
};
|
|
||||||
|
|
||||||
const isPanelFull = (panel: Side) => {
|
|
||||||
const currentWidgetCount = getCurrentWidgetCount(panel);
|
|
||||||
const panelCapacity = calculatePanelCapacity(panel);
|
|
||||||
|
|
||||||
return currentWidgetCount > panelCapacity;
|
|
||||||
};
|
|
||||||
|
|
||||||
const duplicateWidget = async () => {
|
const duplicateWidget = async () => {
|
||||||
try {
|
try {
|
||||||
|
|
||||||
|
@ -214,193 +187,117 @@ export const DraggableWidget = ({
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleKebabClick = (event: React.MouseEvent<HTMLDivElement>) => {
|
const handlePointerDown = () => {
|
||||||
event.stopPropagation();
|
if (selectedChartId?.id !== widget.id) {
|
||||||
if (openKebabId === widget.id) {
|
setSelectedChartId(widget);
|
||||||
setOpenKebabId(null);
|
|
||||||
} else {
|
|
||||||
setOpenKebabId(widget.id);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
const { isPlaying } = usePlayButtonStore();
|
||||||
const widgetRef = useRef<HTMLDivElement | null>(null);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const handleClickOutside = (event: MouseEvent) => {
|
|
||||||
if (
|
|
||||||
widgetRef.current &&
|
|
||||||
!widgetRef.current.contains(event.target as Node)
|
|
||||||
) {
|
|
||||||
setOpenKebabId(null);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
document.addEventListener("mousedown", handleClickOutside);
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
document.removeEventListener("mousedown", handleClickOutside);
|
|
||||||
};
|
|
||||||
}, [setOpenKebabId]);
|
|
||||||
|
|
||||||
const handleDragStart = (event: React.DragEvent<HTMLDivElement>) => {
|
|
||||||
event.dataTransfer.setData("text/plain", index.toString()); // Store the index of the dragged widget
|
|
||||||
};
|
|
||||||
const handleDragEnter = (event: React.DragEvent<HTMLDivElement>) => {
|
|
||||||
event.preventDefault(); // Allow drop
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleDragOver = (event: React.DragEvent<HTMLDivElement>) => {
|
|
||||||
event.preventDefault(); // Allow drop
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleDrop = (event: React.DragEvent<HTMLDivElement>) => {
|
|
||||||
event.preventDefault();
|
|
||||||
const fromIndex = parseInt(event.dataTransfer.getData("text/plain"), 10); // Get the dragged widget's index
|
|
||||||
const toIndex = index; // The index of the widget where the drop occurred
|
|
||||||
if (fromIndex !== toIndex) {
|
|
||||||
onReorder(fromIndex, toIndex); // Call the reorder function passed as a prop
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// useClickOutside(chartWidget, () => {
|
|
||||||
// setSelectedChartId(null);
|
|
||||||
// });
|
|
||||||
|
|
||||||
// Track canvas dimensions
|
|
||||||
|
|
||||||
// Current: Two identical useEffect hooks for canvas dimensions
|
|
||||||
// Remove the duplicate and keep only one
|
|
||||||
useEffect(() => {
|
|
||||||
const canvas = document.getElementById("real-time-vis-canvas");
|
|
||||||
if (!canvas) return;
|
|
||||||
|
|
||||||
const updateCanvasDimensions = () => {
|
|
||||||
const rect = canvas.getBoundingClientRect();
|
|
||||||
setCanvasDimensions({
|
|
||||||
width: rect.width,
|
|
||||||
height: rect.height,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
updateCanvasDimensions();
|
|
||||||
const resizeObserver = new ResizeObserver(updateCanvasDimensions);
|
|
||||||
resizeObserver.observe(canvas);
|
|
||||||
|
|
||||||
return () => resizeObserver.unobserve(canvas);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
|
||||||
<div
|
|
||||||
draggable
|
|
||||||
key={widget.id}
|
|
||||||
className={`chart-container ${selectedChartId?.id === widget.id && !isPlaying && "activeChart"
|
|
||||||
}`}
|
|
||||||
onPointerDown={handlePointerDown}
|
|
||||||
onDragStart={handleDragStart}
|
|
||||||
onDragEnter={handleDragEnter}
|
|
||||||
onDragOver={handleDragOver}
|
|
||||||
onDrop={handleDrop}
|
|
||||||
style={{
|
|
||||||
width: ["top", "bottom"].includes(widget.panel)
|
|
||||||
? `calc(${canvasDimensions.width}px / 6)`
|
|
||||||
: undefined,
|
|
||||||
height: ["left", "right"].includes(widget.panel)
|
|
||||||
? `calc(${canvasDimensions.height - 15}px / 4)`
|
|
||||||
: undefined,
|
|
||||||
}}
|
|
||||||
ref={chartWidget}
|
|
||||||
onClick={() => {
|
|
||||||
setSelectedChartId(widget);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{/* Kebab Icon */}
|
|
||||||
<div className="icon kebab" onClick={handleKebabClick}>
|
|
||||||
<KebabIcon />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Kebab Options */}
|
<div
|
||||||
{openKebabId === widget.id && (
|
|
||||||
<div className="kebab-options" ref={widgetRef}>
|
|
||||||
<div
|
|
||||||
className={`edit btn ${isPanelFull(widget.panel) ? "btn-blur" : ""
|
|
||||||
}`}
|
|
||||||
onClick={duplicateWidget}
|
|
||||||
>
|
|
||||||
<div className="icon">
|
|
||||||
<DublicateIcon />
|
|
||||||
</div>
|
|
||||||
<div className="label">Duplicate</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
className="edit btn"
|
|
||||||
onClick={(e) => {
|
|
||||||
e.stopPropagation();
|
|
||||||
deleteSelectedChart();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div className="icon">
|
|
||||||
<DeleteIcon />
|
|
||||||
</div>
|
|
||||||
<div className="label">Delete</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Render charts based on widget type */}
|
className={`item-content ${selectedChartId?.id === widget.id && !isPlaying && "activeChart"
|
||||||
|
}`}
|
||||||
|
onClick={() => {
|
||||||
|
setSelectedChartId(widget);
|
||||||
|
}}
|
||||||
|
onPointerDown={handlePointerDown}
|
||||||
|
|
||||||
{widget.type === "progress 1" && (
|
>
|
||||||
<ProgressCard1 title={widget.title} id={widget.id} />
|
<span className="order"> {index + 1} </span>
|
||||||
)}
|
<div className="icon kebab" onClick={handleKebabClick}>
|
||||||
{widget.type === "progress 2" && (
|
<KebabIcon />
|
||||||
<ProgressCard2 title={widget.title} id={widget.id} />
|
|
||||||
)}
|
|
||||||
{widget.type === "line" && (
|
|
||||||
<LineGraphComponent
|
|
||||||
id={widget.id}
|
|
||||||
type={widget.type}
|
|
||||||
title={widget.title}
|
|
||||||
fontSize={widget.fontSize}
|
|
||||||
fontWeight={widget.fontWeight}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{widget.type === "bar" && (
|
|
||||||
<BarGraphComponent
|
|
||||||
id={widget.id}
|
|
||||||
type={widget.type}
|
|
||||||
title={widget.title}
|
|
||||||
fontSize={widget.fontSize}
|
|
||||||
fontWeight={widget.fontWeight}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{widget.type === "pie" && (
|
|
||||||
<PieGraphComponent
|
|
||||||
id={widget.id}
|
|
||||||
type={widget.type}
|
|
||||||
title={widget.title}
|
|
||||||
fontSize={widget.fontSize}
|
|
||||||
fontWeight={widget.fontWeight}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{widget.type === "doughnut" && (
|
|
||||||
<DoughnutGraphComponent
|
|
||||||
id={widget.id}
|
|
||||||
type={widget.type}
|
|
||||||
title={widget.title}
|
|
||||||
fontSize={widget.fontSize}
|
|
||||||
fontWeight={widget.fontWeight}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{widget.type === "polarArea" && (
|
|
||||||
<PolarAreaGraphComponent
|
|
||||||
id={widget.id}
|
|
||||||
type={widget.type}
|
|
||||||
title={widget.title}
|
|
||||||
fontSize={widget.fontSize}
|
|
||||||
fontWeight={widget.fontWeight}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</>
|
{openKebabId === widget.id && (
|
||||||
|
<div className="kebab-options" ref={widgetRef}>
|
||||||
|
<div
|
||||||
|
className={`edit btn ${isPanelFull(widget.panel) ? "btn-blur" : ""
|
||||||
|
}`}
|
||||||
|
onClick={duplicateWidget}
|
||||||
|
>
|
||||||
|
<div className="icon">
|
||||||
|
<DublicateIcon />
|
||||||
|
</div>
|
||||||
|
<div className="label">Duplicate</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className="edit btn"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
deleteSelectedChart();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className="icon">
|
||||||
|
<DeleteIcon />
|
||||||
|
</div>
|
||||||
|
<div className="label">Delete</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Render charts based on widget type */}
|
||||||
|
|
||||||
|
{widget.type === "progress 1" && (
|
||||||
|
<ProgressCard1 title={widget.title} id={widget.id} />
|
||||||
|
)}
|
||||||
|
|
||||||
|
{widget.type === "progress 2" && (
|
||||||
|
<ProgressCard2 title={widget.title} id={widget.id} />
|
||||||
|
)}
|
||||||
|
|
||||||
|
{widget.type === "line" && (
|
||||||
|
<LineGraphComponent
|
||||||
|
id={widget.id}
|
||||||
|
type={widget.type}
|
||||||
|
title={widget.title}
|
||||||
|
fontSize={widget.fontSize}
|
||||||
|
fontWeight={widget.fontWeight}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{widget.type === "bar" && (
|
||||||
|
<BarGraphComponent
|
||||||
|
id={widget.id}
|
||||||
|
type={widget.type}
|
||||||
|
title={widget.title}
|
||||||
|
fontSize={widget.fontSize}
|
||||||
|
fontWeight={widget.fontWeight}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{widget.type === "pie" && (
|
||||||
|
<PieGraphComponent
|
||||||
|
id={widget.id}
|
||||||
|
type={widget.type}
|
||||||
|
title={widget.title}
|
||||||
|
fontSize={widget.fontSize}
|
||||||
|
fontWeight={widget.fontWeight}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{widget.type === "doughnut" && (
|
||||||
|
<DoughnutGraphComponent
|
||||||
|
id={widget.id}
|
||||||
|
type={widget.type}
|
||||||
|
title={widget.title}
|
||||||
|
fontSize={widget.fontSize}
|
||||||
|
fontWeight={widget.fontWeight}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{widget.type === "polarArea" && (
|
||||||
|
<PolarAreaGraphComponent
|
||||||
|
id={widget.id}
|
||||||
|
type={widget.type}
|
||||||
|
title={widget.title}
|
||||||
|
fontSize={widget.fontSize}
|
||||||
|
fontWeight={widget.fontWeight}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
|
|
@ -47,17 +47,8 @@ export default function Dropped3dWidgets() {
|
||||||
const { top, setTop } = useTopData();
|
const { top, setTop } = useTopData();
|
||||||
const { left, setLeft } = useLeftData();
|
const { left, setLeft } = useLeftData();
|
||||||
const { rightSelect, setRightSelect } = useRightSelected();
|
const { rightSelect, setRightSelect } = useRightSelected();
|
||||||
const { editWidgetOptions, setEditWidgetOptions } =
|
const { editWidgetOptions, setEditWidgetOptions } =useEditWidgetOptionsStore();
|
||||||
useEditWidgetOptionsStore();
|
const {zoneWidgetData,setZoneWidgetData,addWidget,updateWidgetPosition,updateWidgetRotation,tempWidget,tempWidgetPosition,} = useZoneWidgetStore();
|
||||||
const {
|
|
||||||
zoneWidgetData,
|
|
||||||
setZoneWidgetData,
|
|
||||||
addWidget,
|
|
||||||
updateWidgetPosition,
|
|
||||||
updateWidgetRotation,
|
|
||||||
tempWidget,
|
|
||||||
tempWidgetPosition,
|
|
||||||
} = useZoneWidgetStore();
|
|
||||||
const { setWidgets3D } = use3DWidget();
|
const { setWidgets3D } = use3DWidget();
|
||||||
const { visualizationSocket } = useSocketStore();
|
const { visualizationSocket } = useSocketStore();
|
||||||
const { rightClickSelected, setRightClickSelected } = useRightClickSelected();
|
const { rightClickSelected, setRightClickSelected } = useRightClickSelected();
|
||||||
|
@ -68,12 +59,8 @@ export default function Dropped3dWidgets() {
|
||||||
const mouseStartRef = useRef<{ x: number; y: number }>({ x: 0, y: 0 });
|
const mouseStartRef = useRef<{ x: number; y: number }>({ x: 0, y: 0 });
|
||||||
const { setSelectedChartId } = useWidgetStore();
|
const { setSelectedChartId } = useWidgetStore();
|
||||||
const { measurements, duration } = useChartStore();
|
const { measurements, duration } = useChartStore();
|
||||||
let [floorPlanesVertical, setFloorPlanesVertical] = useState(
|
let [floorPlanesVertical, setFloorPlanesVertical] = useState(new THREE.Plane(new THREE.Vector3(0, 1, 0)));
|
||||||
new THREE.Plane(new THREE.Vector3(0, 1, 0))
|
const [intersectcontextmenu, setintersectcontextmenu] = useState<number | undefined>();
|
||||||
);
|
|
||||||
const [intersectcontextmenu, setintersectcontextmenu] = useState<
|
|
||||||
number | undefined
|
|
||||||
>();
|
|
||||||
const [horizontalX, setHorizontalX] = useState<number | undefined>();
|
const [horizontalX, setHorizontalX] = useState<number | undefined>();
|
||||||
const [horizontalZ, setHorizontalZ] = useState<number | undefined>();
|
const [horizontalZ, setHorizontalZ] = useState<number | undefined>();
|
||||||
|
|
||||||
|
@ -86,12 +73,7 @@ export default function Dropped3dWidgets() {
|
||||||
|
|
||||||
|
|
||||||
async function get3dWidgetData() {
|
async function get3dWidgetData() {
|
||||||
const result = await get3dWidgetZoneData(
|
const result = await get3dWidgetZoneData(selectedZone.zoneUuid,organization, projectId);
|
||||||
selectedZone.zoneUuid,
|
|
||||||
organization,
|
|
||||||
projectId
|
|
||||||
);
|
|
||||||
|
|
||||||
setWidgets3D(result);
|
setWidgets3D(result);
|
||||||
if (result.length < 0) return;
|
if (result.length < 0) return;
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,26 @@
|
||||||
import React, { useEffect, useMemo, useRef, useState } from "react";
|
import React, {
|
||||||
|
useCallback,
|
||||||
import { arrayMove } from "@dnd-kit/sortable";
|
useEffect,
|
||||||
import { useSocketStore } from "../../../../store/builder/store";
|
useMemo,
|
||||||
import { usePlayButtonStore } from "../../../../store/usePlayButtonStore";
|
useRef,
|
||||||
import { useWidgetStore } from "../../../../store/useWidgetStore";
|
useState,
|
||||||
|
} from "react";
|
||||||
|
import gsap from "gsap";
|
||||||
|
import { Draggable } from "gsap/Draggable";
|
||||||
import { DraggableWidget } from "../2d/DraggableWidget";
|
import { DraggableWidget } from "../2d/DraggableWidget";
|
||||||
|
import { useWidgetStore } from "../../../../store/useWidgetStore";
|
||||||
|
import { generateUniqueId } from "../../../../functions/generateUniqueId";
|
||||||
|
import { useSocketStore } from "../../../../store/builder/store";
|
||||||
import { useParams } from "react-router-dom";
|
import { useParams } from "react-router-dom";
|
||||||
|
import { arrayMove } from "@dnd-kit/sortable";
|
||||||
|
import { clamp } from "three/src/math/MathUtils";
|
||||||
import { getUserData } from "../../../../functions/getUserData";
|
import { getUserData } from "../../../../functions/getUserData";
|
||||||
|
|
||||||
|
gsap.registerPlugin(Draggable);
|
||||||
|
|
||||||
|
|
||||||
type Side = "top" | "bottom" | "left" | "right";
|
type Side = "top" | "bottom" | "left" | "right";
|
||||||
|
const GAP = 6;
|
||||||
|
|
||||||
interface Widget {
|
interface Widget {
|
||||||
id: string;
|
id: string;
|
||||||
|
@ -30,50 +42,92 @@ interface PanelProps {
|
||||||
zoneViewPortPosition: number[];
|
zoneViewPortPosition: number[];
|
||||||
widgets: Widget[];
|
widgets: Widget[];
|
||||||
};
|
};
|
||||||
setSelectedZone: React.Dispatch<
|
setSelectedZone: React.Dispatch<React.SetStateAction<any>>;
|
||||||
React.SetStateAction<{
|
|
||||||
zoneName: string;
|
|
||||||
activeSides: Side[];
|
|
||||||
panelOrder: Side[];
|
|
||||||
lockedPanels: Side[];
|
|
||||||
points: [];
|
|
||||||
zoneUuid: string;
|
|
||||||
zoneViewPortTarget: number[];
|
|
||||||
zoneViewPortPosition: number[];
|
|
||||||
widgets: Widget[];
|
|
||||||
}>
|
|
||||||
>;
|
|
||||||
hiddenPanels: any;
|
hiddenPanels: any;
|
||||||
setZonesData: React.Dispatch<React.SetStateAction<any>>;
|
setZonesData: React.Dispatch<React.SetStateAction<any>>;
|
||||||
waitingPanels: any;
|
waitingPanels: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
const generateUniqueId = () =>
|
type SortableItem = {
|
||||||
`${Date.now()}-${Math.random().toString(36).slice(2, 11)}`;
|
dragger: Draggable;
|
||||||
|
element: HTMLDivElement;
|
||||||
|
index: number;
|
||||||
|
setIndex: (idx: number) => void;
|
||||||
|
};
|
||||||
|
|
||||||
const Panel: React.FC<PanelProps> = ({
|
const Panel: React.FC<PanelProps> = ({
|
||||||
selectedZone,
|
selectedZone,
|
||||||
setSelectedZone,
|
setSelectedZone,
|
||||||
hiddenPanels,
|
hiddenPanels,
|
||||||
setZonesData,
|
|
||||||
waitingPanels,
|
|
||||||
}) => {
|
}) => {
|
||||||
const panelRefs = useRef<{ [side in Side]?: HTMLDivElement }>({});
|
// const rowSize = 160;
|
||||||
const [panelDimensions, setPanelDimensions] = useState<{
|
// const colSize = 160;
|
||||||
[side in Side]?: { width: number; height: number };
|
const [rowSize, setRowSize] = useState<number>(160);
|
||||||
}>({});
|
const [colSize, setColSize] = useState<number>(160);
|
||||||
const [openKebabId, setOpenKebabId] = useState<string | null>(null);
|
const [openKebabId, setOpenKebabId] = useState<string | null>(null);
|
||||||
const { isPlaying } = usePlayButtonStore();
|
const [initialItems, setInitialItems] = useState<Widget[]>([]);
|
||||||
|
|
||||||
|
const containerRef = useRef<{ [side in Side]?: HTMLDivElement }>({});
|
||||||
|
const itemRefs = useRef<{ [side in Side]?: HTMLDivElement[] }>({});
|
||||||
|
const sortables = useRef<{ [side in Side]?: SortableItem[] }>({});
|
||||||
|
const draggables = useRef<{ [side in Side]?: Draggable[] }>({});
|
||||||
|
|
||||||
const { visualizationSocket } = useSocketStore();
|
const { visualizationSocket } = useSocketStore();
|
||||||
const [canvasDimensions, setCanvasDimensions] = useState({
|
const [canvasDimensions, setCanvasDimensions] = useState({
|
||||||
width: 0,
|
width: 0,
|
||||||
height: 0,
|
height: 0,
|
||||||
});
|
});
|
||||||
|
const [panelDimensions, setPanelDimensions] = useState<{
|
||||||
|
[side in Side]?: { width: number; height: number };
|
||||||
|
}>({});
|
||||||
const { projectId } = useParams();
|
const { projectId } = useParams();
|
||||||
|
const panelRefs = useRef<{ [side in Side]?: HTMLDivElement }>({});
|
||||||
const { userId, organization, email } = getUserData();
|
const { userId, organization, email } = getUserData();
|
||||||
|
|
||||||
|
|
||||||
|
// function handleResize() {
|
||||||
|
// const canvas = document.getElementById("sceneCanvas");
|
||||||
|
// if (canvas) {
|
||||||
|
// const rect = canvas.getBoundingClientRect();
|
||||||
|
// setRowSize(rect.height / 6)
|
||||||
|
// setColSize(rect.width / 6)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// useEffect(() => {
|
||||||
|
// window.addEventListener("resize", handleResize);
|
||||||
|
// return () => {
|
||||||
|
// window.removeEventListener("resize", handleResize);
|
||||||
|
// };
|
||||||
|
// }, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const observers: ResizeObserver[] = [];
|
||||||
|
const currentPanelRefs = panelRefs.current;
|
||||||
|
selectedZone.activeSides.forEach((side) => {
|
||||||
|
const element = currentPanelRefs[side];
|
||||||
|
if (element) {
|
||||||
|
const observer = new ResizeObserver((entries) => {
|
||||||
|
for (const entry of entries) {
|
||||||
|
const { width, height } = entry.contentRect;
|
||||||
|
setPanelDimensions((prev) => ({
|
||||||
|
...prev,
|
||||||
|
[side]: { width, height },
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
observer.observe(element);
|
||||||
|
observers.push(observer);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
observers.forEach((observer) => observer.disconnect());
|
||||||
|
};
|
||||||
|
}, [selectedZone.activeSides]);
|
||||||
// Track canvas dimensions
|
// Track canvas dimensions
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const canvas = document.getElementById("real-time-vis-canvas");
|
const canvas = document.getElementById("real-time-vis-canvas");
|
||||||
if (!canvas) return;
|
if (!canvas) return;
|
||||||
|
@ -98,8 +152,10 @@ const Panel: React.FC<PanelProps> = ({
|
||||||
Math.min(canvasDimensions.width * 0.25, canvasDimensions.height * 0.25),
|
Math.min(canvasDimensions.width * 0.25, canvasDimensions.height * 0.25),
|
||||||
170 // Min 170px
|
170 // Min 170px
|
||||||
);
|
);
|
||||||
|
useEffect(() => {
|
||||||
|
setInitialItems(selectedZone.widgets);
|
||||||
|
}, [selectedZone]);
|
||||||
|
|
||||||
// Define getPanelStyle
|
|
||||||
const getPanelStyle = useMemo(
|
const getPanelStyle = useMemo(
|
||||||
() => (side: Side) => {
|
() => (side: Side) => {
|
||||||
const currentIndex = selectedZone.panelOrder.indexOf(side);
|
const currentIndex = selectedZone.panelOrder.indexOf(side);
|
||||||
|
@ -140,27 +196,139 @@ const Panel: React.FC<PanelProps> = ({
|
||||||
},
|
},
|
||||||
[selectedZone.panelOrder, panelSize]
|
[selectedZone.panelOrder, panelSize]
|
||||||
);
|
);
|
||||||
|
const handleReorder = (fromIndex: number, toIndex: number, panel: Side) => {
|
||||||
|
setSelectedZone((prev: any) => {
|
||||||
|
const widgetsInPanel = prev.widgets.filter((w: any) => w.panel === panel);
|
||||||
|
const reorderedWidgets = arrayMove(widgetsInPanel, fromIndex, toIndex);
|
||||||
|
const updatedWidgets = prev.widgets
|
||||||
|
.filter((w: any) => w.panel !== panel)
|
||||||
|
.concat(reorderedWidgets);
|
||||||
|
|
||||||
// Handle drop event
|
return {
|
||||||
const handleDrop = (e: React.DragEvent, panel: Side) => {
|
...prev,
|
||||||
e.preventDefault();
|
widgets: updatedWidgets,
|
||||||
const { draggedAsset } = useWidgetStore.getState();
|
};
|
||||||
if (
|
});
|
||||||
!draggedAsset ||
|
setInitialItems(selectedZone.widgets);
|
||||||
isPanelLocked(panel) ||
|
|
||||||
hiddenPanels[selectedZone.zoneUuid]?.includes(panel)
|
|
||||||
)
|
|
||||||
return;
|
|
||||||
|
|
||||||
const currentWidgetsCount = getCurrentWidgetCount(panel);
|
|
||||||
const maxCapacity = calculatePanelCapacity(panel);
|
|
||||||
|
|
||||||
if (currentWidgetsCount < maxCapacity) {
|
|
||||||
addWidgetToPanel(draggedAsset, panel);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Check if panel is locked
|
useEffect(() => { }, [initialItems]);
|
||||||
|
|
||||||
|
let fromIndex = useRef<Number>();
|
||||||
|
let toIndex = useRef<Number>();
|
||||||
|
let itemsRef = useRef<any>();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
selectedZone.activeSides.forEach((side) => {
|
||||||
|
const container = containerRef.current[side];
|
||||||
|
const widgets = initialItems.filter((w) => w.panel === side);
|
||||||
|
const itemList = itemRefs.current[side] || [];
|
||||||
|
|
||||||
|
if (!container || widgets.length === 0) return;
|
||||||
|
|
||||||
|
gsap.to(container, { autoAlpha: 1, duration: 0.5 });
|
||||||
|
|
||||||
|
// Cleanup old draggables
|
||||||
|
draggables.current[side]?.forEach((d) => d.kill());
|
||||||
|
draggables.current[side] = [];
|
||||||
|
sortables.current[side] = [];
|
||||||
|
|
||||||
|
widgets.forEach((widget, index) => {
|
||||||
|
const element = itemList[index];
|
||||||
|
if (!element) return;
|
||||||
|
|
||||||
|
const content = element.querySelector(".item-content") as HTMLElement;
|
||||||
|
const order = element.querySelector(".order") as HTMLElement;
|
||||||
|
|
||||||
|
const animation = gsap.to(content, {
|
||||||
|
boxShadow: "rgba(0,0,0,0.2) 0px 16px 32px 0px",
|
||||||
|
scale: 1.05,
|
||||||
|
paused: true,
|
||||||
|
duration: 0.25,
|
||||||
|
});
|
||||||
|
|
||||||
|
const sortable: SortableItem = {
|
||||||
|
dragger: null as any,
|
||||||
|
element,
|
||||||
|
index,
|
||||||
|
setIndex(idx: number) {
|
||||||
|
sortable.index = idx;
|
||||||
|
if (order) order.textContent = (idx + 1).toString();
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const isHorizontal = side === "top" || side === "bottom";
|
||||||
|
|
||||||
|
const dragger = Draggable.create(element, {
|
||||||
|
type: isHorizontal ? "x" : "y",
|
||||||
|
cursor: "grab",
|
||||||
|
onPress() {
|
||||||
|
animation.play();
|
||||||
|
this.update();
|
||||||
|
},
|
||||||
|
onDrag() {
|
||||||
|
const pos = isHorizontal ? this.x : this.y;
|
||||||
|
const newIndex = clamp(
|
||||||
|
Math.round(pos / rowSize),
|
||||||
|
0,
|
||||||
|
widgets.length - 1
|
||||||
|
);
|
||||||
|
if (newIndex !== sortable.index) {
|
||||||
|
const arr = sortables.current[side]!;
|
||||||
|
const movedArr = arrayMove(arr, sortable.index, newIndex);
|
||||||
|
|
||||||
|
sortables.current[side] = movedArr;
|
||||||
|
if (newIndex === movedArr.length - 1) {
|
||||||
|
container.appendChild(element);
|
||||||
|
} else {
|
||||||
|
const i = sortable.index > newIndex ? newIndex : newIndex + 1;
|
||||||
|
container.insertBefore(element, container.children[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
movedArr.forEach((s, idx) => {
|
||||||
|
s.setIndex(idx);
|
||||||
|
if (s.element !== element) {
|
||||||
|
gsap.to(s.element, {
|
||||||
|
[isHorizontal ? "x" : "y"]: idx * (isHorizontal ? colSize : rowSize + GAP),
|
||||||
|
duration: 0.25,
|
||||||
|
overwrite: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onRelease() {
|
||||||
|
animation.reverse();
|
||||||
|
handleReorder(index, sortable.index, side);
|
||||||
|
|
||||||
|
gsap.to(element, {
|
||||||
|
[isHorizontal ? "x" : "y"]: sortable.index * (isHorizontal ? colSize : rowSize + GAP),
|
||||||
|
duration: 0.3,
|
||||||
|
});
|
||||||
|
|
||||||
|
},
|
||||||
|
})[0];
|
||||||
|
|
||||||
|
sortable.dragger = dragger;
|
||||||
|
draggables.current[side]?.push(dragger);
|
||||||
|
sortables.current[side]?.push(sortable);
|
||||||
|
|
||||||
|
gsap.set(element, {
|
||||||
|
[isHorizontal ? "x" : "y"]: index * (isHorizontal ? colSize : rowSize + GAP),
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
Object.values(draggables.current).forEach((draggers) => {
|
||||||
|
draggers?.forEach((d) => d.kill());
|
||||||
|
});
|
||||||
|
draggables.current = {};
|
||||||
|
sortables.current = {};
|
||||||
|
};
|
||||||
|
}, [initialItems]);
|
||||||
|
|
||||||
const isPanelLocked = (panel: Side) =>
|
const isPanelLocked = (panel: Side) =>
|
||||||
selectedZone.lockedPanels.includes(panel);
|
selectedZone.lockedPanels.includes(panel);
|
||||||
|
|
||||||
|
@ -174,6 +342,7 @@ const Panel: React.FC<PanelProps> = ({
|
||||||
const CHART_HEIGHT = panelSize - 10;
|
const CHART_HEIGHT = panelSize - 10;
|
||||||
|
|
||||||
const dimensions = panelDimensions[panel];
|
const dimensions = panelDimensions[panel];
|
||||||
|
|
||||||
if (!dimensions) {
|
if (!dimensions) {
|
||||||
return panel === "top" || panel === "bottom" ? 5 : 3; // Fallback capacities
|
return panel === "top" || panel === "bottom" ? 5 : 3; // Fallback capacities
|
||||||
}
|
}
|
||||||
|
@ -197,65 +366,40 @@ const Panel: React.FC<PanelProps> = ({
|
||||||
organization,
|
organization,
|
||||||
zoneUuid: selectedZone.zoneUuid,
|
zoneUuid: selectedZone.zoneUuid,
|
||||||
widget: newWidget,
|
widget: newWidget,
|
||||||
projectId, userId
|
projectId,
|
||||||
|
userId,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (visualizationSocket) {
|
if (visualizationSocket) {
|
||||||
visualizationSocket.emit("v1:viz-widget:add", addWidget);
|
visualizationSocket.emit("v1:viz-widget:add", addWidget);
|
||||||
}
|
}
|
||||||
|
|
||||||
setSelectedZone((prev) => ({
|
setSelectedZone((prev: any) => ({
|
||||||
...prev,
|
...prev,
|
||||||
widgets: [...prev.widgets, newWidget],
|
widgets: [...prev.widgets, newWidget],
|
||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
// Observe panel dimensions
|
// Handle drop event
|
||||||
useEffect(() => {
|
const handleDrop = (e: React.DragEvent, panel: Side) => {
|
||||||
const observers: ResizeObserver[] = [];
|
e.preventDefault();
|
||||||
const currentPanelRefs = panelRefs.current;
|
|
||||||
|
|
||||||
selectedZone.activeSides.forEach((side) => {
|
const { draggedAsset } = useWidgetStore.getState();
|
||||||
const element = currentPanelRefs[side];
|
if (
|
||||||
if (element) {
|
!draggedAsset ||
|
||||||
const observer = new ResizeObserver((entries) => {
|
isPanelLocked(panel) ||
|
||||||
for (const entry of entries) {
|
hiddenPanels[selectedZone.zoneUuid]?.includes(panel)
|
||||||
const { width, height } = entry.contentRect;
|
)
|
||||||
setPanelDimensions((prev) => ({
|
return;
|
||||||
...prev,
|
|
||||||
[side]: { width, height },
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
observer.observe(element);
|
const currentWidgetsCount = getCurrentWidgetCount(panel);
|
||||||
observers.push(observer);
|
const maxCapacity = calculatePanelCapacity(panel);
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return () => {
|
if (currentWidgetsCount < maxCapacity) {
|
||||||
observers.forEach((observer) => observer.disconnect());
|
addWidgetToPanel(draggedAsset, panel);
|
||||||
};
|
} else {
|
||||||
}, [selectedZone.activeSides]);
|
}
|
||||||
|
|
||||||
// Handle widget reordering
|
|
||||||
const handleReorder = (fromIndex: number, toIndex: number, panel: Side) => {
|
|
||||||
setSelectedZone((prev) => {
|
|
||||||
const widgetsInPanel = prev.widgets.filter((w) => w.panel === panel);
|
|
||||||
const reorderedWidgets = arrayMove(widgetsInPanel, fromIndex, toIndex);
|
|
||||||
|
|
||||||
const updatedWidgets = prev.widgets
|
|
||||||
.filter((widget) => widget.panel !== panel)
|
|
||||||
.concat(reorderedWidgets);
|
|
||||||
|
|
||||||
return {
|
|
||||||
...prev,
|
|
||||||
widgets: updatedWidgets,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Calculate capacities and dimensions
|
|
||||||
const topWidth = getPanelStyle("top").width;
|
const topWidth = getPanelStyle("top").width;
|
||||||
const bottomWidth = getPanelStyle("bottom").height;
|
const bottomWidth = getPanelStyle("bottom").height;
|
||||||
const leftHeight = getPanelStyle("left").height;
|
const leftHeight = getPanelStyle("left").height;
|
||||||
|
@ -265,7 +409,6 @@ const Panel: React.FC<PanelProps> = ({
|
||||||
const bottomCapacity = calculatePanelCapacity("bottom");
|
const bottomCapacity = calculatePanelCapacity("bottom");
|
||||||
const leftCapacity = calculatePanelCapacity("left");
|
const leftCapacity = calculatePanelCapacity("left");
|
||||||
const rightCapacity = calculatePanelCapacity("right");
|
const rightCapacity = calculatePanelCapacity("right");
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<style>
|
<style>
|
||||||
|
@ -275,7 +418,6 @@ const Panel: React.FC<PanelProps> = ({
|
||||||
--bottomWidth: ${bottomWidth} ;
|
--bottomWidth: ${bottomWidth} ;
|
||||||
--leftHeight: ${leftHeight};
|
--leftHeight: ${leftHeight};
|
||||||
--rightHeight: ${rightHeight};
|
--rightHeight: ${rightHeight};
|
||||||
|
|
||||||
--topCapacity: ${topCapacity};
|
--topCapacity: ${topCapacity};
|
||||||
--bottomCapacity: ${bottomCapacity};
|
--bottomCapacity: ${bottomCapacity};
|
||||||
--leftCapacity: ${leftCapacity};
|
--leftCapacity: ${leftCapacity};
|
||||||
|
@ -286,10 +428,6 @@ const Panel: React.FC<PanelProps> = ({
|
||||||
|
|
||||||
{selectedZone.activeSides.map((side) => (
|
{selectedZone.activeSides.map((side) => (
|
||||||
<div
|
<div
|
||||||
key={side}
|
|
||||||
id={`panel-wrapper-${side}`}
|
|
||||||
className={`panel ${side}-panel absolute ${hiddenPanels[selectedZone.zoneUuid]?.includes(side) ? "hidePanel" : ""
|
|
||||||
}`}
|
|
||||||
style={getPanelStyle(side)}
|
style={getPanelStyle(side)}
|
||||||
onDrop={(e) => handleDrop(e, side)}
|
onDrop={(e) => handleDrop(e, side)}
|
||||||
onDragOver={(e) => e.preventDefault()}
|
onDragOver={(e) => e.preventDefault()}
|
||||||
|
@ -300,39 +438,53 @@ const Panel: React.FC<PanelProps> = ({
|
||||||
delete panelRefs.current[side];
|
delete panelRefs.current[side];
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
|
key={side}
|
||||||
|
id={`panel-wrapper-${side}`}
|
||||||
|
className={`panel ${side}-panel absolute ${hiddenPanels[selectedZone.zoneUuid]?.includes(side)
|
||||||
|
? "hidePanel"
|
||||||
|
: ""
|
||||||
|
}`}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className={`panel-content ${waitingPanels === side ? `${side}-closing` : ""
|
className="container"
|
||||||
}${!hiddenPanels[selectedZone.zoneUuid]?.includes(side) &&
|
// ref={containerRef}
|
||||||
waitingPanels !== side
|
ref={(el) => {
|
||||||
? `${side}-opening`
|
if (el) containerRef.current[side] = el;
|
||||||
: ""
|
|
||||||
} ${isPlaying ? "fullScreen" : ""}`}
|
|
||||||
style={{
|
|
||||||
pointerEvents:
|
|
||||||
selectedZone.lockedPanels.includes(side) ||
|
|
||||||
hiddenPanels[selectedZone.zoneUuid]?.includes(side)
|
|
||||||
? "none"
|
|
||||||
: "auto",
|
|
||||||
opacity: selectedZone.lockedPanels.includes(side) ? "0.8" : "1",
|
|
||||||
}}
|
}}
|
||||||
|
style={{ position: "relative", width: "100%" }}
|
||||||
>
|
>
|
||||||
{selectedZone.widgets
|
{initialItems
|
||||||
.filter((w) => w.panel === side)
|
.filter((w) => w.panel === side)
|
||||||
.map((widget, index) => (
|
.map((label, index) => (
|
||||||
<DraggableWidget
|
<div
|
||||||
hiddenPanels={hiddenPanels}
|
className="list-item chart-container"
|
||||||
widget={widget}
|
key={label.id}
|
||||||
key={widget.id}
|
data-panel={label.panel}
|
||||||
index={index}
|
|
||||||
onReorder={(fromIndex, toIndex) =>
|
ref={(el) => {
|
||||||
handleReorder(fromIndex, toIndex, side)
|
if (!itemRefs.current[side]) itemRefs.current[side] = [];
|
||||||
}
|
if (el) itemRefs.current[side]![index] = el;
|
||||||
openKebabId={openKebabId}
|
}}
|
||||||
setOpenKebabId={setOpenKebabId}
|
|
||||||
selectedZone={selectedZone}
|
style={{
|
||||||
setSelectedZone={setSelectedZone}
|
position: "absolute",
|
||||||
/>
|
left: 0,
|
||||||
|
height: ["left", "right"].includes(side) ? rowSize : "",
|
||||||
|
width: ["left", "right"].includes(side) ? "" : colSize,
|
||||||
|
cursor: "grab",
|
||||||
|
userSelect: "none",
|
||||||
|
// marginBottom: side === "left" || side === "right" ? "10px" : "",
|
||||||
|
// marginRight: side === "top" || side === "bottom" ? "10px" : "",
|
||||||
|
}}
|
||||||
|
|
||||||
|
>
|
||||||
|
<DraggableWidget
|
||||||
|
widget={label}
|
||||||
|
index={index}
|
||||||
|
openKebabId={openKebabId}
|
||||||
|
setOpenKebabId={setOpenKebabId}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -341,4 +493,4 @@ const Panel: React.FC<PanelProps> = ({
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Panel;
|
export default Panel;
|
||||||
|
|
|
@ -178,7 +178,6 @@
|
||||||
color: var(--accent-color);
|
color: var(--accent-color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -199,8 +198,10 @@
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
display: flex;
|
// display: flex;
|
||||||
flex-direction: column;
|
// flex-direction: column;
|
||||||
|
display: block;
|
||||||
|
position: relative;
|
||||||
gap: 6px;
|
gap: 6px;
|
||||||
background: var(--background-color);
|
background: var(--background-color);
|
||||||
backdrop-filter: blur(10px);
|
backdrop-filter: blur(10px);
|
||||||
|
@ -217,9 +218,18 @@
|
||||||
|
|
||||||
box-shadow: var(--box-shadow-medium);
|
box-shadow: var(--box-shadow-medium);
|
||||||
background: var(--background-color);
|
background: var(--background-color);
|
||||||
position: relative;
|
position: absolute;
|
||||||
padding: 0 10px;
|
padding: 0 10px;
|
||||||
animation: scaleFadeIn 0.4s forwards;
|
// position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
user-select: none;
|
||||||
|
z-index: 1;
|
||||||
|
// animation: scaleFadeIn 0.4s forwards;
|
||||||
|
.chart-container.dragging {
|
||||||
|
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.3);
|
||||||
|
transition: box-shadow 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
.kebab {
|
.kebab {
|
||||||
width: 30px;
|
width: 30px;
|
||||||
|
@ -370,9 +380,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Side Buttons
|
// Side Buttons
|
||||||
.side-button-container {
|
.side-button-container {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
@ -430,7 +437,7 @@
|
||||||
|
|
||||||
path {
|
path {
|
||||||
stroke: var(--text-button-color);
|
stroke: var(--text-button-color);
|
||||||
strokeWidth: 2;
|
stroke-width: 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -442,7 +449,7 @@
|
||||||
|
|
||||||
path {
|
path {
|
||||||
stroke: #f65648;
|
stroke: #f65648;
|
||||||
strokeWidth: 1.3;
|
stroke-width: 1.3;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -980,4 +987,165 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Drad
|
||||||
|
|
||||||
|
.container {
|
||||||
|
position: absolute;
|
||||||
|
// top: 50%;
|
||||||
|
// left: 50%;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 12px;
|
||||||
|
min-width: 160px;
|
||||||
|
height: 100%;
|
||||||
|
opacity: 0;
|
||||||
|
visibility: hidden;
|
||||||
|
// transform: translate(-50%, -50%);
|
||||||
|
pointer-events: all;
|
||||||
|
}
|
||||||
|
|
||||||
|
// .list-item {
|
||||||
|
// position: absolute;
|
||||||
|
// top: 0;
|
||||||
|
// left: 0;
|
||||||
|
// height: 90px;
|
||||||
|
// width: 100%;
|
||||||
|
// }
|
||||||
|
|
||||||
|
.panel {
|
||||||
|
background: var(--background-color);
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
pointer-events: all;
|
||||||
|
padding: 6px;
|
||||||
|
&.left-panel,
|
||||||
|
&.right-panel {
|
||||||
|
min-width: 150px;
|
||||||
|
.panel-content {
|
||||||
|
flex-direction: column;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
gap: 6px;
|
||||||
|
|
||||||
|
.chart-container {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
min-height: 150px;
|
||||||
|
max-height: 100%;
|
||||||
|
border-radius: #{$border-radius-medium};
|
||||||
|
box-shadow: var(--box-shadow-medium);
|
||||||
|
padding: 6px 0;
|
||||||
|
background: var(--background-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.top-panel,
|
||||||
|
&.bottom-panel {
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
min-height: 150px;
|
||||||
|
.container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
.chart-container {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.panel-content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
min-height: 150px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.chart-container {
|
||||||
|
width: 100%;
|
||||||
|
min-height: 150px;
|
||||||
|
max-height: 100%;
|
||||||
|
border-radius: #{$border-radius-medium};
|
||||||
|
|
||||||
|
box-shadow: var(--box-shadow-medium);
|
||||||
|
background: var(--background-color);
|
||||||
|
position: relative;
|
||||||
|
// margin: 6px 0;
|
||||||
|
|
||||||
|
.item-content {
|
||||||
|
height: 100%;
|
||||||
|
padding: 6px;
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 6px 0;
|
||||||
|
.kebab {
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
position: absolute;
|
||||||
|
top: 0px;
|
||||||
|
right: 0px;
|
||||||
|
z-index: 10;
|
||||||
|
cursor: pointer;
|
||||||
|
@include flex-center;
|
||||||
|
}
|
||||||
|
.kebab-options {
|
||||||
|
position: absolute;
|
||||||
|
top: 18px;
|
||||||
|
right: 5px;
|
||||||
|
transform: translate(0px, 0);
|
||||||
|
background: var(--background-color);
|
||||||
|
z-index: 10;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 6px;
|
||||||
|
border-radius: #{$border-radius-small};
|
||||||
|
|
||||||
|
box-shadow: var(--box-shadow-medium);
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
display: flex;
|
||||||
|
gap: 6px;
|
||||||
|
align-items: center;
|
||||||
|
padding: 5px 10px;
|
||||||
|
color: var(--text-color);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: var(--highlight-accent-color);
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
.label {
|
||||||
|
color: var(--accent-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
svg {
|
||||||
|
&:first-child {
|
||||||
|
fill: var(--accent-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
fill: auto;
|
||||||
|
stroke: var(--accent-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-blur {
|
||||||
|
color: var(--text-disabled);
|
||||||
|
cursor: not-allowed;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
canvas {
|
||||||
|
height: 80%;
|
||||||
|
}
|
||||||
|
.order {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue