created new window
This commit is contained in:
48
app/package-lock.json
generated
48
app/package-lock.json
generated
@@ -2029,7 +2029,7 @@
|
||||
"version": "0.8.1",
|
||||
"resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
|
||||
"integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/trace-mapping": "0.3.9"
|
||||
},
|
||||
@@ -2041,7 +2041,7 @@
|
||||
"version": "0.3.9",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz",
|
||||
"integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/resolve-uri": "^3.0.3",
|
||||
"@jridgewell/sourcemap-codec": "^1.4.10"
|
||||
@@ -4192,6 +4192,26 @@
|
||||
"url": "https://github.com/sponsors/gregberge"
|
||||
}
|
||||
},
|
||||
"node_modules/@testing-library/dom": {
|
||||
"version": "10.4.1",
|
||||
"resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.1.tgz",
|
||||
"integrity": "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==",
|
||||
"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",
|
||||
"dom-accessibility-api": "^0.5.9",
|
||||
"lz-string": "^1.5.0",
|
||||
"picocolors": "1.1.1",
|
||||
"pretty-format": "^27.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@testing-library/jest-dom": {
|
||||
"version": "5.17.0",
|
||||
"resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-5.17.0.tgz",
|
||||
@@ -4303,25 +4323,25 @@
|
||||
"version": "1.0.11",
|
||||
"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz",
|
||||
"integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==",
|
||||
"dev": true
|
||||
"devOptional": true
|
||||
},
|
||||
"node_modules/@tsconfig/node12": {
|
||||
"version": "1.0.11",
|
||||
"resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz",
|
||||
"integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==",
|
||||
"dev": true
|
||||
"devOptional": true
|
||||
},
|
||||
"node_modules/@tsconfig/node14": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz",
|
||||
"integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==",
|
||||
"dev": true
|
||||
"devOptional": true
|
||||
},
|
||||
"node_modules/@tsconfig/node16": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz",
|
||||
"integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==",
|
||||
"dev": true
|
||||
"devOptional": true
|
||||
},
|
||||
"node_modules/@turf/along": {
|
||||
"version": "7.2.0",
|
||||
@@ -9093,7 +9113,7 @@
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
|
||||
"integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
|
||||
"dev": true
|
||||
"devOptional": true
|
||||
},
|
||||
"node_modules/cross-env": {
|
||||
"version": "7.0.3",
|
||||
@@ -9970,7 +9990,7 @@
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
|
||||
"integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"engines": {
|
||||
"node": ">=0.3.1"
|
||||
}
|
||||
@@ -15354,7 +15374,7 @@
|
||||
"version": "1.3.6",
|
||||
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
|
||||
"integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
|
||||
"dev": true
|
||||
"devOptional": true
|
||||
},
|
||||
"node_modules/makeerror": {
|
||||
"version": "1.0.12",
|
||||
@@ -20908,7 +20928,7 @@
|
||||
"version": "10.9.2",
|
||||
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz",
|
||||
"integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"dependencies": {
|
||||
"@cspotcode/source-map-support": "^0.8.0",
|
||||
"@tsconfig/node10": "^1.0.7",
|
||||
@@ -20951,7 +20971,7 @@
|
||||
"version": "8.3.4",
|
||||
"resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz",
|
||||
"integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"dependencies": {
|
||||
"acorn": "^8.11.0"
|
||||
},
|
||||
@@ -20963,7 +20983,7 @@
|
||||
"version": "4.1.3",
|
||||
"resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
|
||||
"integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
|
||||
"dev": true
|
||||
"devOptional": true
|
||||
},
|
||||
"node_modules/tsconfig-paths": {
|
||||
"version": "3.15.0",
|
||||
@@ -21459,7 +21479,7 @@
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
|
||||
"integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==",
|
||||
"dev": true
|
||||
"devOptional": true
|
||||
},
|
||||
"node_modules/v8-to-istanbul": {
|
||||
"version": "8.1.1",
|
||||
@@ -22518,7 +22538,7 @@
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
|
||||
"integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
import { useCompareProductDataStore, useLoadingProgress, useIsComparing } from "../../../store/builder/store";
|
||||
import {
|
||||
useCompareProductDataStore,
|
||||
useLoadingProgress,
|
||||
useIsComparing,
|
||||
useCreateNewWindow,
|
||||
} from "../../../store/builder/store";
|
||||
import { useSimulationState } from "../../../store/simulation/useSimulationStore";
|
||||
import { usePlayButtonStore } from "../../../store/ui/usePlayButtonStore";
|
||||
import { useEffect, useState } from "react";
|
||||
@@ -11,7 +16,8 @@ import { useSimulationManager } from "../../../store/rough/useSimulationManagerS
|
||||
import { useParams } from "react-router-dom";
|
||||
import { validateSimulationDataApi } from "../../../services/simulation/comparison/validateSimulationDataApi";
|
||||
import { calculateSimulationData } from "./functions/calculateSimulationData";
|
||||
|
||||
import NewWindowScene from "../../ui/compareVersion/NewWindowScene";
|
||||
import Button from "../../ui/compareVersion/Button";
|
||||
type AssetData = {
|
||||
activeTime: number;
|
||||
idleTime: number;
|
||||
@@ -41,7 +47,11 @@ export interface CompareProduct {
|
||||
//shiftsPerDay: number;
|
||||
};
|
||||
}
|
||||
export const createCompareProduct = (productUuid: string, productName: string, assets: AssetData[]): CompareProduct => ({
|
||||
export const createCompareProduct = (
|
||||
productUuid: string,
|
||||
productName: string,
|
||||
assets: AssetData[]
|
||||
): CompareProduct => ({
|
||||
productUuid,
|
||||
productName,
|
||||
simulationData: calculateSimulationData(assets),
|
||||
@@ -61,8 +71,10 @@ function ComparisonScene() {
|
||||
const { setCompareProductsData } = useCompareProductDataStore();
|
||||
const [shouldShowComparisonResult, setShouldShowComparisonResult] = useState(false);
|
||||
const { addSimulationRecord } = useSimulationManager();
|
||||
|
||||
useEffect(() => {});
|
||||
const { createNewWindow } = useCreateNewWindow();
|
||||
useEffect(() => {
|
||||
console.log("comparisonScene: ", comparisonScene);
|
||||
}, [comparisonScene]);
|
||||
|
||||
const handleSelectVersion = (option: string) => {
|
||||
const version = versionHistory.find((version) => version.versionName === option);
|
||||
@@ -77,12 +89,22 @@ function ComparisonScene() {
|
||||
echo.log(getData.message);
|
||||
const getSimulate = getData?.data?.existingSimulatedData;
|
||||
if (!getSimulate) return;
|
||||
if (!selectedVersion?.versionId || !projectId || getSimulate === undefined || !selectedProduct.productUuid) {
|
||||
if (
|
||||
!selectedVersion?.versionId ||
|
||||
!projectId ||
|
||||
getSimulate === undefined ||
|
||||
!selectedProduct.productUuid
|
||||
) {
|
||||
echo.warn("No prebacked Data found");
|
||||
alert("Please run the simulation before comparing.");
|
||||
return;
|
||||
}
|
||||
addSimulationRecord(projectId, selectedVersion?.versionId || "", selectedProduct.productUuid || "", getSimulate.data);
|
||||
addSimulationRecord(
|
||||
projectId,
|
||||
selectedVersion?.versionId || "",
|
||||
selectedProduct.productUuid || "",
|
||||
getSimulate.data
|
||||
);
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -105,7 +127,12 @@ function ComparisonScene() {
|
||||
echo.warn(getData.message);
|
||||
const getSimulate = getData?.data?.existingSimulatedData;
|
||||
if (!getSimulate) return;
|
||||
addSimulationRecord(projectId, selectedVersion?.versionId || "", product.productUuid || "", getSimulate.data);
|
||||
addSimulationRecord(
|
||||
projectId,
|
||||
selectedVersion?.versionId || "",
|
||||
product.productUuid || "",
|
||||
getSimulate.data
|
||||
);
|
||||
});
|
||||
setComparisonState(data);
|
||||
}
|
||||
@@ -113,12 +140,32 @@ function ComparisonScene() {
|
||||
|
||||
useEffect(() => {
|
||||
if (mainScene && comparisonScene && selectedVersion) {
|
||||
const mainVersion = useSimulationManager.getState().getProductById(projectId, mainScene.version.versionUuid, mainScene.product.productUuid);
|
||||
const mainVersion = useSimulationManager
|
||||
.getState()
|
||||
.getProductById(
|
||||
projectId,
|
||||
mainScene.version.versionUuid,
|
||||
mainScene.product.productUuid
|
||||
);
|
||||
|
||||
const compareVersion = useSimulationManager.getState().getProductById(projectId, comparisonScene.version.versionUuid, comparisonScene.product.productUuid);
|
||||
const compareVersion = useSimulationManager
|
||||
.getState()
|
||||
.getProductById(
|
||||
projectId,
|
||||
comparisonScene.version.versionUuid,
|
||||
comparisonScene.product.productUuid
|
||||
);
|
||||
|
||||
const mainVompareversion = createCompareProduct(mainVersion?.productId ?? "", mainScene.product.productName, mainVersion?.simulateData || []);
|
||||
const compareProduct2 = createCompareProduct(compareVersion?.productId ?? "", comparisonScene.product.productName, compareVersion?.simulateData || []);
|
||||
const mainVompareversion = createCompareProduct(
|
||||
mainVersion?.productId ?? "",
|
||||
mainScene.product.productName,
|
||||
mainVersion?.simulateData || []
|
||||
);
|
||||
const compareProduct2 = createCompareProduct(
|
||||
compareVersion?.productId ?? "",
|
||||
comparisonScene.product.productName,
|
||||
compareVersion?.simulateData || []
|
||||
);
|
||||
|
||||
const comparedArray = [mainVompareversion, compareProduct2];
|
||||
|
||||
@@ -131,7 +178,14 @@ function ComparisonScene() {
|
||||
} else {
|
||||
setShouldShowComparisonResult(false);
|
||||
}
|
||||
}, [mainScene, comparisonScene, selectedVersion, projectId, setCompareProductsData, simulationRecords]);
|
||||
}, [
|
||||
mainScene,
|
||||
comparisonScene,
|
||||
selectedVersion,
|
||||
projectId,
|
||||
setCompareProductsData,
|
||||
simulationRecords,
|
||||
]);
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -154,7 +208,20 @@ function ComparisonScene() {
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<CompareLayOut />
|
||||
{selectedVersion?.versionId && (
|
||||
<div
|
||||
style={{
|
||||
position: "absolute",
|
||||
top: "10px",
|
||||
right: "900px",
|
||||
zIndex: 10,
|
||||
}}
|
||||
>
|
||||
<Button />
|
||||
</div>
|
||||
)}
|
||||
{<CompareLayOut />}
|
||||
{createNewWindow && <NewWindowScene />}
|
||||
{shouldShowComparisonResult && !loadingProgress && <ComparisonResult />}
|
||||
</>
|
||||
)}
|
||||
|
||||
@@ -2,182 +2,382 @@ import React, { ReactNode, useEffect, useRef, useState } from "react";
|
||||
import { createPortal } from "react-dom";
|
||||
|
||||
type NewWindowProps = {
|
||||
children: ReactNode;
|
||||
title?: string;
|
||||
width?: number;
|
||||
height?: number;
|
||||
left?: number;
|
||||
top?: number;
|
||||
center?: boolean;
|
||||
features?: Partial<{
|
||||
toolbar: boolean;
|
||||
menubar: boolean;
|
||||
scrollbars: boolean;
|
||||
resizable: boolean;
|
||||
location: boolean;
|
||||
status: boolean;
|
||||
}>;
|
||||
onClose?: () => void;
|
||||
copyStyles?: boolean;
|
||||
noopener?: boolean;
|
||||
className?: string;
|
||||
theme?: string | null;
|
||||
children: ReactNode;
|
||||
title?: string;
|
||||
width?: number;
|
||||
height?: number;
|
||||
left?: number;
|
||||
top?: number;
|
||||
center?: boolean;
|
||||
features?: Partial<{
|
||||
toolbar: boolean;
|
||||
menubar: boolean;
|
||||
scrollbars: boolean;
|
||||
resizable: boolean;
|
||||
location: boolean;
|
||||
status: boolean;
|
||||
}>;
|
||||
onClose?: () => void;
|
||||
copyStyles?: boolean;
|
||||
noopener?: boolean;
|
||||
className?: string;
|
||||
theme?: string | null;
|
||||
};
|
||||
|
||||
// export const RenderInNewWindow: React.FC<NewWindowProps> = ({
|
||||
// children,
|
||||
// title = "New Window",
|
||||
// width = 900,
|
||||
// height = 700,
|
||||
// left,
|
||||
// top,
|
||||
// center = true,
|
||||
// features,
|
||||
// onClose,
|
||||
// copyStyles = true,
|
||||
// noopener = true,
|
||||
// className,
|
||||
// theme = localStorage.getItem("theme") ?? "light",
|
||||
// }) => {
|
||||
// const [mounted, setMounted] = useState(false);
|
||||
// const childWindowRef = useRef<Window | null>(null);
|
||||
// const containerElRef = useRef<HTMLDivElement | null>(null);
|
||||
|
||||
// useEffect(() => {
|
||||
// if (typeof window === "undefined") return;
|
||||
|
||||
// const screenLeft = window.screenLeft ?? window.screenX ?? 0;
|
||||
// const screenTop = window.screenTop ?? window.screenY ?? 0;
|
||||
// const availWidth = window.outerWidth ?? window.innerWidth;
|
||||
// const availHeight = window.outerHeight ?? window.innerHeight;
|
||||
|
||||
// const finalLeft =
|
||||
// center && availWidth ? Math.max(0, screenLeft + (availWidth - width) / 2) : left ?? 100;
|
||||
|
||||
// const finalTop =
|
||||
// center && availHeight
|
||||
// ? Math.max(0, screenTop + (availHeight - height) / 2)
|
||||
// : top ?? 100;
|
||||
|
||||
// const baseFeatures = [
|
||||
// `width=${Math.floor(width)}`,
|
||||
// `height=${Math.floor(height)}`,
|
||||
// `left=${Math.floor(finalLeft)}`,
|
||||
// `top=${Math.floor(finalTop)}`,
|
||||
// ];
|
||||
|
||||
// const featureFlags = features ?? {
|
||||
// toolbar: false,
|
||||
// menubar: false,
|
||||
// scrollbars: true,
|
||||
// resizable: true,
|
||||
// location: false,
|
||||
// status: false,
|
||||
// };
|
||||
|
||||
// Object.entries(featureFlags).forEach(([k, v]) =>
|
||||
// baseFeatures.push(`${k}=${v ? "yes" : "no"}`)
|
||||
// );
|
||||
|
||||
// const newWin = window.open("", "_blank", baseFeatures.join(","));
|
||||
// if (!newWin) {
|
||||
// console.warn("Popup blocked or failed to open window.");
|
||||
// onClose?.();
|
||||
// return;
|
||||
// }
|
||||
|
||||
// if (noopener) {
|
||||
// try {
|
||||
// newWin.opener = null;
|
||||
// } catch {}
|
||||
// }
|
||||
|
||||
// newWin.document.open();
|
||||
// newWin.document.write(`<!doctype html>
|
||||
// <html data-theme=${theme}>
|
||||
// <head>
|
||||
// <meta charset="utf-8" />
|
||||
// <title>${title}</title>
|
||||
// <meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
// </head>
|
||||
// <body style="margin:0;"></body>
|
||||
// </html>`);
|
||||
// newWin.document.close();
|
||||
|
||||
// if (copyStyles) {
|
||||
// const head = newWin.document.head;
|
||||
// Array.from(document.styleSheets).forEach((styleSheet) => {
|
||||
// try {
|
||||
// if ((styleSheet as CSSStyleSheet).cssRules) {
|
||||
// const newStyleEl = newWin.document.createElement("style");
|
||||
// const rules = Array.from((styleSheet as CSSStyleSheet).cssRules).map(
|
||||
// (r) => r.cssText
|
||||
// );
|
||||
// newStyleEl.appendChild(newWin.document.createTextNode(rules.join("\n")));
|
||||
// head.appendChild(newStyleEl);
|
||||
// }
|
||||
// } catch {
|
||||
// const ownerNode = styleSheet.ownerNode as HTMLElement | null;
|
||||
// if (ownerNode && ownerNode.tagName === "LINK") {
|
||||
// const link = ownerNode as HTMLLinkElement;
|
||||
// const newLink = newWin.document.createElement("link");
|
||||
// newLink.rel = link.rel;
|
||||
// newLink.href = link.href;
|
||||
// newLink.media = link.media;
|
||||
// head.appendChild(newLink);
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
|
||||
// const container = newWin.document.createElement("div");
|
||||
// if (className) container.className = className;
|
||||
// newWin.document.body.appendChild(container);
|
||||
|
||||
// newWin.document.title = title;
|
||||
|
||||
// // Handle child window close
|
||||
// const handleChildUnload = () => {
|
||||
// onClose?.();
|
||||
// };
|
||||
// newWin.addEventListener("beforeunload", handleChildUnload);
|
||||
|
||||
// // 👇 Handle parent refresh/close → auto close child
|
||||
// const handleParentUnload = () => {
|
||||
// try {
|
||||
// newWin.close();
|
||||
// } catch {}
|
||||
// };
|
||||
// window.addEventListener("beforeunload", handleParentUnload);
|
||||
|
||||
// childWindowRef.current = newWin;
|
||||
// containerElRef.current = container;
|
||||
// setMounted(true);
|
||||
|
||||
// return () => {
|
||||
// newWin.removeEventListener("beforeunload", handleChildUnload);
|
||||
// window.removeEventListener("beforeunload", handleParentUnload);
|
||||
// try {
|
||||
// newWin.close();
|
||||
// } catch {}
|
||||
// childWindowRef.current = null;
|
||||
// containerElRef.current = null;
|
||||
// };
|
||||
// // eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
// }, []);
|
||||
|
||||
// useEffect(() => {
|
||||
// const w = childWindowRef.current;
|
||||
// if (w && !w.closed) {
|
||||
// w.document.title = title;
|
||||
// }
|
||||
// }, [title]);
|
||||
|
||||
// if (!mounted || !containerElRef.current) return null;
|
||||
|
||||
// return createPortal(children, containerElRef.current);
|
||||
// };
|
||||
export const RenderInNewWindow: React.FC<NewWindowProps> = ({
|
||||
children,
|
||||
title = "New Window",
|
||||
width = 900,
|
||||
height = 700,
|
||||
left,
|
||||
top,
|
||||
center = true,
|
||||
features,
|
||||
onClose,
|
||||
copyStyles = true,
|
||||
noopener = true,
|
||||
className,
|
||||
theme = localStorage.getItem('theme') ?? 'light',
|
||||
children,
|
||||
title = "3D Viewer",
|
||||
width = 900,
|
||||
height = 700,
|
||||
left,
|
||||
top,
|
||||
center = true,
|
||||
features,
|
||||
onClose,
|
||||
copyStyles = true,
|
||||
noopener = true,
|
||||
className,
|
||||
theme = localStorage.getItem("theme") ?? "light",
|
||||
}) => {
|
||||
const [mounted, setMounted] = useState(false);
|
||||
const childWindowRef = useRef<Window | null>(null);
|
||||
const containerElRef = useRef<HTMLDivElement | null>(null);
|
||||
const [mounted, setMounted] = useState(false);
|
||||
const childWindowRef = useRef<Window | null>(null);
|
||||
const containerElRef = useRef<HTMLDivElement | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (typeof window === "undefined") return;
|
||||
useEffect(() => {
|
||||
if (typeof window === "undefined") return;
|
||||
|
||||
const screenLeft = window.screenLeft ?? window.screenX ?? 0;
|
||||
const screenTop = window.screenTop ?? window.screenY ?? 0;
|
||||
const availWidth = window.outerWidth ?? window.innerWidth;
|
||||
const availHeight = window.outerHeight ?? window.innerHeight;
|
||||
const screenLeft = window.screenLeft ?? window.screenX ?? 0;
|
||||
const screenTop = window.screenTop ?? window.screenY ?? 0;
|
||||
const availWidth = window.outerWidth ?? window.innerWidth;
|
||||
const availHeight = window.outerHeight ?? window.innerHeight;
|
||||
|
||||
const finalLeft =
|
||||
center && availWidth
|
||||
? Math.max(0, screenLeft + (availWidth - width) / 2)
|
||||
: left ?? 100;
|
||||
const finalLeft =
|
||||
center && availWidth ? Math.max(0, screenLeft + (availWidth - width) / 2) : left ?? 100;
|
||||
|
||||
const finalTop =
|
||||
center && availHeight
|
||||
? Math.max(0, screenTop + (availHeight - height) / 2)
|
||||
: top ?? 100;
|
||||
const finalTop =
|
||||
center && availHeight
|
||||
? Math.max(0, screenTop + (availHeight - height) / 2)
|
||||
: top ?? 100;
|
||||
|
||||
const baseFeatures = [
|
||||
`width=${Math.floor(width)}`,
|
||||
`height=${Math.floor(height)}`,
|
||||
`left=${Math.floor(finalLeft)}`,
|
||||
`top=${Math.floor(finalTop)}`,
|
||||
];
|
||||
const baseFeatures = [
|
||||
`width=${Math.floor(width)}`,
|
||||
`height=${Math.floor(height)}`,
|
||||
`left=${Math.floor(finalLeft)}`,
|
||||
`top=${Math.floor(finalTop)}`,
|
||||
];
|
||||
|
||||
const featureFlags = features ?? {
|
||||
toolbar: false,
|
||||
menubar: false,
|
||||
scrollbars: true,
|
||||
resizable: true,
|
||||
location: false,
|
||||
status: false,
|
||||
};
|
||||
const featureFlags = features ?? {
|
||||
toolbar: false,
|
||||
menubar: false,
|
||||
scrollbars: true,
|
||||
resizable: true,
|
||||
location: false,
|
||||
status: false,
|
||||
};
|
||||
|
||||
Object.entries(featureFlags).forEach(([k, v]) =>
|
||||
baseFeatures.push(`${k}=${v ? "yes" : "no"}`)
|
||||
);
|
||||
Object.entries(featureFlags).forEach(([k, v]) =>
|
||||
baseFeatures.push(`${k}=${v ? "yes" : "no"}`)
|
||||
);
|
||||
|
||||
const newWin = window.open("", "_blank", baseFeatures.join(","));
|
||||
if (!newWin) {
|
||||
console.warn("Popup blocked or failed to open window.");
|
||||
onClose?.();
|
||||
return;
|
||||
}
|
||||
const newWin = window.open("", "_blank", baseFeatures.join(","));
|
||||
if (!newWin) {
|
||||
console.warn("Popup blocked or failed to open window.");
|
||||
onClose?.();
|
||||
return;
|
||||
}
|
||||
|
||||
if (noopener) {
|
||||
try {
|
||||
newWin.opener = null;
|
||||
} catch {}
|
||||
}
|
||||
if (noopener) {
|
||||
try {
|
||||
newWin.opener = null;
|
||||
} catch {}
|
||||
}
|
||||
|
||||
newWin.document.open();
|
||||
newWin.document.write(`<!doctype html>
|
||||
newWin.document.open();
|
||||
newWin.document.write(`<!doctype html>
|
||||
<html data-theme=${theme}>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>${title}</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
}
|
||||
#three-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body style="margin:0;"></body>
|
||||
<body>
|
||||
<div id="three-container"></div>
|
||||
</body>
|
||||
</html>`);
|
||||
newWin.document.close();
|
||||
newWin.document.close();
|
||||
|
||||
if (copyStyles) {
|
||||
const head = newWin.document.head;
|
||||
Array.from(document.styleSheets).forEach((styleSheet) => {
|
||||
try {
|
||||
if ((styleSheet as CSSStyleSheet).cssRules) {
|
||||
const newStyleEl = newWin.document.createElement("style");
|
||||
const rules = Array.from(
|
||||
(styleSheet as CSSStyleSheet).cssRules
|
||||
).map((r) => r.cssText);
|
||||
newStyleEl.appendChild(
|
||||
newWin.document.createTextNode(rules.join("\n"))
|
||||
);
|
||||
head.appendChild(newStyleEl);
|
||||
}
|
||||
} catch {
|
||||
const ownerNode = styleSheet.ownerNode as HTMLElement | null;
|
||||
if (ownerNode && ownerNode.tagName === "LINK") {
|
||||
const link = ownerNode as HTMLLinkElement;
|
||||
const newLink = newWin.document.createElement("link");
|
||||
newLink.rel = link.rel;
|
||||
newLink.href = link.href;
|
||||
newLink.media = link.media;
|
||||
head.appendChild(newLink);
|
||||
}
|
||||
if (copyStyles) {
|
||||
const head = newWin.document.head;
|
||||
Array.from(document.styleSheets).forEach((styleSheet) => {
|
||||
try {
|
||||
if ((styleSheet as CSSStyleSheet).cssRules) {
|
||||
const newStyleEl = newWin.document.createElement("style");
|
||||
const rules = Array.from((styleSheet as CSSStyleSheet).cssRules).map(
|
||||
(r) => r.cssText
|
||||
);
|
||||
newStyleEl.appendChild(newWin.document.createTextNode(rules.join("\n")));
|
||||
head.appendChild(newStyleEl);
|
||||
}
|
||||
} catch {
|
||||
const ownerNode = styleSheet.ownerNode as HTMLElement | null;
|
||||
if (ownerNode && ownerNode.tagName === "LINK") {
|
||||
const link = ownerNode as HTMLLinkElement;
|
||||
const newLink = newWin.document.createElement("link");
|
||||
newLink.rel = link.rel;
|
||||
newLink.href = link.href;
|
||||
newLink.media = link.media;
|
||||
head.appendChild(newLink);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const container = newWin.document.createElement("div");
|
||||
if (className) container.className = className;
|
||||
newWin.document.body.appendChild(container);
|
||||
const container = newWin.document.getElementById(
|
||||
"three-container"
|
||||
) as HTMLDivElement | null;
|
||||
if (!container) return;
|
||||
|
||||
newWin.document.title = title;
|
||||
if (className) container.className = className;
|
||||
|
||||
// Handle child window close
|
||||
const handleChildUnload = () => {
|
||||
onClose?.();
|
||||
};
|
||||
newWin.addEventListener("beforeunload", handleChildUnload);
|
||||
newWin.document.title = title;
|
||||
|
||||
// 👇 Handle parent refresh/close → auto close child
|
||||
const handleParentUnload = () => {
|
||||
try {
|
||||
newWin.close();
|
||||
} catch {}
|
||||
};
|
||||
window.addEventListener("beforeunload", handleParentUnload);
|
||||
// ✅ CRITICAL FIX: Wait for window to be fully ready then trigger resize
|
||||
const initializeWindow = () => {
|
||||
// Force maximize handling
|
||||
setTimeout(() => {
|
||||
// Get actual window dimensions (might be maximized)
|
||||
const actualWidth = newWin.innerWidth;
|
||||
const actualHeight = newWin.innerHeight;
|
||||
|
||||
childWindowRef.current = newWin;
|
||||
containerElRef.current = container;
|
||||
setMounted(true);
|
||||
console.log("Window dimensions:", actualWidth, actualHeight);
|
||||
|
||||
return () => {
|
||||
newWin.removeEventListener("beforeunload", handleChildUnload);
|
||||
window.removeEventListener("beforeunload", handleParentUnload);
|
||||
try {
|
||||
newWin.close();
|
||||
} catch {}
|
||||
childWindowRef.current = null;
|
||||
containerElRef.current = null;
|
||||
};
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
// Trigger resize event for Three.js
|
||||
newWin.dispatchEvent(new Event("resize"));
|
||||
|
||||
useEffect(() => {
|
||||
const w = childWindowRef.current;
|
||||
if (w && !w.closed) {
|
||||
w.document.title = title;
|
||||
}
|
||||
}, [title]);
|
||||
// Additional safety: trigger again after a short delay
|
||||
setTimeout(() => {
|
||||
newWin.dispatchEvent(new Event("resize"));
|
||||
}, 200);
|
||||
}, 100);
|
||||
};
|
||||
|
||||
if (!mounted || !containerElRef.current) return null;
|
||||
// Handle both load and focus events
|
||||
newWin.addEventListener("load", initializeWindow);
|
||||
newWin.addEventListener("focus", initializeWindow);
|
||||
|
||||
return createPortal(children, containerElRef.current);
|
||||
// Also initialize immediately if window is already loaded
|
||||
if (newWin.document.readyState === "complete") {
|
||||
initializeWindow();
|
||||
} else {
|
||||
newWin.addEventListener("DOMContentLoaded", initializeWindow);
|
||||
}
|
||||
|
||||
// Handle child window close
|
||||
const handleChildUnload = () => {
|
||||
onClose?.();
|
||||
};
|
||||
newWin.addEventListener("beforeunload", handleChildUnload);
|
||||
|
||||
// Handle parent refresh/close → auto close child
|
||||
const handleParentUnload = () => {
|
||||
try {
|
||||
newWin.close();
|
||||
} catch {}
|
||||
};
|
||||
window.addEventListener("beforeunload", handleParentUnload);
|
||||
|
||||
childWindowRef.current = newWin;
|
||||
containerElRef.current = container;
|
||||
setMounted(true);
|
||||
|
||||
return () => {
|
||||
newWin.removeEventListener("load", initializeWindow);
|
||||
newWin.removeEventListener("focus", initializeWindow);
|
||||
newWin.removeEventListener("DOMContentLoaded", initializeWindow);
|
||||
newWin.removeEventListener("beforeunload", handleChildUnload);
|
||||
window.removeEventListener("beforeunload", handleParentUnload);
|
||||
try {
|
||||
newWin.close();
|
||||
} catch {}
|
||||
childWindowRef.current = null;
|
||||
containerElRef.current = null;
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const w = childWindowRef.current;
|
||||
if (w && !w.closed) {
|
||||
w.document.title = title;
|
||||
}
|
||||
}, [title]);
|
||||
|
||||
if (!mounted || !containerElRef.current) return null;
|
||||
|
||||
return createPortal(children, containerElRef.current);
|
||||
};
|
||||
|
||||
@@ -9,14 +9,18 @@ interface LoadingPageProps {
|
||||
}
|
||||
|
||||
const LoadingPage: React.FC<LoadingPageProps> = ({ progress }) => {
|
||||
console.log('progress: ', progress);
|
||||
const { projectName } = useProjectName();
|
||||
const { comparisonScene } = useSimulationState();
|
||||
|
||||
const validatedProgress = Math.min(100, Math.max(0, progress));
|
||||
|
||||
console.log("comparisonScene: ", comparisonScene);
|
||||
return (
|
||||
<RenderOverlay>
|
||||
<div className={`loading-wrapper ${comparisonScene != null ? "comparisionLoading" : ""}`}>
|
||||
<div
|
||||
className={`loading-wrapper ${comparisonScene != null ? "comparisionLoading" : ""}`}
|
||||
>
|
||||
<div className="loading-container">
|
||||
<div className="project-name">{projectName}</div>
|
||||
<div className="loading-hero-container">
|
||||
|
||||
47
app/src/components/ui/compareVersion/Button.tsx
Normal file
47
app/src/components/ui/compareVersion/Button.tsx
Normal file
@@ -0,0 +1,47 @@
|
||||
import React from "react";
|
||||
import {
|
||||
useCreateNewWindow,
|
||||
useIsComparing,
|
||||
useLoadingProgress,
|
||||
} from "../../../store/builder/store";
|
||||
import { useSimulationState } from "../../../store/simulation/useSimulationStore";
|
||||
|
||||
const Button = () => {
|
||||
const { isComparing, setIsComparing } = useIsComparing();
|
||||
const { createNewWindow, setCreateNewWindow } = useCreateNewWindow();
|
||||
const { setLoadingProgress } = useLoadingProgress();
|
||||
const { clearComparisonState } = useSimulationState();
|
||||
|
||||
const handleExit = () => {
|
||||
setIsComparing(false);
|
||||
setCreateNewWindow(false);
|
||||
setLoadingProgress(0);
|
||||
clearComparisonState();
|
||||
};
|
||||
|
||||
const handleOpenInNewWindow = () => {
|
||||
// 🧹 Immediately reset any loading or scene state
|
||||
setLoadingProgress(0);
|
||||
setCreateNewWindow(true);
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
position: "absolute",
|
||||
top: "10px",
|
||||
left: "10px",
|
||||
zIndex: 1000,
|
||||
display: "flex",
|
||||
gap: "10px",
|
||||
}}
|
||||
>
|
||||
{isComparing && <button onClick={handleExit}>Exit</button>}
|
||||
{isComparing && !createNewWindow && (
|
||||
<button onClick={handleOpenInNewWindow}>Open in New Window</button>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Button;
|
||||
@@ -1,7 +1,11 @@
|
||||
import { useParams } from "react-router-dom";
|
||||
import React, { useState, useRef, useEffect, Suspense } from "react";
|
||||
import { CompareLayoutIcon, LayoutIcon, ResizerIcon } from "../../icons/SimulationIcons";
|
||||
import { useLoadingProgress, useIsComparing } from "../../../store/builder/store";
|
||||
import {
|
||||
useLoadingProgress,
|
||||
useIsComparing,
|
||||
useCreateNewWindow,
|
||||
} from "../../../store/builder/store";
|
||||
import { useSimulationState } from "../../../store/simulation/useSimulationStore";
|
||||
import { usePlayButtonStore } from "../../../store/ui/usePlayButtonStore";
|
||||
import { useSceneContext } from "../../../modules/scene/sceneContext";
|
||||
@@ -13,11 +17,18 @@ import useRestStates from "../../../hooks/useResetStates";
|
||||
|
||||
import { getVersionHistoryApi } from "../../../services/factoryBuilder/versionControl/getVersionHistoryApi";
|
||||
import { validateSimulationDataApi } from "../../../services/simulation/comparison/validateSimulationDataApi";
|
||||
import Button from "./Button";
|
||||
|
||||
const CompareLayOut = () => {
|
||||
const { clearComparisonState, comparisonScene, setComparisonState } = useSimulationState();
|
||||
const { versionStore } = useSceneContext();
|
||||
const { versionHistory, selectedVersion, setSelectedVersion, clearSelectedVersion, setVersions } = versionStore();
|
||||
const {
|
||||
versionHistory,
|
||||
selectedVersion,
|
||||
setSelectedVersion,
|
||||
clearSelectedVersion,
|
||||
setVersions,
|
||||
} = versionStore();
|
||||
const { setLoadingProgress } = useLoadingProgress();
|
||||
const [width, setWidth] = useState("50vw");
|
||||
const [isResizing, setIsResizing] = useState(false);
|
||||
@@ -30,6 +41,7 @@ const CompareLayOut = () => {
|
||||
const { setIsPlaying } = usePlayButtonStore();
|
||||
const { projectId } = useParams();
|
||||
const { resetStates } = useRestStates();
|
||||
const { createNewWindow } = useCreateNewWindow();
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
resetStates();
|
||||
@@ -162,8 +174,8 @@ const CompareLayOut = () => {
|
||||
setLoadingProgress(1);
|
||||
const singleData = {
|
||||
projectId: projectId,
|
||||
versionId: version.versionId,
|
||||
productUuid: data[0].productUuid,
|
||||
versionId: version.versionId,
|
||||
productUuid: data[0].productUuid,
|
||||
};
|
||||
|
||||
validateSimulationDataApi(singleData).then((getData) => {
|
||||
@@ -178,59 +190,77 @@ const CompareLayOut = () => {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={`compareLayOut-wrapper ${width === "0px" ? "closed" : ""}`} ref={wrapperRef} style={{ width }}>
|
||||
{loadingProgress === 0 && selectedVersion?.versionId && (
|
||||
<button title="resize-canvas" id="compare-resize-slider-btn" className="resizer" onMouseDown={handleStartResizing}>
|
||||
<ResizerIcon />
|
||||
</button>
|
||||
)}
|
||||
<div className="chooseLayout-container">
|
||||
{selectedVersion?.versionId && (
|
||||
<div className="compare-layout-canvas-container">
|
||||
<Suspense fallback={null}>
|
||||
<Scene layout="Comparison Layout" />
|
||||
</Suspense>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{width !== "0px" &&
|
||||
!selectedVersion?.versionId && ( // Show only if no layout selected
|
||||
<div className="chooseLayout-wrapper">
|
||||
<div className="icon">
|
||||
<CompareLayoutIcon />
|
||||
<>
|
||||
{!createNewWindow && (
|
||||
<div
|
||||
className={`compareLayOut-wrapper ${width === "0px" ? "closed" : ""}`}
|
||||
ref={wrapperRef}
|
||||
style={{ width }}
|
||||
>
|
||||
{loadingProgress === 0 && selectedVersion?.versionId && (
|
||||
<button
|
||||
title="resize-canvas"
|
||||
id="compare-resize-slider-btn"
|
||||
className="resizer"
|
||||
onMouseDown={handleStartResizing}
|
||||
>
|
||||
<ResizerIcon />
|
||||
</button>
|
||||
)}
|
||||
<div className="chooseLayout-container">
|
||||
{selectedVersion?.versionId && (
|
||||
<div className="compare-layout-canvas-container">
|
||||
<Suspense fallback={null}>
|
||||
<Scene layout="Comparison Layout" />
|
||||
</Suspense>
|
||||
</div>
|
||||
<div className="value">Choose Version to compare</div>
|
||||
<button className="selectLayout" onClick={() => setShowLayoutDropdown(!showLayoutDropdown)}>
|
||||
Select Version
|
||||
</button>
|
||||
)}
|
||||
|
||||
{showLayoutDropdown && (
|
||||
<div className="displayLayouts-container">
|
||||
<div className="header">Versions</div>
|
||||
<Search onChange={() => {}} />
|
||||
<div className="layouts-container">
|
||||
{versionHistory.map((version) => (
|
||||
<button
|
||||
key={version.versionId}
|
||||
className="layout-wrapper"
|
||||
onClick={() => {
|
||||
handleSelectLayout(version);
|
||||
setShowLayoutDropdown(false);
|
||||
}}
|
||||
>
|
||||
<LayoutIcon />
|
||||
<div className="layout">{version.versionName}</div>
|
||||
</button>
|
||||
))}
|
||||
{width !== "0px" &&
|
||||
!selectedVersion?.versionId && ( // Show only if no layout selected
|
||||
<div className="chooseLayout-wrapper">
|
||||
<div className="icon">
|
||||
<CompareLayoutIcon />
|
||||
</div>
|
||||
<div className="value">Choose Version to compare</div>
|
||||
<button
|
||||
className="selectLayout"
|
||||
onClick={() => setShowLayoutDropdown(!showLayoutDropdown)}
|
||||
>
|
||||
Select Version
|
||||
</button>
|
||||
|
||||
{showLayoutDropdown && (
|
||||
<div className="displayLayouts-container">
|
||||
<div className="header">Versions</div>
|
||||
<Search onChange={() => {}} />
|
||||
<div className="layouts-container">
|
||||
{versionHistory.map((version) => (
|
||||
<button
|
||||
key={version.versionId}
|
||||
className="layout-wrapper"
|
||||
onClick={() => {
|
||||
handleSelectLayout(version);
|
||||
setShowLayoutDropdown(false);
|
||||
}}
|
||||
>
|
||||
<LayoutIcon />
|
||||
<div className="layout">
|
||||
{version.versionName}
|
||||
</div>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Always show after layout is selected */}
|
||||
</div>
|
||||
</div>
|
||||
{/* Always show after layout is selected */}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
44
app/src/components/ui/compareVersion/NewWindowScene.tsx
Normal file
44
app/src/components/ui/compareVersion/NewWindowScene.tsx
Normal file
@@ -0,0 +1,44 @@
|
||||
import React, { Suspense, useEffect, useState } from "react";
|
||||
import { RenderInNewWindow } from "../../templates/CreateNewWindow";
|
||||
import { useSceneContext } from "../../../modules/scene/sceneContext";
|
||||
import { useCreateNewWindow, useLoadingProgress } from "../../../store/builder/store";
|
||||
import Scene from "../../../modules/scene/scene";
|
||||
import ComparisonResult from "./ComparisonResult";
|
||||
import Button from "./Button";
|
||||
|
||||
const NewWindowScene = () => {
|
||||
const { versionStore } = useSceneContext();
|
||||
const { selectedVersion } = versionStore();
|
||||
const { setCreateNewWindow } = useCreateNewWindow();
|
||||
const { loadingProgress } = useLoadingProgress();
|
||||
|
||||
return (
|
||||
<>
|
||||
{selectedVersion?.versionId && (
|
||||
<div style={{ width: "100%", height: "100%" }}>
|
||||
<Suspense fallback={null}>
|
||||
<RenderInNewWindow
|
||||
title="3D Viewer"
|
||||
onClose={() => setCreateNewWindow(false)}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
position: "absolute",
|
||||
top: "10px",
|
||||
right: "900px",
|
||||
zIndex: 1000,
|
||||
}}
|
||||
>
|
||||
<Button />
|
||||
</div>
|
||||
<Scene layout="Comparison Layout" />
|
||||
{!loadingProgress && <ComparisonResult />}
|
||||
</RenderInNewWindow>
|
||||
</Suspense>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default NewWindowScene;
|
||||
@@ -1,185 +1,164 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import {
|
||||
LogListIcon,
|
||||
CloseIcon,
|
||||
ExpandIcon2,
|
||||
} from "../../icons/ExportCommonIcons"; // Adjust path as needed
|
||||
import { LogListIcon, CloseIcon, ExpandIcon2 } from "../../icons/ExportCommonIcons"; // Adjust path as needed
|
||||
import { LogEntry, useLogger } from "./LoggerContext";
|
||||
import { GetLogIcon } from "../../footer/getLogIcons";
|
||||
import { RenderInNewWindow } from "../../templates/CreateNewWindow";
|
||||
|
||||
// --- Logs Component ---
|
||||
type LogsProps = {
|
||||
selectedTab: "all" | "info" | "warning" | "error";
|
||||
setSelectedTab: (tab: "all" | "info" | "warning" | "error") => void;
|
||||
clear: () => void;
|
||||
filteredLogs: LogEntry[];
|
||||
formatTimestamp: (date: Date) => string;
|
||||
selectedTab: "all" | "info" | "warning" | "error";
|
||||
setSelectedTab: (tab: "all" | "info" | "warning" | "error") => void;
|
||||
clear: () => void;
|
||||
filteredLogs: LogEntry[];
|
||||
formatTimestamp: (date: Date) => string;
|
||||
};
|
||||
|
||||
const Logs: React.FC<LogsProps> = ({
|
||||
selectedTab,
|
||||
setSelectedTab,
|
||||
clear,
|
||||
filteredLogs,
|
||||
formatTimestamp,
|
||||
selectedTab,
|
||||
setSelectedTab,
|
||||
clear,
|
||||
filteredLogs,
|
||||
formatTimestamp,
|
||||
}) => {
|
||||
return (
|
||||
<>
|
||||
<div className="log-nav-container">
|
||||
<div className="log-nav-wrapper">
|
||||
{["all", "info", "warning", "error"].map((type) => (
|
||||
<button
|
||||
id="log-type"
|
||||
title="log-type"
|
||||
key={type}
|
||||
className={`log-nav ${selectedTab === type ? "active" : ""}`}
|
||||
onClick={() => setSelectedTab(type as any)}
|
||||
>
|
||||
{`${type.charAt(0).toUpperCase() + type.slice(1)} Logs`}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
<button
|
||||
id="clean-btn"
|
||||
title="clear-btn"
|
||||
className="clear-button"
|
||||
onClick={clear}
|
||||
>
|
||||
clear
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Log Entries */}
|
||||
<div className="log-entry-wrapper">
|
||||
{filteredLogs.length > 0 ? (
|
||||
filteredLogs.map((log) => (
|
||||
<div key={log.id} className={`log-entry ${log.type}`}>
|
||||
<div className="log-icon">{GetLogIcon(log.type)}</div>
|
||||
<div className="log-entry-message-container">
|
||||
<div className="log-entry-message">{log.message}</div>
|
||||
<div className="message-time">
|
||||
{formatTimestamp(log.timestamp)}
|
||||
return (
|
||||
<>
|
||||
<div className="log-nav-container">
|
||||
<div className="log-nav-wrapper">
|
||||
{["all", "info", "warning", "error"].map((type) => (
|
||||
<button
|
||||
id="log-type"
|
||||
title="log-type"
|
||||
key={type}
|
||||
className={`log-nav ${selectedTab === type ? "active" : ""}`}
|
||||
onClick={() => setSelectedTab(type as any)}
|
||||
>
|
||||
{`${type.charAt(0).toUpperCase() + type.slice(1)} Logs`}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<button id="clean-btn" title="clear-btn" className="clear-button" onClick={clear}>
|
||||
clear
|
||||
</button>
|
||||
</div>
|
||||
))
|
||||
) : (
|
||||
<div className="no-log">
|
||||
There are no logs to display at the moment.
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
||||
{/* Log Entries */}
|
||||
<div className="log-entry-wrapper">
|
||||
{filteredLogs.length > 0 ? (
|
||||
filteredLogs.map((log) => (
|
||||
<div key={log.id} className={`log-entry ${log.type}`}>
|
||||
<div className="log-icon">{GetLogIcon(log.type)}</div>
|
||||
<div className="log-entry-message-container">
|
||||
<div className="log-entry-message">{log.message}</div>
|
||||
<div className="message-time">{formatTimestamp(log.timestamp)}</div>
|
||||
</div>
|
||||
</div>
|
||||
))
|
||||
) : (
|
||||
<div className="no-log">There are no logs to display at the moment.</div>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
// --- LogList Component ---
|
||||
const LogList: React.FC = () => {
|
||||
const {
|
||||
logs,
|
||||
clear,
|
||||
setIsLogListVisible,
|
||||
isLogListVisible,
|
||||
selectedTab,
|
||||
setSelectedTab,
|
||||
} = useLogger();
|
||||
const { logs, clear, setIsLogListVisible, isLogListVisible, selectedTab, setSelectedTab } =
|
||||
useLogger();
|
||||
|
||||
const formatTimestamp = (date: Date) => new Date(date).toLocaleTimeString();
|
||||
const [open, setOpen] = useState(false);
|
||||
const formatTimestamp = (date: Date) => new Date(date).toLocaleTimeString();
|
||||
const [open, setOpen] = useState(false);
|
||||
console.log('open: ', open);
|
||||
const filteredLogs =
|
||||
selectedTab === "all"
|
||||
? [...logs].reverse()
|
||||
: [...logs].filter((log) => log.type === selectedTab).reverse();
|
||||
|
||||
const filteredLogs =
|
||||
selectedTab === "all"
|
||||
? [...logs].reverse()
|
||||
: [...logs].filter((log) => log.type === selectedTab).reverse();
|
||||
useEffect(() => {
|
||||
if (isLogListVisible && logs.length > 0) {
|
||||
const lastLog = logs[logs.length - 1];
|
||||
const validTypes = ["all", "info", "warning", "error"];
|
||||
if (validTypes.includes(lastLog.type)) {
|
||||
setSelectedTab(lastLog.type as any);
|
||||
} else {
|
||||
setSelectedTab("all");
|
||||
}
|
||||
}
|
||||
// eslint-disable-next-line
|
||||
}, [isLogListVisible]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isLogListVisible && logs.length > 0) {
|
||||
const lastLog = logs[logs.length - 1];
|
||||
const validTypes = ["all", "info", "warning", "error"];
|
||||
if (validTypes.includes(lastLog.type)) {
|
||||
setSelectedTab(lastLog.type as any);
|
||||
} else {
|
||||
setSelectedTab("all");
|
||||
}
|
||||
}
|
||||
// eslint-disable-next-line
|
||||
}, [isLogListVisible]);
|
||||
return (
|
||||
<>
|
||||
{!open ? (
|
||||
<div className="log-list-container" onClick={() => setIsLogListVisible(false)}>
|
||||
{/* eslint-disable-next-line */}
|
||||
<div
|
||||
className="log-list-wrapper"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
}}
|
||||
>
|
||||
<div className="log-header">
|
||||
<div className="log-header-wrapper">
|
||||
<div className="icon">
|
||||
<LogListIcon />
|
||||
</div>
|
||||
<div className="head">Log List</div>
|
||||
</div>
|
||||
<div className="action-buttons-container">
|
||||
<button
|
||||
id="expand-log-btn"
|
||||
title="open in new tab"
|
||||
className="expand-btn"
|
||||
onClick={() => {
|
||||
setOpen(true);
|
||||
}}
|
||||
>
|
||||
<ExpandIcon2 />
|
||||
</button>
|
||||
<button
|
||||
id="close-btn"
|
||||
title="close"
|
||||
className="close"
|
||||
onClick={() => setIsLogListVisible(false)}
|
||||
>
|
||||
<CloseIcon />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
return (
|
||||
<>
|
||||
{!open ? (
|
||||
<div
|
||||
className="log-list-container"
|
||||
onClick={() => setIsLogListVisible(false)}
|
||||
>
|
||||
{/* eslint-disable-next-line */}
|
||||
<div
|
||||
className="log-list-wrapper"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
}}
|
||||
>
|
||||
<div className="log-header">
|
||||
<div className="log-header-wrapper">
|
||||
<div className="icon">
|
||||
<LogListIcon />
|
||||
{/* Logs Section */}
|
||||
<Logs
|
||||
selectedTab={selectedTab as any}
|
||||
setSelectedTab={setSelectedTab as any}
|
||||
clear={clear}
|
||||
filteredLogs={filteredLogs}
|
||||
formatTimestamp={formatTimestamp}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="head">Log List</div>
|
||||
</div>
|
||||
<div className="action-buttons-container">
|
||||
<button
|
||||
id="expand-log-btn"
|
||||
title="open in new tab"
|
||||
className="expand-btn"
|
||||
onClick={() => {
|
||||
setOpen(true);
|
||||
}}
|
||||
) : (
|
||||
<RenderInNewWindow
|
||||
title="Log list"
|
||||
onClose={() => {
|
||||
setOpen(false);
|
||||
setIsLogListVisible(false);
|
||||
}}
|
||||
>
|
||||
<ExpandIcon2 />
|
||||
</button>
|
||||
<button
|
||||
id="close-btn"
|
||||
title="close"
|
||||
className="close"
|
||||
onClick={() => setIsLogListVisible(false)}
|
||||
>
|
||||
<CloseIcon />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Logs Section */}
|
||||
<Logs
|
||||
selectedTab={selectedTab as any}
|
||||
setSelectedTab={setSelectedTab as any}
|
||||
clear={clear}
|
||||
filteredLogs={filteredLogs}
|
||||
formatTimestamp={formatTimestamp}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<RenderInNewWindow
|
||||
title="Log list"
|
||||
onClose={() => {
|
||||
setOpen(false);
|
||||
setIsLogListVisible(false);
|
||||
}}
|
||||
>
|
||||
<div className="log-list-new-window-wrapper">
|
||||
<Logs
|
||||
selectedTab={selectedTab as any}
|
||||
setSelectedTab={setSelectedTab as any}
|
||||
clear={clear}
|
||||
filteredLogs={filteredLogs}
|
||||
formatTimestamp={formatTimestamp}
|
||||
/>
|
||||
</div>
|
||||
</RenderInNewWindow>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
<div className="log-list-new-window-wrapper">
|
||||
<Logs
|
||||
selectedTab={selectedTab as any}
|
||||
setSelectedTab={setSelectedTab as any}
|
||||
clear={clear}
|
||||
filteredLogs={filteredLogs}
|
||||
formatTimestamp={formatTimestamp}
|
||||
/>
|
||||
</div>
|
||||
</RenderInNewWindow>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default LogList;
|
||||
|
||||
@@ -13,13 +13,17 @@ import { getAssetFieldApi } from "../../../../../services/factoryBuilder/asset/f
|
||||
import { ModelAnimator } from "./animator/modelAnimator";
|
||||
import { useModelEventHandlers } from "./eventHandlers/useModelEventHandlers";
|
||||
|
||||
function Model({ asset, isRendered, loader }: Readonly<{ asset: Asset; isRendered: boolean; loader: GLTFLoader }>) {
|
||||
function Model({
|
||||
asset,
|
||||
isRendered,
|
||||
loader,
|
||||
}: Readonly<{ asset: Asset; isRendered: boolean; loader: GLTFLoader }>) {
|
||||
const url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`;
|
||||
const savedTheme: string = localStorage.getItem("theme") || "light";
|
||||
const { toolMode } = useToolMode();
|
||||
const { toggleView } = useToggleView();
|
||||
const { activeModule } = useModuleStore();
|
||||
const { assetStore } = useSceneContext();
|
||||
const { assetStore, layout } = useSceneContext();
|
||||
const { resetAnimation, hasSelectedAsset, updateSelectedAsset, selectedAssets } = assetStore();
|
||||
const { setDeletableFloorAsset } = useBuilderStore();
|
||||
const [gltfScene, setGltfScene] = useState<GLTF["scene"] | null>(null);
|
||||
@@ -52,7 +56,12 @@ function Model({ asset, isRendered, loader }: Readonly<{ asset: Asset; isRendere
|
||||
}, [activeModule, toolMode, selectedAssets]);
|
||||
|
||||
useEffect(() => {
|
||||
if (groupRef.current && selectedAssets.length === 1 && selectedAssets[0].userData.modelUuid === asset.modelUuid && hasSelectedAsset(asset.modelUuid)) {
|
||||
if (
|
||||
groupRef.current &&
|
||||
selectedAssets.length === 1 &&
|
||||
selectedAssets[0].userData.modelUuid === asset.modelUuid &&
|
||||
hasSelectedAsset(asset.modelUuid)
|
||||
) {
|
||||
updateSelectedAsset(groupRef.current);
|
||||
}
|
||||
}, [isRendered, selectedAssets, asset]);
|
||||
@@ -130,7 +139,10 @@ function Model({ asset, isRendered, loader }: Readonly<{ asset: Asset; isRendere
|
||||
logModelStatus(assetId, "backend-loaded");
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(`[Backend] Error storing/loading ${asset.modelName}:`, error);
|
||||
console.error(
|
||||
`[Backend] Error storing/loading ${asset.modelName}:`,
|
||||
error
|
||||
);
|
||||
});
|
||||
},
|
||||
undefined,
|
||||
@@ -144,7 +156,8 @@ function Model({ asset, isRendered, loader }: Readonly<{ asset: Asset; isRendere
|
||||
});
|
||||
}, []);
|
||||
|
||||
const { handleDblClick, handleClick, handlePointerOver, handlePointerOut, handleContextMenu } = useModelEventHandlers({ boundingBox, groupRef, asset });
|
||||
const { handleDblClick, handleClick, handlePointerOver, handlePointerOut, handleContextMenu } =
|
||||
useModelEventHandlers({ boundingBox, groupRef, asset });
|
||||
|
||||
return (
|
||||
<group
|
||||
@@ -197,9 +210,25 @@ function Model({ asset, isRendered, loader }: Readonly<{ asset: Asset; isRendere
|
||||
<ModelAnimator asset={asset} gltfScene={gltfScene} />
|
||||
</>
|
||||
) : (
|
||||
<>{!hasSelectedAsset(asset.modelUuid) && <AssetBoundingBox name="Asset Fallback" boundingBox={boundingBox} color="gray" lineWidth={2.5} />}</>
|
||||
<>
|
||||
{!hasSelectedAsset(asset.modelUuid) && (
|
||||
<AssetBoundingBox
|
||||
name="Asset Fallback"
|
||||
boundingBox={boundingBox}
|
||||
color="gray"
|
||||
lineWidth={2.5}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
{hasSelectedAsset(asset.modelUuid) && (
|
||||
<AssetBoundingBox
|
||||
name="Asset BBox"
|
||||
boundingBox={boundingBox}
|
||||
color={savedTheme === "dark" ? "#c4abf1" : "#6f42c1"}
|
||||
lineWidth={2.7}
|
||||
/>
|
||||
)}
|
||||
{hasSelectedAsset(asset.modelUuid) && <AssetBoundingBox name="Asset BBox" boundingBox={boundingBox} color={savedTheme === "dark" ? "#c4abf1" : "#6f42c1"} lineWidth={2.7} />}
|
||||
</>
|
||||
)}
|
||||
</group>
|
||||
|
||||
@@ -3,7 +3,11 @@ import { Group, Vector3 } from "three";
|
||||
import { CameraControls } from "@react-three/drei";
|
||||
import { GLTFLoader } from "three/examples/jsm/Addons";
|
||||
import { useThree, useFrame } from "@react-three/fiber";
|
||||
import { useContextActionStore, useLimitDistance, useRenderDistance } from "../../../../store/builder/store";
|
||||
import {
|
||||
useContextActionStore,
|
||||
useLimitDistance,
|
||||
useRenderDistance,
|
||||
} from "../../../../store/builder/store";
|
||||
import { useSelectedAsset } from "../../../../store/simulation/useSimulationStore";
|
||||
import { useSceneContext } from "../../../scene/sceneContext";
|
||||
import useZoomMesh from "../../hooks/useZoomMesh";
|
||||
@@ -11,12 +15,14 @@ import useCallBackOnKey from "../../../../utils/hooks/useCallBackOnKey";
|
||||
|
||||
import Model from "./model/model";
|
||||
|
||||
const distanceWorker = new Worker(new URL("../../../../services/factoryBuilder/webWorkers/distanceWorker.js", import.meta.url));
|
||||
const distanceWorker = new Worker(
|
||||
new URL("../../../../services/factoryBuilder/webWorkers/distanceWorker.js", import.meta.url)
|
||||
);
|
||||
|
||||
function Models({ loader }: { readonly loader: GLTFLoader }) {
|
||||
const { controls, camera } = useThree();
|
||||
const assetGroupRef = useRef<Group>(null);
|
||||
const { assetStore } = useSceneContext();
|
||||
const { assetStore, layout } = useSceneContext();
|
||||
const { assets, selectedAssets, getSelectedAssetUuids } = assetStore();
|
||||
const { selectedAsset, clearSelectedAsset } = useSelectedAsset();
|
||||
const { contextAction, setContextAction } = useContextActionStore();
|
||||
@@ -30,6 +36,14 @@ function Models({ loader }: { readonly loader: GLTFLoader }) {
|
||||
// console.log(assets);
|
||||
}, [assets]);
|
||||
|
||||
useEffect(() => {
|
||||
const initialRenderMap: Record<string, boolean> = {};
|
||||
assets.forEach((asset) => {
|
||||
initialRenderMap[asset.modelUuid] = true;
|
||||
});
|
||||
setRenderMap(initialRenderMap);
|
||||
}, [assets.length]);
|
||||
|
||||
useEffect(() => {
|
||||
if (contextAction === "focusAsset") {
|
||||
zoomMeshes(getSelectedAssetUuids());
|
||||
@@ -55,20 +69,51 @@ function Models({ loader }: { readonly loader: GLTFLoader }) {
|
||||
return { ...prev, [modelUuid]: shouldRender };
|
||||
});
|
||||
};
|
||||
}, []);
|
||||
|
||||
return () => {
|
||||
distanceWorker.terminate();
|
||||
};
|
||||
}, [distanceWorker, layout]);
|
||||
|
||||
useFrame(() => {
|
||||
camera.getWorldPosition(cameraPos.current);
|
||||
for (const asset of assets) {
|
||||
const isRendered = renderMap[asset.modelUuid] ?? false;
|
||||
distanceWorker.postMessage({
|
||||
modelUuid: asset.modelUuid,
|
||||
assetPosition: { x: asset.position[0], y: asset.position[1], z: asset.position[2] },
|
||||
cameraPosition: cameraPos.current,
|
||||
limitDistance,
|
||||
renderDistance,
|
||||
isRendered,
|
||||
});
|
||||
// distanceWorker.postMessage({
|
||||
// modelUuid: asset.modelUuid,
|
||||
// assetPosition: { x: asset.position[0], y: asset.position[1], z: asset.position[2] },
|
||||
// cameraPosition: cameraPos.current,
|
||||
// limitDistance,
|
||||
// renderDistance,
|
||||
// isRendered,
|
||||
// });
|
||||
|
||||
const assetVec = new Vector3(...asset.position);
|
||||
const cameraVec = new Vector3(
|
||||
cameraPos.current.x,
|
||||
cameraPos.current.y,
|
||||
cameraPos.current.z
|
||||
);
|
||||
const distance = assetVec.distanceTo(cameraVec);
|
||||
|
||||
if (limitDistance) {
|
||||
if (!isRendered && distance <= renderDistance) {
|
||||
setRenderMap((prev) => {
|
||||
if (prev[asset.modelUuid] === true) return prev;
|
||||
return { ...prev, [asset.modelUuid]: true };
|
||||
});
|
||||
} else if (isRendered && distance > renderDistance) {
|
||||
setRenderMap((prev) => {
|
||||
if (prev[asset.modelUuid] === false) return prev;
|
||||
return { ...prev, [asset.modelUuid]: false };
|
||||
});
|
||||
}
|
||||
} else if (!isRendered) {
|
||||
setRenderMap((prev) => {
|
||||
if (prev[asset.modelUuid] === true) return prev;
|
||||
return { ...prev, [asset.modelUuid]: true };
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -88,7 +133,12 @@ function Models({ loader }: { readonly loader: GLTFLoader }) {
|
||||
}}
|
||||
>
|
||||
{assets.map((asset) => (
|
||||
<Model key={asset.modelUuid} asset={asset} isRendered={renderMap[asset.modelUuid] ?? false} loader={loader} />
|
||||
<Model
|
||||
key={asset.modelUuid}
|
||||
asset={asset}
|
||||
isRendered={renderMap[asset.modelUuid] ?? false}
|
||||
loader={loader}
|
||||
/>
|
||||
))}
|
||||
</group>
|
||||
);
|
||||
|
||||
@@ -2,13 +2,11 @@ import { Vector3 } from "three";
|
||||
import { useFrame, useThree } from "@react-three/fiber";
|
||||
import { CameraControls } from "@react-three/drei";
|
||||
import { useSceneContext } from "../sceneContext";
|
||||
import { useIsComparing } from "../../../store/builder/store";
|
||||
import { useCreateNewWindow, useIsComparing } from "../../../store/builder/store";
|
||||
import { useSceneStore } from "../../../store/scene/useSceneStore";
|
||||
import { useSimulationState } from "../../../store/simulation/useSimulationStore";
|
||||
import useModuleStore from "../../../store/ui/useModuleStore";
|
||||
|
||||
import * as CONSTANTS from "../../../types/world/worldConstants";
|
||||
|
||||
function SyncCam() {
|
||||
const { layout } = useSceneContext();
|
||||
const { controls } = useThree();
|
||||
@@ -16,19 +14,39 @@ function SyncCam() {
|
||||
const { activeModule } = useModuleStore();
|
||||
const { comparisonScene } = useSimulationState();
|
||||
const { setCamera, camState } = useSceneStore();
|
||||
const { windowRendered } = useCreateNewWindow();
|
||||
|
||||
function getControls() {
|
||||
const position = (controls as CameraControls).getPosition(new Vector3());
|
||||
const target = (controls as CameraControls).getTarget(new Vector3());
|
||||
setCamera(position, target);
|
||||
}
|
||||
|
||||
function setControls() {
|
||||
(controls as CameraControls).setLookAt(
|
||||
camState.position.x,
|
||||
camState.position.y,
|
||||
camState.position.z,
|
||||
camState.target.x,
|
||||
camState.target.y,
|
||||
camState.target.z,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
useFrame(() => {
|
||||
if (layout === "Comparison Layout" && controls && camState) {
|
||||
(controls as any).mouseButtons.left = CONSTANTS.controlsTransition.leftMouse;
|
||||
(controls as any).mouseButtons.right = CONSTANTS.controlsTransition.rightMouse;
|
||||
(controls as any).mouseButtons.wheel = CONSTANTS.controlsTransition.wheelMouse;
|
||||
(controls as any).mouseButtons.middle = CONSTANTS.controlsTransition.middleMouse;
|
||||
(controls as CameraControls).setLookAt(camState.position.x, camState.position.y, camState.position.z, camState.target.x, camState.target.y, camState.target.z, true);
|
||||
}
|
||||
if (layout === "Main Layout" && controls && isComparing && activeModule === "simulation" && comparisonScene) {
|
||||
const position = (controls as CameraControls).getPosition(new Vector3());
|
||||
const target = (controls as CameraControls).getTarget(new Vector3());
|
||||
setCamera(position, target);
|
||||
if (
|
||||
controls &&
|
||||
isComparing &&
|
||||
activeModule === "simulation" &&
|
||||
comparisonScene &&
|
||||
camState
|
||||
) {
|
||||
if (layout === windowRendered) {
|
||||
getControls();
|
||||
} else {
|
||||
setControls();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useEffect, useMemo } from "react";
|
||||
import { Canvas } from "@react-three/fiber";
|
||||
import { Canvas, useThree } from "@react-three/fiber";
|
||||
import { KeyboardControls } from "@react-three/drei";
|
||||
import { useSceneContext } from "./sceneContext";
|
||||
|
||||
@@ -11,13 +11,17 @@ import Collaboration from "../collaboration/collaboration";
|
||||
import useModuleStore from "../../store/ui/useModuleStore";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { getUserData } from "../../functions/getUserData";
|
||||
import { useLoadingProgress } from "../../store/builder/store";
|
||||
import { useCreateNewWindow, useLoadingProgress } from "../../store/builder/store";
|
||||
import { useSocketStore } from "../../store/socket/useSocketStore";
|
||||
import { Color, SRGBColorSpace } from "three";
|
||||
import { compressImage } from "../../utils/compressImage";
|
||||
import { ALPHA_ORG } from "../../pages/Dashboard";
|
||||
|
||||
export default function Scene({ layout }: { readonly layout: "Main Layout" | "Comparison Layout" }) {
|
||||
export default function Scene({
|
||||
layout,
|
||||
}: {
|
||||
readonly layout: "Main Layout" | "Comparison Layout";
|
||||
}) {
|
||||
const map = useMemo(
|
||||
() => [
|
||||
{ name: "forward", keys: ["ArrowUp", "w", "W"] },
|
||||
@@ -35,11 +39,16 @@ export default function Scene({ layout }: { readonly layout: "Main Layout" | "Co
|
||||
const { projectSocket } = useSocketStore();
|
||||
const { activeModule } = useModuleStore();
|
||||
const { loadingProgress } = useLoadingProgress();
|
||||
const { setWindowRendered } = useCreateNewWindow();
|
||||
|
||||
useEffect(() => {
|
||||
if (!projectId || loadingProgress !== 0) return;
|
||||
const canvas = document.getElementById("sceneCanvas")?.getElementsByTagName("canvas")[0];
|
||||
if (!canvas || !(layoutType === "default" || (layoutType === "useCase" && organization === ALPHA_ORG))) return;
|
||||
if (
|
||||
!canvas ||
|
||||
!(layoutType === "default" || (layoutType === "useCase" && organization === ALPHA_ORG))
|
||||
)
|
||||
return;
|
||||
compressImage(canvas.toDataURL("image/png")).then((screenshotDataUrl) => {
|
||||
const updateProjects = {
|
||||
projectId,
|
||||
@@ -64,11 +73,21 @@ export default function Scene({ layout }: { readonly layout: "Main Layout" | "Co
|
||||
onContextMenu={(e) => {
|
||||
e.preventDefault();
|
||||
}}
|
||||
resize={{ polyfill: ResizeObserver }}
|
||||
style={{ width: "100vw", height: "100vh", background: "#202020" }}
|
||||
performance={{ min: 0.9, max: 1.0 }}
|
||||
onCreated={(e) => {
|
||||
e.scene.background = layout === "Main Layout" ? null : new Color(0x19191d);
|
||||
}}
|
||||
gl={{ outputColorSpace: SRGBColorSpace, powerPreference: "high-performance", antialias: true, preserveDrawingBuffer: true }}
|
||||
gl={{
|
||||
outputColorSpace: SRGBColorSpace,
|
||||
powerPreference: "high-performance",
|
||||
antialias: true,
|
||||
preserveDrawingBuffer: true,
|
||||
}}
|
||||
onPointerEnter={() => {
|
||||
setWindowRendered(layout);
|
||||
}}
|
||||
>
|
||||
<Setup />
|
||||
<Collaboration />
|
||||
|
||||
@@ -171,6 +171,7 @@ const Project: React.FC = () => {
|
||||
<MainScene />
|
||||
</SceneProvider>
|
||||
<SceneProvider layout="Comparison Layout" layoutType={layoutType}>
|
||||
|
||||
<ComparisonScene />
|
||||
</SceneProvider>
|
||||
{selectedUser && <FollowPerson />}
|
||||
|
||||
@@ -138,7 +138,8 @@ export const useDrieTemp = create<any>((set: any) => ({
|
||||
export const useDrieUIValue = create<any>((set: any) => ({
|
||||
drieUIValue: { touch: null, temperature: null, humidity: null },
|
||||
|
||||
setDrieUIValue: (x: any) => set((state: any) => ({ drieUIValue: { ...state.drieUIValue, ...x } })),
|
||||
setDrieUIValue: (x: any) =>
|
||||
set((state: any) => ({ drieUIValue: { ...state.drieUIValue, ...x } })),
|
||||
|
||||
setTouch: (value: any) =>
|
||||
set((state: any) => ({
|
||||
@@ -462,9 +463,18 @@ interface DecalStore {
|
||||
// Create the Zustand store with types
|
||||
export const useDecalStore = create<DecalStore>((set) => ({
|
||||
selectedSubCategory: "Safety",
|
||||
setSelectedSubCategory: (subCategory: string | null) => set({ selectedSubCategory: subCategory }),
|
||||
setSelectedSubCategory: (subCategory: string | null) =>
|
||||
set({ selectedSubCategory: subCategory }),
|
||||
}));
|
||||
|
||||
export const comparsionMaterialData = create<any>((set: any) => ({
|
||||
materialData: [],
|
||||
setMaterialData: (x: any) => set({ materialData: x }),
|
||||
}));
|
||||
|
||||
export const useCreateNewWindow = create<any>((set: any) => ({
|
||||
createNewWindow: false,
|
||||
setCreateNewWindow: (x: any) => set({ createNewWindow: x }),
|
||||
windowRendered: "",
|
||||
setWindowRendered: (x: any) => set({ windowRendered: x }),
|
||||
}));
|
||||
|
||||
Reference in New Issue
Block a user