UNPKG

vue3-text-clamp

Version:

Vue3 component which allows you to ellipsis your multiline text.

2 lines (1 loc) 3.25 kB
import{defineComponent as e,ref as t,reactive as a,computed as l,watch as n,nextTick as o,onUnmounted as i,onMounted as r,openBlock as s,createElementBlock as d,normalizeStyle as p,unref as u,createElementVNode as x,renderSlot as f}from"vue";import{addListener as c,removeListener as m}from"resize-detector";const g=["aria-label"];var v=e({__name:"text-clamp",props:{text:{type:String,required:!0},maxHeight:{type:[String,Number],required:!1},maxLines:{type:Number,required:!1},expanded:{type:Boolean,required:!1,default:!1},location:{type:String,required:!1,default:"end"},ellipsis:{type:String,required:!1,default:"…"},autoResize:{type:Boolean,required:!1,default:!1}},emits:["clamp-change","update:expanded"],setup(e,{emit:v}){const h=e,y=t(null),b=t(null),E=t(null),H=a({offset:0,localExpanded:!!h.expanded,unregisterResizeCallback:null}),R=l((()=>{if(!H.localExpanded&&h.maxHeight)return"number"==typeof h?.maxHeight?`${h?.maxHeight}px`:h?.maxHeight})),_=()=>{E.value&&(E.value.textContent=A.value)},k=()=>{H.localExpanded||(_(),(C()||w.value)&&$())},q=()=>{h.text&&(H.offset=h.text.length,z(),h.autoResize&&y.value&&(c(y.value,k),H.unregisterResizeCallback=()=>{y.value&&m(y.value,k)}),k())},z=()=>{H.unregisterResizeCallback?.()},C=()=>!(!h.maxLines&&!h.maxHeight)&&(!!y.value&&(!!(h.maxLines&&L()>h.maxLines)||!!(h.maxHeight&&y.value.scrollHeight>y.value.offsetHeight))),L=()=>b.value?Object.keys(Array.prototype.slice.call(b.value.getClientRects()).reduce(((e,{top:t,bottom:a})=>{const l=`${t}/${a}`;return e[l]||(e[l]=!0),e}),{})).length:0,$=(...e)=>{const[t=0,a=H.offset]=e;if(a-t<=3)return void j();const l=Math.floor((a+t)/2);S(l),C()?$(t,l):$(l,a)},S=e=>{H.offset=e,_()},j=()=>{B(),M()},B=()=>{for(;(!C()||L()<2)&&H.offset<h.text.length;)N(1)},M=()=>{for(;C()&&L()>1&&H.offset>0;)N(-1)},N=e=>{S(H.offset+e)},w=l((()=>!!h.text&&H.offset!==h.text.length));n((()=>w.value),(e=>{setTimeout((()=>{v("clamp-change",e)}),0)}),{immediate:!0});const A=l((()=>w.value?O.value:h.text)),O=l((()=>{if("start"===h.location)return h.ellipsis+(h.text.slice(-H.offset)||"").trim();if("middle"===h.location){const e=Math.floor(H.offset/2);return(h.text.slice(0,e)||"").trim()+h.ellipsis+(h.text.slice(-e)||"").trim()}return(h.text.slice(0,H.offset)||"").trim()+h.ellipsis})),T=()=>{H.localExpanded=!0},D=()=>{H.localExpanded=!1},F=()=>{H.localExpanded=!H.localExpanded};return n((()=>h.expanded),(e=>{H.localExpanded=e})),n((()=>H.localExpanded),(e=>{e?S(h.text.length):k(),h.expanded!==e&&v("update:expanded",e)})),n((()=>[h.maxLines,h.maxHeight,h.ellipsis,h.location,w.value].join()),(()=>{o((()=>{k()}))})),n((()=>[h.text,h.autoResize].join()),(()=>{o((()=>{q()}))})),i((()=>{z()})),r((()=>{q()})),(t,a)=>(s(),d("div",{ref_key:"textClampRef",ref:y,class:"text-clamp",style:p({overflow:"hidden",maxHeight:u(R)})},[x("span",{ref_key:"contentRef",ref:b},[f(t.$slots,"before",{expand:T,collapse:D,toggle:F,clamped:u(w),expanded:H.localExpanded}),x("span",{ref_key:"textRef",ref:E,"aria-label":e.text},null,8,g),f(t.$slots,"after",{expand:T,collapse:D,toggle:F,clamped:u(w),expanded:H.localExpanded})],512)],4))}});v.__file="package/text-clamp.vue";const h=e=>(e.install=function(t){t.component(e.__name,e)},e),y=h(v);export{y as default,h as withInstall};