react-native-animateable-text
Version:
A fork of React Native's `<Text/> component that supports Reanimated Shared Values as text!
219 lines (213 loc) • 6.85 kB
JavaScript
/**
* 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';
function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); }
const React = require('react');
const ReactNativeViewAttributes = require('react-native/Libraries/Components/View/ReactNativeViewAttributes');
const Touchable = require('react-native/Libraries/Components/Touchable/Touchable');
const createReactNativeComponentClass = require('react-native/Libraries/Renderer/shims/createReactNativeComponentClass').default;
const nullthrows = require('nullthrows');
const processColor = require('react-native/Libraries/StyleSheet/processColor');
const PRESS_RECT_OFFSET = {
top: 20,
left: 20,
right: 20,
bottom: 30
};
const viewConfig = {
validAttributes: {
...ReactNativeViewAttributes.UIView,
isHighlighted: true,
isPressable: true,
numberOfLines: true,
ellipsizeMode: true,
allowFontScaling: true,
dynamicTypeRamp: true,
maxFontSizeMultiplier: true,
disabled: true,
selectable: true,
selectionColor: true,
adjustsFontSizeToFit: true,
minimumFontScale: true,
textBreakStrategy: true,
onTextLayout: true,
onInlineViewLayout: true,
dataDetectorType: true,
android_hyphenationFrequency: true,
lineBreakStrategyIOS: true,
text: true
},
directEventTypes: {
topTextLayout: {
registrationName: 'onTextLayout'
},
topInlineViewLayout: {
registrationName: 'onInlineViewLayout'
}
},
uiViewClassName: 'JBAnimatedText'
};
/**
* A React component for displaying text.
*
* See https://reactnative.dev/docs/text.html
*/
class TouchableText extends React.Component {
static defaultProps = {
accessible: true,
allowFontScaling: true,
ellipsizeMode: 'tail'
};
state = {
isHighlighted: false,
createResponderHandlers: this._createResponseHandlers.bind(this),
responseHandlers: null
};
static getDerivedStateFromProps(nextProps, prevState) {
return prevState.responseHandlers == null && isTouchable(nextProps) ? {
responseHandlers: prevState.createResponderHandlers()
} : null;
}
static viewConfig = viewConfig;
render() {
let props = this.props;
if (isTouchable(props)) {
props = {
...props,
isHighlighted: this.state.isHighlighted
};
}
if (props.selectionColor != null) {
props = {
...props,
selectionColor: processColor(props.selectionColor)
};
}
if (__DEV__) {
if (Touchable.TOUCH_TARGET_DEBUG && props.onPress != null) {
props = {
...props,
style: [props.style, {
color: 'magenta'
}]
};
}
}
return /*#__PURE__*/React.createElement(RCTText, _extends({}, props, {
ref: props.forwardedRef
}));
}
_createResponseHandlers() {
return {
onStartShouldSetResponder: () => {
const {
onStartShouldSetResponder
} = this.props;
const shouldSetResponder = (onStartShouldSetResponder == null ? false : onStartShouldSetResponder()) || isTouchable(this.props);
if (shouldSetResponder) {
this._attachTouchHandlers();
}
return shouldSetResponder;
},
onResponderGrant: event => {
nullthrows(this.touchableHandleResponderGrant)(event);
if (this.props.onResponderGrant != null) {
this.props.onResponderGrant.call(this, event);
}
},
onResponderMove: event => {
nullthrows(this.touchableHandleResponderMove)(event);
if (this.props.onResponderMove != null) {
this.props.onResponderMove.call(this, event);
}
},
onResponderRelease: event => {
nullthrows(this.touchableHandleResponderRelease)(event);
if (this.props.onResponderRelease != null) {
this.props.onResponderRelease.call(this, event);
}
},
onResponderTerminate: event => {
nullthrows(this.touchableHandleResponderTerminate)(event);
if (this.props.onResponderTerminate != null) {
this.props.onResponderTerminate.call(this, event);
}
},
onResponderTerminationRequest: () => {
const {
onResponderTerminationRequest
} = this.props;
if (!nullthrows(this.touchableHandleResponderTerminationRequest)()) {
return false;
}
if (onResponderTerminationRequest == null) {
return true;
}
return onResponderTerminationRequest();
}
};
}
/**
* Lazily attaches Touchable.Mixin handlers.
*/
_attachTouchHandlers() {
if (this.touchableGetPressRectOffset != null) {
return;
}
for (const key in Touchable.Mixin) {
if (typeof Touchable.Mixin[key] === 'function') {
this[key] = Touchable.Mixin[key].bind(this);
}
}
this.touchableHandleActivePressIn = () => {
if (!this.props.suppressHighlighting && isTouchable(this.props)) {
this.setState({
isHighlighted: true
});
}
};
this.touchableHandleActivePressOut = () => {
if (!this.props.suppressHighlighting && isTouchable(this.props)) {
this.setState({
isHighlighted: false
});
}
};
this.touchableHandlePress = event => {
if (this.props.onPress != null) {
this.props.onPress(event);
}
};
this.touchableHandleLongPress = event => {
if (this.props.onLongPress != null) {
this.props.onLongPress(event);
}
};
this.touchableGetPressRectOffset = () => this.props.pressRetentionOffset == null ? PRESS_RECT_OFFSET : this.props.pressRetentionOffset;
}
}
const isTouchable = props => props.onPress != null || props.onLongPress != null ||
// @ts-expect-error
props.onStartShouldSetResponder != null;
const RCTText = createReactNativeComponentClass(viewConfig.uiViewClassName, () => viewConfig);
const Text = (props, forwardedRef) => {
// @ts-expect-error
return /*#__PURE__*/React.createElement(TouchableText, _extends({}, props, {
forwardedRef: forwardedRef
}));
};
const TextToExport = React.forwardRef(Text);
TextToExport.displayName = 'Animateable';
// TODO: Deprecate this.
/* $FlowFixMe(>=0.89.0 site=react_native_fb) This comment suppresses an error
* found when Flow v0.89 was deployed. To see the error, delete this comment
* and run Flow. */
export const AnimateableText = TextToExport;
//# sourceMappingURL=AnimateableText.js.map