@enact/sandstone
Version:
Large-screen/TV support library for Enact, containing a variety of UI components.
347 lines (337 loc) • 17.9 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports["default"] = exports.Video = void 0;
var _handle = require("@enact/core/handle");
var _ForwardRef = _interopRequireDefault(require("@enact/ui/ForwardRef"));
var _Media = require("@enact/ui/Media");
var _propTypes = _interopRequireDefault(require("@enact/core/internal/prop-types"));
var _Slottable = _interopRequireDefault(require("@enact/ui/Slottable"));
var _compose = _interopRequireDefault(require("ramda/src/compose"));
var _react = require("react");
var _VideoPlayerModule = _interopRequireDefault(require("./VideoPlayer.module.css"));
var _propTypes2 = _interopRequireDefault(require("prop-types"));
var _jsxRuntime = require("react/jsx-runtime");
var _excluded = ["preloadSource", "source", "mediaComponent"];
var _class;
/**
* Adds support for preloading a video source for `VideoPlayer`.
*
* @class VideoBase
* @memberof sandstone/VideoPlayer
* @ui
* @private
*/
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; }
function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t["return"] && (u = t["return"](), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }
function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }
function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor); } }
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; }
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : String(i); }
function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); Object.defineProperty(subClass, "prototype", { writable: false }); if (superClass) _setPrototypeOf(subClass, superClass); }
function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }
function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }
function _possibleConstructorReturn(self, call) { if (call && (typeof call === "object" || typeof call === "function")) { return call; } else if (call !== void 0) { throw new TypeError("Derived constructors may only return object or undefined"); } return _assertThisInitialized(self); }
function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }
function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function _isNativeReflectConstruct() { return !!t; })(); }
function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf.bind() : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }
var VideoBase = (_class = /*#__PURE__*/function (_Component) {
_inherits(VideoBase, _Component);
var _super = _createSuper(VideoBase);
function VideoBase() {
var _this;
_classCallCheck(this, VideoBase);
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
_this = _super.call.apply(_super, [this].concat(args));
_this.keys = ['media-1', 'media-2'];
_this.prevSourceKey = null;
_this.prevPreloadKey = null;
_this.handlePreloadLoadStart = function (ev) {
// persist the event so we can cache it to re-emit when the preload becomes active
ev.persist();
_this.preloadLoadStart = ev;
// prevent the from bubbling to upstream handlers
ev.stopPropagation();
};
_this.setVideoRef = function (node) {
_this.video = node;
_this.setMedia();
};
_this.setPreloadRef = function (node) {
if (node) {
node.load();
}
_this.preloadVideo = node;
};
return _this;
}
_createClass(VideoBase, [{
key: "componentDidUpdate",
value: function componentDidUpdate(prevProps) {
var _this$props = this.props,
source = _this$props.source,
preloadSource = _this$props.preloadSource;
var prevSource = prevProps.source,
prevPreloadSource = prevProps.preloadSource;
var key = (0, _Media.getKeyFromSource)(source);
var prevKey = (0, _Media.getKeyFromSource)(prevSource);
var preloadKey = (0, _Media.getKeyFromSource)(preloadSource);
var prevPreloadKey = (0, _Media.getKeyFromSource)(prevPreloadSource);
if (this.props.setMedia !== prevProps.setMedia) {
this.clearMedia(prevProps);
this.setMedia();
}
if (source) {
if (key === prevPreloadKey && preloadKey !== prevPreloadKey) {
// if there's source and it was the preload source
// if the preloaded video didn't error, notify VideoPlayer it is ready to reset
if (this.preloadLoadStart) {
(0, _handle.forward)('onLoadStart', this.preloadLoadStart, this.props);
}
// emit onUpdate to give VideoPlayer an opportunity to updates its internal state
// since it won't receive the onLoadStart or onError event
(0, _handle.forward)('onUpdate', {
type: 'onUpdate'
}, this.props);
this.autoPlay();
} else if (key !== prevKey) {
// if there's source and it has changed.
this.autoPlay();
}
}
if (preloadSource && preloadKey !== prevPreloadKey) {
this.preloadLoadStart = null;
// In the case that the previous source equalled the previous preload (causing the
// preload video node to not be created) and then the preload source was changed, we
// need to guard against accessing the preloadVideo node.
if (this.preloadVideo) {
this.preloadVideo.load();
}
}
}
}, {
key: "componentWillUnmount",
value: function componentWillUnmount() {
this.clearMedia();
}
}, {
key: "clearMedia",
value: function clearMedia() {
var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.props,
setMedia = _ref.setMedia;
if (setMedia) {
setMedia(null);
}
}
}, {
key: "setMedia",
value: function setMedia() {
var _ref2 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.props,
_setMedia = _ref2.setMedia;
if (_setMedia) {
_setMedia(this.video);
}
}
}, {
key: "autoPlay",
value: function autoPlay() {
if (!this.props.autoPlay) return;
var playPromise = this.video.play();
if (playPromise) {
playPromise.then(function () {
// Auto-play started
})["catch"](function () {
// Auto-play was prevented
});
}
}
}, {
key: "getKeys",
value: function getKeys() {
var _this$props2 = this.props,
source = _this$props2.source,
preloadSource = _this$props2.preloadSource;
var sourceKey = source && (0, _Media.getKeyFromSource)(source);
var preloadKey = preloadSource && (0, _Media.getKeyFromSource)(preloadSource);
// If the same source is used for both, clear the preload key to avoid rendering duplicate
// video elements.
if (sourceKey === preloadKey) {
preloadKey = null;
}
// if either the source or preload existed previously in the other "slot", swap the keys so
// the preload video becomes the active video and vice versa
if (sourceKey === this.prevPreloadKey && this.prevPreloadKey || preloadKey === this.prevSourceKey && this.prevSourceKey) {
this.keys.reverse();
}
// cache the previous keys so we know if the sources change the next time
this.prevSourceKey = sourceKey;
this.prevPreloadKey = preloadKey;
// if preload is unset, clear the key so we don't render that media node at all
return preloadKey ? this.keys : this.keys.slice(0, 1);
}
}, {
key: "render",
value: function render() {
var _this$props3 = this.props,
preloadSource = _this$props3.preloadSource,
source = _this$props3.source,
mediaComponent = _this$props3.mediaComponent,
rest = _objectWithoutProperties(_this$props3, _excluded);
delete rest.setMedia;
var _this$getKeys = this.getKeys(),
_this$getKeys2 = _slicedToArray(_this$getKeys, 2),
sourceKey = _this$getKeys2[0],
preloadKey = _this$getKeys2[1];
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_react.Fragment, {
children: [sourceKey ? /*#__PURE__*/(0, _react.createElement)(_Media.Media, _objectSpread(_objectSpread({}, rest), {}, {
className: _VideoPlayerModule["default"].video,
controls: false,
key: sourceKey,
mediaComponent: mediaComponent,
preload: "none",
ref: this.setVideoRef,
source: /*#__PURE__*/(0, _react.isValidElement)(source) ? source : /*#__PURE__*/(0, _jsxRuntime.jsx)("source", {
src: source
})
})) : null, preloadKey ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_Media.Media, {
autoPlay: false,
className: _VideoPlayerModule["default"].preloadVideo,
controls: false,
mediaComponent: mediaComponent,
onLoadStart: this.handlePreloadLoadStart,
preload: "none",
ref: this.setPreloadRef,
source: /*#__PURE__*/(0, _react.isValidElement)(preloadSource) ? preloadSource : /*#__PURE__*/(0, _jsxRuntime.jsx)("source", {
src: preloadSource
})
}, preloadKey) : null]
});
}
}]);
return VideoBase;
}(_react.Component), _class.displayName = 'Video', _class.propTypes = /** @lends sandstone/VideoPlayer.Video.prototype */{
/**
* Video plays automatically.
*
* @type {Boolean}
* @default false
* @public
*/
autoPlay: _propTypes2["default"].bool,
/**
* Video component to use.
*
* The default (`'video'`) renders an `HTMLVideoElement`. Custom video components must have
* a similar API structure, exposing the following APIs:
*
* Properties:
* * `currentTime` {Number} - Playback index of the media in seconds
* * `duration` {Number} - Media's entire duration in seconds
* * `error` {Boolean} - `true` if video playback has errored.
* * `loading` {Boolean} - `true` if video playback is loading.
* * `paused` {Boolean} - Playing vs paused state. `true` means the media is paused
* * `playbackRate` {Number} - Current playback rate, as a number
* * `proportionLoaded` {Number} - A value between `0` and `1`
* representing the proportion of the media that has loaded
* * `proportionPlayed` {Number} - A value between `0` and `1` representing the
* proportion of the media that has already been shown
*
* Events:
* * `onLoadStart` - Called when the video starts to load
* * `onPlay` - Sent when playback of the media starts after having been paused
* * `onUpdate` - Sent when any of the properties were updated
*
* Methods:
* * `play()` - play video
* * `pause()` - pause video
* * `load()` - load video
*
* The {@link sandstone/VideoPlayer.Video.source|source} property is passed to
* the video component as a child node.
*
* @type {String|Component|Element}
* @default 'video'
* @public
*/
mediaComponent: _propTypes["default"].renderableOverride,
/**
* The video source to be preloaded. Expects a `<source>` node.
*
* @type {Node}
* @public
*/
preloadSource: _propTypes2["default"].node,
/**
* Called with a reference to the active {@link ui/Media.Media|Media} component.
*
* @type {Function}
* @private
*/
setMedia: _propTypes2["default"].func,
/**
* The video source to be played.
*
* Any children `<source>` elements will be sent directly to the `mediaComponent` as video
* sources.
*
* See: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/source
*
* @type {String|Node}
* @public
*/
source: _propTypes2["default"].oneOfType([_propTypes2["default"].string, _propTypes2["default"].node])
}, _class.defaultProps = {
mediaComponent: 'video'
}, _class);
var VideoDecorator = (0, _compose["default"])((0, _ForwardRef["default"])({
prop: 'setMedia'
}), (0, _Slottable["default"])({
slots: ['source', 'preloadSource']
}));
/**
* Provides support for more advanced video configurations for `VideoPlayer`.
*
* Custom Video Tag
*
* ```
* <VideoPlayer>
* <Video mediaComponent="custom-video-element">
* <source src="path/to/source.mp4" />
* </Video>
* </VideoPlayer>
* ```
*
* Preload Video Source
*
* ```
* <VideoPlayer>
* <Video>
* <source src="path/to/source.mp4" />
* <source src="path/to/preload-source.mp4" slot="preloadSource" />
* </Video>
* </VideoPlayer>
* ```
*
* @class Video
* @mixes ui/Slottable.Slottable
* @memberof sandstone/VideoPlayer
* @ui
* @public
*/
var Video = exports.Video = VideoDecorator(VideoBase);
Video.defaultSlot = 'videoComponent';
var _default = exports["default"] = Video;