feat: Add simulation dashboard with interactive block and element editing capabilities.
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
import { useParams } from "react-router-dom";
|
||||
import React, { useState, useRef, useEffect } from "react";
|
||||
import ControlPanel from "./ControlPanel";
|
||||
import SwapModal from "./SwapModal";
|
||||
import { Block } from "../../types/exportedTypes";
|
||||
import AnalyzerManager from "./AnalyzerManager";
|
||||
|
||||
@@ -9,7 +8,7 @@ import { useSceneContext } from "../../modules/scene/sceneContext";
|
||||
import useModuleStore from "../../store/ui/useModuleStore";
|
||||
import { usePlayButtonStore } from "../../store/ui/usePlayButtonStore";
|
||||
import { calculateMinBlockSize } from "./functions/block/calculateMinBlockSize";
|
||||
import { handleElementDragStart, handleElementResizeStart, handleBlockResizeStart, handleSwapStart, handleSwapTarget, handleBlockClick, handleElementClick } from "./functions/eventHandlers";
|
||||
import { handleElementDragStart, handleElementResizeStart, handleBlockResizeStart, handleBlockClick, handleElementClick } from "./functions/eventHandlers";
|
||||
import BlockGrid from "./components/block/BlockGrid";
|
||||
import BlockEditor from "./components/block/BlockEditor";
|
||||
import ElementEditor from "./components/element/ElementEditor";
|
||||
@@ -60,7 +59,7 @@ const DashboardEditor: React.FC = () => {
|
||||
peekUpdateCommonValue,
|
||||
peekUpdateDataValue,
|
||||
peekUpdateDataSource,
|
||||
peekSwapElements,
|
||||
|
||||
} = simulationDashBoardStore();
|
||||
|
||||
const [editMode, setEditMode] = useState(false);
|
||||
@@ -73,8 +72,7 @@ const DashboardEditor: React.FC = () => {
|
||||
width: number;
|
||||
height: number;
|
||||
} | null>(null);
|
||||
const [showSwapUI, setShowSwapUI] = useState(false);
|
||||
const [swapSource, setSwapSource] = useState<string | null>(null);
|
||||
|
||||
const [showElementDropdown, setShowElementDropdown] = useState<string | null>(null);
|
||||
const [draggingBlock, setDraggingBlock] = useState<string | null>(null);
|
||||
const [elementDragOffset, setElementDragOffset] = useState<Position>({ x: 0, y: 0 });
|
||||
@@ -89,12 +87,12 @@ const DashboardEditor: React.FC = () => {
|
||||
const currentBlock = blocks.find((b) => b.blockUuid === selectedBlock);
|
||||
const currentElement = currentBlock?.elements.find((el) => el.elementUuid === selectedElement);
|
||||
|
||||
useEffect(()=>{
|
||||
useEffect(() => {
|
||||
if (!editMode) {
|
||||
setSelectedBlock(null);
|
||||
setSelectedElement(null);
|
||||
}
|
||||
},[editMode])
|
||||
}, [editMode])
|
||||
|
||||
// Helper function to send updates to backend - only sends the specific block that changed
|
||||
const updateBackend = async (updatedBlock: Block) => {
|
||||
@@ -176,7 +174,7 @@ const DashboardEditor: React.FC = () => {
|
||||
if (!isInsideEditor) {
|
||||
setSelectedBlock(null);
|
||||
setSelectedElement(null);
|
||||
setShowSwapUI(false);
|
||||
setSelectedElement(null);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -190,7 +188,7 @@ const DashboardEditor: React.FC = () => {
|
||||
if (!isBlock && !isElement && !isButton && !isResizeHandle) {
|
||||
setSelectedBlock(null);
|
||||
setSelectedElement(null);
|
||||
setShowSwapUI(false);
|
||||
setSelectedElement(null);
|
||||
setShowElementDropdown(null);
|
||||
}
|
||||
}
|
||||
@@ -396,28 +394,14 @@ const DashboardEditor: React.FC = () => {
|
||||
editMode={editMode}
|
||||
selectedBlock={selectedBlock}
|
||||
selectedElement={selectedElement}
|
||||
showSwapUI={showSwapUI}
|
||||
swapSource={swapSource}
|
||||
calculateMinBlockSize={calculateMinBlockSize}
|
||||
handleBlockClick={(blockId, event) => handleBlockClick(blockId, event, editMode, setSelectedBlock, setSelectedElement, setShowSwapUI, setShowElementDropdown, showElementDropdown)}
|
||||
handleBlockClick={(blockId, event) => handleBlockClick(blockId, event, editMode, setSelectedBlock, setSelectedElement, setShowElementDropdown, showElementDropdown)}
|
||||
handleElementClick={(blockId, elementId, event) => {
|
||||
handleBlockClick(blockId, event, editMode, setSelectedBlock, setSelectedElement, setShowSwapUI, setShowElementDropdown, showElementDropdown);
|
||||
handleElementClick(blockId, elementId, event, editMode, setSelectedElement, setSelectedBlock, setShowSwapUI, setShowElementDropdown);
|
||||
handleElementClick(blockId, elementId, event, editMode, setSelectedElement, setSelectedBlock, 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={async (elementId, event) => {
|
||||
if (!selectedBlock) return;
|
||||
handleSwapTarget(elementId, event, swapSource, selectedBlock, async (blockId, el1, el2) => {
|
||||
const updatedBlocks = peekSwapElements(blockId, el1, el2);
|
||||
const updatedBlock = getBlockFromPeekedBlocks(updatedBlocks, blockId);
|
||||
if (updatedBlock) {
|
||||
await updateBackend(updatedBlock);
|
||||
}
|
||||
});
|
||||
}}
|
||||
handleBlockDragStart={(blockId, event) => handleBlockDragStart(blockId, event, setDraggingBlock, setBlockDragOffset)}
|
||||
setShowElementDropdown={setShowElementDropdown}
|
||||
showElementDropdown={showElementDropdown}
|
||||
@@ -607,13 +591,10 @@ const DashboardEditor: React.FC = () => {
|
||||
console.error("Failed to delete element:", error);
|
||||
}
|
||||
}}
|
||||
setSwapSource={setSwapSource}
|
||||
setShowSwapUI={setShowSwapUI}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{showSwapUI && <SwapModal setShowSwapUI={setShowSwapUI} setSwapSource={setSwapSource} />}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
import React from "react";
|
||||
|
||||
interface SwapModalProps {
|
||||
setShowSwapUI: (show: boolean) => void;
|
||||
setSwapSource: (source: string | null) => void;
|
||||
}
|
||||
|
||||
const SwapModal: React.FC<SwapModalProps> = ({ setShowSwapUI, setSwapSource }) => {
|
||||
return (
|
||||
<div className="swap-modal">
|
||||
<h4>Swap Elements</h4>
|
||||
<p>Click on another element to swap positions with the selected element</p>
|
||||
<button
|
||||
onClick={() => {
|
||||
setShowSwapUI(false);
|
||||
setSwapSource(null);
|
||||
}}
|
||||
className="cancel-button"
|
||||
>
|
||||
Cancel Swap
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SwapModal;
|
||||
@@ -10,16 +10,12 @@ interface BlockComponentProps {
|
||||
editMode: boolean;
|
||||
selectedBlock: string | null;
|
||||
selectedElement: string | null;
|
||||
showSwapUI: boolean;
|
||||
swapSource: string | null;
|
||||
calculateMinBlockSize: (block: Block) => { width: number; height: number };
|
||||
handleBlockClick: (blockId: string, event: React.MouseEvent) => void;
|
||||
handleElementClick: (blockId: string, elementId: string, event: React.MouseEvent) => void;
|
||||
handleElementDragStart: (elementId: string, event: React.MouseEvent) => void;
|
||||
handleElementResizeStart: (elementId: string, event: React.MouseEvent) => void;
|
||||
handleBlockResizeStart: (blockId: string, event: React.MouseEvent) => void;
|
||||
handleSwapStart: (elementId: string, event: React.MouseEvent) => void;
|
||||
handleSwapTarget: (elementId: string, event: React.MouseEvent) => void;
|
||||
setShowElementDropdown: (blockId: string | null) => void;
|
||||
showElementDropdown: string | null;
|
||||
blockRef: RefObject<HTMLDivElement>;
|
||||
@@ -32,16 +28,12 @@ const BlockComponent: React.FC<BlockComponentProps> = ({
|
||||
editMode,
|
||||
selectedBlock,
|
||||
selectedElement,
|
||||
showSwapUI,
|
||||
swapSource,
|
||||
calculateMinBlockSize,
|
||||
handleBlockClick,
|
||||
handleElementClick,
|
||||
handleElementDragStart,
|
||||
handleElementResizeStart,
|
||||
handleBlockResizeStart,
|
||||
handleSwapStart,
|
||||
handleSwapTarget,
|
||||
setShowElementDropdown,
|
||||
showElementDropdown,
|
||||
blockRef,
|
||||
@@ -109,13 +101,9 @@ const BlockComponent: React.FC<BlockComponentProps> = ({
|
||||
blockId={block.blockUuid}
|
||||
editMode={editMode}
|
||||
selectedElement={selectedElement}
|
||||
showSwapUI={showSwapUI}
|
||||
swapSource={swapSource}
|
||||
handleElementClick={handleElementClick}
|
||||
handleElementDragStart={handleElementDragStart}
|
||||
handleElementResizeStart={handleElementResizeStart}
|
||||
handleSwapStart={handleSwapStart}
|
||||
handleSwapTarget={handleSwapTarget}
|
||||
/>
|
||||
))}
|
||||
|
||||
|
||||
@@ -9,16 +9,12 @@ interface BlockGridProps {
|
||||
editMode: boolean;
|
||||
selectedBlock: string | null;
|
||||
selectedElement: string | null;
|
||||
showSwapUI: boolean;
|
||||
swapSource: string | null;
|
||||
calculateMinBlockSize: (block: Block) => { width: number; height: number };
|
||||
handleBlockClick: (blockId: string, event: React.MouseEvent) => void;
|
||||
handleElementClick: (blockId: string, elementId: string, event: React.MouseEvent) => void;
|
||||
handleElementDragStart: (elementId: string, event: React.MouseEvent) => void;
|
||||
handleElementResizeStart: (elementId: string, event: React.MouseEvent) => void;
|
||||
handleBlockResizeStart: (blockId: string, event: React.MouseEvent) => void;
|
||||
handleSwapStart: (elementId: string, event: React.MouseEvent) => void;
|
||||
handleSwapTarget: (elementId: string, event: React.MouseEvent) => void;
|
||||
handleBlockDragStart: (blockId: string, event: React.MouseEvent) => void;
|
||||
setShowElementDropdown: (blockId: string | null) => void;
|
||||
showElementDropdown: string | null;
|
||||
@@ -31,16 +27,12 @@ const BlockGrid: React.FC<BlockGridProps> = ({
|
||||
editMode,
|
||||
selectedBlock,
|
||||
selectedElement,
|
||||
showSwapUI,
|
||||
swapSource,
|
||||
calculateMinBlockSize,
|
||||
handleBlockClick,
|
||||
handleElementClick,
|
||||
handleElementDragStart,
|
||||
handleElementResizeStart,
|
||||
handleBlockResizeStart,
|
||||
handleSwapStart,
|
||||
handleSwapTarget,
|
||||
setShowElementDropdown,
|
||||
handleBlockDragStart,
|
||||
showElementDropdown,
|
||||
@@ -58,16 +50,12 @@ const BlockGrid: React.FC<BlockGridProps> = ({
|
||||
editMode={editMode}
|
||||
selectedBlock={selectedBlock}
|
||||
selectedElement={selectedElement}
|
||||
showSwapUI={showSwapUI}
|
||||
swapSource={swapSource}
|
||||
calculateMinBlockSize={calculateMinBlockSize}
|
||||
handleBlockClick={handleBlockClick}
|
||||
handleElementClick={handleElementClick}
|
||||
handleElementDragStart={handleElementDragStart}
|
||||
handleElementResizeStart={handleElementResizeStart}
|
||||
handleBlockResizeStart={handleBlockResizeStart}
|
||||
handleSwapStart={handleSwapStart}
|
||||
handleSwapTarget={handleSwapTarget}
|
||||
handleBlockDragStart={handleBlockDragStart}
|
||||
setShowElementDropdown={setShowElementDropdown}
|
||||
showElementDropdown={showElementDropdown}
|
||||
|
||||
@@ -9,13 +9,9 @@ interface ElementComponentProps {
|
||||
blockId: string;
|
||||
editMode: boolean;
|
||||
selectedElement: string | null;
|
||||
showSwapUI: boolean;
|
||||
swapSource: string | null;
|
||||
handleElementClick: (blockId: string, elementId: string, event: React.MouseEvent) => void;
|
||||
handleElementDragStart: (elementId: string, event: React.MouseEvent) => void;
|
||||
handleElementResizeStart: (elementId: string, event: React.MouseEvent) => void;
|
||||
handleSwapStart: (elementId: string, event: React.MouseEvent) => void;
|
||||
handleSwapTarget: (elementId: string, event: React.MouseEvent) => void;
|
||||
}
|
||||
|
||||
const ElementComponent: React.FC<ElementComponentProps> = ({
|
||||
@@ -23,16 +19,11 @@ const ElementComponent: React.FC<ElementComponentProps> = ({
|
||||
blockId,
|
||||
editMode,
|
||||
selectedElement,
|
||||
showSwapUI,
|
||||
swapSource,
|
||||
handleElementClick,
|
||||
handleElementDragStart,
|
||||
handleElementResizeStart,
|
||||
handleSwapStart,
|
||||
handleSwapTarget,
|
||||
}) => {
|
||||
const isSelected = selectedElement === element.elementUuid;
|
||||
const isSwapSource = swapSource === element.elementUuid;
|
||||
|
||||
const elementClasses = [
|
||||
"element",
|
||||
@@ -40,8 +31,6 @@ const ElementComponent: React.FC<ElementComponentProps> = ({
|
||||
element.type === "graph" ? "graph" : "",
|
||||
editMode ? "edit-mode" : "",
|
||||
isSelected ? "selected" : "",
|
||||
isSwapSource ? "swap-source" : "",
|
||||
showSwapUI && !isSwapSource ? "swap-target" : "",
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(" ");
|
||||
@@ -64,11 +53,7 @@ const ElementComponent: React.FC<ElementComponentProps> = ({
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
if (showSwapUI && swapSource !== element.elementUuid) {
|
||||
handleSwapTarget(element.elementUuid, e);
|
||||
} else {
|
||||
handleElementClick(blockId, element.elementUuid, e);
|
||||
}
|
||||
handleElementClick(blockId, element.elementUuid, e);
|
||||
}}
|
||||
onMouseDown={(e) => handleElementDragStart(element.elementUuid, e)}
|
||||
>
|
||||
|
||||
@@ -57,8 +57,6 @@ interface ElementDesignProps {
|
||||
updateDataValue: (blockId: string, elementId: string, dataValue: string | string[]) => void;
|
||||
updateDataSource: (blockId: string, elementId: string, dataSource: string | string[]) => void;
|
||||
handleRemoveElement: (blockId: string, elementId: string) => void;
|
||||
setSwapSource: (source: string | null) => void;
|
||||
setShowSwapUI: (show: boolean) => void;
|
||||
}
|
||||
|
||||
const ElementDesign: React.FC<ElementDesignProps> = ({
|
||||
@@ -74,8 +72,6 @@ const ElementDesign: React.FC<ElementDesignProps> = ({
|
||||
updateElementZIndex,
|
||||
updateGraphTitle,
|
||||
updateGraphType,
|
||||
setSwapSource,
|
||||
setShowSwapUI,
|
||||
}) => {
|
||||
const [color, setColor] = useState("#000000");
|
||||
|
||||
@@ -396,7 +392,7 @@ const ElementDesign: React.FC<ElementDesignProps> = ({
|
||||
value={color}
|
||||
onChange={(e) => {
|
||||
updateElementStyle(selectedBlock, selectedElement, {
|
||||
backgroundColor: color,
|
||||
backgroundColor: hexToRgba(e.target.value),
|
||||
});
|
||||
setColor(e.target.value);
|
||||
}}
|
||||
|
||||
@@ -30,8 +30,6 @@ interface ElementEditorProps {
|
||||
updateDataValue: (blockId: string, elementId: string, dataValue: string | string[]) => void;
|
||||
updateDataSource: (blockId: string, elementId: string, dataSource: string | string[]) => void;
|
||||
handleRemoveElement: (blockId: string, elementId: string) => void;
|
||||
setSwapSource: (source: string | null) => void;
|
||||
setShowSwapUI: (show: boolean) => void;
|
||||
}
|
||||
|
||||
const ElementEditor: React.FC<ElementEditorProps> = ({
|
||||
@@ -53,8 +51,6 @@ const ElementEditor: React.FC<ElementEditorProps> = ({
|
||||
updateDataValue,
|
||||
updateDataSource,
|
||||
handleRemoveElement,
|
||||
setSwapSource,
|
||||
setShowSwapUI,
|
||||
}) => {
|
||||
const { simulationDashBoardStore, productStore } = useSceneContext();
|
||||
const { selectedProduct, getProductById, getEventByModelUuid } = productStore();
|
||||
@@ -160,7 +156,7 @@ const ElementEditor: React.FC<ElementEditorProps> = ({
|
||||
window.removeEventListener("pointerup", onPointerUp);
|
||||
try {
|
||||
(panel as Element).releasePointerCapture?.((e as any).pointerId);
|
||||
} catch (err) {}
|
||||
} catch (err) { }
|
||||
};
|
||||
|
||||
window.addEventListener("pointermove", onPointerMove);
|
||||
@@ -519,8 +515,6 @@ const ElementEditor: React.FC<ElementEditorProps> = ({
|
||||
updateDataValue={updateDataValue}
|
||||
updateDataSource={updateDataSource}
|
||||
handleRemoveElement={handleRemoveElement}
|
||||
setSwapSource={setSwapSource}
|
||||
setShowSwapUI={setShowSwapUI}
|
||||
/>
|
||||
)}
|
||||
{selectType === "data" && (
|
||||
@@ -553,12 +547,12 @@ const ElementEditor: React.FC<ElementEditorProps> = ({
|
||||
value={
|
||||
element.dataBinding?.dataSource
|
||||
? {
|
||||
id: element.dataBinding.dataSource as string,
|
||||
label:
|
||||
getEventByModelUuid(selectedProduct.productUuid, element.dataBinding.dataSource as string)?.modelName ??
|
||||
(element.dataBinding.dataSource === "global" ? "Global" : ""),
|
||||
icon: <DeviceIcon />,
|
||||
}
|
||||
id: element.dataBinding.dataSource as string,
|
||||
label:
|
||||
getEventByModelUuid(selectedProduct.productUuid, element.dataBinding.dataSource as string)?.modelName ??
|
||||
(element.dataBinding.dataSource === "global" ? "Global" : ""),
|
||||
icon: <DeviceIcon />,
|
||||
}
|
||||
: null
|
||||
}
|
||||
onChange={(value) => {
|
||||
@@ -577,13 +571,13 @@ const ElementEditor: React.FC<ElementEditorProps> = ({
|
||||
value={
|
||||
element.dataBinding?.dataValue
|
||||
? {
|
||||
id: element.dataBinding.dataValue as string,
|
||||
label:
|
||||
getLableValueDropdownItems(element.dataBinding?.dataSource as string | undefined)
|
||||
.flatMap((section) => section.items)
|
||||
.find((item) => item.id === element.dataBinding?.dataValue)?.label ?? "",
|
||||
icon: <ParametersIcon />,
|
||||
}
|
||||
id: element.dataBinding.dataValue as string,
|
||||
label:
|
||||
getLableValueDropdownItems(element.dataBinding?.dataSource as string | undefined)
|
||||
.flatMap((section) => section.items)
|
||||
.find((item) => item.id === element.dataBinding?.dataValue)?.label ?? "",
|
||||
icon: <ParametersIcon />,
|
||||
}
|
||||
: null
|
||||
}
|
||||
onChange={(value) => {
|
||||
|
||||
@@ -24,23 +24,6 @@ export const handleElementDragStart = (
|
||||
}
|
||||
};
|
||||
|
||||
export const handleBlockDragStart = (
|
||||
blockId: string,
|
||||
event: React.MouseEvent,
|
||||
setDraggingBlock: (blockId: string | null) => void,
|
||||
setBlockDragOffset: (offset: Position) => void // Change to specific offset
|
||||
): void => {
|
||||
setDraggingBlock(blockId);
|
||||
const element = event.currentTarget as HTMLElement;
|
||||
const rect = element.getBoundingClientRect();
|
||||
setBlockDragOffset({
|
||||
x: event.clientX - rect.left,
|
||||
y: event.clientY - rect.top,
|
||||
});
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
};
|
||||
|
||||
export const handleElementResizeStart = (
|
||||
elementId: string,
|
||||
event: React.MouseEvent,
|
||||
@@ -81,24 +64,7 @@ export const handleBlockResizeStart = (
|
||||
}
|
||||
};
|
||||
|
||||
export const handleSwapStart = (elementId: string, event: React.MouseEvent, setSwapSource: (source: string | null) => void, setShowSwapUI: (show: boolean) => void): void => {
|
||||
event.stopPropagation();
|
||||
setSwapSource(elementId);
|
||||
setShowSwapUI(true);
|
||||
};
|
||||
|
||||
export const handleSwapTarget = (
|
||||
elementId: string,
|
||||
event: React.MouseEvent,
|
||||
swapSource: string | null,
|
||||
selectedBlock: string | null,
|
||||
swapElements: (blockId: string, elementId1: string, elementId2: string) => void
|
||||
): void => {
|
||||
event.stopPropagation();
|
||||
if (swapSource && swapSource !== elementId && selectedBlock) {
|
||||
swapElements(selectedBlock, swapSource, elementId);
|
||||
}
|
||||
};
|
||||
|
||||
export const handleBlockClick = (
|
||||
blockId: string,
|
||||
@@ -106,7 +72,6 @@ export const handleBlockClick = (
|
||||
editMode: boolean,
|
||||
setSelectedBlock: (blockId: string | null) => void,
|
||||
setSelectedElement: (elementId: string | null) => void,
|
||||
setShowSwapUI: (show: boolean) => void,
|
||||
setShowElementDropdown: (blockId: string | null) => void,
|
||||
showElementDropdown: string | null
|
||||
): void => {
|
||||
@@ -114,7 +79,6 @@ export const handleBlockClick = (
|
||||
if (editMode) {
|
||||
setSelectedBlock(blockId);
|
||||
setSelectedElement(null);
|
||||
setShowSwapUI(false);
|
||||
if (showElementDropdown && showElementDropdown !== blockId) {
|
||||
setShowElementDropdown(blockId);
|
||||
}
|
||||
@@ -128,14 +92,12 @@ export const handleElementClick = (
|
||||
editMode: boolean,
|
||||
setSelectedElement: (elementId: string | null) => void,
|
||||
setSelectedBlock: (blockId: string | null) => void,
|
||||
setShowSwapUI: (show: boolean) => void,
|
||||
setShowElementDropdown: (blockId: string | null) => void
|
||||
): void => {
|
||||
event.stopPropagation();
|
||||
if (editMode) {
|
||||
setSelectedBlock(blockId);
|
||||
setSelectedElement(elementId);
|
||||
// setSelectedBlock(blockId);
|
||||
// setShowSwapUI(false);
|
||||
// setShowElementDropdown(null);
|
||||
setShowElementDropdown(null);
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user