Skip to content

Commit f187f7b

Browse files
rubennortefacebook-github-bot
authored andcommitted
Implement Event and EventTarget (facebook#48429)
Summary: Changelog: [internal] This implements a (mostly) spec-compliant version of the [`Event`](https://dom.spec.whatwg.org/#interface-event) and [`EventTarget`](https://dom.spec.whatwg.org/#interface-eventtarget) Web interfaces. It does not implement legacy methods in either of the interfaces, and ignores the parts of the spec that are related to Web-specific quirks (shadow roots, re-mapping of animation events with webkit prefixes, etc.). IMPORTANT: This only creates the interfaces and does not expose them externally yet (no `Event` or `EventTarget` in the global scope). Differential Revision: D67738145
1 parent 6d85c7c commit f187f7b

File tree

6 files changed

+1816
-0
lines changed

6 files changed

+1816
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
/**
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
* @flow strict
8+
* @format
9+
*/
10+
11+
/**
12+
* This module implements the `Event` interface from the DOM.
13+
* See https://dom.spec.whatwg.org/#interface-event.
14+
*/
15+
16+
// flowlint unsafe-getters-setters:off
17+
18+
import type EventTarget from './EventTarget';
19+
20+
import {
21+
COMPOSED_PATH_KEY,
22+
CURRENT_TARGET_KEY,
23+
EVENT_PHASE_KEY,
24+
IN_PASSIVE_LISTENER_FLAG_KEY,
25+
IS_TRUSTED_KEY,
26+
STOP_IMMEDIATE_PROPAGATION_FLAG_KEY,
27+
STOP_PROPAGATION_FLAG_KEY,
28+
TARGET_KEY,
29+
getComposedPath,
30+
getCurrentTarget,
31+
getEventPhase,
32+
getInPassiveListenerFlag,
33+
getIsTrusted,
34+
getTarget,
35+
setStopImmediatePropagationFlag,
36+
setStopPropagationFlag,
37+
} from './internals/EventInternals';
38+
39+
type EventInit = {
40+
bubbles?: boolean,
41+
cancelable?: boolean,
42+
composed?: boolean,
43+
};
44+
45+
export default class Event {
46+
static NONE: 0 = 0;
47+
static CAPTURING_PHASE: 1 = 1;
48+
static AT_TARGET: 2 = 2;
49+
static BUBBLING_PHASE: 3 = 3;
50+
51+
#bubbles: boolean;
52+
#cancelable: boolean;
53+
#composed: boolean;
54+
#type: string;
55+
56+
#defaultPrevented: boolean = false;
57+
#timeStamp: number = performance.now();
58+
59+
// $FlowExpectedError[unsupported-syntax]
60+
[COMPOSED_PATH_KEY]: boolean = [];
61+
62+
// $FlowExpectedError[unsupported-syntax]
63+
[CURRENT_TARGET_KEY]: EventTarget | null = null;
64+
65+
// $FlowExpectedError[unsupported-syntax]
66+
[EVENT_PHASE_KEY]: boolean = Event.NONE;
67+
68+
// $FlowExpectedError[unsupported-syntax]
69+
[IN_PASSIVE_LISTENER_FLAG_KEY]: boolean = false;
70+
71+
// $FlowExpectedError[unsupported-syntax]
72+
[IS_TRUSTED_KEY]: boolean = false;
73+
74+
// $FlowExpectedError[unsupported-syntax]
75+
[STOP_IMMEDIATE_PROPAGATION_FLAG_KEY]: boolean = false;
76+
77+
// $FlowExpectedError[unsupported-syntax]
78+
[STOP_PROPAGATION_FLAG_KEY]: boolean = false;
79+
80+
// $FlowExpectedError[unsupported-syntax]
81+
[TARGET_KEY]: EventTarget | null = null;
82+
83+
constructor(type: string, options?: ?EventInit) {
84+
if (arguments.length < 1) {
85+
throw new TypeError(
86+
"Failed to construct 'Event': 1 argument required, but only 0 present.",
87+
);
88+
}
89+
90+
if (options != null && typeof options !== 'object') {
91+
throw new TypeError(
92+
"Failed to construct 'Event': The provided value is not of type 'EventInit'.",
93+
);
94+
}
95+
96+
this.#type = String(type);
97+
this.#bubbles = Boolean(options?.bubbles);
98+
this.#cancelable = Boolean(options?.cancelable);
99+
this.#composed = Boolean(options?.composed);
100+
}
101+
102+
get bubbles(): boolean {
103+
return this.#bubbles;
104+
}
105+
106+
get cancelable(): boolean {
107+
return this.#cancelable;
108+
}
109+
110+
get composed(): boolean {
111+
return this.#composed;
112+
}
113+
114+
get currentTarget(): EventTarget | null {
115+
return getCurrentTarget(this);
116+
}
117+
118+
get defaultPrevented(): boolean {
119+
return this.#defaultPrevented;
120+
}
121+
122+
get eventPhase(): EventPhase {
123+
return getEventPhase(this);
124+
}
125+
126+
get isTrusted(): boolean {
127+
return getIsTrusted(this);
128+
}
129+
130+
get target(): EventTarget | null {
131+
return getTarget(this);
132+
}
133+
134+
get timeStamp(): number {
135+
return this.#timeStamp;
136+
}
137+
138+
get type(): string {
139+
return this.#type;
140+
}
141+
142+
composedPath(): $ReadOnlyArray<EventTarget> {
143+
return getComposedPath(this).slice();
144+
}
145+
146+
preventDefault(): void {
147+
if (!this.#cancelable) {
148+
return;
149+
}
150+
151+
if (getInPassiveListenerFlag(this)) {
152+
console.error(
153+
new Error(
154+
'Unable to preventDefault inside passive event listener invocation.',
155+
),
156+
);
157+
return;
158+
}
159+
160+
this.#defaultPrevented = true;
161+
}
162+
163+
stopImmediatePropagation(): void {
164+
setStopPropagationFlag(this, true);
165+
setStopImmediatePropagationFlag(this, true);
166+
}
167+
168+
stopPropagation(): void {
169+
setStopPropagationFlag(this, true);
170+
}
171+
}
172+
173+
export type EventPhase =
174+
| (typeof Event)['NONE']
175+
| (typeof Event)['CAPTURING_PHASE']
176+
| (typeof Event)['AT_TARGET']
177+
| (typeof Event)['BUBBLING_PHASE'];

0 commit comments

Comments
 (0)