UNPKG

4.66 kBPlain TextView Raw
1import { EventEmitter } from "events";
2
3export class Store<T extends Object> {
4 protected state: T;
5 protected event = new EventEmitter();
6
7 /**
8 * Create a Store that hold the state of the component
9 * @param name The name of the store
10 * @param initialState The initial state of the store
11 */
12 constructor(public name: string, private initialState: T) {
13 this.state = this.initialState;
14 }
15
16 /** Listen on event from the store */
17 public get on() {
18 return this.event.on;
19 }
20
21 /** Liste once on event from the store */
22 public get once() {
23 return this.event.once;
24 }
25
26 /** Update one field of the state */
27 public update(state: Partial<T>) {
28 this.state = { ...this.state, ...state };
29 }
30
31 /** Get one field of the state */
32 public get<Key extends keyof T>(key: Key): T[Key] {
33 return this.state[key];
34 }
35
36 /** Reset the state its initial value */
37 public reset() {
38 this.state = this.initialState;
39 }
40
41 /** Dispatch an event with the new state */
42 public dispatch() {
43 this.event.emit("newState", this.state);
44 }
45}
46
47interface EntityState<T> {
48 ids: string[];
49 actives: string[];
50 entities: {
51 [id: string]: T;
52 };
53 [key: string]: any;
54}
55
56export class EntityStore<T> extends Store<EntityState<T>> {
57 /**
58 * Create a entity Store that hold a map entity of the same model
59 * @param name The name of the store
60 * @param initialState The initial state of the store
61 * @param keyId The value used as a key for the `entities` and inside `ids`
62 */
63 constructor(
64 name: string,
65 initialState: EntityState<T>,
66 private keyId: keyof T
67 ) {
68 super(name, initialState);
69 }
70
71 ////////////
72 // GETTER //
73 ////////////
74
75 /** Tne entities as a Map */
76 get entities() {
77 return this.state.entities;
78 }
79
80 /** List of all the ids */
81 get ids() {
82 return this.state.ids;
83 }
84
85 /** List of all active ID */
86 get actives() {
87 return this.state.actives;
88 }
89
90 /** Return the length of the entity collection */
91 get length() {
92 return this.state.ids.length;
93 }
94
95 /////////////
96 // SETTERS //
97 /////////////
98
99 /** Add a new entity to the state */
100 public add(entity: T) {
101 const id = entity[this.keyId];
102 if (typeof id !== "string") {
103 throw new Error(
104 `${id} should be of type 'string', but is of type ${typeof id}`
105 );
106 }
107 this.state.entities[id as string] = entity;
108 this.state.ids.push(id);
109 }
110
111 /** Remove an entity from the state */
112 public remove(id: string) {
113 delete this.state.entities[id];
114 this.state.ids.slice(this.state.ids.indexOf(id));
115 this.state.actives.slice(this.state.ids.indexOf(id));
116 }
117
118 /** Update one entity of the state */
119 public updateOne(id: string, update: Partial<T>) {
120 this.state.entities[id] = {
121 ...this.state.entities[id],
122 ...update
123 };
124 }
125
126 /** Activate one or several entity from the state */
127 public activate(ids: string[] | string) {
128 Array.isArray(ids)
129 ? this.state.actives.push()
130 : this.state.actives.concat(ids);
131 this.event.emit("activate", ids);
132 }
133
134 /** Remove one or */
135 public deactivate(ids: string[] | string) {
136 Array.isArray(ids)
137 ? ids.forEach(id => this.state.actives.slice(this.state.ids.indexOf(id)))
138 : this.state.actives.slice(this.state.ids.indexOf(ids));
139 }
140
141 ///////////
142 // QUERY //
143 ///////////
144
145 /** Get one entity */
146 getOne(id: string) {
147 return this.state.entities[id];
148 }
149
150 /** Get many entities as an array */
151 getMany(ids: string[]) {
152 return ids.map(id => this.state.entities[id]);
153 }
154
155 /** Get all the entities as an array */
156 getAll() {
157 return this.state.ids.map(id => this.state.entities[id]);
158 }
159
160 /** Get all active entities */
161 getActives() {
162 return this.state.actives.map(id => this.state.entities[id]);
163 }
164
165 ////////////////
166 // CONDITIONS //
167 ////////////////
168
169 /** Is the entity active */
170 public isActive(id: string) {
171 return this.state.actives.includes(id);
172 }
173
174 /** Is this id inside the store */
175 public hasEntity(id: string) {
176 return this.state.ids.includes(id);
177 }
178
179 /** Is the state empty */
180 public isEmpty() {
181 return this.state.ids.length === 0;
182 }
183}
184
185/** Store the state of the stores into LocalStorage */
186function localState(stores: Store<any>[]) {
187 stores.forEach(store => {
188 const name = store.name;
189 store.on("newState", (state: any) => {
190 localStorage.setItem(name, JSON.stringify(state));
191 });
192 });
193}