eugene
eugene

Reputation: 41685

react-native customize text elipsis to add "More" after elipsis

Following is from instagram which is known for using react-native

As you can see there is a text (더보기, which means More) after the elipsis (...)
How can I simulate that too?

enter image description here

Upvotes: 1

Views: 1579

Answers (2)

Daniel Dimitrov
Daniel Dimitrov

Reputation: 2018

Well, that is one of those things that on the surface looks easy enough, but actually is a complicated endeavor.

Due to this thing over here: https://github.com/facebook/react-native/issues/22811 you won't be able to nest the <Text> tag and use the numberOfLines prop to achieve what you want.

In the past I've tried to deal with something similar: React Native chat bubble flexbox similar to whatsapp

Back then there was no onTextLayout function(or at least I didn't know about it). The onTextLayout callback on the Text component gives you a line prop. The line is an array that contains information about every line of text. You could use this to place the more button where you want.

const MoreInfo = text => {
  const [moreLeft, setMoreLeft] = React.useState(0)
  const [moreTop, setMoreTop] = React.useState(0)
  return (
    <View style={{ marginTop: 20 }}>
      <Text
        numberOfLines={2}
        ellipsizeMode={"tail"}
        onTextLayout={({ nativeEvent: { lines } }) => {
          const width = lines[lines.length - 1].width
          const height = lines[lines.length - 1].y

          setMoreTop(height)
          setMoreLeft(width)
        }}
      >
        {text}
      </Text>
      <Text
        style={{
          backgroundColor: "white",
          position: "absolute",
          left: moreLeft - 30,
          top: moreTop,
        }}
      >
        ... More
      </Text>
    </View>
  )
}

Obviously the above implementation is not perfect. As you can see I just fake the ... by placing them in the More button. Sometimes it looks good, othertimes it cuts off the last letter in the string in the middle. But it serves to show the direction you need to go to solve this.

Some ideas: inspect the lines array - if it has 2 lines is the last line width smaller than the first line width. Will your "More" text fit in the width difference? If yes, then just set the left position to the width. if the second line is as long as the first one, then the ellepsis is at the end and you'll need to resort to the "... More" trick.

Upvotes: 1

Sameer Kumar Jain
Sameer Kumar Jain

Reputation: 2135

Here is a quick workaround. Create a function which takes text as an argument and show truncated text with a more button. Upon click, we will render full text with a less button.

const MoreLessComponent = ({ truncatedText, fullText }) => {
  const [more, setMore] = React.useState(false);
  return (
    <Text>
      {!more ? `${truncatedText}...` : fullText}
      <TouchableOpacity onPress={() => setMore(!more)}>
        <Text>{more ? 'less' : 'more'}</Text>
      </TouchableOpacity>
    </Text>
  );
};

const MoreInfo = (text, linesToTruncate) => {
  const [clippedText, setClippedText] = React.useState(false);
  return clippedText ? (
    <MoreLessComponent truncatedText={clippedText} fullText={text} />
  ) : (
    <Text
      numberOfLines={linesToTruncate}
      ellipsizeMode={'tail'}
      onTextLayout={(event) => {
        //get all lines
        const { lines } = event.nativeEvent;
        //get lines after it truncate
        let text = lines
          .splice(0, linesToTruncate)
          .map((line) => line.text)
          .join('');
        //substring with some random digit, this might need more work here based on the font size
        //
        setClippedText(text.substr(0, text.length - 9));
      }}>
      {text}
    </Text>
  );
};


Now call it like this

<View>
        {MoreInfo('Change code in the editor and watch it change on your phone! Save to get a shareable url.')}
      </View>

Here is the snack https://snack.expo.io/@saachitech/react-native-more-less

Upvotes: 0

Related Questions