FileList.jsx 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. import classNames from 'classnames'
  2. import { h } from 'preact'
  3. import FileItem from './FileItem/index.jsx'
  4. import VirtualList from './VirtualList.jsx'
  5. function chunks (list, size) {
  6. const chunked = []
  7. let currentChunk = []
  8. list.forEach((item) => {
  9. if (currentChunk.length < size) {
  10. currentChunk.push(item)
  11. } else {
  12. chunked.push(currentChunk)
  13. currentChunk = [item]
  14. }
  15. })
  16. if (currentChunk.length) chunked.push(currentChunk)
  17. return chunked
  18. }
  19. export default (props) => {
  20. const noFiles = props.totalFileCount === 0
  21. const dashboardFilesClass = classNames(
  22. 'uppy-Dashboard-files',
  23. { 'uppy-Dashboard-files--noFiles': noFiles },
  24. )
  25. // It's not great that this is hardcoded!
  26. // It's ESPECIALLY not great that this is checking against `itemsPerRow`!
  27. const rowHeight = props.itemsPerRow === 1
  28. // Mobile
  29. ? 71
  30. // 190px height + 2 * 5px margin
  31. : 200
  32. const fileProps = {
  33. // FIXME This is confusing, it's actually the Dashboard's plugin ID
  34. id: props.id,
  35. error: props.error,
  36. // TODO move this to context
  37. i18n: props.i18n,
  38. uppy: props.uppy,
  39. // features
  40. acquirers: props.acquirers,
  41. resumableUploads: props.resumableUploads,
  42. individualCancellation: props.individualCancellation,
  43. // visual options
  44. hideRetryButton: props.hideRetryButton,
  45. hidePauseResumeButton: props.hidePauseResumeButton,
  46. hideCancelButton: props.hideCancelButton,
  47. showLinkToFileUploadResult: props.showLinkToFileUploadResult,
  48. showRemoveButtonAfterComplete: props.showRemoveButtonAfterComplete,
  49. isWide: props.isWide,
  50. metaFields: props.metaFields,
  51. recoveredState: props.recoveredState,
  52. // callbacks
  53. toggleFileCard: props.toggleFileCard,
  54. handleRequestThumbnail: props.handleRequestThumbnail,
  55. handleCancelThumbnail: props.handleCancelThumbnail,
  56. }
  57. const sortByGhostComesFirst = (file1, file2) => {
  58. return props.files[file2].isGhost - props.files[file1].isGhost
  59. }
  60. // Sort files by file.isGhost, ghost files first, only if recoveredState is present
  61. const files = Object.keys(props.files)
  62. if (props.recoveredState) files.sort(sortByGhostComesFirst)
  63. const rows = chunks(files, props.itemsPerRow)
  64. const renderRow = (row) => (
  65. // The `role="presentation` attribute ensures that the list items are properly
  66. // associated with the `VirtualList` element.
  67. // We use the first file ID as the key—this should not change across scroll rerenders
  68. <div role="presentation" key={row[0]}>
  69. {row.map((fileID) => (
  70. <FileItem
  71. key={fileID}
  72. uppy={props.uppy}
  73. {...fileProps} // eslint-disable-line react/jsx-props-no-spreading
  74. role="listitem"
  75. openFileEditor={props.openFileEditor}
  76. canEditFile={props.canEditFile}
  77. toggleAddFilesPanel={props.toggleAddFilesPanel}
  78. file={props.files[fileID]}
  79. />
  80. ))}
  81. </div>
  82. )
  83. return (
  84. <VirtualList
  85. class={dashboardFilesClass}
  86. role="list"
  87. data={rows}
  88. renderRow={renderRow}
  89. rowHeight={rowHeight}
  90. />
  91. )
  92. }