1 | "use strict";
|
2 | Object.defineProperty(exports, "__esModule", { value: true });
|
3 | exports.TreeState = void 0;
|
4 | var tslib_1 = require("tslib");
|
5 | var util_value_1 = require("@platform/util.value");
|
6 | var rxjs_1 = require("rxjs");
|
7 | var operators_1 = require("rxjs/operators");
|
8 | var common_1 = require("../common");
|
9 | var StateObject_1 = require("../StateObject");
|
10 | var TreeIdentity_1 = require("../TreeIdentity");
|
11 | var TreeQuery_1 = require("../TreeQuery");
|
12 | var helpers_1 = require("./helpers");
|
13 | var events = require("./TreeState.events");
|
14 | var path = require("./TreeState.path");
|
15 | var sync = require("./TreeState.sync");
|
16 | var Identity = TreeIdentity_1.TreeIdentity;
|
17 | var TreeState = (function () {
|
18 | function TreeState(args) {
|
19 | var _this = this;
|
20 | this._children = [];
|
21 | this._kind = 'TreeState';
|
22 | this._dispose$ = new rxjs_1.Subject();
|
23 | this.dispose$ = this._dispose$.pipe(operators_1.share());
|
24 | this._event$ = new rxjs_1.Subject();
|
25 | this.change = function (fn, options) { return _this._change(fn, options); };
|
26 | this.add = function (args) {
|
27 | if (TreeState.isInstance(args)) {
|
28 | args = { parent: _this.id, root: args };
|
29 | }
|
30 | var self = _this;
|
31 | var child = _this.getOrCreateInstance(args);
|
32 | if (_this.childExists(child)) {
|
33 | var err = "Cannot add child '" + child.id + "' as it already exists within the parent '" + _this.state.id + "'.";
|
34 | throw new Error(err);
|
35 | }
|
36 | _this._children.push(child);
|
37 | _this.change(function (draft, ctx) {
|
38 | var root = args.parent && args.parent !== draft.id ? ctx.findById(args.parent) : draft;
|
39 | if (!root) {
|
40 | var err = "Cannot add child-state because the parent sub-node '" + args.parent + "' within '" + draft.id + "' does not exist.";
|
41 | throw new Error(err);
|
42 | }
|
43 | TreeState.children(root).push(child.state);
|
44 | });
|
45 | _this.listen(child);
|
46 | child.dispose$
|
47 | .pipe(operators_1.take(1))
|
48 | .pipe(operators_1.filter(function () { return _this.childExists(child); }))
|
49 | .subscribe(function () { return _this.remove(child); });
|
50 | _this.fire({ type: 'TreeState/child/added', payload: { parent: self, child: child } });
|
51 | return child;
|
52 | };
|
53 | this.remove = function (input) {
|
54 | var child = _this.child(input);
|
55 | if (!child) {
|
56 | var err = "Cannot remove child-state as it does not exist in the parent '" + _this.state.id + "'.";
|
57 | throw new Error(err);
|
58 | }
|
59 | _this._children = _this._children.filter(function (item) { return item.state.id !== child.state.id; });
|
60 | var self = _this;
|
61 | _this.fire({ type: 'TreeState/child/removed', payload: { parent: self, child: child } });
|
62 | return child;
|
63 | };
|
64 | this.clear = function () {
|
65 | _this.children.forEach(function (child) { return _this.remove(child); });
|
66 | return _this;
|
67 | };
|
68 | this.contains = function (match) {
|
69 | return Boolean(_this.find(match));
|
70 | };
|
71 | this.find = function (input) {
|
72 | if (!input) {
|
73 | return undefined;
|
74 | }
|
75 | var match = typeof input === 'function'
|
76 | ? input
|
77 | : function (e) {
|
78 | var id = TreeIdentity_1.TreeIdentity.toNodeId(input);
|
79 | return e.id === id ? true : Boolean(e.tree.query.findById(id));
|
80 | };
|
81 | var result;
|
82 | _this.walkDown(function (e) {
|
83 | if (e.level > 0) {
|
84 | if (match(e) === true) {
|
85 | e.stop();
|
86 | result = e.tree;
|
87 | }
|
88 | }
|
89 | });
|
90 | return result;
|
91 | };
|
92 | this.walkDown = function (visit) {
|
93 | var inner = function (level, index, tree, parent, state) {
|
94 | if (state.stopped) {
|
95 | return;
|
96 | }
|
97 | var skipped = false;
|
98 | var args = {
|
99 | level: level,
|
100 | id: tree.id,
|
101 | key: Identity.key(tree.id),
|
102 | namespace: tree.namespace,
|
103 | index: index,
|
104 | tree: tree,
|
105 | parent: parent,
|
106 | stop: function () { return (state.stopped = true); },
|
107 | skip: function () { return (skipped = true); },
|
108 | toString: function () { return tree.id; },
|
109 | };
|
110 | visit(args);
|
111 | if (state.stopped) {
|
112 | return;
|
113 | }
|
114 | if (!skipped && tree.children.length) {
|
115 | tree.children.forEach(function (child, i) {
|
116 | inner(level + 1, i, child, tree, state);
|
117 | });
|
118 | }
|
119 | };
|
120 | return inner(0, -1, _this, undefined, {});
|
121 | };
|
122 | this.syncFrom = function (args) {
|
123 | var until$ = args.until$;
|
124 | var isObservable = common_1.is.observable(args.source.event$);
|
125 | var source$ = isObservable
|
126 | ? args.source.event$
|
127 | : args.source.event.$;
|
128 | var parent = isObservable
|
129 | ? args.source.parent
|
130 | : args.source.parent;
|
131 | var initial = isObservable ? undefined : args.source.state;
|
132 | return sync.syncFrom({ target: _this, parent: parent, initial: initial, source$: source$, until$: until$ });
|
133 | };
|
134 | this.fire = function (e) { return _this._event$.next(e); };
|
135 | var root = (typeof args.root === 'string' ? { id: args.root } : args.root);
|
136 | if (root.id.includes('/')) {
|
137 | var err = "Tree node IDs cannot contain the \"/\" character";
|
138 | throw new Error(err);
|
139 | }
|
140 | this.key = Identity.key(root.id);
|
141 | this.namespace = Identity.namespace(root.id) || util_value_1.id.cuid();
|
142 | this.parent = args.parent;
|
143 | var store = (this._store = StateObject_1.StateObject.create(root));
|
144 | this.event = events.create({
|
145 | event$: this._event$,
|
146 | until$: this.dispose$,
|
147 | });
|
148 | this._change(function (draft) { return helpers_1.helpers.ensureNamespace(draft, _this.namespace); }, {
|
149 | ensureNamespace: false,
|
150 | });
|
151 | store.event.changed$.pipe(operators_1.takeUntil(this.dispose$)).subscribe(function (e) {
|
152 | _this.fire({ type: 'TreeState/changed', payload: e });
|
153 | });
|
154 | store.event.patched$.pipe(operators_1.takeUntil(this.dispose$)).subscribe(function (e) {
|
155 | _this.fire({ type: 'TreeState/patched', payload: e });
|
156 | });
|
157 | if (args.dispose$) {
|
158 | args.dispose$.subscribe(function () { return _this.dispose(); });
|
159 | }
|
160 | }
|
161 | TreeState.create = function (args) {
|
162 | var root = (args === null || args === void 0 ? void 0 : args.root) || 'node';
|
163 | var e = tslib_1.__assign(tslib_1.__assign({}, args), { root: root });
|
164 | return new TreeState(e);
|
165 | };
|
166 | TreeState.prototype.dispose = function () {
|
167 | if (!this.isDisposed) {
|
168 | this.children.forEach(function (child) { return child.dispose(); });
|
169 | this._store.dispose();
|
170 | this.fire({
|
171 | type: 'TreeState/disposed',
|
172 | payload: { final: this.state },
|
173 | });
|
174 | this._dispose$.next();
|
175 | this._dispose$.complete();
|
176 | }
|
177 | };
|
178 | Object.defineProperty(TreeState.prototype, "isDisposed", {
|
179 | get: function () {
|
180 | return this._dispose$.isStopped;
|
181 | },
|
182 | enumerable: false,
|
183 | configurable: true
|
184 | });
|
185 | Object.defineProperty(TreeState.prototype, "readonly", {
|
186 | get: function () {
|
187 | return this;
|
188 | },
|
189 | enumerable: false,
|
190 | configurable: true
|
191 | });
|
192 | Object.defineProperty(TreeState.prototype, "state", {
|
193 | get: function () {
|
194 | return this._store.state;
|
195 | },
|
196 | enumerable: false,
|
197 | configurable: true
|
198 | });
|
199 | Object.defineProperty(TreeState.prototype, "id", {
|
200 | get: function () {
|
201 | return this.state.id;
|
202 | },
|
203 | enumerable: false,
|
204 | configurable: true
|
205 | });
|
206 | Object.defineProperty(TreeState.prototype, "children", {
|
207 | get: function () {
|
208 | return this._children;
|
209 | },
|
210 | enumerable: false,
|
211 | configurable: true
|
212 | });
|
213 | Object.defineProperty(TreeState.prototype, "query", {
|
214 | get: function () {
|
215 | var root = this.state;
|
216 | var namespace = this.namespace;
|
217 | return TreeQuery_1.TreeQuery.create({ root: root, namespace: namespace });
|
218 | },
|
219 | enumerable: false,
|
220 | configurable: true
|
221 | });
|
222 | Object.defineProperty(TreeState.prototype, "path", {
|
223 | get: function () {
|
224 | return path.create(this);
|
225 | },
|
226 | enumerable: false,
|
227 | configurable: true
|
228 | });
|
229 | TreeState.prototype._change = function (fn, options) {
|
230 | var _this = this;
|
231 | if (options === void 0) { options = {}; }
|
232 | var action = options.action;
|
233 | var res = this._store.change(function (draft) {
|
234 | var ctx = _this.ctx(draft);
|
235 | fn(draft, ctx);
|
236 | if (options.ensureNamespace !== false) {
|
237 | helpers_1.helpers.ensureNamespace(draft, _this.namespace);
|
238 | }
|
239 | }, { action: action });
|
240 | return res;
|
241 | };
|
242 | TreeState.prototype.ctx = function (root) {
|
243 | var namespace = this.namespace;
|
244 | var query = TreeQuery_1.TreeQuery.create({ root: root, namespace: namespace });
|
245 | var ctx = tslib_1.__assign(tslib_1.__assign({}, query), { props: TreeState.props, children: TreeState.children, toObject: function (draft) { return StateObject_1.StateObject.toObject(draft); }, query: function (node, namespace) {
|
246 | return TreeQuery_1.TreeQuery.create({ root: node || root, namespace: namespace });
|
247 | } });
|
248 | return ctx;
|
249 | };
|
250 | TreeState.prototype.child = function (id) {
|
251 | id = typeof id === 'string' ? id : id.state.id;
|
252 | return this.children.find(function (item) { return item.id === id; });
|
253 | };
|
254 | TreeState.prototype.childExists = function (input) {
|
255 | return Boolean(this.child(input));
|
256 | };
|
257 | TreeState.prototype.getOrCreateInstance = function (args) {
|
258 | var root = (typeof args.root === 'string' ? { id: args.root } : args.root);
|
259 | if (TreeState.isInstance(root)) {
|
260 | return args.root;
|
261 | }
|
262 | var parent = Identity.toString(args.parent);
|
263 | parent = parent ? parent : Identity.stripNamespace(this.id);
|
264 | if (!this.query.exists(function (e) { return e.key === parent; })) {
|
265 | var err = "Cannot add child-state because the parent node '" + parent + "' does not exist.";
|
266 | throw new Error(err);
|
267 | }
|
268 | parent = Identity.format(this.namespace, parent);
|
269 | return TreeState.create({ parent: parent, root: root });
|
270 | };
|
271 | TreeState.prototype.listen = function (child) {
|
272 | var _this = this;
|
273 | var removed$ = this.event
|
274 | .payload('TreeState/child/removed')
|
275 | .pipe(operators_1.filter(function (e) { return e.child.id === child.id; }));
|
276 | removed$.subscribe(function (e) {
|
277 | _this.change(function (draft, ctx) {
|
278 | draft.children = TreeState.children(draft).filter(function (_a) {
|
279 | var id = _a.id;
|
280 | return id !== child.id;
|
281 | });
|
282 | });
|
283 | });
|
284 | child.event
|
285 | .payload('TreeState/changed')
|
286 | .pipe(operators_1.takeUntil(child.dispose$), operators_1.takeUntil(removed$))
|
287 | .subscribe(function (e) {
|
288 | _this.change(function (draft, ctx) {
|
289 | var children = TreeState.children(draft);
|
290 | var index = children.findIndex(function (_a) {
|
291 | var id = _a.id;
|
292 | return id === child.id;
|
293 | });
|
294 | if (index > -1) {
|
295 | children[index] = e.to;
|
296 | }
|
297 | });
|
298 | });
|
299 | };
|
300 | TreeState.identity = Identity;
|
301 | TreeState.props = helpers_1.helpers.props;
|
302 | TreeState.children = helpers_1.helpers.children;
|
303 | TreeState.isInstance = helpers_1.helpers.isInstance;
|
304 | return TreeState;
|
305 | }());
|
306 | exports.TreeState = TreeState;
|