Design system.
Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.
 
 
 

215 rader
8.6 KiB

  1. import * as React from 'react';
  2. import {augmentAudioFile} from 'packages/web-kitchensink-reactnext/src/utils/blob';
  3. import theme from 'packages/web-kitchensink-reactnext/src/styles/theme';
  4. import {useMediaControls} from '../../hooks/interactive';
  5. import {useFileMetadata, useFileUrl} from 'src/index';
  6. import clsx from 'clsx';
  7. import {SpectrogramCanvas, WaveformCanvas} from 'packages/web-kitchensink-reactnext/src/packages/react-wavesurfer';
  8. import {useClientSide} from 'packages/react-utils';
  9. export type AudioMiniFilePreviewDerivedElement = HTMLAudioElement;
  10. export interface AudioMiniFilePreviewProps<F extends Partial<File> = Partial<File>> extends Omit<React.HTMLProps<AudioMiniFilePreviewDerivedElement>, 'controls'> {
  11. file?: F;
  12. disabled?: boolean;
  13. enhanced?: boolean;
  14. }
  15. export const AudioMiniFilePreview = React.forwardRef<AudioMiniFilePreviewDerivedElement, AudioMiniFilePreviewProps>(({
  16. file,
  17. style,
  18. className,
  19. enhanced: enhancedProp = false,
  20. disabled = false,
  21. ...etcProps
  22. }, forwardedRef) => {
  23. const { fileWithUrl } = useFileUrl({
  24. file,
  25. });
  26. const { fileWithMetadata, error } = useFileMetadata({
  27. file: fileWithUrl,
  28. augmentFunction: augmentAudioFile,
  29. });
  30. const {
  31. mediaControllerRef,
  32. refreshControls,
  33. reset,
  34. updateSeekFromPlayback,
  35. isPlaying,
  36. isSeeking,
  37. currentTimeDisplay = 0,
  38. seekTimeDisplay = 0,
  39. durationDisplay = 0,
  40. isSeekTimeCountingDown,
  41. adjustVolume,
  42. volumeRef,
  43. handleAction,
  44. filenameRef,
  45. seekRef,
  46. startSeek,
  47. endSeek,
  48. setSeek,
  49. visualizationId,
  50. formId,
  51. } = useMediaControls<HTMLAudioElement>({
  52. controllerRef: forwardedRef,
  53. visualizationMode: 'waveform',
  54. });
  55. const { clientSide } = useClientSide({ clientSide: enhancedProp });
  56. if (!fileWithMetadata) {
  57. return null;
  58. }
  59. const finalSeekTimeDisplay = isSeekTimeCountingDown ? (durationDisplay - seekTimeDisplay) : seekTimeDisplay;
  60. const finalCurrentTimeDisplay = isSeekTimeCountingDown ? (durationDisplay - currentTimeDisplay) : currentTimeDisplay;
  61. return (
  62. <div
  63. className={clsx(
  64. 'flex flex-col sm:grid sm:grid-cols-3 gap-8 w-full',
  65. className,
  66. )}
  67. style={style}
  68. >
  69. <div className="sm:h-full relative col-span-2">
  70. {
  71. typeof fileWithMetadata.url === 'string'
  72. && (
  73. <div
  74. className="w-full h-full bg-black flex flex-col items-stretch"
  75. data-testid="preview"
  76. >
  77. <div
  78. className="w-full flex-auto relative aspect-video sm:aspect-auto"
  79. key={`${fileWithMetadata?.url ?? ''}:${fileWithMetadata?.type ?? ''}`}
  80. >
  81. <audio
  82. {...etcProps}
  83. controls={!clientSide}
  84. ref={mediaControllerRef}
  85. onLoadedMetadata={refreshControls}
  86. onDurationChange={refreshControls}
  87. onEnded={reset}
  88. onTimeUpdate={updateSeekFromPlayback}
  89. >
  90. <source
  91. key={`${fileWithMetadata?.url ?? ''}:${fileWithMetadata?.type ?? ''}`}
  92. src={fileWithMetadata.url}
  93. type={fileWithMetadata.type}
  94. />
  95. Audio playback not supported.
  96. </audio>
  97. {clientSide && (
  98. <>
  99. <div className="flex justify-end w-full h-full gap-4 absolute top-0 right-0 z-[5] px-4">
  100. <div className="contents">
  101. <input
  102. type="radio"
  103. name="visualizationMode"
  104. value="waveform"
  105. className="sr-only peer/waveform"
  106. defaultChecked
  107. id={`${visualizationId}-waveform`}
  108. />
  109. <label
  110. htmlFor={`${visualizationId}-waveform`}
  111. className={clsx(
  112. 'relative z-[5]',
  113. 'h-12 flex items-center justify-center leading-none gap-4 select-none',
  114. 'text-primary cursor-pointer',
  115. 'peer-focus/waveform:text-secondary',
  116. 'peer-active/waveform:text-tertiary',
  117. 'peer-checked/waveform:text-tertiary',
  118. 'peer-disabled/waveform:text-primary peer-disabled/waveform:cursor-not-allowed peer-disabled/waveform:opacity-50',
  119. )}
  120. >
  121. <span
  122. className={clsx(
  123. 'flex items-center uppercase font-bold h-full w-full whitespace-nowrap overflow-hidden text-ellipsis',
  124. )}
  125. >
  126. Waveform
  127. </span>
  128. </label>
  129. <WaveformCanvas
  130. className={clsx(
  131. 'absolute w-full sm:h-full top-0 left-0 block object-center object-contain flex-auto aspect-video sm:aspect-auto bg-primary/10 cursor-text opacity-0',
  132. 'peer-checked/waveform:opacity-100',
  133. )}
  134. audioRef={mediaControllerRef}
  135. data-testid="preview"
  136. barWidth={1}
  137. barGap={1}
  138. progressColor={`rgb(${theme['color-primary']})`}
  139. waveColor={`rgb(${theme['color-primary'].split(' ').map((c) => Math.floor(Number(c) / 2)).join(' ')})`}
  140. interact
  141. // waveColor={`rgb(${theme.primary})`}
  142. // barHeight={4}
  143. // minPxPerSec={20000}
  144. // hideScrollbar
  145. // autoCenter
  146. // autoScroll
  147. />
  148. </div>
  149. <div
  150. className="contents"
  151. >
  152. <input
  153. type="radio"
  154. name="visualizationMode"
  155. value="spectrum"
  156. className="sr-only peer/waveform"
  157. id={`${visualizationId}-spectrum`}
  158. />
  159. <label
  160. htmlFor={`${visualizationId}-spectrum`}
  161. className={clsx(
  162. 'relative z-[5]',
  163. 'h-12 flex items-center justify-center leading-none gap-4 select-none',
  164. 'text-primary cursor-pointer',
  165. 'peer-focus/waveform:text-secondary',
  166. 'peer-active/waveform:text-tertiary',
  167. 'peer-checked/waveform:text-tertiary',
  168. 'peer-disabled/waveform:text-primary peer-disabled/waveform:cursor-not-allowed peer-disabled/waveform:opacity-50',
  169. )}
  170. >
  171. <span
  172. className={clsx(
  173. 'flex items-center uppercase font-bold h-full w-full whitespace-nowrap overflow-hidden text-ellipsis',
  174. )}
  175. >
  176. Spectrum
  177. </span>
  178. </label>
  179. <SpectrogramCanvas
  180. className={clsx(
  181. 'absolute w-full sm:h-full top-0 left-0 block object-center object-contain flex-auto aspect-video sm:aspect-auto bg-primary/10 pointer-events-none opacity-0',
  182. 'peer-checked/waveform:opacity-100',
  183. )}
  184. audioRef={mediaControllerRef}
  185. data-testid="preview"
  186. barWidth={1}
  187. barGap={1}
  188. waveColor={`rgb(${theme['color-primary']})`}
  189. cursorWidth={2}
  190. minPxPerSec={20000}
  191. hideScrollbar
  192. autoCenter
  193. autoScroll
  194. />
  195. </div>
  196. </div>
  197. </>
  198. )}
  199. </div>
  200. </div>
  201. )
  202. }
  203. </div>
  204. </div>
  205. );
  206. });
  207. AudioMiniFilePreview.displayName = 'AudioMiniFilePreview';