feat: Refactor DashboardEditor and simulation store with peek methods for optimized block and element updates
This commit is contained in:
@@ -19,7 +19,7 @@ interface SimulationDashboardStore {
|
||||
setSelectedElement: (elementId: string | null) => void;
|
||||
|
||||
// Block operations
|
||||
addBlock: () => void;
|
||||
addBlock: (newBlock : Block) => void;
|
||||
removeBlock: (blockId: string) => void;
|
||||
updateBlock: (blockId: string, updates: Partial<Block>) => void;
|
||||
clearBlocks: () => void;
|
||||
@@ -53,6 +53,29 @@ interface SimulationDashboardStore {
|
||||
// Element swapping
|
||||
swapElements: (blockId: string, elementId1: string, elementId2: string) => void;
|
||||
|
||||
// Peek operations (get updated value without setting state)
|
||||
peekAddBlock: () => Block[];
|
||||
peekRemoveBlock: (blockId: string) => Block[];
|
||||
peekUpdateBlock: (blockId: string, updates: Partial<Block>) => Block[];
|
||||
peekUpdateBlockStyle: (blockId: string, newStyle: React.CSSProperties) => Block[];
|
||||
peekUpdateBlockSize: (blockId: string, size: Size) => Block[];
|
||||
peekUpdateBlockPosition: (blockId: string, position: Position) => Block[];
|
||||
peekUpdateBlockPositionType: (blockId: string, positionType: "relative" | "absolute" | "fixed") => Block[];
|
||||
peekUpdateBlockZIndex: (blockId: string, zIndex: number) => Block[];
|
||||
peekAddElement: (blockId: string, type: UIType, graphType?: GraphTypes, dataType?: DataType) => Block[];
|
||||
peekRemoveElement: (blockId: string, elementId: string) => Block[];
|
||||
peekUpdateElement: (blockId: string, elementId: string, updates: Partial<UIElement>) => Block[];
|
||||
peekUpdateElementStyle: (blockId: string, elementId: string, newStyle: ExtendedCSSProperties) => Block[];
|
||||
peekUpdateElementSize: (blockId: string, elementId: string, size: Size) => Block[];
|
||||
peekUpdateElementPosition: (blockId: string, elementId: string, position: Position) => Block[];
|
||||
peekUpdateElementPositionType: (blockId: string, elementId: string, positionType: "relative" | "absolute" | "fixed") => Block[];
|
||||
peekUpdateElementZIndex: (blockId: string, elementId: string, zIndex: number) => Block[];
|
||||
peekUpdateElementData: (blockId: string, elementId: string, updates: Partial<DataBinding>) => Block[];
|
||||
peekUpdateGraphData: (blockId: string, elementId: string, newData: GraphDataPoint[]) => Block[];
|
||||
peekUpdateGraphTitle: (blockId: string, elementId: string, title: string) => Block[];
|
||||
peekUpdateGraphType: (blockId: string, elementId: string, graphType: GraphTypes) => Block[];
|
||||
peekSwapElements: (blockId: string, elementId1: string, elementId2: string) => Block[];
|
||||
|
||||
// Helper functions
|
||||
getBlockById: (blockId: string) => Block | undefined;
|
||||
getElementById: (blockId: string, elementId: string) => UIElement | undefined;
|
||||
@@ -64,6 +87,11 @@ interface SimulationDashboardStore {
|
||||
|
||||
const subscribers = new Set<() => void>();
|
||||
|
||||
// Helper function to deep clone blocks
|
||||
const cloneBlocks = (blocks: Block[]): Block[] => {
|
||||
return JSON.parse(JSON.stringify(blocks));
|
||||
};
|
||||
|
||||
export const createSimulationDashboardStore = () => {
|
||||
return create<SimulationDashboardStore>()(
|
||||
immer((set, get) => ({
|
||||
@@ -96,7 +124,6 @@ export const createSimulationDashboardStore = () => {
|
||||
setSelectedBlock: (blockId) => {
|
||||
set((state) => {
|
||||
state.selectedBlock = blockId;
|
||||
// Clear element selection when selecting a block
|
||||
if (blockId) {
|
||||
state.selectedElement = null;
|
||||
}
|
||||
@@ -110,26 +137,8 @@ export const createSimulationDashboardStore = () => {
|
||||
},
|
||||
|
||||
// Block operations
|
||||
addBlock: () => {
|
||||
addBlock: (newBlock) => {
|
||||
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);
|
||||
});
|
||||
},
|
||||
@@ -292,7 +301,6 @@ export const createSimulationDashboardStore = () => {
|
||||
commonValue: "metric-1",
|
||||
};
|
||||
} else {
|
||||
// Default to single-machine
|
||||
newElement = {
|
||||
...baseGraphProps,
|
||||
dataType: "single-machine" as const,
|
||||
@@ -334,7 +342,7 @@ export const createSimulationDashboardStore = () => {
|
||||
break;
|
||||
|
||||
default:
|
||||
return; // Should not happen
|
||||
return;
|
||||
}
|
||||
|
||||
block.elements.push(newElement);
|
||||
@@ -471,7 +479,6 @@ export const createSimulationDashboardStore = () => {
|
||||
if (element && element.type === "graph") {
|
||||
element.graphType = graphType;
|
||||
element.graphTitle = `${graphType.charAt(0).toUpperCase() + graphType.slice(1)} Chart`;
|
||||
|
||||
element.graphData = defaultGraphData;
|
||||
}
|
||||
}
|
||||
@@ -498,6 +505,367 @@ export const createSimulationDashboardStore = () => {
|
||||
});
|
||||
},
|
||||
|
||||
// Peek operations - return updated blocks without setting state
|
||||
peekAddBlock: () => {
|
||||
const blocks = cloneBlocks(get().blocks);
|
||||
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",
|
||||
};
|
||||
blocks.push(newBlock);
|
||||
return blocks;
|
||||
},
|
||||
|
||||
peekRemoveBlock: (blockId) => {
|
||||
const blocks = cloneBlocks(get().blocks);
|
||||
return blocks.filter((block) => block.blockUuid !== blockId);
|
||||
},
|
||||
|
||||
peekUpdateBlock: (blockId, updates) => {
|
||||
const blocks = cloneBlocks(get().blocks);
|
||||
const block = blocks.find((b) => b.blockUuid === blockId);
|
||||
if (block) {
|
||||
Object.assign(block, updates);
|
||||
}
|
||||
return blocks;
|
||||
},
|
||||
|
||||
peekUpdateBlockStyle: (blockId, newStyle) => {
|
||||
const blocks = cloneBlocks(get().blocks);
|
||||
const block = blocks.find((b) => b.blockUuid === blockId);
|
||||
if (block) {
|
||||
block.style = { ...block.style, ...newStyle };
|
||||
}
|
||||
return blocks;
|
||||
},
|
||||
|
||||
peekUpdateBlockSize: (blockId, size) => {
|
||||
const blocks = cloneBlocks(get().blocks);
|
||||
const block = blocks.find((b) => b.blockUuid === blockId);
|
||||
if (block) {
|
||||
block.size = size;
|
||||
}
|
||||
return blocks;
|
||||
},
|
||||
|
||||
peekUpdateBlockPosition: (blockId, position) => {
|
||||
const blocks = cloneBlocks(get().blocks);
|
||||
const block = blocks.find((b) => b.blockUuid === blockId);
|
||||
if (block) {
|
||||
block.position = position;
|
||||
}
|
||||
return blocks;
|
||||
},
|
||||
|
||||
peekUpdateBlockPositionType: (blockId, positionType) => {
|
||||
const blocks = cloneBlocks(get().blocks);
|
||||
const block = blocks.find((b) => b.blockUuid === blockId);
|
||||
if (block) {
|
||||
block.positionType = positionType;
|
||||
}
|
||||
return blocks;
|
||||
},
|
||||
|
||||
peekUpdateBlockZIndex: (blockId, zIndex) => {
|
||||
const blocks = cloneBlocks(get().blocks);
|
||||
const block = blocks.find((b) => b.blockUuid === blockId);
|
||||
if (block) {
|
||||
block.zIndex = zIndex;
|
||||
}
|
||||
return blocks;
|
||||
},
|
||||
|
||||
peekAddElement: (blockId, type, graphType, dataType?: DataType) => {
|
||||
const blocks = cloneBlocks(get().blocks);
|
||||
const block = blocks.find((b) => b.blockUuid === blockId);
|
||||
if (!block) return blocks;
|
||||
|
||||
let newElement: UIElement;
|
||||
|
||||
const commonProps = {
|
||||
elementUuid: MathUtils.generateUUID(),
|
||||
positionType: "relative" as const,
|
||||
position: { x: 0, y: 0 },
|
||||
zIndex: 1,
|
||||
data: {
|
||||
key: MathUtils.generateUUID(),
|
||||
dataSource: "static" as const,
|
||||
staticValue: "",
|
||||
label: undefined,
|
||||
},
|
||||
};
|
||||
|
||||
switch (type) {
|
||||
case "label-value":
|
||||
newElement = {
|
||||
...commonProps,
|
||||
type: "label-value",
|
||||
title: "Label Value",
|
||||
dataSource: "machine-1",
|
||||
dataValue: "metric-1",
|
||||
style: {
|
||||
color: "#ffffff",
|
||||
fontSize: 14,
|
||||
textAlign: "left" as const,
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
gap: "4px",
|
||||
alignItems: "flex-start",
|
||||
justifyContent: "center",
|
||||
labelColor: "#ffffff",
|
||||
valueColor: "#ffffff",
|
||||
},
|
||||
data: {
|
||||
...commonProps.data,
|
||||
staticValue: "Value",
|
||||
label: "Label",
|
||||
},
|
||||
size: { width: 200, height: 60 },
|
||||
};
|
||||
break;
|
||||
|
||||
case "graph":
|
||||
const baseGraphProps = {
|
||||
...commonProps,
|
||||
type: "graph" as const,
|
||||
graphType: graphType,
|
||||
graphTitle: "Graph Title",
|
||||
style: {
|
||||
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",
|
||||
},
|
||||
graphData: defaultGraphData,
|
||||
size: { width: 400, height: 200 },
|
||||
};
|
||||
|
||||
if (dataType === "multiple-machine") {
|
||||
newElement = {
|
||||
...baseGraphProps,
|
||||
dataType: "multiple-machine" as const,
|
||||
title: "Multi Machine Chart",
|
||||
dataSource: ["machine-1", "machine-2", "machine-3"],
|
||||
commonValue: "metric-1",
|
||||
};
|
||||
} else {
|
||||
newElement = {
|
||||
...baseGraphProps,
|
||||
dataType: "single-machine" as const,
|
||||
title: "Single Machine Chart",
|
||||
dataSource: "machine-1",
|
||||
dataValue: ["metric-1", "metric-2", "metric-3"],
|
||||
};
|
||||
}
|
||||
break;
|
||||
|
||||
case "text":
|
||||
newElement = {
|
||||
...commonProps,
|
||||
type: "text",
|
||||
style: {
|
||||
color: "#ffffff",
|
||||
fontSize: 14,
|
||||
textAlign: "left" as const,
|
||||
},
|
||||
data: {
|
||||
...commonProps.data,
|
||||
staticValue: "Text",
|
||||
},
|
||||
size: { width: 200, height: 40 },
|
||||
};
|
||||
break;
|
||||
|
||||
case "icon":
|
||||
newElement = {
|
||||
...commonProps,
|
||||
type: "icon",
|
||||
style: {
|
||||
color: "#ffffff",
|
||||
fontSize: 14,
|
||||
textAlign: "center" as const,
|
||||
},
|
||||
size: { width: 40, height: 40 },
|
||||
};
|
||||
break;
|
||||
|
||||
default:
|
||||
return blocks;
|
||||
}
|
||||
|
||||
block.elements.push(newElement);
|
||||
return blocks;
|
||||
},
|
||||
|
||||
peekRemoveElement: (blockId, elementId) => {
|
||||
const blocks = cloneBlocks(get().blocks);
|
||||
const block = blocks.find((b) => b.blockUuid === blockId);
|
||||
if (block) {
|
||||
block.elements = block.elements.filter((el) => el.elementUuid !== elementId);
|
||||
}
|
||||
return blocks;
|
||||
},
|
||||
|
||||
peekUpdateElement: (blockId, elementId, updates) => {
|
||||
const blocks = cloneBlocks(get().blocks);
|
||||
const block = blocks.find((b) => b.blockUuid === blockId);
|
||||
if (block) {
|
||||
const element = block.elements.find((el) => el.elementUuid === elementId);
|
||||
if (element) {
|
||||
Object.assign(element, updates);
|
||||
}
|
||||
}
|
||||
return blocks;
|
||||
},
|
||||
|
||||
peekUpdateElementStyle: (blockId, elementId, newStyle) => {
|
||||
const blocks = cloneBlocks(get().blocks);
|
||||
const block = blocks.find((b) => b.blockUuid === blockId);
|
||||
if (block) {
|
||||
const element = block.elements.find((el) => el.elementUuid === elementId);
|
||||
if (element) {
|
||||
element.style = { ...element.style, ...newStyle };
|
||||
}
|
||||
}
|
||||
return blocks;
|
||||
},
|
||||
|
||||
peekUpdateElementSize: (blockId, elementId, size) => {
|
||||
const blocks = cloneBlocks(get().blocks);
|
||||
const block = blocks.find((b) => b.blockUuid === blockId);
|
||||
if (block) {
|
||||
const element = block.elements.find((el) => el.elementUuid === elementId);
|
||||
if (element) {
|
||||
element.size = size;
|
||||
}
|
||||
}
|
||||
return blocks;
|
||||
},
|
||||
|
||||
peekUpdateElementPosition: (blockId, elementId, position) => {
|
||||
const blocks = cloneBlocks(get().blocks);
|
||||
const block = blocks.find((b) => b.blockUuid === blockId);
|
||||
if (block) {
|
||||
const element = block.elements.find((el) => el.elementUuid === elementId);
|
||||
if (element) {
|
||||
element.position = position;
|
||||
}
|
||||
}
|
||||
return blocks;
|
||||
},
|
||||
|
||||
peekUpdateElementPositionType: (blockId, elementId, positionType) => {
|
||||
const blocks = cloneBlocks(get().blocks);
|
||||
const block = blocks.find((b) => b.blockUuid === blockId);
|
||||
if (block) {
|
||||
const element = block.elements.find((el) => el.elementUuid === elementId);
|
||||
if (element) {
|
||||
element.positionType = positionType;
|
||||
}
|
||||
}
|
||||
return blocks;
|
||||
},
|
||||
|
||||
peekUpdateElementZIndex: (blockId, elementId, zIndex) => {
|
||||
const blocks = cloneBlocks(get().blocks);
|
||||
const block = blocks.find((b) => b.blockUuid === blockId);
|
||||
if (block) {
|
||||
const element = block.elements.find((el) => el.elementUuid === elementId);
|
||||
if (element) {
|
||||
element.zIndex = zIndex;
|
||||
}
|
||||
}
|
||||
return blocks;
|
||||
},
|
||||
|
||||
peekUpdateElementData: (blockId, elementId, updates) => {
|
||||
const blocks = cloneBlocks(get().blocks);
|
||||
const block = 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 };
|
||||
}
|
||||
}
|
||||
return blocks;
|
||||
},
|
||||
|
||||
peekUpdateGraphData: (blockId, elementId, newData) => {
|
||||
const blocks = cloneBlocks(get().blocks);
|
||||
const block = blocks.find((b) => b.blockUuid === blockId);
|
||||
if (block) {
|
||||
const element = block.elements.find((el) => el.elementUuid === elementId);
|
||||
if (element) {
|
||||
element.graphData = newData;
|
||||
}
|
||||
}
|
||||
return blocks;
|
||||
},
|
||||
|
||||
peekUpdateGraphTitle: (blockId, elementId, title) => {
|
||||
const blocks = cloneBlocks(get().blocks);
|
||||
const block = blocks.find((b) => b.blockUuid === blockId);
|
||||
if (block) {
|
||||
const element = block.elements.find((el) => el.elementUuid === elementId);
|
||||
if (element) {
|
||||
element.graphTitle = title;
|
||||
}
|
||||
}
|
||||
return blocks;
|
||||
},
|
||||
|
||||
peekUpdateGraphType: (blockId, elementId, graphType) => {
|
||||
const blocks = cloneBlocks(get().blocks);
|
||||
const block = 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.graphData = defaultGraphData;
|
||||
}
|
||||
}
|
||||
return blocks;
|
||||
},
|
||||
|
||||
peekSwapElements: (blockId, elementId1, elementId2) => {
|
||||
const blocks = cloneBlocks(get().blocks);
|
||||
const block = 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;
|
||||
});
|
||||
}
|
||||
return blocks;
|
||||
},
|
||||
|
||||
// Helper functions
|
||||
getBlockById: (blockId) => {
|
||||
return get().blocks.find((b) => b.blockUuid === blockId);
|
||||
|
||||
Reference in New Issue
Block a user