@lexical/selection 0.52.0 → 0.52.1-nightly.20260930.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.
@@ -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, $caretFromPoint, $isExtendableTextPointCaret, $findMatchingParent, INTERNAL_$isBlock, flipDirection, $getSlotHost, $isTextPointCaret, $isTabNode, $extendCaretToRange, $isChildCaret, $isDecoratorNode, $getSlotFrame, $isRootOrShadowRoot, $hasAncestor, $isLeafNode, $setSelection } from 'lexical';
9
+ import { getRootOwnerDocument, $isTextNode, $getEditor, $isRootNode, $getSelection, $isElementNode, $isRangeSelection, $isTokenOrSegmented, $getTextPointCaretSliceForNode, $caretRangeFromSelection, $cloneWithPropertiesEphemeral, $getNodeByKey, $getPreviousSelection, $createTextNode, $splitTextPointCaretSlice, getStyleObjectFromCSS as getStyleObjectFromCSS$1, $caretFromPoint, $isExtendableTextPointCaret, $findMatchingParent, INTERNAL_$isBlock, flipDirection, $getSlotHost, $isTextPointCaret, $isTabNode, $extendCaretToRange, $isChildCaret, $isDecoratorNode, $getSlotFrame, $isRootOrShadowRoot, $hasAncestor, $isLeafNode, $setSelection } from 'lexical';
10
10
  export { $cloneWithProperties, $selectAll } from 'lexical';
11
11
 
12
12
  /**
@@ -243,44 +243,21 @@ function $isParentRTL(node) {
243
243
  * @returns The updated TextNode or clone.
244
244
  */
245
245
  function $sliceSelectedTextNodeContent(selection, textNode, mutates = 'self') {
246
- const anchorAndFocus = selection.getStartEndPoints();
247
- if (textNode.isSelected(selection) && !$isTokenOrSegmented(textNode) && anchorAndFocus !== null) {
248
- const [anchor, focus] = anchorAndFocus;
249
- const isBackward = selection.isBackward();
250
- const anchorNode = anchor.getNode();
251
- const focusNode = focus.getNode();
252
- const isAnchor = textNode.is(anchorNode);
253
- const isFocus = textNode.is(focusNode);
254
- if (isAnchor || isFocus) {
255
- const [anchorOffset, focusOffset] = $getCharacterOffsets(selection);
256
- const isSame = anchorNode.is(focusNode);
257
- const isFirst = textNode.is(isBackward ? focusNode : anchorNode);
258
- const isLast = textNode.is(isBackward ? anchorNode : focusNode);
259
- let startOffset = 0;
260
- let endOffset = undefined;
261
- if (isSame) {
262
- startOffset = anchorOffset > focusOffset ? focusOffset : anchorOffset;
263
- endOffset = anchorOffset > focusOffset ? anchorOffset : focusOffset;
264
- } else if (isFirst) {
265
- const offset = isBackward ? focusOffset : anchorOffset;
266
- startOffset = offset;
267
- endOffset = undefined;
268
- } else if (isLast) {
269
- const offset = isBackward ? anchorOffset : focusOffset;
270
- startOffset = 0;
271
- endOffset = offset;
272
- }
273
-
274
- // NOTE: This mutates __text directly because the primary use case is to
275
- // modify a $cloneWithProperties node that should never be added
276
- // to the EditorState so we must not call getWritable via setTextContent
277
- const text = textNode.__text.slice(startOffset, endOffset);
278
- if (text !== textNode.__text) {
279
- if (mutates === 'clone') {
280
- textNode = $cloneWithPropertiesEphemeral(textNode);
281
- }
282
- textNode.__text = text;
246
+ const points = selection.getStartEndPoints();
247
+ if (points !== null && textNode.isSelected(selection) && !$isTokenOrSegmented(textNode)) {
248
+ const [start, end] = selection.isBackward() ? [points[1], points[0]] : points;
249
+ // Text endpoints can trim directly. Element endpoints need caret
250
+ // normalization so equivalent mixed points produce an empty interval.
251
+ const slice = $isRangeSelection(selection) && (start.type === 'element' || end.type === 'element') ? $getTextPointCaretSliceForNode($caretRangeFromSelection(selection).getTextSlices(), textNode) : undefined;
252
+ const [startOffset, endOffset] = slice ? slice.getSliceIndices() : [textNode.__key === start.key ? start.offset : 0, textNode.__key === end.key ? end.offset : undefined];
253
+ const text = textNode.__text.slice(startOffset, endOffset);
254
+ // This may be an ephemeral clone: do not resolve getLatest() or insert
255
+ // it into the editor state when changing its own text.
256
+ if (text !== textNode.__text) {
257
+ if (mutates === 'clone') {
258
+ textNode = $cloneWithPropertiesEphemeral(textNode);
283
259
  }
260
+ textNode.__text = text;
284
261
  }
285
262
  }
286
263
  return textNode;
@@ -477,7 +454,7 @@ function $patchStyleText(selection, patch) {
477
454
  $patchStyle(emptyNode, patch);
478
455
  }
479
456
  }
480
- $forEachSelectedTextNode(textNode => {
457
+ $forEachSelectedTextNodeInSelection(selection, textNode => {
481
458
  $patchStyle(textNode, patch);
482
459
  });
483
460
  const nodes = selection.getNodes();
@@ -496,44 +473,24 @@ function $patchStyleText(selection, patch) {
496
473
  }
497
474
  }
498
475
  function $forEachSelectedTextNode(fn) {
499
- const selection = $getSelection();
476
+ $forEachSelectedTextNodeInSelection($getSelection(), fn);
477
+ }
478
+ function $forEachSelectedTextNodeInSelection(selection, fn) {
500
479
  if (!selection) {
501
480
  return;
502
481
  }
503
- const slicedTextNodes = new Map();
504
- const getSliceIndices = node => slicedTextNodes.get(node.getKey()) || [0, node.getTextContentSize()];
505
- if ($isRangeSelection(selection)) {
506
- for (const slice of $caretRangeFromSelection(selection).getTextSlices()) {
507
- if (slice) {
508
- slicedTextNodes.set(slice.caret.origin.getKey(), slice.getSliceIndices());
509
- }
510
- }
511
- }
512
- const selectedNodes = selection.getNodes();
513
- for (const selectedNode of selectedNodes) {
514
- if (!($isTextNode(selectedNode) && selectedNode.canHaveFormat())) {
482
+ const slices = $isRangeSelection(selection) ? $caretRangeFromSelection(selection).getTextSlices() : [];
483
+ for (const node of selection.getNodes()) {
484
+ if (!$isTextNode(node) || !node.canHaveFormat()) {
515
485
  continue;
516
486
  }
517
- const [startOffset, endOffset] = getSliceIndices(selectedNode);
518
- // No actual text is selected, so do nothing.
519
- if (endOffset === startOffset) {
487
+ const slice = $getTextPointCaretSliceForNode(slices, node);
488
+ if (slice ? slice.distance === 0 : node.getTextContentSize() === 0) {
520
489
  continue;
521
490
  }
522
-
523
- // The entire node is selected or a token/segment, so just format it
524
- if ($isTokenOrSegmented(selectedNode) || startOffset === 0 && endOffset === selectedNode.getTextContentSize()) {
525
- fn(selectedNode);
526
- } else {
527
- // The node is partially selected, so split it into two or three nodes
528
- // and style the selected one.
529
- const splitNodes = selectedNode.splitText(startOffset, endOffset);
530
- const replacement = splitNodes[startOffset === 0 ? 0 : 1];
531
- fn(replacement);
532
- }
491
+ fn(slice && !$isTokenOrSegmented(node) ? $splitTextPointCaretSlice(slice, $isRangeSelection(selection) ? selection : null) : node);
533
492
  }
534
- // Prior to NodeCaret #7046 this would have been a side-effect
535
- // so we do this for test compatibility.
536
- // TODO: we may want to consider simplifying by removing this
493
+ // Preserve the existing single-text-node forward selection convention.
537
494
  if ($isRangeSelection(selection) && selection.anchor.type === 'text' && selection.focus.type === 'text' && selection.anchor.key === selection.focus.key) {
538
495
  $ensureForwardRangeSelection(selection);
539
496
  }
@@ -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 r,$isRangeSelection as l,$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,$getSlotHost as C,$isTextPointCaret as N,$isTabNode as T,$extendCaretToRange as v,$isChildCaret as w,$isDecoratorNode as P,$getSlotFrame as K,$isRootOrShadowRoot as E,$hasAncestor as I,$isLeafNode as b,$setSelection as B}from"lexical";export{$cloneWithProperties,$selectAll}from"lexical";function k(e,...t){throw function(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);return n.search=o.toString(),new 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.`)}(e,...t)}
10
- /*@__INLINE__*/function F(e){return()=>{}}function z(e){let t=e;for(;null!=t;){if(t.nodeType===Node.TEXT_NODE)return t;t=t.firstChild}return null}function A(e){const t=e.parentNode;if(null==t)throw new Error("Should never happen");return[t,Array.from(t.childNodes).indexOf(e)]}function R(n,o,r,l,i){const s=o.getKey(),c=l.getKey(),f=e(n.getRootElement()).createRange();let u=n.getElementByKey(s),g=n.getElementByKey(c),a=r,d=i;if(t(o)&&(u=z(u)),t(l)&&(g=z(g)),void 0===o||void 0===l||null===u||null===g)return null;"BR"===u.nodeName&&([u,a]=A(u)),"BR"===g.nodeName&&([g,d]=A(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 O(e,t){const n=e.getRootElement();if(null===n)return[];const o=n.getBoundingClientRect(),r=getComputedStyle(n),l=parseFloat(r.paddingLeft)+parseFloat(r.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.bottom>=t.bottom&&s.left<=t.left&&s.right>=t.right,r=t.width+l===o.width;n||r?(i.splice(e--,1),c--):s=t}return i}function _(e){let t="";for(const n in e)n&&(t+=`${n}: ${e[n]};`);return t}function L(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 M(e){const t=o(e)?e:e.getParent();return t&&L(t)}function $(e){const t=M(e);return null!==t&&"rtl"===t.direction}function D(e,t,n="self"){const o=e.getStartEndPoints();if(t.isSelected(e)&&!s(t)&&null!==o){const[r,l]=o,i=e.isBackward(),s=r.getNode(),c=l.getNode(),g=t.is(s),a=t.is(c);if(g||a){const[o,r]=f(e),l=s.is(c),g=t.is(i?c:s),a=t.is(i?s:c);let d,p=0;l?(p=o>r?r:o,d=o>r?o:r):g?(p=i?r:o,d=void 0):a&&(p=0,d=i?o:r);const h=t.__text.slice(p,d);h!==t.__text&&("clone"===n&&(t=u(t)),t.__text=h)}}return t}function U(e){if("text"===e.type)return e.offset===e.getNode().getTextContentSize();const t=e.getNode();return c(t)||k(177),e.offset===t.getChildrenSize()}function j(e,n,r){let i=n.getNode(),s=r;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 r=i.getPreviousSibling(),f=0;if(null===r){let e=i.getParentOrThrow(),t=e.getPreviousSibling();for(;null===t;){if(e=e.getParent(),null===e){r=null;break}t=e.getPreviousSibling()}null!==e&&(f=e.isInline()?0:2,r=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=r}else{const o=i.getKey(),r=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!==r&&r!==u){const e=a();let t=i;if(i.isSimpleText())i.setTextContent(r);else{const e=d(r);i.replace(e),t=e}if(l(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 r=e?t-s:0,l=e?t:c;if(e&&0===r){const[e]=i.splitText(r,l);e.remove()}else{const[,e]=i.splitText(r,l);e.remove()}}else{const e=d(f);i.replace(e)}s=0}}}const H=/* @__PURE__ */F();function V(e,n){(l(e)?e.isCollapsed():t(e)||c(e))||k(280);const o=p(l(e)?e.style:t(e)?e.getStyle():e.getTextStyle()),r=_(Object.entries(n).reduce((t,[n,r])=>("function"==typeof r?t[n]=r(o[n],e):null===r?delete t[n]:t[n]=r,t),{...o}));l(e)||t(e)?e.setStyle(r):e.setTextStyle(r)}function W(e,t){const n=new Set;if(l(e)&&e.isCollapsed()){V(e,t);const o=e.anchor.getNode();c(o)&&o.isEmpty()&&(n.add(o.getKey()),V(o,t))}X(e=>{V(e,t)});const o=e.getNodes();if(o.length>0)for(const e of o){if(!c(e)||!e.canBeEmpty()||0!==e.getChildrenSize())continue;const o=e.getKey();n.has(o)||(n.add(o),V(e,t))}}function X(e){const n=r();if(!n)return;const o=new Map,c=e=>o.get(e.getKey())||[0,e.getTextContentSize()];if(l(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,r]=c(n);r!==o&&(s(n)||0===o&&r===n.getTextContentSize()?e(n):e(n.splitText(o,r)[0===o?0:1]))}l(n)&&"text"===n.anchor.type&&"text"===n.focus.type&&n.anchor.key===n.focus.key&&q(n)}function q(e){if(e.isBackward()){const{anchor:t,focus:n}=e,{key:o,offset:r,type:l}=t;t.set(n.key,n.offset,n.type),n.set(o,r,l)}}function G(e,t){const n=e.getFormatType(),o=e.getIndent();n!==t.getFormatType()&&t.setFormat(n),o!==t.getIndent()&&t.setIndent(o)}function J(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 Q(e,t,n=G){if(!e)return;const o=e.getStartEndPoints();let r=!1,l=null;const i=new Map;if(o){const[t,n]=o,s=m(t.getNode(),S);l=m(n.getNode(),S);const f=e.isBackward()?"previous":"next";r=c(l)&&!l.is(s)&&function(e,t,n){const o=e.getNode();return(!c(o)||!o.isEmpty())&&J(e,t,n)}(n,l,x(f)),c(s)&&i.set(s.getKey(),s),c(l)&&!r&&i.set(l.getKey(),l)}for(const t of e.getNodes())if(c(t)&&S(t)){if(r&&t.is(l))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()){if(null!==C(e))continue;const o=t();n(e,o),e.replace(o,!0)}}function Y(e){return e.getNode().isAttached()}function Z(e){let t=e;for(;null!==t&&!E(t);){const e=t.getLatest(),n=t.getParent();0===e.getChildrenSize()&&t.remove(!0),t=n}}function ee(e,t,n=null){const o=e.getStartEndPoints(),r=o?o[0]:null,l=e.getNodes(),i=l.length;if(null!==r){const e=K(r.getNode());if(null!==e&&!E(e))return}if(null!==r&&(0===i||1===i&&"element"===r.type&&0===r.getNode().getChildrenSize())){const e="text"===r.type?r.getNode().getParentOrThrow():r.getNode(),o=e.getChildren();let l=t();return l.setFormat(e.getFormatType()),l.setIndent(e.getIndent()),o.forEach(e=>l.append(e)),n&&(l=n.append(l)),void e.replace(l)}let s=null,c=[];for(let o=0;o<i;o++){const r=l[o];E(r)?(te(e,c,c.length,t,n),c=[],s=r):null===s||null!==s&&I(r,s)?c.push(r):(te(e,c,c.length,t,n),c=[r])}te(e,c,c.length,t,n)}function te(e,t,n,o,r=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(),E(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 r=n.getParent();if(null!==r&&r.isInline()&&(r=r.getParent()),null!==r&&b(n)&&!h.has(n.getKey())){const e=r.getKey();if(void 0===s.get(e)){const t=o();t.setFormat(r.getFormatType()),t.setIndent(r.getIndent()),f.push(t),s.set(e,t);const n=r.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);Z(r)}}else if(p.has(n.getKey())){c(n)||k(179);const e=o();e.setFormat(n.getFormatType()),e.setIndent(n.getIndent()),f.push(e),n.remove(!0)}}if(null!==r)for(let e=0;e<f.length;e++){const t=f[e];r.append(t)}let y=null;if(E(g))if(d)if(null!==r)g.insertAfter(r);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(r)e.append(r);else for(let t=0;t<f.length;t++){const n=f[t];e.append(n),y=n}else if(null!==r)t.insertBefore(r);else for(let e=0;e<f.length;e++){const n=f[e];t.insertBefore(n),y=n}}else if(r)g.insertAfter(r);else for(let e=f.length-1;e>=0;e--){const t=f[e];g.insertAfter(t),y=t}const m=a();l(m)&&Y(m.anchor)&&Y(m.focus)?B(m.clone()):null!==y?y.selectEnd():e.dirty=!0}function ne(e){const t=oe(e);return null!==t&&"vertical-rl"===t.writingMode}function oe(e){const t=e.anchor.getNode();return c(t)?L(t):M(t)}function re(e,n){let o=ne(e)?!n:n;ie(e)&&(o=!o);const r=h(e.focus,o?"previous":"next");if(y(r))return!1;if(N(r)&&!T(r.origin)&&r.origin.isUnmergeable()){const e=r.getNodeAtCaret();if(t(e)&&!T(e))return!0}for(const e of v(r)){if(w(e))return!e.origin.isInline();if(!c(e.origin)){if(P(e.origin))return!0;break}}return!1}function le(e,t,n,o){e.modify(t?"extend":"move",n,o)}function ie(e){const t=oe(e);return null!==t&&"rtl"===t.direction}function se(e,t,n){const o=ie(e);let r;r=ne(e)||o?!n:n,le(e,t,r,"character")}function ce(e,t,n){const o=e.getStyle(),r=p(o);return null!==r&&r[t]||n}function fe(e,n,o=""){let r=null;const i=e.getNodes();let s,c;if(l(e)){if(e.isCollapsed()&&""!==e.style){const t=p(e.style);if(null!==t&&n in t)return t[n]}const{anchor:o,focus:r}=e,l=e.isBackward(),i=l?r.getNode():o.getNode(),f=l?o.getNode():r.getNode(),u=l?r.offset:o.offset,g=l?o.offset:r.offset;t(i)&&u===i.getTextContentSize()&&(s=i),0===g&&(c=f)}for(let e=0;e<i.length;e++){const l=i[e];if(t(l)&&!l.is(0===e?s:c)){const e=ce(l,n,o);if(null===r)r=e;else if(r!==e){r="";break}}}return null===r?o:r}const ue=p,ge=j;export{H as $addNodeStyle,G as $copyBlockFormatIndent,q as $ensureForwardRangeSelection,X as $forEachSelectedTextNode,L as $getComputedStyleForElement,M as $getComputedStyleForParent,fe as $getSelectionStyleValueForProperty,J as $isAtEdgeOfElement,U as $isAtNodeEnd,ie as $isParentElementRTL,$ as $isParentRTL,le as $moveCaretSelection,se as $moveCharacter,W as $patchStyleText,Q as $setBlocksType,re as $shouldOverrideDefaultCharacterSelection,D as $sliceSelectedTextNodeContent,j as $trimTextContentFromAnchor,ee as $wrapNodes,R as createDOMRange,O as createRectsFromDOMRange,_ as getCSSFromStyleObject,ue as getStyleObjectFromCSS,ge as trimTextContentFromAnchor};
9
+ import{getRootOwnerDocument as e,$isTextNode as t,$getEditor as n,$isRootNode as o,$getSelection as l,$isElementNode as r,$isRangeSelection as i,$isTokenOrSegmented as s,$getTextPointCaretSliceForNode as c,$caretRangeFromSelection as f,$cloneWithPropertiesEphemeral as u,$getNodeByKey as g,$getPreviousSelection as a,$createTextNode as d,$splitTextPointCaretSlice as p,getStyleObjectFromCSS as h,$caretFromPoint as y,$isExtendableTextPointCaret as m,$findMatchingParent as S,INTERNAL_$isBlock as x,flipDirection as C,$getSlotHost as N,$isTextPointCaret as v,$isTabNode as T,$extendCaretToRange as w,$isChildCaret as P,$isDecoratorNode as E,$getSlotFrame as K,$isRootOrShadowRoot as k,$hasAncestor as I,$isLeafNode as b,$setSelection as B}from"lexical";export{$cloneWithProperties,$selectAll}from"lexical";function F(e,...t){throw function(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);return n.search=o.toString(),new 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.`)}(e,...t)}
10
+ /*@__INLINE__*/function A(e){return()=>{}}function R(e){let t=e;for(;null!=t;){if(t.nodeType===Node.TEXT_NODE)return t;t=t.firstChild}return null}function _(e){const t=e.parentNode;if(null==t)throw new Error("Should never happen");return[t,Array.from(t.childNodes).indexOf(e)]}function z(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=R(u)),t(r)&&(g=R(g)),void 0===o||void 0===r||null===u||null===g)return null;"BR"===u.nodeName&&([u,a]=_(u)),"BR"===g.nodeName&&([g,d]=_(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 O(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.bottom>=t.bottom&&s.left<=t.left&&s.right>=t.right,l=t.width+r===o.width;n||l?(i.splice(e--,1),c--):s=t}return i}function L(e){let t="";for(const n in e)n&&(t+=`${n}: ${e[n]};`);return t}function $(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 M(e){const t=o(e)?e:e.getParent();return t&&$(t)}function D(e){const t=M(e);return null!==t&&"rtl"===t.direction}function U(e,t,n="self"){const o=e.getStartEndPoints();if(null!==o&&t.isSelected(e)&&!s(t)){const[l,r]=e.isBackward()?[o[1],o[0]]:o,s=!i(e)||"element"!==l.type&&"element"!==r.type?void 0:c(f(e).getTextSlices(),t),[g,a]=s?s.getSliceIndices():[t.__key===l.key?l.offset:0,t.__key===r.key?r.offset:void 0],d=t.__text.slice(g,a);d!==t.__text&&("clone"===n&&(t=u(t)),t.__text=d)}return t}function j(e){if("text"===e.type)return e.offset===e.getNode().getTextContentSize();const t=e.getNode();return r(t)||F(177),e.offset===t.getChildrenSize()}function H(e,n,l){let s=n.getNode(),c=l;if(r(s)){const e=s.getDescendantByIndex(n.offset);null!==e&&(s=e)}for(;c>0&&null!==s;){if(r(s)){const e=s.getLastDescendant();null!==e&&(s=e)}let l=s.getPreviousSibling(),f=0;if(null===l){let e=s.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=s.getTextContent();""===u&&r(s)&&!s.isInline()&&(u="\n\n");const p=u.length;if(!t(s)||c>=p){const e=s.getParent();s.remove(),null==e||0!==e.getChildrenSize()||o(e)||e.remove(),c-=p+f,s=l}else{const o=s.getKey(),l=e.read("latest",()=>{const e=g(o);return t(e)&&e.isSimpleText()?e.getTextContent():null}),r=p-c,f=u.slice(0,r);if(null!==l&&l!==u){const e=a();let t=s;if(s.isSimpleText())s.setTextContent(l);else{const e=d(l);s.replace(e),t=e}if(i(e)&&e.isCollapsed()){const n=e.anchor.offset;t.select(n,n)}}else if(s.isSimpleText()){const e=n.key===o;let t=n.offset;t<c&&(t=p);const l=e?t-c:0,i=e?t:r;if(e&&0===l){const[e]=s.splitText(l,i);e.remove()}else{const[,e]=s.splitText(l,i);e.remove()}}else{const e=d(f);s.replace(e)}c=0}}}const V=/* @__PURE__ */A();function W(e,n){(i(e)?e.isCollapsed():t(e)||r(e))||F(280);const o=h(i(e)?e.style:t(e)?e.getStyle():e.getTextStyle()),l=L(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}));i(e)||t(e)?e.setStyle(l):e.setTextStyle(l)}function X(e,t){const n=new Set;if(i(e)&&e.isCollapsed()){W(e,t);const o=e.anchor.getNode();r(o)&&o.isEmpty()&&(n.add(o.getKey()),W(o,t))}G(e,e=>{W(e,t)});const o=e.getNodes();if(o.length>0)for(const e of o){if(!r(e)||!e.canBeEmpty()||0!==e.getChildrenSize())continue;const o=e.getKey();n.has(o)||(n.add(o),W(e,t))}}function q(e){G(l(),e)}function G(e,n){if(!e)return;const o=i(e)?f(e).getTextSlices():[];for(const l of e.getNodes()){if(!t(l)||!l.canHaveFormat())continue;const r=c(o,l);(r?0!==r.distance:0!==l.getTextContentSize())&&n(r&&!s(l)?p(r,i(e)?e:null):l)}i(e)&&"text"===e.anchor.type&&"text"===e.focus.type&&e.anchor.key===e.focus.key&&J(e)}function J(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 Q(e,t){const n=e.getFormatType(),o=e.getIndent();n!==t.getFormatType()&&t.setFormat(n),o!==t.getIndent()&&t.setIndent(o)}function Y(e,t,n){let o=y(e,n);if(m(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 Z(e,t,n=Q){if(!e)return;const o=e.getStartEndPoints();let l=!1,i=null;const s=new Map;if(o){const[t,n]=o,c=S(t.getNode(),x);i=S(n.getNode(),x);const f=e.isBackward()?"previous":"next";l=r(i)&&!i.is(c)&&function(e,t,n){const o=e.getNode();return(!r(o)||!o.isEmpty())&&Y(e,t,n)}(n,i,C(f)),r(c)&&s.set(c.getKey(),c),r(i)&&!l&&s.set(i.getKey(),i)}for(const t of e.getNodes())if(r(t)&&x(t)){if(l&&t.is(i))continue;s.set(t.getKey(),t)}else if(!o){const e=S(t,x);r(e)&&s.set(e.getKey(),e)}for(const e of s.values()){if(null!==N(e))continue;const o=t();n(e,o),e.replace(o,!0)}}function ee(e){return e.getNode().isAttached()}function te(e){let t=e;for(;null!==t&&!k(t);){const e=t.getLatest(),n=t.getParent();0===e.getChildrenSize()&&t.remove(!0),t=n}}function ne(e,t,n=null){const o=e.getStartEndPoints(),l=o?o[0]:null,r=e.getNodes(),i=r.length;if(null!==l){const e=K(l.getNode());if(null!==e&&!k(e))return}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];k(l)?(oe(e,c,c.length,t,n),c=[],s=l):null===s||null!==s&&I(l,s)?c.push(l):(oe(e,c,c.length,t,n),c=[l])}oe(e,c,c.length,t,n)}function oe(e,t,n,o,l=null){if(0===t.length)return;const s=t[0],c=new Map,f=[],u=r(s)?s:s.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(),k(g))break}const p=new Set;for(let e=0;e<n;e++){const n=t[e];r(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&&b(n)&&!h.has(n.getKey())){const e=l.getKey();if(void 0===c.get(e)){const t=o();t.setFormat(l.getFormatType()),t.setIndent(l.getIndent()),f.push(t),c.set(e,t);const n=l.getChildren();t.splice(t.getChildrenSize(),0,n);for(const e of n)if(h.add(e.getKey()),r(e))for(const t of e.getChildrenKeys())h.add(t);te(l)}}else if(p.has(n.getKey())){r(n)||F(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(k(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(r(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();i(m)&&ee(m.anchor)&&ee(m.focus)?B(m.clone()):null!==y?y.selectEnd():e.dirty=!0}function le(e){const t=re(e);return null!==t&&"vertical-rl"===t.writingMode}function re(e){const t=e.anchor.getNode();return r(t)?$(t):M(t)}function ie(e,n){let o=le(e)?!n:n;ce(e)&&(o=!o);const l=y(e.focus,o?"previous":"next");if(m(l))return!1;if(v(l)&&!T(l.origin)&&l.origin.isUnmergeable()){const e=l.getNodeAtCaret();if(t(e)&&!T(e))return!0}for(const e of w(l)){if(P(e))return!e.origin.isInline();if(!r(e.origin)){if(E(e.origin))return!0;break}}return!1}function se(e,t,n,o){e.modify(t?"extend":"move",n,o)}function ce(e){const t=re(e);return null!==t&&"rtl"===t.direction}function fe(e,t,n){const o=ce(e);let l;l=le(e)||o?!n:n,se(e,t,l,"character")}function ue(e,t,n){const o=e.getStyle(),l=h(o);return null!==l&&l[t]||n}function ge(e,n,o=""){let l=null;const r=e.getNodes();let s,c;if(i(e)){if(e.isCollapsed()&&""!==e.style){const t=h(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<r.length;e++){const i=r[e];if(t(i)&&!i.is(0===e?s:c)){const e=ue(i,n,o);if(null===l)l=e;else if(l!==e){l="";break}}}return null===l?o:l}const ae=h,de=H;export{V as $addNodeStyle,Q as $copyBlockFormatIndent,J as $ensureForwardRangeSelection,q as $forEachSelectedTextNode,$ as $getComputedStyleForElement,M as $getComputedStyleForParent,ge as $getSelectionStyleValueForProperty,Y as $isAtEdgeOfElement,j as $isAtNodeEnd,ce as $isParentElementRTL,D as $isParentRTL,se as $moveCaretSelection,fe as $moveCharacter,X as $patchStyleText,Z as $setBlocksType,ie as $shouldOverrideDefaultCharacterSelection,U as $sliceSelectedTextNodeContent,H as $trimTextContentFromAnchor,ne as $wrapNodes,z as createDOMRange,O as createRectsFromDOMRange,L as getCSSFromStyleObject,ae as getStyleObjectFromCSS,de as trimTextContentFromAnchor};
package/package.json CHANGED
@@ -9,7 +9,7 @@
9
9
  "selection"
10
10
  ],
11
11
  "license": "MIT",
12
- "version": "0.52.0",
12
+ "version": "0.52.1-nightly.20260930.0",
13
13
  "main": "./dist/LexicalSelection.js",
14
14
  "types": "./dist/typescript-too-old.d.ts",
15
15
  "repository": {
@@ -30,8 +30,8 @@
30
30
  }
31
31
  },
32
32
  "dependencies": {
33
- "@lexical/internal": "0.52.0",
34
- "lexical": "0.52.0"
33
+ "@lexical/internal": "0.52.1-nightly.20260930.0",
34
+ "lexical": "0.52.1-nightly.20260930.0"
35
35
  },
36
36
  "files": [
37
37
  "dist",
@@ -11,15 +11,16 @@ import {
11
11
  $caretRangeFromSelection,
12
12
  $cloneWithPropertiesEphemeral,
13
13
  $createTextNode,
14
- $getCharacterOffsets,
15
14
  $getNodeByKey,
16
15
  $getPreviousSelection,
17
16
  $getSelection,
17
+ $getTextPointCaretSliceForNode,
18
18
  $isElementNode,
19
19
  $isRangeSelection,
20
20
  $isRootNode,
21
21
  $isTextNode,
22
22
  $isTokenOrSegmented,
23
+ $splitTextPointCaretSlice,
23
24
  type BaseSelection,
24
25
  type ElementNode,
25
26
  getStyleObjectFromCSS,
@@ -46,50 +47,39 @@ export function $sliceSelectedTextNodeContent<T extends TextNode>(
46
47
  textNode: T,
47
48
  mutates: 'clone' | 'self' = 'self',
48
49
  ): T {
49
- const anchorAndFocus = selection.getStartEndPoints();
50
+ const points = selection.getStartEndPoints();
50
51
  if (
52
+ points !== null &&
51
53
  textNode.isSelected(selection) &&
52
- !$isTokenOrSegmented(textNode) &&
53
- anchorAndFocus !== null
54
+ !$isTokenOrSegmented(textNode)
54
55
  ) {
55
- const [anchor, focus] = anchorAndFocus;
56
- const isBackward = selection.isBackward();
57
- const anchorNode = anchor.getNode();
58
- const focusNode = focus.getNode();
59
- const isAnchor = textNode.is(anchorNode);
60
- const isFocus = textNode.is(focusNode);
61
-
62
- if (isAnchor || isFocus) {
63
- const [anchorOffset, focusOffset] = $getCharacterOffsets(selection);
64
- const isSame = anchorNode.is(focusNode);
65
- const isFirst = textNode.is(isBackward ? focusNode : anchorNode);
66
- const isLast = textNode.is(isBackward ? anchorNode : focusNode);
67
- let startOffset = 0;
68
- let endOffset = undefined;
69
-
70
- if (isSame) {
71
- startOffset = anchorOffset > focusOffset ? focusOffset : anchorOffset;
72
- endOffset = anchorOffset > focusOffset ? anchorOffset : focusOffset;
73
- } else if (isFirst) {
74
- const offset = isBackward ? focusOffset : anchorOffset;
75
- startOffset = offset;
76
- endOffset = undefined;
77
- } else if (isLast) {
78
- const offset = isBackward ? anchorOffset : focusOffset;
79
- startOffset = 0;
80
- endOffset = offset;
81
- }
82
-
83
- // NOTE: This mutates __text directly because the primary use case is to
84
- // modify a $cloneWithProperties node that should never be added
85
- // to the EditorState so we must not call getWritable via setTextContent
86
- const text = textNode.__text.slice(startOffset, endOffset);
87
- if (text !== textNode.__text) {
88
- if (mutates === 'clone') {
89
- textNode = $cloneWithPropertiesEphemeral(textNode);
90
- }
91
- textNode.__text = text;
56
+ const [start, end] = selection.isBackward()
57
+ ? [points[1], points[0]]
58
+ : points;
59
+ // Text endpoints can trim directly. Element endpoints need caret
60
+ // normalization so equivalent mixed points produce an empty interval.
61
+ const slice =
62
+ $isRangeSelection(selection) &&
63
+ (start.type === 'element' || end.type === 'element')
64
+ ? $getTextPointCaretSliceForNode(
65
+ $caretRangeFromSelection(selection).getTextSlices(),
66
+ textNode,
67
+ )
68
+ : undefined;
69
+ const [startOffset, endOffset] = slice
70
+ ? slice.getSliceIndices()
71
+ : [
72
+ textNode.__key === start.key ? start.offset : 0,
73
+ textNode.__key === end.key ? end.offset : undefined,
74
+ ];
75
+ const text = textNode.__text.slice(startOffset, endOffset);
76
+ // This may be an ephemeral clone: do not resolve getLatest() or insert
77
+ // it into the editor state when changing its own text.
78
+ if (text !== textNode.__text) {
79
+ if (mutates === 'clone') {
80
+ textNode = $cloneWithPropertiesEphemeral(textNode);
92
81
  }
82
+ textNode.__text = text;
93
83
  }
94
84
  }
95
85
  return textNode;
@@ -332,7 +322,7 @@ export function $patchStyleText(
332
322
  $patchStyle(emptyNode, patch);
333
323
  }
334
324
  }
335
- $forEachSelectedTextNode(textNode => {
325
+ $forEachSelectedTextNodeInSelection(selection, textNode => {
336
326
  $patchStyle(textNode, patch);
337
327
  });
338
328
 
@@ -359,59 +349,37 @@ export function $patchStyleText(
359
349
  export function $forEachSelectedTextNode(
360
350
  fn: (textNode: TextNode) => void,
361
351
  ): void {
362
- const selection = $getSelection();
352
+ $forEachSelectedTextNodeInSelection($getSelection(), fn);
353
+ }
354
+
355
+ function $forEachSelectedTextNodeInSelection(
356
+ selection: BaseSelection | null,
357
+ fn: (textNode: TextNode) => void,
358
+ ): void {
363
359
  if (!selection) {
364
360
  return;
365
361
  }
366
-
367
- const slicedTextNodes = new Map<
368
- NodeKey,
369
- [startIndex: number, endIndex: number]
370
- >();
371
- const getSliceIndices = (
372
- node: TextNode,
373
- ): [startIndex: number, endIndex: number] =>
374
- slicedTextNodes.get(node.getKey()) || [0, node.getTextContentSize()];
375
-
376
- if ($isRangeSelection(selection)) {
377
- for (const slice of $caretRangeFromSelection(selection).getTextSlices()) {
378
- if (slice) {
379
- slicedTextNodes.set(
380
- slice.caret.origin.getKey(),
381
- slice.getSliceIndices(),
382
- );
383
- }
384
- }
385
- }
386
-
387
- const selectedNodes = selection.getNodes();
388
- for (const selectedNode of selectedNodes) {
389
- if (!($isTextNode(selectedNode) && selectedNode.canHaveFormat())) {
362
+ const slices = $isRangeSelection(selection)
363
+ ? $caretRangeFromSelection(selection).getTextSlices()
364
+ : [];
365
+ for (const node of selection.getNodes()) {
366
+ if (!$isTextNode(node) || !node.canHaveFormat()) {
390
367
  continue;
391
368
  }
392
- const [startOffset, endOffset] = getSliceIndices(selectedNode);
393
- // No actual text is selected, so do nothing.
394
- if (endOffset === startOffset) {
369
+ const slice = $getTextPointCaretSliceForNode(slices, node);
370
+ if (slice ? slice.distance === 0 : node.getTextContentSize() === 0) {
395
371
  continue;
396
372
  }
397
-
398
- // The entire node is selected or a token/segment, so just format it
399
- if (
400
- $isTokenOrSegmented(selectedNode) ||
401
- (startOffset === 0 && endOffset === selectedNode.getTextContentSize())
402
- ) {
403
- fn(selectedNode);
404
- } else {
405
- // The node is partially selected, so split it into two or three nodes
406
- // and style the selected one.
407
- const splitNodes = selectedNode.splitText(startOffset, endOffset);
408
- const replacement = splitNodes[startOffset === 0 ? 0 : 1];
409
- fn(replacement);
410
- }
373
+ fn(
374
+ slice && !$isTokenOrSegmented(node)
375
+ ? $splitTextPointCaretSlice(
376
+ slice,
377
+ $isRangeSelection(selection) ? selection : null,
378
+ )!
379
+ : node,
380
+ );
411
381
  }
412
- // Prior to NodeCaret #7046 this would have been a side-effect
413
- // so we do this for test compatibility.
414
- // TODO: we may want to consider simplifying by removing this
382
+ // Preserve the existing single-text-node forward selection convention.
415
383
  if (
416
384
  $isRangeSelection(selection) &&
417
385
  selection.anchor.type === 'text' &&