StatusBarUI.tsx 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  1. import type { Body, Meta, UppyFile } from '@uppy/utils/lib/UppyFile'
  2. import type { I18n } from '@uppy/utils/lib/Translator'
  3. import type { Uppy, State } from '@uppy/core/lib/Uppy'
  4. import { h } from 'preact'
  5. import classNames from 'classnames'
  6. import statusBarStates from './StatusBarStates.ts'
  7. import calculateProcessingProgress from './calculateProcessingProgress.ts'
  8. import {
  9. UploadBtn,
  10. RetryBtn,
  11. CancelBtn,
  12. PauseResumeButton,
  13. DoneBtn,
  14. ProgressBarProcessing,
  15. ProgressBarError,
  16. ProgressBarUploading,
  17. ProgressBarComplete,
  18. } from './Components.tsx'
  19. const {
  20. STATE_ERROR,
  21. STATE_WAITING,
  22. STATE_PREPROCESSING,
  23. STATE_UPLOADING,
  24. STATE_POSTPROCESSING,
  25. STATE_COMPLETE,
  26. } = statusBarStates
  27. export interface StatusBarUIProps<M extends Meta, B extends Body> {
  28. newFiles: number
  29. allowNewUpload: boolean
  30. isUploadInProgress: boolean
  31. isAllPaused: boolean
  32. resumableUploads: boolean
  33. error: any
  34. hideUploadButton?: boolean
  35. hidePauseResumeButton?: boolean
  36. hideCancelButton?: boolean
  37. hideRetryButton?: boolean
  38. recoveredState: State<M, B>['recoveredState']
  39. uploadState: (typeof statusBarStates)[keyof typeof statusBarStates]
  40. totalProgress: number
  41. files: Record<string, UppyFile<M, B>>
  42. supportsUploadProgress: boolean
  43. hideAfterFinish?: boolean
  44. isSomeGhost: boolean
  45. doneButtonHandler?: (() => void) | null
  46. isUploadStarted: boolean
  47. i18n: I18n
  48. startUpload: () => void
  49. uppy: Uppy<M, B>
  50. isAllComplete: boolean
  51. showProgressDetails?: boolean
  52. numUploads: number
  53. complete: number
  54. totalSize: number
  55. totalETA: number
  56. totalUploadedSize: number
  57. }
  58. // TODO: rename the function to StatusBarUI on the next major.
  59. export default function StatusBar<M extends Meta, B extends Body>(
  60. props: StatusBarUIProps<M, B>,
  61. ): JSX.Element {
  62. const {
  63. newFiles,
  64. allowNewUpload,
  65. isUploadInProgress,
  66. isAllPaused,
  67. resumableUploads,
  68. error,
  69. hideUploadButton,
  70. hidePauseResumeButton,
  71. hideCancelButton,
  72. hideRetryButton,
  73. recoveredState,
  74. uploadState,
  75. totalProgress,
  76. files,
  77. supportsUploadProgress,
  78. hideAfterFinish,
  79. isSomeGhost,
  80. doneButtonHandler,
  81. isUploadStarted,
  82. i18n,
  83. startUpload,
  84. uppy,
  85. isAllComplete,
  86. showProgressDetails,
  87. numUploads,
  88. complete,
  89. totalSize,
  90. totalETA,
  91. totalUploadedSize,
  92. } = props
  93. function getProgressValue(): number | null {
  94. switch (uploadState) {
  95. case STATE_POSTPROCESSING:
  96. case STATE_PREPROCESSING: {
  97. const progress = calculateProcessingProgress(files)
  98. if (progress.mode === 'determinate') {
  99. return progress.value * 100
  100. }
  101. return totalProgress
  102. }
  103. case STATE_ERROR: {
  104. return null
  105. }
  106. case STATE_UPLOADING: {
  107. if (!supportsUploadProgress) {
  108. return null
  109. }
  110. return totalProgress
  111. }
  112. default:
  113. return totalProgress
  114. }
  115. }
  116. function getIsIndeterminate(): boolean {
  117. switch (uploadState) {
  118. case STATE_POSTPROCESSING:
  119. case STATE_PREPROCESSING: {
  120. const { mode } = calculateProcessingProgress(files)
  121. return mode === 'indeterminate'
  122. }
  123. case STATE_UPLOADING: {
  124. if (!supportsUploadProgress) {
  125. return true
  126. }
  127. return false
  128. }
  129. default:
  130. return false
  131. }
  132. }
  133. function getIsHidden(): boolean | undefined {
  134. if (recoveredState) {
  135. return false
  136. }
  137. switch (uploadState) {
  138. case STATE_WAITING:
  139. return hideUploadButton || newFiles === 0
  140. case STATE_COMPLETE:
  141. return hideAfterFinish
  142. default:
  143. return false
  144. }
  145. }
  146. const progressValue = getProgressValue()
  147. const isHidden = getIsHidden()
  148. const width = progressValue ?? 100
  149. const showUploadBtn =
  150. !error &&
  151. newFiles &&
  152. !isUploadInProgress &&
  153. !isAllPaused &&
  154. allowNewUpload &&
  155. !hideUploadButton
  156. const showCancelBtn =
  157. !hideCancelButton &&
  158. uploadState !== STATE_WAITING &&
  159. uploadState !== STATE_COMPLETE
  160. const showPauseResumeBtn =
  161. resumableUploads &&
  162. !hidePauseResumeButton &&
  163. uploadState === STATE_UPLOADING
  164. const showRetryBtn = error && !isAllComplete && !hideRetryButton
  165. const showDoneBtn = doneButtonHandler && uploadState === STATE_COMPLETE
  166. const progressClassNames = classNames('uppy-StatusBar-progress', {
  167. 'is-indeterminate': getIsIndeterminate(),
  168. })
  169. const statusBarClassNames = classNames(
  170. 'uppy-StatusBar',
  171. `is-${uploadState}`,
  172. { 'has-ghosts': isSomeGhost },
  173. )
  174. return (
  175. <div className={statusBarClassNames} aria-hidden={isHidden}>
  176. <div
  177. className={progressClassNames}
  178. style={{ width: `${width}%` }}
  179. role="progressbar"
  180. aria-label={`${width}%`}
  181. aria-valuetext={`${width}%`}
  182. aria-valuemin={0}
  183. aria-valuemax={100}
  184. aria-valuenow={progressValue!}
  185. />
  186. {((): JSX.Element | null => {
  187. switch (uploadState) {
  188. case STATE_PREPROCESSING:
  189. case STATE_POSTPROCESSING:
  190. return (
  191. <ProgressBarProcessing
  192. progress={calculateProcessingProgress(files)}
  193. />
  194. )
  195. case STATE_COMPLETE:
  196. return <ProgressBarComplete i18n={i18n} />
  197. case STATE_ERROR:
  198. return (
  199. <ProgressBarError
  200. error={error}
  201. i18n={i18n}
  202. numUploads={numUploads}
  203. complete={complete}
  204. />
  205. )
  206. case STATE_UPLOADING:
  207. return (
  208. <ProgressBarUploading
  209. i18n={i18n}
  210. supportsUploadProgress={supportsUploadProgress}
  211. totalProgress={totalProgress}
  212. showProgressDetails={showProgressDetails}
  213. isUploadStarted={isUploadStarted}
  214. isAllComplete={isAllComplete}
  215. isAllPaused={isAllPaused}
  216. newFiles={newFiles}
  217. numUploads={numUploads}
  218. complete={complete}
  219. totalUploadedSize={totalUploadedSize}
  220. totalSize={totalSize}
  221. totalETA={totalETA}
  222. startUpload={startUpload}
  223. />
  224. )
  225. default:
  226. return null
  227. }
  228. })()}
  229. <div className="uppy-StatusBar-actions">
  230. {recoveredState || showUploadBtn ?
  231. <UploadBtn
  232. newFiles={newFiles}
  233. isUploadStarted={isUploadStarted}
  234. recoveredState={recoveredState}
  235. i18n={i18n}
  236. isSomeGhost={isSomeGhost}
  237. startUpload={startUpload}
  238. uploadState={uploadState}
  239. />
  240. : null}
  241. {showRetryBtn ?
  242. <RetryBtn i18n={i18n} uppy={uppy} />
  243. : null}
  244. {showPauseResumeBtn ?
  245. <PauseResumeButton
  246. isAllPaused={isAllPaused}
  247. i18n={i18n}
  248. isAllComplete={isAllComplete}
  249. resumableUploads={resumableUploads}
  250. uppy={uppy}
  251. />
  252. : null}
  253. {showCancelBtn ?
  254. <CancelBtn i18n={i18n} uppy={uppy} />
  255. : null}
  256. {showDoneBtn ?
  257. <DoneBtn i18n={i18n} doneButtonHandler={doneButtonHandler} />
  258. : null}
  259. </div>
  260. </div>
  261. )
  262. }
  263. StatusBar.defaultProps = {
  264. doneButtonHandler: undefined,
  265. hideAfterFinish: false,
  266. hideCancelButton: false,
  267. hidePauseResumeButton: false,
  268. hideRetryButton: false,
  269. hideUploadButton: undefined,
  270. showProgressDetails: undefined,
  271. }