diff --git a/src/components/Carousel/Carousel.svelte b/src/components/Carousel/Carousel.svelte index b4964ae..9449e16 100644 --- a/src/components/Carousel/Carousel.svelte +++ b/src/components/Carousel/Carousel.svelte @@ -79,7 +79,8 @@ */ export let initialPageIndex = 0 $: { - data.initialPageIndexInit = initialPageIndex + console.log('initialPageIndex', initialPageIndex) + data.initialPageIndex = initialPageIndex } /** @@ -217,7 +218,6 @@ // call after adding clones data.particlesCount = particlesContainer.children.length - data.initialPageIndex = initialPageIndex pageWindowElementResizeObserver.observe(pageWindowElement); } diff --git a/src/components/Carousel/createCarousel.js b/src/components/Carousel/createCarousel.js index be75c37..630b541 100644 --- a/src/components/Carousel/createCarousel.js +++ b/src/components/Carousel/createCarousel.js @@ -1,4 +1,4 @@ -import simplyReactive from 'simply-reactive' +import simplyReactive from '../../../../simply-reactive/main' import { NEXT, PREV } from '../../direction' import { @@ -22,257 +22,233 @@ function createCarousel(onChange) { const [data, methods, service] = simplyReactive( { - particlesCountWithoutClones: 0, - particlesToShow: 1, // normalized - particlesToShowInit: 1, // initial value - particlesToScroll: 1, // normalized - particlesToScrollInit: 1, // initial value - initialPageIndex: 1, // normalized - initialPageIndexInit: 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: [], - }, - { - setCurrentPageIndex: (data) => { - data.currentPageIndex = getCurrentPageIndexByCurrentParticleIndex({ - currentParticleIndex: data.currentParticleIndex, - particlesCount: data.particlesCount, - clonesCountHead: data.clonesCountHead, - clonesCountTotal: data.clonesCountTotal, - infinite: data.initialPageIndex, - particlesToScroll: data.particlesToScroll, - }) + data: { + particlesCountWithoutClones: 0, + particlesToShow: 1, // normalized + particlesToShowInit: 1, // initial value + particlesToScroll: 1, // normalized + particlesToScrollInit: 1, // initial value + initialPageIndex: 0, + 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: [], }, - 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 - }, - setPagesCount: (data) => { - data.pagesCount = getPagesCountByParticlesCount({ - infinite: data.infinite, - particlesCountWithoutClones: data.particlesCountWithoutClones, - particlesToScroll: data.particlesToScroll, - }) - }, - setProgressManagerAutoplayDuration: (data) => { - progressManager.setAutoplayDuration(data.autoplayDuration) - }, - toggleProgressManager: ({ pauseOnFocus, focused }) => { - if (pauseOnFocus) { - if (focused) { - progressManager.pause() - } else { - progressManager.resume() + 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, { _applyAutoplayIfNeeded }) => { - // prevent _applyAutoplayIfNeeded to be called with watcher - // to prevent its data added to deps - data.autoplay && _applyAutoplayIfNeeded(data.autoplay) - }, - setParticlesToShow(data) { - data.particlesToShow = getValueInRange( - 1, - data.particlesToShowInit, - data.particlesCountWithoutClones - ) - }, - setParticlesToScroll(data) { - data.particlesToScroll = getValueInRange( - 1, - data.particlesToScrollInit, - data.particlesCountWithoutClones - ) - }, - setInitialPageIndex(data) { - data.initialPageIndex = getValueInRange( - 1, - data.initialPageIndexInit, - data.pagesCount - ) - }, - setLoaded(data) { - data.loaded = getAdjacentIndexes({ - infinite: data.infinite, - pageIndex: data.currentPageIndex, - pagesCount: data.pagesCount, - particlesCount: data.particlesCountWithoutClones, - particlesToShow: data.particlesToShow, - particlesToScroll: data.particlesToScroll, - }).particleIndexes - }, - }, - { - _prev: (data) => { - const newCurrentParticleIndex = getParticleIndexByPageIndex({ - infinite: data.infinite, - pageIndex: data.currentPageIndex - 1, - clonesCountHead: data.clonesCountHead, - clonesCountTail: data.clonesCountTail, - particlesToScroll: data.particlesToScroll, - particlesCount: data.particlesCount, - particlesToShow: data.particlesToShow, - }) - data.currentParticleIndex = newCurrentParticleIndex - }, - _next: (data) => { - const newCurrentParticleIndex = getParticleIndexByPageIndex({ - infinite: data.infinite, - pageIndex: data.currentPageIndex + 1, - clonesCountHead: data.clonesCountHead, - clonesCountTail: data.clonesCountTail, - particlesToScroll: data.particlesToScroll, - particlesCount: data.particlesCount, - particlesToShow: data.particlesToShow, - }) - data.currentParticleIndex = newCurrentParticleIndex - }, - _moveToParticle: (data, _, particleIndex) => { - const newCurrentParticleIndex = getValueInRange( - 0, - particleIndex, - data.particlesCount - 1 - ) - data.currentParticleIndex = newCurrentParticleIndex - }, - toggleFocused: (data) => { - data.focused = !data.focused - }, - async _applyAutoplayIfNeeded( - { - infinite, - autoplayDirection, - currentParticleIndex, - particlesCount, - autoplay, }, - { showNextPage, showPrevPage } - ) { - // 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) { - const onFinish = () => - switcher({ - [NEXT]: async () => showNextPage(), - [PREV]: async () => showPrevPage(), - })(autoplayDirection) - - await progressManager.start(onFinish) - } - }, - // makes delayed jump to 1st or last element - _jumpIfNeeded: async ( - { - infinite, - currentParticleIndex, - particlesCount, - clonesCountTotal, - clonesCountTail, - clonesCountHead, + initDuration({ data }) { + data.durationMs = data.durationMsInit }, - { showParticle } - ) => { - let jumped = false - if (infinite) { - if (currentParticleIndex === 0) { - await showParticle(particlesCount - clonesCountTotal, { - animated: false, - }) - jumped = true - } else if ( - currentParticleIndex === - particlesCount - clonesCountTail + applyAutoplay({ data, methods: { _applyAutoplayIfNeeded } }) { + // prevent _applyAutoplayIfNeeded to be called with watcher + // to prevent its data added to deps + data.autoplay && _applyAutoplayIfNeeded(data.autoplay) + }, + setParticlesToShow({ data }) { + data.particlesToShow = getValueInRange( + 1, + data.particlesToShowInit, + data.particlesCountWithoutClones + ) + }, + setPagesCount({ data }) { + data.pagesCount = getPagesCountByParticlesCount({ + infinite: data.infinite, + particlesCountWithoutClones: data.particlesCountWithoutClones, + particlesToScroll: data.particlesToScroll, + }) + }, + setParticlesToScroll({ data }) { + data.particlesToScroll = getValueInRange( + 1, + data.particlesToScrollInit, + data.particlesCountWithoutClones + ) + }, + setInitialPageIndex({ data, methods: { showPage } }) { + const ind = getValueInRange(0, data.initialPageIndex, data.pagesCount) + console.log('ind', data.initialPageIndex, ind) + showPage(5, { + animated: false, + }) + }, + }, + methods: { + _prev({ data }) { + const newCurrentParticleIndex = getParticleIndexByPageIndex({ + infinite: data.infinite, + pageIndex: data.currentPageIndex - 1, + clonesCountHead: data.clonesCountHead, + clonesCountTail: data.clonesCountTail, + particlesToScroll: data.particlesToScroll, + particlesCount: data.particlesCount, + particlesToShow: data.particlesToShow, + }) + data.currentParticleIndex = newCurrentParticleIndex + }, + _next({ data }) { + const newCurrentParticleIndex = getParticleIndexByPageIndex({ + infinite: data.infinite, + pageIndex: data.currentPageIndex + 1, + clonesCountHead: data.clonesCountHead, + clonesCountTail: data.clonesCountTail, + particlesToScroll: data.particlesToScroll, + particlesCount: data.particlesCount, + particlesToShow: data.particlesToShow, + }) + data.currentParticleIndex = newCurrentParticleIndex + }, + _moveToParticle({ data }, particleIndex) { + const newCurrentParticleIndex = getValueInRange( + 0, + particleIndex, + data.particlesCount - 1 + ) + data.currentParticleIndex = newCurrentParticleIndex + }, + 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)) ) { - await showParticle(clonesCountHead, { - animated: false, - }) - jumped = true + progressManager.reset() + return } - } - return jumped - }, - changePage: async ( - data, - { offsetPage, _applyAutoplayIfNeeded, _jumpIfNeeded }, - updateStoreFn, - options - ) => { - progressManager.reset() - if (data.disabled) return - data.disabled = true + if (data.autoplay) { + const onFinish = () => + switcher({ + [NEXT]: async () => methods.showNextPage(), + [PREV]: async () => methods.showPrevPage(), + })(data.autoplayDirection) - updateStoreFn() - await offsetPage({ animated: get(options, 'animated', true) }) - data.disabled = false + 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 - const jumped = await _jumpIfNeeded() - !jumped && _applyAutoplayIfNeeded() // no need to wait it finishes - }, - showNextPage: async ({ disabled }, { changePage, _next }, options) => { - if (disabled) return - await changePage(_next, options) - }, - showPrevPage: async ({ disabled }, { changePage, _prev }, options) => { - if (disabled) return - await changePage(_prev, options) - }, - showParticle: async ( - _, - { changePage, _moveToParticle }, - particleIndex, - options - ) => { - await changePage(() => _moveToParticle(particleIndex), options) - }, - showPage: async (data, { showParticle }, pageIndex, options) => { - await showParticle( - getParticleIndexByPageIndex({ + 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 + ) + }, + async showPage({ data, methods }, pageIndex, options) { + const ind = getParticleIndexByPageIndex({ infinite: data.infinite, pageIndex, clonesCountHead: data.clonesCountHead, @@ -280,23 +256,26 @@ function createCarousel(onChange) { particlesToScroll: data.particlesToScroll, particlesCount: data.particlesCount, particlesToShow: data.particlesToShow, - }), - 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) - }) + }) + console.log('ParticleIndex', ind) + await methods.showParticle(ind, 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 + { + onChange, + } ) return [{ data, progressManager }, methods, service] diff --git a/src/components/Carousel/reactive.js b/src/components/Carousel/reactive.js deleted file mode 100644 index a74cd7a..0000000 --- a/src/components/Carousel/reactive.js +++ /dev/null @@ -1,111 +0,0 @@ -// Code that has to run when a -// reactive property changes it's value. - -const objectsAreSame = (x, y) => { - // return false - let _objectsAreSame = true - for (let propertyName in x) { - if (Number.isNaN(x[propertyName]) || Number.isNaN(y[propertyName])) { - continue - } - if (x[propertyName] !== y[propertyName]) { - _objectsAreSame = false - break - } - } - - return _objectsAreSame -} - -const getObject = (oldData, newData) => { - const newDeps = {} - - Object.entries(oldData).forEach(([key, value]) => { - // console.log('oldData', key, value) - newDeps[key] = newData[key] - }) - // console.log('isDiff', oldData, newDeps) - - return newDeps -} - -const useSubscription = () => { - const subscribers = {} - - const memoDependency = (target, dep) => { - const { watcherName, fn } = target - const { key, value } = dep - - if (!subscribers[watcherName]) { - subscribers[watcherName] = { - deps: {}, - fn, - } - } - subscribers[watcherName].deps[key] = value - } - - return { - subscribe: (target, dep) => { - if (target) { - memoDependency(target, dep) - } - }, - notify: (data) => { - Object.entries(subscribers).forEach(([watcherName, { deps }]) => { - const newDeps = getObject(deps, data) - if (!objectsAreSame(deps, newDeps)) { - subscribers[watcherName].deps = newDeps - subscribers[watcherName].fn() - } - }) - }, - } -} - -const useWatcher = () => { - let target = null - - return { - watch: (watcherName, fn) => { - target = { - watcherName, - fn, - } - target.fn() - target = null - }, - getTarget: () => { - return target - }, - } -} - -export const reactive = (data, watchers, methods, onChange) => { - const { subscribe, notify } = useSubscription() - const { watch, getTarget } = useWatcher() - - const _data = new Proxy(data, { - get(target, key) { - subscribe(getTarget(), { key, value: target[key] }) - return Reflect.get(...arguments) - }, - set(_, key, value) { - Reflect.set(...arguments) - onChange && onChange(key, value) - notify(_data) - return true - }, - }) - - Object.entries(watchers).forEach(([watcherName, watcher]) => { - watch(watcherName, () => watcher(_data)) - }) - - const _methods = {} - Object.entries(methods).forEach(([methodName, method]) => { - _methods[methodName] = (...args) => method(_data, _methods, ...args) - }) - - return [_data, _methods] -}