@lexical/clipboard
Version:
This package provides the copy/paste functionality for Lexical.
10 lines (8 loc) • 8.43 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.
*
*/
;var e=require("@lexical/extension"),t=require("@lexical/html"),n=require("@lexical/selection"),o=require("@lexical/utils"),r=require("lexical");function i(e,...t){const n=new URL("https://lexical.dev/docs/error"),o=new URLSearchParams;o.append("code",e);for(const e of t)o.append("v",e);throw n.search=o.toString(),Error(`Minified Lexical error #${e}; visit ${n.toString()} for the full message or use the non-minified dev environment for full errors and additional helpful warnings.`)}function l(e,n=r.$getSelection()){return null==n&&i(166),r.$isRangeSelection(n)&&n.isCollapsed()||0===n.getNodes().length?"":t.$generateHtmlFromNodes(e,n)}function s(e,t=r.$getSelection()){return null==t&&i(166),r.$isRangeSelection(t)&&t.isCollapsed()||0===t.getNodes().length?null:JSON.stringify(x(e,t))}function a(e,t){const n=e.getData("text/plain")||e.getData("text/uri-list");null!=n&&t.insertRawText(n)}function c(e,n,o){const i=e.getData("application/x-lexical-editor");if(i)try{const e=JSON.parse(i);if(e.namespace===o._config.namespace&&Array.isArray(e.nodes)){return p(o,m(e.nodes),n)}}catch(e){console.error(e)}const l=e.getData("text/html"),s=e.getData("text/plain");if(l&&s!==l)try{const e=(new DOMParser).parseFromString(function(e){if(window.trustedTypes&&window.trustedTypes.createPolicy){return window.trustedTypes.createPolicy("lexical",{createHTML:e=>e}).createHTML(e)}return e}(l),"text/html");return p(o,t.$generateNodesFromDOM(o,e),n)}catch(e){console.error(e)}const a=s||e.getData("text/uri-list");if(null!=a)if(r.$isRangeSelection(n)){const e=a.split(/(\r?\n|\t)/);""===e[e.length-1]&&e.pop();for(let t=0;t<e.length;t++){const n=r.$getSelection();if(r.$isRangeSelection(n)){const o=e[t];"\n"===o||"\r\n"===o?n.insertParagraph():"\t"===o?n.insertNodes([r.$createTabNode()]):n.insertText(o)}}}else n.insertRawText(a)}const u="application/x-lexical-drag";function d(e){const t=function(e,t){if(void 0!==document.caretRangeFromPoint){const n=document.caretRangeFromPoint(e,t);return null===n?null:{node:n.startContainer,offset:n.startOffset}}if("undefined"!==document.caretPositionFromPoint){const n=document.caretPositionFromPoint(e,t);return null===n?null:{node:n.offsetNode,offset:n.offset}}return null}(e.clientX,e.clientY);if(null===t)return null;const n=r.$getNearestNodeFromDOMNode(t.node);if(null===n)return null;if(r.$isTextNode(n))return r.$getTextPointCaret(n,"next",t.offset);if(r.$isElementNode(n))return r.$getChildCaretAtIndex(n,t.offset,"next");const o=n.getParent();return null===o?null:r.$getChildCaretAtIndex(o,n.getIndexWithinParent()+1,"next")}function f(e,t,n){const o=e.dataTransfer;if(null===o)return!1;const i=function(e){const t=e.getData(u);if(!t)return null;let n;try{n=JSON.parse(t)}catch(e){return null}return null!==(o=n)&&"object"==typeof o&&"editorKey"in o&&"string"==typeof o.editorKey?n:null;var o}(o);if(null===i)return!1;const l=d(e);if(null===l)return!1;const s=r.$splitAtPointCaretNext(l);if(null===s)return!1;const a=i.editorKey===t.getKey(),c=r.$getSelection();if(a){if(!r.$isRangeSelection(c)||c.isCollapsed())return!1;if(function(e,t){const{anchor:n,focus:o}=r.$getCaretRangeInDirection(r.$caretRangeFromSelection(t),"next");return r.$comparePointCaretNext(n,e)<0&&r.$comparePointCaretNext(e,o)<0}(l,c))return e.preventDefault(),!0;c.removeText()}if(!s.origin.isAttached())return e.preventDefault(),!0;if(n(o,r.$setSelectionFromCaretRange(r.$getCollapsedCaretRange(s)),t),!a){const e=t.getRootElement(),n=e?e.ownerDocument:null,o=n?function(e,t){const n=t.querySelectorAll('[data-lexical-editor="true"]');for(const t of Array.from(n)){const n=t.__lexicalEditor;if(n&&n.getKey()===e)return t}return null}(i.editorKey,n):null;null!==o&&o.dispatchEvent(new InputEvent("beforeinput",{bubbles:!0,cancelable:!0,inputType:"deleteByDrag"}))}return e.preventDefault(),!0}function p(e,t,n){e.dispatchCommand(r.SELECTION_INSERT_CLIPBOARD_NODES_COMMAND,{nodes:t,selection:n})||(n.insertNodes(t),function(e){if(r.$isRangeSelection(e)&&e.isCollapsed()){const t=e.anchor;let n=null;const o=r.$caretFromPoint(t,"previous");if(o)if(r.$isTextPointCaret(o))n=o.origin;else{const e=r.$getCaretRange(o,r.$getChildCaret(r.$getRoot(),"next").getFlipped());for(const t of e){if(r.$isTextNode(t.origin)){n=t.origin;break}if(r.$isElementNode(t.origin)&&!t.origin.isInline())break}}if(n&&r.$isTextNode(n)){const t=n.getFormat(),o=n.getStyle();e.format===t&&e.style===o||(e.format=t,e.style=o,e.dirty=!0)}}}(n))}function g(e,t,o,l=[]){let s=null===t||o.isSelected(t);const a=r.$isElementNode(o)&&o.excludeFromCopy("html");let c=o;null!==t&&r.$isTextNode(c)&&(c=n.$sliceSelectedTextNodeContent(t,c,"clone"));const u=r.$isElementNode(c)?c.getChildren():[],d=function(e){const t=e.exportJSON(),n=e.constructor;if(t.type!==n.getType()&&i(58,n.name),r.$isElementNode(e)){const e=t.children;Array.isArray(e)||i(59,n.name)}return t}(c);r.$isTextNode(c)&&0===c.getTextContentSize()&&(s=!1);for(let n=0;n<u.length;n++){const i=u[n],l=g(e,t,i,d.children);!s&&r.$isElementNode(o)&&l&&o.extractWithChild(i,t,"clone")&&(s=!0)}if(s&&!a)l.push(d);else if(Array.isArray(d.children))for(let e=0;e<d.children.length;e++){const t=d.children[e];l.push(t)}return s}function x(e,t){const n=[],o=r.$getRoot().getChildren();for(let r=0;r<o.length;r++){g(e,t,o[r],n)}return{namespace:e._config.namespace,nodes:n}}function m(e){const t=[];for(let o=0;o<e.length;o++){const i=e[o],l=r.$parseSerializedNode(i);r.$isTextNode(l)&&n.$addNodeStyle(l),t.push(l)}return t}let $=null;function h(e,t,n){if(void 0===n){const t=r.getDOMSelection(e._window),o=r.$getSelection();if(!o||o.isCollapsed())return!1;if(!t)return!1;const i=t.anchorNode,l=t.focusNode;if(null!==i&&null!==l&&!r.isSelectionWithinEditor(e,i,l))return!1;n=C(o)}t.preventDefault();const o=t.clipboardData;return null!==o&&(N(o,n),!0)}const y=[["text/html",l],["application/x-lexical-editor",s]];function C(t=r.$getSelection()){return function(e,t){const n={"text/plain":""};for(const[o,r]of Object.entries(e)){const e=S(r,t);null!==e&&(n[o]=e)}return n}(function(){const t=r.$getEditor(),n=e.LexicalBuilder.maybeFromEditor(t);if(n&&n.hasExtensionByName(D.name))return e.getExtensionDependencyFromEditor(t,D).output;return T}(),t)}function N(e,t){for(const[n]of y)void 0===t[n]&&e.setData(n,"");for(const n in t){const o=t[n];void 0!==o&&e.setData(n,o)}}const T={"application/x-lexical-editor":[(e,t)=>e?s(r.$getEditor(),e):t()],"text/html":[(e,t)=>e?l(r.$getEditor(),e):t()],"text/plain":[(e,t)=>e?e.getTextContent():t()]};function S(e,t){const n=o=>e[o]?e[o](t,n.bind(null,o-1)):null;return n(e.length-1)}const D=r.defineExtension({build:(e,t,n)=>t.$exportMimeType,config:r.safeCast({$exportMimeType:T}),mergeConfig(e,t){const n=r.shallowMergeConfig(e,t);if(t.$exportMimeType){const o={...e.$exportMimeType};for(const[e,n]of Object.entries(t.$exportMimeType))o[e]=[...o[e],...n];n.$exportMimeType=o}return n},name:"@lexical/clipboard/GetClipboardData"});exports.$generateJSONFromSelectedNodes=x,exports.$generateNodesFromSerializedNodes=m,exports.$getClipboardDataFromSelection=C,exports.$getHtmlContent=l,exports.$getLexicalContent=s,exports.$handlePlainTextDrop=function(e,t){return f(e,t,(e,t)=>a(e,t))},exports.$handleRichTextDrop=function(e,t){return f(e,t,c)},exports.$insertDataTransferForPlainText=a,exports.$insertDataTransferForRichText=c,exports.$insertGeneratedNodes=p,exports.$writeDragSourceToDataTransfer=function(e,t){const n={editorKey:t.getKey()};e.setData(u,JSON.stringify(n))},exports.copyToClipboard=async function(e,t,n){if(null!==$)return!1;if(null!==t)return new Promise((o,r)=>{e.update(()=>{o(h(e,t,n))})});const i=e.getRootElement(),l=e._window||window,s=l.document,a=r.getDOMSelection(l);if(null===i||null===a)return!1;const c=s.createElement("span");c.style.position="fixed",c.style.top="-1000px",c.append(s.createTextNode("#")),i.append(c);const u=new Range;return u.setStart(c,0),u.setEnd(c,1),a.removeAllRanges(),a.addRange(u),new Promise((t,i)=>{const a=e.registerCommand(r.COPY_COMMAND,r=>(o.objectKlassEquals(r,ClipboardEvent)&&(a(),null!==$&&(l.clearTimeout($),$=null),t(h(e,r,n))),!0),r.COMMAND_PRIORITY_CRITICAL);$=l.setTimeout(()=>{a(),$=null,t(!1)},50),s.execCommand("copy"),c.remove()})},exports.setLexicalClipboardDataTransfer=N;