add modal dialog to fix fullscreen video
This commit is contained in:
parent
8951b1ba48
commit
adfa5d5fb5
5
package-lock.json
generated
5
package-lock.json
generated
|
@ -1295,6 +1295,11 @@
|
|||
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
|
||||
"integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
|
||||
},
|
||||
"dialog-polyfill": {
|
||||
"version": "0.4.9",
|
||||
"resolved": "https://registry.npmjs.org/dialog-polyfill/-/dialog-polyfill-0.4.9.tgz",
|
||||
"integrity": "sha512-iM4ZXRLOA/qpbW6XznGOq7Iq58JoXhZGo+OMG8K3wFxvIatnMTnIancLGEKa6WYy6oTkndfb2UXQ6FGtoUBn1g=="
|
||||
},
|
||||
"diffie-hellman": {
|
||||
"version": "5.0.2",
|
||||
"resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.2.tgz",
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
"concurrently": "^3.5.1",
|
||||
"cross-env": "^5.1.3",
|
||||
"css-loader": "^0.28.7",
|
||||
"dialog-polyfill": "^0.4.9",
|
||||
"express": "^4.16.2",
|
||||
"extract-text-webpack-plugin": "^3.0.2",
|
||||
"fg-loadcss": "^2.0.1",
|
||||
|
|
|
@ -2,12 +2,19 @@
|
|||
{{#each mediaAttachments as media}}
|
||||
<div class="status-media-container {{hasNoNativeWidthHeight(media) ? 'no-native-width-height' : ''}}">
|
||||
{{#if media.type === 'video'}}
|
||||
<video poster="{{media.preview_url}}"
|
||||
src="{{media.url}}"
|
||||
width="{{getSmallWidth(media)}}"
|
||||
height="{{getSmallHeight(media)}}"
|
||||
controls
|
||||
/>
|
||||
<button type="button"
|
||||
class="play-video-button"
|
||||
aria-label="Play video"
|
||||
on:click="onClickPlayVideoButton(media, getSmallWidth(media), getSmallHeight(media))">
|
||||
<div class="svg-wrapper">
|
||||
<svg>
|
||||
<use xlink:href="#fa-play-circle" />
|
||||
</svg>
|
||||
</div>
|
||||
<img src="{{media.preview_url}}"
|
||||
width="{{getSmallWidth(media)}}"
|
||||
height="{{getSmallHeight(media)}}" />
|
||||
</button>
|
||||
{{elseif media.type === 'gifv'}}
|
||||
<video
|
||||
poster="{{media.preview_url}}"
|
||||
|
@ -59,11 +66,40 @@
|
|||
max-width: calc(100vw - 40px);
|
||||
object-fit: cover;
|
||||
}
|
||||
.play-video-button {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
position: relative;
|
||||
border-radius: 0;
|
||||
border: none;
|
||||
background: none;
|
||||
}
|
||||
.play-video-button .svg-wrapper {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 40;
|
||||
pointer-events: none;
|
||||
}
|
||||
.play-video-button svg {
|
||||
width: 72px;
|
||||
height: 72px;
|
||||
fill: var(--mask-svg-fill);
|
||||
border-radius: 100%;
|
||||
background: var(--mask-opaque-bg);
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
const DEFAULT_MEDIA_WIDTH = 300
|
||||
const DEFAULT_MEDIA_HEIGHT = 200
|
||||
|
||||
import VideoDialog from './VideoDialog.html'
|
||||
|
||||
export default {
|
||||
helpers: {
|
||||
getSmallWidth: media => media.meta && media.meta.small && typeof media.meta.small.width === 'number' ? media.meta.small.width : DEFAULT_MEDIA_WIDTH,
|
||||
|
@ -72,6 +108,24 @@
|
|||
},
|
||||
computed: {
|
||||
minMediaWidth: (mediaAttachments) => Math.min.apply(Math, mediaAttachments.map(media => media.meta && media.meta.small && typeof media.meta.small.width === 'number' ? media.meta.small.width : DEFAULT_MEDIA_WIDTH))
|
||||
},
|
||||
methods: {
|
||||
async onClickPlayVideoButton(media, width, height) {
|
||||
let dialog = document.createElement('dialog')
|
||||
dialog.classList.add('video-dialog')
|
||||
document.body.appendChild(dialog)
|
||||
let videoDialog = new VideoDialog({
|
||||
target: dialog,
|
||||
data: {
|
||||
poster: media.preview_url,
|
||||
src: media.url,
|
||||
dialog: dialog,
|
||||
width: width,
|
||||
height: height
|
||||
}
|
||||
})
|
||||
videoDialog.showModal()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
80
routes/_components/VideoDialog.html
Normal file
80
routes/_components/VideoDialog.html
Normal file
|
@ -0,0 +1,80 @@
|
|||
<div class="video-dialog-wrapper">
|
||||
<div class="close-video-button-wrapper">
|
||||
<button class="close-video-button" aria-label="Close video" on:click="onCloseButtonClicked()">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<video poster="{{poster}}"
|
||||
src="{{src}}"
|
||||
width="{{width}}"
|
||||
height="{{height}}"
|
||||
controls
|
||||
/>
|
||||
</div>
|
||||
<style>
|
||||
:global(.video-dialog) {
|
||||
position: fixed;
|
||||
top: 50%;
|
||||
transform: translate(0, -50%);
|
||||
background: #000;
|
||||
padding: 0;
|
||||
}
|
||||
:global(.video-dialog-wrapper) {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
max-width: calc(100vw - 40px);
|
||||
}
|
||||
:global(.video-dialog video) {
|
||||
max-width: 100%;
|
||||
}
|
||||
.close-video-button-wrapper {
|
||||
text-align: right;
|
||||
width: 100%;
|
||||
background: var(--nav-bg)
|
||||
}
|
||||
.close-video-button {
|
||||
margin: 0 0 2px;
|
||||
padding: 0;
|
||||
background: none;
|
||||
border: none;
|
||||
}
|
||||
.close-video-button span {
|
||||
padding: 0 15px;
|
||||
font-size: 48px;
|
||||
color: var(--button-primary-text);
|
||||
}
|
||||
:global(dialog.video-dialog::backdrop) {
|
||||
background: rgba(51, 51, 51, 0.8);
|
||||
}
|
||||
:global(dialog.video-dialog + .backdrop) {
|
||||
background: rgba(51, 51, 51, 0.8);
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
|
||||
import { importDialogPolyfill } from '../_utils/asyncModules'
|
||||
|
||||
export default {
|
||||
oncreate() {
|
||||
this.registration = this.register()
|
||||
},
|
||||
methods: {
|
||||
async register() {
|
||||
if (typeof HTMLDialogElement === 'undefined') {
|
||||
let dialogPolyfill = await importDialogPolyfill()
|
||||
dialogPolyfill.registerDialog(this.get('dialog'))
|
||||
}
|
||||
},
|
||||
async showModal() {
|
||||
await this.registration
|
||||
this.get('dialog').showModal()
|
||||
},
|
||||
onCloseButtonClicked() {
|
||||
this.get('dialog').close()
|
||||
document.body.removeChild(this.get('dialog'))
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -1,3 +1,5 @@
|
|||
import { loadCSS } from 'fg-loadcss';
|
||||
|
||||
const importURLSearchParams = () => import(
|
||||
/* webpackChunkName: 'url-search-params' */ 'url-search-params'
|
||||
).then(Params => {
|
||||
|
@ -25,10 +27,25 @@ const importIndexedDBGetAllShim = () => import(
|
|||
/* webpackChunkName: 'indexeddb-getall-shim' */ 'indexeddb-getall-shim'
|
||||
)
|
||||
|
||||
const importDialogPolyfill = (() => {
|
||||
let cached
|
||||
return () => {
|
||||
if (cached) {
|
||||
return Promise.resolve(cached)
|
||||
}
|
||||
loadCSS('/dialog-polyfill.css') // TODO: handle error
|
||||
return import(/* webpackChunkName: 'dialog-polyfill' */ 'dialog-polyfill').then(res => {
|
||||
cached = res
|
||||
return cached
|
||||
})
|
||||
}
|
||||
})()
|
||||
|
||||
export {
|
||||
importURLSearchParams,
|
||||
importTimeline,
|
||||
importIntersectionObserver,
|
||||
importRequestIdleCallback,
|
||||
importIndexedDBGetAllShim
|
||||
importIndexedDBGetAllShim,
|
||||
importDialogPolyfill
|
||||
}
|
|
@ -7,12 +7,6 @@
|
|||
width: 100%;
|
||||
border: 1px solid var(--settings-list-item-border);
|
||||
margin: 20px auto;
|
||||
max-width: 80%;
|
||||
}
|
||||
|
||||
@media (max-width: 767px) {
|
||||
ul.settings-list {
|
||||
max-width: 90%;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
|
@ -54,6 +54,7 @@
|
|||
|
||||
--mask-bg: $toast-bg;
|
||||
--mask-svg-fill: $secondary-text-color;
|
||||
--mask-opaque-bg: rgba($toast-bg, 0.8);
|
||||
|
||||
--deemphasized-text-color: #666;
|
||||
}
|
||||
|
|
|
@ -10,9 +10,9 @@
|
|||
|
||||
<style>
|
||||
/* auto-generated w/ build-sass.js */
|
||||
body{--button-primary-bg:#6081e6;--button-primary-text:#fff;--button-primary-border:#132c76;--button-primary-bg-active:#456ce2;--button-primary-bg-hover:#6988e7;--button-bg:#e6e6e6;--button-text:#333;--button-border:#a7a7a7;--button-bg-active:#bfbfbf;--button-bg-hover:#f2f2f2;--input-border:#dadada;--anchor-text:#4169e1;--main-bg:#fff;--body-bg:#e8edfb;--body-text-color:#333;--main-border:#dadada;--svg-fill:#4169e1;--form-bg:#f7f7f7;--form-border:#c1c1c1;--nav-bg:#4169e1;--nav-border:#214cce;--nav-a-border:#4169e1;--nav-a-selected-border:#fff;--nav-a-selected-bg:#6d8ce8;--nav-svg-fill:#fff;--nav-text-color:#fff;--nav-a-selected-border-hover:#fff;--nav-a-selected-bg-hover:#839deb;--nav-a-bg-hover:#577ae4;--nav-a-border-hover:#4169e1;--nav-svg-fill-hover:#fff;--nav-text-color-hover:#fff;--action-button-fill-color:#839deb;--action-button-fill-color-hover:#99afef;--action-button-fill-color-active:#577ae4;--settings-list-item-bg:#fff;--settings-list-item-text:#4169e1;--settings-list-item-text-hover:#4169e1;--settings-list-item-border:#dadada;--settings-list-item-bg-active:#e6e6e6;--settings-list-item-bg-hover:#fafafa;--toast-bg:#333;--toast-border:#fafafa;--toast-text:#fff;--mask-bg:#333;--mask-svg-fill:#fff;--deemphasized-text-color:#666}
|
||||
body{--button-primary-bg:#6081e6;--button-primary-text:#fff;--button-primary-border:#132c76;--button-primary-bg-active:#456ce2;--button-primary-bg-hover:#6988e7;--button-bg:#e6e6e6;--button-text:#333;--button-border:#a7a7a7;--button-bg-active:#bfbfbf;--button-bg-hover:#f2f2f2;--input-border:#dadada;--anchor-text:#4169e1;--main-bg:#fff;--body-bg:#e8edfb;--body-text-color:#333;--main-border:#dadada;--svg-fill:#4169e1;--form-bg:#f7f7f7;--form-border:#c1c1c1;--nav-bg:#4169e1;--nav-border:#214cce;--nav-a-border:#4169e1;--nav-a-selected-border:#fff;--nav-a-selected-bg:#6d8ce8;--nav-svg-fill:#fff;--nav-text-color:#fff;--nav-a-selected-border-hover:#fff;--nav-a-selected-bg-hover:#839deb;--nav-a-bg-hover:#577ae4;--nav-a-border-hover:#4169e1;--nav-svg-fill-hover:#fff;--nav-text-color-hover:#fff;--action-button-fill-color:#839deb;--action-button-fill-color-hover:#99afef;--action-button-fill-color-active:#577ae4;--settings-list-item-bg:#fff;--settings-list-item-text:#4169e1;--settings-list-item-text-hover:#4169e1;--settings-list-item-border:#dadada;--settings-list-item-bg-active:#e6e6e6;--settings-list-item-bg-hover:#fafafa;--toast-bg:#333;--toast-border:#fafafa;--toast-text:#fff;--mask-bg:#333;--mask-svg-fill:#fff;--mask-opaque-bg:rgba(51,51,51,0.8);--deemphasized-text-color:#666}
|
||||
body{margin:0;font-family:system-ui, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue;font-size:14px;line-height:1.3;color:var(--body-text-color);background:var(--body-bg);position:fixed;left:0;right:0;bottom:0;top:0}.container{overflow-y:auto;overflow-x:hidden;-webkit-overflow-scrolling:touch;position:absolute;top:72px;left:0;right:0;bottom:0;will-change:transform}main{position:relative;width:602px;max-width:100vw;padding:15px 0;box-sizing:border-box;margin:15px auto 15px;background:var(--main-bg);border:1px solid var(--main-border);border-radius:1px}@media (max-width: 767px){main{margin:5px auto 15px}}h1,h2,h3,h4,h5,h6{margin:0 0 0.5em 0;font-weight:400;line-height:1.2}h1{font-size:2em}a{color:var(--anchor-text);text-decoration:none}a:visited{color:var(--anchor-text)}a:hover{text-decoration:underline}input{border:1px solid var(--input-border);padding:5px}button{font-size:1.2em;background:var(--button-bg);border-radius:2px;padding:10px 15px;border:1px solid var(--button-border);cursor:pointer;color:var(--button-text)}button:hover{background:var(--button-bg-hover)}button:active{background:var(--button-bg-active)}button[disabled]{opacity:0.35;pointer-events:none;cursor:not-allowed}button.primary{border:1px solid var(--button-primary-border);background:var(--button-primary-bg);color:var(--button-primary-text)}button.primary:hover{background:var(--button-primary-bg-hover)}button.primary:active{background:var(--button-primary-bg-active)}p,label,input{font-size:1.3em}ul,li,p{padding:0;margin:0}.hidden{opacity:0}
|
||||
body.offline,body.theme-hotpants.offline,body.theme-majesty.offline,body.theme-oaken.offline,body.theme-scarlet.offline,body.theme-seafoam.offline{--button-primary-bg:#ababab;--button-primary-text:#fff;--button-primary-border:#4d4d4d;--button-primary-bg-active:#9c9c9c;--button-primary-bg-hover:#b0b0b0;--button-bg:#e6e6e6;--button-text:#333;--button-border:#a7a7a7;--button-bg-active:#bfbfbf;--button-bg-hover:#f2f2f2;--input-border:#dadada;--anchor-text:#999;--main-bg:#fff;--body-bg:#fafafa;--body-text-color:#333;--main-border:#dadada;--svg-fill:#999;--form-bg:#f7f7f7;--form-border:#c1c1c1;--nav-bg:#999;--nav-border:gray;--nav-a-border:#999;--nav-a-selected-border:#fff;--nav-a-selected-bg:#b3b3b3;--nav-svg-fill:#fff;--nav-text-color:#fff;--nav-a-selected-border-hover:#fff;--nav-a-selected-bg-hover:#bfbfbf;--nav-a-bg-hover:#a6a6a6;--nav-a-border-hover:#999;--nav-svg-fill-hover:#fff;--nav-text-color-hover:#fff;--action-button-fill-color:#bfbfbf;--action-button-fill-color-hover:#ccc;--action-button-fill-color-active:#a6a6a6;--settings-list-item-bg:#fff;--settings-list-item-text:#999;--settings-list-item-text-hover:#999;--settings-list-item-border:#dadada;--settings-list-item-bg-active:#e6e6e6;--settings-list-item-bg-hover:#fafafa;--toast-bg:#333;--toast-border:#fafafa;--toast-text:#fff;--mask-bg:#333;--mask-svg-fill:#fff;--deemphasized-text-color:#666}
|
||||
body.offline,body.theme-hotpants.offline,body.theme-majesty.offline,body.theme-oaken.offline,body.theme-scarlet.offline,body.theme-seafoam.offline{--button-primary-bg:#ababab;--button-primary-text:#fff;--button-primary-border:#4d4d4d;--button-primary-bg-active:#9c9c9c;--button-primary-bg-hover:#b0b0b0;--button-bg:#e6e6e6;--button-text:#333;--button-border:#a7a7a7;--button-bg-active:#bfbfbf;--button-bg-hover:#f2f2f2;--input-border:#dadada;--anchor-text:#999;--main-bg:#fff;--body-bg:#fafafa;--body-text-color:#333;--main-border:#dadada;--svg-fill:#999;--form-bg:#f7f7f7;--form-border:#c1c1c1;--nav-bg:#999;--nav-border:gray;--nav-a-border:#999;--nav-a-selected-border:#fff;--nav-a-selected-bg:#b3b3b3;--nav-svg-fill:#fff;--nav-text-color:#fff;--nav-a-selected-border-hover:#fff;--nav-a-selected-bg-hover:#bfbfbf;--nav-a-bg-hover:#a6a6a6;--nav-a-border-hover:#999;--nav-svg-fill-hover:#fff;--nav-text-color-hover:#fff;--action-button-fill-color:#bfbfbf;--action-button-fill-color-hover:#ccc;--action-button-fill-color-active:#a6a6a6;--settings-list-item-bg:#fff;--settings-list-item-text:#999;--settings-list-item-text-hover:#999;--settings-list-item-border:#dadada;--settings-list-item-bg-active:#e6e6e6;--settings-list-item-bg-hover:#fafafa;--toast-bg:#333;--toast-border:#fafafa;--toast-text:#fff;--mask-bg:#333;--mask-svg-fill:#fff;--mask-opaque-bg:rgba(51,51,51,0.8);--deemphasized-text-color:#666}
|
||||
|
||||
</style>
|
||||
|
||||
|
@ -105,7 +105,13 @@ body.offline,body.theme-hotpants.offline,body.theme-majesty.offline,body.theme-o
|
|||
<path d="M1536 1399q0 109-62.5 187t-150.5 78H469q-88 0-150.5-78T256 1399q0-85 8.5-160.5t31.5-152 58.5-131 94-89T583 832q131 128 313 128t313-128q76 0 134.5 34.5t94 89 58.5 131 31.5 152 8.5 160.5zm-256-887q0 159-112.5 271.5T896 896 624.5 783.5 512 512t112.5-271.5T896 128t271.5 112.5T1280 512z"></path>
|
||||
</symbol>
|
||||
|
||||
</svg>
|
||||
<symbol id="fa-play-circle" viewBox="0 0 1792 1792">
|
||||
<title>Play</title>
|
||||
<path d="M896 128q209 0 385.5 103T1561 510.5 1664 896t-103 385.5-279.5 279.5T896 1664t-385.5-103T231 1281.5 128 896t103-385.5T510.5 231 896 128zm384 823q32-18 32-55t-32-55L736 521q-31-19-64-1-32 19-32 56v640q0 37 32 56 16 8 32 8 17 0 32-9z"/>
|
||||
</symbol>
|
||||
|
||||
|
||||
</svg>
|
||||
<!-- The application will be rendered inside this element,
|
||||
because `templates/main.js` references it -->
|
||||
<div id='sapper'>%sapper.html%</div>
|
||||
|
|
|
@ -6,7 +6,8 @@ import {
|
|||
importURLSearchParams,
|
||||
importIntersectionObserver,
|
||||
importRequestIdleCallback,
|
||||
importIndexedDBGetAllShim
|
||||
importIndexedDBGetAllShim,
|
||||
importDialogPolyfill
|
||||
} from '../routes/_utils/asyncModules'
|
||||
|
||||
// polyfills
|
||||
|
@ -14,7 +15,8 @@ Promise.all([
|
|||
typeof URLSearchParams === 'undefined' && importURLSearchParams(),
|
||||
typeof IntersectionObserver === 'undefined' && importIntersectionObserver(),
|
||||
typeof requestIdleCallback === 'undefined' && importRequestIdleCallback(),
|
||||
!IDBObjectStore.prototype.getAll && importIndexedDBGetAllShim()
|
||||
!IDBObjectStore.prototype.getAll && importIndexedDBGetAllShim(),
|
||||
typeof HTMLDialogElement === 'undefined' && importDialogPolyfill()
|
||||
]).then(() => {
|
||||
// `routes` is an array of route objects injected by Sapper
|
||||
init(document.querySelector('#sapper'), __routes__)
|
||||
|
|
Loading…
Reference in a new issue