1import React, {Component} from 'react' 2import openURL from '../util/open-url' 3import {fontSizeToSizeStyle, lineClamp, metaData} from './text.meta.native' 4import * as Styles from '../styles' 5import shallowEqual from 'shallowequal' 6import {NativeClipboard, NativeText, NativeAlert} from './native-wrappers.native' 7import {Props, TextType} from './text' 8 9const modes = ['positive', 'negative'] 10 11const styles = Styles.styleSheetCreate(() => 12 Object.keys(metaData()).reduce<any>( 13 (map, type) => { 14 const meta = metaData()[type as TextType] 15 modes.forEach(mode => { 16 map[`${type}:${mode}`] = { 17 ...fontSizeToSizeStyle(meta.fontSize), 18 color: meta.colorForBackground[mode] || Styles.globalColors.black, 19 ...meta.styleOverride, 20 } 21 }) 22 return map 23 }, 24 {center: {textAlign: 'center'}} 25 ) 26) 27 28// Init common styles for perf 29 30class Text extends Component<Props> { 31 static defaultProps = { 32 allowFontScaling: false, 33 } 34 _nativeText: any 35 36 highlightText() { 37 // ignored 38 } 39 40 focus() { 41 if (this._nativeText) { 42 this._nativeText.focus() 43 } 44 } 45 46 _urlClick = () => { 47 this.props.onClickURL && openURL(this.props.onClickURL) 48 } 49 50 _urlCopy = (url: string | null) => { 51 if (!url) return 52 NativeClipboard.setString(url) 53 } 54 55 _urlChooseOption = () => { 56 const url = this.props.onLongPressURL 57 if (!url) return 58 NativeAlert.alert('', url, [ 59 {style: 'cancel', text: 'Cancel'}, 60 {onPress: () => openURL(url), text: 'Open Link'}, 61 {onPress: () => this._urlCopy(url), text: 'Copy Link'}, 62 ]) 63 } 64 65 shouldComponentUpdate(nextProps: Props): boolean { 66 return !shallowEqual(this.props, nextProps, (obj, oth, key) => { 67 if (key === 'style') { 68 return shallowEqual(obj, oth) 69 } else if (key === 'children' && this.props.plainText && nextProps.plainText) { 70 // child will be plain text 71 return shallowEqual(obj, oth) 72 } 73 return undefined 74 }) 75 } 76 77 render() { 78 const baseStyle = styles[`${this.props.type}:${this.props.negative ? 'negative' : 'positive'}`] 79 const dynamicStyle = this.props.negative 80 ? _getStyle( 81 this.props.type, 82 this.props.negative, 83 this.props.lineClamp, 84 !!this.props.onClick, 85 !!this.props.underline 86 ) 87 : {} 88 89 let style 90 if (!Object.keys(dynamicStyle).length) { 91 style = 92 this.props.style || this.props.center 93 ? [baseStyle, this.props.center && styles.center, this.props.style] 94 : baseStyle 95 } else { 96 style = [baseStyle, dynamicStyle, this.props.center && styles.center, this.props.style] 97 } 98 99 const onPress = 100 this.props.onClick || 101 (this.props.onClickURL ? this._urlClick : undefined) || 102 // If selectable and there isn't already an onClick handler, 103 // make a dummy one so that it shows the selection (on iOS). 104 (this.props.selectable ? () => {} : undefined) 105 106 const onLongPress = 107 this.props.onLongPress || (this.props.onLongPressURL ? this._urlChooseOption : undefined) 108 109 return ( 110 <NativeText 111 ref={ref => { 112 this._nativeText = ref 113 }} 114 selectable={this.props.selectable} 115 textBreakStrategy={this.props.textBreakStrategy} 116 style={style} 117 {...lineClamp(this.props.lineClamp || undefined, this.props.ellipsizeMode || undefined)} 118 onPress={onPress} 119 onLongPress={onLongPress} 120 allowFontScaling={this.props.allowFontScaling} 121 > 122 {this.props.children} 123 </NativeText> 124 ) 125 } 126} 127 128// external things call this so leave the original alone 129function _getStyle( 130 type: TextType, 131 negative?: boolean, 132 _?: number | null, 133 __?: boolean | null, 134 // @ts-ignore the order of these parameters because this is used in a lot 135 // of places 136 forceUnderline: boolean 137) { 138 if (!negative) { 139 return forceUnderline ? {textDecorationLine: 'underline'} : {} 140 } 141 // negative === true 142 const meta = metaData()[type] 143 const colorStyle = {color: meta.colorForBackground.negative} 144 const textDecoration = meta.isLink ? {textDecorationLine: 'underline'} : {} 145 146 return { 147 ...colorStyle, 148 ...textDecoration, 149 } 150} 151function getStyle(type: TextType, negative?: boolean, _?: number | null, __?: boolean | null) { 152 const meta = metaData()[type] 153 const sizeStyle = fontSizeToSizeStyle(meta.fontSize) 154 const colorStyle = {color: meta.colorForBackground[negative ? 'negative' : 'positive']} 155 const textDecoration = meta.isLink && negative ? {textDecorationLine: 'underline'} : {} 156 157 return { 158 ...sizeStyle, 159 ...colorStyle, 160 ...textDecoration, 161 ...meta.styleOverride, 162 } 163} 164 165export default Text 166export {getStyle} 167export {Text as TextMixed} 168export {allTextTypes} from './text.shared' 169