mirror of
				https://github.com/langgenius/dify.git
				synced 2025-11-03 20:33:00 +00:00 
			
		
		
		
	feat: add search input field (#18409)
This commit is contained in:
		
							parent
							
								
									67eefd0ba1
								
							
						
					
					
						commit
						94e22ba0fd
					
				@ -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<MenuRenderFn<PickerBlockMenuOption>>((
 | 
			
		||||
    anchorElementRef,
 | 
			
		||||
    { options, selectedIndex, selectOptionAndCleanUp, setHighlightedIndex },
 | 
			
		||||
@ -141,7 +149,31 @@ const ComponentPicker = ({
 | 
			
		||||
                  visibility: isPositioned ? 'visible' : 'hidden',
 | 
			
		||||
                }}
 | 
			
		||||
                ref={refs.setFloating}
 | 
			
		||||
                data-testid="component-picker-container"
 | 
			
		||||
              >
 | 
			
		||||
                {
 | 
			
		||||
                  workflowVariableBlock?.show && (
 | 
			
		||||
                    <div className='p-1'>
 | 
			
		||||
                      <VarReferenceVars
 | 
			
		||||
                        searchBoxClassName='mt-1'
 | 
			
		||||
                        vars={workflowVariableOptions}
 | 
			
		||||
                        onChange={(variables: string[]) => {
 | 
			
		||||
                          handleSelectWorkflowVariable(variables)
 | 
			
		||||
                        }}
 | 
			
		||||
                        maxHeightClass='max-h-[34vh]'
 | 
			
		||||
                        isSupportFileVar={isSupportFileVar}
 | 
			
		||||
                        onClose={handleClose}
 | 
			
		||||
                        onBlur={handleClose}
 | 
			
		||||
                      />
 | 
			
		||||
                    </div>
 | 
			
		||||
                  )
 | 
			
		||||
                }
 | 
			
		||||
                {
 | 
			
		||||
                  workflowVariableBlock?.show && !!options.length && (
 | 
			
		||||
                    <div className='my-1 h-px w-full -translate-x-1 bg-divider-subtle'></div>
 | 
			
		||||
                  )
 | 
			
		||||
                }
 | 
			
		||||
                <div data-testid="options-list">
 | 
			
		||||
                  {
 | 
			
		||||
                    options.map((option, index) => (
 | 
			
		||||
                      <Fragment key={option.key}>
 | 
			
		||||
@ -164,28 +196,7 @@ const ComponentPicker = ({
 | 
			
		||||
                      </Fragment>
 | 
			
		||||
                    ))
 | 
			
		||||
                  }
 | 
			
		||||
                {
 | 
			
		||||
                  workflowVariableBlock?.show && (
 | 
			
		||||
                    <>
 | 
			
		||||
                      {
 | 
			
		||||
                        (!!options.length) && (
 | 
			
		||||
                          <div className='my-1 h-px w-full -translate-x-1 bg-divider-subtle'></div>
 | 
			
		||||
                        )
 | 
			
		||||
                      }
 | 
			
		||||
                      <div className='p-1'>
 | 
			
		||||
                        <VarReferenceVars
 | 
			
		||||
                          hideSearch
 | 
			
		||||
                          vars={workflowVariableOptions}
 | 
			
		||||
                          onChange={(variables: string[]) => {
 | 
			
		||||
                            handleSelectWorkflowVariable(variables)
 | 
			
		||||
                          }}
 | 
			
		||||
                          maxHeightClass='max-h-[34vh]'
 | 
			
		||||
                          isSupportFileVar={isSupportFileVar}
 | 
			
		||||
                        />
 | 
			
		||||
                </div>
 | 
			
		||||
                    </>
 | 
			
		||||
                  )
 | 
			
		||||
                }
 | 
			
		||||
              </div>
 | 
			
		||||
            </div>,
 | 
			
		||||
            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 (
 | 
			
		||||
    <LexicalTypeaheadMenuPlugin
 | 
			
		||||
 | 
			
		||||
@ -37,14 +37,16 @@ const OnBlurBlock: FC<OnBlurBlockProps> = ({
 | 
			
		||||
      ),
 | 
			
		||||
      editor.registerCommand(
 | 
			
		||||
        BLUR_COMMAND,
 | 
			
		||||
        () => {
 | 
			
		||||
        (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,
 | 
			
		||||
 | 
			
		||||
@ -258,6 +258,8 @@ type Props = {
 | 
			
		||||
  onChange: (value: ValueSelector, item: Var) => void
 | 
			
		||||
  itemWidth?: number
 | 
			
		||||
  maxHeightClass?: string
 | 
			
		||||
  onClose?: () => void
 | 
			
		||||
  onBlur?: () => void
 | 
			
		||||
}
 | 
			
		||||
const VarReferenceVars: FC<Props> = ({
 | 
			
		||||
  hideSearch,
 | 
			
		||||
@ -267,10 +269,19 @@ const VarReferenceVars: FC<Props> = ({
 | 
			
		||||
  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<Props> = ({
 | 
			
		||||
      {
 | 
			
		||||
        !hideSearch && (
 | 
			
		||||
          <>
 | 
			
		||||
            <div className={cn('mx-2 mb-1 mt-2', searchBoxClassName)} onClick={e => e.stopPropagation()}>
 | 
			
		||||
            <div className={cn('var-search-input-wrapper mx-2 mb-1 mt-2', searchBoxClassName)} onClick={e => e.stopPropagation()}>
 | 
			
		||||
              <Input
 | 
			
		||||
                className='var-search-input'
 | 
			
		||||
                showLeftIcon
 | 
			
		||||
                showClearIcon
 | 
			
		||||
                value={searchText}
 | 
			
		||||
                placeholder={t('workflow.common.searchVar') || ''}
 | 
			
		||||
                onChange={e => setSearchText(e.target.value)}
 | 
			
		||||
                onKeyDown={handleKeyDown}
 | 
			
		||||
                onClear={() => setSearchText('')}
 | 
			
		||||
                onBlur={onBlur}
 | 
			
		||||
                autoFocus
 | 
			
		||||
              />
 | 
			
		||||
            </div>
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user