@37signals/lexxy 0.9.9-beta.preview4 → 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 +103 -60
- 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,39 +6540,16 @@ class EarlyEscapeCodeNode extends CodeNode {
|
|
|
6510
6540
|
}
|
|
6511
6541
|
|
|
6512
6542
|
insertNewAfter(selection, restoreSelection) {
|
|
6513
|
-
if (!selection.isCollapsed())
|
|
6514
|
-
|
|
6515
|
-
|
|
6516
|
-
|
|
6517
|
-
|
|
6518
|
-
|
|
6519
|
-
|
|
6520
|
-
|
|
6521
|
-
|
|
6522
|
-
|
|
6523
|
-
|
|
6524
|
-
if (this.#isCursorAtStart(selection)) {
|
|
6525
|
-
this.insertBefore($createParagraphNode());
|
|
6526
|
-
return null
|
|
6527
|
-
}
|
|
6528
|
-
|
|
6529
|
-
if (this.#isCursorOnEmptyLastLine(selection)) {
|
|
6530
|
-
$trimTrailingBlankNodes(this);
|
|
6531
|
-
|
|
6532
|
-
const paragraph = $createParagraphNode();
|
|
6533
|
-
this.insertAfter(paragraph);
|
|
6534
|
-
return paragraph
|
|
6535
|
-
}
|
|
6536
|
-
|
|
6537
|
-
return super.insertNewAfter(selection, restoreSelection)
|
|
6538
|
-
}
|
|
6539
|
-
|
|
6540
|
-
#clampSelectionOffset(selection) {
|
|
6541
|
-
const childrenSize = this.getChildrenSize();
|
|
6542
|
-
for (const point of [ selection.anchor, selection.focus ]) {
|
|
6543
|
-
if (point.type === "element" && point.key === this.__key && point.offset > childrenSize) {
|
|
6544
|
-
point.set(this.__key, childrenSize, "element");
|
|
6545
|
-
}
|
|
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)
|
|
6546
6553
|
}
|
|
6547
6554
|
}
|
|
6548
6555
|
|
|
@@ -6561,6 +6568,32 @@ class EarlyEscapeCodeNode extends CodeNode {
|
|
|
6561
6568
|
return textContent === "" || textContent.endsWith("\n")
|
|
6562
6569
|
}
|
|
6563
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
|
+
}
|
|
6564
6597
|
}
|
|
6565
6598
|
|
|
6566
6599
|
class EarlyEscapeListItemNode extends ListItemNode {
|
|
@@ -6965,7 +6998,7 @@ class LexicalEditorElement extends HTMLElement {
|
|
|
6965
6998
|
}
|
|
6966
6999
|
|
|
6967
7000
|
get #isContentFocused() {
|
|
6968
|
-
return !!this.
|
|
7001
|
+
return !!this.editor && isEditorFocused(this.editor)
|
|
6969
7002
|
}
|
|
6970
7003
|
|
|
6971
7004
|
get value() {
|
|
@@ -6979,14 +7012,24 @@ class LexicalEditorElement extends HTMLElement {
|
|
|
6979
7012
|
}
|
|
6980
7013
|
|
|
6981
7014
|
set value(html) {
|
|
7015
|
+
const editorHasFocus = this.#isContentFocused;
|
|
7016
|
+
|
|
6982
7017
|
this.editor.update(() => {
|
|
7018
|
+
if (editorHasFocus) {
|
|
7019
|
+
// Address Safari inconsistently placing the cursor in the contenteditable by forcing focus back onto the editor
|
|
7020
|
+
// Use direct `editor.focus` to bypass the pre-existing focus optimization and skip the callback
|
|
7021
|
+
$onUpdate(() => this.editor.focus());
|
|
7022
|
+
} else {
|
|
7023
|
+
$addUpdateTag(SKIP_DOM_SELECTION_TAG);
|
|
7024
|
+
}
|
|
7025
|
+
|
|
6983
7026
|
$getRoot()
|
|
6984
7027
|
.clear()
|
|
6985
7028
|
.selectEnd()
|
|
6986
7029
|
.insertNodes(this.#parseHtmlIntoLexicalNodes(html));
|
|
6987
7030
|
|
|
6988
7031
|
this.#toggleEmptyStatus();
|
|
6989
|
-
}, { discrete: true
|
|
7032
|
+
}, { discrete: true });
|
|
6990
7033
|
}
|
|
6991
7034
|
|
|
6992
7035
|
get canUndo() {
|
|
@@ -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
|