mirror of
https://git.mirrors.martin98.com/https://github.com/langgenius/dify.git
synced 2025-05-20 01:28:36 +08: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,51 +149,54 @@ const ComponentPicker = ({
|
||||
visibility: isPositioned ? 'visible' : 'hidden',
|
||||
}}
|
||||
ref={refs.setFloating}
|
||||
data-testid="component-picker-container"
|
||||
>
|
||||
{
|
||||
options.map((option, index) => (
|
||||
<Fragment key={option.key}>
|
||||
{
|
||||
// Divider
|
||||
index !== 0 && options.at(index - 1)?.group !== option.group && (
|
||||
<div className='my-1 h-px w-full -translate-x-1 bg-divider-subtle'></div>
|
||||
)
|
||||
}
|
||||
{option.renderMenuOption({
|
||||
queryString,
|
||||
isSelected: selectedIndex === index,
|
||||
onSelect: () => {
|
||||
selectOptionAndCleanUp(option)
|
||||
},
|
||||
onSetHighlight: () => {
|
||||
setHighlightedIndex(index)
|
||||
},
|
||||
})}
|
||||
</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 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}>
|
||||
{
|
||||
// Divider
|
||||
index !== 0 && options.at(index - 1)?.group !== option.group && (
|
||||
<div className='my-1 h-px w-full -translate-x-1 bg-divider-subtle'></div>
|
||||
)
|
||||
}
|
||||
{option.renderMenuOption({
|
||||
queryString,
|
||||
isSelected: selectedIndex === index,
|
||||
onSelect: () => {
|
||||
selectOptionAndCleanUp(option)
|
||||
},
|
||||
onSetHighlight: () => {
|
||||
setHighlightedIndex(index)
|
||||
},
|
||||
})}
|
||||
</Fragment>
|
||||
))
|
||||
}
|
||||
</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,
|
||||
() => {
|
||||
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,
|
||||
|
@ -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