feat: added title based tooltips and updated handle styling to reflect in and output handles

ref: N25B-450
This commit is contained in:
JGerla
2026-01-22 13:04:56 +01:00
parent f92467b409
commit bb053fda21
9 changed files with 30 additions and 27 deletions

View File

@@ -1,7 +1,16 @@
:global(.react-flow__handle.source){
border-radius: 100%;
}
:global(.react-flow__handle.target){
border-radius: 15%;
}
:global(.react-flow__handle.connected) {
background: lightgray;
border-color: green;
filter: drop-shadow(0 0 0.25rem green);
filter: drop-shadow(0 0 0.15rem green);
}
:global(.singleConnectionHandle.connected) {
@@ -16,19 +25,19 @@
:global(.singleConnectionHandle.unconnected){
background: lightsalmon;
border-color: #ff6060;
filter: drop-shadow(0 0 0.25rem #ff6060);
filter: drop-shadow(0 0 0.15rem #ff6060);
}
:global(.react-flow__handle.connectingto) {
background: #ff6060;
border-color: coral;
filter: drop-shadow(0 0 0.25rem coral);
filter: drop-shadow(0 0 0.15rem coral);
}
:global(.react-flow__handle.valid) {
background: #55dd99;
border-color: green;
filter: drop-shadow(0 0 0.25rem green);
filter: drop-shadow(0 0 0.15rem green);
}
:global(.react-flow__handle) {

View File

@@ -4,7 +4,6 @@ import {
type Connection,
useNodeId, useNodeConnections
} from '@xyflow/react';
import {useState} from 'react';
import { type HandleRule, useHandleRules} from "../HandleRuleLogic.ts";
import "./RuleBasedHandle.module.css";
@@ -29,21 +28,16 @@ export function MultiConnectionHandle({
handleId: id!
})
// initialise the handles state with { isValid: true } to show that connections are possible
const [handleState, setHandleState] = useState<{ isSatisfied: boolean, message?: string }>({ isSatisfied: true });
return (
<Handle
{...otherProps}
id={id}
type={type}
className={"multiConnectionHandle" + (connections.length === 0 ? " unconnected" : " connected")}
className={"multiConnectionHandle" + (connections.length === 0 ? " unconnected" : " connected") + ` ${type}`}
isValidConnection={(connection) => {
const result = validate(connection as Connection);
setHandleState(result);
return result.isSatisfied;
}}
title={handleState.message}
/>
);
}
@@ -66,22 +60,18 @@ export function SingleConnectionHandle({
handleId: id!
})
// initialise the handles state with { isValid: true } to show that connections are possible
const [handleState, setHandleState] = useState<{ isSatisfied: boolean, message?: string }>({ isSatisfied: true });
return (
<Handle
{...otherProps}
id={id}
type={type}
className={"singleConnectionHandle" + (connections.length === 0 ? " unconnected" : " connected")}
className={"singleConnectionHandle" + (connections.length === 0 ? " unconnected" : " connected") + ` ${type}`}
isConnectable={connections.length === 0}
isValidConnection={(connection) => {
const result = validate(connection as Connection);
setHandleState(result);
return result.isSatisfied;
}}
title={handleState.message}
/>
);
}

View File

@@ -190,7 +190,7 @@ export default function BasicBeliefNode(props: NodeProps<BasicBeliefNode>) {
)}
<MultiConnectionHandle type="source" position={Position.Right} id="source" rules={[
allowOnlyConnectionsFromHandle([{nodeType:"trigger",handleId:"TriggerBeliefs"}, {nodeType:"norm",handleId:"NormBeliefs"}]),
]}/>
]} title="Connect to any number of trigger and/or normNode(-s)"/>
</div>
</>
);

View File

@@ -60,7 +60,7 @@ export default function EndNode(props: NodeProps<EndNode>) {
</div>
<SingleConnectionHandle type="target" position={Position.Left} id="target" rules={[
allowOnlyConnectionsFromType(["phase"])
]}/>
]} title="Connect to a phaseNode"/>
</div>
</>
);

View File

@@ -118,9 +118,11 @@ export default function GoalNode({id, data}: NodeProps<GoalNode>) {
</div>
<MultiConnectionHandle type="source" position={Position.Right} id="GoalSource" rules={[
allowOnlyConnectionsFromHandle([{nodeType:"phase",handleId:"data"}]),
]}/>
]} title="Connect to any number of phase and/or goalNode(-s)"/>
<MultiConnectionHandle type="target" position={Position.Bottom} id="GoalTarget" rules={[allowOnlyConnectionsFromType(["goal"])]}/>
<MultiConnectionHandle type="target" position={Position.Bottom} id="GoalTarget" rules={[
allowOnlyConnectionsFromType(["goal"])]
} title="Connect to any number of goalNode(-s)"/>
</div>

View File

@@ -79,10 +79,10 @@ export default function NormNode(props: NodeProps<NormNode>) {
<MultiConnectionHandle type="source" position={Position.Right} id="norms" rules={[
allowOnlyConnectionsFromHandle([{nodeType:"phase",handleId:"data"}])
]}/>
]} title="Connect to any number of phaseNode(-s)"/>
<SingleConnectionHandle type="target" position={Position.Bottom} id="NormBeliefs" rules={[
allowOnlyConnectionsFromType(["basic_belief"])
]}/>
]} title="Connect to a beliefNode or a set of beliefs combined using the AND/OR node"/>
</div>
</>;
};

View File

@@ -148,14 +148,14 @@ export default function PhaseNode(props: NodeProps<PhaseNode>) {
<SingleConnectionHandle type="target" position={Position.Left} id="target" rules={[
noSelfConnections,
allowOnlyConnectionsFromType(["phase", "start"]),
]}/>
]} title="Connect to a phase or the startNode"/>
<MultiConnectionHandle type="target" position={Position.Bottom} id="data" rules={[
allowOnlyConnectionsFromType(["norm", "goal", "trigger"])
]}/>
]} title="Connect to any number of norm, goal, and TriggerNode(-s)"/>
<SingleConnectionHandle type="source" position={Position.Right} id="source" rules={[
noSelfConnections,
allowOnlyConnectionsFromType(["phase", "end"]),
]}/>
]} title="Connect to a phase or the endNode"/>
</div>
</>
);

View File

@@ -58,7 +58,7 @@ export default function StartNode(props: NodeProps<StartNode>) {
</div>
<SingleConnectionHandle type="source" position={Position.Right} id="source" rules={[
allowOnlyConnectionsFromHandle([{nodeType:"phase",handleId:"target"}])
]}/>
]} title="Connect to a phaseNode"/>
</div>
</>
);

View File

@@ -65,7 +65,7 @@ export default function TriggerNode(props: NodeProps<TriggerNode>) {
<div className={"flex-row gap-md"}>Plan{data.plan ? (": " + data.plan.name) : ""} is currently {data.plan ? "" : "not"} set. {data.plan ? "🟢" : "🔴"}</div>
<MultiConnectionHandle type="source" position={Position.Right} id="TriggerSource" rules={[
allowOnlyConnectionsFromHandle([{nodeType:"phase",handleId:"data"}]),
]}/>
]} title="Connect to any number of phaseNodes"/>
<SingleConnectionHandle
type="target"
position={Position.Bottom}
@@ -74,6 +74,7 @@ export default function TriggerNode(props: NodeProps<TriggerNode>) {
rules={[
allowOnlyConnectionsFromType(['basic_belief']),
]}
title="Connect to a beliefNode or a set of beliefs combined using the AND/OR node"
/>
<MultiConnectionHandle
@@ -84,6 +85,7 @@ export default function TriggerNode(props: NodeProps<TriggerNode>) {
rules={[
allowOnlyConnectionsFromType(['goal']),
]}
title="Connect to any number of goalNodes"
/>
<PlanEditorDialog