file-item.tsx 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. import {
  2. RiCloseLine,
  3. RiDownloadLine,
  4. } from '@remixicon/react'
  5. import {
  6. downloadFile,
  7. fileIsUploaded,
  8. getFileAppearanceType,
  9. getFileExtension,
  10. } from '../utils'
  11. import FileTypeIcon from '../file-type-icon'
  12. import type { FileEntity } from '../types'
  13. import cn from '@/utils/classnames'
  14. import { formatFileSize } from '@/utils/format'
  15. import ProgressCircle from '@/app/components/base/progress-bar/progress-circle'
  16. import { ReplayLine } from '@/app/components/base/icons/src/vender/other'
  17. import ActionButton from '@/app/components/base/action-button'
  18. import Button from '@/app/components/base/button'
  19. type FileItemProps = {
  20. file: FileEntity
  21. showDeleteAction?: boolean
  22. showDownloadAction?: boolean
  23. onRemove?: (fileId: string) => void
  24. onReUpload?: (fileId: string) => void
  25. }
  26. const FileItem = ({
  27. file,
  28. showDeleteAction,
  29. showDownloadAction = true,
  30. onRemove,
  31. onReUpload,
  32. }: FileItemProps) => {
  33. const { id, name, type, progress, url, isRemote } = file
  34. const ext = getFileExtension(name, type, isRemote)
  35. const uploadError = progress === -1
  36. return (
  37. <div
  38. className={cn(
  39. 'group/file-item relative p-2 w-[144px] h-[68px] rounded-lg border-[0.5px] border-components-panel-border bg-components-card-bg shadow-xs',
  40. !uploadError && 'hover:bg-components-card-bg-alt',
  41. uploadError && 'border border-state-destructive-border bg-state-destructive-hover',
  42. uploadError && 'hover:border-[0.5px] hover:border-state-destructive-border bg-state-destructive-hover-alt',
  43. )}
  44. >
  45. {
  46. showDeleteAction && (
  47. <Button
  48. className='hidden group-hover/file-item:flex absolute -right-1.5 -top-1.5 p-0 w-5 h-5 rounded-full z-[11]'
  49. onClick={() => onRemove?.(id)}
  50. >
  51. <RiCloseLine className='w-4 h-4 text-components-button-secondary-text' />
  52. </Button>
  53. )
  54. }
  55. <div
  56. className='mb-1 h-8 line-clamp-2 system-xs-medium text-text-tertiary break-all'
  57. title={name}
  58. >
  59. {name}
  60. </div>
  61. <div className='relative flex items-center justify-between'>
  62. <div className='flex items-center system-2xs-medium-uppercase text-text-tertiary'>
  63. <FileTypeIcon
  64. size='sm'
  65. type={getFileAppearanceType(name, type)}
  66. className='mr-1'
  67. />
  68. {
  69. ext && (
  70. <>
  71. {ext}
  72. <div className='mx-1'>·</div>
  73. </>
  74. )
  75. }
  76. {
  77. !!file.size && formatFileSize(file.size)
  78. }
  79. </div>
  80. {
  81. showDownloadAction && (
  82. <ActionButton
  83. size='m'
  84. className='hidden group-hover/file-item:flex absolute -right-1 -top-1'
  85. onClick={(e) => {
  86. e.stopPropagation()
  87. downloadFile(url || '', name)
  88. }}
  89. >
  90. <RiDownloadLine className='w-3.5 h-3.5 text-text-tertiary' />
  91. </ActionButton>
  92. )
  93. }
  94. {
  95. progress >= 0 && !fileIsUploaded(file) && (
  96. <ProgressCircle
  97. percentage={progress}
  98. size={12}
  99. className='shrink-0'
  100. />
  101. )
  102. }
  103. {
  104. uploadError && (
  105. <ReplayLine
  106. className='w-4 h-4 text-text-tertiary'
  107. onClick={() => onReUpload?.(id)}
  108. />
  109. )
  110. }
  111. </div>
  112. </div>
  113. )
  114. }
  115. export default FileItem