Add selectionColor prop for Text on Android

Summary:
**Motivation**

Customizing the selection color allows to use brand colors in the app. The PR implements a `selectionColor` prop for `Text` component similar to `TextInput`.

**Test plan (required)**

Run UIExplorer example with the changes and verify everything works fine.

![image](https://cloud.githubusercontent.com/assets/1174278/22023258/70197d84-dceb-11e6-8662-2879d78d14d4.png)

cc brentvatne
Closes https://github.com/facebook/react-native/pull/11947

Differential Revision: D4430265

fbshipit-source-id: 462f16548d93ab03aadb27d6f12acf90842627ab
This commit is contained in:
Satyajit Sahoo 2017-01-18 12:53:58 -08:00 committed by Facebook Github Bot
parent 81c33b542d
commit 72be2d35cc
3 changed files with 51 additions and 22 deletions

View File

@ -386,10 +386,15 @@ class TextExample extends React.Component {
</Text>
</UIExplorerBlock>
<UIExplorerBlock title="selectable attribute">
<Text selectable={true}>
<Text selectable>
This text is selectable if you click-and-hold, and will offer the native Android selection menus.
</Text>
</UIExplorerBlock>
<UIExplorerBlock title="selectionColor attribute">
<Text selectable selectionColor="orange">
This text will have a orange highlight on selection.
</Text>
</UIExplorerBlock>
<UIExplorerBlock title="Inline images">
<Text>
This text contains an inline image <Image source={require('./flux.png')}/>. Neat, huh?

View File

@ -11,6 +11,7 @@
*/
'use strict';
const ColorPropType = require('ColorPropType');
const EdgeInsetsPropType = require('EdgeInsetsPropType');
const NativeMethodsMixin = require('NativeMethodsMixin');
const Platform = require('Platform');
@ -20,9 +21,12 @@ const StyleSheetPropType = require('StyleSheetPropType');
const TextStylePropTypes = require('TextStylePropTypes');
const Touchable = require('Touchable');
const processColor = require('processColor');
const createReactNativeComponentClass = require('createReactNativeComponentClass');
const mergeFast = require('mergeFast');
const { PropTypes } = React;
const stylePropType = StyleSheetPropType(TextStylePropTypes);
const viewConfig = {
@ -32,6 +36,7 @@ const viewConfig = {
ellipsizeMode: true,
allowFontScaling: true,
selectable: true,
selectionColor: true,
adjustsFontSizeToFit: true,
minimumFontScale: true,
textBreakStrategy: true,
@ -109,7 +114,7 @@ const Text = React.createClass({
*
* > `clip` is working only for iOS
*/
ellipsizeMode: React.PropTypes.oneOf(['head', 'middle', 'tail', 'clip']),
ellipsizeMode: PropTypes.oneOf(['head', 'middle', 'tail', 'clip']),
/**
* Used to truncate the text with an ellipsis after computing the text
* layout, including line wrapping, such that the total number of lines
@ -117,31 +122,31 @@ const Text = React.createClass({
*
* This prop is commonly used with `ellipsizeMode`.
*/
numberOfLines: React.PropTypes.number,
numberOfLines: PropTypes.number,
/**
* Set text break strategy on Android API Level 23+, possible values are `simple`, `highQuality`, `balanced`
* The default value is `highQuality`.
* @platform android
*/
textBreakStrategy: React.PropTypes.oneOf(['simple', 'highQuality', 'balanced']),
textBreakStrategy: PropTypes.oneOf(['simple', 'highQuality', 'balanced']),
/**
* Invoked on mount and layout changes with
*
* `{nativeEvent: {layout: {x, y, width, height}}}`
*/
onLayout: React.PropTypes.func,
onLayout: PropTypes.func,
/**
* This function is called on press.
*
* e.g., `onPress={() => console.log('1st')}``
*/
onPress: React.PropTypes.func,
onPress: PropTypes.func,
/**
* This function is called on long press.
*
* e.g., `onLongPress={this.increaseSize}>``
*/
onLongPress: React.PropTypes.func,
onLongPress: PropTypes.func,
/**
* When the scroll view is disabled, this defines how far your touch may
* move off of the button, before deactivating the button. Once deactivated,
@ -153,24 +158,28 @@ const Text = React.createClass({
/**
* Lets the user select text, to use the native copy and paste functionality.
*/
selectable: React.PropTypes.bool,
selectable: PropTypes.bool,
/**
* The highlight color of the text.
* @platform android
*/
selectionColor: ColorPropType,
/**
* 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,
suppressHighlighting: PropTypes.bool,
style: stylePropType,
/**
* Used to locate this view in end-to-end tests.
*/
testID: React.PropTypes.string,
testID: PropTypes.string,
/**
* Specifies whether fonts should scale to respect Text Size accessibility setting on iOS. The
* default is `true`.
*/
allowFontScaling: React.PropTypes.bool,
allowFontScaling: PropTypes.bool,
/**
* When set to `true`, indicates that the view is an accessibility element. The default value
* for a `Text` element is `true`.
@ -179,18 +188,18 @@ const Text = React.createClass({
* [Accessibility guide](/react-native/docs/accessibility.html#accessible-ios-android)
* for more information.
*/
accessible: React.PropTypes.bool,
accessible: PropTypes.bool,
/**
* Specifies whether font should be scaled down automatically to fit given style constraints.
* @platform ios
*/
adjustsFontSizeToFit: React.PropTypes.bool,
adjustsFontSizeToFit: PropTypes.bool,
/**
* Specifies smallest possible scale a font can reach when adjustsFontSizeToFit is enabled. (values 0.01-1.0).
* @platform ios
*/
minimumFontScale: React.PropTypes.number,
minimumFontScale: PropTypes.number,
},
getDefaultProps(): Object {
return {
@ -210,10 +219,10 @@ const Text = React.createClass({
return {isInAParentText: true};
},
childContextTypes: {
isInAParentText: React.PropTypes.bool
isInAParentText: PropTypes.bool
},
contextTypes: {
isInAParentText: React.PropTypes.bool
isInAParentText: PropTypes.bool
},
/**
* Only assigned if touch is needed.
@ -317,6 +326,12 @@ const Text = React.createClass({
isHighlighted: this.state.isHighlighted,
};
}
if (newProps.selectionColor != null) {
newProps = {
...newProps,
selectionColor: processColor(newProps.selectionColor)
};
}
if (Touchable.TOUCH_TARGET_DEBUG && newProps.onPress) {
newProps = {
...newProps,

View File

@ -9,25 +9,25 @@
package com.facebook.react.views.text;
import javax.annotation.Nullable;
import android.text.Spannable;
import android.text.TextUtils;
import android.view.Gravity;
import android.widget.TextView;
import com.facebook.yoga.YogaConstants;
import com.facebook.react.bridge.JSApplicationIllegalArgumentException;
import com.facebook.react.common.annotations.VisibleForTesting;
import com.facebook.react.module.annotations.ReactModule;
import com.facebook.react.uimanager.BaseViewManager;
import com.facebook.react.uimanager.PixelUtil;
import com.facebook.react.uimanager.annotations.ReactProp;
import com.facebook.react.uimanager.Spacing;
import com.facebook.react.uimanager.ThemedReactContext;
import com.facebook.react.uimanager.ViewDefaults;
import com.facebook.react.uimanager.ViewProps;
import com.facebook.react.common.annotations.VisibleForTesting;
import com.facebook.react.uimanager.annotations.ReactProp;
import com.facebook.react.uimanager.annotations.ReactPropGroup;
import com.facebook.yoga.YogaConstants;
import javax.annotation.Nullable;
/**
* Manages instances of spannable {@link TextView}.
@ -96,6 +96,15 @@ public class ReactTextViewManager extends BaseViewManager<ReactTextView, ReactTe
view.setTextIsSelectable(isSelectable);
}
@ReactProp(name = "selectionColor", customType = "Color")
public void setSelectionColor(ReactTextView view, @Nullable Integer color) {
if (color == null) {
view.setHighlightColor(DefaultStyleValuesUtil.getDefaultTextColorHighlight(view.getContext()));
} else {
view.setHighlightColor(color);
}
}
@ReactPropGroup(names = {
ViewProps.BORDER_RADIUS,
ViewProps.BORDER_TOP_LEFT_RADIUS,