From b03e7be1c7738a7ca207dae53cf3d13c4492cf64 Mon Sep 17 00:00:00 2001 From: Nalvazhuthi Date: Fri, 17 Oct 2025 15:31:46 +0530 Subject: [PATCH] Updated --- app/package-lock.json | 48 +- .../SimulationDashboard/DashboardEditor.tsx | 357 +++---- .../components/block/BlockComponent.tsx | 47 +- .../components/block/BlockEditor.tsx | 71 +- .../components/element/ElementComponent.tsx | 6 +- .../components/element/ElementContent.tsx | 43 +- .../components/element/ElementDropdown.tsx | 14 +- .../components/element/ElementEditor.tsx | 10 +- .../components/models/DataModelPanel.tsx | 4 +- app/src/components/icons/ExportToolsIcons.tsx | 12 + .../simulation/useSimulationDashBoardStore.ts | 929 ++++++++++-------- .../_simulationDashBoard.scss | 170 +++- app/src/styles/layout/_sidebar.scss | 2 + 13 files changed, 997 insertions(+), 716 deletions(-) diff --git a/app/package-lock.json b/app/package-lock.json index 91c994b..d9ce5b7 100644 --- a/app/package-lock.json +++ b/app/package-lock.json @@ -2031,7 +2031,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==", - "devOptional": true, + "dev": true, "dependencies": { "@jridgewell/trace-mapping": "0.3.9" }, @@ -2043,7 +2043,7 @@ "version": "0.3.9", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", - "devOptional": true, + "dev": true, "dependencies": { "@jridgewell/resolve-uri": "^3.0.3", "@jridgewell/sourcemap-codec": "^1.4.10" @@ -4242,26 +4242,6 @@ "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", @@ -4373,25 +4353,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==", - "devOptional": true + "dev": 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==", - "devOptional": true + "dev": 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==", - "devOptional": true + "dev": 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==", - "devOptional": true + "dev": true }, "node_modules/@turf/along": { "version": "7.2.0", @@ -9233,7 +9213,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==", - "devOptional": true + "dev": true }, "node_modules/cross-env": { "version": "7.0.3", @@ -10249,7 +10229,7 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "devOptional": true, + "dev": true, "engines": { "node": ">=0.3.1" } @@ -15652,7 +15632,7 @@ "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "devOptional": true + "dev": true }, "node_modules/makeerror": { "version": "1.0.12", @@ -21308,7 +21288,7 @@ "version": "10.9.2", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", - "devOptional": true, + "dev": true, "dependencies": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", @@ -21351,7 +21331,7 @@ "version": "8.3.4", "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", - "devOptional": true, + "dev": true, "dependencies": { "acorn": "^8.11.0" }, @@ -21363,7 +21343,7 @@ "version": "4.1.3", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "devOptional": true + "dev": true }, "node_modules/tsconfig-paths": { "version": "3.15.0", @@ -21859,7 +21839,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==", - "devOptional": true + "dev": true }, "node_modules/v8-to-istanbul": { "version": "8.1.1", @@ -22952,7 +22932,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "devOptional": true, + "dev": true, "engines": { "node": ">=6" } diff --git a/app/src/components/SimulationDashboard/DashboardEditor.tsx b/app/src/components/SimulationDashboard/DashboardEditor.tsx index f7d780c..faaf2ff 100644 --- a/app/src/components/SimulationDashboard/DashboardEditor.tsx +++ b/app/src/components/SimulationDashboard/DashboardEditor.tsx @@ -32,7 +32,6 @@ const DashboardEditor: React.FC = () => { updateBlockSize, updateElementSize, addBlock, - addElement, updateBlockStyle, updateBlockPositionType, updateBlockZIndex, @@ -46,6 +45,7 @@ const DashboardEditor: React.FC = () => { swapElements, subscribe, } = simulationDashBoardStore(); + const [selectedBlock, setSelectedBlock] = useState(null); const [selectedElement, setSelectedElement] = useState(null); const [editMode, setEditMode] = useState(false); @@ -183,144 +183,144 @@ const DashboardEditor: React.FC = () => { }, [selectedBlock, selectedElement]); // Drag and drop handler -useEffect(() => { - const handleMouseMove = (e: MouseEvent): void => { - // Element dragging - direct DOM manipulation - if (draggingElement && selectedBlock && currentElement?.positionType === "absolute") { - const blockElement = document.querySelector(`[data-block-id="${selectedBlock}"]`) as HTMLElement; - const elementToDrag = document.querySelector(`[data-element-id="${draggingElement}"]`) as HTMLElement; - - if (blockElement && elementToDrag) { - const blockRect = blockElement.getBoundingClientRect(); - const newX = e.clientX - blockRect.left - elementDragOffset.x; - const newY = e.clientY - blockRect.top - elementDragOffset.y; + useEffect(() => { + const handleMouseMove = (e: MouseEvent): void => { + // Element dragging - direct DOM manipulation + if (draggingElement && selectedBlock && currentElement?.positionType === "absolute") { + const blockElement = document.querySelector(`[data-block-id="${selectedBlock}"]`) as HTMLElement; + const elementToDrag = document.querySelector(`[data-element-id="${draggingElement}"]`) as HTMLElement; - // Direct DOM manipulation - elementToDrag.style.left = `${Math.max(0, Math.min(blockRect.width - 50, newX))}px`; - elementToDrag.style.top = `${Math.max(0, Math.min(blockRect.height - 30, newY))}px`; + if (blockElement && elementToDrag) { + const blockRect = blockElement.getBoundingClientRect(); + const newX = e.clientX - blockRect.left - elementDragOffset.x; + const newY = e.clientY - blockRect.top - elementDragOffset.y; + + // Direct DOM manipulation + elementToDrag.style.left = `${Math.max(0, Math.min(blockRect.width - 50, newX))}px`; + elementToDrag.style.top = `${Math.max(0, Math.min(blockRect.height - 30, newY))}px`; + } } - } - // Block dragging - direct DOM manipulation - if (draggingBlock && currentBlock?.positionType && (currentBlock.positionType === "absolute" || currentBlock.positionType === "fixed")) { - const editorElement = editorRef.current; - const blockToDrag = document.querySelector(`[data-block-id="${draggingBlock}"]`) as HTMLElement; - - if (editorElement && blockToDrag) { - const editorRect = editorElement.getBoundingClientRect(); - const newX = e.clientX - editorRect.left - blockDragOffset.x; - const newY = e.clientY - editorRect.top - blockDragOffset.y; + // Block dragging - direct DOM manipulation + if (draggingBlock && currentBlock?.positionType && (currentBlock.positionType === "absolute" || currentBlock.positionType === "fixed")) { + const editorElement = editorRef.current; + const blockToDrag = document.querySelector(`[data-block-id="${draggingBlock}"]`) as HTMLElement; - // Direct DOM manipulation - blockToDrag.style.left = `${Math.max(0, Math.min(editorRect.width - (currentBlock.size?.width || 400), newX))}px`; - blockToDrag.style.top = `${Math.max(0, Math.min(editorRect.height - (currentBlock.size?.height || 300), newY))}px`; + if (editorElement && blockToDrag) { + const editorRect = editorElement.getBoundingClientRect(); + const newX = e.clientX - editorRect.left - blockDragOffset.x; + const newY = e.clientY - editorRect.top - blockDragOffset.y; + + // Direct DOM manipulation + blockToDrag.style.left = `${Math.max(0, Math.min(editorRect.width - (currentBlock.size?.width || 400), newX))}px`; + blockToDrag.style.top = `${Math.max(0, Math.min(editorRect.height - (currentBlock.size?.height || 300), newY))}px`; + } } - } - // Resizing - direct DOM manipulation - if ((resizingElement || resizingBlock) && resizeStart) { + // Resizing - direct DOM manipulation + if ((resizingElement || resizingBlock) && resizeStart) { + if (resizingElement && selectedBlock) { + const elementToResize = document.querySelector(`[data-element-id="${resizingElement}"]`) as HTMLElement; + if (elementToResize) { + const deltaX = e.clientX - resizeStart.x; + const deltaY = e.clientY - resizeStart.y; + + const newWidth = Math.max(100, resizeStart.width + deltaX); + const newHeight = Math.max(50, resizeStart.height + deltaY); + + // Direct DOM manipulation + elementToResize.style.width = `${newWidth}px`; + elementToResize.style.height = `${newHeight}px`; + } + } else if (resizingBlock) { + const blockToResize = document.querySelector(`[data-block-id="${resizingBlock}"]`) as HTMLElement; + if (blockToResize) { + const deltaX = e.clientX - resizeStart.x; + const deltaY = e.clientY - resizeStart.y; + + const currentBlock = blocks.find((b) => b.blockUuid === resizingBlock); + const minSize = currentBlock ? calculateMinBlockSize(currentBlock) : { width: 300, height: 200 }; + + const newWidth = Math.max(minSize.width, resizeStart.width + deltaX); + const newHeight = Math.max(minSize.height, resizeStart.height + deltaY); + + // Direct DOM manipulation + blockToResize.style.width = `${newWidth}px`; + blockToResize.style.height = `${newHeight}px`; + } + } + } + }; + + const handleMouseUp = (): void => { + // Update state only on mouse up for elements + if (draggingElement && selectedBlock && currentElement?.positionType === "absolute") { + const blockElement = document.querySelector(`[data-block-id="${selectedBlock}"]`) as HTMLElement; + const elementToDrag = document.querySelector(`[data-element-id="${draggingElement}"]`) as HTMLElement; + + if (blockElement && elementToDrag) { + const computedStyle = window.getComputedStyle(elementToDrag); + const x = parseFloat(computedStyle.left); + const y = parseFloat(computedStyle.top); + + updateElementPosition(selectedBlock, draggingElement, { x, y }); + } + } + + // Update state only on mouse up for blocks + if (draggingBlock && currentBlock?.positionType && (currentBlock.positionType === "absolute" || currentBlock.positionType === "fixed")) { + const blockToDrag = document.querySelector(`[data-block-id="${draggingBlock}"]`) as HTMLElement; + + if (blockToDrag) { + const computedStyle = window.getComputedStyle(blockToDrag); + const x = parseFloat(computedStyle.left); + const y = parseFloat(computedStyle.top); + + updateBlockPosition(draggingBlock, { x, y }); + } + } + + // Update state only on mouse up for resizing if (resizingElement && selectedBlock) { const elementToResize = document.querySelector(`[data-element-id="${resizingElement}"]`) as HTMLElement; if (elementToResize) { - const deltaX = e.clientX - resizeStart.x; - const deltaY = e.clientY - resizeStart.y; + const computedStyle = window.getComputedStyle(elementToResize); + const width = parseFloat(computedStyle.width); + const height = parseFloat(computedStyle.height); - const newWidth = Math.max(100, resizeStart.width + deltaX); - const newHeight = Math.max(50, resizeStart.height + deltaY); - - // Direct DOM manipulation - elementToResize.style.width = `${newWidth}px`; - elementToResize.style.height = `${newHeight}px`; + updateElementSize(selectedBlock, resizingElement, { width, height }); } - } else if (resizingBlock) { + } + + if (resizingBlock) { const blockToResize = document.querySelector(`[data-block-id="${resizingBlock}"]`) as HTMLElement; if (blockToResize) { - const deltaX = e.clientX - resizeStart.x; - const deltaY = e.clientY - resizeStart.y; + const computedStyle = window.getComputedStyle(blockToResize); + const width = parseFloat(computedStyle.width); + const height = parseFloat(computedStyle.height); - const currentBlock = blocks.find((b) => b.blockUuid === resizingBlock); - const minSize = currentBlock ? calculateMinBlockSize(currentBlock) : { width: 300, height: 200 }; - - const newWidth = Math.max(minSize.width, resizeStart.width + deltaX); - const newHeight = Math.max(minSize.height, resizeStart.height + deltaY); - - // Direct DOM manipulation - blockToResize.style.width = `${newWidth}px`; - blockToResize.style.height = `${newHeight}px`; + updateBlockSize(resizingBlock, { width, height }); } } - } - }; - const handleMouseUp = (): void => { - // Update state only on mouse up for elements - if (draggingElement && selectedBlock && currentElement?.positionType === "absolute") { - const blockElement = document.querySelector(`[data-block-id="${selectedBlock}"]`) as HTMLElement; - const elementToDrag = document.querySelector(`[data-element-id="${draggingElement}"]`) as HTMLElement; - - if (blockElement && elementToDrag) { - const computedStyle = window.getComputedStyle(elementToDrag); - const x = parseFloat(computedStyle.left); - const y = parseFloat(computedStyle.top); - - updateElementPosition(selectedBlock, draggingElement, { x, y }); - } + // Reset all dragging states + setDraggingElement(null); + setResizingElement(null); + setDraggingBlock(null); + setResizingBlock(null); + setResizeStart(null); + }; + + if (draggingElement || draggingBlock || resizingElement || resizingBlock) { + document.addEventListener("mousemove", handleMouseMove); + document.addEventListener("mouseup", handleMouseUp); } - // Update state only on mouse up for blocks - if (draggingBlock && currentBlock?.positionType && (currentBlock.positionType === "absolute" || currentBlock.positionType === "fixed")) { - const blockToDrag = document.querySelector(`[data-block-id="${draggingBlock}"]`) as HTMLElement; - - if (blockToDrag) { - const computedStyle = window.getComputedStyle(blockToDrag); - const x = parseFloat(computedStyle.left); - const y = parseFloat(computedStyle.top); - - updateBlockPosition(draggingBlock, { x, y }); - } - } - - // Update state only on mouse up for resizing - if (resizingElement && selectedBlock) { - const elementToResize = document.querySelector(`[data-element-id="${resizingElement}"]`) as HTMLElement; - if (elementToResize) { - const computedStyle = window.getComputedStyle(elementToResize); - const width = parseFloat(computedStyle.width); - const height = parseFloat(computedStyle.height); - - updateElementSize(selectedBlock, resizingElement, { width, height }); - } - } - - if (resizingBlock) { - const blockToResize = document.querySelector(`[data-block-id="${resizingBlock}"]`) as HTMLElement; - if (blockToResize) { - const computedStyle = window.getComputedStyle(blockToResize); - const width = parseFloat(computedStyle.width); - const height = parseFloat(computedStyle.height); - - updateBlockSize(resizingBlock, { width, height }); - } - } - - // Reset all dragging states - setDraggingElement(null); - setResizingElement(null); - setDraggingBlock(null); - setResizingBlock(null); - setResizeStart(null); - }; - - if (draggingElement || draggingBlock || resizingElement || resizingBlock) { - document.addEventListener("mousemove", handleMouseMove); - document.addEventListener("mouseup", handleMouseUp); - } - - return () => { - document.removeEventListener("mousemove", handleMouseMove); - document.removeEventListener("mouseup", handleMouseUp); - }; -}, [draggingElement, resizingElement, draggingBlock, resizingBlock, elementDragOffset, blockDragOffset, selectedBlock, currentElement, resizeStart, currentBlock, blocks]); + return () => { + document.removeEventListener("mousemove", handleMouseMove); + document.removeEventListener("mouseup", handleMouseUp); + }; + }, [draggingElement, resizingElement, draggingBlock, resizingBlock, elementDragOffset, blockDragOffset, selectedBlock, currentElement, resizeStart, currentBlock, blocks]); // Update dropdown position when showElementDropdown changes useEffect(() => { @@ -339,69 +339,70 @@ useEffect(() => { addBlock()} showDataModelPanel={showDataModelPanel} setShowDataModelPanel={setShowDataModelPanel} /> )} - handleBlockClick(blockId, event, editMode, setSelectedBlock, setSelectedElement, setShowSwapUI)} - handleElementClick={(blockId, elementId, event) => handleElementClick(blockId, elementId, event, editMode, setSelectedElement, setSelectedBlock, setShowSwapUI, setShowElementDropdown)} - handleElementDragStart={(elementId, event) => handleElementDragStart(elementId, event, currentElement, setDraggingElement, setElementDragOffset)} - handleElementResizeStart={(elementId, event) => handleElementResizeStart(elementId, event, setResizingElement, setResizeStart)} - handleBlockResizeStart={(blockId, event) => handleBlockResizeStart(blockId, event, setResizingBlock, setResizeStart)} - handleSwapStart={(elementId, event) => handleSwapStart(elementId, event, setSwapSource, setShowSwapUI)} - handleSwapTarget={(elementId, event) => handleSwapTarget(elementId, event, swapSource, selectedBlock, swapElements)} - handleBlockDragStart={(blockId, event) => handleBlockDragStart(blockId, event, setDraggingBlock, setBlockDragOffset)} - setShowElementDropdown={setShowElementDropdown} - showElementDropdown={showElementDropdown} - blockRef={blockRef} - /> + {/* BlockGrid */} + +
+ handleBlockClick(blockId, event, editMode, setSelectedBlock, setSelectedElement, setShowSwapUI)} + handleElementClick={(blockId, elementId, event) => handleElementClick(blockId, elementId, event, editMode, setSelectedElement, setSelectedBlock, setShowSwapUI, setShowElementDropdown)} + handleElementDragStart={(elementId, event) => handleElementDragStart(elementId, event, currentElement, setDraggingElement, setElementDragOffset)} + handleElementResizeStart={(elementId, event) => handleElementResizeStart(elementId, event, setResizingElement, setResizeStart)} + handleBlockResizeStart={(blockId, event) => handleBlockResizeStart(blockId, event, setResizingBlock, setResizeStart)} + handleSwapStart={(elementId, event) => handleSwapStart(elementId, event, setSwapSource, setShowSwapUI)} + handleSwapTarget={(elementId, event) => handleSwapTarget(elementId, event, swapSource, selectedBlock, swapElements)} + handleBlockDragStart={(blockId, event) => handleBlockDragStart(blockId, event, setDraggingBlock, setBlockDragOffset)} + setShowElementDropdown={setShowElementDropdown} + showElementDropdown={showElementDropdown} + blockRef={blockRef} + /> + {/* BlockEditor */} + {selectedBlock && editMode && !selectedElement && currentBlock && ( + updateBlockStyle(blockId, style)} + updateBlockSize={(blockId, size) => updateBlockSize(blockId, size)} + updateBlockPosition={(blockId, position) => updateBlockPosition(blockId, position)} + updateBlockPositionType={(blockId, positionType) => updateBlockPositionType(blockId, positionType)} + updateBlockZIndex={(blockId, zIndex) => updateBlockZIndex(blockId, zIndex)} + /> + )} + {selectedElement && editMode && selectedBlock && currentElement && ( + updateElementStyle(blockId, elementId, style)} + updateElementSize={(blockId, elementId, size) => updateElementSize(blockId, elementId, size)} + updateElementPosition={(blockId, elementId, position) => updateElementPosition(blockId, elementId, position)} + updateElementPositionType={(blockId, elementId, positionType) => updateElementPositionType(blockId, elementId, positionType)} + updateElementZIndex={(blockId, elementId, zIndex) => updateElementZIndex(blockId, elementId, zIndex)} + updateElementData={(blockId, elementId, updates) => updateElementData(blockId, elementId, updates)} + updateGraphData={(blockId, elementId, newData) => updateGraphData(blockId, elementId, newData)} + updateGraphTitle={(blockId, elementId, title) => updateGraphTitle(blockId, elementId, title)} + updateGraphType={(blockId, elementId, type) => updateGraphType(blockId, elementId, type)} + setSwapSource={setSwapSource} + setShowSwapUI={setShowSwapUI} + dataModelManager={dataModelManager} + /> + )} +
+ - addElement(blockId, type as UIType, graphType as GraphTypes)} - dropdownRef={dropdownRef} - /> {showDataModelPanel && editMode && } - {selectedBlock && editMode && !selectedElement && currentBlock && ( - updateBlockStyle(blockId, style)} - updateBlockSize={(blockId, size) => updateBlockSize(blockId, size)} - updateBlockPosition={(blockId, position) => updateBlockPosition(blockId, position)} - updateBlockPositionType={(blockId, positionType) => updateBlockPositionType(blockId, positionType)} - updateBlockZIndex={(blockId, zIndex) => updateBlockZIndex(blockId, zIndex)} - /> - )} - {selectedElement && editMode && selectedBlock && currentElement && ( - updateElementStyle(blockId, elementId, style)} - updateElementSize={(blockId, elementId, size) => updateElementSize(blockId, elementId, size)} - updateElementPosition={(blockId, elementId, position) => updateElementPosition(blockId, elementId, position)} - updateElementPositionType={(blockId, elementId, positionType) => updateElementPositionType(blockId, elementId, positionType)} - updateElementZIndex={(blockId, elementId, zIndex) => updateElementZIndex(blockId, elementId, zIndex)} - updateElementData={(blockId, elementId, updates) => updateElementData(blockId, elementId, updates)} - updateGraphData={(blockId, elementId, newData) => updateGraphData(blockId, elementId, newData)} - updateGraphTitle={(blockId, elementId, title) => updateGraphTitle(blockId, elementId, title)} - updateGraphType={(blockId, elementId, type) => updateGraphType(blockId, elementId, type)} - setSwapSource={setSwapSource} - setShowSwapUI={setShowSwapUI} - dataModelManager={dataModelManager} - /> - )} + {showSwapUI && } diff --git a/app/src/components/SimulationDashboard/components/block/BlockComponent.tsx b/app/src/components/SimulationDashboard/components/block/BlockComponent.tsx index 8970db2..290cedd 100644 --- a/app/src/components/SimulationDashboard/components/block/BlockComponent.tsx +++ b/app/src/components/SimulationDashboard/components/block/BlockComponent.tsx @@ -1,6 +1,10 @@ -import type { RefObject } from "react"; +import { useRef, type RefObject } from "react"; import { Block } from "../../../../types/exportedTypes"; import ElementComponent from "../element/ElementComponent"; +import { ResizeIcon } from "../../../icons/ExportToolsIcons"; +import ElementDropdown from "../element/ElementDropdown"; +import { createSimulationDashboardStore } from "../../../../store/simulation/useSimulationDashBoardStore"; +import { useSceneContext } from "../../../../modules/scene/sceneContext"; interface BlockComponentProps { block: Block; @@ -43,6 +47,11 @@ const BlockComponent: React.FC = ({ blockRef, handleBlockDragStart, }) => { + const { simulationDashBoardStore } = useSceneContext(); + const { addElement } = simulationDashBoardStore() + + const dropdownRef = useRef(null); + const minSize = calculateMinBlockSize(block); const isSelected = selectedBlock === block.blockUuid; const isDraggable = editMode && (block.positionType === "absolute" || block.positionType === "fixed"); @@ -54,6 +63,8 @@ const BlockComponent: React.FC = ({ handleBlockClick(block.blockUuid, event); }; + + return (
= ({ > {/* Add Element Button */} {editMode && isSelected && ( - +
+ + + addElement(blockId, type as UIType, graphType as GraphTypes)} + dropdownRef={dropdownRef} + /> +
)} {/* Elements */} @@ -106,7 +125,10 @@ const BlockComponent: React.FC = ({ ))} {/* Block resize handle */} - {editMode && isSelected &&
handleBlockResizeStart(block.blockUuid, e)} />} + {editMode && isSelected && +
handleBlockResizeStart(block.blockUuid, e)}> + +
} {/* Drag handle indicator for absolute/fixed blocks */} {isDraggable && editMode && ( @@ -133,6 +155,9 @@ const BlockComponent: React.FC = ({ ⤣
)} + + +
); }; diff --git a/app/src/components/SimulationDashboard/components/block/BlockEditor.tsx b/app/src/components/SimulationDashboard/components/block/BlockEditor.tsx index 0e890d6..1391f35 100644 --- a/app/src/components/SimulationDashboard/components/block/BlockEditor.tsx +++ b/app/src/components/SimulationDashboard/components/block/BlockEditor.tsx @@ -5,6 +5,9 @@ import { getCurrentBlockStyleValue } from "../../functions/helpers/getCurrentBlo import { handleBackgroundColorChange } from "../../functions/helpers/handleBackgroundColorChange"; import { handleBackgroundAlphaChange } from "../../functions/helpers/handleBackgroundAlphaChange"; import { handleBlurAmountChange } from "../../functions/helpers/handleBlurAmountChange"; +import InputRange from "../../../ui/inputs/InputRange"; +import RegularDropDown from "../../../ui/inputs/RegularDropDown"; +import { DeleteIcon } from "../../../icons/ContextMenuIcons"; interface BlockEditorProps { blockEditorRef: RefObject; @@ -32,11 +35,13 @@ const BlockEditor: React.FC = ({ }) => { return (
-

Block Style

- +
+

Block Style

+
+
- + */} + + + updateBlockPositionType( + selectedBlock, + option as "relative" | "absolute" | "fixed" + )} + search={false} + /> +
{currentBlock.positionType === "absolute" && ( @@ -105,7 +122,7 @@ const BlockEditor: React.FC = ({
-
-
diff --git a/app/src/components/SimulationDashboard/components/element/ElementComponent.tsx b/app/src/components/SimulationDashboard/components/element/ElementComponent.tsx index 00a36d8..1cfeed6 100644 --- a/app/src/components/SimulationDashboard/components/element/ElementComponent.tsx +++ b/app/src/components/SimulationDashboard/components/element/ElementComponent.tsx @@ -2,6 +2,7 @@ import React from "react"; import type { UIElement } from "../../../../types/exportedTypes"; import { resolveElementValue } from "../../functions/helpers/resolveElementValue"; import ElementContent from "./ElementContent"; +import { ResizeIcon } from "../../../icons/ExportToolsIcons"; interface ElementComponentProps { element: UIElement; @@ -79,7 +80,10 @@ const ElementComponent: React.FC = ({ Swap )} -
handleElementResizeStart(element.elementUuid, e)} /> +
handleElementResizeStart(element.elementUuid, e)} > + + +
)}
diff --git a/app/src/components/SimulationDashboard/components/element/ElementContent.tsx b/app/src/components/SimulationDashboard/components/element/ElementContent.tsx index 933309b..e81208b 100644 --- a/app/src/components/SimulationDashboard/components/element/ElementContent.tsx +++ b/app/src/components/SimulationDashboard/components/element/ElementContent.tsx @@ -26,10 +26,11 @@ interface ElementContentProps { resolvedData: ResolvedElementValue; } -const COLORS = ["#0088FE", "#00C49F", "#FFBB28", "#FF8042", "#8884D8", "#82CA9D"]; +const COLORS = ["#6f42c1", "#c4abf1", "#a07ad8", "#d2baff", "#b992e2", "#c4abf1"]; const ElementContent: React.FC = ({ element, resolvedData }) => { - const chartData = resolvedData.graphData || + const chartData = + resolvedData.graphData || element.graphData || [ { name: "Jan", value: 400 }, { name: "Feb", value: 300 }, @@ -79,7 +80,7 @@ const ElementContent: React.FC = ({ element, resolvedData } - + ) : element.graphType === "area" ? ( @@ -87,7 +88,14 @@ const ElementContent: React.FC = ({ element, resolvedData } - + ) : element.graphType === "pie" ? ( @@ -96,12 +104,13 @@ const ElementContent: React.FC = ({ element, resolvedData } cx="50%" cy="50%" labelLine={false} - label={({ name, percent }: any) => `${name} ${((percent as number) * 100).toFixed(0)}%`} + label={({ name, percent }: any) => `${name} ${(percent * 100).toFixed(0)}%`} outerRadius={80} - fill="#8884d8" + fill="#6f42c1" dataKey="value" + isAnimationActive={false} > - {chartData.map((_, index: number) => ( + {chartData.map((_, index) => ( ))} @@ -109,10 +118,17 @@ const ElementContent: React.FC = ({ element, resolvedData } ) : element.graphType === "radar" ? ( - - - - + + + + ) : ( @@ -124,12 +140,13 @@ const ElementContent: React.FC = ({ element, resolvedData } )} diff --git a/app/src/components/SimulationDashboard/components/element/ElementDropdown.tsx b/app/src/components/SimulationDashboard/components/element/ElementDropdown.tsx index 2977da7..5fd7bc9 100644 --- a/app/src/components/SimulationDashboard/components/element/ElementDropdown.tsx +++ b/app/src/components/SimulationDashboard/components/element/ElementDropdown.tsx @@ -1,14 +1,13 @@ import React, { type RefObject } from "react"; -import { createPortal } from "react-dom"; interface ElementDropdownProps { showElementDropdown: string | null; - dropDownPosition: { top: number; left: number }; + // dropDownPosition: { top: number; left: number }; addElement: (blockId: string, type: string, graphType?: string | undefined) => void; dropdownRef: RefObject; } -const ElementDropdown: React.FC = ({ showElementDropdown, dropDownPosition, addElement, dropdownRef }) => { +const ElementDropdown: React.FC = ({ showElementDropdown, addElement, dropdownRef }) => { if (!showElementDropdown) return null; const elementTypes = [ { label: "Label-Value", type: "label-value" }, @@ -21,22 +20,17 @@ const ElementDropdown: React.FC = ({ showElementDropdown, { label: "Radar Chart", type: "graph", graphType: "radar" }, ]; - return createPortal( + return (
{elementTypes.map((elementType) => ( ))} -
, - document.body + ); }; diff --git a/app/src/components/SimulationDashboard/components/element/ElementEditor.tsx b/app/src/components/SimulationDashboard/components/element/ElementEditor.tsx index acc239f..2800288 100644 --- a/app/src/components/SimulationDashboard/components/element/ElementEditor.tsx +++ b/app/src/components/SimulationDashboard/components/element/ElementEditor.tsx @@ -2,6 +2,7 @@ import type { RefObject } from "react"; import { ExtendedCSSProperties, UIElement } from "../../../../types/exportedTypes"; import { getCurrentElementStyleValue } from "../../functions/helpers/getCurrentElementStyleValue"; import type { DataModelManager } from "../../data/dataModel"; +import { DeleteIcon } from "../../../icons/ContextMenuIcons"; interface ElementEditorProps { elementEditorRef: RefObject; @@ -51,8 +52,11 @@ const ElementEditor: React.FC = ({ return (
-

Element Style

+
+

Element Style

+
+
{currentElement.type === "label-value" && ( <>
@@ -388,7 +392,9 @@ const ElementEditor: React.FC = ({
{/* Data Binding Section */} -

Data Binding

+
+

Data Binding

+
diff --git a/app/src/components/SimulationDashboard/components/models/DataModelPanel.tsx b/app/src/components/SimulationDashboard/components/models/DataModelPanel.tsx index c0e8e81..2e14b81 100644 --- a/app/src/components/SimulationDashboard/components/models/DataModelPanel.tsx +++ b/app/src/components/SimulationDashboard/components/models/DataModelPanel.tsx @@ -24,7 +24,9 @@ const DataModelPanel: React.FC = ({ dataModel, dataModelMan return (
-

Data Model

+
+

Data Model

+
Available Data Keys:
diff --git a/app/src/components/icons/ExportToolsIcons.tsx b/app/src/components/icons/ExportToolsIcons.tsx index 4dc7a5b..92d7f8c 100644 --- a/app/src/components/icons/ExportToolsIcons.tsx +++ b/app/src/components/icons/ExportToolsIcons.tsx @@ -665,3 +665,15 @@ export function MeasureToolIcon({ isActive }: { isActive: boolean }) { ); } + + +export function ResizeIcon() { + return ( + + + + + + + ) +} \ No newline at end of file diff --git a/app/src/store/simulation/useSimulationDashBoardStore.ts b/app/src/store/simulation/useSimulationDashBoardStore.ts index edee36e..4e851ff 100644 --- a/app/src/store/simulation/useSimulationDashBoardStore.ts +++ b/app/src/store/simulation/useSimulationDashBoardStore.ts @@ -2,464 +2,561 @@ import { MathUtils } from "three"; import { create } from "zustand"; import { immer } from "zustand/middleware/immer"; import { defaultGraphData } from "../../components/SimulationDashboard/data/defaultGraphData"; -import { Block, UIElement, ExtendedCSSProperties } from "../../types/exportedTypes"; +import { + Block, + UIElement, + ExtendedCSSProperties, +} from "../../types/exportedTypes"; interface SimulationDashboardStore { - blocks: Block[]; - selectedBlockId: string | null; - selectedElementId: string | null; + blocks: Block[]; + selectedBlockId: string | null; + selectedElementId: string | null; - // Subscription management - subscribe: (callback: () => void) => () => void; - _notifySubscribers: () => void; - saveBlocks: () => void; + // Subscription management + subscribe: (callback: () => void) => () => void; + _notifySubscribers: () => void; + saveBlocks: () => void; - // Block operations - addBlock: () => void; - removeBlock: (blockId: string) => void; - updateBlock: (blockId: string, updates: Partial) => void; - clearBlocks: () => void; - setBlocks: (blocks: Block[]) => void; + // Block operations + addBlock: () => void; + removeBlock: (blockId: string) => void; + updateBlock: (blockId: string, updates: Partial) => void; + clearBlocks: () => void; + setBlocks: (blocks: Block[]) => void; - // Block styling and positioning - updateBlockStyle: (blockId: string, newStyle: React.CSSProperties) => void; - updateBlockSize: (blockId: string, size: Size) => void; - updateBlockPosition: (blockId: string, position: Position) => void; - updateBlockPositionType: (blockId: string, positionType: "relative" | "absolute" | "fixed") => void; - updateBlockZIndex: (blockId: string, zIndex: number) => void; + // Block styling and positioning + updateBlockStyle: (blockId: string, newStyle: React.CSSProperties) => void; + updateBlockSize: (blockId: string, size: Size) => void; + updateBlockPosition: (blockId: string, position: Position) => void; + updateBlockPositionType: ( + blockId: string, + positionType: "relative" | "absolute" | "fixed" + ) => void; + updateBlockZIndex: (blockId: string, zIndex: number) => void; - // Element operations - addElement: (blockId: string, type: UIType, graphType?: GraphTypes) => void; - removeElement: (blockId: string, elementId: string) => void; - updateElement: (blockId: string, elementId: string, updates: Partial) => void; + // Element operations + addElement: (blockId: string, type: UIType, graphType?: GraphTypes) => void; + removeElement: (blockId: string, elementId: string) => void; + updateElement: ( + blockId: string, + elementId: string, + updates: Partial + ) => void; - // Element styling and positioning - updateElementStyle: (blockId: string, elementId: string, newStyle: ExtendedCSSProperties) => void; - updateElementSize: (blockId: string, elementId: string, size: Size) => void; - updateElementPosition: (blockId: string, elementId: string, position: Position) => void; - updateElementPositionType: (blockId: string, elementId: string, positionType: "relative" | "absolute" | "fixed") => void; - updateElementZIndex: (blockId: string, elementId: string, zIndex: number) => void; + // Element styling and positioning + updateElementStyle: ( + blockId: string, + elementId: string, + newStyle: ExtendedCSSProperties + ) => void; + updateElementSize: (blockId: string, elementId: string, size: Size) => void; + updateElementPosition: ( + blockId: string, + elementId: string, + position: Position + ) => void; + updateElementPositionType: ( + blockId: string, + elementId: string, + positionType: "relative" | "absolute" | "fixed" + ) => void; + updateElementZIndex: ( + blockId: string, + elementId: string, + zIndex: number + ) => void; - // Element data operations - updateElementData: (blockId: string, elementId: string, updates: Partial) => void; - updateGraphData: (blockId: string, elementId: string, newData: GraphDataPoint[]) => void; - updateGraphTitle: (blockId: string, elementId: string, title: string) => void; - updateGraphType: (blockId: string, elementId: string, graphType: GraphTypes) => void; + // Element data operations + updateElementData: ( + blockId: string, + elementId: string, + updates: Partial + ) => void; + updateGraphData: ( + blockId: string, + elementId: string, + newData: GraphDataPoint[] + ) => void; + updateGraphTitle: (blockId: string, elementId: string, title: string) => void; + updateGraphType: ( + blockId: string, + elementId: string, + graphType: GraphTypes + ) => void; - // Selection and hover management - setSelectedBlock: (blockId: string | null) => void; - setSelectedElement: (elementId: string | null) => void; + // Selection and hover management + setSelectedBlock: (blockId: string | null) => void; + setSelectedElement: (elementId: string | null) => void; - // Element swapping - swapElements: (blockId: string, elementId1: string, elementId2: string) => void; + // Element swapping + swapElements: ( + blockId: string, + elementId1: string, + elementId2: string + ) => void; - // Helper functions - getBlockById: (blockId: string) => Block | undefined; - getElementById: (blockId: string, elementId: string) => UIElement | undefined; - getSelectedBlock: () => Block | undefined; - getSelectedElement: () => UIElement | undefined; - hasBlock: (blockId: string) => boolean; - hasElement: (blockId: string, elementId: string) => boolean; + // Helper functions + getBlockById: (blockId: string) => Block | undefined; + getElementById: (blockId: string, elementId: string) => UIElement | undefined; + getSelectedBlock: () => Block | undefined; + getSelectedElement: () => UIElement | undefined; + hasBlock: (blockId: string) => boolean; + hasElement: (blockId: string, elementId: string) => boolean; } const subscribers = new Set<() => void>(); export const createSimulationDashboardStore = () => { - return create()( - immer((set, get) => ({ - blocks: [], - selectedBlockId: null, - selectedElementId: null, + return create()( + immer((set, get) => ({ + blocks: [], + selectedBlockId: null, + selectedElementId: null, - subscribe: (callback: () => void) => { - subscribers.add(callback); - return () => { - subscribers.delete(callback); - }; + subscribe: (callback: () => void) => { + subscribers.add(callback); + return () => { + subscribers.delete(callback); + }; + }, + + _notifySubscribers: () => { + subscribers.forEach((callback) => { + try { + callback(); + } catch (error) { + console.error("Error in store subscriber:", error); + } + }); + }, + + saveBlocks: () => { + get()._notifySubscribers(); + }, + + // Block operations + addBlock: () => { + set((state) => { + const newBlock: Block = { + blockUuid: MathUtils.generateUUID(), + style: { + backgroundColor: "rgba(50, 50, 50, 0.8)", + backdropFilter: "blur(10px)", + padding: 10, + borderRadius: 8, + border: "1px solid rgba(255, 255, 255, 0.1)", + position: "relative", + minHeight: "200px", + minWidth: "300px", }, + elements: [], + zIndex: 1, + size: { width: 400, height: 300 }, + position: { x: 0, y: 0 }, + positionType: "relative", + }; + state.blocks.push(newBlock); + }); + }, - _notifySubscribers: () => { - subscribers.forEach((callback) => { - try { - callback(); - } catch (error) { - console.error("Error in store subscriber:", error); + removeBlock: (blockId) => { + set((state) => { + state.blocks = state.blocks.filter( + (block) => block.blockUuid !== blockId + ); + if (state.selectedBlockId === blockId) { + state.selectedBlockId = null; + } + }); + }, + + updateBlock: (blockId, updates) => { + set((state) => { + const block = state.blocks.find((b) => b.blockUuid === blockId); + if (block) { + Object.assign(block, updates); + } + }); + }, + + clearBlocks: () => { + set((state) => { + state.blocks = []; + state.selectedBlockId = null; + state.selectedElementId = null; + }); + }, + + setBlocks: (blocks) => { + set((state) => { + state.blocks = blocks; + }); + }, + + // Block styling and positioning + updateBlockStyle: (blockId, newStyle) => { + set((state) => { + const block = state.blocks.find((b) => b.blockUuid === blockId); + if (block) { + block.style = { ...block.style, ...newStyle }; + } + }); + }, + + updateBlockSize: (blockId, size) => { + set((state) => { + const block = state.blocks.find((b) => b.blockUuid === blockId); + if (block) { + block.size = size; + } + }); + }, + + updateBlockPosition: (blockId, position) => { + set((state) => { + const block = state.blocks.find((b) => b.blockUuid === blockId); + if (block) { + block.position = position; + } + }); + }, + + updateBlockPositionType: (blockId, positionType) => { + set((state) => { + const block = state.blocks.find((b) => b.blockUuid === blockId); + if (block) { + block.positionType = positionType; + } + }); + }, + + updateBlockZIndex: (blockId, zIndex) => { + set((state) => { + const block = state.blocks.find((b) => b.blockUuid === blockId); + if (block) { + block.zIndex = zIndex; + } + }); + }, + + // Element operations + addElement: (blockId, type, graphType) => { + set((state) => { + const block = state.blocks.find((b) => b.blockUuid === blockId); + if (block) { + const newElement: UIElement = { + elementUuid: MathUtils.generateUUID(), + type: type, + graphType, + graphTitle: graphType + ? `${ + graphType.charAt(0).toUpperCase() + graphType.slice(1) + } Chart` + : undefined, + style: + type === "graph" + ? { + width: "100%", + height: "100%", + minHeight: "120px", + color: "#ffffff", + fontSize: 14, + textAlign: "left" as const, + backgroundColor: "rgba(0, 0, 0, 0.2)", + borderRadius: "4px", + padding: "8px", } - }); - }, - - saveBlocks: () => { - get()._notifySubscribers(); - }, - - // Block operations - addBlock: () => { - set((state) => { - const newBlock: Block = { - blockUuid: MathUtils.generateUUID(), - style: { - backgroundColor: "rgba(50, 50, 50, 0.8)", - backdropFilter: "blur(10px)", - padding: 10, - borderRadius: 8, - border: "1px solid rgba(255, 255, 255, 0.1)", - position: "relative", - minHeight: "200px", - minWidth: "300px", - }, - elements: [], - zIndex: 1, - size: { width: 400, height: 300 }, - position: { x: 0, y: 0 }, - positionType: "relative", - }; - state.blocks.push(newBlock); - }); - }, - - removeBlock: (blockId) => { - set((state) => { - state.blocks = state.blocks.filter((block) => block.blockUuid !== blockId); - if (state.selectedBlockId === blockId) { - state.selectedBlockId = null; + : type === "label-value" + ? { + color: "#ffffff", + fontSize: 14, + textAlign: "left" as const, + display: "flex", + flexDirection: "column", + gap: "4px", + alignItems: "flex-start", + justifyContent: "center", + labelColor: "#ffffff", + valueColor: "#ffffff", } - }); - }, + : { + color: "#ffffff", + fontSize: 14, + textAlign: "left" as const, + }, + positionType: "relative", + position: { x: 0, y: 0 }, + zIndex: 1, + data: { + key: MathUtils.generateUUID(), + dataSource: "static", + staticValue: + type === "label-value" + ? "Value" + : type === "text" + ? "Text" + : "", + label: type === "label-value" ? "Label" : undefined, + }, + graphData: type === "graph" ? defaultGraphData : undefined, + size: + type === "graph" + ? { width: 400, height: 200 } + : { width: 200, height: 60 }, + }; + block.elements.push(newElement); + } + }); + }, - updateBlock: (blockId, updates) => { - set((state) => { - const block = state.blocks.find((b) => b.blockUuid === blockId); - if (block) { - Object.assign(block, updates); - } - }); - }, + removeElement: (blockId, elementId) => { + set((state) => { + const block = state.blocks.find((b) => b.blockUuid === blockId); + if (block) { + block.elements = block.elements.filter( + (el) => el.elementUuid !== elementId + ); + if (state.selectedElementId === elementId) { + state.selectedElementId = null; + } + } + }); + }, - clearBlocks: () => { - set((state) => { - state.blocks = []; - state.selectedBlockId = null; - state.selectedElementId = null; - }); - }, + updateElement: (blockId, elementId, updates) => { + set((state) => { + const block = state.blocks.find((b) => b.blockUuid === blockId); + if (block) { + const element = block.elements.find( + (el) => el.elementUuid === elementId + ); + if (element) { + Object.assign(element, updates); + } + } + }); + }, - setBlocks: (blocks) => { - set((state) => { - state.blocks = blocks; - }); - }, + // Element styling and positioning + updateElementStyle: (blockId, elementId, newStyle) => { + set((state) => { + const block = state.blocks.find((b) => b.blockUuid === blockId); + if (block) { + const element = block.elements.find( + (el) => el.elementUuid === elementId + ); + if (element) { + element.style = { ...element.style, ...newStyle }; + } + } + }); + }, - // Block styling and positioning - updateBlockStyle: (blockId, newStyle) => { - set((state) => { - const block = state.blocks.find((b) => b.blockUuid === blockId); - if (block) { - block.style = { ...block.style, ...newStyle }; - } - }); - }, + updateElementSize: (blockId, elementId, size) => { + set((state) => { + const block = state.blocks.find((b) => b.blockUuid === blockId); + if (block) { + const element = block.elements.find( + (el) => el.elementUuid === elementId + ); + if (element) { + element.size = size; + } + } + }); + }, - updateBlockSize: (blockId, size) => { - set((state) => { - const block = state.blocks.find((b) => b.blockUuid === blockId); - if (block) { - block.size = size; - } - }); - }, + updateElementPosition: (blockId, elementId, position) => { + set((state) => { + const block = state.blocks.find((b) => b.blockUuid === blockId); + if (block) { + const element = block.elements.find( + (el) => el.elementUuid === elementId + ); + if (element) { + element.position = position; + } + } + }); + }, - updateBlockPosition: (blockId, position) => { - set((state) => { - const block = state.blocks.find((b) => b.blockUuid === blockId); - if (block) { - block.position = position; - } - }); - }, + updateElementPositionType: (blockId, elementId, positionType) => { + set((state) => { + const block = state.blocks.find((b) => b.blockUuid === blockId); + if (block) { + const element = block.elements.find( + (el) => el.elementUuid === elementId + ); + if (element) { + element.positionType = positionType; + } + } + }); + }, - updateBlockPositionType: (blockId, positionType) => { - set((state) => { - const block = state.blocks.find((b) => b.blockUuid === blockId); - if (block) { - block.positionType = positionType; - } - }); - }, + updateElementZIndex: (blockId, elementId, zIndex) => { + set((state) => { + const block = state.blocks.find((b) => b.blockUuid === blockId); + if (block) { + const element = block.elements.find( + (el) => el.elementUuid === elementId + ); + if (element) { + element.zIndex = zIndex; + } + } + }); + }, - updateBlockZIndex: (blockId, zIndex) => { - set((state) => { - const block = state.blocks.find((b) => b.blockUuid === blockId); - if (block) { - block.zIndex = zIndex; - } - }); - }, + // Element data operations + updateElementData: (blockId, elementId, updates) => { + set((state) => { + const block = state.blocks.find((b) => b.blockUuid === blockId); + if (block) { + const element = block.elements.find( + (el) => el.elementUuid === elementId + ); + if (element?.data) { + element.data = { ...element.data, ...updates }; + } + } + }); + }, - // Element operations - addElement: (blockId, type, graphType) => { - set((state) => { - const block = state.blocks.find((b) => b.blockUuid === blockId); - if (block) { - const newElement: UIElement = { - elementUuid: MathUtils.generateUUID(), - type: type, - graphType, - graphTitle: graphType ? `${graphType.charAt(0).toUpperCase() + graphType.slice(1)} Chart` : undefined, - style: - type === "graph" - ? { - width: "100%", - height: "100%", - minHeight: "120px", - color: "#ffffff", - fontSize: 14, - textAlign: "left" as const, - backgroundColor: "rgba(0, 0, 0, 0.2)", - borderRadius: "4px", - padding: "8px", - } - : type === "label-value" - ? { - color: "#ffffff", - fontSize: 14, - textAlign: "left" as const, - display: "flex", - flexDirection: "column", - gap: "4px", - alignItems: "flex-start", - justifyContent: "center", - labelColor: "#ffffff", - valueColor: "#ffffff", - } - : { - color: "#ffffff", - fontSize: 14, - textAlign: "left" as const, - }, - positionType: "relative", - position: { x: 0, y: 0 }, - zIndex: 1, - data: { - key: MathUtils.generateUUID(), - dataSource: "static", - staticValue: type === "label-value" ? "Value" : type === "text" ? "Text" : "", - label: type === "label-value" ? "Label" : undefined, - }, - graphData: type === "graph" ? defaultGraphData : undefined, - size: type === "graph" ? { width: 400, height: 200 } : { width: 200, height: 60 }, - }; - block.elements.push(newElement); - } - }); - }, + updateGraphData: (blockId, elementId, newData) => { + set((state) => { + const block = state.blocks.find((b) => b.blockUuid === blockId); + if (block) { + const element = block.elements.find( + (el) => el.elementUuid === elementId + ); + if (element) { + element.graphData = newData; + } + } + }); + }, - removeElement: (blockId, elementId) => { - set((state) => { - const block = state.blocks.find((b) => b.blockUuid === blockId); - if (block) { - block.elements = block.elements.filter((el) => el.elementUuid !== elementId); - if (state.selectedElementId === elementId) { - state.selectedElementId = null; - } - } - }); - }, + updateGraphTitle: (blockId, elementId, title) => { + set((state) => { + const block = state.blocks.find((b) => b.blockUuid === blockId); + if (block) { + const element = block.elements.find( + (el) => el.elementUuid === elementId + ); + if (element) { + element.graphTitle = title; + } + } + }); + }, - updateElement: (blockId, elementId, updates) => { - set((state) => { - const block = state.blocks.find((b) => b.blockUuid === blockId); - if (block) { - const element = block.elements.find((el) => el.elementUuid === elementId); - if (element) { - Object.assign(element, updates); - } - } - }); - }, + updateGraphType: (blockId, elementId, graphType) => { + set((state) => { + const block = state.blocks.find((b) => b.blockUuid === blockId); + if (block) { + const element = block.elements.find( + (el) => el.elementUuid === elementId + ); + if (element && element.type === "graph") { + element.graphType = graphType; + element.graphTitle = `${ + graphType.charAt(0).toUpperCase() + graphType.slice(1) + } Chart`; - // Element styling and positioning - updateElementStyle: (blockId, elementId, newStyle) => { - set((state) => { - const block = state.blocks.find((b) => b.blockUuid === blockId); - if (block) { - const element = block.elements.find((el) => el.elementUuid === elementId); - if (element) { - element.style = { ...element.style, ...newStyle }; - } - } - }); - }, + element.graphData = defaultGraphData; + } + } + }); + }, - updateElementSize: (blockId, elementId, size) => { - set((state) => { - const block = state.blocks.find((b) => b.blockUuid === blockId); - if (block) { - const element = block.elements.find((el) => el.elementUuid === elementId); - if (element) { - element.size = size; - } - } - }); - }, + // Selection and hover management + setSelectedBlock: (blockId) => { + set((state) => { + state.selectedBlockId = blockId; + // Clear element selection when selecting a block + if (blockId) { + state.selectedElementId = null; + } + }); + }, - updateElementPosition: (blockId, elementId, position) => { - set((state) => { - const block = state.blocks.find((b) => b.blockUuid === blockId); - if (block) { - const element = block.elements.find((el) => el.elementUuid === elementId); - if (element) { - element.position = position; - } - } - }); - }, + setSelectedElement: (elementId) => { + set((state) => { + state.selectedElementId = elementId; + }); + }, - updateElementPositionType: (blockId, elementId, positionType) => { - set((state) => { - const block = state.blocks.find((b) => b.blockUuid === blockId); - if (block) { - const element = block.elements.find((el) => el.elementUuid === elementId); - if (element) { - element.positionType = positionType; - } - } - }); - }, + // Element swapping + swapElements: (blockId, elementId1, elementId2) => { + set((state) => { + const block = state.blocks.find((b) => b.blockUuid === blockId); + if (block) { + block.elements = block.elements.map((el) => { + if (el.elementUuid === elementId1) { + const targetElement = block.elements.find( + (e) => e.elementUuid === elementId2 + ); + return targetElement + ? { ...targetElement, elementUuid: elementId1 } + : el; + } + if (el.elementUuid === elementId2) { + const sourceElement = block.elements.find( + (e) => e.elementUuid === elementId1 + ); + return sourceElement + ? { ...sourceElement, elementUuid: elementId2 } + : el; + } + return el; + }); + } + }); + }, - updateElementZIndex: (blockId, elementId, zIndex) => { - set((state) => { - const block = state.blocks.find((b) => b.blockUuid === blockId); - if (block) { - const element = block.elements.find((el) => el.elementUuid === elementId); - if (element) { - element.zIndex = zIndex; - } - } - }); - }, + // Helper functions + getBlockById: (blockId) => { + return get().blocks.find((b) => b.blockUuid === blockId); + }, - // Element data operations - updateElementData: (blockId, elementId, updates) => { - set((state) => { - const block = state.blocks.find((b) => b.blockUuid === blockId); - if (block) { - const element = block.elements.find((el) => el.elementUuid === elementId); - if (element?.data) { - element.data = { ...element.data, ...updates }; - } - } - }); - }, + getElementById: (blockId, elementId) => { + const block = get().blocks.find((b) => b.blockUuid === blockId); + return block?.elements.find((el) => el.elementUuid === elementId); + }, - updateGraphData: (blockId, elementId, newData) => { - set((state) => { - const block = state.blocks.find((b) => b.blockUuid === blockId); - if (block) { - const element = block.elements.find((el) => el.elementUuid === elementId); - if (element) { - element.graphData = newData; - } - } - }); - }, + getSelectedBlock: () => { + const { selectedBlockId, blocks } = get(); + return selectedBlockId + ? blocks.find((b) => b.blockUuid === selectedBlockId) + : undefined; + }, - updateGraphTitle: (blockId, elementId, title) => { - set((state) => { - const block = state.blocks.find((b) => b.blockUuid === blockId); - if (block) { - const element = block.elements.find((el) => el.elementUuid === elementId); - if (element) { - element.graphTitle = title; - } - } - }); - }, + getSelectedElement: () => { + const { selectedElementId, selectedBlockId, blocks } = get(); + if (!selectedElementId || !selectedBlockId) return undefined; - updateGraphType: (blockId, elementId, graphType) => { - set((state) => { - const block = state.blocks.find((b) => b.blockUuid === blockId); - if (block) { - const element = block.elements.find((el) => el.elementUuid === elementId); - if (element && element.type === "graph") { - element.graphType = graphType; - element.graphTitle = `${graphType.charAt(0).toUpperCase() + graphType.slice(1)} Chart`; + const block = blocks.find((b) => b.blockUuid === selectedBlockId); + return block?.elements.find( + (el) => el.elementUuid === selectedElementId + ); + }, - element.graphData = defaultGraphData; - } - } - }); - }, + hasBlock: (blockId) => { + return get().blocks.some((b) => b.blockUuid === blockId); + }, - // Selection and hover management - setSelectedBlock: (blockId) => { - set((state) => { - state.selectedBlockId = blockId; - // Clear element selection when selecting a block - if (blockId) { - state.selectedElementId = null; - } - }); - }, - - setSelectedElement: (elementId) => { - set((state) => { - state.selectedElementId = elementId; - }); - }, - - // Element swapping - swapElements: (blockId, elementId1, elementId2) => { - set((state) => { - const block = state.blocks.find((b) => b.blockUuid === blockId); - if (block) { - block.elements = block.elements.map((el) => { - if (el.elementUuid === elementId1) { - const targetElement = block.elements.find((e) => e.elementUuid === elementId2); - return targetElement ? { ...targetElement, elementUuid: elementId1 } : el; - } - if (el.elementUuid === elementId2) { - const sourceElement = block.elements.find((e) => e.elementUuid === elementId1); - return sourceElement ? { ...sourceElement, elementUuid: elementId2 } : el; - } - return el; - }); - } - }); - }, - - // Helper functions - getBlockById: (blockId) => { - return get().blocks.find((b) => b.blockUuid === blockId); - }, - - getElementById: (blockId, elementId) => { - const block = get().blocks.find((b) => b.blockUuid === blockId); - return block?.elements.find((el) => el.elementUuid === elementId); - }, - - getSelectedBlock: () => { - const { selectedBlockId, blocks } = get(); - return selectedBlockId ? blocks.find((b) => b.blockUuid === selectedBlockId) : undefined; - }, - - getSelectedElement: () => { - const { selectedElementId, selectedBlockId, blocks } = get(); - if (!selectedElementId || !selectedBlockId) return undefined; - - const block = blocks.find((b) => b.blockUuid === selectedBlockId); - return block?.elements.find((el) => el.elementUuid === selectedElementId); - }, - - hasBlock: (blockId) => { - return get().blocks.some((b) => b.blockUuid === blockId); - }, - - hasElement: (blockId, elementId) => { - const block = get().blocks.find((b) => b.blockUuid === blockId); - return block?.elements.some((el) => el.elementUuid === elementId) || false; - }, - })) - ); + hasElement: (blockId, elementId) => { + const block = get().blocks.find((b) => b.blockUuid === blockId); + return ( + block?.elements.some((el) => el.elementUuid === elementId) || false + ); + }, + })) + ); }; -export type SimulationDashboardStoreType = ReturnType; \ No newline at end of file +export type SimulationDashboardStoreType = ReturnType< + typeof createSimulationDashboardStore +>; diff --git a/app/src/styles/components/simulationDashboard/_simulationDashBoard.scss b/app/src/styles/components/simulationDashboard/_simulationDashBoard.scss index 5c3cacb..617bc29 100644 --- a/app/src/styles/components/simulationDashboard/_simulationDashBoard.scss +++ b/app/src/styles/components/simulationDashboard/_simulationDashBoard.scss @@ -11,9 +11,12 @@ top: 0; left: 0; + pointer-events: none; + .control-panel { margin-bottom: 20px; display: flex; + justify-content: end; gap: 10px; flex-wrap: wrap; @@ -21,13 +24,14 @@ padding: 8px 16px; color: white; border: none; - border-radius: 4px; + border-radius: 20px; cursor: pointer; backdrop-filter: blur(10px); transition: background-color 0.2s ease; + pointer-events: auto; &:not(.edit-mode-active) { - background-color: #4caf50; + background-color: var(--background-color-button); } &.edit-mode-active { @@ -39,7 +43,7 @@ } &.secondary { - background-color: rgba(156, 39, 176, 0.8); + background-color: var(--background-color-button); } &:hover { @@ -50,9 +54,10 @@ .block-grid { display: flex; + flex: 1; flex-wrap: wrap; gap: 20px; - margin-top: 20px; + // margin-top: 20px; max-height: calc(100vh - 100px); overflow: auto; padding: 10px; @@ -74,8 +79,7 @@ min-width: 300px; &.selected { - border: 2px solid #4caf50; - box-shadow: 0 0 20px rgba(76, 175, 80, 0.3); + outline: 2px solid var(--border-color-accent); } &.edit-mode { @@ -107,13 +111,27 @@ } } - .add-element-button { + .element-button-container { + position: relative; + + } + + .element-container { + display: flex; + justify-content: end; + position: absolute; top: 10px; right: 10px; - background: rgba(33, 150, 243, 0.9); + } + + .add-element-button { + // position: absolute; + // top: 10px; + // right: 10px; + background: var(--background-color-button); border: none; - border-radius: 4px; + border-radius: 20px; color: white; font-size: 12px; padding: 6px 12px; @@ -122,15 +140,15 @@ backdrop-filter: blur(5px); transition: background-color 0.2s ease; - &:hover { - background: rgba(33, 150, 243, 1); - } + // &:hover { + // background: rgba(33, 150, 243, 1); + // } } .resize-handle { - width: 16px; - height: 16px; - background-color: rgba(76, 175, 80, 0.8); + // width: 16px; + // height: 16px; + // background-color: rgba(76, 175, 80, 0.8); border-radius: 3px; position: absolute; bottom: 2px; @@ -139,6 +157,10 @@ opacity: 0; transition: opacity 0.2s ease; z-index: 10; + + display: flex; + justify-content: center; + align-items: center; } &.selected .resize-handle { @@ -151,7 +173,7 @@ margin: 5px; border-radius: 4px; padding: 8px; - transition: all 0.2s ease; + // transition: all 0.2s ease; user-select: none; border: 1px solid transparent; @@ -211,8 +233,8 @@ } .resize-handle { - width: 12px; - height: 12px; + width: 20px; + height: 20px; position: absolute; bottom: 2px; right: 2px; @@ -228,22 +250,53 @@ } } - .panel { - position: fixed; - background-color: rgba(50, 50, 50, 0.95); - backdrop-filter: blur(15px); - border: 1px solid rgba(255, 255, 255, 0.2); - padding: 20px; - z-index: 1000; - border-radius: 12px; - color: #ffffff; - box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4); + .block-grid-container { + width: 100%; + display: flex; + justify-content: space-between; - h4 { - margin: 0 0 20px 0; - font-size: 16px; + pointer-events: auto; + } + + .panel { + // position: fixed; + padding: 20px; + color: #ffffff; + + + + background: var(--background-color); + backdrop-filter: blur(20px); + border-radius: 20px; + outline: 1px solid var(--border-color); + box-shadow: 0px 4px 8px rgba(60, 60, 67, 0.1019607843); + z-index: 3; + + .header { + display: flex; + justify-content: space-between; + align-items: center; + margin: 0 0 10px 0; + border-bottom: 1px solid gray; + padding-bottom: 10px; + + h4 { + font-size: var(--font-size-regular); + font-weight: 600; + } + + .icon { + cursor: pointer; + + svg { + path { + stroke: #ff4444; + } + } + } } + &.data-model-panel { left: 20px; top: 50px; @@ -252,19 +305,20 @@ max-height: 80vh; overflow-y: auto; - h4 { - color: #9c27b0; + .control-button { + &.primary { + background-color: var(--background-color-button); + } } } &.block-editor-panel { - right: 20px; - top: 50px; min-width: 280px; + height: fit-content; - h4 { - color: #4caf50; - } + // h4 { + // color: #4caf50; + // } } &.element-editor-panel { @@ -274,19 +328,45 @@ max-height: 84vh; overflow: auto; - h4 { - color: #ff9800; - } + + } } .form-group { + display: flex; + justify-content: space-between; + align-items: center; + gap: 6px; margin-bottom: 15px; + .input-range-container { + width: 100%; + display: flex; + justify-content: space-between; + align-items: center; + gap: 6px; + + } + .form-label { display: block; - margin-bottom: 8px; + // margin-bottom: 8px; font-size: 14px; + text-wrap: nowrap; + } + + .input-range-container { + padding: 0; + } + + .form-select, + .color-input, + .range-input, + .form-input, + .input-container, + .regularDropdown-container { + max-width: 140px; } .form-input, @@ -373,6 +453,7 @@ display: flex; flex-direction: column; overflow: hidden; + padding: 6px; .chart-title { font-size: 14px; @@ -430,7 +511,8 @@ } .element-dropdown { - position: fixed; + position: absolute; + transform: translateY(35px); background-color: rgba(50, 50, 50, 0.95); backdrop-filter: blur(10px); border: 1px solid rgba(255, 255, 255, 0.2); @@ -456,4 +538,4 @@ background-color: rgba(85, 85, 85, 1); } } -} +} \ No newline at end of file diff --git a/app/src/styles/layout/_sidebar.scss b/app/src/styles/layout/_sidebar.scss index 04fa235..ba047fa 100644 --- a/app/src/styles/layout/_sidebar.scss +++ b/app/src/styles/layout/_sidebar.scss @@ -940,9 +940,11 @@ font-size: var(--font-weight-regular); color: var(--text-color); padding: 12px; + .appearance-container, .element-container { + background: var(--background-color); backdrop-filter: blur(20px); border-radius: 15px;