178 lines
6.1 KiB
TypeScript
178 lines
6.1 KiB
TypeScript
import {
|
|
type NodeProps,
|
|
Position,
|
|
type Node,
|
|
} from '@xyflow/react';
|
|
import { Toolbar } from '../components/NodeComponents';
|
|
import styles from '../../VisProg.module.css';
|
|
import { TextField } from '../../../../components/TextField';
|
|
import {MultiConnectionHandle} from "../components/RuleBasedHandle.tsx";
|
|
import {allowOnlyConnectionsFromHandle} from "../HandleRules.ts";
|
|
import useFlowStore from '../VisProgStores';
|
|
import { DoesPlanIterate, PlanReduce, type Plan } from '../components/Plan';
|
|
import PlanEditorDialog from '../components/PlanEditor';
|
|
import { MultilineTextField } from '../../../../components/MultilineTextField';
|
|
|
|
/**
|
|
* The default data dot a phase node
|
|
* @param label: the label of this phase
|
|
* @param droppable: whether this node is droppable from the drop bar (initialized as true)
|
|
* @param desciption: description of the goal - this will be checked for completion
|
|
* @param hasReduce: whether this node has reducing functionality (true by default)
|
|
* @param can_fail: whether this plan should be checked- this plan could possible fail
|
|
* @param plan: The (possible) attached plan to this goal
|
|
*/
|
|
export type GoalNodeData = {
|
|
label: string;
|
|
name: string;
|
|
description: string;
|
|
droppable: boolean;
|
|
achieved: boolean;
|
|
hasReduce: boolean;
|
|
can_fail: boolean;
|
|
plan?: Plan;
|
|
};
|
|
|
|
export type GoalNode = Node<GoalNodeData>
|
|
|
|
|
|
/**
|
|
* Defines how a Goal node should be rendered
|
|
* @param props NodeProps, like id, label, children
|
|
* @returns React.JSX.Element
|
|
*/
|
|
export default function GoalNode({id, data}: NodeProps<GoalNode>) {
|
|
const {updateNodeData} = useFlowStore();
|
|
|
|
const text_input_id = `goal_${id}_text_input`;
|
|
const checkbox_id = `goal_${id}_checkbox`;
|
|
const planIterate = DoesPlanIterate(data.plan);
|
|
|
|
const setDescription = (value: string) => {
|
|
updateNodeData(id, {...data, description: value});
|
|
}
|
|
|
|
const setName= (value: string) => {
|
|
updateNodeData(id, {...data, name: value})
|
|
}
|
|
|
|
const setFailable = (value: boolean) => {
|
|
updateNodeData(id, {...data, can_fail: value});
|
|
}
|
|
|
|
return <>
|
|
<Toolbar nodeId={id} allowDelete={true}/>
|
|
<div className={`${styles.defaultNode} ${styles.nodeGoal} flex-col gap-sm`}>
|
|
<div className={"flex-row gap-md"}>
|
|
<label htmlFor={text_input_id}>Goal:</label>
|
|
<TextField
|
|
id={text_input_id}
|
|
value={data.name}
|
|
setValue={(val) => setName(val)}
|
|
placeholder={"To ..."}
|
|
/>
|
|
</div>
|
|
|
|
{data.can_fail && (<div>
|
|
<label htmlFor={text_input_id}>Description/ Condition of goal:</label>
|
|
<div className={"flex-wrap"}>
|
|
<MultilineTextField
|
|
id={text_input_id}
|
|
value={data.description}
|
|
setValue={setDescription}
|
|
placeholder={"Describe the condition of this goal..."}
|
|
/>
|
|
</div>
|
|
</div>)}
|
|
<div>
|
|
<label> {!data.plan ? "No plan set to execute while goal is not reached. 🔴" : "Will follow plan '" + data.plan.name + "' until all steps complete. 🟢"} </label>
|
|
</div>
|
|
{data.plan && (<div className={"flex-row gap-md align-center " + (planIterate ? "" : styles.planNoIterate)}>
|
|
{planIterate ? "" : <s></s>}
|
|
<label htmlFor={checkbox_id}>{!planIterate ? "This plan always succeeds!" : "Check if this plan fails"}:</label>
|
|
<input
|
|
id={checkbox_id}
|
|
type={"checkbox"}
|
|
disabled={!planIterate}
|
|
checked={!planIterate || data.can_fail}
|
|
onChange={(e) => planIterate ? setFailable(e.target.checked) : setFailable(false)}
|
|
/>
|
|
</div>
|
|
)}
|
|
|
|
<div>
|
|
<PlanEditorDialog
|
|
plan={data.plan}
|
|
onSave={(plan) => {
|
|
updateNodeData(id, {
|
|
...data,
|
|
plan,
|
|
});
|
|
}}
|
|
description={data.name}
|
|
/>
|
|
</div>
|
|
<MultiConnectionHandle type="source" position={Position.Right} id="GoalSource" rules={[
|
|
allowOnlyConnectionsFromHandle([{nodeType:"phase",handleId:"data"}]),
|
|
]}/>
|
|
</div>
|
|
</>;
|
|
}
|
|
|
|
|
|
/**
|
|
* Reduces each Goal, including its children down into its relevant data.
|
|
* @param node The Node Properties of this node.
|
|
* @param _nodes all the nodes in the graph
|
|
*/
|
|
export function GoalReduce(node: Node, _nodes: Node[]) {
|
|
const data = node.data as GoalNodeData;
|
|
return {
|
|
id: node.id,
|
|
name: data.name,
|
|
description: data.description,
|
|
can_fail: data.can_fail,
|
|
plan: data.plan ? PlanReduce(data.plan) : "",
|
|
}
|
|
}
|
|
|
|
|
|
export const GoalTooltip = `
|
|
The goal node allows you to set goals that Pepper has to achieve
|
|
before moving to the next phase of your program`;
|
|
|
|
/**
|
|
* This function is called whenever a connection is made with this node type as the target
|
|
* @param _thisNode the node of this node type which function is called
|
|
* @param _sourceNodeId the source of the received connection
|
|
*/
|
|
export function GoalConnectionTarget(_thisNode: Node, _sourceNodeId: string) {
|
|
// no additional connection logic exists yet
|
|
}
|
|
|
|
/**
|
|
* This function is called whenever a connection is made with this node type as the source
|
|
* @param _thisNode the node of this node type which function is called
|
|
* @param _targetNodeId the target of the created connection
|
|
*/
|
|
export function GoalConnectionSource(_thisNode: Node, _targetNodeId: string) {
|
|
// no additional connection logic exists yet
|
|
}
|
|
|
|
/**
|
|
* This function is called whenever a connection is disconnected with this node type as the target
|
|
* @param _thisNode the node of this node type which function is called
|
|
* @param _sourceNodeId the source of the disconnected connection
|
|
*/
|
|
export function GoalDisconnectionTarget(_thisNode: Node, _sourceNodeId: string) {
|
|
// no additional connection logic exists yet
|
|
}
|
|
|
|
/**
|
|
* This function is called whenever a connection is disconnected with this node type as the source
|
|
* @param _thisNode the node of this node type which function is called
|
|
* @param _targetNodeId the target of the diconnected connection
|
|
*/
|
|
export function GoalDisconnectionSource(_thisNode: Node, _targetNodeId: string) {
|
|
// no additional connection logic exists yet
|
|
} |