11import React from 'react' ;
22import { Text , PanResponder } from 'react-native' ;
3- import reactNativeTextSize from 'react-native-text-size' ;
43import PropTypes from 'prop-types' ;
5- import debounce from 'lodash/debounce ' ;
4+ import SeeMoreUtil from './SeeMoreUtil ' ;
65
76class SeeMore extends React . Component {
87 panResponder = PanResponder . create ( {
@@ -13,9 +12,9 @@ class SeeMore extends React.Component {
1312 onPanResponderRelease : ( ) => this . handleLinkReleased ( ) ,
1413 } ) ;
1514
16- setDebouncedWidth ;
17-
18- // Map of containerWidth to truncationIndex so that we don't calculate it each time
15+ /**
16+ * Map of containerWidth and truncationIndex so that we don't calculate it each time
17+ */
1918 containerWidthToTruncationIndexMap ;
2019
2120 constructor ( props ) {
@@ -26,90 +25,63 @@ class SeeMore extends React.Component {
2625 isShowingMore : false ,
2726 truncationIndex : undefined ,
2827 } ;
29-
30- this . setDebouncedWidth = debounce ( ( e ) => {
31- this . findTruncationIndex ( e . nativeEvent . layout . width ) ;
32- } , 100 ) ;
3328 }
3429
30+ isExpanded = ( ) => {
31+ const { isShowingMore } = this . state ;
32+ return isShowingMore ;
33+ } ;
34+
3535 onLayout = ( e ) => {
3636 // e.persist() keeps the original synthetic event intact
3737 e . persist ( ) ;
38- this . setDebouncedWidth ( e ) ;
38+ this . findAndUpdateTruncationIndex ( e . nativeEvent . layout . width ) ;
39+ } ;
40+
41+ findAndUpdateTruncationIndex = async ( containerWidth ) => {
42+ const truncationIndex = await this . findTruncationIndex ( containerWidth ) ;
43+ this . setState ( { truncationIndex } ) ;
3944 } ;
4045
4146 findTruncationIndex = async ( containerWidth ) => {
4247 if (
43- this . containerWidthToTruncationIndexMap
44- && this . containerWidthToTruncationIndexMap [ containerWidth ]
48+ this . containerWidthToTruncationIndexMap &&
49+ this . containerWidthToTruncationIndexMap [ containerWidth ]
4550 ) {
46- this . setState ( { truncationIndex : this . containerWidthToTruncationIndexMap [ containerWidth ] } ) ;
47- return ;
51+ return this . containerWidthToTruncationIndexMap [ containerWidth ] ;
4852 }
4953
5054 const {
5155 children : text ,
5256 style : { fontSize, fontFamily, fontWeight } ,
53- seeMoreText,
5457 numberOfLines,
58+ seeMoreText,
5559 } = this . props ;
5660
57- const { width : textWidth } = await reactNativeTextSize . measure ( {
61+ const truncationIndex = await SeeMoreUtil . getTruncationIndex (
5862 text ,
63+ numberOfLines ,
5964 fontSize ,
6065 fontFamily ,
6166 fontWeight ,
62- } ) ;
63-
64- const textWidthLimit = containerWidth * numberOfLines ;
65-
66- if ( textWidth < textWidthLimit ) {
67- this . setState ( { truncationIndex : undefined } ) ;
68- return ;
69- }
70-
71- const { width : seeMoreTextWidth } = await reactNativeTextSize . measure ( {
72- text : ` ...${ seeMoreText } ` ,
73- fontSize,
74- fontFamily,
75- fontWeight,
76- } ) ;
77-
78- const truncatedWidth = textWidthLimit - 2 * seeMoreTextWidth ;
79-
80- let index = 0 ;
81- let start = 0 ;
82- let end = text . length - 1 ;
83-
84- while ( start <= end ) {
85- const middle = start + ( end - start ) / 2 ;
86- // eslint-disable-next-line no-await-in-loop
87- const { width : partialWidth } = await reactNativeTextSize . measure ( {
88- text : text . slice ( 0 , middle ) ,
89- fontSize,
90- fontFamily,
91- fontWeight,
92- } ) ;
93- if ( Math . abs ( truncatedWidth - partialWidth ) <= 10 ) {
94- index = middle ;
95- break ;
96- } else if ( partialWidth > truncatedWidth ) {
97- end = middle - 1 ;
98- } else {
99- start = middle + 1 ;
100- }
101- }
102-
103- const truncationIndex = Math . floor ( index ) ;
67+ containerWidth ,
68+ seeMoreText ,
69+ ) ;
10470
105- // Map truncation index to width so that we don't calculate it again
10671 this . containerWidthToTruncationIndexMap = {
10772 ...this . containerWidthToTruncationIndexMap ,
10873 [ containerWidth ] : truncationIndex ,
10974 } ;
110- this . setState ( { truncationIndex } ) ;
75+
76+ return truncationIndex ;
11177 } ;
11278
79+ collapse ( ) {
80+ return new Promise ( ( resolve ) => {
81+ this . setState ( { isShowingMore : false } , ( ) => resolve ( ) ) ;
82+ } ) ;
83+ }
84+
11385 handleLinkPressed ( ) {
11486 this . setState ( {
11587 isLinkPressed : true ,
@@ -130,36 +102,47 @@ class SeeMore extends React.Component {
130102 } ) ;
131103 }
132104
133- render ( ) {
105+ renderSeeMoreSeeLessLink ( ) {
134106 const { isLinkPressed, isShowingMore, truncationIndex } = this . state ;
135107 const {
136108 children : text ,
137- numberOfLines,
138109 linkColor,
139110 linkPressedColor,
140111 seeMoreText,
141112 seeLessText,
142113 } = this . props ;
143114 const isTruncable = truncationIndex < text . length ;
144115
116+ if ( ! isTruncable ) {
117+ return null ;
118+ }
119+
120+ return (
121+ < Text
122+ { ...this . props }
123+ { ...this . panResponder . panHandlers }
124+ style = { { color : isLinkPressed ? linkPressedColor : linkColor } }
125+ >
126+ { isShowingMore ? null : < Text { ...this . props } > ...</ Text > }
127+ { isShowingMore ? ` ${ seeLessText } ` : ` ${ seeMoreText } ` }
128+ </ Text >
129+ ) ;
130+ }
131+
132+ render ( ) {
133+ const { isShowingMore, truncationIndex } = this . state ;
134+ const { children : text , numberOfLines } = this . props ;
135+
145136 return (
146137 < Text
147138 onLayout = { isShowingMore ? undefined : this . onLayout }
148139 numberOfLines = { isShowingMore ? undefined : numberOfLines }
140+ { ...this . panResponder . panHandlers }
149141 >
150- < Text { ...this . props } > { isShowingMore ? text : text . slice ( 0 , truncationIndex ) } </ Text >
151- { isTruncable ? (
152- < >
153- { isShowingMore ? null : < Text { ...this . props } > ...</ Text > }
154- < Text
155- { ...this . props }
156- { ...this . panResponder . panHandlers }
157- style = { { color : isLinkPressed ? linkPressedColor : linkColor } }
158- >
159- { isShowingMore ? ` ${ seeLessText } ` : ` ${ seeMoreText } ` }
160- </ Text >
161- </ >
162- ) : null }
142+ < Text { ...this . props } >
143+ { isShowingMore ? text : text . slice ( 0 , truncationIndex ) }
144+ </ Text >
145+ { this . renderSeeMoreSeeLessLink ( ) }
163146 </ Text >
164147 ) ;
165148 }
0 commit comments