suneditor 3.0.0-beta.13 → 3.0.0-beta.15

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "suneditor",
3
- "version": "3.0.0-beta.13",
3
+ "version": "3.0.0-beta.15",
4
4
  "description": "Vanilla javascript based WYSIWYG web editor",
5
5
  "author": "Yi JiHong",
6
6
  "license": "MIT",
@@ -81,11 +81,11 @@
81
81
  },
82
82
  "browserslist": [
83
83
  "chrome >= 80",
84
+ "firefox >= 90",
85
+ "safari >= 14.1",
84
86
  "edge >= 80",
85
- "firefox >= 74",
86
- "safari >= 13.1",
87
- "ios >= 13.4",
88
87
  "opera >= 67",
88
+ "ios >= 14.5",
89
89
  "android >= 80",
90
90
  "samsung >= 13",
91
91
  "not IE <= 11",
@@ -121,4 +121,4 @@
121
121
  "web editor",
122
122
  "browser editor"
123
123
  ]
124
- }
124
+ }
@@ -25,7 +25,7 @@ export default {
25
25
  print: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M18,3H6V7H18M19,12A1,1 0 0,1 18,11A1,1 0 0,1 19,10A1,1 0 0,1 20,11A1,1 0 0,1 19,12M16,19H8V14H16M19,8H5A3,3 0 0,0 2,11V17H6V21H18V17H22V11A3,3 0 0,0 19,8Z" /></svg>',
26
26
  template:
27
27
  '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M13,7.5H18V9.5H13V7.5M13,14.5H18V16.5H13V14.5M19,3A2,2 0 0,1 21,5V19A2,2 0 0,1 19,21H5A2,2 0 0,1 3,19V5A2,2 0 0,1 5,3H19M19,19V5H5V19H19M11,6V11H6V6H11M10,10V7H7V10H10M11,13V18H6V13H11M10,17V14H7V17H10Z" /></svg>',
28
- layout: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 21H11V13H3M5 15H9V19H5M3 11H11V3H3M5 5H9V9H5M13 3V11H21V3M19 9H15V5H19M18 16H21V18H18V21H16V18H13V16H16V13H18Z" /></svg>',
28
+ layout: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M21 13H3A1 1 0 0 0 2 14V20A1 1 0 0 0 3 21H21A1 1 0 0 0 22 20V14A1 1 0 0 0 21 13M20 19H4V15H20M21 3H3A1 1 0 0 0 2 4V10A1 1 0 0 0 3 11H21A1 1 0 0 0 22 10V4A1 1 0 0 0 21 3M20 9H4V5H20Z" /></svg>',
29
29
  new_document: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M14,2H6A2,2 0 0,0 4,4V20A2,2 0 0,0 6,22H18A2,2 0 0,0 20,20V8L14,2M18,20H6V4H13V9H18V20Z" /></svg>',
30
30
  select_all:
31
31
  '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9,9H15V15H9M7,17H17V7H7M15,5H17V3H15M15,21H17V19H15M19,17H21V15H19M19,9H21V7H19M19,21A2,2 0 0,0 21,19H19M19,13H21V11H19M11,21H13V19H11M9,3H7V5H9M3,17H5V15H3M5,21V19H3A2,2 0 0,0 5,21M19,3V5H21A2,2 0 0,0 19,3M13,3H11V5H13M3,9H5V7H3M7,21H9V19H7M3,13H5V11H3M3,5H5V3A2,2 0 0,0 3,5Z" /></svg>',
@@ -202,6 +202,9 @@ export default {
202
202
  alert_outline:
203
203
  '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M11,15H13V17H11V15M11,7H13V13H11V7M12,2C6.47,2 2,6.5 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2M12,20A8,8 0 0,1 4,12A8,8 0 0,1 12,4A8,8 0 0,1 20,12A8,8 0 0,1 12,20Z" /></svg>',
204
204
  // More icons
205
+ more_media:
206
+ '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9 13V5C9 3.9 9.9 3 11 3H20C21.1 3 22 3.9 22 5V11H18.57L17.29 9.26C17.23 9.17 17.11 9.17 17.05 9.26L15.06 12C15 12.06 14.88 12.07 14.82 12L13.39 10.25C13.33 10.18 13.22 10.18 13.16 10.25L11.05 12.91C10.97 13 11.04 13.15 11.16 13.15H17.5V15H11C9.89 15 9 14.11 9 13M6 22V21H4V22H2V2H4V3H6V2H8.39C7.54 2.74 7 3.8 7 5V13C7 15.21 8.79 17 11 17H15.7C14.67 17.83 14 19.08 14 20.5C14 21.03 14.11 21.53 14.28 22H6M4 7H6V5H4V7M4 11H6V9H4V11M4 15H6V13H4V15M6 19V17H4V19H6M23 13V15H21V20.5C21 21.88 19.88 23 18.5 23S16 21.88 16 20.5 17.12 18 18.5 18C18.86 18 19.19 18.07 19.5 18.21V13H23Z" /></svg>',
207
+ more_view: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 21H11V13H3M5 15H9V19H5M3 11H11V3H3M5 5H9V9H5M13 3V11H21V3M19 9H15V5H19M18 16H21V18H18V21H16V18H13V16H16V13H18Z" /></svg>',
205
208
  more_text:
206
209
  '<svg class="se-ci" xmlns="http://www.w3.org/2000/svg" viewBox="10 10 180 180"><g><path d="M49.711,142.188h49.027c2.328,0.002,4.394,1.492,5.129,3.699l9.742,29.252c0.363,1.092,1.385,1.828,2.537,1.83l15.883,0.01c0.859,0,1.667-0.412,2.17-1.109s0.641-1.594,0.37-2.41l-16.625-50.045L86.503,28.953c-0.36-1.097-1.383-1.839-2.537-1.842H64.532c-1.153-0.001-2.178,0.736-2.542,1.831L13.847,173.457c-0.271,0.816-0.135,1.713,0.369,2.412c0.503,0.697,1.311,1.109,2.171,1.109h15.872c1.151,0,2.173-0.736,2.537-1.828l9.793-29.287C45.325,143.66,47.39,142.18,49.711,142.188L49.711,142.188z M53.493,119.098l15.607-46.9c0.744-2.196,2.806-3.674,5.125-3.674s4.381,1.478,5.125,3.674l15.607,46.904c0.537,1.621,0.263,3.402-0.736,4.789c-1.018,1.408-2.649,2.24-4.386,2.24H58.615c-1.736,0-3.368-0.832-4.386-2.24C53.23,122.504,52.956,120.721,53.493,119.098L53.493,119.098z M190.465,63.32c0-2.919-1.015-5.396-3.059-7.428c-2.029-2.031-4.496-3.047-7.383-3.047c-2.889,0-5.355,1.016-7.388,3.047c-2.029,2.032-3.056,4.498-3.056,7.386c0,2.889,1.026,5.354,3.056,7.385c2.032,2.032,4.499,3.059,7.388,3.059c2.887,0,5.354-1.026,7.383-3.059C189.45,68.633,190.465,66.178,190.465,63.32L190.465,63.32z M190.465,101.994c0-2.858-1.015-5.313-3.059-7.333c-2.029-2.042-4.496-3.047-7.383-3.047c-2.889,0-5.355,1.005-7.388,3.047c-2.029,2.021-3.056,4.486-3.056,7.376c0,2.887,1.026,5.352,3.056,7.395c2.032,2.021,4.499,3.047,7.388,3.047c2.887,0,5.354-1.025,7.383-3.047C189.45,107.389,190.465,104.914,190.465,101.994L190.465,101.994z M190.465,140.76c0-2.918-1.015-5.395-3.059-7.438c-2.029-2.041-4.496-3.047-7.383-3.047c-2.889,0-5.355,1.006-7.388,3.047c-2.029,2.043-3.056,4.52-3.056,7.438c0,2.922,1.026,5.398,3.056,7.439c2.032,2.021,4.499,3.047,7.388,3.047c2.887,0,5.354-1.025,7.383-3.047C189.45,146.158,190.465,143.682,190.465,140.76L190.465,140.76z"/></g></svg>',
207
210
  more_paragraph:
@@ -20,9 +20,7 @@
20
20
  line-height: var(--se-edit-line-height);
21
21
  }
22
22
 
23
- .sun-editor-editable.se-type-document-editable {
24
- padding: var(--se-edit-inner-padding-doc-type);
25
- }
23
+ .sun-editor .se-wrapper .se-wrapper-inner.sun-editor-editable.se-type-document-editable,
26
24
  .sun-editor-editable.se-document-page-mirror-a4 {
27
25
  width: 21cm;
28
26
  padding: var(--se-edit-inner-padding-doc-type);
@@ -366,6 +366,7 @@
366
366
  outline: 1px solid var(--se-active-dark5-color) !important;
367
367
  box-shadow: 0 0 0 0.3rem var(--se-active-light5-color);
368
368
  transition: box-shadow 0.1s ease-in-out;
369
+ z-index: 1;
369
370
  }
370
371
 
371
372
  /** --- primary button */
@@ -760,7 +761,7 @@
760
761
  top: -12px;
761
762
  content: ' ';
762
763
  border-bottom-width: 0;
763
- border-top-color: var(--se-main-background-color);
764
+ border-top-color: var(--se-controller-background-color);
764
765
  }
765
766
 
766
767
  /** --- container */
@@ -1652,6 +1653,9 @@
1652
1653
  .sun-editor .se-wrapper .se-code-wrapper {
1653
1654
  width: 100%;
1654
1655
  }
1656
+ .sun-editor .se-wrapper .se-code-wrapper > div {
1657
+ width: 100%;
1658
+ }
1655
1659
  .sun-editor .se-wrapper .se-code-wrapper .se-code-view-line {
1656
1660
  background-color: var(--se-code-view-color);
1657
1661
  color: var(--se-code-view-background-color);
@@ -986,11 +986,11 @@ EventManager.prototype = {
986
986
  * @this {EventManagerThis}
987
987
  * @description Adjusts the position of the editor's toolbar, controllers, and other floating elements based on scroll position.
988
988
  * - Ensures UI elements maintain their intended relative positions when scrolling.
989
- * @param {Element} eventWysiwyg The wysiwyg event object containing scroll data
989
+ * @param {*} eventWysiwyg The wysiwyg event object containing scroll data (Window or element)
990
990
  */
991
991
  _moveContainer(eventWysiwyg) {
992
- const y = eventWysiwyg.scrollTop || 0;
993
- const x = eventWysiwyg.scrollLeft || 0;
992
+ const y = eventWysiwyg.scrollTop || eventWysiwyg.scrollY || 0;
993
+ const x = eventWysiwyg.scrollLeft || eventWysiwyg.scrollX || 0;
994
994
 
995
995
  if (this.editor.isBalloon) {
996
996
  this.context.get('toolbar.main').style.top = this.toolbar._balloonOffset.top - y + 'px';
@@ -1270,7 +1270,7 @@ EventManager.prototype = {
1270
1270
  /**
1271
1271
  * @this {EventManagerThis}
1272
1272
  * @param {__se__FrameContext} frameContext - frame context object
1273
- * @param {Element} eventWysiwyg - wysiwyg event object
1273
+ * @param {Element|Window} eventWysiwyg - wysiwyg event object
1274
1274
  * @param {Event} e - Event object
1275
1275
  */
1276
1276
  function OnScroll_wysiwyg(frameContext, eventWysiwyg, e) {
@@ -496,7 +496,7 @@ Component.prototype = {
496
496
  componentTop = top;
497
497
  w = target.offsetWidth / 2 / 2;
498
498
 
499
- t_style.top = componentTop - scrollY - cH / 2 + 'px';
499
+ t_style.top = componentTop - cH / 2 + 'px';
500
500
  t_style[dir[0]] = (isNonSelected ? sideOffset - cW / 2 : sideOffset + w) + 'px';
501
501
  t_style[dir[1]] = '';
502
502
 
@@ -522,7 +522,7 @@ Component.prototype = {
522
522
  w = target.offsetWidth / 2 / 2;
523
523
  }
524
524
 
525
- b_style.top = componentTop + target.offsetHeight - scrollY - cH / 2 + 'px';
525
+ b_style.top = componentTop + target.offsetHeight - cH / 2 + 'px';
526
526
  b_style[dir[0]] = sideOffset + target.offsetWidth - (isNonSelected ? 0 : w) - (isNonSelected ? cW / 2 : cW) + 'px';
527
527
  b_style[dir[1]] = '';
528
528
 
@@ -1091,6 +1091,7 @@ HTML.prototype = {
1091
1091
  * @param {number|Array<number>} [options.rootKey=null] Root index
1092
1092
  */
1093
1093
  set(html, { rootKey } = {}) {
1094
+ this.ui._offCurrentController();
1094
1095
  this.selection.removeRange();
1095
1096
  const convertValue = html === null || html === undefined ? '' : this.clean(html, { forceFormat: true, whitelist: null, blacklist: null });
1096
1097
 
@@ -1119,6 +1120,8 @@ HTML.prototype = {
1119
1120
  * @param {number|Array<number>} [options.rootKey=null] Root index
1120
1121
  */
1121
1122
  add(html, { rootKey } = {}) {
1123
+ this.ui._offCurrentController();
1124
+
1122
1125
  if (!rootKey) rootKey = [this.status.rootKey];
1123
1126
  else if (!Array.isArray(rootKey)) rootKey = [rootKey];
1124
1127
 
@@ -149,8 +149,8 @@ Offset.prototype = {
149
149
  left: offsetLeft,
150
150
  top: offsetTop,
151
151
  right: offsetElement?.offsetWidth ? offsetElement.offsetWidth - (offsetLeft - l + targetWidth) + r : 0,
152
- scrollX: eventWysiwyg.scrollX || eventWysiwyg.scrollLeft || 0,
153
- scrollY: eventWysiwyg.scrollY || eventWysiwyg.scrollTop || 0
152
+ scrollX: eventWysiwyg.scrollLeft || eventWysiwyg.scrollX || 0,
153
+ scrollY: eventWysiwyg.scrollTop || eventWysiwyg.scrollY || 0
154
154
  };
155
155
  },
156
156
 
@@ -349,13 +349,13 @@ Offset.prototype = {
349
349
  getWWScroll() {
350
350
  const eventWysiwyg = this.editor.frameContext.get('wysiwyg');
351
351
  const rects = this.selection.getRects(eventWysiwyg, 'start').rects;
352
- const top = eventWysiwyg.scrollY || eventWysiwyg.scrollTop || 0;
353
- const height = eventWysiwyg.scrollHeight || 0;
352
+ const top = eventWysiwyg.scrollTop || eventWysiwyg.scrollY || 0;
353
+ const height = eventWysiwyg.scrollHeight || eventWysiwyg.document?.documentElement.scrollHeight || 0;
354
354
 
355
355
  return {
356
356
  top,
357
- left: eventWysiwyg.scrollX || eventWysiwyg.scrollLeft || 0,
358
- width: eventWysiwyg.scrollWidth || 0,
357
+ left: eventWysiwyg.scrollLeft || eventWysiwyg.scrollX || 0,
358
+ width: eventWysiwyg.scrollWidth || eventWysiwyg.document?.documentElement.scrollWidth || 0,
359
359
  height,
360
360
  bottom: top + height,
361
361
  rects
@@ -453,6 +453,7 @@ Offset.prototype = {
453
453
  * @param {{left:number, top:number}} [params.addOffset={left:0, top:0}] Additional offset
454
454
  * @param {"bottom"|"top"} [params.position="bottom"] Position ('bottom'|'top')
455
455
  * @param {*} params.inst Instance object of caller
456
+ * @param {HTMLElement} [params.sibling] The sibling controller element
456
457
  * @returns {{position: "top" | "bottom"} | undefined} Success -> {position: current position}
457
458
  */
458
459
  setAbsPosition(element, target, params) {
@@ -469,55 +470,76 @@ Offset.prototype = {
469
470
  addOffset.left *= -1;
470
471
  }
471
472
 
472
- const isWWTarget = this.editor.frameContext.get('wrapper').contains(target) || params.isWWTarget;
473
- const isCtrlTarget = getParentElement(target, '.se-controller');
473
+ const isIframe = this.editor.frameOptions.get('iframe');
474
+ const isWWTarget = this.editor.frameContext.get('wrapper').contains(target) || params.isWWTarget || (isIframe ? this.editor.frameContext.get('wysiwyg').contains(target) : false);
475
+
476
+ const isCtrlTarget = target.nodeType === 1;
474
477
  const isTargetAbs = isWWTarget && !isCtrlTarget;
478
+ const isInlineTarget = isCtrlTarget && /inline/.test(_w.getComputedStyle(target).display);
475
479
  const clientSize = getClientSize(_d);
476
480
  const wwScroll = isTargetAbs ? this.getWWScroll() : this._getWindowScroll();
477
- const targetRect = isCtrlTarget ? target.getBoundingClientRect() : this.selection.getRects(target, 'start').rects;
481
+ const targetRect = !isIframe && isCtrlTarget ? target.getBoundingClientRect() : this.selection.getRects(target, 'start').rects;
478
482
  const targetOffset = this.getGlobal(target);
479
483
  const arrow = /** @type {HTMLElement} */ (hasClass(element.firstElementChild, 'se-arrow') ? element.firstElementChild : null);
480
484
 
481
485
  // top ----------------------------------------------------------------------------------------------------
486
+ const siblingH = params.sibling?.offsetHeight || 0;
482
487
  const ah = arrow ? arrow.offsetHeight : 0;
483
488
  const elH = element.offsetHeight;
484
489
  const targetH = target.offsetHeight;
485
490
  // margin
486
491
  const tmtw = targetRect.top;
487
492
  const tmbw = clientSize.h - targetRect.bottom;
488
- const toolbarH = !this.editor.toolbar._sticky && (this.editor.isBalloon || this.editor.isInline) ? 0 : this.context.get('toolbar.main').offsetHeight;
493
+ const globalTop = this.getGlobal(this.editor.frameContext.get('topArea')).top;
494
+ const wScrollY = _w.scrollY;
495
+ const th = this.context.get('toolbar.main').offsetHeight;
496
+ const containerToolbar = this.options.get('toolbar_container');
497
+ const headLess = this.editor.isBalloon || this.editor.isInline || containerToolbar;
498
+ const toolbarH = (containerToolbar && globalTop - wScrollY - th > 0) || (!this.editor.toolbar._sticky && headLess) ? 0 : th;
489
499
 
490
500
  // check margin
491
- const { rmt, rmb, rt } = this._getVMargin(tmtw, tmbw, toolbarH, clientSize, targetRect, isTargetAbs, wwScroll);
492
- if (isWWTarget && (rmb + targetH <= 0 || rmt + rt + targetH <= 0)) return;
501
+ const { rmt, rmb, bMargin, rt } = this._getVMargin(tmtw, tmbw, toolbarH, clientSize, targetRect, isTargetAbs, wwScroll);
502
+ if (isWWTarget && ((rmb > 0 ? bMargin : rmb) + targetH <= 0 || rmt + rt + targetH - (this.editor.toolbar._sticky && isInlineTarget ? toolbarH : 0) <= 0)) return;
493
503
 
504
+ const isSticky = this.editor.toolbar._sticky && this.context.get('toolbar.main').style.display !== 'none' && (!headLess || this.editor.frameContext.get('topArea').getBoundingClientRect().top <= th);
505
+ const statusBarH = this.editor.frameContext.get('statusbar')?.offsetHeight || 0;
494
506
  let t = addOffset.top;
495
507
  let y = 0;
496
508
  let arrowDir = '';
509
+
510
+ // [bottom] position
497
511
  if (position === 'bottom') {
512
+ let trmt = rmt - (isSticky && globalTop - wScrollY <= toolbarH ? toolbarH : 0);
513
+ if (isSticky && trmt + toolbarH < 0) trmt += toolbarH;
498
514
  arrowDir = 'up';
499
- t += targetRect.bottom + ah + _w.scrollY;
500
- y = rmb - (elH + ah);
501
- if (y < 0) {
515
+ t += targetRect.bottom + ah + wScrollY;
516
+ y = rmb - (elH + ah) - statusBarH;
517
+ // change to <top> position
518
+ if (y - siblingH < 0) {
502
519
  arrowDir = 'down';
503
520
  t -= targetH + elH + ah * 2;
504
- y = toolbarH + rmt - (elH + ah);
505
- if (y < 0) {
521
+ y = trmt - (elH + ah);
522
+ // sticky the <top> position
523
+ if (y - siblingH < 0) {
506
524
  arrowDir = '';
507
- t -= y;
525
+ t -= y - siblingH - Math.max(1, y + elH + ah) + (!isSticky && trmt < 0 ? toolbarH : 0) - (isSticky ? this.context.get('toolbar.main').offsetTop : 0);
508
526
  }
509
527
  }
510
- } else {
528
+ }
529
+ // <top> position
530
+ else {
511
531
  arrowDir = 'down';
512
- t += targetRect.top - elH - ah + _w.scrollY;
513
- y = rmt - (elH + ah);
514
- if (y < 0) {
532
+ t += targetRect.top - elH - ah + wScrollY;
533
+ y = (isSticky ? targetRect.top - toolbarH : rmt) - elH - ah;
534
+ // change to [bottom] position
535
+ if (y - siblingH < 0) {
515
536
  arrowDir = 'up';
516
537
  t += targetH + elH + ah * 2;
517
- y = rmb - (elH + ah);
518
- if (y < 0) {
538
+ y = (rmb > 0 ? bMargin : rmb) - (elH + ah) - statusBarH;
539
+ // sticky the [bottom] position
540
+ if (y - siblingH < 0) {
519
541
  arrowDir = '';
520
- t += y;
542
+ t += y - 2;
521
543
  }
522
544
  }
523
545
  }
@@ -734,24 +756,32 @@ Offset.prototype = {
734
756
  * @param {RectsInfo} targetRect Target rect object
735
757
  * @param {boolean} isTargetAbs Is target absolute position
736
758
  * @param {OffsetWWScrollInfo} wwScroll WYSIWYG scroll info
737
- * @returns {{rmt:number, rmb:number, rt:number}} Margin values (rmt: top margin, rmb: bottom margin, rt: Toolbar height offset adjustment)
759
+ * @returns {{rmt:number, rmb:number, rt:number, tMargin:number, bMargin:number}} Margin values
760
+ * - rmt: top margin to frame
761
+ * - rmb: bottom margin to frame
762
+ * - rt: Toolbar height offset adjustment
763
+ * - tMargin: top margin
764
+ * - bMargin: bottom margin
738
765
  */
739
766
  _getVMargin(tmtw, tmbw, toolbarH, clientSize, targetRect, isTargetAbs, wwScroll) {
740
767
  let rmt = 0;
741
768
  let rmb = 0;
742
769
  let rt = 0;
770
+ let tMargin = 0;
771
+ let bMargin = 0;
772
+
743
773
  if (this.editor.frameContext.get('isFullScreen')) {
744
774
  rmt = tmtw - toolbarH;
745
775
  rmb = tmbw;
746
776
  } else {
747
- const isIframe = isTargetAbs && this.editor.frameOptions.get('iframe');
748
- const tMargin = targetRect.top;
749
- const bMargin = clientSize.h - targetRect.bottom;
777
+ const isIframeAbs = isTargetAbs && this.editor.frameOptions.get('iframe');
778
+ tMargin = targetRect.top;
779
+ bMargin = clientSize.h - targetRect.bottom;
750
780
  const editorOffset = this.getGlobal();
751
781
  const editorScroll = this.getGlobalScroll();
752
782
  const statusBarH = this.editor.frameContext.get('statusbar')?.offsetHeight || 0;
753
783
 
754
- if (isIframe) {
784
+ if (isIframeAbs) {
755
785
  const emt = editorOffset.top - editorScroll.top - editorScroll.ts;
756
786
  const editorH = this.editor.frameContext.get('topArea').offsetHeight;
757
787
  rmt = targetRect.top - emt;
@@ -764,29 +794,28 @@ Offset.prototype = {
764
794
  if (toolbarH > wst) {
765
795
  if (this.editor.toolbar._sticky) {
766
796
  st = toolbarH;
767
- toolbarH = 0;
768
797
  } else {
769
798
  st = wst + toolbarH;
770
799
  }
771
- } else if (this.options.get('toolbar_container')) {
800
+ } else if (this.options.get('toolbar_container') && !this.editor.toolbar._sticky) {
772
801
  toolbarH = 0;
773
802
  } else {
774
- st = wst + (this.editor.toolbar._sticky ? toolbarH : 0);
803
+ st = wst + toolbarH;
775
804
  }
776
805
 
777
- rmt = targetRect.top - st;
778
- rmb = wwScroll.rects.bottom - targetRect.bottom - wsb - statusBarH;
806
+ rmt = targetRect.top - wwScroll.rects.top - st + toolbarH;
807
+ rmb = wwScroll.rects.bottom - targetRect.bottom - wsb;
808
+ // display margin
809
+ rmt = rmt > 0 ? rmt : rmt - toolbarH;
779
810
  }
780
-
781
- // display margin
782
- rmt = (rmt > 0 ? tMargin : rmt) - toolbarH;
783
- rmb = rmb > 0 ? bMargin : rmb;
784
811
  }
785
812
 
786
813
  return {
787
814
  rmt,
788
815
  rmb,
789
- rt
816
+ rt,
817
+ tMargin,
818
+ bMargin
790
819
  };
791
820
  },
792
821
 
@@ -362,7 +362,7 @@ Toolbar.prototype = {
362
362
  let left = container.offsetLeft;
363
363
  let top = container.offsetTop;
364
364
 
365
- while (!container.parentElement.contains(editorParent) || !/^(BODY|HTML)$/i.test(container.parentElement.nodeName)) {
365
+ while (!container.parentElement.contains(editorParent) && !/^(BODY|HTML)$/i.test(container.parentElement.nodeName)) {
366
366
  container = container.offsetParent;
367
367
  left += container.offsetLeft;
368
368
  top += container.offsetTop;
@@ -1,5 +1,6 @@
1
1
  import { env, converter, dom, numbers } from '../helper';
2
- import Constructor, { InitOptions, UpdateButton, CreateShortcuts, CreateStatusbar, OPTION_FRAME_FIXED_FLAG, OPTION_FIXED_FLAG } from './section/constructor';
2
+ import Constructor, { InitOptions, UpdateButton, CreateShortcuts, CreateStatusbar } from './section/constructor';
3
+ import { OPTION_FRAME_FIXED_FLAG, OPTION_FIXED_FLAG } from './section/options';
3
4
  import { UpdateStatusbarContext } from './section/context';
4
5
  import { BASIC_COMMANDS, ACTIVE_EVENT_COMMANDS, SELECT_ALL, DIR_BTN_ACTIVE, SAVE, COPY_FORMAT, FONT_STYLE, PAGE_BREAK } from './section/actives';
5
6
  import History from './base/history';
@@ -14,119 +14,6 @@ const _d = env._d;
14
14
  * @typedef {import('./options').EditorInitOptions} EditorInitOptions
15
15
  */
16
16
 
17
- /** ------------- [OPTIONS FRAG] ------------- */
18
- /**
19
- * @description For all EditorInitOptions keys, only boolean | null values are allowed.
20
- * - 'fixed' → Immutable / null → Resettable.
21
- * @type {Partial<Record<keyof EditorInitOptions, "fixed" | true>>}
22
- */
23
- export const OPTION_FRAME_FIXED_FLAG = {
24
- value: 'fixed',
25
- placeholder: true,
26
- editableFrameAttributes: true,
27
- width: true,
28
- minWidth: true,
29
- maxWidth: true,
30
- height: true,
31
- minHeight: true,
32
- maxHeight: true,
33
- editorStyle: true,
34
- iframe: 'fixed',
35
- iframe_fullPage: 'fixed',
36
- iframe_attributes: true,
37
- iframe_cssFileName: true,
38
- statusbar: true,
39
- statusbar_showPathLabel: true,
40
- statusbar_resizeEnable: 'fixed',
41
- charCounter: true,
42
- charCounter_max: true,
43
- charCounter_label: true,
44
- charCounter_type: true
45
- };
46
- /**
47
- * @description For all EditorInitOptions keys, only boolean | null values are allowed.
48
- * - 'fixed' → Immutable / null → Resettable.
49
- * @type {Partial<Record<keyof EditorInitOptions, "fixed" | true>>}
50
- */
51
- export const OPTION_FIXED_FLAG = {
52
- plugins: 'fixed',
53
- excludedPlugins: 'fixed',
54
- buttonList: 'fixed',
55
- v2Migration: 'fixed',
56
- strictMode: 'fixed',
57
- mode: 'fixed',
58
- type: 'fixed',
59
- theme: true,
60
- lang: 'fixed',
61
- fontSizeUnits: 'fixed',
62
- allowedClassName: 'fixed',
63
- closeModalOutsideClick: 'fixed',
64
- copyFormatKeepOn: true,
65
- syncTabIndent: true,
66
- tabDisable: true,
67
- autoLinkify: true,
68
- autoStyleify: true,
69
- scrollToOptions: true,
70
- componentScrollToOptions: true,
71
- retainStyleMode: true,
72
- allowedExtraTags: 'fixed',
73
- events: true,
74
- __textStyleTags: 'fixed',
75
- textStyleTags: 'fixed',
76
- convertTextTags: 'fixed',
77
- __tagStyles: 'fixed',
78
- tagStyles: 'fixed',
79
- spanStyles: 'fixed',
80
- lineStyles: 'fixed',
81
- textDirection: true,
82
- reverseButtons: 'fixed',
83
- historyStackDelayTime: true,
84
- lineAttrReset: true,
85
- printClass: true,
86
- defaultLine: 'fixed',
87
- defaultLineBreakFormat: true,
88
- scopeSelectionTags: true,
89
- __defaultElementWhitelist: 'fixed',
90
- elementWhitelist: 'fixed',
91
- elementBlacklist: 'fixed',
92
- __defaultAttributeWhitelist: 'fixed',
93
- attributeWhitelist: 'fixed',
94
- attributeBlacklist: 'fixed',
95
- __defaultFormatLine: 'fixed',
96
- formatLine: 'fixed',
97
- __defaultFormatBrLine: 'fixed',
98
- formatBrLine: 'fixed',
99
- __defaultFormatClosureBrLine: 'fixed',
100
- formatClosureBrLine: 'fixed',
101
- __defaultFormatBlock: 'fixed',
102
- formatBlock: 'fixed',
103
- __defaultFormatClosureBlock: 'fixed',
104
- formatClosureBlock: 'fixed',
105
- allowedEmptyTags: true,
106
- toolbar_width: true,
107
- toolbar_container: 'fixed',
108
- toolbar_sticky: true,
109
- toolbar_hide: true,
110
- subToolbar: 'fixed',
111
- statusbar_container: 'fixed',
112
- shortcutsHint: true,
113
- shortcutsDisable: 'fixed',
114
- shortcuts: 'fixed',
115
- fullScreenOffset: true,
116
- previewTemplate: true,
117
- printTemplate: true,
118
- componentAutoSelect: true,
119
- defaultUrlProtocol: true,
120
- allUsedStyles: 'fixed',
121
- toastMessageTime: true,
122
- icons: 'fixed',
123
- freeCodeViewMode: true,
124
- __lineFormatFilter: true,
125
- __pluginRetainFilter: true,
126
- __listCommonStyle: 'fixed',
127
- externalLibs: 'fixed'
128
- };
129
-
130
17
  /**
131
18
  * @description Creates a new SunEditor instance with specified options.
132
19
  * @param {Array<{target: Element, key: *, options: EditorFrameOptions}>} editorTargets - Target element or multi-root object.
@@ -555,7 +442,7 @@ export function InitOptions(options, editorTargets, plugins) {
555
442
  return _default;
556
443
  }, {})
557
444
  );
558
- o.set('_textStylesRegExp', new RegExp(`\\s*[^-a-zA-Z](${DEFAULTS.TEXT_STYLES}${options.spanStyles ? '|' + options.spanStyles : ''})\\s*:[^;]+(?!;)*`, 'gi'));
445
+ o.set('_textStylesRegExp', new RegExp(`\\s*[^-a-zA-Z](${DEFAULTS.SPAN_STYLES}${options.spanStyles ? '|' + options.spanStyles : ''})\\s*:[^;]+(?!;)*`, 'gi'));
559
446
  o.set('_lineStylesRegExp', new RegExp(`\\s*[^-a-zA-Z](${DEFAULTS.LINE_STYLES}${options.lineStyles ? '|' + options.lineStyles : ''})\\s*:[^;]+(?!;)*`, 'gi'));
560
447
  o.set('_defaultStyleTagMap', {
561
448
  strong: textTags.bold,
@@ -788,7 +675,7 @@ export function InitOptions(options, editorTargets, plugins) {
788
675
  const allUsedStyles = new Set(DEFAULTS.CONTENT_STYLES.split('|'));
789
676
  const _ss = options.spanStyles?.split('|') || [];
790
677
  const _ls = o.get('__listCommonStyle');
791
- const _dts = DEFAULTS.TEXT_STYLES.split('|');
678
+ const _dts = DEFAULTS.SPAN_STYLES.split('|');
792
679
  for (let i = 0, len = _dts.length; i < len; i++) {
793
680
  allUsedStyles.add(_dts[i]);
794
681
  }
@@ -130,9 +130,11 @@ DocumentType.prototype = {
130
130
  this._rePageTimeout = _w.setTimeout(async () => {
131
131
  await dom.utils.waitForMediaLoad(this._mirror, 1500);
132
132
 
133
- const mirrorHeight = this._mirror.scrollHeight;
133
+ const heightGap = this.ww.scrollHeight > this._mirror.scrollHeight ? this.ww.scrollHeight - this._mirror.scrollHeight : 0;
134
+ const mirrorHeight = this._mirror.scrollHeight + heightGap;
134
135
  const pageBreaks = this._mirror.querySelectorAll('.se-page-break');
135
136
  if (!force && this.pageHeight === mirrorHeight && this.pageBreaksCnt === pageBreaks.length) return;
137
+
136
138
  this.pageHeight = mirrorHeight;
137
139
  this.pageBreaksCnt = pageBreaks.length;
138
140
 
@@ -145,18 +147,12 @@ DocumentType.prototype = {
145
147
  for (let i = 0; i < pageBreaks.length; i++) {
146
148
  const breakPosition = pageBreaks[i].offsetTop;
147
149
  const sectionHeight = breakPosition - lastBreakPosition;
148
-
149
- if (sectionHeight % A4_PAGE_HEIGHT !== 0) {
150
- additionalPages++;
151
- }
152
-
150
+ if (sectionHeight % A4_PAGE_HEIGHT !== 0) additionalPages++;
153
151
  lastBreakPosition = breakPosition;
154
152
  }
155
153
 
156
154
  const lastSectionHeight = mirrorHeight - lastBreakPosition;
157
- if (lastSectionHeight > 0 && lastSectionHeight % A4_PAGE_HEIGHT !== 0) {
158
- additionalPages++;
159
- }
155
+ if (lastSectionHeight > 0 && lastSectionHeight % A4_PAGE_HEIGHT !== 0) additionalPages++;
160
156
  }
161
157
 
162
158
  const scrollTop = this.isAutoHeight ? 0 : this._getWWScrollTop();
@@ -168,25 +164,18 @@ DocumentType.prototype = {
168
164
  pages.push({ number: i, top: pageBreaks[i].offsetTop + pageBreakHeight - scrollTop });
169
165
  }
170
166
 
171
- // A4 position
172
167
  this._mirrorCache = 0;
173
168
  const chr = this.ww.children;
174
169
  const mChr = this._mirror.children;
175
170
  this._initializeCache(mChr);
171
+
176
172
  pages.push({ number: 0, top: 0 });
173
+
177
174
  for (let i = 1, t = 0; i < totalPages; i++) {
178
175
  t += A4_PAGE_HEIGHT + (i === 1 ? this._paddingTop + this._paddingBottom : this._paddingTop);
179
- if (!pages.some((p) => Math.abs(p.top - t) < 1)) {
180
- const { ci, cm, ch } = this._getElementAtPosition(t, mChr);
181
- const el = chr[ci];
182
- if (!el) break;
183
-
184
- if (chr[this._mirrorCache]) {
185
- t += numbers.get(_w.getComputedStyle(chr[this._mirrorCache]).marginBottom);
186
- }
187
-
188
- const elBottom = el.offsetTop + el.offsetHeight;
189
- const top = elBottom + cm + (el.offsetHeight - ch);
176
+ if (!pages.some((p) => Math.abs(p.top - t) < 3)) {
177
+ const top = this._calcPageBreakTop(t, chr, mChr);
178
+ if (top === null) break;
190
179
  pages.push({ number: i, top });
191
180
  }
192
181
  }
@@ -202,11 +191,21 @@ DocumentType.prototype = {
202
191
  pages.sort((a, b) => a.top - b.top);
203
192
  this.page.innerHTML = '';
204
193
  this.pages = [];
194
+
205
195
  for (let i = 0, t; i < totalPages; i++) {
206
196
  if (!pages[i]) continue;
207
197
  t = pages[i].top;
208
198
  if (mirrorHeight < t) break;
209
- const pageNumber = dom.utils.createElement('DIV', { style: `top:${t - scrollTop}px`, innerHTML: String(i + 1) }, `<div class="se-document-page-line" style="width: ${wwWidth}px;"></div>${i + 1}`);
199
+
200
+ const pageNumber = dom.utils.createElement(
201
+ 'DIV',
202
+ {
203
+ style: `top:${t - scrollTop}px`,
204
+ innerHTML: String(i + 1)
205
+ },
206
+ `<div class="se-document-page-line" style="width: ${wwWidth}px;"></div>${i + 1}`
207
+ );
208
+
210
209
  this.page.appendChild(pageNumber);
211
210
  this.pages.push(pageNumber);
212
211
  }
@@ -217,6 +216,28 @@ DocumentType.prototype = {
217
216
  }, 400);
218
217
  },
219
218
 
219
+ /**
220
+ * @private
221
+ * @description Calculates and compensates for the vertical gap between the rendered content (current page)
222
+ * - and the mirrored preview page due to differences in width and layout.
223
+ * @param {number} t - The initial top position value to be adjusted.
224
+ * @param {HTMLElement[]} chr - The elements array in the current (main) page.
225
+ * @param {HTMLElement[]} mChr - The elements array in the mirrored page.
226
+ * @returns {number|null} - The adjusted top value.
227
+ */
228
+ _calcPageBreakTop(t, chr, mChr) {
229
+ const { ci } = this._getElementAtPosition(t, mChr);
230
+ const mel = mChr[ci];
231
+ const el = chr[ci];
232
+ if (!mel || !el) return null;
233
+
234
+ const offsetDiff = el.offsetTop - mel.offsetTop;
235
+ const heightDiff = el.offsetHeight - mel.offsetHeight;
236
+
237
+ const top = t + offsetDiff + heightDiff / 2;
238
+ return Math.round(top);
239
+ },
240
+
220
241
  /**
221
242
  * @private
222
243
  * @description Initializes the cache for document elements.
@@ -242,7 +263,7 @@ DocumentType.prototype = {
242
263
  * @private
243
264
  * @description Retrieves the element at a given position.
244
265
  * @param {number} pageTop - The vertical position to check.
245
- * @param {NodeList} mChr - List of mirrored elements.
266
+ * @param {HTMLElement[]} mChr - List of mirrored elements.
246
267
  * @returns {{ci: number, cm: number, ch: number}} The closest element and its related data.
247
268
  * - ci: The index of the closest element.
248
269
  * - cm: The distance between the top of the closest element and the given position.