rich-textarea
Version:
A small customizable textarea for React to colorize, highlight, decorate texts, offer autocomplete and much more.
4 lines (3 loc) • 10.5 kB
JavaScript
"use client";
import{jsx as e,jsxs as t,Fragment as n}from"react/jsx-runtime";import{useLayoutEffect as o,useEffect as r,useRef as i,memo as l,useState as s,useImperativeHandle as u,useMemo as c,forwardRef as d,useCallback as a}from"react";const{min:p,max:h}=Math,g="current",f=e("span",{style:{color:"transparent"},children:""});function b(e,t,n,o){var r=e.ownerDocument;o||(o=r.createRange());let i,l,s,u=r.createNodeIterator(e,NodeFilter.SHOW_TEXT,null,!1),c={},d={};for(;i=u.nextNode();)l=i.nodeValue,s=l.length,!c.node&&s>t&&(c.node=i,c.offset=t),!d.node&&s>=n&&(d.node=i,d.offset=n),t-=s,n-=s;return c.node&&o.setStart(c.node,h(c.offset,0)),d.node&&o.setEnd(d.node,h(d.offset,0)),o}const v=["direction","padding","paddingTop","paddingBottom","paddingLeft","paddingRight","margin","marginTop","marginBottom","marginLeft","marginRight","border","borderWidth","borderTopWidth","borderBottomWidth","borderLeftWidth","borderRightWidth","borderStyle","borderTopStyle","borderBottomStyle","borderLeftStyle","borderRightStyle","fontSize","fontFamily","fontStyle","fontVariant","fontWeight","fontStretch","fontSizeAdjust","textAlign","textTransform","textIndent","letterSpacing","wordSpacing","lineHeight","whiteSpace","wordBreak","overflowWrap","tabSize","MozTabSize"],S=(e,t)=>e.getPropertyValue(t),m=(e,t,n)=>{e.setProperty(t,n)},x=(e,t)=>{const n=S(e,t);return n?parseInt(n,10):0},y=e=>getComputedStyle(e),w=e=>"string"==typeof e&&e.endsWith("%"),_=e=>x(e,"padding-top")+x(e,"padding-bottom")+x(e,"border-top")+x(e,"border-bottom"),M=e=>x(e,"padding-left")+x(e,"padding-right")+x(e,"border-left")+x(e,"border-right"),k=(e,t,n)=>{const o="pointer-events",r=e.style,i=t.style,l=S(r,o),s=S(i,o);m(r,o,"none"),m(i,o,"auto");const u=document.elementFromPoint(n.clientX,n.clientY);return m(r,o,l),m(i,o,s),z(u,t)?u:null},z=(e,t)=>!!e&&t!==e&&t.contains(e),T=(e,t,n)=>{e.dispatchEvent(new MouseEvent(t,n))},C=(e,t)=>{T(e,t.type,t)},E=(e,t)=>{e&&T(e,"mouseout",t)},A=e=>{e.stopPropagation()},W=(e,t,n,o)=>{const r=y(e),i=e.style,l=t.style;n[g]||(n[g]=S(r,"color")),v.forEach((e=>{l[e]=r[e]})),i.color=l.borderColor="transparent",i.caretColor=(null==o?void 0:o.caretColor)||n[g]},R=(e,t,n)=>(e.addEventListener(t,n),()=>{e.removeEventListener(t,n)}),j=(e,t)=>{let n,o=[null,null];const r={t(){setTimeout((()=>{t((()=>{const e=r.o(),t=r.i();return o[0]===e&&o[1]===t||(o=[e,t]),o})())}))},l(e){n=e},o(){const t=e[g];if(!t)return 0;const o=t.selectionStart;return n?p(o,t.selectionEnd-n.data.length):o},i(){const t=e[g];if(!t)return 0;const o=t.selectionEnd;return n?p(o,t.selectionStart+n.data.length):o}};return r},B="undefined"!=typeof window?o:r,D=e=>{const t=i(void 0);return t[g]||(t[g]=e())},L=(e,t,n,o,r)=>{let i=null;const l=new ResizeObserver((([t])=>{const{contentRect:{width:n,height:r},borderBoxSize:i}=t;if(i&&i[0])return void o([n,r,i[0].inlineSize-n,i[0].blockSize-r]);const l=y(e);o([n,r,M(l),_(l)])})),s=R(e,"focus",(()=>{r(!0)})),u=R(e,"blur",(()=>{r(!1)})),c=R(e,"scroll",(()=>{const{scrollTop:n,scrollLeft:o}=e;t.style.transform=`translate(${-o}px, ${-n}px)`})),d=R(e,"mousedown",(o=>{n.t();const r=R(document,"mouseup",(()=>{n.t(),r()})),i=k(e,t,o);i&&C(i,o)})),a=R(e,"mouseup",(n=>{const o=k(e,t,n);o&&C(o,n)})),p=R(e,"mousemove",(n=>{const o=k(e,t,n);((e,t,n)=>{e&&C(e,n),t!==e&&(E(t,n),e&&T(e,"mouseover",n))})(o,i,n),i=o})),h=R(e,"mouseleave",(e=>{E(i,e),i=null})),g=R(e,"click",(n=>{const o=k(e,t,n);o&&C(o,n)})),f=R(e,"input",(()=>{n.t()})),b=R(e,"compositionstart",(e=>{n.l(e)})),v=R(e,"compositionupdate",(e=>{n.l(e)})),S=R(e,"compositionend",(()=>{n.l()}));return l.observe(e),()=>{s(),u(),c(),d(),a(),p(),h(),g(),f(),b(),v(),S(),l.disconnect()}},U=/*#__PURE__*/l((({u:e,p:n,h:o,v:r})=>{const[i,l]=s("");return u(n,(()=>l),[]),t("div",{ref:e,"aria-hidden":!0,style:c((()=>({width:r,transform:"translate(0px, 0px)",pointerEvents:"none",userSelect:"none",msUserSelect:"none",WebkitUserSelect:"none",boxSizing:"content-box",textSizeAdjust:"100%",WebkitTextSizeAdjust:"100%",MozTextSizeAdjust:"100%"})),[r]),onClick:A,onMouseDown:A,onMouseUp:A,onMouseOver:A,onMouseOut:A,onMouseMove:A,children:[c((()=>o?o(i):i),[i,o]),f]})})),$=/*#__PURE__*/d((({children:n,autoHeight:o,style:l,onChange:d,onKeyDown:p,onSelectionChange:h,...f},v)=>{const S=i(null),m=i(null),x=i(null),[[y,_,M,k],z]=s([0,0,0,0]),[T,C]=s(!1),E=i(""),[[A,R],$]=s([null,null]),O=D((()=>j(S,$))),H=y+M,K=_+k,I=!!(H+K);return u(v,(()=>{const e=S[g],t={get selectionStart(){return O.o()},get selectionEnd(){return O.i()},setRangeText(t,n,o,r){e.setRangeText?e.setRangeText(t,n,o,r):(e.focus(),e.selectionStart=n,e.selectionEnd=o,document.execCommand("insertText",!1,t)),e.dispatchEvent(new Event("input",{bubbles:!0}))}};return new Proxy(e,{get(e,n){if(t[n])return t[n];const o=e[n];return"function"==typeof o?o.bind(e):o},set:(e,t,n)=>(e[t]=n,!0)})}),[]),B((()=>{const e=S[g],t=m[g];if(e&&t)return L(e,t,O,z,C)}),[]),B((()=>{const e=S[g];e&&x[g](e.value)}),[f.value]),B((()=>{const e=S[g],t=m[g];t&&e&&W(e,t,E,l)}),[l]),r((()=>{if(null!=A&&null!=R&&h)if(T){const e=b(m[g],A,A+1).getBoundingClientRect();h({focused:!0,top:e.top,left:e.left,height:e.height,selectionStart:A,selectionEnd:R})}else h({focused:!1,selectionStart:A,selectionEnd:R})}),[T,A,R]),r((()=>{const e=S[g];o&&e&&(e.style.height="auto",e.style.height=e.scrollHeight+"px")})),t("div",{style:c((()=>{let e=H,t=K;return l&&(w(l.width)&&(e=l.width),w(l.height)&&(t=l.height)),{display:"inline-block",position:"relative",width:e,height:t}}),[H,K,l]),children:[e("div",{style:c((()=>{const e={position:"absolute",overflow:"hidden",top:0,left:0,width:H,height:K};return l?(["background","backgroundColor"].forEach((t=>{l[t]&&(e[t]=l[t])})),e):e}),[H,K,l]),children:e(U,{u:m,p:x,h:n,v:y})}),e("textarea",{...f,ref:S,style:c((()=>({...l,background:"transparent",margin:0,position:"absolute",...!I&&{position:void 0,verticalAlign:"top"}})),[l,I]),onChange:a((e=>{var t;null===(t=x[g])||void 0===t||t.call(x,e.target.value),null==d||d(e)}),[d]),onKeyDown:a((e=>{e.nativeEvent.isComposing||229===e.nativeEvent.keyCode||(null==p||p(e),O.t())}),[p])})]})})),O=/*#__PURE__*/l((({u:n,p:o,h:r,S:i})=>{const[l,d]=s("");return u(o,(()=>d),[]),e("div",{ref:n,"aria-hidden":!0,onClick:A,onMouseDown:A,onMouseUp:A,onMouseOver:A,onMouseOut:A,onMouseMove:A,children:e("div",{style:{height:i},children:t("div",{style:c((()=>({transform:"translate(0px, 0px)",pointerEvents:"none",userSelect:"none",msUserSelect:"none",WebkitUserSelect:"none",boxSizing:"content-box",textSizeAdjust:"100%",WebkitTextSizeAdjust:"100%",MozTextSizeAdjust:"100%",whiteSpace:"pre"})),[]),children:[c((()=>r?r(l):l),[l,r]),f]})})})})),H=/*#__PURE__*/d((({children:n,autoHeight:o,style:l,onChange:d,onKeyDown:p,onSelectionChange:h,...f},v)=>{const S=i(null),m=i(null),x=i(null),[[y,_,M,k],z]=s([0,0,0,0]),[T,C]=s(!1),E=i(""),[[A,R],U]=s([null,null]),$=D((()=>{const e=(()=>{if("undefined"==typeof navigator)return!1;const e=navigator.userAgent.toLowerCase();return e.indexOf("safari")>-1&&e.indexOf("chrome")<=-1})();return j(S,(t=>{if(U(t),e){if(!S[g]||!m[g])return;const{scrollTop:e,scrollLeft:t}=S[g];m[g].style.transform=`translate(${-t}px, ${-e}px)`}}))})),H=y+M,K=_+k,I=!!(H+K);return u(v,(()=>{const e=S[g],t={get selectionStart(){return $.o()},get selectionEnd(){return $.i()},setRangeText(t,n,o,r){e.setRangeText?e.setRangeText(t,n,o,r):(e.focus(),e.selectionStart=n,e.selectionEnd=o,document.execCommand("insertText",!1,t)),e.dispatchEvent(new Event("input",{bubbles:!0}))}};return new Proxy(e,{get(e,n){if(t[n])return t[n];const o=e[n];return"function"==typeof o?o.bind(e):o},set:(e,t,n)=>(e[t]=n,!0)})}),[]),B((()=>{const e=S[g],t=m[g];if(e&&t)return L(e,t,$,z,C)}),[]),B((()=>{const e=S[g];e&&x[g](e.value)}),[f.value]),B((()=>{const e=S[g],t=m[g];t&&e&&W(e,t,E,l)}),[l]),r((()=>{if(null!=A&&null!=R&&h)if(T){const e=b(m[g],A,A+1).getBoundingClientRect();h({focused:!0,top:e.top,left:e.left,height:e.height,selectionStart:A,selectionEnd:R})}else h({focused:!1,selectionStart:A,selectionEnd:R})}),[T,A,R]),r((()=>{const e=S[g];o&&e&&(e.style.height="auto",e.style.height=e.scrollHeight+"px")})),t("div",{style:c((()=>{let e=H,t=K;return l&&(w(l.width)&&(e=l.width),w(l.height)&&(t=l.height)),{display:"inline-block",position:"relative",width:e,height:t}}),[H,K,l]),children:[e("div",{style:c((()=>{const e={position:"absolute",overflow:"hidden",top:0,left:0,width:H,height:K};return l?(["background","backgroundColor"].forEach((t=>{l[t]&&(e[t]=l[t])})),e):e}),[H,K,l]),children:e(O,{u:m,p:x,h:n,S:_})}),e("input",{...f,ref:S,style:c((()=>({...l,background:"transparent",margin:0,position:"absolute",...!I&&{position:void 0,verticalAlign:"top"}})),[l,I]),onChange:a((e=>{var t;null===(t=x[g])||void 0===t||t.call(x,e.target.value),null==d||d(e)}),[d]),onKeyDown:a((e=>{e.nativeEvent.isComposing||229===e.nativeEvent.keyCode||(null==p||p(e),$.t())}),[p])})]})})),K=(e,t,n)=>{const o=[];let r=null;for(;r=e.exec(t);)n&&!n(t,r)||o.push(r);return o},I=t=>{const n=t.map((([,e])=>e));return o=>{var r;const[i,l,s]=t.reduce(((e,[t,n,r])=>(K(t,o,r).forEach((t=>{const o=t.index,r=t.index+t[0].length;e[0].add(o).add(r);let i=e[1].get(o),l=e[2].get(r);i||e[1].set(o,i=[]),l||e[2].set(r,l=[]),i.push(n),l.push(n)})),e)),[new Set,new Map,new Map]),u=Array.from(i);u.sort(((e,t)=>e-t));let c=0;const d=[],a=[];for(let t=0;t<u.length;t++){const i=u[t],p=null!==(r=u[t+1])&&void 0!==r?r:o.length;if(i===p)continue;const h=o.slice(c,i);h&&a.push(h);const g=l.get(i),f=s.get(p);g&&(g.forEach((e=>{d.push(e)})),d.sort(((e,t)=>n.indexOf(t)-n.indexOf(e))));const b=o.slice(i,p);a.push(d.reduceRight(((t,n,o)=>{const r=0===o?i+"":void 0;return"function"==typeof n?n({children:t,value:b,key:r}):e("span",{style:n,children:t},r)}),b)),f&&f.forEach((e=>{d.splice(d.indexOf(e),1)})),c=p}const p=o.slice(c);return p&&a.push(p),a}},F=e=>"rich-textarea-"+e,P=/*#__PURE__*/l((({m:t,_:n})=>{const o=i(null);return B((()=>{const e=o.current.firstChild,r=n.map((n=>new Highlight(...K(n,t).map((t=>{const n=t.index,o=t.index+t[0].length,r=new Range;return r.setStart(e,n),r.setEnd(e,o),r})))));return r.forEach(((e,t)=>{CSS.highlights.set(F(t),e)})),()=>{r.forEach(((e,t)=>{CSS.highlights.delete(F(t)),e.clear()}))}}),[t]),e("div",{ref:o,children:t})})),N=o=>{const r=o.reduce(((e,[,t],n)=>e+`::highlight(${F(n)}){${t}}`),""),i=o.map((([e])=>e));return o=>t(n,{children:[e(P,{m:o,_:i}),e("style",{children:r})]})};export{$ as RichTextarea,N as createCSSCustomHighlightRenderer,I as createRegexRenderer,H as experimental_RichInput};
//# sourceMappingURL=index.mjs.map