@douyinfe/semi-ui
Version:
A modern, comprehensive, flexible design system and UI library. Connect DesignOps & DevOps. Quickly build beautiful React apps. Maintained by Douyin-fe team.
501 lines (499 loc) • 20.1 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = exports.VideoPlayer = void 0;
var _isUndefined2 = _interopRequireDefault(require("lodash/isUndefined"));
var _react = _interopRequireDefault(require("react"));
var _classnames = _interopRequireDefault(require("classnames"));
var _baseComponent = _interopRequireDefault(require("../_base/baseComponent"));
var _constants = require("@douyinfe/semi-foundation/lib/cjs/videoPlayer/constants");
var _foundation = _interopRequireDefault(require("@douyinfe/semi-foundation/lib/cjs/videoPlayer/foundation"));
require("@douyinfe/semi-foundation/lib/cjs/videoPlayer/videoPlayer.css");
var _semiIcons = require("@douyinfe/semi-icons");
var _button = _interopRequireDefault(require("../button"));
var _popover = _interopRequireDefault(require("../popover"));
var _audioSlider = _interopRequireDefault(require("../audioPlayer/audioSlider"));
var _dropdown = _interopRequireDefault(require("../dropdown"));
var _videoProgress = _interopRequireDefault(require("./videoProgress"));
var _utils = require("./utils");
var _isNullOrUndefined = _interopRequireDefault(require("@douyinfe/semi-foundation/lib/cjs/utils/isNullOrUndefined"));
var _localeConsumer = _interopRequireDefault(require("../locale/localeConsumer"));
var _ErrorSvg = _interopRequireDefault(require("./ErrorSvg"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
/* eslint-disable jsx-a11y/click-events-have-key-events */
const prefixCls = _constants.cssClasses.PREFIX;
class VideoPlayer extends _baseComponent.default {
constructor(props) {
super(props);
this.handleMouseEnterWrapper = () => {
this.foundation.handleMouseEnterWrapper();
};
this.handleMouseLeaveWrapper = () => {
this.foundation.handleMouseLeaveWrapper();
};
this.handleTimeChange = value => {
this.foundation.handleTimeChange(value);
};
this.handleTimeUpdate = () => {
this.foundation.handleTimeUpdate();
};
this.handleError = () => {
this.foundation.handleError();
};
this.handlePlay = () => {
this.foundation.handlePlay();
};
this.handlePause = () => {
this.foundation.handlePause();
};
this.handleVideoPlay = () => {
this.foundation.handleVideoPlay();
};
this.handleVideoPause = () => {
this.foundation.handleVideoPause();
};
this.handleCanPlay = () => {
this.foundation.handleCanPlay();
};
this.handleWaiting = locale => {
this.foundation.handleWaiting(locale);
};
this.handleStalled = locale => {
this.foundation.handleStalled(locale);
};
this.handleProgress = () => {
this.foundation.handleProgress();
};
this.handleEnded = () => {
this.foundation.handleEnded();
};
this.handleDurationChange = () => {
this.foundation.handleDurationChange();
};
this.handleVolumeChange = value => {
this.foundation.handleVolumeChange(value);
};
this.handleVolumeSilent = () => {
this.foundation.handleVolumeSilent();
};
this.handleRateChange = (option, locale) => {
this.foundation.handleRateChange(option, locale);
};
this.handleQualityChange = (option, locale) => {
this.foundation.handleQualityChange(option, locale);
};
this.handleRouteChange = (option, locale) => {
this.foundation.handleRouteChange(option, locale);
};
this.handleMirror = locale => {
this.foundation.handleMirror(locale);
};
this.handleFullscreen = () => {
this.foundation.handleFullscreen();
};
this.handlePictureInPicture = () => {
this.foundation.handlePictureInPicture();
};
this.getVolumeIcon = () => {
const {
volume,
muted
} = this.state;
if (muted) {
return /*#__PURE__*/_react.default.createElement(_semiIcons.IconMute, null);
}
if (volume < 50) {
return /*#__PURE__*/_react.default.createElement(_semiIcons.IconVolume1, null);
}
return /*#__PURE__*/_react.default.createElement(_semiIcons.IconVolume2, null);
};
this.isResourceNotFound = () => {
const {
src
} = this.props;
return (0, _isNullOrUndefined.default)(src);
};
this.renderTime = () => {
const {
currentTime,
totalTime
} = this.state;
if (this.foundation.shouldShowControlItem(_constants.strings.TIME)) {
return /*#__PURE__*/_react.default.createElement("div", {
className: (0, _classnames.default)(`${_constants.cssClasses.PREFIX_CONTROLS}-time`)
}, (0, _utils.formatTime)(currentTime), " / ", (0, _utils.formatTime)(totalTime));
}
return null;
};
this.renderResourceNotFound = locale => {
return /*#__PURE__*/_react.default.createElement("div", {
className: (0, _classnames.default)(`${prefixCls}-resource-not-found`)
}, locale.noResource);
};
this.renderPauseIcon = () => {
const {
isPlaying,
isError
} = this.state;
if (!isPlaying && !isError) {
return (
/*#__PURE__*/
// eslint-disable-next-line jsx-a11y/no-static-element-interactions
_react.default.createElement("div", {
className: (0, _classnames.default)(`${prefixCls}-pause`)
}, /*#__PURE__*/_react.default.createElement(_semiIcons.IconPlayCircle, null))
);
}
return null;
};
this.renderError = locale => {
const {
isError
} = this.state;
const {
theme
} = this.props;
if (isError) {
return /*#__PURE__*/_react.default.createElement("div", {
className: (0, _classnames.default)(`${prefixCls}-error`, {
[`${prefixCls}-error-${theme}`]: theme
})
}, /*#__PURE__*/_react.default.createElement("div", {
className: (0, _classnames.default)(`${prefixCls}-error-svg`)
}, /*#__PURE__*/_react.default.createElement(_ErrorSvg.default, null)), locale.videoError);
}
return null;
};
this.renderPoster = () => {
const {
poster
} = this.props;
const {
isPlaying,
currentTime,
totalTime
} = this.state;
const isHide = currentTime > 0 && currentTime < totalTime;
if (!isPlaying && poster) {
return /*#__PURE__*/_react.default.createElement("img", {
className: (0, _classnames.default)(`${prefixCls}-poster`, {
[`${prefixCls}-poster-hide`]: isHide
}),
src: poster,
alt: "poster"
});
}
return null;
};
this.renderNotification = () => {
const {
showNotification,
notificationContent
} = this.state;
if (!showNotification || !notificationContent) {
return null;
}
return /*#__PURE__*/_react.default.createElement("div", {
className: (0, _classnames.default)(`${prefixCls}-notification`)
}, this.state.notificationContent);
};
this.renderVolume = () => {
const {
volume,
muted
} = this.state;
if (this.foundation.shouldShowControlItem(_constants.strings.VOLUME)) {
return /*#__PURE__*/_react.default.createElement(_popover.default, {
autoAdjustOverflow: true,
position: 'top',
className: (0, _classnames.default)(`${_constants.cssClasses.PREFIX_CONTROLS}-popover`),
content: /*#__PURE__*/_react.default.createElement("div", {
className: (0, _classnames.default)(`${_constants.cssClasses.PREFIX_CONTROLS}-volume`)
}, /*#__PURE__*/_react.default.createElement("div", {
className: (0, _classnames.default)(`${_constants.cssClasses.PREFIX_CONTROLS}-volume-title`)
}, muted ? 0 : volume, "%"), /*#__PURE__*/_react.default.createElement(_audioSlider.default, {
value: muted ? 0 : volume,
max: 100,
vertical: true,
height: 120,
showTooltip: false,
onChange: this.handleVolumeChange
}))
}, /*#__PURE__*/_react.default.createElement(_button.default, {
className: (0, _classnames.default)(`${_constants.cssClasses.PREFIX_CONTROLS}-menu-item`, `${_constants.cssClasses.PREFIX_CONTROLS}-menu-button`),
theme: 'borderless',
icon: this.getVolumeIcon(),
onClick: this.handleVolumeSilent
}));
}
return null;
};
this.renderIconButton = (icon, onClick, name) => {
if (!this.foundation.shouldShowControlItem(name)) {
return null;
}
return /*#__PURE__*/_react.default.createElement(_button.default, {
theme: 'borderless',
className: (0, _classnames.default)(`${_constants.cssClasses.PREFIX_CONTROLS}-menu-item`, `${_constants.cssClasses.PREFIX_CONTROLS}-menu-button`),
icon: icon,
onClick: onClick
});
};
this.renderDropdownButton = (currentValue, list, handleChange, name, locale) => {
var _a;
if (this.foundation.shouldShowControlItem(name)) {
return /*#__PURE__*/_react.default.createElement(_dropdown.default, {
position: 'top',
className: (0, _classnames.default)(`${_constants.cssClasses.PREFIX_CONTROLS}-popup-menu`),
render: /*#__PURE__*/_react.default.createElement(_dropdown.default.Menu, null, list.map(option => (/*#__PURE__*/_react.default.createElement(_dropdown.default.Item, {
className: (0, _classnames.default)(`${_constants.cssClasses.PREFIX_CONTROLS}-popup-menu-item`),
key: option.value,
onClick: () => handleChange(option, locale),
active: option.value === currentValue
}, option.label)))),
onChange: option => handleChange(option, locale)
}, /*#__PURE__*/_react.default.createElement("div", {
className: (0, _classnames.default)(`${_constants.cssClasses.PREFIX_CONTROLS}-menu-item`, `${_constants.cssClasses.PREFIX_CONTROLS}-popup`)
}, (_a = list.find(option => option.value === currentValue)) === null || _a === void 0 ? void 0 : _a.label));
}
return null;
};
this.state = {
bufferedValue: 0,
currentQuality: props.defaultQuality || '',
currentRoute: props.defaultRoute || '',
currentTime: 0,
isError: false,
isMirror: false,
isPlaying: false,
muted: props.muted,
notificationContent: '',
playbackRate: props.defaultPlaybackRate || 1,
playbackRateList: props.playbackRateList,
showNotification: false,
showControls: true,
src: props.src || '',
totalTime: 0,
volume: props.muted ? 0 : props.volume
};
this.videoRef = /*#__PURE__*/_react.default.createRef();
this.videoWrapperRef = /*#__PURE__*/_react.default.createRef();
this.foundation = new _foundation.default(this.adapter);
}
get adapter() {
return Object.assign(Object.assign({}, super.adapter), {
getVideo: () => this.videoRef.current,
getVideoWrapper: () => this.videoWrapperRef.current,
notifyPause: () => {
var _a, _b;
return (_b = (_a = this.props).onPause) === null || _b === void 0 ? void 0 : _b.call(_a);
},
notifyPlay: () => {
var _a, _b;
return (_b = (_a = this.props).onPlay) === null || _b === void 0 ? void 0 : _b.call(_a);
},
notifyQualityChange: quality => {
var _a, _b;
return (_b = (_a = this.props).onQualityChange) === null || _b === void 0 ? void 0 : _b.call(_a, quality);
},
notifyRateChange: rate => {
var _a, _b;
return (_b = (_a = this.props).onRateChange) === null || _b === void 0 ? void 0 : _b.call(_a, rate);
},
notifyRouteChange: route => {
var _a, _b;
return (_b = (_a = this.props).onRouteChange) === null || _b === void 0 ? void 0 : _b.call(_a, route);
},
notifyVolumeChange: volume => {
var _a, _b;
return (_b = (_a = this.props).onVolumeChange) === null || _b === void 0 ? void 0 : _b.call(_a, volume);
},
setBufferedValue: bufferedValue => this.setState({
bufferedValue
}),
setCurrentTime: currentTime => this.setState({
currentTime
}),
setIsError: isError => this.setState({
isError
}),
setIsMirror: isMirror => this.setState({
isMirror
}),
setIsPlaying: isPlaying => this.setState({
isPlaying
}),
setMuted: muted => this.setState({
muted
}),
setNotificationContent: content => this.setState({
notificationContent: content
}),
setPlaybackRate: rate => this.setState({
playbackRate: rate
}),
setQuality: quality => this.setState({
currentQuality: quality
}),
setRoute: route => this.setState({
currentRoute: route
}),
setShowControls: showControls => this.setState({
showControls
}),
setShowNotification: showNotification => this.setState({
showNotification: showNotification
}),
setTotalTime: totalTime => this.setState({
totalTime
}),
setVolume: volume => this.setState({
volume
})
});
}
getVideoRef() {
const {
forwardRef
} = this.props;
if (!(0, _isUndefined2.default)(forwardRef)) {
if (typeof forwardRef === 'function') {
return node => {
forwardRef(node);
this.videoRef = {
current: node
};
};
} else if (Object.prototype.toString.call(forwardRef) === '[object Object]') {
this.videoRef = forwardRef;
return forwardRef;
}
}
return this.videoRef;
}
static getDerivedStateFromProps(props, state) {
const states = {};
if (!(0, _isNullOrUndefined.default)(props.src) && props.src !== state.src) {
states.src = props.src;
}
return states;
}
componentDidMount() {
this.foundation.init();
}
componentWillUnmount() {
this.foundation.destroy();
}
render() {
const {
markers,
qualityList,
routeList,
width,
height,
autoPlay,
style,
className,
loop,
captionsSrc,
crossOrigin,
theme
} = this.props;
const {
isPlaying,
playbackRate,
playbackRateList,
isMirror,
currentTime,
totalTime,
currentQuality,
currentRoute,
src,
bufferedValue,
showControls
} = this.state;
return /*#__PURE__*/_react.default.createElement(_localeConsumer.default, {
componentName: "VideoPlayer"
}, locale => {
return /*#__PURE__*/_react.default.createElement("div", {
className: (0, _classnames.default)(`${prefixCls}`, className, {
[`${prefixCls}-mirror`]: isMirror
}),
style: Object.assign({
width,
height
}, style),
ref: this.videoWrapperRef,
onMouseEnter: this.handleMouseEnterWrapper,
onMouseLeave: this.handleMouseLeaveWrapper
}, /*#__PURE__*/_react.default.createElement("div", {
className: (0, _classnames.default)(`${prefixCls}-wrapper`, {
[`${_constants.cssClasses.PREFIX}-wrapper-${theme}`]: theme
})
}, /*#__PURE__*/_react.default.createElement("video", {
ref: this.getVideoRef(),
autoPlay: autoPlay,
loop: loop,
controls: false,
crossOrigin: crossOrigin,
src: src,
onTimeUpdate: this.handleTimeUpdate,
onDurationChange: this.handleDurationChange,
onPlay: this.handleVideoPlay,
onPause: this.handleVideoPause,
onClick: () => {
this.foundation.handlePlayOrPause();
},
// An error occurred while getting the media data, or the resource is in an unsupported format.
onError: this.handleError,
onCanPlay: this.handleCanPlay,
// Playback stopped due to temporary lack of data.
onWaiting: () => this.handleWaiting(locale),
// The user agent attempted to fetch media data but was unexpectedly unable to fetch the data.
onStalled: () => this.handleStalled(locale),
onProgress: this.handleProgress,
onEnded: this.handleEnded
}, /*#__PURE__*/_react.default.createElement("track", {
kind: "captions",
src: captionsSrc
})), this.isResourceNotFound() && this.renderResourceNotFound(locale)), this.renderPoster(), this.renderPauseIcon(), this.renderError(locale), this.renderNotification(), /*#__PURE__*/_react.default.createElement("div", {
className: (0, _classnames.default)(`${_constants.cssClasses.PREFIX_CONTROLS}`, {
[`${_constants.cssClasses.PREFIX_CONTROLS}-hide`]: !showControls
})
}, /*#__PURE__*/_react.default.createElement(_videoProgress.default, {
key: totalTime,
value: currentTime,
max: totalTime,
onChange: this.handleTimeChange,
markers: markers,
bufferedValue: bufferedValue
}), /*#__PURE__*/_react.default.createElement("div", {
className: (0, _classnames.default)(`${_constants.cssClasses.PREFIX_CONTROLS}-menu`)
}, /*#__PURE__*/_react.default.createElement("div", {
className: (0, _classnames.default)(`${_constants.cssClasses.PREFIX_CONTROLS}-menu-left`)
}, this.renderIconButton(isPlaying ? /*#__PURE__*/_react.default.createElement(_semiIcons.IconPause, null) : /*#__PURE__*/_react.default.createElement(_semiIcons.IconPlay, null), isPlaying ? this.handlePause : this.handlePlay, _constants.strings.PLAY), this.renderIconButton(/*#__PURE__*/_react.default.createElement(_semiIcons.IconRestart, {
rotate: 180
}), isPlaying ? this.handlePause : this.handlePlay, _constants.strings.NEXT), this.renderTime(), this.renderVolume(), this.renderDropdownButton(playbackRate, playbackRateList, this.handleRateChange, _constants.strings.PLAYBACK_RATE, locale)), /*#__PURE__*/_react.default.createElement("div", {
className: (0, _classnames.default)(`${_constants.cssClasses.PREFIX_CONTROLS}-menu-right`)
}, qualityList && qualityList.length > 0 && this.renderDropdownButton(currentQuality, qualityList, this.handleQualityChange, _constants.strings.QUALITY, locale), routeList && routeList.length > 0 && this.renderDropdownButton(currentRoute, routeList, this.handleRouteChange, _constants.strings.ROUTE, locale), this.renderIconButton(/*#__PURE__*/_react.default.createElement(_semiIcons.IconFlipHorizontal, null), () => this.handleMirror(locale), _constants.strings.MIRROR), this.renderIconButton(this.foundation.checkFullScreen() ? /*#__PURE__*/_react.default.createElement(_semiIcons.IconMinimize, null) : /*#__PURE__*/_react.default.createElement(_semiIcons.IconMaximize, null), this.handleFullscreen, _constants.strings.FULLSCREEN), this.renderIconButton(/*#__PURE__*/_react.default.createElement(_semiIcons.IconMiniPlayer, null), this.handlePictureInPicture, _constants.strings.PICTURE_IN_PICTURE)))));
});
}
}
exports.VideoPlayer = VideoPlayer;
VideoPlayer.defaultProps = {
autoPlay: false,
clickToPlay: true,
defaultPlaybackRate: _constants.numbers.DEFAULT_PLAYBACK_RATE,
controlsList: [_constants.strings.PLAY, _constants.strings.NEXT, _constants.strings.TIME, _constants.strings.VOLUME, _constants.strings.PLAYBACK_RATE, _constants.strings.QUALITY, _constants.strings.ROUTE, _constants.strings.MIRROR, _constants.strings.FULLSCREEN, _constants.strings.PICTURE_IN_PICTURE],
loop: false,
muted: false,
playbackRateList: _constants.DEFAULT_PLAYBACK_RATE,
seekTime: _constants.numbers.DEFAULT_SEEK_TIME,
theme: _constants.strings.DARK,
volume: _constants.numbers.DEFAULT_VOLUME
};
const ForwardVideoPlayer = /*#__PURE__*/_react.default.forwardRef((props, ref) => (/*#__PURE__*/_react.default.createElement(VideoPlayer, Object.assign({}, props, {
forwardRef: ref
}))));
var _default = exports.default = ForwardVideoPlayer;