pinafore/src/routes/_components/dialog/components/MediaDialog.html

281 lines
7.3 KiB
HTML

<ModalDialog
{id}
{label}
background="var(--muted-modal-bg)"
muted="true"
className="media-modal-dialog"
>
<div class="media-container">
<div class="media-scroll" ref:scroller>
{#each mediaItems as media}
<div class="media-scroll-item">
<div class="media-scroll-item-inner">
<div class="media-scroll-item-inner-inner">
<PinchZoomable className='media-pinch-zoom' disabled={!pinchZoomMode} >
<MediaInDialog {media} />
</PinchZoomable>
</div>
</div>
</div>
{/each}
</div>
<div class="media-controls-outside">
<IconButton
className="media-control-button media-control-button-dummy-spacer"
href="#fa-search"
/>
{#if dots.length > 1}
<div class="media-controls">
<IconButton
className="media-control-button"
disabled={scrolledItem === 0}
label="Show previous media"
href="#fa-angle-left"
on:click="prev()"
/>
{#each dots as dot, i (dot.i)}
<IconButton
className="media-control-button"
pressable={true}
label="Show {nth(i)} media"
pressed={i === scrolledItem}
href={i === scrolledItem ? '#fa-circle' : '#fa-circle-o'}
sameColorWhenPressed={true}
on:click="onClick(i)"
/>
{/each}
<IconButton
className="media-control-button"
disabled={scrolledItem === length - 1}
label="Show next media"
href="#fa-angle-right"
on:click="next()"
/>
</div>
{/if}
<IconButton
className="media-control-button"
pressable={true}
pressed={pinchZoomMode}
label={pinchZoomMode ? 'Disable pinch-zoom mode' : 'Enable pinch-zoom mode'}
href="#fa-search"
on:click="togglePinchZoomMode()"
/>
</div>
</div>
</ModalDialog>
<Shortcut scope='mediaDialog' key="ArrowLeft" on:pressed="prev()" />
<Shortcut scope='mediaDialog' key="ArrowRight" on:pressed="next()" />
<style>
:global(.media-modal-dialog) {
max-width: calc(100vw);
}
.media-container {
height: calc(100% - 64px); /* 44px X button height + 20px padding */
width: calc(100vw);
padding-top: 10px;
display: flex;
flex-direction: column;
}
.media-scroll {
-webkit-overflow-scrolling: touch;
display: flex;
align-items: center;
overflow-x: auto;
width: 100%;
flex: 1;
scrollbar-width: none;
}
.media-scroll::-webkit-scrollbar {
display: none;
}
.media-scroll-item {
height: 100%;
}
.media-scroll-item-inner {
width: 100vw;
height: 100%;
overflow: hidden;
}
.media-scroll-item-inner-inner {
height: calc(100% - 10px);
width: calc(100% - 10px);
padding: 5px;
}
.media-controls-outside {
display: flex;
justify-content: space-between;
margin: 10px;
}
.media-controls {
display: flex;
justify-content: space-between;
}
:global(.media-pinch-zoom) {
width: 100%;
height: 100%;
}
:global(.media-control-button-dummy-spacer) {
visibility: hidden;
}
@media (min-width: 768px) {
:global(.media-control-button) {
margin: 0 5px;
}
}
@media (max-width: 320px) {
:global(.icon-button.media-control-button) {
padding-left: 5px;
padding-right: 5px;
}
}
@supports (scroll-snap-align: start) {
/* modern scroll snap points */
.media-scroll {
scroll-snap-type: x mandatory;
}
.media-scroll-item {
scroll-snap-align: center;
}
}
@supports not (scroll-snap-align: start) {
/* old scroll snap points spec */
.media-scroll {
-webkit-scroll-snap-type: mandatory;
scroll-snap-type: mandatory;
-webkit-scroll-snap-destination: 0% center;
scroll-snap-destination: 0% center;
-webkit-scroll-snap-points-x: repeat(100%);
scroll-snap-points-x: repeat(100%);
}
}
</style>
<script>
import ModalDialog from './ModalDialog.html'
import MediaInDialog from './MediaInDialog.html'
import IconButton from '../../IconButton.html'
import Shortcut from '../../shortcut/Shortcut.html'
import PinchZoomable from './PinchZoomable.html'
import { show } from '../helpers/showDialog'
import { oncreate as onCreateDialog } from '../helpers/onCreateDialog'
import debounce from 'lodash-es/debounce'
import times from 'lodash-es/times'
import { smoothScroll } from '../../../_utils/smoothScroll'
import { doubleRAF } from '../../../_utils/doubleRAF'
import { store } from '../../../_store/store'
import {
pushShortcutScope,
popShortcutScope } from '../../../_utils/shortcuts'
export default {
oncreate () {
onCreateDialog.call(this)
this.onScroll = debounce(this.onScroll.bind(this), 100, { leading: false, trailing: true })
let { scrolledItem } = this.get()
if (scrolledItem) {
doubleRAF(() => {
this.scrollToItem(scrolledItem, false)
this.setupScroll()
})
} else {
this.setupScroll()
}
pushShortcutScope('mediaDialog')
},
ondestroy () {
this.teardownScroll()
popShortcutScope('mediaDialog')
},
store: () => store,
data: () => ({
pinchZoomMode: false
}),
computed: {
length: ({ mediaItems }) => mediaItems.length,
dots: ({ length }) => times(length, i => ({ i }))
},
components: {
ModalDialog,
MediaInDialog,
IconButton,
Shortcut,
PinchZoomable
},
helpers: {
nth (i) {
switch (i) {
case 0:
return 'first'
case 1:
return 'second'
case 2:
return 'third'
case 3:
return 'fourth'
}
}
},
methods: {
show,
setupScroll () {
this.refs.scroller.addEventListener('scroll', this.onScroll)
},
teardownScroll () {
this.refs.scroller.removeEventListener('scroll', this.onScroll)
},
onScroll () {
let { length } = this.get()
let { scrollWidth, scrollLeft } = this.refs.scroller
let scrolledItem = Math.round((scrollLeft / scrollWidth) * length)
this.set({ scrolledItem })
},
onClick (i) {
let { scrolledItem } = this.get()
if (scrolledItem !== i) {
this.scrollToItem(i, true)
}
},
next () {
let { scrolledItem, length } = this.get()
if (scrolledItem < length - 1) {
this.scrollToItem(scrolledItem + 1, true)
}
},
prev () {
let { scrolledItem } = this.get()
if (scrolledItem > 0) {
this.scrollToItem(scrolledItem - 1, true)
}
},
scrollToItem (i, smooth) {
let { length } = this.get()
let { scroller } = this.refs
let { scrollWidth } = scroller
let scrollLeft = Math.floor(scrollWidth * (i / length))
if (smooth) {
smoothScroll(scroller, scrollLeft, true)
} else {
scroller.scrollLeft = scrollLeft
}
},
togglePinchZoomMode () {
this.set({ pinchZoomMode: !this.get().pinchZoomMode })
}
}
}
</script>