react-native
Version:
A framework for building native apps using React
299 lines (246 loc) • 7.5 kB
JavaScript
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict
* @format
*/
/**
* This module implements the `Event` interface from the DOM.
* See https://dom.spec.whatwg.org/#interface-event.
*/
// flowlint unsafe-getters-setters:off
import type EventTarget from './EventTarget';
import {setPlatformObject} from '../../webidl/PlatformObjects';
import {
COMPOSED_PATH_KEY,
CURRENT_TARGET_KEY,
EVENT_INIT_TIMESTAMP_KEY,
EVENT_PHASE_KEY,
IN_PASSIVE_LISTENER_FLAG_KEY,
IS_TRUSTED_KEY,
STOP_IMMEDIATE_PROPAGATION_FLAG_KEY,
STOP_PROPAGATION_FLAG_KEY,
TARGET_KEY,
getComposedPath,
getCurrentTarget,
getEventPhase,
getInPassiveListenerFlag,
getIsTrusted,
getStopPropagationFlag,
getTarget,
setStopImmediatePropagationFlag,
setStopPropagationFlag,
} from './internals/EventInternals';
export interface EventInit {
readonly bubbles?: boolean;
readonly cancelable?: boolean;
readonly composed?: boolean;
// React Native-specific. When true, the event is a "direct" event: it is
// dispatched only to the target node (single AT_TARGET phase) and never
// captures or bubbles through ancestors. A direct event must not bubble, so
// `bubbles` cannot also be true.
readonly rnIsDirect?: boolean;
}
export default class Event {
static readonly NONE: 0;
static readonly CAPTURING_PHASE: 1;
static readonly AT_TARGET: 2;
static readonly BUBBLING_PHASE: 3;
readonly NONE: 0;
readonly CAPTURING_PHASE: 1;
readonly AT_TARGET: 2;
readonly BUBBLING_PHASE: 3;
_bubbles: boolean;
_cancelable: boolean;
_composed: boolean;
_rnIsDirect: boolean;
_type: string;
_defaultPrevented: boolean = false;
_timeStamp: number;
// $FlowExpectedError[unsupported-syntax]
// $FlowFixMe[illegal-key]
[COMPOSED_PATH_KEY]: boolean = [];
// $FlowExpectedError[unsupported-syntax]
// $FlowFixMe[illegal-key]
[CURRENT_TARGET_KEY]: EventTarget | null = null;
// $FlowExpectedError[unsupported-syntax]
// $FlowFixMe[illegal-key]
[EVENT_PHASE_KEY]: boolean = Event.NONE;
// $FlowExpectedError[unsupported-syntax]
// $FlowFixMe[illegal-key]
[IN_PASSIVE_LISTENER_FLAG_KEY]: boolean = false;
// $FlowExpectedError[unsupported-syntax]
// $FlowFixMe[illegal-key]
[IS_TRUSTED_KEY]: boolean = false;
// $FlowExpectedError[unsupported-syntax]
// $FlowFixMe[illegal-key]
[STOP_IMMEDIATE_PROPAGATION_FLAG_KEY]: boolean = false;
// $FlowExpectedError[unsupported-syntax]
// $FlowFixMe[illegal-key]
[STOP_PROPAGATION_FLAG_KEY]: boolean = false;
// $FlowExpectedError[unsupported-syntax]
// $FlowFixMe[illegal-key]
[TARGET_KEY]: EventTarget | null = null;
constructor(type: string, options?: ?EventInit) {
if (arguments.length < 1) {
throw new TypeError(
"Failed to construct 'Event': 1 argument required, but only 0 present.",
);
}
const typeOfOptions = typeof options;
if (
options != null &&
typeOfOptions !== 'object' &&
typeOfOptions !== 'function'
) {
throw new TypeError(
"Failed to construct 'Event': The provided value is not of type 'EventInit'.",
);
}
this._type = String(type);
this._bubbles = Boolean(options?.bubbles);
this._cancelable = Boolean(options?.cancelable);
this._composed = Boolean(options?.composed);
this._rnIsDirect = Boolean(options?.rnIsDirect);
// A direct event is dispatched only to its target and never propagates, so
// it cannot bubble. Reject the contradictory combination at construction
// time.
if (this._rnIsDirect && this._bubbles) {
throw new TypeError(
"Failed to construct 'Event': 'rnIsDirect' cannot be true when 'bubbles' is also true.",
);
}
// For internal construction of events using a custom timestamp (instead of
// event object creation), for use cases like dispatching events from the
// host platform using the original timestamps.
// $FlowExpectedError[prop-missing]
const initTimestamp: number | void = options?.[EVENT_INIT_TIMESTAMP_KEY];
this._timeStamp =
initTimestamp !== undefined ? initTimestamp : performance.now();
}
get bubbles(): boolean {
return this._bubbles;
}
get cancelable(): boolean {
return this._cancelable;
}
/**
* Historical alias for the stop-propagation flag. Reading it returns whether
* `stopPropagation()` has been called (or `cancelBubble` has been set to
* `true`). Setting it to `true` stops propagation; setting it to `false` is a
* no-op. See https://dom.spec.whatwg.org/#dom-event-cancelbubble.
*/
get cancelBubble(): boolean {
return getStopPropagationFlag(this);
}
set cancelBubble(value: boolean) {
if (value) {
setStopPropagationFlag(this, true);
}
}
get composed(): boolean {
return this._composed;
}
/**
* React Native-specific. When true, this event is dispatched only to its
* target (single `AT_TARGET` phase) and never captures or bubbles through
* ancestors.
*/
get rnIsDirect(): boolean {
return this._rnIsDirect;
}
get currentTarget(): EventTarget | null {
return getCurrentTarget(this);
}
get defaultPrevented(): boolean {
return this._defaultPrevented;
}
get eventPhase(): EventPhase {
return getEventPhase(this);
}
get isTrusted(): boolean {
return getIsTrusted(this);
}
get target(): EventTarget | null {
return getTarget(this);
}
get timeStamp(): number {
return this._timeStamp;
}
get type(): string {
return this._type;
}
composedPath(): ReadonlyArray<EventTarget> {
return getComposedPath(this).slice();
}
preventDefault(): void {
if (!this._cancelable) {
return;
}
if (getInPassiveListenerFlag(this)) {
console.error(
new Error(
'Unable to preventDefault inside passive event listener invocation.',
),
);
return;
}
this._defaultPrevented = true;
}
stopImmediatePropagation(): void {
setStopPropagationFlag(this, true);
setStopImmediatePropagationFlag(this, true);
}
stopPropagation(): void {
setStopPropagationFlag(this, true);
}
}
// $FlowExpectedError[cannot-write]
Object.defineProperty(Event, 'NONE', {
enumerable: true,
value: 0,
});
// $FlowExpectedError[cannot-write]
Object.defineProperty(Event.prototype, 'NONE', {
enumerable: true,
value: 0,
});
// $FlowExpectedError[cannot-write]
Object.defineProperty(Event, 'CAPTURING_PHASE', {
enumerable: true,
value: 1,
});
// $FlowExpectedError[cannot-write]
Object.defineProperty(Event.prototype, 'CAPTURING_PHASE', {
enumerable: true,
value: 1,
});
// $FlowExpectedError[cannot-write]
Object.defineProperty(Event, 'AT_TARGET', {
enumerable: true,
value: 2,
});
// $FlowExpectedError[cannot-write]
Object.defineProperty(Event.prototype, 'AT_TARGET', {
enumerable: true,
value: 2,
});
// $FlowExpectedError[cannot-write]
Object.defineProperty(Event, 'BUBBLING_PHASE', {
enumerable: true,
value: 3,
});
// $FlowExpectedError[cannot-write]
Object.defineProperty(Event.prototype, 'BUBBLING_PHASE', {
enumerable: true,
value: 3,
});
export type EventPhase =
| (typeof Event)['NONE']
| (typeof Event)['CAPTURING_PHASE']
| (typeof Event)['AT_TARGET']
| (typeof Event)['BUBBLING_PHASE'];
setPlatformObject(Event);