@lexical/selection 0.46.0 → 0.46.1-nightly.20260630.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/dist/LexicalSelection.dev.js +61 -20
- package/dist/LexicalSelection.dev.mjs +62 -22
- package/dist/LexicalSelection.js.flow +6 -0
- package/dist/LexicalSelection.mjs +1 -0
- package/dist/LexicalSelection.node.mjs +1 -0
- package/dist/LexicalSelection.prod.js +1 -1
- package/dist/LexicalSelection.prod.mjs +2 -2
- package/dist/index.d.ts +1 -1
- package/dist/range-selection.d.ts +16 -1
- package/package.json +3 -3
- package/src/index.ts +1 -0
- package/src/range-selection.ts +74 -21
|
@@ -558,28 +558,62 @@ function $copyBlockFormatIndent(srcNode, destNode) {
|
|
|
558
558
|
destNode.setIndent(indent);
|
|
559
559
|
}
|
|
560
560
|
}
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
561
|
+
|
|
562
|
+
/**
|
|
563
|
+
* Determine whether a point sits at the leading ('previous') or trailing
|
|
564
|
+
* ('next') edge of `element`'s content — i.e. there is no content between the
|
|
565
|
+
* point and that edge of the element.
|
|
566
|
+
*
|
|
567
|
+
* This is the caret-based generalization of {@link $isAtNodeEnd}. An empty
|
|
568
|
+
* `element` is considered to be at both of its edges. `@lexical/utils`
|
|
569
|
+
* re-exports this as the direction-specific `$isAtStartOfNode` /
|
|
570
|
+
* `$isAtEndOfNode` helpers.
|
|
571
|
+
*
|
|
572
|
+
* @param point - The point to test.
|
|
573
|
+
* @param element - The ancestor element whose edge is tested.
|
|
574
|
+
* @param direction - 'previous' for the start of `element`, 'next' for the end.
|
|
575
|
+
*/
|
|
576
|
+
function $isAtEdgeOfElement(point, element, direction) {
|
|
577
|
+
// An extendable TextPointCaret has text remaining in `direction`, so the
|
|
578
|
+
// point is in the middle of a TextNode rather than at the element edge.
|
|
579
|
+
let caret = lexical.$caretFromPoint(point, direction);
|
|
580
|
+
if (lexical.$isExtendableTextPointCaret(caret)) {
|
|
570
581
|
return false;
|
|
571
582
|
}
|
|
572
|
-
|
|
573
|
-
|
|
583
|
+
// Walk up towards element: the point is at the edge only when nothing
|
|
584
|
+
// precedes it in `direction` at every level up to element. The match is read
|
|
585
|
+
// from getParentAtCaret (origin.getParent()) rather than from a CaretRange
|
|
586
|
+
// iteration, because iterating ascends via getParentCaret, which stops at the
|
|
587
|
+
// document root and at shadow-root/slot boundaries — so it would never yield
|
|
588
|
+
// `element` when `element` is itself such a boundary (e.g. a named slot's
|
|
589
|
+
// value, a shadow root).
|
|
590
|
+
for (; caret; caret = caret.getParentCaret()) {
|
|
591
|
+
const parent = caret.getParentAtCaret();
|
|
592
|
+
if (!parent || caret.getNodeAtCaret()) {
|
|
574
593
|
return false;
|
|
575
594
|
}
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
return false;
|
|
595
|
+
if (element.is(parent)) {
|
|
596
|
+
return true;
|
|
579
597
|
}
|
|
580
|
-
node = parent;
|
|
581
598
|
}
|
|
582
|
-
return
|
|
599
|
+
return false;
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
/**
|
|
603
|
+
* Determine whether a point sits at the edge of a block in the given
|
|
604
|
+
* direction: 'previous' for the start of the block, 'next' for the end.
|
|
605
|
+
*
|
|
606
|
+
* Unlike {@link $isAtEdgeOfElement}, an empty block is treated as not being at
|
|
607
|
+
* the edge: when an ElementNode is empty it's not possible to distinguish if
|
|
608
|
+
* the selection's intent is the entire block or the edge so we consider it to
|
|
609
|
+
* be the entire block.
|
|
610
|
+
*/
|
|
611
|
+
function $isPointAtBlockEdge(point, block, direction) {
|
|
612
|
+
const node = point.getNode();
|
|
613
|
+
if (lexical.$isElementNode(node) && node.isEmpty()) {
|
|
614
|
+
return false;
|
|
615
|
+
}
|
|
616
|
+
return $isAtEdgeOfElement(point, block, direction);
|
|
583
617
|
}
|
|
584
618
|
|
|
585
619
|
/**
|
|
@@ -595,24 +629,30 @@ function $setBlocksType(selection, $createElement, $afterCreateElement = $copyBl
|
|
|
595
629
|
// Selections tend to not include their containing blocks so we effectively
|
|
596
630
|
// expand it here
|
|
597
631
|
const anchorAndFocus = selection.getStartEndPoints();
|
|
598
|
-
let
|
|
632
|
+
let skipFocus = false;
|
|
599
633
|
let focusBlock = null;
|
|
600
634
|
const blockMap = new Map();
|
|
601
635
|
if (anchorAndFocus) {
|
|
602
636
|
const [anchor, focus] = anchorAndFocus;
|
|
603
637
|
const anchorBlock = lexical.$findMatchingParent(anchor.getNode(), lexical.INTERNAL_$isBlock);
|
|
604
638
|
focusBlock = lexical.$findMatchingParent(focus.getNode(), lexical.INTERNAL_$isBlock);
|
|
605
|
-
|
|
639
|
+
// The focus is the moving edge of the selection, travelling in `direction`
|
|
640
|
+
// (towards the end of the document for a forward selection). When a
|
|
641
|
+
// selection overshoots, its focus lands at the leading edge of focusBlock
|
|
642
|
+
// in that direction — the edge opposite to travel — so focusBlock holds
|
|
643
|
+
// none of the selection and is skipped.
|
|
644
|
+
const direction = selection.isBackward() ? 'previous' : 'next';
|
|
645
|
+
skipFocus = lexical.$isElementNode(focusBlock) && !focusBlock.is(anchorBlock) && $isPointAtBlockEdge(focus, focusBlock, lexical.flipDirection(direction));
|
|
606
646
|
if (lexical.$isElementNode(anchorBlock)) {
|
|
607
647
|
blockMap.set(anchorBlock.getKey(), anchorBlock);
|
|
608
648
|
}
|
|
609
|
-
if (lexical.$isElementNode(focusBlock) && !
|
|
649
|
+
if (lexical.$isElementNode(focusBlock) && !skipFocus) {
|
|
610
650
|
blockMap.set(focusBlock.getKey(), focusBlock);
|
|
611
651
|
}
|
|
612
652
|
}
|
|
613
653
|
for (const node of selection.getNodes()) {
|
|
614
654
|
if (lexical.$isElementNode(node) && lexical.INTERNAL_$isBlock(node)) {
|
|
615
|
-
if (
|
|
655
|
+
if (skipFocus && node.is(focusBlock)) {
|
|
616
656
|
continue;
|
|
617
657
|
}
|
|
618
658
|
blockMap.set(node.getKey(), node);
|
|
@@ -1065,6 +1105,7 @@ exports.$forEachSelectedTextNode = $forEachSelectedTextNode;
|
|
|
1065
1105
|
exports.$getComputedStyleForElement = $getComputedStyleForElement;
|
|
1066
1106
|
exports.$getComputedStyleForParent = $getComputedStyleForParent;
|
|
1067
1107
|
exports.$getSelectionStyleValueForProperty = $getSelectionStyleValueForProperty;
|
|
1108
|
+
exports.$isAtEdgeOfElement = $isAtEdgeOfElement;
|
|
1068
1109
|
exports.$isAtNodeEnd = $isAtNodeEnd;
|
|
1069
1110
|
exports.$isParentElementRTL = $isParentElementRTL;
|
|
1070
1111
|
exports.$isParentRTL = $isParentRTL;
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import { getRootOwnerDocument, $isTextNode, $getEditor, $isRootNode, $getSelection, $isRangeSelection, $caretRangeFromSelection, $isTokenOrSegmented, $isElementNode, $getCharacterOffsets, $cloneWithPropertiesEphemeral, $getNodeByKey, $getPreviousSelection, $createTextNode, getStyleObjectFromCSS as getStyleObjectFromCSS$1, $
|
|
9
|
+
import { getRootOwnerDocument, $isTextNode, $getEditor, $isRootNode, $getSelection, $isRangeSelection, $caretRangeFromSelection, $isTokenOrSegmented, $isElementNode, $getCharacterOffsets, $cloneWithPropertiesEphemeral, $getNodeByKey, $getPreviousSelection, $createTextNode, getStyleObjectFromCSS as getStyleObjectFromCSS$1, $caretFromPoint, $isExtendableTextPointCaret, $findMatchingParent, INTERNAL_$isBlock, flipDirection, $extendCaretToRange, $isChildCaret, $isDecoratorNode, $isRootOrShadowRoot, $hasAncestor, $isLeafNode, $setSelection } from 'lexical';
|
|
10
10
|
export { $cloneWithProperties, $selectAll } from 'lexical';
|
|
11
11
|
|
|
12
12
|
/**
|
|
@@ -557,28 +557,62 @@ function $copyBlockFormatIndent(srcNode, destNode) {
|
|
|
557
557
|
destNode.setIndent(indent);
|
|
558
558
|
}
|
|
559
559
|
}
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
560
|
+
|
|
561
|
+
/**
|
|
562
|
+
* Determine whether a point sits at the leading ('previous') or trailing
|
|
563
|
+
* ('next') edge of `element`'s content — i.e. there is no content between the
|
|
564
|
+
* point and that edge of the element.
|
|
565
|
+
*
|
|
566
|
+
* This is the caret-based generalization of {@link $isAtNodeEnd}. An empty
|
|
567
|
+
* `element` is considered to be at both of its edges. `@lexical/utils`
|
|
568
|
+
* re-exports this as the direction-specific `$isAtStartOfNode` /
|
|
569
|
+
* `$isAtEndOfNode` helpers.
|
|
570
|
+
*
|
|
571
|
+
* @param point - The point to test.
|
|
572
|
+
* @param element - The ancestor element whose edge is tested.
|
|
573
|
+
* @param direction - 'previous' for the start of `element`, 'next' for the end.
|
|
574
|
+
*/
|
|
575
|
+
function $isAtEdgeOfElement(point, element, direction) {
|
|
576
|
+
// An extendable TextPointCaret has text remaining in `direction`, so the
|
|
577
|
+
// point is in the middle of a TextNode rather than at the element edge.
|
|
578
|
+
let caret = $caretFromPoint(point, direction);
|
|
579
|
+
if ($isExtendableTextPointCaret(caret)) {
|
|
569
580
|
return false;
|
|
570
581
|
}
|
|
571
|
-
|
|
572
|
-
|
|
582
|
+
// Walk up towards element: the point is at the edge only when nothing
|
|
583
|
+
// precedes it in `direction` at every level up to element. The match is read
|
|
584
|
+
// from getParentAtCaret (origin.getParent()) rather than from a CaretRange
|
|
585
|
+
// iteration, because iterating ascends via getParentCaret, which stops at the
|
|
586
|
+
// document root and at shadow-root/slot boundaries — so it would never yield
|
|
587
|
+
// `element` when `element` is itself such a boundary (e.g. a named slot's
|
|
588
|
+
// value, a shadow root).
|
|
589
|
+
for (; caret; caret = caret.getParentCaret()) {
|
|
590
|
+
const parent = caret.getParentAtCaret();
|
|
591
|
+
if (!parent || caret.getNodeAtCaret()) {
|
|
573
592
|
return false;
|
|
574
593
|
}
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
return false;
|
|
594
|
+
if (element.is(parent)) {
|
|
595
|
+
return true;
|
|
578
596
|
}
|
|
579
|
-
node = parent;
|
|
580
597
|
}
|
|
581
|
-
return
|
|
598
|
+
return false;
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
/**
|
|
602
|
+
* Determine whether a point sits at the edge of a block in the given
|
|
603
|
+
* direction: 'previous' for the start of the block, 'next' for the end.
|
|
604
|
+
*
|
|
605
|
+
* Unlike {@link $isAtEdgeOfElement}, an empty block is treated as not being at
|
|
606
|
+
* the edge: when an ElementNode is empty it's not possible to distinguish if
|
|
607
|
+
* the selection's intent is the entire block or the edge so we consider it to
|
|
608
|
+
* be the entire block.
|
|
609
|
+
*/
|
|
610
|
+
function $isPointAtBlockEdge(point, block, direction) {
|
|
611
|
+
const node = point.getNode();
|
|
612
|
+
if ($isElementNode(node) && node.isEmpty()) {
|
|
613
|
+
return false;
|
|
614
|
+
}
|
|
615
|
+
return $isAtEdgeOfElement(point, block, direction);
|
|
582
616
|
}
|
|
583
617
|
|
|
584
618
|
/**
|
|
@@ -594,24 +628,30 @@ function $setBlocksType(selection, $createElement, $afterCreateElement = $copyBl
|
|
|
594
628
|
// Selections tend to not include their containing blocks so we effectively
|
|
595
629
|
// expand it here
|
|
596
630
|
const anchorAndFocus = selection.getStartEndPoints();
|
|
597
|
-
let
|
|
631
|
+
let skipFocus = false;
|
|
598
632
|
let focusBlock = null;
|
|
599
633
|
const blockMap = new Map();
|
|
600
634
|
if (anchorAndFocus) {
|
|
601
635
|
const [anchor, focus] = anchorAndFocus;
|
|
602
636
|
const anchorBlock = $findMatchingParent(anchor.getNode(), INTERNAL_$isBlock);
|
|
603
637
|
focusBlock = $findMatchingParent(focus.getNode(), INTERNAL_$isBlock);
|
|
604
|
-
|
|
638
|
+
// The focus is the moving edge of the selection, travelling in `direction`
|
|
639
|
+
// (towards the end of the document for a forward selection). When a
|
|
640
|
+
// selection overshoots, its focus lands at the leading edge of focusBlock
|
|
641
|
+
// in that direction — the edge opposite to travel — so focusBlock holds
|
|
642
|
+
// none of the selection and is skipped.
|
|
643
|
+
const direction = selection.isBackward() ? 'previous' : 'next';
|
|
644
|
+
skipFocus = $isElementNode(focusBlock) && !focusBlock.is(anchorBlock) && $isPointAtBlockEdge(focus, focusBlock, flipDirection(direction));
|
|
605
645
|
if ($isElementNode(anchorBlock)) {
|
|
606
646
|
blockMap.set(anchorBlock.getKey(), anchorBlock);
|
|
607
647
|
}
|
|
608
|
-
if ($isElementNode(focusBlock) && !
|
|
648
|
+
if ($isElementNode(focusBlock) && !skipFocus) {
|
|
609
649
|
blockMap.set(focusBlock.getKey(), focusBlock);
|
|
610
650
|
}
|
|
611
651
|
}
|
|
612
652
|
for (const node of selection.getNodes()) {
|
|
613
653
|
if ($isElementNode(node) && INTERNAL_$isBlock(node)) {
|
|
614
|
-
if (
|
|
654
|
+
if (skipFocus && node.is(focusBlock)) {
|
|
615
655
|
continue;
|
|
616
656
|
}
|
|
617
657
|
blockMap.set(node.getKey(), node);
|
|
@@ -1055,4 +1095,4 @@ const getStyleObjectFromCSS = getStyleObjectFromCSS$1;
|
|
|
1055
1095
|
/** @deprecated renamed to {@link $trimTextContentFromAnchor} by @lexical/eslint-plugin rules-of-lexical */
|
|
1056
1096
|
const trimTextContentFromAnchor = $trimTextContentFromAnchor;
|
|
1057
1097
|
|
|
1058
|
-
export { $addNodeStyle, $copyBlockFormatIndent, $ensureForwardRangeSelection, $forEachSelectedTextNode, $getComputedStyleForElement, $getComputedStyleForParent, $getSelectionStyleValueForProperty, $isAtNodeEnd, $isParentElementRTL, $isParentRTL, $moveCaretSelection, $moveCharacter, $patchStyleText, $setBlocksType, $shouldOverrideDefaultCharacterSelection, $sliceSelectedTextNodeContent, $trimTextContentFromAnchor, $wrapNodes, createDOMRange, createRectsFromDOMRange, getCSSFromStyleObject, getStyleObjectFromCSS, trimTextContentFromAnchor };
|
|
1098
|
+
export { $addNodeStyle, $copyBlockFormatIndent, $ensureForwardRangeSelection, $forEachSelectedTextNode, $getComputedStyleForElement, $getComputedStyleForParent, $getSelectionStyleValueForProperty, $isAtEdgeOfElement, $isAtNodeEnd, $isParentElementRTL, $isParentRTL, $moveCaretSelection, $moveCharacter, $patchStyleText, $setBlocksType, $shouldOverrideDefaultCharacterSelection, $sliceSelectedTextNodeContent, $trimTextContentFromAnchor, $wrapNodes, createDOMRange, createRectsFromDOMRange, getCSSFromStyleObject, getStyleObjectFromCSS, trimTextContentFromAnchor };
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* @flow strict
|
|
8
8
|
*/
|
|
9
9
|
import type {
|
|
10
|
+
CaretDirection,
|
|
10
11
|
ElementNode,
|
|
11
12
|
LexicalEditor,
|
|
12
13
|
LexicalNode,
|
|
@@ -57,6 +58,11 @@ declare export function $setBlocksType(
|
|
|
57
58
|
createElement: () => ElementNode,
|
|
58
59
|
): void;
|
|
59
60
|
declare export function $isAtNodeEnd(point: Point): boolean;
|
|
61
|
+
declare export function $isAtEdgeOfElement(
|
|
62
|
+
point: Point,
|
|
63
|
+
element: ElementNode,
|
|
64
|
+
direction: CaretDirection,
|
|
65
|
+
): boolean;
|
|
60
66
|
declare export function $shouldOverrideDefaultCharacterSelection(
|
|
61
67
|
selection: RangeSelection,
|
|
62
68
|
isBackward: boolean,
|
|
@@ -17,6 +17,7 @@ export const $forEachSelectedTextNode = mod.$forEachSelectedTextNode;
|
|
|
17
17
|
export const $getComputedStyleForElement = mod.$getComputedStyleForElement;
|
|
18
18
|
export const $getComputedStyleForParent = mod.$getComputedStyleForParent;
|
|
19
19
|
export const $getSelectionStyleValueForProperty = mod.$getSelectionStyleValueForProperty;
|
|
20
|
+
export const $isAtEdgeOfElement = mod.$isAtEdgeOfElement;
|
|
20
21
|
export const $isAtNodeEnd = mod.$isAtNodeEnd;
|
|
21
22
|
export const $isParentElementRTL = mod.$isParentElementRTL;
|
|
22
23
|
export const $isParentRTL = mod.$isParentRTL;
|
|
@@ -15,6 +15,7 @@ export const $forEachSelectedTextNode = mod.$forEachSelectedTextNode;
|
|
|
15
15
|
export const $getComputedStyleForElement = mod.$getComputedStyleForElement;
|
|
16
16
|
export const $getComputedStyleForParent = mod.$getComputedStyleForParent;
|
|
17
17
|
export const $getSelectionStyleValueForProperty = mod.$getSelectionStyleValueForProperty;
|
|
18
|
+
export const $isAtEdgeOfElement = mod.$isAtEdgeOfElement;
|
|
18
19
|
export const $isAtNodeEnd = mod.$isAtNodeEnd;
|
|
19
20
|
export const $isParentElementRTL = mod.$isParentElementRTL;
|
|
20
21
|
export const $isParentRTL = mod.$isParentRTL;
|
|
@@ -7,4 +7,4 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
"use strict";var e=require("lexical");function t(e,...t){const n=new URL("https://lexical.dev/docs/error"),o=new URLSearchParams;o.append("code",e);for(const e of t)o.append("v",e);throw n.search=o.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.`)}
|
|
10
|
-
/*@__INLINE__*/function n(e){let t=e;for(;null!=t;){if(t.nodeType===Node.TEXT_NODE)return t;t=t.firstChild}return null}function o(e){const t=e.parentNode;if(null==t)throw new Error("Should never happen");return[t,Array.from(t.childNodes).indexOf(e)]}function r(e){let t="";for(const n in e)n&&(t+=`${n}: ${e[n]};`);return t}function s(t){const n=e.$getEditor().getElementByKey(t.getKey());if(null===n)return null;const o=n.ownerDocument.defaultView;return null===o?null:o.getComputedStyle(n)}function i(t){return s(e.$isRootNode(t)?t:t.getParentOrThrow())}function l(t,n,o){let r=n.getNode(),s=o;if(e.$isElementNode(r)){const e=r.getDescendantByIndex(n.offset);null!==e&&(r=e)}for(;s>0&&null!==r;){if(e.$isElementNode(r)){const e=r.getLastDescendant();null!==e&&(r=e)}let o=r.getPreviousSibling(),i=0;if(null===o){let e=r.getParentOrThrow(),t=e.getPreviousSibling();for(;null===t;){if(e=e.getParent(),null===e){o=null;break}t=e.getPreviousSibling()}null!==e&&(i=e.isInline()?0:2,o=t)}let l=r.getTextContent();""===l&&e.$isElementNode(r)&&!r.isInline()&&(l="\n\n");const c=l.length;if(!e.$isTextNode(r)||s>=c){const t=r.getParent();r.remove(),null==t||0!==t.getChildrenSize()||e.$isRootNode(t)||t.remove(),s-=c+i,r=o}else{const o=r.getKey(),i=t.read("latest",()=>{const t=e.$getNodeByKey(o);return e.$isTextNode(t)&&t.isSimpleText()?t.getTextContent():null}),f=c-s,d=l.slice(0,f);if(null!==i&&i!==l){const t=e.$getPreviousSelection();let n=r;if(r.isSimpleText())r.setTextContent(i);else{const t=e.$createTextNode(i);r.replace(t),n=t}if(e.$isRangeSelection(t)&&t.isCollapsed()){const e=t.anchor.offset;n.select(e,e)}}else if(r.isSimpleText()){const e=n.key===o;let t=n.offset;t<s&&(t=c);const i=e?t-s:0,l=e?t:f;if(e&&0===i){const[e]=r.splitText(i,l);e.remove()}else{const[,e]=r.splitText(i,l);e.remove()}}else{const t=e.$createTextNode(d);r.replace(t)}s=0}}}const c=()=>{};function f(n,o){(e.$isRangeSelection(n)?n.isCollapsed():e.$isTextNode(n)||e.$isElementNode(n))||t(280);const s=e.getStyleObjectFromCSS(e.$isRangeSelection(n)?n.style:e.$isTextNode(n)?n.getStyle():n.getTextStyle()),i=r(Object.entries(o).reduce((e,[t,o])=>("function"==typeof o?e[t]=o(s[t],n):null===o?delete e[t]:e[t]=o,e),{...s}));e.$isRangeSelection(n)||e.$isTextNode(n)?n.setStyle(i):n.setTextStyle(i)}function d(t){const n=e.$getSelection();if(!n)return;const o=new Map,r=e=>o.get(e.getKey())||[0,e.getTextContentSize()];if(e.$isRangeSelection(n))for(const t of e.$caretRangeFromSelection(n).getTextSlices())t&&o.set(t.caret.origin.getKey(),t.getSliceIndices());const s=n.getNodes();for(const n of s){if(!e.$isTextNode(n)||!n.canHaveFormat())continue;const[o,s]=r(n);if(s!==o)if(e.$isTokenOrSegmented(n)||0===o&&s===n.getTextContentSize())t(n);else{t(n.splitText(o,s)[0===o?0:1])}}e.$isRangeSelection(n)&&"text"===n.anchor.type&&"text"===n.focus.type&&n.anchor.key===n.focus.key&&a(n)}function a(e){if(e.isBackward()){const{anchor:t,focus:n}=e,{key:o,offset:r,type:s}=t;t.set(n.key,n.offset,n.type),n.set(o,r,s)}}function g(e,t){const n=e.getFormatType(),o=e.getIndent();n!==t.getFormatType()&&t.setFormat(n),o!==t.getIndent()&&t.setIndent(o)}function u(e){return e.getNode().isAttached()}function p(t){let n=t;for(;null!==n&&!e.$isRootOrShadowRoot(n);){const e=n.getLatest(),t=n.getParent();0===e.getChildrenSize()&&n.remove(!0),n=t}}function $(n,o,r,s,i=null){if(0===o.length)return;const l=o[0],c=new Map,f=[],d=e.$isElementNode(l)?l:l.getParentOrThrow();let a=d.isInline()?d.getParentOrThrow():d,g=!1;for(;null!==a;){const t=a.getPreviousSibling();if(null!==t){a=t,g=!0;break}if(a=a.getParentOrThrow(),e.$isRootOrShadowRoot(a))break}const $=new Set;for(let t=0;t<r;t++){const n=o[t];e.$isElementNode(n)&&0===n.getChildrenSize()&&$.add(n.getKey())}const h=new Set;for(let n=0;n<r;n++){const r=o[n];let i=r.getParent();if(null!==i&&i.isInline()&&(i=i.getParent()),null!==i&&e.$isLeafNode(r)&&!h.has(r.getKey())){const t=i.getKey();if(void 0===c.get(t)){const n=s();n.setFormat(i.getFormatType()),n.setIndent(i.getIndent()),f.push(n),c.set(t,n);const o=i.getChildren();n.splice(n.getChildrenSize(),0,o);for(const t of o)if(h.add(t.getKey()),e.$isElementNode(t))for(const e of t.getChildrenKeys())h.add(e);p(i)}}else if($.has(r.getKey())){e.$isElementNode(r)||t(179);const n=s();n.setFormat(r.getFormatType()),n.setIndent(r.getIndent()),f.push(n),r.remove(!0)}}if(null!==i)for(let e=0;e<f.length;e++){const t=f[e];i.append(t)}let S=null;if(e.$isRootOrShadowRoot(a))if(g)if(null!==i)a.insertAfter(i);else for(let e=f.length-1;e>=0;e--){const t=f[e];a.insertAfter(t)}else{const t=a,n=t.getFirstChild();if(e.$isElementNode(n)&&(a=n),null===n)if(i)t.append(i);else for(let e=0;e<f.length;e++){const n=f[e];t.append(n),S=n}else if(null!==i)n.insertBefore(i);else for(let e=0;e<f.length;e++){const t=f[e];n.insertBefore(t),S=t}}else if(i)a.insertAfter(i);else for(let e=f.length-1;e>=0;e--){const t=f[e];a.insertAfter(t),S=t}const m=e.$getPreviousSelection();e.$isRangeSelection(m)&&u(m.anchor)&&u(m.focus)?e.$setSelection(m.clone()):null!==S?S.selectEnd():n.dirty=!0}function h(e){const t=S(e);return null!==t&&"vertical-rl"===t.writingMode}function S(t){const n=t.anchor.getNode();return e.$isElementNode(n)?s(n):i(n)}function m(e,t,n,o){e.modify(t?"extend":"move",n,o)}function N(e){const t=S(e);return null!==t&&"rtl"===t.direction}function y(t,n,o){const r=t.getStyle(),s=e.getStyleObjectFromCSS(r);return null!==s&&s[n]||o}const x=e.getStyleObjectFromCSS,T=l;exports.$cloneWithProperties=e.$cloneWithProperties,exports.$selectAll=e.$selectAll,exports.$addNodeStyle=c,exports.$copyBlockFormatIndent=g,exports.$ensureForwardRangeSelection=a,exports.$forEachSelectedTextNode=d,exports.$getComputedStyleForElement=s,exports.$getComputedStyleForParent=i,exports.$getSelectionStyleValueForProperty=function(t,n,o=""){let r=null;const s=t.getNodes();let i,l;if(e.$isRangeSelection(t)){if(t.isCollapsed()&&""!==t.style){const o=e.getStyleObjectFromCSS(t.style);if(null!==o&&n in o)return o[n]}const{anchor:o,focus:r}=t,s=t.isBackward(),c=s?r.getNode():o.getNode(),f=s?o.getNode():r.getNode(),d=s?r.offset:o.offset,a=s?o.offset:r.offset;e.$isTextNode(c)&&d===c.getTextContentSize()&&(i=c),0===a&&(l=f)}for(let t=0;t<s.length;t++){const c=s[t];if(e.$isTextNode(c)&&!c.is(0===t?i:l)){const e=y(c,n,o);if(null===r)r=e;else if(r!==e){r="";break}}}return null===r?o:r},exports.$isAtNodeEnd=function(n){if("text"===n.type)return n.offset===n.getNode().getTextContentSize();const o=n.getNode();return e.$isElementNode(o)||t(177),n.offset===o.getChildrenSize()},exports.$isParentElementRTL=N,exports.$isParentRTL=function(e){const t=i(e);return null!==t&&"rtl"===t.direction},exports.$moveCaretSelection=m,exports.$moveCharacter=function(e,t,n){const o=N(e);let r;r=h(e)||o?!n:n,m(e,t,r,"character")},exports.$patchStyleText=function(t,n){if(e.$isRangeSelection(t)&&t.isCollapsed()){f(t,n);const o=t.anchor.getNode();e.$isElementNode(o)&&o.isEmpty()&&f(o,n)}d(e=>{f(e,n)});const o=t.getNodes();if(o.length>0){const t=new Set;for(const r of o){if(!e.$isElementNode(r)||!r.canBeEmpty()||0!==r.getChildrenSize())continue;const o=r.getKey();t.has(o)||(t.add(o),f(r,n))}}},exports.$setBlocksType=function(t,n,o=g){if(!t)return;const r=t.getStartEndPoints();let s=!1,i=null;const l=new Map;if(r){const[t,n]=r,o=e.$findMatchingParent(t.getNode(),e.INTERNAL_$isBlock);i=e.$findMatchingParent(n.getNode(),e.INTERNAL_$isBlock),s=e.$isElementNode(i)&&!i.is(o)&&function(t,n){if(0!==t.offset)return!1;let o=t.getNode();if(e.$isElementNode(o)&&o.isEmpty())return!1;for(;!o.is(n);){if(null!==o.getPreviousSibling())return!1;const e=o.getParent();if(null===e)return!1;o=e}return!0}(n,i),e.$isElementNode(o)&&l.set(o.getKey(),o),e.$isElementNode(i)&&!s&&l.set(i.getKey(),i)}for(const n of t.getNodes())if(e.$isElementNode(n)&&e.INTERNAL_$isBlock(n)){if(s&&n.is(i))continue;l.set(n.getKey(),n)}else if(!r){const t=e.$findMatchingParent(n,e.INTERNAL_$isBlock);e.$isElementNode(t)&&l.set(t.getKey(),t)}for(const e of l.values()){const t=n();o(e,t),e.replace(t,!0)}},exports.$shouldOverrideDefaultCharacterSelection=function(t,n){let o=h(t)?!n:n;N(t)&&(o=!o);const r=e.$caretFromPoint(t.focus,o?"previous":"next");if(e.$isExtendableTextPointCaret(r))return!1;for(const t of e.$extendCaretToRange(r)){if(e.$isChildCaret(t))return!t.origin.isInline();if(!e.$isElementNode(t.origin)){if(e.$isDecoratorNode(t.origin))return!0;break}}return!1},exports.$sliceSelectedTextNodeContent=function(t,n,o="self"){const r=t.getStartEndPoints();if(n.isSelected(t)&&!e.$isTokenOrSegmented(n)&&null!==r){const[s,i]=r,l=t.isBackward(),c=s.getNode(),f=i.getNode(),d=n.is(c),a=n.is(f);if(d||a){const[r,s]=e.$getCharacterOffsets(t),i=c.is(f),d=n.is(l?f:c),a=n.is(l?c:f);let g,u=0;if(i)u=r>s?s:r,g=r>s?r:s;else if(d){u=l?s:r,g=void 0}else if(a){u=0,g=l?r:s}const p=n.__text.slice(u,g);p!==n.__text&&("clone"===o&&(n=e.$cloneWithPropertiesEphemeral(n)),n.__text=p)}}return n},exports.$trimTextContentFromAnchor=l,exports.$wrapNodes=function(t,n,o=null){const r=t.getStartEndPoints(),s=r?r[0]:null,i=t.getNodes(),l=i.length;if(null!==s&&(0===l||1===l&&"element"===s.type&&0===s.getNode().getChildrenSize())){const e="text"===s.type?s.getNode().getParentOrThrow():s.getNode(),t=e.getChildren();let r=n();return r.setFormat(e.getFormatType()),r.setIndent(e.getIndent()),t.forEach(e=>r.append(e)),o&&(r=o.append(r)),void e.replace(r)}let c=null,f=[];for(let r=0;r<l;r++){const s=i[r];e.$isRootOrShadowRoot(s)?($(t,f,f.length,n,o),f=[],c=s):null===c||null!==c&&e.$hasAncestor(s,c)?f.push(s):($(t,f,f.length,n,o),f=[s])}$(t,f,f.length,n,o)},exports.createDOMRange=function(t,r,s,i,l){const c=r.getKey(),f=i.getKey(),d=e.getRootOwnerDocument(t.getRootElement()).createRange();let a=t.getElementByKey(c),g=t.getElementByKey(f),u=s,p=l;if(e.$isTextNode(r)&&(a=n(a)),e.$isTextNode(i)&&(g=n(g)),void 0===r||void 0===i||null===a||null===g)return null;"BR"===a.nodeName&&([a,u]=o(a)),"BR"===g.nodeName&&([g,p]=o(g));const $=a.firstChild;a===g&&null!=$&&"BR"===$.nodeName&&0===u&&0===p&&(p=1);try{d.setStart(a,u),d.setEnd(g,p)}catch(e){return null}return!d.collapsed||u===p&&c===f||(d.setStart(g,p),d.setEnd(a,u)),d},exports.createRectsFromDOMRange=function(e,t){const n=e.getRootElement();if(null===n)return[];const o=n.getBoundingClientRect(),r=getComputedStyle(n),s=parseFloat(r.paddingLeft)+parseFloat(r.paddingRight),i=Array.from(t.getClientRects());let l,c=i.length;i.sort((e,t)=>{const n=e.top-t.top;return Math.abs(n)<=3?e.left-t.left:n});for(let e=0;e<c;e++){const t=i[e],n=l&&l.top<=t.top&&l.top+l.height>t.top&&l.left+l.width>t.left,r=t.width+s===o.width;n||r?(i.splice(e--,1),c--):l=t}return i},exports.getCSSFromStyleObject=r,exports.getStyleObjectFromCSS=x,exports.trimTextContentFromAnchor=T;
|
|
10
|
+
/*@__INLINE__*/function n(e){let t=e;for(;null!=t;){if(t.nodeType===Node.TEXT_NODE)return t;t=t.firstChild}return null}function o(e){const t=e.parentNode;if(null==t)throw new Error("Should never happen");return[t,Array.from(t.childNodes).indexOf(e)]}function r(e){let t="";for(const n in e)n&&(t+=`${n}: ${e[n]};`);return t}function s(t){const n=e.$getEditor().getElementByKey(t.getKey());if(null===n)return null;const o=n.ownerDocument.defaultView;return null===o?null:o.getComputedStyle(n)}function i(t){return s(e.$isRootNode(t)?t:t.getParentOrThrow())}function l(t,n,o){let r=n.getNode(),s=o;if(e.$isElementNode(r)){const e=r.getDescendantByIndex(n.offset);null!==e&&(r=e)}for(;s>0&&null!==r;){if(e.$isElementNode(r)){const e=r.getLastDescendant();null!==e&&(r=e)}let o=r.getPreviousSibling(),i=0;if(null===o){let e=r.getParentOrThrow(),t=e.getPreviousSibling();for(;null===t;){if(e=e.getParent(),null===e){o=null;break}t=e.getPreviousSibling()}null!==e&&(i=e.isInline()?0:2,o=t)}let l=r.getTextContent();""===l&&e.$isElementNode(r)&&!r.isInline()&&(l="\n\n");const c=l.length;if(!e.$isTextNode(r)||s>=c){const t=r.getParent();r.remove(),null==t||0!==t.getChildrenSize()||e.$isRootNode(t)||t.remove(),s-=c+i,r=o}else{const o=r.getKey(),i=t.read("latest",()=>{const t=e.$getNodeByKey(o);return e.$isTextNode(t)&&t.isSimpleText()?t.getTextContent():null}),f=c-s,d=l.slice(0,f);if(null!==i&&i!==l){const t=e.$getPreviousSelection();let n=r;if(r.isSimpleText())r.setTextContent(i);else{const t=e.$createTextNode(i);r.replace(t),n=t}if(e.$isRangeSelection(t)&&t.isCollapsed()){const e=t.anchor.offset;n.select(e,e)}}else if(r.isSimpleText()){const e=n.key===o;let t=n.offset;t<s&&(t=c);const i=e?t-s:0,l=e?t:f;if(e&&0===i){const[e]=r.splitText(i,l);e.remove()}else{const[,e]=r.splitText(i,l);e.remove()}}else{const t=e.$createTextNode(d);r.replace(t)}s=0}}}const c=()=>{};function f(n,o){(e.$isRangeSelection(n)?n.isCollapsed():e.$isTextNode(n)||e.$isElementNode(n))||t(280);const s=e.getStyleObjectFromCSS(e.$isRangeSelection(n)?n.style:e.$isTextNode(n)?n.getStyle():n.getTextStyle()),i=r(Object.entries(o).reduce((e,[t,o])=>("function"==typeof o?e[t]=o(s[t],n):null===o?delete e[t]:e[t]=o,e),{...s}));e.$isRangeSelection(n)||e.$isTextNode(n)?n.setStyle(i):n.setTextStyle(i)}function d(t){const n=e.$getSelection();if(!n)return;const o=new Map,r=e=>o.get(e.getKey())||[0,e.getTextContentSize()];if(e.$isRangeSelection(n))for(const t of e.$caretRangeFromSelection(n).getTextSlices())t&&o.set(t.caret.origin.getKey(),t.getSliceIndices());const s=n.getNodes();for(const n of s){if(!e.$isTextNode(n)||!n.canHaveFormat())continue;const[o,s]=r(n);if(s!==o)if(e.$isTokenOrSegmented(n)||0===o&&s===n.getTextContentSize())t(n);else{t(n.splitText(o,s)[0===o?0:1])}}e.$isRangeSelection(n)&&"text"===n.anchor.type&&"text"===n.focus.type&&n.anchor.key===n.focus.key&&a(n)}function a(e){if(e.isBackward()){const{anchor:t,focus:n}=e,{key:o,offset:r,type:s}=t;t.set(n.key,n.offset,n.type),n.set(o,r,s)}}function g(e,t){const n=e.getFormatType(),o=e.getIndent();n!==t.getFormatType()&&t.setFormat(n),o!==t.getIndent()&&t.setIndent(o)}function u(t,n,o){let r=e.$caretFromPoint(t,o);if(e.$isExtendableTextPointCaret(r))return!1;for(;r;r=r.getParentCaret()){const e=r.getParentAtCaret();if(!e||r.getNodeAtCaret())return!1;if(n.is(e))return!0}return!1}function p(e){return e.getNode().isAttached()}function $(t){let n=t;for(;null!==n&&!e.$isRootOrShadowRoot(n);){const e=n.getLatest(),t=n.getParent();0===e.getChildrenSize()&&n.remove(!0),n=t}}function h(n,o,r,s,i=null){if(0===o.length)return;const l=o[0],c=new Map,f=[],d=e.$isElementNode(l)?l:l.getParentOrThrow();let a=d.isInline()?d.getParentOrThrow():d,g=!1;for(;null!==a;){const t=a.getPreviousSibling();if(null!==t){a=t,g=!0;break}if(a=a.getParentOrThrow(),e.$isRootOrShadowRoot(a))break}const u=new Set;for(let t=0;t<r;t++){const n=o[t];e.$isElementNode(n)&&0===n.getChildrenSize()&&u.add(n.getKey())}const h=new Set;for(let n=0;n<r;n++){const r=o[n];let i=r.getParent();if(null!==i&&i.isInline()&&(i=i.getParent()),null!==i&&e.$isLeafNode(r)&&!h.has(r.getKey())){const t=i.getKey();if(void 0===c.get(t)){const n=s();n.setFormat(i.getFormatType()),n.setIndent(i.getIndent()),f.push(n),c.set(t,n);const o=i.getChildren();n.splice(n.getChildrenSize(),0,o);for(const t of o)if(h.add(t.getKey()),e.$isElementNode(t))for(const e of t.getChildrenKeys())h.add(e);$(i)}}else if(u.has(r.getKey())){e.$isElementNode(r)||t(179);const n=s();n.setFormat(r.getFormatType()),n.setIndent(r.getIndent()),f.push(n),r.remove(!0)}}if(null!==i)for(let e=0;e<f.length;e++){const t=f[e];i.append(t)}let S=null;if(e.$isRootOrShadowRoot(a))if(g)if(null!==i)a.insertAfter(i);else for(let e=f.length-1;e>=0;e--){const t=f[e];a.insertAfter(t)}else{const t=a,n=t.getFirstChild();if(e.$isElementNode(n)&&(a=n),null===n)if(i)t.append(i);else for(let e=0;e<f.length;e++){const n=f[e];t.append(n),S=n}else if(null!==i)n.insertBefore(i);else for(let e=0;e<f.length;e++){const t=f[e];n.insertBefore(t),S=t}}else if(i)a.insertAfter(i);else for(let e=f.length-1;e>=0;e--){const t=f[e];a.insertAfter(t),S=t}const m=e.$getPreviousSelection();e.$isRangeSelection(m)&&p(m.anchor)&&p(m.focus)?e.$setSelection(m.clone()):null!==S?S.selectEnd():n.dirty=!0}function S(e){const t=m(e);return null!==t&&"vertical-rl"===t.writingMode}function m(t){const n=t.anchor.getNode();return e.$isElementNode(n)?s(n):i(n)}function N(e,t,n,o){e.modify(t?"extend":"move",n,o)}function x(e){const t=m(e);return null!==t&&"rtl"===t.direction}function y(t,n,o){const r=t.getStyle(),s=e.getStyleObjectFromCSS(r);return null!==s&&s[n]||o}const T=e.getStyleObjectFromCSS,E=l;exports.$cloneWithProperties=e.$cloneWithProperties,exports.$selectAll=e.$selectAll,exports.$addNodeStyle=c,exports.$copyBlockFormatIndent=g,exports.$ensureForwardRangeSelection=a,exports.$forEachSelectedTextNode=d,exports.$getComputedStyleForElement=s,exports.$getComputedStyleForParent=i,exports.$getSelectionStyleValueForProperty=function(t,n,o=""){let r=null;const s=t.getNodes();let i,l;if(e.$isRangeSelection(t)){if(t.isCollapsed()&&""!==t.style){const o=e.getStyleObjectFromCSS(t.style);if(null!==o&&n in o)return o[n]}const{anchor:o,focus:r}=t,s=t.isBackward(),c=s?r.getNode():o.getNode(),f=s?o.getNode():r.getNode(),d=s?r.offset:o.offset,a=s?o.offset:r.offset;e.$isTextNode(c)&&d===c.getTextContentSize()&&(i=c),0===a&&(l=f)}for(let t=0;t<s.length;t++){const c=s[t];if(e.$isTextNode(c)&&!c.is(0===t?i:l)){const e=y(c,n,o);if(null===r)r=e;else if(r!==e){r="";break}}}return null===r?o:r},exports.$isAtEdgeOfElement=u,exports.$isAtNodeEnd=function(n){if("text"===n.type)return n.offset===n.getNode().getTextContentSize();const o=n.getNode();return e.$isElementNode(o)||t(177),n.offset===o.getChildrenSize()},exports.$isParentElementRTL=x,exports.$isParentRTL=function(e){const t=i(e);return null!==t&&"rtl"===t.direction},exports.$moveCaretSelection=N,exports.$moveCharacter=function(e,t,n){const o=x(e);let r;r=S(e)||o?!n:n,N(e,t,r,"character")},exports.$patchStyleText=function(t,n){if(e.$isRangeSelection(t)&&t.isCollapsed()){f(t,n);const o=t.anchor.getNode();e.$isElementNode(o)&&o.isEmpty()&&f(o,n)}d(e=>{f(e,n)});const o=t.getNodes();if(o.length>0){const t=new Set;for(const r of o){if(!e.$isElementNode(r)||!r.canBeEmpty()||0!==r.getChildrenSize())continue;const o=r.getKey();t.has(o)||(t.add(o),f(r,n))}}},exports.$setBlocksType=function(t,n,o=g){if(!t)return;const r=t.getStartEndPoints();let s=!1,i=null;const l=new Map;if(r){const[n,o]=r,c=e.$findMatchingParent(n.getNode(),e.INTERNAL_$isBlock);i=e.$findMatchingParent(o.getNode(),e.INTERNAL_$isBlock);const f=t.isBackward()?"previous":"next";s=e.$isElementNode(i)&&!i.is(c)&&function(t,n,o){const r=t.getNode();return(!e.$isElementNode(r)||!r.isEmpty())&&u(t,n,o)}(o,i,e.flipDirection(f)),e.$isElementNode(c)&&l.set(c.getKey(),c),e.$isElementNode(i)&&!s&&l.set(i.getKey(),i)}for(const n of t.getNodes())if(e.$isElementNode(n)&&e.INTERNAL_$isBlock(n)){if(s&&n.is(i))continue;l.set(n.getKey(),n)}else if(!r){const t=e.$findMatchingParent(n,e.INTERNAL_$isBlock);e.$isElementNode(t)&&l.set(t.getKey(),t)}for(const e of l.values()){const t=n();o(e,t),e.replace(t,!0)}},exports.$shouldOverrideDefaultCharacterSelection=function(t,n){let o=S(t)?!n:n;x(t)&&(o=!o);const r=e.$caretFromPoint(t.focus,o?"previous":"next");if(e.$isExtendableTextPointCaret(r))return!1;for(const t of e.$extendCaretToRange(r)){if(e.$isChildCaret(t))return!t.origin.isInline();if(!e.$isElementNode(t.origin)){if(e.$isDecoratorNode(t.origin))return!0;break}}return!1},exports.$sliceSelectedTextNodeContent=function(t,n,o="self"){const r=t.getStartEndPoints();if(n.isSelected(t)&&!e.$isTokenOrSegmented(n)&&null!==r){const[s,i]=r,l=t.isBackward(),c=s.getNode(),f=i.getNode(),d=n.is(c),a=n.is(f);if(d||a){const[r,s]=e.$getCharacterOffsets(t),i=c.is(f),d=n.is(l?f:c),a=n.is(l?c:f);let g,u=0;if(i)u=r>s?s:r,g=r>s?r:s;else if(d){u=l?s:r,g=void 0}else if(a){u=0,g=l?r:s}const p=n.__text.slice(u,g);p!==n.__text&&("clone"===o&&(n=e.$cloneWithPropertiesEphemeral(n)),n.__text=p)}}return n},exports.$trimTextContentFromAnchor=l,exports.$wrapNodes=function(t,n,o=null){const r=t.getStartEndPoints(),s=r?r[0]:null,i=t.getNodes(),l=i.length;if(null!==s&&(0===l||1===l&&"element"===s.type&&0===s.getNode().getChildrenSize())){const e="text"===s.type?s.getNode().getParentOrThrow():s.getNode(),t=e.getChildren();let r=n();return r.setFormat(e.getFormatType()),r.setIndent(e.getIndent()),t.forEach(e=>r.append(e)),o&&(r=o.append(r)),void e.replace(r)}let c=null,f=[];for(let r=0;r<l;r++){const s=i[r];e.$isRootOrShadowRoot(s)?(h(t,f,f.length,n,o),f=[],c=s):null===c||null!==c&&e.$hasAncestor(s,c)?f.push(s):(h(t,f,f.length,n,o),f=[s])}h(t,f,f.length,n,o)},exports.createDOMRange=function(t,r,s,i,l){const c=r.getKey(),f=i.getKey(),d=e.getRootOwnerDocument(t.getRootElement()).createRange();let a=t.getElementByKey(c),g=t.getElementByKey(f),u=s,p=l;if(e.$isTextNode(r)&&(a=n(a)),e.$isTextNode(i)&&(g=n(g)),void 0===r||void 0===i||null===a||null===g)return null;"BR"===a.nodeName&&([a,u]=o(a)),"BR"===g.nodeName&&([g,p]=o(g));const $=a.firstChild;a===g&&null!=$&&"BR"===$.nodeName&&0===u&&0===p&&(p=1);try{d.setStart(a,u),d.setEnd(g,p)}catch(e){return null}return!d.collapsed||u===p&&c===f||(d.setStart(g,p),d.setEnd(a,u)),d},exports.createRectsFromDOMRange=function(e,t){const n=e.getRootElement();if(null===n)return[];const o=n.getBoundingClientRect(),r=getComputedStyle(n),s=parseFloat(r.paddingLeft)+parseFloat(r.paddingRight),i=Array.from(t.getClientRects());let l,c=i.length;i.sort((e,t)=>{const n=e.top-t.top;return Math.abs(n)<=3?e.left-t.left:n});for(let e=0;e<c;e++){const t=i[e],n=l&&l.top<=t.top&&l.top+l.height>t.top&&l.left+l.width>t.left,r=t.width+s===o.width;n||r?(i.splice(e--,1),c--):l=t}return i},exports.getCSSFromStyleObject=r,exports.getStyleObjectFromCSS=T,exports.trimTextContentFromAnchor=E;
|
|
@@ -6,5 +6,5 @@
|
|
|
6
6
|
*
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import{getRootOwnerDocument as e,$isTextNode as t,$getEditor as n,$isRootNode as o,$getSelection as l,$isRangeSelection as r,$caretRangeFromSelection as i,$isTokenOrSegmented as s,$isElementNode as c,$getCharacterOffsets as f,$cloneWithPropertiesEphemeral as u,$getNodeByKey as g,$getPreviousSelection as
|
|
10
|
-
/*@__INLINE__*/function
|
|
9
|
+
import{getRootOwnerDocument as e,$isTextNode as t,$getEditor as n,$isRootNode as o,$getSelection as l,$isRangeSelection as r,$caretRangeFromSelection as i,$isTokenOrSegmented as s,$isElementNode as c,$getCharacterOffsets as f,$cloneWithPropertiesEphemeral as u,$getNodeByKey as g,$getPreviousSelection as a,$createTextNode as d,getStyleObjectFromCSS as p,$caretFromPoint as h,$isExtendableTextPointCaret as y,$findMatchingParent as m,INTERNAL_$isBlock as S,flipDirection as x,$extendCaretToRange as C,$isChildCaret as N,$isDecoratorNode as T,$isRootOrShadowRoot as w,$hasAncestor as v,$isLeafNode as P,$setSelection as K}from"lexical";export{$cloneWithProperties,$selectAll}from"lexical";function E(e,...t){const n=new URL("https://lexical.dev/docs/error"),o=new URLSearchParams;o.append("code",e);for(const e of t)o.append("v",e);throw n.search=o.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.`)}
|
|
10
|
+
/*@__INLINE__*/function I(e){let t=e;for(;null!=t;){if(t.nodeType===Node.TEXT_NODE)return t;t=t.firstChild}return null}function B(e){const t=e.parentNode;if(null==t)throw new Error("Should never happen");return[t,Array.from(t.childNodes).indexOf(e)]}function k(n,o,l,r,i){const s=o.getKey(),c=r.getKey(),f=e(n.getRootElement()).createRange();let u=n.getElementByKey(s),g=n.getElementByKey(c),a=l,d=i;if(t(o)&&(u=I(u)),t(r)&&(g=I(g)),void 0===o||void 0===r||null===u||null===g)return null;"BR"===u.nodeName&&([u,a]=B(u)),"BR"===g.nodeName&&([g,d]=B(g));const p=u.firstChild;u===g&&null!=p&&"BR"===p.nodeName&&0===a&&0===d&&(d=1);try{f.setStart(u,a),f.setEnd(g,d)}catch(e){return null}return!f.collapsed||a===d&&s===c||(f.setStart(g,d),f.setEnd(u,a)),f}function F(e,t){const n=e.getRootElement();if(null===n)return[];const o=n.getBoundingClientRect(),l=getComputedStyle(n),r=parseFloat(l.paddingLeft)+parseFloat(l.paddingRight),i=Array.from(t.getClientRects());let s,c=i.length;i.sort((e,t)=>{const n=e.top-t.top;return Math.abs(n)<=3?e.left-t.left:n});for(let e=0;e<c;e++){const t=i[e],n=s&&s.top<=t.top&&s.top+s.height>t.top&&s.left+s.width>t.left,l=t.width+r===o.width;n||l?(i.splice(e--,1),c--):s=t}return i}function b(e){let t="";for(const n in e)n&&(t+=`${n}: ${e[n]};`);return t}function z(e){const t=n().getElementByKey(e.getKey());if(null===t)return null;const o=t.ownerDocument.defaultView;return null===o?null:o.getComputedStyle(t)}function R(e){return z(o(e)?e:e.getParentOrThrow())}function A(e){const t=R(e);return null!==t&&"rtl"===t.direction}function O(e,t,n="self"){const o=e.getStartEndPoints();if(t.isSelected(e)&&!s(t)&&null!==o){const[l,r]=o,i=e.isBackward(),s=l.getNode(),c=r.getNode(),g=t.is(s),a=t.is(c);if(g||a){const[o,l]=f(e),r=s.is(c),g=t.is(i?c:s),a=t.is(i?s:c);let d,p=0;if(r)p=o>l?l:o,d=o>l?o:l;else if(g){p=i?l:o,d=void 0}else if(a){p=0,d=i?o:l}const h=t.__text.slice(p,d);h!==t.__text&&("clone"===n&&(t=u(t)),t.__text=h)}}return t}function _(e){if("text"===e.type)return e.offset===e.getNode().getTextContentSize();const t=e.getNode();return c(t)||E(177),e.offset===t.getChildrenSize()}function L(e,n,l){let i=n.getNode(),s=l;if(c(i)){const e=i.getDescendantByIndex(n.offset);null!==e&&(i=e)}for(;s>0&&null!==i;){if(c(i)){const e=i.getLastDescendant();null!==e&&(i=e)}let l=i.getPreviousSibling(),f=0;if(null===l){let e=i.getParentOrThrow(),t=e.getPreviousSibling();for(;null===t;){if(e=e.getParent(),null===e){l=null;break}t=e.getPreviousSibling()}null!==e&&(f=e.isInline()?0:2,l=t)}let u=i.getTextContent();""===u&&c(i)&&!i.isInline()&&(u="\n\n");const p=u.length;if(!t(i)||s>=p){const e=i.getParent();i.remove(),null==e||0!==e.getChildrenSize()||o(e)||e.remove(),s-=p+f,i=l}else{const o=i.getKey(),l=e.read("latest",()=>{const e=g(o);return t(e)&&e.isSimpleText()?e.getTextContent():null}),c=p-s,f=u.slice(0,c);if(null!==l&&l!==u){const e=a();let t=i;if(i.isSimpleText())i.setTextContent(l);else{const e=d(l);i.replace(e),t=e}if(r(e)&&e.isCollapsed()){const n=e.anchor.offset;t.select(n,n)}}else if(i.isSimpleText()){const e=n.key===o;let t=n.offset;t<s&&(t=p);const l=e?t-s:0,r=e?t:c;if(e&&0===l){const[e]=i.splitText(l,r);e.remove()}else{const[,e]=i.splitText(l,r);e.remove()}}else{const e=d(f);i.replace(e)}s=0}}}const M=()=>{};function $(e,n){(r(e)?e.isCollapsed():t(e)||c(e))||E(280);const o=p(r(e)?e.style:t(e)?e.getStyle():e.getTextStyle()),l=b(Object.entries(n).reduce((t,[n,l])=>("function"==typeof l?t[n]=l(o[n],e):null===l?delete t[n]:t[n]=l,t),{...o}));r(e)||t(e)?e.setStyle(l):e.setTextStyle(l)}function D(e,t){if(r(e)&&e.isCollapsed()){$(e,t);const n=e.anchor.getNode();c(n)&&n.isEmpty()&&$(n,t)}j(e=>{$(e,t)});const n=e.getNodes();if(n.length>0){const e=new Set;for(const o of n){if(!c(o)||!o.canBeEmpty()||0!==o.getChildrenSize())continue;const n=o.getKey();e.has(n)||(e.add(n),$(o,t))}}}function j(e){const n=l();if(!n)return;const o=new Map,c=e=>o.get(e.getKey())||[0,e.getTextContentSize()];if(r(n))for(const e of i(n).getTextSlices())e&&o.set(e.caret.origin.getKey(),e.getSliceIndices());const f=n.getNodes();for(const n of f){if(!t(n)||!n.canHaveFormat())continue;const[o,l]=c(n);if(l!==o)if(s(n)||0===o&&l===n.getTextContentSize())e(n);else{e(n.splitText(o,l)[0===o?0:1])}}r(n)&&"text"===n.anchor.type&&"text"===n.focus.type&&n.anchor.key===n.focus.key&&U(n)}function U(e){if(e.isBackward()){const{anchor:t,focus:n}=e,{key:o,offset:l,type:r}=t;t.set(n.key,n.offset,n.type),n.set(o,l,r)}}function H(e,t){const n=e.getFormatType(),o=e.getIndent();n!==t.getFormatType()&&t.setFormat(n),o!==t.getIndent()&&t.setIndent(o)}function V(e,t,n){let o=h(e,n);if(y(o))return!1;for(;o;o=o.getParentCaret()){const e=o.getParentAtCaret();if(!e||o.getNodeAtCaret())return!1;if(t.is(e))return!0}return!1}function W(e,t,n=H){if(!e)return;const o=e.getStartEndPoints();let l=!1,r=null;const i=new Map;if(o){const[t,n]=o,s=m(t.getNode(),S);r=m(n.getNode(),S);const f=e.isBackward()?"previous":"next";l=c(r)&&!r.is(s)&&function(e,t,n){const o=e.getNode();return(!c(o)||!o.isEmpty())&&V(e,t,n)}(n,r,x(f)),c(s)&&i.set(s.getKey(),s),c(r)&&!l&&i.set(r.getKey(),r)}for(const t of e.getNodes())if(c(t)&&S(t)){if(l&&t.is(r))continue;i.set(t.getKey(),t)}else if(!o){const e=m(t,S);c(e)&&i.set(e.getKey(),e)}for(const e of i.values()){const o=t();n(e,o),e.replace(o,!0)}}function X(e){return e.getNode().isAttached()}function q(e){let t=e;for(;null!==t&&!w(t);){const e=t.getLatest(),n=t.getParent();0===e.getChildrenSize()&&t.remove(!0),t=n}}function G(e,t,n=null){const o=e.getStartEndPoints(),l=o?o[0]:null,r=e.getNodes(),i=r.length;if(null!==l&&(0===i||1===i&&"element"===l.type&&0===l.getNode().getChildrenSize())){const e="text"===l.type?l.getNode().getParentOrThrow():l.getNode(),o=e.getChildren();let r=t();return r.setFormat(e.getFormatType()),r.setIndent(e.getIndent()),o.forEach(e=>r.append(e)),n&&(r=n.append(r)),void e.replace(r)}let s=null,c=[];for(let o=0;o<i;o++){const l=r[o];w(l)?(J(e,c,c.length,t,n),c=[],s=l):null===s||null!==s&&v(l,s)?c.push(l):(J(e,c,c.length,t,n),c=[l])}J(e,c,c.length,t,n)}function J(e,t,n,o,l=null){if(0===t.length)return;const i=t[0],s=new Map,f=[],u=c(i)?i:i.getParentOrThrow();let g=u.isInline()?u.getParentOrThrow():u,d=!1;for(;null!==g;){const e=g.getPreviousSibling();if(null!==e){g=e,d=!0;break}if(g=g.getParentOrThrow(),w(g))break}const p=new Set;for(let e=0;e<n;e++){const n=t[e];c(n)&&0===n.getChildrenSize()&&p.add(n.getKey())}const h=new Set;for(let e=0;e<n;e++){const n=t[e];let l=n.getParent();if(null!==l&&l.isInline()&&(l=l.getParent()),null!==l&&P(n)&&!h.has(n.getKey())){const e=l.getKey();if(void 0===s.get(e)){const t=o();t.setFormat(l.getFormatType()),t.setIndent(l.getIndent()),f.push(t),s.set(e,t);const n=l.getChildren();t.splice(t.getChildrenSize(),0,n);for(const e of n)if(h.add(e.getKey()),c(e))for(const t of e.getChildrenKeys())h.add(t);q(l)}}else if(p.has(n.getKey())){c(n)||E(179);const e=o();e.setFormat(n.getFormatType()),e.setIndent(n.getIndent()),f.push(e),n.remove(!0)}}if(null!==l)for(let e=0;e<f.length;e++){const t=f[e];l.append(t)}let y=null;if(w(g))if(d)if(null!==l)g.insertAfter(l);else for(let e=f.length-1;e>=0;e--){const t=f[e];g.insertAfter(t)}else{const e=g,t=e.getFirstChild();if(c(t)&&(g=t),null===t)if(l)e.append(l);else for(let t=0;t<f.length;t++){const n=f[t];e.append(n),y=n}else if(null!==l)t.insertBefore(l);else for(let e=0;e<f.length;e++){const n=f[e];t.insertBefore(n),y=n}}else if(l)g.insertAfter(l);else for(let e=f.length-1;e>=0;e--){const t=f[e];g.insertAfter(t),y=t}const m=a();r(m)&&X(m.anchor)&&X(m.focus)?K(m.clone()):null!==y?y.selectEnd():e.dirty=!0}function Q(e){const t=Y(e);return null!==t&&"vertical-rl"===t.writingMode}function Y(e){const t=e.anchor.getNode();return c(t)?z(t):R(t)}function Z(e,t){let n=Q(e)?!t:t;te(e)&&(n=!n);const o=h(e.focus,n?"previous":"next");if(y(o))return!1;for(const e of C(o)){if(N(e))return!e.origin.isInline();if(!c(e.origin)){if(T(e.origin))return!0;break}}return!1}function ee(e,t,n,o){e.modify(t?"extend":"move",n,o)}function te(e){const t=Y(e);return null!==t&&"rtl"===t.direction}function ne(e,t,n){const o=te(e);let l;l=Q(e)||o?!n:n,ee(e,t,l,"character")}function oe(e,t,n){const o=e.getStyle(),l=p(o);return null!==l&&l[t]||n}function le(e,n,o=""){let l=null;const i=e.getNodes();let s,c;if(r(e)){if(e.isCollapsed()&&""!==e.style){const t=p(e.style);if(null!==t&&n in t)return t[n]}const{anchor:o,focus:l}=e,r=e.isBackward(),i=r?l.getNode():o.getNode(),f=r?o.getNode():l.getNode(),u=r?l.offset:o.offset,g=r?o.offset:l.offset;t(i)&&u===i.getTextContentSize()&&(s=i),0===g&&(c=f)}for(let e=0;e<i.length;e++){const r=i[e];if(t(r)&&!r.is(0===e?s:c)){const e=oe(r,n,o);if(null===l)l=e;else if(l!==e){l="";break}}}return null===l?o:l}const re=p,ie=L;export{M as $addNodeStyle,H as $copyBlockFormatIndent,U as $ensureForwardRangeSelection,j as $forEachSelectedTextNode,z as $getComputedStyleForElement,R as $getComputedStyleForParent,le as $getSelectionStyleValueForProperty,V as $isAtEdgeOfElement,_ as $isAtNodeEnd,te as $isParentElementRTL,A as $isParentRTL,ee as $moveCaretSelection,ne as $moveCharacter,D as $patchStyleText,W as $setBlocksType,Z as $shouldOverrideDefaultCharacterSelection,O as $sliceSelectedTextNodeContent,L as $trimTextContentFromAnchor,G as $wrapNodes,k as createDOMRange,F as createRectsFromDOMRange,b as getCSSFromStyleObject,re as getStyleObjectFromCSS,ie as trimTextContentFromAnchor};
|
package/dist/index.d.ts
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
import { getStyleObjectFromCSS as getStyleObjectFromCSS_ } from 'lexical';
|
|
9
9
|
import { $trimTextContentFromAnchor } from './lexical-node';
|
|
10
10
|
export { $addNodeStyle, $ensureForwardRangeSelection, $forEachSelectedTextNode, $isAtNodeEnd, $patchStyleText, $sliceSelectedTextNodeContent, $trimTextContentFromAnchor, } from './lexical-node';
|
|
11
|
-
export { $copyBlockFormatIndent, $getSelectionStyleValueForProperty, $isParentElementRTL, $moveCaretSelection, $moveCharacter, $setBlocksType, $shouldOverrideDefaultCharacterSelection, $wrapNodes, } from './range-selection';
|
|
11
|
+
export { $copyBlockFormatIndent, $getSelectionStyleValueForProperty, $isAtEdgeOfElement, $isParentElementRTL, $moveCaretSelection, $moveCharacter, $setBlocksType, $shouldOverrideDefaultCharacterSelection, $wrapNodes, } from './range-selection';
|
|
12
12
|
export { $getComputedStyleForElement, $getComputedStyleForParent, $isParentRTL, createDOMRange, createRectsFromDOMRange, getCSSFromStyleObject, } from './utils';
|
|
13
13
|
/** @deprecated moved to the `lexical` package */
|
|
14
14
|
export declare const getStyleObjectFromCSS: typeof getStyleObjectFromCSS_;
|
|
@@ -5,8 +5,23 @@
|
|
|
5
5
|
* LICENSE file in the root directory of this source tree.
|
|
6
6
|
*
|
|
7
7
|
*/
|
|
8
|
-
import type { BaseSelection, ElementNode, LexicalNode, RangeSelection } from 'lexical';
|
|
8
|
+
import type { BaseSelection, CaretDirection, ElementNode, LexicalNode, Point, RangeSelection } from 'lexical';
|
|
9
9
|
export declare function $copyBlockFormatIndent(srcNode: ElementNode, destNode: ElementNode): void;
|
|
10
|
+
/**
|
|
11
|
+
* Determine whether a point sits at the leading ('previous') or trailing
|
|
12
|
+
* ('next') edge of `element`'s content — i.e. there is no content between the
|
|
13
|
+
* point and that edge of the element.
|
|
14
|
+
*
|
|
15
|
+
* This is the caret-based generalization of {@link $isAtNodeEnd}. An empty
|
|
16
|
+
* `element` is considered to be at both of its edges. `@lexical/utils`
|
|
17
|
+
* re-exports this as the direction-specific `$isAtStartOfNode` /
|
|
18
|
+
* `$isAtEndOfNode` helpers.
|
|
19
|
+
*
|
|
20
|
+
* @param point - The point to test.
|
|
21
|
+
* @param element - The ancestor element whose edge is tested.
|
|
22
|
+
* @param direction - 'previous' for the start of `element`, 'next' for the end.
|
|
23
|
+
*/
|
|
24
|
+
export declare function $isAtEdgeOfElement(point: Point, element: ElementNode, direction: CaretDirection): boolean;
|
|
10
25
|
/**
|
|
11
26
|
* Converts all nodes in the selection that are of one block type to another.
|
|
12
27
|
* @param selection - The selected blocks to be converted.
|
package/package.json
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"selection"
|
|
10
10
|
],
|
|
11
11
|
"license": "MIT",
|
|
12
|
-
"version": "0.46.0",
|
|
12
|
+
"version": "0.46.1-nightly.20260630.0",
|
|
13
13
|
"main": "./dist/LexicalSelection.js",
|
|
14
14
|
"types": "./dist/typescript-too-old.d.ts",
|
|
15
15
|
"repository": {
|
|
@@ -40,8 +40,8 @@
|
|
|
40
40
|
}
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
|
-
"@lexical/internal": "0.46.0",
|
|
44
|
-
"lexical": "0.46.0"
|
|
43
|
+
"@lexical/internal": "0.46.1-nightly.20260630.0",
|
|
44
|
+
"lexical": "0.46.1-nightly.20260630.0"
|
|
45
45
|
},
|
|
46
46
|
"files": [
|
|
47
47
|
"dist",
|
package/src/index.ts
CHANGED
package/src/range-selection.ts
CHANGED
|
@@ -8,11 +8,13 @@
|
|
|
8
8
|
|
|
9
9
|
import type {
|
|
10
10
|
BaseSelection,
|
|
11
|
+
CaretDirection,
|
|
11
12
|
DecoratorNode,
|
|
12
13
|
ElementNode,
|
|
13
14
|
LexicalNode,
|
|
14
15
|
NodeKey,
|
|
15
16
|
Point,
|
|
17
|
+
PointCaret,
|
|
16
18
|
RangeSelection,
|
|
17
19
|
TextNode,
|
|
18
20
|
} from 'lexical';
|
|
@@ -33,6 +35,7 @@ import {
|
|
|
33
35
|
$isRootOrShadowRoot,
|
|
34
36
|
$isTextNode,
|
|
35
37
|
$setSelection,
|
|
38
|
+
flipDirection,
|
|
36
39
|
getStyleObjectFromCSS,
|
|
37
40
|
INTERNAL_$isBlock,
|
|
38
41
|
} from 'lexical';
|
|
@@ -53,28 +56,72 @@ export function $copyBlockFormatIndent(
|
|
|
53
56
|
}
|
|
54
57
|
}
|
|
55
58
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
59
|
+
/**
|
|
60
|
+
* Determine whether a point sits at the leading ('previous') or trailing
|
|
61
|
+
* ('next') edge of `element`'s content — i.e. there is no content between the
|
|
62
|
+
* point and that edge of the element.
|
|
63
|
+
*
|
|
64
|
+
* This is the caret-based generalization of {@link $isAtNodeEnd}. An empty
|
|
65
|
+
* `element` is considered to be at both of its edges. `@lexical/utils`
|
|
66
|
+
* re-exports this as the direction-specific `$isAtStartOfNode` /
|
|
67
|
+
* `$isAtEndOfNode` helpers.
|
|
68
|
+
*
|
|
69
|
+
* @param point - The point to test.
|
|
70
|
+
* @param element - The ancestor element whose edge is tested.
|
|
71
|
+
* @param direction - 'previous' for the start of `element`, 'next' for the end.
|
|
72
|
+
*/
|
|
73
|
+
export function $isAtEdgeOfElement(
|
|
74
|
+
point: Point,
|
|
75
|
+
element: ElementNode,
|
|
76
|
+
direction: CaretDirection,
|
|
77
|
+
): boolean {
|
|
78
|
+
// An extendable TextPointCaret has text remaining in `direction`, so the
|
|
79
|
+
// point is in the middle of a TextNode rather than at the element edge.
|
|
80
|
+
let caret: PointCaret<typeof direction> | null = $caretFromPoint(
|
|
81
|
+
point,
|
|
82
|
+
direction,
|
|
83
|
+
);
|
|
84
|
+
if ($isExtendableTextPointCaret(caret)) {
|
|
65
85
|
return false;
|
|
66
86
|
}
|
|
67
|
-
|
|
68
|
-
|
|
87
|
+
// Walk up towards element: the point is at the edge only when nothing
|
|
88
|
+
// precedes it in `direction` at every level up to element. The match is read
|
|
89
|
+
// from getParentAtCaret (origin.getParent()) rather than from a CaretRange
|
|
90
|
+
// iteration, because iterating ascends via getParentCaret, which stops at the
|
|
91
|
+
// document root and at shadow-root/slot boundaries — so it would never yield
|
|
92
|
+
// `element` when `element` is itself such a boundary (e.g. a named slot's
|
|
93
|
+
// value, a shadow root).
|
|
94
|
+
for (; caret; caret = caret.getParentCaret()) {
|
|
95
|
+
const parent = caret.getParentAtCaret();
|
|
96
|
+
if (!parent || caret.getNodeAtCaret()) {
|
|
69
97
|
return false;
|
|
70
98
|
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
return false;
|
|
99
|
+
if (element.is(parent)) {
|
|
100
|
+
return true;
|
|
74
101
|
}
|
|
75
|
-
node = parent;
|
|
76
102
|
}
|
|
77
|
-
return
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Determine whether a point sits at the edge of a block in the given
|
|
108
|
+
* direction: 'previous' for the start of the block, 'next' for the end.
|
|
109
|
+
*
|
|
110
|
+
* Unlike {@link $isAtEdgeOfElement}, an empty block is treated as not being at
|
|
111
|
+
* the edge: when an ElementNode is empty it's not possible to distinguish if
|
|
112
|
+
* the selection's intent is the entire block or the edge so we consider it to
|
|
113
|
+
* be the entire block.
|
|
114
|
+
*/
|
|
115
|
+
function $isPointAtBlockEdge(
|
|
116
|
+
point: Point,
|
|
117
|
+
block: ElementNode,
|
|
118
|
+
direction: CaretDirection,
|
|
119
|
+
): boolean {
|
|
120
|
+
const node = point.getNode();
|
|
121
|
+
if ($isElementNode(node) && node.isEmpty()) {
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
124
|
+
return $isAtEdgeOfElement(point, block, direction);
|
|
78
125
|
}
|
|
79
126
|
|
|
80
127
|
/**
|
|
@@ -97,7 +144,7 @@ export function $setBlocksType<T extends ElementNode>(
|
|
|
97
144
|
// Selections tend to not include their containing blocks so we effectively
|
|
98
145
|
// expand it here
|
|
99
146
|
const anchorAndFocus = selection.getStartEndPoints();
|
|
100
|
-
let
|
|
147
|
+
let skipFocus = false;
|
|
101
148
|
let focusBlock: ElementNode | DecoratorNode<unknown> | null = null;
|
|
102
149
|
const blockMap = new Map<NodeKey, ElementNode>();
|
|
103
150
|
if (anchorAndFocus) {
|
|
@@ -107,20 +154,26 @@ export function $setBlocksType<T extends ElementNode>(
|
|
|
107
154
|
INTERNAL_$isBlock,
|
|
108
155
|
);
|
|
109
156
|
focusBlock = $findMatchingParent(focus.getNode(), INTERNAL_$isBlock);
|
|
110
|
-
|
|
157
|
+
// The focus is the moving edge of the selection, travelling in `direction`
|
|
158
|
+
// (towards the end of the document for a forward selection). When a
|
|
159
|
+
// selection overshoots, its focus lands at the leading edge of focusBlock
|
|
160
|
+
// in that direction — the edge opposite to travel — so focusBlock holds
|
|
161
|
+
// none of the selection and is skipped.
|
|
162
|
+
const direction = selection.isBackward() ? 'previous' : 'next';
|
|
163
|
+
skipFocus =
|
|
111
164
|
$isElementNode(focusBlock) &&
|
|
112
165
|
!focusBlock.is(anchorBlock) &&
|
|
113
|
-
$
|
|
166
|
+
$isPointAtBlockEdge(focus, focusBlock, flipDirection(direction));
|
|
114
167
|
if ($isElementNode(anchorBlock)) {
|
|
115
168
|
blockMap.set(anchorBlock.getKey(), anchorBlock);
|
|
116
169
|
}
|
|
117
|
-
if ($isElementNode(focusBlock) && !
|
|
170
|
+
if ($isElementNode(focusBlock) && !skipFocus) {
|
|
118
171
|
blockMap.set(focusBlock.getKey(), focusBlock);
|
|
119
172
|
}
|
|
120
173
|
}
|
|
121
174
|
for (const node of selection.getNodes()) {
|
|
122
175
|
if ($isElementNode(node) && INTERNAL_$isBlock(node)) {
|
|
123
|
-
if (
|
|
176
|
+
if (skipFocus && node.is(focusBlock)) {
|
|
124
177
|
continue;
|
|
125
178
|
}
|
|
126
179
|
blockMap.set(node.getKey(), node);
|