roosterjs 9.29.3 → 9.30.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.
@@ -1,4 +1,4 @@
1
- // Type definitions for roosterjs (Version 9.29.3)
1
+ // Type definitions for roosterjs (Version 9.30.0)
2
2
  // Generated by dts tool from roosterjs
3
3
  // Project: https://github.com/Microsoft/roosterjs
4
4
 
@@ -3616,7 +3616,14 @@ export type ExperimentalFeature = /**
3616
3616
  * Prevent default browser behavior for copy/cut event,
3617
3617
  * and set the clipboard data with custom implementation.
3618
3618
  */
3619
- | 'CustomCopyCut';
3619
+ | 'CustomCopyCut'
3620
+ /**
3621
+ * For CJK keyboard input on mobile, if the user toggles bold/italic/underline on an empty div,
3622
+ * the pending format will be applied on the selection marker. When typing text, the selection moves to the text node and the
3623
+ * selection marker may be recreated during reconciliation, potentially losing its original formatting. This feature ensures
3624
+ * the original formatting of the selection marker is kept to match the pending format.
3625
+ */
3626
+ | 'KeepSelectionMarkerWhenEnteringTextNode';
3620
3627
 
3621
3628
  /**
3622
3629
  * Options for editor
@@ -6189,6 +6196,16 @@ export interface KeyUpEvent extends BasePluginDomEvent<'keyUp', KeyboardEvent> {
6189
6196
  export interface CompositionEndEvent extends BasePluginDomEvent<'compositionEnd', CompositionEvent> {
6190
6197
  }
6191
6198
 
6199
+ /**
6200
+ * Fired when the logical root is about to be changed
6201
+ */
6202
+ export interface BeforeLogicalRootChangeEvent extends BasePluginEvent<'beforeLogicalRootChange'> {
6203
+ /**
6204
+ * The logical root element that will no longer be the logical root
6205
+ */
6206
+ logicalRoot: HTMLDivElement;
6207
+ }
6208
+
6192
6209
  /**
6193
6210
  * Fired when the logical root changes
6194
6211
  */
@@ -6218,7 +6235,7 @@ export interface MouseUpEvent extends BasePluginDomEvent<'mouseUp', MouseEvent>
6218
6235
  /**
6219
6236
  * Editor plugin event interface
6220
6237
  */
6221
- export type PluginEvent = BeforeCutCopyEvent | BeforeDisposeEvent | BeforeKeyboardEditingEvent | BeforePasteEvent | BeforeSetContentEvent | CompositionEndEvent | ContentChangedEvent | ContextMenuEvent | RewriteFromModelEvent | EditImageEvent | EditorReadyEvent | EnterShadowEditEvent | EntityOperationEvent | ExtractContentWithDomEvent | EditorInputEvent | KeyDownEvent | KeyPressEvent | KeyUpEvent | LeaveShadowEditEvent | LogicalRootChangedEvent | MouseDownEvent | MouseUpEvent | ScrollEvent | SelectionChangedEvent | ZoomChangedEvent;
6238
+ export type PluginEvent = BeforeCutCopyEvent | BeforeDisposeEvent | BeforeKeyboardEditingEvent | BeforeLogicalRootChangeEvent | BeforePasteEvent | BeforeSetContentEvent | CompositionEndEvent | ContentChangedEvent | ContextMenuEvent | RewriteFromModelEvent | EditImageEvent | EditorReadyEvent | EnterShadowEditEvent | EntityOperationEvent | ExtractContentWithDomEvent | EditorInputEvent | KeyDownEvent | KeyPressEvent | KeyUpEvent | LeaveShadowEditEvent | LogicalRootChangedEvent | MouseDownEvent | MouseUpEvent | ScrollEvent | SelectionChangedEvent | ZoomChangedEvent;
6222
6239
 
6223
6240
  /**
6224
6241
  * A type to extract data part of a plugin event type. Data part is the plugin event without eventType field.
@@ -6352,7 +6369,13 @@ export type PluginEventType = /**
6352
6369
  * Editor content is about to be changed by keyboard event.
6353
6370
  * This is only used by Content Model editing
6354
6371
  */
6355
- | 'beforeKeyboardEditing';
6372
+ | 'beforeKeyboardEditing'
6373
+ /**
6374
+ * The logical root is about to change
6375
+ * This event is used to clean up any features from the old logical root
6376
+ * before the new logical root is set.
6377
+ */
6378
+ | 'beforeLogicalRootChange';
6356
6379
 
6357
6380
  /**
6358
6381
  * This interface represents a PluginEvent wrapping native scroll event
@@ -9775,6 +9798,7 @@ export class ImageEditPlugin implements ImageEditor, EditorPlugin {
9775
9798
  * @param event The event to handle:
9776
9799
  */
9777
9800
  onPluginEvent(event: PluginEvent): void;
9801
+ private handleBeforeLogicalRootChange;
9778
9802
  private removeImageEditing;
9779
9803
  private isImageSelection;
9780
9804
  private mouseUpHandler;
@@ -7383,6 +7383,7 @@ var roosterjs_content_model_dom_1 = __webpack_require__(/*! roosterjs-content-mo
7383
7383
  * @param selectionOverride Override the current selection. If we want to format a table even currently it is not selected, we can use this parameter to override current selection
7384
7384
  */
7385
7385
  function formatTableWithContentModel(editor, apiName, callback, selectionOverride) {
7386
+ editor.focus();
7386
7387
  editor.formatContentModel(function (model) {
7387
7388
  var _a = (0, tslib_1.__read)((0, roosterjs_content_model_dom_1.getFirstSelectedTable)(model), 2), readonlyTableModel = _a[0], path = _a[1];
7388
7389
  if (readonlyTableModel) {
@@ -10241,6 +10242,12 @@ var setLogicalRoot = function (core, logicalRoot) {
10241
10242
  }
10242
10243
  // if the logical root changed
10243
10244
  if (logicalRoot !== core.logicalRoot) {
10245
+ // tell plugins that the logical root is about to change, so they can clean up listeners or caches
10246
+ var beforeLogicalRootEvent = {
10247
+ eventType: 'beforeLogicalRootChange',
10248
+ logicalRoot: core.logicalRoot,
10249
+ };
10250
+ core.api.triggerEvent(core, beforeLogicalRootEvent, false /*broadcast*/);
10244
10251
  // make sure the old logical root is not content editable and the new one is
10245
10252
  core.logicalRoot.contentEditable = 'false';
10246
10253
  logicalRoot.contentEditable = 'true';
@@ -10432,7 +10439,8 @@ var CachePlugin = /** @class */ (function () {
10432
10439
  this.state = {};
10433
10440
  if (!option.disableCache) {
10434
10441
  this.state.domIndexer = new domIndexerImpl_1.DomIndexerImpl(option.experimentalFeatures &&
10435
- option.experimentalFeatures.indexOf('PersistCache') >= 0);
10442
+ option.experimentalFeatures.indexOf('PersistCache') >= 0, option.experimentalFeatures &&
10443
+ option.experimentalFeatures.indexOf('KeepSelectionMarkerWhenEnteringTextNode') >= 0);
10436
10444
  this.state.textMutationObserver = (0, textMutationObserver_1.createTextMutationObserver)(contentDiv, this.onMutation);
10437
10445
  }
10438
10446
  if (option.enableParagraphMap) {
@@ -10778,8 +10786,9 @@ function unindex(node) {
10778
10786
  * Implementation of DomIndexer
10779
10787
  */
10780
10788
  var DomIndexerImpl = /** @class */ (function () {
10781
- function DomIndexerImpl(persistCache) {
10789
+ function DomIndexerImpl(persistCache, keepSelectionMarkerWhenEnteringTextNode) {
10782
10790
  this.persistCache = persistCache;
10791
+ this.keepSelectionMarkerWhenEnteringTextNode = keepSelectionMarkerWhenEnteringTextNode;
10783
10792
  }
10784
10793
  DomIndexerImpl.prototype.onSegment = function (segmentNode, paragraph, segment) {
10785
10794
  var indexedText = segmentNode;
@@ -10838,6 +10847,7 @@ var DomIndexerImpl = /** @class */ (function () {
10838
10847
  };
10839
10848
  DomIndexerImpl.prototype.reconcileSelection = function (model, newSelection, oldSelection) {
10840
10849
  var _a, _b;
10850
+ var selectionMarker;
10841
10851
  if (oldSelection) {
10842
10852
  var startNode = void 0;
10843
10853
  if (oldSelection.type == 'range' &&
@@ -10849,6 +10859,7 @@ var DomIndexerImpl = /** @class */ (function () {
10849
10859
  this.reconcileTextSelection(startNode);
10850
10860
  }
10851
10861
  else {
10862
+ selectionMarker = this.selectionMarkerToKeepWhenEnteringTextNode(oldSelection, newSelection);
10852
10863
  (0, roosterjs_content_model_dom_1.setSelection)(model);
10853
10864
  }
10854
10865
  }
@@ -10889,7 +10900,7 @@ var DomIndexerImpl = /** @class */ (function () {
10889
10900
  model.hasRevertedRangeSelection = true;
10890
10901
  }
10891
10902
  return (isIndexedSegment(startContainer) &&
10892
- !!this.reconcileTextSelection(startContainer, startOffset, endOffset));
10903
+ !!this.reconcileTextSelection(startContainer, startOffset, endOffset, selectionMarker));
10893
10904
  }
10894
10905
  else {
10895
10906
  var marker1 = this.reconcileNodeSelection(startContainer, startOffset);
@@ -11005,10 +11016,10 @@ var DomIndexerImpl = /** @class */ (function () {
11005
11016
  }
11006
11017
  return marker;
11007
11018
  };
11008
- DomIndexerImpl.prototype.reconcileTextSelection = function (textNode, startOffset, endOffset) {
11019
+ DomIndexerImpl.prototype.reconcileTextSelection = function (textNode, startOffset, endOffset, selectionMarker) {
11009
11020
  var _a;
11010
- var _b, _c;
11011
- var _d = textNode.__roosterjsContentModel, paragraph = _d.paragraph, segments = _d.segments;
11021
+ var _b, _c, _d, _e, _f, _g;
11022
+ var _h = textNode.__roosterjsContentModel, paragraph = _h.paragraph, segments = _h.segments;
11012
11023
  var first = segments[0];
11013
11024
  var last = segments[segments.length - 1];
11014
11025
  var selectable;
@@ -11028,9 +11039,9 @@ var DomIndexerImpl = /** @class */ (function () {
11028
11039
  textSegments.push(first);
11029
11040
  }
11030
11041
  if (endOffset === undefined) {
11031
- var marker = (0, roosterjs_content_model_dom_1.createSelectionMarker)(first.format);
11042
+ var marker = (0, roosterjs_content_model_dom_1.createSelectionMarker)((_b = selectionMarker === null || selectionMarker === void 0 ? void 0 : selectionMarker.format) !== null && _b !== void 0 ? _b : first.format);
11032
11043
  newSegments.push(marker);
11033
- if (startOffset < ((_b = textNode.nodeValue) !== null && _b !== void 0 ? _b : '').length) {
11044
+ if (startOffset < ((_c = textNode.nodeValue) !== null && _c !== void 0 ? _c : '').length) {
11034
11045
  if (first.link) {
11035
11046
  (0, roosterjs_content_model_dom_1.addLink)(marker, first.link);
11036
11047
  }
@@ -11042,14 +11053,14 @@ var DomIndexerImpl = /** @class */ (function () {
11042
11053
  endOffset = startOffset;
11043
11054
  }
11044
11055
  else if (endOffset > startOffset) {
11045
- var middle = (0, roosterjs_content_model_dom_1.createText)(txt.substring(startOffset, endOffset), first.format, first.link, first.code);
11056
+ var middle = (0, roosterjs_content_model_dom_1.createText)(txt.substring(startOffset, endOffset), (_d = selectionMarker === null || selectionMarker === void 0 ? void 0 : selectionMarker.format) !== null && _d !== void 0 ? _d : first.format, first.link, first.code);
11046
11057
  middle.isSelected = true;
11047
11058
  newSegments.push(middle);
11048
11059
  textSegments.push(middle);
11049
11060
  selectable = middle;
11050
11061
  }
11051
11062
  if (endOffset < txt.length) {
11052
- var newLast = (0, roosterjs_content_model_dom_1.createText)(txt.substring(endOffset), first.format, first.link, first.code);
11063
+ var newLast = (0, roosterjs_content_model_dom_1.createText)(txt.substring(endOffset), (_e = selectionMarker === null || selectionMarker === void 0 ? void 0 : selectionMarker.format) !== null && _e !== void 0 ? _e : first.format, first.link, first.code);
11053
11064
  newSegments.push(newLast);
11054
11065
  textSegments.push(newLast);
11055
11066
  }
@@ -11079,7 +11090,7 @@ var DomIndexerImpl = /** @class */ (function () {
11079
11090
  var isBefore = wrapper.previousSibling == delimiter;
11080
11091
  var isAfter = wrapper.nextSibling == delimiter;
11081
11092
  if (index >= 0 && delimiter && (0, roosterjs_content_model_dom_1.isEntityDelimiter)(delimiter) && (isBefore || isAfter)) {
11082
- var marker = (0, roosterjs_content_model_dom_1.createSelectionMarker)(((_c = paragraph.segments[isAfter ? index + 1 : index - 1]) !== null && _c !== void 0 ? _c : first).format);
11093
+ var marker = (0, roosterjs_content_model_dom_1.createSelectionMarker)((_f = selectionMarker === null || selectionMarker === void 0 ? void 0 : selectionMarker.format) !== null && _f !== void 0 ? _f : ((_g = paragraph.segments[isAfter ? index + 1 : index - 1]) !== null && _g !== void 0 ? _g : first).format);
11083
11094
  paragraph.segments.splice(isAfter ? index + 1 : index, 0, marker);
11084
11095
  selectable = marker;
11085
11096
  }
@@ -11184,6 +11195,25 @@ var DomIndexerImpl = /** @class */ (function () {
11184
11195
  paragraph.segments.splice(index, 0, text);
11185
11196
  this.onSegment(textNode, paragraph, [text]);
11186
11197
  };
11198
+ DomIndexerImpl.prototype.selectionMarkerToKeepWhenEnteringTextNode = function (oldSelection, newSelection) {
11199
+ // For CJK keyboard input on mobile, we may have a situation like this:
11200
+ // User toggle bold/italic/underline on an empty div, the pending format will be applied on the selection marker
11201
+ // then type some text, the selection move to the text node and the selection marker will be recreated during the reconciliation and lose its original formatting
11202
+ // In this case, we need to keep the original formatting of the selection marker to match the pending format
11203
+ if (this.keepSelectionMarkerWhenEnteringTextNode &&
11204
+ oldSelection.type == 'range' &&
11205
+ this.isCollapsed(oldSelection) &&
11206
+ newSelection.type == 'range' &&
11207
+ (0, roosterjs_content_model_dom_1.isNodeOfType)(newSelection.range.commonAncestorContainer, 'TEXT_NODE') &&
11208
+ newSelection.range.commonAncestorContainer.parentElement == oldSelection.start.node &&
11209
+ isIndexedSegment(newSelection.range.commonAncestorContainer) &&
11210
+ newSelection.range.commonAncestorContainer.__roosterjsContentModel.paragraph.segments[0]
11211
+ .segmentType == 'SelectionMarker') {
11212
+ return newSelection.range.commonAncestorContainer.__roosterjsContentModel.paragraph
11213
+ .segments[0];
11214
+ }
11215
+ return undefined;
11216
+ };
11187
11217
  return DomIndexerImpl;
11188
11218
  }());
11189
11219
  exports.DomIndexerImpl = DomIndexerImpl;
@@ -12150,13 +12180,16 @@ var DOMEventPlugin = /** @class */ (function () {
12150
12180
  };
12151
12181
  this.keyboardEventHandler = {
12152
12182
  beforeDispatch: function (event) {
12183
+ var _a, _b, _c;
12153
12184
  var eventType = EventTypeMap[event.type];
12154
12185
  if ((0, roosterjs_content_model_dom_1.isCharacterValue)(event) || (0, roosterjs_content_model_dom_1.isCursorMovingKey)(event)) {
12155
12186
  // Stop propagation for Character keys and Up/Down/Left/Right/Home/End/PageUp/PageDown
12156
12187
  // since editor already handles these keys and no need to propagate to parents
12157
12188
  event.stopPropagation();
12158
12189
  }
12159
- if (_this.editor && eventType && !event.isComposing && !_this.state.isInIME) {
12190
+ var isAndroid = (_c = (_b = (_a = _this.editor) === null || _a === void 0 ? void 0 : _a.getEnvironment()) === null || _b === void 0 ? void 0 : _b.isAndroid) !== null && _c !== void 0 ? _c : false;
12191
+ var isComposing = !isAndroid && (event.isComposing || _this.state.isInIME);
12192
+ if (_this.editor && eventType && !isComposing) {
12160
12193
  _this.editor.triggerEvent(eventType, {
12161
12194
  rawEvent: event,
12162
12195
  });
@@ -12165,8 +12198,11 @@ var DOMEventPlugin = /** @class */ (function () {
12165
12198
  };
12166
12199
  this.inputEventHandler = {
12167
12200
  beforeDispatch: function (event) {
12201
+ var _a, _b, _c;
12168
12202
  event.stopPropagation();
12169
- if (_this.editor && !event.isComposing && !_this.state.isInIME) {
12203
+ var isAndroid = (_c = (_b = (_a = _this.editor) === null || _a === void 0 ? void 0 : _a.getEnvironment()) === null || _b === void 0 ? void 0 : _b.isAndroid) !== null && _c !== void 0 ? _c : false;
12204
+ var isComposing = !isAndroid && (event.isComposing || _this.state.isInIME);
12205
+ if (_this.editor && !isComposing) {
12170
12206
  _this.editor.triggerEvent('input', {
12171
12207
  rawEvent: event,
12172
12208
  });
@@ -15436,7 +15472,10 @@ exports.listLevelMetadataApplier = {
15436
15472
  var listStyleType = (0, roosterjs_content_model_dom_1.getAutoListStyleType)(listType, metadata !== null && metadata !== void 0 ? metadata : {}, depth);
15437
15473
  if (listStyleType !== undefined) {
15438
15474
  if (!shouldApplyToItem(listStyleType, listType)) {
15439
- var listStyleTypeFormat = getListStyleValue(listType, listStyleType, context.listFormat.threadItemCounts[depth]);
15475
+ var listNumber = context.listFormat.threadItemCounts[depth] > 0
15476
+ ? context.listFormat.threadItemCounts[depth]
15477
+ : 1;
15478
+ var listStyleTypeFormat = getListStyleValue(listType, listStyleType, listNumber);
15440
15479
  if (listStyleTypeFormat) {
15441
15480
  format.listStyleType = listStyleTypeFormat;
15442
15481
  }
@@ -29334,7 +29373,7 @@ function createMarkdownParagraph(paragraph, context) {
29334
29373
  markdownString += textProcessor(segment);
29335
29374
  break;
29336
29375
  case 'Image':
29337
- markdownString += "![" + segment.alt + "](" + segment.src + ")";
29376
+ markdownString += "![" + (segment.alt || 'image') + "](" + segment.src + ")";
29338
29377
  break;
29339
29378
  case 'Br':
29340
29379
  if (!(context === null || context === void 0 ? void 0 : context.ignoreLineBreaks)) {
@@ -29356,7 +29395,9 @@ function createMarkdownParagraph(paragraph, context) {
29356
29395
  if (paragraph.decorator) {
29357
29396
  var tagName = paragraph.decorator.tagName;
29358
29397
  var prefix = headings_1.MarkdownHeadings[tagName];
29359
- markdownString = "" + prefix + markdownString;
29398
+ if (prefix) {
29399
+ markdownString = "" + prefix + markdownString;
29400
+ }
29360
29401
  }
29361
29402
  return markdownString;
29362
29403
  }
@@ -31647,7 +31688,7 @@ var handleEnterOnParagraph = function (context) {
31647
31688
  var _c = context.insertPoint, paragraph = _c.paragraph, path = _c.path;
31648
31689
  var paraIndex = (_b = (_a = path[0]) === null || _a === void 0 ? void 0 : _a.blocks.indexOf(paragraph)) !== null && _b !== void 0 ? _b : -1;
31649
31690
  if (context.deleteResult == 'notDeleted' && paraIndex >= 0) {
31650
- var newPara = (0, splitParagraph_1.splitParagraph)(context.insertPoint);
31691
+ var newPara = (0, splitParagraph_1.splitParagraph)(context.insertPoint, false /* removeImplicitParagraph */);
31651
31692
  (0, roosterjs_content_model_dom_1.mutateBlock)(path[0]).blocks.splice(paraIndex + 1, 0, newPara);
31652
31693
  context.deleteResult = 'range';
31653
31694
  context.lastParagraph = newPara;
@@ -32335,21 +32376,26 @@ var roosterjs_content_model_dom_1 = __webpack_require__(/*! roosterjs-content-mo
32335
32376
  * Split the given paragraph from insert point into two paragraphs,
32336
32377
  * and move the selection marker to the beginning of the second paragraph
32337
32378
  * @param insertPoint The input insert point which includes the paragraph and selection marker
32338
- * @param formatKeys The format that needs to be copied from the splitted paragraph, if not specified, some default format will be copied
32379
+ * @param removeImplicitParagraph Whether to remove the implicit paragraph if it becomes empty after split
32380
+ * * If set to false, the implicit paragraph will be preserved even if it becomes empty
32381
+ * * If set to true, the implicit paragraph will be removed if it becomes empty
32339
32382
  * @returns The new paragraph it created
32340
32383
  */
32341
- function splitParagraph(insertPoint) {
32384
+ function splitParagraph(insertPoint, removeImplicitParagraph) {
32342
32385
  var _a;
32386
+ if (removeImplicitParagraph === void 0) { removeImplicitParagraph = true; }
32343
32387
  var paragraph = insertPoint.paragraph, marker = insertPoint.marker;
32344
32388
  var newParagraph = (0, roosterjs_content_model_dom_1.createParagraph)(false /*isImplicit*/, {}, paragraph.segmentFormat);
32345
32389
  (0, roosterjs_content_model_dom_1.copyFormat)(newParagraph.format, paragraph.format, roosterjs_content_model_dom_1.ParagraphFormats);
32346
32390
  var markerIndex = paragraph.segments.indexOf(marker);
32347
32391
  var segments = paragraph.segments.splice(markerIndex, paragraph.segments.length - markerIndex);
32348
32392
  (_a = newParagraph.segments).push.apply(_a, (0, tslib_1.__spreadArray)([], (0, tslib_1.__read)(segments), false));
32349
- if (paragraph.segments.length == 0 && !paragraph.isImplicit) {
32393
+ var isEmptyParagraph = paragraph.segments.length == 0;
32394
+ var shouldPreserveImplicitParagraph = !paragraph.isImplicit || !removeImplicitParagraph;
32395
+ if (isEmptyParagraph && shouldPreserveImplicitParagraph) {
32350
32396
  paragraph.segments.push((0, roosterjs_content_model_dom_1.createBr)(marker.format));
32351
32397
  }
32352
- else if (paragraph.segments.length > 0) {
32398
+ else if (!isEmptyParagraph) {
32353
32399
  (0, roosterjs_content_model_dom_1.setParagraphNotImplicit)(paragraph);
32354
32400
  }
32355
32401
  insertPoint.paragraph = newParagraph;
@@ -32960,6 +33006,16 @@ var ImageEditPlugin = /** @class */ (function () {
32960
33006
  case 'extractContentWithDom':
32961
33007
  this.removeImageEditing(event.clonedRoot);
32962
33008
  break;
33009
+ case 'beforeLogicalRootChange':
33010
+ this.handleBeforeLogicalRootChange();
33011
+ break;
33012
+ }
33013
+ };
33014
+ ImageEditPlugin.prototype.handleBeforeLogicalRootChange = function () {
33015
+ if (this.isEditing && this.editor && !this.editor.isDisposed()) {
33016
+ this.applyFormatWithContentModel(this.editor, this.isCropMode, false /* shouldSelectImage */);
33017
+ this.removeImageWrapper();
33018
+ this.cleanInfo();
32963
33019
  }
32964
33020
  };
32965
33021
  ImageEditPlugin.prototype.removeImageEditing = function (clonedRoot) {