@lexical/react
Version:
This package provides Lexical components and hooks for React applications.
10 lines (8 loc) • 9.45 kB
JavaScript
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*/
import{useLexicalComposerContext as t}from"@lexical/react/LexicalComposerContext";import{createCommand as e,COMMAND_PRIORITY_LOW as n,KEY_ARROW_DOWN_COMMAND as o,KEY_ARROW_UP_COMMAND as r,KEY_ESCAPE_COMMAND as l,KEY_TAB_COMMAND as i,KEY_ENTER_COMMAND as s,$getSelection as u,$isRangeSelection as c,$isTextNode as a,getDOMSelection as d}from"lexical";import m,{useLayoutEffect as p,useEffect as f,useRef as g,useCallback as h,useState as y,useMemo as w}from"react";import{mergeRegister as v}from"@lexical/utils";import C from"react-dom";import{jsx as E,jsxs as b}from"react/jsx-runtime";const x="startTransition";const S="undefined"!=typeof window&&void 0!==window.document&&void 0!==window.document.createElement,R=S?p:f;class O{key;ref;icon;title;constructor(t){this.key=t,this.ref={current:null},this.setRefElement=this.setRefElement.bind(this)}setRefElement(t){this.ref={current:t}}}const I=t=>{const e=document.getElementById("typeahead-menu");if(!e)return;const n=e.getBoundingClientRect();n.top+n.height>window.innerHeight&&e.scrollIntoView({block:"center"}),n.top<0&&e.scrollIntoView({block:"center"}),t.scrollIntoView({block:"nearest"})};function N(t,e){const n=t.getBoundingClientRect(),o=e.getBoundingClientRect();return n.top>=o.top-6&&n.top<=o.bottom+6}function A(e,n,o,r){const[l]=t();f(()=>{if(null!=n&&null!=e){const t=l.getRootElement(),e=null!=t?function(t){let e=getComputedStyle(t);const n="absolute"===e.position,o=/(auto|scroll)/;if("fixed"===e.position)return document.body;for(let r=t;r=r.parentElement;)if(e=getComputedStyle(r),(!n||"static"!==e.position)&&o.test(e.overflow+e.overflowY+e.overflowX))return r;return document.body}(t):document.body;let i=!1,s=N(n,e);const u=function(){i||(window.requestAnimationFrame(function(){o(),i=!1}),i=!0);const t=N(n,e);t!==s&&(s=t,null!=r&&r(t))},c=new ResizeObserver(o);return window.addEventListener("resize",o),document.addEventListener("scroll",u,{capture:!0,passive:!0}),c.observe(n),()=>{c.unobserve(n),window.removeEventListener("resize",o),document.removeEventListener("scroll",u,!0)}}},[n,l,r,o,e])}const P=e("SCROLL_TYPEAHEAD_OPTION_INTO_VIEW_COMMAND");function T({index:t,isSelected:e,onClick:n,onMouseEnter:o,option:r}){let l="item";return e&&(l+=" selected"),b("li",{tabIndex:-1,className:l,ref:r.setRefElement,role:"option","aria-selected":e,id:"typeahead-item-"+t,onMouseEnter:o,onClick:n,children:[r.icon,E("span",{className:"text",children:r.title})]},r.key)}function L({close:t,editor:e,anchorElementRef:a,resolution:d,options:m,menuRenderFn:p,onSelectOption:g,shouldSplitNodeWithQuery:b=!1,commandPriority:x=n,preselectFirstItem:S=!0}){const[O,N]=y(null),A=null!==O?Math.min(m.length-1,O):null,L=d.match&&d.match.matchingString;f(()=>{S&&N(0)},[L,S]);const _=h(n=>{e.update(()=>{const e=null!=d.match&&b?function(t){const e=u();if(!c(e)||!e.isCollapsed())return null;const n=e.anchor;if("text"!==n.type)return null;const o=n.getNode();if(!o.isSimpleText())return null;const r=n.offset,l=o.getTextContent().slice(0,r),i=t.replaceableString.length,s=r-function(t,e,n){let o=n;for(let n=o;n<=e.length;n++)t.slice(-n)===e.substring(0,n)&&(o=n);return o}(l,t.matchingString,i);if(s<0)return null;let a;return 0===s?[a]=o.splitText(r):[,a]=o.splitText(s,r),a}(d.match):null;g(n,e,t,d.match?d.match.matchingString:"")})},[e,b,d.match,g,t]),k=h(t=>{const n=e.getRootElement();null!==n&&(n.setAttribute("aria-activedescendant","typeahead-item-"+t),N(t))},[e]),D=h(()=>a.current&&m.length?C.createPortal(E("div",{className:"typeahead-popover mentions-menu",children:E("ul",{children:m.map((t,e)=>E(T,{index:e,isSelected:A===e,onClick:()=>{N(e),_(t)},onMouseEnter:()=>{N(e)},option:t},t.key))})}),a.current):null,[a,m,A,_,N]);f(()=>()=>{const t=e.getRootElement();null!==t&&t.removeAttribute("aria-activedescendant")},[e]),R(()=>{null===m?N(null):null===A&&S&&k(0)},[m,A,k,S]),f(()=>v(e.registerCommand(P,({option:t})=>!(!t.ref||null==t.ref.current)&&(I(t.ref.current),!0),x)),[e,k,x]),f(()=>v(e.registerCommand(o,t=>{const n=t;if(null!==m&&m.length){const t=null===A?0:A!==m.length-1?A+1:0;k(t);const o=m[t];if(!o)return k(-1),n.preventDefault(),n.stopImmediatePropagation(),!0;o.ref&&o.ref.current&&e.dispatchCommand(P,{index:t,option:o}),n.preventDefault(),n.stopImmediatePropagation()}return!0},x),e.registerCommand(r,t=>{const e=t;if(null!==m&&m.length){const t=null===A?m.length-1:0!==A?A-1:m.length-1;k(t);const n=m[t];if(!n)return k(-1),e.preventDefault(),e.stopImmediatePropagation(),!0;n.ref&&n.ref.current&&I(n.ref.current),e.preventDefault(),e.stopImmediatePropagation()}return!0},x),e.registerCommand(l,e=>{const n=e;return n.preventDefault(),n.stopImmediatePropagation(),t(),!0},x),e.registerCommand(i,t=>{const e=t;return null!==m&&null!==A&&null!=m[A]&&(e.preventDefault(),e.stopImmediatePropagation(),_(m[A]),!0)},x),e.registerCommand(s,t=>null!==m&&null!==A&&null!=m[A]&&(null!==t&&(t.preventDefault(),t.stopImmediatePropagation()),_(m[A]),!0),x)),[_,t,e,m,A,k,x]);const $=w(()=>({options:m,selectOptionAndCleanUp:_,selectedIndex:A,setHighlightedIndex:N}),[_,A,m]);return null!=p?p(a,$,d.match?d.match.matchingString:""):D()}function _(t,e){null!=e&&(t.className=e),t.setAttribute("aria-label","Typeahead menu"),t.setAttribute("role","listbox"),t.style.display="block",t.style.position="absolute"}const k="\\.,\\+\\*\\?\\$\\@\\|#{}\\(\\)\\^\\-\\[\\]\\\\/!%'\"~=<>_:;";function D(t,e){let n=getComputedStyle(t);const o="absolute"===n.position,r=e?/(auto|scroll|hidden)/:/(auto|scroll)/;if("fixed"===n.position)return document.body;for(let e=t;e=e.parentElement;)if(n=getComputedStyle(e),(!o||"static"!==n.position)&&r.test(n.overflow+n.overflowY+n.overflowX))return e;return document.body}const $=e("SCROLL_TYPEAHEAD_OPTION_INTO_VIEW_COMMAND");function B(t,{minLength:e=1,maxLength:n=75,punctuation:o=k,allowWhitespace:r=!1}){return h(l=>{const i=new RegExp("(^|\\s|\\()(["+t+"]((?:"+("[^"+t+o+(r?"":"\\s")+"]")+"){0,"+n+"}))$").exec(l);if(null!==i){const t=i[1],n=i[3];if(n.length>=e)return{leadOffset:i.index+t.length,matchingString:n,replaceableString:i[2]}}return null},[r,t,o,n,e])}function F({options:e,onQueryChange:o,onSelectOption:r,onOpen:l,onClose:i,menuRenderFn:s,triggerFn:p,anchorClassName:w,commandPriority:v=n,parent:C,preselectFirstItem:b=!0,ignoreEntityBoundary:R=!1}){const[O]=t(),[I,N]=y(null),P=function(e,n,o,r=(S?document.body:void 0),l=!0){const[i]=t(),s=S?document.createElement("div"):null,u=g(s),c=h(()=>{if(null===u.current||void 0===r)return;u.current.style.top=u.current.style.bottom;const t=i.getRootElement(),n=u.current,s=n.firstChild;if(null!==t&&null!==e){const{left:i,top:c,width:a,height:d}=e.getRect(),m=u.current.offsetHeight;if(n.style.top=`${c+m+3+(l?window.pageYOffset:0)}px`,n.style.left=`${i+window.pageXOffset}px`,n.style.height=`${d}px`,n.style.width=`${a}px`,null!==s){s.style.top=`${c}`;const e=s.getBoundingClientRect(),o=e.height,r=e.width,u=t.getBoundingClientRect();i+r>u.right&&(n.style.left=`${u.right-r+window.pageXOffset}px`),(c+o>window.innerHeight||c+o>u.bottom)&&c-u.top>o+d&&(n.style.top=`${c-o-d+(l?window.pageYOffset:0)}px`)}n.isConnected||(_(n,o),r.append(n)),n.setAttribute("id","typeahead-menu"),t.setAttribute("aria-controls","typeahead-menu")}},[i,e,l,o,r]);f(()=>{const t=i.getRootElement();return null!==e&&c(),()=>{null!==t&&t.removeAttribute("aria-controls");const e=u.current;null!==e&&e.isConnected&&(e.remove(),e.removeAttribute("id"))}},[i,c,e]);const a=h(t=>{null!==e&&(t||n(null))},[e,n]);return A(e,u.current,c,a),null!=s&&s===u.current&&(_(s,o),null!=r&&r.append(s)),u}(I,N,w,C),T=h(()=>{N(null),null!=i&&null!==I&&i()},[i,I]),k=h(t=>{N(t),null!=l&&null===I&&l(t)},[l,I]);return f(()=>{const t=O.registerUpdateListener(()=>{O.getEditorState().read(()=>{if(!O.isEditable())return void T();if(O.isComposing())return;const t=O._window||window,e=t.document.createRange(),n=u(),r=function(t){let e=null;return t.getEditorState().read(()=>{const t=u();c(t)&&(e=function(t){const e=t.anchor;if("text"!==e.type)return null;const n=e.getNode();if(!n.isSimpleText())return null;const o=e.offset;return n.getTextContent().slice(0,o)}(t))}),e}(O);if(!c(n)||!n.isCollapsed()||null===r||null===e)return void T();const l=p(r,O);if(o(l?l.matchingString:null),null!==l&&(R||!function(t,e){return 0===e&&t.getEditorState().read(()=>{const t=u();if(c(t)){const e=t.anchor.getNode().getPreviousSibling();return a(e)&&e.isTextEntity()}return!1})}(O,l.leadOffset))){const n=function(t,e,n){const o=d(n);if(null===o||!o.isCollapsed)return!1;const r=o.anchorNode,l=t,i=o.anchorOffset;if(null==r||null==i)return!1;try{e.setStart(r,l),e.setEnd(r,i)}catch(t){return!1}return!0}(l.leadOffset,e,t);if(null!==n)return i=()=>k({getRect:()=>e.getBoundingClientRect(),match:l}),void(x in m?m[x](i):i())}var i;T()})});return()=>{t()}},[O,p,o,I,T,k,R]),f(()=>O.registerEditableListener(t=>{t||T()}),[O,T]),null===I||null===O||null===P.current?null:E(L,{close:T,resolution:I,editor:O,anchorElementRef:P,options:e,menuRenderFn:s,shouldSplitNodeWithQuery:!0,onSelectOption:r,commandPriority:v,preselectFirstItem:b})}export{F as LexicalTypeaheadMenuPlugin,O as MenuOption,k as PUNCTUATION,$ as SCROLL_TYPEAHEAD_OPTION_INTO_VIEW_COMMAND,D as getScrollParent,B as useBasicTypeaheadTriggerMatch,A as useDynamicPositioning};