feat: Enhance node search functionality, add search result node data interface, optimize layout constants and search result processing logic

pull/22574/head
GuanMu 7 months ago
parent 33fd0fe4fe
commit 253d695714

@ -9,8 +9,36 @@ import { useStore } from '../store'
import { getKeyboardKeyCodeBySystem, isEventTargetInputArea } from '../utils' import { getKeyboardKeyCodeBySystem, isEventTargetInputArea } from '../utils'
import cn from '@/utils/classnames' import cn from '@/utils/classnames'
import { PortalToFollowElem, PortalToFollowElemContent, PortalToFollowElemTrigger } from '@/app/components/base/portal-to-follow-elem' import { PortalToFollowElem, PortalToFollowElemContent, PortalToFollowElemTrigger } from '@/app/components/base/portal-to-follow-elem'
import type { BlockEnum, CommonNodeType } from '../types'
// Constants for layout dimensions and behavior
const SEARCH_LAYOUT_CONSTANTS = {
DEFAULT_CANVAS_WIDTH: 800,
CANVAS_MARGIN: 40,
MAX_RESULTS_WIDTH: 320,
MAX_SEARCH_RESULTS: 8,
BLUR_DELAY_MS: 200,
} as const
// Interface for search result node data
type SearchResultNode = {
blockType: BlockEnum
nodeData: CommonNodeType
title: string
type: string
desc?: string
}
// Props interface for SearchResultItem component
type SearchResultItemProps = {
node: SearchResultNode
searchQuery: string
isSelected: boolean
onClick: () => void
highlightMatch: (text: string, query: string) => React.ReactNode
}
const SearchResultItem = ({ node, searchQuery, isSelected, onClick, highlightMatch }: any) => { const SearchResultItem = ({ node, searchQuery, isSelected, onClick, highlightMatch }: SearchResultItemProps) => {
const toolIcon = useToolIcon(node.nodeData) const toolIcon = useToolIcon(node.nodeData)
return ( return (
@ -106,7 +134,7 @@ const NodeSearch = () => {
const filteredNodes = nodes.filter((node) => { const filteredNodes = nodes.filter((node) => {
if (!node.id || !node.data || node.type === 'sticky') return false if (!node.id || !node.data || node.type === 'sticky') return false
const nodeData = node.data as any const nodeData = node.data as CommonNodeType
const nodeType = nodeData?.type const nodeType = nodeData?.type
const internalStartNodes = ['iteration-start', 'loop-start'] const internalStartNodes = ['iteration-start', 'loop-start']
@ -115,7 +143,7 @@ const NodeSearch = () => {
const result = filteredNodes const result = filteredNodes
.map((node) => { .map((node) => {
const nodeData = node.data as any const nodeData = node.data as CommonNodeType
const processedNode = { const processedNode = {
id: node.id, id: node.id,
@ -155,7 +183,7 @@ const NodeSearch = () => {
}) })
.filter((node): node is NonNullable<typeof node> => node !== null) .filter((node): node is NonNullable<typeof node> => node !== null)
.sort((a, b) => b.score - a.score) .sort((a, b) => b.score - a.score)
.slice(0, 8) .slice(0, SEARCH_LAYOUT_CONSTANTS.MAX_SEARCH_RESULTS)
return results return results
}, [searchableNodes, searchQuery]) }, [searchableNodes, searchQuery])
@ -234,10 +262,13 @@ const NodeSearch = () => {
setTimeout(() => { setTimeout(() => {
setIsOpen(false) setIsOpen(false)
setSelectedIndex(-1) setSelectedIndex(-1)
}, 200) }, SEARCH_LAYOUT_CONSTANTS.BLUR_DELAY_MS)
}, []) }, [])
const maxResultsWidth = Math.min((workflowCanvasWidth || 800) - 40, 320) const maxResultsWidth = Math.min(
(workflowCanvasWidth || SEARCH_LAYOUT_CONSTANTS.DEFAULT_CANVAS_WIDTH) - SEARCH_LAYOUT_CONSTANTS.CANVAS_MARGIN,
SEARCH_LAYOUT_CONSTANTS.MAX_RESULTS_WIDTH,
)
return ( return (
<div className='relative'> <div className='relative'>
@ -250,7 +281,7 @@ const NodeSearch = () => {
<div className='relative'> <div className='relative'>
<Input <Input
ref={inputRef} ref={inputRef}
wrapperClassName='w-[240px]' wrapperClassName="w-[240px]"
value={searchQuery} value={searchQuery}
onChange={(e) => { onChange={(e) => {
setSearchQuery(e.target.value) setSearchQuery(e.target.value)
@ -305,7 +336,7 @@ const NodeSearch = () => {
</div> </div>
</div> </div>
) : searchQuery.trim() && ( ) : searchQuery.trim() && (
<div className='p-8 text-center text-text-tertiary'> <div className="p-8 text-center text-text-tertiary">
<div className='text-sm'>{t('workflow.operator.noNodesFound')}</div> <div className='text-sm'>{t('workflow.operator.noNodesFound')}</div>
<div className='mt-1 text-xs'>{t('workflow.operator.searchHint')}</div> <div className='mt-1 text-xs'>{t('workflow.operator.searchHint')}</div>
</div> </div>

@ -1,5 +1,5 @@
import { memo, useCallback } from 'react' import { memo, useCallback } from 'react'
import type { WorkflowDataUpdater } from '../types' import type { WorkflowRunDetailResponse } from '@/models/log'
import Run from '../run' import Run from '../run'
import { useStore } from '../store' import { useStore } from '../store'
import { useWorkflowUpdate } from '../hooks' import { useWorkflowUpdate } from '../hooks'
@ -9,12 +9,12 @@ const Record = () => {
const historyWorkflowData = useStore(s => s.historyWorkflowData) const historyWorkflowData = useStore(s => s.historyWorkflowData)
const { handleUpdateWorkflowCanvas } = useWorkflowUpdate() const { handleUpdateWorkflowCanvas } = useWorkflowUpdate()
const handleResultCallback = useCallback((res: any) => { const handleResultCallback = useCallback((res: WorkflowRunDetailResponse) => {
const graph: WorkflowDataUpdater = res.graph const graph = res.graph
handleUpdateWorkflowCanvas({ handleUpdateWorkflowCanvas({
nodes: graph.nodes, nodes: graph.nodes,
edges: graph.edges, edges: graph.edges,
viewport: graph.viewport, viewport: graph.viewport || { x: 0, y: 0, zoom: 1 },
}) })
}, [handleUpdateWorkflowCanvas]) }, [handleUpdateWorkflowCanvas])

Loading…
Cancel
Save