Skip to content

Commit 2f10348

Browse files
rubennortefacebook-github-bot
authored andcommitted
[skip ci] 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). Reviewed By: yungsters Differential Revision: D67738145
1 parent aa57608 commit 2f10348

File tree

6 files changed

+2009
-0
lines changed

6 files changed

+2009
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
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+
const typeOfOptions = typeof options;
91+
92+
if (
93+
options != null &&
94+
typeOfOptions !== 'object' &&
95+
typeOfOptions !== 'function'
96+
) {
97+
throw new TypeError(
98+
"Failed to construct 'Event': The provided value is not of type 'EventInit'.",
99+
);
100+
}
101+
102+
this.#type = String(type);
103+
this.#bubbles = Boolean(options?.bubbles);
104+
this.#cancelable = Boolean(options?.cancelable);
105+
this.#composed = Boolean(options?.composed);
106+
}
107+
108+
get bubbles(): boolean {
109+
return this.#bubbles;
110+
}
111+
112+
get cancelable(): boolean {
113+
return this.#cancelable;
114+
}
115+
116+
get composed(): boolean {
117+
return this.#composed;
118+
}
119+
120+
get currentTarget(): EventTarget | null {
121+
return getCurrentTarget(this);
122+
}
123+
124+
get defaultPrevented(): boolean {
125+
return this.#defaultPrevented;
126+
}
127+
128+
get eventPhase(): EventPhase {
129+
return getEventPhase(this);
130+
}
131+
132+
get isTrusted(): boolean {
133+
return getIsTrusted(this);
134+
}
135+
136+
get target(): EventTarget | null {
137+
return getTarget(this);
138+
}
139+
140+
get timeStamp(): number {
141+
return this.#timeStamp;
142+
}
143+
144+
get type(): string {
145+
return this.#type;
146+
}
147+
148+
composedPath(): $ReadOnlyArray<EventTarget> {
149+
return getComposedPath(this).slice();
150+
}
151+
152+
preventDefault(): void {
153+
if (!this.#cancelable) {
154+
return;
155+
}
156+
157+
if (getInPassiveListenerFlag(this)) {
158+
console.error(
159+
new Error(
160+
'Unable to preventDefault inside passive event listener invocation.',
161+
),
162+
);
163+
return;
164+
}
165+
166+
this.#defaultPrevented = true;
167+
}
168+
169+
stopImmediatePropagation(): void {
170+
setStopPropagationFlag(this, true);
171+
setStopImmediatePropagationFlag(this, true);
172+
}
173+
174+
stopPropagation(): void {
175+
setStopPropagationFlag(this, true);
176+
}
177+
}
178+
179+
export type EventPhase =
180+
| (typeof Event)['NONE']
181+
| (typeof Event)['CAPTURING_PHASE']
182+
| (typeof Event)['AT_TARGET']
183+
| (typeof Event)['BUBBLING_PHASE'];

0 commit comments

Comments
 (0)