Configuration Panel Milestone 5
This commit is contained in:
parent
9aff34e8de
commit
e35495c7e3
@ -1,5 +1,5 @@
|
|||||||
////////// PROJECT FILE SEPARATION LINE ////////// CODE AFTER THIS LINE ARE FROM: <ProjectRoot>/Data/WebUI/src/Node_Configuration_Sidebar.jsx
|
////////// PROJECT FILE SEPARATION LINE ////////// CODE AFTER THIS LINE ARE FROM: <ProjectRoot>/Data/WebUI/src/Node_Configuration_Sidebar.jsx
|
||||||
import { Box, Typography, Tabs, Tab, TextField } from "@mui/material";
|
import { Box, Typography, Tabs, Tab, TextField, MenuItem } from "@mui/material";
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { useReactFlow } from "reactflow";
|
import { useReactFlow } from "reactflow";
|
||||||
import ReactMarkdown from "react-markdown"; // Used for Node Usage Documentation
|
import ReactMarkdown from "react-markdown"; // Used for Node Usage Documentation
|
||||||
@ -13,39 +13,117 @@ export default function NodeConfigurationSidebar({ drawerOpen, setDrawerOpen, ti
|
|||||||
const config = nodeData?.config || [];
|
const config = nodeData?.config || [];
|
||||||
const nodeId = nodeData?.nodeId;
|
const nodeId = nodeData?.nodeId;
|
||||||
|
|
||||||
return config.map((field, index) => (
|
return config.map((field, index) => {
|
||||||
<Box key={index} sx={{ mb: 2 }}>
|
const value = nodeData?.[field.key] || "";
|
||||||
<Typography variant="body2" sx={{ color: "#ccc", mb: 0.5 }}>
|
|
||||||
{field.label || field.key}
|
return (
|
||||||
</Typography>
|
<Box key={index} sx={{ mb: 2 }}>
|
||||||
<TextField
|
<Typography variant="body2" sx={{ color: "#ccc", mb: 0.5 }}>
|
||||||
variant="outlined"
|
{field.label || field.key}
|
||||||
size="small"
|
</Typography>
|
||||||
fullWidth
|
|
||||||
value={nodeData?.[field.key] || ""}
|
{field.type === "select" ? (
|
||||||
onChange={(e) => {
|
<TextField
|
||||||
const newValue = e.target.value;
|
select
|
||||||
if (!nodeId) return;
|
fullWidth
|
||||||
setNodes((nds) =>
|
size="small"
|
||||||
nds.map((n) =>
|
value={value}
|
||||||
n.id === nodeId
|
onChange={(e) => {
|
||||||
? { ...n, data: { ...n.data, [field.key]: newValue } }
|
const newValue = e.target.value;
|
||||||
: n
|
if (!nodeId) return;
|
||||||
)
|
setNodes((nds) =>
|
||||||
);
|
nds.map((n) =>
|
||||||
window.BorealisValueBus[nodeId] = newValue;
|
n.id === nodeId
|
||||||
}}
|
? { ...n, data: { ...n.data, [field.key]: newValue } }
|
||||||
InputProps={{
|
: n
|
||||||
sx: {
|
)
|
||||||
backgroundColor: "#1e1e1e",
|
);
|
||||||
color: "#ccc",
|
window.BorealisValueBus[nodeId] = newValue;
|
||||||
"& fieldset": { borderColor: "#444" },
|
}}
|
||||||
"&:hover fieldset": { borderColor: "#666" }
|
SelectProps={{
|
||||||
}
|
MenuProps: {
|
||||||
}}
|
PaperProps: {
|
||||||
/>
|
sx: {
|
||||||
</Box>
|
bgcolor: "#1e1e1e",
|
||||||
));
|
color: "#ccc",
|
||||||
|
border: "1px solid #58a6ff",
|
||||||
|
"& .MuiMenuItem-root": {
|
||||||
|
color: "#ccc",
|
||||||
|
fontSize: "0.85rem",
|
||||||
|
"&:hover": {
|
||||||
|
backgroundColor: "#2a2a2a"
|
||||||
|
},
|
||||||
|
"&.Mui-selected": {
|
||||||
|
backgroundColor: "#2c2c2c !important",
|
||||||
|
color: "#58a6ff"
|
||||||
|
},
|
||||||
|
"&.Mui-selected:hover": {
|
||||||
|
backgroundColor: "#2a2a2a !important"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
sx={{
|
||||||
|
"& .MuiOutlinedInput-root": {
|
||||||
|
backgroundColor: "#1e1e1e",
|
||||||
|
color: "#ccc",
|
||||||
|
fontSize: "0.85rem",
|
||||||
|
"& fieldset": {
|
||||||
|
borderColor: "#444"
|
||||||
|
},
|
||||||
|
"&:hover fieldset": {
|
||||||
|
borderColor: "#58a6ff"
|
||||||
|
},
|
||||||
|
"&.Mui-focused fieldset": {
|
||||||
|
borderColor: "#58a6ff"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"& .MuiSelect-select": {
|
||||||
|
backgroundColor: "#1e1e1e"
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{(field.options || []).map((opt, idx) => (
|
||||||
|
<MenuItem key={idx} value={opt}>
|
||||||
|
{opt}
|
||||||
|
</MenuItem>
|
||||||
|
))}
|
||||||
|
</TextField>
|
||||||
|
|
||||||
|
) : (
|
||||||
|
<TextField
|
||||||
|
variant="outlined"
|
||||||
|
size="small"
|
||||||
|
fullWidth
|
||||||
|
value={value}
|
||||||
|
onChange={(e) => {
|
||||||
|
const newValue = e.target.value;
|
||||||
|
if (!nodeId) return;
|
||||||
|
setNodes((nds) =>
|
||||||
|
nds.map((n) =>
|
||||||
|
n.id === nodeId
|
||||||
|
? { ...n, data: { ...n.data, [field.key]: newValue } }
|
||||||
|
: n
|
||||||
|
)
|
||||||
|
);
|
||||||
|
window.BorealisValueBus[nodeId] = newValue;
|
||||||
|
}}
|
||||||
|
InputProps={{
|
||||||
|
sx: {
|
||||||
|
backgroundColor: "#1e1e1e",
|
||||||
|
color: "#ccc",
|
||||||
|
"& fieldset": { borderColor: "#444" },
|
||||||
|
"&:hover fieldset": { borderColor: "#666" },
|
||||||
|
"&.Mui-focused fieldset": { borderColor: "#58a6ff" }
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -95,7 +173,7 @@ export default function NodeConfigurationSidebar({ drawerOpen, setDrawerOpen, ti
|
|||||||
onChange={handleTabChange}
|
onChange={handleTabChange}
|
||||||
variant="fullWidth"
|
variant="fullWidth"
|
||||||
textColor="inherit"
|
textColor="inherit"
|
||||||
TabIndicatorProps={{ style: { backgroundColor: "#58a6ff" } }}
|
TabIndicatorProps={{ style: { backgroundColor: "#ccc" } }}
|
||||||
sx={{
|
sx={{
|
||||||
borderTop: "1px solid #333",
|
borderTop: "1px solid #333",
|
||||||
borderBottom: "1px solid #333",
|
borderBottom: "1px solid #333",
|
||||||
@ -103,8 +181,27 @@ export default function NodeConfigurationSidebar({ drawerOpen, setDrawerOpen, ti
|
|||||||
height: "36px"
|
height: "36px"
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Tab label="Config" sx={{ color: "#ccc", minHeight: "36px", height: "36px", textTransform: "none" }} />
|
<Tab
|
||||||
<Tab label="Usage Docs" sx={{ color: "#ccc", minHeight: "36px", height: "36px", textTransform: "none" }} />
|
label="Config"
|
||||||
|
sx={{
|
||||||
|
color: "#ccc",
|
||||||
|
"&.Mui-selected": { color: "#ccc" },
|
||||||
|
minHeight: "36px",
|
||||||
|
height: "36px",
|
||||||
|
textTransform: "none"
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Tab
|
||||||
|
label="Usage Docs"
|
||||||
|
sx={{
|
||||||
|
color: "#ccc",
|
||||||
|
"&.Mui-selected": { color: "#ccc" },
|
||||||
|
minHeight: "36px",
|
||||||
|
height: "36px",
|
||||||
|
textTransform: "none"
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
////////// PROJECT FILE SEPARATION LINE ////////// CODE AFTER THIS LINE ARE FROM: <ProjectRoot>/Data/WebUI/src/nodes/General Purpose/Node_Data.jsx
|
////////// PROJECT FILE SEPARATION LINE ////////// CODE AFTER THIS LINE ARE FROM: <ProjectRoot>/Data/WebUI/src/nodes/General Purpose/Node_Data.jsx
|
||||||
import React, { useEffect, useRef } from "react";
|
import React, { useEffect, useRef, useState } from "react";
|
||||||
import { Handle, Position, useReactFlow, useStore } from "reactflow";
|
import { Handle, Position, useReactFlow, useStore } from "reactflow";
|
||||||
import { IconButton } from "@mui/material";
|
import { IconButton } from "@mui/material";
|
||||||
import { Settings as SettingsIcon } from "@mui/icons-material";
|
import { Settings as SettingsIcon } from "@mui/icons-material";
|
||||||
@ -10,10 +10,12 @@ if (!window.BorealisUpdateRate) window.BorealisUpdateRate = 100;
|
|||||||
const DataNode = ({ id, data }) => {
|
const DataNode = ({ id, data }) => {
|
||||||
const { setNodes } = useReactFlow();
|
const { setNodes } = useReactFlow();
|
||||||
const edges = useStore((state) => state.edges);
|
const edges = useStore((state) => state.edges);
|
||||||
const valueRef = useRef(data?.value || "");
|
const [renderValue, setRenderValue] = useState(data?.value || "");
|
||||||
|
const valueRef = useRef(renderValue);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
valueRef.current = data?.value || "";
|
valueRef.current = data?.value || "";
|
||||||
|
setRenderValue(valueRef.current);
|
||||||
window.BorealisValueBus[id] = valueRef.current;
|
window.BorealisValueBus[id] = valueRef.current;
|
||||||
}, [data?.value, id]);
|
}, [data?.value, id]);
|
||||||
|
|
||||||
@ -29,6 +31,7 @@ const DataNode = ({ id, data }) => {
|
|||||||
const upstreamValue = window.BorealisValueBus[inputEdge.source] ?? "";
|
const upstreamValue = window.BorealisValueBus[inputEdge.source] ?? "";
|
||||||
if (upstreamValue !== valueRef.current) {
|
if (upstreamValue !== valueRef.current) {
|
||||||
valueRef.current = upstreamValue;
|
valueRef.current = upstreamValue;
|
||||||
|
setRenderValue(upstreamValue);
|
||||||
window.BorealisValueBus[id] = upstreamValue;
|
window.BorealisValueBus[id] = upstreamValue;
|
||||||
setNodes((nds) =>
|
setNodes((nds) =>
|
||||||
nds.map((n) =>
|
nds.map((n) =>
|
||||||
@ -60,19 +63,22 @@ const DataNode = ({ id, data }) => {
|
|||||||
return (
|
return (
|
||||||
<div className="borealis-node">
|
<div className="borealis-node">
|
||||||
<Handle type="target" position={Position.Left} className="borealis-handle" />
|
<Handle type="target" position={Position.Left} className="borealis-handle" />
|
||||||
|
|
||||||
<div className="borealis-node-header" style={{ display: "flex", justifyContent: "space-between", alignItems: "center" }}>
|
<div className="borealis-node-header" style={{ display: "flex", justifyContent: "space-between", alignItems: "center" }}>
|
||||||
<span>{data?.label || "Data Node"}</span>
|
<span>{data?.label || "Data Node"}</span>
|
||||||
<IconButton
|
<IconButton
|
||||||
size="small"
|
size="small"
|
||||||
onClick={() =>
|
onClick={() => window.BorealisOpenDrawer && window.BorealisOpenDrawer(id, { ...data, nodeId: id })}
|
||||||
window.BorealisOpenDrawer &&
|
sx={{ color: "#888", padding: 0, marginLeft: "auto" }}
|
||||||
window.BorealisOpenDrawer(id, { ...data, nodeId: id })
|
|
||||||
}
|
|
||||||
sx={{ padding: 0, marginRight: "-3px", color: "#58a6ff", width: "20px", height: "20px" }}
|
|
||||||
>
|
>
|
||||||
<SettingsIcon sx={{ fontSize: "16px" }} />
|
<SettingsIcon sx={{ fontSize: 16 }} />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div className="borealis-node-content" style={{ fontSize: "9px", color: "#ccc", marginTop: 4 }}>
|
||||||
|
Value: {renderValue}
|
||||||
|
</div>
|
||||||
|
|
||||||
<Handle type="source" position={Position.Right} className="borealis-handle" />
|
<Handle type="source" position={Position.Right} className="borealis-handle" />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -1,175 +1,161 @@
|
|||||||
////////// PROJECT FILE SEPARATION LINE ////////// CODE AFTER THIS LINE ARE FROM: <ProjectRoot>/Data/WebUI/src/nodes/General Purpose/Node_Logical_Operators.jsx
|
////////// PROJECT FILE SEPARATION LINE ////////// CODE AFTER THIS LINE ARE FROM: <ProjectRoot>/Data/WebUI/src/nodes/General Purpose/Node_Logical_Operators.jsx
|
||||||
|
|
||||||
/**
|
|
||||||
* ==============================================
|
|
||||||
* Borealis - Comparison Node (Logic Evaluation)
|
|
||||||
* ==============================================
|
|
||||||
*
|
|
||||||
* COMPONENT ROLE:
|
|
||||||
* This node takes two input values and evaluates them using a selected comparison operator.
|
|
||||||
* It returns 1 (true) or 0 (false) depending on the result of the comparison.
|
|
||||||
*
|
|
||||||
* FEATURES:
|
|
||||||
* - Dropdown to select input type: "Number" or "String"
|
|
||||||
* - Dropdown to select comparison operator: ==, !=, >, <, >=, <=
|
|
||||||
* - Dynamically disables numeric-only operators for string inputs
|
|
||||||
* - Automatically resets operator to == when switching to String
|
|
||||||
* - Supports summing multiple inputs per side (A, B)
|
|
||||||
* - For "String" mode: concatenates inputs in connection order
|
|
||||||
* - Uses BorealisValueBus for input/output
|
|
||||||
* - Controlled by global update timer
|
|
||||||
*
|
|
||||||
* STRUCTURE:
|
|
||||||
* - Label and Description
|
|
||||||
* - Input A (top-left) and Input B (middle-left)
|
|
||||||
* - Output (right edge) result: 1 (true) or 0 (false)
|
|
||||||
* - Operator dropdown and Input Type dropdown
|
|
||||||
*/
|
|
||||||
|
|
||||||
import React, { useEffect, useRef, useState } from "react";
|
import React, { useEffect, useRef, useState } from "react";
|
||||||
import { Handle, Position, useReactFlow, useStore } from "reactflow";
|
import { Handle, Position, useReactFlow, useStore } from "reactflow";
|
||||||
|
import { IconButton } from "@mui/material";
|
||||||
|
import SettingsIcon from "@mui/icons-material/Settings";
|
||||||
|
|
||||||
if (!window.BorealisValueBus) window.BorealisValueBus = {};
|
if (!window.BorealisValueBus) window.BorealisValueBus = {};
|
||||||
if (!window.BorealisUpdateRate) window.BorealisUpdateRate = 100;
|
if (!window.BorealisUpdateRate) window.BorealisUpdateRate = 100;
|
||||||
|
|
||||||
const ComparisonNode = ({ id, data }) => {
|
const ComparisonNode = ({ id, data }) => {
|
||||||
const { setNodes } = useReactFlow();
|
const { setNodes } = useReactFlow();
|
||||||
const edges = useStore(state => state.edges);
|
const edges = useStore(state => state.edges);
|
||||||
|
const [renderValue, setRenderValue] = useState("0");
|
||||||
|
const valueRef = useRef("0");
|
||||||
|
|
||||||
const [inputType, setInputType] = useState(data?.inputType || "Number");
|
useEffect(() => {
|
||||||
const [operator, setOperator] = useState(data?.operator || "Equal (==)");
|
let currentRate = window.BorealisUpdateRate;
|
||||||
const [renderValue, setRenderValue] = useState("0");
|
let intervalId = null;
|
||||||
const valueRef = useRef("0");
|
|
||||||
|
|
||||||
useEffect(() => {
|
const runNodeLogic = () => {
|
||||||
if (inputType === "String" && !["Equal (==)", "Not Equal (!=)"].includes(operator)) {
|
let inputType = data?.inputType || "Number";
|
||||||
setOperator("Equal (==)");
|
let operator = data?.operator || "Equal (==)";
|
||||||
|
|
||||||
|
if (inputType === "String" && !["Equal (==)", "Not Equal (!=)"].includes(operator)) {
|
||||||
|
operator = "Equal (==)";
|
||||||
|
setNodes(nds =>
|
||||||
|
nds.map(n =>
|
||||||
|
n.id === id ? { ...n, data: { ...n.data, operator } } : n
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const edgeInputsA = edges.filter(e => e?.target === id && e.targetHandle === "a");
|
||||||
|
const edgeInputsB = edges.filter(e => e?.target === id && e.targetHandle === "b");
|
||||||
|
|
||||||
|
const extractValues = (edgeList) => {
|
||||||
|
const values = edgeList.map(e => window.BorealisValueBus[e.source]).filter(v => v !== undefined);
|
||||||
|
if (inputType === "Number") {
|
||||||
|
return values.reduce((sum, v) => sum + (parseFloat(v) || 0), 0);
|
||||||
}
|
}
|
||||||
}, [inputType]);
|
return values.join("");
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
const a = extractValues(edgeInputsA);
|
||||||
let currentRate = window.BorealisUpdateRate;
|
const b = extractValues(edgeInputsB);
|
||||||
let intervalId = null;
|
|
||||||
|
|
||||||
const runNodeLogic = () => {
|
const resultMap = {
|
||||||
const edgeInputsA = edges.filter(e => e?.target === id && e.targetHandle === "a");
|
"Equal (==)": a === b,
|
||||||
const edgeInputsB = edges.filter(e => e?.target === id && e.targetHandle === "b");
|
"Not Equal (!=)": a !== b,
|
||||||
|
"Greater Than (>)": a > b,
|
||||||
|
"Less Than (<)": a < b,
|
||||||
|
"Greater Than or Equal (>=)": a >= b,
|
||||||
|
"Less Than or Equal (<=)": a <= b
|
||||||
|
};
|
||||||
|
|
||||||
const extractValues = (edgeList) => {
|
const result = resultMap[operator] ? "1" : "0";
|
||||||
const values = edgeList.map(e => window.BorealisValueBus[e.source]).filter(v => v !== undefined);
|
|
||||||
if (inputType === "Number") {
|
|
||||||
return values.reduce((sum, v) => sum + (parseFloat(v) || 0), 0);
|
|
||||||
}
|
|
||||||
return values.join("");
|
|
||||||
};
|
|
||||||
|
|
||||||
const a = extractValues(edgeInputsA);
|
valueRef.current = result;
|
||||||
const b = extractValues(edgeInputsB);
|
setRenderValue(result);
|
||||||
|
window.BorealisValueBus[id] = result;
|
||||||
|
|
||||||
const resultMap = {
|
setNodes(nds =>
|
||||||
"Equal (==)": a === b,
|
nds.map(n =>
|
||||||
"Not Equal (!=)": a !== b,
|
n.id === id ? { ...n, data: { ...n.data, value: result } } : n
|
||||||
"Greater Than (>)": a > b,
|
)
|
||||||
"Less Than (<)": a < b,
|
);
|
||||||
"Greater Than or Equal (>=)": a >= b,
|
};
|
||||||
"Less Than or Equal (<=)": a <= b
|
|
||||||
};
|
|
||||||
|
|
||||||
const result = resultMap[operator] ? "1" : "0";
|
intervalId = setInterval(runNodeLogic, currentRate);
|
||||||
|
|
||||||
valueRef.current = result;
|
|
||||||
setRenderValue(result);
|
|
||||||
window.BorealisValueBus[id] = result;
|
|
||||||
|
|
||||||
setNodes(nds =>
|
|
||||||
nds.map(n =>
|
|
||||||
n.id === id ? { ...n, data: { ...n.data, value: result, inputType, operator } } : n
|
|
||||||
)
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
const monitor = setInterval(() => {
|
||||||
|
const newRate = window.BorealisUpdateRate;
|
||||||
|
if (newRate !== currentRate) {
|
||||||
|
clearInterval(intervalId);
|
||||||
|
currentRate = newRate;
|
||||||
intervalId = setInterval(runNodeLogic, currentRate);
|
intervalId = setInterval(runNodeLogic, currentRate);
|
||||||
|
}
|
||||||
|
}, 250);
|
||||||
|
|
||||||
const monitor = setInterval(() => {
|
return () => {
|
||||||
const newRate = window.BorealisUpdateRate;
|
clearInterval(intervalId);
|
||||||
if (newRate !== currentRate) {
|
clearInterval(monitor);
|
||||||
clearInterval(intervalId);
|
};
|
||||||
currentRate = newRate;
|
}, [id, edges, data?.inputType, data?.operator, setNodes]);
|
||||||
intervalId = setInterval(runNodeLogic, currentRate);
|
|
||||||
}
|
|
||||||
}, 250);
|
|
||||||
|
|
||||||
return () => {
|
return (
|
||||||
clearInterval(intervalId);
|
<div className="borealis-node">
|
||||||
clearInterval(monitor);
|
<div style={{ position: "absolute", left: -16, top: 12, fontSize: "8px", color: "#ccc" }}>A</div>
|
||||||
};
|
<div style={{ position: "absolute", left: -16, top: 50, fontSize: "8px", color: "#ccc" }}>B</div>
|
||||||
}, [id, edges, inputType, operator, setNodes]);
|
<Handle type="target" position={Position.Left} id="a" style={{ top: 12 }} className="borealis-handle" />
|
||||||
|
<Handle type="target" position={Position.Left} id="b" style={{ top: 50 }} className="borealis-handle" />
|
||||||
|
|
||||||
return (
|
<div className="borealis-node-header" style={{ display: "flex", justifyContent: "space-between", alignItems: "center" }}>
|
||||||
<div className="borealis-node">
|
<span>{data?.label || "Logic Comparison"}</span>
|
||||||
<div style={{ position: "absolute", left: -16, top: 12, fontSize: "8px", color: "#ccc" }}>A</div>
|
<IconButton
|
||||||
<div style={{ position: "absolute", left: -16, top: 50, fontSize: "8px", color: "#ccc" }}>B</div>
|
size="small"
|
||||||
<Handle type="target" position={Position.Left} id="a" style={{ top: 12 }} className="borealis-handle" />
|
onClick={() => window.BorealisOpenDrawer(id, { ...data, nodeId: id })}
|
||||||
<Handle type="target" position={Position.Left} id="b" style={{ top: 50 }} className="borealis-handle" />
|
sx={{ color: "#888", padding: 0, marginLeft: "auto" }}
|
||||||
|
>
|
||||||
|
<SettingsIcon sx={{ fontSize: 16 }} />
|
||||||
|
</IconButton>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="borealis-node-header">
|
<div className="borealis-node-content" style={{ fontSize: "9px", color: "#ccc", marginTop: 4 }}>
|
||||||
{data?.label || "Comparison Node"}
|
Result: {renderValue}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="borealis-node-content">
|
<Handle type="source" position={Position.Right} className="borealis-handle" />
|
||||||
<div style={{ marginBottom: "6px", fontSize: "9px", color: "#ccc" }}>
|
</div>
|
||||||
{data?.content || "Evaluates A vs B and outputs 1 (true) or 0 (false)."}
|
);
|
||||||
</div>
|
|
||||||
|
|
||||||
<label style={{ fontSize: "9px" }}>Input Type:</label>
|
|
||||||
<select value={inputType} onChange={(e) => setInputType(e.target.value)} style={dropdownStyle}>
|
|
||||||
<option value="Number">Number</option>
|
|
||||||
<option value="String">String</option>
|
|
||||||
</select>
|
|
||||||
|
|
||||||
<label style={{ fontSize: "9px", marginTop: "6px" }}>Operator:</label>
|
|
||||||
<select value={operator} onChange={(e) => setOperator(e.target.value)} style={dropdownStyle}>
|
|
||||||
<option>Equal (==)</option>
|
|
||||||
<option>Not Equal (!=)</option>
|
|
||||||
<option disabled={inputType === "String"}>Greater Than (>)</option>
|
|
||||||
<option disabled={inputType === "String"}>Less Than (<)</option>
|
|
||||||
<option disabled={inputType === "String"}>Greater Than or Equal (>=)</option>
|
|
||||||
<option disabled={inputType === "String"}>Less Than or Equal (<=)</option>
|
|
||||||
</select>
|
|
||||||
|
|
||||||
<div style={{ marginTop: "8px", fontSize: "9px" }}>Result: {renderValue}</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<Handle type="source" position={Position.Right} className="borealis-handle" />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const dropdownStyle = {
|
|
||||||
width: "100%",
|
|
||||||
fontSize: "9px",
|
|
||||||
padding: "4px",
|
|
||||||
background: "#1e1e1e",
|
|
||||||
color: "#ccc",
|
|
||||||
border: "1px solid #444",
|
|
||||||
borderRadius: "2px",
|
|
||||||
marginBottom: "4px"
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
type: "ComparisonNode",
|
type: "ComparisonNode",
|
||||||
label: "Logic Comparison",
|
label: "Logic Comparison",
|
||||||
description: `
|
description: "Compare A vs B using logic operators",
|
||||||
Compare Two Inputs (A vs B)
|
content: "Compare A and B using Logic",
|
||||||
|
component: ComparisonNode,
|
||||||
|
config: [
|
||||||
|
{
|
||||||
|
key: "inputType",
|
||||||
|
label: "Input Type",
|
||||||
|
type: "select",
|
||||||
|
options: ["Number", "String"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "operator",
|
||||||
|
label: "Operator",
|
||||||
|
type: "select",
|
||||||
|
options: [
|
||||||
|
"Equal (==)",
|
||||||
|
"Not Equal (!=)",
|
||||||
|
"Greater Than (>)",
|
||||||
|
"Less Than (<)",
|
||||||
|
"Greater Than or Equal (>=)",
|
||||||
|
"Less Than or Equal (<=)"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
usage_documentation: `
|
||||||
|
### Logic Comparison Node
|
||||||
|
|
||||||
- Uses configurable operator
|
This node compares two inputs (A and B) using the selected operator.
|
||||||
- Supports numeric and string comparison
|
|
||||||
- Aggregates multiple inputs by summing (Number) or joining (String in connection order)
|
**Modes:**
|
||||||
- Only == and != are valid for String mode
|
- **Number**: Sums all connected inputs and compares.
|
||||||
- Automatically resets operator when switching to String mode
|
- **String**: Concatenates all inputs for comparison.
|
||||||
- Outputs 1 (true) or 0 (false) into BorealisValueBus
|
- Only **Equal (==)** and **Not Equal (!=)** are valid for strings.
|
||||||
- Live-updates based on global timer
|
|
||||||
`.trim(),
|
**Output:**
|
||||||
content: "Compare A and B using Logic",
|
- Returns \`1\` if comparison is true.
|
||||||
component: ComparisonNode
|
- Returns \`0\` if comparison is false.
|
||||||
|
|
||||||
|
**Input Notes:**
|
||||||
|
- A and B can each have multiple inputs.
|
||||||
|
- Input order matters for strings (concatenation).
|
||||||
|
- Input handles:
|
||||||
|
- **A** = Top left
|
||||||
|
- **B** = Middle left
|
||||||
|
`.trim()
|
||||||
};
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user