Add first lightbox tests

This commit is contained in:
Luca Bosin
2023-08-28 22:45:11 +02:00
parent d5fc51eb6e
commit bdd5e47ae4

View File

@ -1,5 +1,12 @@
<script lang="ts">
import Photo from './Photo.svelte';
import { createEventDispatcher } from 'svelte';
import { fade } from 'svelte/transition';
import { flip } from 'svelte/animate';
import { getFileName } from '$lib/util/links';
import { str, strf } from '$lib/data/language';
import Icon from './Icon.svelte';
const dispatch = createEventDispatcher();
/** The album. */
export let album: Album;
@ -9,12 +16,111 @@
/** Base URL */
export let base: string;
/** The gallery container. */
let container: HTMLUListElement;
/** The test item. */
let testItem: Item | undefined = undefined;
/** The click handler for the image. */
async function clickHandler(event: MouseEvent, i: number) {
const item = items ? items[i] : album.items[i];
if (!event.shiftKey && !event.ctrlKey) {
event.preventDefault();
if (!testItem) {
testItem = item;
window.addEventListener('keydown', escHandler);
dispatch('lightbox', { album, item });
} else {
testItem = undefined;
window.removeEventListener('keydown', escHandler);
dispatch('lightbox-close', { album, item });
}
} else {
dispatch('open', { album, item });
}
}
/** The right click handler for the image. */
async function rightClickHandler(event: MouseEvent) {
console.log('right click', event);
}
/** The arrow key handler for the gallery. */
async function arrowKeyHandler(event: KeyboardEvent, i: number) {
if (event.key === 'ArrowLeft') {
container.children[i - 1]?.querySelector('a')?.focus();
} else if (event.key === 'ArrowRight') {
container.children[i + 1]?.querySelector('a')?.focus();
}
}
/** The escape handler for the viewbox. */
async function escHandler(event: KeyboardEvent) {
if (event.key === 'Escape') {
testItem = undefined;
window.removeEventListener('keydown', escHandler);
}
}
const thumbnail = (item: Item, size: number | 's' | 'm' | 'l' | 'full' = 's') => `${base}/t/${size}/${item.item}`;
function getDetails(item: Item) {
const filename = getFileName(item.item);
const titleOrNull = item.title ? $strf(item.title) : null;
const descriptionOrNull = item.description ? $strf(item.description) : null;
const title = titleOrNull || filename;
const alt = titleOrNull || descriptionOrNull || filename;
const author = item.authors ? (
Array.isArray(item.authors) ?
item.authors[0]:
typeof item.authors === 'string' ?
item.authors:
"unknown author"):
album.authors ? (
Array.isArray(album.authors) ?
album.authors[0]:
typeof album.authors === 'string' ?
album.authors:
"unknown author"):
"Luca Bosin";
const thumbnails = [
[0, thumbnail(item, 's')],
[1600, thumbnail(item, 'm')],
[2560, thumbnail(item, 'l')]
];
const href = `${base}/i/${item.item}`;
return { filename, title, alt, author, thumbnails, href };
}
</script>
<section>
<ul>
{#each (items || album.items) as item (item.item)}
<Photo {base} {album} {item} />
<ul bind:this={container}>
{#each items || album.items as item, i (item.item)}
{@const details = getDetails(item)}
{@const numThumbnails = details.thumbnails.length}
<li class:test={testItem == item} aria-label={details.title} aria-details={details.alt} animate:flip={{duration: 700}}>
<figure style:--image="url('{thumbnail(item, 3)}')">
<picture>
{#each {length: numThumbnails} as _, j (j)}
{@const [size, thumbnail] = details.thumbnails[numThumbnails - 1 - j]}
{#if j === numThumbnails - 1}
<img src={`${thumbnail}`} alt={details.alt} loading={i > 5 ? 'lazy' : 'eager'} title={details.title} />
{:else}
<source media="(min-width: {size}px)" srcset={`${thumbnail}`} />
{/if}
{/each}
</picture>
<figcaption>
<strong>{details.title}</strong>
<cite>{details.author}</cite>
</figcaption>
</figure>
<a href={details.href} tabindex="0" aria-label={$str('open-name', details.title)} on:click={e => clickHandler(e, i)} on:contextmenu|preventDefault={rightClickHandler} on:keydown={e => arrowKeyHandler(e, i)}>
<Icon class="icon" mdi="arrow-expand" size={2.25}/>
</a>
</li>
{/each}
</ul>
</section>
@ -37,4 +143,135 @@
list-style: none;
gap: 1em;
}
li {
position: relative;
height: fit-content;
border-radius: 10px;
transition: border-radius .5s;
}
figure {
width: var(--image-width, 28.125em);
height: var(--image-height, 18.75em);
background-position: center;
background-size: cover;
overflow: hidden;
border-radius: 10px;
transition: background-color .5s, background-image .5s;
margin: 0 auto;
}
li:not(.test) figure {
background-color: rgba(0,0,0,.25);
background-image: linear-gradient(rgba(0,0,0,.125), rgba(0,0,0,.25)), var(--image);
}
picture {
position: relative;
}
img {
width: var(--image-width, 28.125em);
height: var(--image-height, 18.75em);
object-fit: contain;
object-position: center;
top: 0;
border-radius: 10px;
overflow: hidden;
clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%);
transform: scale(1.001);
transition: transform .5s cubic-bezier(.23,.13,.45,.97), clip-path .5s cubic-bezier(.23,.13,.45,.97);
}
li:not(.test) img {
position: absolute;
}
figcaption {
display: grid;
position: absolute;
bottom: 0;
left: 0;
width: 100%;
padding: 1.5em;
opacity: 0;
transition: opacity .5s, background-color .5s;
z-index: 1;
gap: .25em;
pointer-events: none;
}
strong {
font-size: 1.25em;
letter-spacing: .05em;
}
cite {
font-size: .75em;
font-weight: normal;
font-style: normal;
opacity: .8;
}
a {
display: grid;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: transparent;;
color: transparent;
text-decoration: none;
transition: background-color .5s, color .5s;
justify-content: center;
align-content: center;
border-radius: 10px;
overflow: hidden;
}
li:not(.test) a:focus {
outline: 2px solid var(--main-text-color);
}
li:not(.test):hover img {
transform: scale(1.25);
}
li:hover figcaption {
opacity: 1;
}
li:hover a {
background-color: rgba(0,0,0,.75);
color: inherit;
}
li.test {
--image-width: 100%;
--image-height: 100vh;
z-index: 1;
position: fixed;
border-radius: 0;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
}
li.test figure {
background-color: rgba(0,0,0,0);
background-image: none;
}
li.test figcaption {
background-color: rgba(0,0,0,.75);
opacity: 1;
}
li.test a {
background-color: rgba(0,0,0,.75);
z-index: -1;
}
</style>