Add first lightbox tests
This commit is contained in:
@ -1,5 +1,12 @@
|
|||||||
<script lang="ts">
|
<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. */
|
/** The album. */
|
||||||
export let album: Album;
|
export let album: Album;
|
||||||
@ -9,12 +16,111 @@
|
|||||||
|
|
||||||
/** Base URL */
|
/** Base URL */
|
||||||
export let base: string;
|
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>
|
</script>
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
<ul>
|
<ul bind:this={container}>
|
||||||
{#each (items || album.items) as item (item.item)}
|
{#each items || album.items as item, i (item.item)}
|
||||||
<Photo {base} {album} {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}
|
{/each}
|
||||||
</ul>
|
</ul>
|
||||||
</section>
|
</section>
|
||||||
@ -37,4 +143,135 @@
|
|||||||
list-style: none;
|
list-style: none;
|
||||||
gap: 1em;
|
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>
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user