Skip to content

Commit e9805e3

Browse files
authored
Merge pull request #37 from vadimkorr/feature/#31_Show-pause-indicator
Feature/#31 Show pause indicator
2 parents fa7020e + 3093984 commit e9805e3

File tree

13 files changed

+338
-56
lines changed

13 files changed

+338
-56
lines changed

.babelrc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77
}
88
}]
99
],
10+
"plugins": [
11+
"@babel/plugin-proposal-class-properties"
12+
],
1013
"env": {
1114
"test": {
1215
"presets": [["@babel/preset-env"]]

README.md

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -34,18 +34,19 @@ Import component
3434
```
3535

3636
## Props
37-
| Prop | Type | Default | Description |
38-
|----------------------|------------|-----------------|-----------------------------------------------|
39-
| `arrows` | `boolean` | `true` | Enable Next/Prev arrows |
40-
| `infinite` | `boolean` | `true` | Infinite looping |
41-
| `initialPageIndex` | `number` | `0` | Page to start on |
42-
| `duration` | `number` | `500` | Transition duration (ms) |
43-
| `autoplay` | `boolean` | `false` | Enables autoplay of pages |
44-
| `autoplayDuration` | `number` | `3000` | Autoplay change interval (ms) |
45-
| `autoplayDirection` | `string` | `'next'` | Autoplay change direction (`next` or `prev`) |
46-
| `pauseOnFocus` | `boolean` | `false` | Pause autoplay on focus |
47-
| `dots` | `boolean` | `true` | Current page indicator dots |
48-
| `timingFunction` | `string` | `'ease-in-out'` | CSS animation timing function |
37+
| Prop | Type | Default | Description |
38+
|---------------------------|------------|-----------------|-----------------------------------------------|
39+
| `arrows` | `boolean` | `true` | Enable Next/Prev arrows |
40+
| `infinite` | `boolean` | `true` | Infinite looping |
41+
| `initialPageIndex` | `number` | `0` | Page to start on |
42+
| `duration` | `number` | `500` | Transition duration (ms) |
43+
| `autoplay` | `boolean` | `false` | Enables auto play of pages |
44+
| `autoplayDuration` | `number` | `3000` | Autoplay change interval (ms) |
45+
| `autoplayDirection` | `string` | `'next'` | Autoplay change direction (`next` or `prev`) |
46+
| `pauseOnFocus` | `boolean` | `false` | Pause autoplay on focus |
47+
| `autoplayProgressVisible` | `boolean` | `false` | Show autoplay duration progress indicator |
48+
| `dots` | `boolean` | `true` | Current page indicator dots |
49+
| `timingFunction` | `string` | `'ease-in-out'` | CSS animation timing function |
4950

5051
## Events
5152

src/components/Carousel/Carousel.svelte

Lines changed: 83 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import { createStore } from '../../store'
44
import Dots from '../Dots/Dots.svelte'
55
import Arrow from '../Arrow/Arrow.svelte'
6+
import Progress from '../Progress/Progress.svelte'
67
import { NEXT, PREV } from '../../direction'
78
import { swipeable } from '../../actions/swipeable'
89
import { focusable } from '../../actions/focusable'
@@ -12,12 +13,30 @@
1213
} from '../../utils/event'
1314
import { getAdjacentIndexes } from '../../utils/page'
1415
import { get } from '../../utils/object'
16+
import { ProgressManager } from '../../utils/ProgressManager.js'
1517
1618
const dispatch = createEventDispatcher()
1719
20+
const autoplayDirectionFnDescription = {
21+
[NEXT]: () => {
22+
progressManager.start(() => {
23+
showNextPage()
24+
})
25+
},
26+
[PREV]: () => {
27+
progressManager.start(() => {
28+
showPrevPage()
29+
})
30+
}
31+
}
32+
1833
const directionFnDescription = {
19-
[NEXT]: showNextPage,
20-
[PREV]: showPrevPage
34+
[NEXT]: () => {
35+
showNextPage()
36+
},
37+
[PREV]: () => {
38+
showPrevPage()
39+
}
2140
}
2241
2342
/**
@@ -67,6 +86,11 @@
6786
*/
6887
export let pauseOnFocus = false
6988
89+
/**
90+
* Show autoplay duration progress indicator
91+
*/
92+
export let autoplayProgressVisible = false
93+
7094
/**
7195
* Current page indicator dots
7296
*/
@@ -103,13 +127,20 @@
103127
let pagesElement
104128
let focused = false
105129
106-
let autoplayInterval = null
130+
let progressValue
131+
const progressManager = new ProgressManager({
132+
autoplayDuration,
133+
onProgressValueChange: (value) => {
134+
progressValue = 1 - value
135+
}
136+
})
137+
107138
$: {
108139
if (pauseOnFocus) {
109140
if (focused) {
110-
clearAutoplay()
141+
progressManager.pause()
111142
} else {
112-
applyAutoplay()
143+
progressManager.resume()
113144
}
114145
}
115146
}
@@ -130,19 +161,6 @@
130161
131162
offsetPage(false)
132163
}
133-
134-
function applyAutoplay() {
135-
if (autoplay && !autoplayInterval) {
136-
autoplayInterval = setInterval(() => {
137-
directionFnDescription[autoplayDirection]()
138-
}, autoplayDuration)
139-
}
140-
}
141-
142-
function clearAutoplay() {
143-
clearInterval(autoplayInterval)
144-
autoplayInterval = null
145-
}
146164
147165
function addClones() {
148166
const first = pagesElement.children[0]
@@ -151,13 +169,38 @@
151169
pagesElement.append(first.cloneNode(true))
152170
}
153171
172+
function applyAutoplayIfNeeded(options) {
173+
// prevent progress change if not infinite for first and last page
174+
if (
175+
!infinite && (
176+
(autoplayDirection === NEXT && currentPageIndex === pagesCount - 1) ||
177+
(autoplayDirection === PREV && currentPageIndex === 0)
178+
)
179+
) {
180+
progressManager.reset()
181+
return
182+
}
183+
if (autoplay) {
184+
const delayMs = get(options, 'delayMs', 0)
185+
if (delayMs) {
186+
setTimeout(() => {
187+
autoplayDirectionFnDescription[autoplayDirection]()
188+
}, delayMs)
189+
} else {
190+
autoplayDirectionFnDescription[autoplayDirection]()
191+
}
192+
}
193+
}
194+
154195
let cleanupFns = []
196+
155197
onMount(() => {
156198
(async () => {
157199
await tick()
158200
cleanupFns.push(store.subscribe(value => {
159201
currentPageIndex = value.currentPageIndex
160202
}))
203+
cleanupFns.push(() => progressManager.reset())
161204
if (pagesElement && pageWindowElement) {
162205
// load first and last child to clone them
163206
loaded = [0, pagesElement.children.length - 1]
@@ -167,13 +210,14 @@
167210
store.init(initialPageIndex + Number(infinite))
168211
applyPageSizes()
169212
}
170-
applyAutoplay()
213+
214+
applyAutoplayIfNeeded()
215+
171216
addResizeEventListener(applyPageSizes)
172217
})()
173218
})
174219
175220
onDestroy(() => {
176-
clearAutoplay()
177221
removeResizeEventListener(applyPageSizes)
178222
cleanupFns.filter(fn => fn && typeof fn === 'function').forEach(fn => fn())
179223
})
@@ -218,14 +262,14 @@
218262
219263
function showPage(pageIndex, options) {
220264
const animated = get(options, 'animated', true)
221-
const offsetDelayMs = get(options, 'offsetDelayMs', true)
265+
const offsetDelayMs = get(options, 'offsetDelayMs', 0)
222266
safeChangePage(() => {
223267
store.moveToPage({ pageIndex, pagesCount })
224268
// delayed page transition, used for infinite autoplay to jump to real page
225269
setTimeout(() => {
226270
offsetPage(animated)
227271
const jumped = jumpIfNeeded()
228-
!jumped && applyAutoplay()
272+
!jumped && applyAutoplayIfNeeded({ delayMs: _duration }) // while offset animation is in progress (delayMs = _duration ms) wait for it
229273
}, offsetDelayMs)
230274
}, { animated })
231275
}
@@ -235,7 +279,7 @@
235279
store.prev({ infinite, pagesCount })
236280
offsetPage(animated)
237281
const jumped = jumpIfNeeded()
238-
!jumped && applyAutoplay()
282+
!jumped && applyAutoplayIfNeeded({ delayMs: _duration })
239283
}, { animated })
240284
}
241285
function showNextPage(options) {
@@ -244,7 +288,7 @@
244288
store.next({ infinite, pagesCount })
245289
offsetPage(animated)
246290
const jumped = jumpIfNeeded()
247-
!jumped && applyAutoplay()
291+
!jumped && applyAutoplayIfNeeded({ delayMs: _duration })
248292
}, { animated })
249293
}
250294
@@ -300,7 +344,12 @@
300344
bind:this={pagesElement}
301345
>
302346
<slot {loaded}></slot>
303-
</div>
347+
</div>
348+
{#if autoplayProgressVisible}
349+
<div class="sc-carousel-progress__container">
350+
<Progress value={progressValue} />
351+
</div>
352+
{/if}
304353
</div>
305354
{#if arrows}
306355
<slot name="next" {showNextPage}>
@@ -353,6 +402,7 @@
353402
display: flex;
354403
overflow: hidden;
355404
box-sizing: border-box;
405+
position: relative;
356406
}
357407
.sc-carousel__pages-container {
358408
width: 100%;
@@ -366,4 +416,11 @@
366416
align-items: center;
367417
justify-content: center;
368418
}
369-
</style>
419+
.sc-carousel-progress__container {
420+
width: 100%;
421+
height: 5px;
422+
background-color: var(--sc-color-rgb-light-50p);
423+
position: absolute;
424+
bottom: 0;
425+
}
426+
</style>

src/components/Carousel/stories/CarouselView.svelte

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
/**
55
* CSS animation timing function
66
*/
7-
export let timingFunction = "ease-in-out";
7+
export let timingFunction = 'ease-in-out';
88
99
/**
1010
* Enable Next/Previos arrows
@@ -46,6 +46,11 @@
4646
*/
4747
export let pauseOnFocus = false
4848
49+
/**
50+
* Show autoplay duration progress indicator
51+
*/
52+
export let autoplayProgressVisible = false
53+
4954
/**
5055
* Current page indicator dots
5156
*/
@@ -82,6 +87,7 @@
8287
{autoplayDuration}
8388
{autoplayDirection}
8489
{pauseOnFocus}
90+
{autoplayProgressVisible}
8591
{dots}
8692
on:pageChange={
8793
event => console.log(`Current page index: ${event.detail}`)
@@ -107,6 +113,7 @@
107113
{autoplayDuration}
108114
{autoplayDirection}
109115
{pauseOnFocus}
116+
{autoplayProgressVisible}
110117
{dots}
111118
>
112119
{#each colors2 as { color, text } (color)}

src/components/Carousel/stories/CarouselViewCustomArrows.svelte

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
/**
55
* CSS animation timing function
66
*/
7-
export let timingFunction = "ease-in-out";
7+
export let timingFunction = 'ease-in-out';
88
99
/**
1010
* Enable Next/Previos arrows
@@ -46,6 +46,11 @@
4646
*/
4747
export let pauseOnFocus = false
4848
49+
/**
50+
* Show autoplay duration progress indicator
51+
*/
52+
export let autoplayProgressVisible = false
53+
4954
/**
5055
* Current page indicator dots
5156
*/
@@ -76,6 +81,7 @@
7681
{autoplayDuration}
7782
{autoplayDirection}
7883
{pauseOnFocus}
84+
{autoplayProgressVisible}
7985
{dots}
8086
let:showPrevPage
8187
let:showNextPage

src/components/Carousel/stories/CarouselViewCustomDots.svelte

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
/**
55
* CSS animation timing function
66
*/
7-
export let timingFunction = "ease-in-out";
7+
export let timingFunction = 'ease-in-out';
88
99
/**
1010
* Enable Next/Previos arrows
@@ -46,13 +46,18 @@
4646
*/
4747
export let pauseOnFocus = false
4848
49+
/**
50+
* Show autoplay duration progress indicator
51+
*/
52+
export let autoplayProgressVisible = false
53+
4954
/**
5055
* Current page indicator dots
5156
*/
5257
export let dots = true
5358
5459
function onPageChange(event, showPage) {
55-
showPage(event.target.value)
60+
showPage(Number(event.target.value))
5661
}
5762
5863
const colors = [
@@ -80,6 +85,7 @@
8085
{autoplayDuration}
8186
{autoplayDirection}
8287
{pauseOnFocus}
88+
{autoplayProgressVisible}
8389
{dots}
8490
let:currentPageIndex
8591
let:pagesCount
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<script>
2+
import { tweened } from 'svelte/motion';
3+
import { cubicInOut } from 'svelte/easing';
4+
5+
const MAX_PERCENT = 100;
6+
7+
/**
8+
* Progress value, [0, 1]
9+
*/
10+
export let value = 0
11+
12+
$: width = Math.min(Math.max(value * MAX_PERCENT, 0), MAX_PERCENT)
13+
</script>
14+
15+
<div
16+
class="sc-carousel-progress__indicator"
17+
style="
18+
width: {width}%;
19+
"
20+
></div>
21+
22+
<style>
23+
.sc-carousel-progress__indicator {
24+
height: 100%;
25+
background-color: var(--sc-color-hex-dark-50p);
26+
}
27+
</style>

0 commit comments

Comments
 (0)