mirror of
https://github.com/langgenius/dify.git
synced 2026-04-29 04:26:30 +08:00
feat: prevent duplicate @ insertion in mention input with visual feedback
This commit is contained in:
parent
6af1fea232
commit
e9cdc96c74
@ -277,6 +277,19 @@ export const MentionInput: FC<MentionInputProps> = memo(({
|
|||||||
)
|
)
|
||||||
}, [mentionUsers, mentionQuery])
|
}, [mentionUsers, mentionQuery])
|
||||||
|
|
||||||
|
const shouldDisableMentionButton = useMemo(() => {
|
||||||
|
if (showMentionDropdown)
|
||||||
|
return true
|
||||||
|
|
||||||
|
const textarea = textareaRef.current
|
||||||
|
if (!textarea)
|
||||||
|
return false
|
||||||
|
|
||||||
|
const cursorPosition = textarea.selectionStart || 0
|
||||||
|
const textBeforeCursor = value.slice(0, cursorPosition)
|
||||||
|
return /@\w*$/.test(textBeforeCursor)
|
||||||
|
}, [showMentionDropdown, value])
|
||||||
|
|
||||||
const dropdownPosition = useMemo(() => {
|
const dropdownPosition = useMemo(() => {
|
||||||
if (!showMentionDropdown || !textareaRef.current)
|
if (!showMentionDropdown || !textareaRef.current)
|
||||||
return { x: 0, y: 0, placement: 'bottom' as const }
|
return { x: 0, y: 0, placement: 'bottom' as const }
|
||||||
@ -328,9 +341,20 @@ export const MentionInput: FC<MentionInputProps> = memo(({
|
|||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
|
|
||||||
const textarea = textareaRef.current
|
const textarea = textareaRef.current
|
||||||
if (!textarea) return
|
if (!textarea)
|
||||||
|
return
|
||||||
|
|
||||||
const cursorPosition = textarea.selectionStart || 0
|
const cursorPosition = textarea.selectionStart || 0
|
||||||
|
const textBeforeCursor = value.slice(0, cursorPosition)
|
||||||
|
|
||||||
|
// 🔒 如果已经在 mention 模式,不插入新的 @
|
||||||
|
if (showMentionDropdown)
|
||||||
|
return
|
||||||
|
|
||||||
|
// 🔒 如果光标前已有未完成的 @,不插入新的 @
|
||||||
|
if (/@\w*$/.test(textBeforeCursor))
|
||||||
|
return
|
||||||
|
|
||||||
const newContent = `${value.slice(0, cursorPosition)}@${value.slice(cursorPosition)}`
|
const newContent = `${value.slice(0, cursorPosition)}@${value.slice(cursorPosition)}`
|
||||||
|
|
||||||
onChange(newContent)
|
onChange(newContent)
|
||||||
@ -352,7 +376,7 @@ export const MentionInput: FC<MentionInputProps> = memo(({
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}, 0)
|
}, 0)
|
||||||
}, [value, onChange, evaluateContentLayout, syncHighlightScroll])
|
}, [value, onChange, evaluateContentLayout, syncHighlightScroll, showMentionDropdown])
|
||||||
|
|
||||||
const insertMention = useCallback((user: UserProfile) => {
|
const insertMention = useCallback((user: UserProfile) => {
|
||||||
const textarea = textareaRef.current
|
const textarea = textareaRef.current
|
||||||
@ -500,8 +524,13 @@ export const MentionInput: FC<MentionInputProps> = memo(({
|
|||||||
className="absolute bottom-0 right-1 z-20 flex items-end gap-1"
|
className="absolute bottom-0 right-1 z-20 flex items-end gap-1"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className="z-20 flex h-8 w-8 cursor-pointer items-center justify-center rounded-lg hover:bg-state-base-hover"
|
className={cn(
|
||||||
onClick={handleMentionButtonClick}
|
'z-20 flex h-8 w-8 items-center justify-center rounded-lg transition-opacity',
|
||||||
|
shouldDisableMentionButton
|
||||||
|
? 'cursor-not-allowed opacity-40'
|
||||||
|
: 'cursor-pointer hover:bg-state-base-hover',
|
||||||
|
)}
|
||||||
|
onClick={shouldDisableMentionButton ? undefined : handleMentionButtonClick}
|
||||||
>
|
>
|
||||||
<RiAtLine className="h-4 w-4 text-text-tertiary" />
|
<RiAtLine className="h-4 w-4 text-text-tertiary" />
|
||||||
</div>
|
</div>
|
||||||
@ -522,8 +551,13 @@ export const MentionInput: FC<MentionInputProps> = memo(({
|
|||||||
className="absolute bottom-0 left-1 right-1 z-20 flex items-end justify-between"
|
className="absolute bottom-0 left-1 right-1 z-20 flex items-end justify-between"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className="z-20 flex h-8 w-8 cursor-pointer items-center justify-center rounded-lg hover:bg-state-base-hover"
|
className={cn(
|
||||||
onClick={handleMentionButtonClick}
|
'z-20 flex h-8 w-8 items-center justify-center rounded-lg transition-opacity',
|
||||||
|
shouldDisableMentionButton
|
||||||
|
? 'cursor-not-allowed opacity-40'
|
||||||
|
: 'cursor-pointer hover:bg-state-base-hover',
|
||||||
|
)}
|
||||||
|
onClick={shouldDisableMentionButton ? undefined : handleMentionButtonClick}
|
||||||
>
|
>
|
||||||
<RiAtLine className="h-4 w-4 text-text-tertiary" />
|
<RiAtLine className="h-4 w-4 text-text-tertiary" />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user