Files
Dwinzo_Demo/app/src/store/simulation/useSimulationDashBoardStore.ts

988 lines
42 KiB
TypeScript
Raw Normal View History

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";
interface SimulationDashboardStore {
blocks: Block[];
selectedBlock: string | null;
selectedElement: string | null;
// Subscription management
subscribe: (callback: () => void) => () => void;
_notifySubscribers: () => void;
saveBlocks: () => void;
// Selection and hover management
setSelectedBlock: (blockId: string | null) => void;
setSelectedElement: (elementId: string | null) => void;
// Block operations
addBlock: (newBlock: Block) => void;
removeBlock: (blockId: string) => void;
updateBlock: (blockId: string, updates: Partial<Block>) => 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;
// Element operations
addElement: (blockId: string, type: UIType, graphType?: GraphTypes) => void;
removeElement: (blockId: string, elementId: string) => void;
updateElement: (blockId: string, elementId: string, updates: Partial<UIElement>) => 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<DataBinding>) => 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 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[];
peekUpdateDataType: (blockId: string, elementId: string, dataType: "single-machine" | "multiple-machine") => Block[];
peekUpdateCommonValue: (blockId: string, elementId: string, commonValue: string) => Block[];
peekUpdateDataValue: (blockId: string, elementId: string, dataValue: string | string[]) => Block[];
peekUpdateDataSource: (blockId: string, elementId: string, dataSource: string | string[]) => Block[];
peekSwapElements: (blockId: string, elementId1: string, elementId2: string) => Block[];
// 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>();
// 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) => ({
blocks: [],
selectedBlock: null,
selectedElement: null,
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();
},
// Selection and hover management
setSelectedBlock: (blockId) => {
set((state) => {
state.selectedBlock = blockId;
if (blockId) {
state.selectedElement = null;
}
});
},
setSelectedElement: (elementId) => {
set((state) => {
state.selectedElement = elementId;
});
},
// Block operations
addBlock: (newBlock) => {
set((state) => {
state.blocks.push(newBlock);
});
},
removeBlock: (blockId) => {
set((state) => {
state.blocks = state.blocks.filter((block) => block.blockUuid !== blockId);
if (state.selectedBlock === blockId) {
state.selectedBlock = 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.selectedBlock = null;
state.selectedElement = 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, dataType?: DataType) => {
set((state) => {
const block = state.blocks.find((b) => b.blockUuid === blockId);
if (!block) return;
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: "",
dataValue: "",
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: [],
commonValue: "",
};
} else {
newElement = {
...baseGraphProps,
dataType: "single-machine" as const,
title: "Single Machine Chart",
dataSource: "",
dataValue: [],
};
}
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;
}
block.elements.push(newElement);
});
},
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.selectedElement === elementId) {
state.selectedElement = 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);
}
}
});
},
// 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 };
}
}
});
},
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;
}
}
});
},
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;
}
}
});
},
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;
}
}
});
},
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;
}
}
});
},
// 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 };
}
}
});
},
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;
}
}
});
},
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;
}
}
});
},
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.graphData = defaultGraphData;
}
}
});
},
// 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;
});
}
});
},
// 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: "",
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: [],
commonValue: "",
};
} else {
newElement = {
...baseGraphProps,
dataType: "single-machine" as const,
title: "Single Machine Chart",
dataSource: "",
dataValue: [],
};
}
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;
},
peekUpdateDataType: (blockId, elementId, dataType) => {
const blocks = cloneBlocks(get().blocks);
const block = blocks.find((b) => b.blockUuid === blockId);
if (!block) return blocks;
const index = block.elements.findIndex((el) => el.elementUuid === elementId);
if (index === -1) return blocks;
const element = block.elements[index];
if (element.type !== "graph" || element.dataType === dataType) return blocks;
let newElement: UIElement;
if (dataType === "single-machine") {
const { commonValue, ...rest } = element as Extract<UIElement, { type: "graph"; dataType: "multiple-machine" }>;
newElement = {
...rest,
dataType: "single-machine",
dataSource: "",
dataValue: [],
};
} else {
const { dataValue, ...rest } = element as Extract<UIElement, { type: "graph"; dataType: "single-machine" }>;
newElement = {
...rest,
dataType: "multiple-machine",
dataSource: [],
commonValue: "",
};
}
block.elements[index] = newElement;
return blocks;
},
peekUpdateCommonValue: (blockId, elementId, commonValue) => {
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.dataType === "multiple-machine") {
element.commonValue = commonValue;
}
}
return blocks;
},
peekUpdateDataValue: (blockId, elementId, dataValue) => {
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.dataType === "single-machine") {
element.dataValue = dataValue as string[];
} else if (element && element.type === "label-value") {
element.dataValue = dataValue as string;
}
}
return blocks;
},
peekUpdateDataSource: (blockId, elementId, dataSource) => {
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?.type === "label-value") {
element.dataValue = "";
}
if (element) {
element.dataSource = dataSource;
}
}
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);
},
getElementById: (blockId, elementId) => {
const block = get().blocks.find((b) => b.blockUuid === blockId);
return block?.elements.find((el) => el.elementUuid === elementId);
},
getSelectedBlock: () => {
const { selectedBlock, blocks } = get();
return selectedBlock ? blocks.find((b) => b.blockUuid === selectedBlock) : undefined;
},
getSelectedElement: () => {
const { selectedElement, selectedBlock, blocks } = get();
if (!selectedElement || !selectedBlock) return undefined;
const block = blocks.find((b) => b.blockUuid === selectedBlock);
return block?.elements.find((el) => el.elementUuid === selectedElement);
},
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;
},
}))
);
};
export type SimulationDashboardStoreType = ReturnType<typeof createSimulationDashboardStore>;