refactor: defaults should be in their own file, respecting eslint/ react standards. all tests fail, obviously.
ref: N25B-294
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
import type { EndNodeData } from "./EndNode";
|
||||
|
||||
/**
|
||||
* Default data for this node.
|
||||
*/
|
||||
export const EndNodeDefaults: EndNodeData = {
|
||||
label: "End Node",
|
||||
droppable: false,
|
||||
hasReduce: true
|
||||
};
|
||||
@@ -2,51 +2,20 @@ import {
|
||||
Handle,
|
||||
type NodeProps,
|
||||
Position,
|
||||
type Connection,
|
||||
type Edge,
|
||||
useReactFlow,
|
||||
type Node,
|
||||
} from '@xyflow/react';
|
||||
import { Toolbar } from './NodeDefinitions';
|
||||
import { Toolbar } from '../components/NodeComponents';
|
||||
import styles from '../../VisProg.module.css';
|
||||
|
||||
export type EndNodeData = {
|
||||
label: string;
|
||||
droppable: Boolean;
|
||||
hasReduce: Boolean;
|
||||
};
|
||||
|
||||
|
||||
export const EndNodeDefaults: EndNodeData = {
|
||||
label: "End Node",
|
||||
droppable: false,
|
||||
hasReduce: true
|
||||
droppable: boolean;
|
||||
hasReduce: boolean;
|
||||
};
|
||||
|
||||
export type EndNode = Node<EndNodeData>
|
||||
|
||||
export function EndNodeCanConnect(connection: Connection | Edge): boolean {
|
||||
// connection has: { source, sourceHandle, target, targetHandle }
|
||||
|
||||
// Example rules:
|
||||
if (connection.source === connection.target) return false;
|
||||
|
||||
|
||||
if (connection.targetHandle && !["a", "b"].includes(connection.targetHandle)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (connection.sourceHandle && connection.sourceHandle !== "result") {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If all rules pass
|
||||
return true;
|
||||
}
|
||||
|
||||
export default function EndNode(props: NodeProps<Node>) {
|
||||
const reactFlow = useReactFlow();
|
||||
const label_input_id = `phase_${props.id}_label_input`;
|
||||
return (
|
||||
<>
|
||||
<Toolbar nodeId={props.id} allowDelete={true}/>
|
||||
@@ -54,7 +23,6 @@ export default function EndNode(props: NodeProps<Node>) {
|
||||
<div className={"flex-row gap-sm"}>
|
||||
End
|
||||
</div>
|
||||
<Handle type="target" position={Position.Left} id="target"/>
|
||||
<Handle type="target" position={Position.Bottom} id="norms"/>
|
||||
<Handle type="source" position={Position.Right} id="source"/>
|
||||
</div>
|
||||
@@ -63,11 +31,18 @@ export default function EndNode(props: NodeProps<Node>) {
|
||||
}
|
||||
|
||||
export function EndReduce(node: Node, nodes: Node[]) {
|
||||
return {
|
||||
// Replace this for nodes functionality
|
||||
if (nodes.length <= -1) {
|
||||
console.warn("Impossible nodes length in EndReduce")
|
||||
}
|
||||
return {
|
||||
id: node.id
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function EndConnects(thisNode: Node, otherNode: Node, isThisSource: boolean) {
|
||||
|
||||
// Replace this for connection logic
|
||||
if (thisNode == undefined && otherNode == undefined && isThisSource == false) {
|
||||
console.warn("Impossible node connection called in EndConnects")
|
||||
}
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
import {
|
||||
NodeToolbar} from '@xyflow/react';
|
||||
import '@xyflow/react/dist/style.css';
|
||||
import styles from '../../VisProg.module.css';
|
||||
import useFlowStore from "../VisProgStores.tsx";
|
||||
|
||||
//Toolbar definitions
|
||||
type ToolbarProps = {
|
||||
nodeId: string;
|
||||
allowDelete: boolean;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Node Toolbar definition:
|
||||
* handles: node deleting functionality
|
||||
* can be added to any custom node component as a React component
|
||||
*
|
||||
* @param {string} nodeId
|
||||
* @param {boolean} allowDelete
|
||||
* @returns {React.JSX.Element}
|
||||
* @constructor
|
||||
*/
|
||||
export function Toolbar({nodeId, allowDelete}: ToolbarProps) {
|
||||
const {deleteNode} = useFlowStore();
|
||||
|
||||
const deleteParentNode = ()=> {
|
||||
deleteNode(nodeId);
|
||||
}
|
||||
return (
|
||||
<NodeToolbar>
|
||||
<button className="Node-toolbar__deletebutton" onClick={deleteParentNode} disabled={!allowDelete}>delete</button>
|
||||
</NodeToolbar>);
|
||||
}
|
||||
|
||||
// Renaming component
|
||||
/**
|
||||
* Adds a component that can be used to edit a node's label entry inside its Data
|
||||
* can be added to any custom node that has a label inside its Data
|
||||
*
|
||||
* @param {string} nodeLabel
|
||||
* @param {string} nodeId
|
||||
* @returns {React.JSX.Element}
|
||||
* @constructor
|
||||
*/
|
||||
export function EditableName({nodeLabel = "new node", nodeId} : { nodeLabel : string, nodeId: string}) {
|
||||
const {updateNodeData} = useFlowStore();
|
||||
|
||||
const updateData = (event: React.FocusEvent<HTMLInputElement>) => {
|
||||
const input = event.target.value;
|
||||
updateNodeData(nodeId, {label: input});
|
||||
event.currentTarget.setAttribute("readOnly", "true");
|
||||
window.getSelection()?.empty();
|
||||
event.currentTarget.classList.replace("nodrag", "drag"); // enable dragging of the node with cursor on the input box
|
||||
};
|
||||
|
||||
const updateOnEnter = (event: React.KeyboardEvent<HTMLInputElement>) => { if (event.key === "Enter") (event.target as HTMLInputElement).blur(); };
|
||||
|
||||
const enableEditing = (event: React.MouseEvent<HTMLInputElement>) => {
|
||||
if(event.currentTarget.hasAttribute("readOnly")) {
|
||||
event.currentTarget.removeAttribute("readOnly"); // enable editing
|
||||
event.currentTarget.select(); // select the text input
|
||||
window.getSelection()?.collapseToEnd(); // move the caret to the end of the current value
|
||||
event.currentTarget.classList.replace("drag", "nodrag"); // disable dragging using input box
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={styles.NodeTextBar }>
|
||||
<label>name: </label>
|
||||
<input
|
||||
className={`drag ${styles.nodeTextInput}`} // prevents dragging the component when user has focused the text input
|
||||
type={"text"}
|
||||
defaultValue={nodeLabel}
|
||||
onKeyDown={updateOnEnter}
|
||||
onBlur={updateData}
|
||||
onClick={enableEditing}
|
||||
maxLength={25}
|
||||
readOnly
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
import type { NormNodeData } from "./NormNode";
|
||||
|
||||
/**
|
||||
* Default data for this node
|
||||
*/
|
||||
export const NormNodeDefaults: NormNodeData = {
|
||||
label: "Norm Node",
|
||||
droppable: true,
|
||||
normList: [],
|
||||
hasReduce: true,
|
||||
};
|
||||
@@ -4,13 +4,10 @@ import {
|
||||
Position,
|
||||
type Connection,
|
||||
type Edge,
|
||||
useReactFlow,
|
||||
type Node,
|
||||
} from '@xyflow/react';
|
||||
import { Toolbar } from './NodeDefinitions';
|
||||
import { Toolbar } from '../components/NodeComponents';
|
||||
import styles from '../../VisProg.module.css';
|
||||
import { NodeDefaults, NodeReduces } from '../NodeRegistry';
|
||||
import type { FlowState } from '../VisProgTypes';
|
||||
|
||||
/**
|
||||
* The default data dot a Norm node
|
||||
@@ -25,25 +22,13 @@ export type NormNodeData = {
|
||||
hasReduce: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
* Default data for this node
|
||||
*/
|
||||
export const NormNodeDefaults: NormNodeData = {
|
||||
label: "Norm Node",
|
||||
droppable: true,
|
||||
normList: [],
|
||||
hasReduce: true,
|
||||
};
|
||||
|
||||
|
||||
export type NormNode = Node<NormNodeData>
|
||||
|
||||
/**
|
||||
*
|
||||
* @param connection
|
||||
* @returns
|
||||
*/
|
||||
|
||||
export function NormNodeCanConnect(connection: Connection | Edge): boolean {
|
||||
return true;
|
||||
return (connection != undefined);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -52,7 +37,6 @@ export function NormNodeCanConnect(connection: Connection | Edge): boolean {
|
||||
* @returns React.JSX.Element
|
||||
*/
|
||||
export default function NormNode(props: NodeProps<Node>) {
|
||||
const reactFlow = useReactFlow();
|
||||
const label_input_id = `Norm_${props.id}_label_input`;
|
||||
const data = props.data as NormNodeData;
|
||||
return (
|
||||
@@ -75,6 +59,10 @@ export default function NormNode(props: NodeProps<Node>) {
|
||||
* @param props: The Node Properties of this node.
|
||||
*/
|
||||
export function NormReduce(node: Node, nodes: Node[]) {
|
||||
// Replace this for nodes functionality
|
||||
if (nodes.length <= -1) {
|
||||
console.warn("Impossible nodes length in NormReduce")
|
||||
}
|
||||
const data = node.data as NormNodeData;
|
||||
return {
|
||||
label: data.label,
|
||||
@@ -83,4 +71,8 @@ export function NormReduce(node: Node, nodes: Node[]) {
|
||||
}
|
||||
|
||||
export function NormConnects(thisNode: Node, otherNode: Node, isThisSource: boolean) {
|
||||
// Replace this for connection logic
|
||||
if (thisNode == undefined && otherNode == undefined && isThisSource == false) {
|
||||
console.warn("Impossible node connection called in EndConnects")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import type { PhaseNodeData } from "./PhaseNode";
|
||||
|
||||
/**
|
||||
* Default data for this node
|
||||
*/
|
||||
export const PhaseNodeDefaults: PhaseNodeData = {
|
||||
label: "Phase Node",
|
||||
droppable: true,
|
||||
children: [],
|
||||
hasReduce: true,
|
||||
};
|
||||
@@ -2,15 +2,11 @@ import {
|
||||
Handle,
|
||||
type NodeProps,
|
||||
Position,
|
||||
type Connection,
|
||||
type Edge,
|
||||
useReactFlow,
|
||||
type Node,
|
||||
} from '@xyflow/react';
|
||||
import { Toolbar } from './NodeDefinitions';
|
||||
import { Toolbar } from '../components/NodeComponents';
|
||||
import styles from '../../VisProg.module.css';
|
||||
import { NodeDefaults, NodeReduces } from '../NodeRegistry';
|
||||
import type { FlowState } from '../VisProgTypes';
|
||||
|
||||
/**
|
||||
* The default data dot a phase node
|
||||
@@ -25,26 +21,9 @@ export type PhaseNodeData = {
|
||||
hasReduce: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
* Default data for this node
|
||||
*/
|
||||
export const PhaseNodeDefaults: PhaseNodeData = {
|
||||
label: "Phase Node",
|
||||
droppable: true,
|
||||
children: [],
|
||||
hasReduce: true,
|
||||
};
|
||||
|
||||
export type PhaseNode = Node<PhaseNodeData>
|
||||
|
||||
/**
|
||||
*
|
||||
* @param connection
|
||||
* @returns
|
||||
*/
|
||||
export function PhaseNodeCanConnect(connection: Connection | Edge): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines how a phase node should be rendered
|
||||
@@ -52,7 +31,6 @@ export function PhaseNodeCanConnect(connection: Connection | Edge): boolean {
|
||||
* @returns React.JSX.Element
|
||||
*/
|
||||
export default function PhaseNode(props: NodeProps<Node>) {
|
||||
const reactFlow = useReactFlow();
|
||||
const label_input_id = `phase_${props.id}_label_input`;
|
||||
return (
|
||||
<>
|
||||
@@ -65,6 +43,7 @@ export default function PhaseNode(props: NodeProps<Node>) {
|
||||
<Handle type="target" position={Position.Left} id="target"/>
|
||||
<Handle type="source" position={Position.Right} id="source"/>
|
||||
<Handle type="source" position={Position.Bottom} id="norms"/>
|
||||
<Handle type="source" position={Position.Top} id="norms"/>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
@@ -78,16 +57,16 @@ export function PhaseReduce(node: Node, nodes: Node[]) {
|
||||
const thisnode = node as PhaseNode;
|
||||
const data = thisnode.data as PhaseNodeData;
|
||||
const reducableChildren = Object.entries(NodeDefaults)
|
||||
.filter(([_, data]) => data.hasReduce)
|
||||
.map(([type, _]) => (
|
||||
.filter(([, data]) => data.hasReduce)
|
||||
.map(([type]) => (
|
||||
type
|
||||
));
|
||||
|
||||
let childrenData: any = ""
|
||||
let childrenData: unknown = ""
|
||||
if (data.children != undefined) {
|
||||
childrenData = data.children.map((childId) => {
|
||||
// Reduce each of this phases' children.
|
||||
let child = nodes.find((node) => node.id == childId);
|
||||
const child = nodes.find((node) => node.id == childId);
|
||||
|
||||
// Make sure that we reduce only valid children nodes.
|
||||
if (child == undefined || child.type == undefined || !reducableChildren.includes(child.type)) return ''
|
||||
@@ -109,8 +88,8 @@ export function PhaseReduce(node: Node, nodes: Node[]) {
|
||||
|
||||
export function PhaseConnects(thisNode: Node, otherNode: Node, isThisSource: boolean) {
|
||||
console.log("Connect functionality called.")
|
||||
let node = thisNode as PhaseNode
|
||||
let data = node.data as PhaseNodeData
|
||||
const node = thisNode as PhaseNode
|
||||
const data = node.data as PhaseNodeData
|
||||
if (isThisSource)
|
||||
data.children.push(otherNode.id)
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import type { StartNodeData } from "./StartNode";
|
||||
|
||||
/**
|
||||
* Default data for this node.
|
||||
*/
|
||||
export const StartNodeDefaults: StartNodeData = {
|
||||
label: "Start Node",
|
||||
droppable: false,
|
||||
hasReduce: true
|
||||
};
|
||||
@@ -2,71 +2,22 @@ import {
|
||||
Handle,
|
||||
type NodeProps,
|
||||
Position,
|
||||
type Connection,
|
||||
type Edge,
|
||||
useReactFlow,
|
||||
type Node,
|
||||
} from '@xyflow/react';
|
||||
import { Toolbar } from './NodeDefinitions';
|
||||
import { Toolbar } from '../components/NodeComponents';
|
||||
import styles from '../../VisProg.module.css';
|
||||
|
||||
/* ---------------------------------------------------------
|
||||
* 1. THE DATA SHAPE FOR THIS NODE TYPE
|
||||
* -------------------------------------------------------*/
|
||||
|
||||
export type StartNodeData = {
|
||||
label: string;
|
||||
droppable: boolean;
|
||||
hasReduce: boolean;
|
||||
};
|
||||
|
||||
/* ---------------------------------------------------------
|
||||
* 2. DEFAULT DATA FOR NEW INSTANCES OF THIS NODE
|
||||
* -------------------------------------------------------*/
|
||||
export const StartNodeDefaults: StartNodeData = {
|
||||
label: "Start Node",
|
||||
droppable: false,
|
||||
hasReduce: true,
|
||||
};
|
||||
|
||||
export type StartNode = Node<StartNodeData>
|
||||
|
||||
/* ---------------------------------------------------------
|
||||
* 3. CUSTOM CONNECTION LOGIC FOR THIS NODE
|
||||
* -------------------------------------------------------*/
|
||||
export function startNodeCanConnect(connection: Connection | Edge): boolean {
|
||||
// connection has: { source, sourceHandle, target, targetHandle }
|
||||
|
||||
// Example rules:
|
||||
|
||||
// ❌ Cannot connect to itself
|
||||
if (connection.source === connection.target) return false;
|
||||
|
||||
// ❌ Only allow incoming connections on input slots "a" or "b"
|
||||
if (connection.targetHandle && !["a", "b"].includes(connection.targetHandle)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// ❌ Only allow outgoing connections from "result"
|
||||
if (connection.sourceHandle && connection.sourceHandle !== "result") {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If all rules pass
|
||||
return true;
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------
|
||||
* 4. OPTIONAL: Node execution logic
|
||||
* If your system evaluates nodes, this is where that lives.
|
||||
* -------------------------------------------------------*/
|
||||
|
||||
|
||||
/* ---------------------------------------------------------
|
||||
* 5. THE NODE COMPONENT (UI)
|
||||
* -------------------------------------------------------*/
|
||||
export default function StartNode(props: NodeProps<Node>) {
|
||||
const reactFlow = useReactFlow();
|
||||
const label_input_id = `phase_${props.id}_label_input`;
|
||||
return (
|
||||
<>
|
||||
<Toolbar nodeId={props.id} allowDelete={true}/>
|
||||
@@ -83,11 +34,18 @@ export default function StartNode(props: NodeProps<Node>) {
|
||||
}
|
||||
|
||||
export function StartReduce(node: Node, nodes: Node[]) {
|
||||
return {
|
||||
// Replace this for nodes functionality
|
||||
if (nodes.length <= -1) {
|
||||
console.warn("Impossible nodes length in StartReduce")
|
||||
}
|
||||
return {
|
||||
id: node.id
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function StartConnects(thisNode: Node, otherNode: Node, isThisSource: boolean) {
|
||||
|
||||
// Replace this for connection logic
|
||||
if (thisNode == undefined && otherNode == undefined && isThisSource == false) {
|
||||
console.warn("Impossible node connection called in EndConnects")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user