Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Migrate/xyflow #79

Merged
merged 8 commits into from
Sep 2, 2024
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
fix: fix flow clear bug & add chatsky info
mxerf committed Aug 30, 2024

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
commit 5fdef6e93049e3e4d2d49a389a6fd44a756f8ccd
2 changes: 1 addition & 1 deletion frontend/src/UI/Preloader/preloader.css
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#preloader-wrapper {
background-color: hsl(var(--background));
background-color: var(--background);
position: absolute;
top: 0;
left: 0;
2 changes: 0 additions & 2 deletions frontend/src/api/index.ts
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import axios from "axios"
import { DEV, VITE_BASE_API_URL } from "../env.consts"

const clearUrlFromQueries = (url: string) => {}

// const baseURL = VITE_BASE_API_URL ?? "http://localhost:8000/api/v1"
const baseURL = DEV
? VITE_BASE_API_URL
: window.location.protocol + "//" + window.location.host + "/api/v1"
18 changes: 13 additions & 5 deletions frontend/src/components/header/Header.tsx
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Button, Popover, PopoverContent, PopoverTrigger, Tooltip } from "@nextui-org/react"
import classNames from "classnames"
import { InfoIcon } from "lucide-react"
import { Github, InfoIcon } from "lucide-react"
import { memo, useContext, useMemo } from "react"
import { Link, useLocation } from "react-router-dom"
import { flowContext } from "../../contexts/flowContext"
@@ -116,12 +116,20 @@ const Header = memo(() => {
Chatsky UI
</h4>
<div className='w-full mt-2 [&>p]:text-[12px] flex flex-col items-start justify-start '>
<p>
<p className='mb-1'>
<strong className='text-[14px]'>Version:</strong> {version}
</p>
<p>
<strong className='text-[14px]'>Date:</strong> 31.08.2024
</p>
<a
className='w-full flex items-center justify-center gap-1 rounded-lg border p-1 border-border transition-colors hover:border-node-selected'
href='https://github.com/deeppavlov/chatsky-ui'>
<Github className='w-4 h-4' />
<p className='text-[12px]'>GitHub</p>
</a>
<a
className='w-full flex items-center justify-center gap-1 rounded-lg border p-1 border-border transition-colors hover:border-node-selected mt-1 mb-1'
href='https://deeppavlov.ai'>
<p className='text-[12px]'>DeepPavlov.ai</p>
</a>
</div>
</PopoverContent>
</Popover>
9 changes: 5 additions & 4 deletions frontend/src/components/nodes/LinkNode.tsx
100644 → 100755
Original file line number Diff line number Diff line change
@@ -36,6 +36,7 @@ const LinkNode = memo(({ data }: { data: LinkNodeDataType }) => {
const [toFlow, setToFlow] = useState<FlowType>()
const [toNode, setToNode] = useState<AppNode>()
const [error, setError] = useState(false)
const [r, setR] = useState(0)
const { notification: n } = useContext(NotificationsContext)
// const { openPopUp } = useContext(PopUpContext)

@@ -75,6 +76,9 @@ const LinkNode = memo(({ data }: { data: LinkNodeDataType }) => {
)

useEffect(() => {
if (r === 0) {
return setR((r) => r + 1)
}
if (!TO_FLOW || !TO_NODE) {
setError(true)
n.add({
@@ -110,10 +114,7 @@ const LinkNode = memo(({ data }: { data: LinkNodeDataType }) => {
<>
<div
onDoubleClick={onOpen}
className={classNames(
'default_node px-6 py-4',
error && "border-error"
)}>
className={classNames("default_node px-6 py-4", error && "border-error")}>
<div className=' w-full h-1/3 flex justify-between items-center bg-node rounded-node'>
<Handle
isConnectableEnd
1 change: 1 addition & 0 deletions frontend/src/contexts/flowContext.tsx
Original file line number Diff line number Diff line change
@@ -111,6 +111,7 @@ export const FlowProvider = ({ children }: { children: React.ReactNode }) => {
const { screenLoading } = useContext(MetaContext)

useEffect(() => {
setReactFlowInstance(null)
setTab(flowId || "")
}, [flowId])

125 changes: 43 additions & 82 deletions frontend/src/pages/Flow.tsx
100644 → 100755
Original file line number Diff line number Diff line change
@@ -11,9 +11,9 @@ import {
addEdge,
reconnectEdge,
useEdgesState,
useNodesState
useNodesState,
} from "@xyflow/react"
import React, { useCallback, useContext, useEffect, useMemo, useRef, useState } from "react"
import React, { useCallback, useContext, useEffect, useRef, useState } from "react"

import { a, useTransition } from "@react-spring/web"
import "@xyflow/react/dist/style.css"
@@ -51,14 +51,15 @@ const edgeTypes = {
default: CustomEdge,
}

const untrackedFields = ["position", "positionAbsolute", "targetPosition", "sourcePosition"]

// export const addNodeToGraph = (node: NodeType, graph: FlowType[]) => {}

export default function Flow() {

const { flows, updateFlow, saveFlows, reactFlowInstance, setReactFlowInstance, validateDeletion } =
useContext(flowContext)
const {
flows,
updateFlow,
saveFlows,
reactFlowInstance,
setReactFlowInstance,
validateDeletion,
} = useContext(flowContext)
const {
toggleWorkspaceMode,
workspaceMode,
@@ -69,7 +70,7 @@ export default function Flow() {
managerMode,
} = useContext(workspaceContext)
const { screenLoading } = useContext(MetaContext)
const { takeSnapshot, undo, copy, paste, copiedSelection } = useContext(undoRedoContext)
const { takeSnapshot, copy, paste, copiedSelection } = useContext(undoRedoContext)

const { flowId } = useParams()

@@ -78,25 +79,15 @@ export default function Flow() {
const [nodes, setNodes, onNodesChange] = useNodesState(flow?.data.nodes || [])
const [edges, setEdges, onEdgesChange] = useEdgesState(flow?.data.edges || [])

// const [reactFlowInstance, setReactFlowInstance] = useState<ReactFlowInstance>()
const [selection, setSelection] = useState<OnSelectionChangeParams>()
const [selected, setSelected] = useState<string>()
const isEdgeUpdateSuccess = useRef(false)
const { notification: n } = useContext(NotificationsContext)

// const {
// isOpen: isLinkModalOpen,
// onOpen: onLinkModalOpen,
// onClose: onLinkModalClose,
// } = useDisclosure()

const handleUpdateFlowData = useCallback(() => {
if (reactFlowInstance && flow && flow.name === flowId) {
// const _node = reactFlowInstance.getNodes()[0]
// if (_node && _node.id === flow.data.nodes[0].id) {
flow.data = reactFlowInstance.toObject() as ReactFlowJsonObject<AppNode, Edge>
updateFlow(flow)
// }
}
}, [flow, flowId, reactFlowInstance, updateFlow])

@@ -106,55 +97,33 @@ export default function Flow() {
if (_node && _node.id === flow.data.nodes[0].id) {
flow.data = reactFlowInstance.toObject() as ReactFlowJsonObject<AppNode, Edge>
updateFlow(flow)
// const links: Node<NodeDataType>[] = flow.data.nodes.filter(
// (node) => node.type === "link_node"
// )
// links.forEach((link) => {
// if (
// !flows.find((fl) => link.data.transition.target_flow === fl.name) ||
// !flows.find((fl) =>
// fl.data.nodes.some((node) => node.id === link.data.transition.target_node)
// )
// ) {
// n.add({
// message: `Link ${link.data.id} is broken! Please configure it again.`,
// title: "Link error",
// type: "error",
// })
// }
// })
}
}
}, [flow, flowId, flows, reactFlowInstance, updateFlow])

const filteredNodes = useMemo(() => {
return nodes.map((obj) => {
return Object.fromEntries(
Object.entries(obj).filter(([key]) => !untrackedFields.includes(key))
)
})
}, [nodes])
}, [flow, flowId, reactFlowInstance, updateFlow])

useEffect(() => {
handleUpdateFlowData()
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [edges, nodes.length])

useEffect(() => {
if (reactFlowInstance && flow?.name === flowId) {
setNodes(flow?.data?.nodes ?? [])
setEdges(flow?.data?.edges ?? [])
if (flow?.data?.viewport) {
if (flow && reactFlowInstance && flow.name === flowId) {
setNodes(flow.data.nodes)
setEdges(flow.data.edges)
if (flow.data.viewport) {
// reactFlowInstance.fitView({ padding: 0.5 })
} else {
reactFlowInstance.fitView({ padding: 0.5 })
}
}
}, [flow, flowId, reactFlowInstance, setEdges, setNodes])

const onInit = useCallback((e: CustomReactFlowInstanceType) => {
setReactFlowInstance(e)
}, [])
const onInit = useCallback(
(e: CustomReactFlowInstanceType) => {
setReactFlowInstance(e)
},
[setReactFlowInstance]
)

const onNodesChangeMod = useCallback(
(nds: NodeChange<AppNode>[]) => {
@@ -198,17 +167,6 @@ export default function Flow() {
[setEdges]
)

// const onEdgeUpdateEnd = useCallback(
// (event: MouseEvent | TouchEvent, edge: Edge) => {
// // takeSnapshot()
// // if (!isEdgeUpdateSuccess.current) {
// // setEdges((eds) => eds.filter((ed) => ed.id !== edge.id))
// // }
// },
// // eslint-disable-next-line react-hooks/exhaustive-deps
// [onEdgeUpdate]
// )

const onNodeClick = useCallback(
(event: React.MouseEvent, node: AppNode) => {
const node_ = node as AppNode
@@ -247,23 +205,16 @@ export default function Flow() {
(event: React.DragEvent<HTMLDivElement>) => {
event.preventDefault()
takeSnapshot()

const type: NodesTypes = event.dataTransfer.getData("application/@xyflow/react") as NodesTypes

// check if the dropped element is valid
if (typeof type === "undefined" || !type || !reactFlowInstance) {
return
}

// reactFlowInstance.project was renamed to reactFlowInstance.screenToFlowPosition
// and you don't need to subtract the reactFlowBounds.left/top anymore
// details: https://@xyflow/react.dev/whats-new/2023-11-10
const position = reactFlowInstance.screenToFlowPosition({
x: event.clientX,
y: event.clientY,
})
const newId = type + "_" + v4()

const START_FALLBACK_FLAGS = []
if (
!flows.some((flow) =>
@@ -283,17 +234,18 @@ export default function Flow() {
) {
START_FALLBACK_FLAGS.push("fallback")
}

let newNode = {} as AppNode
if (type === 'default_node') {
if (type === "default_node") {
newNode = {
id: newId,
type,
position,
dragHandle: NODES[type].dragHandle,
data: {
id: newId,
name:NODE_NAMES.find((name) => !nodes.some((node) => node.data.name === name)) ?? "Empty names array",
name:
NODE_NAMES.find((name) => !nodes.some((node) => node.data.name === name)) ??
"Empty names array",
flags: START_FALLBACK_FLAGS,
conditions: NODES[type].conditions,
global_conditions: [],
@@ -302,11 +254,25 @@ export default function Flow() {
},
}
}
if (type === "link_node") {
newNode = {
id: newId,
type,
position,
data: {
id: newId,
name: "Link",
transition: {
target_flow: "",
target_node: "",
},
},
}
}


setNodes((nds) => nds.concat(newNode))
},
[takeSnapshot, reactFlowInstance, flows, setNodes]
[takeSnapshot, reactFlowInstance, flows, setNodes, nodes]
)

const [mousePos, setMousePos] = useState({ x: 0, y: 0 })
@@ -321,12 +287,7 @@ export default function Flow() {
}
if ((e.ctrlKey || e.metaKey) && e.key === "v") {
e.preventDefault()
if (
reactFlowInstance &&
flow &&
flow.name === flowId &&
copiedSelection
) {
if (reactFlowInstance && flow && flow.name === flowId && copiedSelection) {
paste(copiedSelection, { x: mousePos.x, y: mousePos.y })
}
}