@gjsify/dom-events 0.0.4 → 0.1.1

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.
package/src/index.ts CHANGED
@@ -1 +1,642 @@
1
- export * from '@gjsify/deno-runtime/ext/web/02_event'
1
+ // Reference: DOM Living Standard (https://dom.spec.whatwg.org/)
2
+ // EventTarget/Event implementation for GJS
3
+
4
+ // Re-export DOMException from dedicated package for backwards compatibility
5
+ import { DOMException } from '@gjsify/dom-exception';
6
+ export { DOMException };
7
+
8
+ type EventListenerOrEventListenerObject = EventListener | EventListenerObject;
9
+
10
+ interface EventListener {
11
+ (evt: Event): void;
12
+ }
13
+
14
+ interface EventListenerObject {
15
+ handleEvent(evt: Event): void;
16
+ }
17
+
18
+ interface AddEventListenerOptions extends EventListenerOptions {
19
+ once?: boolean;
20
+ passive?: boolean;
21
+ signal?: AbortSignal;
22
+ }
23
+
24
+ interface EventListenerOptions {
25
+ capture?: boolean;
26
+ }
27
+
28
+ interface EventInit {
29
+ bubbles?: boolean;
30
+ cancelable?: boolean;
31
+ composed?: boolean;
32
+ }
33
+
34
+ interface CustomEventInit<T = any> extends EventInit {
35
+ detail?: T;
36
+ }
37
+
38
+ interface ListenerEntry {
39
+ listener: EventListenerOrEventListenerObject;
40
+ capture: boolean;
41
+ once: boolean;
42
+ passive: boolean;
43
+ removed: boolean;
44
+ }
45
+
46
+ // Internal symbols for writable access to readonly properties
47
+ const kType = Symbol('type');
48
+ const kBubbles = Symbol('bubbles');
49
+ const kCancelable = Symbol('cancelable');
50
+ const kComposed = Symbol('composed');
51
+ const kTarget = Symbol('target');
52
+ const kCurrentTarget = Symbol('currentTarget');
53
+ const kEventPhase = Symbol('eventPhase');
54
+ const kDefaultPrevented = Symbol('defaultPrevented');
55
+ const kIsTrusted = Symbol('isTrusted');
56
+ const kTimeStamp = Symbol('timeStamp');
57
+ const kStop = Symbol('stop');
58
+ const kImmediateStop = Symbol('immediateStop');
59
+ const kDispatching = Symbol('dispatching');
60
+ const kInPassiveListener = Symbol('inPassiveListener');
61
+
62
+ export class Event {
63
+ // Internal state
64
+ private [kType]: string;
65
+ private [kBubbles]: boolean;
66
+ private [kCancelable]: boolean;
67
+ private [kComposed]: boolean;
68
+ private [kTarget]: EventTarget | null = null;
69
+ private [kCurrentTarget]: EventTarget | null = null;
70
+ private [kEventPhase]: number = 0;
71
+ private [kDefaultPrevented]: boolean = false;
72
+ private [kIsTrusted]: boolean = false;
73
+ private [kTimeStamp]: number;
74
+ private [kStop]: boolean = false;
75
+ private [kImmediateStop]: boolean = false;
76
+ private [kDispatching]: boolean = false;
77
+ private [kInPassiveListener]: boolean = false;
78
+
79
+ // Readonly getters
80
+ get type(): string { return this[kType]; }
81
+ get bubbles(): boolean { return this[kBubbles]; }
82
+ get cancelable(): boolean { return this[kCancelable]; }
83
+ get composed(): boolean { return this[kComposed]; }
84
+ get target(): EventTarget | null { return this[kTarget]; }
85
+ get currentTarget(): EventTarget | null { return this[kCurrentTarget]; }
86
+ get eventPhase(): number { return this[kEventPhase]; }
87
+ get defaultPrevented(): boolean { return this[kDefaultPrevented]; }
88
+ // isTrusted is defined as a non-configurable own property in the constructor
89
+ get timeStamp(): number { return this[kTimeStamp]; }
90
+
91
+ // Legacy compat
92
+ get cancelBubble(): boolean { return this[kStop]; }
93
+ set cancelBubble(value: boolean) { if (value) this.stopPropagation(); }
94
+
95
+ get returnValue(): boolean { return !this[kDefaultPrevented]; }
96
+ set returnValue(value: boolean) { if (!value) this.preventDefault(); }
97
+
98
+ get srcElement(): EventTarget | null { return this[kTarget]; }
99
+
100
+ // Phase constants (defined as non-writable, non-configurable on prototype below)
101
+ static readonly NONE = 0;
102
+ static readonly CAPTURING_PHASE = 1;
103
+ static readonly AT_TARGET = 2;
104
+ static readonly BUBBLING_PHASE = 3;
105
+ declare readonly NONE: 0;
106
+ declare readonly CAPTURING_PHASE: 1;
107
+ declare readonly AT_TARGET: 2;
108
+ declare readonly BUBBLING_PHASE: 3;
109
+
110
+ get [Symbol.toStringTag]() { return 'Event'; }
111
+
112
+ constructor(type: string, eventInitDict?: EventInit) {
113
+ this[kType] = type;
114
+ this[kBubbles] = eventInitDict?.bubbles ?? false;
115
+ this[kCancelable] = eventInitDict?.cancelable ?? false;
116
+ this[kComposed] = eventInitDict?.composed ?? false;
117
+ this[kTimeStamp] = Date.now();
118
+
119
+ // isTrusted must be a non-configurable own property so subclasses cannot override it
120
+ Object.defineProperty(this, 'isTrusted', {
121
+ get: () => this[kIsTrusted],
122
+ enumerable: true,
123
+ configurable: false,
124
+ });
125
+ }
126
+
127
+ composedPath(): EventTarget[] {
128
+ if (this[kCurrentTarget]) return [this[kCurrentTarget]];
129
+ return [];
130
+ }
131
+
132
+ preventDefault(): void {
133
+ if (this[kCancelable] && !this[kInPassiveListener]) {
134
+ this[kDefaultPrevented] = true;
135
+ }
136
+ }
137
+
138
+ stopPropagation(): void {
139
+ this[kStop] = true;
140
+ }
141
+
142
+ stopImmediatePropagation(): void {
143
+ this[kStop] = true;
144
+ this[kImmediateStop] = true;
145
+ }
146
+ }
147
+
148
+ export class CustomEvent<T = any> extends Event {
149
+ readonly detail: T;
150
+
151
+ constructor(type: string, eventInitDict?: CustomEventInit<T>) {
152
+ super(type, eventInitDict);
153
+ this.detail = eventInitDict?.detail as T;
154
+ }
155
+ }
156
+
157
+ // -- MessageEvent --
158
+ // Reference: refs/happy-dom/packages/happy-dom/src/event/events/MessageEvent.ts
159
+ // Copyright (c) David Ortner (capricorn86). MIT license.
160
+
161
+ interface MessageEventInit<T = any> extends EventInit {
162
+ data?: T;
163
+ origin?: string;
164
+ lastEventId?: string;
165
+ source?: EventTarget | null;
166
+ ports?: MessagePort[];
167
+ }
168
+
169
+ export class MessageEvent<T = any> extends Event {
170
+ readonly data: T;
171
+ readonly origin: string;
172
+ readonly lastEventId: string;
173
+ readonly source: EventTarget | null;
174
+ readonly ports: readonly MessagePort[];
175
+
176
+ constructor(type: string, eventInitDict?: MessageEventInit<T>) {
177
+ super(type, eventInitDict);
178
+ this.data = (eventInitDict?.data ?? null) as T;
179
+ this.origin = eventInitDict?.origin ?? '';
180
+ this.lastEventId = eventInitDict?.lastEventId ?? '';
181
+ this.source = eventInitDict?.source ?? null;
182
+ this.ports = eventInitDict?.ports ?? [];
183
+ }
184
+
185
+ get [Symbol.toStringTag]() { return 'MessageEvent'; }
186
+ }
187
+
188
+ // -- ErrorEvent --
189
+ // Reference: refs/happy-dom/packages/happy-dom/src/event/events/ErrorEvent.ts
190
+ // Copyright (c) David Ortner (capricorn86). MIT license.
191
+
192
+ interface ErrorEventInit extends EventInit {
193
+ message?: string;
194
+ filename?: string;
195
+ lineno?: number;
196
+ colno?: number;
197
+ error?: unknown;
198
+ }
199
+
200
+ export class ErrorEvent extends Event {
201
+ readonly message: string;
202
+ readonly filename: string;
203
+ readonly lineno: number;
204
+ readonly colno: number;
205
+ readonly error: unknown;
206
+
207
+ constructor(type: string, eventInitDict?: ErrorEventInit) {
208
+ super(type, eventInitDict);
209
+ this.message = eventInitDict?.message ?? '';
210
+ this.filename = eventInitDict?.filename ?? '';
211
+ this.lineno = eventInitDict?.lineno ?? 0;
212
+ this.colno = eventInitDict?.colno ?? 0;
213
+ this.error = eventInitDict?.error ?? null;
214
+ }
215
+
216
+ get [Symbol.toStringTag]() { return 'ErrorEvent'; }
217
+ }
218
+
219
+ // -- CloseEvent --
220
+ // Reference: refs/happy-dom/packages/happy-dom/src/event/events/CloseEvent.ts
221
+ // Copyright (c) David Ortner (capricorn86). MIT license.
222
+
223
+ interface CloseEventInit extends EventInit {
224
+ code?: number;
225
+ reason?: string;
226
+ wasClean?: boolean;
227
+ }
228
+
229
+ export class CloseEvent extends Event {
230
+ readonly code: number;
231
+ readonly reason: string;
232
+ readonly wasClean: boolean;
233
+
234
+ constructor(type: string, eventInitDict?: CloseEventInit) {
235
+ super(type, eventInitDict);
236
+ this.code = eventInitDict?.code ?? 0;
237
+ this.reason = eventInitDict?.reason ?? '';
238
+ this.wasClean = eventInitDict?.wasClean ?? false;
239
+ }
240
+
241
+ get [Symbol.toStringTag]() { return 'CloseEvent'; }
242
+ }
243
+
244
+ // -- ProgressEvent --
245
+ // Reference: https://developer.mozilla.org/en-US/docs/Web/API/ProgressEvent
246
+
247
+ interface ProgressEventInit extends EventInit {
248
+ lengthComputable?: boolean;
249
+ loaded?: number;
250
+ total?: number;
251
+ }
252
+
253
+ export class ProgressEvent extends Event {
254
+ readonly lengthComputable: boolean;
255
+ readonly loaded: number;
256
+ readonly total: number;
257
+
258
+ constructor(type: string, eventInitDict?: ProgressEventInit) {
259
+ super(type, eventInitDict);
260
+ this.lengthComputable = eventInitDict?.lengthComputable ?? false;
261
+ this.loaded = eventInitDict?.loaded ?? 0;
262
+ this.total = eventInitDict?.total ?? 0;
263
+ }
264
+
265
+ get [Symbol.toStringTag]() { return 'ProgressEvent'; }
266
+ }
267
+
268
+ export class EventTarget {
269
+ private _listeners = new Map<string, ListenerEntry[]>();
270
+
271
+ get [Symbol.toStringTag]() { return 'EventTarget'; }
272
+
273
+ addEventListener(
274
+ type: string,
275
+ callback: EventListenerOrEventListenerObject | null,
276
+ options?: AddEventListenerOptions | boolean,
277
+ ): void {
278
+ if (callback === null) return;
279
+
280
+ const capture = typeof options === 'boolean' ? options : (options?.capture ?? false);
281
+ const once = typeof options === 'object' ? (options?.once ?? false) : false;
282
+ const passive = typeof options === 'object' ? (options?.passive ?? false) : false;
283
+
284
+ let list = this._listeners.get(type);
285
+ if (!list) {
286
+ list = [];
287
+ this._listeners.set(type, list);
288
+ }
289
+
290
+ for (const entry of list) {
291
+ if (entry.listener === callback && entry.capture === capture) return;
292
+ }
293
+
294
+ const entry: ListenerEntry = { listener: callback, capture, once, passive, removed: false };
295
+ list.push(entry);
296
+
297
+ if (typeof options === 'object' && options?.signal) {
298
+ options.signal.addEventListener('abort', () => {
299
+ this.removeEventListener(type, callback, { capture });
300
+ }, { once: true });
301
+ }
302
+ }
303
+
304
+ removeEventListener(
305
+ type: string,
306
+ callback: EventListenerOrEventListenerObject | null,
307
+ options?: EventListenerOptions | boolean,
308
+ ): void {
309
+ if (callback === null) return;
310
+
311
+ const capture = typeof options === 'boolean' ? options : (options?.capture ?? false);
312
+ const list = this._listeners.get(type);
313
+ if (!list) return;
314
+
315
+ const idx = list.findIndex(e => e.listener === callback && e.capture === capture);
316
+ if (idx !== -1) {
317
+ list[idx].removed = true;
318
+ list.splice(idx, 1);
319
+ if (list.length === 0) this._listeners.delete(type);
320
+ }
321
+ }
322
+
323
+ dispatchEvent(event: Event): boolean {
324
+ // Prevent re-dispatching an event that is currently being dispatched
325
+ if ((event as any)[kDispatching]) {
326
+ throw new DOMException('The event is already being dispatched.', 'InvalidStateError');
327
+ }
328
+
329
+ (event as any)[kDispatching] = true;
330
+ (event as any)[kTarget] = this;
331
+ (event as any)[kCurrentTarget] = this;
332
+ (event as any)[kEventPhase] = Event.AT_TARGET;
333
+
334
+ const list = this._listeners.get(event.type);
335
+ if (list) {
336
+ const entries = [...list];
337
+ for (const entry of entries) {
338
+ if (entry.removed) continue;
339
+ if ((event as any)[kImmediateStop]) break;
340
+ if ((event as any)[kStop]) break;
341
+
342
+ if (entry.once) {
343
+ this.removeEventListener(event.type, entry.listener, { capture: entry.capture });
344
+ }
345
+
346
+ try {
347
+ if (entry.passive) (event as any)[kInPassiveListener] = true;
348
+ if (typeof entry.listener === 'function') {
349
+ entry.listener.call(this, event);
350
+ } else if (typeof entry.listener.handleEvent === 'function') {
351
+ entry.listener.handleEvent.call(entry.listener, event);
352
+ }
353
+ } catch (err) {
354
+ console.error(err);
355
+ } finally {
356
+ (event as any)[kInPassiveListener] = false;
357
+ }
358
+ }
359
+ }
360
+
361
+ (event as any)[kEventPhase] = Event.NONE;
362
+ (event as any)[kCurrentTarget] = null;
363
+ (event as any)[kDispatching] = false;
364
+
365
+ return !event.defaultPrevented;
366
+ }
367
+ }
368
+
369
+ // Define phase constants as non-writable, non-configurable on Event.prototype and Event constructor
370
+ for (const [name, value] of [['NONE', 0], ['CAPTURING_PHASE', 1], ['AT_TARGET', 2], ['BUBBLING_PHASE', 3]] as const) {
371
+ Object.defineProperty(Event.prototype, name, { value, writable: false, enumerable: true, configurable: false });
372
+ Object.defineProperty(Event, name, { value, writable: false, enumerable: true, configurable: false });
373
+ }
374
+
375
+ // -- UIEvent --
376
+ // Reference: https://developer.mozilla.org/en-US/docs/Web/API/UIEvent
377
+
378
+ interface UIEventInit extends EventInit {
379
+ detail?: number;
380
+ view?: null;
381
+ }
382
+
383
+ export class UIEvent extends Event {
384
+ readonly detail: number;
385
+ readonly view: null;
386
+
387
+ constructor(type: string, eventInitDict?: UIEventInit) {
388
+ super(type, eventInitDict);
389
+ this.detail = eventInitDict?.detail ?? 0;
390
+ this.view = null;
391
+ }
392
+
393
+ get [Symbol.toStringTag]() { return 'UIEvent'; }
394
+ }
395
+
396
+ // -- MouseEvent --
397
+ // Reference: https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent
398
+
399
+ interface MouseEventInit extends UIEventInit {
400
+ altKey?: boolean;
401
+ button?: number;
402
+ buttons?: number;
403
+ clientX?: number;
404
+ clientY?: number;
405
+ ctrlKey?: boolean;
406
+ metaKey?: boolean;
407
+ movementX?: number;
408
+ movementY?: number;
409
+ offsetX?: number;
410
+ offsetY?: number;
411
+ screenX?: number;
412
+ screenY?: number;
413
+ shiftKey?: boolean;
414
+ relatedTarget?: EventTarget | null;
415
+ }
416
+
417
+ export class MouseEvent extends UIEvent {
418
+ readonly altKey: boolean;
419
+ readonly button: number;
420
+ readonly buttons: number;
421
+ readonly clientX: number;
422
+ readonly clientY: number;
423
+ readonly ctrlKey: boolean;
424
+ readonly metaKey: boolean;
425
+ readonly movementX: number;
426
+ readonly movementY: number;
427
+ readonly offsetX: number;
428
+ readonly offsetY: number;
429
+ readonly screenX: number;
430
+ readonly screenY: number;
431
+ readonly shiftKey: boolean;
432
+ readonly relatedTarget: EventTarget | null;
433
+
434
+ // Legacy aliases
435
+ get pageX(): number { return this.clientX; }
436
+ get pageY(): number { return this.clientY; }
437
+ get x(): number { return this.clientX; }
438
+ get y(): number { return this.clientY; }
439
+
440
+ constructor(type: string, eventInitDict?: MouseEventInit) {
441
+ super(type, eventInitDict);
442
+ this.altKey = eventInitDict?.altKey ?? false;
443
+ this.button = eventInitDict?.button ?? 0;
444
+ this.buttons = eventInitDict?.buttons ?? 0;
445
+ this.clientX = eventInitDict?.clientX ?? 0;
446
+ this.clientY = eventInitDict?.clientY ?? 0;
447
+ this.ctrlKey = eventInitDict?.ctrlKey ?? false;
448
+ this.metaKey = eventInitDict?.metaKey ?? false;
449
+ this.movementX = eventInitDict?.movementX ?? 0;
450
+ this.movementY = eventInitDict?.movementY ?? 0;
451
+ this.offsetX = eventInitDict?.offsetX ?? 0;
452
+ this.offsetY = eventInitDict?.offsetY ?? 0;
453
+ this.screenX = eventInitDict?.screenX ?? 0;
454
+ this.screenY = eventInitDict?.screenY ?? 0;
455
+ this.shiftKey = eventInitDict?.shiftKey ?? false;
456
+ this.relatedTarget = eventInitDict?.relatedTarget ?? null;
457
+ }
458
+
459
+ getModifierState(key: string): boolean {
460
+ switch (key) {
461
+ case 'Alt': return this.altKey;
462
+ case 'Control': return this.ctrlKey;
463
+ case 'Meta': return this.metaKey;
464
+ case 'Shift': return this.shiftKey;
465
+ default: return false;
466
+ }
467
+ }
468
+
469
+ get [Symbol.toStringTag]() { return 'MouseEvent'; }
470
+ }
471
+
472
+ // -- PointerEvent --
473
+ // Reference: https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent
474
+
475
+ interface PointerEventInit extends MouseEventInit {
476
+ pointerId?: number;
477
+ width?: number;
478
+ height?: number;
479
+ pressure?: number;
480
+ tangentialPressure?: number;
481
+ tiltX?: number;
482
+ tiltY?: number;
483
+ twist?: number;
484
+ altitudeAngle?: number;
485
+ azimuthAngle?: number;
486
+ pointerType?: string;
487
+ isPrimary?: boolean;
488
+ }
489
+
490
+ export class PointerEvent extends MouseEvent {
491
+ readonly pointerId: number;
492
+ readonly width: number;
493
+ readonly height: number;
494
+ readonly pressure: number;
495
+ readonly tangentialPressure: number;
496
+ readonly tiltX: number;
497
+ readonly tiltY: number;
498
+ readonly twist: number;
499
+ readonly altitudeAngle: number;
500
+ readonly azimuthAngle: number;
501
+ readonly pointerType: string;
502
+ readonly isPrimary: boolean;
503
+
504
+ constructor(type: string, eventInitDict?: PointerEventInit) {
505
+ super(type, eventInitDict);
506
+ this.pointerId = eventInitDict?.pointerId ?? 0;
507
+ this.width = eventInitDict?.width ?? 1;
508
+ this.height = eventInitDict?.height ?? 1;
509
+ this.pressure = eventInitDict?.pressure ?? 0;
510
+ this.tangentialPressure = eventInitDict?.tangentialPressure ?? 0;
511
+ this.tiltX = eventInitDict?.tiltX ?? 0;
512
+ this.tiltY = eventInitDict?.tiltY ?? 0;
513
+ this.twist = eventInitDict?.twist ?? 0;
514
+ this.altitudeAngle = eventInitDict?.altitudeAngle ?? Math.PI / 2;
515
+ this.azimuthAngle = eventInitDict?.azimuthAngle ?? 0;
516
+ this.pointerType = eventInitDict?.pointerType ?? '';
517
+ this.isPrimary = eventInitDict?.isPrimary ?? false;
518
+ }
519
+
520
+ getCoalescedEvents(): PointerEvent[] { return []; }
521
+ getPredictedEvents(): PointerEvent[] { return []; }
522
+
523
+ get [Symbol.toStringTag]() { return 'PointerEvent'; }
524
+ }
525
+
526
+ // -- KeyboardEvent --
527
+ // Reference: https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent
528
+
529
+ interface KeyboardEventInit extends UIEventInit {
530
+ altKey?: boolean;
531
+ code?: string;
532
+ ctrlKey?: boolean;
533
+ isComposing?: boolean;
534
+ key?: string;
535
+ location?: number;
536
+ metaKey?: boolean;
537
+ repeat?: boolean;
538
+ shiftKey?: boolean;
539
+ keyCode?: number;
540
+ which?: number;
541
+ }
542
+
543
+ export class KeyboardEvent extends UIEvent {
544
+ readonly altKey: boolean;
545
+ readonly code: string;
546
+ readonly ctrlKey: boolean;
547
+ readonly isComposing: boolean;
548
+ readonly key: string;
549
+ readonly location: number;
550
+ readonly metaKey: boolean;
551
+ readonly repeat: boolean;
552
+ readonly shiftKey: boolean;
553
+ readonly keyCode: number;
554
+ readonly which: number;
555
+
556
+ static readonly DOM_KEY_LOCATION_STANDARD = 0;
557
+ static readonly DOM_KEY_LOCATION_LEFT = 1;
558
+ static readonly DOM_KEY_LOCATION_RIGHT = 2;
559
+ static readonly DOM_KEY_LOCATION_NUMPAD = 3;
560
+
561
+ constructor(type: string, eventInitDict?: KeyboardEventInit) {
562
+ super(type, eventInitDict);
563
+ this.altKey = eventInitDict?.altKey ?? false;
564
+ this.code = eventInitDict?.code ?? '';
565
+ this.ctrlKey = eventInitDict?.ctrlKey ?? false;
566
+ this.isComposing = eventInitDict?.isComposing ?? false;
567
+ this.key = eventInitDict?.key ?? '';
568
+ this.location = eventInitDict?.location ?? 0;
569
+ this.metaKey = eventInitDict?.metaKey ?? false;
570
+ this.repeat = eventInitDict?.repeat ?? false;
571
+ this.shiftKey = eventInitDict?.shiftKey ?? false;
572
+ this.keyCode = eventInitDict?.keyCode ?? 0;
573
+ this.which = eventInitDict?.which ?? 0;
574
+ }
575
+
576
+ getModifierState(key: string): boolean {
577
+ switch (key) {
578
+ case 'Alt': return this.altKey;
579
+ case 'Control': return this.ctrlKey;
580
+ case 'Meta': return this.metaKey;
581
+ case 'Shift': return this.shiftKey;
582
+ default: return false;
583
+ }
584
+ }
585
+
586
+ get [Symbol.toStringTag]() { return 'KeyboardEvent'; }
587
+ }
588
+
589
+ // -- WheelEvent --
590
+ // Reference: https://developer.mozilla.org/en-US/docs/Web/API/WheelEvent
591
+
592
+ interface WheelEventInit extends MouseEventInit {
593
+ deltaX?: number;
594
+ deltaY?: number;
595
+ deltaZ?: number;
596
+ deltaMode?: number;
597
+ }
598
+
599
+ export class WheelEvent extends MouseEvent {
600
+ readonly deltaX: number;
601
+ readonly deltaY: number;
602
+ readonly deltaZ: number;
603
+ readonly deltaMode: number;
604
+
605
+ static readonly DOM_DELTA_PIXEL = 0;
606
+ static readonly DOM_DELTA_LINE = 1;
607
+ static readonly DOM_DELTA_PAGE = 2;
608
+
609
+ constructor(type: string, eventInitDict?: WheelEventInit) {
610
+ super(type, eventInitDict);
611
+ this.deltaX = eventInitDict?.deltaX ?? 0;
612
+ this.deltaY = eventInitDict?.deltaY ?? 0;
613
+ this.deltaZ = eventInitDict?.deltaZ ?? 0;
614
+ this.deltaMode = eventInitDict?.deltaMode ?? 0;
615
+ }
616
+
617
+ get [Symbol.toStringTag]() { return 'WheelEvent'; }
618
+ }
619
+
620
+ // -- FocusEvent --
621
+ // Reference: https://developer.mozilla.org/en-US/docs/Web/API/FocusEvent
622
+
623
+ interface FocusEventInit extends UIEventInit {
624
+ relatedTarget?: EventTarget | null;
625
+ }
626
+
627
+ export class FocusEvent extends UIEvent {
628
+ readonly relatedTarget: EventTarget | null;
629
+
630
+ constructor(type: string, eventInitDict?: FocusEventInit) {
631
+ super(type, eventInitDict);
632
+ this.relatedTarget = eventInitDict?.relatedTarget ?? null;
633
+ }
634
+
635
+ get [Symbol.toStringTag]() { return 'FocusEvent'; }
636
+ }
637
+
638
+ export default {
639
+ Event, CustomEvent, EventTarget,
640
+ MessageEvent, ErrorEvent, CloseEvent, ProgressEvent,
641
+ UIEvent, MouseEvent, PointerEvent, KeyboardEvent, WheelEvent, FocusEvent,
642
+ };
package/src/test.mts CHANGED
@@ -4,5 +4,6 @@ import { run } from '@gjsify/unit';
4
4
  import { ErrorHandlerTest } from './error-handler.spec';
5
5
  import { EventTargetTest } from './event-target.spec';
6
6
  import { EventTest } from './event.spec';
7
+ import { UIEventsTest } from './ui-events.spec';
7
8
 
8
- run({ErrorHandlerTest, EventTargetTest, EventTest});
9
+ run({ErrorHandlerTest, EventTargetTest, EventTest, UIEventsTest});