@lexical/link 0.44.1-nightly.20260519.0 → 0.45.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.
- package/{LexicalLink.dev.js → dist/LexicalLink.dev.js} +56 -0
- package/{LexicalLink.dev.mjs → dist/LexicalLink.dev.mjs} +56 -2
- package/{LexicalLink.mjs → dist/LexicalLink.mjs} +2 -0
- package/{LexicalLink.node.mjs → dist/LexicalLink.node.mjs} +2 -0
- package/dist/LexicalLink.prod.js +9 -0
- package/dist/LexicalLink.prod.mjs +9 -0
- package/dist/LinkImportExtension.d.ts +22 -0
- package/{index.d.ts → dist/index.d.ts} +1 -0
- package/package.json +33 -17
- package/src/ClickableLinkExtension.ts +142 -0
- package/src/LexicalAutoLinkExtension.ts +639 -0
- package/src/LexicalLinkExtension.ts +156 -0
- package/src/LexicalLinkNode.ts +964 -0
- package/src/LinkImportExtension.ts +68 -0
- package/src/index.ts +43 -0
- package/LexicalLink.prod.js +0 -9
- package/LexicalLink.prod.mjs +0 -9
- /package/{ClickableLinkExtension.d.ts → dist/ClickableLinkExtension.d.ts} +0 -0
- /package/{LexicalAutoLinkExtension.d.ts → dist/LexicalAutoLinkExtension.d.ts} +0 -0
- /package/{LexicalLink.js → dist/LexicalLink.js} +0 -0
- /package/{LexicalLink.js.flow → dist/LexicalLink.js.flow} +0 -0
- /package/{LexicalLinkExtension.d.ts → dist/LexicalLinkExtension.d.ts} +0 -0
- /package/{LexicalLinkNode.d.ts → dist/LexicalLinkNode.d.ts} +0 -0
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
var utils = require('@lexical/utils');
|
|
12
12
|
var lexical = require('lexical');
|
|
13
13
|
var extension = require('@lexical/extension');
|
|
14
|
+
var html = require('@lexical/html');
|
|
14
15
|
|
|
15
16
|
/**
|
|
16
17
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
@@ -1344,6 +1345,59 @@ const AutoLinkExtension = lexical.defineExtension({
|
|
|
1344
1345
|
register: registerAutoLink
|
|
1345
1346
|
});
|
|
1346
1347
|
|
|
1348
|
+
/**
|
|
1349
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
1350
|
+
*
|
|
1351
|
+
* This source code is licensed under the MIT license found in the
|
|
1352
|
+
* LICENSE file in the root directory of this source tree.
|
|
1353
|
+
*
|
|
1354
|
+
*/
|
|
1355
|
+
|
|
1356
|
+
const AnchorRule = html.defineImportRule({
|
|
1357
|
+
$import: (ctx, el) => {
|
|
1358
|
+
if (!el.textContent && el.children.length === 0) {
|
|
1359
|
+
return [];
|
|
1360
|
+
}
|
|
1361
|
+
// Use no schema here: when the `<a>` contains block descendants
|
|
1362
|
+
// (e.g. `<a><h1>x</h1><div>y</div></a>`), we want them lifted so each
|
|
1363
|
+
// block becomes a sibling at the link's level, with the link wrapping
|
|
1364
|
+
// its inline content. $distributeInlineWrapper handles both the
|
|
1365
|
+
// common all-inline case (single LinkNode wrapping the run) and the
|
|
1366
|
+
// mixed-block case (per-block recursion).
|
|
1367
|
+
const href = el.getAttribute('href') || '';
|
|
1368
|
+
const attrs = {
|
|
1369
|
+
rel: el.getAttribute('rel'),
|
|
1370
|
+
target: el.getAttribute('target'),
|
|
1371
|
+
title: el.getAttribute('title')
|
|
1372
|
+
};
|
|
1373
|
+
return html.$distributeInlineWrapper(ctx.$importChildren(el), () => $createLinkNode(href, attrs));
|
|
1374
|
+
},
|
|
1375
|
+
match: html.sel.tag('a'),
|
|
1376
|
+
name: '@lexical/link/a'
|
|
1377
|
+
});
|
|
1378
|
+
|
|
1379
|
+
/**
|
|
1380
|
+
* Import rules for {@link LinkNode}.
|
|
1381
|
+
*
|
|
1382
|
+
* @experimental
|
|
1383
|
+
*/
|
|
1384
|
+
const LinkImportRules = [AnchorRule];
|
|
1385
|
+
|
|
1386
|
+
/**
|
|
1387
|
+
* Bundles {@link LinkImportRules} (plus {@link CoreImportExtension}) into
|
|
1388
|
+
* a single dependency. Equivalent to the legacy
|
|
1389
|
+
* `LinkNode.importDOM` registration on the new
|
|
1390
|
+
* {@link DOMImportExtension} pipeline.
|
|
1391
|
+
*
|
|
1392
|
+
* @experimental
|
|
1393
|
+
*/
|
|
1394
|
+
const LinkImportExtension = lexical.defineExtension({
|
|
1395
|
+
dependencies: [html.CoreImportExtension, LinkExtension, lexical.configExtension(html.DOMImportExtension, {
|
|
1396
|
+
rules: LinkImportRules
|
|
1397
|
+
})],
|
|
1398
|
+
name: '@lexical/link/Import'
|
|
1399
|
+
});
|
|
1400
|
+
|
|
1347
1401
|
/**
|
|
1348
1402
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
1349
1403
|
*
|
|
@@ -1365,6 +1419,8 @@ exports.AutoLinkExtension = AutoLinkExtension;
|
|
|
1365
1419
|
exports.AutoLinkNode = AutoLinkNode;
|
|
1366
1420
|
exports.ClickableLinkExtension = ClickableLinkExtension;
|
|
1367
1421
|
exports.LinkExtension = LinkExtension;
|
|
1422
|
+
exports.LinkImportExtension = LinkImportExtension;
|
|
1423
|
+
exports.LinkImportRules = LinkImportRules;
|
|
1368
1424
|
exports.LinkNode = LinkNode;
|
|
1369
1425
|
exports.TOGGLE_LINK_COMMAND = TOGGLE_LINK_COMMAND;
|
|
1370
1426
|
exports.createLinkMatcherWithRegExp = createLinkMatcherWithRegExp;
|
|
@@ -7,8 +7,9 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import { addClassNamesToElement, isHTMLAnchorElement, $findMatchingParent, $insertNodeToNearestRootAtCaret, mergeRegister, objectKlassEquals } from '@lexical/utils';
|
|
10
|
-
import { ElementNode, $copyNode, $isRangeSelection, $applyNodeReplacement, createCommand, $getSelection, $isNodeSelection, $normalizeSelection__EXPERIMENTAL, $setSelection, $getChildCaret, $isElementNode, $rewindSiblingCaret, $caretFromPoint, $normalizeCaret, $isSiblingCaret, $setPointFromCaret, defineExtension, shallowMergeConfig, COMMAND_PRIORITY_EDITOR, PASTE_COMMAND, COMMAND_PRIORITY_LOW, safeCast, isDOMNode, getNearestEditorFromDOMNode, $getNearestNodeFromDOMNode, TextNode, $isTextNode, $isLineBreakNode, $createTextNode } from 'lexical';
|
|
10
|
+
import { ElementNode, $copyNode, $isRangeSelection, $applyNodeReplacement, createCommand, $getSelection, $isNodeSelection, $normalizeSelection__EXPERIMENTAL, $setSelection, $getChildCaret, $isElementNode, $rewindSiblingCaret, $caretFromPoint, $normalizeCaret, $isSiblingCaret, $setPointFromCaret, defineExtension, shallowMergeConfig, COMMAND_PRIORITY_EDITOR, PASTE_COMMAND, COMMAND_PRIORITY_LOW, safeCast, isDOMNode, getNearestEditorFromDOMNode, $getNearestNodeFromDOMNode, TextNode, $isTextNode, $isLineBreakNode, $createTextNode, configExtension } from 'lexical';
|
|
11
11
|
import { namedSignals, effect } from '@lexical/extension';
|
|
12
|
+
import { CoreImportExtension, DOMImportExtension, defineImportRule, sel, $distributeInlineWrapper } from '@lexical/html';
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
15
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
@@ -1342,6 +1343,59 @@ const AutoLinkExtension = defineExtension({
|
|
|
1342
1343
|
register: registerAutoLink
|
|
1343
1344
|
});
|
|
1344
1345
|
|
|
1346
|
+
/**
|
|
1347
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
1348
|
+
*
|
|
1349
|
+
* This source code is licensed under the MIT license found in the
|
|
1350
|
+
* LICENSE file in the root directory of this source tree.
|
|
1351
|
+
*
|
|
1352
|
+
*/
|
|
1353
|
+
|
|
1354
|
+
const AnchorRule = defineImportRule({
|
|
1355
|
+
$import: (ctx, el) => {
|
|
1356
|
+
if (!el.textContent && el.children.length === 0) {
|
|
1357
|
+
return [];
|
|
1358
|
+
}
|
|
1359
|
+
// Use no schema here: when the `<a>` contains block descendants
|
|
1360
|
+
// (e.g. `<a><h1>x</h1><div>y</div></a>`), we want them lifted so each
|
|
1361
|
+
// block becomes a sibling at the link's level, with the link wrapping
|
|
1362
|
+
// its inline content. $distributeInlineWrapper handles both the
|
|
1363
|
+
// common all-inline case (single LinkNode wrapping the run) and the
|
|
1364
|
+
// mixed-block case (per-block recursion).
|
|
1365
|
+
const href = el.getAttribute('href') || '';
|
|
1366
|
+
const attrs = {
|
|
1367
|
+
rel: el.getAttribute('rel'),
|
|
1368
|
+
target: el.getAttribute('target'),
|
|
1369
|
+
title: el.getAttribute('title')
|
|
1370
|
+
};
|
|
1371
|
+
return $distributeInlineWrapper(ctx.$importChildren(el), () => $createLinkNode(href, attrs));
|
|
1372
|
+
},
|
|
1373
|
+
match: sel.tag('a'),
|
|
1374
|
+
name: '@lexical/link/a'
|
|
1375
|
+
});
|
|
1376
|
+
|
|
1377
|
+
/**
|
|
1378
|
+
* Import rules for {@link LinkNode}.
|
|
1379
|
+
*
|
|
1380
|
+
* @experimental
|
|
1381
|
+
*/
|
|
1382
|
+
const LinkImportRules = [AnchorRule];
|
|
1383
|
+
|
|
1384
|
+
/**
|
|
1385
|
+
* Bundles {@link LinkImportRules} (plus {@link CoreImportExtension}) into
|
|
1386
|
+
* a single dependency. Equivalent to the legacy
|
|
1387
|
+
* `LinkNode.importDOM` registration on the new
|
|
1388
|
+
* {@link DOMImportExtension} pipeline.
|
|
1389
|
+
*
|
|
1390
|
+
* @experimental
|
|
1391
|
+
*/
|
|
1392
|
+
const LinkImportExtension = defineExtension({
|
|
1393
|
+
dependencies: [CoreImportExtension, LinkExtension, configExtension(DOMImportExtension, {
|
|
1394
|
+
rules: LinkImportRules
|
|
1395
|
+
})],
|
|
1396
|
+
name: '@lexical/link/Import'
|
|
1397
|
+
});
|
|
1398
|
+
|
|
1345
1399
|
/**
|
|
1346
1400
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
1347
1401
|
*
|
|
@@ -1354,4 +1408,4 @@ const AutoLinkExtension = defineExtension({
|
|
|
1354
1408
|
/** @deprecated renamed to {@link $toggleLink} by @lexical/eslint-plugin rules-of-lexical */
|
|
1355
1409
|
const toggleLink = $toggleLink;
|
|
1356
1410
|
|
|
1357
|
-
export { $createAutoLinkNode, $createLinkNode, $isAutoLinkNode, $isLinkNode, $toggleLink, AutoLinkExtension, AutoLinkNode, ClickableLinkExtension, LinkExtension, LinkNode, TOGGLE_LINK_COMMAND, createLinkMatcherWithRegExp, formatUrl, registerAutoLink, registerClickableLink, registerLink, toggleLink };
|
|
1411
|
+
export { $createAutoLinkNode, $createLinkNode, $isAutoLinkNode, $isLinkNode, $toggleLink, AutoLinkExtension, AutoLinkNode, ClickableLinkExtension, LinkExtension, LinkImportExtension, LinkImportRules, LinkNode, TOGGLE_LINK_COMMAND, createLinkMatcherWithRegExp, formatUrl, registerAutoLink, registerClickableLink, registerLink, toggleLink };
|
|
@@ -18,6 +18,8 @@ export const AutoLinkExtension = mod.AutoLinkExtension;
|
|
|
18
18
|
export const AutoLinkNode = mod.AutoLinkNode;
|
|
19
19
|
export const ClickableLinkExtension = mod.ClickableLinkExtension;
|
|
20
20
|
export const LinkExtension = mod.LinkExtension;
|
|
21
|
+
export const LinkImportExtension = mod.LinkImportExtension;
|
|
22
|
+
export const LinkImportRules = mod.LinkImportRules;
|
|
21
23
|
export const LinkNode = mod.LinkNode;
|
|
22
24
|
export const TOGGLE_LINK_COMMAND = mod.TOGGLE_LINK_COMMAND;
|
|
23
25
|
export const createLinkMatcherWithRegExp = mod.createLinkMatcherWithRegExp;
|
|
@@ -16,6 +16,8 @@ export const AutoLinkExtension = mod.AutoLinkExtension;
|
|
|
16
16
|
export const AutoLinkNode = mod.AutoLinkNode;
|
|
17
17
|
export const ClickableLinkExtension = mod.ClickableLinkExtension;
|
|
18
18
|
export const LinkExtension = mod.LinkExtension;
|
|
19
|
+
export const LinkImportExtension = mod.LinkImportExtension;
|
|
20
|
+
export const LinkImportRules = mod.LinkImportRules;
|
|
19
21
|
export const LinkNode = mod.LinkNode;
|
|
20
22
|
export const TOGGLE_LINK_COMMAND = mod.TOGGLE_LINK_COMMAND;
|
|
21
23
|
export const createLinkMatcherWithRegExp = mod.createLinkMatcherWithRegExp;
|
|
@@ -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("@lexical/utils"),t=require("lexical"),n=require("@lexical/extension"),r=require("@lexical/html");const i=new Set(["http:","https:","mailto:","sms:","tel:"]);class s extends t.ElementNode{__url;__target;__rel;__title;static getType(){return"link"}static clone(e){return new s(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}afterCloneFrom(e){super.afterCloneFrom(e),this.__url=e.__url,this.__rel=e.__rel,this.__target=e.__target,this.__title=e.__title}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:u,priority:1})}}static importJSON(e){return c().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=k(e);try{const t=new URL(k(e));if(!i.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,n=!0){const r=t.$copyNode(this);return this.insertAfter(r,n),r}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://")}shouldMergeAdjacentLink(e){return this.getType()===e.getType()&&this.__url===e.__url&&this.__target===e.__target&&this.__rel===e.__rel&&this.__title===e.__title}}function l(e){const n=t.$caretFromPoint(e,"next");return[n,n.getFlipped()]}function o(e,n){for(const r of n)if(r.origin.isAttached()){const n=t.$normalizeCaret(r);return void t.$setPointFromCaret(e,n)}}function a(n){const r=t.$getSelection();let i=null,s=null;function a(){t.$isRangeSelection(r)&&(o(r.anchor,i),o(r.focus,s),t.$normalizeSelection__EXPERIMENTAL(r))}t.$isRangeSelection(r)&&(i=l(r.anchor),s=l(r.focus));let u=!1;for(const r of t.$getChildCaret(n,"next")){const i=r.origin;if(t.$isElementNode(i)&&!i.isInline()){const s=i.getChildren();if(s.length>0){const e=t.$copyNode(n);e.append(...s),i.append(e),u=!0}e.$insertNodeToNearestRootAtCaret(i,t.$rewindSiblingCaret(r),{$shouldSplit:()=>!1})}}function c(e,n,r){const[i,s]=e,l=e=>t.$isSiblingCaret(e)&&e.origin.is(n);if(!l(i)&&!l(s))return e;const o=t.$normalizeCaret(t.$getChildCaret(r,"next"));return[o,o.getFlipped()]}if(n.isAttached()){const e=n.getPreviousSibling();if(g(e)&&e.shouldMergeAdjacentLink(n))return i&&(i=c(i,e,n)),s&&(s=c(s,e,n)),e.append(...n.getChildren()),n.remove(),void a();const t=n.getNextSibling();g(t)&&n.shouldMergeAdjacentLink(t)&&(i&&(i=c(i,n,t)),s&&(s=c(s,n,t)),n.append(...t.getChildren()),t.remove(),u=!0)}if(u){if(!n.canBeEmpty()&&n.isEmpty()){const e=n.getParent();n.remove(),e&&e.isEmpty()&&e.remove()}a()}}function u(t){let n=null;if(e.isHTMLAnchorElement(t)){const e=t.textContent;(null!==e&&""!==e||t.children.length>0)&&(n=c(t.getAttribute("href")||"",{rel:t.getAttribute("rel"),target:t.getAttribute("target"),title:t.getAttribute("title")}))}return{node:n}}function c(e="",n){return t.$applyNodeReplacement(new s(e,n))}function g(e){return e instanceof s}class d extends s{__isUnlinked;constructor(e="",t={},n){super(e,t,n),this.__isUnlinked=void 0!==t.isUnlinked&&null!==t.isUnlinked&&t.isUnlinked}afterCloneFrom(e){super.afterCloneFrom(e),this.__isUnlinked=e.__isUnlinked}static getType(){return"autolink"}static clone(e){return new d(e.__url,{isUnlinked:e.__isUnlinked,rel:e.__rel,target:e.__target,title:e.__title},e.__key)}shouldMergeAdjacentLink(e){return!1}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 f().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,t=!0){const n=f(this.__url,{isUnlinked:this.__isUnlinked,rel:this.__rel,target:this.__target,title:this.__title});return this.insertAfter(n,t),n}}function f(e="",n){return t.$applyNodeReplacement(new d(e,n))}function h(e){return e instanceof d}const p=t.createCommand("TOGGLE_LINK_COMMAND");function _(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 m(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:l}=r,o=void 0===r.rel?"noreferrer":r.rel,a=t.$getSelection();if(null===a||!t.$isRangeSelection(a)&&!t.$isNodeSelection(a))return;if(t.$isNodeSelection(a)){const t=a.getNodes();if(0===t.length)return;return void t.forEach(t=>{if(null===i){const n=e.$findMatchingParent(t,e=>!h(e)&&g(e));n&&(n.insertBefore(t),0===n.getChildren().length&&n.remove())}else{const n=e.$findMatchingParent(t,e=>!h(e)&&g(e));if(n)n.setURL(i),void 0!==s&&n.setTarget(s),void 0!==o&&n.setRel(o);else{const e=c(i,{rel:o,target:s});t.insertBefore(e),e.append(t)}}})}if(a.isCollapsed()&&null===i)for(const t of a.getNodes()){const n=e.$findMatchingParent(t,e=>!h(e)&&g(e));return void(null!==n&&(n.getParentOrThrow().splice(n.getIndexWithinParent(),0,n.getChildren()),n.remove()))}const u=a.extract();if(null===i){const n=new Set;return void u.forEach(r=>{const i=e.$findMatchingParent(r,e=>!h(e)&&g(e));if(null!==i){const e=i.getKey();if(n.has(e))return;!function(e,n){const r=new Set(n.filter(t=>e.isParentOf(t)).map(e=>e.getKey())),i=e.getChildren(),s=i=>r.has(i.getKey())||t.$isElementNode(i)&&n.some(t=>e.isParentOf(t)&&i.isParentOf(t)),l=i.filter(s);if(l.length===i.length)return i.forEach(t=>e.insertBefore(t)),void e.remove();const o=i.findIndex(s),a=i.findLastIndex(s),u=0===o,c=a===i.length-1;if(u)l.forEach(t=>e.insertBefore(t));else if(c)for(let t=l.length-1;t>=0;t--)e.insertAfter(l[t]);else{for(let t=l.length-1;t>=0;t--)e.insertAfter(l[t]);const n=i.slice(a+1);if(n.length>0){const r=t.$copyNode(e);l[l.length-1].insertAfter(r),n.forEach(e=>r.append(e))}}}(i,u),n.add(e)}})}const d=new Set,f=e=>{d.has(e.getKey())||(d.add(e.getKey()),e.setURL(i),void 0!==s&&e.setTarget(s),void 0!==o&&e.setRel(o),void 0!==l&&e.setTitle(l))};if(1===u.length){const t=u[0],n=e.$findMatchingParent(t,g);if(null!==n)return f(n)}!function(e){const n=t.$getSelection();if(!t.$isRangeSelection(n))return e();const r=t.$normalizeSelection__EXPERIMENTAL(n),i=r.isBackward(),s=_(r.anchor,i?-1:0),l=_(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 u){if(!r.isAttached())continue;const a=e.$findMatchingParent(r,g);if(a){f(a);continue}if(t.$isElementNode(r)){if(!r.isInline())continue;if(g(r)){if(!(h(r)||null!==n&&n.getParentOrThrow().isParentOf(r))){f(r),n=r;continue}for(const e of r.getChildren())r.insertBefore(e);r.remove();continue}}const u=r.getPreviousSibling();g(u)&&u.is(n)?u.append(r):(n=c(i,{rel:o,target:s,title:l}),r.insertAfter(n),n.append(r))}})}const x=/^\+?[0-9\s()-]{5,}$/;function k(e){return e.match(/^[a-z][a-z0-9+.-]*:/i)||e.match(/^[/#.]/)?e:e.includes("@")?`mailto:${e}`:x.test(e)?`tel:${e}`:`https://${e}`}const N={attributes:void 0,validateUrl:void 0};function $(r,i){return e.mergeRegister(r.registerNodeTransform(s,a),r.registerCommand(p,e=>{const t=i.validateUrl.peek(),n=i.attributes.peek();if(null===e)return m(null),!0;if("string"==typeof e)return!(void 0!==t&&!t(e))&&(m(e,n),!0);{const{url:t,target:r,rel:i,title:s}=e;return m(t,{...n,rel:i,target:r,title:s}),!0}},t.COMMAND_PRIORITY_EDITOR),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(p,{...s,url:o}),i.preventDefault(),!0))},t.COMMAND_PRIORITY_LOW)}))}const L=t.defineExtension({build:(e,t,r)=>n.namedSignals(t),config:N,mergeConfig(e,n){const r=t.shallowMergeConfig(e,n);return e.attributes&&(r.attributes=t.shallowMergeConfig(e.attributes,r.attributes)),r},name:"@lexical/link/Link",nodes:()=>[s],register:(e,t,n)=>$(e,n.getOutput())});function S(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 o=null,a=null;if(l.update(()=>{const n=t.$getNearestNodeFromDOMNode(s);if(null!==n){const i=e.$findMatchingParent(n,t.$isElementNode);if(!r.disabled.peek())if(g(i))o=i.sanitizeUrl(i.getURL()),a=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&&(o=t.href,a=t.target)}}}),null===o||""===o)return;const u=n.getEditorState().read(t.$getSelection,{editor:n});if(t.$isRangeSelection(u)&&!u.isCollapsed())return void i.preventDefault();const c="auxclick"===i.type&&1===i.button;window.open(o,r.newTab.peek()||c||i.metaKey||i.ctrlKey||"_blank"===a?"_blank":"_self"),i.preventDefault()},l=e=>{1===e.button&&s(e)};return n.registerRootListener(e=>{if(e)return e.addEventListener("click",s,i),e.addEventListener("mouseup",l,i),()=>{e.removeEventListener("click",s),e.removeEventListener("mouseup",l)}})}const T=t.defineExtension({build:(e,t,r)=>n.namedSignals(t),config:t.safeCast({disabled:!1,newTab:!1}),dependencies:[L],name:"@lexical/link/ClickableLink",register:(e,t,n)=>S(e,n.getOutput())});function b(e,t){for(let n=0;n<t.length;n++){const r=t[n](e);if(r)return r}return null}const C=/[.,;\s]/;function v(e,t){return t.test(e)}function E(e,t){return v(e[e.length-1],t)}function R(e,t){return v(e[0],t)}function U(e,n){let r=e.getPreviousSibling();return t.$isElementNode(r)&&(r=r.getLastDescendant()),null===r||t.$isLineBreakNode(r)||t.$isTextNode(r)&&E(r.getTextContent(),n)}function O(e,n){let r=e.getNextSibling();return t.$isElementNode(r)&&(r=r.getFirstDescendant()),null===r||t.$isLineBreakNode(r)||t.$isTextNode(r)&&R(r.getTextContent(),n)}function A(e,t,n,r,i){if(!(e>0?v(r[e-1],n):U(i[0],n)))return!1;return t<r.length?v(r[t],n):O(i[i.length-1],n)}function M(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 P(e,n,r,i){const s=f(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 I(e,n,r,i){const s=e.getChildren(),l=s.length;for(let n=0;n<l;n++){const i=s[n];if(!t.$isTextNode(i)||!i.isSimpleText())return y(e),void r(null,e.getURL())}const o=e.getTextContent(),a=b(o,n);if(null===a||a.text!==o)return y(e),void r(null,e.getURL());if(!U(e,i)||!O(e,i))return y(e),void r(null,e.getURL());const u=e.getURL();if(u!==a.url&&(e.setURL(a.url),r(a.url,u)),a.attributes){const t=e.getRel();t!==a.attributes.rel&&(e.setRel(a.attributes.rel||null),r(a.attributes.rel||null,t));const n=e.getTarget();n!==a.attributes.target&&(e.setTarget(a.attributes.target||null),r(a.attributes.target||null,n))}}function y(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 D={changeHandlers:[],excludeParents:[],matchers:[],separatorRegex:C};function w(n,r=D){const{matchers:i,changeHandlers:s,excludeParents:l,separatorRegex:o=C}=r,a=(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(h(n))I(n,i,a,o);else if(!g(n)&&!l.some(e=>e(n))){if(e.isSimpleText()&&(R(e.getTextContent(),o)||!h(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,r){for(const t of e){const e=t.getParent();if(h(e)&&!e.getIsUnlinked())return}let i=[...e];const s=i.map(e=>e.getTextContent()).join("");let l,o=s,a=0;for(;(l=b(o,t))&&null!==l;){const e=l.index,t=e+l.length;if(A(a+e,a+t,r,s,i)){const[r,,s,u]=M(i,a+e,a+t);let c=!1;for(const e of s){const t=e.getParent();if(h(t)&&!t.getIsUnlinked()){c=!0;break}}if(c){a+=t,o=o.substring(t);continue}const g=P(s,a+e-r,a+t-r,l);i=g?[g,...u]:u,n(l.url,null),a=0}else a+=t;o=o.substring(t)}}(n,i,a,o)}!function(e,t,n,r){const i=e.getParent(),s=e.getPreviousSibling(),l=e.getNextSibling(),o=e.getTextContent();if(!h(i)||i.getIsUnlinked()){if(h(s)&&!s.getIsUnlinked()&&s.is(e.getPreviousSibling())&&e.getParent()===s.getParent()){if(!R(o,r))return y(s),void n(null,s.getURL());if(a=o,s.isEmailURI()?/^\.[a-zA-Z]{2,}/.test(a):/^\.[a-zA-Z0-9]{1,}/.test(a)){const i=s.getTextContent()+o,l=b(i,t);null!==l&&l.text===i&&(s.append(e),I(s,t,n,r),n(null,s.getURL()))}}var a;!h(l)||l.getIsUnlinked()||E(o,r)||l.is(e.getNextSibling())&&e.getParent()===l.getParent()&&(y(l),n(null,l.getURL()))}}(e,i,a,o)}}),n.registerCommand(p,e=>{const n=t.$getSelection();if(null!==e||!t.$isRangeSelection(n))return!1;return n.extract().forEach(e=>{const t=e.getParent();h(t)&&(t.setIsUnlinked(!t.getIsUnlinked()),t.markDirty())}),!1},t.COMMAND_PRIORITY_LOW))}const F=t.defineExtension({config:D,dependencies:[L],mergeConfig(e,n){const r=t.shallowMergeConfig(e,n);for(const t of["matchers","changeHandlers","excludeParents"]){const i=n[t];Array.isArray(i)&&(r[t]=[...e[t],...i])}return r},name:"@lexical/link/AutoLink",nodes:[d],register:w}),W=[r.defineImportRule({$import:(e,t)=>{if(!t.textContent&&0===t.children.length)return[];const n=t.getAttribute("href")||"",i={rel:t.getAttribute("rel"),target:t.getAttribute("target"),title:t.getAttribute("title")};return r.$distributeInlineWrapper(e.$importChildren(t),()=>c(n,i))},match:r.sel.tag("a"),name:"@lexical/link/a"})],z=t.defineExtension({dependencies:[r.CoreImportExtension,L,t.configExtension(r.DOMImportExtension,{rules:W})],name:"@lexical/link/Import"}),J=m;exports.$createAutoLinkNode=f,exports.$createLinkNode=c,exports.$isAutoLinkNode=h,exports.$isLinkNode=g,exports.$toggleLink=m,exports.AutoLinkExtension=F,exports.AutoLinkNode=d,exports.ClickableLinkExtension=T,exports.LinkExtension=L,exports.LinkImportExtension=z,exports.LinkImportRules=W,exports.LinkNode=s,exports.TOGGLE_LINK_COMMAND=p,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=k,exports.registerAutoLink=w,exports.registerClickableLink=S,exports.registerLink=$,exports.toggleLink=J;
|
|
@@ -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,$insertNodeToNearestRootAtCaret as r,mergeRegister as i,objectKlassEquals as l}from"@lexical/utils";import{ElementNode as s,$copyNode as o,$isRangeSelection as u,$applyNodeReplacement as a,createCommand as c,$getSelection as g,$isNodeSelection as f,$normalizeSelection__EXPERIMENTAL as d,$setSelection as h,$getChildCaret as p,$isElementNode as _,$rewindSiblingCaret as m,$caretFromPoint as x,$normalizeCaret as k,$isSiblingCaret as b,$setPointFromCaret as v,defineExtension as U,shallowMergeConfig as T,COMMAND_PRIORITY_EDITOR as C,PASTE_COMMAND as L,COMMAND_PRIORITY_LOW as S,safeCast as O,isDOMNode as P,getNearestEditorFromDOMNode as y,$getNearestNodeFromDOMNode as A,TextNode as N,$isTextNode as R,$isLineBreakNode as D,$createTextNode as I,configExtension as w}from"lexical";import{namedSignals as E,effect as M}from"@lexical/extension";import{CoreImportExtension as F,DOMImportExtension as W,defineImportRule as J,sel as K,$distributeInlineWrapper as $}from"@lexical/html";const B=new Set(["http:","https:","mailto:","sms:","tel:"]);class z extends s{__url;__target;__rel;__title;static getType(){return"link"}static clone(t){return new z(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}afterCloneFrom(t){super.afterCloneFrom(t),this.__url=t.__url,this.__rel=t.__rel,this.__target=t.__target,this.__title=t.__title}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:Z,priority:1})}}static importJSON(t){return q().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=it(t);try{const e=new URL(it(t));if(!B.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=o(this);return this.insertAfter(n,e),n}canInsertTextBefore(){return!1}canInsertTextAfter(){return!1}canBeEmpty(){return!1}isInline(){return!0}extractWithChild(t,e,n){if(!u(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://")}shouldMergeAdjacentLink(t){return this.getType()===t.getType()&&this.__url===t.__url&&this.__target===t.__target&&this.__rel===t.__rel&&this.__title===t.__title}}function j(t){const e=x(t,"next");return[e,e.getFlipped()]}function H(t,e){for(const n of e)if(n.origin.isAttached()){const e=k(n);return void v(t,e)}}function G(t){const e=g();let n=null,i=null;function l(){u(e)&&(H(e.anchor,n),H(e.focus,i),d(e))}u(e)&&(n=j(e.anchor),i=j(e.focus));let s=!1;for(const e of p(t,"next")){const n=e.origin;if(_(n)&&!n.isInline()){const i=n.getChildren();if(i.length>0){const e=o(t);e.append(...i),n.append(e),s=!0}r(n,m(e),{$shouldSplit:()=>!1})}}function a(t,e,n){const[r,i]=t,l=t=>b(t)&&t.origin.is(e);if(!l(r)&&!l(i))return t;const s=k(p(n,"next"));return[s,s.getFlipped()]}if(t.isAttached()){const e=t.getPreviousSibling();if(Q(e)&&e.shouldMergeAdjacentLink(t))return n&&(n=a(n,e,t)),i&&(i=a(i,e,t)),e.append(...t.getChildren()),t.remove(),void l();const r=t.getNextSibling();Q(r)&&t.shouldMergeAdjacentLink(r)&&(n&&(n=a(n,t,r)),i&&(i=a(i,t,r)),t.append(...r.getChildren()),r.remove(),s=!0)}if(s){if(!t.canBeEmpty()&&t.isEmpty()){const e=t.getParent();t.remove(),e&&e.isEmpty()&&e.remove()}l()}}function Z(t){let n=null;if(e(t)){const e=t.textContent;(null!==e&&""!==e||t.children.length>0)&&(n=q(t.getAttribute("href")||"",{rel:t.getAttribute("rel"),target:t.getAttribute("target"),title:t.getAttribute("title")}))}return{node:n}}function q(t="",e){return a(new z(t,e))}function Q(t){return t instanceof z}class V extends z{__isUnlinked;constructor(t="",e={},n){super(t,e,n),this.__isUnlinked=void 0!==e.isUnlinked&&null!==e.isUnlinked&&e.isUnlinked}afterCloneFrom(t){super.afterCloneFrom(t),this.__isUnlinked=t.__isUnlinked}static getType(){return"autolink"}static clone(t){return new V(t.__url,{isUnlinked:t.__isUnlinked,rel:t.__rel,target:t.__target,title:t.__title},t.__key)}shouldMergeAdjacentLink(t){return!1}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 X().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=X(this.__url,{isUnlinked:this.__isUnlinked,rel:this.__rel,target:this.__target,title:this.__title});return this.insertAfter(n,e),n}}function X(t="",e){return a(new V(t,e))}function Y(t){return t instanceof V}const tt=c("TOGGLE_LINK_COMMAND");function et(t,e){if("element"===t.type){const n=t.getNode();_(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 nt(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,a=g();if(null===a||!u(a)&&!f(a))return;if(f(a)){const t=a.getNodes();if(0===t.length)return;return void t.forEach(t=>{if(null===r){const e=n(t,t=>!Y(t)&&Q(t));e&&(e.insertBefore(t),0===e.getChildren().length&&e.remove())}else{const e=n(t,t=>!Y(t)&&Q(t));if(e)e.setURL(r),void 0!==i&&e.setTarget(i),void 0!==s&&e.setRel(s);else{const e=q(r,{rel:s,target:i});t.insertBefore(e),e.append(t)}}})}if(a.isCollapsed()&&null===r)for(const t of a.getNodes()){const e=n(t,t=>!Y(t)&&Q(t));return void(null!==e&&(e.getParentOrThrow().splice(e.getIndexWithinParent(),0,e.getChildren()),e.remove()))}const c=a.extract();if(null===r){const t=new Set;return void c.forEach(e=>{const r=n(e,t=>!Y(t)&&Q(t));if(null!==r){const e=r.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=>n.has(r.getKey())||_(r)&&e.some(e=>t.isParentOf(e)&&r.isParentOf(e)),l=r.filter(i);if(l.length===r.length)return r.forEach(e=>t.insertBefore(e)),void t.remove();const s=r.findIndex(i),u=r.findLastIndex(i),a=0===s,c=u===r.length-1;if(a)l.forEach(e=>t.insertBefore(e));else if(c)for(let e=l.length-1;e>=0;e--)t.insertAfter(l[e]);else{for(let e=l.length-1;e>=0;e--)t.insertAfter(l[e]);const e=r.slice(u+1);if(e.length>0){const n=o(t);l[l.length-1].insertAfter(n),e.forEach(t=>n.append(t))}}}(r,c),t.add(e)}})}const p=new Set,m=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===c.length){const t=c[0],e=n(t,Q);if(null!==e)return m(e)}!function(t){const e=g();if(!u(e))return t();const n=d(e),r=n.isBackward(),i=et(n.anchor,r?-1:0),l=et(n.focus,r?0:-1),s=t();if(i||l){const t=g();if(u(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")}h(d(e))}}}(()=>{let t=null;for(const e of c){if(!e.isAttached())continue;const o=n(e,Q);if(o){m(o);continue}if(_(e)){if(!e.isInline())continue;if(Q(e)){if(!(Y(e)||null!==t&&t.getParentOrThrow().isParentOf(e))){m(e),t=e;continue}for(const t of e.getChildren())e.insertBefore(t);e.remove();continue}}const u=e.getPreviousSibling();Q(u)&&u.is(t)?u.append(e):(t=q(r,{rel:s,target:i,title:l}),e.insertAfter(t),t.append(e))}})}const rt=/^\+?[0-9\s()-]{5,}$/;function it(t){return t.match(/^[a-z][a-z0-9+.-]*:/i)||t.match(/^[/#.]/)?t:t.includes("@")?`mailto:${t}`:rt.test(t)?`tel:${t}`:`https://${t}`}function lt(t,e){return i(t.registerNodeTransform(z,G),t.registerCommand(tt,t=>{const n=e.validateUrl.peek(),r=e.attributes.peek();if(null===t)return nt(null),!0;if("string"==typeof t)return!(void 0!==n&&!n(t))&&(nt(t,r),!0);{const{url:e,target:n,rel:i,title:l}=t;return nt(e,{...r,rel:i,target:n,title:l}),!0}},C),M(()=>{const n=e.validateUrl.value;if(!n)return;const r=e.attributes.value;return t.registerCommand(L,e=>{const i=g();if(!u(i)||i.isCollapsed()||!l(e,ClipboardEvent))return!1;if(null===e.clipboardData)return!1;const s=e.clipboardData.getData("text");return!!n(s)&&(!i.getNodes().some(t=>_(t))&&(t.dispatchCommand(tt,{...r,url:s}),e.preventDefault(),!0))},S)}))}const st=U({build:(t,e,n)=>E(e),config:{attributes:void 0,validateUrl:void 0},mergeConfig(t,e){const n=T(t,e);return t.attributes&&(n.attributes=T(t.attributes,n.attributes)),n},name:"@lexical/link/Link",nodes:()=>[z],register:(t,e,n)=>lt(t,n.getOutput())});function ot(t,r,i={}){const l=i=>{const l=i.target;if(!P(l))return;const s=y(l);if(null===s)return;let o=null,a=null;if(s.update(()=>{const t=A(l);if(null!==t){const i=n(t,_);if(!r.disabled.peek())if(Q(i))o=i.sanitizeUrl(i.getURL()),a=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&&(o=t.href,a=t.target)}}}),null===o||""===o)return;const c=t.getEditorState().read(g,{editor:t});if(u(c)&&!c.isCollapsed())return void i.preventDefault();const f="auxclick"===i.type&&1===i.button;window.open(o,r.newTab.peek()||f||i.metaKey||i.ctrlKey||"_blank"===a?"_blank":"_self"),i.preventDefault()},s=t=>{1===t.button&&l(t)};return t.registerRootListener(t=>{if(t)return t.addEventListener("click",l,i),t.addEventListener("mouseup",s,i),()=>{t.removeEventListener("click",l),t.removeEventListener("mouseup",s)}})}const ut=U({build:(t,e,n)=>E(e),config:O({disabled:!1,newTab:!1}),dependencies:[st],name:"@lexical/link/ClickableLink",register:(t,e,n)=>ot(t,n.getOutput())});function at(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 ct(t,e){for(let n=0;n<e.length;n++){const r=e[n](t);if(r)return r}return null}const gt=/[.,;\s]/;function ft(t,e){return e.test(t)}function dt(t,e){return ft(t[t.length-1],e)}function ht(t,e){return ft(t[0],e)}function pt(t,e){let n=t.getPreviousSibling();return _(n)&&(n=n.getLastDescendant()),null===n||D(n)||R(n)&&dt(n.getTextContent(),e)}function _t(t,e){let n=t.getNextSibling();return _(n)&&(n=n.getFirstDescendant()),null===n||D(n)||R(n)&&ht(n.getTextContent(),e)}function mt(t,e,n,r,i){if(!(t>0?ft(r[t-1],n):pt(i[0],n)))return!1;return e<r.length?ft(r[e],n):_t(i[i.length-1],n)}function xt(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 kt(t,e,n,r){const i=X(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=I(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 o=[];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)o.push(r);else{const[t,e]=r.splitText(n-l);o.push(t),a=e}s+=i}const c=g(),d=c?c.getNodes().find(R):void 0,h=I(l.getTextContent());return h.setFormat(l.getFormat()),h.setDetail(l.getDetail()),h.setStyle(l.getStyle()),i.append(h,...o),d&&d===l&&(u(c)?h.select(c.anchor.offset,c.focus.offset):f(c)&&h.select(0,h.getTextContent().length)),l.replace(i),a}}function bt(t,e,n,r){const i=t.getChildren(),l=i.length;for(let e=0;e<l;e++){const r=i[e];if(!R(r)||!r.isSimpleText())return vt(t),void n(null,t.getURL())}const s=t.getTextContent(),o=ct(s,e);if(null===o||o.text!==s)return vt(t),void n(null,t.getURL());if(!pt(t,r)||!_t(t,r))return vt(t),void n(null,t.getURL());const u=t.getURL();if(u!==o.url&&(t.setURL(o.url),n(o.url,u)),o.attributes){const e=t.getRel();e!==o.attributes.rel&&(t.setRel(o.attributes.rel||null),n(o.attributes.rel||null,e));const r=t.getTarget();r!==o.attributes.target&&(t.setTarget(o.attributes.target||null),n(o.attributes.target||null,r))}}function vt(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 Ut={changeHandlers:[],excludeParents:[],matchers:[],separatorRegex:gt};function Tt(t,e=Ut){const{matchers:n,changeHandlers:r,excludeParents:l,separatorRegex:s=gt}=e,o=(t,e)=>{for(const n of r)n(t,e)};return i(t.registerNodeTransform(N,t=>{const e=t.getParentOrThrow(),r=t.getPreviousSibling();if(Y(e))bt(e,n,o,s);else if(!Q(e)&&!l.some(t=>t(e))){if(t.isSimpleText()&&(ht(t.getTextContent(),s)||!Y(r))){const e=function(t){const e=[t];let n=t.getNextSibling();for(;null!==n&&R(n)&&n.isSimpleText()&&(e.push(n),!/[\s]/.test(n.getTextContent()));)n=n.getNextSibling();return e}(t);!function(t,e,n,r){for(const e of t){const t=e.getParent();if(Y(t)&&!t.getIsUnlinked())return}let i=[...t];const l=i.map(t=>t.getTextContent()).join("");let s,o=l,u=0;for(;(s=ct(o,e))&&null!==s;){const t=s.index,e=t+s.length;if(mt(u+t,u+e,r,l,i)){const[r,,l,a]=xt(i,u+t,u+e);let c=!1;for(const t of l){const e=t.getParent();if(Y(e)&&!e.getIsUnlinked()){c=!0;break}}if(c){u+=e,o=o.substring(e);continue}const g=kt(l,u+t-r,u+e-r,s);i=g?[g,...a]:a,n(s.url,null),u=0}else u+=e;o=o.substring(e)}}(e,n,o,s)}!function(t,e,n,r){const i=t.getParent(),l=t.getPreviousSibling(),s=t.getNextSibling(),o=t.getTextContent();if(!Y(i)||i.getIsUnlinked()){if(Y(l)&&!l.getIsUnlinked()&&l.is(t.getPreviousSibling())&&t.getParent()===l.getParent()){if(!ht(o,r))return vt(l),void n(null,l.getURL());if(u=o,l.isEmailURI()?/^\.[a-zA-Z]{2,}/.test(u):/^\.[a-zA-Z0-9]{1,}/.test(u)){const i=l.getTextContent()+o,s=ct(i,e);null!==s&&s.text===i&&(l.append(t),bt(l,e,n,r),n(null,l.getURL()))}}var u;!Y(s)||s.getIsUnlinked()||dt(o,r)||s.is(t.getNextSibling())&&t.getParent()===s.getParent()&&(vt(s),n(null,s.getURL()))}}(t,n,o,s)}}),t.registerCommand(tt,t=>{const e=g();if(null!==t||!u(e))return!1;return e.extract().forEach(t=>{const e=t.getParent();Y(e)&&(e.setIsUnlinked(!e.getIsUnlinked()),e.markDirty())}),!1},S))}const Ct=U({config:Ut,dependencies:[st],mergeConfig(t,e){const n=T(t,e);for(const r of["matchers","changeHandlers","excludeParents"]){const i=e[r];Array.isArray(i)&&(n[r]=[...t[r],...i])}return n},name:"@lexical/link/AutoLink",nodes:[V],register:Tt}),Lt=[J({$import:(t,e)=>{if(!e.textContent&&0===e.children.length)return[];const n=e.getAttribute("href")||"",r={rel:e.getAttribute("rel"),target:e.getAttribute("target"),title:e.getAttribute("title")};return $(t.$importChildren(e),()=>q(n,r))},match:K.tag("a"),name:"@lexical/link/a"})],St=U({dependencies:[F,st,w(W,{rules:Lt})],name:"@lexical/link/Import"}),Ot=nt;export{X as $createAutoLinkNode,q as $createLinkNode,Y as $isAutoLinkNode,Q as $isLinkNode,nt as $toggleLink,Ct as AutoLinkExtension,V as AutoLinkNode,ut as ClickableLinkExtension,st as LinkExtension,St as LinkImportExtension,Lt as LinkImportRules,z as LinkNode,tt as TOGGLE_LINK_COMMAND,at as createLinkMatcherWithRegExp,it as formatUrl,Tt as registerAutoLink,ot as registerClickableLink,lt as registerLink,Ot as toggleLink};
|
|
@@ -0,0 +1,22 @@
|
|
|
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 rules for {@link LinkNode}.
|
|
10
|
+
*
|
|
11
|
+
* @experimental
|
|
12
|
+
*/
|
|
13
|
+
export declare const LinkImportRules: import("@lexical/html").DOMImportRule<import("@lexical/html").ElementSelectorBuilder<HTMLAnchorElement, Record<string, never>>>[];
|
|
14
|
+
/**
|
|
15
|
+
* Bundles {@link LinkImportRules} (plus {@link CoreImportExtension}) into
|
|
16
|
+
* a single dependency. Equivalent to the legacy
|
|
17
|
+
* `LinkNode.importDOM` registration on the new
|
|
18
|
+
* {@link DOMImportExtension} pipeline.
|
|
19
|
+
*
|
|
20
|
+
* @experimental
|
|
21
|
+
*/
|
|
22
|
+
export declare const LinkImportExtension: import("lexical").LexicalExtension<import("lexical").ExtensionConfigBase, "@lexical/link/Import", unknown, unknown>;
|
|
@@ -10,5 +10,6 @@ export { type ClickableLinkConfig, ClickableLinkExtension, registerClickableLink
|
|
|
10
10
|
export { type AutoLinkConfig, AutoLinkExtension, type ChangeHandler, createLinkMatcherWithRegExp, type LinkMatcher, registerAutoLink, } from './LexicalAutoLinkExtension';
|
|
11
11
|
export { LinkExtension, registerLink } from './LexicalLinkExtension';
|
|
12
12
|
export { $createAutoLinkNode, $createLinkNode, $isAutoLinkNode, $isLinkNode, $toggleLink, type AutoLinkAttributes, AutoLinkNode, formatUrl, type LinkAttributes, LinkNode, type SerializedAutoLinkNode, type SerializedLinkNode, TOGGLE_LINK_COMMAND, } from './LexicalLinkNode';
|
|
13
|
+
export { LinkImportExtension, LinkImportRules } from './LinkImportExtension';
|
|
13
14
|
/** @deprecated renamed to {@link $toggleLink} by @lexical/eslint-plugin rules-of-lexical */
|
|
14
15
|
export declare const toggleLink: typeof $toggleLink;
|
package/package.json
CHANGED
|
@@ -8,36 +8,52 @@
|
|
|
8
8
|
"link"
|
|
9
9
|
],
|
|
10
10
|
"license": "MIT",
|
|
11
|
-
"version": "0.
|
|
12
|
-
"main": "LexicalLink.js",
|
|
13
|
-
"types": "index.d.ts",
|
|
11
|
+
"version": "0.45.0",
|
|
12
|
+
"main": "./dist/LexicalLink.js",
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
14
|
"dependencies": {
|
|
15
|
-
"@lexical/extension": "0.
|
|
16
|
-
"@lexical/
|
|
17
|
-
"lexical": "0.
|
|
15
|
+
"@lexical/extension": "0.45.0",
|
|
16
|
+
"@lexical/html": "0.45.0",
|
|
17
|
+
"@lexical/internal": "0.45.0",
|
|
18
|
+
"@lexical/utils": "0.45.0",
|
|
19
|
+
"lexical": "0.45.0"
|
|
18
20
|
},
|
|
19
21
|
"repository": {
|
|
20
22
|
"type": "git",
|
|
21
23
|
"url": "git+https://github.com/facebook/lexical.git",
|
|
22
24
|
"directory": "packages/lexical-link"
|
|
23
25
|
},
|
|
24
|
-
"module": "LexicalLink.mjs",
|
|
26
|
+
"module": "./dist/LexicalLink.mjs",
|
|
25
27
|
"sideEffects": false,
|
|
26
28
|
"exports": {
|
|
27
29
|
".": {
|
|
30
|
+
"source": "./src/index.ts",
|
|
28
31
|
"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"
|
|
32
|
+
"types": "./dist/index.d.ts",
|
|
33
|
+
"development": "./dist/LexicalLink.dev.mjs",
|
|
34
|
+
"production": "./dist/LexicalLink.prod.mjs",
|
|
35
|
+
"node": "./dist/LexicalLink.node.mjs",
|
|
36
|
+
"default": "./dist/LexicalLink.mjs"
|
|
34
37
|
},
|
|
35
38
|
"require": {
|
|
36
|
-
"types": "./index.d.ts",
|
|
37
|
-
"development": "./LexicalLink.dev.js",
|
|
38
|
-
"production": "./LexicalLink.prod.js",
|
|
39
|
-
"default": "./LexicalLink.js"
|
|
39
|
+
"types": "./dist/index.d.ts",
|
|
40
|
+
"development": "./dist/LexicalLink.dev.js",
|
|
41
|
+
"production": "./dist/LexicalLink.prod.js",
|
|
42
|
+
"default": "./dist/LexicalLink.js"
|
|
40
43
|
}
|
|
41
44
|
}
|
|
42
|
-
}
|
|
45
|
+
},
|
|
46
|
+
"files": [
|
|
47
|
+
"dist",
|
|
48
|
+
"src",
|
|
49
|
+
"!src/__tests__",
|
|
50
|
+
"!src/__bench__",
|
|
51
|
+
"!src/__mocks__",
|
|
52
|
+
"!src/**/*.test.ts",
|
|
53
|
+
"!src/**/*.test.tsx",
|
|
54
|
+
"!src/**/*.bench.ts",
|
|
55
|
+
"!src/**/*.bench.tsx",
|
|
56
|
+
"README.md",
|
|
57
|
+
"LICENSE"
|
|
58
|
+
]
|
|
43
59
|
}
|
|
@@ -0,0 +1,142 @@
|
|
|
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 {namedSignals, NamedSignalsOutput} from '@lexical/extension';
|
|
10
|
+
import {$findMatchingParent, isHTMLAnchorElement} from '@lexical/utils';
|
|
11
|
+
import {
|
|
12
|
+
$getNearestNodeFromDOMNode,
|
|
13
|
+
$getSelection,
|
|
14
|
+
$isElementNode,
|
|
15
|
+
$isRangeSelection,
|
|
16
|
+
defineExtension,
|
|
17
|
+
getNearestEditorFromDOMNode,
|
|
18
|
+
isDOMNode,
|
|
19
|
+
LexicalEditor,
|
|
20
|
+
safeCast,
|
|
21
|
+
} from 'lexical';
|
|
22
|
+
|
|
23
|
+
import {LinkExtension} from './LexicalLinkExtension';
|
|
24
|
+
import {$isLinkNode} from './LexicalLinkNode';
|
|
25
|
+
|
|
26
|
+
function findMatchingDOM<T extends Node>(
|
|
27
|
+
startNode: Node,
|
|
28
|
+
predicate: (node: Node) => node is T,
|
|
29
|
+
): T | null {
|
|
30
|
+
let node: Node | null = startNode;
|
|
31
|
+
while (node != null) {
|
|
32
|
+
if (predicate(node)) {
|
|
33
|
+
return node;
|
|
34
|
+
}
|
|
35
|
+
node = node.parentNode;
|
|
36
|
+
}
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface ClickableLinkConfig {
|
|
41
|
+
/** Open clicked links in a new tab when true (default false) */
|
|
42
|
+
newTab: boolean;
|
|
43
|
+
/** Disable this extension when true (default false) */
|
|
44
|
+
disabled: boolean;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function registerClickableLink(
|
|
48
|
+
editor: LexicalEditor,
|
|
49
|
+
stores: NamedSignalsOutput<ClickableLinkConfig>,
|
|
50
|
+
eventOptions: Pick<AddEventListenerOptions, 'signal'> = {},
|
|
51
|
+
): () => void {
|
|
52
|
+
const onClick = (event: MouseEvent) => {
|
|
53
|
+
const target = event.target;
|
|
54
|
+
if (!isDOMNode(target)) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
const nearestEditor = getNearestEditorFromDOMNode(target);
|
|
58
|
+
|
|
59
|
+
if (nearestEditor === null) {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
let url = null;
|
|
64
|
+
let urlTarget = null;
|
|
65
|
+
nearestEditor.update(() => {
|
|
66
|
+
const clickedNode = $getNearestNodeFromDOMNode(target);
|
|
67
|
+
if (clickedNode !== null) {
|
|
68
|
+
const maybeLinkNode = $findMatchingParent(clickedNode, $isElementNode);
|
|
69
|
+
if (!stores.disabled.peek()) {
|
|
70
|
+
if ($isLinkNode(maybeLinkNode)) {
|
|
71
|
+
url = maybeLinkNode.sanitizeUrl(maybeLinkNode.getURL());
|
|
72
|
+
urlTarget = maybeLinkNode.getTarget();
|
|
73
|
+
} else {
|
|
74
|
+
const a = findMatchingDOM(target, isHTMLAnchorElement);
|
|
75
|
+
if (a !== null) {
|
|
76
|
+
url = a.href;
|
|
77
|
+
urlTarget = a.target;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
if (url === null || url === '') {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Allow user to select link text without following url
|
|
89
|
+
const selection = editor.getEditorState().read($getSelection, {editor});
|
|
90
|
+
if ($isRangeSelection(selection) && !selection.isCollapsed()) {
|
|
91
|
+
event.preventDefault();
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const isMiddle = event.type === 'auxclick' && event.button === 1;
|
|
96
|
+
window.open(
|
|
97
|
+
url,
|
|
98
|
+
stores.newTab.peek() ||
|
|
99
|
+
isMiddle ||
|
|
100
|
+
event.metaKey ||
|
|
101
|
+
event.ctrlKey ||
|
|
102
|
+
urlTarget === '_blank'
|
|
103
|
+
? '_blank'
|
|
104
|
+
: '_self',
|
|
105
|
+
);
|
|
106
|
+
event.preventDefault();
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
const onMouseUp = (event: MouseEvent) => {
|
|
110
|
+
if (event.button === 1) {
|
|
111
|
+
onClick(event);
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
return editor.registerRootListener(rootElement => {
|
|
116
|
+
if (rootElement) {
|
|
117
|
+
rootElement.addEventListener('click', onClick, eventOptions);
|
|
118
|
+
rootElement.addEventListener('mouseup', onMouseUp, eventOptions);
|
|
119
|
+
return () => {
|
|
120
|
+
rootElement.removeEventListener('click', onClick);
|
|
121
|
+
rootElement.removeEventListener('mouseup', onMouseUp);
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Normally in a Lexical editor the `CLICK_COMMAND` on a LinkNode will cause the
|
|
129
|
+
* selection to change instead of opening a link. This extension can be used to
|
|
130
|
+
* restore the default behavior, e.g. when the editor is not editable.
|
|
131
|
+
*/
|
|
132
|
+
export const ClickableLinkExtension = defineExtension({
|
|
133
|
+
build(editor, config, state) {
|
|
134
|
+
return namedSignals(config);
|
|
135
|
+
},
|
|
136
|
+
config: safeCast<ClickableLinkConfig>({disabled: false, newTab: false}),
|
|
137
|
+
dependencies: [LinkExtension],
|
|
138
|
+
name: '@lexical/link/ClickableLink',
|
|
139
|
+
register(editor, config, state) {
|
|
140
|
+
return registerClickableLink(editor, state.getOutput());
|
|
141
|
+
},
|
|
142
|
+
});
|