@etsoo/shared 1.1.25 → 1.1.28

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,253 @@
1
+ /**
2
+ * Abstract event base class
3
+ * T for type
4
+ * D for data
5
+ */
6
+ export abstract class EventBase<T extends string, D> {
7
+ private _propagationStopped: boolean = false;
8
+ /**
9
+ * stopImmediatePropagation called
10
+ */
11
+ get propagationStopped() {
12
+ return this._propagationStopped;
13
+ }
14
+
15
+ private _timeStamp: number;
16
+ /**
17
+ * Time stamp
18
+ */
19
+ get timeStamp() {
20
+ return this._timeStamp;
21
+ }
22
+
23
+ /**
24
+ * Constructor
25
+ * @param type Type
26
+ */
27
+ constructor(
28
+ public readonly target: EventClass<T, D>,
29
+ public readonly type: T,
30
+ public readonly data: D
31
+ ) {
32
+ this._timeStamp = Date.now();
33
+ }
34
+
35
+ /**
36
+ * Prevent all other listeners from being called
37
+ */
38
+ stopImmediatePropagation() {
39
+ this._propagationStopped = true;
40
+ }
41
+ }
42
+
43
+ /**
44
+ * Event options
45
+ */
46
+ interface EventOptions {
47
+ /**
48
+ * A boolean value indicating that events of this type will be dispatched first
49
+ */
50
+ capture?: boolean;
51
+
52
+ /**
53
+ * A boolean value indicating that the listener should be invoked at most once after being added
54
+ */
55
+ once?: boolean;
56
+ }
57
+
58
+ /**
59
+ * Event class callback
60
+ * T for type
61
+ * D for data
62
+ */
63
+ export type EventClassCallback<T extends string, D> = (
64
+ event: EventBase<T, D>
65
+ ) => void;
66
+
67
+ /**
68
+ * Event class collection definition
69
+ * T for type
70
+ * D for data
71
+ */
72
+ export type EventClassCollection<T extends string, D> = {
73
+ [key in T]?: EventClassCallback<T, D>;
74
+ };
75
+
76
+ /**
77
+ * Event class
78
+ * T for type
79
+ * D for data
80
+ */
81
+ export abstract class EventClass<T extends string, D> {
82
+ // Listeners
83
+ private readonly listeners = new Map<
84
+ T,
85
+ [EventClassCallback<T, D>, EventOptions?][]
86
+ >();
87
+
88
+ /**
89
+ * Has specific type events
90
+ * @param type Type
91
+ */
92
+ hasEvents(type: T): boolean;
93
+
94
+ /**
95
+ * Has specific type and callback events
96
+ * @param type Type
97
+ * @param callback Callback
98
+ */
99
+ hasEvents(type: T, callback: EventClassCallback<T, D>): boolean;
100
+
101
+ /**
102
+ * Has specific type and callback events
103
+ * @param type Type
104
+ * @param callback Callback
105
+ * @returns Result
106
+ */
107
+ hasEvents(type: T, callback?: EventClassCallback<T, D>) {
108
+ const items = this.listeners.get(type);
109
+ if (items == null || items.length === 0) return false;
110
+
111
+ if (callback) {
112
+ return items.some((item) => item[0] == callback);
113
+ }
114
+
115
+ return true;
116
+ }
117
+
118
+ /**
119
+ * Remove all specific type events
120
+ * @param type Type
121
+ */
122
+ off(type: T): void;
123
+
124
+ /**
125
+ * Remove specific type and callback event
126
+ * @param type Type
127
+ * @param callback Callback
128
+ */
129
+ off(type: T, callback: EventClassCallback<T, D>): void;
130
+
131
+ /**
132
+ * Remove specific type and callback event
133
+ * @param type Type
134
+ * @param callback Callback
135
+ */
136
+ off(type: T, callback?: EventClassCallback<T, D>) {
137
+ if (callback == null) {
138
+ this.listeners.delete(type);
139
+ return;
140
+ }
141
+
142
+ const items = this.listeners.get(type);
143
+ if (items == null) return;
144
+
145
+ for (let i = items.length - 1; i >= 0; i--) {
146
+ if (items[i][0] == callback) {
147
+ items.splice(i, 1);
148
+ }
149
+ }
150
+ }
151
+
152
+ /**
153
+ * Add event listeners
154
+ * @param collection Collection of events
155
+ */
156
+ on(collection: EventClassCollection<T, D>): void;
157
+
158
+ /**
159
+ * Add event listener
160
+ * @param type Type
161
+ * @param callback Callback
162
+ * @param options Options
163
+ */
164
+ on(
165
+ type: T,
166
+ callback: EventClassCallback<T, D>,
167
+ options?: EventOptions
168
+ ): void;
169
+
170
+ /**
171
+ * Add events
172
+ * @param type Type
173
+ * @param callback Callback
174
+ * @param options Options
175
+ */
176
+ on(
177
+ type: EventClassCollection<T, D> | T,
178
+ callback?: EventClassCallback<T, D>,
179
+ options?: EventOptions
180
+ ) {
181
+ if (typeof type === 'object') {
182
+ for (const key in type) {
183
+ const item = key as T;
184
+ const itemCallback = type[item] ?? callback;
185
+ if (itemCallback) this.on(item, itemCallback, options);
186
+ }
187
+ return;
188
+ }
189
+
190
+ if (callback == null) return;
191
+ this.listeners.has(type) || this.listeners.set(type, []);
192
+ this.listeners.get(type)?.push([callback, options]);
193
+ }
194
+
195
+ /**
196
+ * Trigger event
197
+ * @param event Event
198
+ */
199
+ trigger(event: EventBase<T, D>) {
200
+ const items = this.listeners.get(event.type);
201
+ if (items == null) return;
202
+
203
+ // Len
204
+ const len = items.length;
205
+ if (len === 0) return;
206
+
207
+ // Need to be removed indicies
208
+ const indicies: number[] = [];
209
+
210
+ // Capture items first
211
+ let stopped: boolean = false;
212
+ for (let c = 0; c < len; c++) {
213
+ const item = items[c];
214
+ const [callback, options] = item;
215
+ if (options == null || !options.capture) continue;
216
+
217
+ callback(event);
218
+
219
+ if (options.once) {
220
+ indicies.push(c);
221
+ }
222
+
223
+ if (event.propagationStopped) {
224
+ stopped = true;
225
+ break;
226
+ }
227
+ }
228
+
229
+ if (!stopped) {
230
+ for (let c = 0; c < len; c++) {
231
+ const item = items[c];
232
+ const [callback, options] = item;
233
+ if (options?.capture) continue;
234
+
235
+ callback(event);
236
+
237
+ if (options?.once) {
238
+ indicies.push(c);
239
+ }
240
+
241
+ if (event.propagationStopped) {
242
+ stopped = true;
243
+ break;
244
+ }
245
+ }
246
+ }
247
+
248
+ // Remove all once handlers
249
+ for (let i = indicies.length - 1; i >= 0; i--) {
250
+ items.splice(indicies[i], 1);
251
+ }
252
+ }
253
+ }