chat-image-uploader.tsx 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. import type { FC } from 'react'
  2. import { useState } from 'react'
  3. import { useTranslation } from 'react-i18next'
  4. import Uploader from './uploader'
  5. import ImageLinkInput from './image-link-input'
  6. import { ImagePlus } from '@/app/components/base/icons/src/vender/line/images'
  7. import { TransferMethod } from '@/types/app'
  8. import {
  9. PortalToFollowElem,
  10. PortalToFollowElemContent,
  11. PortalToFollowElemTrigger,
  12. } from '@/app/components/base/portal-to-follow-elem'
  13. import { Upload03 } from '@/app/components/base/icons/src/vender/line/general'
  14. import type { ImageFile, VisionSettings } from '@/types/app'
  15. type UploadOnlyFromLocalProps = {
  16. onUpload: (imageFile: ImageFile) => void
  17. disabled?: boolean
  18. limit?: number
  19. }
  20. const UploadOnlyFromLocal: FC<UploadOnlyFromLocalProps> = ({
  21. onUpload,
  22. disabled,
  23. limit,
  24. }) => {
  25. return (
  26. <Uploader onUpload={onUpload} disabled={disabled} limit={limit}>
  27. {
  28. hovering => (
  29. <div className={`
  30. relative flex items-center justify-center w-8 h-8 rounded-lg cursor-pointer
  31. ${hovering && 'bg-gray-100'}
  32. `}>
  33. <ImagePlus className='w-4 h-4 text-gray-500' />
  34. </div>
  35. )
  36. }
  37. </Uploader>
  38. )
  39. }
  40. type UploaderButtonProps = {
  41. methods: VisionSettings['transfer_methods']
  42. onUpload: (imageFile: ImageFile) => void
  43. disabled?: boolean
  44. limit?: number
  45. }
  46. const UploaderButton: FC<UploaderButtonProps> = ({
  47. methods,
  48. onUpload,
  49. disabled,
  50. limit,
  51. }) => {
  52. const { t } = useTranslation()
  53. const [open, setOpen] = useState(false)
  54. const hasUploadFromLocal = methods.find(method => method === TransferMethod.local_file)
  55. const handleUpload = (imageFile: ImageFile) => {
  56. setOpen(false)
  57. onUpload(imageFile)
  58. }
  59. const handleToggle = () => {
  60. if (disabled)
  61. return
  62. setOpen(v => !v)
  63. }
  64. return (
  65. <PortalToFollowElem
  66. open={open}
  67. onOpenChange={setOpen}
  68. placement='top-start'
  69. >
  70. <PortalToFollowElemTrigger onClick={handleToggle}>
  71. <div className={`
  72. relative flex items-center justify-center w-8 h-8 hover:bg-gray-100 rounded-lg
  73. ${disabled ? 'cursor-not-allowed' : 'cursor-pointer'}
  74. `}>
  75. <ImagePlus className='w-4 h-4 text-gray-500' />
  76. </div>
  77. </PortalToFollowElemTrigger>
  78. <PortalToFollowElemContent className='z-50'>
  79. <div className='p-2 w-[260px] bg-white rounded-lg border-[0.5px] border-gray-200 shadow-lg'>
  80. <ImageLinkInput onUpload={handleUpload} />
  81. {
  82. hasUploadFromLocal && (
  83. <>
  84. <div className='flex items-center mt-2 px-2 text-xs font-medium text-gray-400'>
  85. <div className='mr-3 w-[93px] h-[1px] bg-gradient-to-l from-[#F3F4F6]' />
  86. OR
  87. <div className='ml-3 w-[93px] h-[1px] bg-gradient-to-r from-[#F3F4F6]' />
  88. </div>
  89. <Uploader onUpload={handleUpload} limit={limit}>
  90. {
  91. hovering => (
  92. <div className={`
  93. flex items-center justify-center h-8 text-[13px] font-medium text-[#155EEF] rounded-lg cursor-pointer
  94. ${hovering && 'bg-primary-50'}
  95. `}>
  96. <Upload03 className='mr-1 w-4 h-4' />
  97. {t('common.imageUploader.uploadFromComputer')}
  98. </div>
  99. )
  100. }
  101. </Uploader>
  102. </>
  103. )
  104. }
  105. </div>
  106. </PortalToFollowElemContent>
  107. </PortalToFollowElem>
  108. )
  109. }
  110. type ChatImageUploaderProps = {
  111. settings: VisionSettings
  112. onUpload: (imageFile: ImageFile) => void
  113. disabled?: boolean
  114. }
  115. const ChatImageUploader: FC<ChatImageUploaderProps> = ({
  116. settings,
  117. onUpload,
  118. disabled,
  119. }) => {
  120. const onlyUploadLocal = settings.transfer_methods.length === 1 && settings.transfer_methods[0] === TransferMethod.local_file
  121. if (onlyUploadLocal) {
  122. return (
  123. <UploadOnlyFromLocal
  124. onUpload={onUpload}
  125. disabled={disabled}
  126. limit={+settings.image_file_size_limit!}
  127. />
  128. )
  129. }
  130. return (
  131. <UploaderButton
  132. methods={settings.transfer_methods}
  133. onUpload={onUpload}
  134. disabled={disabled}
  135. limit={+settings.image_file_size_limit!}
  136. />
  137. )
  138. }
  139. export default ChatImageUploader