yeet
This commit is contained in:
46
node_modules/react-native/Libraries/Components/Touchable/BoundingDimensions.js
generated
vendored
Normal file
46
node_modules/react-native/Libraries/Components/Touchable/BoundingDimensions.js
generated
vendored
Normal file
@ -0,0 +1,46 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @format
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const PooledClass = require('./PooledClass');
|
||||
|
||||
const twoArgumentPooler = PooledClass.twoArgumentPooler;
|
||||
|
||||
/**
|
||||
* PooledClass representing the bounding rectangle of a region.
|
||||
*
|
||||
* @param {number} width Width of bounding rectangle.
|
||||
* @param {number} height Height of bounding rectangle.
|
||||
* @constructor BoundingDimensions
|
||||
*/
|
||||
function BoundingDimensions(width, height) {
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
}
|
||||
|
||||
BoundingDimensions.prototype.destructor = function() {
|
||||
this.width = null;
|
||||
this.height = null;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {HTMLElement} element Element to return `BoundingDimensions` for.
|
||||
* @return {BoundingDimensions} Bounding dimensions of `element`.
|
||||
*/
|
||||
BoundingDimensions.getPooledFromElement = function(element) {
|
||||
return BoundingDimensions.getPooled(
|
||||
element.offsetWidth,
|
||||
element.offsetHeight,
|
||||
);
|
||||
};
|
||||
|
||||
PooledClass.addPoolingTo(BoundingDimensions, twoArgumentPooler);
|
||||
|
||||
module.exports = BoundingDimensions;
|
122
node_modules/react-native/Libraries/Components/Touchable/PooledClass.js
generated
vendored
Normal file
122
node_modules/react-native/Libraries/Components/Touchable/PooledClass.js
generated
vendored
Normal file
@ -0,0 +1,122 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @format
|
||||
* @flow
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const invariant = require('invariant');
|
||||
|
||||
/**
|
||||
* Static poolers. Several custom versions for each potential number of
|
||||
* arguments. A completely generic pooler is easy to implement, but would
|
||||
* require accessing the `arguments` object. In each of these, `this` refers to
|
||||
* the Class itself, not an instance. If any others are needed, simply add them
|
||||
* here, or in their own files.
|
||||
*/
|
||||
const oneArgumentPooler = function(copyFieldsFrom) {
|
||||
const Klass = this;
|
||||
if (Klass.instancePool.length) {
|
||||
const instance = Klass.instancePool.pop();
|
||||
Klass.call(instance, copyFieldsFrom);
|
||||
return instance;
|
||||
} else {
|
||||
return new Klass(copyFieldsFrom);
|
||||
}
|
||||
};
|
||||
|
||||
const twoArgumentPooler = function(a1, a2) {
|
||||
const Klass = this;
|
||||
if (Klass.instancePool.length) {
|
||||
const instance = Klass.instancePool.pop();
|
||||
Klass.call(instance, a1, a2);
|
||||
return instance;
|
||||
} else {
|
||||
return new Klass(a1, a2);
|
||||
}
|
||||
};
|
||||
|
||||
const threeArgumentPooler = function(a1, a2, a3) {
|
||||
const Klass = this;
|
||||
if (Klass.instancePool.length) {
|
||||
const instance = Klass.instancePool.pop();
|
||||
Klass.call(instance, a1, a2, a3);
|
||||
return instance;
|
||||
} else {
|
||||
return new Klass(a1, a2, a3);
|
||||
}
|
||||
};
|
||||
|
||||
const fourArgumentPooler = function(a1, a2, a3, a4) {
|
||||
const Klass = this;
|
||||
if (Klass.instancePool.length) {
|
||||
const instance = Klass.instancePool.pop();
|
||||
Klass.call(instance, a1, a2, a3, a4);
|
||||
return instance;
|
||||
} else {
|
||||
return new Klass(a1, a2, a3, a4);
|
||||
}
|
||||
};
|
||||
|
||||
const standardReleaser = function(instance) {
|
||||
const Klass = this;
|
||||
invariant(
|
||||
instance instanceof Klass,
|
||||
'Trying to release an instance into a pool of a different type.',
|
||||
);
|
||||
instance.destructor();
|
||||
if (Klass.instancePool.length < Klass.poolSize) {
|
||||
Klass.instancePool.push(instance);
|
||||
}
|
||||
};
|
||||
|
||||
const DEFAULT_POOL_SIZE = 10;
|
||||
const DEFAULT_POOLER = oneArgumentPooler;
|
||||
|
||||
type Pooler = any;
|
||||
|
||||
/**
|
||||
* Augments `CopyConstructor` to be a poolable class, augmenting only the class
|
||||
* itself (statically) not adding any prototypical fields. Any CopyConstructor
|
||||
* you give this may have a `poolSize` property, and will look for a
|
||||
* prototypical `destructor` on instances.
|
||||
*
|
||||
* @param {Function} CopyConstructor Constructor that can be used to reset.
|
||||
* @param {Function} pooler Customizable pooler.
|
||||
*/
|
||||
const addPoolingTo = function<T>(
|
||||
CopyConstructor: Class<T>,
|
||||
pooler: Pooler,
|
||||
): Class<T> & {
|
||||
getPooled(
|
||||
...args: $ReadOnlyArray<mixed>
|
||||
): /* arguments of the constructor */ T,
|
||||
release(instance: mixed): void,
|
||||
...
|
||||
} {
|
||||
// Casting as any so that flow ignores the actual implementation and trusts
|
||||
// it to match the type we declared
|
||||
const NewKlass = (CopyConstructor: any);
|
||||
NewKlass.instancePool = [];
|
||||
NewKlass.getPooled = pooler || DEFAULT_POOLER;
|
||||
if (!NewKlass.poolSize) {
|
||||
NewKlass.poolSize = DEFAULT_POOL_SIZE;
|
||||
}
|
||||
NewKlass.release = standardReleaser;
|
||||
return NewKlass;
|
||||
};
|
||||
|
||||
const PooledClass = {
|
||||
addPoolingTo: addPoolingTo,
|
||||
oneArgumentPooler: (oneArgumentPooler: Pooler),
|
||||
twoArgumentPooler: (twoArgumentPooler: Pooler),
|
||||
threeArgumentPooler: (threeArgumentPooler: Pooler),
|
||||
fourArgumentPooler: (fourArgumentPooler: Pooler),
|
||||
};
|
||||
|
||||
module.exports = PooledClass;
|
36
node_modules/react-native/Libraries/Components/Touchable/Position.js
generated
vendored
Normal file
36
node_modules/react-native/Libraries/Components/Touchable/Position.js
generated
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @format
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const PooledClass = require('./PooledClass');
|
||||
|
||||
const twoArgumentPooler = PooledClass.twoArgumentPooler;
|
||||
|
||||
/**
|
||||
* Position does not expose methods for construction via an `HTMLDOMElement`,
|
||||
* because it isn't meaningful to construct such a thing without first defining
|
||||
* a frame of reference.
|
||||
*
|
||||
* @param {number} windowStartKey Key that window starts at.
|
||||
* @param {number} windowEndKey Key that window ends at.
|
||||
*/
|
||||
function Position(left, top) {
|
||||
this.left = left;
|
||||
this.top = top;
|
||||
}
|
||||
|
||||
Position.prototype.destructor = function() {
|
||||
this.left = null;
|
||||
this.top = null;
|
||||
};
|
||||
|
||||
PooledClass.addPoolingTo(Position, twoArgumentPooler);
|
||||
|
||||
module.exports = Position;
|
55
node_modules/react-native/Libraries/Components/Touchable/TVTouchable.js
generated
vendored
Normal file
55
node_modules/react-native/Libraries/Components/Touchable/TVTouchable.js
generated
vendored
Normal file
@ -0,0 +1,55 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its 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
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import invariant from 'invariant';
|
||||
import ReactNative from '../../Renderer/shims/ReactNative';
|
||||
import type {
|
||||
BlurEvent,
|
||||
FocusEvent,
|
||||
PressEvent,
|
||||
} from '../../Types/CoreEventTypes';
|
||||
import Platform from '../../Utilities/Platform';
|
||||
import TVEventHandler from '../../Components/AppleTV/TVEventHandler';
|
||||
|
||||
type TVTouchableConfig = $ReadOnly<{|
|
||||
getDisabled: () => boolean,
|
||||
onBlur: (event: BlurEvent) => mixed,
|
||||
onFocus: (event: FocusEvent) => mixed,
|
||||
onPress: (event: PressEvent) => mixed,
|
||||
|}>;
|
||||
|
||||
export default class TVTouchable {
|
||||
_tvEventHandler: TVEventHandler;
|
||||
|
||||
constructor(component: any, config: TVTouchableConfig) {
|
||||
invariant(Platform.isTV, 'TVTouchable: Requires `Platform.isTV`.');
|
||||
this._tvEventHandler = new TVEventHandler();
|
||||
this._tvEventHandler.enable(component, (_, tvData) => {
|
||||
tvData.dispatchConfig = {};
|
||||
if (ReactNative.findNodeHandle(component) === tvData.tag) {
|
||||
if (tvData.eventType === 'focus') {
|
||||
config.onFocus(tvData);
|
||||
} else if (tvData.eventType === 'blur') {
|
||||
config.onBlur(tvData);
|
||||
} else if (tvData.eventType === 'select') {
|
||||
if (!config.getDisabled()) {
|
||||
config.onPress(tvData);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
destroy(): void {
|
||||
this._tvEventHandler.disable();
|
||||
}
|
||||
}
|
984
node_modules/react-native/Libraries/Components/Touchable/Touchable.js
generated
vendored
Normal file
984
node_modules/react-native/Libraries/Components/Touchable/Touchable.js
generated
vendored
Normal file
@ -0,0 +1,984 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its 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
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const BoundingDimensions = require('./BoundingDimensions');
|
||||
const Platform = require('../../Utilities/Platform');
|
||||
const Position = require('./Position');
|
||||
const React = require('react');
|
||||
const ReactNative = require('../../Renderer/shims/ReactNative');
|
||||
const StyleSheet = require('../../StyleSheet/StyleSheet');
|
||||
const TVEventHandler = require('../AppleTV/TVEventHandler');
|
||||
const UIManager = require('../../ReactNative/UIManager');
|
||||
const View = require('../View/View');
|
||||
const SoundManager = require('../Sound/SoundManager');
|
||||
|
||||
const keyMirror = require('fbjs/lib/keyMirror');
|
||||
const normalizeColor = require('../../StyleSheet/normalizeColor');
|
||||
|
||||
import type {EdgeInsetsProp} from '../../StyleSheet/EdgeInsetsPropType';
|
||||
import type {PressEvent} from '../../Types/CoreEventTypes';
|
||||
|
||||
const extractSingleTouch = nativeEvent => {
|
||||
const touches = nativeEvent.touches;
|
||||
const changedTouches = nativeEvent.changedTouches;
|
||||
const hasTouches = touches && touches.length > 0;
|
||||
const hasChangedTouches = changedTouches && changedTouches.length > 0;
|
||||
|
||||
return !hasTouches && hasChangedTouches
|
||||
? changedTouches[0]
|
||||
: hasTouches
|
||||
? touches[0]
|
||||
: nativeEvent;
|
||||
};
|
||||
|
||||
/**
|
||||
* `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 (
|
||||
* <View
|
||||
* onStartShouldSetResponder={this.touchableHandleStartShouldSetResponder}
|
||||
* onResponderTerminationRequest={this.touchableHandleResponderTerminationRequest}
|
||||
* onResponderGrant={this.touchableHandleResponderGrant}
|
||||
* onResponderMove={this.touchableHandleResponderMove}
|
||||
* onResponderRelease={this.touchableHandleResponderRelease}
|
||||
* onResponderTerminate={this.touchableHandleResponderTerminate}>
|
||||
* <View>
|
||||
* 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.
|
||||
* </View>
|
||||
* </View>
|
||||
* );
|
||||
*
|
||||
* - 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 = keyMirror({
|
||||
NOT_RESPONDER: null, // Not the responder
|
||||
RESPONDER_INACTIVE_PRESS_IN: null, // Responder, inactive, in the `PressRect`
|
||||
RESPONDER_INACTIVE_PRESS_OUT: null, // Responder, inactive, out of `PressRect`
|
||||
RESPONDER_ACTIVE_PRESS_IN: null, // Responder, active, in the `PressRect`
|
||||
RESPONDER_ACTIVE_PRESS_OUT: null, // Responder, active, out of `PressRect`
|
||||
RESPONDER_ACTIVE_LONG_PRESS_IN: null, // Responder, active, in the `PressRect`, after long press threshold
|
||||
RESPONDER_ACTIVE_LONG_PRESS_OUT: null, // Responder, active, out of `PressRect`, after long press threshold
|
||||
ERROR: null,
|
||||
});
|
||||
|
||||
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;
|
||||
|
||||
/*
|
||||
* Quick lookup map for states that are considered to be "active"
|
||||
*/
|
||||
|
||||
const baseStatesConditions = {
|
||||
NOT_RESPONDER: false,
|
||||
RESPONDER_INACTIVE_PRESS_IN: false,
|
||||
RESPONDER_INACTIVE_PRESS_OUT: false,
|
||||
RESPONDER_ACTIVE_PRESS_IN: false,
|
||||
RESPONDER_ACTIVE_PRESS_OUT: false,
|
||||
RESPONDER_ACTIVE_LONG_PRESS_IN: false,
|
||||
RESPONDER_ACTIVE_LONG_PRESS_OUT: false,
|
||||
ERROR: false,
|
||||
};
|
||||
|
||||
const IsActive = {
|
||||
...baseStatesConditions,
|
||||
RESPONDER_ACTIVE_PRESS_OUT: true,
|
||||
RESPONDER_ACTIVE_PRESS_IN: true,
|
||||
};
|
||||
|
||||
/**
|
||||
* Quick lookup for states that are considered to be "pressing" and are
|
||||
* therefore eligible to result in a "selection" if the press stops.
|
||||
*/
|
||||
const IsPressingIn = {
|
||||
...baseStatesConditions,
|
||||
RESPONDER_INACTIVE_PRESS_IN: true,
|
||||
RESPONDER_ACTIVE_PRESS_IN: true,
|
||||
RESPONDER_ACTIVE_LONG_PRESS_IN: true,
|
||||
};
|
||||
|
||||
const IsLongPressingIn = {
|
||||
...baseStatesConditions,
|
||||
RESPONDER_ACTIVE_LONG_PRESS_IN: true,
|
||||
};
|
||||
|
||||
/**
|
||||
* Inputs to the state machine.
|
||||
*/
|
||||
const Signals = keyMirror({
|
||||
DELAY: null,
|
||||
RESPONDER_GRANT: null,
|
||||
RESPONDER_RELEASE: null,
|
||||
RESPONDER_TERMINATED: null,
|
||||
ENTER_PRESS_RECT: null,
|
||||
LEAVE_PRESS_RECT: null,
|
||||
LONG_PRESS_DETECTED: null,
|
||||
});
|
||||
|
||||
type Signal =
|
||||
| typeof Signals.DELAY
|
||||
| typeof Signals.RESPONDER_GRANT
|
||||
| typeof Signals.RESPONDER_RELEASE
|
||||
| typeof Signals.RESPONDER_TERMINATED
|
||||
| typeof Signals.ENTER_PRESS_RECT
|
||||
| typeof Signals.LEAVE_PRESS_RECT
|
||||
| typeof Signals.LONG_PRESS_DETECTED;
|
||||
|
||||
/**
|
||||
* Mapping from States x Signals => States
|
||||
*/
|
||||
const Transitions = {
|
||||
NOT_RESPONDER: {
|
||||
DELAY: States.ERROR,
|
||||
RESPONDER_GRANT: States.RESPONDER_INACTIVE_PRESS_IN,
|
||||
RESPONDER_RELEASE: States.ERROR,
|
||||
RESPONDER_TERMINATED: States.ERROR,
|
||||
ENTER_PRESS_RECT: States.ERROR,
|
||||
LEAVE_PRESS_RECT: States.ERROR,
|
||||
LONG_PRESS_DETECTED: States.ERROR,
|
||||
},
|
||||
RESPONDER_INACTIVE_PRESS_IN: {
|
||||
DELAY: States.RESPONDER_ACTIVE_PRESS_IN,
|
||||
RESPONDER_GRANT: States.ERROR,
|
||||
RESPONDER_RELEASE: States.NOT_RESPONDER,
|
||||
RESPONDER_TERMINATED: States.NOT_RESPONDER,
|
||||
ENTER_PRESS_RECT: States.RESPONDER_INACTIVE_PRESS_IN,
|
||||
LEAVE_PRESS_RECT: States.RESPONDER_INACTIVE_PRESS_OUT,
|
||||
LONG_PRESS_DETECTED: States.ERROR,
|
||||
},
|
||||
RESPONDER_INACTIVE_PRESS_OUT: {
|
||||
DELAY: States.RESPONDER_ACTIVE_PRESS_OUT,
|
||||
RESPONDER_GRANT: States.ERROR,
|
||||
RESPONDER_RELEASE: States.NOT_RESPONDER,
|
||||
RESPONDER_TERMINATED: States.NOT_RESPONDER,
|
||||
ENTER_PRESS_RECT: States.RESPONDER_INACTIVE_PRESS_IN,
|
||||
LEAVE_PRESS_RECT: States.RESPONDER_INACTIVE_PRESS_OUT,
|
||||
LONG_PRESS_DETECTED: States.ERROR,
|
||||
},
|
||||
RESPONDER_ACTIVE_PRESS_IN: {
|
||||
DELAY: States.ERROR,
|
||||
RESPONDER_GRANT: States.ERROR,
|
||||
RESPONDER_RELEASE: States.NOT_RESPONDER,
|
||||
RESPONDER_TERMINATED: States.NOT_RESPONDER,
|
||||
ENTER_PRESS_RECT: States.RESPONDER_ACTIVE_PRESS_IN,
|
||||
LEAVE_PRESS_RECT: States.RESPONDER_ACTIVE_PRESS_OUT,
|
||||
LONG_PRESS_DETECTED: States.RESPONDER_ACTIVE_LONG_PRESS_IN,
|
||||
},
|
||||
RESPONDER_ACTIVE_PRESS_OUT: {
|
||||
DELAY: States.ERROR,
|
||||
RESPONDER_GRANT: States.ERROR,
|
||||
RESPONDER_RELEASE: States.NOT_RESPONDER,
|
||||
RESPONDER_TERMINATED: States.NOT_RESPONDER,
|
||||
ENTER_PRESS_RECT: States.RESPONDER_ACTIVE_PRESS_IN,
|
||||
LEAVE_PRESS_RECT: States.RESPONDER_ACTIVE_PRESS_OUT,
|
||||
LONG_PRESS_DETECTED: States.ERROR,
|
||||
},
|
||||
RESPONDER_ACTIVE_LONG_PRESS_IN: {
|
||||
DELAY: States.ERROR,
|
||||
RESPONDER_GRANT: States.ERROR,
|
||||
RESPONDER_RELEASE: States.NOT_RESPONDER,
|
||||
RESPONDER_TERMINATED: States.NOT_RESPONDER,
|
||||
ENTER_PRESS_RECT: States.RESPONDER_ACTIVE_LONG_PRESS_IN,
|
||||
LEAVE_PRESS_RECT: States.RESPONDER_ACTIVE_LONG_PRESS_OUT,
|
||||
LONG_PRESS_DETECTED: States.RESPONDER_ACTIVE_LONG_PRESS_IN,
|
||||
},
|
||||
RESPONDER_ACTIVE_LONG_PRESS_OUT: {
|
||||
DELAY: States.ERROR,
|
||||
RESPONDER_GRANT: States.ERROR,
|
||||
RESPONDER_RELEASE: States.NOT_RESPONDER,
|
||||
RESPONDER_TERMINATED: States.NOT_RESPONDER,
|
||||
ENTER_PRESS_RECT: States.RESPONDER_ACTIVE_LONG_PRESS_IN,
|
||||
LEAVE_PRESS_RECT: States.RESPONDER_ACTIVE_LONG_PRESS_OUT,
|
||||
LONG_PRESS_DETECTED: States.ERROR,
|
||||
},
|
||||
error: {
|
||||
DELAY: States.NOT_RESPONDER,
|
||||
RESPONDER_GRANT: States.RESPONDER_INACTIVE_PRESS_IN,
|
||||
RESPONDER_RELEASE: States.NOT_RESPONDER,
|
||||
RESPONDER_TERMINATED: States.NOT_RESPONDER,
|
||||
ENTER_PRESS_RECT: States.NOT_RESPONDER,
|
||||
LEAVE_PRESS_RECT: States.NOT_RESPONDER,
|
||||
LONG_PRESS_DETECTED: States.NOT_RESPONDER,
|
||||
},
|
||||
};
|
||||
|
||||
// ==== Typical Constants for integrating into UI components ====
|
||||
// var HIT_EXPAND_PX = 20;
|
||||
// var HIT_VERT_OFFSET_PX = 10;
|
||||
const HIGHLIGHT_DELAY_MS = 130;
|
||||
|
||||
const PRESS_EXPAND_PX = 20;
|
||||
|
||||
const LONG_PRESS_THRESHOLD = 500;
|
||||
|
||||
const LONG_PRESS_DELAY_MS = LONG_PRESS_THRESHOLD - HIGHLIGHT_DELAY_MS;
|
||||
|
||||
const LONG_PRESS_ALLOWED_MOVEMENT = 10;
|
||||
|
||||
// Default amount "active" region protrudes beyond box
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
const TouchableMixin = {
|
||||
componentDidMount: function() {
|
||||
if (!Platform.isTV) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._tvEventHandler = new TVEventHandler();
|
||||
this._tvEventHandler.enable(this, function(cmp, evt) {
|
||||
const myTag = ReactNative.findNodeHandle(cmp);
|
||||
evt.dispatchConfig = {};
|
||||
if (myTag === evt.tag) {
|
||||
if (evt.eventType === 'focus') {
|
||||
cmp.touchableHandleFocus(evt);
|
||||
} else if (evt.eventType === 'blur') {
|
||||
cmp.touchableHandleBlur(evt);
|
||||
} else if (evt.eventType === 'select' && Platform.OS !== 'android') {
|
||||
cmp.touchableHandlePress &&
|
||||
!cmp.props.disabled &&
|
||||
cmp.touchableHandlePress(evt);
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Clear all timeouts on unmount
|
||||
*/
|
||||
componentWillUnmount: function() {
|
||||
if (this._tvEventHandler) {
|
||||
this._tvEventHandler.disable();
|
||||
delete this._tvEventHandler;
|
||||
}
|
||||
this.touchableDelayTimeout && clearTimeout(this.touchableDelayTimeout);
|
||||
this.longPressDelayTimeout && clearTimeout(this.longPressDelayTimeout);
|
||||
this.pressOutDelayTimeout && clearTimeout(this.pressOutDelayTimeout);
|
||||
},
|
||||
|
||||
/**
|
||||
* 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: function(): $TEMPORARY$object<{|
|
||||
touchable: $TEMPORARY$object<{|responderID: null, touchState: void|}>,
|
||||
|}> {
|
||||
return {
|
||||
touchable: {touchState: undefined, responderID: null},
|
||||
};
|
||||
},
|
||||
|
||||
// ==== Hooks to Gesture Responder system ====
|
||||
/**
|
||||
* Must return true if embedded in a native platform scroll view.
|
||||
*/
|
||||
touchableHandleResponderTerminationRequest: function(): any {
|
||||
return !this.props.rejectResponderTermination;
|
||||
},
|
||||
|
||||
/**
|
||||
* Must return true to start the process of `Touchable`.
|
||||
*/
|
||||
touchableHandleStartShouldSetResponder: function(): any {
|
||||
return !this.props.disabled;
|
||||
},
|
||||
|
||||
/**
|
||||
* Return true to cancel press on long press.
|
||||
*/
|
||||
touchableLongPressCancelsPress: function(): boolean {
|
||||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Place as callback for a DOM element's `onResponderGrant` event.
|
||||
* @param {SyntheticEvent} e Synthetic event from event system.
|
||||
*
|
||||
*/
|
||||
touchableHandleResponderGrant: function(e: PressEvent) {
|
||||
const dispatchID = e.currentTarget;
|
||||
// Since e is used in a callback invoked on another event loop
|
||||
// (as in setTimeout etc), we need to call e.persist() on the
|
||||
// event to make sure it doesn't get reused in the event object pool.
|
||||
e.persist();
|
||||
|
||||
this.pressOutDelayTimeout && clearTimeout(this.pressOutDelayTimeout);
|
||||
this.pressOutDelayTimeout = null;
|
||||
|
||||
this.state.touchable.touchState = States.NOT_RESPONDER;
|
||||
this.state.touchable.responderID = dispatchID;
|
||||
this._receiveSignal(Signals.RESPONDER_GRANT, e);
|
||||
let delayMS =
|
||||
this.touchableGetHighlightDelayMS !== undefined
|
||||
? Math.max(this.touchableGetHighlightDelayMS(), 0)
|
||||
: HIGHLIGHT_DELAY_MS;
|
||||
delayMS = isNaN(delayMS) ? HIGHLIGHT_DELAY_MS : delayMS;
|
||||
if (delayMS !== 0) {
|
||||
this.touchableDelayTimeout = setTimeout(
|
||||
this._handleDelay.bind(this, e),
|
||||
delayMS,
|
||||
);
|
||||
} else {
|
||||
this._handleDelay(e);
|
||||
}
|
||||
|
||||
let longDelayMS =
|
||||
this.touchableGetLongPressDelayMS !== undefined
|
||||
? Math.max(this.touchableGetLongPressDelayMS(), 10)
|
||||
: LONG_PRESS_DELAY_MS;
|
||||
longDelayMS = isNaN(longDelayMS) ? LONG_PRESS_DELAY_MS : longDelayMS;
|
||||
this.longPressDelayTimeout = setTimeout(
|
||||
this._handleLongDelay.bind(this, e),
|
||||
longDelayMS + delayMS,
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Place as callback for a DOM element's `onResponderRelease` event.
|
||||
*/
|
||||
touchableHandleResponderRelease: function(e: PressEvent) {
|
||||
this.pressInLocation = null;
|
||||
this._receiveSignal(Signals.RESPONDER_RELEASE, e);
|
||||
},
|
||||
|
||||
/**
|
||||
* Place as callback for a DOM element's `onResponderTerminate` event.
|
||||
*/
|
||||
touchableHandleResponderTerminate: function(e: PressEvent) {
|
||||
this.pressInLocation = null;
|
||||
this._receiveSignal(Signals.RESPONDER_TERMINATED, e);
|
||||
},
|
||||
|
||||
/**
|
||||
* Place as callback for a DOM element's `onResponderMove` event.
|
||||
*/
|
||||
touchableHandleResponderMove: function(e: PressEvent) {
|
||||
// Measurement may not have returned yet.
|
||||
if (!this.state.touchable.positionOnActivate) {
|
||||
return;
|
||||
}
|
||||
|
||||
const positionOnActivate = this.state.touchable.positionOnActivate;
|
||||
const dimensionsOnActivate = this.state.touchable.dimensionsOnActivate;
|
||||
const pressRectOffset = this.touchableGetPressRectOffset
|
||||
? this.touchableGetPressRectOffset()
|
||||
: {
|
||||
left: PRESS_EXPAND_PX,
|
||||
right: PRESS_EXPAND_PX,
|
||||
top: PRESS_EXPAND_PX,
|
||||
bottom: PRESS_EXPAND_PX,
|
||||
};
|
||||
|
||||
let pressExpandLeft = pressRectOffset.left;
|
||||
let pressExpandTop = pressRectOffset.top;
|
||||
let pressExpandRight = pressRectOffset.right;
|
||||
let pressExpandBottom = pressRectOffset.bottom;
|
||||
|
||||
const hitSlop = this.touchableGetHitSlop
|
||||
? this.touchableGetHitSlop()
|
||||
: null;
|
||||
|
||||
if (hitSlop) {
|
||||
pressExpandLeft += hitSlop.left || 0;
|
||||
pressExpandTop += hitSlop.top || 0;
|
||||
pressExpandRight += hitSlop.right || 0;
|
||||
pressExpandBottom += hitSlop.bottom || 0;
|
||||
}
|
||||
|
||||
const touch = extractSingleTouch(e.nativeEvent);
|
||||
const pageX = touch && touch.pageX;
|
||||
const pageY = touch && touch.pageY;
|
||||
|
||||
if (this.pressInLocation) {
|
||||
const movedDistance = this._getDistanceBetweenPoints(
|
||||
pageX,
|
||||
pageY,
|
||||
this.pressInLocation.pageX,
|
||||
this.pressInLocation.pageY,
|
||||
);
|
||||
if (movedDistance > LONG_PRESS_ALLOWED_MOVEMENT) {
|
||||
this._cancelLongPressDelayTimeout();
|
||||
}
|
||||
}
|
||||
|
||||
const isTouchWithinActive =
|
||||
pageX > positionOnActivate.left - pressExpandLeft &&
|
||||
pageY > positionOnActivate.top - pressExpandTop &&
|
||||
pageX <
|
||||
positionOnActivate.left +
|
||||
dimensionsOnActivate.width +
|
||||
pressExpandRight &&
|
||||
pageY <
|
||||
positionOnActivate.top +
|
||||
dimensionsOnActivate.height +
|
||||
pressExpandBottom;
|
||||
if (isTouchWithinActive) {
|
||||
const prevState = this.state.touchable.touchState;
|
||||
this._receiveSignal(Signals.ENTER_PRESS_RECT, e);
|
||||
const curState = this.state.touchable.touchState;
|
||||
if (
|
||||
curState === States.RESPONDER_INACTIVE_PRESS_IN &&
|
||||
prevState !== States.RESPONDER_INACTIVE_PRESS_IN
|
||||
) {
|
||||
// fix for t7967420
|
||||
this._cancelLongPressDelayTimeout();
|
||||
}
|
||||
} else {
|
||||
this._cancelLongPressDelayTimeout();
|
||||
this._receiveSignal(Signals.LEAVE_PRESS_RECT, e);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 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: function(e: Event) {
|
||||
this.props.onFocus && this.props.onFocus(e);
|
||||
},
|
||||
|
||||
/**
|
||||
* 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: function(e: Event) {
|
||||
this.props.onBlur && this.props.onBlur(e);
|
||||
},
|
||||
|
||||
// ==== Abstract Application Callbacks ====
|
||||
|
||||
/**
|
||||
* 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).
|
||||
*
|
||||
* @abstract
|
||||
* touchableHandleActivePressIn: function,
|
||||
*/
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @abstract
|
||||
* touchableHandleActivePressOut: function
|
||||
*/
|
||||
|
||||
/**
|
||||
* 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`.
|
||||
*
|
||||
* @abstract
|
||||
* touchableHandlePress: function
|
||||
*/
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @abstract
|
||||
* touchableHandleLongPress: function
|
||||
*/
|
||||
|
||||
/**
|
||||
* Returns the number of millis to wait before triggering a highlight.
|
||||
*
|
||||
* @abstract
|
||||
* touchableGetHighlightDelayMS: function
|
||||
*/
|
||||
|
||||
/**
|
||||
* Returns the amount to extend the `HitRect` into the `PressRect`. Positive
|
||||
* numbers mean the size expands outwards.
|
||||
*
|
||||
* @abstract
|
||||
* touchableGetPressRectOffset: function
|
||||
*/
|
||||
|
||||
// ==== Internal Logic ====
|
||||
|
||||
/**
|
||||
* Measures the `HitRect` node on activation. The Bounding rectangle is with
|
||||
* respect to viewport - not page, so adding the `pageXOffset/pageYOffset`
|
||||
* should result in points that are in the same coordinate system as an
|
||||
* event's `globalX/globalY` data values.
|
||||
*
|
||||
* - Consider caching this for the lifetime of the component, or possibly
|
||||
* being able to share this cache between any `ScrollMap` view.
|
||||
*
|
||||
* @sideeffects
|
||||
* @private
|
||||
*/
|
||||
_remeasureMetricsOnActivation: function() {
|
||||
const responderID = this.state.touchable.responderID;
|
||||
if (responderID == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof responderID === 'number') {
|
||||
UIManager.measure(responderID, this._handleQueryLayout);
|
||||
} else {
|
||||
responderID.measure(this._handleQueryLayout);
|
||||
}
|
||||
},
|
||||
|
||||
_handleQueryLayout: function(
|
||||
l: number,
|
||||
t: number,
|
||||
w: number,
|
||||
h: number,
|
||||
globalX: number,
|
||||
globalY: number,
|
||||
) {
|
||||
//don't do anything UIManager failed to measure node
|
||||
if (!l && !t && !w && !h && !globalX && !globalY) {
|
||||
return;
|
||||
}
|
||||
this.state.touchable.positionOnActivate &&
|
||||
Position.release(this.state.touchable.positionOnActivate);
|
||||
this.state.touchable.dimensionsOnActivate &&
|
||||
BoundingDimensions.release(this.state.touchable.dimensionsOnActivate);
|
||||
this.state.touchable.positionOnActivate = Position.getPooled(
|
||||
globalX,
|
||||
globalY,
|
||||
);
|
||||
this.state.touchable.dimensionsOnActivate = BoundingDimensions.getPooled(
|
||||
w,
|
||||
h,
|
||||
);
|
||||
},
|
||||
|
||||
_handleDelay: function(e: PressEvent) {
|
||||
this.touchableDelayTimeout = null;
|
||||
this._receiveSignal(Signals.DELAY, e);
|
||||
},
|
||||
|
||||
_handleLongDelay: function(e: PressEvent) {
|
||||
this.longPressDelayTimeout = null;
|
||||
const curState = this.state.touchable.touchState;
|
||||
if (
|
||||
curState === States.RESPONDER_ACTIVE_PRESS_IN ||
|
||||
curState === States.RESPONDER_ACTIVE_LONG_PRESS_IN
|
||||
) {
|
||||
this._receiveSignal(Signals.LONG_PRESS_DETECTED, e);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Receives a state machine signal, performs side effects of the transition
|
||||
* and stores the new state. Validates the transition as well.
|
||||
*
|
||||
* @param {Signals} signal State machine signal.
|
||||
* @throws Error if invalid state transition or unrecognized signal.
|
||||
* @sideeffects
|
||||
*/
|
||||
_receiveSignal: function(signal: Signal, e: PressEvent) {
|
||||
const responderID = this.state.touchable.responderID;
|
||||
const curState = this.state.touchable.touchState;
|
||||
const nextState = Transitions[curState] && Transitions[curState][signal];
|
||||
if (!responderID && signal === Signals.RESPONDER_RELEASE) {
|
||||
return;
|
||||
}
|
||||
if (!nextState) {
|
||||
throw new Error(
|
||||
'Unrecognized signal `' +
|
||||
signal +
|
||||
'` or state `' +
|
||||
curState +
|
||||
'` for Touchable responder `' +
|
||||
typeof this.state.touchable.responderID ===
|
||||
'number'
|
||||
? this.state.touchable.responderID
|
||||
: 'host component' + '`',
|
||||
);
|
||||
}
|
||||
if (nextState === States.ERROR) {
|
||||
throw new Error(
|
||||
'Touchable cannot transition from `' +
|
||||
curState +
|
||||
'` to `' +
|
||||
signal +
|
||||
'` for responder `' +
|
||||
typeof this.state.touchable.responderID ===
|
||||
'number'
|
||||
? this.state.touchable.responderID
|
||||
: '<<host component>>' + '`',
|
||||
);
|
||||
}
|
||||
if (curState !== nextState) {
|
||||
this._performSideEffectsForTransition(curState, nextState, signal, e);
|
||||
this.state.touchable.touchState = nextState;
|
||||
}
|
||||
},
|
||||
|
||||
_cancelLongPressDelayTimeout: function() {
|
||||
this.longPressDelayTimeout && clearTimeout(this.longPressDelayTimeout);
|
||||
this.longPressDelayTimeout = null;
|
||||
},
|
||||
|
||||
_isHighlight: function(state: State): boolean {
|
||||
return (
|
||||
state === States.RESPONDER_ACTIVE_PRESS_IN ||
|
||||
state === States.RESPONDER_ACTIVE_LONG_PRESS_IN
|
||||
);
|
||||
},
|
||||
|
||||
_savePressInLocation: function(e: PressEvent) {
|
||||
const touch = extractSingleTouch(e.nativeEvent);
|
||||
const pageX = touch && touch.pageX;
|
||||
const pageY = touch && touch.pageY;
|
||||
const locationX = touch && touch.locationX;
|
||||
const locationY = touch && touch.locationY;
|
||||
this.pressInLocation = {pageX, pageY, locationX, locationY};
|
||||
},
|
||||
|
||||
_getDistanceBetweenPoints: function(
|
||||
aX: number,
|
||||
aY: number,
|
||||
bX: number,
|
||||
bY: number,
|
||||
): number {
|
||||
const deltaX = aX - bX;
|
||||
const deltaY = aY - bY;
|
||||
return Math.sqrt(deltaX * deltaX + deltaY * deltaY);
|
||||
},
|
||||
|
||||
/**
|
||||
* Will perform a transition between touchable states, and identify any
|
||||
* highlighting or unhighlighting that must be performed for this particular
|
||||
* transition.
|
||||
*
|
||||
* @param {States} curState Current Touchable state.
|
||||
* @param {States} nextState Next Touchable state.
|
||||
* @param {Signal} signal Signal that triggered the transition.
|
||||
* @param {Event} e Native event.
|
||||
* @sideeffects
|
||||
*/
|
||||
_performSideEffectsForTransition: function(
|
||||
curState: State,
|
||||
nextState: State,
|
||||
signal: Signal,
|
||||
e: PressEvent,
|
||||
) {
|
||||
const curIsHighlight = this._isHighlight(curState);
|
||||
const newIsHighlight = this._isHighlight(nextState);
|
||||
|
||||
const isFinalSignal =
|
||||
signal === Signals.RESPONDER_TERMINATED ||
|
||||
signal === Signals.RESPONDER_RELEASE;
|
||||
|
||||
if (isFinalSignal) {
|
||||
this._cancelLongPressDelayTimeout();
|
||||
}
|
||||
|
||||
const isInitialTransition =
|
||||
curState === States.NOT_RESPONDER &&
|
||||
nextState === States.RESPONDER_INACTIVE_PRESS_IN;
|
||||
|
||||
const isActiveTransition = !IsActive[curState] && IsActive[nextState];
|
||||
if (isInitialTransition || isActiveTransition) {
|
||||
this._remeasureMetricsOnActivation();
|
||||
}
|
||||
|
||||
if (IsPressingIn[curState] && signal === Signals.LONG_PRESS_DETECTED) {
|
||||
this.touchableHandleLongPress && this.touchableHandleLongPress(e);
|
||||
}
|
||||
|
||||
if (newIsHighlight && !curIsHighlight) {
|
||||
this._startHighlight(e);
|
||||
} else if (!newIsHighlight && curIsHighlight) {
|
||||
this._endHighlight(e);
|
||||
}
|
||||
|
||||
if (IsPressingIn[curState] && signal === Signals.RESPONDER_RELEASE) {
|
||||
const hasLongPressHandler = !!this.props.onLongPress;
|
||||
const pressIsLongButStillCallOnPress =
|
||||
IsLongPressingIn[curState] && // We *are* long pressing.. // But either has no long handler
|
||||
(!hasLongPressHandler || !this.touchableLongPressCancelsPress()); // or we're told to ignore it.
|
||||
|
||||
const shouldInvokePress =
|
||||
!IsLongPressingIn[curState] || pressIsLongButStillCallOnPress;
|
||||
if (shouldInvokePress && this.touchableHandlePress) {
|
||||
if (!newIsHighlight && !curIsHighlight) {
|
||||
// we never highlighted because of delay, but we should highlight now
|
||||
this._startHighlight(e);
|
||||
this._endHighlight(e);
|
||||
}
|
||||
if (Platform.OS === 'android' && !this.props.touchSoundDisabled) {
|
||||
SoundManager.playTouchSound();
|
||||
}
|
||||
this.touchableHandlePress(e);
|
||||
}
|
||||
}
|
||||
|
||||
this.touchableDelayTimeout && clearTimeout(this.touchableDelayTimeout);
|
||||
this.touchableDelayTimeout = null;
|
||||
},
|
||||
|
||||
_startHighlight: function(e: PressEvent) {
|
||||
this._savePressInLocation(e);
|
||||
this.touchableHandleActivePressIn && this.touchableHandleActivePressIn(e);
|
||||
},
|
||||
|
||||
_endHighlight: function(e: PressEvent) {
|
||||
if (this.touchableHandleActivePressOut) {
|
||||
if (
|
||||
this.touchableGetPressOutDelayMS &&
|
||||
this.touchableGetPressOutDelayMS()
|
||||
) {
|
||||
this.pressOutDelayTimeout = setTimeout(() => {
|
||||
this.touchableHandleActivePressOut(e);
|
||||
}, this.touchableGetPressOutDelayMS());
|
||||
} else {
|
||||
this.touchableHandleActivePressOut(e);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
withoutDefaultFocusAndBlur: ({}: $TEMPORARY$object<{||}>),
|
||||
};
|
||||
|
||||
/**
|
||||
* Provide an optional version of the mixin where `touchableHandleFocus` and
|
||||
* `touchableHandleBlur` can be overridden. This allows appropriate defaults to
|
||||
* be set on TV platforms, without breaking existing implementations of
|
||||
* `Touchable`.
|
||||
*/
|
||||
const {
|
||||
touchableHandleFocus,
|
||||
touchableHandleBlur,
|
||||
...TouchableMixinWithoutDefaultFocusAndBlur
|
||||
} = TouchableMixin;
|
||||
TouchableMixin.withoutDefaultFocusAndBlur = TouchableMixinWithoutDefaultFocusAndBlur;
|
||||
|
||||
const Touchable = {
|
||||
Mixin: TouchableMixin,
|
||||
TOUCH_TARGET_DEBUG: false, // Highlights all touchable targets. Toggle with Inspector.
|
||||
/**
|
||||
* Renders a debugging overlay to visualize touch target with hitSlop (might not work on Android).
|
||||
*/
|
||||
renderDebugView: ({
|
||||
color,
|
||||
hitSlop,
|
||||
}: {
|
||||
color: string | number,
|
||||
hitSlop: EdgeInsetsProp,
|
||||
...
|
||||
}): null | React.Node => {
|
||||
if (!Touchable.TOUCH_TARGET_DEBUG) {
|
||||
return null;
|
||||
}
|
||||
if (!__DEV__) {
|
||||
throw Error(
|
||||
'Touchable.TOUCH_TARGET_DEBUG should not be enabled in prod!',
|
||||
);
|
||||
}
|
||||
const debugHitSlopStyle = {};
|
||||
hitSlop = hitSlop || {top: 0, bottom: 0, left: 0, right: 0};
|
||||
for (const key in hitSlop) {
|
||||
debugHitSlopStyle[key] = -hitSlop[key];
|
||||
}
|
||||
const normalizedColor = normalizeColor(color);
|
||||
if (typeof normalizedColor !== 'number') {
|
||||
return null;
|
||||
}
|
||||
const hexColor =
|
||||
'#' + ('00000000' + normalizedColor.toString(16)).substr(-8);
|
||||
return (
|
||||
<View
|
||||
pointerEvents="none"
|
||||
style={[
|
||||
styles.debug,
|
||||
/* $FlowFixMe(>=0.111.0 site=react_native_fb) This comment suppresses
|
||||
* an error found when Flow v0.111 was deployed. To see the error,
|
||||
* delete this comment and run Flow. */
|
||||
{
|
||||
borderColor: hexColor.slice(0, -2) + '55', // More opaque
|
||||
backgroundColor: hexColor.slice(0, -2) + '0F', // Less opaque
|
||||
...debugHitSlopStyle,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
debug: {
|
||||
position: 'absolute',
|
||||
borderWidth: 1,
|
||||
borderStyle: 'dashed',
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = Touchable;
|
218
node_modules/react-native/Libraries/Components/Touchable/TouchableBounce.js
generated
vendored
Normal file
218
node_modules/react-native/Libraries/Components/Touchable/TouchableBounce.js
generated
vendored
Normal file
@ -0,0 +1,218 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its 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-local
|
||||
* @format
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import Pressability, {
|
||||
type PressabilityConfig,
|
||||
} from '../../Pressability/Pressability';
|
||||
import {PressabilityDebugView} from '../../Pressability/PressabilityDebug';
|
||||
import type {ViewStyleProp} from '../../StyleSheet/StyleSheet';
|
||||
import TVTouchable from './TVTouchable';
|
||||
import typeof TouchableWithoutFeedback from './TouchableWithoutFeedback';
|
||||
import {Animated, Platform} from 'react-native';
|
||||
import * as React from 'react';
|
||||
|
||||
type Props = $ReadOnly<{|
|
||||
...React.ElementConfig<TouchableWithoutFeedback>,
|
||||
|
||||
onPressAnimationComplete?: ?() => void,
|
||||
onPressWithCompletion?: ?(callback: () => void) => void,
|
||||
releaseBounciness?: ?number,
|
||||
releaseVelocity?: ?number,
|
||||
style?: ?ViewStyleProp,
|
||||
|
||||
hostRef: React.Ref<typeof Animated.View>,
|
||||
|}>;
|
||||
|
||||
type State = $ReadOnly<{|
|
||||
pressability: Pressability,
|
||||
scale: Animated.Value,
|
||||
|}>;
|
||||
|
||||
class TouchableBounce extends React.Component<Props, State> {
|
||||
_tvTouchable: ?TVTouchable;
|
||||
|
||||
state: State = {
|
||||
pressability: new Pressability(this._createPressabilityConfig()),
|
||||
scale: new Animated.Value(1),
|
||||
};
|
||||
|
||||
_createPressabilityConfig(): PressabilityConfig {
|
||||
return {
|
||||
cancelable: !this.props.rejectResponderTermination,
|
||||
disabled: this.props.disabled,
|
||||
hitSlop: this.props.hitSlop,
|
||||
delayLongPress: this.props.delayLongPress,
|
||||
delayPressIn: this.props.delayPressIn,
|
||||
delayPressOut: this.props.delayPressOut,
|
||||
minPressDuration: 0,
|
||||
pressRectOffset: this.props.pressRetentionOffset,
|
||||
android_disableSound: this.props.touchSoundDisabled,
|
||||
onBlur: event => {
|
||||
if (Platform.isTV) {
|
||||
this._bounceTo(1, 0.4, 0);
|
||||
}
|
||||
if (this.props.onBlur != null) {
|
||||
this.props.onBlur(event);
|
||||
}
|
||||
},
|
||||
onFocus: event => {
|
||||
if (Platform.isTV) {
|
||||
this._bounceTo(0.93, 0.1, 0);
|
||||
}
|
||||
if (this.props.onFocus != null) {
|
||||
this.props.onFocus(event);
|
||||
}
|
||||
},
|
||||
onLongPress: event => {
|
||||
if (this.props.onLongPress != null) {
|
||||
this.props.onLongPress(event);
|
||||
}
|
||||
},
|
||||
onPress: event => {
|
||||
const {onPressAnimationComplete, onPressWithCompletion} = this.props;
|
||||
const releaseBounciness = this.props.releaseBounciness ?? 10;
|
||||
const releaseVelocity = this.props.releaseVelocity ?? 10;
|
||||
|
||||
if (onPressWithCompletion != null) {
|
||||
onPressWithCompletion(() => {
|
||||
this.state.scale.setValue(0.93);
|
||||
this._bounceTo(
|
||||
1,
|
||||
releaseVelocity,
|
||||
releaseBounciness,
|
||||
onPressAnimationComplete,
|
||||
);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
this._bounceTo(
|
||||
1,
|
||||
releaseVelocity,
|
||||
releaseBounciness,
|
||||
onPressAnimationComplete,
|
||||
);
|
||||
if (this.props.onPress != null) {
|
||||
this.props.onPress(event);
|
||||
}
|
||||
},
|
||||
onPressIn: event => {
|
||||
this._bounceTo(0.93, 0.1, 0);
|
||||
if (this.props.onPressIn != null) {
|
||||
this.props.onPressIn(event);
|
||||
}
|
||||
},
|
||||
onPressOut: event => {
|
||||
this._bounceTo(1, 0.4, 0);
|
||||
if (this.props.onPressOut != null) {
|
||||
this.props.onPressOut(event);
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
_bounceTo(
|
||||
toValue: number,
|
||||
velocity: number,
|
||||
bounciness: number,
|
||||
callback?: ?() => void,
|
||||
) {
|
||||
Animated.spring(this.state.scale, {
|
||||
toValue,
|
||||
velocity,
|
||||
bounciness,
|
||||
useNativeDriver: true,
|
||||
}).start(callback);
|
||||
}
|
||||
|
||||
render(): React.Node {
|
||||
// BACKWARD-COMPATIBILITY: Focus and blur events were never supported before
|
||||
// adopting `Pressability`, so preserve that behavior.
|
||||
const {
|
||||
onBlur,
|
||||
onFocus,
|
||||
...eventHandlersWithoutBlurAndFocus
|
||||
} = this.state.pressability.getEventHandlers();
|
||||
|
||||
return (
|
||||
<Animated.View
|
||||
style={[{transform: [{scale: this.state.scale}]}, this.props.style]}
|
||||
accessible={this.props.accessible !== false}
|
||||
accessibilityLabel={this.props.accessibilityLabel}
|
||||
accessibilityHint={this.props.accessibilityHint}
|
||||
accessibilityRole={this.props.accessibilityRole}
|
||||
accessibilityState={this.props.accessibilityState}
|
||||
accessibilityActions={this.props.accessibilityActions}
|
||||
onAccessibilityAction={this.props.onAccessibilityAction}
|
||||
accessibilityValue={this.props.accessibilityValue}
|
||||
importantForAccessibility={this.props.importantForAccessibility}
|
||||
accessibilityLiveRegion={this.props.accessibilityLiveRegion}
|
||||
accessibilityViewIsModal={this.props.accessibilityViewIsModal}
|
||||
accessibilityElementsHidden={this.props.accessibilityElementsHidden}
|
||||
nativeID={this.props.nativeID}
|
||||
testID={this.props.testID}
|
||||
hitSlop={this.props.hitSlop}
|
||||
focusable={
|
||||
this.props.focusable !== false &&
|
||||
this.props.onPress !== undefined &&
|
||||
!this.props.disabled
|
||||
}
|
||||
ref={this.props.hostRef}
|
||||
{...eventHandlersWithoutBlurAndFocus}>
|
||||
{this.props.children}
|
||||
{__DEV__ ? (
|
||||
<PressabilityDebugView color="orange" hitSlop={this.props.hitSlop} />
|
||||
) : null}
|
||||
</Animated.View>
|
||||
);
|
||||
}
|
||||
|
||||
componentDidMount(): void {
|
||||
if (Platform.isTV) {
|
||||
this._tvTouchable = new TVTouchable(this, {
|
||||
getDisabled: () => this.props.disabled === true,
|
||||
onBlur: event => {
|
||||
if (this.props.onBlur != null) {
|
||||
this.props.onBlur(event);
|
||||
}
|
||||
},
|
||||
onFocus: event => {
|
||||
if (this.props.onFocus != null) {
|
||||
this.props.onFocus(event);
|
||||
}
|
||||
},
|
||||
onPress: event => {
|
||||
if (this.props.onPress != null) {
|
||||
this.props.onPress(event);
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps: Props, prevState: State) {
|
||||
this.state.pressability.configure(this._createPressabilityConfig());
|
||||
}
|
||||
|
||||
componentWillUnmount(): void {
|
||||
if (Platform.isTV) {
|
||||
if (this._tvTouchable != null) {
|
||||
this._tvTouchable.destroy();
|
||||
}
|
||||
}
|
||||
this.state.pressability.reset();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = (React.forwardRef((props, hostRef) => (
|
||||
<TouchableBounce {...props} hostRef={hostRef} />
|
||||
)): React.ComponentType<$ReadOnly<$Diff<Props, {|hostRef: mixed|}>>>);
|
384
node_modules/react-native/Libraries/Components/Touchable/TouchableHighlight.js
generated
vendored
Normal file
384
node_modules/react-native/Libraries/Components/Touchable/TouchableHighlight.js
generated
vendored
Normal file
@ -0,0 +1,384 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its 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-local
|
||||
* @format
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import Pressability, {
|
||||
type PressabilityConfig,
|
||||
} from '../../Pressability/Pressability';
|
||||
import {PressabilityDebugView} from '../../Pressability/PressabilityDebug';
|
||||
import StyleSheet, {type ViewStyleProp} from '../../StyleSheet/StyleSheet';
|
||||
import type {ColorValue} from '../../StyleSheet/StyleSheetTypes';
|
||||
import TVTouchable from './TVTouchable';
|
||||
import typeof TouchableWithoutFeedback from './TouchableWithoutFeedback';
|
||||
import Platform from '../../Utilities/Platform';
|
||||
import View from '../../Components/View/View';
|
||||
import * as React from 'react';
|
||||
|
||||
type AndroidProps = $ReadOnly<{|
|
||||
nextFocusDown?: ?number,
|
||||
nextFocusForward?: ?number,
|
||||
nextFocusLeft?: ?number,
|
||||
nextFocusRight?: ?number,
|
||||
nextFocusUp?: ?number,
|
||||
|}>;
|
||||
|
||||
type IOSProps = $ReadOnly<{|
|
||||
hasTVPreferredFocus?: ?boolean,
|
||||
|}>;
|
||||
|
||||
type Props = $ReadOnly<{|
|
||||
...React.ElementConfig<TouchableWithoutFeedback>,
|
||||
...AndroidProps,
|
||||
...IOSProps,
|
||||
|
||||
activeOpacity?: ?number,
|
||||
underlayColor?: ?ColorValue,
|
||||
style?: ?ViewStyleProp,
|
||||
onShowUnderlay?: ?() => void,
|
||||
onHideUnderlay?: ?() => void,
|
||||
testOnly_pressed?: ?boolean,
|
||||
|
||||
hostRef: React.Ref<typeof View>,
|
||||
|}>;
|
||||
|
||||
type ExtraStyles = $ReadOnly<{|
|
||||
child: ViewStyleProp,
|
||||
underlay: ViewStyleProp,
|
||||
|}>;
|
||||
|
||||
type State = $ReadOnly<{|
|
||||
pressability: Pressability,
|
||||
extraStyles: ?ExtraStyles,
|
||||
|}>;
|
||||
|
||||
/**
|
||||
* A wrapper for making views respond properly to touches.
|
||||
* On press down, the opacity of the wrapped view is decreased, which allows
|
||||
* the underlay color to show through, darkening or tinting the view.
|
||||
*
|
||||
* The underlay comes from wrapping the child in a new View, which can affect
|
||||
* layout, and sometimes cause unwanted visual artifacts if not used correctly,
|
||||
* for example if the backgroundColor of the wrapped view isn't explicitly set
|
||||
* to an opaque color.
|
||||
*
|
||||
* TouchableHighlight must have one child (not zero or more than one).
|
||||
* If you wish to have several child components, wrap them in a View.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ```
|
||||
* renderButton: function() {
|
||||
* return (
|
||||
* <TouchableHighlight onPress={this._onPressButton}>
|
||||
* <Image
|
||||
* style={styles.button}
|
||||
* source={require('./myButton.png')}
|
||||
* />
|
||||
* </TouchableHighlight>
|
||||
* );
|
||||
* },
|
||||
* ```
|
||||
*
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* ```ReactNativeWebPlayer
|
||||
* import React, { Component } from 'react'
|
||||
* import {
|
||||
* AppRegistry,
|
||||
* StyleSheet,
|
||||
* TouchableHighlight,
|
||||
* Text,
|
||||
* View,
|
||||
* } from 'react-native'
|
||||
*
|
||||
* class App extends Component {
|
||||
* constructor(props) {
|
||||
* super(props)
|
||||
* this.state = { count: 0 }
|
||||
* }
|
||||
*
|
||||
* onPress = () => {
|
||||
* this.setState({
|
||||
* count: this.state.count+1
|
||||
* })
|
||||
* }
|
||||
*
|
||||
* render() {
|
||||
* return (
|
||||
* <View style={styles.container}>
|
||||
* <TouchableHighlight
|
||||
* style={styles.button}
|
||||
* onPress={this.onPress}
|
||||
* >
|
||||
* <Text> Touch Here </Text>
|
||||
* </TouchableHighlight>
|
||||
* <View style={[styles.countContainer]}>
|
||||
* <Text style={[styles.countText]}>
|
||||
* { this.state.count !== 0 ? this.state.count: null}
|
||||
* </Text>
|
||||
* </View>
|
||||
* </View>
|
||||
* )
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* const styles = StyleSheet.create({
|
||||
* container: {
|
||||
* flex: 1,
|
||||
* justifyContent: 'center',
|
||||
* paddingHorizontal: 10
|
||||
* },
|
||||
* button: {
|
||||
* alignItems: 'center',
|
||||
* backgroundColor: '#DDDDDD',
|
||||
* padding: 10
|
||||
* },
|
||||
* countContainer: {
|
||||
* alignItems: 'center',
|
||||
* padding: 10
|
||||
* },
|
||||
* countText: {
|
||||
* color: '#FF00FF'
|
||||
* }
|
||||
* })
|
||||
*
|
||||
* AppRegistry.registerComponent('App', () => App)
|
||||
* ```
|
||||
*
|
||||
*/
|
||||
class TouchableHighlight extends React.Component<Props, State> {
|
||||
_hideTimeout: ?TimeoutID;
|
||||
_isMounted: boolean = false;
|
||||
_tvTouchable: ?TVTouchable;
|
||||
|
||||
state: State = {
|
||||
pressability: new Pressability(this._createPressabilityConfig()),
|
||||
extraStyles:
|
||||
this.props.testOnly_pressed === true ? this._createExtraStyles() : null,
|
||||
};
|
||||
|
||||
_createPressabilityConfig(): PressabilityConfig {
|
||||
return {
|
||||
cancelable: !this.props.rejectResponderTermination,
|
||||
disabled: this.props.disabled,
|
||||
hitSlop: this.props.hitSlop,
|
||||
delayLongPress: this.props.delayLongPress,
|
||||
delayPressIn: this.props.delayPressIn,
|
||||
delayPressOut: this.props.delayPressOut,
|
||||
minPressDuration: 0,
|
||||
pressRectOffset: this.props.pressRetentionOffset,
|
||||
android_disableSound: this.props.touchSoundDisabled,
|
||||
onBlur: event => {
|
||||
if (Platform.isTV) {
|
||||
this._hideUnderlay();
|
||||
}
|
||||
if (this.props.onBlur != null) {
|
||||
this.props.onBlur(event);
|
||||
}
|
||||
},
|
||||
onFocus: event => {
|
||||
if (Platform.isTV) {
|
||||
this._showUnderlay();
|
||||
}
|
||||
if (this.props.onFocus != null) {
|
||||
this.props.onFocus(event);
|
||||
}
|
||||
},
|
||||
onLongPress: event => {
|
||||
if (this.props.onLongPress != null) {
|
||||
this.props.onLongPress(event);
|
||||
}
|
||||
},
|
||||
onPress: event => {
|
||||
if (this._hideTimeout != null) {
|
||||
clearTimeout(this._hideTimeout);
|
||||
}
|
||||
if (!Platform.isTV) {
|
||||
this._showUnderlay();
|
||||
this._hideTimeout = setTimeout(() => {
|
||||
this._hideUnderlay();
|
||||
}, this.props.delayPressOut ?? 0);
|
||||
}
|
||||
if (this.props.onPress != null) {
|
||||
this.props.onPress(event);
|
||||
}
|
||||
},
|
||||
onPressIn: event => {
|
||||
if (this._hideTimeout != null) {
|
||||
clearTimeout(this._hideTimeout);
|
||||
this._hideTimeout = null;
|
||||
}
|
||||
this._showUnderlay();
|
||||
if (this.props.onPressIn != null) {
|
||||
this.props.onPressIn(event);
|
||||
}
|
||||
},
|
||||
onPressOut: event => {
|
||||
if (this._hideTimeout == null) {
|
||||
this._hideUnderlay();
|
||||
}
|
||||
if (this.props.onPressOut != null) {
|
||||
this.props.onPressOut(event);
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
_createExtraStyles(): ExtraStyles {
|
||||
return {
|
||||
child: {opacity: this.props.activeOpacity ?? 0.85},
|
||||
underlay: {
|
||||
backgroundColor:
|
||||
this.props.underlayColor === undefined
|
||||
? 'black'
|
||||
: this.props.underlayColor,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
_showUnderlay(): void {
|
||||
if (!this._isMounted || !this._hasPressHandler()) {
|
||||
return;
|
||||
}
|
||||
this.setState({extraStyles: this._createExtraStyles()});
|
||||
if (this.props.onShowUnderlay != null) {
|
||||
this.props.onShowUnderlay();
|
||||
}
|
||||
}
|
||||
|
||||
_hideUnderlay(): void {
|
||||
if (this._hideTimeout != null) {
|
||||
clearTimeout(this._hideTimeout);
|
||||
this._hideTimeout = null;
|
||||
}
|
||||
if (this.props.testOnly_pressed === true) {
|
||||
return;
|
||||
}
|
||||
if (this._hasPressHandler()) {
|
||||
this.setState({extraStyles: null});
|
||||
if (this.props.onHideUnderlay != null) {
|
||||
this.props.onHideUnderlay();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_hasPressHandler(): boolean {
|
||||
return (
|
||||
this.props.onPress != null ||
|
||||
this.props.onPressIn != null ||
|
||||
this.props.onPressOut != null ||
|
||||
this.props.onLongPress != null
|
||||
);
|
||||
}
|
||||
|
||||
render(): React.Node {
|
||||
const child = React.Children.only(this.props.children);
|
||||
|
||||
// BACKWARD-COMPATIBILITY: Focus and blur events were never supported before
|
||||
// adopting `Pressability`, so preserve that behavior.
|
||||
const {
|
||||
onBlur,
|
||||
onFocus,
|
||||
...eventHandlersWithoutBlurAndFocus
|
||||
} = this.state.pressability.getEventHandlers();
|
||||
|
||||
return (
|
||||
<View
|
||||
accessible={this.props.accessible !== false}
|
||||
accessibilityLabel={this.props.accessibilityLabel}
|
||||
accessibilityHint={this.props.accessibilityHint}
|
||||
accessibilityRole={this.props.accessibilityRole}
|
||||
accessibilityState={this.props.accessibilityState}
|
||||
accessibilityValue={this.props.accessibilityValue}
|
||||
accessibilityActions={this.props.accessibilityActions}
|
||||
onAccessibilityAction={this.props.onAccessibilityAction}
|
||||
importantForAccessibility={this.props.importantForAccessibility}
|
||||
accessibilityLiveRegion={this.props.accessibilityLiveRegion}
|
||||
accessibilityViewIsModal={this.props.accessibilityViewIsModal}
|
||||
accessibilityElementsHidden={this.props.accessibilityElementsHidden}
|
||||
style={StyleSheet.compose(
|
||||
this.props.style,
|
||||
this.state.extraStyles?.underlay,
|
||||
)}
|
||||
onLayout={this.props.onLayout}
|
||||
hitSlop={this.props.hitSlop}
|
||||
hasTVPreferredFocus={this.props.hasTVPreferredFocus}
|
||||
nextFocusDown={this.props.nextFocusDown}
|
||||
nextFocusForward={this.props.nextFocusForward}
|
||||
nextFocusLeft={this.props.nextFocusLeft}
|
||||
nextFocusRight={this.props.nextFocusRight}
|
||||
nextFocusUp={this.props.nextFocusUp}
|
||||
focusable={
|
||||
this.props.focusable !== false && this.props.onPress !== undefined
|
||||
}
|
||||
nativeID={this.props.nativeID}
|
||||
testID={this.props.testID}
|
||||
ref={this.props.hostRef}
|
||||
{...eventHandlersWithoutBlurAndFocus}>
|
||||
{React.cloneElement(child, {
|
||||
style: StyleSheet.compose(
|
||||
child.props.style,
|
||||
this.state.extraStyles?.child,
|
||||
),
|
||||
})}
|
||||
{__DEV__ ? (
|
||||
<PressabilityDebugView color="green" hitSlop={this.props.hitSlop} />
|
||||
) : null}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
componentDidMount(): void {
|
||||
this._isMounted = true;
|
||||
if (Platform.isTV) {
|
||||
this._tvTouchable = new TVTouchable(this, {
|
||||
getDisabled: () => this.props.disabled === true,
|
||||
onBlur: event => {
|
||||
if (this.props.onBlur != null) {
|
||||
this.props.onBlur(event);
|
||||
}
|
||||
},
|
||||
onFocus: event => {
|
||||
if (this.props.onFocus != null) {
|
||||
this.props.onFocus(event);
|
||||
}
|
||||
},
|
||||
onPress: event => {
|
||||
if (this.props.onPress != null) {
|
||||
this.props.onPress(event);
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps: Props, prevState: State) {
|
||||
this.state.pressability.configure(this._createPressabilityConfig());
|
||||
}
|
||||
|
||||
componentWillUnmount(): void {
|
||||
this._isMounted = false;
|
||||
if (this._hideTimeout != null) {
|
||||
clearTimeout(this._hideTimeout);
|
||||
}
|
||||
if (Platform.isTV) {
|
||||
if (this._tvTouchable != null) {
|
||||
this._tvTouchable.destroy();
|
||||
}
|
||||
}
|
||||
this.state.pressability.reset();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = (React.forwardRef((props, hostRef) => (
|
||||
<TouchableHighlight {...props} hostRef={hostRef} />
|
||||
)): React.ComponentType<$ReadOnly<$Diff<Props, {|hostRef: mixed|}>>>);
|
348
node_modules/react-native/Libraries/Components/Touchable/TouchableNativeFeedback.js
generated
vendored
Normal file
348
node_modules/react-native/Libraries/Components/Touchable/TouchableNativeFeedback.js
generated
vendored
Normal file
@ -0,0 +1,348 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its 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-local
|
||||
* @format
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import Pressability, {
|
||||
type PressabilityConfig,
|
||||
} from '../../Pressability/Pressability';
|
||||
import {PressabilityDebugView} from '../../Pressability/PressabilityDebug';
|
||||
import TVTouchable from './TVTouchable';
|
||||
import typeof TouchableWithoutFeedback from './TouchableWithoutFeedback';
|
||||
import {Commands} from 'react-native/Libraries/Components/View/ViewNativeComponent';
|
||||
import ReactNative from 'react-native/Libraries/Renderer/shims/ReactNative';
|
||||
import type {PressEvent} from 'react-native/Libraries/Types/CoreEventTypes';
|
||||
import Platform from '../../Utilities/Platform';
|
||||
import View from '../../Components/View/View';
|
||||
import processColor from '../../StyleSheet/processColor';
|
||||
import * as React from 'react';
|
||||
import invariant from 'invariant';
|
||||
|
||||
type Props = $ReadOnly<{|
|
||||
...React.ElementConfig<TouchableWithoutFeedback>,
|
||||
|
||||
/**
|
||||
* Determines the type of background drawable that's going to be used to
|
||||
* display feedback. It takes an object with `type` property and extra data
|
||||
* depending on the `type`. It's recommended to use one of the static
|
||||
* methods to generate that dictionary.
|
||||
*/
|
||||
background?: ?(
|
||||
| $ReadOnly<{|
|
||||
type: 'ThemeAttrAndroid',
|
||||
attribute:
|
||||
| 'selectableItemBackground'
|
||||
| 'selectableItemBackgroundBorderless',
|
||||
rippleRadius: ?number,
|
||||
|}>
|
||||
| $ReadOnly<{|
|
||||
type: 'RippleAndroid',
|
||||
color: ?number,
|
||||
borderless: boolean,
|
||||
rippleRadius: ?number,
|
||||
|}>
|
||||
),
|
||||
|
||||
/**
|
||||
* TV preferred focus (see documentation for the View component).
|
||||
*/
|
||||
hasTVPreferredFocus?: ?boolean,
|
||||
|
||||
/**
|
||||
* TV next focus down (see documentation for the View component).
|
||||
*/
|
||||
nextFocusDown?: ?number,
|
||||
|
||||
/**
|
||||
* TV next focus forward (see documentation for the View component).
|
||||
*/
|
||||
nextFocusForward?: ?number,
|
||||
|
||||
/**
|
||||
* TV next focus left (see documentation for the View component).
|
||||
*/
|
||||
nextFocusLeft?: ?number,
|
||||
|
||||
/**
|
||||
* TV next focus right (see documentation for the View component).
|
||||
*/
|
||||
nextFocusRight?: ?number,
|
||||
|
||||
/**
|
||||
* TV next focus up (see documentation for the View component).
|
||||
*/
|
||||
nextFocusUp?: ?number,
|
||||
|
||||
/**
|
||||
* Set to true to add the ripple effect to the foreground of the view, instead
|
||||
* of the background. This is useful if one of your child views has a
|
||||
* background of its own, or you're e.g. displaying images, and you don't want
|
||||
* the ripple to be covered by them.
|
||||
*
|
||||
* Check TouchableNativeFeedback.canUseNativeForeground() first, as this is
|
||||
* only available on Android 6.0 and above. If you try to use this on older
|
||||
* versions, this will fallback to background.
|
||||
*/
|
||||
useForeground?: ?boolean,
|
||||
|}>;
|
||||
|
||||
type State = $ReadOnly<{|
|
||||
pressability: Pressability,
|
||||
|}>;
|
||||
|
||||
class TouchableNativeFeedback extends React.Component<Props, State> {
|
||||
/**
|
||||
* Creates a value for the `background` prop that uses the Android theme's
|
||||
* default background for selectable elements.
|
||||
*/
|
||||
static SelectableBackground: (
|
||||
rippleRadius: ?number,
|
||||
) => $ReadOnly<{|
|
||||
attribute: 'selectableItemBackground',
|
||||
type: 'ThemeAttrAndroid',
|
||||
rippleRadius: ?number,
|
||||
|}> = (rippleRadius: ?number) => ({
|
||||
type: 'ThemeAttrAndroid',
|
||||
attribute: 'selectableItemBackground',
|
||||
rippleRadius,
|
||||
});
|
||||
|
||||
/**
|
||||
* Creates a value for the `background` prop that uses the Android theme's
|
||||
* default background for borderless selectable elements. Requires API 21+.
|
||||
*/
|
||||
static SelectableBackgroundBorderless: (
|
||||
rippleRadius: ?number,
|
||||
) => $ReadOnly<{|
|
||||
attribute: 'selectableItemBackgroundBorderless',
|
||||
type: 'ThemeAttrAndroid',
|
||||
rippleRadius: ?number,
|
||||
|}> = (rippleRadius: ?number) => ({
|
||||
type: 'ThemeAttrAndroid',
|
||||
attribute: 'selectableItemBackgroundBorderless',
|
||||
rippleRadius,
|
||||
});
|
||||
|
||||
/**
|
||||
* Creates a value for the `background` prop that uses the Android ripple with
|
||||
* the supplied color. If `borderless` is true, the ripple will render outside
|
||||
* of the view bounds. Requires API 21+.
|
||||
*/
|
||||
static Ripple: (
|
||||
color: string,
|
||||
borderless: boolean,
|
||||
rippleRadius: ?number,
|
||||
) => $ReadOnly<{|
|
||||
borderless: boolean,
|
||||
color: ?number,
|
||||
rippleRadius: ?number,
|
||||
type: 'RippleAndroid',
|
||||
|}> = (color: string, borderless: boolean, rippleRadius: ?number) => {
|
||||
const processedColor = processColor(color);
|
||||
invariant(
|
||||
processedColor == null || typeof processedColor === 'number',
|
||||
'Unexpected color given for Ripple color',
|
||||
);
|
||||
return {
|
||||
type: 'RippleAndroid',
|
||||
color: processedColor,
|
||||
borderless,
|
||||
rippleRadius,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Whether `useForeground` is supported.
|
||||
*/
|
||||
static canUseNativeForeground: () => boolean = () =>
|
||||
Platform.OS === 'android' && Platform.Version >= 23;
|
||||
|
||||
_tvTouchable: ?TVTouchable;
|
||||
|
||||
state: State = {
|
||||
pressability: new Pressability(this._createPressabilityConfig()),
|
||||
};
|
||||
|
||||
_createPressabilityConfig(): PressabilityConfig {
|
||||
return {
|
||||
cancelable: !this.props.rejectResponderTermination,
|
||||
disabled: this.props.disabled,
|
||||
hitSlop: this.props.hitSlop,
|
||||
delayLongPress: this.props.delayLongPress,
|
||||
delayPressIn: this.props.delayPressIn,
|
||||
delayPressOut: this.props.delayPressOut,
|
||||
minPressDuration: 0,
|
||||
pressRectOffset: this.props.pressRetentionOffset,
|
||||
android_disableSound: this.props.touchSoundDisabled,
|
||||
onLongPress: this.props.onLongPress,
|
||||
onPress: this.props.onPress,
|
||||
onPressIn: event => {
|
||||
if (Platform.OS === 'android') {
|
||||
this._dispatchPressedStateChange(true);
|
||||
this._dispatchHotspotUpdate(event);
|
||||
}
|
||||
if (this.props.onPressIn != null) {
|
||||
this.props.onPressIn(event);
|
||||
}
|
||||
},
|
||||
onPressMove: event => {
|
||||
if (Platform.OS === 'android') {
|
||||
this._dispatchHotspotUpdate(event);
|
||||
}
|
||||
},
|
||||
onPressOut: event => {
|
||||
if (Platform.OS === 'android') {
|
||||
this._dispatchPressedStateChange(false);
|
||||
}
|
||||
if (this.props.onPressOut != null) {
|
||||
this.props.onPressOut(event);
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
_dispatchPressedStateChange(pressed: boolean): void {
|
||||
if (Platform.OS === 'android') {
|
||||
const hostComponentRef = ReactNative.findHostInstance_DEPRECATED(this);
|
||||
if (hostComponentRef == null) {
|
||||
console.warn(
|
||||
'Touchable: Unable to find HostComponent instance. ' +
|
||||
'Has your Touchable component been unmounted?',
|
||||
);
|
||||
} else {
|
||||
Commands.setPressed(hostComponentRef, pressed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_dispatchHotspotUpdate(event: PressEvent): void {
|
||||
if (Platform.OS === 'android') {
|
||||
const {locationX, locationY} = event.nativeEvent;
|
||||
const hostComponentRef = ReactNative.findHostInstance_DEPRECATED(this);
|
||||
if (hostComponentRef == null) {
|
||||
console.warn(
|
||||
'Touchable: Unable to find HostComponent instance. ' +
|
||||
'Has your Touchable component been unmounted?',
|
||||
);
|
||||
} else {
|
||||
Commands.hotspotUpdate(
|
||||
hostComponentRef,
|
||||
locationX ?? 0,
|
||||
locationY ?? 0,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
render(): React.Node {
|
||||
const element = React.Children.only(this.props.children);
|
||||
const children = [element.props.children];
|
||||
if (__DEV__) {
|
||||
if (element.type === View) {
|
||||
children.push(
|
||||
<PressabilityDebugView color="brown" hitSlop={this.props.hitSlop} />,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// BACKWARD-COMPATIBILITY: Focus and blur events were never supported before
|
||||
// adopting `Pressability`, so preserve that behavior.
|
||||
const {
|
||||
onBlur,
|
||||
onFocus,
|
||||
...eventHandlersWithoutBlurAndFocus
|
||||
} = this.state.pressability.getEventHandlers();
|
||||
|
||||
return React.cloneElement(
|
||||
element,
|
||||
{
|
||||
...eventHandlersWithoutBlurAndFocus,
|
||||
...getBackgroundProp(
|
||||
this.props.background === undefined
|
||||
? TouchableNativeFeedback.SelectableBackground()
|
||||
: this.props.background,
|
||||
this.props.useForeground === true,
|
||||
),
|
||||
accessible: this.props.accessible !== false,
|
||||
accessibilityLabel: this.props.accessibilityLabel,
|
||||
accessibilityRole: this.props.accessibilityRole,
|
||||
accessibilityState: this.props.accessibilityState,
|
||||
accessibilityActions: this.props.accessibilityActions,
|
||||
onAccessibilityAction: this.props.onAccessibilityAction,
|
||||
accessibilityValue: this.props.accessibilityValue,
|
||||
importantForAccessibility: this.props.importantForAccessibility,
|
||||
accessibilityLiveRegion: this.props.accessibilityLiveRegion,
|
||||
accessibilityViewIsModal: this.props.accessibilityViewIsModal,
|
||||
accessibilityElementsHidden: this.props.accessibilityElementsHidden,
|
||||
hasTVPreferredFocus: this.props.hasTVPreferredFocus,
|
||||
hitSlop: this.props.hitSlop,
|
||||
focusable:
|
||||
this.props.focusable !== false &&
|
||||
this.props.onPress !== undefined &&
|
||||
!this.props.disabled,
|
||||
nativeID: this.props.nativeID,
|
||||
nextFocusDown: this.props.nextFocusDown,
|
||||
nextFocusForward: this.props.nextFocusForward,
|
||||
nextFocusLeft: this.props.nextFocusLeft,
|
||||
nextFocusRight: this.props.nextFocusRight,
|
||||
nextFocusUp: this.props.nextFocusUp,
|
||||
onLayout: this.props.onLayout,
|
||||
testID: this.props.testID,
|
||||
},
|
||||
...children,
|
||||
);
|
||||
}
|
||||
|
||||
componentDidMount(): void {
|
||||
if (Platform.isTV) {
|
||||
this._tvTouchable = new TVTouchable(this, {
|
||||
getDisabled: () => this.props.disabled === true,
|
||||
onBlur: event => {
|
||||
if (this.props.onBlur != null) {
|
||||
this.props.onBlur(event);
|
||||
}
|
||||
},
|
||||
onFocus: event => {
|
||||
if (this.props.onFocus != null) {
|
||||
this.props.onFocus(event);
|
||||
}
|
||||
},
|
||||
onPress: event => {
|
||||
if (this.props.onPress != null) {
|
||||
this.props.onPress(event);
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps: Props, prevState: State) {
|
||||
this.state.pressability.configure(this._createPressabilityConfig());
|
||||
}
|
||||
|
||||
componentWillUnmount(): void {
|
||||
if (Platform.isTV) {
|
||||
if (this._tvTouchable != null) {
|
||||
this._tvTouchable.destroy();
|
||||
}
|
||||
}
|
||||
this.state.pressability.reset();
|
||||
}
|
||||
}
|
||||
|
||||
const getBackgroundProp =
|
||||
Platform.OS === 'android'
|
||||
? (background, useForeground) =>
|
||||
useForeground && TouchableNativeFeedback.canUseNativeForeground()
|
||||
? {nativeForegroundAndroid: background}
|
||||
: {nativeBackgroundAndroid: background}
|
||||
: (background, useForeground) => null;
|
||||
|
||||
module.exports = TouchableNativeFeedback;
|
303
node_modules/react-native/Libraries/Components/Touchable/TouchableOpacity.js
generated
vendored
Normal file
303
node_modules/react-native/Libraries/Components/Touchable/TouchableOpacity.js
generated
vendored
Normal file
@ -0,0 +1,303 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its 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-local
|
||||
* @format
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import Pressability, {
|
||||
type PressabilityConfig,
|
||||
} from '../../Pressability/Pressability';
|
||||
import {PressabilityDebugView} from '../../Pressability/PressabilityDebug';
|
||||
import TVTouchable from './TVTouchable';
|
||||
import typeof TouchableWithoutFeedback from './TouchableWithoutFeedback';
|
||||
import Animated from 'react-native/Libraries/Animated/src/Animated';
|
||||
import Easing from 'react-native/Libraries/Animated/src/Easing';
|
||||
import type {ViewStyleProp} from 'react-native/Libraries/StyleSheet/StyleSheet';
|
||||
import flattenStyle from 'react-native/Libraries/StyleSheet/flattenStyle';
|
||||
import Platform from '../../Utilities/Platform';
|
||||
import * as React from 'react';
|
||||
|
||||
type TVProps = $ReadOnly<{|
|
||||
hasTVPreferredFocus?: ?boolean,
|
||||
nextFocusDown?: ?number,
|
||||
nextFocusForward?: ?number,
|
||||
nextFocusLeft?: ?number,
|
||||
nextFocusRight?: ?number,
|
||||
nextFocusUp?: ?number,
|
||||
|}>;
|
||||
|
||||
type Props = $ReadOnly<{|
|
||||
...React.ElementConfig<TouchableWithoutFeedback>,
|
||||
...TVProps,
|
||||
|
||||
activeOpacity?: ?number,
|
||||
style?: ?ViewStyleProp,
|
||||
|
||||
hostRef: React.Ref<typeof Animated.View>,
|
||||
|}>;
|
||||
|
||||
type State = $ReadOnly<{|
|
||||
anim: Animated.Value,
|
||||
pressability: Pressability,
|
||||
|}>;
|
||||
|
||||
/**
|
||||
* A wrapper for making views respond properly to touches.
|
||||
* On press down, the opacity of the wrapped view is decreased, dimming it.
|
||||
*
|
||||
* Opacity is controlled by wrapping the children in an Animated.View, which is
|
||||
* added to the view hierarchy. Be aware that this can affect layout.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ```
|
||||
* renderButton: function() {
|
||||
* return (
|
||||
* <TouchableOpacity onPress={this._onPressButton}>
|
||||
* <Image
|
||||
* style={styles.button}
|
||||
* source={require('./myButton.png')}
|
||||
* />
|
||||
* </TouchableOpacity>
|
||||
* );
|
||||
* },
|
||||
* ```
|
||||
* ### Example
|
||||
*
|
||||
* ```ReactNativeWebPlayer
|
||||
* import React, { Component } from 'react'
|
||||
* import {
|
||||
* AppRegistry,
|
||||
* StyleSheet,
|
||||
* TouchableOpacity,
|
||||
* Text,
|
||||
* View,
|
||||
* } from 'react-native'
|
||||
*
|
||||
* class App extends Component {
|
||||
* state = { count: 0 }
|
||||
*
|
||||
* onPress = () => {
|
||||
* this.setState(state => ({
|
||||
* count: state.count + 1
|
||||
* }));
|
||||
* };
|
||||
*
|
||||
* render() {
|
||||
* return (
|
||||
* <View style={styles.container}>
|
||||
* <TouchableOpacity
|
||||
* style={styles.button}
|
||||
* onPress={this.onPress}>
|
||||
* <Text> Touch Here </Text>
|
||||
* </TouchableOpacity>
|
||||
* <View style={[styles.countContainer]}>
|
||||
* <Text style={[styles.countText]}>
|
||||
* { this.state.count !== 0 ? this.state.count: null}
|
||||
* </Text>
|
||||
* </View>
|
||||
* </View>
|
||||
* )
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* const styles = StyleSheet.create({
|
||||
* container: {
|
||||
* flex: 1,
|
||||
* justifyContent: 'center',
|
||||
* paddingHorizontal: 10
|
||||
* },
|
||||
* button: {
|
||||
* alignItems: 'center',
|
||||
* backgroundColor: '#DDDDDD',
|
||||
* padding: 10
|
||||
* },
|
||||
* countContainer: {
|
||||
* alignItems: 'center',
|
||||
* padding: 10
|
||||
* },
|
||||
* countText: {
|
||||
* color: '#FF00FF'
|
||||
* }
|
||||
* })
|
||||
*
|
||||
* AppRegistry.registerComponent('App', () => App)
|
||||
* ```
|
||||
*
|
||||
*/
|
||||
class TouchableOpacity extends React.Component<Props, State> {
|
||||
_tvTouchable: ?TVTouchable;
|
||||
|
||||
state: State = {
|
||||
anim: new Animated.Value(this._getChildStyleOpacityWithDefault()),
|
||||
pressability: new Pressability(this._createPressabilityConfig()),
|
||||
};
|
||||
|
||||
_createPressabilityConfig(): PressabilityConfig {
|
||||
return {
|
||||
cancelable: !this.props.rejectResponderTermination,
|
||||
disabled: this.props.disabled,
|
||||
hitSlop: this.props.hitSlop,
|
||||
delayLongPress: this.props.delayLongPress,
|
||||
delayPressIn: this.props.delayPressIn,
|
||||
delayPressOut: this.props.delayPressOut,
|
||||
minPressDuration: 0,
|
||||
pressRectOffset: this.props.pressRetentionOffset,
|
||||
onBlur: event => {
|
||||
if (Platform.isTV) {
|
||||
this._opacityInactive(250);
|
||||
}
|
||||
if (this.props.onBlur != null) {
|
||||
this.props.onBlur(event);
|
||||
}
|
||||
},
|
||||
onFocus: event => {
|
||||
if (Platform.isTV) {
|
||||
this._opacityActive(150);
|
||||
}
|
||||
if (this.props.onFocus != null) {
|
||||
this.props.onFocus(event);
|
||||
}
|
||||
},
|
||||
onLongPress: this.props.onLongPress,
|
||||
onPress: this.props.onPress,
|
||||
onPressIn: event => {
|
||||
this._opacityActive(
|
||||
event.dispatchConfig.registrationName === 'onResponderGrant'
|
||||
? 0
|
||||
: 150,
|
||||
);
|
||||
if (this.props.onPressIn != null) {
|
||||
this.props.onPressIn(event);
|
||||
}
|
||||
},
|
||||
onPressOut: event => {
|
||||
this._opacityInactive(250);
|
||||
if (this.props.onPressOut != null) {
|
||||
this.props.onPressOut(event);
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Animate the touchable to a new opacity.
|
||||
*/
|
||||
_setOpacityTo(toValue: number, duration: number): void {
|
||||
Animated.timing(this.state.anim, {
|
||||
toValue,
|
||||
duration,
|
||||
easing: Easing.inOut(Easing.quad),
|
||||
useNativeDriver: true,
|
||||
}).start();
|
||||
}
|
||||
|
||||
_opacityActive(duration: number): void {
|
||||
this._setOpacityTo(this.props.activeOpacity ?? 0.2, duration);
|
||||
}
|
||||
|
||||
_opacityInactive(duration: number): void {
|
||||
this._setOpacityTo(this._getChildStyleOpacityWithDefault(), duration);
|
||||
}
|
||||
|
||||
_getChildStyleOpacityWithDefault(): number {
|
||||
const opacity = flattenStyle(this.props.style)?.opacity;
|
||||
return typeof opacity === 'number' ? opacity : 1;
|
||||
}
|
||||
|
||||
render(): React.Node {
|
||||
// BACKWARD-COMPATIBILITY: Focus and blur events were never supported before
|
||||
// adopting `Pressability`, so preserve that behavior.
|
||||
const {
|
||||
onBlur,
|
||||
onFocus,
|
||||
...eventHandlersWithoutBlurAndFocus
|
||||
} = this.state.pressability.getEventHandlers();
|
||||
|
||||
return (
|
||||
<Animated.View
|
||||
accessible={this.props.accessible !== false}
|
||||
accessibilityLabel={this.props.accessibilityLabel}
|
||||
accessibilityHint={this.props.accessibilityHint}
|
||||
accessibilityRole={this.props.accessibilityRole}
|
||||
accessibilityState={this.props.accessibilityState}
|
||||
accessibilityActions={this.props.accessibilityActions}
|
||||
onAccessibilityAction={this.props.onAccessibilityAction}
|
||||
accessibilityValue={this.props.accessibilityValue}
|
||||
importantForAccessibility={this.props.importantForAccessibility}
|
||||
accessibilityLiveRegion={this.props.accessibilityLiveRegion}
|
||||
accessibilityViewIsModal={this.props.accessibilityViewIsModal}
|
||||
accessibilityElementsHidden={this.props.accessibilityElementsHidden}
|
||||
style={[this.props.style, {opacity: this.state.anim}]}
|
||||
nativeID={this.props.nativeID}
|
||||
testID={this.props.testID}
|
||||
onLayout={this.props.onLayout}
|
||||
nextFocusDown={this.props.nextFocusDown}
|
||||
nextFocusForward={this.props.nextFocusForward}
|
||||
nextFocusLeft={this.props.nextFocusLeft}
|
||||
nextFocusRight={this.props.nextFocusRight}
|
||||
nextFocusUp={this.props.nextFocusUp}
|
||||
hasTVPreferredFocus={this.props.hasTVPreferredFocus}
|
||||
hitSlop={this.props.hitSlop}
|
||||
focusable={
|
||||
this.props.focusable !== false && this.props.onPress !== undefined
|
||||
}
|
||||
ref={this.props.hostRef}
|
||||
{...eventHandlersWithoutBlurAndFocus}>
|
||||
{this.props.children}
|
||||
{__DEV__ ? (
|
||||
<PressabilityDebugView color="cyan" hitSlop={this.props.hitSlop} />
|
||||
) : null}
|
||||
</Animated.View>
|
||||
);
|
||||
}
|
||||
|
||||
componentDidMount(): void {
|
||||
if (Platform.isTV) {
|
||||
this._tvTouchable = new TVTouchable(this, {
|
||||
getDisabled: () => this.props.disabled === true,
|
||||
onBlur: event => {
|
||||
if (this.props.onBlur != null) {
|
||||
this.props.onBlur(event);
|
||||
}
|
||||
},
|
||||
onFocus: event => {
|
||||
if (this.props.onFocus != null) {
|
||||
this.props.onFocus(event);
|
||||
}
|
||||
},
|
||||
onPress: event => {
|
||||
if (this.props.onPress != null) {
|
||||
this.props.onPress(event);
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps: Props, prevState: State) {
|
||||
this.state.pressability.configure(this._createPressabilityConfig());
|
||||
if (this.props.disabled !== prevProps.disabled) {
|
||||
this._opacityInactive(250);
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount(): void {
|
||||
if (Platform.isTV) {
|
||||
if (this._tvTouchable != null) {
|
||||
this._tvTouchable.destroy();
|
||||
}
|
||||
}
|
||||
this.state.pressability.reset();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = (React.forwardRef((props, hostRef) => (
|
||||
<TouchableOpacity {...props} hostRef={hostRef} />
|
||||
)): React.ComponentType<$ReadOnly<$Diff<Props, {|hostRef: mixed|}>>>);
|
194
node_modules/react-native/Libraries/Components/Touchable/TouchableWithoutFeedback.js
generated
vendored
Executable file
194
node_modules/react-native/Libraries/Components/Touchable/TouchableWithoutFeedback.js
generated
vendored
Executable file
@ -0,0 +1,194 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its 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-local
|
||||
* @format
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import Pressability, {
|
||||
type PressabilityConfig,
|
||||
} from '../../Pressability/Pressability';
|
||||
import {PressabilityDebugView} from '../../Pressability/PressabilityDebug';
|
||||
import TVTouchable from './TVTouchable';
|
||||
import type {
|
||||
AccessibilityActionEvent,
|
||||
AccessibilityActionInfo,
|
||||
AccessibilityRole,
|
||||
AccessibilityState,
|
||||
AccessibilityValue,
|
||||
} from '../../Components/View/ViewAccessibility';
|
||||
import type {EdgeInsetsProp} from '../../StyleSheet/EdgeInsetsPropType';
|
||||
import type {
|
||||
BlurEvent,
|
||||
FocusEvent,
|
||||
LayoutEvent,
|
||||
PressEvent,
|
||||
} from '../../Types/CoreEventTypes';
|
||||
import Platform from '../../Utilities/Platform';
|
||||
import View from '../../Components/View/View';
|
||||
import * as React from 'react';
|
||||
|
||||
type Props = $ReadOnly<{|
|
||||
accessibilityActions?: ?$ReadOnlyArray<AccessibilityActionInfo>,
|
||||
accessibilityElementsHidden?: ?boolean,
|
||||
accessibilityHint?: ?Stringish,
|
||||
accessibilityIgnoresInvertColors?: ?boolean,
|
||||
accessibilityLabel?: ?Stringish,
|
||||
accessibilityLiveRegion?: ?('none' | 'polite' | 'assertive'),
|
||||
accessibilityRole?: ?AccessibilityRole,
|
||||
accessibilityState?: ?AccessibilityState,
|
||||
accessibilityValue?: ?AccessibilityValue,
|
||||
accessibilityViewIsModal?: ?boolean,
|
||||
accessible?: ?boolean,
|
||||
children?: ?React.Node,
|
||||
delayLongPress?: ?number,
|
||||
delayPressIn?: ?number,
|
||||
delayPressOut?: ?number,
|
||||
disabled?: ?boolean,
|
||||
focusable?: ?boolean,
|
||||
hitSlop?: ?EdgeInsetsProp,
|
||||
importantForAccessibility?: ?('auto' | 'yes' | 'no' | 'no-hide-descendants'),
|
||||
nativeID?: ?string,
|
||||
onAccessibilityAction?: ?(event: AccessibilityActionEvent) => mixed,
|
||||
onBlur?: ?(event: BlurEvent) => mixed,
|
||||
onFocus?: ?(event: FocusEvent) => mixed,
|
||||
onLayout?: ?(event: LayoutEvent) => mixed,
|
||||
onLongPress?: ?(event: PressEvent) => mixed,
|
||||
onPress?: ?(event: PressEvent) => mixed,
|
||||
onPressIn?: ?(event: PressEvent) => mixed,
|
||||
onPressOut?: ?(event: PressEvent) => mixed,
|
||||
pressRetentionOffset?: ?EdgeInsetsProp,
|
||||
rejectResponderTermination?: ?boolean,
|
||||
testID?: ?string,
|
||||
touchSoundDisabled?: ?boolean,
|
||||
|}>;
|
||||
|
||||
type State = $ReadOnly<{|
|
||||
pressability: Pressability,
|
||||
|}>;
|
||||
|
||||
const PASSTHROUGH_PROPS = [
|
||||
'accessibilityActions',
|
||||
'accessibilityElementsHidden',
|
||||
'accessibilityHint',
|
||||
'accessibilityIgnoresInvertColors',
|
||||
'accessibilityLabel',
|
||||
'accessibilityLiveRegion',
|
||||
'accessibilityRole',
|
||||
'accessibilityState',
|
||||
'accessibilityValue',
|
||||
'accessibilityViewIsModal',
|
||||
'hitSlop',
|
||||
'importantForAccessibility',
|
||||
'nativeID',
|
||||
'onAccessibilityAction',
|
||||
'onBlur',
|
||||
'onFocus',
|
||||
'onLayout',
|
||||
'testID',
|
||||
];
|
||||
|
||||
class TouchableWithoutFeedback extends React.Component<Props, State> {
|
||||
_tvTouchable: ?TVTouchable;
|
||||
|
||||
state: State = {
|
||||
pressability: new Pressability(createPressabilityConfig(this.props)),
|
||||
};
|
||||
|
||||
render(): React.Node {
|
||||
const element = React.Children.only(this.props.children);
|
||||
const children = [element.props.children];
|
||||
if (__DEV__) {
|
||||
if (element.type === View) {
|
||||
children.push(
|
||||
<PressabilityDebugView color="red" hitSlop={this.props.hitSlop} />,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// BACKWARD-COMPATIBILITY: Focus and blur events were never supported before
|
||||
// adopting `Pressability`, so preserve that behavior.
|
||||
const {
|
||||
onBlur,
|
||||
onFocus,
|
||||
...eventHandlersWithoutBlurAndFocus
|
||||
} = this.state.pressability.getEventHandlers();
|
||||
|
||||
const elementProps: {[string]: mixed, ...} = {
|
||||
...eventHandlersWithoutBlurAndFocus,
|
||||
accessible: this.props.accessible !== false,
|
||||
focusable:
|
||||
this.props.focusable !== false && this.props.onPress !== undefined,
|
||||
};
|
||||
for (const prop of PASSTHROUGH_PROPS) {
|
||||
if (this.props[prop] !== undefined) {
|
||||
elementProps[prop] = this.props[prop];
|
||||
}
|
||||
}
|
||||
|
||||
return React.cloneElement(element, elementProps, ...children);
|
||||
}
|
||||
|
||||
componentDidMount(): void {
|
||||
if (Platform.isTV) {
|
||||
this._tvTouchable = new TVTouchable(this, {
|
||||
getDisabled: () => this.props.disabled === true,
|
||||
onBlur: event => {
|
||||
if (this.props.onBlur != null) {
|
||||
this.props.onBlur(event);
|
||||
}
|
||||
},
|
||||
onFocus: event => {
|
||||
if (this.props.onFocus != null) {
|
||||
this.props.onFocus(event);
|
||||
}
|
||||
},
|
||||
onPress: event => {
|
||||
if (this.props.onPress != null) {
|
||||
this.props.onPress(event);
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate(): void {
|
||||
this.state.pressability.configure(createPressabilityConfig(this.props));
|
||||
}
|
||||
|
||||
componentWillUnmount(): void {
|
||||
if (Platform.isTV) {
|
||||
if (this._tvTouchable != null) {
|
||||
this._tvTouchable.destroy();
|
||||
}
|
||||
}
|
||||
this.state.pressability.reset();
|
||||
}
|
||||
}
|
||||
|
||||
function createPressabilityConfig(props: Props): PressabilityConfig {
|
||||
return {
|
||||
cancelable: !props.rejectResponderTermination,
|
||||
disabled: props.disabled,
|
||||
hitSlop: props.hitSlop,
|
||||
delayLongPress: props.delayLongPress,
|
||||
delayPressIn: props.delayPressIn,
|
||||
delayPressOut: props.delayPressOut,
|
||||
minPressDuration: 0,
|
||||
pressRectOffset: props.pressRetentionOffset,
|
||||
android_disableSound: props.touchSoundDisabled,
|
||||
onBlur: props.onBlur,
|
||||
onFocus: props.onFocus,
|
||||
onLongPress: props.onLongPress,
|
||||
onPress: props.onPress,
|
||||
onPressIn: props.onPressIn,
|
||||
onPressOut: props.onPressOut,
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = TouchableWithoutFeedback;
|
12
node_modules/react-native/Libraries/Components/Touchable/__mocks__/ensureComponentIsNative.js
generated
vendored
Normal file
12
node_modules/react-native/Libraries/Components/Touchable/__mocks__/ensureComponentIsNative.js
generated
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @format
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
module.exports = () => true;
|
26
node_modules/react-native/Libraries/Components/Touchable/ensurePositiveDelayProps.js
generated
vendored
Normal file
26
node_modules/react-native/Libraries/Components/Touchable/ensurePositiveDelayProps.js
generated
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @format
|
||||
* @flow
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const invariant = require('invariant');
|
||||
|
||||
const ensurePositiveDelayProps = function(props: any) {
|
||||
invariant(
|
||||
!(
|
||||
props.delayPressIn < 0 ||
|
||||
props.delayPressOut < 0 ||
|
||||
props.delayLongPress < 0
|
||||
),
|
||||
'Touchable components cannot have negative delay properties',
|
||||
);
|
||||
};
|
||||
|
||||
module.exports = ensurePositiveDelayProps;
|
Reference in New Issue
Block a user