file-item.tsx 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  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 } = file
  34. const ext = getFileExtension(name, type)
  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. {formatFileSize(file.size || 0)}
  77. </div>
  78. {
  79. showDownloadAction && (
  80. <ActionButton
  81. size='m'
  82. className='hidden group-hover/file-item:flex absolute -right-1 -top-1'
  83. onClick={(e) => {
  84. e.stopPropagation()
  85. downloadFile(url || '', name)
  86. }}
  87. >
  88. <RiDownloadLine className='w-3.5 h-3.5 text-text-tertiary' />
  89. </ActionButton>
  90. )
  91. }
  92. {
  93. progress >= 0 && !fileIsUploaded(file) && (
  94. <ProgressCircle
  95. percentage={progress}
  96. size={12}
  97. />
  98. )
  99. }
  100. {
  101. uploadError && (
  102. <ReplayLine
  103. className='w-4 h-4 text-text-tertiary'
  104. onClick={() => onReUpload?.(id)}
  105. />
  106. )
  107. }
  108. </div>
  109. </div>
  110. )
  111. }
  112. export default FileItem