suneditor 3.0.0-alpha.13 → 3.0.0-alpha.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.
Files changed (35) hide show
  1. package/dist/suneditor.min.css +1 -1
  2. package/dist/suneditor.min.js +1 -1
  3. package/package.json +2 -1
  4. package/src/assets/icons/_default.js +9 -2
  5. package/src/assets/suneditor-contents.css +9 -22
  6. package/src/assets/suneditor.css +317 -183
  7. package/src/assets/variables.css +137 -0
  8. package/src/core/base/eventHandlers/handler_toolbar.js +1 -0
  9. package/src/core/base/eventHandlers/handler_ww_dragDrop.js +9 -2
  10. package/src/core/base/eventHandlers/handler_ww_key_input.js +1 -1
  11. package/src/core/base/eventManager.js +5 -0
  12. package/src/core/class/component.js +12 -8
  13. package/src/core/class/html.js +5 -5
  14. package/src/core/class/offset.js +11 -3
  15. package/src/core/class/selection.js +5 -4
  16. package/src/core/class/toolbar.js +1 -5
  17. package/src/core/class/viewer.js +29 -7
  18. package/src/core/editor.js +61 -1
  19. package/src/core/section/actives.js +5 -0
  20. package/src/core/section/constructor.js +35 -19
  21. package/src/core/section/documentType.js +143 -19
  22. package/src/helper/domUtils.js +5 -4
  23. package/src/langs/en.js +5 -0
  24. package/src/modules/Modal.js +108 -1
  25. package/src/plugins/command/exportPdf.js +1 -1
  26. package/src/plugins/command/list_bulleted.js +1 -1
  27. package/src/plugins/command/list_numbered.js +1 -1
  28. package/src/plugins/index.js +6 -0
  29. package/src/plugins/input/fontSize.js +3 -3
  30. package/src/plugins/input/pageNavigator.js +47 -0
  31. package/src/plugins/modal/drawing.js +426 -0
  32. package/src/plugins/modal/image.js +3 -2
  33. package/src/plugins/modal/math.js +72 -34
  34. package/src/themes/dark-variables.css +85 -0
  35. package/src/themes/test.css +0 -61
@@ -0,0 +1,137 @@
1
+ .sun-editor,
2
+ .sun-editor-editable {
3
+ /** --------- editable - styles --------- */
4
+ --se-edit-font-size: 13px;
5
+ --se-edit-inner-padding: 16px;
6
+ --se-edit-line-height: 1.5;
7
+ --se-edit-font-color: #333;
8
+ --se-edit-font-pre: #666;
9
+ --se-edit-font-quote: #999;
10
+ --se-edit-background-color: #fff;
11
+ --se-edit-background-pre: #f9f9f9;
12
+ --se-edit-border-light: #e1e1e1;
13
+ --se-edit-border-dark: #b1b1b1;
14
+ --se-edit-border-dark-n1: #c1c1c1;
15
+ --se-edit-border-dark-n2: #d1d1d1;
16
+ --se-edit-anchor: #004cff;
17
+ --se-edit-anchor-on-back: #e8f7ff;
18
+ --se-edit-anchor-on-font: #0093ff;
19
+ --se-edit-hr-color: #333;
20
+ --se-edit-hr-on-back: #c7deff;
21
+ --se-edit-active: #4592ff;
22
+ --se-edit-hover: #80bdff;
23
+ --se-edit-outline: #9e9e9e;
24
+
25
+ /** --------- editor - styles --------- */
26
+ /** main, common */
27
+ --se-min-height: 65px;
28
+ --se-main-out-color: #dadada;
29
+ --se-main-font-family: Helvetica Neue;
30
+ --se-main-color: #000;
31
+ --se-main-color-lighter: #4c4c4d;
32
+ --se-main-background-color: #fff;
33
+ --se-main-background-color2: #f5f5f5;
34
+ --se-main-background-color3: #f9f9f9;
35
+ --se-main-font-color: #333;
36
+ --se-main-font-color2: #222;
37
+ --se-main-font-size: 13px;
38
+ --se-main-divider-color: #e1e1e1;
39
+ --se-main-border-color: #d1d1d1;
40
+ --se-main-outline-color: #b1b1b1;
41
+ --se-main-shadow-color: #ececec;
42
+ --se-border-radius: 2px;
43
+ --se-btn-font-size: 12px;
44
+ --se-statusbar-font-size: 10px;
45
+ --se-statusbar-font-color: #666;
46
+ --se-overlay-background-color: #222;
47
+ /* hover, active */
48
+ --se-active-color: #3288ff;
49
+ --se-active-dark-color: #80bdff;
50
+ --se-active-dark2-color: #407dd1;
51
+ --se-active-dark3-color: #4592ff;
52
+ --se-active-dark4-color: #3f9dff;
53
+ --se-active-dark5-color: #1275ff;
54
+ --se-active-light-color: #e6f2ff;
55
+ --se-active-light2-color: #eaf3ff;
56
+ --se-active-light3-color: #d0e3ff;
57
+ --se-active-light4-color: #dbeaff;
58
+ --se-active-light5-color: #c4ddff;
59
+ --se-active-light6-color: #b7ccf2;
60
+ /* drag */
61
+ --se-drag-over-color: #f0c20a;
62
+ /* modal, browser, dropdown */
63
+ --se-modal-title-font-size: 15px;
64
+ --se-modal-input-font-size: 14px;
65
+ --se-modal-input-width: 70px;
66
+ --se-modal-border-color: #e5e5e5;
67
+ --se-modal-anchor-color: #004cff;
68
+ --se-modal-preview-color: #666;
69
+ --se-modal-input-disabled-color: #999;
70
+ --se-modal-input-disabled-background-color: #f3f3f3;
71
+ /* browser */
72
+ --se-browser-title-font-size: 16px;
73
+ /* dropdown, selectMenu */
74
+ --se-dropdown-font-color: #555;
75
+ /* controller */
76
+ --se-controller-font-size: 14px;
77
+ --se-controller-border-color: #999;
78
+ /* input:btn */
79
+ --se-input-btn-size: 34px;
80
+ --se-input-btn-border-color: #ccc;
81
+ --se-input-btn-disabled-color: #bdbdbd;
82
+ /* btn - success */
83
+ --se-success-color: #008000;
84
+ --se-success-dark-color: #628562;
85
+ --se-success-dark2-color: #419c41;
86
+ --se-success-dark3-color: #006c00;
87
+ --se-success-light-color: #dff4e6;
88
+ --se-success-light2-color: #d5f3e0;
89
+ --se-success-light3-color: #c1f4d3;
90
+ --se-success-light4-color: #769c76;
91
+ --se-success-light5-color: #89b589;
92
+ /* error */
93
+ --se-error-color: #b94a48;
94
+ --se-error-dark-color: #e1a6a2;
95
+ --se-error-dark2-color: #db8d8c;
96
+ --se-error-dark3-color: #d17872;
97
+ --se-error-light-color: #f5d0c8;
98
+ --se-error-light2-color: #eed3d7;
99
+ --se-error-light3-color: #f2dede;
100
+ --se-error-light4-color: #f7deda;
101
+ --se-error-light5-color: #f8e3e1;
102
+ /* document type */
103
+ --se-doc-min-padding: 1rem;
104
+ --se-doc-max-padding: 1.5rem;
105
+ --se-doc-padding-factor: 0.5;
106
+ --se-doc-background: #d2d8e1d1;
107
+ --se-doc-info-font-size: 14px;
108
+ --se-doc-info-width: 18%;
109
+ --se-doc-info-min-width: 16ch;
110
+ --se-doc-info-inner-padding: 2ch;
111
+ --se-doc-info-inner-line-padding: 0.5ch;
112
+ --se-doc-info-page-width: 16px;
113
+ --se-doc-info-page-font-color: #f5f5f5;
114
+ --se-doc-info-page-background-color: #b1b1b1;
115
+ /* document type - active */
116
+ --se-doc-info-active-color: #4a32ff;
117
+ /* document type indent - padding */
118
+ --se-doc-info-inner-line-indent-h1: 0em;
119
+ --se-doc-info-inner-line-indent-h2: 0.4em;
120
+ --se-doc-info-inner-line-indent-h3: 0.8em;
121
+ --se-doc-info-inner-line-indent-h4: 1.2em;
122
+ --se-doc-info-inner-line-indent-h5: 1.6em;
123
+ --se-doc-info-inner-line-indent-h6: 2em;
124
+ /* document type indent - weight */
125
+ --se-doc-info-inner-line-weight-h1: 700;
126
+ --se-doc-info-inner-line-weight-h2: 500;
127
+ --se-doc-info-inner-line-weight-h3: 400;
128
+ --se-doc-info-inner-line-weight-h4: 300;
129
+ --se-doc-info-inner-line-weight-h5: 300;
130
+ --se-doc-info-inner-line-weight-h6: 300;
131
+ /* loading */
132
+ --se-loading-color: #07d;
133
+ /* show blocks */
134
+ --se-show-blocks-color: #3f9dff;
135
+ --se-show-blocks-li-color: #d539ff;
136
+ --se-show-blocks-pre-color: #27c022;
137
+ }
@@ -40,6 +40,7 @@ export function ButtonsHandler(e) {
40
40
  if (!domUtils.isInputElement(eventTarget) || eventTarget.disabled) return;
41
41
 
42
42
  const plugin = this.plugins[command];
43
+ if (!plugin) return;
43
44
 
44
45
  if (this.__inputBlurEvent) this.__removeInput();
45
46
 
@@ -20,8 +20,15 @@ export function OnDragOver_wysiwyg(fc, dragCursor, _iframeTopArea, _innerToolbar
20
20
 
21
21
  const rect = cursorRange.getBoundingClientRect();
22
22
  if (rect.height > 0) {
23
- dragCursor.style.left = `${rect.right + this._w.scrollX + _offset.x}px`;
24
- dragCursor.style.top = `${rect.top + this._w.scrollY + _offset.y - 5}px`;
23
+ const wwFrame = fc.get('wysiwygFrame');
24
+ let frameX = 0;
25
+ let frameY = 0;
26
+ if (/^iframe$/i.test(wwFrame.nodeName)) {
27
+ frameX = wwFrame.offsetLeft;
28
+ frameY = wwFrame.offsetTop;
29
+ }
30
+ dragCursor.style.left = `${rect.right + this._w.scrollX + _offset.x + frameX}px`;
31
+ dragCursor.style.top = `${rect.top + this._w.scrollY + _offset.y - 5 + frameY}px`;
25
32
  dragCursor.style.height = `${rect.height + 10}px`;
26
33
  dragCursor.style.display = 'block';
27
34
  } else {
@@ -6,7 +6,7 @@ const DIR_KEYCODE = /^(3[7-9]|40)$/;
6
6
  const DELETE_KEYCODE = /^(8|46)$/;
7
7
  const NON_TEXT_KEYCODE = /^(8|9|13|1[6-9]|20|27|3[3-9]|40|45|46|11[2-9]|12[0-3]|144|145|229)$/;
8
8
  const HISTORY_IGNORE_KEYCODE = /^(1[6-9]|20|27|3[3-9]|40|45|11[2-9]|12[0-3]|144|145|229)$/;
9
- const DOCUMENT_TYPE_OBSERVER_KEYCODE = /^(13)$/;
9
+ const DOCUMENT_TYPE_OBSERVER_KEYCODE = /^(8|13|46)$/;
10
10
  const FRONT_ZEROWIDTH = new RegExp(unicode.zeroWidthSpace + '+', '');
11
11
  let _styleNodes = null;
12
12
 
@@ -1128,6 +1128,11 @@ function OnScroll_window() {
1128
1128
  }
1129
1129
 
1130
1130
  this._scrollContainer();
1131
+
1132
+ // document type page
1133
+ if (this.editor.frameContext.has('documentType-use-page')) {
1134
+ this.editor.frameContext.get('documentType').scrollWindow();
1135
+ }
1131
1136
  }
1132
1137
 
1133
1138
  function OnResize_viewport() {
@@ -113,6 +113,7 @@ Component.prototype = {
113
113
  let pluginName = '';
114
114
  let options = {};
115
115
  let isFile = false;
116
+ let launcher = null;
116
117
 
117
118
  if (this.is(element)) {
118
119
  if (domUtils.hasClass(element, 'se-component') && !domUtils.hasClass(element, 'se-inline-component')) element = element.firstElementChild || element;
@@ -124,6 +125,7 @@ Component.prototype = {
124
125
  target = comp.target;
125
126
  pluginName = comp.pluginName;
126
127
  options = comp.options;
128
+ launcher = comp.launcher;
127
129
  }
128
130
 
129
131
  if (!target && element.nodeName) {
@@ -135,6 +137,7 @@ Component.prototype = {
135
137
  target = comp.target;
136
138
  pluginName = comp.pluginName;
137
139
  options = comp.options;
140
+ launcher = comp.launcher;
138
141
  }
139
142
 
140
143
  if (!target) {
@@ -150,7 +153,8 @@ Component.prototype = {
150
153
  cover: figureInfo.cover,
151
154
  inlineCover: figureInfo.inlineCover,
152
155
  caption: figureInfo.caption,
153
- isFile: isFile
156
+ isFile,
157
+ launcher
154
158
  });
155
159
  },
156
160
 
@@ -163,7 +167,7 @@ Component.prototype = {
163
167
  const info = this.get(element);
164
168
  if (!info || domUtils.isUneditable(domUtils.getParentElement(element, this.is.bind(this))) || domUtils.isUneditable(element)) return false;
165
169
 
166
- const plugin = this.plugins[pluginName];
170
+ const plugin = info.launcher || this.plugins[pluginName];
167
171
  if (!plugin) return;
168
172
 
169
173
  if (!isInput && _DragHandle.get('__overInfo') !== ON_OVER_COMPONENT) {
@@ -185,7 +189,8 @@ Component.prototype = {
185
189
  let isNonFigureComponent;
186
190
  if (typeof plugin.select === 'function') isNonFigureComponent = plugin.select(element);
187
191
 
188
- if (!isNonFigureComponent && !domUtils.hasClass(info.container, 'se-inline-component')) this._setComponentLineBreaker(info.container || info.cover || element);
192
+ const isBreakComponent = domUtils.hasClass(info.target, 'se-component-line-break');
193
+ if (isBreakComponent || (!isNonFigureComponent && !domUtils.hasClass(info.container, 'se-inline-component'))) this._setComponentLineBreaker(info.container || info.cover || element);
189
194
 
190
195
  this.currentTarget = element;
191
196
  this.currentPlugin = plugin;
@@ -202,7 +207,7 @@ Component.prototype = {
202
207
  }, 0);
203
208
  domUtils.addClass(info.container, 'se-component-selected');
204
209
 
205
- if (__overInfo !== ON_OVER_COMPONENT) {
210
+ if (!isBreakComponent && __overInfo !== ON_OVER_COMPONENT) {
206
211
  domUtils.setDisabled(this.editor._controllerOnDisabledButtons, true);
207
212
 
208
213
  // set zero width space
@@ -221,7 +226,7 @@ Component.prototype = {
221
226
  }
222
227
 
223
228
  this.editor.status.onSelected = true;
224
- } else if (!domUtils.hasClass(info.container, 'se-input-component')) {
229
+ } else if (isBreakComponent || !domUtils.hasClass(info.container, 'se-input-component')) {
225
230
  const dragHandle = this.editor.frameContext.get('wrapper').querySelector('.se-drag-handle');
226
231
  domUtils.addClass(dragHandle, 'se-drag-handle-full');
227
232
  this.editor._visibleControllers(false, false);
@@ -234,7 +239,7 @@ Component.prototype = {
234
239
  dragHandle.style.opacity = 0;
235
240
  dragHandle.style.width = w + 'px';
236
241
  dragHandle.style.height = h + 'px';
237
- dragHandle.style.top = top + 'px';
242
+ dragHandle.style.top = top - 1 + 'px';
238
243
  dragHandle.style.left = left + 'px';
239
244
 
240
245
  _DragHandle.set('__dragHandler', dragHandle);
@@ -353,8 +358,7 @@ Component.prototype = {
353
358
  let componentTop, w;
354
359
  const isRtl = this.options.get('_rtl');
355
360
  const dir = isRtl ? ['right', 'left'] : ['left', 'right'];
356
- const top = offsetTarget.offsetTop;
357
- const { scrollX, scrollY } = this.offset.getLocal(offsetTarget);
361
+ const { top, scrollX, scrollY } = this.offset.getLocal(offsetTarget);
358
362
 
359
363
  if (isList ? !container.previousSibling : !this.format.isLine(container.previousElementSibling)) {
360
364
  const tH = numbers.get(_w.getComputedStyle(lb_t).height, 1);
@@ -301,7 +301,7 @@ HTML.prototype = {
301
301
  * @param {boolean} notCleanData If true, inserts the HTML string without refining it with html.clean.
302
302
  */
303
303
  insert(html, rangeSelection, notCheckCharCount, notCleanData) {
304
- if (!this.editor.frameContext.get('wysiwygFrame').contains(this.selection.get().focusNode)) this.editor.focus();
304
+ if (!this.editor.frameContext.get('wysiwyg').contains(this.selection.get().focusNode)) this.editor.focus();
305
305
 
306
306
  if (typeof html === 'string') {
307
307
  if (!notCleanData) html = this.clean(html, false, null, null);
@@ -800,8 +800,8 @@ HTML.prototype = {
800
800
  }
801
801
  } else {
802
802
  if ((commonCon.nodeType === 1 && startOff === 0 && endOff === 1) || (commonCon.nodeType === 3 && startOff === 0 && endOff === commonCon.textContent.length)) {
803
- const nextEl = domUtils.getNextDeepestNode(commonCon, this.editor.frameContext.get('wysiwygFrame'));
804
- const prevEl = domUtils.getPreviousDeepestNode(commonCon, this.editor.frameContext.get('wysiwygFrame'));
803
+ const nextEl = domUtils.getNextDeepestNode(commonCon, this.editor.frameContext.get('wysiwyg'));
804
+ const prevEl = domUtils.getPreviousDeepestNode(commonCon, this.editor.frameContext.get('wysiwyg'));
805
805
  const line = this.format.getLine(commonCon);
806
806
  domUtils.removeItem(commonCon);
807
807
 
@@ -1011,12 +1011,12 @@ HTML.prototype = {
1011
1011
  if (this.editor.frameOptions.get('iframe_fullPage')) {
1012
1012
  if (includeFullPage) {
1013
1013
  const attrs = domUtils.getAttributesToString(fc.get('_wd').body, ['contenteditable']);
1014
- r = '<!DOCTYPE html><html>' + fc.get('_wd').head.outerHTML + '<body ' + attrs + '>' + content + '</body></html>';
1014
+ r = `<!DOCTYPE html><html>${fc.get('_wd').head.outerHTML}<body ${attrs}>${content}</body></html>`;
1015
1015
  } else {
1016
1016
  r = content;
1017
1017
  }
1018
1018
  } else {
1019
- r = withFrame ? '<div class="sun-editor-editable' + (this.options.get('_rtl') ? ' se-rtl' : '') + '">' + content + '</div>' : renderHTML.innerHTML;
1019
+ r = withFrame ? `<div class="${this.options.get('_editableClass') + '' + (this.options.get('_rtl') ? ' se-rtl' : '')}">${content}</div>` : renderHTML.innerHTML;
1020
1020
  }
1021
1021
 
1022
1022
  resultValue[rootKey[i]] = r;
@@ -42,6 +42,8 @@ Offset.prototype = {
42
42
  getLocal(node) {
43
43
  let offsetLeft = 0;
44
44
  let offsetTop = 0;
45
+ let l = 0;
46
+ let t = 0;
45
47
  let offsetElement = node.nodeType === 3 ? node.parentElement : node;
46
48
  const wysiwyg = getParentElement(node, isWysiwygFrame.bind(this));
47
49
  const self = offsetElement;
@@ -52,10 +54,16 @@ Offset.prototype = {
52
54
  offsetElement = offsetElement.offsetParent;
53
55
  }
54
56
 
57
+ const wwFrame = this.editor.frameContext.get('wysiwygFrame');
58
+ if (/^iframe$/i.test(wwFrame.nodeName) && this.editor.frameContext.get('wysiwyg').contains(node)) {
59
+ l = wwFrame.offsetLeft;
60
+ t = wwFrame.offsetTop;
61
+ }
62
+
55
63
  const eventWysiwyg = this.editor.frameContext.get('eventWysiwyg');
56
64
  return {
57
- left: offsetLeft,
58
- top: offsetTop - (wysiwyg ? wysiwyg.scrollTop : 0),
65
+ left: offsetLeft + l,
66
+ top: offsetTop + t - (wysiwyg ? wysiwyg.scrollTop : 0),
59
67
  scrollX: eventWysiwyg.scrollX || eventWysiwyg.scrollLeft || 0,
60
68
  scrollY: eventWysiwyg.scrollY || eventWysiwyg.scrollTop || 0
61
69
  };
@@ -93,7 +101,7 @@ Offset.prototype = {
93
101
  element = element.offsetParent;
94
102
  }
95
103
 
96
- if (!targetAbs && !isTop && /^iframe$/i.test(wFrame.nodeName) && wFrame.contains(element)) {
104
+ if (!targetAbs && !isTop && /^iframe$/i.test(wFrame.nodeName) && this.editor.frameContext.get('wysiwyg').contains(element)) {
97
105
  element = this.editor.frameContext.get('wrapper');
98
106
  while (element) {
99
107
  t += element.offsetTop;
@@ -119,7 +119,7 @@ Selection.prototype = {
119
119
  this.status._range = range;
120
120
  this._rangeInfo(range, this.get());
121
121
 
122
- if (this.editor.frameOptions.get('iframe')) this.__focus();
122
+ // if (this.editor.frameOptions.get('iframe')) this.__focus();
123
123
 
124
124
  return range;
125
125
  },
@@ -302,11 +302,12 @@ Selection.prototype = {
302
302
  /**
303
303
  * @description Scroll to the corresponding selection or range position.
304
304
  * @param {Selection|Range|Node} ref selection or range object
305
+ * @param {Object?} scrollOption option of scrollTo
305
306
  */
306
- scrollTo(ref) {
307
+ scrollTo(ref, scrollOption) {
307
308
  if (ref instanceof Selection) {
308
309
  ref = ref.getRangeAt(0);
309
- } else if (ref instanceof Node) {
310
+ } else if (ref instanceof Node || /^(1|3)$/.test(ref?.nodeType)) {
310
311
  ref = this.setRange(ref, 1, ref, 1);
311
312
  } else if (typeof ref?.startContainer === 'undefined') {
312
313
  console.warn('[SUNEDITOR.html.scrollTo.warn] "selectionRange" must be Selection or Range or Node object.', ref);
@@ -318,7 +319,7 @@ Selection.prototype = {
318
319
  if (isVisible) return;
319
320
 
320
321
  const el = domUtils.getParentElement(ref.startContainer, (current) => current.nodeType === 1);
321
- el?.scrollIntoView?.(this.options.get('scrollToOptions'));
322
+ el?.scrollIntoView?.(scrollOption || this.options.get('scrollToOptions'));
322
323
  },
323
324
 
324
325
  /**
@@ -257,11 +257,7 @@ Toolbar.prototype = {
257
257
  buttonsObj[size] = buttonGroup[1];
258
258
  }
259
259
 
260
- _rButtonsize
261
- .sort(function (a, b) {
262
- return a - b;
263
- })
264
- .unshift('default');
260
+ _rButtonsize.sort((a, b) => a - b).unshift('default');
265
261
  },
266
262
 
267
263
  _showBalloon(rangeObj) {
@@ -41,6 +41,7 @@ Viewer.prototype = {
41
41
  const codeWrapper = fc.get('codeWrapper');
42
42
  const codeFrame = fc.get('code');
43
43
  const wysiwygFrame = fc.get('wysiwygFrame');
44
+ const wrapper = fc.get('wrapper');
44
45
 
45
46
  if (value) {
46
47
  this._setEditorDataToCodeView();
@@ -77,6 +78,7 @@ Viewer.prototype = {
77
78
  this.status._range = null;
78
79
  codeFrame.focus();
79
80
  domUtils.addClass(this.editor.commandTargets.get('codeView'), 'active');
81
+ domUtils.addClass(wrapper, 'se-code-view-status');
80
82
  } else {
81
83
  if (!domUtils.isNonEditable(wysiwygFrame)) this._setCodeDataToEditor();
82
84
  wysiwygFrame.scrollTop = 0;
@@ -102,6 +104,7 @@ Viewer.prototype = {
102
104
  this.history.push(false);
103
105
  this.history.resetButtons(fc.get('key'), null);
104
106
  }
107
+ domUtils.removeClass(wrapper, 'se-code-view-status');
105
108
  }
106
109
 
107
110
  this.editor._checkPlaceholder();
@@ -334,9 +337,16 @@ Viewer.prototype = {
334
337
  const iframe = domUtils.createElement('IFRAME', { style: 'display: none;' });
335
338
  this._d.body.appendChild(iframe);
336
339
 
337
- const contentHTML = this.options.get('printTemplate') ? this.options.get('printTemplate').replace(/\{\{\s*contents\s*\}\}/i, this.html.get(true)) : this.html.get(true);
340
+ const contentHTML = this.options.get('printTemplate') ? this.options.get('printTemplate').replace(/\{\{\s*contents\s*\}\}/i, this.html.get()) : this.html.get();
338
341
  const printDocument = domUtils.getIframeDocument(iframe);
339
342
  const wDoc = this.editor.frameContext.get('_wd');
343
+ const pageCSS = /*html*/ `
344
+ <style>
345
+ @page {
346
+ size: A4;
347
+ margin: 0;
348
+ }
349
+ </style>`;
340
350
 
341
351
  if (this.editor.frameOptions.get('iframe')) {
342
352
  const arrts = this.options.get('printClass')
@@ -345,7 +355,17 @@ Viewer.prototype = {
345
355
  ? domUtils.getAttributesToString(wDoc.body, ['contenteditable'])
346
356
  : 'class="' + this.options.get('_editableClass') + '"';
347
357
 
348
- printDocument.write('' + '<!DOCTYPE html><html>' + '<head>' + wDoc.head.innerHTML + '</head>' + '<body ' + arrts + '>' + contentHTML + '</body>' + '</html>');
358
+ printDocument.write(/*html*/ `
359
+ <!DOCTYPE html>
360
+ <html>
361
+ <head>
362
+ ${wDoc.head.innerHTML}
363
+ ${pageCSS}
364
+ </head>
365
+ <body ${arrts}>
366
+ ${contentHTML}
367
+ </body>
368
+ </html>`);
349
369
  } else {
350
370
  const links = this._d.head.getElementsByTagName('link');
351
371
  const styles = this._d.head.getElementsByTagName('style');
@@ -357,13 +377,15 @@ Viewer.prototype = {
357
377
  linkHTML += styles[i].outerHTML;
358
378
  }
359
379
 
360
- printDocument.write(/*html*/ `<!DOCTYPE html>
380
+ printDocument.write(/*html*/ `
381
+ <!DOCTYPE html>
361
382
  <html>
362
383
  <head>
363
384
  ${linkHTML}
385
+ ${pageCSS}
364
386
  </head>
365
- <body class="${this.options.get('printClass') ? this.options.get('printClass') : this.options.get('_editableClass')}">
366
- ${contentHTML}
387
+ <body class="${this.options.get('printClass') || this.options.get('_editableClass')}">
388
+ ${contentHTML}
367
389
  </body>
368
390
  </html>`);
369
391
  }
@@ -419,7 +441,7 @@ Viewer.prototype = {
419
441
  <head>
420
442
  ${wDoc.head.innerHTML}
421
443
  <style>
422
- body {overflow:auto !important; margin: 10px auto !important; height:auto !important; outline:1px dashed #ccc;}
444
+ body {overflow:auto !important; height:auto !important;}
423
445
  </style>
424
446
  </head>
425
447
  <body ${arrts}>
@@ -445,7 +467,7 @@ Viewer.prototype = {
445
467
  <title>${this.lang.preview}</title>
446
468
  ${linkHTML}
447
469
  </head>
448
- <body class="${this.options.get('printClass') ? this.options.get('printClass') : this.options.get('_editableClass')}" style="margin:10px auto !important; height:auto !important; outline:1px dashed #ccc;">
470
+ <body class="${this.options.get('printClass') ? this.options.get('printClass') : this.options.get('_editableClass')}" style="height:auto">
449
471
  ${contentHTML}
450
472
  </body>
451
473
  </html>`);
@@ -1,7 +1,7 @@
1
1
  import { env, converter, domUtils, numbers } from '../helper';
2
2
  import Constructor, { InitOptions, UpdateButton, CreateShortcuts, CreateStatusbar, RO_UNAVAILABD } from './section/constructor';
3
3
  import { UpdateStatusbarContext } from './section/context';
4
- import { BASIC_COMMANDS, ACTIVE_EVENT_COMMANDS, SELECT_ALL, DIR_BTN_ACTIVE, SAVE, COPY_FORMAT, FONT_STYLE } from './section/actives';
4
+ import { BASIC_COMMANDS, ACTIVE_EVENT_COMMANDS, SELECT_ALL, DIR_BTN_ACTIVE, SAVE, COPY_FORMAT, FONT_STYLE, PAGE_BREAK } from './section/actives';
5
5
  import History from './base/history';
6
6
  import EventManager from './base/eventManager';
7
7
  import Events from './base/events';
@@ -447,6 +447,15 @@ Editor.prototype = {
447
447
  case 'copyFormat':
448
448
  COPY_FORMAT(this, button);
449
449
  break;
450
+ case 'pageBreak':
451
+ PAGE_BREAK(this);
452
+ break;
453
+ case 'pageUp':
454
+ this.frameContext.get('documentType').pageUp();
455
+ break;
456
+ case 'pageDown':
457
+ this.frameContext.get('documentType').pageDown();
458
+ break;
450
459
  default:
451
460
  FONT_STYLE(this, command);
452
461
  }
@@ -712,10 +721,43 @@ Editor.prototype = {
712
721
  domUtils.addClass(toolbar, 'se-shortcut-hide');
713
722
  }
714
723
 
724
+ // theme
725
+ if (this._originOptions.theme !== (newOptions.theme ?? this._originOptions.theme)) {
726
+ this.setTheme(newOptions.theme);
727
+ }
728
+
715
729
  this.effectNode = null;
716
730
  this._setFrameInfo(this.frameRoots.get(this.status.rootKey));
717
731
  },
718
732
 
733
+ /**
734
+ * @description Set the theme to the editor
735
+ * @param {string} theme Theme name
736
+ */
737
+ setTheme(theme) {
738
+ if (typeof theme !== 'string') return;
739
+ const o = this.options;
740
+ const prevTheme = o.get('_themeClass').trim();
741
+ o.set('theme', theme || '');
742
+ o.set('_themeClass', theme ? ` se-theme-${theme}` : '');
743
+ theme = o.get('_themeClass').trim();
744
+
745
+ const applyTheme = (target) => {
746
+ if (!target) return;
747
+ if (prevTheme) domUtils.removeClass(target, prevTheme);
748
+ if (theme) domUtils.addClass(target, theme);
749
+ };
750
+
751
+ applyTheme(this.carrierWrapper);
752
+ this.applyFrameRoots((e) => {
753
+ applyTheme(e.get('topArea'));
754
+ applyTheme(e.get('wysiwyg'));
755
+ });
756
+
757
+ applyTheme(this.context.get('statusbar._wrapper'));
758
+ applyTheme(this.context.get('toolbar._wrapper'));
759
+ },
760
+
719
761
  /**
720
762
  * @description Change the current root index.
721
763
  * @param {number} rootKey
@@ -1369,6 +1411,24 @@ Editor.prototype = {
1369
1411
  }
1370
1412
  }
1371
1413
 
1414
+ if (this.options.get('buttons').has('pageBreak') || this.options.get('buttons_sub')?.has('pageBreak')) {
1415
+ this._componentManager.push((element) => {
1416
+ if (!element || !domUtils.hasClass(element, 'se-page-break')) return null;
1417
+ return {
1418
+ target: element,
1419
+ launcher: {
1420
+ destroy: (target) => {
1421
+ const focusEl = target.previousElementSibling || target.nextElementSibling;
1422
+ domUtils.removeItem(target);
1423
+ // focus
1424
+ this.focusEdge(focusEl);
1425
+ this.history.push(false);
1426
+ }
1427
+ }
1428
+ };
1429
+ });
1430
+ }
1431
+
1372
1432
  this._fileManager.regExp = new RegExp(`^(${this._fileManager.tags.join('|') || '\\^'})$`, 'i');
1373
1433
  this._fileManager.pluginRegExp = new RegExp(`^(${filePluginRegExp.length === 0 ? '\\^' : filePluginRegExp.join('|')})$`, 'i');
1374
1434
 
@@ -144,3 +144,8 @@ export function FONT_STYLE(editor, command) {
144
144
  editor.format.applyInlineElement(el, StyleMap[command] || null, [nodeName], false);
145
145
  editor.focus();
146
146
  }
147
+
148
+ export function PAGE_BREAK(editor) {
149
+ const pageBreak = domUtils.createElement('DIV', { class: 'se-component se-component-line-break se-page-break' });
150
+ editor.component.insert(pageBreak, true, false);
151
+ }