patrik-piskay / react-truncate-markup

✂️ React component for truncating JSX markup

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Feature suggestion: keep whole words, not cut them in half

burtek opened this issue · comments

Hello again!

Just a quick feature suggestion this time. How about keepWholeWords prop, that - if set to true - would make react-truncate-markup keep full words and not cut the in half? At the moment I keep getting truncated words, i.e. se (whole word is sed):
image

In this example this flag set to true could then render something like this:
image

So basically: if the word is going to be cut in half, just omit it in the result.

I tried to prepare a PR with this, but I got lost in the source code of this repo. I might try again during weekend though

Actually all the magic for this case happens in the splitString method. Currently we split strings in half, but with this "whole words" option we need to split it into words first, then split the list of words in half and concatenate again before returning it as a string again.

We would probably need to allow for the trailing space before ellipsis though, I'll need to think about that.

In the meantime, if you want to tackle the word truncation logic (without the trailing space), you might find the test app in the repo useful. Go to __tests__/app, install dependencies and run yarn develop - it'll start a dev server for examples in __tests__/app/src/pages dir and it'll autoupdate on any TruncateMarkup component src change

Wow! Love the description, thanks! I'll see what I can do :)

I've just sat down to this, since there is little work to be done this week. I started with something like this:

diff --git a/README.md b/README.md
index 91389a1..01eec00 100644
--- a/README.md
+++ b/README.md
@@ -153,6 +153,12 @@ const wordsLeftEllipsis = (rootEl) => {
 
 Numeric value for desired line height in pixels. Generally it will be auto-detected but it can be useful in some cases when the auto-detected value needs to be overridden.
 
+### `keepWholeWords`
+
+> default value: false
+
+Boolean value. If set to true, the truncation will keep whole words, rather than cut them in half. Defaults to `false` for backward compatibility.
+
 Contributing
 -----
 
diff --git a/index.d.ts b/index.d.ts
index 68c16a2..d63afec 100644
--- a/index.d.ts
+++ b/index.d.ts
@@ -4,6 +4,7 @@ export interface TruncateProps {
     lines?: number;
     ellipsis?: React.ReactNode | ((element: React.ReactNode) => React.ReactNode);
     lineHeight?: number | string;
+    keepWholeWords?: boolean;
 }
 
 declare class TruncateMarkup extends React.Component<TruncateProps> {}
diff --git a/src/index.js b/src/index.js
index d7c3f38..4a0e92c 100644
--- a/src/index.js
+++ b/src/index.js
@@ -59,12 +59,14 @@ export default class TruncateMarkup extends React.Component {
       PropTypes.func,
     ]),
     lineHeight: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
+    keepWholeWords: PropTypes.bool,
   };
 
   static defaultProps = {
     lines: 1,
     ellipsis: '...',
     lineHeight: '',
+    keepWholeWords: false,
   };
 
   state = {
@@ -303,6 +305,35 @@ export default class TruncateMarkup extends React.Component {
     return cloneWithChildren(node, newChildren, isRoot, level);
   }
 
+  regexIndexOf(string, regex, startPos) {
+    const indexOf = string.substring(startPos || 0).search(regex);
+    return (indexOf >= 0) ? (indexOf + (startPos || 0)) : indexOf;
+  }
+
+  getSplitStringPivotIndex(string) {
+    let ret, method;
+    if (this.props.keepWholeWords) {
+      const indexes = [];
+      for (let index = 1; index < string.length - 1; index++) {
+        if (/\W/.test(string[index])) {
+          indexes.push(index);
+        }
+      }
+      if (indexes.length === 0) {
+        ret = Math.ceil(string.length / 2);
+        method = 'fallback';
+      } else {
+        ret = indexes[Math.floor(indexes.length / 2)];
+        method = 'words';
+      }
+    } else {
+      ret = Math.ceil(string.length / 2);
+      method = 'letters';
+    }
+    console.log(method, ret, string);
+    return ret;
+  }
+
   splitString(string, splitDirections = []) {
     if (!splitDirections.length) {
       return string;
@@ -324,7 +355,7 @@ export default class TruncateMarkup extends React.Component {
     }
 
     const [splitDirection, ...restSplitDirections] = splitDirections;
-    const pivotIndex = Math.ceil(string.length / 2);
+    const pivotIndex = this.getSplitStringPivotIndex(string);
 
     if (splitDirection === SPLIT.LEFT) {
       const subString = string.substring(0, pivotIndex);

But the outcome is actually same as without the keepWholeWords prop (and it's because of the "fallback"). Without the fallback it enters infinite loop :(

I'm trying to get it working, but no success for now.

Hey @burtek, I'm going away for a month, if you won't manage to get it working, I'll take a stab at it when I get back!

Right! Have a nice vacation! :)

Hopfully solved here #18 :)