Add style for photos

This commit is contained in:
Luca Bosin
2023-08-24 23:11:50 +02:00
parent ca90df83e9
commit c3e571fcb8
8 changed files with 180 additions and 50 deletions

View File

@ -1,39 +1,31 @@
<script> <script>
import { getFileName } from '$lib/util/links';
import { strf } from '$lib/data/language.js';
import Photo from './Photo.svelte'; import Photo from './Photo.svelte';
/** @type {Item[]} */ /** @type {Album} */
export let items = []; export let album;
/** @type {Item[]?} */
export let items = null;
export let base = ''; export let base = '';
export let layout = 'grid';
</script> </script>
<section class="gallery" <section>
class:gallery--grid={layout === 'grid'}> <ul>
{#if layout === 'grid'} {#each (items || album.items) as item (item.item)}
<ul> <Photo src={`${base}${item.item}`} {album} {item} />
{#each items as item (item.item)}
<!-- <Photo src={`${uriBase}&item=${item.item}`} alt={$strf(item.title)} /> -->
<li>
<h3>{item.item}</h3>
<p>
<b>Title:</b> {#if item.title}{$strf(item.title)}{:else}<i>no title</i>{/if}<br />
<b>Description:</b> {#if item.description}{$strf(item.description)}{:else}<i>no description</i>{/if}<br />
<b>Links:</b> <a href={`${base}${item.item}`}>Open</a> &bullet; <a href={`${base}${item.item}/download`}>Download</a><br />
<b>Thumbnails:</b> <a href={`${base}${item.item}/t/s`}>Small</a> &bullet; <a href={`${base}${item.item}/t/m`}>Medium</a> &bullet; <a href={`${base}${item.item}/t/l`}>Large</a> &bullet; <a href={`${base}${item.item}/t/full`}>Full</a>
</p>
<Photo src={`${base}${item.item}`} alt={(item.title ? $strf(item.title) : null) || (item.description ? $strf(item.description) : null) || getFileName(item.item)} />
</li>
{/each} {/each}
</ul> </ul>
{:else}
<i>layout not available</i>
{/if}
</section> </section>
<style> <style>
section {
display: block;
}
ul {
display: flex;
list-style: none;
gap: 1em;
}
</style> </style>

View File

@ -11,7 +11,7 @@
<section class="title"> <section class="title">
{#if back} {#if back}
<a href={back}> <a href={back}>
<Icon name="arrow-left" /> <Icon mdi="arrow-left" />
</a> </a>
{/if} {/if}
<h1>{title}</h1> <h1>{title}</h1>

View File

@ -1,10 +1,33 @@
<script> <script>
export let name = ''; export let clazz = '';
name; export { clazz as class };
export let color = 'currentColor';
/** @type {string} */
export let mdi;
/** @type {number | string} */
export let size = 1;
let mdiOld = '';
let path = '';
$: {
loadIcon(mdi);
}
$: width = size ? (typeof size === 'number' || !Number.isNaN(Number(size)) ? `${size}em` : size) : '1em';
/**
* @param {string} mdi
*/
async function loadIcon(mdi) {
if (mdi === mdiOld) return;
path = (await import(`$lib/icons/${mdi}.js`)).default;
//path = icon.path;
}
</script> </script>
<svg class={clazz} viewBox="0 0 24 24" style="width: {width}; height: {width}">
<path fill={color} d={path} />
<style> </svg>
</style>

View File

@ -1,21 +1,28 @@
<script> <script>
import { strf } from "$lib/data/language";
import { getFileName } from "$lib/util/links";
import Icon from "./Icon.svelte";
/** /**
* The source of the image. * The source of the image.
* @type {string} * @type {string}
*/ */
export let src; export let src;
/** @type {Album} */
export let album;
/** /**
* Alternative text for the image. Used for accessibility. * The item.
* @type {string | undefined} * @type {Item}
*/ */
export let alt = undefined; export let item;
/** /**
* Whether the image is the first one in the viewport. * Whether the image is the first one in the viewport.
* @type {boolean} * @type {boolean}
*/ */
export let firstViewport = false; export let lazy = false;
/** /**
* The click handler for the image. * The click handler for the image.
@ -23,18 +30,120 @@
*/ */
async function clickHandler(event) { async function clickHandler(event) {
event.preventDefault(); event.preventDefault();
console.log('click', event);
} }
/**
* The right click handler for the image.
* @param {MouseEvent} event
*/
async function rightClickHandler(event) {
event.preventDefault();
console.log('right click', event);
}
$: filename = getFileName(item.item);
$: alt = (item.title ? $strf(item.title) : null) || (item.description ? $strf(item.description) : null) || filename;
$: title = item.title ? $strf(item.title) : filename;
$: 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";
</script> </script>
<a href={src} on:click={clickHandler}> <li>
<picture> <figure style="--image: url('{src}/t/3')">
<source media="(min-width: 850px)" srcset="{src}/t/l" /> <picture>
<source media="(min-width: 450px)" srcset="{src}/t/m" /> <source media="(min-width: 2560px)" srcset="{src}/t/l" />
<img src="{src}/t" {alt} loading={firstViewport ? 'eager' : 'lazy'} /> <source media="(min-width: 1600px)" srcset="{src}/t/m" />
</picture> <img src="{src}/t" {alt} loading={lazy ? 'lazy' : 'eager'} />
</a> </picture>
<figcaption>
<b>{title}</b>
<cite>{author}</cite>
</figcaption>
</figure>
<a href={src} on:click={clickHandler} on:contextmenu={rightClickHandler}>
<Icon class="icon" mdi="arrow-expand" size={2.25}/>
</a>
</li>
<style> <style>
li {
position: relative;
height: fit-content;
border-radius: 10px;
overflow: hidden;
min-width: 30vw;
}
figure {
width: 30vw;
height: 20vw;
background-color: rgba(0,0,0,.25);
background-image: var(--image);
background-position: center;
background-size: cover;
}
img {
width: 30vw;
height: 20vw;
object-fit: contain;
}
figcaption {
display: grid;
position: absolute;
bottom: 0;
left: 0;
width: 100%;
padding: 1em;
opacity: 0;
transition: opacity .5s;
z-index: 1;
gap: .25em;
pointer-events: none;
}
b {
font-size: 1.25em;
}
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: rgba(0,0,0,.5);
color: inherit;
text-decoration: none;
opacity: 0;
transition: opacity .5s;
justify-content: center;
align-content: center;
}
li:hover :is(figcaption, a) {
opacity: 1;
}
</style> </style>

View File

@ -0,0 +1,2 @@
const path = 'M10,21V19H6.41L10.91,14.5L9.5,13.09L5,17.59V14H3V21H10M14.5,10.91L19,6.41V10H21V3H14V5H17.59L13.09,9.5L14.5,10.91Z';
export default path;

View File

@ -0,0 +1,2 @@
const path = 'M20,11V13H8L13.5,18.5L12.08,19.92L4.16,12L12.08,4.08L13.5,5.5L8,11H20Z';
export default path;

View File

@ -1,5 +1,5 @@
<script> <script>
import { str, strf } from '$lib/data/language.js'; import { strf } from '$lib/data/language.js';
import Header from '$lib/components/Header.svelte'; import Header from '$lib/components/Header.svelte';
import Gallery from '$lib/components/Gallery.svelte'; import Gallery from '$lib/components/Gallery.svelte';
@ -17,4 +17,6 @@
<b>Sorting Date:</b> {data.album.date}<br /> <b>Sorting Date:</b> {data.album.date}<br />
<b>License:</b> {data.album.license}<br /> <b>License:</b> {data.album.license}<br />
</p> </p>
<Gallery items={data.album.items} base={`${data.base}/i/`} /> <main>
<Gallery album={data.album} base={`${data.base}/i/`} />
</main>

View File

@ -20,12 +20,12 @@ export async function GET({ params }) {
} else if (params.width === 'l') { } else if (params.width === 'l') {
width = 1200; width = 1200;
} else if (!Number.isNaN(Number(params.width))) { } else if (!Number.isNaN(Number(params.width))) {
width = Number(width); width = Number(params.width);
} }
} }
thumbnail = thumbnail || await sharp(content).resize(width).webp({ quality: 90 }).toBuffer(); thumbnail = thumbnail || await sharp(content).resize(width).webp({ quality: 90 }).toBuffer();
} catch (err) { } catch (err) {
console.error(`${err.stack}`.replaceAll('/home/sveltekit', '.')); console.error(`${/** @type {Error} */(err).stack}`.replaceAll('/home/sveltekit', '.'));
throw error(500, 'Error getting thumbnail'); throw error(500, 'Error getting thumbnail');
} }
return new Response(thumbnail, { return new Response(thumbnail, {