Merge pull request #37 from vadimkorr/feature/#31_Show-pause-indicator
Feature/#31 Show pause indicator
This commit is contained in:
3
.babelrc
3
.babelrc
@@ -7,6 +7,9 @@
|
|||||||
}
|
}
|
||||||
}]
|
}]
|
||||||
],
|
],
|
||||||
|
"plugins": [
|
||||||
|
"@babel/plugin-proposal-class-properties"
|
||||||
|
],
|
||||||
"env": {
|
"env": {
|
||||||
"test": {
|
"test": {
|
||||||
"presets": [["@babel/preset-env"]]
|
"presets": [["@babel/preset-env"]]
|
||||||
|
|||||||
25
README.md
25
README.md
@@ -34,18 +34,19 @@ Import component
|
|||||||
```
|
```
|
||||||
|
|
||||||
## Props
|
## Props
|
||||||
| Prop | Type | Default | Description |
|
| Prop | Type | Default | Description |
|
||||||
|----------------------|------------|-----------------|-----------------------------------------------|
|
|---------------------------|------------|-----------------|-----------------------------------------------|
|
||||||
| `arrows` | `boolean` | `true` | Enable Next/Prev arrows |
|
| `arrows` | `boolean` | `true` | Enable Next/Prev arrows |
|
||||||
| `infinite` | `boolean` | `true` | Infinite looping |
|
| `infinite` | `boolean` | `true` | Infinite looping |
|
||||||
| `initialPageIndex` | `number` | `0` | Page to start on |
|
| `initialPageIndex` | `number` | `0` | Page to start on |
|
||||||
| `duration` | `number` | `500` | Transition duration (ms) |
|
| `duration` | `number` | `500` | Transition duration (ms) |
|
||||||
| `autoplay` | `boolean` | `false` | Enables autoplay of pages |
|
| `autoplay` | `boolean` | `false` | Enables auto play of pages |
|
||||||
| `autoplayDuration` | `number` | `3000` | Autoplay change interval (ms) |
|
| `autoplayDuration` | `number` | `3000` | Autoplay change interval (ms) |
|
||||||
| `autoplayDirection` | `string` | `'next'` | Autoplay change direction (`next` or `prev`) |
|
| `autoplayDirection` | `string` | `'next'` | Autoplay change direction (`next` or `prev`) |
|
||||||
| `pauseOnFocus` | `boolean` | `false` | Pause autoplay on focus |
|
| `pauseOnFocus` | `boolean` | `false` | Pause autoplay on focus |
|
||||||
| `dots` | `boolean` | `true` | Current page indicator dots |
|
| `autoplayProgressVisible` | `boolean` | `false` | Show autoplay duration progress indicator |
|
||||||
| `timingFunction` | `string` | `'ease-in-out'` | CSS animation timing function |
|
| `dots` | `boolean` | `true` | Current page indicator dots |
|
||||||
|
| `timingFunction` | `string` | `'ease-in-out'` | CSS animation timing function |
|
||||||
|
|
||||||
## Events
|
## Events
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
import { createStore } from '../../store'
|
import { createStore } from '../../store'
|
||||||
import Dots from '../Dots/Dots.svelte'
|
import Dots from '../Dots/Dots.svelte'
|
||||||
import Arrow from '../Arrow/Arrow.svelte'
|
import Arrow from '../Arrow/Arrow.svelte'
|
||||||
|
import Progress from '../Progress/Progress.svelte'
|
||||||
import { NEXT, PREV } from '../../direction'
|
import { NEXT, PREV } from '../../direction'
|
||||||
import { swipeable } from '../../actions/swipeable'
|
import { swipeable } from '../../actions/swipeable'
|
||||||
import { focusable } from '../../actions/focusable'
|
import { focusable } from '../../actions/focusable'
|
||||||
@@ -12,12 +13,30 @@
|
|||||||
} from '../../utils/event'
|
} from '../../utils/event'
|
||||||
import { getAdjacentIndexes } from '../../utils/page'
|
import { getAdjacentIndexes } from '../../utils/page'
|
||||||
import { get } from '../../utils/object'
|
import { get } from '../../utils/object'
|
||||||
|
import { ProgressManager } from '../../utils/ProgressManager.js'
|
||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
|
|
||||||
|
const autoplayDirectionFnDescription = {
|
||||||
|
[NEXT]: () => {
|
||||||
|
progressManager.start(() => {
|
||||||
|
showNextPage()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
[PREV]: () => {
|
||||||
|
progressManager.start(() => {
|
||||||
|
showPrevPage()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const directionFnDescription = {
|
const directionFnDescription = {
|
||||||
[NEXT]: showNextPage,
|
[NEXT]: () => {
|
||||||
[PREV]: showPrevPage
|
showNextPage()
|
||||||
|
},
|
||||||
|
[PREV]: () => {
|
||||||
|
showPrevPage()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -67,6 +86,11 @@
|
|||||||
*/
|
*/
|
||||||
export let pauseOnFocus = false
|
export let pauseOnFocus = false
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show autoplay duration progress indicator
|
||||||
|
*/
|
||||||
|
export let autoplayProgressVisible = false
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Current page indicator dots
|
* Current page indicator dots
|
||||||
*/
|
*/
|
||||||
@@ -103,13 +127,20 @@
|
|||||||
let pagesElement
|
let pagesElement
|
||||||
let focused = false
|
let focused = false
|
||||||
|
|
||||||
let autoplayInterval = null
|
let progressValue
|
||||||
|
const progressManager = new ProgressManager({
|
||||||
|
autoplayDuration,
|
||||||
|
onProgressValueChange: (value) => {
|
||||||
|
progressValue = 1 - value
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
$: {
|
$: {
|
||||||
if (pauseOnFocus) {
|
if (pauseOnFocus) {
|
||||||
if (focused) {
|
if (focused) {
|
||||||
clearAutoplay()
|
progressManager.pause()
|
||||||
} else {
|
} else {
|
||||||
applyAutoplay()
|
progressManager.resume()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -130,19 +161,6 @@
|
|||||||
|
|
||||||
offsetPage(false)
|
offsetPage(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
function applyAutoplay() {
|
|
||||||
if (autoplay && !autoplayInterval) {
|
|
||||||
autoplayInterval = setInterval(() => {
|
|
||||||
directionFnDescription[autoplayDirection]()
|
|
||||||
}, autoplayDuration)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function clearAutoplay() {
|
|
||||||
clearInterval(autoplayInterval)
|
|
||||||
autoplayInterval = null
|
|
||||||
}
|
|
||||||
|
|
||||||
function addClones() {
|
function addClones() {
|
||||||
const first = pagesElement.children[0]
|
const first = pagesElement.children[0]
|
||||||
@@ -151,13 +169,38 @@
|
|||||||
pagesElement.append(first.cloneNode(true))
|
pagesElement.append(first.cloneNode(true))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function applyAutoplayIfNeeded(options) {
|
||||||
|
// prevent progress change if not infinite for first and last page
|
||||||
|
if (
|
||||||
|
!infinite && (
|
||||||
|
(autoplayDirection === NEXT && currentPageIndex === pagesCount - 1) ||
|
||||||
|
(autoplayDirection === PREV && currentPageIndex === 0)
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
progressManager.reset()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (autoplay) {
|
||||||
|
const delayMs = get(options, 'delayMs', 0)
|
||||||
|
if (delayMs) {
|
||||||
|
setTimeout(() => {
|
||||||
|
autoplayDirectionFnDescription[autoplayDirection]()
|
||||||
|
}, delayMs)
|
||||||
|
} else {
|
||||||
|
autoplayDirectionFnDescription[autoplayDirection]()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let cleanupFns = []
|
let cleanupFns = []
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
(async () => {
|
(async () => {
|
||||||
await tick()
|
await tick()
|
||||||
cleanupFns.push(store.subscribe(value => {
|
cleanupFns.push(store.subscribe(value => {
|
||||||
currentPageIndex = value.currentPageIndex
|
currentPageIndex = value.currentPageIndex
|
||||||
}))
|
}))
|
||||||
|
cleanupFns.push(() => progressManager.reset())
|
||||||
if (pagesElement && pageWindowElement) {
|
if (pagesElement && pageWindowElement) {
|
||||||
// load first and last child to clone them
|
// load first and last child to clone them
|
||||||
loaded = [0, pagesElement.children.length - 1]
|
loaded = [0, pagesElement.children.length - 1]
|
||||||
@@ -167,13 +210,14 @@
|
|||||||
store.init(initialPageIndex + Number(infinite))
|
store.init(initialPageIndex + Number(infinite))
|
||||||
applyPageSizes()
|
applyPageSizes()
|
||||||
}
|
}
|
||||||
applyAutoplay()
|
|
||||||
|
applyAutoplayIfNeeded()
|
||||||
|
|
||||||
addResizeEventListener(applyPageSizes)
|
addResizeEventListener(applyPageSizes)
|
||||||
})()
|
})()
|
||||||
})
|
})
|
||||||
|
|
||||||
onDestroy(() => {
|
onDestroy(() => {
|
||||||
clearAutoplay()
|
|
||||||
removeResizeEventListener(applyPageSizes)
|
removeResizeEventListener(applyPageSizes)
|
||||||
cleanupFns.filter(fn => fn && typeof fn === 'function').forEach(fn => fn())
|
cleanupFns.filter(fn => fn && typeof fn === 'function').forEach(fn => fn())
|
||||||
})
|
})
|
||||||
@@ -218,14 +262,14 @@
|
|||||||
|
|
||||||
function showPage(pageIndex, options) {
|
function showPage(pageIndex, options) {
|
||||||
const animated = get(options, 'animated', true)
|
const animated = get(options, 'animated', true)
|
||||||
const offsetDelayMs = get(options, 'offsetDelayMs', true)
|
const offsetDelayMs = get(options, 'offsetDelayMs', 0)
|
||||||
safeChangePage(() => {
|
safeChangePage(() => {
|
||||||
store.moveToPage({ pageIndex, pagesCount })
|
store.moveToPage({ pageIndex, pagesCount })
|
||||||
// delayed page transition, used for infinite autoplay to jump to real page
|
// delayed page transition, used for infinite autoplay to jump to real page
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
offsetPage(animated)
|
offsetPage(animated)
|
||||||
const jumped = jumpIfNeeded()
|
const jumped = jumpIfNeeded()
|
||||||
!jumped && applyAutoplay()
|
!jumped && applyAutoplayIfNeeded({ delayMs: _duration }) // while offset animation is in progress (delayMs = _duration ms) wait for it
|
||||||
}, offsetDelayMs)
|
}, offsetDelayMs)
|
||||||
}, { animated })
|
}, { animated })
|
||||||
}
|
}
|
||||||
@@ -235,7 +279,7 @@
|
|||||||
store.prev({ infinite, pagesCount })
|
store.prev({ infinite, pagesCount })
|
||||||
offsetPage(animated)
|
offsetPage(animated)
|
||||||
const jumped = jumpIfNeeded()
|
const jumped = jumpIfNeeded()
|
||||||
!jumped && applyAutoplay()
|
!jumped && applyAutoplayIfNeeded({ delayMs: _duration })
|
||||||
}, { animated })
|
}, { animated })
|
||||||
}
|
}
|
||||||
function showNextPage(options) {
|
function showNextPage(options) {
|
||||||
@@ -244,7 +288,7 @@
|
|||||||
store.next({ infinite, pagesCount })
|
store.next({ infinite, pagesCount })
|
||||||
offsetPage(animated)
|
offsetPage(animated)
|
||||||
const jumped = jumpIfNeeded()
|
const jumped = jumpIfNeeded()
|
||||||
!jumped && applyAutoplay()
|
!jumped && applyAutoplayIfNeeded({ delayMs: _duration })
|
||||||
}, { animated })
|
}, { animated })
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -300,7 +344,12 @@
|
|||||||
bind:this={pagesElement}
|
bind:this={pagesElement}
|
||||||
>
|
>
|
||||||
<slot {loaded}></slot>
|
<slot {loaded}></slot>
|
||||||
</div>
|
</div>
|
||||||
|
{#if autoplayProgressVisible}
|
||||||
|
<div class="sc-carousel-progress__container">
|
||||||
|
<Progress value={progressValue} />
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
{#if arrows}
|
{#if arrows}
|
||||||
<slot name="next" {showNextPage}>
|
<slot name="next" {showNextPage}>
|
||||||
@@ -353,6 +402,7 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
position: relative;
|
||||||
}
|
}
|
||||||
.sc-carousel__pages-container {
|
.sc-carousel__pages-container {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@@ -366,4 +416,11 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
</style>
|
.sc-carousel-progress__container {
|
||||||
|
width: 100%;
|
||||||
|
height: 5px;
|
||||||
|
background-color: var(--sc-color-rgb-light-50p);
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
/**
|
/**
|
||||||
* CSS animation timing function
|
* CSS animation timing function
|
||||||
*/
|
*/
|
||||||
export let timingFunction = "ease-in-out";
|
export let timingFunction = 'ease-in-out';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enable Next/Previos arrows
|
* Enable Next/Previos arrows
|
||||||
@@ -46,6 +46,11 @@
|
|||||||
*/
|
*/
|
||||||
export let pauseOnFocus = false
|
export let pauseOnFocus = false
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show autoplay duration progress indicator
|
||||||
|
*/
|
||||||
|
export let autoplayProgressVisible = false
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Current page indicator dots
|
* Current page indicator dots
|
||||||
*/
|
*/
|
||||||
@@ -82,6 +87,7 @@
|
|||||||
{autoplayDuration}
|
{autoplayDuration}
|
||||||
{autoplayDirection}
|
{autoplayDirection}
|
||||||
{pauseOnFocus}
|
{pauseOnFocus}
|
||||||
|
{autoplayProgressVisible}
|
||||||
{dots}
|
{dots}
|
||||||
on:pageChange={
|
on:pageChange={
|
||||||
event => console.log(`Current page index: ${event.detail}`)
|
event => console.log(`Current page index: ${event.detail}`)
|
||||||
@@ -107,6 +113,7 @@
|
|||||||
{autoplayDuration}
|
{autoplayDuration}
|
||||||
{autoplayDirection}
|
{autoplayDirection}
|
||||||
{pauseOnFocus}
|
{pauseOnFocus}
|
||||||
|
{autoplayProgressVisible}
|
||||||
{dots}
|
{dots}
|
||||||
>
|
>
|
||||||
{#each colors2 as { color, text } (color)}
|
{#each colors2 as { color, text } (color)}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
/**
|
/**
|
||||||
* CSS animation timing function
|
* CSS animation timing function
|
||||||
*/
|
*/
|
||||||
export let timingFunction = "ease-in-out";
|
export let timingFunction = 'ease-in-out';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enable Next/Previos arrows
|
* Enable Next/Previos arrows
|
||||||
@@ -46,6 +46,11 @@
|
|||||||
*/
|
*/
|
||||||
export let pauseOnFocus = false
|
export let pauseOnFocus = false
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show autoplay duration progress indicator
|
||||||
|
*/
|
||||||
|
export let autoplayProgressVisible = false
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Current page indicator dots
|
* Current page indicator dots
|
||||||
*/
|
*/
|
||||||
@@ -76,6 +81,7 @@
|
|||||||
{autoplayDuration}
|
{autoplayDuration}
|
||||||
{autoplayDirection}
|
{autoplayDirection}
|
||||||
{pauseOnFocus}
|
{pauseOnFocus}
|
||||||
|
{autoplayProgressVisible}
|
||||||
{dots}
|
{dots}
|
||||||
let:showPrevPage
|
let:showPrevPage
|
||||||
let:showNextPage
|
let:showNextPage
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
/**
|
/**
|
||||||
* CSS animation timing function
|
* CSS animation timing function
|
||||||
*/
|
*/
|
||||||
export let timingFunction = "ease-in-out";
|
export let timingFunction = 'ease-in-out';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enable Next/Previos arrows
|
* Enable Next/Previos arrows
|
||||||
@@ -46,13 +46,18 @@
|
|||||||
*/
|
*/
|
||||||
export let pauseOnFocus = false
|
export let pauseOnFocus = false
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show autoplay duration progress indicator
|
||||||
|
*/
|
||||||
|
export let autoplayProgressVisible = false
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Current page indicator dots
|
* Current page indicator dots
|
||||||
*/
|
*/
|
||||||
export let dots = true
|
export let dots = true
|
||||||
|
|
||||||
function onPageChange(event, showPage) {
|
function onPageChange(event, showPage) {
|
||||||
showPage(event.target.value)
|
showPage(Number(event.target.value))
|
||||||
}
|
}
|
||||||
|
|
||||||
const colors = [
|
const colors = [
|
||||||
@@ -80,6 +85,7 @@
|
|||||||
{autoplayDuration}
|
{autoplayDuration}
|
||||||
{autoplayDirection}
|
{autoplayDirection}
|
||||||
{pauseOnFocus}
|
{pauseOnFocus}
|
||||||
|
{autoplayProgressVisible}
|
||||||
{dots}
|
{dots}
|
||||||
let:currentPageIndex
|
let:currentPageIndex
|
||||||
let:pagesCount
|
let:pagesCount
|
||||||
|
|||||||
27
src/components/Progress/Progress.svelte
Normal file
27
src/components/Progress/Progress.svelte
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
<script>
|
||||||
|
import { tweened } from 'svelte/motion';
|
||||||
|
import { cubicInOut } from 'svelte/easing';
|
||||||
|
|
||||||
|
const MAX_PERCENT = 100;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Progress value, [0, 1]
|
||||||
|
*/
|
||||||
|
export let value = 0
|
||||||
|
|
||||||
|
$: width = Math.min(Math.max(value * MAX_PERCENT, 0), MAX_PERCENT)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="sc-carousel-progress__indicator"
|
||||||
|
style="
|
||||||
|
width: {width}%;
|
||||||
|
"
|
||||||
|
></div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.sc-carousel-progress__indicator {
|
||||||
|
height: 100%;
|
||||||
|
background-color: var(--sc-color-hex-dark-50p);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
13
src/components/Progress/stories/Progress.stories.js
Normal file
13
src/components/Progress/stories/Progress.stories.js
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import ProgressView from './ProgressView.svelte';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: 'Default Components/Progress',
|
||||||
|
component: ProgressView
|
||||||
|
};
|
||||||
|
|
||||||
|
const Template = ({ ...args }) => ({
|
||||||
|
Component: ProgressView,
|
||||||
|
props: args
|
||||||
|
});
|
||||||
|
|
||||||
|
export const Primary = Template.bind({});
|
||||||
23
src/components/Progress/stories/ProgressView.svelte
Normal file
23
src/components/Progress/stories/ProgressView.svelte
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<script>
|
||||||
|
import Progress from '../Progress.svelte'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Progress value
|
||||||
|
*/
|
||||||
|
export let value = 0
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="progress-container">
|
||||||
|
<Progress
|
||||||
|
{value}
|
||||||
|
>
|
||||||
|
</Progress>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.progress-container {
|
||||||
|
width: 100%;
|
||||||
|
height: 5px;
|
||||||
|
background-color: rgba(93, 93, 93, 0.5);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -55,7 +55,7 @@
|
|||||||
|
|
||||||
## Autoplay
|
## Autoplay
|
||||||
<Carousel
|
<Carousel
|
||||||
autoplay={true}
|
autoplay
|
||||||
autoplayDuration={2000}
|
autoplayDuration={2000}
|
||||||
>
|
>
|
||||||
{#each colors as { color, text } (color)}
|
{#each colors as { color, text } (color)}
|
||||||
@@ -65,7 +65,7 @@
|
|||||||
|
|
||||||
```jsx
|
```jsx
|
||||||
<Carousel
|
<Carousel
|
||||||
autoplay={true}
|
autoplay
|
||||||
autoplayDuration={2000}
|
autoplayDuration={2000}
|
||||||
>
|
>
|
||||||
{#each colors as { color, text } (color)}
|
{#each colors as { color, text } (color)}
|
||||||
@@ -76,6 +76,58 @@
|
|||||||
|
|
||||||
<Divider />
|
<Divider />
|
||||||
|
|
||||||
|
## Autoplay with duration progress
|
||||||
|
<Carousel
|
||||||
|
autoplay
|
||||||
|
autoplayDuration={5000}
|
||||||
|
autoplayProgressVisible
|
||||||
|
>
|
||||||
|
{#each colors as { color, text } (color)}
|
||||||
|
<Color {color} {text} />
|
||||||
|
{/each}
|
||||||
|
</Carousel>
|
||||||
|
|
||||||
|
```jsx
|
||||||
|
<Carousel
|
||||||
|
autoplay
|
||||||
|
autoplayDuration={5000}
|
||||||
|
autoplayProgressVisible
|
||||||
|
>
|
||||||
|
{#each colors as { color, text } (color)}
|
||||||
|
<Color {color} {text} />
|
||||||
|
{/each}
|
||||||
|
</Carousel>
|
||||||
|
```
|
||||||
|
|
||||||
|
<Divider />
|
||||||
|
|
||||||
|
## Autoplay with pause on focus
|
||||||
|
<Carousel
|
||||||
|
autoplay
|
||||||
|
autoplayDuration={5000}
|
||||||
|
autoplayProgressVisible
|
||||||
|
pauseOnFocus
|
||||||
|
>
|
||||||
|
{#each colors as { color, text } (color)}
|
||||||
|
<Color {color} {text} />
|
||||||
|
{/each}
|
||||||
|
</Carousel>
|
||||||
|
|
||||||
|
```jsx
|
||||||
|
<Carousel
|
||||||
|
autoplay
|
||||||
|
autoplayDuration={5000}
|
||||||
|
autoplayProgressVisible
|
||||||
|
pauseOnFocus
|
||||||
|
>
|
||||||
|
{#each colors as { color, text } (color)}
|
||||||
|
<Color {color} {text} />
|
||||||
|
{/each}
|
||||||
|
</Carousel>
|
||||||
|
```
|
||||||
|
|
||||||
|
<Divider />
|
||||||
|
|
||||||
## Lazy loading of images
|
## Lazy loading of images
|
||||||
<Carousel
|
<Carousel
|
||||||
let:loaded
|
let:loaded
|
||||||
@@ -208,18 +260,19 @@ Import component
|
|||||||
|
|
||||||
# Props
|
# Props
|
||||||
|
|
||||||
| Prop | Type | Default | Description |
|
| Prop | Type | Default | Description |
|
||||||
|----------------------|------------|-----------------|-----------------------------------------------|
|
|---------------------------|------------|-----------------|-----------------------------------------------|
|
||||||
| `arrows` | `boolean` | `true` | Enable Next/Prev arrows |
|
| `arrows` | `boolean` | `true` | Enable Next/Prev arrows |
|
||||||
| `infinite` | `boolean` | `true` | Infinite looping |
|
| `infinite` | `boolean` | `true` | Infinite looping |
|
||||||
| `initialPageIndex` | `number` | `0` | Page to start on |
|
| `initialPageIndex` | `number` | `0` | Page to start on |
|
||||||
| `duration` | `number` | `500` | Transition duration (ms) |
|
| `duration` | `number` | `500` | Transition duration (ms) |
|
||||||
| `autoplay` | `boolean` | `false` | Enables autoplay of pages |
|
| `autoplay` | `boolean` | `false` | Enables auto play of pages |
|
||||||
| `autoplayDuration` | `number` | `3000` | Autoplay change interval (ms) |
|
| `autoplayDuration` | `number` | `3000` | Autoplay change interval (ms) |
|
||||||
| `autoplayDirection` | `string` | `'next'` | Autoplay change direction (`next` or `prev`) |
|
| `autoplayDirection` | `string` | `'next'` | Autoplay change direction (`next` or `prev`) |
|
||||||
| `dots` | `boolean` | `true` | Current page indicator dots |
|
| `pauseOnFocus` | `boolean` | `false` | Pause autoplay on focus |
|
||||||
| `pauseOnFocus` | `boolean` | `false` | Pause autoplay on focus |
|
| `autoplayProgressVisible` | `boolean` | `false` | Show autoplay duration progress indicator |
|
||||||
| `timingFunction` | `string` | `'ease-in-out'` | CSS animation timing function |
|
| `dots` | `boolean` | `true` | Current page indicator dots |
|
||||||
|
| `timingFunction` | `string` | `'ease-in-out'` | CSS animation timing function |
|
||||||
|
|
||||||
<Divider />
|
<Divider />
|
||||||
|
|
||||||
|
|||||||
52
src/utils/ProgressManager.js
Normal file
52
src/utils/ProgressManager.js
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
import { setIntervalImmediate } from './interval'
|
||||||
|
|
||||||
|
const STEP_MS = 35
|
||||||
|
export class ProgressManager {
|
||||||
|
#autoplayDuration
|
||||||
|
#onProgressValueChange
|
||||||
|
|
||||||
|
#interval
|
||||||
|
#paused = false
|
||||||
|
|
||||||
|
constructor({
|
||||||
|
autoplayDuration,
|
||||||
|
onProgressValueChange,
|
||||||
|
}) {
|
||||||
|
this.#autoplayDuration = autoplayDuration
|
||||||
|
this.#onProgressValueChange = onProgressValueChange
|
||||||
|
}
|
||||||
|
|
||||||
|
start(onFinish) {
|
||||||
|
this.reset()
|
||||||
|
|
||||||
|
const stepMs = Math.min(STEP_MS, this.#autoplayDuration)
|
||||||
|
let progress = -stepMs
|
||||||
|
|
||||||
|
this.#interval = setIntervalImmediate(() => {
|
||||||
|
if (this.#paused) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
progress += stepMs
|
||||||
|
|
||||||
|
const value = progress / this.#autoplayDuration
|
||||||
|
this.#onProgressValueChange(value)
|
||||||
|
|
||||||
|
if (value > 1) {
|
||||||
|
this.reset()
|
||||||
|
onFinish()
|
||||||
|
}
|
||||||
|
}, stepMs)
|
||||||
|
}
|
||||||
|
|
||||||
|
pause() {
|
||||||
|
this.#paused = true
|
||||||
|
}
|
||||||
|
|
||||||
|
resume() {
|
||||||
|
this.#paused = false
|
||||||
|
}
|
||||||
|
|
||||||
|
reset() {
|
||||||
|
clearInterval(this.#interval)
|
||||||
|
}
|
||||||
|
}
|
||||||
4
src/utils/interval.js
Normal file
4
src/utils/interval.js
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
export const setIntervalImmediate = (fn, ms) => {
|
||||||
|
fn();
|
||||||
|
return setInterval(fn, ms);
|
||||||
|
}
|
||||||
30
src/utils/interval.test.js
Normal file
30
src/utils/interval.test.js
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
import {
|
||||||
|
setIntervalImmediate,
|
||||||
|
} from './interval.js'
|
||||||
|
|
||||||
|
describe('setIntervalImmediate', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.useFakeTimers();
|
||||||
|
})
|
||||||
|
|
||||||
|
it('runs callback immediately and them each n ms', () => {
|
||||||
|
let interval
|
||||||
|
const durationMs = 1000
|
||||||
|
|
||||||
|
const callNumbersToStopTimer = 3
|
||||||
|
let calledTimes = 0
|
||||||
|
const callback = () => {
|
||||||
|
calledTimes++
|
||||||
|
if (calledTimes === callNumbersToStopTimer) {
|
||||||
|
clearInterval(interval)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interval = setIntervalImmediate(callback, durationMs)
|
||||||
|
jest.runAllTimers()
|
||||||
|
|
||||||
|
expect(calledTimes).toBe(callNumbersToStopTimer)
|
||||||
|
expect(setInterval).toHaveBeenCalledWith(expect.any(Function), durationMs)
|
||||||
|
expect(clearInterval).toHaveBeenCalledWith(interval)
|
||||||
|
})
|
||||||
|
})
|
||||||
Reference in New Issue
Block a user