A factory for creating models that encapsulate Svelte stores. Stores can be automatically subscribed to, with their values accessible from an attrs
object.
Here's a contrived example to demonstrate the API. A Pattern
is a model that can record
events, and schedule them to replay on repeat. The inner workings don't really matter here, but notice that active
, recordable
, and duration
are referenced on attrs
.
import { writable, derived } from 'svelte/store'
const Pattern = Model(function ({ stores, store, attrs, destroy }) {
return function Pattern ({ bpm, scheduler }) {
let currentPlay
// stores
const recordable = store('recordable', writable(false))
const playing = store('playing', writable(false))
const length = store('length', writable(8))
store('active', derived([recordable, playing],
([$recordable, $playing]) => $recordable || $playing
))
store('duration', derived([bpm, length],
([$bpm, $length]) => (60 / $bpm) * $length
))
function toggle () {
if (attrs.active) {
recordable.set(false)
playing.set(false)
currentPlay?.clear()
} else {
recordable.set(true)
}
}
function record (fn) {
const { recordable, duration } = attrs
currentPlay = recordable && scheduler.repeat({ fn, every: duration })
}
function play () {
recordable.set(false)
playing.set(true)
}
return { ...stores, toggle, play, record, destroy }
}
})
const pattern = Pattern({ bpm: 120, scheduler })
pattern.toggle()
pattern.record()
pattern.active // derived store
As demonstrated above, it's often useful to reference raw store values. Manually subscribing to each of these feels awkward. Another option would be to use get(store)
but as the Svelte docs mention:
This works by creating a subscription, reading the value, then unsubscribing. It's therefore not recommended in hot code paths.
So this approach provides a conventional way to make store values accessible inside a model.
export function Model (definition) {
const stores = {}
const attrs = {}
const unsubscribes = []
function store (name, store) {
stores[name] = store
unsubscribes.push(store.subscribe(value => attrs[name] = value))
return store
}
function destroy () {
while(unsubscribes.length) {
unsubscribes.shift()()
}
}
return definition({ stores, store, attrs, destroy })
}