@37signals/lexxy 0.9.9-beta.preview3.domselection1 → 0.9.9-beta.preview5
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/dist/lexxy.esm.js +91 -40
- package/dist/lexxy_helpers.esm.js +1 -1
- package/package.json +1 -1
package/dist/lexxy.esm.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { isActiveAndVisible, createElement, extractPlainTextFromHtml, createAttachmentFigure, isPreviewableImage, dispatch, parseHtml, addBlockSpacing, generateDomId } from './lexxy_helpers.esm.js';
|
|
2
2
|
export { highlightCode } from './lexxy_helpers.esm.js';
|
|
3
3
|
import DOMPurify from 'dompurify';
|
|
4
|
-
import { getStyleObjectFromCSS, getCSSFromStyleObject, $isAtNodeEnd, $getSelectionStyleValueForProperty, $patchStyleText, $
|
|
5
|
-
import { SKIP_DOM_SELECTION_TAG, CAN_UNDO_COMMAND, COMMAND_PRIORITY_LOW, CAN_REDO_COMMAND, $getSelection, $isRangeSelection, DecoratorNode, $createParagraphNode, $getNodeByKey, $isTextNode, $createRangeSelection, $setSelection, $createTextNode, $isElementNode, $isRootOrShadowRoot, $isRootNode, $createNodeSelection, $isDecoratorNode, $isLineBreakNode, TextNode, createCommand, createState, defineExtension, COMMAND_PRIORITY_NORMAL, $getState, $setState, $hasUpdateTag, PASTE_TAG, FORMAT_TEXT_COMMAND, UNDO_COMMAND, REDO_COMMAND, PASTE_COMMAND, KEY_ARROW_RIGHT_COMMAND, KEY_TAB_COMMAND, OUTDENT_CONTENT_COMMAND, INDENT_CONTENT_COMMAND, COMMAND_PRIORITY_EDITOR, $getEditor, HISTORY_MERGE_TAG, SKIP_SCROLL_INTO_VIEW_TAG, $cloneWithProperties, $getNearestRootOrShadowRoot, $isNodeSelection, $getRoot, KEY_ARROW_LEFT_COMMAND, KEY_ARROW_UP_COMMAND, KEY_ARROW_DOWN_COMMAND, DELETE_CHARACTER_COMMAND, SELECTION_CHANGE_COMMAND, CLICK_COMMAND, isDOMNode, $getNearestNodeFromDOMNode, $addUpdateTag, ElementNode, $
|
|
4
|
+
import { getStyleObjectFromCSS, getCSSFromStyleObject, $ensureForwardRangeSelection, $isAtNodeEnd, $getSelectionStyleValueForProperty, $patchStyleText, $setBlocksType, $forEachSelectedTextNode } from '@lexical/selection';
|
|
5
|
+
import { SKIP_DOM_SELECTION_TAG, CAN_UNDO_COMMAND, COMMAND_PRIORITY_LOW, CAN_REDO_COMMAND, $getSelection, $isRangeSelection, DecoratorNode, $createParagraphNode, $getNodeByKey, $isTextNode, $createRangeSelection, $setSelection, $createTextNode, $isElementNode, $isRootOrShadowRoot, $isRootNode, $createNodeSelection, $isDecoratorNode, $isLineBreakNode, $isParagraphNode, $splitNode, $getSiblingCaret, LineBreakNode, TextNode, createCommand, createState, defineExtension, COMMAND_PRIORITY_NORMAL, $getState, $setState, $hasUpdateTag, PASTE_TAG, FORMAT_TEXT_COMMAND, UNDO_COMMAND, REDO_COMMAND, PASTE_COMMAND, KEY_ARROW_RIGHT_COMMAND, KEY_TAB_COMMAND, OUTDENT_CONTENT_COMMAND, INDENT_CONTENT_COMMAND, COMMAND_PRIORITY_EDITOR, $getEditor, HISTORY_MERGE_TAG, SKIP_SCROLL_INTO_VIEW_TAG, $cloneWithProperties, $getNearestRootOrShadowRoot, $isNodeSelection, $getRoot, KEY_ARROW_LEFT_COMMAND, KEY_ARROW_UP_COMMAND, KEY_ARROW_DOWN_COMMAND, DELETE_CHARACTER_COMMAND, SELECTION_CHANGE_COMMAND, CLICK_COMMAND, isDOMNode, $getNearestNodeFromDOMNode, $addUpdateTag, ElementNode, $getChildCaretAtIndex, $createLineBreakNode, ParagraphNode, RootNode, COMMAND_PRIORITY_HIGH, DRAGSTART_COMMAND, DROP_COMMAND, INSERT_PARAGRAPH_COMMAND, mergeRegister as mergeRegister$1, $findMatchingParent, CLEAR_HISTORY_COMMAND, $onUpdate, KEY_ENTER_COMMAND, COMMAND_PRIORITY_CRITICAL, KEY_SPACE_COMMAND, KEY_BACKSPACE_COMMAND, KEY_DOWN_COMMAND } from 'lexical';
|
|
6
6
|
import { buildEditorFromExtensions } from '@lexical/extension';
|
|
7
7
|
import { ListNode, ListItemNode, $getListDepth, INSERT_UNORDERED_LIST_COMMAND, INSERT_ORDERED_LIST_COMMAND, $isListItemNode, $isListNode, registerList } from '@lexical/list';
|
|
8
8
|
import { LinkNode, $createAutoLinkNode, $toggleLink, $createLinkNode, $isLinkNode, AutoLinkNode } from '@lexical/link';
|
|
9
9
|
import { $getNearestNodeOfType, $wrapNodeInElement, $lastToFirstIterator, mergeRegister, $insertFirst, $unwrapAndFilterDescendants, $firstToLastIterator, $getNearestBlockElementAncestorOrThrow, $descendantsMatching, IS_APPLE } from '@lexical/utils';
|
|
10
10
|
import { registerPlainText } from '@lexical/plain-text';
|
|
11
|
-
import { RichTextExtension, $isQuoteNode, $isHeadingNode,
|
|
11
|
+
import { RichTextExtension, $isQuoteNode, $isHeadingNode, $createHeadingNode, $createQuoteNode, QuoteNode, HeadingNode, registerRichText } from '@lexical/rich-text';
|
|
12
12
|
import { $generateNodesFromDOM, $generateHtmlFromNodes } from '@lexical/html';
|
|
13
13
|
import { $isCodeNode, CodeHighlightNode, CodeNode, $createCodeNode, $isCodeHighlightNode, $createCodeHighlightNode, normalizeCodeLang, registerCodeHighlighting, CODE_LANGUAGE_FRIENDLY_NAME_MAP } from '@lexical/code';
|
|
14
14
|
import { TRANSFORMERS, registerMarkdownShortcuts } from '@lexical/markdown';
|
|
@@ -1460,6 +1460,40 @@ function isAttachmentSpacerTextNode(node, previousNode, index, childCount) {
|
|
|
1460
1460
|
&& previousNode instanceof CustomActionTextAttachmentNode
|
|
1461
1461
|
}
|
|
1462
1462
|
|
|
1463
|
+
function $splitParagraphsAtLineBreakBoundaries(selection) {
|
|
1464
|
+
$ensureForwardRangeSelection(selection);
|
|
1465
|
+
|
|
1466
|
+
// Split focus first so the anchor split position stays valid.
|
|
1467
|
+
$splitAtNearestLineBreak(selection.focus, "next");
|
|
1468
|
+
$splitAtNearestLineBreak(selection.anchor, "previous");
|
|
1469
|
+
}
|
|
1470
|
+
|
|
1471
|
+
function $splitAtNearestLineBreak(point, direction) {
|
|
1472
|
+
const paragraph = point.getNode().getTopLevelElement();
|
|
1473
|
+
if (!paragraph || !$isParagraphNode(paragraph)) return
|
|
1474
|
+
|
|
1475
|
+
const pointNode = point.getNode();
|
|
1476
|
+
const selectionChild = pointNode.getParent().is(paragraph) ? pointNode : pointNode.getParentOrThrow();
|
|
1477
|
+
const lineBreakCaret = $caretAtNearestNodeOfType(selectionChild, LineBreakNode, direction);
|
|
1478
|
+
if (!lineBreakCaret) return
|
|
1479
|
+
|
|
1480
|
+
const lineBreak = lineBreakCaret.origin;
|
|
1481
|
+
const isEdge = lineBreakCaret.getNodeAtCaret() === null;
|
|
1482
|
+
|
|
1483
|
+
if (!isEdge) {
|
|
1484
|
+
$splitNode(paragraph, lineBreak.getIndexWithinParent());
|
|
1485
|
+
}
|
|
1486
|
+
|
|
1487
|
+
lineBreak.remove();
|
|
1488
|
+
}
|
|
1489
|
+
|
|
1490
|
+
function $caretAtNearestNodeOfType(node, klass, direction) {
|
|
1491
|
+
for (const caret of $getSiblingCaret(node, direction)) {
|
|
1492
|
+
if (caret.origin instanceof klass) return caret
|
|
1493
|
+
}
|
|
1494
|
+
return null
|
|
1495
|
+
}
|
|
1496
|
+
|
|
1463
1497
|
// Shared, strictly-contained element used to attach ephemeral nodes when we
|
|
1464
1498
|
// need to read computed styles (e.g. canonicalizing style values, resolving
|
|
1465
1499
|
// CSS custom properties). The container is created once and attached to
|
|
@@ -4646,7 +4680,6 @@ class NodeInserter {
|
|
|
4646
4680
|
static for(selection) {
|
|
4647
4681
|
const INSERTERS = [
|
|
4648
4682
|
CodeNodeInserter,
|
|
4649
|
-
QuoteNodeInserter,
|
|
4650
4683
|
ShadowRootNodeInserter,
|
|
4651
4684
|
NodeSelectionNodeInserter
|
|
4652
4685
|
];
|
|
@@ -4695,25 +4728,6 @@ class CodeNodeInserter extends NodeInserter {
|
|
|
4695
4728
|
|
|
4696
4729
|
}
|
|
4697
4730
|
|
|
4698
|
-
// Lexical will split a QuoteNode when inserting other Elements - we want them simply inserted as-is
|
|
4699
|
-
class QuoteNodeInserter extends NodeInserter {
|
|
4700
|
-
static handles(selection) {
|
|
4701
|
-
return $getNearestNodeOfType(selection.anchor?.getNode(), QuoteNode)
|
|
4702
|
-
}
|
|
4703
|
-
|
|
4704
|
-
insertNodes(nodes) {
|
|
4705
|
-
if (!this.selection.isCollapsed()) { this.selection.removeText(); }
|
|
4706
|
-
|
|
4707
|
-
$ensureForwardRangeSelection(this.selection);
|
|
4708
|
-
let lastNode = this.selection.focus.getNode();
|
|
4709
|
-
for (const node of nodes) {
|
|
4710
|
-
lastNode = lastNode.insertAfter(node);
|
|
4711
|
-
}
|
|
4712
|
-
|
|
4713
|
-
lastNode.selectEnd();
|
|
4714
|
-
}
|
|
4715
|
-
}
|
|
4716
|
-
|
|
4717
4731
|
class ShadowRootNodeInserter extends NodeInserter {
|
|
4718
4732
|
static handles(selection) {
|
|
4719
4733
|
return $isShadowRoot(selection?.anchor.getNode())
|
|
@@ -4861,7 +4875,7 @@ class Contents {
|
|
|
4861
4875
|
} else {
|
|
4862
4876
|
topLevelElements.filter($isQuoteNode).forEach(node => this.#unwrap(node));
|
|
4863
4877
|
|
|
4864
|
-
|
|
4878
|
+
$splitParagraphsAtLineBreakBoundaries(selection);
|
|
4865
4879
|
|
|
4866
4880
|
const elements = this.#topLevelElementsInSelection(selection);
|
|
4867
4881
|
if (elements.length === 0) return
|
|
@@ -5364,7 +5378,13 @@ class Clipboard {
|
|
|
5364
5378
|
paste(event) {
|
|
5365
5379
|
const clipboardData = event.clipboardData;
|
|
5366
5380
|
|
|
5367
|
-
if (!clipboardData
|
|
5381
|
+
if (!clipboardData) return false
|
|
5382
|
+
|
|
5383
|
+
if (this.#isPastingIntoCodeBlock()) {
|
|
5384
|
+
this.#pastePlainTextIntoCodeBlock(clipboardData);
|
|
5385
|
+
event.preventDefault();
|
|
5386
|
+
return true
|
|
5387
|
+
}
|
|
5368
5388
|
|
|
5369
5389
|
if (this.#isPlainTextOrURLPasted(clipboardData)) {
|
|
5370
5390
|
this.#pastePlainText(clipboardData);
|
|
@@ -5413,6 +5433,16 @@ class Clipboard {
|
|
|
5413
5433
|
return result
|
|
5414
5434
|
}
|
|
5415
5435
|
|
|
5436
|
+
#pastePlainTextIntoCodeBlock(clipboardData) {
|
|
5437
|
+
const text = clipboardData.getData("text/plain");
|
|
5438
|
+
if (!text) return
|
|
5439
|
+
|
|
5440
|
+
this.editor.update(() => {
|
|
5441
|
+
const selection = $getSelection();
|
|
5442
|
+
if ($isRangeSelection(selection)) selection.insertRawText(text);
|
|
5443
|
+
}, { tag: PASTE_TAG });
|
|
5444
|
+
}
|
|
5445
|
+
|
|
5416
5446
|
#pastePlainText(clipboardData) {
|
|
5417
5447
|
const item = clipboardData.items[0];
|
|
5418
5448
|
item.getAsString((text) => {
|
|
@@ -6510,22 +6540,17 @@ class EarlyEscapeCodeNode extends CodeNode {
|
|
|
6510
6540
|
}
|
|
6511
6541
|
|
|
6512
6542
|
insertNewAfter(selection, restoreSelection) {
|
|
6513
|
-
if (!selection.isCollapsed())
|
|
6514
|
-
|
|
6515
|
-
if (this.#isCursorAtStart(selection)) {
|
|
6516
|
-
this
|
|
6517
|
-
|
|
6518
|
-
|
|
6519
|
-
|
|
6520
|
-
|
|
6521
|
-
|
|
6522
|
-
|
|
6523
|
-
const paragraph = $createParagraphNode();
|
|
6524
|
-
this.insertAfter(paragraph);
|
|
6525
|
-
return paragraph
|
|
6543
|
+
if ($hasUpdateTag(PASTE_TAG) || !selection.isCollapsed()) {
|
|
6544
|
+
return super.insertNewAfter(selection, restoreSelection)
|
|
6545
|
+
} else if (this.#isCursorAtStart(selection)) {
|
|
6546
|
+
return this.#insertParagraphBefore()
|
|
6547
|
+
} else if (this.#isCursorOnWhitespaceOnlyLastLine(selection)) {
|
|
6548
|
+
return this.#insertBlankLineBelow(selection, restoreSelection)
|
|
6549
|
+
} else if (this.#isCursorOnEmptyLastLine(selection)) {
|
|
6550
|
+
return this.#escapeToNewParagraphAfter()
|
|
6551
|
+
} else {
|
|
6552
|
+
return super.insertNewAfter(selection, restoreSelection)
|
|
6526
6553
|
}
|
|
6527
|
-
|
|
6528
|
-
return super.insertNewAfter(selection, restoreSelection)
|
|
6529
6554
|
}
|
|
6530
6555
|
|
|
6531
6556
|
#isCursorAtStart(selection) {
|
|
@@ -6543,6 +6568,32 @@ class EarlyEscapeCodeNode extends CodeNode {
|
|
|
6543
6568
|
return textContent === "" || textContent.endsWith("\n")
|
|
6544
6569
|
}
|
|
6545
6570
|
|
|
6571
|
+
#isCursorOnWhitespaceOnlyLastLine(selection) {
|
|
6572
|
+
if (!$isCursorOnLastLine(selection)) return false
|
|
6573
|
+
|
|
6574
|
+
const textContent = this.getTextContent();
|
|
6575
|
+
const lastNewlineIndex = textContent.lastIndexOf("\n");
|
|
6576
|
+
const lastLine = lastNewlineIndex === -1 ? textContent : textContent.slice(lastNewlineIndex + 1);
|
|
6577
|
+
return lastLine.length > 0 && lastLine.trim() === ""
|
|
6578
|
+
}
|
|
6579
|
+
|
|
6580
|
+
#insertParagraphBefore() {
|
|
6581
|
+
this.insertBefore($createParagraphNode());
|
|
6582
|
+
return null
|
|
6583
|
+
}
|
|
6584
|
+
|
|
6585
|
+
#insertBlankLineBelow(selection, restoreSelection) {
|
|
6586
|
+
super.insertNewAfter(selection, restoreSelection);
|
|
6587
|
+
this.getLastChild().remove();
|
|
6588
|
+
return null
|
|
6589
|
+
}
|
|
6590
|
+
|
|
6591
|
+
#escapeToNewParagraphAfter() {
|
|
6592
|
+
$trimTrailingBlankNodes(this);
|
|
6593
|
+
const paragraph = $createParagraphNode();
|
|
6594
|
+
this.insertAfter(paragraph);
|
|
6595
|
+
return paragraph
|
|
6596
|
+
}
|
|
6546
6597
|
}
|
|
6547
6598
|
|
|
6548
6599
|
class EarlyEscapeListItemNode extends ListItemNode {
|
|
@@ -114,7 +114,7 @@ function highlightElement(preElement) {
|
|
|
114
114
|
applyHighlightRanges(codeElement, highlights);
|
|
115
115
|
}
|
|
116
116
|
|
|
117
|
-
preElement.
|
|
117
|
+
preElement.replaceChildren(codeElement);
|
|
118
118
|
}
|
|
119
119
|
|
|
120
120
|
// Walk the DOM tree inside a <pre> element and build a list of
|