click on images to show them in a modal

This commit is contained in:
Nolan Lawson 2018-02-04 15:59:42 -08:00
parent 6e2fa24261
commit 6978d27a8c
7 changed files with 187 additions and 78 deletions

View file

@ -11,7 +11,6 @@
position: fixed; position: fixed;
top: 50%; top: 50%;
transform: translate(0, -50%); transform: translate(0, -50%);
background: #000;
padding: 0; padding: 0;
border: 3px solid var(--main-border); border: 3px solid var(--main-border);
} }
@ -28,8 +27,7 @@
background: var(--nav-bg) background: var(--nav-bg)
} }
.close-dialog-button { .close-dialog-button {
margin: 0 0 2px; padding: 0 0 7px;
padding: 0;
background: none; background: none;
border: none; border: none;
} }
@ -41,6 +39,13 @@
:global(dialog::backdrop, .backdrop) { :global(dialog::backdrop, .backdrop) {
background: rgba(51, 51, 51, 0.9) !important; /* TODO: hack for Safari */ background: rgba(51, 51, 51, 0.9) !important; /* TODO: hack for Safari */
} }
@media (max-width: 767px) {
.close-dialog-button span {
padding: 0 10px 4px;
font-size: 32px;
}
}
</style> </style>
<script> <script>
@ -53,6 +58,7 @@
if (typeof setImmediate === 'function' && navigator.userAgent.match(/Edge/)) { if (typeof setImmediate === 'function' && navigator.userAgent.match(/Edge/)) {
this.getDialogElement().style.width = `${this.get('width')}px` this.getDialogElement().style.width = `${this.get('width')}px`
} }
this.getDialogElement().style.background = this.get('background') || '#000'
this.observe('shown', shown => { this.observe('shown', shown => {
if (shown) { if (shown) {
this.show() this.show()

View file

@ -0,0 +1,44 @@
<ModalDialog :shown background="var(--main-bg)">
{{#if type === 'gifv'}}
<video
aria-label="Animated GIF: {{description || ''}}"
poster="{{poster}}"
src="{{src}}"
width="{{width}}"
height="{{height}}"
autoplay
muted
loop
playsinline
/>
{{else}}
<img
src="{{src}}"
width="{{width}}"
height="{{height}}"
aria-label="{{description || ''}}"
/>
{{/if}}
</ModalDialog>
<style>
:global(.modal-dialog img, .modal-dialog video) {
object-fit: contain;
overflow: hidden;
max-width: calc(100vw - 20px);
max-height: calc(100vh - 100px);
}
</style>
<script>
import ModalDialog from '../ModalDialog.html'
export default {
components: {
ModalDialog
},
methods: {
async show() {
this.set({shown: true})
}
}
}
</script>

View file

@ -2,53 +2,60 @@
style="grid-template-columns: repeat(auto-fit, minmax({{maxMediaWidth}}px, 1fr));" > style="grid-template-columns: repeat(auto-fit, minmax({{maxMediaWidth}}px, 1fr));" >
{{#each mediaAttachments as media}} {{#each mediaAttachments as media}}
{{#if media.type === 'video'}} {{#if media.type === 'video'}}
<button type="button" <button type="button"
class="play-video-button" class="play-video-button"
aria-label="Play video: {{media.description || ''}}" aria-label="Play video: {{media.description || ''}}"
on:click="onClickPlayVideoButton(media, media.description)"> on:click="onClickPlayVideoButton(media, getOriginalWidth(media), getOriginalHeight(media))">
<div class="svg-wrapper"> <div class="svg-wrapper">
<svg> <svg>
<use xlink:href="#fa-play-circle" /> <use xlink:href="#fa-play-circle" />
</svg> </svg>
</div> </div>
<img alt="{{media.description || ''}}" <img alt="{{media.description || ''}}"
src="{{media.preview_url}}" src="{{media.preview_url}}"
width="{{getSmallWidth(media)}}" width="{{getSmallWidth(media)}}"
height="{{getSmallHeight(media)}}" height="{{getSmallHeight(media)}}"
class="{{hasNoNativeWidthHeight(media) ? 'no-native-width-height' : ''}}" class="{{hasNoNativeWidthHeight(media) ? 'no-native-width-height' : ''}}"
/>
</button>
{{elseif media.type === 'gifv' && $autoplayGifs}}
<video
class="{{hasNoNativeWidthHeight(media) ? 'no-native-width-height' : ''}}"
aria-label="Animated GIF: {{media.description || ''}}"
poster="{{media.preview_url}}"
src="{{media.url}}"
width="{{getSmallWidth(media)}}"
height="{{getSmallHeight(media)}}"
autoplay
muted
loop
playsinline
/>
{{elseif media.type === 'gifv' && !$autoplayGifs}}
<NonAutoplayGifv
class="{{hasNoNativeWidthHeight(media) ? 'no-native-width-height' : ''}}"
label="Animated GIF: {{media.description || ''}}"
poster="{{media.preview_url}}"
src="{{media.url}}"
staticSrc="{{media.preview_url}}"
width="{{getSmallWidth(media)}}"
height="{{getSmallHeight(media)}}"
/> />
</button>
{{else}} {{else}}
<img class="{{!imageLoaded ? 'image-loading' : ''}} {{imageError ? 'image-error' : ''}} {{hasNoNativeWidthHeight(media) ? 'no-native-width-height' : ''}}" <button type="button"
on:imgLoad="set({imageLoaded: true})" class="show-image-button"
on:imgLoadError="set({imageError: true})" aria-label="Show image: {{media.description || ''}}"
alt="{{media.description || ''}}" on:click="onClickShowImageButton(media, getOriginalWidth(media), getOriginalHeight(media))">
src="{{media.preview_url}}" {{#if media.type === 'gifv' && $autoplayGifs}}
width="{{getSmallWidth(media)}}" <video
height="{{getSmallHeight(media)}}"/> class="{{hasNoNativeWidthHeight(media) ? 'no-native-width-height' : ''}}"
aria-label="Animated GIF: {{media.description || ''}}"
poster="{{media.preview_url}}"
src="{{media.url}}"
width="{{getSmallWidth(media)}}"
height="{{getSmallHeight(media)}}"
autoplay
muted
loop
playsinline
/>
{{elseif media.type === 'gifv' && !$autoplayGifs}}
<NonAutoplayGifv
class="{{hasNoNativeWidthHeight(media) ? 'no-native-width-height' : ''}}"
label="Animated GIF: {{media.description || ''}}"
poster="{{media.preview_url}}"
src="{{media.url}}"
staticSrc="{{media.preview_url}}"
width="{{getSmallWidth(media)}}"
height="{{getSmallHeight(media)}}"
/>
{{else}}
<img class="{{!imageLoaded ? 'image-loading' : ''}} {{imageError ? 'image-error' : ''}} {{hasNoNativeWidthHeight(media) ? 'no-native-width-height' : ''}}"
on:imgLoad="set({imageLoaded: true})"
on:imgLoadError="set({imageError: true})"
alt="{{media.description || ''}}"
src="{{media.preview_url}}"
width="{{getSmallWidth(media)}}"
height="{{getSmallHeight(media)}}"/>
{{/if}}
</button>
{{/if}} {{/if}}
{{/each}} {{/each}}
</div> </div>
@ -84,10 +91,10 @@
.play-video-button { .play-video-button {
margin: 0; margin: 0;
padding: 0; padding: 0;
position: relative;
border-radius: 0; border-radius: 0;
border: none; border: none;
background: none; background: none;
position: relative;
} }
.play-video-button .svg-wrapper { .play-video-button .svg-wrapper {
position: absolute; position: absolute;
@ -108,6 +115,14 @@
border-radius: 100%; border-radius: 100%;
background: var(--mask-opaque-bg); background: var(--mask-opaque-bg);
} }
.show-image-button {
margin: 0;
padding: 0;
border-radius: 0;
border: none;
background: none;
cursor: zoom-in;
}
@media (max-width: 767px) { @media (max-width: 767px) {
.status-media, .status-media video, .status-media img { .status-media, .status-media video, .status-media img {
max-width: calc(100vw - 20px); max-width: calc(100vw - 20px);
@ -120,20 +135,26 @@
import { imgLoad, imgLoadError } from '../../_utils/events' import { imgLoad, imgLoadError } from '../../_utils/events'
import { showVideoDialog } from '../../_utils/showVideoDialog' import { showVideoDialog } from '../../_utils/showVideoDialog'
import { showImageDialog } from '../../_utils/showImageDialog'
import NonAutoplayGifv from '../NonAutoplayGifv.html' import NonAutoplayGifv from '../NonAutoplayGifv.html'
export default { export default {
helpers: { helpers: {
getSmallWidth: media => media.meta && media.meta.small && typeof media.meta.small.width === 'number' ? media.meta.small.width : DEFAULT_MEDIA_WIDTH, getSmallWidth: media => media.meta && media.meta.small && typeof media.meta.small.width === 'number' ? media.meta.small.width : DEFAULT_MEDIA_WIDTH,
getSmallHeight: media => media.meta && media.meta.small && typeof media.meta.small.height === 'number' ? media.meta.small.height : DEFAULT_MEDIA_HEIGHT, getSmallHeight: media => media.meta && media.meta.small && typeof media.meta.small.height === 'number' ? media.meta.small.height : DEFAULT_MEDIA_HEIGHT,
getOriginalWidth: media => media.meta && media.meta.original && typeof media.meta.original.width === 'number' ? media.meta.original.width : DEFAULT_MEDIA_WIDTH,
getOriginalHeight: media => media.meta && media.meta.original && typeof media.meta.original.height === 'number' ? media.meta.original.height : DEFAULT_MEDIA_HEIGHT,
hasNoNativeWidthHeight: media => !(media && media.meta && media.meta.small && typeof media.meta.small.width === 'number' && typeof media.meta.small.height === 'number'), hasNoNativeWidthHeight: media => !(media && media.meta && media.meta.small && typeof media.meta.small.width === 'number' && typeof media.meta.small.height === 'number'),
}, },
computed: { computed: {
maxMediaWidth: (mediaAttachments) => Math.max.apply(Math, mediaAttachments.map(media => media.meta && media.meta.small && typeof media.meta.small.width === 'number' ? media.meta.small.width : DEFAULT_MEDIA_WIDTH)) maxMediaWidth: (mediaAttachments) => Math.max.apply(Math, mediaAttachments.map(media => media.meta && media.meta.small && typeof media.meta.small.width === 'number' ? media.meta.small.width : DEFAULT_MEDIA_WIDTH))
}, },
methods: { methods: {
async onClickPlayVideoButton(media, description) { onClickPlayVideoButton(media, width, height) {
showVideoDialog(media.preview_url, media.url, description) showVideoDialog(media.preview_url, media.url, width, height, media.description)
},
onClickShowImageButton(media, width, height) {
showImageDialog(media.preview_url, media.url, media.type, width, height, media.description)
} }
}, },
events: { events: {

View file

@ -1,24 +1,33 @@
{{#if status.sensitive}} {{#if status.sensitive}}
<div class="status-sensitive-media-container {{sensitiveShown ? 'status-sensitive-media-shown' : 'status-sensitive-media-hidden'}}" <div class="status-sensitive-media-container {{sensitiveShown ? 'status-sensitive-media-shown' : 'status-sensitive-media-hidden'}}"
> >
<button type="button" class="status-sensitive-media-button" {{#if sensitiveShown}}
aria-label="{{sensitiveShown ? 'Hide sensitive media: ' : 'Show sensitive media'}}" <button type="button"
class="status-sensitive-media-button"
aria-label="Hide sensitive media"
on:click="onClickSensitiveMediaButton()" > on:click="onClickSensitiveMediaButton()" >
{{#if !sensitiveShown}}
<div class="status-sensitive-media-warning">
<span>
Sensitive content. Click to show.
</span>
</div>
{{/if}}
<div class="svg-wrapper"> <div class="svg-wrapper">
<svg> <svg>
<use xlink:href="{{sensitiveShown ? '#fa-eye-slash' : '#fa-eye'}}" /> <use xlink:href="#fa-eye-slash" />
</svg> </svg>
</div> </div>
</button> </button>
{{#if sensitiveShown}}
<Media mediaAttachments="{{mediaAttachments}}" sensitive="{{status.sensitive}}"/> <Media mediaAttachments="{{mediaAttachments}}" sensitive="{{status.sensitive}}"/>
{{else}}
<button type="button"
class="status-sensitive-media-button"
aria-label="Show sensitive media"
on:click="onClickSensitiveMediaButton()" >
<div class="status-sensitive-media-warning">
<span>Sensitive content. Click to show.</span>
</div>
<div class="svg-wrapper">
<svg>
<use xlink:href="#fa-eye" />
</svg>
</div>
</button>
{{/if}} {{/if}}
</div> </div>
{{else}} {{else}}
@ -39,8 +48,6 @@
padding: 0; padding: 0;
border: none; border: none;
background: none; background: none;
width: 100%;
height: 100%;
} }
.status-sensitive-media-button:hover { .status-sensitive-media-button:hover {
@ -54,12 +61,17 @@
.status-sensitive-media-shown .status-sensitive-media-button { .status-sensitive-media-shown .status-sensitive-media-button {
position: absolute; position: absolute;
left: 0; left: 0;
right: 0;
top: 0; top: 0;
bottom: 0;
z-index: 90; z-index: 90;
} }
.status-sensitive-media-hidden .status-sensitive-media-button {
right: 0;
bottom: 0;
width: 100%;
height: 100%;
}
.status-sensitive-media-container.status-sensitive-media-hidden { .status-sensitive-media-container.status-sensitive-media-hidden {
width: 100%; width: 100%;
margin: 10px auto; margin: 10px auto;
@ -80,11 +92,6 @@
} }
.status-sensitive-media-container .svg-wrapper { .status-sensitive-media-container .svg-wrapper {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
display: flex; display: flex;
align-items: flex-start; align-items: flex-start;
justify-content: flex-start; justify-content: flex-start;
@ -92,6 +99,13 @@
pointer-events: none; pointer-events: none;
background: var(--mask-bg); background: var(--mask-bg);
} }
.status-sensitive-media-hidden .svg-wrapper {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
.status-sensitive-media-container.status-sensitive-media-shown .svg-wrapper { .status-sensitive-media-container.status-sensitive-media-shown .svg-wrapper {
background: none; background: none;
} }
@ -129,7 +143,7 @@
$sensitivesShown[contextualStatusId] = !$sensitivesShown[contextualStatusId] $sensitivesShown[contextualStatusId] = !$sensitivesShown[contextualStatusId]
this.store.set({'sensitivesShown': $sensitivesShown}) this.store.set({'sensitivesShown': $sensitivesShown})
this.fire('recalculateHeight') this.fire('recalculateHeight')
}, }
} }
} }
</script> </script>

View file

@ -1,13 +1,18 @@
<ModalDialog :shown> <ModalDialog :shown background="#000">
<video poster="{{poster}}" <video poster="{{poster}}"
src="{{src}}" src="{{src}}"
width="{{width}}"
height="{{height}}"
aria-label="Video: {{description || ''}}" aria-label="Video: {{description || ''}}"
controls controls
/> />
</ModalDialog> </ModalDialog>
<style> <style>
:global(.modal-dialog video) { :global(.modal-dialog video) {
max-width: 100%; object-fit: contain;
overflow: hidden;
max-width: calc(100vw - 20px);
max-height: calc(100vh - 100px);
} }
</style> </style>
<script> <script>

View file

@ -0,0 +1,17 @@
import ImageDialog from '../_components/status/ImageDialog.html'
import { createDialogElement } from './dialogs'
export function showImageDialog(poster, src, type, width, height, description) {
let imageDialog = new ImageDialog({
target: createDialogElement('Image dialog'),
data: {
poster,
src,
type,
width,
height,
description
}
})
imageDialog.show()
}

View file

@ -1,13 +1,15 @@
import VideoDialog from '../_components/status/VideoDialog.html' import VideoDialog from '../_components/status/VideoDialog.html'
import { createDialogElement } from './dialogs' import { createDialogElement } from './dialogs'
export function showVideoDialog(poster, src, description) { export function showVideoDialog(poster, src, width, height, description) {
let videoDialog = new VideoDialog({ let videoDialog = new VideoDialog({
target: createDialogElement('Video dialog'), target: createDialogElement('Video dialog'),
data: { data: {
poster: poster, poster,
src: src, src,
description: description width,
height,
description
} }
}) })
videoDialog.show() videoDialog.show()