@ekz/lexical-link 0.40.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ */
8
+
9
+ 'use strict'
10
+ const EkzLexicalLink = process.env.NODE_ENV !== 'production' ? require('./EkzLexicalLink.dev.js') : require('./EkzLexicalLink.prod.js');
11
+ module.exports = EkzLexicalLink;
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ */
8
+
9
+ import * as modDev from './EkzLexicalLink.dev.mjs';
10
+ import * as modProd from './EkzLexicalLink.prod.mjs';
11
+ const mod = process.env.NODE_ENV !== 'production' ? modDev : modProd;
12
+ export const $createAutoLinkNode = mod.$createAutoLinkNode;
13
+ export const $createLinkNode = mod.$createLinkNode;
14
+ export const $isAutoLinkNode = mod.$isAutoLinkNode;
15
+ export const $isLinkNode = mod.$isLinkNode;
16
+ export const $toggleLink = mod.$toggleLink;
17
+ export const AutoLinkExtension = mod.AutoLinkExtension;
18
+ export const AutoLinkNode = mod.AutoLinkNode;
19
+ export const ClickableLinkExtension = mod.ClickableLinkExtension;
20
+ export const LinkExtension = mod.LinkExtension;
21
+ export const LinkNode = mod.LinkNode;
22
+ export const TOGGLE_LINK_COMMAND = mod.TOGGLE_LINK_COMMAND;
23
+ export const createLinkMatcherWithRegExp = mod.createLinkMatcherWithRegExp;
24
+ export const formatUrl = mod.formatUrl;
25
+ export const registerAutoLink = mod.registerAutoLink;
26
+ export const registerClickableLink = mod.registerClickableLink;
27
+ export const registerLink = mod.registerLink;
28
+ export const toggleLink = mod.toggleLink;
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ */
8
+
9
+ const mod = await (process.env.NODE_ENV !== 'production' ? import('./EkzLexicalLink.dev.mjs') : import('./EkzLexicalLink.prod.mjs'));
10
+ export const $createAutoLinkNode = mod.$createAutoLinkNode;
11
+ export const $createLinkNode = mod.$createLinkNode;
12
+ export const $isAutoLinkNode = mod.$isAutoLinkNode;
13
+ export const $isLinkNode = mod.$isLinkNode;
14
+ export const $toggleLink = mod.$toggleLink;
15
+ export const AutoLinkExtension = mod.AutoLinkExtension;
16
+ export const AutoLinkNode = mod.AutoLinkNode;
17
+ export const ClickableLinkExtension = mod.ClickableLinkExtension;
18
+ export const LinkExtension = mod.LinkExtension;
19
+ export const LinkNode = mod.LinkNode;
20
+ export const TOGGLE_LINK_COMMAND = mod.TOGGLE_LINK_COMMAND;
21
+ export const createLinkMatcherWithRegExp = mod.createLinkMatcherWithRegExp;
22
+ export const formatUrl = mod.formatUrl;
23
+ export const registerAutoLink = mod.registerAutoLink;
24
+ export const registerClickableLink = mod.registerClickableLink;
25
+ export const registerLink = mod.registerLink;
26
+ export const toggleLink = mod.toggleLink;
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ */
8
+
9
+ "use strict";var e=require("@ekz/lexical-utils"),t=require("@ekz/lexical"),n=require("@ekz/lexical-extension");const r=new Set(["http:","https:","mailto:","sms:","tel:"]);class i extends t.ElementNode{__url;__target;__rel;__title;static getType(){return"link"}static clone(e){return new i(e.__url,{rel:e.__rel,target:e.__target,title:e.__title},e.__key)}constructor(e="",t={},n){super(n);const{target:r=null,rel:i=null,title:s=null}=t;this.__url=e,this.__target=r,this.__rel=i,this.__title=s}createDOM(t){const n=document.createElement("a");return this.updateLinkDOM(null,n,t),e.addClassNamesToElement(n,t.theme.link),n}updateLinkDOM(t,n,r){if(e.isHTMLAnchorElement(n)){t&&t.__url===this.__url||(n.href=this.sanitizeUrl(this.__url));for(const e of["target","rel","title"]){const r=`__${e}`,i=this[r];t&&t[r]===i||(i?n[e]=i:n.removeAttribute(e))}}}updateDOM(e,t,n){return this.updateLinkDOM(e,t,n),!1}static importDOM(){return{a:e=>({conversion:s,priority:1})}}static importJSON(e){return l().updateFromJSON(e)}updateFromJSON(e){return super.updateFromJSON(e).setURL(e.url).setRel(e.rel||null).setTarget(e.target||null).setTitle(e.title||null)}sanitizeUrl(e){e=p(e);try{const t=new URL(p(e));if(!r.has(t.protocol))return"about:blank"}catch(t){return e}return e}exportJSON(){return{...super.exportJSON(),rel:this.getRel(),target:this.getTarget(),title:this.getTitle(),url:this.getURL()}}getURL(){return this.getLatest().__url}setURL(e){const t=this.getWritable();return t.__url=e,t}getTarget(){return this.getLatest().__target}setTarget(e){const t=this.getWritable();return t.__target=e,t}getRel(){return this.getLatest().__rel}setRel(e){const t=this.getWritable();return t.__rel=e,t}getTitle(){return this.getLatest().__title}setTitle(e){const t=this.getWritable();return t.__title=e,t}insertNewAfter(e,t=!0){const n=l(this.__url,{rel:this.__rel,target:this.__target,title:this.__title});return this.insertAfter(n,t),n}canInsertTextBefore(){return!1}canInsertTextAfter(){return!1}canBeEmpty(){return!1}isInline(){return!0}extractWithChild(e,n,r){if(!t.$isRangeSelection(n))return!1;const i=n.anchor.getNode(),s=n.focus.getNode();return this.isParentOf(i)&&this.isParentOf(s)&&n.getTextContent().length>0}isEmailURI(){return this.__url.startsWith("mailto:")}isWebSiteURI(){return this.__url.startsWith("https://")||this.__url.startsWith("http://")}}function s(t){let n=null;if(e.isHTMLAnchorElement(t)){const e=t.textContent;(null!==e&&""!==e||t.children.length>0)&&(n=l(t.getAttribute("href")||"",{rel:t.getAttribute("rel"),target:t.getAttribute("target"),title:t.getAttribute("title")}))}return{node:n}}function l(e="",n){return t.$applyNodeReplacement(new i(e,n))}function o(e){return e instanceof i}class a extends i{__isUnlinked;constructor(e="",t={},n){super(e,t,n),this.__isUnlinked=void 0!==t.isUnlinked&&null!==t.isUnlinked&&t.isUnlinked}static getType(){return"autolink"}static clone(e){return new a(e.__url,{isUnlinked:e.__isUnlinked,rel:e.__rel,target:e.__target,title:e.__title},e.__key)}getIsUnlinked(){return this.__isUnlinked}setIsUnlinked(e){const t=this.getWritable();return t.__isUnlinked=e,t}createDOM(e){return this.__isUnlinked?document.createElement("span"):super.createDOM(e)}updateDOM(e,t,n){return super.updateDOM(e,t,n)||e.__isUnlinked!==this.__isUnlinked}static importJSON(e){return u().updateFromJSON(e)}updateFromJSON(e){return super.updateFromJSON(e).setIsUnlinked(e.isUnlinked||!1)}static importDOM(){return null}exportJSON(){return{...super.exportJSON(),isUnlinked:this.__isUnlinked}}insertNewAfter(e,n=!0){const r=this.getParentOrThrow().insertNewAfter(e,n);if(t.$isElementNode(r)){const e=u(this.__url,{isUnlinked:this.__isUnlinked,rel:this.__rel,target:this.__target,title:this.__title});return r.append(e),e}return null}}function u(e="",n){return t.$applyNodeReplacement(new a(e,n))}function c(e){return e instanceof a}const g=t.createCommand("TOGGLE_LINK_COMMAND");function d(e,n){if("element"===e.type){const r=e.getNode();t.$isElementNode(r)||function(e,...t){const n=new URL("https://lexical.dev/docs/error"),r=new URLSearchParams;r.append("code",e);for(const e of t)r.append("v",e);throw n.search=r.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.`)}(252);return r.getChildren()[e.offset+n]||null}return null}function f(n,r={}){let i;if(n&&"object"==typeof n){const{url:e,...t}=n;i=e,r={...t,...r}}else i=n;const{target:s,title:a}=r,u=void 0===r.rel?"noreferrer":r.rel,g=t.$getSelection();if(null===g||!t.$isRangeSelection(g)&&!t.$isNodeSelection(g))return;if(t.$isNodeSelection(g)){const t=g.getNodes();if(0===t.length)return;return void t.forEach(t=>{if(null===i){const n=e.$findMatchingParent(t,e=>!c(e)&&o(e));n&&(n.insertBefore(t),0===n.getChildren().length&&n.remove())}else{const n=e.$findMatchingParent(t,e=>!c(e)&&o(e));if(n)n.setURL(i),void 0!==s&&n.setTarget(s),void 0!==u&&n.setRel(u);else{const e=l(i,{rel:u,target:s});t.insertBefore(e),e.append(t)}}})}const f=g.extract();if(null===i){const e=new Set;return void f.forEach(t=>{const n=t.getParent();if(o(n)&&!c(n)){const t=n.getKey();if(e.has(t))return;!function(e,t){const n=new Set(t.filter(t=>e.isParentOf(t)).map(e=>e.getKey())),r=e.getChildren(),i=r.filter(e=>n.has(e.getKey()));if(i.length===r.length)return r.forEach(t=>e.insertBefore(t)),void e.remove();const s=r.findIndex(e=>n.has(e.getKey())),o=r.findLastIndex(e=>n.has(e.getKey())),a=0===s,u=o===r.length-1;if(a)i.forEach(t=>e.insertBefore(t));else if(u)for(let t=i.length-1;t>=0;t--)e.insertAfter(i[t]);else{for(let t=i.length-1;t>=0;t--)e.insertAfter(i[t]);const t=r.slice(o+1);if(t.length>0){const n=l(e.getURL(),{rel:e.getRel(),target:e.getTarget(),title:e.getTitle()});i[i.length-1].insertAfter(n),t.forEach(e=>n.append(e))}}}(n,f),e.add(t)}})}const h=new Set,p=e=>{h.has(e.getKey())||(h.add(e.getKey()),e.setURL(i),void 0!==s&&e.setTarget(s),void 0!==u&&e.setRel(u),void 0!==a&&e.setTitle(a))};if(1===f.length){const t=f[0],n=e.$findMatchingParent(t,o);if(null!==n)return p(n)}!function(e){const n=t.$getSelection();if(!t.$isRangeSelection(n))return e();const r=t.$normalizeSelection__EXPERIMENTAL(n),i=r.isBackward(),s=d(r.anchor,i?-1:0),l=d(r.focus,i?0:-1),o=e();if(s||l){const e=t.$getSelection();if(t.$isRangeSelection(e)){const n=e.clone();if(s){const e=s.getParent();e&&n.anchor.set(e.getKey(),s.getIndexWithinParent()+(i?1:0),"element")}if(l){const e=l.getParent();e&&n.focus.set(e.getKey(),l.getIndexWithinParent()+(i?0:1),"element")}t.$setSelection(t.$normalizeSelection__EXPERIMENTAL(n))}}}(()=>{let n=null;for(const r of f){if(!r.isAttached())continue;const g=e.$findMatchingParent(r,o);if(g){p(g);continue}if(t.$isElementNode(r)){if(!r.isInline())continue;if(o(r)){if(!(c(r)||null!==n&&n.getParentOrThrow().isParentOf(r))){p(r),n=r;continue}for(const e of r.getChildren())r.insertBefore(e);r.remove();continue}}const d=r.getPreviousSibling();o(d)&&d.is(n)?d.append(r):(n=l(i,{rel:u,target:s,title:a}),r.insertAfter(n),n.append(r))}})}const h=/^\+?[0-9\s()-]{5,}$/;function p(e){return e.match(/^[a-z][a-z0-9+.-]*:/i)||e.match(/^[/#.]/)?e:e.includes("@")?`mailto:${e}`:h.test(e)?`tel:${e}`:`https://${e}`}const _={attributes:void 0,validateUrl:void 0};function m(r,i){return e.mergeRegister(n.effect(()=>r.registerCommand(g,e=>{const t=i.validateUrl.peek(),n=i.attributes.peek();if(null===e)return f(null),!0;if("string"==typeof e)return!(void 0!==t&&!t(e))&&(f(e,n),!0);{const{url:t,target:r,rel:i,title:s}=e;return f(t,{...n,rel:i,target:r,title:s}),!0}},t.COMMAND_PRIORITY_LOW)),n.effect(()=>{const n=i.validateUrl.value;if(!n)return;const s=i.attributes.value;return r.registerCommand(t.PASTE_COMMAND,i=>{const l=t.$getSelection();if(!t.$isRangeSelection(l)||l.isCollapsed()||!e.objectKlassEquals(i,ClipboardEvent))return!1;if(null===i.clipboardData)return!1;const o=i.clipboardData.getData("text");return!!n(o)&&(!l.getNodes().some(e=>t.$isElementNode(e))&&(r.dispatchCommand(g,{...s,url:o}),i.preventDefault(),!0))},t.COMMAND_PRIORITY_LOW)}))}const x=t.defineExtension({build:(e,t,r)=>n.namedSignals(t),config:_,mergeConfig(e,n){const r=t.shallowMergeConfig(e,n);return e.attributes&&(r.attributes=t.shallowMergeConfig(e.attributes,r.attributes)),r},name:"@ekz/lexical-link/Link",nodes:()=>[i],register:(e,t,n)=>m(e,n.getOutput())});function k(n,r,i={}){const s=i=>{const s=i.target;if(!t.isDOMNode(s))return;const l=t.getNearestEditorFromDOMNode(s);if(null===l)return;let a=null,u=null;if(l.update(()=>{const n=t.$getNearestNodeFromDOMNode(s);if(null!==n){const i=e.$findMatchingParent(n,t.$isElementNode);if(!r.disabled.peek())if(o(i))a=i.sanitizeUrl(i.getURL()),u=i.getTarget();else{const t=function(e,t){let n=e;for(;null!=n;){if(t(n))return n;n=n.parentNode}return null}(s,e.isHTMLAnchorElement);null!==t&&(a=t.href,u=t.target)}}}),null===a||""===a)return;const c=n.getEditorState().read(t.$getSelection);if(t.$isRangeSelection(c)&&!c.isCollapsed())return void i.preventDefault();const g="auxclick"===i.type&&1===i.button;window.open(a,r.newTab.peek()||g||i.metaKey||i.ctrlKey||"_blank"===u?"_blank":"_self"),i.preventDefault()},l=e=>{1===e.button&&s(e)};return n.registerRootListener((e,t)=>{null!==t&&(t.removeEventListener("click",s),t.removeEventListener("mouseup",l)),null!==e&&(e.addEventListener("click",s,i),e.addEventListener("mouseup",l,i))})}const N=t.defineExtension({build:(e,t,r)=>n.namedSignals(t),config:t.safeCast({disabled:!1,newTab:!1}),dependencies:[x],name:"@ekz/lexical-link/ClickableLink",register:(e,t,n)=>k(e,n.getOutput())});function L(e,t){for(let n=0;n<t.length;n++){const r=t[n](e);if(r)return r}return null}const T=/[.,;\s]/;function S(e){return T.test(e)}function $(e){return S(e[e.length-1])}function b(e){return S(e[0])}function U(e){let n=e.getPreviousSibling();return t.$isElementNode(n)&&(n=n.getLastDescendant()),null===n||t.$isLineBreakNode(n)||t.$isTextNode(n)&&$(n.getTextContent())}function R(e){let n=e.getNextSibling();return t.$isElementNode(n)&&(n=n.getFirstDescendant()),null===n||t.$isLineBreakNode(n)||t.$isTextNode(n)&&b(n.getTextContent())}function v(e,t,n,r){if(!(e>0?S(n[e-1]):U(r[0])))return!1;return t<n.length?S(n[t]):R(r[r.length-1])}function O(e,t,n){const r=[],i=[],s=[];let l=0,o=0;const a=[...e];for(;a.length>0;){const e=a[0],u=e.getTextContent().length,c=o;o+u<=t?(r.push(e),l+=u):c>=n?s.push(e):i.push(e),o+=u,a.shift()}return[l,r,i,s]}function E(e,n,r,i){const s=u(i.url,i.attributes);if(1===e.length){let l,o=e[0];0===n?[l,o]=o.splitText(r):[,l,o]=o.splitText(n,r);const a=t.$createTextNode(i.text);return a.setFormat(l.getFormat()),a.setDetail(l.getDetail()),a.setStyle(l.getStyle()),s.append(a),l.replace(s),o}if(e.length>1){const i=e[0];let l,o=i.getTextContent().length;0===n?l=i:[,l]=i.splitText(n);const a=[];let u;for(let t=1;t<e.length;t++){const n=e[t],i=n.getTextContent().length,s=o;if(s<r)if(o+i<=r)a.push(n);else{const[e,t]=n.splitText(r-s);a.push(e),u=t}o+=i}const c=t.$getSelection(),g=c?c.getNodes().find(t.$isTextNode):void 0,d=t.$createTextNode(l.getTextContent());return d.setFormat(l.getFormat()),d.setDetail(l.getDetail()),d.setStyle(l.getStyle()),s.append(d,...a),g&&g===l&&(t.$isRangeSelection(c)?d.select(c.anchor.offset,c.focus.offset):t.$isNodeSelection(c)&&d.select(0,d.getTextContent().length)),l.replace(s),u}}function C(e,n,r){const i=e.getChildren(),s=i.length;for(let n=0;n<s;n++){const s=i[n];if(!t.$isTextNode(s)||!s.isSimpleText())return M(e),void r(null,e.getURL())}const l=e.getTextContent(),o=L(l,n);if(null===o||o.text!==l)return M(e),void r(null,e.getURL());if(!U(e)||!R(e))return M(e),void r(null,e.getURL());const a=e.getURL();if(a!==o.url&&(e.setURL(o.url),r(o.url,a)),o.attributes){const t=e.getRel();t!==o.attributes.rel&&(e.setRel(o.attributes.rel||null),r(o.attributes.rel||null,t));const n=e.getTarget();n!==o.attributes.target&&(e.setTarget(o.attributes.target||null),r(o.attributes.target||null,n))}}function M(e){const t=e.getChildren();for(let n=t.length-1;n>=0;n--)e.insertAfter(t[n]);return e.remove(),t.map(e=>e.getLatest())}const A={changeHandlers:[],matchers:[]};function D(n,r=A){const{matchers:i,changeHandlers:s}=r,l=(e,t)=>{for(const n of s)n(e,t)};return e.mergeRegister(n.registerNodeTransform(t.TextNode,e=>{const n=e.getParentOrThrow(),r=e.getPreviousSibling();if(c(n)&&!n.getIsUnlinked())C(n,i,l);else if(!o(n)){if(e.isSimpleText()&&(b(e.getTextContent())||!c(r))){const n=function(e){const n=[e];let r=e.getNextSibling();for(;null!==r&&t.$isTextNode(r)&&r.isSimpleText()&&(n.push(r),!/[\s]/.test(r.getTextContent()));)r=r.getNextSibling();return n}(e);!function(e,t,n){let r=[...e];const i=r.map(e=>e.getTextContent()).join("");let s,l=i,o=0;for(;(s=L(l,t))&&null!==s;){const e=s.index,t=e+s.length;if(v(o+e,o+t,i,r)){const[i,,l,a]=O(r,o+e,o+t),u=E(l,o+e-i,o+t-i,s);r=u?[u,...a]:a,n(s.url,null),o=0}else o+=t;l=l.substring(t)}}(n,i,l)}!function(e,t,n){const r=e.getPreviousSibling(),i=e.getNextSibling(),s=e.getTextContent();var l;!c(r)||r.getIsUnlinked()||b(s)&&(l=s,!(r.isEmailURI()?/^\.[a-zA-Z]{2,}/.test(l):/^\.[a-zA-Z0-9]{1,}/.test(l)))||(r.append(e),C(r,t,n),n(null,r.getURL())),!c(i)||i.getIsUnlinked()||$(s)||(M(i),C(i,t,n),n(null,i.getURL()))}(e,i,l)}}),n.registerCommand(g,e=>{const n=t.$getSelection();if(null!==e||!t.$isRangeSelection(n))return!1;return n.extract().forEach(e=>{const t=e.getParent();c(t)&&(t.setIsUnlinked(!t.getIsUnlinked()),t.markDirty())}),!1},t.COMMAND_PRIORITY_LOW))}const y=t.defineExtension({config:A,dependencies:[x],mergeConfig(e,n){const r=t.shallowMergeConfig(e,n);for(const t of["matchers","changeHandlers"]){const i=n[t];Array.isArray(i)&&(r[t]=[...e[t],...i])}return r},name:"@ekz/lexical-link/AutoLink",register:D}),I=f;exports.$createAutoLinkNode=u,exports.$createLinkNode=l,exports.$isAutoLinkNode=c,exports.$isLinkNode=o,exports.$toggleLink=f,exports.AutoLinkExtension=y,exports.AutoLinkNode=a,exports.ClickableLinkExtension=N,exports.LinkExtension=x,exports.LinkNode=i,exports.TOGGLE_LINK_COMMAND=g,exports.createLinkMatcherWithRegExp=function(e,t=e=>e){return n=>{const r=e.exec(n);return null===r?null:{index:r.index,length:r[0].length,text:r[0],url:t(r[0])}}},exports.formatUrl=p,exports.registerAutoLink=D,exports.registerClickableLink=k,exports.registerLink=m,exports.toggleLink=I;
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ */
8
+
9
+ import{addClassNamesToElement as t,isHTMLAnchorElement as e,$findMatchingParent as n,mergeRegister as r,objectKlassEquals as i}from"@ekz/lexical-utils";import{createCommand as l,ElementNode as s,$isRangeSelection as o,$applyNodeReplacement as u,$isElementNode as a,$getSelection as c,$isNodeSelection as g,$normalizeSelection__EXPERIMENTAL as f,$setSelection as d,defineExtension as h,shallowMergeConfig as p,COMMAND_PRIORITY_LOW as _,PASTE_COMMAND as m,safeCast as x,isDOMNode as k,getNearestEditorFromDOMNode as b,$getNearestNodeFromDOMNode as U,TextNode as v,$isTextNode as T,$isLineBreakNode as L,$createTextNode as S}from"@ekz/lexical";import{namedSignals as O,effect as C}from"@ekz/lexical-extension";const R=new Set(["http:","https:","mailto:","sms:","tel:"]);class y extends s{__url;__target;__rel;__title;static getType(){return"link"}static clone(t){return new y(t.__url,{rel:t.__rel,target:t.__target,title:t.__title},t.__key)}constructor(t="",e={},n){super(n);const{target:r=null,rel:i=null,title:l=null}=e;this.__url=t,this.__target=r,this.__rel=i,this.__title=l}createDOM(e){const n=document.createElement("a");return this.updateLinkDOM(null,n,e),t(n,e.theme.link),n}updateLinkDOM(t,n,r){if(e(n)){t&&t.__url===this.__url||(n.href=this.sanitizeUrl(this.__url));for(const e of["target","rel","title"]){const r=`__${e}`,i=this[r];t&&t[r]===i||(i?n[e]=i:n.removeAttribute(e))}}}updateDOM(t,e,n){return this.updateLinkDOM(t,e,n),!1}static importDOM(){return{a:t=>({conversion:N,priority:1})}}static importJSON(t){return D().updateFromJSON(t)}updateFromJSON(t){return super.updateFromJSON(t).setURL(t.url).setRel(t.rel||null).setTarget(t.target||null).setTitle(t.title||null)}sanitizeUrl(t){t=K(t);try{const e=new URL(K(t));if(!R.has(e.protocol))return"about:blank"}catch(e){return t}return t}exportJSON(){return{...super.exportJSON(),rel:this.getRel(),target:this.getTarget(),title:this.getTitle(),url:this.getURL()}}getURL(){return this.getLatest().__url}setURL(t){const e=this.getWritable();return e.__url=t,e}getTarget(){return this.getLatest().__target}setTarget(t){const e=this.getWritable();return e.__target=t,e}getRel(){return this.getLatest().__rel}setRel(t){const e=this.getWritable();return e.__rel=t,e}getTitle(){return this.getLatest().__title}setTitle(t){const e=this.getWritable();return e.__title=t,e}insertNewAfter(t,e=!0){const n=D(this.__url,{rel:this.__rel,target:this.__target,title:this.__title});return this.insertAfter(n,e),n}canInsertTextBefore(){return!1}canInsertTextAfter(){return!1}canBeEmpty(){return!1}isInline(){return!0}extractWithChild(t,e,n){if(!o(e))return!1;const r=e.anchor.getNode(),i=e.focus.getNode();return this.isParentOf(r)&&this.isParentOf(i)&&e.getTextContent().length>0}isEmailURI(){return this.__url.startsWith("mailto:")}isWebSiteURI(){return this.__url.startsWith("https://")||this.__url.startsWith("http://")}}function N(t){let n=null;if(e(t)){const e=t.textContent;(null!==e&&""!==e||t.children.length>0)&&(n=D(t.getAttribute("href")||"",{rel:t.getAttribute("rel"),target:t.getAttribute("target"),title:t.getAttribute("title")}))}return{node:n}}function D(t="",e){return u(new y(t,e))}function w(t){return t instanceof y}class A extends y{__isUnlinked;constructor(t="",e={},n){super(t,e,n),this.__isUnlinked=void 0!==e.isUnlinked&&null!==e.isUnlinked&&e.isUnlinked}static getType(){return"autolink"}static clone(t){return new A(t.__url,{isUnlinked:t.__isUnlinked,rel:t.__rel,target:t.__target,title:t.__title},t.__key)}getIsUnlinked(){return this.__isUnlinked}setIsUnlinked(t){const e=this.getWritable();return e.__isUnlinked=t,e}createDOM(t){return this.__isUnlinked?document.createElement("span"):super.createDOM(t)}updateDOM(t,e,n){return super.updateDOM(t,e,n)||t.__isUnlinked!==this.__isUnlinked}static importJSON(t){return I().updateFromJSON(t)}updateFromJSON(t){return super.updateFromJSON(t).setIsUnlinked(t.isUnlinked||!1)}static importDOM(){return null}exportJSON(){return{...super.exportJSON(),isUnlinked:this.__isUnlinked}}insertNewAfter(t,e=!0){const n=this.getParentOrThrow().insertNewAfter(t,e);if(a(n)){const t=I(this.__url,{isUnlinked:this.__isUnlinked,rel:this.__rel,target:this.__target,title:this.__title});return n.append(t),t}return null}}function I(t="",e){return u(new A(t,e))}function E(t){return t instanceof A}const P=l("TOGGLE_LINK_COMMAND");function M(t,e){if("element"===t.type){const n=t.getNode();a(n)||function(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.`)}(252);return n.getChildren()[t.offset+e]||null}return null}function z(t,e={}){let r;if(t&&"object"==typeof t){const{url:n,...i}=t;r=n,e={...i,...e}}else r=t;const{target:i,title:l}=e,s=void 0===e.rel?"noreferrer":e.rel,u=c();if(null===u||!o(u)&&!g(u))return;if(g(u)){const t=u.getNodes();if(0===t.length)return;return void t.forEach(t=>{if(null===r){const e=n(t,t=>!E(t)&&w(t));e&&(e.insertBefore(t),0===e.getChildren().length&&e.remove())}else{const e=n(t,t=>!E(t)&&w(t));if(e)e.setURL(r),void 0!==i&&e.setTarget(i),void 0!==s&&e.setRel(s);else{const e=D(r,{rel:s,target:i});t.insertBefore(e),e.append(t)}}})}const h=u.extract();if(null===r){const t=new Set;return void h.forEach(e=>{const n=e.getParent();if(w(n)&&!E(n)){const e=n.getKey();if(t.has(e))return;!function(t,e){const n=new Set(e.filter(e=>t.isParentOf(e)).map(t=>t.getKey())),r=t.getChildren(),i=r.filter(t=>n.has(t.getKey()));if(i.length===r.length)return r.forEach(e=>t.insertBefore(e)),void t.remove();const l=r.findIndex(t=>n.has(t.getKey())),s=r.findLastIndex(t=>n.has(t.getKey())),o=0===l,u=s===r.length-1;if(o)i.forEach(e=>t.insertBefore(e));else if(u)for(let e=i.length-1;e>=0;e--)t.insertAfter(i[e]);else{for(let e=i.length-1;e>=0;e--)t.insertAfter(i[e]);const e=r.slice(s+1);if(e.length>0){const n=D(t.getURL(),{rel:t.getRel(),target:t.getTarget(),title:t.getTitle()});i[i.length-1].insertAfter(n),e.forEach(t=>n.append(t))}}}(n,h),t.add(e)}})}const p=new Set,_=t=>{p.has(t.getKey())||(p.add(t.getKey()),t.setURL(r),void 0!==i&&t.setTarget(i),void 0!==s&&t.setRel(s),void 0!==l&&t.setTitle(l))};if(1===h.length){const t=h[0],e=n(t,w);if(null!==e)return _(e)}!function(t){const e=c();if(!o(e))return t();const n=f(e),r=n.isBackward(),i=M(n.anchor,r?-1:0),l=M(n.focus,r?0:-1),s=t();if(i||l){const t=c();if(o(t)){const e=t.clone();if(i){const t=i.getParent();t&&e.anchor.set(t.getKey(),i.getIndexWithinParent()+(r?1:0),"element")}if(l){const t=l.getParent();t&&e.focus.set(t.getKey(),l.getIndexWithinParent()+(r?0:1),"element")}d(f(e))}}}(()=>{let t=null;for(const e of h){if(!e.isAttached())continue;const o=n(e,w);if(o){_(o);continue}if(a(e)){if(!e.isInline())continue;if(w(e)){if(!(E(e)||null!==t&&t.getParentOrThrow().isParentOf(e))){_(e),t=e;continue}for(const t of e.getChildren())e.insertBefore(t);e.remove();continue}}const u=e.getPreviousSibling();w(u)&&u.is(t)?u.append(e):(t=D(r,{rel:s,target:i,title:l}),e.insertAfter(t),t.append(e))}})}const J=/^\+?[0-9\s()-]{5,}$/;function K(t){return t.match(/^[a-z][a-z0-9+.-]*:/i)||t.match(/^[/#.]/)?t:t.includes("@")?`mailto:${t}`:J.test(t)?`tel:${t}`:`https://${t}`}function W(t,e){return r(C(()=>t.registerCommand(P,t=>{const n=e.validateUrl.peek(),r=e.attributes.peek();if(null===t)return z(null),!0;if("string"==typeof t)return!(void 0!==n&&!n(t))&&(z(t,r),!0);{const{url:e,target:n,rel:i,title:l}=t;return z(e,{...r,rel:i,target:n,title:l}),!0}},_)),C(()=>{const n=e.validateUrl.value;if(!n)return;const r=e.attributes.value;return t.registerCommand(m,e=>{const l=c();if(!o(l)||l.isCollapsed()||!i(e,ClipboardEvent))return!1;if(null===e.clipboardData)return!1;const s=e.clipboardData.getData("text");return!!n(s)&&(!l.getNodes().some(t=>a(t))&&(t.dispatchCommand(P,{...r,url:s}),e.preventDefault(),!0))},_)}))}const F=h({build:(t,e,n)=>O(e),config:{attributes:void 0,validateUrl:void 0},mergeConfig(t,e){const n=p(t,e);return t.attributes&&(n.attributes=p(t.attributes,n.attributes)),n},name:"@ekz/lexical-link/Link",nodes:()=>[y],register:(t,e,n)=>W(t,n.getOutput())});function B(t,r,i={}){const l=i=>{const l=i.target;if(!k(l))return;const s=b(l);if(null===s)return;let u=null,g=null;if(s.update(()=>{const t=U(l);if(null!==t){const i=n(t,a);if(!r.disabled.peek())if(w(i))u=i.sanitizeUrl(i.getURL()),g=i.getTarget();else{const t=function(t,e){let n=t;for(;null!=n;){if(e(n))return n;n=n.parentNode}return null}(l,e);null!==t&&(u=t.href,g=t.target)}}}),null===u||""===u)return;const f=t.getEditorState().read(c);if(o(f)&&!f.isCollapsed())return void i.preventDefault();const d="auxclick"===i.type&&1===i.button;window.open(u,r.newTab.peek()||d||i.metaKey||i.ctrlKey||"_blank"===g?"_blank":"_self"),i.preventDefault()},s=t=>{1===t.button&&l(t)};return t.registerRootListener((t,e)=>{null!==e&&(e.removeEventListener("click",l),e.removeEventListener("mouseup",s)),null!==t&&(t.addEventListener("click",l,i),t.addEventListener("mouseup",s,i))})}const $=h({build:(t,e,n)=>O(e),config:x({disabled:!1,newTab:!1}),dependencies:[F],name:"@ekz/lexical-link/ClickableLink",register:(t,e,n)=>B(t,n.getOutput())});function H(t,e=t=>t){return n=>{const r=t.exec(n);return null===r?null:{index:r.index,length:r[0].length,text:r[0],url:e(r[0])}}}function j(t,e){for(let n=0;n<e.length;n++){const r=e[n](t);if(r)return r}return null}const G=/[.,;\s]/;function Z(t){return G.test(t)}function q(t){return Z(t[t.length-1])}function Q(t){return Z(t[0])}function V(t){let e=t.getPreviousSibling();return a(e)&&(e=e.getLastDescendant()),null===e||L(e)||T(e)&&q(e.getTextContent())}function X(t){let e=t.getNextSibling();return a(e)&&(e=e.getFirstDescendant()),null===e||L(e)||T(e)&&Q(e.getTextContent())}function Y(t,e,n,r){if(!(t>0?Z(n[t-1]):V(r[0])))return!1;return e<n.length?Z(n[e]):X(r[r.length-1])}function tt(t,e,n){const r=[],i=[],l=[];let s=0,o=0;const u=[...t];for(;u.length>0;){const t=u[0],a=t.getTextContent().length,c=o;o+a<=e?(r.push(t),s+=a):c>=n?l.push(t):i.push(t),o+=a,u.shift()}return[s,r,i,l]}function et(t,e,n,r){const i=I(r.url,r.attributes);if(1===t.length){let l,s=t[0];0===e?[l,s]=s.splitText(n):[,l,s]=s.splitText(e,n);const o=S(r.text);return o.setFormat(l.getFormat()),o.setDetail(l.getDetail()),o.setStyle(l.getStyle()),i.append(o),l.replace(i),s}if(t.length>1){const r=t[0];let l,s=r.getTextContent().length;0===e?l=r:[,l]=r.splitText(e);const u=[];let a;for(let e=1;e<t.length;e++){const r=t[e],i=r.getTextContent().length,l=s;if(l<n)if(s+i<=n)u.push(r);else{const[t,e]=r.splitText(n-l);u.push(t),a=e}s+=i}const f=c(),d=f?f.getNodes().find(T):void 0,h=S(l.getTextContent());return h.setFormat(l.getFormat()),h.setDetail(l.getDetail()),h.setStyle(l.getStyle()),i.append(h,...u),d&&d===l&&(o(f)?h.select(f.anchor.offset,f.focus.offset):g(f)&&h.select(0,h.getTextContent().length)),l.replace(i),a}}function nt(t,e,n){const r=t.getChildren(),i=r.length;for(let e=0;e<i;e++){const i=r[e];if(!T(i)||!i.isSimpleText())return rt(t),void n(null,t.getURL())}const l=t.getTextContent(),s=j(l,e);if(null===s||s.text!==l)return rt(t),void n(null,t.getURL());if(!V(t)||!X(t))return rt(t),void n(null,t.getURL());const o=t.getURL();if(o!==s.url&&(t.setURL(s.url),n(s.url,o)),s.attributes){const e=t.getRel();e!==s.attributes.rel&&(t.setRel(s.attributes.rel||null),n(s.attributes.rel||null,e));const r=t.getTarget();r!==s.attributes.target&&(t.setTarget(s.attributes.target||null),n(s.attributes.target||null,r))}}function rt(t){const e=t.getChildren();for(let n=e.length-1;n>=0;n--)t.insertAfter(e[n]);return t.remove(),e.map(t=>t.getLatest())}const it={changeHandlers:[],matchers:[]};function lt(t,e=it){const{matchers:n,changeHandlers:i}=e,l=(t,e)=>{for(const n of i)n(t,e)};return r(t.registerNodeTransform(v,t=>{const e=t.getParentOrThrow(),r=t.getPreviousSibling();if(E(e)&&!e.getIsUnlinked())nt(e,n,l);else if(!w(e)){if(t.isSimpleText()&&(Q(t.getTextContent())||!E(r))){const e=function(t){const e=[t];let n=t.getNextSibling();for(;null!==n&&T(n)&&n.isSimpleText()&&(e.push(n),!/[\s]/.test(n.getTextContent()));)n=n.getNextSibling();return e}(t);!function(t,e,n){let r=[...t];const i=r.map(t=>t.getTextContent()).join("");let l,s=i,o=0;for(;(l=j(s,e))&&null!==l;){const t=l.index,e=t+l.length;if(Y(o+t,o+e,i,r)){const[i,,s,u]=tt(r,o+t,o+e),a=et(s,o+t-i,o+e-i,l);r=a?[a,...u]:u,n(l.url,null),o=0}else o+=e;s=s.substring(e)}}(e,n,l)}!function(t,e,n){const r=t.getPreviousSibling(),i=t.getNextSibling(),l=t.getTextContent();var s;!E(r)||r.getIsUnlinked()||Q(l)&&(s=l,!(r.isEmailURI()?/^\.[a-zA-Z]{2,}/.test(s):/^\.[a-zA-Z0-9]{1,}/.test(s)))||(r.append(t),nt(r,e,n),n(null,r.getURL())),!E(i)||i.getIsUnlinked()||q(l)||(rt(i),nt(i,e,n),n(null,i.getURL()))}(t,n,l)}}),t.registerCommand(P,t=>{const e=c();if(null!==t||!o(e))return!1;return e.extract().forEach(t=>{const e=t.getParent();E(e)&&(e.setIsUnlinked(!e.getIsUnlinked()),e.markDirty())}),!1},_))}const st=h({config:it,dependencies:[F],mergeConfig(t,e){const n=p(t,e);for(const r of["matchers","changeHandlers"]){const i=e[r];Array.isArray(i)&&(n[r]=[...t[r],...i])}return n},name:"@ekz/lexical-link/AutoLink",register:lt}),ot=z;export{I as $createAutoLinkNode,D as $createLinkNode,E as $isAutoLinkNode,w as $isLinkNode,z as $toggleLink,st as AutoLinkExtension,A as AutoLinkNode,$ as ClickableLinkExtension,F as LinkExtension,y as LinkNode,P as TOGGLE_LINK_COMMAND,H as createLinkMatcherWithRegExp,K as formatUrl,lt as registerAutoLink,B as registerClickableLink,W as registerLink,ot as toggleLink};
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) Meta Platforms, Inc. and affiliates.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ */
8
+ import type { LexicalEditor } from '@ekz/lexical';
9
+ import { type AutoLinkAttributes } from './LexicalLinkNode';
10
+ export type ChangeHandler = (url: string | null, prevUrl: string | null) => void;
11
+ export interface LinkMatcherResult {
12
+ attributes?: AutoLinkAttributes;
13
+ index: number;
14
+ length: number;
15
+ text: string;
16
+ url: string;
17
+ }
18
+ export type LinkMatcher = (text: string) => LinkMatcherResult | null;
19
+ export declare function createLinkMatcherWithRegExp(regExp: RegExp, urlTransformer?: (text: string) => string): (text: string) => {
20
+ index: number;
21
+ length: number;
22
+ text: string;
23
+ url: string;
24
+ } | null;
25
+ export interface AutoLinkConfig {
26
+ matchers: LinkMatcher[];
27
+ changeHandlers: ChangeHandler[];
28
+ }
29
+ export declare function registerAutoLink(editor: LexicalEditor, config?: AutoLinkConfig): () => void;
30
+ /**
31
+ * An extension to automatically create AutoLinkNode from text
32
+ * that matches the configured matchers. No default implementation
33
+ * is provided for any matcher, see {@link createLinkMatcherWithRegExp}
34
+ * for a helper function to create a matcher from a RegExp, and the
35
+ * Playground's [AutoLinkPlugin](https://github.com/facebook/lexical/blob/main/packages/lexical-playground/src/plugins/AutoLinkPlugin/index.tsx)
36
+ * for some example RegExps that could be used.
37
+ *
38
+ * The given `matchers` and `changeHandlers` will be merged by
39
+ * concatenating the configured arrays.
40
+ */
41
+ export declare const AutoLinkExtension: import("@ekz/lexical").LexicalExtension<AutoLinkConfig, "@ekz/lexical-link/AutoLink", unknown, unknown>;
@@ -0,0 +1,168 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @flow strict
8
+ */
9
+ import type {
10
+ DOMConversionMap,
11
+ EditorConfig,
12
+ LexicalNode,
13
+ NodeKey,
14
+ RangeSelection,
15
+ LexicalCommand,
16
+ SerializedElementNode,
17
+ LexicalEditor,
18
+ } from '@ekz/lexical';
19
+ import {addClassNamesToElement} from '@ekz/lexical-utils';
20
+ import {$isElementNode, ElementNode} from '@ekz/lexical';
21
+ import type {LexicalExtension, NamedSignalsOutput} from '@ekz/lexical-extension';
22
+
23
+ export type LinkAttributes = $ReadOnly<{
24
+ rel?: null | string,
25
+ target?: null | string,
26
+ title?: null | string,
27
+ }>;
28
+ export type SerializedLinkNode = {
29
+ ...SerializedElementNode,
30
+ rel?: null | string,
31
+ target?: null | string,
32
+ title?: null | string,
33
+ url: string,
34
+ ...
35
+ };
36
+ declare export class LinkNode extends ElementNode {
37
+ __url: string;
38
+ __target: null | string;
39
+ __rel: null | string;
40
+ __title: null | string;
41
+ static getType(): string;
42
+ static clone(node: LinkNode): LinkNode;
43
+ constructor(url: string, attributes?: LinkAttributes, key?: NodeKey): void;
44
+ createDOM(config: EditorConfig): HTMLElement;
45
+ static importDOM(): DOMConversionMap | null;
46
+ exportJSON(): SerializedLinkNode;
47
+ getURL(): string;
48
+ setURL(url: string): void;
49
+ getTarget(): null | string;
50
+ setTarget(target: null | string): void;
51
+ getRel(): null | string;
52
+ setRel(rel: null | string): void;
53
+ getTitle(): null | string;
54
+ setTitle(title: null | string): void;
55
+ insertNewAfter(
56
+ selection: RangeSelection,
57
+ restoreSelection?: boolean,
58
+ ): null | ElementNode;
59
+ canInsertTextBefore(): false;
60
+ canInsertTextAfter(): false;
61
+ canBeEmpty(): false;
62
+ isInline(): true;
63
+ }
64
+ declare export function $createLinkNode(
65
+ url: string,
66
+ attributes?: LinkAttributes,
67
+ ): LinkNode;
68
+ declare export function $isLinkNode(
69
+ node: ?LexicalNode,
70
+ ): node is LinkNode;
71
+ export type SerializedAutoLinkNode = {
72
+ ...SerializedLinkNode,
73
+ isUnlinked: boolean,
74
+ ...
75
+ };
76
+ declare export class AutoLinkNode extends LinkNode {
77
+ static getType(): string;
78
+ // $FlowFixMe[incompatible-type] clone method inheritance
79
+ static clone(node: AutoLinkNode): AutoLinkNode;
80
+ insertNewAfter(
81
+ selection: RangeSelection,
82
+ restoreSelection?: boolean,
83
+ ): null | ElementNode;
84
+ }
85
+ declare export function $createAutoLinkNode(
86
+ url: string,
87
+ attributes?: LinkAttributes,
88
+ ): AutoLinkNode;
89
+ declare export function $isAutoLinkNode(
90
+ node: ?LexicalNode,
91
+ ): node is AutoLinkNode;
92
+
93
+ declare export var TOGGLE_LINK_COMMAND: LexicalCommand<
94
+ string | {url: string, ...LinkAttributes} | null,
95
+ >;
96
+ declare export function $toggleLink(
97
+ url: null | string,
98
+ attributes: LinkAttributes,
99
+ ): void;
100
+ /** @deprecated renamed to {@link $toggleLink} by @lexical/eslint-plugin rules-of-lexical */
101
+ declare const toggleLink: typeof $toggleLink;
102
+ declare export function formatUrl(url: string): string;
103
+
104
+ export type ClickableLinkConfig = {
105
+ newTab: boolean;
106
+ disabled: boolean;
107
+ }
108
+
109
+ type AddEventListenerOptions = Exclude<AddEventListenerOptionsOrUseCapture, boolean>;
110
+
111
+ declare export function registerClickableLink(
112
+ editor: LexicalEditor,
113
+ stores: NamedSignalsOutput<ClickableLinkConfig>,
114
+ eventOptions?: Pick<AddEventListenerOptions, 'signal'>,
115
+ ): () => void;
116
+
117
+ declare export var ClickableLinkExtension: LexicalExtension<ClickableLinkConfig, "@ekz/lexical-link/ClickableLink", NamedSignalsOutput<ClickableLinkConfig>, void>;
118
+ declare export function registerClickableLink(
119
+ editor: LexicalEditor,
120
+ stores: NamedSignalsOutput<ClickableLinkConfig>,
121
+ eventOptions?: Pick<AddEventListenerOptions, 'signal'>,
122
+ ): () => void;
123
+
124
+ export type AutoLinkConfig = {
125
+ matchers: LinkMatcher[];
126
+ changeHandlers: ChangeHandler[];
127
+ }
128
+
129
+ export type ChangeHandler = (
130
+ url: string | null,
131
+ prevUrl: string | null,
132
+ ) => void;
133
+
134
+ export type AutoLinkAttributes = Partial<
135
+ {...LinkAttributes, isUnlinked?: boolean}
136
+ >;
137
+
138
+ export type LinkMatcherResult = {
139
+ attributes?: AutoLinkAttributes;
140
+ index: number;
141
+ length: number;
142
+ text: string;
143
+ url: string;
144
+ }
145
+
146
+ export type LinkMatcher = (text: string) => LinkMatcherResult | null;
147
+
148
+ declare export function createLinkMatcherWithRegExp(
149
+ regExp: RegExp,
150
+ urlTransformer?: (text: string) => string,
151
+ ): LinkMatcher;
152
+
153
+ declare export var AutoLinkExtension: LexicalExtension<AutoLinkConfig, "@ekz/lexical-link/AutoLink", void, void>;
154
+
155
+ declare export function registerAutoLink(
156
+ editor: LexicalEditor,
157
+ config?: AutoLinkConfig,
158
+ ): () => void;
159
+
160
+ export type LinkConfig = {
161
+ validateUrl: void | ((url: string) => boolean);
162
+ attributes: void | LinkAttributes;
163
+ }
164
+ declare export var LinkExtension: LexicalExtension<LinkConfig, "@ekz/lexical-link/Link", NamedSignalsOutput<LinkConfig>, void>;
165
+ declare export function registerLink(
166
+ editor: LexicalEditor,
167
+ stores: NamedSignalsOutput<LinkConfig>,
168
+ ): () => void;
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ */
8
+ import { NamedSignalsOutput } from '@ekz/lexical-extension';
9
+ import { LexicalEditor } from '@ekz/lexical';
10
+ import { LinkAttributes } from './LexicalLinkNode';
11
+ export interface LinkConfig {
12
+ /**
13
+ * If this function is specified a {@link PASTE_COMMAND}
14
+ * listener will be registered to wrap selected nodes
15
+ * when a URL is pasted and `validateUrl(url)` returns true.
16
+ * The default of `undefined` will not register this listener.
17
+ *
18
+ * In the implementation of {@link TOGGLE_LINK_COMMAND}
19
+ * it will reject URLs that return false when specified.
20
+ * The default of `undefined` will always accept URLs.
21
+ */
22
+ validateUrl: undefined | ((url: string) => boolean);
23
+ /**
24
+ * The default anchor tag attributes to use for
25
+ * {@link TOGGLE_LINK_COMMAND}
26
+ */
27
+ attributes: undefined | LinkAttributes;
28
+ }
29
+ /** @internal */
30
+ export declare function registerLink(editor: LexicalEditor, stores: NamedSignalsOutput<LinkConfig>): () => void;
31
+ /**
32
+ * Provides {@link LinkNode}, an implementation of
33
+ * {@link TOGGLE_LINK_COMMAND}, and a {@link PASTE_COMMAND}
34
+ * listener to wrap selected nodes in a link when a
35
+ * URL is pasted and `validateUrl` is defined.
36
+ */
37
+ export declare const LinkExtension: import("@ekz/lexical").LexicalExtension<LinkConfig, "@ekz/lexical-link/Link", NamedSignalsOutput<LinkConfig>, unknown>;
@@ -0,0 +1,126 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ */
8
+ import type { BaseSelection, DOMConversionMap, EditorConfig, LexicalCommand, LexicalNode, LexicalUpdateJSON, NodeKey, RangeSelection, SerializedElementNode } from '@ekz/lexical';
9
+ import { ElementNode, Spread } from '@ekz/lexical';
10
+ export type LinkAttributes = {
11
+ rel?: null | string;
12
+ target?: null | string;
13
+ title?: null | string;
14
+ };
15
+ export type AutoLinkAttributes = Partial<Spread<LinkAttributes, {
16
+ isUnlinked?: boolean;
17
+ }>>;
18
+ export type SerializedLinkNode = Spread<{
19
+ url: string;
20
+ }, Spread<LinkAttributes, SerializedElementNode>>;
21
+ type LinkHTMLElementType = HTMLAnchorElement | HTMLSpanElement;
22
+ /** @noInheritDoc */
23
+ export declare class LinkNode extends ElementNode {
24
+ /** @internal */
25
+ __url: string;
26
+ /** @internal */
27
+ __target: null | string;
28
+ /** @internal */
29
+ __rel: null | string;
30
+ /** @internal */
31
+ __title: null | string;
32
+ static getType(): string;
33
+ static clone(node: LinkNode): LinkNode;
34
+ constructor(url?: string, attributes?: LinkAttributes, key?: NodeKey);
35
+ createDOM(config: EditorConfig): LinkHTMLElementType;
36
+ updateLinkDOM(prevNode: this | null, anchor: LinkHTMLElementType, config: EditorConfig): void;
37
+ updateDOM(prevNode: this, anchor: LinkHTMLElementType, config: EditorConfig): boolean;
38
+ static importDOM(): DOMConversionMap | null;
39
+ static importJSON(serializedNode: SerializedLinkNode): LinkNode;
40
+ updateFromJSON(serializedNode: LexicalUpdateJSON<SerializedLinkNode>): this;
41
+ sanitizeUrl(url: string): string;
42
+ exportJSON(): SerializedLinkNode | SerializedAutoLinkNode;
43
+ getURL(): string;
44
+ setURL(url: string): this;
45
+ getTarget(): null | string;
46
+ setTarget(target: null | string): this;
47
+ getRel(): null | string;
48
+ setRel(rel: null | string): this;
49
+ getTitle(): null | string;
50
+ setTitle(title: null | string): this;
51
+ insertNewAfter(_: RangeSelection, restoreSelection?: boolean): null | ElementNode;
52
+ canInsertTextBefore(): false;
53
+ canInsertTextAfter(): false;
54
+ canBeEmpty(): false;
55
+ isInline(): true;
56
+ extractWithChild(child: LexicalNode, selection: BaseSelection, destination: 'clone' | 'html'): boolean;
57
+ isEmailURI(): boolean;
58
+ isWebSiteURI(): boolean;
59
+ }
60
+ /**
61
+ * Takes a URL and creates a LinkNode.
62
+ * @param url - The URL the LinkNode should direct to.
63
+ * @param attributes - Optional HTML a tag attributes \\{ target, rel, title \\}
64
+ * @returns The LinkNode.
65
+ */
66
+ export declare function $createLinkNode(url?: string, attributes?: LinkAttributes): LinkNode;
67
+ /**
68
+ * Determines if node is a LinkNode.
69
+ * @param node - The node to be checked.
70
+ * @returns true if node is a LinkNode, false otherwise.
71
+ */
72
+ export declare function $isLinkNode(node: LexicalNode | null | undefined): node is LinkNode;
73
+ export type SerializedAutoLinkNode = Spread<{
74
+ isUnlinked: boolean;
75
+ }, SerializedLinkNode>;
76
+ export declare class AutoLinkNode extends LinkNode {
77
+ /** @internal */
78
+ /** Indicates whether the autolink was ever unlinked. **/
79
+ __isUnlinked: boolean;
80
+ constructor(url?: string, attributes?: AutoLinkAttributes, key?: NodeKey);
81
+ static getType(): string;
82
+ static clone(node: AutoLinkNode): AutoLinkNode;
83
+ getIsUnlinked(): boolean;
84
+ setIsUnlinked(value: boolean): this;
85
+ createDOM(config: EditorConfig): LinkHTMLElementType;
86
+ updateDOM(prevNode: this, anchor: LinkHTMLElementType, config: EditorConfig): boolean;
87
+ static importJSON(serializedNode: SerializedAutoLinkNode): AutoLinkNode;
88
+ updateFromJSON(serializedNode: LexicalUpdateJSON<SerializedAutoLinkNode>): this;
89
+ static importDOM(): null;
90
+ exportJSON(): SerializedAutoLinkNode;
91
+ insertNewAfter(selection: RangeSelection, restoreSelection?: boolean): null | ElementNode;
92
+ }
93
+ /**
94
+ * Takes a URL and creates an AutoLinkNode. AutoLinkNodes are generally automatically generated
95
+ * during typing, which is especially useful when a button to generate a LinkNode is not practical.
96
+ * @param url - The URL the LinkNode should direct to.
97
+ * @param attributes - Optional HTML a tag attributes. \\{ target, rel, title \\}
98
+ * @returns The LinkNode.
99
+ */
100
+ export declare function $createAutoLinkNode(url?: string, attributes?: AutoLinkAttributes): AutoLinkNode;
101
+ /**
102
+ * Determines if node is an AutoLinkNode.
103
+ * @param node - The node to be checked.
104
+ * @returns true if node is an AutoLinkNode, false otherwise.
105
+ */
106
+ export declare function $isAutoLinkNode(node: LexicalNode | null | undefined): node is AutoLinkNode;
107
+ export declare const TOGGLE_LINK_COMMAND: LexicalCommand<string | ({
108
+ url: string;
109
+ } & LinkAttributes) | null>;
110
+ /**
111
+ * Generates or updates a LinkNode. It can also delete a LinkNode if the URL is null,
112
+ * but saves any children and brings them up to the parent node.
113
+ * @param urlOrAttributes - The URL the link directs to, or an attributes object with an url property
114
+ * @param attributes - Optional HTML a tag attributes. \\{ target, rel, title \\}
115
+ */
116
+ export declare function $toggleLink(urlOrAttributes: null | string | (LinkAttributes & {
117
+ url: null | string;
118
+ }), attributes?: LinkAttributes): void;
119
+ /**
120
+ * Formats a URL string by adding appropriate protocol if missing
121
+ *
122
+ * @param url - URL to format
123
+ * @returns Formatted URL with appropriate protocol
124
+ */
125
+ export declare function formatUrl(url: string): string;
126
+ export {};
package/README.md ADDED
@@ -0,0 +1,7 @@
1
+ # `@lexical/link`
2
+
3
+ [![See API Documentation](https://lexical.dev/img/see-api-documentation.svg)](https://lexical.dev/docs/api/modules/lexical_link)
4
+
5
+ This package contains the functionality for Lexical links.
6
+
7
+ More documentation coming soon.
package/index.d.ts ADDED
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ */
8
+ import { $toggleLink } from './LexicalLinkNode';
9
+ export { type ClickableLinkConfig, ClickableLinkExtension, registerClickableLink, } from './ClickableLinkExtension';
10
+ export { type AutoLinkConfig, AutoLinkExtension, type ChangeHandler, createLinkMatcherWithRegExp, type LinkMatcher, registerAutoLink, } from './LexicalAutoLinkExtension';
11
+ export { LinkExtension, registerLink } from './LexicalLinkExtension';
12
+ export { $createAutoLinkNode, $createLinkNode, $isAutoLinkNode, $isLinkNode, $toggleLink, type AutoLinkAttributes, AutoLinkNode, formatUrl, type LinkAttributes, LinkNode, type SerializedAutoLinkNode, type SerializedLinkNode, TOGGLE_LINK_COMMAND, } from './LexicalLinkNode';
13
+ /** @deprecated renamed to {@link $toggleLink} by @lexical/eslint-plugin rules-of-lexical */
14
+ export declare const toggleLink: typeof $toggleLink;
package/package.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "@ekz/lexical-link",
3
+ "description": "This package contains the functionality for Lexical links.",
4
+ "keywords": [
5
+ "lexical",
6
+ "editor",
7
+ "rich-text",
8
+ "link"
9
+ ],
10
+ "license": "MIT",
11
+ "version": "0.40.0",
12
+ "main": "LexicalLink.js",
13
+ "types": "index.d.ts",
14
+ "dependencies": {
15
+ "@ekz/lexical-extension": "0.40.0",
16
+ "@ekz/lexical-utils": "0.40.0",
17
+ "@ekz/lexical": "0.40.0"
18
+ },
19
+ "repository": {
20
+ "type": "git",
21
+ "url": "git+https://github.com/facebook/lexical.git",
22
+ "directory": "packages/lexical-link"
23
+ },
24
+ "module": "LexicalLink.mjs",
25
+ "sideEffects": false,
26
+ "exports": {
27
+ ".": {
28
+ "import": {
29
+ "types": "./index.d.ts",
30
+ "development": "./LexicalLink.dev.mjs",
31
+ "production": "./LexicalLink.prod.mjs",
32
+ "node": "./LexicalLink.node.mjs",
33
+ "default": "./LexicalLink.mjs"
34
+ },
35
+ "require": {
36
+ "types": "./index.d.ts",
37
+ "development": "./LexicalLink.dev.js",
38
+ "production": "./LexicalLink.prod.js",
39
+ "default": "./LexicalLink.js"
40
+ }
41
+ }
42
+ }
43
+ }