Files
website/src/lib/components/Carousel.svelte
Elad Shechter ec7025ab7d Refactor CSS prefix
- changed from AW to WEB
2024-03-06 11:27:37 +01:00

136 lines
3.5 KiB
Svelte

<script lang="ts">
let carousel: HTMLElement;
export let size: 'default' | 'medium' | 'big' = 'default';
export let gap = 32;
let scroll = 0;
function calculateScrollAmount(prev = false) {
const direction = prev ? -1 : 1;
const carouselSize = carousel?.clientWidth;
const childSize = (carousel.childNodes[0] as HTMLUListElement)?.clientWidth + gap;
scroll = scroll || carouselSize;
const numberOfItems = Math.floor(carouselSize / childSize);
const overflow = scroll % childSize;
const amount = numberOfItems * childSize - overflow * direction;
scroll += amount * direction;
return amount * direction;
}
function next() {
carousel.scrollBy({
left: calculateScrollAmount(),
behavior: 'smooth'
});
}
function prev() {
carousel.scrollBy({
left: calculateScrollAmount(true),
behavior: 'smooth'
});
}
let isEnd = false;
let isStart = true;
function handleScroll() {
isStart = carousel.scrollLeft <= 0;
isEnd = Math.ceil(carousel.scrollLeft + carousel.offsetWidth) >= carousel.scrollWidth;
}
</script>
<div>
<div class="u-flex u-flex-wrap u-cross-center u-margin-block-start-8">
<slot name="header" />
<div class="u-flex u-gap-12 u-cross-end u-margin-inline-start-auto">
<button
class="web-icon-button"
aria-label="Move carousel backward"
disabled={isStart}
on:click={() => prev()}
>
<span class="web-icon-arrow-left" aria-hidden="true" />
</button>
<button
class="web-icon-button"
aria-label="Move carousel forward"
disabled={isEnd}
on:click={() => next()}
>
<span class="web-icon-arrow-right" aria-hidden="true" />
</button>
</div>
</div>
<div class="carousel-wrapper" data-state={isStart ? 'start' : isEnd ? 'end' : 'middle'}>
<ul
class="web-grid-articles u-margin-block-start-32 carousel"
class:is-medium={size === 'medium'}
class:is-big={size === 'big'}
style:gap="{gap}px"
bind:this={carousel}
on:scroll={handleScroll}
>
<slot />
</ul>
</div>
</div>
<style lang="scss">
.carousel-wrapper {
position: relative;
&::before,
&::after {
content: '';
position: absolute;
top: 0;
width: 60px;
height: 100%;
transition: ease 250ms;
z-index: 100;
}
&::before {
left: 0;
background: linear-gradient(
to right,
hsl(var(--web-color-background-docs)),
transparent
);
}
&[data-state='start']::before {
opacity: 0;
}
&::after {
right: 0;
background: linear-gradient(to left, hsl(var(--web-color-background-docs)), transparent);
}
&[data-state='end']::after {
opacity: 0;
}
}
.carousel {
grid-auto-flow: column;
overflow-x: scroll;
scroll-snap-type: x proximity;
scrollbar-width: none;
-ms-overflow-style: none;
&::-webkit-scrollbar {
display: none;
}
}
.carousel :global(li) {
scroll-margin: 48px;
}
</style>