feat: carousel image display

This commit is contained in:
2025-06-05 17:01:03 +01:00
parent d4a9d5559e
commit 6e45851892
4 changed files with 149 additions and 7 deletions

View File

@@ -0,0 +1,3 @@
<svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10 24H38M38 24L24 10M38 24L24 38" stroke="white" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 228 B

View File

@@ -0,0 +1,134 @@
<script lang="ts">
import arrowRightUrl from "$lib/assets/arrow_right.svg";
import crossUrl from "$lib/assets/cross.svg";
import { onMount } from "svelte";
interface Props {
urls?: string[];
ondelete?: (idx: number) => void;
}
const { urls = [], ondelete }: Props = $props();
let carousel = $state<HTMLDivElement>();
let scrollPosition = $state(0);
let scrollWidth = $state(0);
let clientWidth = $state(0);
function updateScroll() {
scrollPosition = carousel?.scrollLeft || 0;
scrollWidth = carousel?.scrollWidth || 0;
clientWidth = carousel?.clientWidth || 0;
}
onMount(updateScroll);
</script>
<div class="controls">
<div class="carousel" bind:this={carousel} onscroll={updateScroll} onscrollend={updateScroll}>
{#each urls as url, idx (`${idx}|${url}`)}
<div class="item">
<img src={url} alt="carousel item" />
{#if ondelete}
<button class="delete" onclick={() => ondelete(idx)}>
<img src={crossUrl} alt="delete item" />
</button>
{/if}
</div>
{/each}
</div>
{#if scrollPosition > clientWidth / 2}
<button
class="arrow left"
onclick={() => {
if (carousel) carousel.scrollLeft -= carousel.clientWidth;
}}
>
<img src={arrowRightUrl} alt="go to previous" />
</button>
{/if}
{#if scrollPosition < scrollWidth - clientWidth * 1.5}
<button
class="arrow right"
onclick={() => {
if (carousel) carousel.scrollLeft += carousel.clientWidth;
}}
>
<img src={arrowRightUrl} alt="go to next" />
</button>
{/if}
<span class="position">
{Math.round(scrollPosition / clientWidth) + 1} / {urls.length}
</span>
</div>
<style>
.carousel {
display: flex;
overflow-x: auto;
overflow-y: hidden;
scroll-snap-type: x mandatory;
scroll-behavior: smooth;
scrollbar-width: none;
}
.controls {
position: relative;
}
.item {
position: relative;
min-width: 100%;
max-width: 100%;
scroll-snap-align: center;
}
.item img {
height: 100%;
width: 100%;
object-fit: contain;
}
.delete,
.position,
.arrow {
display: flex;
align-items: center;
justify-content: center;
position: absolute;
z-index: 10;
border: none;
background-color: rgba(0, 0, 0, 0.5);
border-radius: 9999px;
}
.arrow {
cursor: pointer;
width: 2rem;
aspect-ratio: 1 / 1;
}
.arrow img {
width: 1.4rem;
}
.arrow:hover {
opacity: 0.8;
}
.arrow.left {
left: 0.2rem;
top: 50%;
transform: translateY(-50%);
}
.arrow.left img {
transform: rotate(180deg);
}
.arrow.right {
right: 0.2rem;
top: 50%;
transform: translateY(-50%);
}
.delete {
top: 0.1rem;
right: 0.1rem;
}
.position {
font-size: 0.8rem;
bottom: 0.7rem;
border-radius: 1rem;
right: 0.2rem;
padding: 0.3rem 0.5rem;
background-color: rgba(0, 0, 0, 0.7);
}
</style>

View File

@@ -29,6 +29,7 @@
box-shadow: 0rem 0rem 0.5rem #182125;
align-items: center;
overflow: hidden;
z-index: 100;
}
.logo {