fix: clicking outside image closes modal (#1312)

fixes #862
This commit is contained in:
Nolan Lawson 2019-07-07 14:51:08 -07:00 committed by GitHub
parent 9fd5c8f6d2
commit 8f6681ad7a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 91 additions and 16 deletions

View file

@ -3,15 +3,16 @@
{label} {label}
background="var(--muted-modal-bg)" background="var(--muted-modal-bg)"
muted="true" muted="true"
clickHeaderToClose={true}
className="media-modal-dialog" className="media-modal-dialog"
on:show="onShow()" on:show="onShow()"
> >
<div class="media-container"> <div class="media-container">
<ul class="media-scroll" ref:scroller> <ul class="media-scroll" ref:scroller on:click="onImageClick(event)">
{#each mediaItems as media (media.id)} {#each mediaItems as media (media.id)}
<li class="media-scroll-item"> <li class="media-scroll-item">
<div class="media-scroll-item-inner"> <div class="media-scroll-item-inner">
<div class="media-scroll-item-inner-inner"> <div class="media-scroll-item-image-area">
{#if canPinchZoom && pinchZoomMode} {#if canPinchZoom && pinchZoomMode}
<PinchZoomable className='media-pinch-zoom' > <PinchZoomable className='media-pinch-zoom' >
<MediaInDialog {media} /> <MediaInDialog {media} />
@ -24,10 +25,11 @@
</li> </li>
{/each} {/each}
</ul> </ul>
<div class="media-controls-outside"> <div class="media-controls-outside" on:click="onMediaControlsClick(event)">
{#if canPinchZoom} {#if canPinchZoom}
<IconButton <IconButton
className="media-control-button media-control-button-dummy-spacer" className="media-control-button media-control-button-dummy-spacer"
svgClassName="media-control-button-svg"
href="#fa-search" href="#fa-search"
label="" label=""
ariaHidden={true} ariaHidden={true}
@ -37,6 +39,7 @@
<div class="media-controls"> <div class="media-controls">
<IconButton <IconButton
className="media-control-button" className="media-control-button"
svgClassName="media-control-button-svg"
disabled={scrolledItem === 0} disabled={scrolledItem === 0}
label="Show previous media" label="Show previous media"
href="#fa-angle-left" href="#fa-angle-left"
@ -45,16 +48,18 @@
{#each dots as dot, i (dot.i)} {#each dots as dot, i (dot.i)}
<IconButton <IconButton
className="media-control-button" className="media-control-button"
svgClassName="media-control-button-svg"
pressable={true} pressable={true}
label="Show {nth(i)} media" label="Show {nth(i)} media"
pressed={i === scrolledItem} pressed={i === scrolledItem}
href={i === scrolledItem ? '#fa-circle' : '#fa-circle-o'} href={i === scrolledItem ? '#fa-circle' : '#fa-circle-o'}
sameColorWhenPressed={true} sameColorWhenPressed={true}
on:click="onClick(i)" on:click="onButtonClick(i)"
/> />
{/each} {/each}
<IconButton <IconButton
className="media-control-button" className="media-control-button"
svgClassName="media-control-button-svg"
disabled={scrolledItem === length - 1} disabled={scrolledItem === length - 1}
label="Show next media" label="Show next media"
href="#fa-angle-right" href="#fa-angle-right"
@ -65,6 +70,7 @@
{#if canPinchZoom} {#if canPinchZoom}
<IconButton <IconButton
className="media-control-button" className="media-control-button"
svgClassName="media-control-button-svg"
pressable={true} pressable={true}
pressed={pinchZoomMode} pressed={pinchZoomMode}
label={pinchZoomMode ? 'Disable pinch-zoom mode' : 'Enable pinch-zoom mode'} label={pinchZoomMode ? 'Disable pinch-zoom mode' : 'Enable pinch-zoom mode'}
@ -85,7 +91,6 @@
.media-container { .media-container {
height: calc(100% - 64px); /* 44px X button height + 20px padding */ height: calc(100% - 64px); /* 44px X button height + 20px padding */
width: calc(100vw); width: calc(100vw);
padding-top: 10px;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
} }
@ -118,16 +123,16 @@
height: 100%; height: 100%;
overflow: hidden; overflow: hidden;
} }
.media-scroll-item-inner-inner { .media-scroll-item-image-area {
height: calc(100% - 10px); height: calc(100% - 20px); /* 15px padding top + 5px padding bottom */
width: calc(100% - 10px); width: calc(100% - 10px); /* 5px padding left + 5px padding right */
padding: 5px; padding: 15px 5px 5px 5px;
} }
.media-controls-outside { .media-controls-outside {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
margin: 10px; padding: 10px;
} }
.media-controls { .media-controls {
@ -148,6 +153,11 @@
margin: 0 5px; margin: 0 5px;
} }
:global(.media-control-button-svg) {
/* ensure that click events do not fall on these svgs */
pointer-events: none;
}
@media (max-width: 767px) { @media (max-width: 767px) {
:global(.icon-button.media-control-button) { :global(.icon-button.media-control-button) {
margin: 0; margin: 0;
@ -197,10 +207,21 @@
import PinchZoomable from './PinchZoomable.html' import PinchZoomable from './PinchZoomable.html'
import { show } from '../helpers/showDialog' import { show } from '../helpers/showDialog'
import { oncreate as onCreateDialog } from '../helpers/onCreateDialog' import { oncreate as onCreateDialog } from '../helpers/onCreateDialog'
import { close } from '../helpers/closeDialog'
import debounce from 'lodash-es/debounce' import debounce from 'lodash-es/debounce'
import times from 'lodash-es/times' import times from 'lodash-es/times'
import { smoothScroll, hasNativeSmoothScroll } from '../../../_utils/smoothScroll' import { smoothScroll, hasNativeSmoothScroll } from '../../../_utils/smoothScroll'
import { store } from '../../../_store/store' import { store } from '../../../_store/store'
import { intrinsicScale } from '../../../_thirdparty/intrinsic-scale/intrinsicScale'
import { get } from '../../../_utils/lodash-lite'
// padding for .media-scroll-item-image-area
const IMAGE_AREA_PADDING = {
top: 15,
left: 5,
right: 5,
bottom: 5
}
export default { export default {
oncreate () { oncreate () {
@ -217,7 +238,10 @@
computed: { computed: {
length: ({ mediaItems }) => mediaItems.length, length: ({ mediaItems }) => mediaItems.length,
dots: ({ length }) => times(length, i => ({ i })), dots: ({ length }) => times(length, i => ({ i })),
canPinchZoom: ({ mediaItems }) => !mediaItems.some(media => ['video', 'audio'].includes(media.type)) canPinchZoom: ({ mediaItems }) => !mediaItems.some(media => ['video', 'audio'].includes(media.type)),
mediaItem: ({ mediaItems, scrolledItem }) => mediaItems[scrolledItem],
nativeWidth: ({ mediaItem }) => get(mediaItem, ['meta', 'original', 'width'], 300), // TODO: Pleroma placeholder
nativeHeight: ({ mediaItem }) => get(mediaItem, ['meta', 'original', 'height'], 200) // TODO: Pleroma placeholder
}, },
components: { components: {
ModalDialog, ModalDialog,
@ -242,6 +266,7 @@
}, },
methods: { methods: {
show, show,
close,
setupScroll () { setupScroll () {
this.refs.scroller.addEventListener('scroll', this.onScroll) this.refs.scroller.addEventListener('scroll', this.onScroll)
}, },
@ -258,7 +283,7 @@
let scrolledItem = Math.round((scrollLeft / scrollWidth) * length) let scrolledItem = Math.round((scrollLeft / scrollWidth) * length)
this.set({ scrolledItem }) this.set({ scrolledItem })
}, },
onClick (i) { onButtonClick (i) {
let { scrolledItem } = this.get() let { scrolledItem } = this.get()
if (scrolledItem !== i) { if (scrolledItem !== i) {
this.scrollToItem(i, true) this.scrollToItem(i, true)
@ -308,6 +333,44 @@
}, },
togglePinchZoomMode () { togglePinchZoomMode () {
this.set({ pinchZoomMode: !this.get().pinchZoomMode }) this.set({ pinchZoomMode: !this.get().pinchZoomMode })
},
onImageClick (e) {
let { nativeWidth, nativeHeight, pinchZoomMode } = this.get()
if (pinchZoomMode) {
return
}
let rect = this.refs.scroller.getBoundingClientRect()
// apply padding
rect = {
width: rect.width - IMAGE_AREA_PADDING.left - IMAGE_AREA_PADDING.right,
height: rect.height - IMAGE_AREA_PADDING.top - IMAGE_AREA_PADDING.bottom,
left: rect.left + IMAGE_AREA_PADDING.left,
top: rect.top + IMAGE_AREA_PADDING.top
}
let scale = intrinsicScale(rect.width, rect.height, nativeWidth, nativeHeight)
let x = e.clientX - rect.left
let y = e.clientY - rect.top
let insideImage = x >= scale.x && x <= (scale.x + scale.width) && y >= scale.y && y <= (scale.y + scale.height)
if (!insideImage) {
// close dialog when clicking outside of image
e.preventDefault()
e.stopPropagation()
this.close()
}
},
onMediaControlsClick (e) {
let { pinchZoomMode } = this.get()
if (pinchZoomMode) {
return
}
let { target } = e
if (target.tagName !== 'BUTTON' && !target.classList.contains('media-controls')) {
e.preventDefault()
e.stopPropagation()
// close dialog when clicking on the controls but not on a button inside the controls,
// or between the buttons
this.close()
}
} }
} }
} }

View file

@ -223,8 +223,12 @@
focusX: ({ mediaItem }) => get(mediaItem, ['focusX'], 0), focusX: ({ mediaItem }) => get(mediaItem, ['focusX'], 0),
focusY: ({ mediaItem }) => get(mediaItem, ['focusY'], 0), focusY: ({ mediaItem }) => get(mediaItem, ['focusY'], 0),
previewSrc: ({ mediaItem }) => mediaItem.data.preview_url, previewSrc: ({ mediaItem }) => mediaItem.data.preview_url,
nativeWidth: ({ mediaItem }) => get(mediaItem, ['data', 'meta', 'original', 'width'], 300), nativeWidth: ({ mediaItem }) => (
nativeHeight: ({ mediaItem }) => get(mediaItem, ['data', 'meta', 'original', 'height'], 200), get(mediaItem, ['data', 'meta', 'original', 'width'], 300) // TODO: Pleroma placeholder
),
nativeHeight: ({ mediaItem }) => (
get(mediaItem, ['data', 'meta', 'original', 'height'], 200) // TODO: Pleroma placeholder
),
shortName: ({ mediaItem }) => ( shortName: ({ mediaItem }) => (
// sometimes we no longer have the file, e.g. in a delete and redraft situation, // sometimes we no longer have the file, e.g. in a delete and redraft situation,
// so fall back to the description if it was provided // so fall back to the description if it was provided

View file

@ -8,7 +8,7 @@
ref:node ref:node
> >
<div class="modal-dialog-document" role="document" style="background: {background || '#000'};"> <div class="modal-dialog-document" role="document" style="background: {background || '#000'};">
<div class="modal-dialog-header"> <div class="modal-dialog-header" on:click="onClickHeader(event)">
{#if title} {#if title}
<h1 class="modal-dialog-title">{title}</h1> <h1 class="modal-dialog-title">{title}</h1>
{/if} {/if}
@ -190,7 +190,8 @@
muted: false, muted: false,
className: void 0, className: void 0,
title: void 0, title: void 0,
shrinkWidthToFit: false shrinkWidthToFit: false,
clickHeaderToClose: false
}), }),
computed: { computed: {
backdropClass: ({ fadedIn, shouldAnimate }) => { backdropClass: ({ fadedIn, shouldAnimate }) => {
@ -246,6 +247,13 @@
return return
} }
this._a11yDialog.hide() this._a11yDialog.hide()
},
onClickHeader (e) {
if (this.get().clickHeaderToClose) {
e.preventDefault()
e.stopPropagation()
this._a11yDialog.hide()
}
} }
} }
} }