UNPKG

@platform/state

Version:

A small, simple, strongly typed, [rx/observable] state-machine.

313 lines (312 loc) 12.8 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.TreeState = void 0; var tslib_1 = require("tslib"); var util_value_1 = require("@platform/util.value"); var rxjs_1 = require("rxjs"); var operators_1 = require("rxjs/operators"); var common_1 = require("../common"); var StateObject_1 = require("../StateObject"); var TreeIdentity_1 = require("../TreeIdentity"); var TreeQuery_1 = require("../TreeQuery"); var helpers_1 = require("./helpers"); var events = require("./TreeState.events"); var path = require("./TreeState.path"); var sync = require("./TreeState.sync"); var Identity = TreeIdentity_1.TreeIdentity; var TreeState = (function () { function TreeState(args) { var _this = this; this._children = []; this._kind = 'TreeState'; this._dispose$ = new rxjs_1.Subject(); this.dispose$ = this._dispose$.pipe(operators_1.share()); this._event$ = new rxjs_1.Subject(); this.change = function (fn, options) { return _this._change(fn, options); }; this.add = function (args) { if (TreeState.isInstance(args)) { args = { parent: _this.id, root: args }; } var self = _this; var child = _this.getOrCreateInstance(args); if (_this.childExists(child)) { var err = "Cannot add child '" + child.id + "' as it already exists within the parent '" + _this.root.id + "'."; throw new Error(err); } _this._children.push(child); _this.change(function (draft, ctx) { var root = args.parent && args.parent !== draft.id ? ctx.findById(args.parent) : draft; if (!root) { var err = "Cannot add child-state because the parent sub-node '" + args.parent + "' within '" + draft.id + "' does not exist."; throw new Error(err); } TreeState.children(root).push(child.root); }); _this.listen(child); child.dispose$ .pipe(operators_1.take(1)) .pipe(operators_1.filter(function () { return _this.childExists(child); })) .subscribe(function () { return _this.remove(child); }); _this.fire({ type: 'TreeState/child/added', payload: { parent: self, child: child } }); return child; }; this.remove = function (input) { var child = _this.child(input); if (!child) { var err = "Cannot remove child-state as it does not exist in the parent '" + _this.root.id + "'."; throw new Error(err); } _this._children = _this._children.filter(function (item) { return item.root.id !== child.root.id; }); var self = _this; _this.fire({ type: 'TreeState/child/removed', payload: { parent: self, child: child } }); return child; }; this.clear = function () { _this.children.forEach(function (child) { return _this.remove(child); }); return _this; }; this.contains = function (match) { return Boolean(_this.find(match)); }; this.find = function (input) { if (!input) { return undefined; } var match = typeof input === 'function' ? input : function (e) { var id = TreeIdentity_1.TreeIdentity.toNodeId(input); return e.id === id ? true : Boolean(e.tree.query.findById(id)); }; var result; _this.walkDown(function (e) { if (e.level > 0) { if (match(e) === true) { e.stop(); result = e.tree; } } }); return result; }; this.walkDown = function (visit) { var inner = function (level, index, tree, parent, state) { if (state.stopped) { return; } var skipped = false; var args = { level: level, id: tree.id, key: Identity.key(tree.id), namespace: tree.namespace, index: index, tree: tree, parent: parent, stop: function () { return (state.stopped = true); }, skip: function () { return (skipped = true); }, toString: function () { return tree.id; }, }; visit(args); if (state.stopped) { return; } if (!skipped && tree.children.length) { tree.children.forEach(function (child, i) { inner(level + 1, i, child, tree, state); }); } }; return inner(0, -1, _this, undefined, {}); }; this.syncFrom = function (args) { var until$ = args.until$; var isObservable = common_1.is.observable(args.source.event$); var source$ = isObservable ? args.source.event$ : args.source.event.$; var parent = isObservable ? args.source.parent : args.source.parent; var initial = isObservable ? undefined : args.source.root; return sync.syncFrom({ target: _this, parent: parent, initial: initial, source$: source$, until$: until$ }); }; this.fire = function (e) { return _this._event$.next(e); }; var root = (typeof args.root === 'string' ? { id: args.root } : args.root); if (root.id.includes('/')) { var err = "Tree node IDs cannot contain the \"/\" character"; throw new Error(err); } this.key = Identity.key(root.id); this.namespace = Identity.namespace(root.id) || util_value_1.id.cuid(); this.parent = args.parent; var store = (this._store = StateObject_1.StateObject.create(root)); this.event = events.create({ event$: this._event$, until$: this.dispose$, }); this._change(function (draft) { return helpers_1.helpers.ensureNamespace(draft, _this.namespace); }, { ensureNamespace: false, }); store.event.changed$.pipe(operators_1.takeUntil(this.dispose$)).subscribe(function (e) { _this.fire({ type: 'TreeState/changed', payload: e }); }); store.event.patched$.pipe(operators_1.takeUntil(this.dispose$)).subscribe(function (e) { _this.fire({ type: 'TreeState/patched', payload: e }); }); if (args.dispose$) { args.dispose$.subscribe(function () { return _this.dispose(); }); } } TreeState.create = function (args) { var root = (args === null || args === void 0 ? void 0 : args.root) || 'node'; var e = tslib_1.__assign(tslib_1.__assign({}, args), { root: root }); return new TreeState(e); }; TreeState.prototype.dispose = function () { if (!this.isDisposed) { this.children.forEach(function (child) { return child.dispose(); }); this._store.dispose(); this.fire({ type: 'TreeState/disposed', payload: { final: this.root }, }); this._dispose$.next(); this._dispose$.complete(); } }; Object.defineProperty(TreeState.prototype, "isDisposed", { get: function () { return this._dispose$.isStopped; }, enumerable: false, configurable: true }); Object.defineProperty(TreeState.prototype, "readonly", { get: function () { return this; }, enumerable: false, configurable: true }); Object.defineProperty(TreeState.prototype, "store", { get: function () { return this._store; }, enumerable: false, configurable: true }); Object.defineProperty(TreeState.prototype, "root", { get: function () { return this._store.state; }, enumerable: false, configurable: true }); Object.defineProperty(TreeState.prototype, "id", { get: function () { return this.root.id; }, enumerable: false, configurable: true }); Object.defineProperty(TreeState.prototype, "children", { get: function () { return this._children; }, enumerable: false, configurable: true }); Object.defineProperty(TreeState.prototype, "query", { get: function () { var root = this.root; var namespace = this.namespace; return TreeQuery_1.TreeQuery.create({ root: root, namespace: namespace }); }, enumerable: false, configurable: true }); Object.defineProperty(TreeState.prototype, "path", { get: function () { return path.create(this); }, enumerable: false, configurable: true }); TreeState.prototype._change = function (fn, options) { var _this = this; if (options === void 0) { options = {}; } var action = options.action; var res = this._store.change(function (draft) { var ctx = _this.ctx(draft); fn(draft, ctx); if (options.ensureNamespace !== false) { helpers_1.helpers.ensureNamespace(draft, _this.namespace); } }, { action: action }); return res; }; TreeState.prototype.ctx = function (root) { var namespace = this.namespace; var query = TreeQuery_1.TreeQuery.create({ root: root, namespace: namespace }); return tslib_1.__assign(tslib_1.__assign({}, query), { props: TreeState.props, children: TreeState.children, toObject: function (draft) { return (draft ? StateObject_1.StateObject.toObject(draft) : undefined); }, query: function (node, namespace) { return TreeQuery_1.TreeQuery.create({ root: node || root, namespace: namespace }); } }); }; TreeState.prototype.child = function (id) { id = typeof id === 'string' ? id : id.root.id; return this.children.find(function (item) { return item.root.id === id; }); }; TreeState.prototype.childExists = function (input) { return Boolean(this.child(input)); }; TreeState.prototype.getOrCreateInstance = function (args) { var root = (typeof args.root === 'string' ? { id: args.root } : args.root); if (TreeState.isInstance(root)) { return args.root; } var parent = Identity.toString(args.parent); parent = parent ? parent : Identity.stripNamespace(this.id); if (!this.query.exists(function (e) { return e.key === parent; })) { var err = "Cannot add child-state because the parent node '" + parent + "' does not exist."; throw new Error(err); } parent = Identity.format(this.namespace, parent); return TreeState.create({ parent: parent, root: root }); }; TreeState.prototype.listen = function (child) { var _this = this; var removed$ = this.event .payload('TreeState/child/removed') .pipe(operators_1.filter(function (e) { return e.child.id === child.id; })); removed$.subscribe(function (e) { _this.change(function (draft, ctx) { draft.children = TreeState.children(draft).filter(function (_a) { var id = _a.id; return id !== child.id; }); }); }); child.event .payload('TreeState/changed') .pipe(operators_1.takeUntil(child.dispose$), operators_1.takeUntil(removed$)) .subscribe(function (e) { _this.change(function (draft, ctx) { var children = TreeState.children(draft); var index = children.findIndex(function (_a) { var id = _a.id; return id === child.id; }); if (index > -1) { children[index] = e.to; } }); }); }; TreeState.identity = Identity; TreeState.props = helpers_1.helpers.props; TreeState.children = helpers_1.helpers.children; TreeState.isInstance = helpers_1.helpers.isInstance; return TreeState; }()); exports.TreeState = TreeState;