mirror of
https://github.com/langgenius/dify.git
synced 2026-03-31 18:00:55 -04:00
fix(prompt-editor): fix unexpected blur effect in prompt editor (#34069)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This commit is contained in:
@@ -3,13 +3,10 @@ import { LexicalComposer } from '@lexical/react/LexicalComposer'
|
||||
import { act, render, waitFor } from '@testing-library/react'
|
||||
import {
|
||||
BLUR_COMMAND,
|
||||
COMMAND_PRIORITY_EDITOR,
|
||||
FOCUS_COMMAND,
|
||||
KEY_ESCAPE_COMMAND,
|
||||
} from 'lexical'
|
||||
import OnBlurBlock from '../on-blur-or-focus-block'
|
||||
import { CaptureEditorPlugin } from '../test-utils'
|
||||
import { CLEAR_HIDE_MENU_TIMEOUT } from '../workflow-variable-block'
|
||||
|
||||
const renderOnBlurBlock = (props?: {
|
||||
onBlur?: () => void
|
||||
@@ -75,7 +72,7 @@ describe('OnBlurBlock', () => {
|
||||
expect(onFocus).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
it('should call onBlur and dispatch escape after delay when blur target is not var-search-input', async () => {
|
||||
it('should call onBlur when blur target is not var-search-input', async () => {
|
||||
const onBlur = vi.fn()
|
||||
const { getEditor } = renderOnBlurBlock({ onBlur })
|
||||
|
||||
@@ -85,14 +82,6 @@ describe('OnBlurBlock', () => {
|
||||
|
||||
const editor = getEditor()
|
||||
expect(editor).not.toBeNull()
|
||||
vi.useFakeTimers()
|
||||
|
||||
const onEscape = vi.fn(() => true)
|
||||
const unregister = editor!.registerCommand(
|
||||
KEY_ESCAPE_COMMAND,
|
||||
onEscape,
|
||||
COMMAND_PRIORITY_EDITOR,
|
||||
)
|
||||
|
||||
let handled = false
|
||||
act(() => {
|
||||
@@ -101,18 +90,9 @@ describe('OnBlurBlock', () => {
|
||||
|
||||
expect(handled).toBe(true)
|
||||
expect(onBlur).toHaveBeenCalledTimes(1)
|
||||
expect(onEscape).not.toHaveBeenCalled()
|
||||
|
||||
act(() => {
|
||||
vi.advanceTimersByTime(200)
|
||||
})
|
||||
|
||||
expect(onEscape).toHaveBeenCalledTimes(1)
|
||||
unregister()
|
||||
vi.useRealTimers()
|
||||
})
|
||||
|
||||
it('should dispatch delayed escape when onBlur callback is not provided', async () => {
|
||||
it('should handle blur when onBlur callback is not provided', async () => {
|
||||
const { getEditor } = renderOnBlurBlock()
|
||||
|
||||
await waitFor(() => {
|
||||
@@ -121,28 +101,16 @@ describe('OnBlurBlock', () => {
|
||||
|
||||
const editor = getEditor()
|
||||
expect(editor).not.toBeNull()
|
||||
vi.useFakeTimers()
|
||||
|
||||
const onEscape = vi.fn(() => true)
|
||||
const unregister = editor!.registerCommand(
|
||||
KEY_ESCAPE_COMMAND,
|
||||
onEscape,
|
||||
COMMAND_PRIORITY_EDITOR,
|
||||
)
|
||||
|
||||
let handled = false
|
||||
act(() => {
|
||||
editor!.dispatchCommand(BLUR_COMMAND, createBlurEvent(document.createElement('div')))
|
||||
})
|
||||
act(() => {
|
||||
vi.advanceTimersByTime(200)
|
||||
handled = editor!.dispatchCommand(BLUR_COMMAND, createBlurEvent(document.createElement('div')))
|
||||
})
|
||||
|
||||
expect(onEscape).toHaveBeenCalledTimes(1)
|
||||
unregister()
|
||||
vi.useRealTimers()
|
||||
expect(handled).toBe(true)
|
||||
})
|
||||
|
||||
it('should skip onBlur and delayed escape when blur target is var-search-input', async () => {
|
||||
it('should skip onBlur when blur target is var-search-input', async () => {
|
||||
const onBlur = vi.fn()
|
||||
const { getEditor } = renderOnBlurBlock({ onBlur })
|
||||
|
||||
@@ -152,31 +120,17 @@ describe('OnBlurBlock', () => {
|
||||
|
||||
const editor = getEditor()
|
||||
expect(editor).not.toBeNull()
|
||||
vi.useFakeTimers()
|
||||
|
||||
const target = document.createElement('input')
|
||||
target.classList.add('var-search-input')
|
||||
|
||||
const onEscape = vi.fn(() => true)
|
||||
const unregister = editor!.registerCommand(
|
||||
KEY_ESCAPE_COMMAND,
|
||||
onEscape,
|
||||
COMMAND_PRIORITY_EDITOR,
|
||||
)
|
||||
|
||||
let handled = false
|
||||
act(() => {
|
||||
handled = editor!.dispatchCommand(BLUR_COMMAND, createBlurEvent(target))
|
||||
})
|
||||
act(() => {
|
||||
vi.advanceTimersByTime(200)
|
||||
})
|
||||
|
||||
expect(handled).toBe(true)
|
||||
expect(onBlur).not.toHaveBeenCalled()
|
||||
expect(onEscape).not.toHaveBeenCalled()
|
||||
unregister()
|
||||
vi.useRealTimers()
|
||||
})
|
||||
|
||||
it('should handle focus command when onFocus callback is not provided', async () => {
|
||||
@@ -198,59 +152,6 @@ describe('OnBlurBlock', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('Clear timeout command', () => {
|
||||
it('should clear scheduled escape timeout when clear command is dispatched', async () => {
|
||||
const { getEditor } = renderOnBlurBlock({ onBlur: vi.fn() })
|
||||
|
||||
await waitFor(() => {
|
||||
expect(getEditor()).not.toBeNull()
|
||||
})
|
||||
|
||||
const editor = getEditor()
|
||||
expect(editor).not.toBeNull()
|
||||
vi.useFakeTimers()
|
||||
|
||||
const onEscape = vi.fn(() => true)
|
||||
const unregister = editor!.registerCommand(
|
||||
KEY_ESCAPE_COMMAND,
|
||||
onEscape,
|
||||
COMMAND_PRIORITY_EDITOR,
|
||||
)
|
||||
|
||||
act(() => {
|
||||
editor!.dispatchCommand(BLUR_COMMAND, createBlurEvent(document.createElement('div')))
|
||||
})
|
||||
act(() => {
|
||||
editor!.dispatchCommand(CLEAR_HIDE_MENU_TIMEOUT, undefined)
|
||||
})
|
||||
act(() => {
|
||||
vi.advanceTimersByTime(200)
|
||||
})
|
||||
|
||||
expect(onEscape).not.toHaveBeenCalled()
|
||||
unregister()
|
||||
vi.useRealTimers()
|
||||
})
|
||||
|
||||
it('should handle clear command when no timeout is scheduled', async () => {
|
||||
const { getEditor } = renderOnBlurBlock()
|
||||
|
||||
await waitFor(() => {
|
||||
expect(getEditor()).not.toBeNull()
|
||||
})
|
||||
|
||||
const editor = getEditor()
|
||||
expect(editor).not.toBeNull()
|
||||
|
||||
let handled = false
|
||||
act(() => {
|
||||
handled = editor!.dispatchCommand(CLEAR_HIDE_MENU_TIMEOUT, undefined)
|
||||
})
|
||||
|
||||
expect(handled).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
describe('Lifecycle cleanup', () => {
|
||||
it('should unregister commands when component unmounts', async () => {
|
||||
const { getEditor, unmount } = renderOnBlurBlock()
|
||||
@@ -266,16 +167,13 @@ describe('OnBlurBlock', () => {
|
||||
|
||||
let blurHandled = true
|
||||
let focusHandled = true
|
||||
let clearHandled = true
|
||||
act(() => {
|
||||
blurHandled = editor!.dispatchCommand(BLUR_COMMAND, createBlurEvent(document.createElement('div')))
|
||||
focusHandled = editor!.dispatchCommand(FOCUS_COMMAND, createFocusEvent())
|
||||
clearHandled = editor!.dispatchCommand(CLEAR_HIDE_MENU_TIMEOUT, undefined)
|
||||
})
|
||||
|
||||
expect(blurHandled).toBe(false)
|
||||
expect(focusHandled).toBe(false)
|
||||
expect(clearHandled).toBe(false)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
import type { LexicalEditor } from 'lexical'
|
||||
import { LexicalComposer } from '@lexical/react/LexicalComposer'
|
||||
import { act, render, waitFor } from '@testing-library/react'
|
||||
import { $getRoot, COMMAND_PRIORITY_EDITOR } from 'lexical'
|
||||
import { $getRoot } from 'lexical'
|
||||
import { CustomTextNode } from '../custom-text/node'
|
||||
import { CaptureEditorPlugin } from '../test-utils'
|
||||
import UpdateBlock, {
|
||||
PROMPT_EDITOR_INSERT_QUICKLY,
|
||||
PROMPT_EDITOR_UPDATE_VALUE_BY_EVENT_EMITTER,
|
||||
} from '../update-block'
|
||||
import { CLEAR_HIDE_MENU_TIMEOUT } from '../workflow-variable-block'
|
||||
|
||||
const { mockUseEventEmitterContextContext } = vi.hoisted(() => ({
|
||||
mockUseEventEmitterContextContext: vi.fn(),
|
||||
@@ -157,7 +156,7 @@ describe('UpdateBlock', () => {
|
||||
})
|
||||
|
||||
describe('Quick insert event', () => {
|
||||
it('should insert slash and dispatch clear command when quick insert event matches instance id', async () => {
|
||||
it('should insert slash when quick insert event matches instance id', async () => {
|
||||
const { emit, getEditor } = setup({ instanceId: 'instance-1' })
|
||||
|
||||
await waitFor(() => {
|
||||
@@ -168,13 +167,6 @@ describe('UpdateBlock', () => {
|
||||
|
||||
selectRootEnd(editor!)
|
||||
|
||||
const clearCommandHandler = vi.fn(() => true)
|
||||
const unregister = editor!.registerCommand(
|
||||
CLEAR_HIDE_MENU_TIMEOUT,
|
||||
clearCommandHandler,
|
||||
COMMAND_PRIORITY_EDITOR,
|
||||
)
|
||||
|
||||
emit({
|
||||
type: PROMPT_EDITOR_INSERT_QUICKLY,
|
||||
instanceId: 'instance-1',
|
||||
@@ -183,9 +175,6 @@ describe('UpdateBlock', () => {
|
||||
await waitFor(() => {
|
||||
expect(readEditorText(editor!)).toBe('/')
|
||||
})
|
||||
expect(clearCommandHandler).toHaveBeenCalledTimes(1)
|
||||
|
||||
unregister()
|
||||
})
|
||||
|
||||
it('should ignore quick insert event when instance id does not match', async () => {
|
||||
|
||||
@@ -23,6 +23,8 @@ import {
|
||||
$createTextNode,
|
||||
$getRoot,
|
||||
$setSelection,
|
||||
BLUR_COMMAND,
|
||||
FOCUS_COMMAND,
|
||||
KEY_ESCAPE_COMMAND,
|
||||
} from 'lexical'
|
||||
import * as React from 'react'
|
||||
@@ -704,4 +706,180 @@ describe('ComponentPicker (component-picker-block/index.tsx)', () => {
|
||||
expect(screen.queryByPlaceholderText('workflow.common.searchVar')).not.toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
|
||||
describe('blur/focus menu visibility', () => {
|
||||
it('hides the menu after a 200ms delay when blur command is dispatched', async () => {
|
||||
const captures: Captures = { editor: null, eventEmitter: null }
|
||||
|
||||
render((
|
||||
<MinimalEditor
|
||||
triggerString="{"
|
||||
contextBlock={makeContextBlock()}
|
||||
captures={captures}
|
||||
/>
|
||||
))
|
||||
|
||||
const editor = await waitForEditor(captures)
|
||||
await setEditorText(editor, '{', true)
|
||||
expect(await screen.findByText('common.promptEditor.context.item.title')).toBeInTheDocument()
|
||||
|
||||
vi.useFakeTimers()
|
||||
|
||||
act(() => {
|
||||
editor.dispatchCommand(BLUR_COMMAND, new FocusEvent('blur', { relatedTarget: document.createElement('button') }))
|
||||
})
|
||||
|
||||
expect(screen.queryByText('common.promptEditor.context.item.title')).toBeInTheDocument()
|
||||
|
||||
act(() => {
|
||||
vi.advanceTimersByTime(200)
|
||||
})
|
||||
|
||||
expect(screen.queryByText('common.promptEditor.context.item.title')).not.toBeInTheDocument()
|
||||
|
||||
vi.useRealTimers()
|
||||
})
|
||||
|
||||
it('restores menu visibility when focus command is dispatched after blur hides it', async () => {
|
||||
const captures: Captures = { editor: null, eventEmitter: null }
|
||||
|
||||
render((
|
||||
<MinimalEditor
|
||||
triggerString="{"
|
||||
contextBlock={makeContextBlock()}
|
||||
captures={captures}
|
||||
/>
|
||||
))
|
||||
|
||||
const editor = await waitForEditor(captures)
|
||||
await setEditorText(editor, '{', true)
|
||||
expect(await screen.findByText('common.promptEditor.context.item.title')).toBeInTheDocument()
|
||||
|
||||
vi.useFakeTimers()
|
||||
|
||||
act(() => {
|
||||
editor.dispatchCommand(BLUR_COMMAND, new FocusEvent('blur', { relatedTarget: document.createElement('button') }))
|
||||
})
|
||||
act(() => {
|
||||
vi.advanceTimersByTime(200)
|
||||
})
|
||||
|
||||
expect(screen.queryByText('common.promptEditor.context.item.title')).not.toBeInTheDocument()
|
||||
|
||||
act(() => {
|
||||
editor.dispatchCommand(FOCUS_COMMAND, new FocusEvent('focus'))
|
||||
})
|
||||
|
||||
vi.useRealTimers()
|
||||
|
||||
await setEditorText(editor, '{', true)
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByText('common.promptEditor.context.item.title')).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
|
||||
it('cancels the blur timer when focus arrives before the 200ms timeout', async () => {
|
||||
const captures: Captures = { editor: null, eventEmitter: null }
|
||||
|
||||
render((
|
||||
<MinimalEditor
|
||||
triggerString="{"
|
||||
contextBlock={makeContextBlock()}
|
||||
captures={captures}
|
||||
/>
|
||||
))
|
||||
|
||||
const editor = await waitForEditor(captures)
|
||||
await setEditorText(editor, '{', true)
|
||||
expect(await screen.findByText('common.promptEditor.context.item.title')).toBeInTheDocument()
|
||||
|
||||
vi.useFakeTimers()
|
||||
|
||||
act(() => {
|
||||
editor.dispatchCommand(BLUR_COMMAND, new FocusEvent('blur', { relatedTarget: document.createElement('button') }))
|
||||
})
|
||||
|
||||
act(() => {
|
||||
editor.dispatchCommand(FOCUS_COMMAND, new FocusEvent('focus'))
|
||||
})
|
||||
|
||||
act(() => {
|
||||
vi.advanceTimersByTime(200)
|
||||
})
|
||||
|
||||
expect(screen.queryByText('common.promptEditor.context.item.title')).toBeInTheDocument()
|
||||
|
||||
vi.useRealTimers()
|
||||
})
|
||||
|
||||
it('cancels a pending blur timer when a subsequent blur targets var-search-input', async () => {
|
||||
const captures: Captures = { editor: null, eventEmitter: null }
|
||||
|
||||
render((
|
||||
<MinimalEditor
|
||||
triggerString="{"
|
||||
contextBlock={makeContextBlock()}
|
||||
captures={captures}
|
||||
/>
|
||||
))
|
||||
|
||||
const editor = await waitForEditor(captures)
|
||||
await setEditorText(editor, '{', true)
|
||||
expect(await screen.findByText('common.promptEditor.context.item.title')).toBeInTheDocument()
|
||||
|
||||
vi.useFakeTimers()
|
||||
|
||||
act(() => {
|
||||
editor.dispatchCommand(BLUR_COMMAND, new FocusEvent('blur', { relatedTarget: document.createElement('button') }))
|
||||
})
|
||||
|
||||
const varInput = document.createElement('input')
|
||||
varInput.classList.add('var-search-input')
|
||||
|
||||
act(() => {
|
||||
editor.dispatchCommand(BLUR_COMMAND, new FocusEvent('blur', { relatedTarget: varInput }))
|
||||
})
|
||||
|
||||
act(() => {
|
||||
vi.advanceTimersByTime(200)
|
||||
})
|
||||
|
||||
expect(screen.queryByText('common.promptEditor.context.item.title')).toBeInTheDocument()
|
||||
|
||||
vi.useRealTimers()
|
||||
})
|
||||
|
||||
it('does not hide the menu when blur target is var-search-input', async () => {
|
||||
const captures: Captures = { editor: null, eventEmitter: null }
|
||||
|
||||
render((
|
||||
<MinimalEditor
|
||||
triggerString="{"
|
||||
contextBlock={makeContextBlock()}
|
||||
captures={captures}
|
||||
/>
|
||||
))
|
||||
|
||||
const editor = await waitForEditor(captures)
|
||||
await setEditorText(editor, '{', true)
|
||||
expect(await screen.findByText('common.promptEditor.context.item.title')).toBeInTheDocument()
|
||||
|
||||
vi.useFakeTimers()
|
||||
|
||||
const target = document.createElement('input')
|
||||
target.classList.add('var-search-input')
|
||||
|
||||
act(() => {
|
||||
editor.dispatchCommand(BLUR_COMMAND, new FocusEvent('blur', { relatedTarget: target }))
|
||||
})
|
||||
|
||||
act(() => {
|
||||
vi.advanceTimersByTime(200)
|
||||
})
|
||||
|
||||
expect(screen.queryByText('common.promptEditor.context.item.title')).toBeInTheDocument()
|
||||
|
||||
vi.useRealTimers()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -24,11 +24,15 @@ import {
|
||||
} from '@floating-ui/react'
|
||||
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext'
|
||||
import { LexicalTypeaheadMenuPlugin } from '@lexical/react/LexicalTypeaheadMenuPlugin'
|
||||
import { mergeRegister } from '@lexical/utils'
|
||||
import {
|
||||
$getRoot,
|
||||
$getSelection,
|
||||
$insertNodes,
|
||||
$isRangeSelection,
|
||||
BLUR_COMMAND,
|
||||
COMMAND_PRIORITY_EDITOR,
|
||||
FOCUS_COMMAND,
|
||||
KEY_ESCAPE_COMMAND,
|
||||
} from 'lexical'
|
||||
import {
|
||||
@@ -37,6 +41,8 @@ import {
|
||||
useCallback,
|
||||
useLayoutEffect,
|
||||
useMemo,
|
||||
useEffect,
|
||||
useRef,
|
||||
useState,
|
||||
} from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
@@ -142,6 +148,46 @@ const ComponentPicker = ({
|
||||
const [activeTab, setActiveTab] = useState<'variables' | 'files'>('variables')
|
||||
const [isFileUploadModalOpen, setIsFileUploadModalOpen] = useState(false)
|
||||
const [fileUploadModalKey, setFileUploadModalKey] = useState(0)
|
||||
const [blurHidden, setBlurHidden] = useState(false)
|
||||
const blurTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null)
|
||||
|
||||
const clearBlurTimer = useCallback(() => {
|
||||
if (blurTimerRef.current) {
|
||||
clearTimeout(blurTimerRef.current)
|
||||
blurTimerRef.current = null
|
||||
}
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
const unregister = mergeRegister(
|
||||
editor.registerCommand(
|
||||
BLUR_COMMAND,
|
||||
(event) => {
|
||||
clearBlurTimer()
|
||||
const target = event?.relatedTarget as HTMLElement
|
||||
if (!target?.classList?.contains('var-search-input'))
|
||||
blurTimerRef.current = setTimeout(() => setBlurHidden(true), 200)
|
||||
return false
|
||||
},
|
||||
COMMAND_PRIORITY_EDITOR,
|
||||
),
|
||||
editor.registerCommand(
|
||||
FOCUS_COMMAND,
|
||||
() => {
|
||||
clearBlurTimer()
|
||||
setBlurHidden(false)
|
||||
return false
|
||||
},
|
||||
COMMAND_PRIORITY_EDITOR,
|
||||
),
|
||||
)
|
||||
|
||||
return () => {
|
||||
if (blurTimerRef.current)
|
||||
clearTimeout(blurTimerRef.current)
|
||||
unregister()
|
||||
}
|
||||
}, [editor, clearBlurTimer])
|
||||
|
||||
eventEmitter?.useSubscription((v: any) => {
|
||||
if (v.type === INSERT_VARIABLE_VALUE_BLOCK_COMMAND)
|
||||
@@ -292,6 +338,8 @@ const ComponentPicker = ({
|
||||
anchorElementRef,
|
||||
{ options, selectedIndex, selectOptionAndCleanUp, setHighlightedIndex },
|
||||
) => {
|
||||
if (blurHidden)
|
||||
return null
|
||||
if (triggerString === '@' && !agentBlock?.show)
|
||||
return null
|
||||
|
||||
@@ -518,7 +566,7 @@ const ComponentPicker = ({
|
||||
}
|
||||
</>
|
||||
)
|
||||
}, [isAgentTrigger, isSupportSandbox, triggerString, allFlattenOptions.length, workflowVariableBlock?.show, workflowVariableBlock?.showManageInputField, workflowVariableBlock?.onManageInputField, workflowVariableBlock?.agentNodes, floatingStyles, isPositioned, refs, agentNodes, handleSelectAgent, handleClose, useExternalSearch, queryString, workflowVariableOptions, isSupportFileVar, showAssembleVariables, handleSelectAssembleVariables, currentBlock?.generatorType, t, activeTab, handleSelectWorkflowVariable, handleSelectFileReference, contextBlock?.show, contextBlock?.selectable, handleSelectContext, agentBlock?.show])
|
||||
}, [blurHidden, isAgentTrigger, isSupportSandbox, triggerString, allFlattenOptions.length, workflowVariableBlock?.show, workflowVariableBlock?.showManageInputField, workflowVariableBlock?.onManageInputField, workflowVariableBlock?.agentNodes, floatingStyles, isPositioned, refs, agentNodes, handleSelectAgent, handleClose, useExternalSearch, queryString, workflowVariableOptions, isSupportFileVar, showAssembleVariables, handleSelectAssembleVariables, currentBlock?.generatorType, t, activeTab, handleSelectWorkflowVariable, handleSelectFileReference, contextBlock?.show, contextBlock?.selectable, handleSelectContext, agentBlock?.show])
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
@@ -5,10 +5,8 @@ import {
|
||||
BLUR_COMMAND,
|
||||
COMMAND_PRIORITY_EDITOR,
|
||||
FOCUS_COMMAND,
|
||||
KEY_ESCAPE_COMMAND,
|
||||
} from 'lexical'
|
||||
import { useEffect, useRef } from 'react'
|
||||
import { CLEAR_HIDE_MENU_TIMEOUT } from './workflow-variable-block'
|
||||
import { useEffect } from 'react'
|
||||
|
||||
type OnBlurBlockProps = {
|
||||
onBlur?: () => void
|
||||
@@ -20,35 +18,13 @@ const OnBlurBlock: FC<OnBlurBlockProps> = ({
|
||||
}) => {
|
||||
const [editor] = useLexicalComposerContext()
|
||||
|
||||
const ref = useRef<ReturnType<typeof setTimeout> | null>(null)
|
||||
|
||||
useEffect(() => {
|
||||
const clearHideMenuTimeout = () => {
|
||||
if (ref.current) {
|
||||
clearTimeout(ref.current)
|
||||
ref.current = null
|
||||
}
|
||||
}
|
||||
|
||||
const unregister = mergeRegister(
|
||||
editor.registerCommand(
|
||||
CLEAR_HIDE_MENU_TIMEOUT,
|
||||
() => {
|
||||
clearHideMenuTimeout()
|
||||
return true
|
||||
},
|
||||
COMMAND_PRIORITY_EDITOR,
|
||||
),
|
||||
return mergeRegister(
|
||||
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')) {
|
||||
clearHideMenuTimeout()
|
||||
ref.current = setTimeout(() => {
|
||||
editor.dispatchCommand(KEY_ESCAPE_COMMAND, new KeyboardEvent('keydown', { key: 'Escape' }))
|
||||
}, 200)
|
||||
if (onBlur)
|
||||
onBlur()
|
||||
}
|
||||
@@ -66,11 +42,6 @@ const OnBlurBlock: FC<OnBlurBlockProps> = ({
|
||||
COMMAND_PRIORITY_EDITOR,
|
||||
),
|
||||
)
|
||||
|
||||
return () => {
|
||||
clearHideMenuTimeout()
|
||||
unregister()
|
||||
}
|
||||
}, [editor, onBlur, onFocus])
|
||||
|
||||
return null
|
||||
|
||||
@@ -3,7 +3,6 @@ import { $getRoot, $insertNodes } from 'lexical'
|
||||
import { useEventEmitterContextContext } from '@/context/event-emitter'
|
||||
import { textToEditorState } from '../utils'
|
||||
import { CustomTextNode } from './custom-text/node'
|
||||
import { CLEAR_HIDE_MENU_TIMEOUT } from './workflow-variable-block'
|
||||
|
||||
export const PROMPT_EDITOR_UPDATE_VALUE_BY_EVENT_EMITTER = 'PROMPT_EDITOR_UPDATE_VALUE_BY_EVENT_EMITTER'
|
||||
export const PROMPT_EDITOR_INSERT_QUICKLY = 'PROMPT_EDITOR_INSERT_QUICKLY'
|
||||
@@ -36,8 +35,6 @@ const UpdateBlock = ({
|
||||
editor.update(() => {
|
||||
const textNode = new CustomTextNode('/')
|
||||
$insertNodes([textNode])
|
||||
|
||||
editor.dispatchCommand(CLEAR_HIDE_MENU_TIMEOUT, undefined)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
@@ -9,7 +9,6 @@ import { $insertNodes, COMMAND_PRIORITY_EDITOR } from 'lexical'
|
||||
import { Type } from '@/app/components/workflow/nodes/llm/types'
|
||||
import { BlockEnum } from '@/app/components/workflow/types'
|
||||
import {
|
||||
CLEAR_HIDE_MENU_TIMEOUT,
|
||||
DELETE_WORKFLOW_VARIABLE_BLOCK_COMMAND,
|
||||
INSERT_WORKFLOW_VARIABLE_BLOCK_COMMAND,
|
||||
UPDATE_WORKFLOW_NODES_MAP,
|
||||
@@ -134,7 +133,6 @@ describe('WorkflowVariableBlock', () => {
|
||||
const insertHandler = mockRegisterCommand.mock.calls[0][1] as (variables: string[]) => boolean
|
||||
const result = insertHandler(['node-1', 'answer'])
|
||||
|
||||
expect(mockDispatchCommand).toHaveBeenCalledWith(CLEAR_HIDE_MENU_TIMEOUT, undefined)
|
||||
expect($createWorkflowVariableBlockNode).toHaveBeenCalledWith(
|
||||
['node-1', 'answer'],
|
||||
workflowNodesMap,
|
||||
|
||||
@@ -18,7 +18,6 @@ import {
|
||||
|
||||
export const INSERT_WORKFLOW_VARIABLE_BLOCK_COMMAND = createCommand('INSERT_WORKFLOW_VARIABLE_BLOCK_COMMAND')
|
||||
export const DELETE_WORKFLOW_VARIABLE_BLOCK_COMMAND = createCommand('DELETE_WORKFLOW_VARIABLE_BLOCK_COMMAND')
|
||||
export const CLEAR_HIDE_MENU_TIMEOUT = createCommand('CLEAR_HIDE_MENU_TIMEOUT')
|
||||
|
||||
export type UpdateWorkflowNodesMapPayload = {
|
||||
workflowNodesMap: NonNullable<WorkflowVariableBlockType['workflowNodesMap']>
|
||||
@@ -59,7 +58,6 @@ const WorkflowVariableBlock = memo(({
|
||||
editor.registerCommand(
|
||||
INSERT_WORKFLOW_VARIABLE_BLOCK_COMMAND,
|
||||
(insertedVariables: string[]) => {
|
||||
editor.dispatchCommand(CLEAR_HIDE_MENU_TIMEOUT, undefined)
|
||||
const workflowVariableBlockNode = $createWorkflowVariableBlockNode(insertedVariables, workflowNodesMap, getVarType, undefined, undefined, undefined, variables)
|
||||
|
||||
$insertNodes([workflowVariableBlockNode])
|
||||
|
||||
@@ -1981,7 +1981,7 @@
|
||||
},
|
||||
"app/components/base/prompt-editor/plugins/workflow-variable-block/index.tsx": {
|
||||
"react-refresh/only-export-components": {
|
||||
"count": 4
|
||||
"count": 3
|
||||
}
|
||||
},
|
||||
"app/components/base/prompt-editor/plugins/workflow-variable-block/node.tsx": {
|
||||
|
||||
Reference in New Issue
Block a user