Commit 1840d05a authored by StyleZhang's avatar StyleZhang

record panel

parent 6d561844
...@@ -57,8 +57,8 @@ const NodeSelector: FC<NodeSelectorProps> = ({ ...@@ -57,8 +57,8 @@ const NodeSelector: FC<NodeSelectorProps> = ({
}, [onOpenChange]) }, [onOpenChange])
const handleTrigger = useCallback<MouseEventHandler<HTMLDivElement>>((e) => { const handleTrigger = useCallback<MouseEventHandler<HTMLDivElement>>((e) => {
e.stopPropagation() e.stopPropagation()
handleOpenChange(!open) setLocalOpen(v => !v)
}, [open, handleOpenChange]) }, [])
const handleSelect = useCallback((type: BlockEnum) => { const handleSelect = useCallback((type: BlockEnum) => {
handleOpenChange(false) handleOpenChange(false)
onSelect(type) onSelect(type)
......
...@@ -14,6 +14,8 @@ import { ArrowNarrowLeft } from '@/app/components/base/icons/src/vender/line/arr ...@@ -14,6 +14,8 @@ import { ArrowNarrowLeft } from '@/app/components/base/icons/src/vender/line/arr
const Header: FC = () => { const Header: FC = () => {
const mode = useStore(state => state.mode) const mode = useStore(state => state.mode)
const setShowFeatures = useStore(state => state.setShowFeatures) const setShowFeatures = useStore(state => state.setShowFeatures)
const runStaus = useStore(state => state.runStaus)
const setRunStaus = useStore(state => state.setRunStaus)
const handleShowFeatures = useCallback(() => { const handleShowFeatures = useCallback(() => {
setShowFeatures(true) setShowFeatures(true)
...@@ -36,13 +38,20 @@ const Header: FC = () => { ...@@ -36,13 +38,20 @@ const Header: FC = () => {
</div> </div>
</div> </div>
<div className='flex items-center'> <div className='flex items-center'>
<Button className={` {
mr-2 px-3 py-0 h-8 bg-white text-[13px] font-medium text-primary-600 runStaus && (
border-[0.5px] border-gray-200 shadow-xs <Button
`}> className={`
<ArrowNarrowLeft className='mr-1 w-4 h-4' /> mr-2 px-3 py-0 h-8 bg-white text-[13px] font-medium text-primary-600
Go back to editor border-[0.5px] border-gray-200 shadow-xs
</Button> `}
onClick={() => setRunStaus('')}
>
<ArrowNarrowLeft className='mr-1 w-4 h-4' />
Go back to editor
</Button>
)
}
<RunAndHistory /> <RunAndHistory />
<div className='mx-2 w-[1px] h-3.5 bg-gray-200'></div> <div className='mx-2 w-[1px] h-3.5 bg-gray-200'></div>
{ {
......
...@@ -4,19 +4,39 @@ import { useStore } from '../store' ...@@ -4,19 +4,39 @@ import { useStore } from '../store'
import { Play } from '@/app/components/base/icons/src/vender/line/mediaAndDevices' import { Play } from '@/app/components/base/icons/src/vender/line/mediaAndDevices'
import { ClockPlay } from '@/app/components/base/icons/src/vender/line/time' import { ClockPlay } from '@/app/components/base/icons/src/vender/line/time'
import TooltipPlus from '@/app/components/base/tooltip-plus' import TooltipPlus from '@/app/components/base/tooltip-plus'
import { Loading02 } from '@/app/components/base/icons/src/vender/line/general'
const RunAndHistory: FC = () => { const RunAndHistory: FC = () => {
const showRunHistory = useStore(state => state.showRunHistory) const showRunHistory = useStore(state => state.showRunHistory)
const setShowRunHistory = useStore(state => state.setShowRunHistory) const setShowRunHistory = useStore(state => state.setShowRunHistory)
const runStaus = useStore(state => state.runStaus)
const setRunStaus = useStore(state => state.setRunStaus)
return ( return (
<div className='flex items-center px-0.5 h-8 rounded-lg border-[0.5px] border-gray-200 bg-white shadow-xs'> <div className='flex items-center px-0.5 h-8 rounded-lg border-[0.5px] border-gray-200 bg-white shadow-xs'>
<div className={` <div
flex items-center px-1.5 h-7 rounded-md text-[13px] font-medium text-primary-600 className={`
hover:bg-primary-50 cursor-pointer flex items-center px-1.5 h-7 rounded-md text-[13px] font-medium text-primary-600
`}> hover:bg-primary-50 cursor-pointer
<Play className='mr-1 w-4 h-4' /> ${runStaus === 'running' && 'bg-primary-50 !cursor-not-allowed'}
Run `}
onClick={() => runStaus !== 'running' && setRunStaus('running')}
>
{
runStaus === 'running'
? (
<>
<Loading02 className='mr-1 w-4 h-4 animate-spin' />
Running
</>
)
: (
<>
<Play className='mr-1 w-4 h-4' />
Run
</>
)
}
</div> </div>
<div className='mx-0.5 w-[0.5px] h-8 bg-gray-200'></div> <div className='mx-0.5 w-[0.5px] h-8 bg-gray-200'></div>
<TooltipPlus <TooltipPlus
......
...@@ -16,7 +16,6 @@ import type { ...@@ -16,7 +16,6 @@ import type {
SelectedNode, SelectedNode,
} from './types' } from './types'
import { NodeInitialData } from './constants' import { NodeInitialData } from './constants'
import { initialNodesPosition } from './utils'
export const useWorkflow = () => { export const useWorkflow = () => {
const store = useStoreApi() const store = useStoreApi()
...@@ -78,14 +77,11 @@ export const useWorkflow = () => { ...@@ -78,14 +77,11 @@ export const useWorkflow = () => {
} = store.getState() } = store.getState()
const newNodes = produce(getNodes(), (draft) => { const newNodes = produce(getNodes(), (draft) => {
const selectedNode = draft.find(node => node.id === nodeId) draft.forEach(node => node.selected = false)
const selectedNode = draft.find(node => node.id === nodeId)!
if (selectedNode) { if (!cancelSelection)
if (cancelSelection) selectedNode.selected = true
selectedNode.selected = false
else
selectedNode.selected = true
}
}) })
setNodes(newNodes) setNodes(newNodes)
}, [store]) }, [store])
...@@ -124,9 +120,9 @@ export const useWorkflow = () => { ...@@ -124,9 +120,9 @@ export const useWorkflow = () => {
setEdges, setEdges,
} = store.getState() } = store.getState()
const newEdges = produce(edges, (draft) => { const newEdges = produce(edges, (draft) => {
const currentEdge = draft.find(e => e.id === edge.id) const currentEdge = draft.find(e => e.id === edge.id)!
if (currentEdge)
currentEdge.data = { ...currentEdge.data, hovering: true } currentEdge.data = { ...currentEdge.data, hovering: true }
}) })
setEdges(newEdges) setEdges(newEdges)
}, [store]) }, [store])
...@@ -137,9 +133,9 @@ export const useWorkflow = () => { ...@@ -137,9 +133,9 @@ export const useWorkflow = () => {
setEdges, setEdges,
} = store.getState() } = store.getState()
const newEdges = produce(edges, (draft) => { const newEdges = produce(edges, (draft) => {
const currentEdge = draft.find(e => e.id === edge.id) const currentEdge = draft.find(e => e.id === edge.id)!
if (currentEdge)
currentEdge.data = { ...currentEdge.data, hovering: false } currentEdge.data = { ...currentEdge.data, hovering: false }
}) })
setEdges(newEdges) setEdges(newEdges)
}, [store]) }, [store])
...@@ -281,59 +277,6 @@ export const useWorkflow = () => { ...@@ -281,59 +277,6 @@ export const useWorkflow = () => {
setEdges(newEdges) setEdges(newEdges)
}, [store]) }, [store])
const handleInitialLayoutNodes = useCallback(() => {
const {
getNodes,
setNodes,
edges,
setEdges,
} = store.getState()
setNodes(initialNodesPosition(getNodes(), edges))
setEdges(produce(edges, (draft) => {
draft.forEach((edge) => {
edge.hidden = false
})
}))
}, [store])
const handleUpdateNodesPosition = useCallback(() => {
const {
getNodes,
setNodes,
} = store.getState()
const nodes = getNodes()
const groups = nodes.reduce((acc, cur) => {
const x = cur.data.position.x
if (!acc[x])
acc[x] = [cur]
else
acc[x].push(cur)
return acc
}, {} as Record<string, Node[]>)
const heightMap: Record<string, number> = {}
Object.keys(groups).forEach((key) => {
let baseHeight = 0
groups[key].sort((a, b) => a.data.position!.y - b.data.position!.y).forEach((node) => {
heightMap[node.id] = baseHeight
baseHeight = node.height! + 39
})
})
setNodes(produce(nodes, (draft) => {
draft.forEach((node) => {
node.position = {
...node.position,
x: node.data.position.x * (220 + 64),
y: heightMap[node.id],
}
})
}))
}, [store])
return { return {
handleEnterNode, handleEnterNode,
handleLeaveNode, handleLeaveNode,
...@@ -346,7 +289,5 @@ export const useWorkflow = () => { ...@@ -346,7 +289,5 @@ export const useWorkflow = () => {
handleAddNextNode, handleAddNextNode,
handleChangeCurrentNode, handleChangeCurrentNode,
handleDeleteNode, handleDeleteNode,
handleInitialLayoutNodes,
handleUpdateNodesPosition,
} }
} }
import type { MouseEvent } from 'react'
import { import {
useCallback, useCallback,
useState, useState,
...@@ -35,10 +36,11 @@ export const NodeTargetHandle = ({ ...@@ -35,10 +36,11 @@ export const NodeTargetHandle = ({
const handleOpenChange = useCallback((v: boolean) => { const handleOpenChange = useCallback((v: boolean) => {
setOpen(v) setOpen(v)
}, []) }, [])
const handleHandleClick = () => { const handleHandleClick = useCallback((e: MouseEvent) => {
e.stopPropagation()
if (!connected) if (!connected)
handleOpenChange(!open) setOpen(v => !v)
} }, [connected])
return ( return (
<> <>
...@@ -93,10 +95,11 @@ export const NodeSourceHandle = ({ ...@@ -93,10 +95,11 @@ export const NodeSourceHandle = ({
const handleOpenChange = useCallback((v: boolean) => { const handleOpenChange = useCallback((v: boolean) => {
setOpen(v) setOpen(v)
}, []) }, [])
const handleHandleClick = () => { const handleHandleClick = useCallback((e: MouseEvent) => {
e.stopPropagation()
if (!connected) if (!connected)
handleOpenChange(!open) setOpen(v => !v)
} }, [connected])
const handleSelect = useCallback((type: BlockEnum) => { const handleSelect = useCallback((type: BlockEnum) => {
handleAddNextNode(id, type, handleId) handleAddNextNode(id, type, handleId)
}, [handleAddNextNode, id, handleId]) }, [handleAddNextNode, id, handleId])
......
...@@ -10,9 +10,11 @@ import { useStore } from '../store' ...@@ -10,9 +10,11 @@ import { useStore } from '../store'
import WorkflowInfo from './workflow-info' import WorkflowInfo from './workflow-info'
import DebugAndPreview from './debug-and-preview' import DebugAndPreview from './debug-and-preview'
import RunHistory from './run-history' import RunHistory from './run-history'
import Record from './record'
const Panel: FC = () => { const Panel: FC = () => {
const mode = useStore(state => state.mode) const mode = useStore(state => state.mode)
const runStaus = useStore(state => state.runStaus)
const nodes = useNodes<CommonNodeType>() const nodes = useNodes<CommonNodeType>()
const selectedNode = nodes.find(node => node.selected) const selectedNode = nodes.find(node => node.selected)
const showRunHistory = useStore(state => state.showRunHistory) const showRunHistory = useStore(state => state.showRunHistory)
...@@ -30,6 +32,11 @@ const Panel: FC = () => { ...@@ -30,6 +32,11 @@ const Panel: FC = () => {
return ( return (
<div className='absolute top-14 right-0 bottom-2 flex z-10'> <div className='absolute top-14 right-0 bottom-2 flex z-10'>
{
runStaus && (
<Record />
)
}
{ {
showNodePanel && ( showNodePanel && (
<NodePanel {...selectedNode!} /> <NodePanel {...selectedNode!} />
......
const Record = () => {
return (
<div className='w-[400px] h-full rounded-2xl border-[0.5px] border-gray-200 shadow-xl bg-white'>
<div className='p-4 pb-1 text-base font-semibold text-gray-900'>
Test Run#5
</div>
</div>
)
}
export default Record
...@@ -8,6 +8,7 @@ import { AlertCircle } from '@/app/components/base/icons/src/vender/line/alertsA ...@@ -8,6 +8,7 @@ import { AlertCircle } from '@/app/components/base/icons/src/vender/line/alertsA
const RunHistory = () => { const RunHistory = () => {
const mode = useStore(state => state.mode) const mode = useStore(state => state.mode)
const setShowRunHistory = useStore(state => state.setShowRunHistory) const setShowRunHistory = useStore(state => state.setShowRunHistory)
const setRunStaus = useStore(state => state.setRunStaus)
return ( return (
<div className='w-[200px] h-full bg-white border-[0.5px] border-gray-200 shadow-xl rounded-l-2xl'> <div className='w-[200px] h-full bg-white border-[0.5px] border-gray-200 shadow-xl rounded-l-2xl'>
...@@ -34,7 +35,10 @@ const RunHistory = () => { ...@@ -34,7 +35,10 @@ const RunHistory = () => {
</div> </div>
) )
} }
<div className='flex px-2 py-[7px] rounded-lg hover:bg-primary-50 cursor-pointer'> <div
className='flex px-2 py-[7px] rounded-lg hover:bg-primary-50 cursor-pointer'
onClick={() => setRunStaus('finished')}
>
<AlertCircle className='mt-0.5 mr-1.5 w-3.5 h-3.5 text-[#F79009]' /> <AlertCircle className='mt-0.5 mr-1.5 w-3.5 h-3.5 text-[#F79009]' />
<div> <div>
<div className='flex items-center text-[13px] font-medium text-primary-600 leading-[18px]'>Test Run#6</div> <div className='flex items-center text-[13px] font-medium text-primary-600 leading-[18px]'>Test Run#6</div>
......
...@@ -4,11 +4,13 @@ type State = { ...@@ -4,11 +4,13 @@ type State = {
mode: string mode: string
showRunHistory: boolean showRunHistory: boolean
showFeatures: boolean showFeatures: boolean
runStaus: string
} }
type Action = { type Action = {
setShowRunHistory: (showRunHistory: boolean) => void setShowRunHistory: (showRunHistory: boolean) => void
setShowFeatures: (showFeatures: boolean) => void setShowFeatures: (showFeatures: boolean) => void
setRunStaus: (runStaus: string) => void
} }
export const useStore = create<State & Action>(set => ({ export const useStore = create<State & Action>(set => ({
...@@ -17,4 +19,6 @@ export const useStore = create<State & Action>(set => ({ ...@@ -17,4 +19,6 @@ export const useStore = create<State & Action>(set => ({
setShowRunHistory: showRunHistory => set(() => ({ showRunHistory })), setShowRunHistory: showRunHistory => set(() => ({ showRunHistory })),
showFeatures: false, showFeatures: false,
setShowFeatures: showFeatures => set(() => ({ showFeatures })), setShowFeatures: showFeatures => set(() => ({ showFeatures })),
runStaus: 'finished',
setRunStaus: runStaus => set(() => ({ runStaus })),
})) }))
...@@ -24,13 +24,12 @@ export type Branch = { ...@@ -24,13 +24,12 @@ export type Branch = {
} }
export type CommonNodeType = { export type CommonNodeType = {
position?: { index?: {
x: number x: number
y: number y: number
} }
sortIndexInBranches?: number
hovering?: boolean hovering?: boolean
branches?: Branch[] targetBranches?: Branch[]
title: string title: string
desc: string desc: string
type: BlockEnum type: BlockEnum
......
import produce from 'immer'
import { import {
getConnectedEdges,
getOutgoers, getOutgoers,
} from 'reactflow' } from 'reactflow'
import { cloneDeep } from 'lodash-es'
import type { import type {
Edge, Edge,
Node, Node,
} from './types' } from './types'
import { BlockEnum } from './types'
export const initialNodesPosition = (oldNodes: Node[], edges: Edge[]) => { export const nodesLevelOrderTraverse = (
const nodes = cloneDeep(oldNodes) firstNode: Node,
const start = nodes.find(node => node.data.type === BlockEnum.Start)! nodes: Node[],
edges: Edge[],
callback: (n: Node) => void,
) => {
const queue = [{
node: firstNode,
}]
start.position.x = 0
start.position.y = 0
start.data.position = {
x: 0,
y: 0,
}
const queue = [start]
let depth = 0
let breadth = 0
let baseHeight = 0
while (queue.length) { while (queue.length) {
const node = queue.shift()! const { node } = queue.shift()!
callback(node)
const targetBranches = node.data.targetBranches
if (targetBranches?.length) {
const targetEdges = getConnectedEdges([node], edges)
if (node.data.position?.x !== depth) { if (targetEdges.length) {
breadth = 0 const sortedTargetEdges = targetEdges
baseHeight = 0 .filter(edge => edge.source === node.id)
.sort((a, b) => {
const aIndex = targetBranches.findIndex(branch => branch.id === a.sourceHandle)
const bIndex = targetBranches.findIndex(branch => branch.id === b.sourceHandle)
return aIndex - bIndex
})
const outgoers = getOutgoers(node, nodes, sortedTargetEdges)
queue.push(...outgoers.map((outgoer) => {
return {
node: outgoer,
}
}))
}
} }
else {
const outgoers = getOutgoers(node, nodes, edges)
depth = node.data.position?.x || 0 if (outgoers.length === 1) {
queue.push({
const outgoers = getOutgoers(node, nodes, edges).sort((a, b) => (a.data.sortIndexInBranches || 0) - (b.data.sortIndexInBranches || 0)) node: outgoers[0],
})
if (outgoers.length) { }
queue.push(...outgoers.map((outgoer) => {
outgoer.data.position = {
x: depth + 1,
y: breadth,
}
outgoer.position.x = (depth + 1) * (220 + 64)
outgoer.position.y = baseHeight
baseHeight += ((outgoer.height || 0) + 39)
breadth += 1
return outgoer
}))
} }
} }
}
return nodes export const initialNodesAndEdges = (nodes: Node[], edges: Edge[]) => {
const newNodes = produce(nodes, (draft) => {
draft.forEach((node) => {
node.type = 'custom'
})
})
const newEdges = produce(edges, (draft) => {
draft.forEach((edge) => {
edge.type = 'custom'
})
})
return [newNodes, newEdges]
}
export type PositionMap = {
position: {
x: number
y: number
}
index: {
x: number
y: number
}
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment