8000 feat: implement Event and EventTarget · NativeScript/NativeScript@f22db38 · GitHub
[go: up one dir, main page]

Skip to content

Commit f22db38

Browse files
committed
feat: implement Event and EventTarget
1 parent f252593 commit f22db38

File tree

5 files changed

+59
-36
lines changed

5 files changed

+59
-36
lines changed

packages/core/data/dom-events/dom-event.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ const timeOrigin = Date.now();
1010
*/
1111
const emptyArray = [] as const;
1212

13-
export class DOMEvent {
13+
export class DOMEvent implements Event {
1414
/**
1515
* @private
1616
* Internal API to facilitate testing - to be removed once we've completed
@@ -119,7 +119,7 @@ export class DOMEvent {
119119

120120
// From CustomEvent rather than Event. Can consider factoring out this
121121
// aspect into DOMCustomEvent.
122-
private readonly detail: unknown | null;
122+
readonly detail: unknown | null;
123123

124124
private propagationState: EventPropagationState = EventPropagationState.resume;
125125

packages/core/data/observable/index.ts

Lines changed: 39 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
import type { ViewBase } from '../../ui/core/view-base';
22
import { DOMEvent } from '../dom-events/dom-event';
33

4-
import { Observable as ObservableDefinition, WrappedValue as WrappedValueDefinition } from '.';
5-
64
/**
75
* Base event data.
86
*/
8000
@@ -51,7 +49,7 @@ let _wrappedIndex = 0;
5149
* By default property change will not be fired for a same object.
5250
* By wrapping object into a WrappedValue instance `same object restriction` will be passed.
5351
*/
54-
export class WrappedValue implements WrappedValueDefinition {
52+
export class WrappedValue {
5553
/**
5654
* Creates an instance of WrappedValue object.
5755
* @param wrapped - the real value which should be wrapped.
@@ -96,7 +94,7 @@ const _globalEventHandlers: {
9694
* Please note that should you be using the `new Observable({})` constructor, it is **obsolete** since v3.0,
9795
* and you have to migrate to the "data/observable" `fromObject({})` or the `fromObjectRecursive({})` functions.
9896
*/
99-
export class Observable implements ObservableDefinition {
97+
export class Observable implements EventTarget {
10098
/**
10199
* String value used when hooking to propertyChange event.
102100
*/
@@ -186,27 +184,27 @@ export class Observable implements ObservableDefinition {
186184
* @param thisArg An optional parameter which when set will be used as "this" in callback method call.
187185
* @param options An optional parameter. If passed as a boolean, configures the useCapture value. Otherwise, specifies options.
188186
*/
189-
public addEventListener(eventNames: string, callback: (data: EventData) => void, thisArg?: any, options?: AddEventListenerOptions | boolean): void {
187+
public addEventListener(eventNames: string, callback: EventListenerOrEventListenerObject | ((data: EventData) => void), thisArg?: any, options?: AddEventListenerOptions | boolean): void {
190188
if (typeof eventNames !== 'string') {
191189
throw new TypeError('Events name(s) must be string.');
192190
}
193191

194192
if (typeof callback !== 'function') {
195-
throw new TypeError('callback must be function.');
193+
throw new TypeError('Callback must be function.');
196194
}
197195

198196
const events = eventNames.trim().split(eventDelimiterPattern);
199197
for (let i = 0, l = events.length; i < l; i++) {
200198
const event = events[i];
201199
const list = this.getEventList(event, true);
202-
if (Observable._indexOfListener(list, callback, thisArg, options) >= 0) {
200+
if (Observable._indexOfListener(list, callback as (data: EventData) => void, thisArg, options) >= 0) {
203201
// Don't allow addition of duplicate event listeners.
204202
continue;
205203
}
206204

207205
// TODO: Performance optimization - if we do not have the thisArg specified, do not wrap the callback in additional object (ObserveEntry)
208206
list.push({
209-
callback,
207+
callback: callback as (data: EventData) => void,
210208
thisArg,
211209
...normalizeEventOptions(options),
212210
});
@@ -220,7 +218,7 @@ export class Observable implements ObservableDefinition {
220218
* @param thisArg An optional parameter which when set will be used to refine search of the correct callback which will be removed as event listener.
221219
* @param options An optional parameter. If passed as a boolean, configures the useCapture value. Otherwise, specifies options.
222220
*/
223-
public removeEventListener(eventNames: string, callback?: (data: EventData) => void, thisArg?: any, options?: EventListenerOptions | boolean): void {
221+
public removeEventListener(eventNames: string, callback?: EventListenerOrEventListenerObject | ((data: EventData) => void), thisArg?: any, options?: EventListenerOptions | boolean): void {
224222
if (typeof eventNames !== 'string') {
225223
throw new TypeError('Events name(s) must be string.');
226224
}
@@ -236,14 +234,16 @@ export class Observable implements ObservableDefinition {
236234
}
237235

238236
const list = this.getEventList(event, false);
239-
if (list) {
240-
const index = Observable._indexOfListener(list, callback, thisArg, options);
241-
if (index >= 0) {
242-
list.splice(index, 1);
243-
}
244-
if (list.length === 0) {
245-
delete this._observers[event];
246-
}
237+
if (!list) {
238+
continue;
239+
}
240+
241+
const index = Observable._indexOfListener(list, callback as (data: EventData) => void, thisArg, options);
242+
if (index >= 0) {
243+
list.splice(index, 1);
244+
}
245+
if (list.length === 0) {
246+
delete this._observers[event];
247247
}
248248
}
249249
}
@@ -260,13 +260,13 @@ export class Observable implements ObservableDefinition {
260260
this.removeEventListener(eventName, callback, thisArg, options);
261261
}
262262

263-
public static removeEventListener(eventName: string, callback?: (data: EventData) => void, thisArg?: any, options?: EventListenerOptions | boolean): void {
263+
public static removeEventListener(eventName: string, callback?: EventListenerOrEventListenerObject | ((data: EventData) => void), thisArg?: any, options?: EventListenerOptions | boolean): void {
264264
if (typeof eventName !== 'string') {
265265
throw new TypeError('Event must be string.');
266266
}
267267

268268
if (callback && typeof callback !== 'function') {
269-
throw new TypeError('callback must be function.');
269+
throw new TypeError('Callback, if provided, must be function.');
270270
}
271271

272272
const eventClass = this.name === 'Observable' ? '*' : this.name;
@@ -278,7 +278,7 @@ export class Observable implements ObservableDefinition {
278278

279279
const events = _globalEventHandlers[eventClass][eventName];
280280
if (callback) {
281-
const index = Observable._indexOfListener(events, callback, thisArg, options);
281+
const index = Observable._indexOfListener(events, callback as (data: EventData) => void, thisArg, options);
282282
if (index >= 0) {
283283
events.splice(index, 1);
284284
}
@@ -299,13 +299,13 @@ export class Observable implements ObservableDefinition {
299299
}
300300
}
301301

302-
public static addEventListener(eventName: string, callback: (data: EventData) => void, thisArg?: any, options?: AddEventListenerOptions | boolean): void {
302+
public static addEventListener(eventName: string, callback: EventListenerOrEventListenerObject | ((data: EventData) => void), thisArg?: any, options?: AddEventListenerOptions | boolean): void {
303303
if (typeof eventName !== 'string') {
304304
throw new TypeError('Event must be string.');
305305
}
306306

307307
if (typeof callback !== 'function') {
308-
throw new TypeError('callback must be function.');
308+
throw new TypeError('Callback must be function.');
309309
}
310310

311311
const eventClass = this.name === 'Observable' ? '*' : this.name;
@@ -317,13 +317,13 @@ export class Observable implements ObservableDefinition {
317317
}
318318

319319
const list = _globalEventHandlers[eventClass][eventName];
320-
if (Observable._indexOfListener(list, callback, thisArg, options) >= 0) {
320+
if (Observable._indexOfListener(list, callback as (data: EventData) => void, thisArg, options) >= 0) {
321321
// Don't allow addition of duplicate event listeners.
322322
return;
323323
}
324324

325325
_globalEventHandlers[eventClass][eventName].push({
326-
callback,
326+
callback: callback as (data: EventData) => void,
327327
thisArg,
328328
...normalizeEventOptions(options),
329329
});
@@ -384,6 +384,21 @@ export class Observable implements ObservableDefinition {
384384
});
385385
}
386386

387+
dispatchEvent(event: DOMEvent): boolean {
388+
const data = {
389+
eventName: event.type,
390+
object: this,
391+
detail: event.detail,
392+
};
393+
394+
return event.dispatchTo({
395+
target: this,
396+
data,
397+
getGlobalEventHandlersPreHandling: () => this._getGlobalEventHandlers(data, 'First'),
398+
getGlobalEventHandlersPostHandling: () => this._getGlobalEventHandlers(data, ''),
399+
});
400+
}
401+
387402
private _getGlobalEventHandlers(data: EventData, eventType: 'First' | ''): ListenerEntry[] {
388403
const eventClass = data.object?.constructor?.name;
389404
const globalEventHandlersForOwnClass = _globalEventHandlers[eventClass]?.[`${data.eventName}${eventType}`] ?? [];

packages/core/ui/core/view/view-common.ts

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -292,7 +292,11 @@ export abstract class ViewCommon extends ViewBase implements ViewDefinition {
292292
return this._gestureObservers[type] || [];
293293
}
294294

295-
public addEventListener(arg: string | GestureTypes, callback: (data: EventData) => void, thisArg?: any, options?: AddEventListenerOptions | boolean): void {
295+
public addEventListener(arg: string | GestureTypes, callback: EventListenerOrEventListenerObject | ((data: EventData) => void), thisArg?: any, options?: AddEventListenerOptions | boolean): void {
296+
if (typeof callback !== 'function') {
297+
throw new TypeError('Callback must be function.');
298+
}
299+
296300
// To avoid a full refactor of the Gestures system when migrating to DOM
297301
// Events, we mirror the this._gestureObservers record, creating
298302
// corresponding DOM Event listeners for each gesture.
@@ -308,7 +312,7 @@ export abstract class ViewCommon extends ViewBase implements ViewDefinition {
308312
// the same time).
309313

310314
if (typeof arg === 'number') {
311-
this._observe(arg, callback, thisArg, options);
315+
this._observe(arg, callback as (data: EventData) => void, thisArg, options);
312316
super.addEventListener(gestureToString(arg), callback, thisArg, options);
313317
return;
314318
}
@@ -320,15 +324,19 @@ export abstract class ViewCommon extends ViewBase implements ViewDefinition {
320324
for (const event of events) {
321325
const gesture = gestureFromString(event);
322326
if (gesture && !this._isEvent(arg)) {
323-
this._observe(gesture, callback, thisArg, options);
327+
this._observe(gesture, callback as (data: EventData) => void, thisArg, options);
324328
}
325329
super.addEventListener(event, callback, thisArg, options);
326330
}
327331
}
328332

329-
public removeEventListener(arg: string | GestureTypes, callback?: (data: EventData) => void, thisArg?: any, options?: EventListenerOptions | boolean): void {
333+
public removeEventListener(arg: string | GestureTypes, callback?: EventListenerOrEventListenerObject | ((data: EventData) => void), thisArg?: any, options?: EventListenerOptions | boolean): void {
334+
if (callback && typeof callback !== 'function') {
335+
throw new TypeError('Callback, if provided, must be function.');
336+
}
337+
330338
if (typeof arg === 'number') {
331-
this._disconnectGestureObservers(arg, callback, thisArg, options);
339+
this._disconnectGestureObservers(arg, callback as (data: EventData) => void, thisArg, options);
332340
super.removeEventListener(gestureToString(arg), callback, thisArg, options);
333341
return;
334342
}
@@ -338,7 +346,7 @@ export abstract class ViewCommon extends ViewBase implements ViewDefinition {
338346
for (const event of events) {
339347
const gesture = gestureFromString(event);
340348
if (gesture && !this._isEvent(arg)) {
341-
this._disconnectGestureObservers(gesture, callback, thisArg, options);
349+
this._disconnectGestureObservers(gesture, callback as (data: EventData) => void, thisArg, options);
342350
}
343351
super.removeEventListener(event, callback, thisArg, options);
344352
}

packages/core/ui/scroll-view/scroll-view-common.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ export abstract class ScrollViewBase extends ContentView implements ScrollViewDe
1616
public scrollBarIndicatorVisible: boolean;
1717
public isScrollEnabled: boolean;
1818

19-
public addEventListener(arg: string, callback: (data: EventData) => void, thisArg?: any, options?: AddEventListenerOptions | boolean): void {
19+
public addEventListener(arg: string, callback: EventListenerOrEventListenerObject | ((data: EventData) => void), thisArg?: any, options?: AddEventListenerOptions | boolean): void {
2020
super.addEventListener(arg, callback, thisArg, options);
2121

2222
if (arg === ScrollViewBase.scrollEvent) {
@@ -25,7 +25,7 @@ export abstract class ScrollViewBase extends ContentView implements ScrollViewDe
2525
}
2626
}
2727

28-
public removeEventListener(arg: string, callback?: (data: EventData) => void, thisArg?: any, options?: EventListenerOptions | boolean): void {
28+
public removeEventListener(arg: string, callback?: EventListenerOrEventListenerObject | ((data: EventData) => void), thisArg?: any, options?: EventListenerOptions | boolean): void {
2929
super.removeEventListener(arg, callback, thisArg, options);
3030

3131
if (arg === ScrollViewBase.scrollEvent) {

packages/core/ui/text-base/span.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,12 +81,12 @@ export class Span extends ViewBase implements SpanDefinition {
8181
return this._tappable;
8282
}
8383

84-
addEventListener(arg: string, callback: (data: EventData) => void, thisArg?: any, options?: AddEventListenerOptions | boolean): void {
84+
addEventListener(arg: string, callback: EventListenerOrEventListenerObject | ((data: EventData) => void), thisArg?: any, options?: AddEventListenerOptions | boolean): void {
8585
super.addEventListener(arg, callback, thisArg, options);
8686
this._setTappable(this.hasListeners(Span.linkTapEvent));
8787
}
8888

89-
removeEventListener(arg: string, callback?: (data: EventData) => void, thisArg?: any, options?: EventListenerOptions | boolean): void {
89+
removeEventListener(arg: string, callback?: EventListenerOrEventListenerObject | ((data: EventData) => void), thisArg?: any, options?: EventListenerOptions | boolean): void {
9090
super.removeEventListener(arg, callback, thisArg, options);
9191
this._setTappable(this.hasListeners(Span.linkTapEvent));
9292
}

0 commit comments

Comments
 (0)
0