From d014b3f87ee4a62016626b07bfb2dba39c2b22af Mon Sep 17 00:00:00 2001 From: Jerald-Golden-B Date: Wed, 17 Dec 2025 17:02:45 +0530 Subject: [PATCH] feat: Refactor ElementEditor and DataSourceSelector for improved data handling and UI integration --- .../SimulationDashboard/DashboardEditor.tsx | 2 +- .../components/element/ElementDesign.tsx | 262 +++++++++++++ .../components/element/ElementEditor.tsx | 369 ++++++------------ .../ui/inputs/DataSourceSelector.tsx | 28 +- .../ui/inputs/RegularDropDownID.tsx | 4 +- 5 files changed, 381 insertions(+), 284 deletions(-) create mode 100644 app/src/components/SimulationDashboard/components/element/ElementDesign.tsx diff --git a/app/src/components/SimulationDashboard/DashboardEditor.tsx b/app/src/components/SimulationDashboard/DashboardEditor.tsx index f0e79f1..c884667 100644 --- a/app/src/components/SimulationDashboard/DashboardEditor.tsx +++ b/app/src/components/SimulationDashboard/DashboardEditor.tsx @@ -225,7 +225,7 @@ const DashboardEditor: React.FC = () => { } }; - document.addEventListener("mousedown", handleClickOutside); + // document.addEventListener("mousedown", handleClickOutside); return () => { document.removeEventListener("mousedown", handleClickOutside); }; diff --git a/app/src/components/SimulationDashboard/components/element/ElementDesign.tsx b/app/src/components/SimulationDashboard/components/element/ElementDesign.tsx new file mode 100644 index 0000000..e29a164 --- /dev/null +++ b/app/src/components/SimulationDashboard/components/element/ElementDesign.tsx @@ -0,0 +1,262 @@ +import React, { RefObject } from "react"; +import DataSourceSelector from "../../../ui/inputs/DataSourceSelector"; +import RenameInput from "../../../ui/inputs/RenameInput"; +import { AlignJustifyIcon, AlignLeftIcon, AlignRightIcon, FlexColumnIcon, FlexRowIcon, FlexRowReverseIcon } from "../../../icons/ExportCommonIcons"; +import InputWithDropDown from "../../../ui/inputs/InputWithDropDown"; +import InputRange from "../../../ui/inputs/InputRange"; +import { getAlphaFromRgba, hexToRgba, rgbaToHex } from "../../functions/helpers/colorHandlers"; +import { ExtendedCSSProperties, UIElement } from "../../../../types/exportedTypes"; +import { getCurrentElementStyleValue } from "../../functions/helpers/getCurrentElementStyleValue"; + +interface ElementDesignProps { + element: UIElement | undefined; + elementEditorRef: RefObject; + currentElement: UIElement; + selectedBlock: string; + selectedElement: string; + updateElementStyle: (blockId: string, elementId: string, style: ExtendedCSSProperties) => void; + updateElementSize: (blockId: string, elementId: string, size: { width: number; height: number }) => void; + updateElementPosition: (blockId: string, elementId: string, position: { x: number; y: number }) => void; + updateElementPositionType: (blockId: string, elementId: string, positionType: "relative" | "absolute" | "fixed") => void; + updateElementZIndex: (blockId: string, elementId: string, zIndex: number) => void; + 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, type: GraphTypes) => void; + updateDataType: (blockId: string, elementId: string, dataType: "single-machine" | "multiple-machine") => void; + updateCommonValue: (blockId: string, elementId: string, commonValue: string) => void; + 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 = ({ + element, + elementEditorRef, + currentElement, + selectedBlock, + selectedElement, + updateElementStyle, + updateElementSize, + updateElementPosition, + updateElementPositionType, + updateElementZIndex, + updateElementData, + updateGraphData, + updateGraphTitle, + updateGraphType, + updateDataType, + updateCommonValue, + updateDataValue, + updateDataSource, + handleRemoveElement, + setSwapSource, + setShowSwapUI, +}) => { + return ( +
+ {element?.type === "graph" && ( +
+ { + updateGraphType(selectedBlock, selectedElement, newValue.id as GraphTypes); + }} + showEyeDropper={false} + /> +
+ )} + +
+
Position
+
+ {["relative", "absolute"].map((position) => ( +
updateElementPositionType(selectedBlock, selectedElement, position as "relative" | "absolute" | "fixed")} + > + {position.charAt(0).toUpperCase() + position.slice(1)} +
+ ))} +
+
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+ +
+
+
updateElementStyle(selectedBlock, selectedElement, { textAlign: "right" })} + > + +
+
updateElementStyle(selectedBlock, selectedElement, { textAlign: "justify" })} + > + +
+
updateElementStyle(selectedBlock, selectedElement, { textAlign: "left" })} + > + +
+
+
+
updateElementStyle(selectedBlock, selectedElement, { flexDirection: "row" })} + > + +
+
updateElementStyle(selectedBlock, selectedElement, { flexDirection: "column" })} + > + +
+
updateElementStyle(selectedBlock, selectedElement, { flexDirection: "row-reverse" })} + > + +
+
updateElementStyle(selectedBlock, selectedElement, { flexDirection: "column-reverse" })} + > + +
+
+
+
+ updateElementZIndex(selectedBlock, selectedElement, Number(newValue))} + /> + + updateElementStyle(selectedBlock, selectedElement, { backdropFilter: `blur(${Number(value)}px)` })} + /> + + { + updateElementStyle(selectedBlock, selectedElement, { + borderRadius: Number(newValue), + }); + }} + /> +
+
+ +
+
Appearance
+
+ { + updateElementPosition(selectedBlock, selectedElement, { + ...(currentElement.position || { x: 0, y: 0 }), + x: Number(newValue), + }); + }} + /> + { + updateElementPosition(selectedBlock, selectedElement, { + ...(currentElement.position || { x: 0, y: 0 }), + y: Number(newValue), + }); + }} + /> + { + updateElementSize(selectedBlock, selectedElement, { + ...(currentElement.size || { width: 0, height: 0 }), + width: Number(newValue), + }); + }} + /> + { + updateElementSize(selectedBlock, selectedElement, { + ...(currentElement.size || { width: 0, height: 0 }), + height: Number(newValue), + }); + }} + /> +
+
+ +
+
Element Color
+
+
Color
+
+ { + const currentBg = getCurrentElementStyleValue(currentElement, "backgroundColor") || "rgba(50,50,50,1)"; + const currentAlpha = getAlphaFromRgba(currentBg); + const newBg = hexToRgba(e.target.value, currentAlpha); + updateElementStyle(selectedBlock, selectedElement, { backgroundColor: newBg }); + }} + /> + +
{rgbaToHex(getCurrentElementStyleValue(currentElement, "backgroundColor") || "rgba(50,50,50,1)")}
+
+
+
+
Swap with Another Element
+
+ ); +}; + +export default ElementDesign; diff --git a/app/src/components/SimulationDashboard/components/element/ElementEditor.tsx b/app/src/components/SimulationDashboard/components/element/ElementEditor.tsx index 86e7e63..9f7ef1b 100644 --- a/app/src/components/SimulationDashboard/components/element/ElementEditor.tsx +++ b/app/src/components/SimulationDashboard/components/element/ElementEditor.tsx @@ -1,4 +1,4 @@ -import { useCallback, useState, type RefObject } from "react"; +import { useCallback, useEffect, useMemo, useState, type RefObject } from "react"; import { ExtendedCSSProperties, UIElement } from "../../../../types/exportedTypes"; import { getCurrentElementStyleValue } from "../../functions/helpers/getCurrentElementStyleValue"; import type { DataModelManager } from "../../data/dataModel"; @@ -11,6 +11,7 @@ import { AddIcon, AlignJustifyIcon, AlignLeftIcon, AlignRightIcon, DeviceIcon, F import { getAlphaFromRgba, rgbaToHex, hexToRgba } from "../../functions/helpers/colorHandlers"; import DataDetailedDropdown from "../../../ui/inputs/DataDetailedDropdown"; import { useSceneContext } from "../../../../modules/scene/sceneContext"; +import ElementDesign from "./ElementDesign"; interface ElementEditorProps { elementEditorRef: RefObject; @@ -65,14 +66,9 @@ const ElementEditor: React.FC = ({ const product = getProductById(selectedProduct.productUuid); const { getElementById } = simulationDashBoardStore(); const element = getElementById(selectedBlock, selectedElement); + console.log("element: ", element); const [selectType, setSelectType] = useState("design"); - const [selectDataMapping, setSelectDataMapping] = useState("singleMachine"); - - const [singleFields, setSingleFields] = useState>([ - { id: "data-source", label: "Data Source", showEyeDropper: true }, - { id: "input-1", label: "Input 1", showEyeDropper: false }, - { id: "input-2", label: "Input 2", showEyeDropper: false }, - ]); + const [selectDataMapping, setSelectDataMapping] = useState(element?.type === "graph" && element.dataType === "multiple-machine" ? "multipleMachine" : "singleMachine"); const [multipleFields, setMultipleFields] = useState>([ { id: "common-value", label: "Common Value" }, @@ -306,26 +302,55 @@ const ElementEditor: React.FC = ({ return commonItems; }, []); - const addField = () => { - if (selectDataMapping === "singleMachine") { - setSingleFields((prev) => [ - ...prev, - { - id: `input-${Date.now()}`, - label: `Input ${prev.filter((f) => f.label.startsWith("Input")).length + 1}`, - showEyeDropper: false, - }, - ]); - return; + const singleSourceFields = useMemo( + () => [{ id: "data-source", label: "Data Source", showEyeDropper: true, options: getAssetDropdownItems().map((item) => ({ id: item.id, label: item.label })) }], + [getAssetDropdownItems] + ); + + const singleValueFields = useMemo(() => { + if (element?.type === "graph" && element.dataType === "single-machine") { + // Get the dataValue array - ensure it's always an array + const dataValues = Array.isArray(element.dataValue) ? element.dataValue : []; + + // Generate dropdown options based on the selected data source + const valueOptions = element.dataSource ? getLableValueDropdownItems(element.dataSource).flatMap((section) => section.items.map((item) => ({ id: item.id, label: item.label }))) : []; + + // Create fields based on the number of data values in the element + return dataValues.map((value, index) => ({ + id: `data-value-${index}`, + label: `Data Value ${index + 1}`, + showEyeDropper: true, + options: valueOptions, + })); } - setMultipleFields((prev) => [ - ...prev, + // Return default field if not a graph with single-machine data type + return [ { - id: `data-${Date.now()}`, - label: "Data Source", + id: "data-value", + label: "Data Value", + showEyeDropper: false, + options: [], }, - ]); + ]; + }, [element, getLableValueDropdownItems]); + + const addField = () => { + if (selectDataMapping === "singleMachine" && element?.type === "graph" && element.dataType === "single-machine") { + // For single machine, add new data value field + const currentDataValue = Array.isArray(element.dataValue) ? element.dataValue : []; + const newDataValue = [...currentDataValue, ""]; // Add empty string for new field + + // Update the element's dataValue + updateDataValue(selectedBlock, selectedElement, newDataValue); + } else if (selectDataMapping === "multipleMachine" && element?.type === "graph") { + // For multiple machine, add new data source field + const currentDataSource = Array.isArray(element.dataSource) ? element.dataSource : []; + const newDataSource = [...currentDataSource, ""]; // Add empty string for new field + + // Update the element's dataSource + updateDataSource(selectedBlock, selectedElement, newDataSource); + } }; return ( @@ -354,227 +379,35 @@ const ElementEditor: React.FC = ({ )} {selectType === "design" && ( - <> - {element?.type === "graph" && ( -
- { - if (newValue === "Line Chart") { - updateGraphType(selectedBlock, selectedElement, "line"); - return; - } else if (newValue === "Bar Chart") { - updateGraphType(selectedBlock, selectedElement, "bar"); - return; - } else if (newValue === "Pie Chart") { - updateGraphType(selectedBlock, selectedElement, "pie"); - return; - } else if (newValue === "Area Chart") { - updateGraphType(selectedBlock, selectedElement, "area"); - return; - } else if (newValue === "Radar Chart") { - updateGraphType(selectedBlock, selectedElement, "radar"); - return; - } - }} - showEyeDropper={false} - /> -
- )} - -
-
Position
-
- {["relative", "absolute"].map((position) => ( -
updateElementPositionType(selectedBlock, selectedElement, position as "relative" | "absolute" | "fixed")} - > - {position.charAt(0).toUpperCase() + position.slice(1)} -
- ))} -
-
-
-
- -
-
- -
-
- -
-
- -
-
-
- -
-
-
updateElementStyle(selectedBlock, selectedElement, { textAlign: "right" })} - > - -
-
updateElementStyle(selectedBlock, selectedElement, { textAlign: "justify" })} - > - -
-
updateElementStyle(selectedBlock, selectedElement, { textAlign: "left" })} - > - -
-
-
-
updateElementStyle(selectedBlock, selectedElement, { flexDirection: "row" })} - > - -
-
updateElementStyle(selectedBlock, selectedElement, { flexDirection: "column" })} - > - -
-
updateElementStyle(selectedBlock, selectedElement, { flexDirection: "row-reverse" })} - > - -
-
updateElementStyle(selectedBlock, selectedElement, { flexDirection: "column-reverse" })} - > - -
-
-
-
- updateElementZIndex(selectedBlock, selectedElement, Number(newValue))} - /> - - updateElementStyle(selectedBlock, selectedElement, { backdropFilter: `blur(${Number(value)}px)` })} - /> - - { - updateElementStyle(selectedBlock, selectedElement, { - borderRadius: Number(newValue), - }); - }} - /> -
-
- -
-
Appearance
-
- { - updateElementPosition(selectedBlock, selectedElement, { - ...(currentElement.position || { x: 0, y: 0 }), - x: Number(newValue), - }); - }} - /> - { - updateElementPosition(selectedBlock, selectedElement, { - ...(currentElement.position || { x: 0, y: 0 }), - y: Number(newValue), - }); - }} - /> - { - updateElementSize(selectedBlock, selectedElement, { - ...(currentElement.size || { width: 0, height: 0 }), - width: Number(newValue), - }); - }} - /> - { - updateElementSize(selectedBlock, selectedElement, { - ...(currentElement.size || { width: 0, height: 0 }), - height: Number(newValue), - }); - }} - /> -
-
- -
-
Element Color
-
-
Color
-
- { - const currentBg = getCurrentElementStyleValue(currentElement, "backgroundColor") || "rgba(50,50,50,1)"; - const currentAlpha = getAlphaFromRgba(currentBg); - const newBg = hexToRgba(e.target.value, currentAlpha); - updateElementStyle(selectedBlock, selectedElement, { backgroundColor: newBg }); - }} - /> - -
{rgbaToHex(getCurrentElementStyleValue(currentElement, "backgroundColor") || "rgba(50,50,50,1)")}
-
-
-
-
Swap with Another Element
- + )} {selectType === "data" && (
{element?.type === "label-value" && (
- { }} /> + {}} />
= ({
setSelectDataMapping("singleMachine")}> Single Machine
-
setSelectDataMapping("multipleeMachine")}> +
setSelectDataMapping("multipleMachine")}> Multiple Machine
- {selectDataMapping === "singleMachine" && ( + {selectDataMapping === "singleMachine" && element.dataType === "single-machine" && (
- {singleFields.map((field) => ( - { }} showEyeDropper={!!field.showEyeDropper} - /> - ))} + <> + {singleSourceFields.map((field) => ( + { + updateDataSource(selectedBlock, selectedElement, value.id); + }} + showEyeDropper={!!field.showEyeDropper} + /> + ))} + {singleValueFields.map((field, index) => ( + option.id === element.dataValue?.[index])?.label ?? ""} + onSelect={(value) => { + // Get current dataValue array + const currentDataValue = Array.isArray(element.dataValue) ? element.dataValue : []; + + // Create a new array with the updated value at the correct index + const newDataValue = [...currentDataValue]; + newDataValue[index] = value.id; + + // Update the entire array + updateDataValue(selectedBlock, selectedElement, newDataValue); + }} + showEyeDropper={!!field.showEyeDropper} + /> + ))} +
@@ -653,17 +510,15 @@ const ElementEditor: React.FC = ({
)} - {selectDataMapping === "multipleeMachine" && ( + {selectDataMapping === "multipleMachine" && (
{multipleFields.map((field) => (
{ }} + options={[{ id: "global", label: "Global" }]} + onSelect={() => {}} showEyeDropper={field.label !== "Common Value"} />
diff --git a/app/src/components/ui/inputs/DataSourceSelector.tsx b/app/src/components/ui/inputs/DataSourceSelector.tsx index b222acc..11557d4 100644 --- a/app/src/components/ui/inputs/DataSourceSelector.tsx +++ b/app/src/components/ui/inputs/DataSourceSelector.tsx @@ -1,5 +1,4 @@ import React, { useState } from "react"; -import RegularDropDown from "./RegularDropDown"; import { EyeDroperIcon } from "../../icons/ExportCommonIcons"; import RegularDropDownID from "./RegularDropDownID"; @@ -10,18 +9,12 @@ type DataSourceSelectorProps = { label: string; }[]; selected?: string; - onSelect: (value: string) => void; + onSelect: (value: { id: string; label: string }) => void; eyeDropperActive?: boolean; // initial state showEyeDropper?: boolean; }; -const DataSourceSelector: React.FC = ({ - label = "Data Source", - options, - selected, - onSelect, - showEyeDropper = true, -}) => { +const DataSourceSelector: React.FC = ({ label = "Data Source", options, selected, onSelect, showEyeDropper = true }) => { // Local state to toggle EyeDropper const [isEyeActive, setIsEyeActive] = useState(false); @@ -30,19 +23,10 @@ const DataSourceSelector: React.FC = ({
{label}
- + {showEyeDropper && ( -
setIsEyeActive(!isEyeActive)} - style={{ cursor: "pointer" }} - > +
setIsEyeActive(!isEyeActive)} style={{ cursor: "pointer" }}> {/* Pass the local state here */}
@@ -53,7 +37,3 @@ const DataSourceSelector: React.FC = ({ }; export default DataSourceSelector; - - - - diff --git a/app/src/components/ui/inputs/RegularDropDownID.tsx b/app/src/components/ui/inputs/RegularDropDownID.tsx index 63af607..4aa7ca8 100644 --- a/app/src/components/ui/inputs/RegularDropDownID.tsx +++ b/app/src/components/ui/inputs/RegularDropDownID.tsx @@ -9,7 +9,7 @@ interface DropdownOption { interface DropdownProps { header: string; options: DropdownOption[]; - onSelect: (optionId: string) => void; + onSelect: (value: DropdownOption) => void; search?: boolean; onClick?: () => void; onChange?: () => void; @@ -63,7 +63,7 @@ const RegularDropDownID: React.FC = ({ const handleOptionClick = (opt: DropdownOption) => { setSelectedOption(opt.label); - onSelect(opt.id); + onSelect(opt); setIsOpen(false); };