From 94e22ba0fdc39f539f75d07e4c166f56772b7ccd Mon Sep 17 00:00:00 2001 From: allenZhang <58501701+441126098@users.noreply.github.com> Date: Tue, 22 Apr 2025 11:07:18 +0800 Subject: [PATCH] feat: add search input field (#18409) --- .../plugins/component-picker-block/index.tsx | 93 +++++++++++-------- .../plugins/on-blur-or-focus-block.tsx | 18 ++-- .../variable/var-reference-vars.tsx | 16 +++- 3 files changed, 77 insertions(+), 50 deletions(-) diff --git a/web/app/components/base/prompt-editor/plugins/component-picker-block/index.tsx b/web/app/components/base/prompt-editor/plugins/component-picker-block/index.tsx index d7a3a81417..562bb8c0d9 100644 --- a/web/app/components/base/prompt-editor/plugins/component-picker-block/index.tsx +++ b/web/app/components/base/prompt-editor/plugins/component-picker-block/index.tsx @@ -31,6 +31,7 @@ import { useOptions } from './hooks' import type { PickerBlockMenuOption } from './menu' import VarReferenceVars from '@/app/components/workflow/nodes/_base/components/variable/var-reference-vars' import { useEventEmitterContextContext } from '@/context/event-emitter' +import { KEY_ESCAPE_COMMAND } from 'lexical' type ComponentPickerProps = { triggerString: string @@ -118,6 +119,13 @@ const ComponentPicker = ({ editor.dispatchCommand(INSERT_WORKFLOW_VARIABLE_BLOCK_COMMAND, variables) }, [editor, checkForTriggerMatch, triggerString]) + const handleClose = useCallback(() => { + ReactDOM.flushSync(() => { + const escapeEvent = new KeyboardEvent('keydown', { key: 'Escape' }) + editor.dispatchCommand(KEY_ESCAPE_COMMAND, escapeEvent) + }) + }, [editor]) + const renderMenu = useCallback>(( anchorElementRef, { options, selectedIndex, selectOptionAndCleanUp, setHighlightedIndex }, @@ -141,51 +149,54 @@ const ComponentPicker = ({ visibility: isPositioned ? 'visible' : 'hidden', }} ref={refs.setFloating} + data-testid="component-picker-container" > - { - options.map((option, index) => ( - - { - // Divider - index !== 0 && options.at(index - 1)?.group !== option.group && ( -
- ) - } - {option.renderMenuOption({ - queryString, - isSelected: selectedIndex === index, - onSelect: () => { - selectOptionAndCleanUp(option) - }, - onSetHighlight: () => { - setHighlightedIndex(index) - }, - })} -
- )) - } { workflowVariableBlock?.show && ( - <> - { - (!!options.length) && ( -
- ) - } -
- { - handleSelectWorkflowVariable(variables) - }} - maxHeightClass='max-h-[34vh]' - isSupportFileVar={isSupportFileVar} - /> -
- +
+ { + handleSelectWorkflowVariable(variables) + }} + maxHeightClass='max-h-[34vh]' + isSupportFileVar={isSupportFileVar} + onClose={handleClose} + onBlur={handleClose} + /> +
) } + { + workflowVariableBlock?.show && !!options.length && ( +
+ ) + } +
+ { + options.map((option, index) => ( + + { + // Divider + index !== 0 && options.at(index - 1)?.group !== option.group && ( +
+ ) + } + {option.renderMenuOption({ + queryString, + isSelected: selectedIndex === index, + onSelect: () => { + selectOptionAndCleanUp(option) + }, + onSetHighlight: () => { + setHighlightedIndex(index) + }, + })} +
+ )) + } +
, anchorElementRef.current, @@ -193,7 +204,7 @@ const ComponentPicker = ({ } ) - }, [allFlattenOptions.length, workflowVariableBlock?.show, refs, isPositioned, floatingStyles, queryString, workflowVariableOptions, handleSelectWorkflowVariable]) + }, [allFlattenOptions.length, workflowVariableBlock?.show, refs, isPositioned, floatingStyles, queryString, workflowVariableOptions, handleSelectWorkflowVariable, handleClose, isSupportFileVar]) return ( = ({ ), editor.registerCommand( BLUR_COMMAND, - () => { - ref.current = setTimeout(() => { - editor.dispatchCommand(KEY_ESCAPE_COMMAND, new KeyboardEvent('keydown', { key: 'Escape' })) - }, 200) - - if (onBlur) - onBlur() - + (event) => { + // Check if the clicked target element is var-search-input + const target = event?.relatedTarget as HTMLElement + if (!target?.classList?.contains('var-search-input')) { + ref.current = setTimeout(() => { + editor.dispatchCommand(KEY_ESCAPE_COMMAND, new KeyboardEvent('keydown', { key: 'Escape' })) + }, 200) + if (onBlur) + onBlur() + } return true }, COMMAND_PRIORITY_EDITOR, diff --git a/web/app/components/workflow/nodes/_base/components/variable/var-reference-vars.tsx b/web/app/components/workflow/nodes/_base/components/variable/var-reference-vars.tsx index 751e1990cf..023916ec5b 100644 --- a/web/app/components/workflow/nodes/_base/components/variable/var-reference-vars.tsx +++ b/web/app/components/workflow/nodes/_base/components/variable/var-reference-vars.tsx @@ -258,6 +258,8 @@ type Props = { onChange: (value: ValueSelector, item: Var) => void itemWidth?: number maxHeightClass?: string + onClose?: () => void + onBlur?: () => void } const VarReferenceVars: FC = ({ hideSearch, @@ -267,10 +269,19 @@ const VarReferenceVars: FC = ({ onChange, itemWidth, maxHeightClass, + onClose, + onBlur, }) => { const { t } = useTranslation() const [searchText, setSearchText] = useState('') + const handleKeyDown = (e: React.KeyboardEvent) => { + if (e.key === 'Escape') { + e.preventDefault() + onClose?.() + } + } + const filteredVars = vars.filter((v) => { const children = v.vars.filter(v => checkKeys([v.variable], false).isValid || v.variable.startsWith('sys.') || v.variable.startsWith('env.') || v.variable.startsWith('conversation.')) return children.length > 0 @@ -301,14 +312,17 @@ const VarReferenceVars: FC = ({ { !hideSearch && ( <> -
e.stopPropagation()}> +
e.stopPropagation()}> setSearchText(e.target.value)} + onKeyDown={handleKeyDown} onClear={() => setSearchText('')} + onBlur={onBlur} autoFocus />