@wix/design-system
Version:
@wix/design-system
141 lines • 6.82 kB
JavaScript
import React, { memo, useCallback, useMemo, useState, useEffect, forwardRef, useRef, useImperativeHandle, } from 'react';
import { st, classes, vars } from './AudioPlayer.st.css.js';
import Tooltip from '../Tooltip';
import IconButton from '../IconButton';
import Loader from '../Loader';
import Heading from '../Heading';
import { PlayFilled, PauseFilled } from '@wix/wix-ui-icons-common';
import { dataHooks } from './constants';
import { useAudioManager } from './AudioManager/AudioManager';
import { positionToSeconds, secondsToISO, secondsToPosition } from './utils';
import { useIcons } from '../WixDesignSystemIconThemeProvider';
/** AudioPlayer */
const AudioPlayer = memo(forwardRef(({ dataHook, className, src, format, preload = 'metadata', webAudioAPI = false, onLoad, onLoadError, onPlay, onPause, onEnd, onSeek, autoplay, volume, }, ref) => {
const icons = useIcons('AudioPlayer', {
PlayFilled,
PauseFilled,
});
const [isSliderLocked, setIsSliderLocked] = useState(true);
const [hoverPosition, setHoverPosition] = useState(0);
const [handleSizeInPercentage, setHandleSizeInPercentage] = useState(0);
const [playing, setPlaying] = useState(false);
const [showDuration, setShowDuration] = useState(true);
const playPauseButtonRef = useRef(null);
useImperativeHandle(ref, () => ({
focus: () => playPauseButtonRef.current && playPauseButtonRef.current.focus(),
}));
const _onDestroy = useCallback(() => {
setPlaying(false);
setShowDuration(true);
}, [setPlaying, setShowDuration]);
const _onEnd = useCallback(() => {
setShowDuration(true);
setPlaying(false);
onEnd && onEnd();
}, [onEnd, setShowDuration, setPlaying]);
const _onLoad = useCallback(() => {
if (autoplay) {
setShowDuration(false);
setPlaying(true);
}
onLoad && onLoad();
}, [onLoad, autoplay]);
const { loadingState, duration, seek, setSeek } = useAudioManager({
src,
format,
preload,
webAudioAPI,
volume,
onLoad: _onLoad,
onLoadError,
onPlay,
onSeek,
onPause,
playing,
onDestroy: _onDestroy,
onEnd: _onEnd,
allowSeekLoop: isSliderLocked,
});
const isLoaded = loadingState === 'loaded';
const _hoverISO = useMemo(() => {
if (!isLoaded) {
return secondsToISO(0, false, duration);
}
return secondsToISO(positionToSeconds(hoverPosition, duration), true, duration);
}, [isLoaded, hoverPosition, duration]);
// takes the current seek (in seconds) and converts it to slider position.
const _seekPercentage = useMemo(() => {
if (!isLoaded) {
return 0;
}
return secondsToPosition(seek, duration);
}, [isLoaded, seek, duration]);
const _togglePlayPause = useCallback(() => {
setShowDuration(false);
if (playing) {
setPlaying(false);
}
else {
setPlaying(true);
}
}, [playing, setPlaying, setShowDuration]);
const _playPauseButtonContent = useMemo(() => {
if (loadingState === 'loading') {
return (React.createElement("span", { "data-hook": dataHooks.audioPlayerLoad },
React.createElement(Loader, { size: "tiny" })));
}
return playing ? (React.createElement(icons.PauseFilled, { "data-hook": dataHooks.audioPlayerPause })) : (React.createElement(icons.PlayFilled, { "data-hook": dataHooks.audioPlayerPlay }));
}, [loadingState, playing, icons]);
const _setSliderPositions = useCallback((x, width, clickX) => {
const positionInPixels = ((clickX - x) / width) * 100;
const position = Math.min(Math.max(positionInPixels, 0), 100);
setHandleSizeInPercentage((12 / width) * 100);
setHoverPosition(position);
}, [setHoverPosition]);
const _handleSliderMouseDown = useCallback(event => {
const { clientX, currentTarget } = event;
const { x, width } = currentTarget.getBoundingClientRect();
setIsSliderLocked(false);
_setSliderPositions(x, width, clientX);
}, [_setSliderPositions, setIsSliderLocked]);
const _handleSliderMouseMove = useCallback(event => {
const { clientX, currentTarget } = event;
const { x, width } = currentTarget.getBoundingClientRect();
_setSliderPositions(x, width, clientX);
}, [_setSliderPositions]);
const _handleSliderMouseUp = useCallback(() => {
setIsSliderLocked(true);
}, [setIsSliderLocked]);
useEffect(() => {
window.addEventListener('mouseup', _handleSliderMouseUp);
return () => window.removeEventListener('mouseup', _handleSliderMouseUp);
}, [_handleSliderMouseUp]);
// seek audio file to the slider location when dragged.
useEffect(() => {
if (!isSliderLocked) {
setSeek(positionToSeconds(hoverPosition, duration));
setShowDuration(false);
}
}, [duration, hoverPosition, isSliderLocked, setSeek]);
return (React.createElement("div", { className: st(classes.root, className), "data-hook": dataHook },
React.createElement(IconButton, { ref: playPauseButtonRef, size: "small", onClick: _togglePlayPause, dataHook: dataHooks.audioPlayerPlayPause, className: classes.playPauseButton }, _playPauseButtonContent),
React.createElement("div", { "data-hook": dataHooks.audioPlayerSlider, className: classes.slider, style: {
[vars['audio-player-position']]: `${_seekPercentage}%`,
}, onMouseDown: _handleSliderMouseDown, onMouseMove: _handleSliderMouseMove },
React.createElement("div", { className: classes.track }),
React.createElement("div", { className: classes.tooltip, style: { left: `${hoverPosition}%` } },
React.createElement(Tooltip, { content: `${_hoverISO}` },
React.createElement("div", { className: classes.tooltipTarget }))),
React.createElement("div", { "data-hook": dataHooks.audioPlayerSliderHandle, className: st(classes.handle, {
grow: isLoaded &&
(!isSliderLocked ||
Math.abs(_seekPercentage - hoverPosition) <
handleSizeInPercentage),
}), style: { left: `${_seekPercentage}%` } })),
React.createElement(Heading, { size: "tiny", className: classes.timer, dataHook: dataHooks.audioTimeIndicator }, showDuration
? secondsToISO(duration, isLoaded, duration)
: secondsToISO(seek, isLoaded, duration))));
}));
AudioPlayer.displayName = 'AudioPlayer';
export default AudioPlayer;
//# sourceMappingURL=AudioPlayer.js.map