diff --git a/package.json b/package.json index 4a3ea36..fc7293e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@tp/tp-store", - "version": "1.1.2", + "version": "2.0.0", "description": "", "main": "tp-store.js", "scripts": { diff --git a/store.js b/store.js index 493bf67..c27268d 100644 --- a/store.js +++ b/store.js @@ -1,81 +1,121 @@ /** @license -Copyright (c) 2022 trading_peter +Copyright (c) 2025 trading_peter This program is available under Apache License Version 2.0 */ -const data = new Map(); -const instancesPerKey = new Map(); +export class Store { + constructor() { + this._data = new Map(); + this._instancesPerKey = new Map(); + } -/** - * # Store - * - * A simple key value store. - */ -export const Store = function(superClass) { - return class extends superClass { - storeSubscribe(keys) { - if (Array.isArray(keys) === false) { - keys = [ keys ]; + /** + * Subscribe instance properties to store changes + * @param {Object} instance - Instance to subscribe + * @param {string|Array} keys - Keys to subscribe to + */ + subscribe(instance, keys) { + if (!Array.isArray(keys)) { + keys = [keys]; + } + + keys.forEach(key => { + let targetProperty; + if (typeof key === 'object') { + targetProperty = key.targetProperty; + key = key.key; } + this._addInstance(instance, key, targetProperty); + }); + } - keys.forEach(key => { - let targetProperty; - if (typeof key === 'object') { - targetProperty = key.targetProperty; - key = key.key; - } - this._addInstance(this, key, targetProperty); + /** + * 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); + + const instances = this._instancesPerKey.get(key); + if (Array.isArray(instances)) { + instances.forEach(entry => { + this._notifyInstance(entry.instance, key, value, entry.targetProperty); }); } + } + + /** + * 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) { + if (instance.storeUpdated) { + 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; } - - storeWrite(key, value) { - 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)) { - instances.forEach(entry => { - this._notifyInstance(entry.instance, key, value, entry.targetProperty); - }); - } - } - - _notifyInstance(instance, key, value, targetProperty) { - instance.storeUpdated(key, value, targetProperty); - } }; -} +}; \ No newline at end of file