Merge pull request #80 from vadimkorr/feature/carousel-reactivity-engine
Move logic out of the component
This commit is contained in:
@@ -1,19 +1,21 @@
|
||||
# How to publish new feature:
|
||||
|
||||
1. Update version in `package.json` // docs depend on version
|
||||
|
||||
in feature branch:
|
||||
|
||||
1. Update unit tests
|
||||
2. Update storybook
|
||||
2. Update unit tests
|
||||
3. Update storybook
|
||||
|
||||
3. Update docs in `README.md`
|
||||
4. Update docs in `src/docs`
|
||||
4. Update docs in `README.md`
|
||||
5. Update docs in `src/docs`
|
||||
|
||||
in main branch:
|
||||
|
||||
5. Update version in `package.json`
|
||||
6. `yarn`
|
||||
7. `yarn build:docs`
|
||||
|
||||
8. `npm publish`
|
||||
9. Merge feature branch
|
||||
8. Merge feature branch
|
||||
|
||||
9. `npm publish`
|
||||
10. Create release in GitHub
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
html,body{position:relative;width:100%;height:100%;margin:0;padding:0;box-sizing:border-box;font-family:'Segoe UI',Tahoma,Geneva,Verdana,sans-serif;}
|
||||
.img-container.svelte-4bw00n.svelte-4bw00n{display:block;width:100%;height:200px}.img-container.svelte-4bw00n>img.svelte-4bw00n{width:100%;height:100%;object-fit:cover;-webkit-user-drag:none}.custom-arrow.svelte-4bw00n.svelte-4bw00n{width:20px;background-color:#000000;opacity:0.3;position:absolute;top:0;bottom:0;z-index:1;transition:opacity 150ms ease;display:flex;align-items:center;justify-content:center;cursor:pointer;-webkit-tap-highlight-color:transparent}.custom-arrow.svelte-4bw00n.svelte-4bw00n:hover{opacity:0.5}.custom-arrow.svelte-4bw00n>i.svelte-4bw00n{border:solid #1e1e1e;border-width:0 5px 5px 0;padding:5px;position:relative}.custom-arrow-prev.svelte-4bw00n.svelte-4bw00n{left:0}.custom-arrow-prev.svelte-4bw00n>i.svelte-4bw00n{transform:rotate(135deg);right:-4px}.custom-arrow-next.svelte-4bw00n.svelte-4bw00n{right:0}.custom-arrow-next.svelte-4bw00n>i.svelte-4bw00n{transform:rotate(-45deg);left:-4px}.custom-dots.svelte-4bw00n.svelte-4bw00n{display:flex;flex-wrap:wrap;align-items:center;justify-content:center;padding:0 20px}
|
||||
code[class*="language-"],pre[class*="language-"] {color:#ccc;background:none;font-family:Consolas,Monaco,'Andale Mono','Ubuntu Mono',monospace;font-size:1em;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;line-height:1.5;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-hyphens:none;-moz-hyphens:none;-ms-hyphens:none;hyphens:none;}pre[class*="language-"] {padding:1em;margin:.5em 0;overflow:auto;}:not(pre) > code[class*="language-"],pre[class*="language-"] {background:#2d2d2d;}:not(pre) > code[class*="language-"] {padding:.1em;border-radius:.3em;white-space:normal;}.token.comment,.token.block-comment,.token.prolog,.token.doctype,.token.cdata {color:#999;}.token.punctuation {color:#ccc;}.token.tag,.token.attr-name,.token.namespace,.token.deleted {color:#e2777a;}.token.function-name {color:#6196cc;}.token.boolean,.token.number,.token.function {color:#f08d49;}.token.property,.token.class-name,.token.constant,.token.symbol {color:#f8c555;}.token.selector,.token.important,.token.atrule,.token.keyword,.token.builtin {color:#cc99cd;}.token.string,.token.char,.token.attr-value,.token.regex,.token.variable {color:#7ec699;}.token.operator,.token.entity,.token.url {color:#67cdcc;}.token.important,.token.bold {font-weight:bold;}.token.italic {font-style:italic;}.token.entity {cursor:help;}.token.inserted {color:green;}
|
||||
.img-container.svelte-4bw00n.svelte-4bw00n{display:block;width:100%;height:200px}.img-container.svelte-4bw00n>img.svelte-4bw00n{width:100%;height:100%;object-fit:cover;-webkit-user-drag:none}.custom-arrow.svelte-4bw00n.svelte-4bw00n{width:20px;background-color:#000000;opacity:0.3;position:absolute;top:0;bottom:0;z-index:1;transition:opacity 150ms ease;display:flex;align-items:center;justify-content:center;cursor:pointer;-webkit-tap-highlight-color:transparent}.custom-arrow.svelte-4bw00n.svelte-4bw00n:hover{opacity:0.5}.custom-arrow.svelte-4bw00n>i.svelte-4bw00n{border:solid #1e1e1e;border-width:0 5px 5px 0;padding:5px;position:relative}.custom-arrow-prev.svelte-4bw00n.svelte-4bw00n{left:0}.custom-arrow-prev.svelte-4bw00n>i.svelte-4bw00n{transform:rotate(135deg);right:-4px}.custom-arrow-next.svelte-4bw00n.svelte-4bw00n{right:0}.custom-arrow-next.svelte-4bw00n>i.svelte-4bw00n{transform:rotate(-45deg);left:-4px}.custom-dots.svelte-4bw00n.svelte-4bw00n{display:flex;flex-wrap:wrap;align-items:center;justify-content:center;padding:0 20px}
|
||||
.color-container.svelte-1bsdhrs.svelte-1bsdhrs{height:150px;width:100%;display:flex;align-items:center;justify-content:center;user-select:none}.color-container.svelte-1bsdhrs>p.svelte-1bsdhrs{font-family:'Segoe UI',Tahoma,Geneva,Verdana,sans-serif;font-style:italic;font-size:18px}
|
||||
.divider.svelte-10zimfx{margin-top:15px;margin-bottom:15px;height:1px}
|
||||
.custom-dot__dot-container.svelte-1ufq367{height:25px;width:25px;background-color:#727272;border-radius:50%;opacity:0.7;display:flex;align-items:center;justify-content:center;margin:5px;cursor:pointer;-webkit-tap-highlight-color:transparent}.custom-dot__dot-container.svelte-1ufq367:hover{opacity:0.9}.custom-dot__dot-container_active.svelte-1ufq367{background-color:#009800}.custom-dot__symbol.svelte-1ufq367{font-size:14px;font-weight:bold;color:#eaeaea}
|
||||
:root{--sc-color-rgb-light-50p:rgba(93,93,93,0.5);--sc-color-rgb-light:#5d5d5d;--sc-color-hex-dark-50p:rgba(30,30,30,0.5);--sc-color-hex-dark:#1e1e1e}.sc-carousel__carousel-container.svelte-h7bw08{display:flex;width:100%;flex-direction:column;align-items:center}.sc-carousel__content-container.svelte-h7bw08{position:relative;display:flex;width:100%}.sc-carousel__pages-window.svelte-h7bw08{flex:1;display:flex;overflow:hidden;box-sizing:border-box;position:relative}.sc-carousel__pages-container.svelte-h7bw08{width:100%;display:flex;transition-property:transform}.sc-carousel__arrow-container.svelte-h7bw08{padding:5px;box-sizing:border-box;display:flex;align-items:center;justify-content:center}.sc-carousel-progress__container.svelte-h7bw08{width:100%;height:5px;background-color:var(--sc-color-rgb-light-50p);position:absolute;bottom:0}
|
||||
.color-container.svelte-1bsdhrs.svelte-1bsdhrs{height:150px;width:100%;display:flex;align-items:center;justify-content:center;user-select:none}.color-container.svelte-1bsdhrs>p.svelte-1bsdhrs{font-family:'Segoe UI',Tahoma,Geneva,Verdana,sans-serif;font-style:italic;font-size:18px}
|
||||
.albums-container.svelte-tqqkfc.svelte-tqqkfc{display:flex;justify-content:center;flex-wrap:wrap}.album-container.svelte-tqqkfc.svelte-tqqkfc{width:250px;padding:10px;background-color:#c6c6c6;border-radius:5px;margin:5px}.album-title.svelte-tqqkfc.svelte-tqqkfc{font-size:16px}.album-size.svelte-tqqkfc.svelte-tqqkfc{font-size:10px;color:#585858}.album-tag.svelte-tqqkfc.svelte-tqqkfc{background-color:#8f8f8f;border-radius:5px;padding:1px 5px;color:#ffffff;margin-top:3px;margin-bottom:3px;display:inline-block;font-size:10px}.album-tag.svelte-tqqkfc.svelte-tqqkfc:not(:last-child){margin-right:3px}.album-arrow.svelte-tqqkfc.svelte-tqqkfc{width:20px;background-color:#000000;opacity:0;position:absolute;top:0;bottom:0;z-index:1;transition:opacity 150ms ease;display:flex;align-items:center;justify-content:center;cursor:pointer;-webkit-tap-highlight-color:transparent}.album-arrow.svelte-tqqkfc>i.svelte-tqqkfc{border:solid #1e1e1e;border-width:0 5px 5px 0;padding:5px;position:relative}.album-container.svelte-tqqkfc:hover .album-arrow.svelte-tqqkfc{opacity:0.5}.album-arrow-prev.svelte-tqqkfc.svelte-tqqkfc{left:0}.album-arrow-prev.svelte-tqqkfc>i.svelte-tqqkfc{transform:rotate(135deg);right:-4px}.album-arrow-next.svelte-tqqkfc.svelte-tqqkfc{right:0}.album-arrow-next.svelte-tqqkfc>i.svelte-tqqkfc{transform:rotate(-45deg);left:-4px}
|
||||
.docs__main-layout__main-container.svelte-19ibey6.svelte-19ibey6{background-color:#eaeaea}.docs__main-layout__header-container.svelte-19ibey6.svelte-19ibey6{display:flex;flex-direction:column;align-items:center;justify-content:center;height:300px;padding:10px;box-sizing:border-box;background-color:#f0e68c}.docs__main-layout__logo-container.svelte-19ibey6.svelte-19ibey6{height:80%;max-width:100%;position:relative}.docs__main-layout__version.svelte-19ibey6.svelte-19ibey6{position:absolute;top:20px;right:20px;background-color:#fff;padding:3px 7px;border-radius:5px;line-height:14px;font-size:14px}.docs__main-layout__logo.svelte-19ibey6.svelte-19ibey6{max-height:100%;max-width:100%;object-fit:contain}.docs__main-layout__links-container.svelte-19ibey6.svelte-19ibey6{display:flex;justify-content:center;padding:10px}.docs__main-layout__links-container.svelte-19ibey6>a.svelte-19ibey6{text-decoration:none;display:flex;align-items:center;background-color:#ffffff;padding:5px;border-radius:3px}.docs__main-layout__links-container.svelte-19ibey6>a.svelte-19ibey6:not(:last-child){margin-right:10px}.docs__main-layout__links-container.svelte-19ibey6>a.svelte-19ibey6:last-child{border-top-right-radius:10px;border-bottom-right-radius:10px}.docs__main-layout__links-container.svelte-19ibey6>a.svelte-19ibey6:first-child{border-top-left-radius:10px;border-bottom-left-radius:10px}.docs__main-layout__link-icon.svelte-19ibey6.svelte-19ibey6{width:25px}.docs__main-layout__link-text.svelte-19ibey6.svelte-19ibey6{color:#009800;font-size:18px;font-weight:500}.docs__main-layout__content-container.svelte-19ibey6.svelte-19ibey6{margin:0 auto}@media screen and (min-width:0px){.docs__main-layout__content-container.svelte-19ibey6.svelte-19ibey6{width:95%}}@media screen and (min-width:768px){.docs__main-layout__content-container.svelte-19ibey6.svelte-19ibey6{width:70%}}@media screen and (min-width:992px){.docs__main-layout__content-container.svelte-19ibey6.svelte-19ibey6{width:60%}}@media screen and (min-width:1200px){.docs__main-layout__content-container.svelte-19ibey6.svelte-19ibey6{width:50%}}
|
||||
.sc-carousel-progress__indicator.svelte-nuyenl{height:100%;background-color:var(--sc-color-hex-dark-50p)}
|
||||
:root{--sc-arrow-size:2px}.sc-carousel-arrow__circle.svelte-9ztt4p{width:20px;height:20px;border-radius:50%;background-color:var(--sc-color-rgb-light-50p);display:flex;align-items:center;justify-content:center;transition:opacity 100ms ease;cursor:pointer;-webkit-tap-highlight-color:transparent}.sc-carousel-arrow__circle.svelte-9ztt4p:hover{opacity:0.9}.sc-carousel-arrow__arrow.svelte-9ztt4p{border:solid var(--sc-color-hex-dark);border-width:0 var(--sc-arrow-size) var(--sc-arrow-size) 0;padding:var(--sc-arrow-size);position:relative}.sc-carousel-arrow__arrow-next.svelte-9ztt4p{transform:rotate(-45deg);left:calc(var(--sc-arrow-size) / -2)}.sc-carousel-arrow__arrow-prev.svelte-9ztt4p{transform:rotate(135deg);right:calc(var(--sc-arrow-size) / -2)}.sc-carousel-arrow__circle_disabled.svelte-9ztt4p,.sc-carousel-arrow__circle_disabled.svelte-9ztt4p:hover{opacity:0.5}
|
||||
.docs__main-layout__main-container.svelte-1g4wbsx.svelte-1g4wbsx{background-color:#eaeaea}.docs__main-layout__header-container.svelte-1g4wbsx.svelte-1g4wbsx{display:flex;flex-direction:column;align-items:center;justify-content:center;height:300px;padding:10px;box-sizing:border-box;background-color:#f0e68c}.docs__main-layout__logo-container.svelte-1g4wbsx.svelte-1g4wbsx{position:relative;max-width:min(90%,500px)}.docs__main-layout__version.svelte-1g4wbsx.svelte-1g4wbsx{position:absolute;top:20px;right:20px;background-color:#fff;padding:3px 7px;border-radius:5px;line-height:14px;font-size:14px}.docs__main-layout__logo.svelte-1g4wbsx.svelte-1g4wbsx{max-height:100%;max-width:100%;object-fit:contain}.docs__main-layout__links-container.svelte-1g4wbsx.svelte-1g4wbsx{display:flex;justify-content:center;padding:10px}.docs__main-layout__links-container.svelte-1g4wbsx>a.svelte-1g4wbsx{text-decoration:none;display:flex;align-items:center;background-color:#ffffff;padding:5px;border-radius:3px}.docs__main-layout__links-container.svelte-1g4wbsx>a.svelte-1g4wbsx:not(:last-child){margin-right:10px}.docs__main-layout__links-container.svelte-1g4wbsx>a.svelte-1g4wbsx:last-child{border-top-right-radius:10px;border-bottom-right-radius:10px}.docs__main-layout__links-container.svelte-1g4wbsx>a.svelte-1g4wbsx:first-child{border-top-left-radius:10px;border-bottom-left-radius:10px}.docs__main-layout__link-icon.svelte-1g4wbsx.svelte-1g4wbsx{width:25px}.docs__main-layout__link-text.svelte-1g4wbsx.svelte-1g4wbsx{color:#009800;font-size:18px;font-weight:500}.docs__main-layout__content-container.svelte-1g4wbsx.svelte-1g4wbsx{margin:0 auto}@media screen and (min-width:0px){.docs__main-layout__content-container.svelte-1g4wbsx.svelte-1g4wbsx{width:95%}}@media screen and (min-width:768px){.docs__main-layout__content-container.svelte-1g4wbsx.svelte-1g4wbsx{width:70%}}@media screen and (min-width:992px){.docs__main-layout__content-container.svelte-1g4wbsx.svelte-1g4wbsx{width:60%}}@media screen and (min-width:1200px){.docs__main-layout__content-container.svelte-1g4wbsx.svelte-1g4wbsx{width:50%}}
|
||||
.sc-carousel-dots__container.svelte-1oj5bge{display:flex;align-items:center;justify-content:center;flex-wrap:wrap;padding:0 30px}.sc-carousel-dots__dot-container.svelte-1oj5bge{height:calc(var(--sc-dot-size) + 14px);width:calc(var(--sc-dot-size) + 10px);display:flex;align-items:center;justify-content:center}
|
||||
:root{--sc-arrow-size:2px}.sc-carousel-arrow__circle.svelte-9ztt4p{width:20px;height:20px;border-radius:50%;background-color:var(--sc-color-rgb-light-50p);display:flex;align-items:center;justify-content:center;transition:opacity 100ms ease;cursor:pointer;-webkit-tap-highlight-color:transparent}.sc-carousel-arrow__circle.svelte-9ztt4p:hover{opacity:0.9}.sc-carousel-arrow__arrow.svelte-9ztt4p{border:solid var(--sc-color-hex-dark);border-width:0 var(--sc-arrow-size) var(--sc-arrow-size) 0;padding:var(--sc-arrow-size);position:relative}.sc-carousel-arrow__arrow-next.svelte-9ztt4p{transform:rotate(-45deg);left:calc(var(--sc-arrow-size) / -2)}.sc-carousel-arrow__arrow-prev.svelte-9ztt4p{transform:rotate(135deg);right:calc(var(--sc-arrow-size) / -2)}.sc-carousel-arrow__circle_disabled.svelte-9ztt4p,.sc-carousel-arrow__circle_disabled.svelte-9ztt4p:hover{opacity:0.5}
|
||||
.sc-carousel-progress__indicator.svelte-nuyenl{height:100%;background-color:var(--sc-color-hex-dark-50p)}
|
||||
.image-container.svelte-1cv82er{display:block;width:100%;height:150px}img.svelte-1cv82er{width:100%;height:100%;object-fit:cover;-webkit-user-drag:none}
|
||||
td.svelte-17sscyq{padding:2px 10px}
|
||||
tr.svelte-yxhgu7{border-bottom:2px solid #009800}
|
||||
|
||||
File diff suppressed because one or more lines are too long
BIN
docs/svelte-carousel-logo-md-full-width.png
Normal file
BIN
docs/svelte-carousel-logo-md-full-width.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 385 KiB |
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "svelte-carousel",
|
||||
"version": "1.0.16",
|
||||
"version": "1.0.17-rc1",
|
||||
"description": "Svelte carousel",
|
||||
"main": "src/main.js",
|
||||
"author": "vadimkorr",
|
||||
@@ -53,5 +53,7 @@
|
||||
"svelte": "^3.31.2",
|
||||
"svelte-loader": "^2.13.6"
|
||||
},
|
||||
"dependencies": {}
|
||||
"dependencies": {
|
||||
"simply-reactive": "vadimkorr/simply-reactive#1.0.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<script>
|
||||
import { onDestroy, onMount, tick, createEventDispatcher } from 'svelte'
|
||||
import { createStore } from '../../store'
|
||||
import Dots from '../Dots/Dots.svelte'
|
||||
import Arrow from '../Arrow/Arrow.svelte'
|
||||
import Progress from '../Progress/Progress.svelte'
|
||||
@@ -10,39 +9,36 @@
|
||||
import { tappable } from '../../actions/tappable'
|
||||
import {
|
||||
applyParticleSizes,
|
||||
getCurrentPageIndexByCurrentParticleIndex,
|
||||
getPartialPageSize,
|
||||
getPagesCountByParticlesCount,
|
||||
getParticleIndexByPageIndex,
|
||||
createResizeObserver,
|
||||
} from '../../utils/page'
|
||||
import {
|
||||
getClones,
|
||||
applyClones,
|
||||
getClonesCount,
|
||||
} from '../../utils/clones'
|
||||
import {
|
||||
getAdjacentIndexes,
|
||||
} from '../../utils/lazy'
|
||||
import {
|
||||
getValueInRange,
|
||||
} from '../../utils/math'
|
||||
import { get } from '../../utils/object'
|
||||
import { ProgressManager } from '../../utils/ProgressManager'
|
||||
import { wait } from '../../utils/interval'
|
||||
import { get, switcher } from '../../utils/object'
|
||||
import createCarousel from './createCarousel'
|
||||
|
||||
// used for lazy loading images, preloaded only current, adjacent and cloanable images
|
||||
let loaded = []
|
||||
let currentPageIndex
|
||||
let progressValue
|
||||
let offset = 0
|
||||
let durationMs = 0
|
||||
let pagesCount = 1
|
||||
|
||||
const [{ data, progressManager }, methods, service] = createCarousel((key, value) => {
|
||||
switcher({
|
||||
'currentPageIndex': () => currentPageIndex = value,
|
||||
'progressValue': () => progressValue = value,
|
||||
'offset': () => offset = value,
|
||||
'durationMs': () => durationMs = value,
|
||||
'pagesCount': () => pagesCount = value,
|
||||
'loaded': () => loaded = value,
|
||||
})(key)
|
||||
})
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
const autoplayDirectionFnDescription = {
|
||||
[NEXT]: async () => await progressManager.start(showNextPage),
|
||||
[PREV]: async () => await progressManager.start(showPrevPage)
|
||||
}
|
||||
|
||||
const directionFnDescription = {
|
||||
[NEXT]: showNextPage,
|
||||
[PREV]: showPrevPage
|
||||
}
|
||||
|
||||
/**
|
||||
* CSS animation timing function
|
||||
* examples: 'linear', 'steps(5, end)', 'cubic-bezier(0.1, -0.6, 0.2, 0)'
|
||||
@@ -58,6 +54,9 @@
|
||||
* Infinite looping
|
||||
*/
|
||||
export let infinite = true
|
||||
$: {
|
||||
data.infinite = infinite
|
||||
}
|
||||
|
||||
/**
|
||||
* Page to start on
|
||||
@@ -68,30 +67,41 @@
|
||||
* Transition duration (ms)
|
||||
*/
|
||||
export let duration = 500
|
||||
let _duration = duration
|
||||
$: {
|
||||
data.durationMsInit = duration
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables autoplay of pages
|
||||
*/
|
||||
export let autoplay = false
|
||||
$: {
|
||||
applyAutoplayIfNeeded(autoplay)
|
||||
data.autoplay = autoplay
|
||||
}
|
||||
|
||||
/**
|
||||
* Autoplay change interval (ms)
|
||||
*/
|
||||
export let autoplayDuration = 3000
|
||||
$: {
|
||||
data.autoplayDuration = autoplayDuration
|
||||
}
|
||||
|
||||
/**
|
||||
* Autoplay change direction ('next', 'prev')
|
||||
*/
|
||||
export let autoplayDirection = NEXT
|
||||
$: {
|
||||
data.autoplayDirection = autoplayDirection
|
||||
}
|
||||
|
||||
/**
|
||||
* Pause autoplay on focus
|
||||
*/
|
||||
export let pauseOnFocus = false
|
||||
$: {
|
||||
data.pauseOnFocus = pauseOnFocus
|
||||
}
|
||||
|
||||
/**
|
||||
* Show autoplay duration progress indicator
|
||||
@@ -112,70 +122,37 @@
|
||||
* Number of particles to show
|
||||
*/
|
||||
export let particlesToShow = 1
|
||||
$: {
|
||||
data.particlesToShowInit = particlesToShow
|
||||
}
|
||||
|
||||
/**
|
||||
* Number of particles to scroll
|
||||
*/
|
||||
export let particlesToScroll = 1
|
||||
$: {
|
||||
data.particlesToScrollInit = particlesToScroll
|
||||
}
|
||||
|
||||
export async function goTo(pageIndex, options) {
|
||||
const animated = get(options, 'animated', true)
|
||||
if (typeof pageIndex !== 'number') {
|
||||
throw new Error('pageIndex should be a number')
|
||||
}
|
||||
await showParticle(getParticleIndexByPageIndex({
|
||||
infinite,
|
||||
pageIndex,
|
||||
clonesCountHead: clonesCount.head,
|
||||
clonesCountTail: clonesCount.tail,
|
||||
particlesToScroll,
|
||||
particlesCount,
|
||||
particlesToShow,
|
||||
}), { animated })
|
||||
await methods.showPage(pageIndex, { animated })
|
||||
}
|
||||
|
||||
export async function goToPrev(options) {
|
||||
const animated = get(options, 'animated', true)
|
||||
await showPrevPage({ animated })
|
||||
await methods.showPrevPage({ animated })
|
||||
}
|
||||
|
||||
export async function goToNext(options) {
|
||||
const animated = get(options, 'animated', true)
|
||||
await showNextPage({ animated })
|
||||
await methods.showNextPage({ animated })
|
||||
}
|
||||
|
||||
let store = createStore()
|
||||
|
||||
$: clonesCount = getClonesCount({
|
||||
infinite,
|
||||
particlesToShow,
|
||||
partialPageSize,
|
||||
})
|
||||
|
||||
let currentParticleIndex = 0
|
||||
$: currentPageIndex = getCurrentPageIndexByCurrentParticleIndex({
|
||||
currentParticleIndex,
|
||||
particlesCount,
|
||||
clonesCountHead: clonesCount.head,
|
||||
clonesCountTotal: clonesCount.total,
|
||||
infinite,
|
||||
particlesToScroll,
|
||||
})
|
||||
$: dispatch('pageChange', currentPageIndex)
|
||||
|
||||
let particlesCount = 0
|
||||
let particlesCountWithoutClones = 1
|
||||
$: pagesCount = getPagesCountByParticlesCount({
|
||||
infinite,
|
||||
particlesCountWithoutClones,
|
||||
particlesToScroll,
|
||||
})
|
||||
|
||||
let partialPageSize = 0
|
||||
|
||||
let pageWindowWidth = 0
|
||||
let particleWidth = 0
|
||||
let offset = 0
|
||||
let pageWindowElement
|
||||
let particlesContainer
|
||||
|
||||
@@ -183,54 +160,22 @@
|
||||
width,
|
||||
}) => {
|
||||
pageWindowWidth = width
|
||||
particleWidth = pageWindowWidth / particlesToShow
|
||||
data.particleWidth = pageWindowWidth / data.particlesToShow
|
||||
|
||||
applyParticleSizes({
|
||||
particlesContainerChildren: particlesContainer.children,
|
||||
particleWidth,
|
||||
})
|
||||
offsetPage({
|
||||
animated: false,
|
||||
particleWidth: data.particleWidth,
|
||||
})
|
||||
methods.offsetPage({ animated: false })
|
||||
})
|
||||
|
||||
let focused = false
|
||||
|
||||
let progressValue
|
||||
const progressManager = new ProgressManager({
|
||||
autoplayDuration,
|
||||
onProgressValueChange: (value) => {
|
||||
progressValue = 1 - value
|
||||
}
|
||||
})
|
||||
|
||||
$: {
|
||||
if (pauseOnFocus) {
|
||||
if (focused) {
|
||||
progressManager.pause()
|
||||
} else {
|
||||
progressManager.resume()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// used for lazy loading images, preloaded only current, adjacent and cloanable images
|
||||
$: loaded = getAdjacentIndexes({
|
||||
infinite,
|
||||
pageIndex: currentPageIndex,
|
||||
pagesCount,
|
||||
particlesCount: particlesCountWithoutClones,
|
||||
particlesToShow,
|
||||
particlesToScroll,
|
||||
}).particleIndexes
|
||||
|
||||
function addClones() {
|
||||
const {
|
||||
clonesToAppend,
|
||||
clonesToPrepend,
|
||||
} = getClones({
|
||||
clonesCountHead: clonesCount.head,
|
||||
clonesCountTail: clonesCount.tail,
|
||||
clonesCountHead: data.clonesCountHead,
|
||||
clonesCountTail: data.clonesCountTail,
|
||||
particlesContainerChildren: particlesContainer.children,
|
||||
})
|
||||
applyClones({
|
||||
@@ -240,58 +185,19 @@
|
||||
})
|
||||
}
|
||||
|
||||
async function applyAutoplayIfNeeded(autoplay) {
|
||||
// prevent progress change if not infinite for first and last page
|
||||
if (
|
||||
!infinite && (
|
||||
(autoplayDirection === NEXT && currentParticleIndex === particlesCount - 1) ||
|
||||
(autoplayDirection === PREV && currentParticleIndex === 0)
|
||||
)
|
||||
) {
|
||||
progressManager.reset()
|
||||
return
|
||||
}
|
||||
|
||||
if (autoplay) {
|
||||
await autoplayDirectionFnDescription[autoplayDirection]()
|
||||
}
|
||||
}
|
||||
|
||||
let cleanupFns = []
|
||||
|
||||
onMount(() => {
|
||||
(async () => {
|
||||
await tick()
|
||||
cleanupFns.push(store.subscribe(value => {
|
||||
currentParticleIndex = value.currentParticleIndex
|
||||
}))
|
||||
cleanupFns.push(() => progressManager.reset())
|
||||
if (particlesContainer && pageWindowElement) {
|
||||
particlesCountWithoutClones = particlesContainer.children.length
|
||||
|
||||
particlesToShow = getValueInRange(1, particlesToShow, particlesCountWithoutClones)
|
||||
particlesToScroll = getValueInRange(1, particlesToScroll, particlesCountWithoutClones)
|
||||
initialPageIndex = getValueInRange(0, initialPageIndex, particlesCountWithoutClones - 1)
|
||||
|
||||
partialPageSize = getPartialPageSize({
|
||||
particlesToScroll,
|
||||
particlesToShow,
|
||||
particlesCountWithoutClones,
|
||||
})
|
||||
data.particlesCountWithoutClones = particlesContainer.children.length
|
||||
|
||||
await tick()
|
||||
infinite && addClones()
|
||||
particlesCount = particlesContainer.children.length
|
||||
data.infinite && addClones()
|
||||
|
||||
store.init(getParticleIndexByPageIndex({
|
||||
infinite,
|
||||
pageIndex: initialPageIndex,
|
||||
clonesCountHead: clonesCount.head,
|
||||
clonesCountTail: clonesCount.tail,
|
||||
particlesToScroll,
|
||||
particlesCount,
|
||||
particlesToShow,
|
||||
}))
|
||||
// call after adding clones
|
||||
data.particlesCount = particlesContainer.children.length
|
||||
|
||||
methods.showPage(initialPageIndex, { animated: false })
|
||||
|
||||
pageWindowElementResizeObserver.observe(pageWindowElement);
|
||||
}
|
||||
@@ -300,139 +206,55 @@
|
||||
|
||||
onDestroy(() => {
|
||||
pageWindowElementResizeObserver.disconnect()
|
||||
cleanupFns.filter(fn => fn && typeof fn === 'function').forEach(fn => fn())
|
||||
progressManager.reset()
|
||||
})
|
||||
|
||||
async function handlePageChange(pageIndex) {
|
||||
await showParticle(getParticleIndexByPageIndex({
|
||||
infinite,
|
||||
pageIndex,
|
||||
clonesCountHead: clonesCount.head,
|
||||
clonesCountTail: clonesCount.tail,
|
||||
particlesToScroll,
|
||||
particlesCount,
|
||||
particlesToShow,
|
||||
}))
|
||||
}
|
||||
|
||||
function offsetPage(options) {
|
||||
const animated = get(options, 'animated', true)
|
||||
return new Promise((resolve) => {
|
||||
// _duration is an offset animation time
|
||||
_duration = animated ? duration : 0
|
||||
offset = -currentParticleIndex * particleWidth
|
||||
setTimeout(() => {
|
||||
resolve()
|
||||
}, _duration)
|
||||
})
|
||||
}
|
||||
|
||||
// makes delayed jump to 1st or last element
|
||||
async function jumpIfNeeded() {
|
||||
let jumped = false
|
||||
if (infinite) {
|
||||
if (currentParticleIndex === 0) {
|
||||
await showParticle(particlesCount - clonesCount.total, { animated: false })
|
||||
jumped = true
|
||||
} else if (currentParticleIndex === particlesCount - clonesCount.tail) {
|
||||
await showParticle(clonesCount.head, { animated: false })
|
||||
jumped = true
|
||||
}
|
||||
}
|
||||
return jumped
|
||||
}
|
||||
|
||||
// Disable page change while animation is in progress
|
||||
let disabled = false
|
||||
async function changePage(updateStoreFn, options) {
|
||||
progressManager.reset()
|
||||
if (disabled) return
|
||||
disabled = true
|
||||
|
||||
updateStoreFn()
|
||||
await offsetPage({ animated: get(options, 'animated', true) })
|
||||
disabled = false
|
||||
|
||||
const jumped = await jumpIfNeeded()
|
||||
!jumped && applyAutoplayIfNeeded(autoplay) // no need to wait it finishes
|
||||
}
|
||||
|
||||
async function showParticle(particleIndex, options) {
|
||||
await changePage(
|
||||
() => store.moveToParticle({
|
||||
particleIndex,
|
||||
particlesCount,
|
||||
}),
|
||||
options
|
||||
)
|
||||
}
|
||||
async function showPrevPage(options) {
|
||||
if (disabled) return
|
||||
|
||||
await changePage(
|
||||
() => store.prev({
|
||||
infinite,
|
||||
currentPageIndex,
|
||||
clonesCountHead: clonesCount.head,
|
||||
clonesCountTail: clonesCount.tail,
|
||||
particlesToScroll,
|
||||
particlesCount,
|
||||
particlesToShow,
|
||||
}),
|
||||
options,
|
||||
)
|
||||
}
|
||||
async function showNextPage(options) {
|
||||
if (disabled) return
|
||||
|
||||
await changePage(
|
||||
() => store.next({
|
||||
infinite,
|
||||
currentPageIndex,
|
||||
particlesCount,
|
||||
particlesToScroll,
|
||||
particlesToShow,
|
||||
clonesCountHead: clonesCount.head,
|
||||
clonesCountTail: clonesCount.tail,
|
||||
}),
|
||||
options,
|
||||
)
|
||||
await methods.showPage(pageIndex, { animated: true })
|
||||
}
|
||||
|
||||
// gestures
|
||||
function handleSwipeStart() {
|
||||
if (!swiping) return
|
||||
_duration = 0
|
||||
data.durationMs = 0
|
||||
}
|
||||
async function handleSwipeThresholdReached(event) {
|
||||
if (!swiping) return
|
||||
await directionFnDescription[event.detail.direction]()
|
||||
await switcher({
|
||||
[NEXT]: methods.showNextPage,
|
||||
[PREV]: methods.showPrevPage
|
||||
})(event.detail.direction)
|
||||
}
|
||||
function handleSwipeMove(event) {
|
||||
if (!swiping) return
|
||||
offset += event.detail.dx
|
||||
data.offset += event.detail.dx
|
||||
}
|
||||
function handleSwipeEnd() {
|
||||
if (!swiping) return
|
||||
showParticle(currentParticleIndex)
|
||||
methods.showParticle(data.currentParticleIndex)
|
||||
}
|
||||
async function handleSwipeFailed() {
|
||||
if (!swiping) return
|
||||
await offsetPage({ animated: true })
|
||||
await methods.offsetPage({ animated: true })
|
||||
}
|
||||
|
||||
function handleHovered(event) {
|
||||
focused = event.detail.value
|
||||
data.focused = event.detail.value
|
||||
}
|
||||
function handleTapped(event) {
|
||||
focused = !focused
|
||||
function handleTapped() {
|
||||
methods.toggleFocused()
|
||||
}
|
||||
|
||||
function showPrevPage() {
|
||||
methods.showPrevPage()
|
||||
console.log(service._getSubscribers())
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="sc-carousel__carousel-container">
|
||||
<div class="sc-carousel__content-container">
|
||||
{#if arrows}
|
||||
<slot name="prev" {showPrevPage}>
|
||||
<slot name="prev" showPrevPage={methods.showPrevPage}>
|
||||
<div class="sc-carousel__arrow-container">
|
||||
<Arrow
|
||||
direction="prev"
|
||||
@@ -462,7 +284,7 @@
|
||||
on:swipeThresholdReached={handleSwipeThresholdReached}
|
||||
style="
|
||||
transform: translateX({offset}px);
|
||||
transition-duration: {_duration}ms;
|
||||
transition-duration: {durationMs}ms;
|
||||
transition-timing-function: {timingFunction};
|
||||
"
|
||||
bind:this={particlesContainer}
|
||||
@@ -476,12 +298,12 @@
|
||||
{/if}
|
||||
</div>
|
||||
{#if arrows}
|
||||
<slot name="next" {showNextPage}>
|
||||
<slot name="next" showNextPage={methods.showNextPage}>
|
||||
<div class="sc-carousel__arrow-container">
|
||||
<Arrow
|
||||
direction="next"
|
||||
disabled={!infinite && currentPageIndex === pagesCount - 1}
|
||||
on:click={showNextPage}
|
||||
on:click={methods.showNextPage}
|
||||
/>
|
||||
</div>
|
||||
</slot>
|
||||
|
||||
277
src/components/Carousel/createCarousel.js
Normal file
277
src/components/Carousel/createCarousel.js
Normal file
@@ -0,0 +1,277 @@
|
||||
import simplyReactive from '../../../../simply-reactive/main'
|
||||
|
||||
import { NEXT, PREV } from '../../direction'
|
||||
import {
|
||||
getCurrentPageIndexByCurrentParticleIndex,
|
||||
getPartialPageSize,
|
||||
getPagesCountByParticlesCount,
|
||||
getParticleIndexByPageIndex,
|
||||
} from '../../utils/page'
|
||||
import { getClonesCount } from '../../utils/clones'
|
||||
import { getAdjacentIndexes } from '../../utils/lazy'
|
||||
import { getValueInRange } from '../../utils/math'
|
||||
import { get, switcher } from '../../utils/object'
|
||||
import { ProgressManager } from '../../utils/ProgressManager'
|
||||
|
||||
function createCarousel(onChange) {
|
||||
const progressManager = new ProgressManager({
|
||||
onProgressValueChange: (value) => {
|
||||
onChange('progressValue', 1 - value)
|
||||
},
|
||||
})
|
||||
|
||||
const reactive = simplyReactive(
|
||||
{
|
||||
data: {
|
||||
particlesCountWithoutClones: 0,
|
||||
particlesToShow: 1, // normalized
|
||||
particlesToShowInit: 1, // initial value
|
||||
particlesToScroll: 1, // normalized
|
||||
particlesToScrollInit: 1, // initial value
|
||||
particlesCount: 1,
|
||||
currentParticleIndex: 1,
|
||||
infinite: false,
|
||||
autoplayDuration: 1000,
|
||||
clonesCountHead: 0,
|
||||
clonesCountTail: 0,
|
||||
clonesCountTotal: 0,
|
||||
partialPageSize: 1,
|
||||
currentPageIndex: 1,
|
||||
pagesCount: 1,
|
||||
pauseOnFocus: false,
|
||||
focused: false,
|
||||
autoplay: false,
|
||||
autoplayDirection: 'next',
|
||||
disabled: false, // disable page change while animation is in progress
|
||||
durationMsInit: 1000,
|
||||
durationMs: 1000,
|
||||
offset: 0,
|
||||
particleWidth: 0,
|
||||
loaded: [],
|
||||
},
|
||||
watch: {
|
||||
setLoaded({ data }) {
|
||||
data.loaded = getAdjacentIndexes({
|
||||
infinite: data.infinite,
|
||||
pageIndex: data.currentPageIndex,
|
||||
pagesCount: data.pagesCount,
|
||||
particlesCount: data.particlesCountWithoutClones,
|
||||
particlesToShow: data.particlesToShow,
|
||||
particlesToScroll: data.particlesToScroll,
|
||||
}).particleIndexes
|
||||
},
|
||||
setCurrentPageIndex({ data }) {
|
||||
data.currentPageIndex = getCurrentPageIndexByCurrentParticleIndex({
|
||||
currentParticleIndex: data.currentParticleIndex,
|
||||
particlesCount: data.particlesCount,
|
||||
clonesCountHead: data.clonesCountHead,
|
||||
clonesCountTotal: data.clonesCountTotal,
|
||||
infinite: data.infinite,
|
||||
particlesToScroll: data.particlesToScroll,
|
||||
})
|
||||
},
|
||||
setPartialPageSize({ data }) {
|
||||
data.partialPageSize = getPartialPageSize({
|
||||
particlesToScroll: data.particlesToScroll,
|
||||
particlesToShow: data.particlesToShow,
|
||||
particlesCountWithoutClones: data.particlesCountWithoutClones,
|
||||
})
|
||||
},
|
||||
setClonesCount({ data }) {
|
||||
const { head, tail } = getClonesCount({
|
||||
infinite: data.infinite,
|
||||
particlesToShow: data.particlesToShow,
|
||||
partialPageSize: data.partialPageSize,
|
||||
})
|
||||
data.clonesCountHead = head
|
||||
data.clonesCountTail = tail
|
||||
data.clonesCountTotal = head + tail
|
||||
},
|
||||
setProgressManagerAutoplayDuration({ data }) {
|
||||
progressManager.setAutoplayDuration(data.autoplayDuration)
|
||||
},
|
||||
toggleProgressManager({ data: { pauseOnFocus, focused } }) {
|
||||
// as focused is in if block, it will not be put to deps, read them in data: {}
|
||||
if (pauseOnFocus) {
|
||||
if (focused) {
|
||||
progressManager.pause()
|
||||
} else {
|
||||
progressManager.resume()
|
||||
}
|
||||
}
|
||||
},
|
||||
initDuration({ data }) {
|
||||
data.durationMs = data.durationMsInit
|
||||
},
|
||||
applyAutoplay({ data, methods: { _applyAutoplayIfNeeded } }) {
|
||||
// prevent _applyAutoplayIfNeeded to be called with watcher
|
||||
// to prevent its data added to deps
|
||||
data.autoplay && _applyAutoplayIfNeeded(data.autoplay)
|
||||
},
|
||||
setPagesCount({ data }) {
|
||||
data.pagesCount = getPagesCountByParticlesCount({
|
||||
infinite: data.infinite,
|
||||
particlesCountWithoutClones: data.particlesCountWithoutClones,
|
||||
particlesToScroll: data.particlesToScroll,
|
||||
particlesToShow: data.particlesToShow,
|
||||
})
|
||||
},
|
||||
setParticlesToShow({ data }) {
|
||||
data.particlesToShow = getValueInRange(
|
||||
1,
|
||||
data.particlesToShowInit,
|
||||
data.particlesCountWithoutClones
|
||||
)
|
||||
},
|
||||
setParticlesToScroll({ data }) {
|
||||
data.particlesToScroll = getValueInRange(
|
||||
1,
|
||||
data.particlesToScrollInit,
|
||||
data.particlesCountWithoutClones
|
||||
)
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
_prev({ data }) {
|
||||
data.currentParticleIndex = getParticleIndexByPageIndex({
|
||||
infinite: data.infinite,
|
||||
pageIndex: data.currentPageIndex - 1,
|
||||
clonesCountHead: data.clonesCountHead,
|
||||
clonesCountTail: data.clonesCountTail,
|
||||
particlesToScroll: data.particlesToScroll,
|
||||
particlesCount: data.particlesCount,
|
||||
particlesToShow: data.particlesToShow,
|
||||
})
|
||||
},
|
||||
_next({ data }) {
|
||||
data.currentParticleIndex = getParticleIndexByPageIndex({
|
||||
infinite: data.infinite,
|
||||
pageIndex: data.currentPageIndex + 1,
|
||||
clonesCountHead: data.clonesCountHead,
|
||||
clonesCountTail: data.clonesCountTail,
|
||||
particlesToScroll: data.particlesToScroll,
|
||||
particlesCount: data.particlesCount,
|
||||
particlesToShow: data.particlesToShow,
|
||||
})
|
||||
},
|
||||
_moveToParticle({ data }, particleIndex) {
|
||||
data.currentParticleIndex = getValueInRange(
|
||||
0,
|
||||
particleIndex,
|
||||
data.particlesCount - 1
|
||||
)
|
||||
},
|
||||
toggleFocused({ data }) {
|
||||
data.focused = !data.focused
|
||||
},
|
||||
async _applyAutoplayIfNeeded({ data, methods }) {
|
||||
// prevent progress change if not infinite for first and last page
|
||||
if (
|
||||
!data.infinite &&
|
||||
((data.autoplayDirection === NEXT &&
|
||||
data.currentParticleIndex === data.particlesCount - 1) ||
|
||||
(data.autoplayDirection === PREV &&
|
||||
data.currentParticleIndex === 0))
|
||||
) {
|
||||
progressManager.reset()
|
||||
return
|
||||
}
|
||||
|
||||
if (data.autoplay) {
|
||||
const onFinish = () =>
|
||||
switcher({
|
||||
[NEXT]: async () => methods.showNextPage(),
|
||||
[PREV]: async () => methods.showPrevPage(),
|
||||
})(data.autoplayDirection)
|
||||
|
||||
await progressManager.start(onFinish)
|
||||
}
|
||||
},
|
||||
// makes delayed jump to 1st or last element
|
||||
async _jumpIfNeeded({ data, methods }) {
|
||||
let jumped = false
|
||||
if (data.infinite) {
|
||||
if (data.currentParticleIndex === 0) {
|
||||
await methods.showParticle(
|
||||
data.particlesCount - data.clonesCountTotal,
|
||||
{
|
||||
animated: false,
|
||||
}
|
||||
)
|
||||
jumped = true
|
||||
} else if (
|
||||
data.currentParticleIndex ===
|
||||
data.particlesCount - data.clonesCountTail
|
||||
) {
|
||||
await methods.showParticle(data.clonesCountHead, {
|
||||
animated: false,
|
||||
})
|
||||
jumped = true
|
||||
}
|
||||
}
|
||||
return jumped
|
||||
},
|
||||
async changePage({ data, methods }, updateStoreFn, options) {
|
||||
progressManager.reset()
|
||||
if (data.disabled) return
|
||||
data.disabled = true
|
||||
|
||||
updateStoreFn()
|
||||
await methods.offsetPage({ animated: get(options, 'animated', true) })
|
||||
data.disabled = false
|
||||
|
||||
const jumped = await methods._jumpIfNeeded()
|
||||
!jumped && methods._applyAutoplayIfNeeded() // no need to wait it finishes
|
||||
},
|
||||
async showNextPage({ data, methods }, options) {
|
||||
if (data.disabled) return
|
||||
await methods.changePage(methods._next, options)
|
||||
},
|
||||
async showPrevPage({ data, methods }, options) {
|
||||
if (data.disabled) return
|
||||
await methods.changePage(methods._prev, options)
|
||||
},
|
||||
async showParticle({ methods }, particleIndex, options) {
|
||||
await methods.changePage(
|
||||
() => methods._moveToParticle(particleIndex),
|
||||
options
|
||||
)
|
||||
},
|
||||
_getParticleIndexByPageIndex({ data }, pageIndex) {
|
||||
return getParticleIndexByPageIndex({
|
||||
infinite: data.infinite,
|
||||
pageIndex,
|
||||
clonesCountHead: data.clonesCountHead,
|
||||
clonesCountTail: data.clonesCountTail,
|
||||
particlesToScroll: data.particlesToScroll,
|
||||
particlesCount: data.particlesCount,
|
||||
particlesToShow: data.particlesToShow,
|
||||
})
|
||||
},
|
||||
async showPage({ methods }, pageIndex, options) {
|
||||
const particleIndex = methods._getParticleIndexByPageIndex(pageIndex)
|
||||
await methods.showParticle(particleIndex, options)
|
||||
},
|
||||
offsetPage({ data }, options) {
|
||||
const animated = get(options, 'animated', true)
|
||||
return new Promise((resolve) => {
|
||||
// durationMs is an offset animation time
|
||||
data.durationMs = animated ? data.durationMsInit : 0
|
||||
data.offset = -data.currentParticleIndex * data.particleWidth
|
||||
setTimeout(() => {
|
||||
resolve()
|
||||
}, data.durationMs)
|
||||
})
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
onChange,
|
||||
}
|
||||
)
|
||||
const [data, methods] = reactive
|
||||
|
||||
return [{ data, progressManager }, methods, reactive._internal]
|
||||
}
|
||||
|
||||
export default createCarousel
|
||||
@@ -1,4 +1,5 @@
|
||||
<script context="module">
|
||||
import Carousel from '../../components/Carousel/Carousel.svelte'
|
||||
import Table from './custom/Table.svelte'
|
||||
import Th from './custom/Th.svelte'
|
||||
import Tr from './custom/Tr.svelte'
|
||||
@@ -31,8 +32,12 @@
|
||||
<div class="docs__main-layout__main-container">
|
||||
<div class="docs__main-layout__header-container">
|
||||
<div class="docs__main-layout__logo-container">
|
||||
<Carousel>
|
||||
{#each Array.from(Array(3).keys()) as index (index)}
|
||||
<img class="docs__main-layout__logo" src="./svelte-carousel-logo-md-full-width.png" alt="svelte-carousel-logo" />
|
||||
{/each}
|
||||
</Carousel>
|
||||
<span class="docs__main-layout__version">v{pkg.version}</span>
|
||||
<img class="docs__main-layout__logo" src="./svelte-carousel-logo-md.png" alt="svelte-carousel-logo" />
|
||||
</div>
|
||||
<div class="docs__main-layout__links-container">
|
||||
{#each links as { url, title, iconName } (title)}
|
||||
@@ -68,9 +73,8 @@
|
||||
}
|
||||
|
||||
.docs__main-layout__logo-container {
|
||||
height: 80%;
|
||||
max-width: 100%;
|
||||
position: relative;
|
||||
max-width: min(90%, 500px);
|
||||
}
|
||||
.docs__main-layout__version {
|
||||
position: absolute;
|
||||
|
||||
98
src/store.js
98
src/store.js
@@ -1,98 +0,0 @@
|
||||
import {
|
||||
writable,
|
||||
} from 'svelte/store';
|
||||
import {
|
||||
getParticleIndexByPageIndex,
|
||||
} from './utils/page'
|
||||
import {
|
||||
getValueInRange,
|
||||
} from './utils/math'
|
||||
|
||||
const initState = {
|
||||
currentParticleIndex: 0,
|
||||
}
|
||||
|
||||
function createStore() {
|
||||
const { subscribe, set, update } = writable(initState);
|
||||
|
||||
function init(initialParticleIndex) {
|
||||
set({
|
||||
...initState,
|
||||
currentParticleIndex: initialParticleIndex,
|
||||
})
|
||||
}
|
||||
|
||||
function moveToParticle({
|
||||
particleIndex,
|
||||
particlesCount,
|
||||
}) {
|
||||
update(store => {
|
||||
return {
|
||||
...store,
|
||||
currentParticleIndex: getValueInRange(0, particleIndex, particlesCount - 1),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function next({
|
||||
infinite,
|
||||
currentPageIndex,
|
||||
particlesCount,
|
||||
particlesToScroll,
|
||||
particlesToShow,
|
||||
clonesCountHead,
|
||||
clonesCountTail,
|
||||
}) {
|
||||
update(store => {
|
||||
const newCurrentParticleIndex = getParticleIndexByPageIndex({
|
||||
infinite,
|
||||
pageIndex: currentPageIndex + 1,
|
||||
clonesCountHead,
|
||||
clonesCountTail,
|
||||
particlesToScroll,
|
||||
particlesCount,
|
||||
particlesToShow,
|
||||
})
|
||||
return {
|
||||
...store,
|
||||
currentParticleIndex: newCurrentParticleIndex,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function prev({
|
||||
infinite,
|
||||
currentPageIndex,
|
||||
clonesCountHead,
|
||||
clonesCountTail,
|
||||
particlesToScroll,
|
||||
particlesCount,
|
||||
particlesToShow,
|
||||
}) {
|
||||
update(store => {
|
||||
const newCurrentParticleIndex = getParticleIndexByPageIndex({
|
||||
infinite,
|
||||
pageIndex: currentPageIndex - 1,
|
||||
clonesCountHead,
|
||||
clonesCountTail,
|
||||
particlesToScroll,
|
||||
particlesCount,
|
||||
particlesToShow,
|
||||
})
|
||||
return {
|
||||
...store,
|
||||
currentParticleIndex: newCurrentParticleIndex,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
subscribe,
|
||||
next,
|
||||
prev,
|
||||
init,
|
||||
moveToParticle,
|
||||
};
|
||||
}
|
||||
|
||||
export { createStore };
|
||||
@@ -4,35 +4,35 @@ const STEP_MS = 35
|
||||
const MAX_VALUE = 1
|
||||
|
||||
export class ProgressManager {
|
||||
#autoplayDuration
|
||||
#onProgressValueChange
|
||||
constructor({ onProgressValueChange }) {
|
||||
this._onProgressValueChange = onProgressValueChange
|
||||
|
||||
#interval
|
||||
#paused = false
|
||||
this._autoplayDuration
|
||||
this._onProgressValueChange
|
||||
|
||||
this._interval
|
||||
this._paused = false
|
||||
}
|
||||
|
||||
constructor({
|
||||
autoplayDuration,
|
||||
onProgressValueChange,
|
||||
}) {
|
||||
this.#autoplayDuration = autoplayDuration
|
||||
this.#onProgressValueChange = onProgressValueChange
|
||||
setAutoplayDuration(autoplayDuration) {
|
||||
this._autoplayDuration = autoplayDuration
|
||||
}
|
||||
|
||||
start(onFinish) {
|
||||
return new Promise((resolve) => {
|
||||
this.reset()
|
||||
|
||||
const stepMs = Math.min(STEP_MS, this.#autoplayDuration)
|
||||
const stepMs = Math.min(STEP_MS, this._autoplayDuration)
|
||||
let progress = -stepMs
|
||||
|
||||
this.#interval = setIntervalImmediate(async () => {
|
||||
if (this.#paused) {
|
||||
this._interval = setIntervalImmediate(async () => {
|
||||
if (this._paused) {
|
||||
return
|
||||
}
|
||||
progress += stepMs
|
||||
|
||||
const value = progress / this.#autoplayDuration
|
||||
this.#onProgressValueChange(value)
|
||||
const value = progress / this._autoplayDuration
|
||||
this._onProgressValueChange(value)
|
||||
|
||||
if (value > MAX_VALUE) {
|
||||
this.reset()
|
||||
@@ -44,15 +44,15 @@ export class ProgressManager {
|
||||
}
|
||||
|
||||
pause() {
|
||||
this.#paused = true
|
||||
this._paused = true
|
||||
}
|
||||
|
||||
resume() {
|
||||
this.#paused = false
|
||||
this._paused = false
|
||||
}
|
||||
|
||||
reset() {
|
||||
clearInterval(this.#interval)
|
||||
this.#onProgressValueChange(MAX_VALUE)
|
||||
clearInterval(this._interval)
|
||||
this._onProgressValueChange(MAX_VALUE)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,3 +7,7 @@ export const get = (object, fieldName, defaultValue) => {
|
||||
}
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
export const switcher = (description) => (key) => {
|
||||
description[key] && description[key]()
|
||||
}
|
||||
|
||||
@@ -60,14 +60,21 @@ export function _getPagesCountByParticlesCountInfinite({
|
||||
export function _getPagesCountByParticlesCountLimited({
|
||||
particlesCountWithoutClones,
|
||||
particlesToScroll,
|
||||
particlesToShow,
|
||||
}) {
|
||||
return Math.round(particlesCountWithoutClones / particlesToScroll)
|
||||
const partialPageSize = getPartialPageSize({
|
||||
particlesCountWithoutClones,
|
||||
particlesToScroll,
|
||||
particlesToShow,
|
||||
})
|
||||
return Math.ceil(particlesCountWithoutClones / particlesToScroll) - partialPageSize
|
||||
}
|
||||
|
||||
export function getPagesCountByParticlesCount({
|
||||
infinite,
|
||||
particlesCountWithoutClones,
|
||||
particlesToScroll,
|
||||
particlesToShow,
|
||||
}) {
|
||||
return infinite
|
||||
? _getPagesCountByParticlesCountInfinite({
|
||||
@@ -77,6 +84,7 @@ export function getPagesCountByParticlesCount({
|
||||
: _getPagesCountByParticlesCountLimited({
|
||||
particlesCountWithoutClones,
|
||||
particlesToScroll,
|
||||
particlesToShow,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -435,17 +435,42 @@ describe('_getPagesCountByParticlesCountLimited', () => {
|
||||
const testCases = [{
|
||||
particlesCountWithoutClones: 5,
|
||||
particlesToScroll: 2,
|
||||
expected: 3,
|
||||
particlesToShow: 3,
|
||||
expected: 2,
|
||||
}]
|
||||
|
||||
testCases.forEach(({
|
||||
particlesCountWithoutClones,
|
||||
particlesToScroll,
|
||||
particlesToShow,
|
||||
expected,
|
||||
}) => {
|
||||
expect(_getPagesCountByParticlesCountLimited({
|
||||
particlesCountWithoutClones,
|
||||
particlesToScroll,
|
||||
particlesToShow,
|
||||
})).toBe(expected)
|
||||
})
|
||||
})
|
||||
|
||||
it('returns result as expected if particlesCountWithoutClones: 9; particlesToScroll: 2 (particlesToShow: 3)', () => {
|
||||
const testCases = [{
|
||||
particlesCountWithoutClones: 9,
|
||||
particlesToScroll: 2,
|
||||
particlesToShow: 3,
|
||||
expected: 4,
|
||||
}]
|
||||
|
||||
testCases.forEach(({
|
||||
particlesCountWithoutClones,
|
||||
particlesToScroll,
|
||||
particlesToShow,
|
||||
expected,
|
||||
}) => {
|
||||
expect(_getPagesCountByParticlesCountLimited({
|
||||
particlesCountWithoutClones,
|
||||
particlesToScroll,
|
||||
particlesToShow,
|
||||
})).toBe(expected)
|
||||
})
|
||||
})
|
||||
@@ -454,17 +479,20 @@ describe('_getPagesCountByParticlesCountLimited', () => {
|
||||
const testCases = [{
|
||||
particlesCountWithoutClones: 6,
|
||||
particlesToScroll: 2,
|
||||
particlesToShow: 2,
|
||||
expected: 3,
|
||||
}]
|
||||
|
||||
testCases.forEach(({
|
||||
particlesCountWithoutClones,
|
||||
particlesToScroll,
|
||||
particlesToShow,
|
||||
expected,
|
||||
}) => {
|
||||
expect(_getPagesCountByParticlesCountLimited({
|
||||
particlesCountWithoutClones,
|
||||
particlesToScroll,
|
||||
particlesToShow,
|
||||
})).toBe(expected)
|
||||
})
|
||||
})
|
||||
@@ -473,17 +501,20 @@ describe('_getPagesCountByParticlesCountLimited', () => {
|
||||
const testCases = [{
|
||||
particlesCountWithoutClones: 5,
|
||||
particlesToScroll: 3,
|
||||
particlesToShow: 2,
|
||||
expected: 2,
|
||||
}]
|
||||
|
||||
testCases.forEach(({
|
||||
particlesCountWithoutClones,
|
||||
particlesToScroll,
|
||||
particlesToShow,
|
||||
expected,
|
||||
}) => {
|
||||
expect(_getPagesCountByParticlesCountLimited({
|
||||
particlesCountWithoutClones,
|
||||
particlesToScroll,
|
||||
particlesToShow,
|
||||
})).toBe(expected)
|
||||
})
|
||||
})
|
||||
|
||||
23
yarn.lock
23
yarn.lock
@@ -7561,11 +7561,26 @@ lodash-es@^4.17.15:
|
||||
resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.20.tgz#29f6332eefc60e849f869c264bc71126ad61e8f7"
|
||||
integrity sha512-JD1COMZsq8maT6mnuz1UMV0jvYD0E0aUsSOdrr1/nAG3dhqQXwRRgeW0cSqH1U43INKcqxaiVIQNOUDld7gRDA==
|
||||
|
||||
lodash.clonedeep@^4.5.0:
|
||||
version "4.5.0"
|
||||
resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef"
|
||||
integrity sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=
|
||||
|
||||
lodash.debounce@^4.0.8:
|
||||
version "4.0.8"
|
||||
resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af"
|
||||
integrity sha1-gteb/zCmfEAF/9XiUVMArZyk168=
|
||||
|
||||
lodash.get@^4.4.2:
|
||||
version "4.4.2"
|
||||
resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99"
|
||||
integrity sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=
|
||||
|
||||
lodash.isequal@^4.5.0:
|
||||
version "4.5.0"
|
||||
resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0"
|
||||
integrity sha1-QVxEePK8wwEgwizhDtMib30+GOA=
|
||||
|
||||
lodash.sortby@^4.7.0:
|
||||
version "4.7.0"
|
||||
resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438"
|
||||
@@ -9935,6 +9950,14 @@ signal-exit@^3.0.0, signal-exit@^3.0.2:
|
||||
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c"
|
||||
integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==
|
||||
|
||||
simply-reactive@vadimkorr/simply-reactive#1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://codeload.github.com/vadimkorr/simply-reactive/tar.gz/f707120085a5dd5d3b3f0ee852dde4d98cfe1a76"
|
||||
dependencies:
|
||||
lodash.clonedeep "^4.5.0"
|
||||
lodash.get "^4.4.2"
|
||||
lodash.isequal "^4.5.0"
|
||||
|
||||
sirv-cli@^1.0.11:
|
||||
version "1.0.11"
|
||||
resolved "https://registry.yarnpkg.com/sirv-cli/-/sirv-cli-1.0.11.tgz#a3f4bed53b7c09306ed7f16ebea6e1e7be676c74"
|
||||
|
||||
Reference in New Issue
Block a user