@atlaskit/editor-plugin-paste-options-toolbar
Version:
Paste options toolbar for @atlaskit/editor-core
137 lines (127 loc) • 4.8 kB
JavaScript
/**
* Returns true if the given text node's sole mark is a `link` mark whose href
* matches the node's text content exactly (i.e. the link label IS the URL).
* Or if the text content is a parseable URL.
*/
var isUrlOnlyLinkTextNode = function isUrlOnlyLinkTextNode(node) {
var _node$marks$0$attrs$h, _node$marks$0$attrs, _node$text;
if (!node.isText) {
return false;
}
if (node.marks.length !== 1 || node.marks[0].type.name !== 'link') {
return false;
}
var href = (_node$marks$0$attrs$h = (_node$marks$0$attrs = node.marks[0].attrs) === null || _node$marks$0$attrs === void 0 ? void 0 : _node$marks$0$attrs.href) !== null && _node$marks$0$attrs$h !== void 0 ? _node$marks$0$attrs$h : '';
// Check if the text content is a URL
var textOfNode = (_node$text = node.text) !== null && _node$text !== void 0 ? _node$text : '';
var parseableUrl = isParseableUrl(textOfNode);
return node.text === href || parseableUrl;
};
/**
* Checks if a given string is parseable as a URL.
*/
var isParseableUrl = function isParseableUrl(text) {
var _URL;
if (typeof ((_URL = URL) === null || _URL === void 0 ? void 0 : _URL.canParse) === 'function') {
return URL.canParse(text);
}
// Fallback for older environments
try {
new URL(text);
return true;
} catch (e) {
return false;
}
};
/**
* Returns true if the slice represents a single smart-link card node.
* Handles two shapes:
* - paragraph > inlineCard|blockCard (smartlink from editor/renderer, wrapped in a paragraph)
* - inlineCard|blockCard (top-level card with no paragraph wrapper)
*/
var isSingleSmartLinkCard = function isSingleSmartLinkCard(slice) {
var isSupportedCard = function isSupportedCard(node) {
return node.type.name === 'inlineCard' || node.type.name === 'blockCard';
};
if (slice.content.childCount !== 1) {
return false;
}
var topNode = slice.content.child(0);
// Top-level inlineCard/blockCard (no paragraph wrapper)
if (isSupportedCard(topNode)) {
return true;
}
// paragraph > inlineCard/blockCard
if (topNode.type.name !== 'paragraph') {
return false;
}
if (topNode.childCount !== 1) {
return false;
}
return isSupportedCard(topNode.child(0));
};
/**
* Returns the children of a Fragment, filtering out whitespace-only text nodes.
* This handles trailing/leading spaces that browsers sometimes include when
* copying a link (e.g. `<a href="...">URL</a> `).
*/
var significantChildren = function significantChildren(fragment) {
var children = [];
fragment.forEach(function (child) {
var _child$text;
if (child.isText && ((_child$text = child.text) === null || _child$text === void 0 ? void 0 : _child$text.trim()) === '') {
return;
}
children.push(child);
});
return children;
};
/**
* Returns true if the slice represents a single bare link with no label.
* Handles two shapes:
* - paragraph > text(link mark) (standard rich-text paste, wrapped in a paragraph)
* - text(link mark) (top-level text node with no paragraph wrapper)
* Whitespace-only sibling text nodes are ignored in both cases.
*/
var isSingleBareLink = function isSingleBareLink(slice) {
// Top-level text node with a link mark (no paragraph wrapper)
var significantTopChildren = significantChildren(slice.content);
if (significantTopChildren.length === 1 && isUrlOnlyLinkTextNode(significantTopChildren[0])) {
return true;
}
// paragraph > text(link mark)
if (slice.content.childCount !== 1) {
return false;
}
var topNode = slice.content.child(0);
if (topNode.type.name !== 'paragraph') {
return false;
}
var children = significantChildren(topNode.content);
if (children.length !== 1) {
return false;
}
return isUrlOnlyLinkTextNode(children[0]);
};
/**
* Returns `true` when the pasted content is NOT a single standalone link.
*
* A paste is considered a "single link" (returns `false`) when:
* - The slice contains exactly one paragraph with one text node whose text
* equals its `link` mark href (bare URL link, no custom label), OR
* - The slice contains exactly one paragraph with a single `inlineCard` node
* (smartlink from the renderer or editor).
*
* Returns `true` (not a single link) when:
* - The pasted link has a custom label (text ≠ href)
* - There are multiple paragraphs or sibling nodes
* - There is additional text alongside the link in the same paragraph
* - The slice is absent (plain-text paste)
*/
export var isNotSingleLink = function isNotSingleLink(slice) {
if (!slice || !slice.content.size) {
// No rich-text slice → plain text paste, not a single link in the relevant sense
return true;
}
return !isSingleBareLink(slice) && !isSingleSmartLinkCard(slice);
};