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