diff --git a/src/aimcore/web/ui/src/components/AudioPlayer/AudioPlayer.d.ts b/src/aimcore/web/ui/src/components/AudioPlayer/AudioPlayer.d.ts new file mode 100644 index 0000000000..aa1e22d0b4 --- /dev/null +++ b/src/aimcore/web/ui/src/components/AudioPlayer/AudioPlayer.d.ts @@ -0,0 +1,15 @@ +import React from 'react'; + +export interface AudioPlayerProps extends React.HTMLProps { + audioRef: React.MutableRefObject; + src: string; + isPlaying: boolean; + processing: boolean; + onDownload?: () => void; + caption?: string; + readyToPlay: boolean; + onEnded: () => void; + onCanPlay: () => void; + onPlay: () => void; + onPause: () => void; +} diff --git a/src/aimcore/web/ui/src/components/AudioPlayer/AudioPlayer.scss b/src/aimcore/web/ui/src/components/AudioPlayer/AudioPlayer.scss new file mode 100644 index 0000000000..1d3aab135d --- /dev/null +++ b/src/aimcore/web/ui/src/components/AudioPlayer/AudioPlayer.scss @@ -0,0 +1,58 @@ +@use 'src/styles/abstracts' as *; + +.AudioPlayer { + max-width: toRem(268px); + &__controllers { + position: relative; + display: flex; + align-items: center; + padding: $space-xs; + background-color: $pico-5; + border-radius: $border-radius-main; + width: toRem(268px); + height: toRem(40px); + &__download { + min-width: 1.5rem; + } + &__player { + min-width: 1.5rem; + height: 1.5rem; + border-radius: $radius-main; + position: relative; + display: flex; + align-items: center; + justify-content: center; + transition: background-color 0.18s ease-out; + &:hover { + background-color: rgba(28, 40, 82, 0.04); + } + .Icon__container { + position: absolute; + z-index: 0; + font-size: $text-sm; + } + .MuiPaper-root { + height: 1.5rem; + width: 1.5rem; + display: flex; + align-items: center; + justify-self: center; + background-color: transparent; + box-shadow: unset; + .MuiGrid-root { + padding: 0; + display: flex; + svg { + font-size: $text-xxl; + color: transparent; + z-index: 1; + position: relative; + &:hover { + color: transparent; + } + } + } + } + } + } +} diff --git a/src/aimcore/web/ui/src/components/AudioPlayer/AudioPlayer.tsx b/src/aimcore/web/ui/src/components/AudioPlayer/AudioPlayer.tsx new file mode 100644 index 0000000000..8b2fa44676 --- /dev/null +++ b/src/aimcore/web/ui/src/components/AudioPlayer/AudioPlayer.tsx @@ -0,0 +1,101 @@ +import React from 'react'; + +import ErrorBoundary from 'components/ErrorBoundary'; +import { Tooltip, Text } from 'components/kit_v2'; + +import { Button, Icon, Spinner } from '../kit'; + +import { AudioPlayerProps, AudioPlayerProgress, AudioPlayerVolume } from '.'; + +import './AudioPlayer.scss'; + +function AudioPlayer(props: AudioPlayerProps) { + const { + preload = 'metadata', + muted = true, + autoPlay = true, + audioRef, + src, + onEnded, + onCanPlay, + onPlay, + onPause, + isPlaying = false, + processing = false, + caption = '', + readyToPlay = false, + onDownload, + } = props; + + return ( + +
+
+
+
+ + +
+ +
+
+ {caption ? ( + + + {caption} + + + ) : null} +
+
+ ); +} + +AudioPlayer.displayName = 'AudioPlayer'; +export default AudioPlayer; diff --git a/src/aimcore/web/ui/src/components/AudioPlayer/AudioPlayerProgress/AudioPlayerProgress.d.ts b/src/aimcore/web/ui/src/components/AudioPlayer/AudioPlayerProgress/AudioPlayerProgress.d.ts new file mode 100644 index 0000000000..8d6cb40266 --- /dev/null +++ b/src/aimcore/web/ui/src/components/AudioPlayer/AudioPlayerProgress/AudioPlayerProgress.d.ts @@ -0,0 +1,6 @@ +export interface AudioPlayerProgressProps { + audio: HTMLAudioElement | null; + isPlaying: boolean; + src: string; + disabled?: boolean; +} diff --git a/src/aimcore/web/ui/src/components/AudioPlayer/AudioPlayerProgress/AudioPlayerProgress.scss b/src/aimcore/web/ui/src/components/AudioPlayer/AudioPlayerProgress/AudioPlayerProgress.scss new file mode 100644 index 0000000000..0397377272 --- /dev/null +++ b/src/aimcore/web/ui/src/components/AudioPlayer/AudioPlayerProgress/AudioPlayerProgress.scss @@ -0,0 +1,75 @@ +@use 'src/styles/abstracts' as *; + +.AudioPlayerProgress { + display: flex; + align-items: center; + width: 100%; + &__timer { + display: flex; + span { + width: 2rem; + text-align: right; + @include monospaceFontFamily(); + &:last-child { + text-align: left; + } + } + &-long { + min-width: toRem(74px); + span { + min-width: 2.5rem; + text-align: right; + display: inline-block; + &:last-child { + text-align: left; + } + } + } + } + &__progressSlider { + margin: 0 $space-xxxs 0 $space-xxs; + height: toRem(40px); + .MuiSlider-root { + height: 100% !important; + } + &.Slider { + &:hover { + .MuiSlider-thumb { + opacity: 1; + visibility: visible; + } + .Mui-disabled { + .MuiSlider-thumb { + opacity: 0; + visibility: hidden; + } + } + } + .MuiSlider { + &-root { + height: 0.375rem; + display: flex; + align-items: center; + } + &-thumb { + width: 0.5rem; + height: 0.5rem; + box-shadow: unset; + margin-top: 0; + transition: opacity 0.18s ease-out; + opacity: 0; + visibility: hidden; + &:hover { + box-shadow: unset; + } + &::after { + width: toRem(18px); + height: toRem(22px); + top: toRem(-6px); + left: toRem(-5px); + } + } + } + } + } +} diff --git a/src/aimcore/web/ui/src/modules/BaseExplorer/components/AudioBox/AudioBoxProgress.tsx b/src/aimcore/web/ui/src/components/AudioPlayer/AudioPlayerProgress/AudioPlayerProgress.tsx similarity index 52% rename from src/aimcore/web/ui/src/modules/BaseExplorer/components/AudioBox/AudioBoxProgress.tsx rename to src/aimcore/web/ui/src/components/AudioPlayer/AudioPlayerProgress/AudioPlayerProgress.tsx index d4fc4d1a5a..a9c2097030 100644 --- a/src/aimcore/web/ui/src/modules/BaseExplorer/components/AudioBox/AudioBoxProgress.tsx +++ b/src/aimcore/web/ui/src/components/AudioPlayer/AudioPlayerProgress/AudioPlayerProgress.tsx @@ -2,16 +2,18 @@ import React from 'react'; import moment from 'moment'; import { Slider, Text } from 'components/kit'; -import ErrorBoundary from 'components/ErrorBoundary/ErrorBoundary'; +import ErrorBoundary from 'components/ErrorBoundary'; -import { IAudioBoxProgressProps } from '.'; +import { AudioPlayerProgressProps } from './AudioPlayerProgress.d'; -function AudioBoxProgress({ +import './AudioPlayerProgress.scss'; + +function AudioPlayerProgress({ audio, isPlaying, src, disabled, -}: IAudioBoxProgressProps) { +}: AudioPlayerProgressProps) { const [trackProgress, setTrackProgress] = React.useState(0); const intervalRef = React.useRef(); @@ -28,12 +30,12 @@ function AudioBoxProgress({ clearInterval(intervalRef.current); } // eslint-disable-next-line react-hooks/exhaustive-deps - }, [isPlaying, src]); + }, [isPlaying, src, audio]); function startTimer(): void { clearInterval(intervalRef.current); intervalRef.current = window.setInterval(() => { - setTrackProgress(audio.currentTime); + setTrackProgress(audio?.currentTime || 0); }, 100); } @@ -56,8 +58,8 @@ function AudioBoxProgress({ function formatDuration(): string { return moment - .utc(Math.round(audio.duration || 0) * 1000) - .format(defineTimeFormat(audio.duration || 0)); + .utc(Math.round(audio?.duration || 0) * 1000) + .format(defineTimeFormat(audio?.duration || 0)); } function defineTimeFormat(duration: number): string { @@ -67,34 +69,38 @@ function AudioBoxProgress({ function formatProgress(): string { return moment .utc(Math.round(trackProgress) * 1000) - .format(defineTimeFormat(audio.duration || 0)); + .format(defineTimeFormat(audio?.duration || 0)); } return ( - -
3600 ? 'AudioBox__controllers__timer-long' : '' - }`} - > - - {(audio && formatProgress()) || '00:00'} - - - /{(audio && formatDuration()) || '00:00'} - +
+ +
3600 + ? 'AudioPlayerProgress__timer-long' + : '' + }`} + > + + {(audio && formatProgress()) || '00:00'} + + + /{(audio && formatDuration()) || '00:00'} + +
); } -export default AudioBoxProgress; +export default AudioPlayerProgress; diff --git a/src/aimcore/web/ui/src/components/AudioPlayer/AudioPlayerVolume/AudioPlayerVolume.d.ts b/src/aimcore/web/ui/src/components/AudioPlayer/AudioPlayerVolume/AudioPlayerVolume.d.ts new file mode 100644 index 0000000000..885678eae0 --- /dev/null +++ b/src/aimcore/web/ui/src/components/AudioPlayer/AudioPlayerVolume/AudioPlayerVolume.d.ts @@ -0,0 +1,3 @@ +export interface AudioPlayerVolumeProps { + audio: HTMLAudioElement | null; +} diff --git a/src/aimcore/web/ui/src/components/AudioPlayer/AudioPlayerVolume/AudioPlayerVolume.scss b/src/aimcore/web/ui/src/components/AudioPlayer/AudioPlayerVolume/AudioPlayerVolume.scss new file mode 100644 index 0000000000..2abcce9296 --- /dev/null +++ b/src/aimcore/web/ui/src/components/AudioPlayer/AudioPlayerVolume/AudioPlayerVolume.scss @@ -0,0 +1,52 @@ +@use 'src/styles/abstracts' as *; + +.AudioPlayerVolume { + margin: 0 $space-xxs 0 $space-xs; + position: relative; + &__button { + justify-content: flex-start; + } + &:hover { + .AudioPlayerVolume__slider { + opacity: 1; + visibility: visible; + } + } + &__slider { + background-color: $pico-5; + opacity: 0; + visibility: hidden; + transition: all 0.18s ease-out; + bottom: -1rem; + left: 0; + position: absolute; + width: toRem(56px); + box-shadow: 0 0.125rem 0.25rem rgba(28, 40, 82, 0.15); + border-radius: $border-radius-xss; + height: 1rem; + display: flex; + justify-content: center; + z-index: 2; + .Slider { + width: 2rem; + .MuiSlider { + &-root { + display: flex; + align-items: center; + height: 0.375rem; + color: $pico-80; + } + &-rail, + &-track { + height: 0.125rem; + } + &-thumb { + height: toRem(6px); + width: toRem(6px); + margin-left: toRem(-4px); + margin-top: 0; + } + } + } + } +} diff --git a/src/aimcore/web/ui/src/modules/BaseExplorer/components/AudioBox/AudioBoxVolume.tsx b/src/aimcore/web/ui/src/components/AudioPlayer/AudioPlayerVolume/AudioPlayerVolume.tsx similarity index 74% rename from src/aimcore/web/ui/src/modules/BaseExplorer/components/AudioBox/AudioBoxVolume.tsx rename to src/aimcore/web/ui/src/components/AudioPlayer/AudioPlayerVolume/AudioPlayerVolume.tsx index b5d9cf56aa..92d781d028 100644 --- a/src/aimcore/web/ui/src/modules/BaseExplorer/components/AudioBox/AudioBoxVolume.tsx +++ b/src/aimcore/web/ui/src/components/AudioPlayer/AudioPlayerVolume/AudioPlayerVolume.tsx @@ -1,11 +1,13 @@ import React from 'react'; -import ErrorBoundary from 'components/ErrorBoundary/ErrorBoundary'; +import ErrorBoundary from 'components/ErrorBoundary'; import { Button, Icon, Slider } from 'components/kit'; -import { IAudioBoxVolumeProps } from './AudioBox.d'; +import { AudioPlayerVolumeProps } from './AudioPlayerVolume.d'; -function AudioBoxVolume({ audio }: IAudioBoxVolumeProps) { +import './AudioPlayerVolume.scss'; + +function AudioPlayerVolume({ audio }: AudioPlayerVolumeProps) { const [volume, setVolume] = React.useState(0.99); const [isMuted, setIsMuted] = React.useState(false); @@ -33,16 +35,16 @@ function AudioBoxVolume({ audio }: IAudioBoxVolumeProps) { return ( -
+
-
+
)} - - + - ) : ( - <> - - {processing ? ( - - ) : ( - - )} - - )} -
- - -
- -
-
- -
- - {caption || ''} - -
-
-
+ setIsPlaying(false)} + onCanPlay={() => setProcessing(false)} + onPlay={onPlay} + onPause={onPause} + onDownload={onDownload} + processing={processing} + isPlaying={isPlaying} + readyToPlay={readyToPlayRef.current} + caption={caption} + /> ); } diff --git a/src/aimcore/web/ui/src/modules/BaseExplorer/components/AudioBox/index.tsx b/src/aimcore/web/ui/src/modules/BaseExplorer/components/AudioBox/index.tsx index 11a85e22c7..e23f812e70 100644 --- a/src/aimcore/web/ui/src/modules/BaseExplorer/components/AudioBox/index.tsx +++ b/src/aimcore/web/ui/src/modules/BaseExplorer/components/AudioBox/index.tsx @@ -1,5 +1,2 @@ import AudioBox from './AudioBox'; - -export * from './AudioBox.d'; - export default AudioBox;