2020-01-29 20:37:25 +01:00
< template >
2023-04-08 02:01:42 +02:00
< div class = "hoawjimk" >
< XBanner
v - for = "media in mediaList.filter((media) => !previewable(media))"
: key = "media.id"
: media = "media"
/ >
< div
v - if = "mediaList.filter((media) => previewable(media)).length > 0"
class = "gird-container"
: class = "{ dmWidth: inDm }"
>
< div
ref = "gallery"
: data - count = "
mediaList . filter ( ( media ) => previewable ( media ) ) . length
"
2023-05-08 21:37:28 +02:00
@ click . stop
2023-04-08 02:01:42 +02:00
>
< template
v - for = " media in mediaList . filter ( ( media ) =>
previewable ( media )
) "
>
< XVideo
v - if = "media.type.startsWith('video')"
: key = "media.id"
: video = "media"
/ >
< XImage
v - else - if = "media.type.startsWith('image')"
: key = "media.id"
class = "image"
: data - id = "media.id"
: image = "media"
: raw = "raw"
/ >
< / template >
< / div >
2020-01-29 20:37:25 +01:00
< / div >
< / div >
< / template >
2022-01-27 16:52:05 +01:00
< script lang = "ts" setup >
2023-04-08 02:01:42 +02:00
import { onMounted , ref } from "vue" ;
import * as misskey from "calckey-js" ;
import PhotoSwipeLightbox from "photoswipe/lightbox" ;
import PhotoSwipe from "photoswipe" ;
import "photoswipe/style.css" ;
import XBanner from "@/components/MkMediaBanner.vue" ;
import XImage from "@/components/MkMediaImage.vue" ;
import XVideo from "@/components/MkMediaVideo.vue" ;
import * as os from "@/os" ;
import { FILE _TYPE _BROWSERSAFE } from "@/const" ;
import { defaultStore } from "@/store" ;
2020-01-29 20:37:25 +01:00
2022-01-27 16:52:05 +01:00
const props = defineProps < {
mediaList : misskey . entities . DriveFile [ ] ;
raw ? : boolean ;
2022-11-15 05:25:59 +01:00
inDm ? : boolean ;
2022-01-27 16:52:05 +01:00
} > ( ) ;
2021-07-13 17:13:23 +02:00
2022-01-27 16:52:05 +01:00
const gallery = ref ( null ) ;
2023-04-08 02:01:42 +02:00
const pswpZIndex = os . claimZIndex ( "middle" ) ;
2021-10-24 19:28:18 +02:00
2022-01-27 16:52:05 +01:00
onMounted ( ( ) => {
const lightbox = new PhotoSwipeLightbox ( {
dataSource : props . mediaList
2023-04-08 02:01:42 +02:00
. filter ( ( media ) => {
if ( media . type === "image/svg+xml" ) return true ; // svgのwebpublicはpngなのでtrue
return (
media . type . startsWith ( "image" ) &&
FILE _TYPE _BROWSERSAFE . includes ( media . type )
) ;
2022-01-27 16:52:05 +01:00
} )
2023-04-08 02:01:42 +02:00
. map ( ( media ) => {
2022-01-27 16:52:05 +01:00
const item = {
src : media . url ,
w : media . properties . width ,
h : media . properties . height ,
2023-04-05 08:58:55 +02:00
alt : media . comment ,
2022-01-27 16:52:05 +01:00
} ;
2023-04-08 02:01:42 +02:00
if (
media . properties . orientation != null &&
media . properties . orientation >= 5
) {
2022-01-27 16:52:05 +01:00
[ item . w , item . h ] = [ item . h , item . w ] ;
}
return item ;
} ) ,
gallery : gallery . value ,
2023-04-08 02:01:42 +02:00
children : ".image" ,
thumbSelector : ".image" ,
2022-01-27 16:52:05 +01:00
loop : false ,
2023-04-08 02:01:42 +02:00
padding :
window . innerWidth > 500
? {
top : 32 ,
bottom : 32 ,
left : 32 ,
right : 32 ,
}
: {
top : 0 ,
bottom : 0 ,
left : 0 ,
right : 0 ,
} ,
imageClickAction : "close" ,
tapAction : "toggle-controls" ,
2023-05-17 18:34:45 +02:00
preloadFirstSlide : false ,
2022-01-27 16:52:05 +01:00
pswpModule : PhotoSwipe ,
} ) ;
2021-07-13 17:13:23 +02:00
2023-04-08 02:01:42 +02:00
lightbox . on ( "itemData" , ( ev ) => {
2022-01-27 16:52:05 +01:00
const { itemData } = ev ;
2021-07-13 17:13:23 +02:00
2022-01-27 16:52:05 +01:00
// element is children
const { element } = itemData ;
2021-10-24 19:28:18 +02:00
2022-01-27 16:52:05 +01:00
const id = element . dataset . id ;
2023-04-08 02:01:42 +02:00
const file = props . mediaList . find ( ( media ) => media . id === id ) ;
2021-10-24 19:28:18 +02:00
2022-01-27 16:52:05 +01:00
itemData . src = file . url ;
itemData . w = Number ( file . properties . width ) ;
itemData . h = Number ( file . properties . height ) ;
2023-04-08 02:01:42 +02:00
if (
file . properties . orientation != null &&
file . properties . orientation >= 5
) {
2022-01-27 16:52:05 +01:00
[ itemData . w , itemData . h ] = [ itemData . h , itemData . w ] ;
}
itemData . msrc = file . thumbnailUrl ;
2023-04-05 08:58:55 +02:00
itemData . alt = file . comment ;
2022-01-27 16:52:05 +01:00
itemData . thumbCropped = true ;
} ) ;
2021-10-24 19:28:18 +02:00
2023-04-08 02:01:42 +02:00
lightbox . on ( "uiRegister" , ( ) => {
2022-09-29 06:21:07 +02:00
lightbox . pswp . ui . registerElement ( {
2023-04-08 02:01:42 +02:00
name : "altText" ,
className : "pwsp__alt-text-container" ,
appendTo : "wrapper" ,
2022-09-29 06:21:07 +02:00
onInit : ( el , pwsp ) => {
2023-04-08 02:01:42 +02:00
let textBox = document . createElement ( "p" ) ;
textBox . className = "pwsp__alt-text" ;
2022-09-29 06:21:07 +02:00
el . appendChild ( textBox ) ;
2023-04-08 02:01:42 +02:00
let preventProp = function ( ev : Event ) : void {
2023-04-05 08:58:55 +02:00
ev . stopPropagation ( ) ;
} ;
// Allow scrolling/text selection
el . onwheel = preventProp ;
el . onclick = preventProp ;
el . onpointerdown = preventProp ;
el . onpointercancel = preventProp ;
el . onpointermove = preventProp ;
2023-04-08 02:01:42 +02:00
pwsp . on ( "change" , ( ) => {
2023-04-05 08:58:55 +02:00
textBox . textContent = pwsp . currSlide . data . alt ? . trim ( ) ;
2022-09-29 06:21:07 +02:00
} ) ;
} ,
} ) ;
} ) ;
2023-05-17 18:34:45 +02:00
lightbox . on ( "afterInit" , ( ) => {
history . pushState ( null , "" , location . href ) ;
addEventListener ( "popstate" , close ) ;
2023-05-17 19:46:00 +02:00
// This is a workaround. Not sure why, but when clicking to open, it doesn't move focus to the photoswipe. Preventing using esc to close. However when using keyboard to open it already focuses the lightbox fine.
lightbox . pswp . element . focus ( ) ;
2023-05-17 18:34:45 +02:00
} )
lightbox . on ( "close" , ( ) => {
removeEventListener ( "popstate" , close ) ;
history . back ( ) ;
} )
2022-01-27 16:52:05 +01:00
lightbox . init ( ) ;
2023-05-17 18:34:45 +02:00
function close ( ) {
removeEventListener ( "popstate" , close ) ;
history . forward ( ) ;
lightbox . pswp . close ( ) ;
}
2020-01-29 20:37:25 +01:00
} ) ;
2022-01-27 16:52:05 +01:00
const previewable = ( file : misskey . entities . DriveFile ) : boolean => {
2023-04-08 02:01:42 +02:00
if ( file . type === "image/svg+xml" ) return true ; // svgのwebpublic/thumbnailはpngなのでtrue
2022-01-27 16:52:05 +01:00
// FILE_TYPE_BROWSERSAFEに適合しないものはブラウザで表示するのに不適切
2023-04-08 02:01:42 +02:00
return (
( file . type . startsWith ( "video" ) || file . type . startsWith ( "image" ) ) &&
FILE _TYPE _BROWSERSAFE . includes ( file . type )
) ;
2022-01-27 16:52:05 +01:00
} ;
2020-01-29 20:37:25 +01:00
< / script >
2023-04-05 08:58:55 +02:00
< style lang = "scss" scoped >
. hoawjimk {
> . dmWidth {
min - width : 20 rem ;
max - width : 40 rem ;
2022-11-15 05:25:59 +01:00
}
2023-04-05 08:58:55 +02:00
> . gird - container {
position : relative ;
width : 100 % ;
margin - top : 4 px ;
border - radius : var ( -- radius ) ;
overflow : hidden ;
2023-05-08 21:39:20 +02:00
pointer - events : none ;
2023-04-05 08:58:55 +02:00
& : before {
2023-04-08 02:01:42 +02:00
content : "" ;
2023-04-05 08:58:55 +02:00
display : block ;
2023-04-08 02:01:42 +02:00
padding - top : 56.25 % ; // 16:9;
2020-01-29 20:37:25 +01:00
}
2023-04-05 08:58:55 +02:00
> div {
position : absolute ;
top : 0 ;
right : 0 ;
bottom : 0 ;
left : 0 ;
display : grid ;
grid - gap : 8 px ;
> * {
overflow : hidden ;
border - radius : 6 px ;
2023-05-08 21:39:20 +02:00
pointer - events : all ;
2023-04-05 08:58:55 +02:00
}
& [ data - count = "1" ] {
grid - template - rows : 1 fr ;
}
& [ data - count = "2" ] {
grid - template - columns : 1 fr 1 fr ;
grid - template - rows : 1 fr ;
}
& [ data - count = "3" ] {
grid - template - columns : 1 fr 0.5 fr ;
grid - template - rows : 1 fr 1 fr ;
> * : nth - child ( 1 ) {
grid - row : 1 / 3 ;
}
2023-04-05 06:47:30 +02:00
2023-04-05 08:58:55 +02:00
> * : nth - child ( 3 ) {
grid - column : 2 / 3 ;
grid - row : 2 / 3 ;
}
}
& [ data - count = "4" ] {
grid - template - columns : 1 fr 1 fr ;
grid - template - rows : 1 fr 1 fr ;
}
> * : 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 ;
}
2020-01-29 20:37:25 +01:00
}
}
}
< / style >
2021-12-23 17:07:04 +01:00
< style lang = "scss" >
2023-04-05 08:58:55 +02:00
. pswp {
// なぜか機能しない
2023-04-08 02:01:42 +02:00
//z-index: v-bind(pswpZIndex);
2023-04-05 08:58:55 +02:00
z - index : 2000000 ;
2021-12-23 17:07:04 +01:00
}
2022-09-29 06:21:07 +02:00
. pwsp _ _alt - text - container {
display : flex ;
flex - direction : row ;
align - items : center ;
position : absolute ;
2023-04-05 08:58:55 +02:00
bottom : 30 px ;
2022-09-29 06:21:07 +02:00
left : 50 % ;
transform : translateX ( - 50 % ) ;
width : 75 % ;
}
. pwsp _ _alt - text {
2023-04-05 08:58:55 +02:00
color : white ;
2022-09-29 06:21:07 +02:00
margin : 0 auto ;
text - align : center ;
2023-04-05 08:58:55 +02:00
padding : 10 px ;
background : rgba ( 0 , 0 , 0 , 0.5 ) ;
border - radius : 5 px ;
max - height : 10 vh ;
overflow - x : clip ;
2022-09-29 06:21:07 +02:00
overflow - y : auto ;
2023-04-05 08:58:55 +02:00
overscroll - behavior : contain ;
}
. pwsp _ _alt - text : empty {
display : none ;
2022-09-29 06:21:07 +02:00
}
2021-12-23 17:07:04 +01:00
< / style >