mirror of
https://git.mirrors.martin98.com/https://github.com/langgenius/dify.git
synced 2025-08-20 00:49:05 +08:00
file-uploader
This commit is contained in:
parent
6e15d7f777
commit
2498c238b2
@ -16,7 +16,7 @@
|
||||
}
|
||||
|
||||
.action-btn-l {
|
||||
@apply p-1.5 w-[34px] h-[34px] rounded-lg
|
||||
@apply p-1.5 w-8 h-8 rounded-lg
|
||||
}
|
||||
|
||||
/* m is for the regular button */
|
||||
|
36
web/app/components/base/chat/chat/chat-input-area/hooks.ts
Normal file
36
web/app/components/base/chat/chat/chat-input-area/hooks.ts
Normal file
@ -0,0 +1,36 @@
|
||||
import {
|
||||
useCallback,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useRef,
|
||||
useState,
|
||||
} from 'react'
|
||||
import type { TextAreaRef } from 'rc-textarea'
|
||||
|
||||
export const useTextAreaHeight = () => {
|
||||
const textareaRef = useRef<TextAreaRef>(null)
|
||||
const [height, setHeight] = useState(0)
|
||||
|
||||
const handleComputeHeight = () => {
|
||||
const textareaElement = textareaRef.current?.resizableTextArea.textArea
|
||||
if (textareaElement) {
|
||||
const { height } = textareaElement.getBoundingClientRect()
|
||||
|
||||
setHeight(height)
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
handleComputeHeight()
|
||||
}, [])
|
||||
|
||||
const handleTextareaResize = useCallback(() => {
|
||||
handleComputeHeight()
|
||||
}, [])
|
||||
|
||||
return {
|
||||
textareaRef,
|
||||
handleTextareaResize,
|
||||
isMultipleLine: useMemo(() => height > 32, [height]),
|
||||
}
|
||||
}
|
51
web/app/components/base/chat/chat/chat-input-area/index.tsx
Normal file
51
web/app/components/base/chat/chat/chat-input-area/index.tsx
Normal file
@ -0,0 +1,51 @@
|
||||
import {
|
||||
memo,
|
||||
} from 'react'
|
||||
import Textarea from 'rc-textarea'
|
||||
import { RiSendPlane2Fill } from '@remixicon/react'
|
||||
import { useTextAreaHeight } from './hooks'
|
||||
import Button from '@/app/components/base/button'
|
||||
import { FileUploaderInChatInput } from '@/app/components/base/file-uploader'
|
||||
import cn from '@/utils/classnames'
|
||||
|
||||
const ChatInputArea = () => {
|
||||
const {
|
||||
textareaRef,
|
||||
handleTextareaResize,
|
||||
isMultipleLine,
|
||||
} = useTextAreaHeight()
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
'p-[9px] bg-components-panel-bg-blur border border-components-chat-input-border rounded-xl shadow-md',
|
||||
'max-h-[210px] overflow-y-auto',
|
||||
!isMultipleLine && 'flex items-center',
|
||||
)}
|
||||
>
|
||||
<Textarea
|
||||
ref={textareaRef}
|
||||
className='grow w-full p-1 leading-6 body-lg-regular text-text-tertiary'
|
||||
placeholder='Enter message...'
|
||||
autoSize
|
||||
onResize={handleTextareaResize}
|
||||
/>
|
||||
<div className={cn(
|
||||
'shrink-0 flex items-center justify-end ml-1',
|
||||
isMultipleLine && 'sticky bottom-0',
|
||||
)}>
|
||||
<div className='flex items-center'>
|
||||
<FileUploaderInChatInput />
|
||||
</div>
|
||||
<Button
|
||||
className='ml-3 px-0 w-8'
|
||||
variant='primary'
|
||||
>
|
||||
<RiSendPlane2Fill className='w-4 h-4' />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default memo(ChatInputArea)
|
@ -0,0 +1,9 @@
|
||||
import { memo } from 'react'
|
||||
|
||||
const FileListFlex = () => {
|
||||
return (
|
||||
<div className='flex'></div>
|
||||
)
|
||||
}
|
||||
|
||||
export default memo(FileListFlex)
|
@ -0,0 +1,9 @@
|
||||
import { memo } from 'react'
|
||||
|
||||
const FileListFullWidth = () => {
|
||||
return (
|
||||
<div></div>
|
||||
)
|
||||
}
|
||||
|
||||
export default memo(FileListFullWidth)
|
@ -0,0 +1,51 @@
|
||||
import {
|
||||
memo,
|
||||
useState,
|
||||
} from 'react'
|
||||
import {
|
||||
RiAttachmentLine,
|
||||
} from '@remixicon/react'
|
||||
import {
|
||||
PortalToFollowElem,
|
||||
PortalToFollowElemContent,
|
||||
PortalToFollowElemTrigger,
|
||||
} from '@/app/components/base/portal-to-follow-elem'
|
||||
import ActionButton from '@/app/components/base/action-button'
|
||||
import Button from '@/app/components/base/button'
|
||||
|
||||
const FileUploaderInChatInput = () => {
|
||||
const [open, setOpen] = useState(false)
|
||||
|
||||
return (
|
||||
<PortalToFollowElem
|
||||
open={open}
|
||||
onOpenChange={setOpen}
|
||||
placement='top'
|
||||
offset={4}
|
||||
>
|
||||
<PortalToFollowElemTrigger onClick={() => setOpen(v => !v)}>
|
||||
<ActionButton size='l'>
|
||||
<RiAttachmentLine className='w-5 h-5' />
|
||||
</ActionButton>
|
||||
</PortalToFollowElemTrigger>
|
||||
<PortalToFollowElemContent>
|
||||
<div className='p-3 w-[280px] bg-components-panel-bg-blur border-[0.5px] border-components-panel-border rounded-xl shadow-lg'>
|
||||
<div className='flex items-center p-1 bg-components-input-bg-active border border-components-input-border-active rounded-lg shadow-xs'>
|
||||
<input
|
||||
className='mr-0.5 p-1 appearance-none system-sm-regular text-text-secondary'
|
||||
placeholder='Enter URL...'
|
||||
/>
|
||||
<Button
|
||||
size='small'
|
||||
variant='primary'
|
||||
>
|
||||
OK ↩
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</PortalToFollowElemContent>
|
||||
</PortalToFollowElem>
|
||||
)
|
||||
}
|
||||
|
||||
export default memo(FileUploaderInChatInput)
|
@ -1,2 +1,3 @@
|
||||
export { default as FileUploaderInAttachment } from './file-uploader-in-attachment'
|
||||
export { default as FileUploaderInChatInput } from './file-uploader-in-chat-input'
|
||||
export { default as FileTypeIcon } from './file-type-icon'
|
||||
|
Loading…
x
Reference in New Issue
Block a user