BucketTableRow.tsx 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. import { FilesBucket as FilesBucketIcon } from 'icons'
  2. import { ChevronRight } from 'lucide-react'
  3. import { KeyboardEventHandler, MouseEventHandler } from 'react'
  4. import { Badge, cn, TableCell, TableRow, Tooltip, TooltipContent, TooltipTrigger } from 'ui'
  5. import type { AllowedBucketType } from './types'
  6. import { PUBLIC_BUCKET_TOOLTIP } from '@/components/interfaces/Storage/Storage.constants'
  7. import { useBucketPolicyCount } from '@/components/interfaces/Storage/useBucketPolicyCount'
  8. import { VirtualizedTableCell, VirtualizedTableRow } from '@/components/ui/VirtualizedTable'
  9. import { Bucket } from '@/data/storage/buckets-query'
  10. import { formatBytes } from '@/lib/helpers'
  11. type BucketTableMode = 'standard' | 'virtualized'
  12. type BucketTableEmptyStateProps = {
  13. mode: BucketTableMode
  14. filterString: string
  15. }
  16. export const BucketTableEmptyState = ({ mode, filterString }: BucketTableEmptyStateProps) => {
  17. const BucketTableRow = mode === 'standard' ? TableRow : VirtualizedTableRow
  18. const BucketTableCell = mode === 'standard' ? TableCell : VirtualizedTableCell
  19. return (
  20. <BucketTableRow className="[&>td]:hover:bg-inherit">
  21. <BucketTableCell colSpan={5}>
  22. <p className="text-sm text-foreground">No results found</p>
  23. <p className="text-sm text-foreground-lighter">
  24. Your search for “{filterString}” did not return any results
  25. </p>
  26. </BucketTableCell>
  27. </BucketTableRow>
  28. )
  29. }
  30. type BucketTableRowProps = {
  31. mode: BucketTableMode
  32. bucket: Bucket
  33. onSelectBucket: (bucket: Bucket) => void
  34. allowedBucketType: AllowedBucketType
  35. formattedGlobalUploadLimit: string
  36. }
  37. export const BucketTableRow = ({
  38. mode,
  39. bucket,
  40. onSelectBucket,
  41. allowedBucketType,
  42. formattedGlobalUploadLimit,
  43. }: BucketTableRowProps) => {
  44. const { getPolicyCount } = useBucketPolicyCount()
  45. const BucketTableRow = mode === 'standard' ? TableRow : VirtualizedTableRow
  46. const BucketTableCell = mode === 'standard' ? TableCell : VirtualizedTableCell
  47. const isDisabled = !(
  48. allowedBucketType === 'all' ||
  49. (allowedBucketType === 'public' && bucket.public) ||
  50. (allowedBucketType === 'private' && !bucket.public)
  51. )
  52. const handleRowActivate: MouseEventHandler<HTMLTableRowElement> = (e) => {
  53. e.preventDefault()
  54. if (isDisabled) return
  55. onSelectBucket(bucket)
  56. }
  57. const handleRowKeyDown: KeyboardEventHandler<HTMLTableRowElement> = (e) => {
  58. if (isDisabled) return
  59. if (e.key === 'Enter' || e.key === ' ') {
  60. e.preventDefault()
  61. onSelectBucket(bucket)
  62. }
  63. }
  64. return (
  65. <Tooltip>
  66. <TooltipTrigger asChild>
  67. <BucketTableRow
  68. key={bucket.id}
  69. data-bucket-id={bucket.id}
  70. className={cn(
  71. 'relative cursor-pointer h-16 group inset-focus',
  72. isDisabled && 'opacity-50 [&>td]:hover:bg-transparent cursor-not-allowed'
  73. )}
  74. onClick={handleRowActivate}
  75. onKeyDown={handleRowKeyDown}
  76. tabIndex={isDisabled ? -1 : 0}
  77. aria-disabled={isDisabled || undefined}
  78. >
  79. <BucketTableCell className="w-2 pr-1">
  80. <FilesBucketIcon aria-label="bucket icon" size={16} className="text-foreground-muted" />
  81. </BucketTableCell>
  82. <BucketTableCell className="flex-1">
  83. <div className="flex items-center gap-2.5">
  84. <p className="whitespace-nowrap max-w-[512px] truncate">{bucket.id}</p>
  85. {bucket.public && (
  86. <Tooltip>
  87. <TooltipTrigger asChild>
  88. <Badge variant="warning" className="flex">
  89. Public
  90. </Badge>
  91. </TooltipTrigger>
  92. <TooltipContent side="top">{PUBLIC_BUCKET_TOOLTIP}</TooltipContent>
  93. </Tooltip>
  94. )}
  95. </div>
  96. </BucketTableCell>
  97. <BucketTableCell>
  98. <p className="text-foreground-light">{getPolicyCount(bucket.id)}</p>
  99. </BucketTableCell>
  100. <BucketTableCell>
  101. <p
  102. className={`whitespace-nowrap ${bucket.file_size_limit ? 'text-foreground-light' : 'text-foreground-muted'}`}
  103. >
  104. {bucket.file_size_limit
  105. ? formatBytes(bucket.file_size_limit)
  106. : `Unset (${formattedGlobalUploadLimit})`}
  107. </p>
  108. </BucketTableCell>
  109. <BucketTableCell>
  110. <p
  111. className={
  112. bucket.allowed_mime_types ? 'text-foreground-light' : 'text-foreground-muted'
  113. }
  114. >
  115. {bucket.allowed_mime_types ? bucket.allowed_mime_types.join(', ') : 'Any'}
  116. </p>
  117. </BucketTableCell>
  118. <BucketTableCell>
  119. {!isDisabled && (
  120. <>
  121. <div className="flex justify-end items-center h-full">
  122. <ChevronRight aria-hidden={true} size={14} className="text-foreground-muted/60" />
  123. </div>
  124. <button tabIndex={-1} className="sr-only">
  125. Go to bucket details
  126. </button>
  127. </>
  128. )}
  129. </BucketTableCell>
  130. </BucketTableRow>
  131. </TooltipTrigger>
  132. {isDisabled && (
  133. <TooltipContent>
  134. {allowedBucketType === 'public'
  135. ? 'Private buckets are not selectable for this action. Please select a public bucket.'
  136. : 'Public buckets are not selectable for this action. Please select a private bucket.'}
  137. </TooltipContent>
  138. )}
  139. </Tooltip>
  140. )
  141. }