fluidstate
Version:
Library for fine-grained reactivity state management
272 lines (271 loc) • 10.8 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.createReactiveMap = void 0;
var _reactiveNames = require("../extras/reactive-names");
var _reactivePlugin = require("../extras/reactive-plugin");
var _reactivePluginMap = require("../extras/reactive-plugin-map");
var _reactiveAction = require("../primitives/reactive-action");
var _reactiveAtom = require("../primitives/reactive-atom");
var _reactiveLayer = require("../primitives/reactive-layer");
var _reactiveOptions = require("../primitives/reactive-options");
var _reactiveTracking = require("../primitives/reactive-tracking");
var _reactiveChildren = require("./reactive-children");
var _reactiveProxy = require("./reactive-proxy");
const createReactiveMap = (inertMap, reactiveOptions, nameOptions) => {
const atomName = (0, _reactiveNames.createAtomNameFromOptions)(nameOptions);
const proxy = new Proxy(inertMap, new ReactiveMapProxyHandler(new ReactiveMapOverrides(atomName, inertMap, {
items: new Map(),
size: (0, _reactiveAtom.createAtom)((0, _reactiveNames.createMapSizeAtomName)(atomName))
}, () => proxy, reactiveOptions)));
return proxy;
};
exports.createReactiveMap = createReactiveMap;
class ReactiveMapProxyHandler {
constructor(mapOverrides) {
this.mapOverrides = mapOverrides;
}
get(inertMap, property, receiver) {
const ownPropertyDescriptor = Reflect.getOwnPropertyDescriptor(inertMap, property);
const isInherited = Reflect.has(inertMap, property) && !ownPropertyDescriptor;
if (isInherited && property in this.mapOverrides) {
return this.mapOverrides[property];
}
return Reflect.get(inertMap, property, receiver);
}
}
class ReactiveMapOverrides {
#atomName;
#inertMap;
#reactive;
#getProxy;
#options;
constructor(atomName, inertMap, reactive, getProxy, reactiveOptions) {
this.#atomName = atomName;
this.#inertMap = inertMap;
this.#reactive = reactive;
this.#getProxy = getProxy;
this.#options = {
reactiveOptions
};
}
[Symbol.iterator] = () => {
return this.#getMapEntries();
};
entries = () => {
return this.#getMapEntries();
};
keys = () => {
return this.#keys();
};
values = () => {
return this.#values();
};
get [Symbol.toStringTag]() {
return Map.prototype[Symbol.toStringTag];
}
get size() {
this.#reactive.size.reportObserved();
return this.#inertMap.size;
}
has = key => {
const storedKey = this.#getStored(key);
const result = this.#hasStoredKey(storedKey);
this.#readItem(storedKey);
return result;
};
get = key => {
const storedKey = this.#getStored(key);
const result = this.#getStoredValueByKey(storedKey);
this.#readItem(storedKey);
return (0, _reactiveChildren.getChild)(this.#options, result, () => (0, _reactiveProxy.createProxy)(result, (0, _reactiveChildren.getChildOptions)(this.#options), {
parentAtomName: this.#atomName,
createAtomName: _reactiveNames.createMapValueAtomName,
childIndex: this.#getChildIndex(storedKey)
}));
};
set = (key, value) => {
const storedKey = this.#getStored(key);
const storedValue = this.#getStored(value);
const itemAlreadyExists = this.#hasStoredKey(storedKey);
const currentStoredValue = this.#getStoredValueByKey(storedKey);
const isValueChanged = !(0, _reactiveOptions.reactiveValueEquals)(currentStoredValue, storedValue);
const changes = !this.#hasPlugins() || itemAlreadyExists && !isValueChanged ? null : (0, _reactivePluginMap.getMapSetChanges)(this.#getProxy(), storedKey, currentStoredValue, storedValue);
if (changes) {
(0, _reactivePlugin.beforeChange)(changes, this.#options.reactiveOptions?.plugins);
}
Map.prototype.set.call(this.#inertMap, storedKey, storedValue);
(0, _reactiveAction.runTransaction)(() => {
if (!itemAlreadyExists || isValueChanged) {
this.#notifyItem(storedKey);
}
if (!itemAlreadyExists) {
this.#reactive.size.reportChanged();
}
});
if (changes) {
(0, _reactivePlugin.afterChange)(changes, this.#options.reactiveOptions?.plugins);
}
return this;
};
delete = key => {
const storedKey = this.#getStored(key);
const itemAlreadyExists = this.#hasStoredKey(storedKey);
const changes = !this.#hasPlugins() || !itemAlreadyExists ? null : (0, _reactivePluginMap.getMapDeleteChanges)(this.#getProxy(), storedKey, this.#getStoredValueByKey(storedKey));
if (changes) {
(0, _reactivePlugin.beforeChange)(changes, this.#options.reactiveOptions?.plugins);
}
const resultStored = Map.prototype.delete.call(this.#inertMap, storedKey);
const reactive = (0, _reactiveProxy.getReactive)(storedKey);
const resultReactive = !!(reactive && Map.prototype.delete.call(this.#inertMap, reactive));
const result = resultStored || resultReactive;
if (result) {
(0, _reactiveAction.runTransaction)(() => {
this.#notifyItem(storedKey);
this.#reactive.size.reportChanged();
});
}
if (changes) {
(0, _reactivePlugin.afterChange)(changes, this.#options.reactiveOptions?.plugins);
}
return result;
};
clear = () => {
const isAlreadyEmpty = this.#inertMap.size === 0;
const changes = !this.#hasPlugins() || isAlreadyEmpty ? null : (0, _reactivePluginMap.getMapClearChanges)(this.#getProxy(), Array.from(this.#inertMap.entries()));
if (changes) {
(0, _reactivePlugin.beforeChange)(changes, this.#options.reactiveOptions?.plugins);
}
(0, _reactiveAction.runTransaction)(() => {
Map.prototype.forEach.call(this.#inertMap, (_value, key) => {
Map.prototype.delete.call(this.#inertMap, key);
this.#notifyItem(key);
});
if (!isAlreadyEmpty) {
this.#reactive.size.reportChanged();
}
});
if (changes) {
(0, _reactivePlugin.afterChange)(changes, this.#options.reactiveOptions?.plugins);
}
};
forEach = (callbackfn, thisArg) => {
this.#reactive.size.reportObserved();
const result = Map.prototype.forEach.call(this.#inertMap, (value, key, map) => {
this.#readItem(key);
const childIndex = this.#getChildIndex(key);
return callbackfn.call(this, (0, _reactiveChildren.getChild)(this.#options, value, () => (0, _reactiveProxy.createProxy)(value, (0, _reactiveChildren.getChildOptions)(this.#options), {
parentAtomName: this.#atomName,
createAtomName: _reactiveNames.createMapValueAtomName,
childIndex
})), (0, _reactiveChildren.getChild)(this.#options, key, () => (0, _reactiveProxy.createProxy)(key, (0, _reactiveChildren.getChildOptions)(this.#options), {
parentAtomName: this.#atomName,
createAtomName: _reactiveNames.createMapKeyAtomName,
childIndex
})), map);
}, thisArg);
return result;
};
*#keys() {
const result = Map.prototype.entries.call(this.#inertMap);
this.#reactive.size.reportObserved();
for (const [key] of result) {
this.#readItem(key);
yield (0, _reactiveChildren.getChild)(this.#options, key, () => (0, _reactiveProxy.createProxy)(key, (0, _reactiveChildren.getChildOptions)(this.#options), {
parentAtomName: this.#atomName,
createAtomName: _reactiveNames.createMapKeyAtomName,
childIndex: this.#getChildIndex(key)
}));
}
}
*#values() {
const result = Map.prototype.entries.call(this.#inertMap);
this.#reactive.size.reportObserved();
for (const [key, value] of result) {
this.#readItem(key);
yield (0, _reactiveChildren.getChild)(this.#options, value, () => (0, _reactiveProxy.createProxy)(value, (0, _reactiveChildren.getChildOptions)(this.#options), {
parentAtomName: this.#atomName,
createAtomName: _reactiveNames.createMapValueAtomName,
childIndex: this.#getChildIndex(key)
}));
}
}
#getStored(value) {
return this.#options.reactiveOptions?.deep === false ? value : (0, _reactiveProxy.ensureInert)(value);
}
#getStoredValueByKey(storedKey) {
const hasStoredValueDirectly = Map.prototype.has.call(this.#inertMap, storedKey);
if (hasStoredValueDirectly || this.#options.reactiveOptions?.deep === false) {
return Map.prototype.get.call(this.#inertMap, storedKey);
}
const reactiveKey = (0, _reactiveProxy.getReactive)(storedKey);
return reactiveKey ? Map.prototype.get.call(this.#inertMap, reactiveKey) : undefined;
}
#hasStoredKey(storedKey) {
const hasStoredValueDirectly = Map.prototype.has.call(this.#inertMap, storedKey);
if (hasStoredValueDirectly || this.#options.reactiveOptions?.deep === false) {
return hasStoredValueDirectly;
}
const reactiveKey = (0, _reactiveProxy.getReactive)(storedKey);
return reactiveKey && Map.prototype.has.call(this.#inertMap, reactiveKey) || false;
}
#cleanupItem(key) {
this.#reactive.items.delete(key);
}
#notifyItem(key) {
const atom = this.#reactive.items.get(key);
if (atom) {
atom.reportChanged();
}
}
#getChildIndex(key) {
switch (typeof key) {
case "string":
case "number":
case "bigint":
case "boolean":
case "symbol":
case "undefined":
return String(key);
default:
return undefined;
}
}
#readItem(key) {
let atom = this.#reactive.items.get(key);
if (!atom) {
if (!(0, _reactiveTracking.isTracking)()) {
return;
}
atom = (0, _reactiveLayer.getReactiveLayer)().createAtom((0, _reactiveNames.createMapItemAtomName)(this.#atomName, this.#getChildIndex(key)), {
onBecomeUnobservedListener: () => {
this.#cleanupItem(key);
}
});
this.#reactive.items.set(key, atom);
}
atom.reportObserved();
}
*#getMapEntries() {
const result = Map.prototype.entries.call(this.#inertMap);
this.#reactive.size.reportObserved();
for (const [key, value] of result) {
this.#readItem(key);
const childIndex = this.#getChildIndex(key);
yield [(0, _reactiveChildren.getChild)(this.#options, key, () => (0, _reactiveProxy.createProxy)(key, (0, _reactiveChildren.getChildOptions)(this.#options), {
parentAtomName: this.#atomName,
createAtomName: _reactiveNames.createMapKeyAtomName,
childIndex
})), (0, _reactiveChildren.getChild)(this.#options, value, () => (0, _reactiveProxy.createProxy)(value, (0, _reactiveChildren.getChildOptions)(this.#options), {
parentAtomName: this.#atomName,
createAtomName: _reactiveNames.createMapValueAtomName,
childIndex
}))];
}
}
#hasPlugins() {
return (this.#options.reactiveOptions?.plugins?.length ?? 0) > 0;
}
}
//# sourceMappingURL=reactive-map.js.map