improve images modal

improve the modal by:
 - allowing the navigation of images
 - making sure that the whole image is visible
 - adding controls
 - extracting the modal as a standalone component
pull/162/head
Dario Piotrowicz 2023-01-31 18:50:16 +00:00
rodzic d77df85b6d
commit 88f6dbf676
4 zmienionych plików z 83 dodań i 42 usunięć

Wyświetl plik

@ -1,8 +1,9 @@
import { component$, useStore, $ } from '@builder.io/qwik'
import { component$, useStore, PropFunction } from '@builder.io/qwik'
import { MediaAttachment } from '~/types'
type Props = {
mediaAttachment: MediaAttachment
onOpenImagesModal$: PropFunction<(id: string) => void>
}
export const focusToObjectFit = (focus: { x: number; y: number }) => {
@ -15,7 +16,7 @@ export const focusToObjectFit = (focus: { x: number; y: number }) => {
return { x: Math.floor(x2 * 100) / 100, y: Math.floor(y2 * 100) / 100 }
}
export default component$<Props>(({ mediaAttachment }) => {
export default component$<Props>(({ mediaAttachment, onOpenImagesModal$ }) => {
const store = useStore({
isModalOpen: false,
})
@ -25,35 +26,17 @@ export default component$<Props>(({ mediaAttachment }) => {
objectFit = focusToObjectFit(mediaAttachment.meta.focus)
}
const onPreviewClick = $(() => {
document.body.style.overflowY = 'hidden'
store.isModalOpen = true
})
const onModalClose = $(() => {
document.body.style.overflowY = 'scroll'
store.isModalOpen = false
})
return (
<>
<div class={`${store.isModalOpen ? '' : 'cursor-zoom-in'} w-full h-full`}>
<img
class="object-cover w-full h-full rounded"
class="object-cover w-full h-full rounded cursor-pointer"
style={{
...(objectFit && { 'object-position': `${objectFit.x}% ${objectFit.y}%` }),
}}
src={mediaAttachment.preview_url || mediaAttachment.url}
onClick$={onPreviewClick}
onClick$={() => onOpenImagesModal$(mediaAttachment.id)}
/>
{store.isModalOpen && (
<div class="relative pointer-events-auto z-50">
<div class="overlay inset-0 fixed z-60 bg-black opacity-70"></div>
<div class="fixed z-70 inset-0 grid place-items-center" onClick$={onModalClose}>
<img src={mediaAttachment.url} />
</div>
</div>
)}
</div>
</>
)

Wyświetl plik

@ -0,0 +1,46 @@
import { component$, useSignal, PropFunction } from '@builder.io/qwik'
import { MediaAttachment } from '~/types'
type Props = {
images: MediaAttachment[]
idxOfCurrentImage: number
onCloseImagesModal$: PropFunction<() => void>
}
export const ImagesModal = component$<Props>(({ images, idxOfCurrentImage: initialIdx, onCloseImagesModal$ }) => {
const idxOfCurrentImage = useSignal(initialIdx)
return (
<div class="pointer-events-auto cursor-default z-50 fixed inset-0 isolate flex items-center justify-between backdrop-blur-sm">
<div class="inset-0 absolute z-[-1] bg-wildebeest-900 opacity-70" onClick$={() => onCloseImagesModal$()}></div>
{images.length > 1 && (
<button
class="cursor-pointer text-4xl opacity-60 hover:opacity-90 focus-visible:opacity-90"
onClick$={() => {
const idx = idxOfCurrentImage.value - 1
idxOfCurrentImage.value = idx < 0 ? images.length - 1 : idx
}}
>
<i class="fa-solid fa-chevron-left ml-5"></i>
</button>
)}
<img class="ma max-w-[80vw] max-h-[90vh] m-auto" src={images[idxOfCurrentImage.value].url} />
{images.length > 1 && (
<button
class="cursor-pointer text-4xl opacity-60 hover:opacity-90 focus-visible:opacity-90"
onClick$={() => {
idxOfCurrentImage.value = (idxOfCurrentImage.value + 1) % images.length
}}
>
<i class="fa-solid fa-chevron-right mr-5"></i>
</button>
)}
<button
class="cursor-pointer absolute top-7 right-7 text-4xl opacity-60 hover:opacity-90 focus-visible:opacity-90"
onClick$={() => onCloseImagesModal$()}
>
<i class="fa-solid fa-xmark"></i>
</button>
</div>
)
})

Wyświetl plik

@ -1,17 +0,0 @@
import { component$ } from '@builder.io/qwik'
import Image from './Image'
import Video from './Video'
import { MediaAttachment } from '~/types'
type Props = {
mediaAttachment: MediaAttachment
}
export default component$<Props>(({ mediaAttachment }) => {
return (
<>
{mediaAttachment.type === 'image' ? <Image mediaAttachment={mediaAttachment} /> : ''}
{mediaAttachment.type === 'video' ? <Video mediaAttachment={mediaAttachment} /> : ''}
</>
)
})

Wyświetl plik

@ -1,7 +1,9 @@
import { component$, useStylesScoped$ } from '@builder.io/qwik'
import { component$, useStylesScoped$, $, useStore } from '@builder.io/qwik'
import { MediaAttachment } from '~/types'
import Media from './Media'
import Image from './Image'
import Video from './Video'
import styles from './index.scss?inline'
import { ImagesModal } from './ImagesModal'
type Props = {
medias: MediaAttachment[]
@ -10,17 +12,44 @@ type Props = {
export const MediaGallery = component$<Props>(({ medias }) => {
useStylesScoped$(styles)
const images = medias.filter((media) => media.type === 'image')
const imagesModalState = useStore<{ isOpen: boolean; idxOfCurrentImage: number }>({
isOpen: false,
idxOfCurrentImage: 0,
})
const onOpenImagesModal = $((imgId: string) => {
document.body.style.overflowY = 'hidden'
imagesModalState.isOpen = true
const idx = images.findIndex(({ id }) => id === imgId)
imagesModalState.idxOfCurrentImage = idx === -1 ? 0 : idx
})
const onCloseImagesModal = $(() => {
document.body.style.overflowY = 'scroll'
imagesModalState.isOpen = false
})
return (
<>
{!!medias.length && (
<div class={`media-gallery overflow-hidden grid gap-1 h-52 md:h-60 lg:h-72 xl:h-80`}>
{medias.map((media) => (
<div class="w-full flex items-center justify-center overflow-hidden bg-black">
<Media mediaAttachment={media} />
{media.type === 'image' && <Image mediaAttachment={media} onOpenImagesModal$={onOpenImagesModal} />}
{media.type === 'video' && <Video mediaAttachment={media} />}
</div>
))}
</div>
)}
{imagesModalState.isOpen && (
<ImagesModal
images={images}
idxOfCurrentImage={imagesModalState.idxOfCurrentImage}
onCloseImagesModal$={onCloseImagesModal}
/>
)}
</>
)
})