123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302 |
- /**
- * 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 DeprecatedTextPropTypes = require('../DeprecatedPropTypes/DeprecatedTextPropTypes');
- const React = require('react');
- const ReactNativeViewAttributes = require('../Components/View/ReactNativeViewAttributes');
- const TextAncestor = require('./TextAncestor');
- const Touchable = require('../Components/Touchable/Touchable');
- const UIManager = require('../ReactNative/UIManager');
- const createReactNativeComponentClass = require('../Renderer/shims/createReactNativeComponentClass');
- const nullthrows = require('nullthrows');
- const processColor = require('../StyleSheet/processColor');
- import type {PressEvent} from '../Types/CoreEventTypes';
- import type {HostComponent} from '../Renderer/shims/ReactNativeTypes';
- import type {PressRetentionOffset, TextProps} from './TextProps';
- type ResponseHandlers = $ReadOnly<{|
- onStartShouldSetResponder: () => boolean,
- onResponderGrant: (event: PressEvent, dispatchID: string) => void,
- onResponderMove: (event: PressEvent) => void,
- onResponderRelease: (event: PressEvent) => void,
- onResponderTerminate: (event: PressEvent) => void,
- onResponderTerminationRequest: () => boolean,
- |}>;
- type Props = $ReadOnly<{|
- ...TextProps,
- forwardedRef: ?React.Ref<'RCTText' | 'RCTVirtualText'>,
- |}>;
- type State = {|
- touchable: {|
- touchState: ?string,
- responderID: ?number,
- |},
- isHighlighted: boolean,
- createResponderHandlers: () => ResponseHandlers,
- responseHandlers: ?ResponseHandlers,
- |};
- const PRESS_RECT_OFFSET = {top: 20, left: 20, right: 20, bottom: 30};
- const viewConfig = {
- validAttributes: {
- ...ReactNativeViewAttributes.UIView,
- isHighlighted: true,
- numberOfLines: true,
- ellipsizeMode: true,
- allowFontScaling: true,
- maxFontSizeMultiplier: true,
- disabled: true,
- selectable: true,
- selectionColor: true,
- adjustsFontSizeToFit: true,
- minimumFontScale: true,
- textBreakStrategy: true,
- onTextLayout: true,
- onInlineViewLayout: true,
- dataDetectorType: true,
- },
- directEventTypes: {
- topTextLayout: {
- registrationName: 'onTextLayout',
- },
- topInlineViewLayout: {
- registrationName: 'onInlineViewLayout',
- },
- },
- uiViewClassName: 'RCTText',
- };
- /**
- * A React component for displaying text.
- *
- * See https://reactnative.dev/docs/text.html
- */
- class TouchableText extends React.Component<Props, State> {
- static defaultProps = {
- accessible: true,
- allowFontScaling: true,
- ellipsizeMode: 'tail',
- };
- touchableGetPressRectOffset: ?() => PressRetentionOffset;
- touchableHandleActivePressIn: ?() => void;
- touchableHandleActivePressOut: ?() => void;
- touchableHandleLongPress: ?(event: PressEvent) => void;
- touchableHandlePress: ?(event: PressEvent) => void;
- touchableHandleResponderGrant: ?(
- event: PressEvent,
- dispatchID: string,
- ) => void;
- touchableHandleResponderMove: ?(event: PressEvent) => void;
- touchableHandleResponderRelease: ?(event: PressEvent) => void;
- touchableHandleResponderTerminate: ?(event: PressEvent) => void;
- touchableHandleResponderTerminationRequest: ?() => boolean;
- state = {
- ...Touchable.Mixin.touchableGetInitialState(),
- isHighlighted: false,
- createResponderHandlers: this._createResponseHandlers.bind(this),
- responseHandlers: null,
- };
- static getDerivedStateFromProps(
- nextProps: Props,
- prevState: State,
- ): $Shape<State> | null {
- return prevState.responseHandlers == null && isTouchable(nextProps)
- ? {
- responseHandlers: prevState.createResponderHandlers(),
- }
- : null;
- }
- static viewConfig = viewConfig;
- render(): React.Node {
- let props = this.props;
- if (isTouchable(props)) {
- props = {
- ...props,
- ...this.state.responseHandlers,
- 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 (
- <TextAncestor.Consumer>
- {hasTextAncestor =>
- hasTextAncestor ? (
- <RCTVirtualText {...props} ref={props.forwardedRef} />
- ) : (
- <TextAncestor.Provider value={true}>
- <RCTText {...props} ref={props.forwardedRef} />
- </TextAncestor.Provider>
- )
- }
- </TextAncestor.Consumer>
- );
- }
- _createResponseHandlers(): ResponseHandlers {
- return {
- onStartShouldSetResponder: (): boolean => {
- const {onStartShouldSetResponder} = this.props;
- const shouldSetResponder =
- (onStartShouldSetResponder == null
- ? false
- : onStartShouldSetResponder()) || isTouchable(this.props);
- if (shouldSetResponder) {
- this._attachTouchHandlers();
- }
- return shouldSetResponder;
- },
- onResponderGrant: (event: PressEvent, dispatchID: string): void => {
- nullthrows(this.touchableHandleResponderGrant)(event, dispatchID);
- if (this.props.onResponderGrant != null) {
- this.props.onResponderGrant.call(this, event, dispatchID);
- }
- },
- onResponderMove: (event: PressEvent): void => {
- nullthrows(this.touchableHandleResponderMove)(event);
- if (this.props.onResponderMove != null) {
- this.props.onResponderMove.call(this, event);
- }
- },
- onResponderRelease: (event: PressEvent): void => {
- nullthrows(this.touchableHandleResponderRelease)(event);
- if (this.props.onResponderRelease != null) {
- this.props.onResponderRelease.call(this, event);
- }
- },
- onResponderTerminate: (event: PressEvent): void => {
- nullthrows(this.touchableHandleResponderTerminate)(event);
- if (this.props.onResponderTerminate != null) {
- this.props.onResponderTerminate.call(this, event);
- }
- },
- onResponderTerminationRequest: (): boolean => {
- const {onResponderTerminationRequest} = this.props;
- if (!nullthrows(this.touchableHandleResponderTerminationRequest)()) {
- return false;
- }
- if (onResponderTerminationRequest == null) {
- return true;
- }
- return onResponderTerminationRequest();
- },
- };
- }
- /**
- * Lazily attaches Touchable.Mixin handlers.
- */
- _attachTouchHandlers(): void {
- if (this.touchableGetPressRectOffset != null) {
- return;
- }
- for (const key in Touchable.Mixin) {
- if (typeof Touchable.Mixin[key] === 'function') {
- (this: any)[key] = Touchable.Mixin[key].bind(this);
- }
- }
- this.touchableHandleActivePressIn = (): void => {
- if (!this.props.suppressHighlighting && isTouchable(this.props)) {
- this.setState({isHighlighted: true});
- }
- };
- this.touchableHandleActivePressOut = (): void => {
- if (!this.props.suppressHighlighting && isTouchable(this.props)) {
- this.setState({isHighlighted: false});
- }
- };
- this.touchableHandlePress = (event: PressEvent): void => {
- if (this.props.onPress != null) {
- this.props.onPress(event);
- }
- };
- this.touchableHandleLongPress = (event: PressEvent): void => {
- if (this.props.onLongPress != null) {
- this.props.onLongPress(event);
- }
- };
- this.touchableGetPressRectOffset = (): PressRetentionOffset =>
- this.props.pressRetentionOffset == null
- ? PRESS_RECT_OFFSET
- : this.props.pressRetentionOffset;
- }
- }
- const isTouchable = (props: Props): boolean =>
- props.onPress != null ||
- props.onLongPress != null ||
- props.onStartShouldSetResponder != null;
- const RCTText = createReactNativeComponentClass(
- viewConfig.uiViewClassName,
- () => viewConfig,
- );
- const RCTVirtualText =
- UIManager.getViewManagerConfig('RCTVirtualText') == null
- ? RCTText
- : createReactNativeComponentClass('RCTVirtualText', () => ({
- validAttributes: {
- ...ReactNativeViewAttributes.UIView,
- isHighlighted: true,
- maxFontSizeMultiplier: true,
- },
- uiViewClassName: 'RCTVirtualText',
- }));
- const Text = (
- props: TextProps,
- forwardedRef: ?React.Ref<'RCTText' | 'RCTVirtualText'>,
- ) => {
- return <TouchableText {...props} forwardedRef={forwardedRef} />;
- };
- const TextToExport = React.forwardRef(Text);
- TextToExport.displayName = 'Text';
- // 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. */
- TextToExport.propTypes = DeprecatedTextPropTypes;
- type TextStatics = $ReadOnly<{|
- propTypes: typeof DeprecatedTextPropTypes,
- |}>;
- module.exports = ((TextToExport: any): React.AbstractComponent<
- TextProps,
- React.ElementRef<HostComponent<TextProps>>,
- > &
- TextStatics);
|