Repository: appleple/react-modal-video
Branch: master
Commit: 237250e30c26
Files: 16
Total size: 39.6 KB
Directory structure:
gitextract_so3v8z3u/
├── .editorconfig
├── .eslintrc
├── .gitignore
├── .node-version
├── LICENSE
├── babel.config.js
├── circle.yml
├── css/
│ └── modal-video.css
├── lib/
│ └── index.js
├── nodemon.json
├── package.json
├── readme.md
├── scss/
│ └── modal-video.scss
├── src/
│ └── index.jsx
└── test/
├── index.html
└── src/
└── index.jsx
================================================
FILE CONTENTS
================================================
================================================
FILE: .editorconfig
================================================
# editorconfig.org
root = true
[*.js]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.html]
# すべてのファイルに適用する
charset = utf-8
# 文字コードを統一
indent_style = tab
#インデントを統一する。「tab」か「 space」
indent_size = 2
# インデントの数を統一
trim_trailing_whitespace = true
# 行末のホワイトスペースを削除
insert_final_newline = true
# フォルダの最後の行に改行
end_of_line = lf
================================================
FILE: .eslintrc
================================================
{
"env": {
"browser": true
},
"globals": {
"document": true,
"window": true
},
"rules":{
"comma-dangle":0
},
"parserOptions": {
"sourceType": "module",
"ecmaVersion": 2015,
"ecmaFeatures": {
"jsx": true
}
}
}
================================================
FILE: .gitignore
================================================
node_modules/*
yarn.lock
.DS_Store
.idea
test/dist
================================================
FILE: .node-version
================================================
18.12.1
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2016 appleple
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: babel.config.js
================================================
module.exports = {
presets: [
['@babel/preset-env',
{
targets: {
ie: 11,
},
useBuiltIns: 'usage',
corejs: 3,
},
],
['@babel/preset-react'],
]
};
================================================
FILE: circle.yml
================================================
machine:
node:
version: 6.2.0
dependencies:
override:
- "npm install"
test:
override:
- "npm run test"
================================================
FILE: css/modal-video.css
================================================
@keyframes modal-video {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
@keyframes modal-video-inner {
from {
transform: translate(0, 100px);
}
to {
transform: translate(0, 0);
}
}
.modal-video {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
z-index: 1000000;
cursor: pointer;
opacity: 1;
animation-timing-function: ease-out;
animation-duration: 0.3s;
animation-name: modal-video;
-webkit-transition: opacity 0.3s ease-out;
-moz-transition: opacity 0.3s ease-out;
-ms-transition: opacity 0.3s ease-out;
-o-transition: opacity 0.3s ease-out;
transition: opacity 0.3s ease-out;
}
.modal-video-effect-exit {
opacity: 0;
}
.modal-video-effect-exit .modal-video-movie-wrap {
-webkit-transform: translate(0, 100px);
-moz-transform: translate(0, 100px);
-ms-transform: translate(0, 100px);
-o-transform: translate(0, 100px);
transform: translate(0, 100px);
}
.modal-video-body {
max-width: 960px;
width: 100%;
height: 100%;
margin: 0 auto;
padding: 0 10px;
display: flex;
justify-content: center;
box-sizing: border-box;
}
.modal-video-inner {
display: flex;
justify-content: center;
align-items: center;
width: 100%;
height: 100%;
}
@media (orientation: landscape) {
.modal-video-inner {
padding: 10px 60px;
box-sizing: border-box;
}
}
.modal-video-movie-wrap {
width: 100%;
height: 0;
position: relative;
padding-bottom: 56.25%;
background-color: #333;
animation-timing-function: ease-out;
animation-duration: 0.3s;
animation-name: modal-video-inner;
-webkit-transform: translate(0, 0);
-moz-transform: translate(0, 0);
-ms-transform: translate(0, 0);
-o-transform: translate(0, 0);
transform: translate(0, 0);
-webkit-transition: -webkit-transform 0.3s ease-out;
-moz-transition: -moz-transform 0.3s ease-out;
-ms-transition: -ms-transform 0.3s ease-out;
-o-transition: -o-transform 0.3s ease-out;
transition: transform 0.3s ease-out;
}
.modal-video-movie-wrap iframe {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.modal-video-close-btn {
position: absolute;
z-index: 2;
top: -45px;
right: 0px;
display: inline-block;
width: 35px;
height: 35px;
overflow: hidden;
border: none;
background: transparent;
}
@media (orientation: landscape) {
.modal-video-close-btn {
top: 0;
right: -45px;
}
}
.modal-video-close-btn:before {
transform: rotate(45deg);
}
.modal-video-close-btn:after {
transform: rotate(-45deg);
}
.modal-video-close-btn:before, .modal-video-close-btn:after {
content: "";
position: absolute;
height: 2px;
width: 100%;
top: 50%;
left: 0;
margin-top: -1px;
background: #fff;
border-radius: 5px;
margin-top: -6px;
}
================================================
FILE: lib/index.js
================================================
"use strict";
function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }, _typeof(obj); }
require("core-js/modules/es.object.to-string.js");
require("core-js/modules/es.reflect.construct.js");
require("core-js/modules/es.symbol.to-primitive.js");
require("core-js/modules/es.date.to-primitive.js");
require("core-js/modules/es.symbol.js");
require("core-js/modules/es.symbol.description.js");
require("core-js/modules/es.symbol.iterator.js");
require("core-js/modules/es.array.iterator.js");
require("core-js/modules/es.string.iterator.js");
require("core-js/modules/web.dom-collections.iterator.js");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
require("core-js/modules/es.array.concat.js");
require("core-js/modules/es.number.constructor.js");
require("core-js/modules/es.object.get-prototype-of.js");
var _react = _interopRequireDefault(require("react"));
var _CSSTransition = _interopRequireDefault(require("react-transition-group/CSSTransition"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
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(arg) { var key = _toPrimitive(arg, "string"); return _typeof(key) === "symbol" ? key : String(key); }
function _toPrimitive(input, hint) { if (_typeof(input) !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (_typeof(res) !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); }
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() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } }
function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf.bind() : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }
var ModalVideo = /*#__PURE__*/function (_React$Component) {
_inherits(ModalVideo, _React$Component);
var _super = _createSuper(ModalVideo);
function ModalVideo(props) {
var _this;
_classCallCheck(this, ModalVideo);
_this = _super.call(this, props);
_this.state = {
isOpen: false,
modalVideoWidth: '100%'
};
_this.closeModal = _this.closeModal.bind(_assertThisInitialized(_this));
_this.updateFocus = _this.updateFocus.bind(_assertThisInitialized(_this));
_this.timeout; // used for resizing video.
return _this;
}
_createClass(ModalVideo, [{
key: "openModal",
value: function openModal() {
this.setState({
isOpen: true
});
}
}, {
key: "closeModal",
value: function closeModal() {
this.setState({
isOpen: false
});
if (typeof this.props.onClose === 'function') {
this.props.onClose();
}
}
}, {
key: "keydownHandler",
value: function keydownHandler(e) {
if (e.keyCode === 27) {
this.closeModal();
}
}
}, {
key: "componentDidMount",
value: function componentDidMount() {
document.addEventListener('keydown', this.keydownHandler.bind(this));
window.addEventListener('resize', this.resizeModalVideoWhenHeightGreaterThanWindowHeight.bind(this));
this.setState({
modalVideoWidth: this.getWidthFulfillAspectRatio(this.props.ratio, window.innerHeight, window.innerWidth)
});
}
}, {
key: "componentWillUnmount",
value: function componentWillUnmount() {
document.removeEventListener('keydown', this.keydownHandler.bind(this));
window.removeEventListener('resize', this.resizeModalVideoWhenHeightGreaterThanWindowHeight.bind(this));
}
}, {
key: "componentDidUpdate",
value: function componentDidUpdate() {
if (this.state.isOpen && this.modal) {
this.modal.focus();
}
}
}, {
key: "updateFocus",
value: function updateFocus(e) {
if (this.state.isOpen) {
e.preventDefault();
e.stopPropagation();
if (e.keyCode === 9) {
if (this.modal === document.activeElement) {
this.modaliflame.focus();
} else if (this.modalbtn === document.activeElement) {
this.modal.focus();
}
}
}
}
/**
* Resize modal-video-iframe-wrap when window size changed when the height of the video is greater than the height of the window.
*/
}, {
key: "resizeModalVideoWhenHeightGreaterThanWindowHeight",
value: function resizeModalVideoWhenHeightGreaterThanWindowHeight() {
var _this2 = this;
clearTimeout(this.timeout);
this.timeout = setTimeout(function () {
var width = _this2.getWidthFulfillAspectRatio(_this2.props.ratio, window.innerHeight, window.innerWidth);
if (_this2.state.modalVideoWidth != width) {
_this2.setState({
modalVideoWidth: width
});
}
}, 10);
}
}, {
key: "getQueryString",
value: function getQueryString(obj) {
var url = '';
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
if (obj[key] !== null) {
url += "".concat(key, "=").concat(obj[key], "&");
}
}
}
return url.substr(0, url.length - 1);
}
}, {
key: "getYoutubeUrl",
value: function getYoutubeUrl(youtube, videoId) {
var query = this.getQueryString(youtube);
return "//www.youtube.com/embed/".concat(videoId, "?").concat(query);
}
}, {
key: "getVimeoUrl",
value: function getVimeoUrl(vimeo, videoId) {
var query = this.getQueryString(vimeo);
return "//player.vimeo.com/video/".concat(videoId, "?").concat(query);
}
}, {
key: "getYoukuUrl",
value: function getYoukuUrl(youku, videoId) {
var query = this.getQueryString(youku);
return "//player.youku.com/embed/".concat(videoId, "?").concat(query);
}
}, {
key: "getVideoUrl",
value: function getVideoUrl(opt, videoId) {
if (opt.channel === 'youtube') {
return this.getYoutubeUrl(opt.youtube, videoId);
}
if (opt.channel === 'vimeo') {
return this.getVimeoUrl(opt.vimeo, videoId);
}
if (opt.channel === 'youku') {
return this.getYoukuUrl(opt.youku, videoId);
}
if (opt.channel === 'custom') {
return opt.url;
}
}
}, {
key: "getPadding",
value: function getPadding(ratio) {
var arr = ratio.split(':');
var width = Number(arr[0]);
var height = Number(arr[1]);
var padding = height * 100 / width;
return "".concat(padding, "%");
}
/**
* Calculate the width of the video fulfill aspect ratio.
* When the height of the video is greater than the height of the window,
* this function return the width that fulfill the aspect ratio for the height of the window.
* In other cases, this function return '100%'(the height relative to the width is determined by css).
*
* @param string ratio
* @param number maxWidth
* @returns number | '100%'
*/
}, {
key: "getWidthFulfillAspectRatio",
value: function getWidthFulfillAspectRatio(ratio, maxHeight, maxWidth) {
var arr = ratio.split(':');
var width = Number(arr[0]);
var height = Number(arr[1]);
// Height that fulfill the aspect ratio for maxWidth.
var videoHeight = maxWidth * (height / width);
if (maxHeight < videoHeight) {
// when the height of the video is greater than the height of the window.
// calculate the width that fulfill the aspect ratio for the height of the window.
// ex: 16:9 aspect ratio
// 16:9 = width : height
// → width = 16 / 9 * height
return Math.floor(width / height * maxHeight);
}
return '100%';
}
}, {
key: "render",
value: function render() {
var _this3 = this;
var modalVideoInnerStyle = {
width: this.state.modalVideoWidth
};
var modalVideoIframeWrapStyle = {
paddingBottom: this.getPadding(this.props.ratio)
};
return /*#__PURE__*/_react.default.createElement(_CSSTransition.default, {
classNames: this.props.classNames.modalVideoEffect,
timeout: this.props.animationSpeed
}, function () {
if (!_this3.state.isOpen) {
return null;
}
return /*#__PURE__*/_react.default.createElement("div", {
className: _this3.props.classNames.modalVideo,
tabIndex: "-1",
role: "dialog",
"area-modal": "true",
"aria-label": _this3.props.aria.openMessage,
onClick: _this3.closeModal,
ref: function ref(node) {
_this3.modal = node;
},
onKeyDown: _this3.updateFocus
}, /*#__PURE__*/_react.default.createElement("div", {
className: _this3.props.classNames.modalVideoBody
}, /*#__PURE__*/_react.default.createElement("div", {
className: _this3.props.classNames.modalVideoInner,
style: modalVideoInnerStyle
}, /*#__PURE__*/_react.default.createElement("div", {
className: _this3.props.classNames.modalVideoIframeWrap,
style: modalVideoIframeWrapStyle
}, _this3.props.children || /*#__PURE__*/_react.default.createElement("iframe", {
width: "460",
height: "230",
src: _this3.getVideoUrl(_this3.props, _this3.props.videoId),
frameBorder: "0",
allow: 'accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture',
allowFullScreen: _this3.props.allowFullScreen,
onKeyDown: _this3.updateFocus,
ref: function ref(node) {
_this3.modaliflame = node;
},
tabIndex: "-1"
}), /*#__PURE__*/_react.default.createElement("button", {
className: _this3.props.classNames.modalVideoCloseBtn,
"aria-label": _this3.props.aria.dismissBtnMessage,
ref: function ref(node) {
_this3.modalbtn = node;
},
onKeyDown: _this3.updateFocus
})))));
});
}
}], [{
key: "getDerivedStateFromProps",
value: function getDerivedStateFromProps(props) {
return {
isOpen: props.isOpen
};
}
}]);
return ModalVideo;
}(_react.default.Component);
exports.default = ModalVideo;
ModalVideo.defaultProps = {
channel: 'youtube',
isOpen: false,
youtube: {
autoplay: 1,
cc_load_policy: 1,
color: null,
controls: 1,
disablekb: 0,
enablejsapi: 0,
end: null,
fs: 1,
h1: null,
iv_load_policy: 1,
list: null,
listType: null,
loop: 0,
modestbranding: null,
origin: null,
playlist: null,
playsinline: null,
rel: 0,
showinfo: 1,
start: 0,
wmode: 'transparent',
theme: 'dark',
mute: 0
},
ratio: '16:9',
vimeo: {
api: false,
autopause: true,
autoplay: true,
byline: true,
callback: null,
color: null,
height: null,
loop: false,
maxheight: null,
maxwidth: null,
player_id: null,
portrait: true,
title: true,
width: null,
xhtml: false
},
youku: {
autoplay: 1,
show_related: 0
},
allowFullScreen: true,
animationSpeed: 300,
classNames: {
modalVideoEffect: 'modal-video-effect',
modalVideo: 'modal-video',
modalVideoClose: 'modal-video-close',
modalVideoBody: 'modal-video-body',
modalVideoInner: 'modal-video-inner',
modalVideoIframeWrap: 'modal-video-movie-wrap',
modalVideoCloseBtn: 'modal-video-close-btn'
},
aria: {
openMessage: 'You just opened the modal video',
dismissBtnMessage: 'Close the modal by clicking here'
}
};
================================================
FILE: nodemon.json
================================================
{
"execMap": {
"js": "node",
"jsx": "jsx {{filename}} | node"
},
"ext": "jsx scss",
"ignore": [
"test/dist",
"node_modules",
"lib"
],
"verbose": true
}
================================================
FILE: package.json
================================================
{
"name": "react-modal-video",
"version": "2.0.2",
"main": "lib/index.js",
"description": "Modal Video Viewer",
"author": "appleple",
"license": "MIT",
"scripts": {
"test": "eslint ./src/index.jsx --fix",
"build:js": "npm-run-all -p build:lib build:test",
"build:lib": "npm run babel",
"build:test": "browserify ./test/src/index.jsx -t babelify -o ./test/dist/index.js",
"build:sass": "npm-run-all -p sass sass:min",
"babel": "babel src --out-dir lib",
"sass": "sass ./scss/modal-video.scss ./css/modal-video.css --style expanded --no-source-map",
"sass:min": "sass ./scss/modal-video.scss ./css/modal-video.min.css --style compressed --no-source-map",
"watch:js": "onchange \"src/\" -- npm run build:js",
"watch:sass": "onchange \"scss\" -- npm run build:sass",
"watch:test": "onchange \"test/src\" -- npm run build:test",
"sync": "browser-sync start --server './' --files './test/dist/*.js' './css/*.css' --startPath '/test/index.html'",
"start": "npm-run-all -p watch:js watch:sass watch:test sync",
"deploy": "np --no-cleanup"
},
"repository": {
"type": "git",
"url": "https://github.com/appleple/react-modal-video.git"
},
"devDependencies": {
"@babel/cli": "^7.17.10",
"@babel/core": "^7.18.5",
"@babel/preset-env": "^7.20.2",
"@babel/preset-react": "^7.18.6",
"babelify": "^10.0.0",
"browser-sync": "^2.27.10",
"browserify": "^17.0.0",
"eslint": "^8.17.0",
"eslint-config-airbnb": "^19.0.4",
"eslint-config-airbnb-base": "^15.0.0",
"eslint-plugin-import": "^2.26.0",
"npm-run-all": "^4.1.5",
"sass": "^1.52.3",
"onchange": "^7.1.0",
"np": "^7.6.1"
},
"dependencies": {
"core-js": "^3.27.2",
"react-transition-group": "^4.4.2"
},
"peerDependencies": {
"react": "^17.0.0 || ^18.2.0",
"react-dom": "^17.0.0 || ^18.2.0"
}
}
================================================
FILE: readme.md
================================================
# react-modal-video
React Modal Video Component
## Features
- Not affected by dom structure.
- Beautiful transition
- Accessible for keyboard navigation and screen readers.
- Rich options for youtube API and Vimeo API
## Demo
[https://unpkg.com/react-modal-video@latest/test/index.html](https://unpkg.com/react-modal-video@latest/test/index.html)
## Install
### npm
```sh
npm install react-modal-video
```
## Usage
import sass file to your project
```scss
@import 'node_modules/react-modal-video/scss/modal-video.scss';
```
### Functional Implementation with Hooks
```jsx
import React, { useState } from 'react';
import ReactDOM from 'react-dom';
import ModalVideo from 'react-modal-video';
const App = () => {
const [isOpen, setOpen] = useState(false);
return (
| properties | default | |
|---|---|---|
| channel | 'youtube' | |
| youtube | autoplay | 1 |
| cc_load_policy | 1 | |
| color | null | |
| controls | 1 | |
| disablekb | 0 | |
| enablejsapi | 0 | |
| end | null | |
| fs | 1 | |
| h1 | null | |
| iv_load_policy | 1 | |
| list | null | |
| listType | null | |
| loop | 0 | |
| modestbranding | null | |
| origin | null | |
| playlist | null | |
| playsinline | null | |
| rel | 0 | |
| showinfo | 1 | |
| start | 0 | |
| wmode | 'transparent' | |
| theme | 'dark' | |
| mute | 0 | |
| vimeo | api | false |
| autopause | true | |
| autoplay | true | |
| byline | true | |
| callback | null | |
| color | null | |
| height | null | |
| loop | false | |
| maxheight | null | |
| maxwidth | null | |
| player_id | null | |
| portrait | true | |
| title | true | |
| width | null | |
| xhtml | false | |
| youku | autoplay | 1 |
| show_related | 0 | |
| custom | url | MP4 URL / iframe URL |
| ratio | '16:9' | |
| allowFullScreen | true | |
| animationSpeed | 300 | |
| classNames | modalVideo | 'modal-video' |
| modalVideoClose | 'modal-video-close' | |
| modalVideoBody | 'modal-video-body' | |
| modalVideoInner | 'modal-video-inner' | |
| modalVideoIframeWrap | 'modal-video-movie-wrap' | |
| modalVideoCloseBtn | 'modal-video-close-btn' | |
| aria | openMessage | 'You just opened the modal video' |
| dismissBtnMessage | 'Close the modal by clicking here' | |