mirror of
				https://github.com/langgenius/dify.git
				synced 2025-11-04 12:53:38 +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 type { PickerBlockMenuOption } from './menu'
 | 
				
			||||||
import VarReferenceVars from '@/app/components/workflow/nodes/_base/components/variable/var-reference-vars'
 | 
					import VarReferenceVars from '@/app/components/workflow/nodes/_base/components/variable/var-reference-vars'
 | 
				
			||||||
import { useEventEmitterContextContext } from '@/context/event-emitter'
 | 
					import { useEventEmitterContextContext } from '@/context/event-emitter'
 | 
				
			||||||
 | 
					import { KEY_ESCAPE_COMMAND } from 'lexical'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type ComponentPickerProps = {
 | 
					type ComponentPickerProps = {
 | 
				
			||||||
  triggerString: string
 | 
					  triggerString: string
 | 
				
			||||||
@ -118,6 +119,13 @@ const ComponentPicker = ({
 | 
				
			|||||||
      editor.dispatchCommand(INSERT_WORKFLOW_VARIABLE_BLOCK_COMMAND, variables)
 | 
					      editor.dispatchCommand(INSERT_WORKFLOW_VARIABLE_BLOCK_COMMAND, variables)
 | 
				
			||||||
  }, [editor, checkForTriggerMatch, triggerString])
 | 
					  }, [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>>((
 | 
					  const renderMenu = useCallback<MenuRenderFn<PickerBlockMenuOption>>((
 | 
				
			||||||
    anchorElementRef,
 | 
					    anchorElementRef,
 | 
				
			||||||
    { options, selectedIndex, selectOptionAndCleanUp, setHighlightedIndex },
 | 
					    { options, selectedIndex, selectOptionAndCleanUp, setHighlightedIndex },
 | 
				
			||||||
@ -141,7 +149,31 @@ const ComponentPicker = ({
 | 
				
			|||||||
                  visibility: isPositioned ? 'visible' : 'hidden',
 | 
					                  visibility: isPositioned ? 'visible' : 'hidden',
 | 
				
			||||||
                }}
 | 
					                }}
 | 
				
			||||||
                ref={refs.setFloating}
 | 
					                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) => (
 | 
					                    options.map((option, index) => (
 | 
				
			||||||
                      <Fragment key={option.key}>
 | 
					                      <Fragment key={option.key}>
 | 
				
			||||||
@ -164,28 +196,7 @@ const ComponentPicker = ({
 | 
				
			|||||||
                      </Fragment>
 | 
					                      </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>
 | 
					              </div>
 | 
				
			||||||
            </div>,
 | 
					            </div>,
 | 
				
			||||||
            anchorElementRef.current,
 | 
					            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 (
 | 
					  return (
 | 
				
			||||||
    <LexicalTypeaheadMenuPlugin
 | 
					    <LexicalTypeaheadMenuPlugin
 | 
				
			||||||
 | 
				
			|||||||
@ -37,14 +37,16 @@ const OnBlurBlock: FC<OnBlurBlockProps> = ({
 | 
				
			|||||||
      ),
 | 
					      ),
 | 
				
			||||||
      editor.registerCommand(
 | 
					      editor.registerCommand(
 | 
				
			||||||
        BLUR_COMMAND,
 | 
					        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(() => {
 | 
					            ref.current = setTimeout(() => {
 | 
				
			||||||
              editor.dispatchCommand(KEY_ESCAPE_COMMAND, new KeyboardEvent('keydown', { key: 'Escape' }))
 | 
					              editor.dispatchCommand(KEY_ESCAPE_COMMAND, new KeyboardEvent('keydown', { key: 'Escape' }))
 | 
				
			||||||
            }, 200)
 | 
					            }, 200)
 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (onBlur)
 | 
					            if (onBlur)
 | 
				
			||||||
              onBlur()
 | 
					              onBlur()
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
          return true
 | 
					          return true
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        COMMAND_PRIORITY_EDITOR,
 | 
					        COMMAND_PRIORITY_EDITOR,
 | 
				
			||||||
 | 
				
			|||||||
@ -258,6 +258,8 @@ type Props = {
 | 
				
			|||||||
  onChange: (value: ValueSelector, item: Var) => void
 | 
					  onChange: (value: ValueSelector, item: Var) => void
 | 
				
			||||||
  itemWidth?: number
 | 
					  itemWidth?: number
 | 
				
			||||||
  maxHeightClass?: string
 | 
					  maxHeightClass?: string
 | 
				
			||||||
 | 
					  onClose?: () => void
 | 
				
			||||||
 | 
					  onBlur?: () => void
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
const VarReferenceVars: FC<Props> = ({
 | 
					const VarReferenceVars: FC<Props> = ({
 | 
				
			||||||
  hideSearch,
 | 
					  hideSearch,
 | 
				
			||||||
@ -267,10 +269,19 @@ const VarReferenceVars: FC<Props> = ({
 | 
				
			|||||||
  onChange,
 | 
					  onChange,
 | 
				
			||||||
  itemWidth,
 | 
					  itemWidth,
 | 
				
			||||||
  maxHeightClass,
 | 
					  maxHeightClass,
 | 
				
			||||||
 | 
					  onClose,
 | 
				
			||||||
 | 
					  onBlur,
 | 
				
			||||||
}) => {
 | 
					}) => {
 | 
				
			||||||
  const { t } = useTranslation()
 | 
					  const { t } = useTranslation()
 | 
				
			||||||
  const [searchText, setSearchText] = useState('')
 | 
					  const [searchText, setSearchText] = useState('')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const handleKeyDown = (e: React.KeyboardEvent) => {
 | 
				
			||||||
 | 
					    if (e.key === 'Escape') {
 | 
				
			||||||
 | 
					      e.preventDefault()
 | 
				
			||||||
 | 
					      onClose?.()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const filteredVars = vars.filter((v) => {
 | 
					  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.'))
 | 
					    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
 | 
					    return children.length > 0
 | 
				
			||||||
@ -301,14 +312,17 @@ const VarReferenceVars: FC<Props> = ({
 | 
				
			|||||||
      {
 | 
					      {
 | 
				
			||||||
        !hideSearch && (
 | 
					        !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
 | 
					              <Input
 | 
				
			||||||
 | 
					                className='var-search-input'
 | 
				
			||||||
                showLeftIcon
 | 
					                showLeftIcon
 | 
				
			||||||
                showClearIcon
 | 
					                showClearIcon
 | 
				
			||||||
                value={searchText}
 | 
					                value={searchText}
 | 
				
			||||||
                placeholder={t('workflow.common.searchVar') || ''}
 | 
					                placeholder={t('workflow.common.searchVar') || ''}
 | 
				
			||||||
                onChange={e => setSearchText(e.target.value)}
 | 
					                onChange={e => setSearchText(e.target.value)}
 | 
				
			||||||
 | 
					                onKeyDown={handleKeyDown}
 | 
				
			||||||
                onClear={() => setSearchText('')}
 | 
					                onClear={() => setSearchText('')}
 | 
				
			||||||
 | 
					                onBlur={onBlur}
 | 
				
			||||||
                autoFocus
 | 
					                autoFocus
 | 
				
			||||||
              />
 | 
					              />
 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user