+ `); + }); + + it('renders styles', () => { + const style = { + display: 'flex', + flex: 1, + backgroundColor: 'white', + marginInlineStart: 10, + userSelect: 'none', + verticalAlign: 'middle', + }; + + const instance = ReactTestRenderer.create(); + + expect(instance.toJSON()).toMatchInlineSnapshot(` + + `); + }); +}); diff --git a/Libraries/Components/TextInput/__tests__/__snapshots__/TextInput-test.js.snap b/Libraries/Components/TextInput/__tests__/__snapshots__/TextInput-test.js.snap index 874fae2b158a92..ac93dc2e644c7c 100644 --- a/Libraries/Components/TextInput/__tests__/__snapshots__/TextInput-test.js.snap +++ b/Libraries/Components/TextInput/__tests__/__snapshots__/TextInput-test.js.snap @@ -4,7 +4,6 @@ exports[`TextInput tests should render as expected: should deep render when mock @@ -33,7 +33,6 @@ exports[`TextInput tests should render as expected: should deep render when not `; -exports[`TextInput tests should render as expected: should shallow render as when mocked 1`] = ``; +exports[`TextInput tests should render as expected: should shallow render as when mocked 1`] = ``; -exports[`TextInput tests should render as expected: should shallow render as when not mocked 1`] = ``; +exports[`TextInput tests should render as expected: should shallow render as when not mocked 1`] = ``; diff --git a/Libraries/Components/ToastAndroid/NativeToastAndroid.js b/Libraries/Components/ToastAndroid/NativeToastAndroid.js index b35757b25d43b4..3a13f89bd45085 100644 --- a/Libraries/Components/ToastAndroid/NativeToastAndroid.js +++ b/Libraries/Components/ToastAndroid/NativeToastAndroid.js @@ -9,6 +9,7 @@ */ import type {TurboModule} from '../../TurboModule/RCTExport'; + import * as TurboModuleRegistry from '../../TurboModule/TurboModuleRegistry'; export interface Spec extends TurboModule { diff --git a/Libraries/Components/ToastAndroid/ToastAndroid.d.ts b/Libraries/Components/ToastAndroid/ToastAndroid.d.ts new file mode 100644 index 00000000000000..9e0854380b6462 --- /dev/null +++ b/Libraries/Components/ToastAndroid/ToastAndroid.d.ts @@ -0,0 +1,47 @@ +/** + * 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. + * + * @format + */ + +/** + * This exposes the native ToastAndroid module as a JS module. This has a function 'show' + * which takes the following parameters: + * + * 1. String message: A string with the text to toast + * 2. int duration: The duration of the toast. May be ToastAndroid.SHORT or ToastAndroid.LONG + * + * There is also a function `showWithGravity` to specify the layout gravity. May be + * ToastAndroid.TOP, ToastAndroid.BOTTOM, ToastAndroid.CENTER + */ +export interface ToastAndroidStatic { + /** + * String message: A string with the text to toast + * int duration: The duration of the toast. + * May be ToastAndroid.SHORT or ToastAndroid.LONG + */ + show(message: string, duration: number): void; + /** `gravity` may be ToastAndroid.TOP, ToastAndroid.BOTTOM, ToastAndroid.CENTER */ + showWithGravity(message: string, duration: number, gravity: number): void; + + showWithGravityAndOffset( + message: string, + duration: number, + gravity: number, + xOffset: number, + yOffset: number, + ): void; + // Toast duration constants + SHORT: number; + LONG: number; + // Toast gravity constants + TOP: number; + BOTTOM: number; + CENTER: number; +} + +export const ToastAndroid: ToastAndroidStatic; +export type ToastAndroid = ToastAndroidStatic; diff --git a/Libraries/Components/Touchable/PooledClass.js b/Libraries/Components/Touchable/PooledClass.js index 139f1657ea48d3..67847c1ba0c095 100644 --- a/Libraries/Components/Touchable/PooledClass.js +++ b/Libraries/Components/Touchable/PooledClass.js @@ -18,7 +18,9 @@ import invariant from 'invariant'; * the Class itself, not an instance. If any others are needed, simply add them * here, or in their own files. */ -const oneArgumentPooler = function (copyFieldsFrom) { +/* $FlowFixMe[missing-this-annot] The 'this' type annotation(s) required by + * Flow's LTI update could not be added via codemod */ +const oneArgumentPooler = function (copyFieldsFrom: any) { const Klass = this; // eslint-disable-line consistent-this if (Klass.instancePool.length) { const instance = Klass.instancePool.pop(); @@ -29,7 +31,9 @@ const oneArgumentPooler = function (copyFieldsFrom) { } }; -const twoArgumentPooler = function (a1, a2) { +/* $FlowFixMe[missing-this-annot] The 'this' type annotation(s) required by + * Flow's LTI update could not be added via codemod */ +const twoArgumentPooler = function (a1: any, a2: any) { const Klass = this; // eslint-disable-line consistent-this if (Klass.instancePool.length) { const instance = Klass.instancePool.pop(); @@ -40,7 +44,9 @@ const twoArgumentPooler = function (a1, a2) { } }; -const threeArgumentPooler = function (a1, a2, a3) { +/* $FlowFixMe[missing-this-annot] The 'this' type annotation(s) required by + * Flow's LTI update could not be added via codemod */ +const threeArgumentPooler = function (a1: any, a2: any, a3: any) { const Klass = this; // eslint-disable-line consistent-this if (Klass.instancePool.length) { const instance = Klass.instancePool.pop(); @@ -51,7 +57,9 @@ const threeArgumentPooler = function (a1, a2, a3) { } }; -const fourArgumentPooler = function (a1, a2, a3, a4) { +/* $FlowFixMe[missing-this-annot] The 'this' type annotation(s) required by + * Flow's LTI update could not be added via codemod */ +const fourArgumentPooler = function (a1: any, a2: any, a3: any, a4: any) { const Klass = this; // eslint-disable-line consistent-this if (Klass.instancePool.length) { const instance = Klass.instancePool.pop(); @@ -62,6 +70,10 @@ const fourArgumentPooler = function (a1, a2, a3, a4) { } }; +/* $FlowFixMe[missing-local-annot] The type annotation(s) required by Flow's + * LTI update could not be added via codemod */ +/* $FlowFixMe[missing-this-annot] The 'this' type annotation(s) required by + * Flow's LTI update could not be added via codemod */ const standardReleaser = function (instance) { const Klass = this; // eslint-disable-line consistent-this invariant( diff --git a/Libraries/Components/Touchable/Touchable.d.ts b/Libraries/Components/Touchable/Touchable.d.ts new file mode 100644 index 00000000000000..6915dd27927d62 --- /dev/null +++ b/Libraries/Components/Touchable/Touchable.d.ts @@ -0,0 +1,90 @@ +/** + * 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. + * + * @format + */ + +import type * as React from 'react'; +import {Insets} from '../../../types/public/Insets'; +import {GestureResponderEvent} from '../../Types/CoreEventTypes'; + +/** + * //FIXME: need to find documentation on which component is a TTouchable and can implement that interface + * @see React.DOMAtributes + */ +export interface Touchable { + onTouchStart?: ((event: GestureResponderEvent) => void) | undefined; + onTouchMove?: ((event: GestureResponderEvent) => void) | undefined; + onTouchEnd?: ((event: GestureResponderEvent) => void) | undefined; + onTouchCancel?: ((event: GestureResponderEvent) => void) | undefined; + onTouchEndCapture?: ((event: GestureResponderEvent) => void) | undefined; +} + +export const Touchable: { + TOUCH_TARGET_DEBUG: boolean; + renderDebugView: (config: { + color: string | number; + hitSlop?: Insets | undefined; + }) => React.ReactElement | null; +}; + +/** + * @see https://github.com/facebook/react-native/blob/0.34-stable\Libraries\Components\Touchable\Touchable.js + */ +interface TouchableMixin { + /** + * Invoked when the item should be highlighted. Mixers should implement this + * to visually distinguish the `VisualRect` so that the user knows that + * releasing a touch will result in a "selection" (analog to click). + */ + touchableHandleActivePressIn(e: GestureResponderEvent): void; + + /** + * Invoked when the item is "active" (in that it is still eligible to become + * a "select") but the touch has left the `PressRect`. Usually the mixer will + * want to unhighlight the `VisualRect`. If the user (while pressing) moves + * back into the `PressRect` `touchableHandleActivePressIn` will be invoked + * again and the mixer should probably highlight the `VisualRect` again. This + * event will not fire on an `touchEnd/mouseUp` event, only move events while + * the user is depressing the mouse/touch. + */ + touchableHandleActivePressOut(e: GestureResponderEvent): void; + + /** + * Invoked when the item is "selected" - meaning the interaction ended by + * letting up while the item was either in the state + * `RESPONDER_ACTIVE_PRESS_IN` or `RESPONDER_INACTIVE_PRESS_IN`. + */ + touchableHandlePress(e: GestureResponderEvent): void; + + /** + * Invoked when the item is long pressed - meaning the interaction ended by + * letting up while the item was in `RESPONDER_ACTIVE_LONG_PRESS_IN`. If + * `touchableHandleLongPress` is *not* provided, `touchableHandlePress` will + * be called as it normally is. If `touchableHandleLongPress` is provided, by + * default any `touchableHandlePress` callback will not be invoked. To + * override this default behavior, override `touchableLongPressCancelsPress` + * to return false. As a result, `touchableHandlePress` will be called when + * lifting up, even if `touchableHandleLongPress` has also been called. + */ + touchableHandleLongPress(e: GestureResponderEvent): void; + + /** + * Returns the amount to extend the `HitRect` into the `PressRect`. Positive + * numbers mean the size expands outwards. + */ + touchableGetPressRectOffset(): Insets; + + /** + * Returns the number of millis to wait before triggering a highlight. + */ + touchableGetHighlightDelayMS(): number; + + // These methods are undocumented but still being used by TouchableMixin internals + touchableGetLongPressDelayMS(): number; + touchableGetPressOutDelayMS(): number; + touchableGetHitSlop(): Insets; +} diff --git a/Libraries/Components/Touchable/Touchable.flow.js b/Libraries/Components/Touchable/Touchable.flow.js new file mode 100644 index 00000000000000..b93087cbec5d0c --- /dev/null +++ b/Libraries/Components/Touchable/Touchable.flow.js @@ -0,0 +1,284 @@ +/** + * 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 + * @format + */ + +import type {EdgeInsetsProp} from '../../StyleSheet/EdgeInsetsPropType'; +import type {ColorValue} from '../../StyleSheet/StyleSheet'; +import type {PressEvent} from '../../Types/CoreEventTypes'; + +import * as React from 'react'; + +/** + * `Touchable`: Taps done right. + * + * You hook your `ResponderEventPlugin` events into `Touchable`. `Touchable` + * will measure time/geometry and tells you when to give feedback to the user. + * + * ====================== Touchable Tutorial =============================== + * The `Touchable` mixin helps you handle the "press" interaction. It analyzes + * the geometry of elements, and observes when another responder (scroll view + * etc) has stolen the touch lock. It notifies your component when it should + * give feedback to the user. (bouncing/highlighting/unhighlighting). + * + * - When a touch was activated (typically you highlight) + * - When a touch was deactivated (typically you unhighlight) + * - When a touch was "pressed" - a touch ended while still within the geometry + * of the element, and no other element (like scroller) has "stolen" touch + * lock ("responder") (Typically you bounce the element). + * + * A good tap interaction isn't as simple as you might think. There should be a + * slight delay before showing a highlight when starting a touch. If a + * subsequent touch move exceeds the boundary of the element, it should + * unhighlight, but if that same touch is brought back within the boundary, it + * should rehighlight again. A touch can move in and out of that boundary + * several times, each time toggling highlighting, but a "press" is only + * triggered if that touch ends while within the element's boundary and no + * scroller (or anything else) has stolen the lock on touches. + * + * To create a new type of component that handles interaction using the + * `Touchable` mixin, do the following: + * + * - Initialize the `Touchable` state. + * + * getInitialState: function() { + * return merge(this.touchableGetInitialState(), yourComponentState); + * } + * + * - Choose the rendered component who's touches should start the interactive + * sequence. On that rendered node, forward all `Touchable` responder + * handlers. You can choose any rendered node you like. Choose a node whose + * hit target you'd like to instigate the interaction sequence: + * + * // In render function: + * return ( + * + * + * Even though the hit detection/interactions are triggered by the + * wrapping (typically larger) node, we usually end up implementing + * custom logic that highlights this inner one. + * + * + * ); + * + * - You may set up your own handlers for each of these events, so long as you + * also invoke the `touchable*` handlers inside of your custom handler. + * + * - Implement the handlers on your component class in order to provide + * feedback to the user. See documentation for each of these class methods + * that you should implement. + * + * touchableHandlePress: function() { + * this.performBounceAnimation(); // or whatever you want to do. + * }, + * touchableHandleActivePressIn: function() { + * this.beginHighlighting(...); // Whatever you like to convey activation + * }, + * touchableHandleActivePressOut: function() { + * this.endHighlighting(...); // Whatever you like to convey deactivation + * }, + * + * - There are more advanced methods you can implement (see documentation below): + * touchableGetHighlightDelayMS: function() { + * return 20; + * } + * // In practice, *always* use a predeclared constant (conserve memory). + * touchableGetPressRectOffset: function() { + * return {top: 20, left: 20, right: 20, bottom: 100}; + * } + */ + +/** + * Touchable states. + */ + +const States = { + NOT_RESPONDER: 'NOT_RESPONDER', // Not the responder + RESPONDER_INACTIVE_PRESS_IN: 'RESPONDER_INACTIVE_PRESS_IN', // Responder, inactive, in the `PressRect` + RESPONDER_INACTIVE_PRESS_OUT: 'RESPONDER_INACTIVE_PRESS_OUT', // Responder, inactive, out of `PressRect` + RESPONDER_ACTIVE_PRESS_IN: 'RESPONDER_ACTIVE_PRESS_IN', // Responder, active, in the `PressRect` + RESPONDER_ACTIVE_PRESS_OUT: 'RESPONDER_ACTIVE_PRESS_OUT', // Responder, active, out of `PressRect` + RESPONDER_ACTIVE_LONG_PRESS_IN: 'RESPONDER_ACTIVE_LONG_PRESS_IN', // Responder, active, in the `PressRect`, after long press threshold + RESPONDER_ACTIVE_LONG_PRESS_OUT: 'RESPONDER_ACTIVE_LONG_PRESS_OUT', // Responder, active, out of `PressRect`, after long press threshold + ERROR: 'ERROR', +}; + +type State = + | typeof States.NOT_RESPONDER + | typeof States.RESPONDER_INACTIVE_PRESS_IN + | typeof States.RESPONDER_INACTIVE_PRESS_OUT + | typeof States.RESPONDER_ACTIVE_PRESS_IN + | typeof States.RESPONDER_ACTIVE_PRESS_OUT + | typeof States.RESPONDER_ACTIVE_LONG_PRESS_IN + | typeof States.RESPONDER_ACTIVE_LONG_PRESS_OUT + | typeof States.ERROR; + +/** + * By convention, methods prefixed with underscores are meant to be @private, + * and not @protected. Mixers shouldn't access them - not even to provide them + * as callback handlers. + * + * + * ========== Geometry ========= + * `Touchable` only assumes that there exists a `HitRect` node. The `PressRect` + * is an abstract box that is extended beyond the `HitRect`. + * + * +--------------------------+ + * | | - "Start" events in `HitRect` cause `HitRect` + * | +--------------------+ | to become the responder. + * | | +--------------+ | | - `HitRect` is typically expanded around + * | | | | | | the `VisualRect`, but shifted downward. + * | | | VisualRect | | | - After pressing down, after some delay, + * | | | | | | and before letting up, the Visual React + * | | +--------------+ | | will become "active". This makes it eligible + * | | HitRect | | for being highlighted (so long as the + * | +--------------------+ | press remains in the `PressRect`). + * | PressRect o | + * +----------------------|---+ + * Out Region | + * +-----+ This gap between the `HitRect` and + * `PressRect` allows a touch to move far away + * from the original hit rect, and remain + * highlighted, and eligible for a "Press". + * Customize this via + * `touchableGetPressRectOffset()`. + * + * + * + * ======= State Machine ======= + * + * +-------------+ <---+ RESPONDER_RELEASE + * |NOT_RESPONDER| + * +-------------+ <---+ RESPONDER_TERMINATED + * + + * | RESPONDER_GRANT (HitRect) + * v + * +---------------------------+ DELAY +-------------------------+ T + DELAY +------------------------------+ + * |RESPONDER_INACTIVE_PRESS_IN|+-------->|RESPONDER_ACTIVE_PRESS_IN| +------------> |RESPONDER_ACTIVE_LONG_PRESS_IN| + * +---------------------------+ +-------------------------+ +------------------------------+ + * + ^ + ^ + ^ + * |LEAVE_ |ENTER_ |LEAVE_ |ENTER_ |LEAVE_ |ENTER_ + * |PRESS_RECT |PRESS_RECT |PRESS_RECT |PRESS_RECT |PRESS_RECT |PRESS_RECT + * | | | | | | + * v + v + v + + * +----------------------------+ DELAY +--------------------------+ +-------------------------------+ + * |RESPONDER_INACTIVE_PRESS_OUT|+------->|RESPONDER_ACTIVE_PRESS_OUT| |RESPONDER_ACTIVE_LONG_PRESS_OUT| + * +----------------------------+ +--------------------------+ +-------------------------------+ + * + * T + DELAY => LONG_PRESS_DELAY_MS + DELAY + * + * Not drawn are the side effects of each transition. The most important side + * effect is the `touchableHandlePress` abstract method invocation that occurs + * when a responder is released while in either of the "Press" states. + * + * The other important side effects are the highlight abstract method + * invocations (internal callbacks) to be implemented by the mixer. + * + * + * @lends Touchable.prototype + */ +interface TouchableMixinType { + /** + * Invoked when the item receives focus. Mixers might override this to + * visually distinguish the `VisualRect` so that the user knows that it + * currently has the focus. Most platforms only support a single element being + * focused at a time, in which case there may have been a previously focused + * element that was blurred just prior to this. This can be overridden when + * using `Touchable.Mixin.withoutDefaultFocusAndBlur`. + */ + touchableHandleFocus: (e: Event) => void; + + /** + * Invoked when the item loses focus. Mixers might override this to + * visually distinguish the `VisualRect` so that the user knows that it + * no longer has focus. Most platforms only support a single element being + * focused at a time, in which case the focus may have moved to another. + * This can be overridden when using + * `Touchable.Mixin.withoutDefaultFocusAndBlur`. + */ + touchableHandleBlur: (e: Event) => void; + + componentDidMount: () => void; + + /** + * Clear all timeouts on unmount + */ + componentWillUnmount: () => void; + + /** + * It's prefer that mixins determine state in this way, having the class + * explicitly mix the state in the one and only `getInitialState` method. + * + * @return {object} State object to be placed inside of + * `this.state.touchable`. + */ + touchableGetInitialState: () => { + touchable: { + touchState: ?State, + responderID: ?PressEvent['currentTarget'], + }, + }; + + // ==== Hooks to Gesture Responder system ==== + /** + * Must return true if embedded in a native platform scroll view. + */ + touchableHandleResponderTerminationRequest: () => any; + + /** + * Must return true to start the process of `Touchable`. + */ + touchableHandleStartShouldSetResponder: () => any; + + /** + * Return true to cancel press on long press. + */ + touchableLongPressCancelsPress: () => boolean; + + /** + * Place as callback for a DOM element's `onResponderGrant` event. + * @param {SyntheticEvent} e Synthetic event from event system. + * + */ + touchableHandleResponderGrant: (e: PressEvent) => void; + + /** + * Place as callback for a DOM element's `onResponderRelease` event. + */ + touchableHandleResponderRelease: (e: PressEvent) => void; + + /** + * Place as callback for a DOM element's `onResponderTerminate` event. + */ + touchableHandleResponderTerminate: (e: PressEvent) => void; + + /** + * Place as callback for a DOM element's `onResponderMove` event. + */ + touchableHandleResponderMove: (e: PressEvent) => void; + + withoutDefaultFocusAndBlur: {...}; +} + +export type TouchableType = { + Mixin: TouchableMixinType, + /** + * Renders a debugging overlay to visualize touch target with hitSlop (might not work on Android). + */ + renderDebugView: ({ + color: ColorValue, + hitSlop: EdgeInsetsProp, + ... + }) => null | React.Node, +}; diff --git a/Libraries/Components/Touchable/Touchable.js b/Libraries/Components/Touchable/Touchable.js index 47fdfa892d0662..2a97ad708a1652 100644 --- a/Libraries/Components/Touchable/Touchable.js +++ b/Libraries/Components/Touchable/Touchable.js @@ -8,20 +8,36 @@ * @format */ -import * as React from 'react'; -import BoundingDimensions from './BoundingDimensions'; -import Platform from '../../Utilities/Platform'; -import Position from './Position'; -import UIManager from '../../ReactNative/UIManager'; -import SoundManager from '../Sound/SoundManager'; - -import {PressabilityDebugView} from '../../Pressability/PressabilityDebug'; - -import type {ColorValue} from '../../StyleSheet/StyleSheet'; import type {EdgeInsetsProp} from '../../StyleSheet/EdgeInsetsPropType'; +import type {ColorValue} from '../../StyleSheet/StyleSheet'; import type {PressEvent} from '../../Types/CoreEventTypes'; +import type {TouchableType} from './Touchable.flow'; + +import {PressabilityDebugView} from '../../Pressability/PressabilityDebug'; +import UIManager from '../../ReactNative/UIManager'; +import Platform from '../../Utilities/Platform'; +import SoundManager from '../Sound/SoundManager'; +import BoundingDimensions from './BoundingDimensions'; +import Position from './Position'; +import * as React from 'react'; -const extractSingleTouch = nativeEvent => { +const extractSingleTouch = (nativeEvent: { + +altKey?: ?boolean, // [macOS] + +button?: ?number, // [macOS] + +changedTouches: $ReadOnlyArray, + +ctrlKey?: ?boolean, // [macOS] + +force?: number, + +identifier: number, + +locationX: number, + +locationY: number, + +metaKey?: ?boolean, // [macOS] + +pageX: number, + +pageY: number, + +shiftKey?: ?boolean, // [macOS] + +target: ?number, + +timestamp: number, + +touches: $ReadOnlyArray, +}) => { const touches = nativeEvent.touches; const changedTouches = nativeEvent.changedTouches; const hasTouches = touches && touches.length > 0; @@ -370,6 +386,8 @@ const TouchableMixin = { /** * Clear all timeouts on unmount */ + /* $FlowFixMe[missing-this-annot] The 'this' type annotation(s) required by + * Flow's LTI update could not be added via codemod */ componentWillUnmount: function () { this.touchableDelayTimeout && clearTimeout(this.touchableDelayTimeout); this.longPressDelayTimeout && clearTimeout(this.longPressDelayTimeout); @@ -383,9 +401,12 @@ const TouchableMixin = { * @return {object} State object to be placed inside of * `this.state.touchable`. */ - touchableGetInitialState: function (): $TEMPORARY$object<{| - touchable: $TEMPORARY$object<{|responderID: null, touchState: void|}>, - |}> { + touchableGetInitialState: function (): { + touchable: { + touchState: ?State, + responderID: ?PressEvent['currentTarget'], + }, + } { return { touchable: {touchState: undefined, responderID: null}, }; @@ -395,6 +416,8 @@ const TouchableMixin = { /** * Must return true if embedded in a native platform scroll view. */ + /* $FlowFixMe[missing-this-annot] The 'this' type annotation(s) required by + * Flow's LTI update could not be added via codemod */ touchableHandleResponderTerminationRequest: function (): any { return !this.props.rejectResponderTermination; }, @@ -402,6 +425,8 @@ const TouchableMixin = { /** * Must return true to start the process of `Touchable`. */ + /* $FlowFixMe[missing-this-annot] The 'this' type annotation(s) required by + * Flow's LTI update could not be added via codemod */ touchableHandleStartShouldSetResponder: function (): any { return !this.props.disabled; }, @@ -418,6 +443,8 @@ const TouchableMixin = { * @param {SyntheticEvent} e Synthetic event from event system. * */ + /* $FlowFixMe[missing-this-annot] The 'this' type annotation(s) required by + * Flow's LTI update could not be added via codemod */ touchableHandleResponderGrant: function (e: PressEvent) { const dispatchID = e.currentTarget; // Since e is used in a callback invoked on another event loop @@ -459,6 +486,8 @@ const TouchableMixin = { /** * Place as callback for a DOM element's `onResponderRelease` event. */ + /* $FlowFixMe[missing-this-annot] The 'this' type annotation(s) required by + * Flow's LTI update could not be added via codemod */ touchableHandleResponderRelease: function (e: PressEvent) { this.pressInLocation = null; this._receiveSignal(Signals.RESPONDER_RELEASE, e); @@ -467,6 +496,8 @@ const TouchableMixin = { /** * Place as callback for a DOM element's `onResponderTerminate` event. */ + /* $FlowFixMe[missing-this-annot] The 'this' type annotation(s) required by + * Flow's LTI update could not be added via codemod */ touchableHandleResponderTerminate: function (e: PressEvent) { this.pressInLocation = null; this._receiveSignal(Signals.RESPONDER_TERMINATED, e); @@ -475,6 +506,8 @@ const TouchableMixin = { /** * Place as callback for a DOM element's `onResponderMove` event. */ + /* $FlowFixMe[missing-this-annot] The 'this' type annotation(s) required by + * Flow's LTI update could not be added via codemod */ touchableHandleResponderMove: function (e: PressEvent) { // Measurement may not have returned yet. if (!this.state.touchable.positionOnActivate) { @@ -560,6 +593,8 @@ const TouchableMixin = { * element that was blurred just prior to this. This can be overridden when * using `Touchable.Mixin.withoutDefaultFocusAndBlur`. */ + /* $FlowFixMe[missing-this-annot] The 'this' type annotation(s) required by + * Flow's LTI update could not be added via codemod */ touchableHandleFocus: function (e: Event) { this.props.onFocus && this.props.onFocus(e); }, @@ -572,6 +607,8 @@ const TouchableMixin = { * This can be overridden when using * `Touchable.Mixin.withoutDefaultFocusAndBlur`. */ + /* $FlowFixMe[missing-this-annot] The 'this' type annotation(s) required by + * Flow's LTI update could not be added via codemod */ touchableHandleBlur: function (e: Event) { this.props.onBlur && this.props.onBlur(e); }, @@ -652,6 +689,8 @@ const TouchableMixin = { * @sideeffects * @private */ + /* $FlowFixMe[missing-this-annot] The 'this' type annotation(s) required by + * Flow's LTI update could not be added via codemod */ _remeasureMetricsOnActivation: function () { const responderID = this.state.touchable.responderID; if (responderID == null) { @@ -665,6 +704,8 @@ const TouchableMixin = { } }, + /* $FlowFixMe[missing-this-annot] The 'this' type annotation(s) required by + * Flow's LTI update could not be added via codemod */ _handleQueryLayout: function ( l: number, t: number, @@ -691,11 +732,15 @@ const TouchableMixin = { ); }, + /* $FlowFixMe[missing-this-annot] The 'this' type annotation(s) required by + * Flow's LTI update could not be added via codemod */ _handleDelay: function (e: PressEvent) { this.touchableDelayTimeout = null; this._receiveSignal(Signals.DELAY, e); }, + /* $FlowFixMe[missing-this-annot] The 'this' type annotation(s) required by + * Flow's LTI update could not be added via codemod */ _handleLongDelay: function (e: PressEvent) { this.longPressDelayTimeout = null; const curState = this.state.touchable.touchState; @@ -715,6 +760,8 @@ const TouchableMixin = { * @throws Error if invalid state transition or unrecognized signal. * @sideeffects */ + /* $FlowFixMe[missing-this-annot] The 'this' type annotation(s) required by + * Flow's LTI update could not be added via codemod */ _receiveSignal: function (signal: Signal, e: PressEvent) { const responderID = this.state.touchable.responderID; const curState = this.state.touchable.touchState; @@ -754,6 +801,8 @@ const TouchableMixin = { } }, + /* $FlowFixMe[missing-this-annot] The 'this' type annotation(s) required by + * Flow's LTI update could not be added via codemod */ _cancelLongPressDelayTimeout: function () { this.longPressDelayTimeout && clearTimeout(this.longPressDelayTimeout); this.longPressDelayTimeout = null; @@ -766,6 +815,8 @@ const TouchableMixin = { ); }, + /* $FlowFixMe[missing-this-annot] The 'this' type annotation(s) required by + * Flow's LTI update could not be added via codemod */ _savePressInLocation: function (e: PressEvent) { const touch = extractSingleTouch(e.nativeEvent); const pageX = touch && touch.pageX; @@ -797,6 +848,8 @@ const TouchableMixin = { * @param {Event} e Native event. * @sideeffects */ + /* $FlowFixMe[missing-this-annot] The 'this' type annotation(s) required by + * Flow's LTI update could not be added via codemod */ _performSideEffectsForTransition: function ( curState: State, nextState: State, @@ -858,11 +911,15 @@ const TouchableMixin = { this.touchableDelayTimeout = null; }, + /* $FlowFixMe[missing-this-annot] The 'this' type annotation(s) required by + * Flow's LTI update could not be added via codemod */ _startHighlight: function (e: PressEvent) { this._savePressInLocation(e); this.touchableHandleActivePressIn && this.touchableHandleActivePressIn(e); }, + /* $FlowFixMe[missing-this-annot] The 'this' type annotation(s) required by + * Flow's LTI update could not be added via codemod */ _endHighlight: function (e: PressEvent) { if (this.touchableHandleActivePressOut) { if ( @@ -878,7 +935,7 @@ const TouchableMixin = { } }, - withoutDefaultFocusAndBlur: ({}: $TEMPORARY$object<{||}>), + withoutDefaultFocusAndBlur: ({}: {...}), }; /** @@ -895,7 +952,7 @@ const { TouchableMixin.withoutDefaultFocusAndBlur = TouchableMixinWithoutDefaultFocusAndBlur; -const Touchable = { +const Touchable: TouchableType = { Mixin: TouchableMixin, /** * Renders a debugging overlay to visualize touch target with hitSlop (might not work on Android). diff --git a/Libraries/Components/Touchable/TouchableBounce.js b/Libraries/Components/Touchable/TouchableBounce.js index 5fd06a8d80ea58..82e1bf4324b981 100644 --- a/Libraries/Components/Touchable/TouchableBounce.js +++ b/Libraries/Components/Touchable/TouchableBounce.js @@ -8,13 +8,15 @@ * @format */ +import type {ViewStyleProp} from '../../StyleSheet/StyleSheet'; +import typeof TouchableWithoutFeedback from './TouchableWithoutFeedback'; + +import Animated from '../../Animated/Animated'; import Pressability, { type PressabilityConfig, } from '../../Pressability/Pressability'; import {PressabilityDebugView} from '../../Pressability/PressabilityDebug'; -import type {ViewStyleProp} from '../../StyleSheet/StyleSheet'; -import typeof TouchableWithoutFeedback from './TouchableWithoutFeedback'; -import {Animated, Platform} from 'react-native'; +import Platform from '../../Utilities/Platform'; import * as React from 'react'; type Props = $ReadOnly<{| @@ -131,26 +133,59 @@ class TouchableBounce extends React.Component { const { onBlur, onFocus, - onMouseEnter, // [macOS] - onMouseLeave, // [macOS] + onMouseEnter, + onMouseLeave, ...eventHandlersWithoutBlurAndFocus - } = this.state.pressability.getEventHandlers(); + } = this.state.pressability.getEventHandlers(); // [macOS] + const accessibilityLiveRegion = + this.props['aria-live'] === 'off' + ? 'none' + : this.props['aria-live'] ?? this.props.accessibilityLiveRegion; + const _accessibilityState = { + busy: this.props['aria-busy'] ?? this.props.accessibilityState?.busy, + checked: + this.props['aria-checked'] ?? this.props.accessibilityState?.checked, + disabled: + this.props['aria-disabled'] ?? this.props.accessibilityState?.disabled, + expanded: + this.props['aria-expanded'] ?? this.props.accessibilityState?.expanded, + selected: + this.props['aria-selected'] ?? this.props.accessibilityState?.selected, + }; + const accessibilityValue = { + max: this.props['aria-valuemax'] ?? this.props.accessibilityValue?.max, + min: this.props['aria-valuemin'] ?? this.props.accessibilityValue?.min, + now: this.props['aria-valuenow'] ?? this.props.accessibilityValue?.now, + text: this.props['aria-valuetext'] ?? this.props.accessibilityValue?.text, + }; + + const accessibilityLabel = + this.props['aria-label'] ?? this.props.accessibilityLabel; return (