mirror of
https://github.com/langgenius/dify.git
synced 2026-05-02 15:17:39 +08:00
make mention input can display name different color
This commit is contained in:
parent
e0df12c212
commit
0f3f8bc0d9
@ -1,6 +1,6 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import type { FC } from 'react'
|
import type { FC, ReactNode } from 'react'
|
||||||
import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||||
import { createPortal } from 'react-dom'
|
import { createPortal } from 'react-dom'
|
||||||
import { useParams } from 'next/navigation'
|
import { useParams } from 'next/navigation'
|
||||||
@ -47,6 +47,76 @@ export const MentionInput: FC<MentionInputProps> = memo(({
|
|||||||
const [selectedMentionIndex, setSelectedMentionIndex] = useState(0)
|
const [selectedMentionIndex, setSelectedMentionIndex] = useState(0)
|
||||||
const [mentionedUserIds, setMentionedUserIds] = useState<string[]>([])
|
const [mentionedUserIds, setMentionedUserIds] = useState<string[]>([])
|
||||||
|
|
||||||
|
const mentionNameList = useMemo(() => {
|
||||||
|
const names = mentionUsers
|
||||||
|
.map(user => user.name?.trim())
|
||||||
|
.filter((name): name is string => Boolean(name))
|
||||||
|
|
||||||
|
const uniqueNames = Array.from(new Set(names))
|
||||||
|
uniqueNames.sort((a, b) => b.length - a.length)
|
||||||
|
return uniqueNames
|
||||||
|
}, [mentionUsers])
|
||||||
|
|
||||||
|
const highlightedValue = useMemo<ReactNode>(() => {
|
||||||
|
if (!value)
|
||||||
|
return ''
|
||||||
|
|
||||||
|
if (mentionNameList.length === 0)
|
||||||
|
return value
|
||||||
|
|
||||||
|
const segments: ReactNode[] = []
|
||||||
|
let cursor = 0
|
||||||
|
let hasMention = false
|
||||||
|
|
||||||
|
while (cursor < value.length) {
|
||||||
|
let nextMatchStart = -1
|
||||||
|
let matchedName = ''
|
||||||
|
|
||||||
|
for (const name of mentionNameList) {
|
||||||
|
const searchStart = value.indexOf(`@${name}`, cursor)
|
||||||
|
if (searchStart === -1)
|
||||||
|
continue
|
||||||
|
|
||||||
|
const previousChar = searchStart > 0 ? value[searchStart - 1] : ''
|
||||||
|
if (searchStart > 0 && !/\s/.test(previousChar))
|
||||||
|
continue
|
||||||
|
|
||||||
|
if (
|
||||||
|
nextMatchStart === -1
|
||||||
|
|| searchStart < nextMatchStart
|
||||||
|
|| (searchStart === nextMatchStart && name.length > matchedName.length)
|
||||||
|
) {
|
||||||
|
nextMatchStart = searchStart
|
||||||
|
matchedName = name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nextMatchStart === -1)
|
||||||
|
break
|
||||||
|
|
||||||
|
if (nextMatchStart > cursor)
|
||||||
|
segments.push(<span key={`text-${cursor}`}>{value.slice(cursor, nextMatchStart)}</span>)
|
||||||
|
|
||||||
|
const mentionEnd = nextMatchStart + matchedName.length + 1
|
||||||
|
segments.push(
|
||||||
|
<span key={`mention-${nextMatchStart}`} className='text-primary-600'>
|
||||||
|
{value.slice(nextMatchStart, mentionEnd)}
|
||||||
|
</span>,
|
||||||
|
)
|
||||||
|
|
||||||
|
hasMention = true
|
||||||
|
cursor = mentionEnd
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasMention)
|
||||||
|
return value
|
||||||
|
|
||||||
|
if (cursor < value.length)
|
||||||
|
segments.push(<span key={`text-${cursor}`}>{value.slice(cursor)}</span>)
|
||||||
|
|
||||||
|
return segments
|
||||||
|
}, [value, mentionNameList])
|
||||||
|
|
||||||
const loadMentionableUsers = useCallback(async () => {
|
const loadMentionableUsers = useCallback(async () => {
|
||||||
if (!appId) return
|
if (!appId) return
|
||||||
try {
|
try {
|
||||||
@ -220,10 +290,21 @@ export const MentionInput: FC<MentionInputProps> = memo(({
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className={cn('relative flex items-center', className)}>
|
<div className={cn('relative flex items-center', className)}>
|
||||||
|
<div
|
||||||
|
aria-hidden
|
||||||
|
className={cn(
|
||||||
|
'pointer-events-none absolute inset-0 z-0 overflow-hidden whitespace-pre-wrap break-words p-1 leading-6',
|
||||||
|
'body-lg-regular text-text-primary',
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{highlightedValue}
|
||||||
|
{''}
|
||||||
|
</div>
|
||||||
<Textarea
|
<Textarea
|
||||||
ref={textareaRef}
|
ref={textareaRef}
|
||||||
className={cn(
|
className={cn(
|
||||||
'body-lg-regular w-full resize-none bg-transparent p-1 leading-6 text-text-primary caret-primary-500 outline-none',
|
'body-lg-regular relative z-10 w-full resize-none bg-transparent p-1 leading-6 text-transparent caret-primary-500 outline-none',
|
||||||
|
'placeholder:text-text-tertiary',
|
||||||
)}
|
)}
|
||||||
placeholder={placeholder}
|
placeholder={placeholder}
|
||||||
autoFocus={autoFocus}
|
autoFocus={autoFocus}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user