Add reactivity
This commit is contained in:
@@ -31,22 +31,18 @@
|
||||
import { wait } from '../../utils/interval'
|
||||
|
||||
import { carouselEngine } from './carousel'
|
||||
import { carousel2 } from './carousel2'
|
||||
import { reactive } from './reactive'
|
||||
|
||||
const data = reactive(
|
||||
{
|
||||
message: 'hello',
|
||||
someValue: 'some-value'
|
||||
},
|
||||
{
|
||||
renderFunction: (data) => {
|
||||
console.log('renderFunction watcher called', data.message)
|
||||
},
|
||||
}
|
||||
)
|
||||
console.log('console', data)
|
||||
let currentPageIndex
|
||||
|
||||
const [data, methods] = carousel2((key, value) => {
|
||||
// console.log('onChange', key, value)
|
||||
if (key === 'currentPageIndex') {
|
||||
currentPageIndex = value
|
||||
}
|
||||
}) // put init data
|
||||
|
||||
data.message = 'Hello!'
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
@@ -76,7 +72,7 @@
|
||||
*/
|
||||
export let infinite
|
||||
$: {
|
||||
setInfinite(infinite)
|
||||
data.infinite = infinite
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -151,13 +147,13 @@
|
||||
throw new Error('pageIndex should be a number')
|
||||
}
|
||||
await showParticle(getParticleIndexByPageIndex({
|
||||
infinite,
|
||||
infinite: data.infinite,
|
||||
pageIndex,
|
||||
clonesCountHead: clonesCount.head,
|
||||
clonesCountTail: clonesCount.tail,
|
||||
particlesToScroll,
|
||||
particlesCount,
|
||||
particlesToShow,
|
||||
clonesCountHead: data.clonesCountHead,
|
||||
clonesCountTail: data.clonesCountTail,
|
||||
particlesToScroll: data.particlesToScroll,
|
||||
particlesCount: data.particlesCount,
|
||||
particlesToShow: data.particlesToShow,
|
||||
}), { animated })
|
||||
}
|
||||
|
||||
@@ -171,44 +167,6 @@
|
||||
await showNextPage({ animated })
|
||||
}
|
||||
|
||||
|
||||
///// ==========================
|
||||
let currentPageIndex
|
||||
let pagesCount
|
||||
let particlesCountWithoutClones
|
||||
let clonesCount
|
||||
let currentParticleIndex = 0
|
||||
let particlesCount = 0
|
||||
let _particlesToShow
|
||||
let _particlesToScroll
|
||||
|
||||
const {
|
||||
setParticlesToShow,
|
||||
setParticlesToScroll,
|
||||
setParticlesCountWithoutClones,
|
||||
setPartialPageSize,
|
||||
setParticlesCount,
|
||||
setCurrentParticleIndex,
|
||||
setInitialPageIndex,
|
||||
setInfinite,
|
||||
prev,
|
||||
next,
|
||||
moveToParticle,
|
||||
} = carouselEngine((values, computed) => {
|
||||
// console.log('hello')
|
||||
currentPageIndex = computed.currentPageIndex
|
||||
pagesCount = computed.pagesCount
|
||||
particlesCountWithoutClones = values.particlesCountWithoutClones
|
||||
clonesCount = computed.clonesCount
|
||||
currentParticleIndex = values.currentParticleIndex
|
||||
particlesCount = values.particlesCount
|
||||
_particlesToShow = values.particlesToShow
|
||||
_particlesToScroll = values.particlesToScroll
|
||||
})
|
||||
|
||||
///// ==========================
|
||||
|
||||
|
||||
let pageWindowWidth = 0
|
||||
let particleWidth = 0
|
||||
let offset = 0
|
||||
@@ -220,7 +178,7 @@
|
||||
width,
|
||||
}) => {
|
||||
pageWindowWidth = width
|
||||
particleWidth = pageWindowWidth / _particlesToShow
|
||||
particleWidth = pageWindowWidth / data.particlesToShow
|
||||
|
||||
applyParticleSizes({
|
||||
particlesContainerChildren: particlesContainer.children,
|
||||
@@ -267,8 +225,8 @@
|
||||
clonesToAppend,
|
||||
clonesToPrepend,
|
||||
} = getClones({
|
||||
clonesCountHead: clonesCount.head,
|
||||
clonesCountTail: clonesCount.tail,
|
||||
clonesCountHead: data.clonesCountHead,
|
||||
clonesCountTail: data.clonesCountTail,
|
||||
particlesContainerChildren: particlesContainer.children,
|
||||
})
|
||||
applyClones({
|
||||
@@ -281,9 +239,9 @@
|
||||
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)
|
||||
!data.infinite && (
|
||||
(autoplayDirection === NEXT && data.currentParticleIndex === data.particlesCount - 1) ||
|
||||
(autoplayDirection === PREV && data.currentParticleIndex === 0)
|
||||
)
|
||||
) {
|
||||
progressManager.reset()
|
||||
@@ -302,16 +260,16 @@
|
||||
await tick()
|
||||
cleanupFns.push(() => progressManager.reset())
|
||||
if (particlesContainer && pageWindowElement) {
|
||||
setParticlesCountWithoutClones(particlesContainer.children.length)
|
||||
setParticlesToShow(particlesToShow)
|
||||
setParticlesToScroll(particlesToScroll)
|
||||
data.particlesCountWithoutClones = particlesContainer.children.length
|
||||
data.particlesToShow = particlesToShow
|
||||
data.particlesToScroll = particlesToScroll
|
||||
|
||||
await tick()
|
||||
infinite && addClones()
|
||||
data.infinite && addClones()
|
||||
|
||||
// call after adding clones
|
||||
setParticlesCount(particlesContainer.children.length)
|
||||
setInitialPageIndex(initialPageIndex)
|
||||
data.particlesCount = particlesContainer.children.length
|
||||
data.initialPageIndex = initialPageIndex
|
||||
|
||||
pageWindowElementResizeObserver.observe(pageWindowElement);
|
||||
}
|
||||
@@ -325,13 +283,13 @@
|
||||
|
||||
async function handlePageChange(pageIndex) {
|
||||
await showParticle(getParticleIndexByPageIndex({
|
||||
infinite,
|
||||
infinite: data.infinite,
|
||||
pageIndex,
|
||||
clonesCountHead: clonesCount.head,
|
||||
clonesCountTail: clonesCount.tail,
|
||||
particlesToScroll,
|
||||
particlesCount,
|
||||
particlesToShow: _particlesToShow,
|
||||
clonesCountHead: data.clonesCountHead,
|
||||
clonesCountTail: data.clonesCountTail,
|
||||
particlesToScroll: data.particlesToScroll,
|
||||
particlesCount: data.particlesCount,
|
||||
particlesToShow: data.particlesToShow,
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -340,7 +298,7 @@
|
||||
return new Promise((resolve) => {
|
||||
// _duration is an offset animation time
|
||||
_duration = animated ? duration : 0
|
||||
offset = -currentParticleIndex * particleWidth
|
||||
offset = -data.currentParticleIndex * particleWidth
|
||||
setTimeout(() => {
|
||||
resolve()
|
||||
}, _duration)
|
||||
@@ -350,12 +308,12 @@
|
||||
// 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 })
|
||||
if (data.infinite) {
|
||||
if (data.currentParticleIndex === 0) {
|
||||
await showParticle(data.particlesCount - data.clonesCountTotal, { animated: false })
|
||||
jumped = true
|
||||
} else if (currentParticleIndex === particlesCount - clonesCount.tail) {
|
||||
await showParticle(clonesCount.head, { animated: false })
|
||||
} else if (data.currentParticleIndex === data.particlesCount - data.clonesCountTail) {
|
||||
await showParticle(data.clonesCountHead, { animated: false })
|
||||
jumped = true
|
||||
}
|
||||
}
|
||||
@@ -378,8 +336,9 @@
|
||||
}
|
||||
|
||||
async function showParticle(particleIndex, options) {
|
||||
console.log('showParticle => particleIndex', particleIndex)
|
||||
await changePage(
|
||||
() => moveToParticle(particleIndex),
|
||||
() => methods.moveToParticle(particleIndex),
|
||||
options
|
||||
)
|
||||
}
|
||||
@@ -387,7 +346,7 @@
|
||||
if (disabled) return
|
||||
|
||||
await changePage(
|
||||
prev,
|
||||
methods.prev,
|
||||
options,
|
||||
)
|
||||
}
|
||||
@@ -395,7 +354,7 @@
|
||||
if (disabled) return
|
||||
|
||||
await changePage(
|
||||
next,
|
||||
methods.next,
|
||||
options,
|
||||
)
|
||||
}
|
||||
@@ -415,7 +374,7 @@
|
||||
}
|
||||
function handleSwipeEnd() {
|
||||
if (!swiping) return
|
||||
showParticle(currentParticleIndex)
|
||||
showParticle(data.currentParticleIndex)
|
||||
}
|
||||
async function handleSwipeFailed() {
|
||||
if (!swiping) return
|
||||
@@ -437,7 +396,7 @@
|
||||
<div class="sc-carousel__arrow-container">
|
||||
<Arrow
|
||||
direction="prev"
|
||||
disabled={!infinite && currentPageIndex === 0}
|
||||
disabled={!data.infinite && currentPageIndex === 0}
|
||||
on:click={showPrevPage}
|
||||
/>
|
||||
</div>
|
||||
@@ -481,7 +440,7 @@
|
||||
<div class="sc-carousel__arrow-container">
|
||||
<Arrow
|
||||
direction="next"
|
||||
disabled={!infinite && currentPageIndex === pagesCount - 1}
|
||||
disabled={!data.infinite && currentPageIndex === data.pagesCount - 1}
|
||||
on:click={showNextPage}
|
||||
/>
|
||||
</div>
|
||||
@@ -492,11 +451,11 @@
|
||||
<slot
|
||||
name="dots"
|
||||
currentPageIndex={currentPageIndex}
|
||||
pagesCount={pagesCount}
|
||||
pagesCount={data.pagesCount}
|
||||
showPage={handlePageChange}
|
||||
>
|
||||
<Dots
|
||||
pagesCount={pagesCount}
|
||||
pagesCount={data.pagesCount}
|
||||
currentPageIndex={currentPageIndex}
|
||||
on:pageChange={event => handlePageChange(event.detail)}
|
||||
></Dots>
|
||||
|
||||
111
src/components/Carousel/carousel2.js
Normal file
111
src/components/Carousel/carousel2.js
Normal file
@@ -0,0 +1,111 @@
|
||||
import { NEXT, PREV } from '../../direction'
|
||||
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 { reactive } from './reactive'
|
||||
|
||||
// return only getters
|
||||
export const carousel2 = (onChange) => {
|
||||
return reactive(
|
||||
{
|
||||
particlesCountWithoutClones: 0,
|
||||
particlesToShow: 1,
|
||||
particlesToScroll: 1,
|
||||
initialPageIndex: 1,
|
||||
particlesCount: 1,
|
||||
currentParticleIndex: 1,
|
||||
infinite: false,
|
||||
clonesCountHead: 0,
|
||||
clonesCountTail: 0,
|
||||
clonesCountTotal: 0,
|
||||
partialPageSize: 1,
|
||||
currentPageIndex: 1,
|
||||
pagesCount: 1,
|
||||
},
|
||||
{
|
||||
setCurrentPageIndex: (data) => {
|
||||
const ind = getCurrentPageIndexByCurrentParticleIndex({
|
||||
currentParticleIndex: data.currentParticleIndex,
|
||||
particlesCount: data.particlesCount,
|
||||
clonesCountHead: data.clonesCountHead,
|
||||
clonesCountTotal: data.clonesCountTotal,
|
||||
infinite: data.initialPageIndex,
|
||||
particlesToScroll: data.particlesToScroll,
|
||||
})
|
||||
data.currentPageIndex = ind
|
||||
console.log('===> data.currentPageIndex', ind)
|
||||
},
|
||||
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,
|
||||
})
|
||||
},
|
||||
},
|
||||
{
|
||||
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
|
||||
},
|
||||
},
|
||||
onChange
|
||||
)
|
||||
}
|
||||
@@ -1,80 +1,116 @@
|
||||
// let data = {
|
||||
// message: '',
|
||||
// }
|
||||
|
||||
// let methods = {
|
||||
// changeMessage: function () {
|
||||
// data.message = document.getElementById('messageInput').value
|
||||
// },
|
||||
// }
|
||||
|
||||
// Code that has to run when a
|
||||
// reactive property changes it's value.
|
||||
let target = null
|
||||
|
||||
class Dep {
|
||||
constructor() {
|
||||
this.subscribers = []
|
||||
const objectsAreSame = (x, y) => {
|
||||
// return false
|
||||
let _objectsAreSame = true
|
||||
for (let propertyName in x) {
|
||||
if (Number.isNaN(x[propertyName]) || Number.isNaN(y[propertyName])) {
|
||||
continue
|
||||
}
|
||||
depend() {
|
||||
// Saves target function into subscribers array
|
||||
if (target && !this.subscribers.includes(target)) {
|
||||
this.subscribers.push(target)
|
||||
if (x[propertyName] !== y[propertyName]) {
|
||||
_objectsAreSame = false
|
||||
break
|
||||
}
|
||||
}
|
||||
notify() {
|
||||
// Replays target functions saved in the subscribers array
|
||||
this.subscribers.forEach((sub) => sub())
|
||||
|
||||
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()
|
||||
}
|
||||
})
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
let watch = function (func) {
|
||||
// Here, a watcher is a function that encapsulates the code
|
||||
// that needs to recorded/watched.
|
||||
// PS: It just runs once, because after that, the target code is stored
|
||||
// in the subscriber's list of the Dep() instance.
|
||||
target = func // Then it assigns the function to target
|
||||
target() // Run the target function
|
||||
target = null // Reset target to null
|
||||
const useWatcher = () => {
|
||||
let target = null
|
||||
|
||||
return {
|
||||
watch: (watcherName, fn) => {
|
||||
target = {
|
||||
watcherName,
|
||||
fn,
|
||||
}
|
||||
target.fn()
|
||||
target = null
|
||||
},
|
||||
getTarget: () => {
|
||||
return target
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// reactive(
|
||||
// {
|
||||
// message: '',
|
||||
// },
|
||||
// {
|
||||
// renderFunction: (data) => {
|
||||
// console.log(data.message)
|
||||
// },
|
||||
// }
|
||||
// )
|
||||
export const reactive = (data, watchers, methods, onChange) => {
|
||||
const { subscribe, notify } = useSubscription()
|
||||
const { watch, getTarget } = useWatcher()
|
||||
|
||||
export const reactive = (data, watchers, methods) => {
|
||||
const _data = {}
|
||||
|
||||
Object.keys(data).forEach((key) => {
|
||||
let internalValue = data[key]
|
||||
|
||||
// Each property gets a dependency instance
|
||||
const dep = new Dep()
|
||||
let currentValue = data[key]
|
||||
|
||||
Object.defineProperty(_data, key, {
|
||||
get() {
|
||||
console.log(`Getting value, ${internalValue}`)
|
||||
dep.depend() // Saves the target function into the subscribers array
|
||||
return internalValue
|
||||
subscribe(getTarget(), { key, value: currentValue })
|
||||
return currentValue
|
||||
},
|
||||
set(newVal) {
|
||||
console.log(`Setting the internalValue to ${newVal}`)
|
||||
internalValue = newVal
|
||||
dep.notify() // Reruns saved target functions in the subscribers array
|
||||
currentValue = newVal
|
||||
onChange && onChange(key, newVal)
|
||||
notify(_data)
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
Object.entries(watchers).forEach(([watcherName, watcher]) => {
|
||||
watch(() => watcher(_data))
|
||||
watch(watcherName, () => watcher(_data))
|
||||
})
|
||||
|
||||
return _data
|
||||
const _methods = {}
|
||||
Object.entries(methods).forEach(([methodName, method]) => {
|
||||
_methods[methodName] = (args) => method(_data, args)
|
||||
})
|
||||
|
||||
return [_data, _methods]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user