roosterjs 8.40.2 → 8.42.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.
@@ -2843,9 +2843,11 @@ function getElementBasedFormatState(editor, event) {
2843
2843
  canUnlink: !!editor.queryElements('a[href]', 1 /* OnSelection */)[0],
2844
2844
  canAddImageAltText: !!editor.queryElements('img', 1 /* OnSelection */)[0],
2845
2845
  isBlockQuote: !!editor.queryElements('blockquote', 1 /* OnSelection */)[0],
2846
+ isCodeBlock: !!editor.queryElements('pre>code', 1 /* OnSelection */)[0],
2846
2847
  isInTable: !!table,
2847
2848
  tableFormat: tableFormat,
2848
2849
  tableHasHeader: hasHeader,
2850
+ canMergeTableCell: canMergeTableCell(editor),
2849
2851
  };
2850
2852
  }
2851
2853
  exports.getElementBasedFormatState = getElementBasedFormatState;
@@ -2863,6 +2865,23 @@ function getFormatState(editor, event) {
2863
2865
  return __assign(__assign(__assign(__assign(__assign({}, editor.getPendableFormatState(false /* forceGetStateFromDom */)), getElementBasedFormatState(editor, event)), editor.getStyleBasedFormatState()), editor.getUndoState()), { isDarkMode: editor.isDarkMode(), zoomScale: editor.getZoomScale() });
2864
2866
  }
2865
2867
  exports.default = getFormatState;
2868
+ /**
2869
+ * Checks whether the editor selection range is starting and ending at a table element.
2870
+ * @param editor Editor Instance
2871
+ * @returns
2872
+ */
2873
+ var canMergeTableCell = function (editor) {
2874
+ var selection = editor.getSelectionRangeEx();
2875
+ var isATable = selection && selection.type === 1 /* TableSelection */;
2876
+ if (isATable && selection.coordinates) {
2877
+ var _a = selection.coordinates, firstCell = _a.firstCell, lastCell = _a.lastCell;
2878
+ if (firstCell.x !== lastCell.x || firstCell.y !== lastCell.y) {
2879
+ return true;
2880
+ }
2881
+ return false;
2882
+ }
2883
+ return false;
2884
+ };
2866
2885
 
2867
2886
 
2868
2887
  /***/ }),
@@ -2972,12 +2991,18 @@ exports.default = insertEntity;
2972
2991
  Object.defineProperty(exports, "__esModule", { value: true });
2973
2992
  var formatUndoSnapshot_1 = __webpack_require__(/*! ../utils/formatUndoSnapshot */ "./packages/roosterjs-editor-api/lib/utils/formatUndoSnapshot.ts");
2974
2993
  var roosterjs_editor_dom_1 = __webpack_require__(/*! roosterjs-editor-dom */ "./packages/roosterjs-editor-dom/lib/index.ts");
2975
- function insertImage(editor, imageFile, attributes) {
2976
- if (typeof imageFile == 'string') {
2977
- insertImageWithSrc(editor, imageFile, attributes);
2994
+ /**
2995
+ * Insert an image to editor at current selection
2996
+ * @param editor The editor instance
2997
+ * @param imageFileOrSrc Either the image file blob or source string of the image.
2998
+ * @param attributes Optional image element attributes
2999
+ */
3000
+ function insertImage(editor, imageFileOrSrc, attributes) {
3001
+ if (typeof imageFileOrSrc == 'string') {
3002
+ insertImageWithSrc(editor, imageFileOrSrc, attributes);
2978
3003
  }
2979
3004
  else {
2980
- (0, roosterjs_editor_dom_1.readFile)(imageFile, function (dataUrl) {
3005
+ (0, roosterjs_editor_dom_1.readFile)(imageFileOrSrc, function (dataUrl) {
2981
3006
  if (dataUrl && !editor.isDisposed()) {
2982
3007
  insertImageWithSrc(editor, dataUrl, attributes);
2983
3008
  }
@@ -3237,7 +3262,7 @@ var roosterjs_editor_dom_1 = __webpack_require__(/*! roosterjs-editor-dom */ "./
3237
3262
  **/
3238
3263
  function setBackgroundColor(editor, color) {
3239
3264
  (0, applyInlineStyle_1.default)(editor, function (element, isInnerNode) {
3240
- (0, roosterjs_editor_dom_1.setColor)(element, isInnerNode ? '' : color, true /*isBackground*/, editor.isDarkMode());
3265
+ (0, roosterjs_editor_dom_1.setColor)(element, isInnerNode ? '' : color, true /*isBackground*/, editor.isDarkMode(), false /*shouldAdaptFontColor*/, editor.getDarkColorHandler());
3241
3266
  }, 'setBackgroundColor');
3242
3267
  }
3243
3268
  exports.default = setBackgroundColor;
@@ -3542,7 +3567,7 @@ var roosterjs_editor_dom_1 = __webpack_require__(/*! roosterjs-editor-dom */ "./
3542
3567
  function setTextColor(editor, color, shouldApplyInlineStyle) {
3543
3568
  (0, applyListItemWrap_1.default)(editor, 'color', function (element, isInnerNode) {
3544
3569
  if (!shouldApplyInlineStyle || shouldApplyInlineStyle(element)) {
3545
- (0, roosterjs_editor_dom_1.setColor)(element, isInnerNode ? '' : color, false /*isBackground*/, editor.isDarkMode());
3570
+ (0, roosterjs_editor_dom_1.setColor)(element, isInnerNode ? '' : color, false /*isBackground*/, editor.isDarkMode(), false /*shouldAdaptFontColor*/, editor.getDarkColorHandler());
3546
3571
  }
3547
3572
  }, 'setTextColor');
3548
3573
  }
@@ -4025,7 +4050,7 @@ function applyCellShading(editor, color) {
4025
4050
  var regions = editor.getSelectedRegions();
4026
4051
  regions.forEach(function (region) {
4027
4052
  if ((0, roosterjs_editor_dom_1.safeInstanceOf)(region.rootNode, 'HTMLTableCellElement')) {
4028
- (0, roosterjs_editor_dom_1.setColor)(region.rootNode, color, true /* isBackgroundColor */, editor.isDarkMode(), true /** shouldAdaptFontColor */);
4053
+ (0, roosterjs_editor_dom_1.setColor)(region.rootNode, color, true /* isBackgroundColor */, editor.isDarkMode(), true /** shouldAdaptFontColor */, editor.getDarkColorHandler());
4029
4054
  (0, roosterjs_editor_dom_1.saveTableCellMetadata)(region.rootNode, { bgColorOverride: true });
4030
4055
  }
4031
4056
  });
@@ -5177,7 +5202,7 @@ var ensureTypeInContainer = function (core, position, keyboardEvent, applyFormat
5177
5202
  position = new roosterjs_editor_dom_1.Position(formatNode, 0 /* Begin */);
5178
5203
  }
5179
5204
  if (formatNode && core.lifecycle.defaultFormat) {
5180
- (0, roosterjs_editor_dom_1.applyFormat)(formatNode, core.lifecycle.defaultFormat, core.lifecycle.isDarkMode);
5205
+ (0, roosterjs_editor_dom_1.applyFormat)(formatNode, core.lifecycle.defaultFormat, core.lifecycle.isDarkMode, core.darkColorHandler);
5181
5206
  }
5182
5207
  // If this is triggered by a keyboard event, let's select the new position
5183
5208
  if (keyboardEvent) {
@@ -5273,7 +5298,7 @@ var getContent = function (core, mode) {
5273
5298
  else if (mode == 3 /* PlainText */) {
5274
5299
  content = (0, roosterjs_editor_dom_1.getTextContent)(root);
5275
5300
  }
5276
- else if (triggerExtractContentEvent || core.lifecycle.isDarkMode) {
5301
+ else if (triggerExtractContentEvent || core.lifecycle.isDarkMode || core.darkColorHandler) {
5277
5302
  var clonedRoot = cloneNode(root);
5278
5303
  clonedRoot.normalize();
5279
5304
  var originalRange = core.api.getSelectionRange(core, true /*tryGetFromCache*/);
@@ -5285,8 +5310,8 @@ var getContent = function (core, mode) {
5285
5310
  ? (0, roosterjs_editor_dom_1.getSelectionPath)(core.contentDiv, originalRange)
5286
5311
  : null;
5287
5312
  var range = path && (0, roosterjs_editor_dom_1.createRange)(clonedRoot, path.start, path.end);
5288
- if (core.lifecycle.isDarkMode) {
5289
- core.api.transformColor(core, clonedRoot, false /*includeSelf*/, null /*callback*/, 1 /* DarkToLight */);
5313
+ if (core.lifecycle.isDarkMode || core.darkColorHandler) {
5314
+ core.api.transformColor(core, clonedRoot, false /*includeSelf*/, null /*callback*/, 1 /* DarkToLight */, !!core.darkColorHandler);
5290
5315
  }
5291
5316
  if (triggerExtractContentEvent) {
5292
5317
  core.api.triggerEvent(core, {
@@ -5599,39 +5624,93 @@ var getStyleBasedFormatState = function (core, node) {
5599
5624
  pendableFormatSpan.style.backgroundColor,
5600
5625
  ];
5601
5626
  }
5602
- var styles = node ? (0, roosterjs_editor_dom_1.getComputedStyles)(node) : [];
5603
- var isDarkMode = core.lifecycle.isDarkMode;
5604
- var root = core.contentDiv;
5605
- var ogTextColorNode = isDarkMode &&
5606
- (override[2]
5607
- ? pendableFormatSpan
5608
- : (0, roosterjs_editor_dom_1.findClosestElementAncestor)(node, root, ORIGINAL_STYLE_COLOR_SELECTOR));
5609
- var ogBackgroundColorNode = isDarkMode &&
5610
- (override[3]
5611
- ? pendableFormatSpan
5612
- : (0, roosterjs_editor_dom_1.findClosestElementAncestor)(node, root, ORIGINAL_STYLE_BACK_COLOR_SELECTOR));
5613
- return {
5614
- fontName: override[0] || styles[0],
5615
- fontSize: override[1] || styles[1],
5616
- textColor: override[2] || styles[2],
5617
- backgroundColor: override[3] || styles[3],
5618
- textColors: ogTextColorNode
5619
- ? {
5620
- darkModeColor: override[2] || styles[2],
5621
- lightModeColor: ogTextColorNode.dataset["ogsc" /* OriginalStyleColor */] ||
5622
- ogTextColorNode.dataset["ogac" /* OriginalAttributeColor */] ||
5623
- styles[2],
5624
- }
5625
- : undefined,
5626
- backgroundColors: ogBackgroundColorNode
5627
- ? {
5628
- darkModeColor: override[3] || styles[3],
5629
- lightModeColor: ogBackgroundColorNode.dataset["ogsb" /* OriginalStyleBackgroundColor */] ||
5630
- ogBackgroundColorNode.dataset["ogab" /* OriginalAttributeBackgroundColor */] ||
5631
- styles[3],
5627
+ var styles = node
5628
+ ? (0, roosterjs_editor_dom_1.getComputedStyles)(node, [
5629
+ 'font-family',
5630
+ 'font-size',
5631
+ 'color',
5632
+ 'background-color',
5633
+ 'line-height',
5634
+ 'margin-top',
5635
+ 'margin-bottom',
5636
+ ])
5637
+ : [];
5638
+ var contentDiv = core.contentDiv, darkColorHandler = core.darkColorHandler, isDarkMode = core.lifecycle.isDarkMode;
5639
+ if (darkColorHandler) {
5640
+ var styleTextColor = void 0;
5641
+ var styleBackColor = void 0;
5642
+ while (node &&
5643
+ (0, roosterjs_editor_dom_1.contains)(contentDiv, node, true /*treatSameNodeAsContain*/) &&
5644
+ !(styleTextColor && styleBackColor)) {
5645
+ if (node.nodeType == 1 /* Element */) {
5646
+ var element = node;
5647
+ styleTextColor = styleTextColor || element.style.getPropertyValue('color');
5648
+ styleBackColor =
5649
+ styleBackColor || element.style.getPropertyValue('background-color');
5632
5650
  }
5633
- : undefined,
5634
- };
5651
+ node = node.parentNode;
5652
+ }
5653
+ if (!core.lifecycle.isDarkMode && node == core.contentDiv) {
5654
+ styleTextColor = styleTextColor || styles[2];
5655
+ styleBackColor = styleBackColor || styles[3];
5656
+ }
5657
+ var textColor = darkColorHandler.parseColorValue(override[2] || styleTextColor);
5658
+ var backColor = darkColorHandler.parseColorValue(override[3] || styleBackColor);
5659
+ return {
5660
+ fontName: override[0] || styles[0],
5661
+ fontSize: override[1] || styles[1],
5662
+ textColor: textColor.lightModeColor,
5663
+ backgroundColor: backColor.lightModeColor,
5664
+ textColors: textColor.darkModeColor
5665
+ ? {
5666
+ lightModeColor: textColor.lightModeColor,
5667
+ darkModeColor: textColor.darkModeColor,
5668
+ }
5669
+ : undefined,
5670
+ backgroundColors: backColor.darkModeColor
5671
+ ? {
5672
+ lightModeColor: backColor.lightModeColor,
5673
+ darkModeColor: backColor.darkModeColor,
5674
+ }
5675
+ : undefined,
5676
+ lineHeight: styles[4],
5677
+ marginTop: styles[5],
5678
+ marginBottom: styles[6],
5679
+ };
5680
+ }
5681
+ else {
5682
+ var ogTextColorNode = isDarkMode &&
5683
+ (override[2]
5684
+ ? pendableFormatSpan
5685
+ : (0, roosterjs_editor_dom_1.findClosestElementAncestor)(node, contentDiv, ORIGINAL_STYLE_COLOR_SELECTOR));
5686
+ var ogBackgroundColorNode = isDarkMode &&
5687
+ (override[3]
5688
+ ? pendableFormatSpan
5689
+ : (0, roosterjs_editor_dom_1.findClosestElementAncestor)(node, contentDiv, ORIGINAL_STYLE_BACK_COLOR_SELECTOR));
5690
+ return {
5691
+ fontName: override[0] || styles[0],
5692
+ fontSize: override[1] || styles[1],
5693
+ textColor: override[2] || styles[2],
5694
+ backgroundColor: override[3] || styles[3],
5695
+ textColors: ogTextColorNode
5696
+ ? {
5697
+ darkModeColor: override[2] || styles[2],
5698
+ lightModeColor: ogTextColorNode.dataset["ogsc" /* OriginalStyleColor */] ||
5699
+ ogTextColorNode.dataset["ogac" /* OriginalAttributeColor */] ||
5700
+ styles[2],
5701
+ }
5702
+ : undefined,
5703
+ backgroundColors: ogBackgroundColorNode
5704
+ ? {
5705
+ darkModeColor: override[3] || styles[3],
5706
+ lightModeColor: ogBackgroundColorNode.dataset["ogsb" /* OriginalStyleBackgroundColor */] ||
5707
+ ogBackgroundColorNode.dataset["ogab" /* OriginalAttributeBackgroundColor */] ||
5708
+ styles[3],
5709
+ }
5710
+ : undefined,
5711
+ lineHeight: styles[4],
5712
+ };
5713
+ }
5635
5714
  };
5636
5715
  exports.getStyleBasedFormatState = getStyleBasedFormatState;
5637
5716
 
@@ -6373,21 +6452,38 @@ var ColorAttributeName = [
6373
6452
  * Pass true to this value to force do color transformation even editor core is in light mode
6374
6453
  */
6375
6454
  var transformColor = function (core, rootNode, includeSelf, callback, direction, forceTransform) {
6455
+ var darkColorHandler = core.darkColorHandler;
6376
6456
  var elements = rootNode && (forceTransform || core.lifecycle.isDarkMode)
6377
6457
  ? getAll(rootNode, includeSelf)
6378
6458
  : [];
6379
6459
  callback === null || callback === void 0 ? void 0 : callback();
6380
- if (direction == 1 /* DarkToLight */) {
6381
- transformToLightMode(elements);
6382
- }
6383
- else if (core.lifecycle.onExternalContentTransform) {
6384
- elements.forEach(function (element) { return core.lifecycle.onExternalContentTransform(element); });
6460
+ if (darkColorHandler) {
6461
+ transformV2(elements, darkColorHandler, direction == 0 /* LightToDark */);
6385
6462
  }
6386
6463
  else {
6387
- transformToDarkMode(elements, core.lifecycle.getDarkColor);
6464
+ if (direction == 1 /* DarkToLight */) {
6465
+ transformToLightMode(elements);
6466
+ }
6467
+ else if (core.lifecycle.onExternalContentTransform) {
6468
+ elements.forEach(function (element) { return core.lifecycle.onExternalContentTransform(element); });
6469
+ }
6470
+ else {
6471
+ transformToDarkMode(elements, core.lifecycle.getDarkColor);
6472
+ }
6388
6473
  }
6389
6474
  };
6390
6475
  exports.transformColor = transformColor;
6476
+ function transformV2(elements, darkColorHandler, toDark) {
6477
+ elements.forEach(function (element) {
6478
+ ColorAttributeName.forEach(function (names, i) {
6479
+ var color = darkColorHandler.parseColorValue(element.style.getPropertyValue(names[0 /* CssColor */]) ||
6480
+ element.getAttribute(names[1 /* HtmlColor */])).lightModeColor;
6481
+ if (color && color != 'inherit') {
6482
+ (0, roosterjs_editor_dom_1.setColor)(element, color, i != 0, toDark, false /*shouldAdaptFontColor*/, darkColorHandler);
6483
+ }
6484
+ });
6485
+ });
6486
+ }
6391
6487
  function transformToLightMode(elements) {
6392
6488
  elements.forEach(function (element) {
6393
6489
  ColorAttributeName.forEach(function (names) {
@@ -7665,10 +7761,12 @@ var LifecyclePlugin = /** @class */ (function () {
7665
7761
  this.adjustColor = options.doNotAdjustEditorColor
7666
7762
  ? function () { }
7667
7763
  : function () {
7764
+ var _a;
7668
7765
  var textColors = DARK_MODE_DEFAULT_FORMAT.textColors, backgroundColors = DARK_MODE_DEFAULT_FORMAT.backgroundColors;
7669
7766
  var isDarkMode = _this.state.isDarkMode;
7670
- (0, roosterjs_editor_dom_1.setColor)(contentDiv, textColors, false /*isBackground*/, isDarkMode);
7671
- (0, roosterjs_editor_dom_1.setColor)(contentDiv, backgroundColors, true /*isBackground*/, isDarkMode);
7767
+ var darkColorHandler = (_a = _this.editor) === null || _a === void 0 ? void 0 : _a.getDarkColorHandler();
7768
+ (0, roosterjs_editor_dom_1.setColor)(contentDiv, textColors, false /*isBackground*/, isDarkMode, false /*shouldAdaptFontColor*/, darkColorHandler);
7769
+ (0, roosterjs_editor_dom_1.setColor)(contentDiv, backgroundColors, true /*isBackground*/, isDarkMode, false /*shouldAdaptFontColor*/, darkColorHandler);
7672
7770
  };
7673
7771
  this.state = {
7674
7772
  customData: {},
@@ -8140,7 +8238,7 @@ var PendingFormatStatePlugin = /** @class */ (function () {
8140
8238
  this.state.pendableFormatSpan) {
8141
8239
  this.state.pendableFormatSpan.removeAttribute('contentEditable');
8142
8240
  this.editor.insertNode(this.state.pendableFormatSpan);
8143
- this.editor.select(this.state.pendableFormatSpan, -2 /* Before */, this.state.pendableFormatSpan, -1 /* End */);
8241
+ this.editor.select(this.state.pendableFormatSpan, 0 /* Begin */, this.state.pendableFormatSpan, -1 /* End */);
8144
8242
  this.clear();
8145
8243
  }
8146
8244
  else if ((event.eventType == 0 /* KeyDown */ &&
@@ -8148,7 +8246,10 @@ var PendingFormatStatePlugin = /** @class */ (function () {
8148
8246
  event.rawEvent.which <= 40 /* DOWN */) ||
8149
8247
  (this.state.pendableFormatPosition &&
8150
8248
  (currentPosition = this.getCurrentPosition()) &&
8151
- !this.state.pendableFormatPosition.equalTo(currentPosition))) {
8249
+ !this.state.pendableFormatPosition.equalTo(currentPosition)) ||
8250
+ (event.eventType == 7 /* ContentChanged */ &&
8251
+ (event.source == "SwitchToDarkMode" /* SwitchToDarkMode */ ||
8252
+ event.source == "SwitchToLightMode" /* SwitchToLightMode */))) {
8152
8253
  // If content or position is changed (by keyboard, mouse, or code),
8153
8254
  // check if current position is still the same with the cached one (if exist),
8154
8255
  // and clear cached format if position is changed since it is out-of-date now
@@ -8179,11 +8280,12 @@ var PendingFormatStatePlugin = /** @class */ (function () {
8179
8280
  span.appendChild(doc.createTextNode(ZERO_WIDTH_SPACE));
8180
8281
  span.style.setProperty('font-family', (_a = currentStyle.fontName) !== null && _a !== void 0 ? _a : null);
8181
8282
  span.style.setProperty('font-size', (_b = currentStyle.fontSize) !== null && _b !== void 0 ? _b : null);
8283
+ var darkColorHandler = this.editor.getDarkColorHandler();
8182
8284
  if (currentStyle.textColors || currentStyle.textColor) {
8183
- (0, roosterjs_editor_dom_1.setColor)(span, (currentStyle.textColors || currentStyle.textColor), false /*isBackground*/, isDarkMode);
8285
+ (0, roosterjs_editor_dom_1.setColor)(span, (currentStyle.textColors || currentStyle.textColor), false /*isBackground*/, isDarkMode, false /*shouldAdaptFontColor*/, darkColorHandler);
8184
8286
  }
8185
8287
  if (currentStyle.backgroundColors || currentStyle.backgroundColor) {
8186
- (0, roosterjs_editor_dom_1.setColor)(span, (currentStyle.backgroundColors || currentStyle.backgroundColor), true /*isBackground*/, isDarkMode);
8288
+ (0, roosterjs_editor_dom_1.setColor)(span, (currentStyle.backgroundColors || currentStyle.backgroundColor), true /*isBackground*/, isDarkMode, false /*shouldAdaptFontColor*/, darkColorHandler);
8187
8289
  }
8188
8290
  }
8189
8291
  if (span) {
@@ -8684,6 +8786,98 @@ var removeCellsOutsideSelection = function (vTable) {
8684
8786
  exports.removeCellsOutsideSelection = removeCellsOutsideSelection;
8685
8787
 
8686
8788
 
8789
+ /***/ }),
8790
+
8791
+ /***/ "./packages/roosterjs-editor-core/lib/editor/DarkColorHandlerImpl.ts":
8792
+ /*!***************************************************************************!*\
8793
+ !*** ./packages/roosterjs-editor-core/lib/editor/DarkColorHandlerImpl.ts ***!
8794
+ \***************************************************************************/
8795
+ /*! no static exports found */
8796
+ /***/ (function(module, exports, __webpack_require__) {
8797
+
8798
+ "use strict";
8799
+
8800
+ Object.defineProperty(exports, "__esModule", { value: true });
8801
+ var roosterjs_editor_dom_1 = __webpack_require__(/*! roosterjs-editor-dom */ "./packages/roosterjs-editor-dom/lib/index.ts");
8802
+ var VARIABLE_REGEX = /^\s*var\(\s*(\-\-[a-zA-Z0-9\-_]+)\s*(?:,\s*(.*))?\)\s*$/;
8803
+ var VARIABLE_PREFIX = 'var(';
8804
+ var COLOR_VAR_PREFIX = 'darkColor';
8805
+ /**
8806
+ * @internal
8807
+ */
8808
+ var DarkColorHandlerImpl = /** @class */ (function () {
8809
+ function DarkColorHandlerImpl(contentDiv, getDarkColor) {
8810
+ this.contentDiv = contentDiv;
8811
+ this.getDarkColor = getDarkColor;
8812
+ this.knownColors = {};
8813
+ }
8814
+ /**
8815
+ * Given a light mode color value and an optional dark mode color value, register this color
8816
+ * so that editor can handle it, then return the CSS color value for current color mode.
8817
+ * @param lightModeColor Light mode color value
8818
+ * @param isDarkMode Whether current color mode is dark mode
8819
+ * @param darkModeColor Optional dark mode color value. If not passed, we will calculate one.
8820
+ */
8821
+ DarkColorHandlerImpl.prototype.registerColor = function (lightModeColor, isDarkMode, darkModeColor) {
8822
+ var parsedColor = this.parseColorValue(lightModeColor);
8823
+ var colorKey;
8824
+ if (parsedColor) {
8825
+ lightModeColor = parsedColor.lightModeColor;
8826
+ darkModeColor = parsedColor.darkModeColor || darkModeColor;
8827
+ colorKey = parsedColor.key;
8828
+ }
8829
+ if (isDarkMode && lightModeColor) {
8830
+ colorKey =
8831
+ colorKey || "--" + COLOR_VAR_PREFIX + "_" + lightModeColor.replace(/[^\d\w]/g, '_');
8832
+ if (!this.knownColors[colorKey]) {
8833
+ darkModeColor = darkModeColor || this.getDarkColor(lightModeColor);
8834
+ this.knownColors[colorKey] = { lightModeColor: lightModeColor, darkModeColor: darkModeColor };
8835
+ this.contentDiv.style.setProperty(colorKey, darkModeColor);
8836
+ }
8837
+ return "var(" + colorKey + ", " + lightModeColor + ")";
8838
+ }
8839
+ else {
8840
+ return lightModeColor;
8841
+ }
8842
+ };
8843
+ /**
8844
+ * Reset known color record, clean up registered color variables.
8845
+ */
8846
+ DarkColorHandlerImpl.prototype.reset = function () {
8847
+ var _this = this;
8848
+ (0, roosterjs_editor_dom_1.getObjectKeys)(this.knownColors).forEach(function (key) { return _this.contentDiv.style.removeProperty(key); });
8849
+ this.knownColors = {};
8850
+ };
8851
+ /**
8852
+ * Parse an existing color value, if it is in variable-based color format, extract color key,
8853
+ * light color and query related dark color if any
8854
+ * @param color The color string to parse
8855
+ */
8856
+ DarkColorHandlerImpl.prototype.parseColorValue = function (color) {
8857
+ var _a;
8858
+ var key;
8859
+ var lightModeColor = color || '';
8860
+ var darkModeColor;
8861
+ if (color) {
8862
+ var match = color.startsWith(VARIABLE_PREFIX) ? VARIABLE_REGEX.exec(color) : null;
8863
+ if (match) {
8864
+ if (match[2]) {
8865
+ key = match[1];
8866
+ lightModeColor = match[2];
8867
+ darkModeColor = (_a = this.knownColors[key]) === null || _a === void 0 ? void 0 : _a.darkModeColor;
8868
+ }
8869
+ else {
8870
+ lightModeColor = '';
8871
+ }
8872
+ }
8873
+ }
8874
+ return { key: key, lightModeColor: lightModeColor, darkModeColor: darkModeColor };
8875
+ };
8876
+ return DarkColorHandlerImpl;
8877
+ }());
8878
+ exports.default = DarkColorHandlerImpl;
8879
+
8880
+
8687
8881
  /***/ }),
8688
8882
 
8689
8883
  /***/ "./packages/roosterjs-editor-core/lib/editor/Editor.ts":
@@ -8708,6 +8902,7 @@ var __assign = (this && this.__assign) || function () {
8708
8902
  };
8709
8903
  Object.defineProperty(exports, "__esModule", { value: true });
8710
8904
  var createCorePlugins_1 = __webpack_require__(/*! ../corePlugins/createCorePlugins */ "./packages/roosterjs-editor-core/lib/corePlugins/createCorePlugins.ts");
8905
+ var DarkColorHandlerImpl_1 = __webpack_require__(/*! ./DarkColorHandlerImpl */ "./packages/roosterjs-editor-core/lib/editor/DarkColorHandlerImpl.ts");
8711
8906
  var coreApiMap_1 = __webpack_require__(/*! ../coreApi/coreApiMap */ "./packages/roosterjs-editor-core/lib/coreApi/coreApiMap.ts");
8712
8907
  var roosterjs_editor_dom_1 = __webpack_require__(/*! roosterjs-editor-dom */ "./packages/roosterjs-editor-dom/lib/index.ts");
8713
8908
  /**
@@ -8750,6 +8945,9 @@ var Editor = /** @class */ (function () {
8750
8945
  ? [scrollContainer]
8751
8946
  : [scrollContainer, contentDiv]);
8752
8947
  }), imageSelectionBorderColor: options.imageSelectionBorderColor });
8948
+ if (this.isFeatureEnabled("VariableBasedDarkColor" /* VariableBasedDarkColor */)) {
8949
+ this.core.darkColorHandler = new DarkColorHandlerImpl_1.default(contentDiv, this.core.lifecycle.getDarkColor);
8950
+ }
8753
8951
  // 3. Initialize plugins
8754
8952
  this.core.plugins.forEach(function (plugin) { return plugin.initialize(_this); });
8755
8953
  // 4. Ensure user will type in a container node, not the editor content DIV
@@ -8759,10 +8957,12 @@ var Editor = /** @class */ (function () {
8759
8957
  * Dispose this editor, dispose all plugins and custom data
8760
8958
  */
8761
8959
  Editor.prototype.dispose = function () {
8960
+ var _a;
8762
8961
  var core = this.getCore();
8763
8962
  for (var i = core.plugins.length - 1; i >= 0; i--) {
8764
8963
  core.plugins[i].dispose();
8765
8964
  }
8965
+ (_a = core.darkColorHandler) === null || _a === void 0 ? void 0 : _a.reset();
8766
8966
  this.core = null;
8767
8967
  };
8768
8968
  /**
@@ -9473,6 +9673,12 @@ var Editor = /** @class */ (function () {
9473
9673
  var core = this.getCore();
9474
9674
  core.api.transformColor(core, node, true /*includeSelf*/, null /*callback*/, 0 /* LightToDark */);
9475
9675
  };
9676
+ /**
9677
+ * Get a darkColorHandler object for this editor. It will return null if experimental feature "VariableBasedDarkColor" is not enabled
9678
+ */
9679
+ Editor.prototype.getDarkColorHandler = function () {
9680
+ return this.getCore().darkColorHandler || null;
9681
+ };
9476
9682
  /**
9477
9683
  * Make the editor in "Shadow Edit" mode.
9478
9684
  * In Shadow Edit mode, all format change will finally be ignored.
@@ -18118,8 +18324,10 @@ var setColor_1 = __webpack_require__(/*! ./setColor */ "./packages/roosterjs-edi
18118
18324
  * Apply format to an HTML element
18119
18325
  * @param element The HTML element to apply format to
18120
18326
  * @param format The format to apply
18327
+ * @param isDarkMode Whether the content should be formatted in dark mode
18328
+ * @param darkColorHandler An optional dark color handler object. When it is passed, we will use this handler to do variable-based dark color instead of original dataset base dark color
18121
18329
  */
18122
- function applyFormat(element, format, isDarkMode) {
18330
+ function applyFormat(element, format, isDarkMode, darkColorHandler) {
18123
18331
  if (format) {
18124
18332
  var elementStyle = element.style;
18125
18333
  var fontFamily = format.fontFamily, fontSize = format.fontSize, textColor = format.textColor, textColors = format.textColors, backgroundColor = format.backgroundColor, backgroundColors = format.backgroundColors, bold = format.bold, italic = format.italic, underline = format.underline;
@@ -18130,16 +18338,16 @@ function applyFormat(element, format, isDarkMode) {
18130
18338
  elementStyle.fontSize = fontSize;
18131
18339
  }
18132
18340
  if (textColors) {
18133
- (0, setColor_1.default)(element, textColors, false /*isBackground*/, isDarkMode);
18341
+ (0, setColor_1.default)(element, textColors, false /*isBackground*/, isDarkMode, false /*shouldAdaptFontColor*/, darkColorHandler);
18134
18342
  }
18135
18343
  else if (textColor) {
18136
- (0, setColor_1.default)(element, textColor, false /*isBackground*/, isDarkMode);
18344
+ (0, setColor_1.default)(element, textColor, false /*isBackground*/, isDarkMode, false /*shouldAdaptFontColor*/, darkColorHandler);
18137
18345
  }
18138
18346
  if (backgroundColors) {
18139
- (0, setColor_1.default)(element, backgroundColors, true /*isBackground*/, isDarkMode);
18347
+ (0, setColor_1.default)(element, backgroundColors, true /*isBackground*/, isDarkMode, false /*shouldAdaptFontColor*/, darkColorHandler);
18140
18348
  }
18141
18349
  else if (backgroundColor) {
18142
- (0, setColor_1.default)(element, backgroundColor, true /*isBackground*/, isDarkMode);
18350
+ (0, setColor_1.default)(element, backgroundColor, true /*isBackground*/, isDarkMode, false /*shouldAdaptFontColor*/, darkColorHandler);
18143
18351
  }
18144
18352
  if (bold) {
18145
18353
  elementStyle.fontWeight = 'bold';
@@ -18376,7 +18584,7 @@ exports.KnownCreateElementData = (_a = {},
18376
18584
  },
18377
18585
  _a[6 /* ImageEditWrapper */] = {
18378
18586
  tag: 'span',
18379
- style: 'max-width:100%;position:fixed',
18587
+ style: 'max-width:100%;vertical-align:bottom',
18380
18588
  children: [
18381
18589
  {
18382
18590
  tag: 'div',
@@ -18665,8 +18873,8 @@ function getIntersectedRect(elements, additionalRects) {
18665
18873
  if (additionalRects === void 0) { additionalRects = []; }
18666
18874
  var rects = elements
18667
18875
  .map(function (element) { return (0, normalizeRect_1.default)(element.getBoundingClientRect()); })
18668
- .filter(function (element) { return !!element; })
18669
- .concat(additionalRects);
18876
+ .concat(additionalRects)
18877
+ .filter(function (element) { return !!element; });
18670
18878
  var result = {
18671
18879
  top: Math.max.apply(Math, rects.map(function (r) { return r.top; })),
18672
18880
  bottom: Math.min.apply(Math, rects.map(function (r) { return r.bottom; })),
@@ -19393,28 +19601,35 @@ var TRANSPARENT_COLOR = 'transparent';
19393
19601
  * @param isBackgroundColor Whether set background color or text color
19394
19602
  * @param isDarkMode Whether current mode is dark mode. @default false
19395
19603
  * @param shouldAdaptTheFontColor Whether the font color needs to be adapted to be visible in a dark or bright background color. @default false
19396
- * @param defaultFontColor Set the default colors that needs to be set to the to be visible.
19604
+ * @param darkColorHandler An optional dark color handler object. When it is passed, we will use this handler to do variable-based dark color instead of original dataset base dark color
19397
19605
  */
19398
- function setColor(element, color, isBackgroundColor, isDarkMode, shouldAdaptTheFontColor) {
19606
+ function setColor(element, color, isBackgroundColor, isDarkMode, shouldAdaptTheFontColor, darkColorHandler) {
19399
19607
  var colorString = typeof color === 'string' ? color.trim() : '';
19400
19608
  var modeIndependentColor = typeof color === 'string' ? null : color;
19609
+ var cssName = isBackgroundColor ? 'background-color' : 'color';
19401
19610
  if (colorString || modeIndependentColor) {
19402
- element.style.setProperty(isBackgroundColor ? 'background-color' : 'color', (isDarkMode
19403
- ? modeIndependentColor === null || modeIndependentColor === void 0 ? void 0 : modeIndependentColor.darkModeColor
19404
- : modeIndependentColor === null || modeIndependentColor === void 0 ? void 0 : modeIndependentColor.lightModeColor) || colorString);
19405
- if (element.dataset) {
19406
- var dataSetName = isBackgroundColor
19407
- ? "ogsb" /* OriginalStyleBackgroundColor */
19408
- : "ogsc" /* OriginalStyleColor */;
19409
- if (!isDarkMode || color == TRANSPARENT_COLOR) {
19410
- delete element.dataset[dataSetName];
19411
- }
19412
- else if (modeIndependentColor) {
19413
- element.dataset[dataSetName] = modeIndependentColor.lightModeColor;
19611
+ if (darkColorHandler) {
19612
+ var colorValue = darkColorHandler.registerColor((modeIndependentColor === null || modeIndependentColor === void 0 ? void 0 : modeIndependentColor.lightModeColor) || colorString, !!isDarkMode, modeIndependentColor === null || modeIndependentColor === void 0 ? void 0 : modeIndependentColor.darkModeColor);
19613
+ element.style.setProperty(cssName, colorValue);
19614
+ }
19615
+ else {
19616
+ element.style.setProperty(cssName, (isDarkMode
19617
+ ? modeIndependentColor === null || modeIndependentColor === void 0 ? void 0 : modeIndependentColor.darkModeColor
19618
+ : modeIndependentColor === null || modeIndependentColor === void 0 ? void 0 : modeIndependentColor.lightModeColor) || colorString);
19619
+ if (element.dataset) {
19620
+ var dataSetName = isBackgroundColor
19621
+ ? "ogsb" /* OriginalStyleBackgroundColor */
19622
+ : "ogsc" /* OriginalStyleColor */;
19623
+ if (!isDarkMode || color == TRANSPARENT_COLOR) {
19624
+ delete element.dataset[dataSetName];
19625
+ }
19626
+ else if (modeIndependentColor) {
19627
+ element.dataset[dataSetName] = modeIndependentColor.lightModeColor;
19628
+ }
19414
19629
  }
19415
19630
  }
19416
19631
  if (isBackgroundColor && shouldAdaptTheFontColor) {
19417
- adaptFontColorToBackgroundColor(element, isDarkMode);
19632
+ adaptFontColorToBackgroundColor(element, (modeIndependentColor === null || modeIndependentColor === void 0 ? void 0 : modeIndependentColor.lightModeColor) || colorString, isDarkMode, darkColorHandler);
19418
19633
  }
19419
19634
  }
19420
19635
  }
@@ -19422,17 +19637,11 @@ exports.default = setColor;
19422
19637
  /**
19423
19638
  * Change the font color to white or some other color, so the text can be visible with a darker background
19424
19639
  * @param element The element that contains text.
19640
+ * @param lightModeBackgroundColor Existing background color in light mode
19641
+ * @param isDarkMode Whether the content is in dark mode
19642
+ * @param darkColorHandler An optional dark color handler object. When it is passed, we will use this handler to do variable-based dark color instead of original dataset base dark color
19425
19643
  */
19426
- function adaptFontColorToBackgroundColor(element, isDarkMode) {
19427
- var _a;
19428
- if ((_a = element.firstElementChild) === null || _a === void 0 ? void 0 : _a.hasAttribute('style')) {
19429
- return;
19430
- }
19431
- var backgroundColor = element.style.getPropertyValue('background-color');
19432
- var lightModeBackgroundColor = (isDarkMode &&
19433
- (element.dataset["ogsb" /* OriginalStyleBackgroundColor */] ||
19434
- element.dataset["ogab" /* OriginalAttributeBackgroundColor */])) ||
19435
- backgroundColor;
19644
+ function adaptFontColorToBackgroundColor(element, lightModeBackgroundColor, isDarkMode, darkColorHandler) {
19436
19645
  if (!lightModeBackgroundColor || lightModeBackgroundColor === TRANSPARENT) {
19437
19646
  return;
19438
19647
  }
@@ -19443,14 +19652,14 @@ function adaptFontColorToBackgroundColor(element, isDarkMode) {
19443
19652
  lightModeColor: WHITE,
19444
19653
  darkModeColor: GRAY,
19445
19654
  };
19446
- setColor(element, fontForDark, false /*isBackground*/, isDarkMode);
19655
+ setColor(element, fontForDark, false /*isBackground*/, isDarkMode, false /*shouldAdaptFontColor*/, darkColorHandler);
19447
19656
  break;
19448
19657
  case 0 /* BRIGHT */:
19449
19658
  var fontForLight = {
19450
19659
  lightModeColor: BLACK,
19451
19660
  darkModeColor: WHITE,
19452
19661
  };
19453
- setColor(element, fontForLight, false /*isBackground*/, isDarkMode);
19662
+ setColor(element, fontForLight, false /*isBackground*/, isDarkMode, false /*shouldAdaptFontColor*/, darkColorHandler);
19454
19663
  break;
19455
19664
  }
19456
19665
  }
@@ -20812,6 +21021,7 @@ var IndentWhenTab = {
20812
21021
  shouldHandleEvent: shouldHandleIndentationEvent(true),
20813
21022
  handleEvent: handleIndentationEvent(true),
20814
21023
  allowFunctionKeys: true,
21024
+ defaultDisabled: roosterjs_editor_dom_1.Browser.isMac,
20815
21025
  };
20816
21026
  /**
20817
21027
  * OutdentWhenShiftTab edit feature, provides the ability to outdent current list when user press Shift+TAB
@@ -20821,6 +21031,7 @@ var OutdentWhenShiftTab = {
20821
21031
  shouldHandleEvent: shouldHandleIndentationEvent(false),
20822
21032
  handleEvent: handleIndentationEvent(false),
20823
21033
  allowFunctionKeys: true,
21034
+ defaultDisabled: roosterjs_editor_dom_1.Browser.isMac,
20824
21035
  };
20825
21036
  /**
20826
21037
  * MergeInNewLine edit feature, provides the ability to merge current line into a new line when user press
@@ -21118,7 +21329,7 @@ function shouldTriggerList(event, editor, getListStyle, listType) {
21118
21329
  var textBeforeCursor = searcher.getSubStringBefore(4);
21119
21330
  var traverser = editor.getBlockTraverser();
21120
21331
  var text = traverser && traverser.currentBlockElement
21121
- ? traverser.currentBlockElement.getTextContent()
21332
+ ? traverser.currentBlockElement.getTextContent().slice(0, textBeforeCursor.length)
21122
21333
  : null;
21123
21334
  var isATheBeginning = text && text === textBeforeCursor;
21124
21335
  var listChains = getListChains(editor);
@@ -22857,7 +23068,6 @@ var applyChange_1 = __webpack_require__(/*! ./editInfoUtils/applyChange */ "./pa
22857
23068
  var canRegenerateImage_1 = __webpack_require__(/*! ./api/canRegenerateImage */ "./packages/roosterjs-editor-plugins/lib/plugins/ImageEdit/api/canRegenerateImage.ts");
22858
23069
  var DragAndDropHelper_1 = __webpack_require__(/*! ../../pluginUtils/DragAndDropHelper */ "./packages/roosterjs-editor-plugins/lib/pluginUtils/DragAndDropHelper.ts");
22859
23070
  var getGeneratedImageSize_1 = __webpack_require__(/*! ./editInfoUtils/getGeneratedImageSize */ "./packages/roosterjs-editor-plugins/lib/plugins/ImageEdit/editInfoUtils/getGeneratedImageSize.ts");
22860
- var getLastZIndex_1 = __webpack_require__(/*! ./editInfoUtils/getLastZIndex */ "./packages/roosterjs-editor-plugins/lib/plugins/ImageEdit/editInfoUtils/getLastZIndex.ts");
22861
23071
  var Cropper_1 = __webpack_require__(/*! ./imageEditors/Cropper */ "./packages/roosterjs-editor-plugins/lib/plugins/ImageEdit/imageEditors/Cropper.ts");
22862
23072
  var editInfo_1 = __webpack_require__(/*! ./editInfoUtils/editInfo */ "./packages/roosterjs-editor-plugins/lib/plugins/ImageEdit/editInfoUtils/editInfo.ts");
22863
23073
  var Rotator_1 = __webpack_require__(/*! ./imageEditors/Rotator */ "./packages/roosterjs-editor-plugins/lib/plugins/ImageEdit/imageEditors/Rotator.ts");
@@ -22926,14 +23136,11 @@ var ImageEdit = /** @class */ (function () {
22926
23136
  * Remove the temp wrapper of the image
22927
23137
  */
22928
23138
  this.removeWrapper = function () {
22929
- var _a, _b;
22930
- var doc = _this.editor.getDocument();
22931
- if (_this.zoomWrapper && ((_a = doc.body) === null || _a === void 0 ? void 0 : _a.contains(_this.zoomWrapper))) {
22932
- (_b = doc.body) === null || _b === void 0 ? void 0 : _b.removeChild(_this.zoomWrapper);
22933
- _this.toggleImageVisibility(_this.image, true /** showImage */);
23139
+ if (_this.editor.contains(_this.image) && _this.wrapper) {
23140
+ (0, roosterjs_editor_dom_1.unwrap)(_this.image.parentNode);
22934
23141
  }
22935
23142
  _this.wrapper = null;
22936
- _this.zoomWrapper = null;
23143
+ _this.shadowSpan = null;
22937
23144
  };
22938
23145
  /**
22939
23146
  * Update image edit elements to reflect current editing result
@@ -22946,12 +23153,14 @@ var ImageEdit = /** @class */ (function () {
22946
23153
  var cropContainers = getEditElements(wrapper, "r_cropC" /* CropContainer */);
22947
23154
  var cropOverlays = getEditElements(wrapper, "r_cropO" /* CropOverlay */);
22948
23155
  var resizeHandles = getEditElements(wrapper, "r_resizeH" /* ResizeHandle */);
23156
+ var rotateCenter = getEditElements(wrapper, "r_rotateC" /* RotateCenter */)[0];
23157
+ var rotateHandle = getEditElements(wrapper, "r_rotateH" /* RotateHandle */)[0];
22949
23158
  var cropHandles = getEditElements(wrapper, "r_cropH" /* CropHandle */);
22950
23159
  // Cropping and resizing will show different UI, so check if it is cropping here first
22951
- var isCropping = cropContainers.length == 1 && cropOverlays.length == 4;
23160
+ _this.isCropping = cropContainers.length == 1 && cropOverlays.length == 4;
22952
23161
  var _a = _this.editInfo, angleRad = _a.angleRad, bottomPercent = _a.bottomPercent, leftPercent = _a.leftPercent, rightPercent = _a.rightPercent, topPercent = _a.topPercent;
22953
23162
  // Width/height of the image
22954
- var _b = (0, getGeneratedImageSize_1.default)(_this.editInfo, isCropping), targetWidth = _b.targetWidth, targetHeight = _b.targetHeight, originalWidth = _b.originalWidth, originalHeight = _b.originalHeight, visibleWidth = _b.visibleWidth, visibleHeight = _b.visibleHeight;
23163
+ var _b = (0, getGeneratedImageSize_1.default)(_this.editInfo, _this.isCropping), targetWidth = _b.targetWidth, targetHeight = _b.targetHeight, originalWidth = _b.originalWidth, originalHeight = _b.originalHeight, visibleWidth = _b.visibleWidth, visibleHeight = _b.visibleHeight;
22955
23164
  var marginHorizontal = (targetWidth - visibleWidth) / 2;
22956
23165
  var marginVertical = (targetHeight - visibleHeight) / 2;
22957
23166
  var cropLeftPx = originalWidth * leftPercent;
@@ -22959,20 +23168,16 @@ var ImageEdit = /** @class */ (function () {
22959
23168
  var cropTopPx = originalHeight * topPercent;
22960
23169
  var cropBottomPx = originalHeight * bottomPercent;
22961
23170
  // Update size and margin of the wrapper
22962
- wrapper.style.width = getPx(visibleWidth);
22963
- wrapper.style.height = getPx(visibleHeight);
22964
23171
  wrapper.style.margin = marginVertical + "px " + marginHorizontal + "px";
22965
23172
  wrapper.style.transform = "rotate(" + angleRad + "rad)";
22966
- _this.zoomWrapper.style.width = getPx(visibleWidth);
22967
- _this.zoomWrapper.style.height = getPx(visibleHeight);
22968
- fitImageContainer(_this.editor, _this.zoomWrapper, angleRad);
23173
+ setWrapperSizeDimensions(wrapper, _this.image, visibleWidth, visibleHeight);
22969
23174
  // Update the text-alignment to avoid the image to overflow if the parent element have align center or right
22970
23175
  // or if the direction is Right To Left
22971
- wrapper.style.textAlign = isRtl(wrapper.parentNode) ? 'right' : 'left';
23176
+ wrapper.style.textAlign = isRtl(_this.shadowSpan.parentElement) ? 'right' : 'left';
22972
23177
  // Update size of the image
22973
23178
  _this.clonedImage.style.width = getPx(originalWidth);
22974
23179
  _this.clonedImage.style.height = getPx(originalHeight);
22975
- if (isCropping) {
23180
+ if (_this.isCropping) {
22976
23181
  // For crop, we also need to set position of the overlays
22977
23182
  setSize(cropContainers[0], cropLeftPx, cropTopPx, cropRightPx, cropBottomPx, undefined, undefined);
22978
23183
  setSize(cropOverlays[0], 0, 0, cropRightPx, undefined, undefined, cropTopPx);
@@ -22992,6 +23197,10 @@ var ImageEdit = /** @class */ (function () {
22992
23197
  (0, Resizer_1.doubleCheckResize)(_this.editInfo, _this.options.preserveRatio, clientWidth, clientHeight);
22993
23198
  _this.updateWrapper();
22994
23199
  }
23200
+ var viewport = _this.editor.getVisibleViewport();
23201
+ if (rotateHandle && rotateCenter && viewport) {
23202
+ (0, Rotator_1.updateRotateHandlePosition)(_this.editInfo, _this.editor.getVisibleViewport(), marginVertical, rotateCenter, rotateHandle);
23203
+ }
22995
23204
  updateHandleCursor(resizeHandles, angleRad);
22996
23205
  }
22997
23206
  }
@@ -23014,8 +23223,16 @@ var ImageEdit = /** @class */ (function () {
23014
23223
  * @param editor Editor instance
23015
23224
  */
23016
23225
  ImageEdit.prototype.initialize = function (editor) {
23226
+ var _this = this;
23017
23227
  this.editor = editor;
23018
- this.disposer = editor.addDomEventHandler('blur', this.onBlur);
23228
+ this.disposer = editor.addDomEventHandler({
23229
+ blur: function () { return _this.onBlur(); },
23230
+ dragstart: function (e) {
23231
+ if (_this.image) {
23232
+ e.preventDefault();
23233
+ }
23234
+ },
23235
+ });
23019
23236
  };
23020
23237
  /**
23021
23238
  * Dispose this plugin
@@ -23039,7 +23256,14 @@ var ImageEdit = /** @class */ (function () {
23039
23256
  }
23040
23257
  break;
23041
23258
  case 5 /* MouseDown */:
23042
- this.setEditingImage(null);
23259
+ // When left click in a image that already in editing mode, do not quit edit mode
23260
+ var mouseTarget = e.rawEvent.target;
23261
+ var button = e.rawEvent.button;
23262
+ if (this.shadowSpan !== mouseTarget ||
23263
+ (this.shadowSpan === mouseTarget && button !== 0) ||
23264
+ this.isCropping) {
23265
+ this.setEditingImage(null);
23266
+ }
23043
23267
  break;
23044
23268
  case 0 /* KeyDown */:
23045
23269
  this.setEditingImage(null);
@@ -23054,8 +23278,8 @@ var ImageEdit = /** @class */ (function () {
23054
23278
  (0, editInfo_1.deleteEditInfo)(img);
23055
23279
  });
23056
23280
  break;
23057
- case 14 /* Scroll */:
23058
- this.setEditingImage(null);
23281
+ case 12 /* BeforeDispose */:
23282
+ this.removeWrapper();
23059
23283
  break;
23060
23284
  }
23061
23285
  };
@@ -23086,6 +23310,7 @@ var ImageEdit = /** @class */ (function () {
23086
23310
  this.editInfo = null;
23087
23311
  this.lastSrc = null;
23088
23312
  this.clonedImage = null;
23313
+ this.isCropping = false;
23089
23314
  }
23090
23315
  if (!this.image && (image === null || image === void 0 ? void 0 : image.isContentEditable)) {
23091
23316
  // If there is new image to edit, enter editing mode for this image
@@ -23104,7 +23329,6 @@ var ImageEdit = /** @class */ (function () {
23104
23329
  // Init drag and drop
23105
23330
  this.dndHelpers = __spreadArray(__spreadArray(__spreadArray(__spreadArray([], this.createDndHelpers("r_resizeH" /* ResizeHandle */, Resizer_1.Resizer), true), this.createDndHelpers("r_rotateH" /* RotateHandle */, Rotator_1.Rotator), true), this.createDndHelpers("r_cropH" /* CropHandle */, Cropper_1.Cropper), true), this.createDndHelpers("r_cropC" /* CropContainer */, Cropper_1.Cropper), true);
23106
23331
  this.editor.select(this.image);
23107
- this.toggleImageVisibility(this.image, false /** showImage */);
23108
23332
  }
23109
23333
  };
23110
23334
  /**
@@ -23117,18 +23341,12 @@ var ImageEdit = /** @class */ (function () {
23117
23341
  this.clonedImage.removeAttribute('id');
23118
23342
  this.wrapper = (0, roosterjs_editor_dom_1.createElement)(6 /* ImageEditWrapper */, this.image.ownerDocument);
23119
23343
  this.wrapper.firstChild.appendChild(this.clonedImage);
23120
- // keep the same vertical align
23121
- var originalVerticalAlign = getStylePropertyValue(this.image, 'vertical-align');
23122
- if (originalVerticalAlign) {
23123
- this.wrapper.style.verticalAlign = originalVerticalAlign;
23124
- }
23125
23344
  this.wrapper.style.display = roosterjs_editor_dom_1.Browser.isSafari ? 'inline-block' : 'inline-flex';
23126
23345
  // Cache current src so that we can compare it after edit see if src is changed
23127
23346
  this.lastSrc = this.image.getAttribute('src');
23128
23347
  // Set image src to original src to help show editing UI, also it will be used when regenerate image dataURL after editing
23129
23348
  this.clonedImage.src = this.editInfo.src;
23130
23349
  this.clonedImage.style.position = 'absolute';
23131
- this.clonedImage.style.maxWidth = null;
23132
23350
  // Get HTML for all edit elements (resize handle, rotate handle, crop handle and overlay, ...) and create HTML element
23133
23351
  var options = {
23134
23352
  borderColor: getColorString(this.options.borderColor, this.editor.isDarkMode()),
@@ -23150,24 +23368,15 @@ var ImageEdit = /** @class */ (function () {
23150
23368
  _this.wrapper.appendChild(element);
23151
23369
  }
23152
23370
  });
23153
- this.insertImageWrapper(this.editor, this.image, this.wrapper, this.editor.getZoomScale());
23154
- };
23155
- ImageEdit.prototype.toggleImageVisibility = function (image, showImage) {
23156
- var editorId = this.editor.getEditorDomAttribute('id');
23157
- var doc = this.editor.getDocument();
23158
- var editingId = 'editingId' + editorId;
23159
- if (showImage) {
23160
- (0, roosterjs_editor_dom_1.removeGlobalCssStyle)(doc, editingId);
23161
- }
23162
- else {
23163
- var cssRule = "#" + editorId + " #" + image.id + " {visibility: hidden}";
23164
- (0, roosterjs_editor_dom_1.setGlobalCssStyles)(doc, cssRule, editingId);
23165
- }
23371
+ this.insertImageWrapper(this.wrapper);
23166
23372
  };
23167
- ImageEdit.prototype.insertImageWrapper = function (editor, image, wrapper, scale) {
23168
- this.zoomWrapper = copyElementRect(image, createZoomWrapper(editor, wrapper, scale));
23169
- this.zoomWrapper.style.zIndex = "" + ((0, getLastZIndex_1.default)(editor.getScrollContainer()) + 1);
23170
- this.editor.getDocument().body.appendChild(this.zoomWrapper);
23373
+ ImageEdit.prototype.insertImageWrapper = function (wrapper) {
23374
+ this.shadowSpan = (0, roosterjs_editor_dom_1.wrap)(this.image, 'span');
23375
+ var shadowRoot = this.shadowSpan.attachShadow({
23376
+ mode: 'open',
23377
+ });
23378
+ this.shadowSpan.style.verticalAlign = 'bottom';
23379
+ shadowRoot.appendChild(wrapper);
23171
23380
  };
23172
23381
  /**
23173
23382
  * Create drag and drop helpers
@@ -23208,6 +23417,17 @@ function setSize(element, left, top, right, bottom, width, height) {
23208
23417
  element.style.width = getPx(width);
23209
23418
  element.style.height = getPx(height);
23210
23419
  }
23420
+ function setWrapperSizeDimensions(wrapper, image, width, height) {
23421
+ var hasBorder = image.style.borderStyle;
23422
+ if (hasBorder) {
23423
+ var borderWidth = image.style.borderWidth ? 2 * parseInt(image.style.borderWidth) : 2;
23424
+ wrapper.style.width = getPx(width + borderWidth);
23425
+ wrapper.style.height = getPx(height + borderWidth);
23426
+ return;
23427
+ }
23428
+ wrapper.style.width = getPx(width);
23429
+ wrapper.style.height = getPx(height);
23430
+ }
23211
23431
  function getPx(value) {
23212
23432
  return value === undefined ? null : value + 'px';
23213
23433
  }
@@ -23273,40 +23493,6 @@ function getColorString(color, isDarkMode) {
23273
23493
  }
23274
23494
  return isDarkMode ? color.darkModeColor.trim() : color.lightModeColor.trim();
23275
23495
  }
23276
- function fitImageContainer(editor, zoomWrapper, angle) {
23277
- var _a, _b;
23278
- var angleIndex = handleRadIndexCalculator(angle);
23279
- var isVertical = (angleIndex >= 2 && angleIndex < 4) || angleIndex >= 6;
23280
- var editorTop = (_b = (_a = editor.getScrollContainer()) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect()) === null || _b === void 0 ? void 0 : _b.top;
23281
- var _c = zoomWrapper === null || zoomWrapper === void 0 ? void 0 : zoomWrapper.getBoundingClientRect(), top = _c.top, width = _c.width, height = _c.height;
23282
- if (editorTop > top) {
23283
- var rotatePercent = 100 * Math.abs(angle);
23284
- var zoomWrapperHeight = editorTop - top;
23285
- var zoomWrapperHeightPercent = isVertical
23286
- ? rotatePercent * (zoomWrapperHeight / width)
23287
- : 100 * (zoomWrapperHeight / height);
23288
- zoomWrapper.style.clipPath = "polygon(0 " + zoomWrapperHeightPercent + "%, 100% " + zoomWrapperHeightPercent + "%, 100% " + (isVertical ? rotatePercent : '100') + "%, 0 " + (isVertical ? rotatePercent : '100') + "%)";
23289
- }
23290
- }
23291
- function copyElementRect(originalElement, element) {
23292
- var _a = originalElement.getBoundingClientRect(), top = _a.top, left = _a.left, right = _a.right, bottom = _a.bottom;
23293
- element.style.top = top + "px";
23294
- element.style.bottom = bottom + "px";
23295
- element.style.right = right + "px";
23296
- element.style.left = left + "px";
23297
- return element;
23298
- }
23299
- function createZoomWrapper(editor, wrapper, scale) {
23300
- var zoomWrapper = editor.getDocument().createElement('div');
23301
- zoomWrapper.style.transform = "scale(" + (scale || 1) + ")";
23302
- zoomWrapper.style.transformOrigin = 'top left';
23303
- zoomWrapper.style.position = 'fixed';
23304
- zoomWrapper.appendChild(wrapper);
23305
- return zoomWrapper;
23306
- }
23307
- function getStylePropertyValue(element, property) {
23308
- return element.ownerDocument.defaultView.getComputedStyle(element).getPropertyValue(property);
23309
- }
23310
23496
 
23311
23497
 
23312
23498
  /***/ }),
@@ -23533,7 +23719,6 @@ function applyChange(editor, image, editInfo, previousSrc, wasResized, editingIm
23533
23719
  var _a = (0, getGeneratedImageSize_1.default)(editInfo), targetWidth = _a.targetWidth, targetHeight = _a.targetHeight;
23534
23720
  image.src = newSrc;
23535
23721
  if (wasResized || state == 3 /* FullyChanged */) {
23536
- image.style.maxWidth = 'initial';
23537
23722
  image.width = targetWidth;
23538
23723
  image.height = targetHeight;
23539
23724
  image.style.width = targetWidth + 'px';
@@ -23765,40 +23950,6 @@ function getGeneratedImageSize(editInfo, beforeCrop) {
23765
23950
  exports.default = getGeneratedImageSize;
23766
23951
 
23767
23952
 
23768
- /***/ }),
23769
-
23770
- /***/ "./packages/roosterjs-editor-plugins/lib/plugins/ImageEdit/editInfoUtils/getLastZIndex.ts":
23771
- /*!************************************************************************************************!*\
23772
- !*** ./packages/roosterjs-editor-plugins/lib/plugins/ImageEdit/editInfoUtils/getLastZIndex.ts ***!
23773
- \************************************************************************************************/
23774
- /*! no static exports found */
23775
- /***/ (function(module, exports, __webpack_require__) {
23776
-
23777
- "use strict";
23778
-
23779
- Object.defineProperty(exports, "__esModule", { value: true });
23780
- var roosterjs_editor_dom_1 = __webpack_require__(/*! roosterjs-editor-dom */ "./packages/roosterjs-editor-dom/lib/index.ts");
23781
- /**
23782
- * @internal
23783
- * Search through from editor div to it's root for the latest z-index value
23784
- * @param editorDiv the editor div element
23785
- * @returns the z index value
23786
- */
23787
- function getLatestZIndex(editorDiv) {
23788
- var child = editorDiv;
23789
- var zIndex = 0;
23790
- while (child && (0, roosterjs_editor_dom_1.getTagOfNode)(child) !== 'BODY') {
23791
- var childZIndex = parseInt(child.style.zIndex || getComputedStyle(child).zIndex, 10);
23792
- if (childZIndex) {
23793
- zIndex = Math.max(zIndex, childZIndex);
23794
- }
23795
- child = child.parentElement;
23796
- }
23797
- return zIndex;
23798
- }
23799
- exports.default = getLatestZIndex;
23800
-
23801
-
23802
23953
  /***/ }),
23803
23954
 
23804
23955
  /***/ "./packages/roosterjs-editor-plugins/lib/plugins/ImageEdit/editInfoUtils/getTargetSizeByPercentage.ts":
@@ -24196,7 +24347,7 @@ var __assign = (this && this.__assign) || function () {
24196
24347
  return __assign.apply(this, arguments);
24197
24348
  };
24198
24349
  Object.defineProperty(exports, "__esModule", { value: true });
24199
- exports.getRotateHTML = exports.Rotator = void 0;
24350
+ exports.getRotateHTML = exports.updateRotateHandlePosition = exports.Rotator = void 0;
24200
24351
  var ROTATE_SIZE = 32;
24201
24352
  var ROTATE_GAP = 15;
24202
24353
  var DEG_PER_RAD = 180 / Math.PI;
@@ -24231,6 +24382,28 @@ exports.Rotator = {
24231
24382
  }
24232
24383
  },
24233
24384
  };
24385
+ /**
24386
+ * @internal
24387
+ * Move rotate handle. When image is very close to the border of editor, rotate handle may not be visible.
24388
+ * Fix it by reduce the distance from image to rotate handle
24389
+ */
24390
+ function updateRotateHandlePosition(editInfo, editorRect, marginVertical, rotateCenter, rotateHandle) {
24391
+ var rotateHandleRect = rotateHandle.getBoundingClientRect();
24392
+ if (rotateHandleRect) {
24393
+ var top_1 = rotateHandleRect.top - (editorRect === null || editorRect === void 0 ? void 0 : editorRect.top);
24394
+ var angleRad = editInfo.angleRad, heightPx = editInfo.heightPx;
24395
+ var cosAngle = Math.cos(angleRad);
24396
+ var adjustedDistance = cosAngle <= 0
24397
+ ? Number.MAX_SAFE_INTEGER
24398
+ : (top_1 + heightPx / 2 + marginVertical) / cosAngle - heightPx / 2;
24399
+ var rotateGap = Math.max(Math.min(ROTATE_GAP, adjustedDistance), 0);
24400
+ var rotateTop = Math.max(Math.min(ROTATE_SIZE, adjustedDistance - rotateGap), 0);
24401
+ rotateCenter.style.top = -rotateGap + 'px';
24402
+ rotateCenter.style.height = rotateGap + 'px';
24403
+ rotateHandle.style.top = -rotateTop + 'px';
24404
+ }
24405
+ }
24406
+ exports.updateRotateHandlePosition = updateRotateHandlePosition;
24234
24407
  /**
24235
24408
  * @internal
24236
24409
  * Get HTML for rotate elements, including the rotate handle with icon, and a line between the handle and the image
@@ -28546,7 +28719,7 @@ var Watermark = /** @class */ (function () {
28546
28719
  this.removeWatermark(wrapper);
28547
28720
  }
28548
28721
  else if (event.operation == 0 /* NewEntity */) {
28549
- (0, roosterjs_editor_dom_1.applyFormat)(wrapper, this.format, this.editor.isDarkMode());
28722
+ (0, roosterjs_editor_dom_1.applyFormat)(wrapper, this.format, this.editor.isDarkMode(), this.editor.getDarkColorHandler());
28550
28723
  wrapper.spellcheck = false;
28551
28724
  }
28552
28725
  }
@@ -29595,6 +29768,12 @@ var CompatibleExperimentalFeatures;
29595
29768
  * the block element (In most case, the DIV element) so keep the block element clean.
29596
29769
  */
29597
29770
  CompatibleExperimentalFeatures["DefaultFormatInSpan"] = "DefaultFormatInSpan";
29771
+ /**
29772
+ * Use variable-based dark mode solution rather than dataset-based solution.
29773
+ * When enable this feature, need to pass in a DarkModelHandler object to each call of setColor and applyFormat
29774
+ * if you need them work for dark mode
29775
+ */
29776
+ CompatibleExperimentalFeatures["VariableBasedDarkColor"] = "VariableBasedDarkColor";
29598
29777
  })(CompatibleExperimentalFeatures = exports.CompatibleExperimentalFeatures || (exports.CompatibleExperimentalFeatures = {}));
29599
29778
 
29600
29779