Refactor store
This commit is contained in:
parent
952a3cd34c
commit
9933f4f9ff
15 changed files with 208 additions and 287 deletions
|
|
@ -1,4 +1,6 @@
|
|||
import { Component } from '@angular/core';
|
||||
import { InnerNode, InnerNodeState } from './store/inner-node';
|
||||
import { Root } from './store/root';
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
|
|
@ -6,5 +8,26 @@ import { Component } from '@angular/core';
|
|||
styleUrls: ['./app.component.scss']
|
||||
})
|
||||
export class AppComponent {
|
||||
title = 'frontend';
|
||||
title = 'life';
|
||||
|
||||
/* tests
|
||||
constructor() {
|
||||
|
||||
const root = new Root<InnerNode>();
|
||||
root.log();
|
||||
|
||||
const l = new InnerNode();
|
||||
const r = new InnerNode();
|
||||
root.addChildren([l, r]);
|
||||
root.log();
|
||||
|
||||
const rl = new InnerNode();
|
||||
const rr = new InnerNode();
|
||||
r.addChildren([rl, rr]);
|
||||
root.log();
|
||||
|
||||
rr.changeKeys<InnerNodeState>({ dummy: 8 });
|
||||
root.log();
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,8 @@ import { RemoveBlockComponent } from './components/modal/modals/remove-block/rem
|
|||
import { ToggleComponent } from './components/shared/toggle/toggle.component';
|
||||
import { TasksComponent } from './components/pages/page/tower/tasks/tasks.component';
|
||||
import { ColorPipe } from './pipes/color.pipe';
|
||||
import { Root } from './store/root';
|
||||
import { InnerNode, InnerNodeState } from './store/inner-node';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { Component, Input } from '@angular/core';
|
||||
import { Block } from '../../../../../model/block';
|
||||
import { Block, BlockState } from '../../../../../model/block';
|
||||
import { ModalService } from '../../../../../services/modal.service';
|
||||
import { ColoredBlock, Tower } from '../../../../../model/tower';
|
||||
|
||||
|
|
@ -23,7 +23,7 @@ export class BlockComponent {
|
|||
isDone: this.block.isDone
|
||||
});
|
||||
console.log(description);
|
||||
this.block.changeProperties({
|
||||
this.block.changeKeys<BlockState>({
|
||||
tag: selected,
|
||||
description,
|
||||
isDone
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { Component, ElementRef, Input, OnInit, ViewChild } from '@angular/core';
|
||||
import { Block } from '../../../../../model/block';
|
||||
import { Block, BlockState } from '../../../../../model/block';
|
||||
import { Tower } from '../../../../../model/tower';
|
||||
import { ModalService } from '../../../../../services/modal.service';
|
||||
import { CancelService } from '../../../../../services/cancel.service';
|
||||
|
|
@ -55,7 +55,7 @@ export class TasksComponent implements OnInit {
|
|||
change.created = new Date();
|
||||
}
|
||||
|
||||
block.changeProperties(change);
|
||||
block.changeKeys<BlockState>(change);
|
||||
} catch {
|
||||
// pass
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,30 +1,21 @@
|
|||
import { Serializable } from './serializable';
|
||||
import { IBlock } from '../interfaces/persistance/block';
|
||||
import { Node } from '../store/node';
|
||||
import { InnerNodeState } from '../store/inner-node';
|
||||
|
||||
export class Block extends Serializable implements IBlock {
|
||||
constructor(parent: Node, props: IBlock) {
|
||||
super(parent, props, 'Block');
|
||||
this.onAfterClone();
|
||||
}
|
||||
export interface BlockState extends IBlock, InnerNodeState {}
|
||||
|
||||
protected onAfterClone(): void {
|
||||
if (this.created.constructor.name !== 'Date') {
|
||||
this.created = new Date(this.created);
|
||||
}
|
||||
|
||||
// TODO: remove.
|
||||
if (this.isDone === null || this.isDone === undefined) {
|
||||
this.isDone = false;
|
||||
}
|
||||
}
|
||||
|
||||
changeProperties(values: Partial<IBlock>) {
|
||||
this.changeKeys(values);
|
||||
}
|
||||
|
||||
created: Date;
|
||||
isDone: boolean;
|
||||
export class Block extends Serializable implements IBlock, BlockState {
|
||||
readonly created: Date;
|
||||
readonly isDone: boolean;
|
||||
readonly description: string;
|
||||
readonly tag: string;
|
||||
|
||||
constructor(props: IBlock) {
|
||||
console.log('b');
|
||||
if (props.created.constructor.name !== 'Date') {
|
||||
props.created = new Date(props.created);
|
||||
}
|
||||
super(props, 'Block');
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,17 +1,12 @@
|
|||
import { Serializable } from './serializable';
|
||||
import { IPage } from '../interfaces/persistance/page';
|
||||
import { Tower } from './tower';
|
||||
import { Node } from '../store/node';
|
||||
import { InnerNodeState } from '../store/inner-node';
|
||||
|
||||
export class Page extends Serializable implements IPage {
|
||||
constructor(parent: Node, props: IPage) {
|
||||
super(parent, props, 'Page');
|
||||
}
|
||||
export interface PageState extends InnerNodeState, IPage {}
|
||||
|
||||
export class Page extends Serializable implements IPage, PageState {
|
||||
readonly name: string;
|
||||
get towers(): Array<Tower> {
|
||||
return this.children as Array<Tower>;
|
||||
}
|
||||
|
||||
readonly userData: {
|
||||
hideCreateTowerButton: boolean;
|
||||
|
|
@ -21,10 +16,17 @@ export class Page extends Serializable implements IPage {
|
|||
};
|
||||
};
|
||||
|
||||
constructor(props: IPage) {
|
||||
super(props, 'Page');
|
||||
}
|
||||
|
||||
get towers(): Array<Tower> {
|
||||
return this.children as Array<Tower>;
|
||||
}
|
||||
|
||||
setHideCreateTowerButton(value: boolean) {
|
||||
this.changeKey({
|
||||
propertyName: 'userData',
|
||||
value: {
|
||||
this.changeKeys<PageState>({
|
||||
userData: {
|
||||
...this.userData,
|
||||
hideCreateTowerButton: value
|
||||
}
|
||||
|
|
@ -41,9 +43,8 @@ export class Page extends Serializable implements IPage {
|
|||
towers.splice(previousIndex, 1);
|
||||
towers.splice(currentIndex, 0, tower);
|
||||
|
||||
this.changeValue({
|
||||
oldValue: this.towers,
|
||||
newValue: towers
|
||||
this.changeKeys<PageState>({
|
||||
children: towers
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -53,17 +54,18 @@ export class Page extends Serializable implements IPage {
|
|||
hue = Math.random() * 360;
|
||||
} while (30 <= hue && hue <= 200);
|
||||
|
||||
new Tower(this, {
|
||||
name,
|
||||
blocks: [],
|
||||
baseColor: { h: hue, s: 100, l: 50 }
|
||||
});
|
||||
this.addChildren([
|
||||
new Tower({
|
||||
name,
|
||||
blocks: [],
|
||||
baseColor: { h: hue, s: 100, l: 50 }
|
||||
})
|
||||
]);
|
||||
}
|
||||
|
||||
removeTower(tower: Tower) {
|
||||
this.changeValue({
|
||||
oldValue: this.towers,
|
||||
newValue: this.towers.filter(t => t !== tower)
|
||||
this.changeKeys<PageState>({
|
||||
towers: this.towers.filter(t => t !== tower)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,6 @@
|
|||
import { Cloneable } from '../store/cloneable';
|
||||
import { Node } from '../store/node';
|
||||
import { InnerNode } from '../store/inner-node';
|
||||
|
||||
export class Serializable extends Cloneable {
|
||||
protected type: string;
|
||||
|
||||
private static propertyList: any = {};
|
||||
export class Serializable extends InnerNode {
|
||||
static childrenMap: {
|
||||
[type: string]: {
|
||||
childrenConstructor: typeof Serializable;
|
||||
|
|
@ -13,12 +9,11 @@ export class Serializable extends Cloneable {
|
|||
};
|
||||
};
|
||||
|
||||
protected onAfterClone(): void {
|
||||
// pass
|
||||
}
|
||||
private static propertyList: any = {};
|
||||
protected type: string;
|
||||
|
||||
protected constructor(parent: Node, properties: any, type: string) {
|
||||
super(parent);
|
||||
protected constructor(properties: any, type: string) {
|
||||
super();
|
||||
|
||||
const compiledType = this.constructor.name;
|
||||
if (!Serializable.propertyList.hasOwnProperty(compiledType)) {
|
||||
|
|
@ -28,16 +23,15 @@ export class Serializable extends Cloneable {
|
|||
if (properties.hasOwnProperty(property)) {
|
||||
const propertyValue = properties[property];
|
||||
// This should be ran after the original constructor has finished.
|
||||
console.log(type);
|
||||
if (property === Serializable.childrenMap[type].childrenListName) {
|
||||
new Promise(r => r()).then(() => {
|
||||
for (let child of propertyValue) {
|
||||
new Serializable.childrenMap[type].childrenConstructor(
|
||||
this,
|
||||
child,
|
||||
Serializable.childrenMap[type].childrenType
|
||||
);
|
||||
}
|
||||
const children = propertyValue.map(
|
||||
c =>
|
||||
new Serializable.childrenMap[type].childrenConstructor(c, Serializable.childrenMap[type].childrenType)
|
||||
);
|
||||
console.log(type, 'created');
|
||||
this.addChildren(children);
|
||||
console.log(type, 'added');
|
||||
});
|
||||
} else {
|
||||
this[property] = properties[property];
|
||||
|
|
|
|||
|
|
@ -3,16 +3,16 @@ import { lighten } from '../utils/color';
|
|||
import { Block } from './block';
|
||||
import { Serializable } from './serializable';
|
||||
import { hash } from '../utils/hash';
|
||||
import { Node } from '../store/node';
|
||||
import { IColor } from '../interfaces/color';
|
||||
import { InnerNodeState } from '../store/inner-node';
|
||||
|
||||
export type ColoredBlock = Block & { color: IColor };
|
||||
|
||||
export class Tower extends Serializable implements ITower {
|
||||
protected type = 'Tower';
|
||||
export interface TowerState extends ITower, InnerNodeState {}
|
||||
|
||||
export class Tower extends Serializable implements ITower, TowerState {
|
||||
tags: string[];
|
||||
name: string;
|
||||
readonly name: string;
|
||||
|
||||
get blocks(): Array<Block> {
|
||||
return this.children as Array<Block>;
|
||||
|
|
@ -22,12 +22,11 @@ export class Tower extends Serializable implements ITower {
|
|||
|
||||
readonly baseColor: IColor;
|
||||
|
||||
constructor(parent: Node, props: ITower) {
|
||||
super(parent, props, 'Tower');
|
||||
this.onAfterClone();
|
||||
constructor(props: ITower) {
|
||||
super(props, 'Tower');
|
||||
}
|
||||
|
||||
protected onAfterClone(): void {
|
||||
protected onAfterClone() {
|
||||
this.blocks.sort((a, b) => {
|
||||
return a.created.getTime() - b.created.getTime();
|
||||
});
|
||||
|
|
@ -47,15 +46,15 @@ export class Tower extends Serializable implements ITower {
|
|||
}
|
||||
|
||||
addBlock(props: { tag: string; description: string; isDone: boolean }) {
|
||||
new Block(this, {
|
||||
created: new Date(),
|
||||
...props
|
||||
});
|
||||
this.addChildren([
|
||||
new Block({
|
||||
created: new Date(),
|
||||
...props
|
||||
})
|
||||
]);
|
||||
}
|
||||
|
||||
changeName(newName: string) {
|
||||
// For optimization purposes.
|
||||
this.name = newName;
|
||||
this.mutatedUpdate();
|
||||
changeName(name: string) {
|
||||
this.changeKeys<TowerState>({ name });
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,10 +13,6 @@ import { Observable } from 'rxjs/internal/Observable';
|
|||
providedIn: 'root'
|
||||
})
|
||||
export class DataService extends Root<Page> {
|
||||
get pages(): Array<Page> {
|
||||
return this.children;
|
||||
}
|
||||
|
||||
private readonly _safeChildren: BehaviorSubject<Array<Page>> = new BehaviorSubject(null);
|
||||
readonly safeChildren$: Observable<Array<Page>> = this._safeChildren.asObservable();
|
||||
|
||||
|
|
@ -25,6 +21,30 @@ export class DataService extends Root<Page> {
|
|||
this.init().catch();
|
||||
}
|
||||
|
||||
get pages(): Array<Page> {
|
||||
return this.children;
|
||||
}
|
||||
|
||||
save(timeout: number) {
|
||||
this.storeService.scheduleSave(this.pages, timeout);
|
||||
}
|
||||
|
||||
addPage(name: string) {
|
||||
const page = new Page({
|
||||
name,
|
||||
userData: {},
|
||||
towers: []
|
||||
});
|
||||
this.addChildren([page]);
|
||||
page.addTower();
|
||||
}
|
||||
|
||||
removePage(page: Page) {
|
||||
this.changeKeys<any>({
|
||||
children: this.children.filter(c => c !== page)
|
||||
});
|
||||
}
|
||||
|
||||
private async init() {
|
||||
const pages = await this.storeService.load();
|
||||
Serializable.childrenMap = {
|
||||
|
|
@ -44,43 +64,15 @@ export class DataService extends Root<Page> {
|
|||
childrenType: null
|
||||
}
|
||||
};
|
||||
this.children$.subscribe(value => {
|
||||
this.log();
|
||||
});
|
||||
|
||||
for (let page of pages) {
|
||||
new Page(this, page);
|
||||
}
|
||||
setTimeout(() => {
|
||||
this.children$.subscribe(value => {
|
||||
this.log();
|
||||
});
|
||||
}, 0);
|
||||
this.addChildren(pages.map(p => new Page(p)));
|
||||
|
||||
this.children$.subscribe(value => {
|
||||
this._safeChildren.next(value);
|
||||
this.save(0);
|
||||
});
|
||||
}
|
||||
|
||||
mutatedUpdate() {
|
||||
this.save(2500);
|
||||
}
|
||||
|
||||
save(timeout: number) {
|
||||
this.storeService.scheduleSave(this.pages, timeout);
|
||||
}
|
||||
|
||||
addPage(name: string) {
|
||||
const page = new Page(this, {
|
||||
name,
|
||||
userData: {},
|
||||
towers: []
|
||||
});
|
||||
page.addTower();
|
||||
}
|
||||
|
||||
removePage(page: Page) {
|
||||
this.changeValue({
|
||||
oldValue: this.children,
|
||||
newValue: this.children.filter(c => c !== page)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -95,7 +95,7 @@ export class StoreService<T> {
|
|||
|
||||
constructor() {
|
||||
const localStorageData = localStorage.getItem(LOCAL_STORAGE_KEY);
|
||||
this.storedData = JSON.parse(localStorageData ? localStorageData : this.mockData) as T;
|
||||
this.storedData = JSON.parse(false ? localStorageData : this.mockData) as T;
|
||||
}
|
||||
|
||||
scheduleSave(data: T, timeout: number) {
|
||||
|
|
|
|||
|
|
@ -1,102 +0,0 @@
|
|||
import { InnerNode } from './inner-node';
|
||||
import { Node } from './node';
|
||||
|
||||
export abstract class Cloneable extends InnerNode {
|
||||
protected constructor(parent: Node) {
|
||||
super(parent);
|
||||
}
|
||||
|
||||
protected abstract onAfterClone(): void;
|
||||
|
||||
protected cloneWithMap(map: (node: this) => void): this {
|
||||
const insides = Object.getOwnPropertyDescriptors(this);
|
||||
|
||||
const insidesProxy = new Proxy(insides, {
|
||||
get: (target, prop, proxy) => {
|
||||
if (prop == '__target__') {
|
||||
return target;
|
||||
}
|
||||
if (target.hasOwnProperty(prop)) {
|
||||
const value = target[prop as string].value;
|
||||
if (typeof value === 'function') {
|
||||
return value.bind(proxy);
|
||||
}
|
||||
return value;
|
||||
} else if (target.prototype.hasOwnProperty(prop)) {
|
||||
const value = target.prototype[prop];
|
||||
if (typeof value === 'function') {
|
||||
return value.bind(proxy);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
},
|
||||
set: (target, prop, value) => {
|
||||
return (target[prop as string].value = value);
|
||||
}
|
||||
});
|
||||
map(<any>insidesProxy);
|
||||
|
||||
return this.cloneFromInsides(<any>insidesProxy.__target__);
|
||||
}
|
||||
|
||||
protected cloneWithAdd({ propertyName, value }: { value: any; propertyName: string }): this {
|
||||
if (this[propertyName] === value) {
|
||||
return this;
|
||||
}
|
||||
|
||||
const insides = Object.getOwnPropertyDescriptors(this);
|
||||
insides[propertyName].value = value;
|
||||
return this.cloneFromInsides(insides);
|
||||
}
|
||||
|
||||
protected cloneWithChangedKeys(props: { [propertyName: string]: any }): this {
|
||||
const insides = Object.getOwnPropertyDescriptors(this);
|
||||
|
||||
for (let key in props) {
|
||||
if (props.hasOwnProperty(key)) {
|
||||
if (insides.hasOwnProperty(key)) {
|
||||
insides[key].value = props[key];
|
||||
} else {
|
||||
// @ts-ignore
|
||||
insides[key] = {
|
||||
value: props[key]
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return this.cloneFromInsides(insides);
|
||||
}
|
||||
|
||||
protected cloneWithModify({ oldValue, newValue }: { oldValue: any; newValue: any }): this {
|
||||
if (oldValue === newValue) {
|
||||
return this;
|
||||
}
|
||||
|
||||
const insides = Object.getOwnPropertyDescriptors(this);
|
||||
|
||||
let wasMatch = false;
|
||||
for (let name in insides) {
|
||||
if (insides.hasOwnProperty(name) && insides[name].value === oldValue) {
|
||||
insides[name].value = newValue;
|
||||
wasMatch = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!wasMatch) {
|
||||
throw new TypeError(`Object has no property with value: ${oldValue.toString()}`);
|
||||
}
|
||||
|
||||
return this.cloneFromInsides(insides);
|
||||
}
|
||||
|
||||
private cloneFromInsides(insides): this {
|
||||
insides.id.value = Node.id++;
|
||||
insides.copyCount.value++;
|
||||
Node.sumCopyCount++;
|
||||
|
||||
const clone = Object.create(Object.getPrototypeOf(this), insides);
|
||||
clone.onAfterClone();
|
||||
return clone;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,9 +1,18 @@
|
|||
import { Node } from './node';
|
||||
import { Node, NodeState } from './node';
|
||||
|
||||
export abstract class InnerNode extends Node {
|
||||
readonly children: Array<InnerNode> = [];
|
||||
protected parent: Node;
|
||||
export interface InnerNodeState extends NodeState {
|
||||
dummy: any;
|
||||
}
|
||||
|
||||
export class InnerNode extends Node implements InnerNodeState {
|
||||
readonly dummy = 3;
|
||||
parent: Node;
|
||||
private nextVersion: this = null;
|
||||
readonly children: Array<InnerNode> = [];
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
get latestVersion(): this {
|
||||
let version;
|
||||
|
|
@ -13,40 +22,34 @@ export abstract class InnerNode extends Node {
|
|||
return version;
|
||||
}
|
||||
|
||||
mutatedUpdate() {
|
||||
this.parent.mutatedUpdate();
|
||||
addChildren(children: Array<InnerNode>) {
|
||||
super.addChildren.call(this.latestVersion, children);
|
||||
}
|
||||
|
||||
map(map: (a: this) => void) {
|
||||
return this.update((self: this) => this.cloneWithMap.call(self, map));
|
||||
}
|
||||
|
||||
changeKeys(props: { [propertyName: string]: any }): this {
|
||||
return this.update((self: this) => this.cloneWithChangedKeys.call(self, props));
|
||||
}
|
||||
|
||||
addChild(update: { child: InnerNode }) {
|
||||
super.addChild.call(this.latestVersion, update);
|
||||
}
|
||||
|
||||
changeChild(update: { oldValue: InnerNode; newValue: InnerNode }) {
|
||||
replaceChild(update: { oldValue: InnerNode; newValue: InnerNode }) {
|
||||
super.replaceChild.call(this.latestVersion, update);
|
||||
}
|
||||
|
||||
protected abstract cloneWithMap(map: (a: this) => void): this;
|
||||
protected abstract cloneWithChangedKeys(props: { [propertyName: string]: any }): this;
|
||||
|
||||
private update(cloneMethod: (self: this) => this): this {
|
||||
changeKeys<T extends NodeState>(props: Partial<T>): this {
|
||||
if (this.nextVersion !== null) {
|
||||
this.latestVersion.update(cloneMethod);
|
||||
this.latestVersion.changeKeys(props);
|
||||
}
|
||||
|
||||
const clone = cloneMethod(this);
|
||||
if (clone === this) {
|
||||
return this;
|
||||
const clone = this.cloneWithChangedKeys(props);
|
||||
|
||||
let shouldClone = false;
|
||||
for (const prop in props) {
|
||||
// @ts-ignore
|
||||
if (props.hasOwnProperty(prop) && props[prop] !== this[prop]) {
|
||||
shouldClone = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!shouldClone) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (let child of clone.children) {
|
||||
for (const child of clone.children) {
|
||||
child.parent = clone;
|
||||
}
|
||||
|
||||
|
|
@ -58,4 +61,28 @@ export abstract class InnerNode extends Node {
|
|||
this.nextVersion = clone;
|
||||
return clone;
|
||||
}
|
||||
|
||||
protected onAfterClone() {}
|
||||
|
||||
protected cloneWithChangedKeys<T extends NodeState>(props: Partial<T>): this {
|
||||
const insides = Object.getOwnPropertyDescriptors(this);
|
||||
|
||||
for (const key in props) {
|
||||
if (props.hasOwnProperty(key)) {
|
||||
if (insides.hasOwnProperty(key)) {
|
||||
insides[key].value = props[key];
|
||||
} else {
|
||||
// @ts-ignore
|
||||
insides[key] = {
|
||||
value: props[key]
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const clone = Object.create(Object.getPrototypeOf(this), insides);
|
||||
clone.initiate();
|
||||
clone.onAfterClone();
|
||||
return clone;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,22 +1,24 @@
|
|||
import { InnerNode } from './inner-node';
|
||||
import { Unique } from './unique';
|
||||
import { InnerNode } from './inner-node';
|
||||
|
||||
export abstract class Node extends Unique {
|
||||
readonly children: Array<InnerNode>;
|
||||
// TODO: fix types.
|
||||
protected abstract changeKeys(props: any): this;
|
||||
abstract mutatedUpdate(): void;
|
||||
export interface NodeState {
|
||||
children: Array<InnerNode>;
|
||||
}
|
||||
|
||||
private copyCount = 0;
|
||||
export abstract class Node extends Unique implements NodeState {
|
||||
protected copyCount = 1;
|
||||
abstract readonly children: Array<InnerNode>;
|
||||
|
||||
protected abstract changeKeys<T extends NodeState>(props: Partial<T>): this;
|
||||
|
||||
protected initiate() {
|
||||
super.initiate();
|
||||
this.copyCount++;
|
||||
++this.copyCount;
|
||||
}
|
||||
|
||||
addChild({ child }: { child: InnerNode }) {
|
||||
this.changeKeys({
|
||||
children: [...this.children, child]
|
||||
addChildren(children: Array<InnerNode>) {
|
||||
this.changeKeys<NodeState>({
|
||||
children: [...this.children, ...children]
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -25,7 +27,7 @@ export abstract class Node extends Unique {
|
|||
return;
|
||||
}
|
||||
|
||||
this.changeKeys({
|
||||
this.changeKeys<NodeState>({
|
||||
children: this.children.map(c => (c === oldValue ? newValue : c))
|
||||
});
|
||||
}
|
||||
|
|
@ -33,7 +35,7 @@ export abstract class Node extends Unique {
|
|||
protected _log(indent = ''): string {
|
||||
const basicInfo = `${indent} - ${this.constructor.name}, #${this.id}`;
|
||||
let response = `${basicInfo}${' '.repeat(25 - basicInfo.length)}siblings: ${this.copyCount}\n`;
|
||||
for (let c of this.children) {
|
||||
for (const c of this.children) {
|
||||
response += `${c._log(indent + ' ')}`;
|
||||
}
|
||||
return response;
|
||||
|
|
|
|||
|
|
@ -1,12 +1,16 @@
|
|||
import { BehaviorSubject } from 'rxjs/internal/BehaviorSubject';
|
||||
import { Observable } from 'rxjs/internal/Observable';
|
||||
import { Node } from './node';
|
||||
import { Node, NodeState } from './node';
|
||||
import { InnerNode } from './inner-node';
|
||||
|
||||
export class Root<T extends InnerNode> extends Node {
|
||||
private readonly _children: BehaviorSubject<Array<T>> = new BehaviorSubject([]);
|
||||
readonly children$: Observable<Array<T>> = this._children.asObservable();
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
get children(): Array<T> {
|
||||
return this._children.getValue();
|
||||
}
|
||||
|
|
@ -15,27 +19,14 @@ export class Root<T extends InnerNode> extends Node {
|
|||
this._children.next(value);
|
||||
}
|
||||
|
||||
mutatedUpdate() {
|
||||
// pass
|
||||
}
|
||||
|
||||
changeValue({ oldValue, newValue }: { oldValue: any; newValue: any }) {
|
||||
if (this.children !== oldValue) {
|
||||
throw new TypeError('Only children can be changed.');
|
||||
}
|
||||
this.children = newValue;
|
||||
for (let child of this.children) {
|
||||
child.parent = this;
|
||||
}
|
||||
}
|
||||
|
||||
changeKey({ propertyName, value }: { propertyName: string; value: any }) {
|
||||
if (propertyName !== 'children') {
|
||||
throw new TypeError('Only children can be changed.');
|
||||
}
|
||||
this.children = value;
|
||||
for (let child of this.children) {
|
||||
child.parent = this;
|
||||
changeKeys<U extends NodeState>(props: Partial<U>): this {
|
||||
if (props.hasOwnProperty('children')) {
|
||||
// @ts-ignore
|
||||
this.children = props.children;
|
||||
for (const child of this.children) {
|
||||
child.parent = this;
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,10 +6,10 @@ export abstract class Unique extends Initiable {
|
|||
return Unique.nextId;
|
||||
}
|
||||
|
||||
private _id: number;
|
||||
get id(): number {
|
||||
return this._id;
|
||||
}
|
||||
private _id: number;
|
||||
|
||||
protected initiate() {
|
||||
this._id = Unique.nextId++;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue