Skip to content

Commit

Permalink
Merge pull request #8 from jakemoresca/action-pane
Browse files Browse the repository at this point in the history
working add and delete node functions
  • Loading branch information
jakemoresca authored Jul 15, 2024
2 parents bfc42a0 + c7e5f80 commit 66e1b05
Show file tree
Hide file tree
Showing 8 changed files with 274 additions and 98 deletions.
24 changes: 23 additions & 1 deletion actionFlow.editor/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion actionFlow.editor/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,14 @@
"flowbite-react": "^0.10.1",
"next": "14.1.3",
"react": "^18",
"react-dom": "^18"
"react-dom": "^18",
"uuid": "^10.0.0"
},
"devDependencies": {
"@types/node": "^20",
"@types/react": "^18",
"@types/react-dom": "^18",
"@types/uuid": "^10.0.0",
"autoprefixer": "^10.4.19",
"eslint": "^8",
"eslint-config-next": "14.1.3",
Expand Down
190 changes: 96 additions & 94 deletions actionFlow.editor/src/components/Flow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,28 +9,106 @@ import {
useEdgesState,
type OnConnect,
Panel,
Node,
Edge,
getIncomers,
getOutgoers,
getConnectedEdges,
} from "@xyflow/react";

import "@xyflow/react/dist/style.css";

import { initialNodes, nodeTypes, type CustomNodeType } from "./nodes";
import { initialEdges, edgeTypes, type CustomEdgeType } from "./edges";
import { Button, Drawer, Modal } from "flowbite-react";
import ActionDrawer from "./left-pane/ActionDrawer";
import AddActionModal from "./left-pane/AddActionModal";
import { generateNode } from "@/modules/nodes/node-generator";

export default function App() {
const [nodes, , onNodesChange] = useNodesState<CustomNodeType>(initialNodes);
const [nodes, setNodes, onNodesChange] =
useNodesState<CustomNodeType>(initialNodes);
const [edges, setEdges, onEdgesChange] =
useEdgesState<CustomEdgeType>(initialEdges);
const onConnect: OnConnect = useCallback(
(connection) => setEdges((edges) => addEdge(connection, edges)),
[setEdges]
);

const [openModal, setOpenModal] = useState(false);
const [showAddActionModal, setOpenAddActionModal] = useState(false);
const [selectedNodes, setSelectedNodes] = useState<Node[]>([]);

const actionIcon = (<svg className="w-6 h-6 text-gray-800 dark:text-white" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 24 24">
<path fillRule="evenodd" d="M3 4a1 1 0 0 0-.822 1.57L6.632 12l-4.454 6.43A1 1 0 0 0 3 20h13.153a1 1 0 0 0 .822-.43l4.847-7a1 1 0 0 0 0-1.14l-4.847-7a1 1 0 0 0-.822-.43H3Z" clipRule="evenodd" />
</svg>)
const handleAddAction = () => {
setOpenAddActionModal(true);
};

const handleCloseAddActionModal = () => {
setOpenAddActionModal(false);
};

const handleFlowSelectionChange = (params: {
nodes: Node[];
edges: Edge[];
}) => {
setSelectedNodes(params.nodes);
};

const handleDeleteNodes = useCallback(() => {
selectedNodes.forEach((selectedNode) => {
setEdges((edges) => {
const incomers = getIncomers(selectedNode, nodes, edges);
const outgoers = getOutgoers(selectedNode, nodes, edges);
const connectedEdges = getConnectedEdges([selectedNode], edges);

const remainingEdges = edges.filter(
(edge) => !connectedEdges.includes(edge)
);

const createdEdges = incomers.flatMap(({ id: source }) =>
outgoers.map(({ id: target }) => ({
id: `${source}->${target}`,
source,
target,
}))
);

return [...remainingEdges, ...createdEdges];
});
});

setNodes((nodes) =>
nodes.filter(
(node) =>
selectedNodes.find((selected) => selected.id == node.id) == undefined
)
);
}, [selectedNodes, nodes, edges, setNodes, setEdges]);

const handleAddNode = useCallback((nodeType: string) => {
const parentNode = selectedNodes[0];
const nodeToAdd = generateNode(nodeType, parentNode);

setNodes((nds) => nds.concat(nodeToAdd));

setEdges((edges) => {
const connectedEdges = getConnectedEdges([parentNode], edges);

const remainingEdges = edges.map(
(edge) => connectedEdges.includes(edge) && edge.source === parentNode.id ? {
...edge,
id: `${nodeToAdd.id}->${edge.target}`,
source: nodeToAdd.id
} : edge
);

const createdEdges = [{ id: `${parentNode.id}->${nodeToAdd.id}`, source: parentNode.id, target: nodeToAdd.id, animated: false }]

return [...remainingEdges, ...createdEdges];
});

setSelectedNodes([nodeToAdd]);
setOpenAddActionModal(false);

}, [selectedNodes, nodes, edges, setNodes, setEdges, setSelectedNodes, setOpenAddActionModal])

return (
<ReactFlow<CustomNodeType, CustomEdgeType>
Expand All @@ -44,104 +122,28 @@ export default function App() {
nodesConnectable={true}
nodesDraggable={true}
elementsSelectable={true}
onSelectionChange={handleFlowSelectionChange}
fitView
className="bg-white dark:bg-gray-900 antialiased"
>
<Background />
<MiniMap />

<Panel position="top-left">

<Drawer open={true} onClose={() => { }} backdrop={false}>
<Drawer.Header title="Drawer" />
<Drawer.Items>
<p className="mb-6 text-sm text-gray-500 dark:text-gray-400">
Supercharge your hiring by taking advantage of our&nbsp;
<a href="#" className="text-cyan-600 underline hover:no-underline dark:text-cyan-500">
limited-time sale
</a>
&nbsp;for Flowbite Docs + Job Board. Unlimited access to over 190K top-ranked candidates and the #1 design
job board.
</p>
<div className="grid grid-cols-1 gap-4 md:grid-cols-2">
<a
href="#"
className="rounded-lg border border-gray-200 bg-white px-4 py-2 text-center text-sm font-medium text-gray-900 hover:bg-gray-100 hover:text-cyan-700 focus:z-10 focus:outline-none focus:ring-4 focus:ring-gray-200 dark:border-gray-600 dark:bg-gray-800 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-white dark:focus:ring-gray-700"
>
Learn more
</a>
<a
href="#"
className="inline-flex items-center rounded-lg bg-cyan-700 px-4 py-2 text-center text-sm font-medium text-white hover:bg-cyan-800 focus:outline-none focus:ring-4 focus:ring-cyan-300 dark:bg-cyan-600 dark:hover:bg-cyan-700 dark:focus:ring-cyan-800"
>
Get access&nbsp;
<svg
className="ms-2 h-3.5 w-3.5 rtl:rotate-180"
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 14 10"
>
<path
stroke="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
d="M1 5h12m0 0L9 1m4 4L9 9"
/>
</svg>
</a>
</div>

<Button.Group>
<Button color="gray" onClick={() => setOpenModal(true)}>Add</Button>
<Button color="gray">Delete</Button>
</Button.Group>

</Drawer.Items>
</Drawer>
<ActionDrawer
onAddAction={handleAddAction}
selectedNodes={selectedNodes}
onDeleteAction={handleDeleteNodes}
/>
</Panel>

<Controls />

<Modal show={openModal} size="xl" onClose={() => setOpenModal(false)} popup>
<Modal.Header />
<Modal.Body>
<div className="grid grid-cols-3 gap-4 p-4 lg:grid-cols-4">
<div className="cursor-pointer rounded-lg bg-gray-50 p-4 hover:bg-gray-100 dark:bg-gray-700 dark:hover:bg-gray-600">
<div className="mx-auto mb-2 flex h-[48px] max-h-[48px] w-[48px] max-w-[48px] items-center justify-center rounded-full bg-gray-200 p-2 dark:bg-gray-600">
{actionIcon}
</div>
<div className="text-center font-medium text-gray-500 dark:text-gray-400">Set Variable</div>
</div>
<div className="cursor-pointer rounded-lg bg-gray-50 p-4 hover:bg-gray-100 dark:bg-gray-700 dark:hover:bg-gray-600">
<div className="mx-auto mb-2 flex h-[48px] max-h-[48px] w-[48px] max-w-[48px] items-center justify-center rounded-full bg-gray-200 p-2 dark:bg-gray-600">
{actionIcon}
</div>
<div className="text-center font-medium text-gray-500 dark:text-gray-400">Loop</div>
</div>
<div className="cursor-pointer rounded-lg bg-gray-50 p-4 hover:bg-gray-100 dark:bg-gray-700 dark:hover:bg-gray-600">
<div className="mx-auto mb-2 flex h-[48px] max-h-[48px] w-[48px] max-w-[48px] items-center justify-center rounded-full bg-gray-200 p-2 dark:bg-gray-600">
{actionIcon}
</div>
<div className="text-center font-medium text-gray-500 dark:text-gray-400">Http Call</div>
</div>
<div className="cursor-pointer rounded-lg bg-gray-50 p-4 hover:bg-gray-100 dark:bg-gray-700 dark:hover:bg-gray-600">
<div className="mx-auto mb-2 flex h-[48px] max-h-[48px] w-[48px] max-w-[48px] items-center justify-center rounded-full bg-gray-200 p-2 dark:bg-gray-600">
{actionIcon}
</div>
<div className="text-center font-medium text-gray-500 dark:text-gray-400">Control Flow</div>
</div>
<div className="cursor-pointer rounded-lg bg-gray-50 p-4 hover:bg-gray-100 dark:bg-gray-700 dark:hover:bg-gray-600">
<div className="mx-auto mb-2 flex h-[48px] max-h-[48px] w-[48px] max-w-[48px] items-center justify-center rounded-full bg-gray-200 p-2 dark:bg-gray-600">
{actionIcon}
</div>
<div className="text-center font-medium text-gray-500 dark:text-gray-400">Call Workflow</div>
</div>
</div>
</Modal.Body>
</Modal>
<Controls className="left-80" />

<AddActionModal
showModal={showAddActionModal}
onCloseModal={handleCloseAddActionModal}
onAddNode={handleAddNode}
/>
</ReactFlow>
);
}
39 changes: 39 additions & 0 deletions actionFlow.editor/src/components/left-pane/ActionDrawer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { Drawer, Button } from "flowbite-react"
import { Node } from "@xyflow/react";

export type ActionDrawerData = {
onAddAction?: () => void;
onDeleteAction?: () => void;
selectedNodes?: Node[];
}

export default function ActionDrawer({ onAddAction: addAction, onDeleteAction, selectedNodes }: ActionDrawerData) {

const validNodeTypesToDelete = [
"variable"
]

const validNodeTypesToAddTo = [
"input",
"variable"
]

const canDelete = selectedNodes && selectedNodes.length > 0 && selectedNodes?.every(x => {
return x.type && validNodeTypesToDelete.includes(x.type);
})

const canAdd = selectedNodes && selectedNodes.length == 1 && selectedNodes[0].type && validNodeTypesToAddTo.includes(selectedNodes[0].type);

return (
<Drawer open={true} onClose={() => { }} backdrop={false}>
<Drawer.Header title="Action Flow Editor" />
<Drawer.Items>
<Button.Group>
<Button color="gray" disabled={!canAdd} onClick={() => addAction && addAction()}>Add</Button>
<Button color="gray" disabled={!canDelete} onClick={() => onDeleteAction && onDeleteAction()}>Delete</Button>
</Button.Group>

</Drawer.Items>
</Drawer>
)
}
Loading

0 comments on commit 66e1b05

Please sign in to comment.