|
1 | 1 | <script lang="ts">
|
2 | 2 | import { type CarouselProps, type State, Slide } from "$lib";
|
3 |
| - import { getTheme } from "$lib/theme/themeUtils"; |
| 3 | + import { getTheme, warnThemeDeprecation } from "$lib/theme/themeUtils"; |
4 | 4 | import clsx from "clsx";
|
5 | 5 | import { onMount, setContext } from "svelte";
|
6 | 6 | import { canChangeSlide } from "./CarouselSlide";
|
7 | 7 | import { carousel } from "./theme";
|
8 | 8 |
|
9 | 9 | const SLIDE_DURATION_RATIO = 0.25;
|
10 | 10 |
|
11 |
| - let { children, slide, images, index = $bindable(0), slideDuration = 1000, transition, duration = 0, "aria-label": ariaLabel = "Draggable Carousel", disableSwipe = false, imgClass = "", class: className, onchange, divClass, isPreload = false, ...restProps }: CarouselProps = $props(); |
| 11 | + let { children, slide, images, index = $bindable(0), slideDuration = 1000, slideFit, transition, duration = 0, "aria-label": ariaLabel = "Draggable Carousel", disableSwipe = false, imgClass = "", class: className, classes, onchange, isPreload = false, ...restProps }: CarouselProps = $props(); |
12 | 12 |
|
| 13 | + warnThemeDeprecation("Carousel", { imgClass }, { imgClass: "slide" }); |
| 14 | + |
| 15 | + const styling = $derived(classes ?? { slide: imgClass }); |
| 16 | + |
| 17 | + // // Theme context |
13 | 18 | const theme = getTheme("carousel");
|
14 | 19 |
|
15 |
| - const _state: State = $state({ images, index: index ?? 0, forward: true, slideDuration, lastSlideChange: new Date() }); |
| 20 | + let { base, slide: slideCls } = $derived(carousel()); |
| 21 | + |
| 22 | + const changeSlide = (n: number) => { |
| 23 | + if (images.length === 0) return; |
| 24 | + |
| 25 | + if (n % images.length === _state.index) return; |
| 26 | + |
| 27 | + if (!canChangeSlide({ lastSlideChange: _state.lastSlideChange, slideDuration, slideDurationRatio: SLIDE_DURATION_RATIO })) return; |
| 28 | + |
| 29 | + _state.forward = n >= _state.index; |
| 30 | + _state.index = (images.length + n) % images.length; |
| 31 | + _state.lastSlideChange = new Date(); |
| 32 | + |
| 33 | + index = _state.index; // Update the bindable index |
| 34 | + onchange?.(images[_state.index]); |
| 35 | + }; |
| 36 | + |
| 37 | + const _state: State = $state({ images, index: index ?? 0, forward: true, slideDuration, lastSlideChange: new Date(), changeSlide }); |
16 | 38 |
|
17 | 39 | setContext("state", _state);
|
18 | 40 |
|
19 | 41 | let initialized = false;
|
20 | 42 |
|
21 | 43 | $effect(() => {
|
22 |
| - index = _state.index; |
23 |
| - onchange?.(images[index]); |
| 44 | + changeSlide(index); |
24 | 45 | });
|
25 | 46 |
|
26 | 47 | onMount(() => {
|
|
29 | 50 | });
|
30 | 51 |
|
31 | 52 | const nextSlide = () => {
|
32 |
| - if (!canChangeSlide({ lastSlideChange: _state.lastSlideChange, slideDuration, slideDurationRatio: SLIDE_DURATION_RATIO })) return _state; |
33 |
| - |
34 |
| - _state.forward = true; |
35 |
| - _state.index = _state.index >= images.length - 1 ? 0 : _state.index + 1; |
36 |
| - _state.lastSlideChange = new Date(); |
37 |
| - |
38 |
| - return _state; |
| 53 | + changeSlide(_state.index + 1); |
39 | 54 | };
|
40 | 55 |
|
41 | 56 | const prevSlide = () => {
|
42 |
| - if (!canChangeSlide({ lastSlideChange: _state.lastSlideChange, slideDuration, slideDurationRatio: SLIDE_DURATION_RATIO })) return _state; |
43 |
| - |
44 |
| - _state.forward = false; |
45 |
| - _state.index = _state.index <= 0 ? images.length - 1 : _state.index - 1; |
46 |
| - _state.lastSlideChange = new Date(); |
47 |
| - |
48 |
| - return _state; |
| 57 | + changeSlide(_state.index - 1); |
49 | 58 | };
|
50 | 59 |
|
51 | 60 | const loop = (node: HTMLElement) => {
|
|
79 | 88 |
|
80 | 89 | const getPositionFromEvent = (evt: MouseEvent | TouchEvent) => {
|
81 | 90 | const mousePos = (evt as MouseEvent)?.clientX;
|
82 |
| - if (mousePos) return mousePos; |
| 91 | + if (mousePos !== undefined) return mousePos; |
83 | 92 |
|
84 | 93 | let touchEvt = evt as TouchEvent;
|
85 | 94 | if (/^touch/.test(touchEvt?.type)) {
|
|
164 | 173 |
|
165 | 174 | <!-- The move listeners go here, so things keep working if the touch strays out of the element. -->
|
166 | 175 | <svelte:document onmousemove={onDragMove} onmouseup={onDragStop} ontouchmove={onDragMove} ontouchend={onDragStop} />
|
167 |
| -<div bind:this={carouselDiv} class={clsx("relative", divClass)} onmousedown={onDragStart} ontouchstart={onDragStart} onmousemove={onDragMove} onmouseup={onDragStop} ontouchmove={onDragMove} ontouchend={onDragStop} role="button" aria-label={ariaLabel} tabindex="0"> |
168 |
| - <div {...restProps} class={carousel({ class: clsx(activeDragGesture === undefined ? "transition-transform" : "", theme, className) })} {@attach loop}> |
169 |
| - {#if slide} |
170 |
| - {@render slide({ index, Slide })} |
171 |
| - {:else} |
172 |
| - <Slide image={images[index]} class={clsx(imgClass)} {transition} /> |
173 |
| - {/if} |
174 |
| - </div> |
175 |
| - {@render children?.(index)} |
| 176 | +<div bind:this={carouselDiv} onmousedown={onDragStart} ontouchstart={onDragStart} onmousemove={onDragMove} onmouseup={onDragStop} ontouchmove={onDragMove} ontouchend={onDragStop} role="button" aria-label={ariaLabel} tabindex="0" {...restProps} class={base({ class: clsx(activeDragGesture === undefined ? "transition-transform" : "", theme?.base, className) })} {@attach loop}> |
| 177 | + {#if slide} |
| 178 | + {@render slide({ index: _state.index, Slide })} |
| 179 | + {:else} |
| 180 | + <Slide image={images[_state.index]} fit={slideFit} class={slideCls({ class: clsx(theme?.slide, styling.slide) })} {transition} /> |
| 181 | + {/if} |
| 182 | + |
| 183 | + {@render children?.(_state.index)} |
176 | 184 | </div>
|
177 | 185 |
|
178 | 186 | <!--
|
|
190 | 198 | @prop duration = 0
|
191 | 199 | @prop "aria-label": ariaLabel = "Draggable Carousel"
|
192 | 200 | @prop disableSwipe = false
|
193 |
| -@prop imgClass = "" |
| 201 | +@prop imgClass = "" // deprecated; use classes.slide instead |
194 | 202 | @prop class: className
|
195 | 203 | @prop onchange
|
196 | 204 | @prop divClass
|
|
0 commit comments