Fix adding extra deps

This commit is contained in:
Vadim
2021-10-09 10:30:27 +03:00
parent 87a60678ba
commit de213cba88
3 changed files with 239 additions and 371 deletions

View File

@@ -79,7 +79,8 @@
*/ */
export let initialPageIndex = 0 export let initialPageIndex = 0
$: { $: {
data.initialPageIndexInit = initialPageIndex console.log('initialPageIndex', initialPageIndex)
data.initialPageIndex = initialPageIndex
} }
/** /**
@@ -217,7 +218,6 @@
// call after adding clones // call after adding clones
data.particlesCount = particlesContainer.children.length data.particlesCount = particlesContainer.children.length
data.initialPageIndex = initialPageIndex
pageWindowElementResizeObserver.observe(pageWindowElement); pageWindowElementResizeObserver.observe(pageWindowElement);
} }

View File

@@ -1,4 +1,4 @@
import simplyReactive from 'simply-reactive' import simplyReactive from '../../../../simply-reactive/main'
import { NEXT, PREV } from '../../direction' import { NEXT, PREV } from '../../direction'
import { import {
@@ -22,257 +22,233 @@ function createCarousel(onChange) {
const [data, methods, service] = simplyReactive( const [data, methods, service] = simplyReactive(
{ {
particlesCountWithoutClones: 0, data: {
particlesToShow: 1, // normalized particlesCountWithoutClones: 0,
particlesToShowInit: 1, // initial value particlesToShow: 1, // normalized
particlesToScroll: 1, // normalized particlesToShowInit: 1, // initial value
particlesToScrollInit: 1, // initial value particlesToScroll: 1, // normalized
initialPageIndex: 1, // normalized particlesToScrollInit: 1, // initial value
initialPageIndexInit: 1, // initial value initialPageIndex: 0,
particlesCount: 1, particlesCount: 1,
currentParticleIndex: 1, currentParticleIndex: 1,
infinite: false, infinite: false,
autoplayDuration: 1000, autoplayDuration: 1000,
clonesCountHead: 0, clonesCountHead: 0,
clonesCountTail: 0, clonesCountTail: 0,
clonesCountTotal: 0, clonesCountTotal: 0,
partialPageSize: 1, partialPageSize: 1,
currentPageIndex: 1, currentPageIndex: 1,
pagesCount: 1, pagesCount: 1,
pauseOnFocus: false, pauseOnFocus: false,
focused: false, focused: false,
autoplay: false, autoplay: false,
autoplayDirection: 'next', autoplayDirection: 'next',
disabled: false, // Disable page change while animation is in progress disabled: false, // Disable page change while animation is in progress
durationMsInit: 1000, durationMsInit: 1000,
durationMs: 1000, durationMs: 1000,
offset: 0, offset: 0,
particleWidth: 0, particleWidth: 0,
loaded: [], loaded: [],
},
{
setCurrentPageIndex: (data) => {
data.currentPageIndex = getCurrentPageIndexByCurrentParticleIndex({
currentParticleIndex: data.currentParticleIndex,
particlesCount: data.particlesCount,
clonesCountHead: data.clonesCountHead,
clonesCountTotal: data.clonesCountTotal,
infinite: data.initialPageIndex,
particlesToScroll: data.particlesToScroll,
})
}, },
setPartialPageSize: (data) => { watch: {
data.partialPageSize = getPartialPageSize({ setLoaded({ data }) {
particlesToScroll: data.particlesToScroll, data.loaded = getAdjacentIndexes({
particlesToShow: data.particlesToShow, infinite: data.infinite,
particlesCountWithoutClones: data.particlesCountWithoutClones, pageIndex: data.currentPageIndex,
}) pagesCount: data.pagesCount,
}, particlesCount: data.particlesCountWithoutClones,
setClonesCount: (data) => { particlesToShow: data.particlesToShow,
const { head, tail } = getClonesCount({ particlesToScroll: data.particlesToScroll,
infinite: data.infinite, }).particleIndexes
particlesToShow: data.particlesToShow, },
partialPageSize: data.partialPageSize, setCurrentPageIndex({ data }) {
}) data.currentPageIndex = getCurrentPageIndexByCurrentParticleIndex({
currentParticleIndex: data.currentParticleIndex,
data.clonesCountHead = head particlesCount: data.particlesCount,
data.clonesCountTail = tail clonesCountHead: data.clonesCountHead,
data.clonesCountTotal = head + tail clonesCountTotal: data.clonesCountTotal,
}, infinite: data.infinite,
setPagesCount: (data) => { particlesToScroll: data.particlesToScroll,
data.pagesCount = getPagesCountByParticlesCount({ })
infinite: data.infinite, },
particlesCountWithoutClones: data.particlesCountWithoutClones, setPartialPageSize({ data }) {
particlesToScroll: data.particlesToScroll, data.partialPageSize = getPartialPageSize({
}) particlesToScroll: data.particlesToScroll,
}, particlesToShow: data.particlesToShow,
setProgressManagerAutoplayDuration: (data) => { particlesCountWithoutClones: data.particlesCountWithoutClones,
progressManager.setAutoplayDuration(data.autoplayDuration) })
}, },
toggleProgressManager: ({ pauseOnFocus, focused }) => { setClonesCount({ data }) {
if (pauseOnFocus) { const { head, tail } = getClonesCount({
if (focused) { infinite: data.infinite,
progressManager.pause() particlesToShow: data.particlesToShow,
} else { partialPageSize: data.partialPageSize,
progressManager.resume() })
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 } initDuration({ data }) {
) { data.durationMs = data.durationMsInit
// 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,
}, },
{ showParticle } applyAutoplay({ data, methods: { _applyAutoplayIfNeeded } }) {
) => { // prevent _applyAutoplayIfNeeded to be called with watcher
let jumped = false // to prevent its data added to deps
if (infinite) { data.autoplay && _applyAutoplayIfNeeded(data.autoplay)
if (currentParticleIndex === 0) { },
await showParticle(particlesCount - clonesCountTotal, { setParticlesToShow({ data }) {
animated: false, data.particlesToShow = getValueInRange(
}) 1,
jumped = true data.particlesToShowInit,
} else if ( data.particlesCountWithoutClones
currentParticleIndex === )
particlesCount - clonesCountTail },
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, { progressManager.reset()
animated: false, return
})
jumped = true
} }
}
return jumped
},
changePage: async ( if (data.autoplay) {
data, const onFinish = () =>
{ offsetPage, _applyAutoplayIfNeeded, _jumpIfNeeded }, switcher({
updateStoreFn, [NEXT]: async () => methods.showNextPage(),
options [PREV]: async () => methods.showPrevPage(),
) => { })(data.autoplayDirection)
progressManager.reset()
if (data.disabled) return
data.disabled = true
updateStoreFn() await progressManager.start(onFinish)
await offsetPage({ animated: get(options, 'animated', true) }) }
data.disabled = false },
// 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() updateStoreFn()
!jumped && _applyAutoplayIfNeeded() // no need to wait it finishes await methods.offsetPage({ animated: get(options, 'animated', true) })
}, data.disabled = false
showNextPage: async ({ disabled }, { changePage, _next }, options) => {
if (disabled) return const jumped = await methods._jumpIfNeeded()
await changePage(_next, options) !jumped && methods._applyAutoplayIfNeeded() // no need to wait it finishes
}, },
showPrevPage: async ({ disabled }, { changePage, _prev }, options) => { async showNextPage({ data, methods }, options) {
if (disabled) return if (data.disabled) return
await changePage(_prev, options) await methods.changePage(methods._next, options)
}, },
showParticle: async ( async showPrevPage({ data, methods }, options) {
_, if (data.disabled) return
{ changePage, _moveToParticle }, await methods.changePage(methods._prev, options)
particleIndex, },
options async showParticle({ methods }, particleIndex, options) {
) => { await methods.changePage(
await changePage(() => _moveToParticle(particleIndex), options) () => methods._moveToParticle(particleIndex),
}, options
showPage: async (data, { showParticle }, pageIndex, options) => { )
await showParticle( },
getParticleIndexByPageIndex({ async showPage({ data, methods }, pageIndex, options) {
const ind = getParticleIndexByPageIndex({
infinite: data.infinite, infinite: data.infinite,
pageIndex, pageIndex,
clonesCountHead: data.clonesCountHead, clonesCountHead: data.clonesCountHead,
@@ -280,23 +256,26 @@ function createCarousel(onChange) {
particlesToScroll: data.particlesToScroll, particlesToScroll: data.particlesToScroll,
particlesCount: data.particlesCount, particlesCount: data.particlesCount,
particlesToShow: data.particlesToShow, particlesToShow: data.particlesToShow,
}), })
options console.log('ParticleIndex', ind)
) await methods.showParticle(ind, options)
}, },
offsetPage(data, _, options) { offsetPage({ data }, options) {
const animated = get(options, 'animated', true) const animated = get(options, 'animated', true)
return new Promise((resolve) => { return new Promise((resolve) => {
// durationMs is an offset animation time // durationMs is an offset animation time
data.durationMs = animated ? data.durationMsInit : 0 data.durationMs = animated ? data.durationMsInit : 0
data.offset = -data.currentParticleIndex * data.particleWidth data.offset = -data.currentParticleIndex * data.particleWidth
setTimeout(() => { setTimeout(() => {
resolve() resolve()
}, data.durationMs) }, data.durationMs)
}) })
},
}, },
}, },
onChange {
onChange,
}
) )
return [{ data, progressManager }, methods, service] return [{ data, progressManager }, methods, service]

View File

@@ -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]
}