UNPKG

react-smart-state

Version:

Next generation local and global state management

288 lines (287 loc) 10.6 kB
"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;