Moved the simulationDashBoard from src to components
This commit is contained in:
309
app/src/components/SimulationDashboard/DashboardEditor.tsx
Normal file
309
app/src/components/SimulationDashboard/DashboardEditor.tsx
Normal file
@@ -0,0 +1,309 @@
|
||||
import React, { useState, useRef, useEffect } from "react";
|
||||
import { dataModelManager } from "./data/dataModel";
|
||||
import ControlPanel from "./ControlPanel";
|
||||
import SwapModal from "./SwapModal";
|
||||
import DataModelPanel from "./components/models/DataModelPanel";
|
||||
|
||||
import { useSceneContext } from "../../modules/scene/sceneContext";
|
||||
import { calculateMinBlockSize } from "./functions/block/calculateMinBlockSize";
|
||||
import { handleElementDragStart, handleElementResizeStart, handleBlockResizeStart, handleSwapStart, handleSwapTarget, handleBlockClick, handleElementClick } from "./functions/eventHandlers";
|
||||
import BlockGrid from "./components/block/BlockGrid";
|
||||
import ElementDropdown from "./components/element/ElementDropdown";
|
||||
import BlockEditor from "./components/block/BlockEditor";
|
||||
import ElementEditor from "./components/element/ElementEditor";
|
||||
import { handleBlockDragStart } from "./functions/block/handleBlockDragStart";
|
||||
|
||||
const DashboardEditor: React.FC = () => {
|
||||
const { simulationDashBoardStore } = useSceneContext();
|
||||
const {
|
||||
blocks,
|
||||
setBlocks,
|
||||
updateBlockPosition,
|
||||
updateElementPosition,
|
||||
updateBlockSize,
|
||||
updateElementSize,
|
||||
addBlock,
|
||||
addElement,
|
||||
updateBlockStyle,
|
||||
updateBlockPositionType,
|
||||
updateBlockZIndex,
|
||||
updateElementStyle,
|
||||
updateElementPositionType,
|
||||
updateElementZIndex,
|
||||
updateElementData,
|
||||
updateGraphData,
|
||||
updateGraphTitle,
|
||||
swapElements,
|
||||
} = simulationDashBoardStore();
|
||||
const [selectedBlock, setSelectedBlock] = useState<string | null>(null);
|
||||
const [selectedElement, setSelectedElement] = useState<string | null>(null);
|
||||
const [editMode, setEditMode] = useState(false);
|
||||
const [draggingElement, setDraggingElement] = useState<string | null>(null);
|
||||
const [resizingElement, setResizingElement] = useState<string | null>(null);
|
||||
const [resizingBlock, setResizingBlock] = useState<string | null>(null);
|
||||
const [resizeStart, setResizeStart] = useState<{
|
||||
x: number;
|
||||
y: number;
|
||||
width: number;
|
||||
height: number;
|
||||
} | null>(null);
|
||||
const [showSwapUI, setShowSwapUI] = useState(false);
|
||||
const [swapSource, setSwapSource] = useState<string | null>(null);
|
||||
const [dataModel, setDataModel] = useState<DataModel>(dataModelManager.getDataSnapshot());
|
||||
const [showDataModelPanel, setShowDataModelPanel] = useState(false);
|
||||
const [showElementDropdown, setShowElementDropdown] = useState<string | null>(null);
|
||||
const [dropDownPosition, setDropDownPosition] = useState({ top: 0, left: 0 });
|
||||
const [draggingBlock, setDraggingBlock] = useState<string | null>(null);
|
||||
const [elementDragOffset, setElementDragOffset] = useState<Position>({ x: 0, y: 0 });
|
||||
const [blockDragOffset, setBlockDragOffset] = useState<Position>({ x: 0, y: 0 });
|
||||
|
||||
const editorRef = useRef<HTMLDivElement>(null);
|
||||
const blockEditorRef = useRef<HTMLDivElement>(null);
|
||||
const elementEditorRef = useRef<HTMLDivElement>(null);
|
||||
const dropdownRef = useRef<HTMLDivElement>(null);
|
||||
const blockRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const currentBlock = blocks.find((b) => b.id === selectedBlock);
|
||||
const currentElement = currentBlock?.elements.find((el) => el.id === selectedElement);
|
||||
|
||||
// Subscribe to data model changes
|
||||
useEffect(() => {
|
||||
const handleDataChange = (): void => {
|
||||
setDataModel(dataModelManager.getDataSnapshot());
|
||||
};
|
||||
|
||||
const keys = dataModelManager.getAvailableKeys();
|
||||
const subscriptions: Array<[string, () => void]> = [];
|
||||
|
||||
keys.forEach((key) => {
|
||||
const callback = () => handleDataChange();
|
||||
dataModelManager.subscribe(key, callback);
|
||||
subscriptions.push([key, callback]);
|
||||
});
|
||||
|
||||
const interval = setInterval(() => {
|
||||
const currentKeys = dataModelManager.getAvailableKeys();
|
||||
const newKeys = currentKeys.filter((key) => !keys.includes(key));
|
||||
|
||||
newKeys.forEach((key) => {
|
||||
const callback = () => handleDataChange();
|
||||
dataModelManager.subscribe(key, callback);
|
||||
subscriptions.push([key, callback]);
|
||||
});
|
||||
}, 1000);
|
||||
|
||||
return () => {
|
||||
subscriptions.forEach(([key, callback]) => {
|
||||
dataModelManager.unsubscribe(key, callback);
|
||||
});
|
||||
clearInterval(interval);
|
||||
};
|
||||
}, []);
|
||||
|
||||
// Click outside handler
|
||||
useEffect(() => {
|
||||
const handleClickOutside = (event: MouseEvent): void => {
|
||||
const target = event.target as Node;
|
||||
|
||||
const isInsideBlockEditor = blockEditorRef.current?.contains(target);
|
||||
const isInsideElementEditor = elementEditorRef.current?.contains(target);
|
||||
const isInsideDropdown = dropdownRef.current?.contains(target);
|
||||
const isInsideEditor = editorRef.current?.contains(target);
|
||||
|
||||
if (!isInsideEditor) {
|
||||
setSelectedBlock(null);
|
||||
setSelectedElement(null);
|
||||
setShowSwapUI(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (isInsideEditor && !isInsideBlockEditor && !isInsideElementEditor && !isInsideDropdown) {
|
||||
const clickedElement = event.target as HTMLElement;
|
||||
const isBlock = clickedElement.closest("[data-block-id]");
|
||||
const isElement = clickedElement.closest("[data-element-id]");
|
||||
const isButton = clickedElement.closest("button");
|
||||
const isResizeHandle = clickedElement.closest(".resize-handle");
|
||||
|
||||
if (!isBlock && !isElement && !isButton && !isResizeHandle) {
|
||||
setSelectedBlock(null);
|
||||
setSelectedElement(null);
|
||||
setShowSwapUI(false);
|
||||
setShowElementDropdown(null);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener("mousedown", handleClickOutside);
|
||||
return () => {
|
||||
document.removeEventListener("mousedown", handleClickOutside);
|
||||
};
|
||||
}, [selectedBlock, selectedElement]);
|
||||
|
||||
// Drag and drop handler
|
||||
useEffect(() => {
|
||||
const handleMouseMove = (e: MouseEvent): void => {
|
||||
// Element dragging - use elementDragOffset
|
||||
if (draggingElement && selectedBlock && currentElement?.positionType === "absolute") {
|
||||
const blockElement = document.querySelector(`[data-block-id="${selectedBlock}"]`) as HTMLElement;
|
||||
if (blockElement) {
|
||||
const blockRect = blockElement.getBoundingClientRect();
|
||||
const newX = e.clientX - blockRect.left - elementDragOffset.x; // Use elementDragOffset
|
||||
const newY = e.clientY - blockRect.top - elementDragOffset.y; // Use elementDragOffset
|
||||
|
||||
updateElementPosition(selectedBlock, draggingElement, {
|
||||
x: Math.max(0, Math.min(blockRect.width - 50, newX)),
|
||||
y: Math.max(0, Math.min(blockRect.height - 30, newY)),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Block dragging - use blockDragOffset
|
||||
if (draggingBlock && currentBlock?.positionType && (currentBlock.positionType === "absolute" || currentBlock.positionType === "fixed")) {
|
||||
const editorElement = editorRef.current;
|
||||
if (editorElement) {
|
||||
const editorRect = editorElement.getBoundingClientRect();
|
||||
const newX = e.clientX - editorRect.left - blockDragOffset.x; // Use blockDragOffset
|
||||
const newY = e.clientY - editorRect.top - blockDragOffset.y; // Use blockDragOffset
|
||||
|
||||
updateBlockPosition(draggingBlock, {
|
||||
x: Math.max(0, Math.min(editorRect.width - (currentBlock.size?.width || 400), newX)),
|
||||
y: Math.max(0, Math.min(editorRect.height - (currentBlock.size?.height || 300), newY)),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if ((resizingElement || resizingBlock) && resizeStart) {
|
||||
if (resizingElement && selectedBlock) {
|
||||
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);
|
||||
|
||||
updateElementSize(selectedBlock, resizingElement, {
|
||||
width: newWidth,
|
||||
height: newHeight,
|
||||
});
|
||||
} else if (resizingBlock) {
|
||||
const deltaX = e.clientX - resizeStart.x;
|
||||
const deltaY = e.clientY - resizeStart.y;
|
||||
|
||||
const currentBlock = blocks.find((b) => b.id === 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);
|
||||
|
||||
updateBlockSize(resizingBlock, {
|
||||
width: newWidth,
|
||||
height: newHeight,
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleMouseUp = (): void => {
|
||||
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]);
|
||||
|
||||
// Update dropdown position when showElementDropdown changes
|
||||
useEffect(() => {
|
||||
if (showElementDropdown && blockRef.current) {
|
||||
const rect = blockRef.current.getBoundingClientRect();
|
||||
setDropDownPosition({
|
||||
top: rect.bottom + window.scrollY,
|
||||
left: rect.left + window.scrollX,
|
||||
});
|
||||
}
|
||||
}, [showElementDropdown]);
|
||||
|
||||
return (
|
||||
<div ref={editorRef} className="dashboard-editor">
|
||||
<ControlPanel editMode={editMode} setEditMode={setEditMode} addBlock={() => addBlock()} showDataModelPanel={showDataModelPanel} setShowDataModelPanel={setShowDataModelPanel} />
|
||||
|
||||
<BlockGrid
|
||||
blocks={blocks}
|
||||
editMode={editMode}
|
||||
selectedBlock={selectedBlock}
|
||||
selectedElement={selectedElement}
|
||||
showSwapUI={showSwapUI}
|
||||
swapSource={swapSource}
|
||||
calculateMinBlockSize={calculateMinBlockSize}
|
||||
handleBlockClick={(blockId, event) => 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}
|
||||
/>
|
||||
|
||||
<ElementDropdown
|
||||
showElementDropdown={showElementDropdown}
|
||||
dropDownPosition={dropDownPosition}
|
||||
addElement={(blockId, type, graphType) => addElement(blockId, type as UIType, graphType as GraphTypes)}
|
||||
dropdownRef={dropdownRef}
|
||||
/>
|
||||
|
||||
{showDataModelPanel && editMode && <DataModelPanel dataModel={dataModel} dataModelManager={dataModelManager} />}
|
||||
|
||||
{selectedBlock && editMode && !selectedElement && currentBlock && (
|
||||
<BlockEditor
|
||||
blockEditorRef={blockEditorRef}
|
||||
currentBlock={currentBlock}
|
||||
selectedBlock={selectedBlock}
|
||||
updateBlockStyle={(blockId, style) => 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 && (
|
||||
<ElementEditor
|
||||
elementEditorRef={elementEditorRef}
|
||||
currentElement={currentElement}
|
||||
selectedBlock={selectedBlock}
|
||||
selectedElement={selectedElement}
|
||||
blocks={blocks}
|
||||
setBlocks={setBlocks}
|
||||
updateElementStyle={(blockId, elementId, style) => 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)}
|
||||
setSwapSource={setSwapSource}
|
||||
setShowSwapUI={setShowSwapUI}
|
||||
dataModelManager={dataModelManager}
|
||||
/>
|
||||
)}
|
||||
|
||||
{showSwapUI && <SwapModal setShowSwapUI={setShowSwapUI} setSwapSource={setSwapSource} />}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DashboardEditor;
|
||||
Reference in New Issue
Block a user