@strg/react-snip
Version:
React component to clamp text to a specified number of lines.
2 lines (1 loc) • 3.38 kB
JavaScript
import React,{useRef as useRef,useLayoutEffect as useLayoutEffect,Children as Children,isValidElement as isValidElement,cloneElement as cloneElement}from"react";var snipTextCSS=function(e,t){var n=t.current,t=n.options,n=n.element;e.current.textContent=n.fullText,e.current.style.display="-webkit-box",e.current.style.webkitLineClamp=""+t.lines,e.current.style.webkitBoxOrient="vertical",e.current.style.overflow="hidden"},addObserver=function(e,t){var n=t.current.element,r=n.observer||new ResizeObserver(function(){e.current.clientWidth===n.prevWidth&&e.current.clientHeight===n.prevHeight||snipText(e,t)});r.observe(e.current),n.observer=r},destroyObserver=function(e,t){t=t.current.element;t.observer&&(t.observer.disconnect(),t.prevWidth=null,t.prevHeight=null,t.observer=null)},UA_LINE_HEIGHT=1.2,elementLines=function(e){var t=window.getComputedStyle(e),e=parseInt(t.height),t="normal"===t.lineHeight?parseInt(t.fontSize)*UA_LINE_HEIGHT:parseInt(t.lineHeight);return 0===e&&0===t?0:Math.ceil(e/t)},defaultOptions={method:"css",lines:3,midWord:!0,ellipsis:". . ."},supportsWebkitClamp=function(){return"undefined"!=typeof CSS&&CSS.supports("display","-webkit-box")&&CSS.supports("-webkit-line-clamp","3")&&CSS.supports("-webkit-box-orient","vertical")},getOptions=function(e){var t;return{method:supportsWebkitClamp()?null!==(t=null==e?void 0:e.method)&&void 0!==t?t:defaultOptions.method:"js",lines:null!==(t=null==e?void 0:e.lines)&&void 0!==t?t:defaultOptions.lines,midWord:null!==(t=null==e?void 0:e.midWord)&&void 0!==t?t:defaultOptions.midWord,ellipsis:null!==(e=null==e?void 0:e.ellipsis)&&void 0!==e?e:defaultOptions.ellipsis}},snipTextJS=function(i,e){var s,e=e.current,l=e.options,e=e.element;i.current.style.display=null,i.current.style.webkitLineClamp=null,i.current.style.webkitBoxOrient=null,i.current.style.overflow=null,i.current.textContent=e.fullText,l.lines<=0||elementLines(i.current)<=l.lines||(s={unprocessed:e.fullText,processed:""},(l.midWord?[". ",", "," ",""]:[". ",", "," "]).forEach(function(e){for(var t=0,n=s.unprocessed.split(e);t<n.length;t++){var r=n[t];if(i.current.textContent=""+s.processed+r+e+l.ellipsis,elementLines(i.current)>l.lines){s.unprocessed=r;break}s.processed=""+s.processed+r+e}}),i.current.textContent=""+s.processed.trim()+l.ellipsis)},snipText=function(e,t){var n=t.current,r=n.options,n=n.element;"css"!==r.method?"js"===r.method&&(snipTextJS(e,t),n.prevWidth=e.current.clientWidth,n.prevHeight=e.current.clientHeight):snipTextCSS(e,t)},useSnip=function(i,e){var s=useRef({});useLayoutEffect(function(){s.current.options=getOptions(e)},[e]),useLayoutEffect(function(){s.current.element={fullText:i.current.textContent}},[i]),useLayoutEffect(function(){var e="undefined"!=typeof ResizeObserver,t="js"===s.current.options.method,n=!!s.current.element.observer,r=e&&t&&!n,t=e&&!t&&n;return r&&addObserver(i,s),t&&destroyObserver(i,s),r||snipText(i,s),function(){return n&&destroyObserver(i,s)}},[i,s.current.options])},ReactSnip=function(e){var t=e.children,n=e.method,r=e.lines,i=e.midWord,e=e.ellipsis,s=useRef(null);useSnip(s,{method:n,lines:r,midWord:i,ellipsis:e});var l=function(e){return 1<Children.count(e)||!isValidElement(e)?React.createElement("div",{ref:s},e):isValidElement(e.props.children)?cloneElement(e,{children:l(e.props.children)}):cloneElement(e,{ref:s})};return l(t)};export{ReactSnip as ReactSnip};