Skip to content

Commit

Permalink
Merge pull request #39 from MertenD/38-implement-dynamic-connectionru…
Browse files Browse the repository at this point in the history
…les-for-multiple-nodetypes

38 implement dynamic connectionrules for multiple nodetypes
  • Loading branch information
MertenD authored Aug 27, 2023
2 parents 1e1a950 + 4fd1e33 commit b086eab
Show file tree
Hide file tree
Showing 23 changed files with 588 additions and 68 deletions.
13 changes: 7 additions & 6 deletions src/components/editor/pages/canvas/DragAndDropFlow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import './edges/EdgeGradientStyles.css'
import OptionsToolbar from "@/components/editor/pages/canvas/toolbars/OptionsToolbar";
import {usePlayStore} from "@/stores/editor/PlayStore";
import OnCanvasNodesToolbar from "@/components/editor/pages/canvas/toolbars/OnCanvasNodesSelector";
import {connectionRules} from "@/config/ConnectionRules";
import {getConnectionRule} from "@/config/ConnectionRules";
import {nodesMetadataMap} from "@/config/NodesMetadata";
import {NodeType} from "@/config/NodeType";
import {selectedColor, toolbarBackgroundColor} from "@/config/colors";
Expand All @@ -36,11 +36,11 @@ const selector = (state: any) => ({
nodeTypes: state.nodeTypes,
edgeTypes: state.edgeTypes,
getNodeById: state.getNodeById,
setCurrentConnectionStartNodeType: state.setCurrentConnectionStartNodeType
setCurrentConnectionStartNode: state.setCurrentConnectionStartNode
});

export default function DragAndDropFlow() {
const { nodes, edges, onNodesChange, onEdgesChange, onConnect, nodeTypes, edgeTypes, getNodeById, setCurrentConnectionStartNodeType } = useReactFlowStore(selector, shallow)
const { nodes, edges, onNodesChange, onEdgesChange, onConnect, nodeTypes, edgeTypes, getNodeById, setCurrentConnectionStartNode } = useReactFlowStore(selector, shallow)

const setSelectedNodes = useReactFlowStore((state) => state.setSelectedNodes)

Expand Down Expand Up @@ -82,12 +82,12 @@ export default function DragAndDropFlow() {

const onConnectStart = useCallback((event: any, node: OnConnectStartParams) => {
connectStartParams.current = node;
setCurrentConnectionStartNodeType(getNodeById(node.nodeId)?.type)
setCurrentConnectionStartNode(getNodeById(node.nodeId))
}, []);

const onConnectEnd = useCallback(
(event: any) => {
setCurrentConnectionStartNodeType(null)
setCurrentConnectionStartNode(null)
const targetIsPane = event.target.classList.contains('react-flow__pane');
const targetIsChallengeNode = event.target.parentElement.classList.contains("react-flow__node-challengeNode")

Expand Down Expand Up @@ -173,12 +173,13 @@ export default function DragAndDropFlow() {

if (nodeType !== null && connectStartParams.current !== null && connectStartParams.current?.nodeId !== null) {
const id = addNodeAtPosition(reactFlowInstance.project(lastEventPosition), nodeType)
let rule = connectionRules.get(nodeType)
let rule = getConnectionRule(nodeType, id)
if(rule && rule.inputRules && rule.inputRules.length > 0) {
onConnect({
source: connectStartParams.current?.nodeId,
target: id,
sourceHandle: connectStartParams.current?.handleId,
// TODO Eventuell abändern, sobald ich dynamische nodes in dem onCanvasSelect drin hab
targetHandle: rule.inputRules[0].handleId
} as Connection)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,3 +70,11 @@
stroke-width: 2;
stroke-opacity: 0.75;
}

/* DatabaseHighlight Edge */
/*noinspection ALL,CssUnusedSymbol*/
.react-flow__edge.database .react-flow__edge-path {
stroke: url(#database-edge-gradient);
stroke-width: 2;
stroke-opacity: 0.75;
}
3 changes: 2 additions & 1 deletion src/components/editor/pages/canvas/edges/Edges.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ export const highlightEdges: HighlightEdgeInfoTypes = {
[OutputValueType.NONE]: createEdge(defaultEdgeColor, defaultEdgeColor, OutputValueType.NONE),
[OutputValueType.TEXT]: createEdge("#7CFC00", "#7CFC00", OutputValueType.TEXT),
[OutputValueType.JSON]: createEdge("#87CEEB", "#87CEEB", OutputValueType.JSON),
[OutputValueType.HTML]: createEdge("#FF00FF", "#FF00FF", OutputValueType.HTML)
[OutputValueType.HTML]: createEdge("#FF00FF", "#FF00FF", OutputValueType.HTML),
[OutputValueType.DATABASE]: createEdge("#FFFF00", "#FFFF00", OutputValueType.DATABASE)
}

type HighlightEdgeInfoTypes = {
Expand Down
136 changes: 136 additions & 0 deletions src/components/editor/pages/canvas/nodes/DatabaseTableNode.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
// --- Data ---
import {
createDynamicNodeComponent,
createNodeShapeStyle,
createOptionsComponent
} from "@/components/editor/pages/canvas/nodes/util/Creators";
import {NodeType} from "@/config/NodeType";
import React, {useEffect} from "react";
import {NodeMetadata} from "@/config/NodesMetadata";
import {DynamicNodeData, NodeData} from "@/model/NodeData";
import {EngineDatabaseTableNode} from "@/engine/nodes/EngineDatabaseTableNode";
import {Button, IconButton} from "@mui/material";
import {InputRule} from "@/config/ConnectionRules";
import {OutputValueType} from "@/config/OutputValueType";
import TextInputOption from "@/components/form/TextInputOption";
import StorageIcon from '@mui/icons-material/Storage';
import RowOptionsContainer from "@/components/form/RowOptionsContainer";
import DeleteForeverIcon from '@mui/icons-material/DeleteForever';
import MultiSelectOption from "@/components/form/MultiSelectOption";
import useReactFlowStore from "@/stores/editor/ReactFlowStore";

export interface DatabaseTableNodeData extends DynamicNodeData {
tableName: string
}

// --- Style ---
export const DatabaseTableShapeStyle = createNodeShapeStyle({
height: 70,
width: 100,
display: "flex",
flexDirection: "column",
alignItems: "center"
})

// --- Node ---
export const DatabaseTableNode = createDynamicNodeComponent<DynamicNodeData>(
NodeType.DATABASE_TABLE_NODE,
DatabaseTableShapeStyle,
() => {
return <div style={{
width: 60,
height: 60,
display: "flex",
justifyContent: "center",
alignItems: "center",
textAlign: "center"
}}>
<StorageIcon />
</div>
}
)

// --- Options ---
export const DatabaseTableOptions = createOptionsComponent<DatabaseTableNodeData>("Database Table", ({ id, data, onDataUpdated }) => {

const [inputs, setInputs] = React.useState<InputRule[]>(data.connectionRule ? [...data.connectionRule.inputRules] : [])

function addInput() {
setInputs([...inputs, {
handleId: "",
allowedValueTypes: [
OutputValueType.HTML
],
maxConnections: 999
} as InputRule])
}

useEffect(() => {
onDataUpdated("connectionRule", {
...data.connectionRule,
inputRules: inputs,
outputValueType: OutputValueType.DATABASE
})
}, [inputs]);

return <>
<TextInputOption
label="Name"
value={data.tableName}
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
onDataUpdated("tableName", event.target.value)
}}
/>
{
inputs.map((input: InputRule, index: number) => {
return <RowOptionsContainer>
<TextInputOption
key={index}
label={"Input Name"}
value={input.handleId}
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
const cleanedValue = event.target.value.replaceAll(" ", "-")
useReactFlowStore.getState().replaceEdgeAfterHandleRename(id, input.handleId, cleanedValue)
const newInputs = [...inputs]
newInputs[index].handleId = cleanedValue
setInputs(newInputs)
}}
/>
<MultiSelectOption
values={Object.values(OutputValueType)}
selectedValues={input.allowedValueTypes}
onSelectionChanged={(newSelection: string[]) => {
const newInputs = [...inputs]
newInputs[index].allowedValueTypes = newSelection.map(selection => selection as OutputValueType)
setInputs(newInputs)
}}
/>
<IconButton onClick={
() => {
const newInputs = [...inputs]
newInputs.splice(index, 1)
setInputs(newInputs)
}
} style={{ width: 40, height: 40 }} >
<DeleteForeverIcon />
</IconButton>
</RowOptionsContainer>
})
}
<Button onClick={addInput}>Add Input</Button>
</>
})


// --- Metadata ---
export const DatabaseTableNodeMetadata = {
title: "Database Table",
type: NodeType.DATABASE_TABLE_NODE,
getNodeComponent: DatabaseTableNode,
getOptionsComponent: (id: string) => <DatabaseTableOptions id={id} />,
style: DatabaseTableShapeStyle(true),
icon: <StorageIcon />,
getEngineNode: (id: string, data: NodeData) => {
return new EngineDatabaseTableNode(id, data as DatabaseTableNodeData)
}
} as NodeMetadata
5 changes: 3 additions & 2 deletions src/components/editor/pages/canvas/nodes/ExtractorNode.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// --- Data ---
import {
createNodeComponent,
createStaticNodeComponent,
createNodeShapeStyle,
createOptionsComponent
} from "@/components/editor/pages/canvas/nodes/util/Creators";
Expand All @@ -23,7 +23,7 @@ export interface ExtractorNodeData extends NodeData {
export const extractorShapeStyle = createNodeShapeStyle()

// --- Node ---
export const ExtractorNode = createNodeComponent<ExtractorNodeData>(
export const ExtractorNode = createStaticNodeComponent<ExtractorNodeData>(
NodeType.EXTRACTOR_NODE,
extractorShapeStyle,
() => {
Expand Down Expand Up @@ -56,6 +56,7 @@ export const ExtractorOptions = createOptionsComponent<ExtractorNodeData>("Extra
onSelectionChanged={(newSelection: string) => {
onDataUpdated("extractionMode", newSelection)
}}
label={"Select what should be extracted"}
/>
{ data.extractionMode === ExtractionMode.ATTRIBUTE && <TextInputOption
label={"Attribute Name"}
Expand Down
4 changes: 2 additions & 2 deletions src/components/editor/pages/canvas/nodes/FetchWebsiteNode.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { Badge, Button, IconButton, Typography } from "@mui/material";
import CloudDownloadTwoToneIcon from '@mui/icons-material/CloudDownloadTwoTone';
import {NodeData} from "@/model/NodeData";
import {
createNodeComponent,
createStaticNodeComponent,
createNodeShapeStyle,
createOptionsComponent
} from "@/components/editor/pages/canvas/nodes/util/Creators";
Expand All @@ -31,7 +31,7 @@ export const fetchWebsiteShapeStyle = createNodeShapeStyle({


// --- Node ---
export const FetchWebsiteNode = createNodeComponent<FetchWebsiteNodeData>(
export const FetchWebsiteNode = createStaticNodeComponent<FetchWebsiteNodeData>(
NodeType.FETCH_WEBSITE_NODE,
fetchWebsiteShapeStyle,
(id, selected, data) => {
Expand Down
4 changes: 2 additions & 2 deletions src/components/editor/pages/canvas/nodes/HtmlToTextNode.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import React from "react";
import {NodeData} from "@/model/NodeData";
import {
createNodeComponent,
createStaticNodeComponent,
createNodeShapeStyle,
createOptionsComponent
} from "@/components/editor/pages/canvas/nodes/util/Creators";
Expand All @@ -26,7 +26,7 @@ export const htmlToTextShapeStyle = createNodeShapeStyle()


// --- Node ---
export const HtmlToTextNode = createNodeComponent<HtmlToTextNodeData>(
export const HtmlToTextNode = createStaticNodeComponent<HtmlToTextNodeData>(
NodeType.HTML_TO_TEXT_NODE,
htmlToTextShapeStyle,
() => {
Expand Down
4 changes: 2 additions & 2 deletions src/components/editor/pages/canvas/nodes/SaveNode.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {NodeData} from "@/model/NodeData";
import FileNameInputOption from "@/components/form/FileNameInputOption";
import TextInputOption from "@/components/form/TextInputOption";
import {
createNodeComponent,
createStaticNodeComponent,
createNodeShapeStyle,
createOptionsComponent
} from "@/components/editor/pages/canvas/nodes/util/Creators";
Expand All @@ -28,7 +28,7 @@ export const saveShapeStyle = createNodeShapeStyle()


// --- Node ---
export const SaveNode = createNodeComponent<SaveNodeData>(
export const SaveNode = createStaticNodeComponent<SaveNodeData>(
NodeType.SAVE_NODE,
saveShapeStyle,
() => {
Expand Down
4 changes: 2 additions & 2 deletions src/components/editor/pages/canvas/nodes/StartNode.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {LoadingButton} from "@mui/lab";
import PlayCircleFilledIcon from '@mui/icons-material/PlayCircleFilled';
import {usePlayStore} from "@/stores/editor/PlayStore";
import {
createNodeComponent,
createStaticNodeComponent,
createNodeShapeStyle,
createOptionsComponent
} from "@/components/editor/pages/canvas/nodes/util/Creators";
Expand All @@ -27,7 +27,7 @@ export const startShapeStyle = createNodeShapeStyle({


// --- Node ---
export const StartNode = createNodeComponent<StartNodeData>(
export const StartNode = createStaticNodeComponent<StartNodeData>(
NodeType.START_NODE,
startShapeStyle,
() => {
Expand Down
4 changes: 2 additions & 2 deletions src/components/editor/pages/canvas/nodes/ZipNode.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import React from "react";
import {NodeData} from "@/model/NodeData";
import {
createNodeComponent,
createStaticNodeComponent,
createNodeShapeStyle,
createOptionsComponent
} from "@/components/editor/pages/canvas/nodes/util/Creators";
Expand All @@ -24,7 +24,7 @@ export const zipShapeStyle = createNodeShapeStyle({})


// --- Node ---
export const ZipNode = createNodeComponent<ZipNodeData>(
export const ZipNode = createStaticNodeComponent<ZipNodeData>(
NodeType.ZIP_NODE,
zipShapeStyle,
() => {
Expand Down
Loading

0 comments on commit b086eab

Please sign in to comment.