From d79b6869923769a43df62e979d5ccf86043de337 Mon Sep 17 00:00:00 2001 From: StyleZhang Date: Fri, 8 Mar 2024 18:09:55 +0800 Subject: [PATCH] block selector --- web/app/components/base/tooltip/index.tsx | 3 + .../workflow/block-selector/blocks.tsx | 106 ++++++++++++++++++ .../workflow/block-selector/index.tsx | 13 ++- .../workflow/block-selector/tabs.tsx | 64 ++--------- .../workflow/block-selector/tools/index.tsx | 16 ++- web/i18n/en-US/workflow.ts | 1 + web/i18n/zh-Hans/workflow.ts | 1 + 7 files changed, 150 insertions(+), 54 deletions(-) create mode 100644 web/app/components/workflow/block-selector/blocks.tsx diff --git a/web/app/components/base/tooltip/index.tsx b/web/app/components/base/tooltip/index.tsx index b26acdfed0..49b139f946 100644 --- a/web/app/components/base/tooltip/index.tsx +++ b/web/app/components/base/tooltip/index.tsx @@ -14,6 +14,7 @@ type TooltipProps = { position?: 'top' | 'right' | 'bottom' | 'left' clickable?: boolean children: React.ReactNode + noArrow?: boolean } const Tooltip: FC = ({ @@ -25,6 +26,7 @@ const Tooltip: FC = ({ htmlContent, className, clickable, + noArrow, }) => { return (
@@ -39,6 +41,7 @@ const Tooltip: FC = ({ place={position} clickable={clickable} isOpen={disabled ? false : undefined} + noArrow={noArrow} > {htmlContent && htmlContent} diff --git a/web/app/components/workflow/block-selector/blocks.tsx b/web/app/components/workflow/block-selector/blocks.tsx new file mode 100644 index 0000000000..8feda5dccf --- /dev/null +++ b/web/app/components/workflow/block-selector/blocks.tsx @@ -0,0 +1,106 @@ +import { + memo, + useCallback, + useMemo, +} from 'react' +import { useTranslation } from 'react-i18next' +import { groupBy } from 'lodash-es' +import BlockIcon from '../block-icon' +import { BlockEnum } from '../types' +import { + useIsChatMode, + useNodesExtraData, +} from '../hooks' +import { BLOCK_CLASSIFICATIONS } from './constants' +import { useBlocks } from './hooks' +import type { ToolDefaultValue } from './types' +import Tooltip from '@/app/components/base/tooltip' + +type BlocksProps = { + searchText: string + onSelect: (type: BlockEnum, tool?: ToolDefaultValue) => void +} +const Blocks = ({ + searchText, + onSelect, +}: BlocksProps) => { + const { t } = useTranslation() + const isChatMode = useIsChatMode() + const nodesExtraData = useNodesExtraData() + const blocks = useBlocks() + + const groups = useMemo(() => { + return BLOCK_CLASSIFICATIONS.reduce((acc, classification) => { + const list = groupBy(blocks, 'classification')[classification].filter((block) => { + if (block.type === BlockEnum.DirectAnswer && !isChatMode) + return false + + return block.title.toLowerCase().includes(searchText.toLowerCase()) + }) + + return { + ...acc, + [classification]: list, + } + }, {} as Record) + }, [blocks, isChatMode, searchText]) + const isEmpty = Object.values(groups).every(list => !list.length) + + const renderGroup = useCallback((classification: string) => { + const list = groups[classification] + + return ( +
+ { + classification !== '-' && !!list.length && ( +
+ {t(`workflow.tabs.${classification}`)} +
+ ) + } + { + list.map(block => ( + +
onSelect(block.type)} + > + +
{block.title}
+
+
+ )) + } +
+ ) + }, [groups, nodesExtraData, onSelect, t]) + + return ( +
+ { + isEmpty && ( +
{t('workflow.tabs.noResult')}
+ ) + } + { + !isEmpty && BLOCK_CLASSIFICATIONS.map(renderGroup) + } +
+ ) +} + +export default memo(Blocks) diff --git a/web/app/components/workflow/block-selector/index.tsx b/web/app/components/workflow/block-selector/index.tsx index 8c9cf7f8fb..4d97e19d56 100644 --- a/web/app/components/workflow/block-selector/index.tsx +++ b/web/app/components/workflow/block-selector/index.tsx @@ -23,6 +23,7 @@ import { SearchLg, } from '@/app/components/base/icons/src/vender/line/general' import type { OnSelectBlock } from '@/app/components/workflow/types' +import { XCircle } from '@/app/components/base/icons/src/vender/solid/general' type NodeSelectorProps = { open?: boolean @@ -105,10 +106,20 @@ const NodeSelector: FC = ({ setSearchText(e.target.value)} /> + { + searchText && ( +
setSearchText('')} + > + +
+ ) + }
= ({ searchText, onSelect, }) => { - const { t } = useTranslation() - const isChatMode = useIsChatMode() - const blocks = useBlocks() const tabs = useTabs() const [activeTab, setActiveTab] = useState(tabs[0].key) @@ -53,55 +43,25 @@ const Tabs: FC = ({ { activeTab === TabsEnum.Blocks && ( -
- { - BLOCK_CLASSIFICATIONS.map(classification => ( -
- { - classification !== '-' && ( -
- {t(`workflow.tabs.${classification}`)} -
- ) - } - { - groupBy(blocks, 'classification')[classification].filter((block) => { - if (block.type === BlockEnum.DirectAnswer && !isChatMode) - return false - - return true - }).map(block => ( -
onSelect(block.type)} - > - -
{block.title}
-
- )) - } -
- )) - } -
+ ) } { activeTab === TabsEnum.BuiltInTool && ( - + ) } { activeTab === TabsEnum.CustomTool && ( ) diff --git a/web/app/components/workflow/block-selector/tools/index.tsx b/web/app/components/workflow/block-selector/tools/index.tsx index c0f0e928fe..9dc3fcec0d 100644 --- a/web/app/components/workflow/block-selector/tools/index.tsx +++ b/web/app/components/workflow/block-selector/tools/index.tsx @@ -1,8 +1,10 @@ import { memo, useCallback, + useMemo, } from 'react' import produce from 'immer' +import { useTranslation } from 'react-i18next' import { useStore } from '../../store' import type { BlockEnum } from '../../types' import type { @@ -14,13 +16,20 @@ import Item from './item' type ToolsProps = { isCustom?: boolean onSelect: (type: BlockEnum, tool?: ToolDefaultValue) => void + searchText: string } const Tools = ({ isCustom, onSelect, + searchText, }: ToolsProps) => { + const { t } = useTranslation() const totalToolsets = useStore(state => state.toolsets) - const toolsets = totalToolsets.filter(toolset => toolset.type === (isCustom ? 'api' : 'builtin')) + const toolsets = useMemo(() => { + return totalToolsets.filter((toolset) => { + return toolset.type === (isCustom ? 'api' : 'builtin') && toolset.name.toLowerCase().includes(searchText.toLowerCase()) + }) + }, [totalToolsets, isCustom, searchText]) const setToolsets = useStore(state => state.setToolsets) const toolsMap = useStore(state => state.toolsMap) const setToolsMap = useStore(state => state.setToolsMap) @@ -63,6 +72,11 @@ const Tools = ({ return (
+ { + !toolsets.length && ( +
{t('workflow.tabs.noResult')}
+ ) + } { toolsets.map(toolset => (