UNPKG

@lexical/clipboard

Version:

This package provides the copy/paste functionality for Lexical.

10 lines (8 loc) • 9.96 kB
/** * 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{configExtension as t,$getPeerDependency as e,getPeerDependencyFromEditor as n}from"@lexical/extension";import{$generateNodesFromDOMViaExtension as r,contextValue as o,ImportSource as i,ImportSourceDataTransfer as l,$generateNodesFromDOM as c,$generateHtmlFromNodes as s}from"@lexical/html";import{$sliceSelectedTextNodeContent as u}from"@lexical/selection";import{objectKlassEquals as a}from"@lexical/utils";import{defineExtension as f,$getEditor as p,shallowMergeConfig as d,safeCast as m,$isRangeSelection as y,tokenizeRawText as x,$getSelection as g,$createTabNode as h,$getRoot as T,$parseSerializedNode as v,SELECTION_INSERT_CLIPBOARD_NODES_COMMAND as w,getDOMSelection as M,COPY_COMMAND as b,COMMAND_PRIORITY_CRITICAL as $,$isElementNode as D,$isTextNode as C,$splitAtPointCaretNext as P,$setSelectionFromCaretRange as S,$getCollapsedCaretRange as N,$caretFromPoint as O,$isTextPointCaret as R,$getCaretRange as A,$getChildCaret as E,isSelectionWithinEditor as F,$getNearestNodeFromDOMNode as K,$getTextPointCaret as _,$getChildCaretAtIndex as j,$getCaretRangeInDirection as I,$caretRangeFromSelection as J,$comparePointCaretNext as L}from"lexical";function k(t,e){if(void 0!==document.caretRangeFromPoint){const n=document.caretRangeFromPoint(t,e);return null===n?null:{node:n.startContainer,offset:n.startOffset}}if("undefined"!==document.caretPositionFromPoint){const n=document.caretPositionFromPoint(t,e);return null===n?null:{node:n.offsetNode,offset:n.offset}}return null}function H(t,...e){const n=new URL("https://lexical.dev/docs/error"),r=new URLSearchParams;r.append("code",t);for(const t of e)r.append("v",t);throw n.search=r.toString(),Error(`Minified Lexical error #${t}; visit ${n.toString()} for the full message or use the non-minified dev environment for full errors and additional helpful warnings.`)}const U={"application/x-lexical-editor":0,"text/html":10,"text/plain":20,"text/uri-list":30};function W(t){if(window.trustedTypes&&window.trustedTypes.createPolicy){return window.trustedTypes.createPolicy("lexical",{createHTML:t=>t}).createHTML(t)}return t}const q=(t,e)=>{if(!y(e))return e.insertRawText(t),!0;const n=t=>{const e=g();y(e)&&t(e)};return x(t,{linebreak:()=>n(t=>t.insertParagraph()),tab:()=>n(t=>t.insertNodes([h()])),text:t=>n(e=>e.insertText(t))}),!0},z={"application/x-lexical-editor":[(t,e,n)=>{try{const n=p(),r=JSON.parse(t);if(r&&r.namespace===n._config.namespace&&Array.isArray(r.nodes)){return ct(n,at(r.nodes),e),!0}}catch(t){console.error(t)}return n()}],"text/html":[(t,e,n)=>{try{const n=p(),r=(new DOMParser).parseFromString(W(t),"text/html");return ct(n,c(n,r),e),!0}catch(t){return console.error(t),n()}}],"text/plain":[q],"text/uri-list":[q]};function B(t,e,n,r){if(!t)return!1;const o=i=>!!t[i]&&t[i](e,n,o.bind(null,i-1),r);return o(t.length-1)}function G(t,e,n){const r=e.getData("text/plain");for(const o of function(t){return Object.keys(t.$importMimeType).filter(e=>void 0!==t.$importMimeType[e]).sort((e,n)=>{const r=t.priority[e],o=t.priority[n];return void 0===r&&void 0===o?e<n?-1:e>n?1:0:void 0===r?1:void 0===o?-1:r-o})}(t)){const i=e.getData(o);if(i&&(("text/html"!==o||i!==r)&&B(t.$importMimeType[o],i,n,e)))return!0}return!1}const X={$importMimeType:z,$insertDataTransfer:(t,e)=>G({$importMimeType:z,priority:U},t,e),priority:U};const Y=f({build:(t,e)=>({$importMimeType:e.$importMimeType,$insertDataTransfer:(t,n)=>G(e,t,n),priority:e.priority}),config:m({$importMimeType:z,priority:U}),mergeConfig(t,e){const n=d(t,e);if(e.$importMimeType){const r={...t.$importMimeType};for(const[t,n]of Object.entries(e.$importMimeType))if(n){const e=r[t];r[t]=e?[...e,...n]:n}n.$importMimeType=r}return e.priority&&(n.priority={...t.priority,...e.priority}),n},name:"@lexical/clipboard/Import"}),Q=f({dependencies:[t(Y,{$importMimeType:{"text/html":[(t,e,n,c)=>{const s=(new DOMParser).parseFromString(W(t),"text/html"),u=r(s,{context:[o(i,"paste"),o(l,c)]});return ct(p(),u,e),!0}]}})],name:"@lexical/clipboard/DOMImport"});function V(t,e=g()){return null==e&&H(166),y(e)&&e.isCollapsed()||0===e.getNodes().length?"":s(t,e)}function Z(t,e=g()){return null==e&&H(166),y(e)&&e.isCollapsed()||0===e.getNodes().length?null:JSON.stringify(ut(t,e))}function tt(t,e){const n=t.getData("text/plain")||t.getData("text/uri-list");null!=n&&e.insertRawText(n)}function et(t,n,r){(function(){const t=e(Y.name);return t?t.output:X})().$insertDataTransfer(t,n)}const nt="application/x-lexical-drag";function rt(t,e){const n={editorKey:e.getKey()};t.setData(nt,JSON.stringify(n))}function ot(t,e,n){const r=t.dataTransfer;if(null===r)return!1;const o=function(t){const e=t.getData(nt);if(!e)return null;let n;try{n=JSON.parse(e)}catch(t){return null}return null!==(r=n)&&"object"==typeof r&&"editorKey"in r&&"string"==typeof r.editorKey?n:null;var r}(r);if(null===o)return!1;const i=function(t){const e=k(t.clientX,t.clientY);if(null===e)return null;const n=K(e.node);if(null===n)return null;if(C(n))return _(n,"next",e.offset);if(D(n))return j(n,e.offset,"next");const r=n.getParent();return null===r?null:j(r,n.getIndexWithinParent()+1,"next")}(t);if(null===i)return!1;const l=P(i);if(null===l)return!1;const c=o.editorKey===e.getKey(),s=g();if(c){if(!y(s)||s.isCollapsed())return!1;if(function(t,e){const{anchor:n,focus:r}=I(J(e),"next");return L(n,t)<0&&L(t,r)<0}(i,s))return t.preventDefault(),!0;s.removeText()}if(!l.origin.isAttached())return t.preventDefault(),!0;if(n(r,S(N(l)),e),!c){const t=e.getRootElement(),n=t?t.ownerDocument:null,r=n?function(t,e){const n=e.querySelectorAll('[data-lexical-editor="true"]');for(const e of Array.from(n)){const n=e.__lexicalEditor;if(n&&n.getKey()===t)return e}return null}(o.editorKey,n):null;null!==r&&r.dispatchEvent(new InputEvent("beforeinput",{bubbles:!0,cancelable:!0,inputType:"deleteByDrag"}))}return t.preventDefault(),!0}function it(t,e){return ot(t,e,et)}function lt(t,e){return ot(t,e,(t,e)=>tt(t,e))}function ct(t,e,n){t.dispatchCommand(w,{nodes:e,selection:n})||(n.insertNodes(e),function(t){if(y(t)&&t.isCollapsed()){const e=t.anchor;let n=null;const r=O(e,"previous");if(r)if(R(r))n=r.origin;else{const t=A(r,E(T(),"next").getFlipped());for(const e of t){if(C(e.origin)){n=e.origin;break}if(D(e.origin)&&!e.origin.isInline())break}}if(n&&C(n)){const e=n.getFormat(),r=n.getStyle();t.format===e&&t.style===r||(t.format=e,t.style=r,t.dirty=!0)}}}(n))}function st(t,e,n,r=[]){let o=null===e||n.isSelected(e);const i=D(n)&&n.excludeFromCopy("html");let l=n;null!==e&&C(l)&&(l=u(e,l,"clone"));const c=D(l)?l.getChildren():[],s=function(t){const e=t.exportJSON(),n=t.constructor;if(e.type!==n.getType()&&H(58,n.name),D(t)){const t=e.children;Array.isArray(t)||H(59,n.name)}return e}(l);C(l)&&0===l.getTextContentSize()&&(o=!1);for(let r=0;r<c.length;r++){const i=c[r],l=st(t,e,i,s.children);!o&&D(n)&&l&&n.extractWithChild(i,e,"clone")&&(o=!0)}if(o&&!i)r.push(s);else if(Array.isArray(s.children))for(let t=0;t<s.children.length;t++){const e=s.children[t];r.push(e)}return o}function ut(t,e){const n=[],r=T().getChildren();for(let o=0;o<r.length;o++){st(t,e,r[o],n)}return{namespace:t._config.namespace,nodes:n}}function at(t){const e=[];for(const n of t)e.push(v(n));return e}let ft=null;async function pt(t,e,n){if(null!==ft)return!1;if(null!==e)return new Promise((r,o)=>{t.update(()=>{r(dt(t,e,n))})});const r=t.getRootElement(),o=t._window||window,i=o.document,l=M(o);if(null===r||null===l)return!1;const c=i.createElement("span");c.style.position="fixed",c.style.top="-1000px",c.append(i.createTextNode("#")),r.append(c);const s=new Range;return s.setStart(c,0),s.setEnd(c,1),l.removeAllRanges(),l.addRange(s),new Promise((e,r)=>{const l=t.registerCommand(b,r=>(a(r,ClipboardEvent)&&(l(),null!==ft&&(o.clearTimeout(ft),ft=null),e(dt(t,r,n))),!0),$);ft=o.setTimeout(()=>{l(),ft=null,e(!1)},50),i.execCommand("copy"),c.remove()})}function dt(t,e,n){if(void 0===n){const e=M(t._window),r=g();if(!r||r.isCollapsed())return!1;if(!e)return!1;const o=e.anchorNode,i=e.focusNode;if(null!==o&&null!==i&&!F(t,o,i))return!1;n=yt(r)}e.preventDefault();const r=e.clipboardData;return null!==r&&(xt(r,n),!0)}const mt=[["text/html",V],["application/x-lexical-editor",Z]];function yt(t=g()){return function(t,e){const n={"text/plain":""};for(const[r,o]of Object.entries(t))if(o){const t=Tt(o,e);null!==t&&(n[r]=t)}return n}(gt(),t)}function xt(t,e){for(const[n]of mt)void 0===e[n]&&t.setData(n,"");for(const n in e){const r=e[n];void 0!==r&&t.setData(n,r)}}function gt(t=p()){const e=n(t,wt.name);return e?e.output:ht}const ht={"application/x-lexical-editor":[(t,e)=>t?Z(p(),t):e()],"text/html":[(t,e)=>t?V(p(),t):e()],"text/plain":[(t,e)=>t?t.getTextContent():e()]};function Tt(t,e){const n=r=>t[r]?t[r](e,n.bind(null,r-1)):null;return n(t.length-1)}function vt(t,e=g()){return Tt(gt()[t]||[],e)}const wt=f({build:(t,e,n)=>e.$exportMimeType,config:m({$exportMimeType:ht}),mergeConfig(t,e){const n=d(t,e);if(e.$exportMimeType){const r={...t.$exportMimeType};for(const[t,n]of Object.entries(e.$exportMimeType))if(n){const e=r[t];r[t]=e?[...e,...n]:n}n.$exportMimeType=r}return n},name:"@lexical/clipboard/GetClipboardData"});export{vt as $exportMimeTypeFromSelection,ut as $generateJSONFromSelectedNodes,at as $generateNodesFromSerializedNodes,yt as $getClipboardDataFromSelection,V as $getHtmlContent,Z as $getLexicalContent,lt as $handlePlainTextDrop,it as $handleRichTextDrop,tt as $insertDataTransferForPlainText,et as $insertDataTransferForRichText,ct as $insertGeneratedNodes,rt as $writeDragSourceToDataTransfer,Q as ClipboardDOMImportExtension,Y as ClipboardImportExtension,z as DEFAULT_IMPORT_MIME_TYPE,U as DEFAULT_IMPORT_MIME_TYPE_PRIORITY,wt as GetClipboardDataExtension,k as caretFromPoint,pt as copyToClipboard,xt as setLexicalClipboardDataTransfer};