@enact/ui
Version:
A collection of simplified unstyled cross-platform UI components for Enact
351 lines (344 loc) • 14 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.handledMediaEventsMap = exports.getKeyFromSource = exports["default"] = exports.Media = void 0;
var _dispatcher = require("@enact/core/dispatcher");
var _handle = require("@enact/core/handle");
var _propTypes = _interopRequireDefault(require("@enact/core/internal/prop-types"));
var _propTypes2 = _interopRequireDefault(require("prop-types"));
var _react = require("react");
var _jsxRuntime = require("react/jsx-runtime");
var _excluded = ["customMediaEventsMap", "mediaComponent", "source"];
function _interopRequireDefault(e) { return e && e.__esModule ? e : { "default": e }; }
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(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
function _objectWithoutProperties(e, t) { if (null == e) return {}; var o, r, i = _objectWithoutPropertiesLoose(e, t); if (Object.getOwnPropertySymbols) { var n = Object.getOwnPropertySymbols(e); for (r = 0; r < n.length; r++) o = n[r], -1 === t.indexOf(o) && {}.propertyIsEnumerable.call(e, o) && (i[o] = e[o]); } return i; }
function _objectWithoutPropertiesLoose(r, e) { if (null == r) return {}; var t = {}; for (var n in r) if ({}.hasOwnProperty.call(r, n)) { if (-1 !== e.indexOf(n)) continue; t[n] = r[n]; } return t; }
function _classCallCheck(a, n) { if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function"); }
function _defineProperties(e, r) { for (var t = 0; t < r.length; t++) { var o = r[t]; o.enumerable = o.enumerable || !1, o.configurable = !0, "value" in o && (o.writable = !0), Object.defineProperty(e, _toPropertyKey(o.key), o); } }
function _createClass(e, r, t) { return r && _defineProperties(e.prototype, r), t && _defineProperties(e, t), Object.defineProperty(e, "prototype", { writable: !1 }), e; }
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : 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 _callSuper(t, o, e) { return o = _getPrototypeOf(o), _possibleConstructorReturn(t, _isNativeReflectConstruct() ? Reflect.construct(o, e || [], _getPrototypeOf(t).constructor) : o.apply(t, e)); }
function _possibleConstructorReturn(t, e) { if (e && ("object" == typeof e || "function" == typeof e)) return e; if (void 0 !== e) throw new TypeError("Derived constructors may only return object or undefined"); return _assertThisInitialized(t); }
function _assertThisInitialized(e) { if (void 0 === e) throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); return e; }
function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function _isNativeReflectConstruct() { return !!t; })(); }
function _getPrototypeOf(t) { return _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf.bind() : function (t) { return t.__proto__ || Object.getPrototypeOf(t); }, _getPrototypeOf(t); }
function _inherits(t, e) { if ("function" != typeof e && null !== e) throw new TypeError("Super expression must either be null or a function"); t.prototype = Object.create(e && e.prototype, { constructor: { value: t, writable: !0, configurable: !0 } }), Object.defineProperty(t, "prototype", { writable: !1 }), e && _setPrototypeOf(t, e); }
function _setPrototypeOf(t, e) { return _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function (t, e) { return t.__proto__ = e, t; }, _setPrototypeOf(t, e); } /**
* A representation of `HTMLMediaElement` component with an ability to attach a custom event
* handler.
*
* @module ui/Media
* @exports getKeyFromSource
* @exports handledMediaEventsMap
* @exports Media
*/ /**
* Generates a key representing the source node or nodes provided
*
* Example:
* ```
* getKeyFromSource('path/file.mp4'); // 'path/file.mp4'
* getKeyFromSource(
* <source src="path/file.mp4" type="video/mp4" />
* ); // 'path/file.mp4'
* getKeyFromSource([
* <source src="path/file.mp4" type="video/mp4" />,
* <source src="path/file.ogg" type="video/ogg" />,
* ]); // 'path/file.mp4+path/file.ogg'
* ```
*
* @function
* @param {String|Element|Element[]} source URI for a source, `<source>` node, or array of
* `<source>` nodes
* @returns {String} Key representing sources
* @memberof ui/Media
* @public
*/
var getKeyFromSource = exports.getKeyFromSource = function getKeyFromSource() {
var source = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
if (/*#__PURE__*/(0, _react.isValidElement)(source)) {
return _react.Children.toArray(source).filter(function (s) {
return !!s;
}).map(function (s) {
return s.props.src;
}).join('+');
}
return String(source);
};
/**
* Maps standard media event `type` values to React-style callback prop names
*
* See https://react.dev/reference/react-dom/components/common#common-props
*
* ```
* {
* abort : 'onAbort',
* canplay : 'onCanPlay',
* canplaythrough : 'onCanPlayThrough',
* durationchange : 'onDurationChange',
* emptied : 'onEmptied',
* encrypted : 'onEncrypted',
* ended : 'onEnded',
* error : 'onError',
* loadeddata : 'onLoadedData',
* loadedmetadata : 'onLoadedMetadata',
* loadstart : 'onLoadStart',
* pause : 'onPause',
* play : 'onPlay',
* playing : 'onPlaying',
* progress : 'onProgress',
* ratechange : 'onRateChange',
* seeked : 'onSeeked',
* seeking : 'onSeeking',
* stalled : 'onStalled',
* suspend : 'onSuspend',
* timeupdate : 'onTimeUpdate',
* volumechange : 'onVolumeChange',
* waiting : 'onWaiting'
* }
* ```
*
* @typedef {Object} handledMediaEventsMap
* @memberof ui/Media
* @public
*/
var handledMediaEventsMap = exports.handledMediaEventsMap = {
abort: 'onAbort',
canplay: 'onCanPlay',
canplaythrough: 'onCanPlayThrough',
durationchange: 'onDurationChange',
emptied: 'onEmptied',
encrypted: 'onEncrypted',
ended: 'onEnded',
error: 'onError',
loadeddata: 'onLoadedData',
loadedmetadata: 'onLoadedMetadata',
loadstart: 'onLoadStart',
pause: 'onPause',
play: 'onPlay',
playing: 'onPlaying',
progress: 'onProgress',
ratechange: 'onRateChange',
seeked: 'onSeeked',
seeking: 'onSeeking',
stalled: 'onStalled',
suspend: 'onSuspend',
timeupdate: 'onTimeUpdate',
volumechange: 'onVolumeChange',
waiting: 'onWaiting'
};
/**
* A component representation of HTMLMediaElement.
*
* @class Media
* @memberof ui/Media
* @ui
* @public
*/
var Media = exports.Media = /*#__PURE__*/function (_ReactComponent) {
function Media(props) {
var _this;
_classCallCheck(this, Media);
_this = _callSuper(this, Media, [props]);
_this.attachCustomMediaEvents = function () {
for (var eventName in _this.handledCustomMediaForwards) {
(0, _dispatcher.on)(eventName, _this.handledCustomMediaForwards[eventName], _this.media);
}
};
_this.detachCustomMediaEvents = function () {
for (var eventName in _this.handledCustomMediaForwards) {
(0, _dispatcher.off)(eventName, _this.handledCustomMediaForwards[eventName], _this.media);
}
};
_this.handleEvent = function (ev) {
(0, _handle.forwardCustom)('onUpdate')(null, _this.props);
// fetch the forward() we generated earlier, using the event type as a key to find the real event name.
var fwd = _this.handledMediaForwards[handledMediaEventsMap[ev.type]];
if (fwd) {
fwd(ev, _this.props);
}
};
_this.mediaRef = function (node) {
_this.media = node;
};
_this.media = null;
_this.handledMediaForwards = {};
_this.handledMediaEvents = {};
_this.handledCustomMediaForwards = {};
// Generate event handling forwarders and a smooth block to pass to <Video>
for (var key in props.mediaEventsMap) {
var eventName = props.mediaEventsMap[key];
_this.handledMediaForwards[eventName] = (0, _handle.forward)(eventName);
_this.handledMediaEvents[eventName] = _this.handleEvent;
}
// Generate event handling forwarders for the custom events too
var _loop = function _loop() {
var propName = props.customMediaEventsMap[_eventName];
var forwardEvent = (0, _handle.forward)(propName);
_this.handledCustomMediaForwards[_eventName] = function (ev) {
return forwardEvent(ev, _this.props);
};
};
for (var _eventName in props.customMediaEventsMap) {
_loop();
}
return _this;
}
_inherits(Media, _ReactComponent);
return _createClass(Media, [{
key: "componentDidMount",
value: function componentDidMount() {
this.attachCustomMediaEvents();
}
}, {
key: "componentDidUpdate",
value: function componentDidUpdate(_ref) {
var prevSource = _ref.source;
var source = this.props.source;
if (getKeyFromSource(source) !== getKeyFromSource(prevSource)) {
this.load();
}
}
}, {
key: "componentWillUnmount",
value: function componentWillUnmount() {
this.detachCustomMediaEvents();
}
}, {
key: "play",
value: function play() {
return this.media.play();
}
}, {
key: "pause",
value: function pause() {
this.media.pause();
}
}, {
key: "load",
value: function load() {
this.media.load();
}
}, {
key: "currentTime",
get: function get() {
return this.media.currentTime;
},
set: function set(currentTime) {
this.media.currentTime = currentTime;
}
}, {
key: "duration",
get: function get() {
return this.media.duration;
}
}, {
key: "error",
get: function get() {
return this.media.networkState === this.media.NETWORK_NO_SOURCE;
}
}, {
key: "loading",
get: function get() {
return this.media.readyState < this.media.HAVE_ENOUGH_DATA;
}
}, {
key: "paused",
get: function get() {
return this.media.paused;
}
}, {
key: "playbackRate",
get: function get() {
return this.media.playbackRate;
},
set: function set(playbackRate) {
this.media.playbackRate = playbackRate;
}
}, {
key: "proportionLoaded",
get: function get() {
return this.media.buffered.length && this.media.buffered.end(this.media.buffered.length - 1) / this.media.duration;
}
}, {
key: "proportionPlayed",
get: function get() {
return this.media.currentTime / this.media.duration;
}
}, {
key: "render",
value: function render() {
var _this$props = this.props,
customMediaEventsMap = _this$props.customMediaEventsMap,
Component = _this$props.mediaComponent,
source = _this$props.source,
rest = _objectWithoutProperties(_this$props, _excluded);
delete rest.mediaEventsMap;
delete rest.onUpdate;
// Remove the events we manually added so they aren't added twice or fail.
for (var eventName in customMediaEventsMap) {
delete rest[customMediaEventsMap[eventName]];
}
return /*#__PURE__*/(0, _jsxRuntime.jsx)(Component, _objectSpread(_objectSpread(_objectSpread({}, rest), this.handledMediaEvents), {}, {
ref: this.mediaRef,
children: source
}));
}
}]);
}(_react.Component);
Media.propTypes = /** @lends ui/Media.Media.prototype */{
/**
* A type of media component.
*
* @type {String|Component}
* @required
* @public
*/
mediaComponent: _propTypes["default"].renderable.isRequired,
/**
* An event map object for custom media events.
*
* List custom events that aren't standard to React. These will be directly added to the media
* element and props matching their name will be executed as callback functions when the event fires.
*
* Example:
* ```
* {'umsmediainfo': 'onUMSMediaInfo'} // `onUMSMediaInfo` prop function will execute when the `umsmediainfo` event happens
* ```
*
* @type {Object}
* @public
*/
customMediaEventsMap: _propTypes2["default"].object,
/**
* A event map object for media events.
*
* @type {Object}
* @default {@link ui/Media.handledMediaEventsMap}
* @public
*/
mediaEventsMap: _propTypes2["default"].object,
/**
* A function to be run when media updates.
*
* @type {Function}
* @public
*/
onUpdate: _propTypes2["default"].func,
/**
* Media sources passed as children to `mediaComponent`
*
* See: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/source
*
* @type {*}
* @public
*/
source: _propTypes2["default"].any
};
Media.defaultProps = {
mediaEventsMap: handledMediaEventsMap
};
var _default = exports["default"] = Media;