| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130 | import {  memo,  useState,} from 'react'import { useTranslation } from 'react-i18next'import { RiUploadCloud2Line } from '@remixicon/react'import FileInput from '../file-input'import { useFile } from '../hooks'import { useStore } from '../store'import { FILE_URL_REGEX } from '../constants'import {  PortalToFollowElem,  PortalToFollowElemContent,  PortalToFollowElemTrigger,} from '@/app/components/base/portal-to-follow-elem'import Button from '@/app/components/base/button'import type { FileUpload } from '@/app/components/base/features/types'import cn from '@/utils/classnames'type FileFromLinkOrLocalProps = {  showFromLink?: boolean  showFromLocal?: boolean  trigger: (open: boolean) => React.ReactNode  fileConfig: FileUpload}const FileFromLinkOrLocal = ({  showFromLink = true,  showFromLocal = true,  trigger,  fileConfig,}: FileFromLinkOrLocalProps) => {  const { t } = useTranslation()  const files = useStore(s => s.files)  const [open, setOpen] = useState(false)  const [url, setUrl] = useState('')  const [showError, setShowError] = useState(false)  const { handleLoadFileFromLink } = useFile(fileConfig)  const disabled = !!fileConfig.number_limits && files.length >= fileConfig.number_limits  const handleSaveUrl = () => {    if (!url)      return    if (!FILE_URL_REGEX.test(url)) {      setShowError(true)      return    }    handleLoadFileFromLink(url)    setUrl('')  }  return (    <PortalToFollowElem      placement='top'      offset={4}      open={open}      onOpenChange={setOpen}    >      <PortalToFollowElemTrigger onClick={() => setOpen(v => !v)} asChild>        {trigger(open)}      </PortalToFollowElemTrigger>      <PortalToFollowElemContent className='z-10'>        <div className='p-3 w-[280px] bg-components-panel-bg-blur border-[0.5px] border-components-panel-border rounded-xl shadow-lg'>          {            showFromLink && (              <>                <div className={cn(                  'flex items-center p-1 h-8 bg-components-input-bg-active border border-components-input-border-active rounded-lg shadow-xs',                  showError && 'border-components-input-border-destructive',                )}>                  <input                    className='grow block mr-0.5 px-1 bg-transparent system-sm-regular outline-none appearance-none'                    placeholder={t('common.fileUploader.pasteFileLinkInputPlaceholder') || ''}                    value={url}                    onChange={(e) => {                      setShowError(false)                      setUrl(e.target.value)                    }}                    disabled={disabled}                  />                  <Button                    className='shrink-0'                    size='small'                    variant='primary'                    disabled={!url || disabled}                    onClick={handleSaveUrl}                  >                    {t('common.operation.ok')}                  </Button>                </div>                {                  showError && (                    <div className='mt-0.5 body-xs-regular text-text-destructive'>                      {t('common.fileUploader.pasteFileLinkInvalid')}                    </div>                  )                }              </>            )          }          {            showFromLink && showFromLocal && (              <div className='flex items-center p-2 h-7 system-2xs-medium-uppercase text-text-quaternary'>                <div className='mr-2 w-[93px] h-[1px] bg-gradient-to-l from-[rgba(16,24,40,0.08)]' />                OR                <div className='ml-2 w-[93px] h-[1px] bg-gradient-to-r from-[rgba(16,24,40,0.08)]' />              </div>            )          }          {            showFromLocal && (              <Button                className='relative w-full'                variant='secondary-accent'                disabled={disabled}              >                <RiUploadCloud2Line className='mr-1 w-4 h-4' />                {t('common.fileUploader.uploadFromComputer')}                <FileInput fileConfig={fileConfig} />              </Button>            )          }        </div>      </PortalToFollowElemContent>    </PortalToFollowElem>  )}export default memo(FileFromLinkOrLocal)
 |