UNPKG

@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
"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;