react-smart-state
Version:
Next generation local and global state management
288 lines (287 loc) • 10.6 kB
JavaScript
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ObservableArray = exports.FastList = exports.EventTrigger = exports.CustomError = void 0;
const methods_1 = require("./methods");
class CustomError extends Error {
constructor(message, options = {}) {
super(message);
this.name = 'react-smart-state-Error'.toUpperCase();
this.originalError = options.originalError;
this.code = options.code;
this.details = options.details;
Object.setPrototypeOf(this, new.target.prototype);
}
toJSON() {
return {
name: this.name,
message: this.message,
code: this.code,
details: this.details,
originalError: this.originalError,
stack: this.stack,
};
}
}
exports.CustomError = CustomError;
class EventTrigger {
constructor(ignoredKeys, hardIgnoreKeys) {
this.events = new Map();
this.timer = undefined;
this.waitingEvents = new FastList();
this.addedPaths = new FastList();
this.localBindedEvents = new FastList();
this.speed = 2;
this.batching = new FastList();
this.seen = new WeakMap();
this.ignoreKeys = ignoredKeys !== null && ignoredKeys !== void 0 ? ignoredKeys : {};
this.hardIgnoreKeys = hardIgnoreKeys !== null && hardIgnoreKeys !== void 0 ? hardIgnoreKeys : {};
}
add(id, item) {
var _a;
item.type = (_a = item.type) !== null && _a !== void 0 ? _a : "Auto";
this.events.set(id, item);
}
remove(id) {
this.events.delete(id);
}
hasChange(items, parentState) {
let hasChanges = false;
let newState = parentState !== null && parentState !== void 0 ? parentState : {};
for (const { key, oldValue, newValue } of Object.values(items)) {
if (oldValue !== newValue) {
hasChanges = true;
}
newState[key] = newValue;
}
return { hasChanges, parentState: newState };
}
triggerSavedChanges() {
return __awaiter(this, void 0, void 0, function* () {
clearTimeout(this.timer); // Proper debouncing
if (this.batching.size > 0)
return;
const runHandlers = (e, items) => {
var _a;
(_a = e.func) === null || _a === void 0 ? void 0 : _a.call(e, items);
};
const trigger = (fn) => {
if (this.speed == undefined) {
fn();
}
else {
this.timer = setTimeout(fn, this.speed);
}
};
trigger(() => {
let itemKeys = this.waitingEvents.values;
this.waitingEvents.clear();
for (let { event, items } of itemKeys) {
runHandlers(event, items.records());
}
});
});
}
trigger(event, key, oldValue, newValue) {
return __awaiter(this, void 0, void 0, function* () {
const { eventId, event: evt } = event;
const waitingItems = (this.waitingEvents.has(eventId) ? this.waitingEvents.get(eventId) : this.waitingEvents.set(eventId, {
event: evt,
items: new FastList()
})).items;
waitingItems.set(key, { key, oldValue, newValue });
if (this.batching.size > 0)
return;
yield this.triggerSavedChanges();
});
}
onChange(key_1, _a) {
return __awaiter(this, arguments, void 0, function* (key, { oldValue, newValue }, fromBind = false) {
try {
// if the child of the hooked key is changes, then hook should still trigger if there is a hook for it
// const parts = key.split(".");
for (const [eventId, event] of this.events) {
if ((event.keys.AllKeys && !this.hardIgnoreKeys[key] && (event.type == "Path" || !fromBind)) || (event.keys[key] || event.keys[key + "."])) {
yield this.trigger({ eventId, event }, key, oldValue, newValue);
}
// Traverse up the key chain: "a.b.c" → "a.b" → "a"
/* for (let i = parts.length - 1; i > 0; i--) {
const parentKey = parts.slice(0, i).join(".");
if (event.keys[parentKey]) {
this.trigger({ eventId, event }, key, oldValue, newValue);
break; // stop at the first match for performance
}
}*/
}
}
catch (e) {
console.error(e);
}
});
}
}
exports.EventTrigger = EventTrigger;
class FastList {
constructor() {
this.items = {};
this.length = 0;
}
invalidateCache() {
this.cachedKeys = undefined;
this.cachedValues = undefined;
}
clear() {
this.items = {};
this.length = 0;
this.invalidateCache();
return this;
}
get hasValue() {
return this.length > 0;
}
get values() {
var _a;
return (_a = this.cachedValues) !== null && _a !== void 0 ? _a : (this.cachedValues = Object.values(this.items));
}
get keys() {
var _a;
return (_a = this.cachedKeys) !== null && _a !== void 0 ? _a : (this.cachedKeys = Object.keys(this.items));
}
find(func) {
for (const [key, value] of Object.entries(this.items)) {
if (func(value, key))
return value;
}
return undefined;
}
findKey(func) {
for (const [key, value] of Object.entries(this.items)) {
if (func(value, key))
return key;
}
return undefined;
}
delete(key) {
if (key in this.items) {
delete this.items[key];
this.length--;
this.invalidateCache();
}
return this;
}
get(key) {
return this.items[key];
}
records() {
return this.items;
}
record(key) {
const result = {};
const val = this.get(key);
if (val !== undefined)
result[key] = val;
return result;
}
has(key) {
return key in this.items;
}
append(key, item) {
const existing = this.items[key];
if (existing)
Object.assign(existing, item);
else
this.set(key, item);
return this;
}
set(key, value) {
if (value === undefined) {
this.delete(key);
return value;
}
if (!(key in this.items)) {
this.length++;
this.invalidateCache();
}
this.items[key] = value;
return value;
}
get size() {
return this.length;
}
}
exports.FastList = FastList;
class ObservableArray extends Array {
getInstanceType() {
return "react-smart-state-array";
}
constructor(parentKey, process, onChange) {
super();
this.parentKey = parentKey;
this.process = process;
this.onChange = onChange;
// Required to fix instanceof issues when extending Array
Object.setPrototypeOf(this, ObservableArray.prototype);
}
getChanges() {
// always update
const item = { key: this.parentKey, oldValue: true, newValue: (0, methods_1.newId)() };
return item;
}
push(...items) {
var _a;
const processed = items.map((item, index) => this.process(item, index, this.parentKey));
const result = super.push(...processed);
if (processed.length && this.hasInit)
(_a = this.onChange) === null || _a === void 0 ? void 0 : _a.call(this, 'add', processed, this.getChanges());
return result;
}
unshift(...items) {
var _a;
const processed = items.map((item, index) => this.process(item, index, this.parentKey));
const result = super.unshift(...processed);
if (processed.length && this.hasInit)
(_a = this.onChange) === null || _a === void 0 ? void 0 : _a.call(this, 'add', processed, this.getChanges());
return result;
}
pop() {
var _a;
const removed = super.pop();
if (removed !== undefined && this.hasInit)
(_a = this.onChange) === null || _a === void 0 ? void 0 : _a.call(this, 'remove', [removed], this.getChanges());
return removed;
}
shift() {
var _a;
const removed = super.shift();
if (removed !== undefined && this.hasInit)
(_a = this.onChange) === null || _a === void 0 ? void 0 : _a.call(this, 'remove', [removed], this.getChanges());
return removed;
}
splice(start, deleteCount, ...items) {
var _a, _b;
const processed = items.map((item, index) => this.process(item, index, this.parentKey));
const removed = super.splice(start, deleteCount !== null && deleteCount !== void 0 ? deleteCount : this.length, ...processed);
if (removed.length && this.hasInit)
(_a = this.onChange) === null || _a === void 0 ? void 0 : _a.call(this, 'remove', removed, this.getChanges());
if (processed.length && this.hasInit)
(_b = this.onChange) === null || _b === void 0 ? void 0 : _b.call(this, 'add', processed, this.getChanges());
return removed;
}
clear() {
var _a;
if (this.length > 0) {
const removed = this.splice(0);
if (this.hasInit)
(_a = this.onChange) === null || _a === void 0 ? void 0 : _a.call(this, 'remove', removed, this.getChanges());
}
}
}
exports.ObservableArray = ObservableArray;