diff options
Diffstat (limited to 'packages/client/src/components/media-list.vue')
| -rw-r--r-- | packages/client/src/components/media-list.vue | 167 |
1 files changed, 167 insertions, 0 deletions
diff --git a/packages/client/src/components/media-list.vue b/packages/client/src/components/media-list.vue new file mode 100644 index 0000000000..51eaa86f35 --- /dev/null +++ b/packages/client/src/components/media-list.vue @@ -0,0 +1,167 @@ +<template> +<div class="hoawjimk"> + <XBanner v-for="media in mediaList.filter(media => !previewable(media))" :media="media" :key="media.id"/> + <div v-if="mediaList.filter(media => previewable(media)).length > 0" class="gird-container"> + <div :data-count="mediaList.filter(media => previewable(media)).length" ref="gallery"> + <template v-for="media in mediaList"> + <XVideo :video="media" :key="media.id" v-if="media.type.startsWith('video')"/> + <XImage class="image" :data-id="media.id" :image="media" :key="media.id" v-else-if="media.type.startsWith('image')" :raw="raw"/> + </template> + </div> + </div> +</div> +</template> + +<script lang="ts"> +import { defineComponent, onMounted, PropType, ref } from 'vue'; +import * as misskey from 'misskey-js'; +import PhotoSwipeLightbox from 'photoswipe/dist/photoswipe-lightbox.esm.js'; +import PhotoSwipe from 'photoswipe/dist/photoswipe.esm.js'; +import 'photoswipe/dist/photoswipe.css'; +import XBanner from './media-banner.vue'; +import XImage from './media-image.vue'; +import XVideo from './media-video.vue'; +import * as os from '@/os'; +import { defaultStore } from '@/store'; + +export default defineComponent({ + components: { + XBanner, + XImage, + XVideo, + }, + props: { + mediaList: { + type: Array as PropType<misskey.entities.DriveFile[]>, + required: true, + }, + raw: { + default: false + }, + }, + setup(props) { + const gallery = ref(null); + + onMounted(() => { + const lightbox = new PhotoSwipeLightbox({ + dataSource: props.mediaList.filter(media => media.type.startsWith('image')).map(media => ({ + src: media.url, + w: media.properties.width, + h: media.properties.height, + alt: media.name, + })), + gallery: gallery.value, + children: '.image', + thumbSelector: '.image', + pswpModule: PhotoSwipe + }); + + lightbox.on('itemData', (e) => { + const { itemData } = e; + + // element is children + const { element } = itemData; + + const id = element.dataset.id; + const file = props.mediaList.find(media => media.id === id); + + itemData.src = file.url; + itemData.w = Number(file.properties.width); + itemData.h = Number(file.properties.height); + itemData.msrc = file.thumbnailUrl; + itemData.thumbCropped = true; + }); + + lightbox.init(); + }); + + const previewable = (file: misskey.entities.DriveFile): boolean => { + return file.type.startsWith('video') || file.type.startsWith('image'); + }; + + return { + previewable, + gallery, + }; + }, +}); +</script> + +<style lang="scss" scoped> +.hoawjimk { + > .gird-container { + position: relative; + width: 100%; + margin-top: 4px; + + &:before { + content: ''; + display: block; + padding-top: 56.25% // 16:9; + } + + > div { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + display: grid; + grid-gap: 4px; + + > * { + overflow: hidden; + border-radius: 6px; + } + + &[data-count="1"] { + grid-template-rows: 1fr; + } + + &[data-count="2"] { + grid-template-columns: 1fr 1fr; + grid-template-rows: 1fr; + } + + &[data-count="3"] { + grid-template-columns: 1fr 0.5fr; + grid-template-rows: 1fr 1fr; + + > *:nth-child(1) { + grid-row: 1 / 3; + } + + > *:nth-child(3) { + grid-column: 2 / 3; + grid-row: 2 / 3; + } + } + + &[data-count="4"] { + grid-template-columns: 1fr 1fr; + grid-template-rows: 1fr 1fr; + } + + > *:nth-child(1) { + grid-column: 1 / 2; + grid-row: 1 / 2; + } + + > *:nth-child(2) { + grid-column: 2 / 3; + grid-row: 1 / 2; + } + + > *:nth-child(3) { + grid-column: 1 / 2; + grid-row: 2 / 3; + } + + > *:nth-child(4) { + grid-column: 2 / 3; + grid-row: 2 / 3; + } + } + } +} +</style> |