109 lines
3.2 KiB
JavaScript
109 lines
3.2 KiB
JavaScript
import { writable } from "svelte/store"
|
|
|
|
// we assume that the reference to this state object
|
|
// will remain for the life of the application
|
|
const rootState = {}
|
|
const rootStore = writable(rootState)
|
|
const contextStores = {}
|
|
|
|
// contextProviderId is the component id that provides the data for the context
|
|
const contextStoreKey = (dataProviderId, childIndex) =>
|
|
`${dataProviderId}${childIndex >= 0 ? ":" + childIndex : ""}`
|
|
|
|
// creates a store for a datacontext (e.g. each item in a list component)
|
|
// overrides store if already exists
|
|
const create = (data, dataProviderId, childIndex, parentContextStoreId) => {
|
|
const key = contextStoreKey(dataProviderId, childIndex)
|
|
const state = { data }
|
|
|
|
// add reference to parent state object,
|
|
// so we can use bindings like state.parent.parent
|
|
// (if no parent, then parent is rootState )
|
|
state.parent = parentContextStoreId
|
|
? contextStores[parentContextStoreId].state
|
|
: rootState
|
|
|
|
contextStores[key] = {
|
|
store: writable(state),
|
|
subscriberCount: 0,
|
|
state,
|
|
parentContextStoreId,
|
|
}
|
|
|
|
return key
|
|
}
|
|
|
|
const subscribe = (subscription, storeKey) => {
|
|
if (!storeKey) {
|
|
return rootStore.subscribe(subscription)
|
|
}
|
|
const contextStore = contextStores[storeKey]
|
|
|
|
// we are subscribing to multiple stores,
|
|
// we dont want to run our listener for every subscription, the first time
|
|
// as this could repeatedly run $set on the same component
|
|
// ... which already has its initial properties set properly
|
|
const ignoreFirstSubscription = () => {
|
|
let hasRunOnce = false
|
|
return () => {
|
|
if (hasRunOnce) subscription(contextStore.state)
|
|
hasRunOnce = true
|
|
}
|
|
}
|
|
|
|
const unsubscribes = [rootStore.subscribe(ignoreFirstSubscription())]
|
|
|
|
// we subscribe to all stores in the hierarchy
|
|
const ancestorSubscribe = ctxStore => {
|
|
// unsubscribe func returned by svelte store
|
|
const svelteUnsub = ctxStore.store.subscribe(ignoreFirstSubscription())
|
|
|
|
// we wrap the svelte unsubscribe, so we can
|
|
// cleanup stores when they are no longer subscribed to
|
|
const unsub = () => {
|
|
ctxStore.subscriberCount = contextStore.subscriberCount - 1
|
|
// when no subscribers left, we delete the store
|
|
if (ctxStore.subscriberCount === 0) {
|
|
delete ctxStore[storeKey]
|
|
}
|
|
svelteUnsub()
|
|
}
|
|
unsubscribes.push(unsub)
|
|
if (ctxStore.parentContextStoreId) {
|
|
ancestorSubscribe(contextStores[ctxStore.parentContextStoreId])
|
|
}
|
|
}
|
|
|
|
ancestorSubscribe(contextStore)
|
|
|
|
// our final unsubscribe function calls unsubscribe on all stores
|
|
return () => unsubscribes.forEach(u => u())
|
|
}
|
|
|
|
const findStore = (dataProviderId, childIndex) =>
|
|
dataProviderId
|
|
? contextStores[contextStoreKey(dataProviderId, childIndex)].store
|
|
: rootStore
|
|
|
|
const update = (updatefunc, dataProviderId, childIndex) =>
|
|
findStore(dataProviderId, childIndex).update(updatefunc)
|
|
|
|
const set = (value, dataProviderId, childIndex) =>
|
|
findStore(dataProviderId, childIndex).set(value)
|
|
|
|
const getState = contextStoreKey =>
|
|
contextStoreKey ? contextStores[contextStoreKey].state : rootState
|
|
|
|
const getStore = contextStoreKey =>
|
|
contextStoreKey ? contextStores[contextStoreKey] : rootStore
|
|
|
|
export default {
|
|
subscribe,
|
|
update,
|
|
set,
|
|
getState,
|
|
create,
|
|
contextStoreKey,
|
|
getStore,
|
|
}
|