/** * Copyright (c) 2015-present, Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * * @providesModule Text * @flow */ 'use strict'; var NativeMethodsMixin = require('NativeMethodsMixin'); var Platform = require('Platform'); var React = require('React'); var ReactInstanceMap = require('ReactInstanceMap'); var ReactNativeViewAttributes = require('ReactNativeViewAttributes'); var StyleSheetPropType = require('StyleSheetPropType'); var TextStylePropTypes = require('TextStylePropTypes'); var Touchable = require('Touchable'); var createReactNativeComponentClass = require('createReactNativeComponentClass'); var merge = require('merge'); var stylePropType = StyleSheetPropType(TextStylePropTypes); var viewConfig = { validAttributes: merge(ReactNativeViewAttributes.UIView, { isHighlighted: true, numberOfLines: true, allowFontScaling: true, }), uiViewClassName: 'RCTText', }; /** * A React component for displaying text which supports nesting, * styling, and touch handling. In the following example, the nested title and * body text will inherit the `fontFamily` from `styles.baseText`, but the title * provides its own additional styles. The title and body will stack on top of * each other on account of the literal newlines: * * ``` * renderText: function() { * return ( * * * {this.state.titleText + '\n\n'} * * * {this.state.bodyText} * * * ); * }, * ... * var styles = StyleSheet.create({ * baseText: { * fontFamily: 'Cochin', * }, * titleText: { * fontSize: 20, * fontWeight: 'bold', * }, * }; * ``` */ var Text = React.createClass({ mixins: [Touchable.Mixin, NativeMethodsMixin], propTypes: { /** * Used to truncate the text with an elipsis after computing the text * layout, including line wrapping, such that the total number of lines * does not exceed this number. */ numberOfLines: React.PropTypes.number, /** * Invoked on mount and layout changes with * * `{nativeEvent: {layout: {x, y, width, height}}}` */ onLayout: React.PropTypes.func, /** * This function is called on press. */ onPress: React.PropTypes.func, /** * When true, no visual change is made when text is pressed down. By * default, a gray oval highlights the text on press down. * @platform ios */ suppressHighlighting: React.PropTypes.bool, style: stylePropType, /** * Used to locate this view in end-to-end tests. */ testID: React.PropTypes.string, /** * Specifies should fonts scale to respect Text Size accessibility setting on iOS. */ allowFontScaling: React.PropTypes.bool, }, viewConfig: viewConfig, getInitialState: function(): Object { return merge(this.touchableGetInitialState(), { isHighlighted: false, }); }, getDefaultProps: function(): Object { return { allowFontScaling: true, }; }, onStartShouldSetResponder: function(): bool { var shouldSetFromProps = this.props.onStartShouldSetResponder && this.props.onStartShouldSetResponder(); return shouldSetFromProps || !!this.props.onPress; }, /* * Returns true to allow responder termination */ handleResponderTerminationRequest: function(): bool { // Allow touchable or props.onResponderTerminationRequest to deny // the request var allowTermination = this.touchableHandleResponderTerminationRequest(); if (allowTermination && this.props.onResponderTerminationRequest) { allowTermination = this.props.onResponderTerminationRequest(); } return allowTermination; }, handleResponderGrant: function(e: SyntheticEvent, dispatchID: string) { this.touchableHandleResponderGrant(e, dispatchID); this.props.onResponderGrant && this.props.onResponderGrant.apply(this, arguments); }, handleResponderMove: function(e: SyntheticEvent) { this.touchableHandleResponderMove(e); this.props.onResponderMove && this.props.onResponderMove.apply(this, arguments); }, handleResponderRelease: function(e: SyntheticEvent) { this.touchableHandleResponderRelease(e); this.props.onResponderRelease && this.props.onResponderRelease.apply(this, arguments); }, handleResponderTerminate: function(e: SyntheticEvent) { this.touchableHandleResponderTerminate(e); this.props.onResponderTerminate && this.props.onResponderTerminate.apply(this, arguments); }, touchableHandleActivePressIn: function() { if (this.props.suppressHighlighting || !this.props.onPress) { return; } this.setState({ isHighlighted: true, }); }, touchableHandleActivePressOut: function() { if (this.props.suppressHighlighting || !this.props.onPress) { return; } this.setState({ isHighlighted: false, }); }, touchableHandlePress: function() { this.props.onPress && this.props.onPress(); }, touchableGetPressRectOffset: function(): RectOffset { return PRESS_RECT_OFFSET; }, getChildContext: function(): Object { return {isInAParentText: true}; }, childContextTypes: { isInAParentText: React.PropTypes.bool }, render: function() { var props = {}; for (var key in this.props) { props[key] = this.props[key]; } // Text is accessible by default if (props.accessible !== false) { props.accessible = true; } props.isHighlighted = this.state.isHighlighted; props.onStartShouldSetResponder = this.onStartShouldSetResponder; props.onResponderTerminationRequest = this.handleResponderTerminationRequest; props.onResponderGrant = this.handleResponderGrant; props.onResponderMove = this.handleResponderMove; props.onResponderRelease = this.handleResponderRelease; props.onResponderTerminate = this.handleResponderTerminate; // TODO: Switch to use contextTypes and this.context after React upgrade var context = ReactInstanceMap.get(this)._context; if (context.isInAParentText) { return ; } else { return ; } }, }); type RectOffset = { top: number; left: number; right: number; bottom: number; } var PRESS_RECT_OFFSET = {top: 20, left: 20, right: 20, bottom: 30}; var RCTText = createReactNativeComponentClass(viewConfig); var RCTVirtualText = RCTText; if (Platform.OS === 'android') { RCTVirtualText = createReactNativeComponentClass({ validAttributes: merge(ReactNativeViewAttributes.UIView, { isHighlighted: true, }), uiViewClassName: 'RCTVirtualText', }); } module.exports = Text;