Add reactive fields

This commit is contained in:
Vadim
2021-10-05 12:03:30 +03:00
parent 28f24f5e0c
commit e80ea005d1
4 changed files with 381 additions and 179 deletions

View File

@@ -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'
@@ -31,6 +30,24 @@
import { ProgressManager } from '../../utils/ProgressManager'
import { wait } from '../../utils/interval'
import { carouselEngine } from './carousel'
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)
data.message = 'Hello!'
const dispatch = createEventDispatcher()
const autoplayDirectionFnDescription = {
@@ -57,7 +74,10 @@
/**
* Infinite looping
*/
export let infinite = true
export let infinite
$: {
setInfinite(infinite)
}
/**
* Page to start on
@@ -112,11 +132,18 @@
* Number of particles to show
*/
export let particlesToShow = 1
$: {
console.log('particlesToShow changed')
// setParticlesToShow(particlesToShow)
}
/**
* Number of particles to scroll
*/
export let particlesToScroll = 1
$: {
// setParticlesToScroll(particlesToScroll)
}
export async function goTo(pageIndex, options) {
const animated = get(options, 'animated', true)
@@ -144,38 +171,48 @@
await showNextPage({ animated })
}
let store = createStore()
$: clonesCount = getClonesCount({
infinite,
particlesToShow,
partialPageSize,
})
///// ==========================
let currentPageIndex
let pagesCount
let particlesCountWithoutClones
let clonesCount
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 _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 partialPageSize = 0
///// ==========================
let pageWindowWidth = 0
let particleWidth = 0
let offset = 0
let pageWindowElement
let particlesContainer
@@ -183,7 +220,7 @@
width,
}) => {
pageWindowWidth = width
particleWidth = pageWindowWidth / particlesToShow
particleWidth = pageWindowWidth / _particlesToShow
applyParticleSizes({
particlesContainerChildren: particlesContainer.children,
@@ -215,14 +252,15 @@
}
// used for lazy loading images, preloaded only current, adjacent and cloanable images
$: loaded = getAdjacentIndexes({
infinite,
pageIndex: currentPageIndex,
pagesCount,
particlesCount: particlesCountWithoutClones,
particlesToShow,
particlesToScroll,
}).particleIndexes
let loaded = []
// $: loaded = getAdjacentIndexes({
// infinite,
// pageIndex: currentPageIndex,
// pagesCount,
// particlesCount: particlesCountWithoutClones,
// particlesToShow: _particlesToShow,
// particlesToScroll,
// }).particleIndexes
function addClones() {
const {
@@ -262,36 +300,18 @@
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,
})
setParticlesCountWithoutClones(particlesContainer.children.length)
setParticlesToShow(particlesToShow)
setParticlesToScroll(particlesToScroll)
await tick()
infinite && addClones()
particlesCount = particlesContainer.children.length
store.init(getParticleIndexByPageIndex({
infinite,
pageIndex: initialPageIndex,
clonesCountHead: clonesCount.head,
clonesCountTail: clonesCount.tail,
particlesToScroll,
particlesCount,
particlesToShow,
}))
// call after adding clones
setParticlesCount(particlesContainer.children.length)
setInitialPageIndex(initialPageIndex)
pageWindowElementResizeObserver.observe(pageWindowElement);
}
@@ -311,7 +331,7 @@
clonesCountTail: clonesCount.tail,
particlesToScroll,
particlesCount,
particlesToShow,
particlesToShow: _particlesToShow,
}))
}
@@ -359,10 +379,7 @@
async function showParticle(particleIndex, options) {
await changePage(
() => store.moveToParticle({
particleIndex,
particlesCount,
}),
() => moveToParticle(particleIndex),
options
)
}
@@ -370,15 +387,7 @@
if (disabled) return
await changePage(
() => store.prev({
infinite,
currentPageIndex,
clonesCountHead: clonesCount.head,
clonesCountTail: clonesCount.tail,
particlesToScroll,
particlesCount,
particlesToShow,
}),
prev,
options,
)
}
@@ -386,15 +395,7 @@
if (disabled) return
await changePage(
() => store.next({
infinite,
currentPageIndex,
particlesCount,
particlesToScroll,
particlesToShow,
clonesCountHead: clonesCount.head,
clonesCountTail: clonesCount.tail,
}),
next,
options,
)
}
@@ -548,3 +549,6 @@
bottom: 0;
}
</style>

View File

@@ -0,0 +1,216 @@
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'
function getCapitalized(str) {
const _str = String(str)
return `${_str.charAt(0).toUpperCase()}${_str.slice(1)}`
}
// TODO: merge values and computed
const carousel = (effects, options) => {
const values = {}
const setters = {}
const computed = get(options, 'computed', {})
const memo = {}
const actions = {}
Object.entries(get(options, 'actions', {})).forEach(([actionName, fn]) => {
actions[actionName] = (args) => fn(values, setters, computed, actions, args)
})
Object.entries(effects).forEach(([subject, descriptor]) => {
setters[`set${getCapitalized(subject)}`] = (value) => {
console.log('===> setter', subject)
// if (memo[subject])
const _value = get(descriptor, 'normalize', (value) => value)(
value,
values,
computed
)
values[subject] = _value
Object.entries(computed).forEach(([c, v]) => {
if (v.deps.includes(subject)) {
const dV = v.deps.map((d) => values[d])
const _memo = dV.join(' ')
// console.log()
if (dV.every((v) => v !== undefined) && memo[c] !== _memo) {
v.fn(values, computed)
}
memo[c] = _memo
}
})
get(options, 'onChange', () => {})(values, computed)
}
})
return {
...setters,
...actions,
}
}
export const carouselEngine = (onChange) => {
const _carousel = carousel(
{
particlesCountWithoutClones: {
effect: ({ values, setters }) => {},
},
particlesToShow: {
effect: ({ values, setters }) => {},
normalize: (value, values) => {
return getValueInRange(1, value, values.particlesCountWithoutClones)
},
},
particlesToScroll: {
effect: ({ values, setters }) => {},
normalize: (value, values) => {
return getValueInRange(1, value, values.particlesCountWithoutClones)
},
},
initialPageIndex: {
effect: ({ values }) => {
console.log('initialPageIndex effect', values)
},
// ignore calc if value is undefined
normalize: (value, values, computed) => {
return getParticleIndexByPageIndex({
infinite: values.infinite,
pageIndex: value,
clonesCountHead: computed.clonesCount.head,
clonesCountTail: computed.clonesCount.tail,
particlesToScroll: values.particlesToScroll,
particlesCount: values.particlesCount,
particlesToShow: values.particlesToShow,
})
},
},
particlesCount: {
effect: ({ values }) => {},
},
currentParticleIndex: {
effect: ({ values }) => {},
},
infinite: {},
},
{
onChange,
computed: {
clonesCount: {
fn: (values, computed) => {
return getClonesCount({
infinite: values.infinite,
particlesToShow: values.particlesToShow,
partialPageSize: values.partialPageSize,
})
},
deps: ['infinite', 'particlesToShow', 'partialPageSize'], // try to use proxy to detect deps
},
partialPageSize: {
fn: (values, computed) => {
return getPartialPageSize({
particlesToScroll: values.particlesToScroll,
particlesToShow: values.particlesToShow,
particlesCountWithoutClones: values.particlesCountWithoutClones,
})
},
deps: [
'particlesToScroll',
'particlesToShow',
'particlesCountWithoutClones',
],
},
currentPageIndex: {
fn: (values, computed) => {
console.log('===> currentPageIndex', currentPageIndex)
// dispatch('pageChange', currentPageIndex)
return getCurrentPageIndexByCurrentParticleIndex({
currentParticleIndex: values.currentParticleIndex,
particlesCount: values.particlesCount,
clonesCountHead: clonesCount.head,
clonesCountTotal: clonesCount.total,
infinite: values.initialPageIndex,
particlesToScroll: values.particlesToScroll,
})
},
deps: [
'currentParticleIndex',
'particlesCount',
'initialPageIndex',
'particlesToScroll',
'clonesCount',
],
},
pagesCount: {
fn: (values, computed) => {
return getPagesCountByParticlesCount({
infinite: values.infinite,
particlesCountWithoutClones: values.particlesCountWithoutClones,
particlesToScroll: values.particlesToScroll,
})
},
deps: [
'infinite',
'particlesCountWithoutClones',
'particlesToScroll',
],
},
},
actions: {
prev: (values, setters, computed, actions) => {
const newCurrentParticleIndex = getParticleIndexByPageIndex({
infinite: values.infinite,
pageIndex: computed.currentPageIndex - 1,
clonesCountHead: computed.clonesCount.head,
clonesCountTail: computed.clonesCount.tail,
particlesToScroll: values.particlesToScroll,
particlesCount: values.particlesCount,
particlesToShow: values.particlesToShow,
})
setters.setCurrentParticleIndex(newCurrentParticleIndex)
},
next: (values, setters, computed, actions) => {
const newCurrentParticleIndex = getParticleIndexByPageIndex({
infinite: values.infinite,
pageIndex: computed.currentPageIndex + 1,
clonesCountHead: computed.clonesCount.head,
clonesCountTail: computed.clonesCount.tail,
particlesToScroll: values.particlesToScroll,
particlesCount: values.particlesCount,
particlesToShow: values.particlesToShow,
})
setters.setCurrentParticleIndex(newCurrentParticleIndex)
},
moveToParticle: (values, setters, computed, actions, args) => {
const newCurrentParticleIndex = getValueInRange(
0,
args.particleIndex,
values.particlesCount - 1
)
setters.setCurrentParticleIndex(newCurrentParticleIndex)
},
},
}
)
return _carousel
}

View File

@@ -0,0 +1,80 @@
// 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 = []
}
depend() {
// Saves target function into subscribers array
if (target && !this.subscribers.includes(target)) {
this.subscribers.push(target)
}
}
notify() {
// Replays target functions saved in the subscribers array
this.subscribers.forEach((sub) => sub())
}
}
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
}
// reactive(
// {
// message: '',
// },
// {
// renderFunction: (data) => {
// console.log(data.message)
// },
// }
// )
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()
Object.defineProperty(_data, key, {
get() {
console.log(`Getting value, ${internalValue}`)
dep.depend() // Saves the target function into the subscribers array
return internalValue
},
set(newVal) {
console.log(`Setting the internalValue to ${newVal}`)
internalValue = newVal
dep.notify() // Reruns saved target functions in the subscribers array
},
})
})
Object.entries(watchers).forEach(([watcherName, watcher]) => {
watch(() => watcher(_data))
})
return _data
}

View File

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