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.
@@ -66,7 +66,7 @@ export const DEFAULTS = {
66
66
  col: 'width',
67
67
  'ol|ul': 'list-style-type'
68
68
  },
69
- TEXT_STYLES: 'font-family|font-size|color|background-color',
69
+ SPAN_STYLES: 'font-family|font-size|color|background-color|width|height',
70
70
  LINE_STYLES: 'text-align|margin-left|margin-right|line-height',
71
71
 
72
72
  RETAIN_STYLE_MODE: ['repeat', 'always', 'none']
@@ -145,8 +145,8 @@ export const DEFAULTS = {
145
145
  * @property {Object<string, string>} [__tagStyles=CONSTANTS.TAG_STYLES] - The basic tags that serves as the base for "tagStyles"
146
146
  * - The default follows {@link DEFAULTS.TAG_STYLES}
147
147
  * @property {Object<string, string>} [tagStyles={}] - Specifies allowed styles for HTML tags.
148
- * @property {string} [spanStyles=CONSTANTS.TEXT_STYLES] - Specifies allowed styles for the "span" tag.
149
- * - The default follows {@link DEFAULTS.TEXT_STYLES}
148
+ * @property {string} [spanStyles=CONSTANTS.SPAN_STYLES] - Specifies allowed styles for the "span" tag.
149
+ * - The default follows {@link DEFAULTS.SPAN_STYLES}
150
150
  * @property {string} [lineStyles=CONSTANTS.LINE_STYLES] - Specifies allowed styles for the "line" element (p..).
151
151
  * - The default follows {@link DEFAULTS.LINE_STYLES}
152
152
  * @property {string} [textDirection="ltr"] - Text direction: "ltr" or "rtl".
@@ -256,3 +256,116 @@ export const DEFAULTS = {
256
256
  /**
257
257
  * @typedef {EditorBaseOptions & EditorFrameOptions} EditorInitOptions
258
258
  */
259
+
260
+ /** ------------- [OPTIONS FRAG] ------------- */
261
+ /**
262
+ * @description For all EditorInitOptions keys, only boolean | null values are allowed.
263
+ * - 'fixed' → Immutable / null → Resettable.
264
+ * @type {Partial<Record<keyof EditorInitOptions, "fixed" | true>>}
265
+ */
266
+ export const OPTION_FRAME_FIXED_FLAG = {
267
+ value: 'fixed',
268
+ placeholder: true,
269
+ editableFrameAttributes: true,
270
+ width: true,
271
+ minWidth: true,
272
+ maxWidth: true,
273
+ height: true,
274
+ minHeight: true,
275
+ maxHeight: true,
276
+ editorStyle: true,
277
+ iframe: 'fixed',
278
+ iframe_fullPage: 'fixed',
279
+ iframe_attributes: true,
280
+ iframe_cssFileName: true,
281
+ statusbar: true,
282
+ statusbar_showPathLabel: true,
283
+ statusbar_resizeEnable: 'fixed',
284
+ charCounter: true,
285
+ charCounter_max: true,
286
+ charCounter_label: true,
287
+ charCounter_type: true
288
+ };
289
+ /**
290
+ * @description For all EditorInitOptions keys, only boolean | null values are allowed.
291
+ * - 'fixed' → Immutable / null → Resettable.
292
+ * @type {Partial<Record<keyof EditorInitOptions, "fixed" | true>>}
293
+ */
294
+ export const OPTION_FIXED_FLAG = {
295
+ plugins: 'fixed',
296
+ excludedPlugins: 'fixed',
297
+ buttonList: 'fixed',
298
+ v2Migration: 'fixed',
299
+ strictMode: 'fixed',
300
+ mode: 'fixed',
301
+ type: 'fixed',
302
+ theme: true,
303
+ lang: 'fixed',
304
+ fontSizeUnits: 'fixed',
305
+ allowedClassName: 'fixed',
306
+ closeModalOutsideClick: 'fixed',
307
+ copyFormatKeepOn: true,
308
+ syncTabIndent: true,
309
+ tabDisable: true,
310
+ autoLinkify: true,
311
+ autoStyleify: true,
312
+ scrollToOptions: true,
313
+ componentScrollToOptions: true,
314
+ retainStyleMode: true,
315
+ allowedExtraTags: 'fixed',
316
+ events: true,
317
+ __textStyleTags: 'fixed',
318
+ textStyleTags: 'fixed',
319
+ convertTextTags: 'fixed',
320
+ __tagStyles: 'fixed',
321
+ tagStyles: 'fixed',
322
+ spanStyles: 'fixed',
323
+ lineStyles: 'fixed',
324
+ textDirection: true,
325
+ reverseButtons: 'fixed',
326
+ historyStackDelayTime: true,
327
+ lineAttrReset: true,
328
+ printClass: true,
329
+ defaultLine: 'fixed',
330
+ defaultLineBreakFormat: true,
331
+ scopeSelectionTags: true,
332
+ __defaultElementWhitelist: 'fixed',
333
+ elementWhitelist: 'fixed',
334
+ elementBlacklist: 'fixed',
335
+ __defaultAttributeWhitelist: 'fixed',
336
+ attributeWhitelist: 'fixed',
337
+ attributeBlacklist: 'fixed',
338
+ __defaultFormatLine: 'fixed',
339
+ formatLine: 'fixed',
340
+ __defaultFormatBrLine: 'fixed',
341
+ formatBrLine: 'fixed',
342
+ __defaultFormatClosureBrLine: 'fixed',
343
+ formatClosureBrLine: 'fixed',
344
+ __defaultFormatBlock: 'fixed',
345
+ formatBlock: 'fixed',
346
+ __defaultFormatClosureBlock: 'fixed',
347
+ formatClosureBlock: 'fixed',
348
+ allowedEmptyTags: true,
349
+ toolbar_width: true,
350
+ toolbar_container: 'fixed',
351
+ toolbar_sticky: true,
352
+ toolbar_hide: true,
353
+ subToolbar: 'fixed',
354
+ statusbar_container: 'fixed',
355
+ shortcutsHint: true,
356
+ shortcutsDisable: 'fixed',
357
+ shortcuts: 'fixed',
358
+ fullScreenOffset: true,
359
+ previewTemplate: true,
360
+ printTemplate: true,
361
+ componentAutoSelect: true,
362
+ defaultUrlProtocol: true,
363
+ allUsedStyles: 'fixed',
364
+ toastMessageTime: true,
365
+ icons: 'fixed',
366
+ freeCodeViewMode: true,
367
+ __lineFormatFilter: true,
368
+ __pluginRetainFilter: true,
369
+ __listCommonStyle: 'fixed',
370
+ externalLibs: 'fixed'
371
+ };
@@ -28,7 +28,9 @@ const INDEX_1 = '2147483640';
28
28
  * @property {Array<HTMLElement>=} [parents=[]] The parent "controller" array when "controller" is opened nested.
29
29
  * @property {boolean=} [parentsHide=false] If true, the parent element is hidden when the controller is opened.
30
30
  * @property {HTMLElement=} [sibling=null] The related sibling controller element that this controller is positioned relative to.
31
- * @property {"top"|"side"} [siblingPosition="top"] The relative position of this controller to the sibling element (e.g., display above or beside the sibling).
31
+ * - e.g.) table plugin :: 118
32
+ * @property {boolean=} [siblingMain=false] If true, This sibling controller is the main controller.
33
+ * - You must specify this option, if use "sibling"
32
34
  * @property {boolean=} [isInsideForm=false] If the controller is inside a form, set it to true.
33
35
  * @property {boolean=} [isOutsideForm=false] If the controller is outside a form, set it to true.
34
36
  */
@@ -61,7 +63,7 @@ class Controller extends EditorInjector {
61
63
  this.parents = /** @type {Array<HTMLElement>} */ (params.parents || []);
62
64
  this.parentsHide = !!params.parentsHide;
63
65
  this.sibling = /** @type {HTMLElement} */ (params.sibling || null);
64
- this.siblingPosition = ['top', 'side'].includes(params.siblingPosition) ? params.siblingPosition : 'top';
66
+ this.siblingMain = !!params.siblingMain;
65
67
  this.isInsideForm = !!params.isInsideForm;
66
68
  this.isOutsideForm = !!params.isOutsideForm;
67
69
  this.toTop = false;
@@ -137,14 +139,6 @@ class Controller extends EditorInjector {
137
139
 
138
140
  this.__addGlobalEvent();
139
141
 
140
- // add sibling offset
141
- if (this.sibling) {
142
- if (this.siblingPosition === 'top') {
143
- this.__addOffset.top += -this.sibling.offsetHeight + 1;
144
- } else {
145
- this.__addOffset.left += this.form.offsetWidth + this.sibling.offsetWidth - 1;
146
- }
147
- }
148
142
  // display controller
149
143
  this._setControllerPosition(this.form, this.currentPositionTarget, false);
150
144
 
@@ -290,6 +284,11 @@ class Controller extends EditorInjector {
290
284
  controller.style.visibility = 'hidden';
291
285
  controller.style.display = 'block';
292
286
 
287
+ if (this.sibling && this.sibling.style.display !== 'block') {
288
+ this.sibling.style.visibility = 'hidden';
289
+ this.sibling.style.display = 'block';
290
+ }
291
+
293
292
  if (this.selection.isRange(refer)) {
294
293
  if (!this.offset.setRangePosition(this.form, /** @type {Range} */ (refer), { position: 'bottom' })) {
295
294
  this.hide();
@@ -297,18 +296,19 @@ class Controller extends EditorInjector {
297
296
  }
298
297
  } else {
299
298
  if (refer) {
300
- const positionResult = this.offset.setAbsPosition(controller, /** @type {HTMLElement} */ (refer), { addOffset: this.__addOffset, position: this.position, isWWTarget: this.isWWTarget, inst: this });
299
+ const positionResult = this.offset.setAbsPosition(controller, /** @type {HTMLElement} */ (refer), { addOffset: this.__addOffset, position: this.position, isWWTarget: this.isWWTarget, inst: this, sibling: this.sibling });
301
300
  if (!positionResult) {
302
301
  this.hide();
303
302
  return;
304
303
  }
305
304
 
306
- if (!skipAutoReposition && this.sibling && this.siblingPosition === 'top' && positionResult.position !== this.position) {
305
+ if (!skipAutoReposition && this.sibling && !this.siblingMain) {
307
306
  const resetPosition = controller.offsetTop - this.__addOffset.top;
308
307
  if (positionResult.position === 'bottom') {
309
308
  this._reserveIndex = true;
310
- controller.style.top = resetPosition + this.sibling.offsetHeight - 2 + 'px';
309
+ controller.style.top = resetPosition + this.sibling.offsetHeight - 1 + 'px';
311
310
  } else {
311
+ this._reserveIndex = false;
312
312
  controller.style.top = resetPosition - this.sibling.offsetHeight + 2 + 'px';
313
313
  }
314
314
  } else {
@@ -308,9 +308,7 @@ class Figure extends EditorInjector {
308
308
  return;
309
309
  }
310
310
 
311
- if (_DragHandle.get('__overInfo') !== ON_OVER_COMPONENT) {
312
- this.ui._offCurrentController();
313
- } else {
311
+ if (_DragHandle.get('__overInfo') === ON_OVER_COMPONENT) {
314
312
  nonBorder = true;
315
313
  }
316
314
 
@@ -523,7 +521,7 @@ class Figure extends EditorInjector {
523
521
 
524
522
  const w = !/%$/.test(target.style.width) ? target.style.width : ((figure.container && numbers.get(figure.container.style.width, 2)) || 100) + '%';
525
523
  const h = figure.inlineCover
526
- ? figure.inlineCover.style.height
524
+ ? figure.inlineCover.style.height || /** @type {HTMLElement} */ (targetNode).style.height || String(/** @type {HTMLImageElement} */ (targetNode).height || '')
527
525
  : numbers.get(figure.cover.style.paddingBottom, 0) > 0 && !this.isVertical
528
526
  ? figure.cover.style.height
529
527
  : !/%$/.test(target.style.height) || !/%$/.test(target.style.width)
@@ -575,6 +573,7 @@ class Figure extends EditorInjector {
575
573
  * @description As style[block, inline] the component
576
574
  * @param {?Node} targetNode Target element
577
575
  * @param {"block"|"inline"} formatStyle Format style
576
+ * @returns {HTMLElement} New target element after conversion
578
577
  */
579
578
  convertAsFormat(targetNode, formatStyle) {
580
579
  if (!targetNode) targetNode = this._element;
@@ -583,6 +582,10 @@ class Figure extends EditorInjector {
583
582
  const { w, h } = this.getSize(target);
584
583
 
585
584
  const newTarget = /** @type {HTMLElement} */ (target.cloneNode(false));
585
+ newTarget.style.width = '';
586
+ newTarget.style.height = '';
587
+ newTarget.removeAttribute('width');
588
+ newTarget.removeAttribute('height');
586
589
 
587
590
  switch (formatStyle) {
588
591
  case 'inline': {
@@ -592,8 +595,6 @@ class Figure extends EditorInjector {
592
595
  const next = container.nextElementSibling;
593
596
  const parent = container.parentElement;
594
597
 
595
- newTarget.style.width = '';
596
- newTarget.style.height = '';
597
598
  const figure = Figure.CreateInlineContainer(newTarget);
598
599
  dom.utils.addClass(
599
600
  figure.container,
@@ -623,8 +624,6 @@ class Figure extends EditorInjector {
623
624
  dom.utils.removeItem(s.previousElementSibling);
624
625
  }
625
626
 
626
- newTarget.style.width = '';
627
- newTarget.style.height = '';
628
627
  const figure = Figure.CreateContainer(newTarget);
629
628
  dom.utils.addClass(
630
629
  figure.container,
@@ -641,6 +640,8 @@ class Figure extends EditorInjector {
641
640
  break;
642
641
  }
643
642
  }
643
+
644
+ return newTarget;
644
645
  }
645
646
 
646
647
  /**
@@ -1208,8 +1209,10 @@ class Figure extends EditorInjector {
1208
1209
  e.stopPropagation();
1209
1210
  e.preventDefault();
1210
1211
 
1211
- const eventTarget = dom.query.getEventTarget(e);
1212
1212
  const inst = _DragHandle.get('__figureInst');
1213
+ if (!inst) return;
1214
+
1215
+ const eventTarget = dom.query.getEventTarget(e);
1213
1216
  const direction = (inst._resize_direction = eventTarget.classList[0]);
1214
1217
  inst._resizeClientX = e.clientX;
1215
1218
  inst._resizeClientY = e.clientY;
@@ -98,7 +98,7 @@ class SelectMenu extends CoreInjector {
98
98
  on(referElement, selectMethod, attr) {
99
99
  if (!attr) attr = {};
100
100
  this._refer = /** @type {HTMLElement} */ (referElement);
101
- this._keydownTarget = dom.check.isInputElement(referElement) ? referElement : this._w;
101
+ this._keydownTarget = dom.check.isInputElement(referElement) ? referElement : this.editor.frameContext.get('_ww');
102
102
  this._selectMethod = selectMethod;
103
103
  this.form = dom.utils.createElement(
104
104
  'DIV',
@@ -4,8 +4,8 @@ import { Controller, SelectMenu, ColorPicker, Figure, _DragHandle } from '../../
4
4
 
5
5
  const { _w, ON_OVER_COMPONENT } = env;
6
6
 
7
- const ROW_SELECT_MARGIN = 5;
8
- const CELL_SELECT_MARGIN = 2;
7
+ const ROW_SELECT_MARGIN = 6;
8
+ const CELL_SELECT_MARGIN = 6;
9
9
  const CELL_DECIMAL_END = 0;
10
10
 
11
11
  const RESIZE_CELL_CLASS = '.se-table-resize-line';
@@ -115,12 +115,17 @@ class Table extends EditorInjector {
115
115
  });
116
116
 
117
117
  // members - Controller
118
- this.controller_cell = new Controller(this, controller_cell.html, { position: this.cellControllerTop ? 'top' : 'bottom' });
119
- this.controller_table = new Controller(this, controller_table, { position: 'top' });
120
118
  if (this.cellControllerTop) {
119
+ this.controller_cell = new Controller(this, controller_cell.html, { position: 'top' });
120
+ this.controller_table = new Controller(this, controller_table, { position: 'top' });
121
121
  this.controller_cell.sibling = this.controller_table.form;
122
- this.controller_cell.siblingPosition = 'top';
122
+ this.controller_table.sibling = this.controller_cell.form;
123
+ this.controller_table.siblingMain = true;
124
+ } else {
125
+ this.controller_table = new Controller(this, controller_table, { position: 'top' });
126
+ this.controller_cell = new Controller(this, controller_cell.html, { position: 'bottom' });
123
127
  }
128
+
124
129
  // props
125
130
  const propsTargetForms = [this.controller_table.form, this.controller_cell.form];
126
131
  this.controller_props = new Controller(this, controller_props.html, { position: 'bottom', parents: propsTargetForms, isInsideForm: true });
@@ -428,24 +433,69 @@ class Table extends EditorInjector {
428
433
  // create colgroup
429
434
  if (!ColgroupEl) {
430
435
  const rows = element.rows;
431
- const firstRow = rows[0];
432
- const maxCount = GetMaxColumns(element);
433
- const colHTML = [];
436
+ const maxColumnCount = GetMaxColumns(element);
437
+ const colWidths = new Array(maxColumnCount).fill(null);
438
+
439
+ for (let r = 0, rLen = rows.length, cellsCount; r < rLen; r++) {
440
+ const cellsInRow = rows[r].cells;
441
+ cellsCount = cellsInRow.length;
442
+ let currentLogicalCol = 0;
443
+ const rowColOccupancy = new Array(maxColumnCount).fill(false);
444
+
445
+ for (let c = 0; c < cellsCount; c++) {
446
+ const cell = cellsInRow[c];
447
+ const cellWidth = cell.style.width;
448
+ const colSpan = cell.colSpan || 1;
449
+
450
+ while (currentLogicalCol < maxColumnCount && rowColOccupancy[currentLogicalCol]) {
451
+ currentLogicalCol++;
452
+ }
453
+
454
+ if (currentLogicalCol >= maxColumnCount) break;
455
+ if (cellWidth && !colWidths[currentLogicalCol]) colWidths[currentLogicalCol] = cellWidth;
434
456
 
435
- for (let i = 0; i < maxCount; i++) {
436
- let colStyle = '';
437
- if (firstRow && firstRow.cells[i]) {
438
- const styleWidth = firstRow.cells[i].style.width;
439
- if (styleWidth) {
440
- colStyle = ` style="width: ${styleWidth};"`;
441
- dom.utils.setStyle(firstRow.cells[i], 'width', '');
457
+ for (let i = 0; i < colSpan; i++) {
458
+ if (currentLogicalCol + i >= maxColumnCount || !cellWidth) continue;
459
+
460
+ rowColOccupancy[currentLogicalCol + i] = true;
461
+ const currentPxWidth = parseFloat(cellWidth);
462
+
463
+ for (let j = 0; j < colSpan; j++) {
464
+ const targetColIndex = currentLogicalCol + j;
465
+ if (targetColIndex >= maxColumnCount) continue;
466
+
467
+ const existingWidth = colWidths[targetColIndex];
468
+ if (existingWidth === null) {
469
+ colWidths[targetColIndex] = `width: ${cellWidth};`;
470
+ } else {
471
+ const existingPxWidth = parseFloat(existingWidth.replace('width: ', '').replace(';', ''));
472
+ if (colSpan === 1 && currentPxWidth !== existingPxWidth) {
473
+ colWidths[targetColIndex] = `width: ${cellWidth};`;
474
+ }
475
+ }
476
+ }
442
477
  }
478
+ currentLogicalCol += colSpan;
443
479
  }
480
+
481
+ if (cellsCount === maxColumnCount) break;
482
+ }
483
+
484
+ const colHTML = [];
485
+ for (let i = 0; i < maxColumnCount; i++) {
486
+ const colStyle = colWidths[i] ? ` style="${colWidths[i]}"` : '';
444
487
  colHTML.push(`<col${colStyle}>`);
445
488
  }
446
489
 
447
490
  const colGroup = dom.utils.createElement('colgroup', null, colHTML.join(''));
448
491
  element.insertBefore(colGroup, element.firstElementChild);
492
+
493
+ for (let r = 0; r < rows.length; r++) {
494
+ const cellsInRow = rows[r].cells;
495
+ for (let c = 0; c < cellsInRow.length; c++) {
496
+ dom.utils.setStyle(cellsInRow[c], 'width', '');
497
+ }
498
+ }
449
499
  }
450
500
 
451
501
  // figure
@@ -860,13 +910,17 @@ class Table extends EditorInjector {
860
910
  this._maxWidth = !this._maxWidth;
861
911
  this._setTableStyle('width', false);
862
912
  this._historyPush();
863
- this.component.select(this._element, Table.key, { isInput: true });
913
+ _w.setTimeout(() => {
914
+ this.component.select(this._element, Table.key, { isInput: true });
915
+ }, 0);
864
916
  break;
865
917
  case 'layout':
866
918
  this._fixedColumn = !this._fixedColumn;
867
919
  this._setTableStyle('column', false);
868
920
  this._historyPush();
869
- this.component.select(this._element, Table.key, { isInput: true });
921
+ _w.setTimeout(() => {
922
+ this.component.select(this._element, Table.key, { isInput: true });
923
+ }, 0);
870
924
  break;
871
925
  case 'copy':
872
926
  this.component.copy(this._figure);
@@ -2141,16 +2195,17 @@ class Table extends EditorInjector {
2141
2195
  /**
2142
2196
  * @private
2143
2197
  * @description Converts the width of <col> elements to percentages.
2198
+ * @param {HTMLTableElement} target - The target table element.
2144
2199
  */
2145
- _resizePercentCol() {
2146
- const cols = this._element.querySelector('colgroup').querySelectorAll('col');
2147
- const tableTotalWidth = this._element.offsetWidth;
2200
+ _resizePercentCol(target) {
2201
+ const cols = target.querySelector('colgroup').querySelectorAll('col');
2202
+ const tableTotalWidth = target.offsetWidth;
2148
2203
 
2149
2204
  cols.forEach((col) => {
2150
- const colWidthString = _w.getComputedStyle(col).getPropertyValue('width');
2205
+ const colWidthString = col.style.width;
2151
2206
 
2152
2207
  if (!colWidthString.endsWith('%')) {
2153
- const pixelWidth = numbers.get(colWidthString, 1);
2208
+ const pixelWidth = col.offsetWidth || numbers.get(colWidthString, CELL_DECIMAL_END);
2154
2209
  const percentage = (pixelWidth / tableTotalWidth) * 100;
2155
2210
  col.style.width = percentage + '%';
2156
2211
  }
@@ -2166,7 +2221,7 @@ class Table extends EditorInjector {
2166
2221
  * @param {boolean} isLeftEdge Whether the resizing is on the left edge.
2167
2222
  */
2168
2223
  _startCellResizing(col, startX, startWidth, isLeftEdge) {
2169
- this._resizePercentCol();
2224
+ this._resizePercentCol(this._element);
2170
2225
  this._setResizeLinePosition(this._figure, this._tdElement, this._resizeLinePrev, isLeftEdge);
2171
2226
  this._resizeLinePrev.style.display = 'block';
2172
2227
  const prevValue = col.style.width;
@@ -2192,7 +2247,7 @@ class Table extends EditorInjector {
2192
2247
  ),
2193
2248
  () => {
2194
2249
  this.__removeGlobalEvents();
2195
- this._resizePercentCol();
2250
+ this._resizePercentCol(this._element);
2196
2251
  this.history.push(true);
2197
2252
  this.component.select(this._element, Table.key, { isInput: true });
2198
2253
  },
@@ -91,7 +91,7 @@ class Image_ extends EditorInjector {
91
91
  ? [[ctrlAs, 'mirror_h', 'mirror_v', 'align', 'caption', 'edit', 'revert', 'copy', 'remove']]
92
92
  : [
93
93
  [ctrlAs, 'resize_auto,100,75,50', 'rotate_l', 'rotate_r', 'mirror_h', 'mirror_v'],
94
- ['align', 'caption', 'edit', 'revert', 'copy', 'remove']
94
+ ['edit', 'align', 'caption', 'revert', 'copy', 'remove']
95
95
  ];
96
96
 
97
97
  // show align
@@ -276,7 +276,7 @@ class Image_ extends EditorInjector {
276
276
  query: 'img',
277
277
  method: (element) => {
278
278
  const figureInfo = Figure.GetContainer(element);
279
- if (figureInfo && figureInfo.container && figureInfo.cover) return;
279
+ if (figureInfo && figureInfo.container && (figureInfo.cover || figureInfo.inlineCover)) return;
280
280
 
281
281
  this._ready(element);
282
282
  this._fileCheck(this._origin_w, this._origin_h);
@@ -585,6 +585,13 @@ class Image_ extends EditorInjector {
585
585
  if (!height) height = this.inputY?.value || 'auto';
586
586
 
587
587
  let imageEl = this._element;
588
+
589
+ // as (block | inline)
590
+ if ((this.as === 'block' && !this._cover) || (this.as === 'inline' && this._cover)) {
591
+ imageEl = this.figure.convertAsFormat(imageEl, this.as);
592
+ }
593
+
594
+ // --- update image ---
588
595
  const cover = this._cover;
589
596
  const container = this._container === this._cover ? null : this._container;
590
597
 
@@ -661,6 +668,8 @@ class Image_ extends EditorInjector {
661
668
  imageEl.onload = () => {
662
669
  this.select(imageEl);
663
670
  };
671
+
672
+ this._ready(imageEl);
664
673
  }
665
674
 
666
675
  /**
@@ -692,16 +701,6 @@ class Image_ extends EditorInjector {
692
701
  this.figure.open(imageEl, { nonResizing: true, nonSizeInfo: false, nonBorder: false, figureTarget: false, __fileManagerInfo: true });
693
702
  }
694
703
 
695
- // check size
696
- let changeSize;
697
- const x = numbers.is(width) ? width + this.sizeUnit : width;
698
- const y = numbers.is(height) ? height + this.sizeUnit : height;
699
- if (/%$/.test(imageEl.style.width)) {
700
- changeSize = x !== container.style.width || y !== container.style.height;
701
- } else {
702
- changeSize = x !== imageEl.style.width || y !== imageEl.style.height;
703
- }
704
-
705
704
  // alt
706
705
  imageEl.alt = this.altText.value;
707
706
 
@@ -749,9 +748,11 @@ class Image_ extends EditorInjector {
749
748
  }
750
749
 
751
750
  // size
752
- if (this._resizing && changeSize) {
753
- this._applySize(width, height);
754
- }
751
+ imageEl.style.width = '';
752
+ imageEl.style.height = '';
753
+ imageEl.removeAttribute('width');
754
+ imageEl.removeAttribute('height');
755
+ this._applySize(width, height);
755
756
 
756
757
  if (isNewAnchor) {
757
758
  if (!isNewContainer) {
@@ -765,7 +766,7 @@ class Image_ extends EditorInjector {
765
766
  }
766
767
 
767
768
  // transform
768
- if (modifiedCaption || (!this._onlyPercentage && changeSize)) {
769
+ if (modifiedCaption || !this._onlyPercentage) {
769
770
  if (/\d+/.test(imageEl.style.height) || (this.figure.isVertical && this.captionCheckEl.checked)) {
770
771
  if (/auto|%$/.test(width) || /auto|%$/.test(height)) {
771
772
  this.figure.deleteTransform(imageEl);
package/src/suneditor.js CHANGED
@@ -1,10 +1,10 @@
1
1
  import Editor from './core/editor';
2
2
 
3
- import EditorInjector from './editorInjector';
4
- import Plugins from './plugins';
5
- import Langs from './langs';
6
- import Modules from './modules';
7
- import Helper from './helper';
3
+ import editorInjector from './editorInjector';
4
+ import plugins from './plugins';
5
+ import langs from './langs';
6
+ import modules from './modules';
7
+ import helper from './helper';
8
8
 
9
9
  /**
10
10
  * @module SunEditorExports
@@ -21,27 +21,27 @@ import Helper from './helper';
21
21
  /**
22
22
  * Editor Injector module, Inject "editor" and basic frequently used objects by calling it with "call(this, editor)".
23
23
  */
24
- export { EditorInjector };
24
+ export { editorInjector };
25
25
 
26
26
  /**
27
27
  * Available editor plugins
28
28
  */
29
- export { Plugins };
29
+ export { plugins };
30
30
 
31
31
  /**
32
32
  * Editor modules
33
33
  */
34
- export { Modules };
34
+ export { modules };
35
35
 
36
36
  /**
37
37
  * Language packs for the editor
38
38
  */
39
- export { Langs };
39
+ export { langs };
40
40
 
41
41
  /**
42
42
  * Helper functions for the editor
43
43
  */
44
- export { Helper };
44
+ export { helper };
45
45
 
46
46
  /**
47
47
  * SunEditor Factory Object
@@ -62,7 +62,10 @@ export default {
62
62
 
63
63
  /**
64
64
  * Creates a new instance of the SunEditor
65
- * @param {Element|Object<string, {target: Element, options: EditorFrameOptions_suneditor}>} target - Target element or multi-root object
65
+ * @param {Element|string|Object<string, {target: Element, options: EditorFrameOptions_suneditor}>} target
66
+ * - Element: The direct DOM element to initialize the editor on.
67
+ * - string: A CSS selector string. The corresponding element is selected using `document.querySelector`.
68
+ * - Object: For multi-root setup. Each key maps to a config with `{target, options}`.
66
69
  * @param {EditorInitOptions_suneditor} options - Initialization options
67
70
  * @param {EditorInitOptions_suneditor} [_init_options] - Optional preset initialization options
68
71
  * @returns {Editor} - Instance of the SunEditor
@@ -90,7 +93,11 @@ export default {
90
93
  if (!target) throw Error("[SUNEDITOR.create.fail] suneditor requires textarea's element");
91
94
 
92
95
  const multiTargets = [];
93
- if (target.nodeType === 1) {
96
+ if (typeof target === 'string') {
97
+ const t = document.querySelector(target);
98
+ if (!t) throw Error(`[SUNEDITOR.create.fail]-[document.querySelector(${target})] Cannot find target element. Make sure "${target}" is a valid selector and exists in the document.`);
99
+ multiTargets.push({ key: null, target: t });
100
+ } else if (target.nodeType === 1) {
94
101
  multiTargets.push({ key: null, target: target });
95
102
  } else {
96
103
  let props;
@@ -134,6 +134,8 @@ declare namespace _default {
134
134
  export let side_menu_item: string;
135
135
  export let side_menu_folder_plus: string;
136
136
  export let alert_outline: string;
137
+ export let more_media: string;
138
+ export let more_view: string;
137
139
  export let more_text: string;
138
140
  export let more_paragraph: string;
139
141
  export let more_plus: string;
@@ -283,9 +283,9 @@ declare class EventManager {
283
283
  * @this {EventManagerThis}
284
284
  * @description Adjusts the position of the editor's toolbar, controllers, and other floating elements based on scroll position.
285
285
  * - Ensures UI elements maintain their intended relative positions when scrolling.
286
- * @param {Element} eventWysiwyg The wysiwyg event object containing scroll data
286
+ * @param {*} eventWysiwyg The wysiwyg event object containing scroll data (Window or element)
287
287
  */
288
- _moveContainer(this: Omit<EventManager & Partial<import('../../editorInjector').default>, 'eventManager'>, eventWysiwyg: Element): void;
288
+ _moveContainer(this: Omit<EventManager & Partial<import('../../editorInjector').default>, 'eventManager'>, eventWysiwyg: any): void;
289
289
  /**
290
290
  * @private
291
291
  * @this {EventManagerThis}
@@ -370,6 +370,7 @@ declare class Offset {
370
370
  * @param {{left:number, top:number}} [params.addOffset={left:0, top:0}] Additional offset
371
371
  * @param {"bottom"|"top"} [params.position="bottom"] Position ('bottom'|'top')
372
372
  * @param {*} params.inst Instance object of caller
373
+ * @param {HTMLElement} [params.sibling] The sibling controller element
373
374
  * @returns {{position: "top" | "bottom"} | undefined} Success -> {position: current position}
374
375
  */
375
376
  setAbsPosition(
@@ -384,6 +385,7 @@ declare class Offset {
384
385
  };
385
386
  position?: 'bottom' | 'top';
386
387
  inst: any;
388
+ sibling?: HTMLElement;
387
389
  }
388
390
  ):
389
391
  | {
@@ -459,7 +461,12 @@ declare class Offset {
459
461
  * @param {RectsInfo} targetRect Target rect object
460
462
  * @param {boolean} isTargetAbs Is target absolute position
461
463
  * @param {OffsetWWScrollInfo} wwScroll WYSIWYG scroll info
462
- * @returns {{rmt:number, rmb:number, rt:number}} Margin values (rmt: top margin, rmb: bottom margin, rt: Toolbar height offset adjustment)
464
+ * @returns {{rmt:number, rmb:number, rt:number, tMargin:number, bMargin:number}} Margin values
465
+ * - rmt: top margin to frame
466
+ * - rmb: bottom margin to frame
467
+ * - rt: Toolbar height offset adjustment
468
+ * - tMargin: top margin
469
+ * - bMargin: bottom margin
463
470
  */
464
471
  _getVMargin(
465
472
  this: Omit<Offset & Partial<import('../../editorInjector').default>, 'offset'>,
@@ -477,6 +484,8 @@ declare class Offset {
477
484
  rmt: number;
478
485
  rmb: number;
479
486
  rt: number;
487
+ tMargin: number;
488
+ bMargin: number;
480
489
  };
481
490
  /**
482
491
  * @private