Rewrite to allow for multiple, independent stores.

This commit is contained in:
trading_peter 2025-02-06 22:27:39 +01:00
parent 1bcdb09b54
commit cc3a5054e5
2 changed files with 107 additions and 67 deletions

View File

@ -1,6 +1,6 @@
{ {
"name": "@tp/tp-store", "name": "@tp/tp-store",
"version": "1.1.2", "version": "2.0.0",
"description": "", "description": "",
"main": "tp-store.js", "main": "tp-store.js",
"scripts": { "scripts": {

138
store.js
View File

@ -1,21 +1,22 @@
/** /**
@license @license
Copyright (c) 2022 trading_peter Copyright (c) 2025 trading_peter
This program is available under Apache License Version 2.0 This program is available under Apache License Version 2.0
*/ */
const data = new Map(); export class Store {
const instancesPerKey = new Map(); constructor() {
this._data = new Map();
this._instancesPerKey = new Map();
}
/** /**
* # Store * Subscribe instance properties to store changes
* * @param {Object} instance - Instance to subscribe
* A simple key value store. * @param {string|Array<string|Object>} keys - Keys to subscribe to
*/ */
export const Store = function(superClass) { subscribe(instance, keys) {
return class extends superClass { if (!Array.isArray(keys)) {
storeSubscribe(keys) {
if (Array.isArray(keys) === false) {
keys = [keys]; keys = [keys];
} }
@ -25,48 +26,19 @@ export const Store = function(superClass) {
targetProperty = key.targetProperty; targetProperty = key.targetProperty;
key = key.key; key = key.key;
} }
this._addInstance(this, key, targetProperty); this._addInstance(instance, key, targetProperty);
}); });
} }
storeUpdated(key, newValue, targetProperty) { /**
this[targetProperty || key] = newValue; * Write a value to the store
} * @param {string} key - Key to write to
* @param {any} value - Value to store
*/
write(key, value) {
this._data.set(key, value);
storeWrite(key, value) { const instances = this._instancesPerKey.get(key);
this._writeKey(key, value);
}
disconnectedCallback() {
super.disconnectedCallback();
// Clean up all references to this instance.
instancesPerKey.forEach((instances, key) => {
const remainingInstances = instances.filter(entry => entry.instance !== this);
if (remainingInstances.length === 0) {
instancesPerKey.delete(key);
} else {
instancesPerKey.set(key, remainingInstances);
}
});
}
_addInstance(instance, key, targetProperty) {
if (instancesPerKey.has(key) === false) {
instancesPerKey.set(key, [ { instance, targetProperty } ]);
} else {
instancesPerKey.get(key).push({ instance, targetProperty });
}
if (data.has(key)) {
this._notifyInstance(instance, key, data.get(key), targetProperty);
}
}
_writeKey(key, value) {
data.set(key, value);
const instances = instancesPerKey.get(key);
if (Array.isArray(instances)) { if (Array.isArray(instances)) {
instances.forEach(entry => { instances.forEach(entry => {
this._notifyInstance(entry.instance, key, value, entry.targetProperty); this._notifyInstance(entry.instance, key, value, entry.targetProperty);
@ -74,8 +46,76 @@ export const Store = function(superClass) {
} }
} }
/**
* Read a value from the store
* @param {string} key - Key to read
* @returns {any} Stored value
*/
read(key) {
return this._data.get(key);
}
/**
* Unsubscribe an instance from all store changes
* @param {Object} instance - Instance to unsubscribe
*/
unsubscribe(instance) {
this._instancesPerKey.forEach((instances, key) => {
const remainingInstances = instances.filter(entry => entry.instance !== instance);
if (remainingInstances.length === 0) {
this._instancesPerKey.delete(key);
} else {
this._instancesPerKey.set(key, remainingInstances);
}
});
}
_addInstance(instance, key, targetProperty) {
if (!this._instancesPerKey.has(key)) {
this._instancesPerKey.set(key, [{ instance, targetProperty }]);
} else {
this._instancesPerKey.get(key).push({ instance, targetProperty });
}
if (this._data.has(key)) {
this._notifyInstance(instance, key, this._data.get(key), targetProperty);
}
}
_notifyInstance(instance, key, value, targetProperty) { _notifyInstance(instance, key, value, targetProperty) {
if (instance.storeUpdated) {
instance.storeUpdated(key, value, targetProperty); instance.storeUpdated(key, value, targetProperty);
} else {
instance[targetProperty || key] = value;
}
}
}
// Backwards compatibility layer for the mixin
export const StoreSubcriber = function(superClass) {
return class extends superClass {
constructor() {
super();
this._store = window.defaultStore = window.defaultStore || new Store();
}
storeSubscribe(keys) {
this._store.subscribe(this, keys);
}
storeWrite(key, value) {
this._store.write(key, value);
}
disconnectedCallback() {
if (super.disconnectedCallback) {
super.disconnectedCallback();
}
this._store.unsubscribe(this);
}
storeUpdated(key, newValue, targetProperty) {
this[targetProperty || key] = newValue;
} }
}; };
} };