import type { ReactNode, Ref } from 'react' import type { BlockEnum, NodeDefault, OnNodeAdd, OnSelectBlock, ToolWithProvider } from '../types' import { cn } from '@langgenius/dify-ui/cn' import { Tabs, TabsIndicator, TabsList, TabsPanel, TabsTab } from '@langgenius/dify-ui/tabs' import { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/tooltip' import { useDebounce } from 'ahooks' import { useState } from 'react' import { useTranslation } from 'react-i18next' import { SearchInput } from '@/app/components/base/search-input' import SearchBox from '@/app/components/plugins/marketplace/search-box' import AllStartBlocks from './all-start-blocks' import Blocks from './blocks' import DataSources from './data-sources' import Snippets from './snippets' import { ToolPanel } from './tool-panel' import { TabType } from './types' type TabConfig = { key: TabType name: string disabled?: boolean disabledTip?: ReactNode } type SelectorContentProps = { defaultTab: TabType standalonePanel?: TabType tabs: TabConfig[] searchInputRef: Ref onSelect: OnSelectBlock onRequestClose: () => void availableBlocksTypes?: BlockEnum[] blocks: NodeDefault[] dataSources?: ToolWithProvider[] allowStartNodeSelection?: boolean hasUserInputNode?: boolean hasTriggerNode?: boolean snippetInsertPayload?: Parameters[1] } function TabHeaderItem({ tab, fallbackDisabledTip, }: { tab: TabConfig fallbackDisabledTip: ReactNode }) { const tabElement = ( {tab.name} ) if (!tab.disabled) return tabElement return ( {tab.disabledTip || fallbackDisabledTip} ) } function SelectorContent({ defaultTab, standalonePanel, tabs, searchInputRef, onSelect, onRequestClose, availableBlocksTypes, blocks, dataSources = [], allowStartNodeSelection = false, hasUserInputNode = false, hasTriggerNode = false, snippetInsertPayload, }: SelectorContentProps) { const { t } = useTranslation() const [searchText, setSearchText] = useState('') const debouncedSearchText = useDebounce(searchText, { wait: 500 }) const [tags, setTags] = useState([]) const fallbackDisabledTip = t(($) => $['tabs.startDisabledTip'], { ns: 'workflow' }) const renderSearchFilter = (tab: TabType, inputRef?: Ref) => { if (tab === TabType.Snippets) return null const filter = (() => { if (tab === TabType.Start) { return ( $['tabs.searchTrigger'], { ns: 'workflow' })} inputClassName="grow" /> ) } if (tab === TabType.Tools) { return ( $.searchTools, { ns: 'plugin' })!} inputClassName="grow" /> ) } return ( $['tabs.searchBlock'], { ns: 'workflow' }) : t(($) => $['tabs.searchDataSource'], { ns: 'workflow' }) } aria-label={ tab === TabType.Blocks ? t(($) => $['tabs.searchBlock'], { ns: 'workflow' }) : t(($) => $['tabs.searchDataSource'], { ns: 'workflow' }) } onValueChange={setSearchText} /> ) })() return
{filter}
} const renderPanel = (tab: TabType, inputRef?: Ref) => { const searchFilter = renderSearchFilter(tab, inputRef) if (tab === TabType.Start) { return ( <> {searchFilter}
) } if (tab === TabType.Blocks) { return ( <> {searchFilter}
) } if (tab === TabType.Sources) { return ( <> {searchFilter}
) } if (tab === TabType.Tools) { return ( <> {searchFilter} ) } return ( ) } if (standalonePanel) { return (
{renderPanel( standalonePanel, standalonePanel === TabType.Snippets ? undefined : searchInputRef, )}
) } return ( {tabs.map((tab) => ( ))} {tabs.map((tab) => ( {renderPanel( tab.key, tab.key === defaultTab && tab.key !== TabType.Snippets ? searchInputRef : undefined, )} ))} ) } export { SelectorContent }