Переглянути джерело

fix: auto closing when close local image uploading (#2767)

Rozstone 1 рік тому
батько
коміт
b75d8ca621

+ 1 - 1
web/app/components/app/configuration/base/warning-mask/index.tsx

@@ -24,7 +24,7 @@ const WarningMask: FC<IWarningMaskProps> = ({
   return (
     <div className={`${s.mask} absolute z-10 inset-0 pt-16`}
     >
-      <div className='mx-auto w-[535px]'>
+      <div className='mx-auto px-10'>
         <div className={`${s.icon} flex items-center justify-center w-11 h-11 rounded-xl bg-white`}>{warningIcon}</div>
         <div className='mt-4 text-[24px] leading-normal font-semibold text-gray-800'>
           {title}

+ 53 - 44
web/app/components/base/image-uploader/chat-image-uploader.tsx

@@ -1,6 +1,7 @@
 import type { FC } from 'react'
 import { useState } from 'react'
 import { useTranslation } from 'react-i18next'
+import cn from 'classnames'
 import Uploader from './uploader'
 import ImageLinkInput from './image-link-input'
 import { ImagePlus } from '@/app/components/base/icons/src/vender/line/images'
@@ -25,16 +26,16 @@ const UploadOnlyFromLocal: FC<UploadOnlyFromLocalProps> = ({
 }) => {
   return (
     <Uploader onUpload={onUpload} disabled={disabled} limit={limit}>
-      {
-        hovering => (
-          <div className={`
+      {hovering => (
+        <div
+          className={`
             relative flex items-center justify-center w-8 h-8 rounded-lg cursor-pointer
             ${hovering && 'bg-gray-100'}
-          `}>
-            <ImagePlus className='w-4 h-4 text-gray-500' />
-          </div>
-        )
-      }
+          `}
+        >
+          <ImagePlus className="w-4 h-4 text-gray-500" />
+        </div>
+      )}
     </Uploader>
   )
 }
@@ -54,13 +55,16 @@ const UploaderButton: FC<UploaderButtonProps> = ({
   const { t } = useTranslation()
   const [open, setOpen] = useState(false)
 
-  const hasUploadFromLocal = methods.find(method => method === TransferMethod.local_file)
+  const hasUploadFromLocal = methods.find(
+    method => method === TransferMethod.local_file,
+  )
 
   const handleUpload = (imageFile: ImageFile) => {
-    setOpen(false)
     onUpload(imageFile)
   }
 
+  const closePopover = () => setOpen(false)
+
   const handleToggle = () => {
     if (disabled)
       return
@@ -72,43 +76,46 @@ const UploaderButton: FC<UploaderButtonProps> = ({
     <PortalToFollowElem
       open={open}
       onOpenChange={setOpen}
-      placement='top-start'
+      placement="top-start"
     >
       <PortalToFollowElemTrigger onClick={handleToggle}>
-        <div className={`
-          relative flex items-center justify-center w-8 h-8 hover:bg-gray-100 rounded-lg
-          ${disabled ? 'cursor-not-allowed' : 'cursor-pointer'}
-        `}>
-          <ImagePlus className='w-4 h-4 text-gray-500' />
-        </div>
+        <button
+          type="button"
+          disabled={disabled}
+          className="relative flex items-center justify-center w-8 h-8 enabled:hover:bg-gray-100 rounded-lg disabled:cursor-not-allowed"
+        >
+          <ImagePlus className="w-4 h-4 text-gray-500" />
+        </button>
       </PortalToFollowElemTrigger>
-      <PortalToFollowElemContent className='z-50'>
-        <div className='p-2 w-[260px] bg-white rounded-lg border-[0.5px] border-gray-200 shadow-lg'>
+      <PortalToFollowElemContent className="z-50">
+        <div className="p-2 w-[260px] bg-white rounded-lg border-[0.5px] border-gray-200 shadow-lg">
           <ImageLinkInput onUpload={handleUpload} />
-          {
-            hasUploadFromLocal && (
-              <>
-                <div className='flex items-center mt-2 px-2 text-xs font-medium text-gray-400'>
-                  <div className='mr-3 w-[93px] h-[1px] bg-gradient-to-l from-[#F3F4F6]' />
-                  OR
-                  <div className='ml-3 w-[93px] h-[1px] bg-gradient-to-r from-[#F3F4F6]' />
-                </div>
-                <Uploader onUpload={handleUpload} limit={limit}>
-                  {
-                    hovering => (
-                      <div className={`
-                        flex items-center justify-center h-8 text-[13px] font-medium text-[#155EEF] rounded-lg cursor-pointer
-                        ${hovering && 'bg-primary-50'}
-                      `}>
-                        <Upload03 className='mr-1 w-4 h-4' />
-                        {t('common.imageUploader.uploadFromComputer')}
-                      </div>
-                    )
-                  }
-                </Uploader>
-              </>
-            )
-          }
+          {hasUploadFromLocal && (
+            <>
+              <div className="flex items-center mt-2 px-2 text-xs font-medium text-gray-400">
+                <div className="mr-3 w-[93px] h-[1px] bg-gradient-to-l from-[#F3F4F6]" />
+                OR
+                <div className="ml-3 w-[93px] h-[1px] bg-gradient-to-r from-[#F3F4F6]" />
+              </div>
+              <Uploader
+                onUpload={handleUpload}
+                limit={limit}
+                closePopover={closePopover}
+              >
+                {hovering => (
+                  <div
+                    className={cn(
+                      'flex items-center justify-center h-8 text-[13px] font-medium text-[#155EEF] rounded-lg cursor-pointer',
+                      hovering && 'bg-primary-50',
+                    )}
+                  >
+                    <Upload03 className="mr-1 w-4 h-4" />
+                    {t('common.imageUploader.uploadFromComputer')}
+                  </div>
+                )}
+              </Uploader>
+            </>
+          )}
         </div>
       </PortalToFollowElemContent>
     </PortalToFollowElem>
@@ -125,7 +132,9 @@ const ChatImageUploader: FC<ChatImageUploaderProps> = ({
   onUpload,
   disabled,
 }) => {
-  const onlyUploadLocal = settings.transfer_methods.length === 1 && settings.transfer_methods[0] === TransferMethod.local_file
+  const onlyUploadLocal
+    = settings.transfer_methods.length === 1
+    && settings.transfer_methods[0] === TransferMethod.local_file
 
   if (onlyUploadLocal) {
     return (

+ 1 - 0
web/app/components/base/image-uploader/image-link-input.tsx

@@ -30,6 +30,7 @@ const ImageLinkInput: FC<ImageLinkInputProps> = ({
   return (
     <div className='flex items-center pl-1.5 pr-1 h-8 border border-gray-200 bg-white shadow-xs rounded-lg'>
       <input
+        type="text"
         className='grow mr-0.5 px-1 h-[18px] text-[13px] outline-none appearance-none'
         value={imageLink}
         onChange={e => setImageLink(e.target.value)}

+ 93 - 79
web/app/components/base/image-uploader/image-list.tsx

@@ -1,7 +1,11 @@
 import type { FC } from 'react'
 import { useState } from 'react'
 import { useTranslation } from 'react-i18next'
-import { Loading02, XClose } from '@/app/components/base/icons/src/vender/line/general'
+import cn from 'classnames'
+import {
+  Loading02,
+  XClose,
+} from '@/app/components/base/icons/src/vender/line/general'
 import { RefreshCcw01 } from '@/app/components/base/icons/src/vender/line/arrows'
 import { AlertTriangle } from '@/app/components/base/icons/src/vender/solid/alertsAndFeedback'
 import TooltipPlus from '@/app/components/base/tooltip-plus'
@@ -30,7 +34,11 @@ const ImageList: FC<ImageListProps> = ({
   const [imagePreviewUrl, setImagePreviewUrl] = useState('')
 
   const handleImageLinkLoadSuccess = (item: ImageFile) => {
-    if (item.type === TransferMethod.remote_url && onImageLinkLoadSuccess && item.progress !== -1)
+    if (
+      item.type === TransferMethod.remote_url
+      && onImageLinkLoadSuccess
+      && item.progress !== -1
+    )
       onImageLinkLoadSuccess(item._id)
   }
   const handleImageLinkLoadError = (item: ImageFile) => {
@@ -39,89 +47,95 @@ const ImageList: FC<ImageListProps> = ({
   }
 
   return (
-    <div className='flex flex-wrap'>
-      {
-        list.map(item => (
-          <div
-            key={item._id}
-            className='group relative mr-1 border-[0.5px] border-black/5 rounded-lg'
-          >
-            {
-              item.type === TransferMethod.local_file && item.progress !== 100 && (
-                <>
-                  <div
-                    className='absolute inset-0 flex items-center justify-center z-[1] bg-black/30'
-                    style={{ left: item.progress > -1 ? `${item.progress}%` : 0 }}
-                  >
-                    {
-                      item.progress === -1 && (
-                        <RefreshCcw01 className='w-5 h-5 text-white' onClick={() => onReUpload && onReUpload(item._id)} />
-                      )
-                    }
-                  </div>
-                  {
-                    item.progress > -1 && (
-                      <span className='absolute top-[50%] left-[50%] translate-x-[-50%] translate-y-[-50%] text-sm text-white mix-blend-lighten z-[1]'>{item.progress}%</span>
-                    )
-                  }
-                </>
-              )
-            }
-            {
-              item.type === TransferMethod.remote_url && item.progress !== 100 && (
-                <div className={`
+    <div className="flex flex-wrap">
+      {list.map(item => (
+        <div
+          key={item._id}
+          className="group relative mr-1 border-[0.5px] border-black/5 rounded-lg"
+        >
+          {item.type === TransferMethod.local_file && item.progress !== 100 && (
+            <>
+              <div
+                className="absolute inset-0 flex items-center justify-center z-[1] bg-black/30"
+                style={{ left: item.progress > -1 ? `${item.progress}%` : 0 }}
+              >
+                {item.progress === -1 && (
+                  <RefreshCcw01
+                    className="w-5 h-5 text-white"
+                    onClick={() => onReUpload && onReUpload(item._id)}
+                  />
+                )}
+              </div>
+              {item.progress > -1 && (
+                <span className="absolute top-[50%] left-[50%] translate-x-[-50%] translate-y-[-50%] text-sm text-white mix-blend-lighten z-[1]">
+                  {item.progress}%
+                </span>
+              )}
+            </>
+          )}
+          {item.type === TransferMethod.remote_url && item.progress !== 100 && (
+            <div
+              className={`
                   absolute inset-0 flex items-center justify-center rounded-lg z-[1] border
-                  ${item.progress === -1 ? 'bg-[#FEF0C7] border-[#DC6803]' : 'bg-black/[0.16] border-transparent'}
-                `}>
-                  {
-                    item.progress > -1 && (
-                      <Loading02 className='animate-spin w-5 h-5 text-white' />
-                    )
-                  }
-                  {
-                    item.progress === -1 && (
-                      <TooltipPlus popupContent={t('common.imageUploader.pasteImageLinkInvalid')}>
-                        <AlertTriangle className='w-4 h-4 text-[#DC6803]' />
-                      </TooltipPlus>
-                    )
-                  }
-                </div>
-              )
+                  ${
+            item.progress === -1
+              ? 'bg-[#FEF0C7] border-[#DC6803]'
+              : 'bg-black/[0.16] border-transparent'
             }
-            <img
-              className='w-16 h-16 rounded-lg object-cover cursor-pointer border-[0.5px] border-black/5'
-              alt=''
-              onLoad={() => handleImageLinkLoadSuccess(item)}
-              onError={() => handleImageLinkLoadError(item)}
-              src={item.type === TransferMethod.remote_url ? item.url : item.base64Url}
-              onClick={() => item.progress === 100 && setImagePreviewUrl((item.type === TransferMethod.remote_url ? item.url : item.base64Url) as string)}
-            />
-            {
-              !readonly && (
-                <div
-                  className={`
-                    absolute z-10 -top-[9px] -right-[9px] items-center justify-center w-[18px] h-[18px] 
-                    bg-white hover:bg-gray-50 border-[0.5px] border-black/[0.02] rounded-2xl shadow-lg
-                    cursor-pointer
-                    ${item.progress === -1 ? 'flex' : 'hidden group-hover:flex'}
-                  `}
-                  onClick={() => onRemove && onRemove(item._id)}
+                `}
+            >
+              {item.progress > -1 && (
+                <Loading02 className="animate-spin w-5 h-5 text-white" />
+              )}
+              {item.progress === -1 && (
+                <TooltipPlus
+                  popupContent={t('common.imageUploader.pasteImageLinkInvalid')}
                 >
-                  <XClose className='w-3 h-3 text-gray-500' />
-                </div>
+                  <AlertTriangle className="w-4 h-4 text-[#DC6803]" />
+                </TooltipPlus>
+              )}
+            </div>
+          )}
+          <img
+            className="w-16 h-16 rounded-lg object-cover cursor-pointer border-[0.5px] border-black/5"
+            alt={item.file?.name}
+            onLoad={() => handleImageLinkLoadSuccess(item)}
+            onError={() => handleImageLinkLoadError(item)}
+            src={
+              item.type === TransferMethod.remote_url
+                ? item.url
+                : item.base64Url
+            }
+            onClick={() =>
+              item.progress === 100
+              && setImagePreviewUrl(
+                (item.type === TransferMethod.remote_url
+                  ? item.url
+                  : item.base64Url) as string,
               )
             }
-          </div>
-        ))
-      }
-      {
-        imagePreviewUrl && (
-          <ImagePreview
-            url={imagePreviewUrl}
-            onCancel={() => setImagePreviewUrl('')}
           />
-        )
-      }
+          {!readonly && (
+            <button
+              type="button"
+              className={cn(
+                'absolute z-10 -top-[9px] -right-[9px] items-center justify-center w-[18px] h-[18px]',
+                'bg-white hover:bg-gray-50 border-[0.5px] border-black/[0.02] rounded-2xl shadow-lg',
+                item.progress === -1 ? 'flex' : 'hidden group-hover:flex',
+              )}
+              onClick={() => onRemove && onRemove(item._id)}
+            >
+              <XClose className="w-3 h-3 text-gray-500" />
+            </button>
+          )}
+        </div>
+      ))}
+      {imagePreviewUrl && (
+        <ImagePreview
+          url={imagePreviewUrl}
+          onCancel={() => setImagePreviewUrl('')}
+        />
+      )}
     </div>
   )
 }

+ 10 - 6
web/app/components/base/image-uploader/uploader.tsx

@@ -7,6 +7,7 @@ import { ALLOW_FILE_EXTENSIONS } from '@/types/app'
 type UploaderProps = {
   children: (hovering: boolean) => JSX.Element
   onUpload: (imageFile: ImageFile) => void
+  closePopover?: () => void
   limit?: number
   disabled?: boolean
 }
@@ -14,11 +15,16 @@ type UploaderProps = {
 const Uploader: FC<UploaderProps> = ({
   children,
   onUpload,
+  closePopover,
   limit,
   disabled,
 }) => {
   const [hovering, setHovering] = useState(false)
-  const { handleLocalFileUpload } = useLocalFileUploader({ limit, onUpload, disabled })
+  const { handleLocalFileUpload } = useLocalFileUploader({
+    limit,
+    onUpload,
+    disabled,
+  })
 
   const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
     const file = e.target.files?.[0]
@@ -27,6 +33,7 @@ const Uploader: FC<UploaderProps> = ({
       return
 
     handleLocalFileUpload(file)
+    closePopover?.()
   }
 
   return (
@@ -37,11 +44,8 @@ const Uploader: FC<UploaderProps> = ({
     >
       {children(hovering)}
       <input
-        className={`
-          absolute block inset-0 opacity-0 text-[0] w-full
-          ${disabled ? 'cursor-not-allowed' : 'cursor-pointer'}
-        `}
-        onClick={e => (e.target as HTMLInputElement).value = ''}
+        className='absolute block inset-0 opacity-0 text-[0] w-full disabled:cursor-not-allowed cursor-pointer'
+        onClick={e => ((e.target as HTMLInputElement).value = '')}
         type='file'
         accept={ALLOW_FILE_EXTENSIONS.map(ext => `.${ext}`).join(',')}
         onChange={handleChange}