Commit 1840d05a authored by StyleZhang's avatar StyleZhang

record panel

parent 6d561844
......@@ -57,8 +57,8 @@ const NodeSelector: FC<NodeSelectorProps> = ({
}, [onOpenChange])
const handleTrigger = useCallback<MouseEventHandler<HTMLDivElement>>((e) => {
e.stopPropagation()
handleOpenChange(!open)
}, [open, handleOpenChange])
setLocalOpen(v => !v)
}, [])
const handleSelect = useCallback((type: BlockEnum) => {
handleOpenChange(false)
onSelect(type)
......
......@@ -14,6 +14,8 @@ import { ArrowNarrowLeft } from '@/app/components/base/icons/src/vender/line/arr
const Header: FC = () => {
const mode = useStore(state => state.mode)
const setShowFeatures = useStore(state => state.setShowFeatures)
const runStaus = useStore(state => state.runStaus)
const setRunStaus = useStore(state => state.setRunStaus)
const handleShowFeatures = useCallback(() => {
setShowFeatures(true)
......@@ -36,13 +38,20 @@ const Header: FC = () => {
</div>
</div>
<div className='flex items-center'>
<Button className={`
mr-2 px-3 py-0 h-8 bg-white text-[13px] font-medium text-primary-600
border-[0.5px] border-gray-200 shadow-xs
`}>
<ArrowNarrowLeft className='mr-1 w-4 h-4' />
Go back to editor
</Button>
{
runStaus && (
<Button
className={`
mr-2 px-3 py-0 h-8 bg-white text-[13px] font-medium text-primary-600
border-[0.5px] border-gray-200 shadow-xs
`}
onClick={() => setRunStaus('')}
>
<ArrowNarrowLeft className='mr-1 w-4 h-4' />
Go back to editor
</Button>
)
}
<RunAndHistory />
<div className='mx-2 w-[1px] h-3.5 bg-gray-200'></div>
{
......
......@@ -4,19 +4,39 @@ import { useStore } from '../store'
import { Play } from '@/app/components/base/icons/src/vender/line/mediaAndDevices'
import { ClockPlay } from '@/app/components/base/icons/src/vender/line/time'
import TooltipPlus from '@/app/components/base/tooltip-plus'
import { Loading02 } from '@/app/components/base/icons/src/vender/line/general'
const RunAndHistory: FC = () => {
const showRunHistory = useStore(state => state.showRunHistory)
const setShowRunHistory = useStore(state => state.setShowRunHistory)
const runStaus = useStore(state => state.runStaus)
const setRunStaus = useStore(state => state.setRunStaus)
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-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' />
Run
<div
className={`
flex items-center px-1.5 h-7 rounded-md text-[13px] font-medium text-primary-600
hover:bg-primary-50 cursor-pointer
${runStaus === 'running' && 'bg-primary-50 !cursor-not-allowed'}
`}
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 className='mx-0.5 w-[0.5px] h-8 bg-gray-200'></div>
<TooltipPlus
......
......@@ -16,7 +16,6 @@ import type {
SelectedNode,
} from './types'
import { NodeInitialData } from './constants'
import { initialNodesPosition } from './utils'
export const useWorkflow = () => {
const store = useStoreApi()
......@@ -78,14 +77,11 @@ export const useWorkflow = () => {
} = store.getState()
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)
selectedNode.selected = false
else
selectedNode.selected = true
}
if (!cancelSelection)
selectedNode.selected = true
})
setNodes(newNodes)
}, [store])
......@@ -124,9 +120,9 @@ export const useWorkflow = () => {
setEdges,
} = store.getState()
const newEdges = produce(edges, (draft) => {
const currentEdge = draft.find(e => e.id === edge.id)
if (currentEdge)
currentEdge.data = { ...currentEdge.data, hovering: true }
const currentEdge = draft.find(e => e.id === edge.id)!
currentEdge.data = { ...currentEdge.data, hovering: true }
})
setEdges(newEdges)
}, [store])
......@@ -137,9 +133,9 @@ export const useWorkflow = () => {
setEdges,
} = store.getState()
const newEdges = produce(edges, (draft) => {
const currentEdge = draft.find(e => e.id === edge.id)
if (currentEdge)
currentEdge.data = { ...currentEdge.data, hovering: false }
const currentEdge = draft.find(e => e.id === edge.id)!
currentEdge.data = { ...currentEdge.data, hovering: false }
})
setEdges(newEdges)
}, [store])
......@@ -281,59 +277,6 @@ export const useWorkflow = () => {
setEdges(newEdges)
}, [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 {
handleEnterNode,
handleLeaveNode,
......@@ -346,7 +289,5 @@ export const useWorkflow = () => {
handleAddNextNode,
handleChangeCurrentNode,
handleDeleteNode,
handleInitialLayoutNodes,
handleUpdateNodesPosition,
}
}
import type { MouseEvent } from 'react'
import {
useCallback,
useState,
......@@ -35,10 +36,11 @@ export const NodeTargetHandle = ({
const handleOpenChange = useCallback((v: boolean) => {
setOpen(v)
}, [])
const handleHandleClick = () => {
const handleHandleClick = useCallback((e: MouseEvent) => {
e.stopPropagation()
if (!connected)
handleOpenChange(!open)
}
setOpen(v => !v)
}, [connected])
return (
<>
......@@ -93,10 +95,11 @@ export const NodeSourceHandle = ({
const handleOpenChange = useCallback((v: boolean) => {
setOpen(v)
}, [])
const handleHandleClick = () => {
const handleHandleClick = useCallback((e: MouseEvent) => {
e.stopPropagation()
if (!connected)
handleOpenChange(!open)
}
setOpen(v => !v)
}, [connected])
const handleSelect = useCallback((type: BlockEnum) => {
handleAddNextNode(id, type, handleId)
}, [handleAddNextNode, id, handleId])
......
......@@ -10,9 +10,11 @@ import { useStore } from '../store'
import WorkflowInfo from './workflow-info'
import DebugAndPreview from './debug-and-preview'
import RunHistory from './run-history'
import Record from './record'
const Panel: FC = () => {
const mode = useStore(state => state.mode)
const runStaus = useStore(state => state.runStaus)
const nodes = useNodes<CommonNodeType>()
const selectedNode = nodes.find(node => node.selected)
const showRunHistory = useStore(state => state.showRunHistory)
......@@ -30,6 +32,11 @@ const Panel: FC = () => {
return (
<div className='absolute top-14 right-0 bottom-2 flex z-10'>
{
runStaus && (
<Record />
)
}
{
showNodePanel && (
<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
const RunHistory = () => {
const mode = useStore(state => state.mode)
const setShowRunHistory = useStore(state => state.setShowRunHistory)
const setRunStaus = useStore(state => state.setRunStaus)
return (
<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 = () => {
</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]' />
<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 = {
mode: string
showRunHistory: boolean
showFeatures: boolean
runStaus: string
}
type Action = {
setShowRunHistory: (showRunHistory: boolean) => void
setShowFeatures: (showFeatures: boolean) => void
setRunStaus: (runStaus: string) => void
}
export const useStore = create<State & Action>(set => ({
......@@ -17,4 +19,6 @@ export const useStore = create<State & Action>(set => ({
setShowRunHistory: showRunHistory => set(() => ({ showRunHistory })),
showFeatures: false,
setShowFeatures: showFeatures => set(() => ({ showFeatures })),
runStaus: 'finished',
setRunStaus: runStaus => set(() => ({ runStaus })),
}))
......@@ -24,13 +24,12 @@ export type Branch = {
}
export type CommonNodeType = {
position?: {
index?: {
x: number
y: number
}
sortIndexInBranches?: number
hovering?: boolean
branches?: Branch[]
targetBranches?: Branch[]
title: string
desc: string
type: BlockEnum
......
import produce from 'immer'
import {
getConnectedEdges,
getOutgoers,
} from 'reactflow'
import { cloneDeep } from 'lodash-es'
import type {
Edge,
Node,
} from './types'
import { BlockEnum } from './types'
export const initialNodesPosition = (oldNodes: Node[], edges: Edge[]) => {
const nodes = cloneDeep(oldNodes)
const start = nodes.find(node => node.data.type === BlockEnum.Start)!
export const nodesLevelOrderTraverse = (
firstNode: Node,
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) {
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) {
breadth = 0
baseHeight = 0
if (targetEdges.length) {
const sortedTargetEdges = targetEdges
.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
const outgoers = getOutgoers(node, nodes, edges).sort((a, b) => (a.data.sortIndexInBranches || 0) - (b.data.sortIndexInBranches || 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
}))
if (outgoers.length === 1) {
queue.push({
node: outgoers[0],
})
}
}
}
}
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