|
@@ -8,15 +8,17 @@ const Spruce = {
|
|
|
|
|
|
subscribers: [],
|
|
|
|
|
|
+ watchers: {},
|
|
|
+
|
|
|
disableReactivity: false,
|
|
|
|
|
|
async start() {
|
|
|
await domReady()
|
|
|
-
|
|
|
+
|
|
|
this.attach()
|
|
|
|
|
|
this.stores = createObservable(this.stores, {
|
|
|
- set: () => {
|
|
|
+ set: (target, key, value) => {
|
|
|
if (this.disableReactivity) {
|
|
|
return
|
|
|
}
|
|
@@ -25,19 +27,21 @@ const Spruce = {
|
|
|
|
|
|
this.disableReactivity = true
|
|
|
|
|
|
+ this.runWatchers(this.stores, target, key, value)
|
|
|
+
|
|
|
try {
|
|
|
- this.persisted.forEach(this.updateLocalStorage.bind(this))
|
|
|
+ this.persisted.forEach(this.updateLocalStorage.bind(this))
|
|
|
} catch (e) {
|
|
|
// Do nothing here (thanks Safari!)
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
this.disableReactivity = false
|
|
|
}
|
|
|
})
|
|
|
},
|
|
|
|
|
|
attach() {
|
|
|
- if (! window.Alpine) {
|
|
|
+ if (!window.Alpine) {
|
|
|
throw new Error('[Spruce] You must be using Alpine >= 2.5.0 to use Spruce.')
|
|
|
}
|
|
|
|
|
@@ -55,7 +59,7 @@ const Spruce = {
|
|
|
try {
|
|
|
this.stores[name] = this.retrieveFromLocalStorage(name, getMethods(state))
|
|
|
|
|
|
- if (! this.persisted.includes(name)) {
|
|
|
+ if (!this.persisted.includes(name)) {
|
|
|
this.persisted.push(name)
|
|
|
}
|
|
|
} catch (e) {
|
|
@@ -63,7 +67,7 @@ const Spruce = {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- if (! this.stores[name]) {
|
|
|
+ if (!this.stores[name]) {
|
|
|
this.stores[name] = state
|
|
|
}
|
|
|
|
|
@@ -75,7 +79,7 @@ const Spruce = {
|
|
|
},
|
|
|
|
|
|
subscribe(el) {
|
|
|
- if (! this.subscribers.includes(el)) {
|
|
|
+ if (!this.subscribers.includes(el)) {
|
|
|
this.subscribers.push(el)
|
|
|
}
|
|
|
|
|
@@ -83,7 +87,7 @@ const Spruce = {
|
|
|
},
|
|
|
|
|
|
updateSubscribers() {
|
|
|
- this.subscribers.filter(el => !! el.__x).forEach(el => {
|
|
|
+ this.subscribers.filter(el => !!el.__x).forEach(el => {
|
|
|
el.__x.updateElements(el)
|
|
|
})
|
|
|
},
|
|
@@ -96,6 +100,38 @@ const Spruce = {
|
|
|
|
|
|
updateLocalStorage(name) {
|
|
|
window.localStorage.setItem(`__spruce:${name}`, JSON.stringify(this.store(name)))
|
|
|
+ },
|
|
|
+
|
|
|
+ watch(name, callback) {
|
|
|
+ if (!this.watchers[name]) {
|
|
|
+ this.watchers[name] = []
|
|
|
+ }
|
|
|
+
|
|
|
+ this.watchers[name].push(callback)
|
|
|
+ },
|
|
|
+
|
|
|
+ runWatchers(stores, target, key, value) {
|
|
|
+ const self = this
|
|
|
+
|
|
|
+ if (self.watchers[key]) {
|
|
|
+ return self.watchers[key].forEach(callback => callback(value))
|
|
|
+ }
|
|
|
+
|
|
|
+ Object.keys(self.watchers)
|
|
|
+ .filter(watcher => watcher.includes('.'))
|
|
|
+ .forEach(fullDotNotationKey => {
|
|
|
+ let dotNotationParts = fullDotNotationKey.split('.')
|
|
|
+
|
|
|
+ if (key !== dotNotationParts[dotNotationParts.length - 1]) return
|
|
|
+
|
|
|
+ dotNotationParts.reduce((comparison, part) => {
|
|
|
+ if (comparison[key] === target[key] || Object.is(target, comparison)) {
|
|
|
+ self.watchers[fullDotNotationKey].forEach(callback => callback(value))
|
|
|
+ }
|
|
|
+
|
|
|
+ return comparison[part]
|
|
|
+ }, stores)
|
|
|
+ })
|
|
|
}
|
|
|
}
|
|
|
|