src/components/shared/Carousel.svelte
<script lang="ts">
import { browser } from '$app/environment';
import { mod } from 'utils/mod';
import { uniqueId } from 'utils/uniqueId';
interface CarouselItem {
properties?: Record<string, any>;
component: ConstructorOfATypedSvelteComponent;
}
export let items: CarouselItem[];
let clazz: string;
export { clazz as class };
export let bottomNav: boolean = true;
const id = uniqueId();
let timeout: NodeJS.Timeout;
let currentIndex = 0;
const restartTimeout = (_: number) => {
clearTimeout(timeout);
timeout = setTimeout(() => {
currentIndex = mod(currentIndex + 1, items.length);
}, 5000);
};
$: {
if (browser) restartTimeout(currentIndex);
}
</script>
{#each items as _, i}
<input
class="carousel-selector hidden"
type="radio"
name={id}
id="{id}-{i}"
value={i}
bind:group={currentIndex}
/>
{/each}
<div class="carousel-container relative z-30 {clazz}">
{#each items as item, i}
<figure
class="carousel-item absolute top-0 left-0 h-[100%] w-[100%] transition-opacity duration-1000 opacity-0 pointer-events-none"
>
<svelte:component this={item.component} {...item.properties} />
{#each [-1, 1] as direction}
<label
for="{id}-{mod(i + direction, items.length)}"
class="carousel-control absolute top-[50%] translate-y-[-50%] px-4 z-40 cursor-pointer transition-opacity duration-1000 opacity-0 {direction ===
-1
? 'left-0'
: 'right-0'}"
>
<svg
aria-hidden="true"
class="w-5 h-5 text-white sm:w-6 sm:h-6 dark:text-gray-300"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
><path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="3"
d={direction === -1 ? 'M15 19l-7-7 7-7' : 'M9 5l7 7-7 7'}
/></svg
>
</label>
{/each}
</figure>
{/each}
{#if bottomNav}
<div class="carousel-nav absolute bottom-4 left-[50%] translate-x-[-50%] z-40 flex">
{#each items as _, i}
<label
for="{id}-{i}"
class="px-2 py-4 cursor-pointer opacity-50 flex justify-center items-center"
>
<div class="block w-[50px] h-[2px] bg-white" />
</label>
{/each}
</div>
{/if}
</div>