suneditor 3.1.4 → 3.2.1
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/suneditor-contents.min.css +1 -1
- package/dist/suneditor.min.css +28 -1
- package/dist/suneditor.min.js +1 -1
- package/package.json +2 -1
- package/src/assets/design/color.css +12 -0
- package/src/assets/design/size.css +9 -2
- package/src/assets/icons/defaultIcons.js +96 -47
- package/src/assets/suneditor-contents.css +3 -2
- package/src/assets/suneditor.css +481 -31
- package/src/core/config/contextProvider.js +2 -1
- package/src/core/config/eventManager.js +41 -2
- package/src/core/config/optionProvider.js +23 -2
- package/src/core/editor.js +32 -14
- package/src/core/event/actions/index.js +76 -9
- package/src/core/event/effects/common.registry.js +2 -1
- package/src/core/event/effects/keydown.registry.js +169 -33
- package/src/core/event/effects/ruleHelpers.js +20 -6
- package/src/core/event/eventOrchestrator.js +162 -30
- package/src/core/event/handlers/handler_toolbar.js +4 -2
- package/src/core/event/handlers/handler_ww_dragDrop.js +10 -0
- package/src/core/event/handlers/handler_ww_input.js +84 -2
- package/src/core/event/handlers/handler_ww_key.js +82 -21
- package/src/core/event/handlers/handler_ww_mouse.js +45 -8
- package/src/core/event/ports.js +27 -13
- package/src/core/event/reducers/keydown.reducer.js +33 -5
- package/src/core/event/rules/keydown.rule.arrow.js +12 -2
- package/src/core/event/rules/keydown.rule.backspace.js +110 -15
- package/src/core/event/rules/keydown.rule.delete.js +62 -7
- package/src/core/event/rules/keydown.rule.enter.js +62 -31
- package/src/core/event/support/defaultLineManager.js +9 -2
- package/src/core/event/support/selectionState.js +13 -3
- package/src/core/kernel/coreKernel.js +1 -0
- package/src/core/logic/dom/char.js +13 -3
- package/src/core/logic/dom/format.js +171 -39
- package/src/core/logic/dom/html.js +350 -72
- package/src/core/logic/dom/inline.js +229 -34
- package/src/core/logic/dom/listFormat.js +111 -20
- package/src/core/logic/dom/nodeTransform.js +36 -11
- package/src/core/logic/dom/offset.js +116 -31
- package/src/core/logic/dom/selection.js +147 -29
- package/src/core/logic/panel/blockHandle.js +785 -0
- package/src/core/logic/panel/blockResolver.js +278 -0
- package/src/core/logic/panel/finder.js +16 -6
- package/src/core/logic/panel/menu.js +119 -18
- package/src/core/logic/panel/toolbar.js +75 -19
- package/src/core/logic/panel/viewer.js +76 -20
- package/src/core/logic/shell/_commandExecutor.js +44 -9
- package/src/core/logic/shell/commandDispatcher.js +46 -4
- package/src/core/logic/shell/component.js +102 -25
- package/src/core/logic/shell/focusManager.js +13 -3
- package/src/core/logic/shell/history.js +21 -6
- package/src/core/logic/shell/pluginManager.js +21 -13
- package/src/core/logic/shell/shortcuts.js +9 -1
- package/src/core/logic/shell/ui.js +139 -37
- package/src/core/schema/frameContext.js +34 -3
- package/src/core/schema/options.js +46 -4
- package/src/core/section/constructor.js +537 -106
- package/src/core/section/documentType.js +35 -6
- package/src/helper/converter.js +24 -7
- package/src/helper/dom/domCheck.js +25 -5
- package/src/helper/dom/domQuery.js +2 -1
- package/src/helper/dom/domUtils.js +17 -4
- package/src/helper/env.js +11 -2
- package/src/helper/keyCodeMap.js +6 -2
- package/src/helper/markdown.js +24 -5
- package/src/helper/msOffice.js +9 -2
- package/src/interfaces/plugins.js +1 -1
- package/src/langs/ckb.js +0 -1
- package/src/langs/cs.js +0 -1
- package/src/langs/da.js +0 -1
- package/src/langs/de.js +0 -1
- package/src/langs/en.js +0 -1
- package/src/langs/es.js +0 -1
- package/src/langs/fa.js +0 -1
- package/src/langs/fr.js +0 -1
- package/src/langs/he.js +0 -1
- package/src/langs/hu.js +0 -1
- package/src/langs/index.js +27 -1
- package/src/langs/it.js +0 -1
- package/src/langs/ja.js +0 -1
- package/src/langs/km.js +0 -1
- package/src/langs/ko.js +0 -1
- package/src/langs/lv.js +0 -1
- package/src/langs/nl.js +0 -1
- package/src/langs/pl.js +0 -1
- package/src/langs/pt_br.js +0 -1
- package/src/langs/ro.js +0 -1
- package/src/langs/ru.js +0 -1
- package/src/langs/se.js +0 -1
- package/src/langs/tr.js +0 -1
- package/src/langs/uk.js +0 -1
- package/src/langs/ur.js +0 -1
- package/src/langs/zh_cn.js +0 -1
- package/src/modules/contract/Browser.js +58 -14
- package/src/modules/contract/ColorPicker.js +12 -4
- package/src/modules/contract/Controller.js +37 -7
- package/src/modules/contract/Figure.js +156 -36
- package/src/modules/contract/HueSlider.js +27 -4
- package/src/modules/contract/Modal.js +50 -10
- package/src/modules/manager/FileManager.js +50 -8
- package/src/modules/ui/CommandMenu.js +597 -0
- package/src/modules/ui/ModalAnchorEditor.js +33 -6
- package/src/modules/ui/SelectMenu.js +481 -36
- package/src/modules/ui/index.js +1 -0
- package/src/plugins/browser/audioGallery.js +2 -1
- package/src/plugins/browser/fileBrowser.js +9 -2
- package/src/plugins/browser/fileGallery.js +2 -1
- package/src/plugins/browser/imageGallery.js +18 -3
- package/src/plugins/browser/videoGallery.js +13 -4
- package/src/plugins/command/blockquote.js +6 -1
- package/src/plugins/command/codeBlock.js +43 -5
- package/src/plugins/command/exportPDF.js +20 -4
- package/src/plugins/command/fileUpload.js +46 -11
- package/src/plugins/command/list_bulleted.js +6 -1
- package/src/plugins/command/list_numbered.js +6 -1
- package/src/plugins/dropdown/align.js +21 -33
- package/src/plugins/dropdown/backgroundColor.js +10 -2
- package/src/plugins/dropdown/blockStyle.js +17 -22
- package/src/plugins/dropdown/font.js +34 -34
- package/src/plugins/dropdown/hr.js +15 -43
- package/src/plugins/dropdown/layout.js +10 -27
- package/src/plugins/dropdown/lineHeight.js +10 -33
- package/src/plugins/dropdown/list.js +19 -25
- package/src/plugins/dropdown/paragraphStyle.js +16 -24
- package/src/plugins/dropdown/table/index.js +72 -17
- package/src/plugins/dropdown/table/render/table.html.js +3 -1
- package/src/plugins/dropdown/table/services/table.cell.js +33 -7
- package/src/plugins/dropdown/table/services/table.clipboard.js +5 -1
- package/src/plugins/dropdown/table/services/table.grid.js +40 -9
- package/src/plugins/dropdown/table/services/table.resize.js +55 -11
- package/src/plugins/dropdown/table/services/table.selection.js +27 -7
- package/src/plugins/dropdown/table/services/table.style.js +120 -20
- package/src/plugins/dropdown/template.js +13 -29
- package/src/plugins/dropdown/textStyle.js +22 -35
- package/src/plugins/field/autocomplete.js +8 -4
- package/src/plugins/field/slashCommand.js +297 -0
- package/src/plugins/index.js +3 -0
- package/src/plugins/input/fontSize.js +37 -8
- package/src/plugins/input/pageNavigator.js +7 -1
- package/src/plugins/modal/audio.js +71 -17
- package/src/plugins/modal/drawing.js +41 -11
- package/src/plugins/modal/embed.js +92 -22
- package/src/plugins/modal/image/index.js +114 -28
- package/src/plugins/modal/image/services/image.size.js +9 -2
- package/src/plugins/modal/image/services/image.upload.js +38 -4
- package/src/plugins/modal/link.js +9 -2
- package/src/plugins/modal/math.js +10 -2
- package/src/plugins/modal/video/index.js +112 -26
- package/src/plugins/modal/video/render/video.html.js +4 -1
- package/src/plugins/modal/video/services/video.size.js +15 -4
- package/src/plugins/modal/video/services/video.upload.js +10 -2
- package/src/plugins/popup/anchor.js +14 -2
- package/src/suneditor.js +8 -2
- package/src/themes/cobalt.css +12 -0
- package/src/themes/cream.css +12 -0
- package/src/themes/dark.css +12 -0
- package/src/themes/midnight.css +12 -0
- package/src/typedef.js +32 -21
- package/types/assets/icons/defaultIcons.d.ts +1 -0
- package/types/core/config/contextProvider.d.ts +7 -1
- package/types/core/config/eventManager.d.ts +37 -4
- package/types/core/config/optionProvider.d.ts +5 -1
- package/types/core/event/actions/index.d.ts +44 -6
- package/types/core/event/effects/keydown.registry.d.ts +37 -7
- package/types/core/event/effects/ruleHelpers.d.ts +6 -1
- package/types/core/event/eventOrchestrator.d.ts +13 -3
- package/types/core/event/handlers/handler_ww_clipboard.d.ts +15 -3
- package/types/core/event/handlers/handler_ww_dragDrop.d.ts +14 -2
- package/types/core/event/handlers/handler_ww_input.d.ts +10 -2
- package/types/core/event/handlers/handler_ww_key.d.ts +10 -2
- package/types/core/event/handlers/handler_ww_mouse.d.ts +25 -5
- package/types/core/event/ports.d.ts +11 -4
- package/types/core/event/reducers/keydown.reducer.d.ts +12 -3
- package/types/core/event/rules/keydown.rule.backspace.d.ts +6 -1
- package/types/core/kernel/store.d.ts +4 -1
- package/types/core/logic/dom/format.d.ts +32 -14
- package/types/core/logic/dom/html.d.ts +11 -3
- package/types/core/logic/dom/nodeTransform.d.ts +6 -6
- package/types/core/logic/panel/blockHandle.d.ts +64 -0
- package/types/core/logic/panel/blockResolver.d.ts +50 -0
- package/types/core/logic/panel/menu.d.ts +72 -3
- package/types/core/logic/shell/commandDispatcher.d.ts +8 -0
- package/types/core/logic/shell/component.d.ts +5 -3
- package/types/core/logic/shell/shortcuts.d.ts +10 -1
- package/types/core/logic/shell/ui.d.ts +7 -1
- package/types/core/schema/context.d.ts +7 -1
- package/types/core/schema/frameContext.d.ts +36 -4
- package/types/core/schema/options.d.ts +83 -2
- package/types/core/section/constructor.d.ts +38 -1
- package/types/events.d.ts +208 -32
- package/types/helper/converter.d.ts +4 -2
- package/types/helper/dom/domCheck.d.ts +12 -2
- package/types/helper/dom/domQuery.d.ts +35 -7
- package/types/helper/dom/domUtils.d.ts +4 -1
- package/types/interfaces/plugins.d.ts +2 -2
- package/types/langs/_Lang.d.ts +0 -1
- package/types/modules/contract/ColorPicker.d.ts +2 -2
- package/types/modules/contract/Controller.d.ts +7 -0
- package/types/modules/contract/Figure.d.ts +31 -4
- package/types/modules/ui/CommandMenu.d.ts +262 -0
- package/types/modules/ui/SelectMenu.d.ts +56 -3
- package/types/modules/ui/index.d.ts +1 -0
- package/types/plugins/browser/audioGallery.d.ts +1 -1
- package/types/plugins/browser/fileBrowser.d.ts +1 -1
- package/types/plugins/browser/fileGallery.d.ts +1 -1
- package/types/plugins/browser/imageGallery.d.ts +1 -1
- package/types/plugins/browser/videoGallery.d.ts +1 -1
- package/types/plugins/dropdown/align.d.ts +1 -1
- package/types/plugins/dropdown/blockStyle.d.ts +3 -1
- package/types/plugins/dropdown/table/index.d.ts +4 -1
- package/types/plugins/dropdown/table/services/table.clipboard.d.ts +5 -1
- package/types/plugins/dropdown/table/services/table.grid.d.ts +10 -2
- package/types/plugins/field/autocomplete.d.ts +0 -1
- package/types/plugins/field/slashCommand.d.ts +165 -0
- package/types/plugins/index.d.ts +3 -0
- package/types/plugins/modal/link.d.ts +4 -1
- package/types/typedef.d.ts +48 -23
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { dom, converter, keyCodeMap, env, numbers } from '../../../helper';
|
|
2
2
|
import { _DragHandle } from '../../../modules/ui';
|
|
3
|
+
import BlockHandle from '../panel/blockHandle.js';
|
|
3
4
|
import { COMMAND_BUTTONS } from './commandDispatcher';
|
|
4
5
|
|
|
5
6
|
const DISABLE_BUTTONS_CODEVIEW = `${COMMAND_BUTTONS}:not([class~="se-code-view-enabled"]):not([data-type="MORE"])`;
|
|
@@ -65,6 +66,12 @@ class UIManager {
|
|
|
65
66
|
*/
|
|
66
67
|
#lineBreaker_b = null;
|
|
67
68
|
|
|
69
|
+
/**
|
|
70
|
+
* @description Block handle instance
|
|
71
|
+
* @type {BlockHandle}
|
|
72
|
+
*/
|
|
73
|
+
#blockHandle = null;
|
|
74
|
+
|
|
68
75
|
/**
|
|
69
76
|
* @constructor
|
|
70
77
|
* @param {SunEditor.Kernel} kernel
|
|
@@ -216,10 +223,17 @@ class UIManager {
|
|
|
216
223
|
this.#options.set('textDirection', dir);
|
|
217
224
|
|
|
218
225
|
// update _editableClass / printClass
|
|
219
|
-
const editableClass = rtl
|
|
226
|
+
const editableClass = rtl
|
|
227
|
+
? this.#options.get('_editableClass').replace(/\s*se-rtl/, '') + ' se-rtl'
|
|
228
|
+
: this.#options.get('_editableClass').replace(/\s*se-rtl/, '');
|
|
220
229
|
this.#options.set('_editableClass', editableClass);
|
|
221
230
|
if (this.#options.get('printClass')) {
|
|
222
|
-
this.#options.set(
|
|
231
|
+
this.#options.set(
|
|
232
|
+
'printClass',
|
|
233
|
+
rtl
|
|
234
|
+
? this.#options.get('printClass').replace(/\s*se-rtl/, '') + ' se-rtl'
|
|
235
|
+
: this.#options.get('printClass').replace(/\s*se-rtl/, ''),
|
|
236
|
+
);
|
|
223
237
|
}
|
|
224
238
|
|
|
225
239
|
this.offCurrentController();
|
|
@@ -240,7 +254,10 @@ class UIManager {
|
|
|
240
254
|
dom.utils.addClass([this.#carrierWrapper, toolbarWrapper, statusbarWrapper], 'se-rtl');
|
|
241
255
|
} else {
|
|
242
256
|
this.#contextProvider.applyToRoots((e) => {
|
|
243
|
-
dom.utils.removeClass(
|
|
257
|
+
dom.utils.removeClass(
|
|
258
|
+
[e.get('topArea'), e.get('wysiwyg'), e.get('documentTypePageMirror')],
|
|
259
|
+
'se-rtl',
|
|
260
|
+
);
|
|
244
261
|
e.get('wysiwyg').removeAttribute('dir');
|
|
245
262
|
});
|
|
246
263
|
dom.utils.removeClass([this.#carrierWrapper, toolbarWrapper, statusbarWrapper], 'se-rtl');
|
|
@@ -249,7 +266,10 @@ class UIManager {
|
|
|
249
266
|
const lineNodes = dom.query.getListChildren(
|
|
250
267
|
fc.get('wysiwyg'),
|
|
251
268
|
(current) => {
|
|
252
|
-
return
|
|
269
|
+
return (
|
|
270
|
+
this.#$.format.isLine(current) &&
|
|
271
|
+
!!(current.style.marginRight || current.style.marginLeft || current.style.textAlign)
|
|
272
|
+
);
|
|
253
273
|
},
|
|
254
274
|
null,
|
|
255
275
|
);
|
|
@@ -277,16 +297,6 @@ class UIManager {
|
|
|
277
297
|
this.#reverseToolbarButtons(this.#context.get('toolbar_sub_buttonTray'));
|
|
278
298
|
}
|
|
279
299
|
|
|
280
|
-
// document type
|
|
281
|
-
if (fc.has('documentType_use_header')) {
|
|
282
|
-
if (rtl) fc.get('wrapper').appendChild(fc.get('documentTypeInner'));
|
|
283
|
-
else fc.get('wrapper').insertBefore(fc.get('documentTypeInner'), fc.get('wysiwygFrame'));
|
|
284
|
-
}
|
|
285
|
-
if (fc.has('documentType_use_page')) {
|
|
286
|
-
if (rtl) fc.get('wrapper').insertBefore(fc.get('documentTypePage'), fc.get('wysiwygFrame'));
|
|
287
|
-
else fc.get('wrapper').appendChild(fc.get('documentTypePage'));
|
|
288
|
-
}
|
|
289
|
-
|
|
290
300
|
if (this.#store.mode.isBalloon) this.#$.toolbar._showBalloon();
|
|
291
301
|
else if (this.#store.mode.isSubBalloon) this.#$.subToolbar._showBalloon();
|
|
292
302
|
} catch (e) {
|
|
@@ -397,7 +407,11 @@ class UIManager {
|
|
|
397
407
|
* @param {string} [rootKey] Root key
|
|
398
408
|
*/
|
|
399
409
|
showLoading(rootKey) {
|
|
400
|
-
const el = /** @type {HTMLElement} */ (
|
|
410
|
+
const el = /** @type {HTMLElement} */ (
|
|
411
|
+
(rootKey ? this.#frameRoots.get(rootKey).get('container') : this.#carrierWrapper).querySelector(
|
|
412
|
+
'.se-loading-box',
|
|
413
|
+
)
|
|
414
|
+
);
|
|
401
415
|
el.style.display = 'block';
|
|
402
416
|
el.showPopover?.();
|
|
403
417
|
}
|
|
@@ -407,7 +421,11 @@ class UIManager {
|
|
|
407
421
|
* @param {string} [rootKey] Root key
|
|
408
422
|
*/
|
|
409
423
|
hideLoading(rootKey) {
|
|
410
|
-
const el = /** @type {HTMLElement} */ (
|
|
424
|
+
const el = /** @type {HTMLElement} */ (
|
|
425
|
+
(rootKey ? this.#frameRoots.get(rootKey).get('container') : this.#carrierWrapper).querySelector(
|
|
426
|
+
'.se-loading-box',
|
|
427
|
+
)
|
|
428
|
+
);
|
|
411
429
|
el.hidePopover?.();
|
|
412
430
|
el.style.display = 'none';
|
|
413
431
|
}
|
|
@@ -423,7 +441,8 @@ class UIManager {
|
|
|
423
441
|
dom.utils.removeClass(this.alertModal, 'se-alert-error|se-alert-success');
|
|
424
442
|
if (type) dom.utils.addClass(this.alertModal, `se-alert-${type}`);
|
|
425
443
|
|
|
426
|
-
if (this.#closeSignal)
|
|
444
|
+
if (this.#closeSignal)
|
|
445
|
+
this.#bindAlertClick = this.#eventManager.addEvent(this.#alertInner, 'click', this.#closeListener[1]);
|
|
427
446
|
this.#bindClose &&= this.#eventManager.removeGlobalEvent(this.#bindClose);
|
|
428
447
|
this.#bindClose = this.#eventManager.addGlobalEvent('keydown', this.#closeListener[0]);
|
|
429
448
|
|
|
@@ -573,10 +592,31 @@ class UIManager {
|
|
|
573
592
|
/**
|
|
574
593
|
* @param {SunEditor.FrameContext} rt Root target[key] FrameContext
|
|
575
594
|
*/
|
|
595
|
+
/**
|
|
596
|
+
* @description Block handle instance for external access.
|
|
597
|
+
* @type {BlockHandle}
|
|
598
|
+
*/
|
|
599
|
+
get blockHandle() {
|
|
600
|
+
return this.#blockHandle;
|
|
601
|
+
}
|
|
602
|
+
|
|
576
603
|
reset(rt) {
|
|
577
604
|
rt.set('_editorHeight', rt.get('wysiwygFrame').offsetHeight);
|
|
578
605
|
this.#lineBreaker_t = rt.get('lineBreaker_t');
|
|
579
606
|
this.#lineBreaker_b = rt.get('lineBreaker_b');
|
|
607
|
+
|
|
608
|
+
// block handle init
|
|
609
|
+
const blockHandleOpt = this.#options.get('blockHandle');
|
|
610
|
+
if (blockHandleOpt && rt.get('blockHandleArea') && !this.#blockHandle) {
|
|
611
|
+
this.#blockHandle = new BlockHandle(
|
|
612
|
+
this.#$,
|
|
613
|
+
rt.get('blockHandleArea'),
|
|
614
|
+
rt.get('blockHandle'),
|
|
615
|
+
rt.get('blockHandlePlus'),
|
|
616
|
+
rt.get('blockHandleDrag'),
|
|
617
|
+
blockHandleOpt.menu,
|
|
618
|
+
);
|
|
619
|
+
}
|
|
580
620
|
}
|
|
581
621
|
|
|
582
622
|
/**
|
|
@@ -624,6 +664,9 @@ class UIManager {
|
|
|
624
664
|
this.#context.get('toolbar_sub_main').style.left = this.#$.subToolbar.balloonOffset.left - x + 'px';
|
|
625
665
|
}
|
|
626
666
|
|
|
667
|
+
// block handle scroll sync
|
|
668
|
+
this.#blockHandle?.syncScroll();
|
|
669
|
+
|
|
627
670
|
if (this.controllerTargetContext !== this.#frameContext.get('topArea')) {
|
|
628
671
|
this.offCurrentController();
|
|
629
672
|
}
|
|
@@ -724,7 +767,10 @@ class UIManager {
|
|
|
724
767
|
|
|
725
768
|
// change dir buttons
|
|
726
769
|
this.#$.commandDispatcher.applyTargets('dir', (e) => {
|
|
727
|
-
dom.utils.changeTxt(
|
|
770
|
+
dom.utils.changeTxt(
|
|
771
|
+
e.querySelector('.se-tooltip-text'),
|
|
772
|
+
this.#contextProvider.lang[rtl ? 'dir_ltr' : 'dir_rtl'],
|
|
773
|
+
);
|
|
728
774
|
dom.utils.changeElement(e.firstElementChild, icons[rtl ? 'dir_ltr' : 'dir_rtl']);
|
|
729
775
|
});
|
|
730
776
|
|
|
@@ -758,12 +804,22 @@ class UIManager {
|
|
|
758
804
|
_initToggleButtons() {
|
|
759
805
|
const ctx = this.#context;
|
|
760
806
|
|
|
761
|
-
this.#codeViewDisabledButtons = converter.nodeListToArray(
|
|
762
|
-
|
|
807
|
+
this.#codeViewDisabledButtons = converter.nodeListToArray(
|
|
808
|
+
ctx.get('toolbar_buttonTray').querySelectorAll(DISABLE_BUTTONS_CODEVIEW),
|
|
809
|
+
);
|
|
810
|
+
this.#controllerOnDisabledButtons = converter.nodeListToArray(
|
|
811
|
+
ctx.get('toolbar_buttonTray').querySelectorAll(DISABLE_BUTTONS_CONTROLLER),
|
|
812
|
+
);
|
|
763
813
|
|
|
764
814
|
if (this.#options.has('_subMode')) {
|
|
765
|
-
this.#codeViewDisabledButtons = this.#codeViewDisabledButtons.concat(
|
|
766
|
-
|
|
815
|
+
this.#codeViewDisabledButtons = this.#codeViewDisabledButtons.concat(
|
|
816
|
+
converter.nodeListToArray(ctx.get('toolbar_sub_buttonTray').querySelectorAll(DISABLE_BUTTONS_CODEVIEW)),
|
|
817
|
+
);
|
|
818
|
+
this.#controllerOnDisabledButtons = this.#controllerOnDisabledButtons.concat(
|
|
819
|
+
converter.nodeListToArray(
|
|
820
|
+
ctx.get('toolbar_sub_buttonTray').querySelectorAll(DISABLE_BUTTONS_CONTROLLER),
|
|
821
|
+
),
|
|
822
|
+
);
|
|
767
823
|
}
|
|
768
824
|
}
|
|
769
825
|
|
|
@@ -790,7 +846,10 @@ class UIManager {
|
|
|
790
846
|
* @returns {boolean}
|
|
791
847
|
*/
|
|
792
848
|
isButtonDisabled(button) {
|
|
793
|
-
if (
|
|
849
|
+
if (
|
|
850
|
+
this.#frameContext.get('isReadOnly') &&
|
|
851
|
+
dom.utils.arrayIncludes(this.#controllerOnDisabledButtons, button)
|
|
852
|
+
) {
|
|
794
853
|
return true;
|
|
795
854
|
}
|
|
796
855
|
return false;
|
|
@@ -804,20 +863,45 @@ class UIManager {
|
|
|
804
863
|
*/
|
|
805
864
|
_updatePlaceholder(fc) {
|
|
806
865
|
fc ||= this.#frameContext;
|
|
807
|
-
const
|
|
866
|
+
const isCodeView = fc.get('isMarkdownView') || fc.get('isCodeView');
|
|
867
|
+
|
|
868
|
+
const lineShown = !isCodeView && this.#updateLinePlaceholder(fc);
|
|
808
869
|
|
|
870
|
+
const placeholder = fc.get('placeholder');
|
|
809
871
|
if (placeholder) {
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
}
|
|
872
|
+
placeholder.style.display = !isCodeView && !lineShown && this.#$.facade.isEmpty(fc) ? 'block' : 'none';
|
|
873
|
+
}
|
|
874
|
+
}
|
|
814
875
|
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
876
|
+
/**
|
|
877
|
+
* @internal
|
|
878
|
+
* @description Notion-style per-line placeholder. Marks the focused empty line so a CSS `::before`
|
|
879
|
+
* renders the hint text on it.
|
|
880
|
+
* @param {SunEditor.FrameContext} fc - Frame context
|
|
881
|
+
* @returns {boolean} Whether the line placeholder is currently shown.
|
|
882
|
+
*/
|
|
883
|
+
#updateLinePlaceholder(fc) {
|
|
884
|
+
const text = fc.get('placeholder_line');
|
|
885
|
+
if (!text) return false;
|
|
886
|
+
|
|
887
|
+
const wysiwyg = fc.get('wysiwyg');
|
|
888
|
+
const prev = wysiwyg.querySelector('.se-placeholder-line');
|
|
889
|
+
|
|
890
|
+
const line = this.#store.get('hasFocus') ? this.#$.format.getLine(this.#$.selection.selectionNode) : null;
|
|
891
|
+
const target = dom.check.isEmptyLine(line) && !dom.check.isListCell(line) ? line : null;
|
|
892
|
+
|
|
893
|
+
// Single-marker invariant: drop the previous marker unless it is still the target line.
|
|
894
|
+
if (prev && prev !== target) {
|
|
895
|
+
prev.classList.remove('se-placeholder-line');
|
|
896
|
+
prev.removeAttribute('data-se-placeholder-line');
|
|
897
|
+
if (!prev.getAttribute('class')) prev.removeAttribute('class');
|
|
820
898
|
}
|
|
899
|
+
|
|
900
|
+
if (!target) return false;
|
|
901
|
+
|
|
902
|
+
target.classList.add('se-placeholder-line');
|
|
903
|
+
target.setAttribute('data-se-placeholder-line', text);
|
|
904
|
+
return true;
|
|
821
905
|
}
|
|
822
906
|
|
|
823
907
|
/**
|
|
@@ -887,10 +971,17 @@ class UIManager {
|
|
|
887
971
|
h === -1
|
|
888
972
|
? resizeObserverEntry?.borderBoxSize && resizeObserverEntry.borderBoxSize[0]
|
|
889
973
|
? resizeObserverEntry.borderBoxSize[0].blockSize
|
|
890
|
-
: resizeObserverEntry.contentRect.height +
|
|
974
|
+
: resizeObserverEntry.contentRect.height +
|
|
975
|
+
numbers.get(fc.get('wwComputedStyle').getPropertyValue('padding-left')) +
|
|
976
|
+
numbers.get(fc.get('wwComputedStyle').getPropertyValue('padding-right'))
|
|
891
977
|
: h;
|
|
892
978
|
if (fc.get('_editorHeight') !== h) {
|
|
893
|
-
this.#eventManager.triggerEvent('onResizeEditor', {
|
|
979
|
+
this.#eventManager.triggerEvent('onResizeEditor', {
|
|
980
|
+
height: h,
|
|
981
|
+
prevHeight: fc.get('_editorHeight'),
|
|
982
|
+
frameContext: fc,
|
|
983
|
+
observerEntry: resizeObserverEntry,
|
|
984
|
+
});
|
|
894
985
|
fc.set('_editorHeight', h);
|
|
895
986
|
}
|
|
896
987
|
|
|
@@ -901,7 +992,11 @@ class UIManager {
|
|
|
901
992
|
}
|
|
902
993
|
|
|
903
994
|
init() {
|
|
904
|
-
this.#closeSignal = !this.#eventManager.addEvent(
|
|
995
|
+
this.#closeSignal = !this.#eventManager.addEvent(
|
|
996
|
+
this.alertModal.querySelector('[data-command="close"]'),
|
|
997
|
+
'click',
|
|
998
|
+
this.alertClose.bind(this),
|
|
999
|
+
);
|
|
905
1000
|
this._initToggleButtons();
|
|
906
1001
|
}
|
|
907
1002
|
|
|
@@ -943,13 +1038,20 @@ class UIManager {
|
|
|
943
1038
|
this.opendBrowser = null;
|
|
944
1039
|
this.#lineBreaker_t = null;
|
|
945
1040
|
this.#lineBreaker_b = null;
|
|
1041
|
+
this.#blockHandle?.destroy();
|
|
1042
|
+
this.#blockHandle = null;
|
|
946
1043
|
this.#controllerOnDisabledButtons = null;
|
|
947
1044
|
this.#codeViewDisabledButtons = null;
|
|
948
1045
|
}
|
|
949
1046
|
}
|
|
950
1047
|
|
|
951
1048
|
function CreateAlertHTML({ lang, icons }) {
|
|
952
|
-
const html =
|
|
1049
|
+
const html =
|
|
1050
|
+
'<div><button class="close" data-command="close" title="' +
|
|
1051
|
+
lang.close +
|
|
1052
|
+
'">' +
|
|
1053
|
+
icons.cancel +
|
|
1054
|
+
'</button></div><div><span></span></div>';
|
|
953
1055
|
return dom.utils.createElement('DIV', { class: 'se-alert-content' }, html);
|
|
954
1056
|
}
|
|
955
1057
|
|
|
@@ -34,6 +34,7 @@ import { get as getNumber } from '../../helper/numbers';
|
|
|
34
34
|
* @property {HTMLTextAreaElement} markdown - Markdown view editing element (a <textarea>).
|
|
35
35
|
* @property {HTMLTextAreaElement} markdownNumbers - Element displaying line numbers in markdown view mode.
|
|
36
36
|
* @property {HTMLElement} placeholder - Placeholder element shown when the editor is empty.
|
|
37
|
+
* @property {string} placeholder_line - Per-line placeholder text, rendered via a `::before` on the focused empty line.
|
|
37
38
|
* @property {HTMLElement} statusbar - Editor status bar element (for resizing, info, etc.).
|
|
38
39
|
* @property {HTMLElement} navigation - Navigation element (e.g., for outline or bookmarks).
|
|
39
40
|
* @property {HTMLElement} charWrapper - Wrapper for the character counter element.
|
|
@@ -46,6 +47,10 @@ import { get as getNumber } from '../../helper/numbers';
|
|
|
46
47
|
* === UI Utilities & Visual Components ===
|
|
47
48
|
* @property {HTMLElement} lineBreaker_t - Top floating line-breaker UI element (for line insertion).
|
|
48
49
|
* @property {HTMLElement} lineBreaker_b - Bottom floating line-breaker UI element (for line insertion).
|
|
50
|
+
* @property {?HTMLElement} blockHandleArea - Gutter container (`.se-block-handle-area`) holding the per-line block handle. `null` when the `blockHandle` option is disabled.
|
|
51
|
+
* @property {?HTMLElement} blockHandle - Block handle group (`.se-block-handle`) containing the plus and drag buttons. `null` when disabled.
|
|
52
|
+
* @property {?HTMLElement} blockHandlePlus - Plus button (`.se-block-handle-plus`) — inserts a new line after the current block. `null` when disabled.
|
|
53
|
+
* @property {?HTMLElement} blockHandleDrag - Drag button (`.se-block-handle-drag`) — opens the action menu / starts drag-to-reorder. `null` when disabled.
|
|
49
54
|
* @property {HTMLElement} [_stickyDummy] - Placeholder element used for sticky toolbar behavior.
|
|
50
55
|
* @property {HTMLElement} [_toolbarShadow] - Shadow element below the toolbar for visual effects.
|
|
51
56
|
* @property {{main: HTMLElement, border: HTMLElement, display: HTMLElement, handles: HTMLElement[]}} [_figure] - Current active figure component (image, table, etc.).
|
|
@@ -91,12 +96,27 @@ import { get as getNumber } from '../../helper/numbers';
|
|
|
91
96
|
* @param {HTMLElement} wwFrame Editor wysiwyg frame
|
|
92
97
|
* @param {HTMLElement} codeWrapper Editor code view wrapper
|
|
93
98
|
* @param {HTMLElement} codeFrame Editor code view frame
|
|
94
|
-
* @param {
|
|
99
|
+
* @param {?HTMLElement} markdownWrapper Editor markdown view wrapper (only present when the markdown view button is enabled)
|
|
100
|
+
* @param {?HTMLElement} markdownFrame Editor markdown view frame (only present when the markdown view button is enabled)
|
|
95
101
|
* @param {?HTMLElement} statusbar Editor statusbar
|
|
102
|
+
* @param {{inner: HTMLElement, page: HTMLElement, pageMirror: HTMLElement}} documentTypeInner Document type elements
|
|
103
|
+
* @param {?HTMLElement} blockHandleArea Block handle gutter (`.se-block-handle-area`). `null` when the `blockHandle` option is disabled.
|
|
96
104
|
* @param {*} key root key
|
|
97
105
|
* @returns {FrameContexType}
|
|
98
106
|
*/
|
|
99
|
-
export function CreateFrameContext(
|
|
107
|
+
export function CreateFrameContext(
|
|
108
|
+
editorTarget,
|
|
109
|
+
top,
|
|
110
|
+
wwFrame,
|
|
111
|
+
codeWrapper,
|
|
112
|
+
codeFrame,
|
|
113
|
+
markdownWrapper,
|
|
114
|
+
markdownFrame,
|
|
115
|
+
statusbar,
|
|
116
|
+
documentTypeInner,
|
|
117
|
+
blockHandleArea,
|
|
118
|
+
key,
|
|
119
|
+
) {
|
|
100
120
|
const m = /** @type {FrameContexType} */ (
|
|
101
121
|
new Map([
|
|
102
122
|
['key', key],
|
|
@@ -114,10 +134,18 @@ export function CreateFrameContext(editorTarget, top, wwFrame, codeWrapper, code
|
|
|
114
134
|
// markdown
|
|
115
135
|
['markdownWrapper', markdownWrapper],
|
|
116
136
|
['markdown', markdownFrame],
|
|
117
|
-
[
|
|
137
|
+
[
|
|
138
|
+
'markdownNumbers',
|
|
139
|
+
/** @type {HTMLTextAreaElement} */ (markdownWrapper?.querySelector('.se-markdown-view-line')),
|
|
140
|
+
],
|
|
118
141
|
// linebreak, toolbar internal
|
|
119
142
|
['lineBreaker_t', top.querySelector('.se-line-breaker-component-t')],
|
|
120
143
|
['lineBreaker_b', top.querySelector('.se-line-breaker-component-b')],
|
|
144
|
+
// block handle
|
|
145
|
+
['blockHandleArea', blockHandleArea],
|
|
146
|
+
['blockHandle', blockHandleArea?.querySelector('.se-block-handle') || null],
|
|
147
|
+
['blockHandlePlus', blockHandleArea?.querySelector('.se-block-handle-plus') || null],
|
|
148
|
+
['blockHandleDrag', blockHandleArea?.querySelector('.se-block-handle-drag') || null],
|
|
121
149
|
['_stickyDummy', top.querySelector('.se-toolbar-sticky-dummy')],
|
|
122
150
|
['_toolbarShadow', top.querySelector('.se-toolbar-shadow')],
|
|
123
151
|
['_minHeight', getNumber(wwFrame.style.minHeight || '65', 0)],
|
|
@@ -157,6 +185,9 @@ export function CreateFrameContext(editorTarget, top, wwFrame, codeWrapper, code
|
|
|
157
185
|
const placeholder = top.querySelector('.se-placeholder');
|
|
158
186
|
if (placeholder) m.set('placeholder', placeholder);
|
|
159
187
|
|
|
188
|
+
const placeholderLine = editorTarget.options.get('placeholder_line');
|
|
189
|
+
if (placeholderLine) m.set('placeholder_line', placeholderLine);
|
|
190
|
+
|
|
160
191
|
return m;
|
|
161
192
|
}
|
|
162
193
|
|
|
@@ -39,7 +39,8 @@ export const DEFAULTS = {
|
|
|
39
39
|
FORMAT_BLOCK: 'BLOCKQUOTE|OL|UL|FIGCAPTION|TABLE|THEAD|TBODY|TR|CAPTION|DETAILS',
|
|
40
40
|
FORMAT_CLOSURE_BLOCK: 'TH|TD',
|
|
41
41
|
|
|
42
|
-
ALLOWED_EMPTY_NODE_LIST:
|
|
42
|
+
ALLOWED_EMPTY_NODE_LIST:
|
|
43
|
+
'.se-component, pre, blockquote, hr, li, table, img, iframe, video, audio, canvas, details',
|
|
43
44
|
|
|
44
45
|
SIZE_UNITS: ['px', 'pt', 'em', 'rem'],
|
|
45
46
|
|
|
@@ -69,7 +70,8 @@ export const DEFAULTS = {
|
|
|
69
70
|
TAG_STYLES: {
|
|
70
71
|
'@text': 'font-family|font-size|color|background-color|width|height',
|
|
71
72
|
'@line': 'text-align|margin|margin-left|margin-right|line-height',
|
|
72
|
-
'table|th|td':
|
|
73
|
+
'table|th|td':
|
|
74
|
+
'border|border-[a-z]+|color|background-color|text-align|float|font-weight|text-decoration|font-style|vertical-align',
|
|
73
75
|
'table|td': 'width',
|
|
74
76
|
tr: 'height',
|
|
75
77
|
col: 'width',
|
|
@@ -77,7 +79,8 @@ export const DEFAULTS = {
|
|
|
77
79
|
'ol|ul': 'list-style-type',
|
|
78
80
|
figure: 'display|width|height|padding|padding-bottom',
|
|
79
81
|
figcaption: 'margin|margin-top|margin-bottom|text-align',
|
|
80
|
-
'img|video|iframe':
|
|
82
|
+
'img|video|iframe':
|
|
83
|
+
'transform|transform-origin|width|min-width|max-width|height|min-height|max-height|float|margin|margin-top',
|
|
81
84
|
hr: '',
|
|
82
85
|
},
|
|
83
86
|
|
|
@@ -99,7 +102,9 @@ export const DEFAULTS = {
|
|
|
99
102
|
*
|
|
100
103
|
* === Content & Editing ===
|
|
101
104
|
* @property {string} [value=""] - Initial value for the editor.
|
|
102
|
-
* @property {string} [placeholder=""] - Placeholder text.
|
|
105
|
+
* @property {string} [placeholder=""] - Placeholder text shown when the whole editor is empty.
|
|
106
|
+
* @property {string} [placeholder_line=""] - per-line placeholder shown on the focused
|
|
107
|
+
* line when that line is empty. Takes priority over `placeholder` while a line is focused.
|
|
103
108
|
* @property {Object<string, string>} [editableFrameAttributes={spellcheck: "false"}] - Attributes for the editable frame[.sun-editor-editable].
|
|
104
109
|
* ```js
|
|
105
110
|
* { editableFrameAttributes: { spellcheck: 'true', autocomplete: 'on' } }
|
|
@@ -110,6 +115,13 @@ export const DEFAULTS = {
|
|
|
110
115
|
* @property {string|number} [width="100%"] - Width for the editor.
|
|
111
116
|
* @property {string|number} [minWidth=""] - Min width for the editor.
|
|
112
117
|
* @property {string|number} [maxWidth=""] - Max width for the editor.
|
|
118
|
+
* @property {string} [innerWidth=""] - Optional max-width for the editor body (`.se-wrapper`).
|
|
119
|
+
* - **CSS length** (`"740px"`, `"60ch"`): editor body's content is clamped to this width and centered horizontally; scroll container stays full width (scrollbar at the outer edge, wheel/touch active over the whole area).
|
|
120
|
+
* - **`"auto"`** or **empty string** (default): no constraint — natural full-width layout with default content padding.
|
|
121
|
+
* - In `blockHandle` mode, the gutter handle tracks the centered text band.
|
|
122
|
+
* ```js
|
|
123
|
+
* { innerWidth: '740px' }
|
|
124
|
+
* ```
|
|
113
125
|
* @property {string|number} [height="auto"] - Height for the editor.
|
|
114
126
|
* @property {string|number} [minHeight=""] - Min height for the editor.
|
|
115
127
|
* @property {string|number} [maxHeight=""] - Max height for the editor.
|
|
@@ -287,6 +299,22 @@ export const DEFAULTS = {
|
|
|
287
299
|
* === Modes & Themes ===
|
|
288
300
|
* @property {boolean} [v2Migration=false] - Enables migration mode for SunEditor v2.
|
|
289
301
|
* @property {"classic"|"inline"|"balloon"|"balloon-always"|"classic:bottom"|"inline:bottom"} [mode="classic"] - Toolbar mode: `classic`, `inline`, `balloon`, `balloon-always`. Append `:bottom` to place toolbar at the bottom (e.g. `classic:bottom`, `inline:bottom`).
|
|
302
|
+
* @property {Object} [blockHandle] - Block handle configuration. When provided, a per-line block handle UI is shown. Works independently of `mode`.
|
|
303
|
+
* @property {Array<string | { title: string, icon?: string, action: (deps: SunEditor.Deps, ctx: { block: HTMLElement }) => void }>} [blockHandle.menu] - Menu entries.
|
|
304
|
+
* - **String entries** — resolved via `ResolveButton`. Accepts the same values as toolbar `buttonList`:
|
|
305
|
+
* - **Block format keys**: `p`, `h1`-`h6`, `heading` (H1-H6 submenu), `ul`, `ol`, `list` (UL/OL submenu), `blockquote`, `pre`.
|
|
306
|
+
* - **Plugin names**: Any registered plugin name (e.g. `blockStyle`, `align`, `image`, `link`). Dropdown plugins display their items as a submenu. Modal plugins open the modal on click.
|
|
307
|
+
* - **Built-in commands**: `bold`, `italic`, `underline`, `strike`, `undo`, `redo`, etc.
|
|
308
|
+
* - **Custom item objects** — `{ title, icon?, action }`. `icon` is an icon key from `$.icons` or a raw `<svg>` HTML string; `action` is invoked with `($, { block })`.
|
|
309
|
+
* - Defaults to `['p', 'heading', 'list', 'blockquote', 'pre']`.
|
|
310
|
+
* ```js
|
|
311
|
+
* blockHandle: {
|
|
312
|
+
* menu: [
|
|
313
|
+
* 'p', 'heading', 'blockStyle',
|
|
314
|
+
* { title: 'Duplicate', icon: 'copy', action: ($, { block }) => block.after(block.cloneNode(true)) },
|
|
315
|
+
* ],
|
|
316
|
+
* }
|
|
317
|
+
* ```
|
|
290
318
|
* @property {string} [type=""] - Editor type. Use `"document"` for a document-style layout, with optional sub-types after `:`.
|
|
291
319
|
* ```js
|
|
292
320
|
* // type
|
|
@@ -470,6 +498,15 @@ export const DEFAULTS = {
|
|
|
470
498
|
* @property {boolean} [syncTabIndent=true] - Synchronizes tab indent with spaces.
|
|
471
499
|
* @property {boolean} [tabDisable=false] - Disables tab key input.
|
|
472
500
|
* @property {string} [toolbar_width="auto"] - Toolbar width.
|
|
501
|
+
* @property {string} [toolbar_innerWidth=""] - Center the toolbar's button row (`.se-btn-tray`) horizontally.
|
|
502
|
+
* - **CSS length** (e.g. `"500px"`, `"740px"`, `"60ch"`): toolbar background spans full width while the button row is capped at that width and centered. If buttons fit, the row stays on a single line; if they exceed the cap, they wrap inside it.
|
|
503
|
+
* - **`"auto"`**: no cap — the row is sized to its content and simply centered.
|
|
504
|
+
* - **Empty string** (default): no constraint.
|
|
505
|
+
* - **Note on `responsiveButtonList`**: thresholds compare against the toolbar's full width, not the band. When this option is set, calibrate `responsiveButtonList` keys around the toolbar size you actually want each button set to kick in at.
|
|
506
|
+
* ```js
|
|
507
|
+
* { toolbar_innerWidth: '740px' }
|
|
508
|
+
* { toolbar_innerWidth: 'auto' }
|
|
509
|
+
* ```
|
|
473
510
|
* @property {?HTMLElement} [toolbar_container] - Container element for the toolbar.
|
|
474
511
|
* @property {number|{top: number, offset: number}} [toolbar_sticky=0] - Enables sticky toolbar.
|
|
475
512
|
* - `number`: Sets the sticky top position (px). Use `-1` to disable sticky.
|
|
@@ -604,6 +641,7 @@ export const DEFAULTS = {
|
|
|
604
641
|
* @property {import('../../plugins/modal/link.js').LinkPluginOptions} [link]
|
|
605
642
|
* @property {import('../../plugins/modal/math.js').MathPluginOptions} [math]
|
|
606
643
|
* @property {import('../../plugins/dropdown/paragraphStyle.js').ParagraphStylePluginOptions} [paragraphStyle]
|
|
644
|
+
* @property {import('../../plugins/field/slashCommand.js').SlashCommandPluginOptions} [slashCommand]
|
|
607
645
|
* @property {import('../../plugins/dropdown/table/index.js').TablePluginOptions} [table]
|
|
608
646
|
* @property {import('../../plugins/dropdown/template.js').TemplatePluginOptions} [template]
|
|
609
647
|
* @property {import('../../plugins/dropdown/textStyle.js').TextStylePluginOptions} [textStyle]
|
|
@@ -671,10 +709,12 @@ export const DEFAULTS = {
|
|
|
671
709
|
export const OPTION_FRAME_FIXED_FLAG = {
|
|
672
710
|
value: 'fixed',
|
|
673
711
|
placeholder: true,
|
|
712
|
+
placeholder_line: true,
|
|
674
713
|
editableFrameAttributes: true,
|
|
675
714
|
width: true,
|
|
676
715
|
minWidth: true,
|
|
677
716
|
maxWidth: true,
|
|
717
|
+
innerWidth: 'fixed',
|
|
678
718
|
height: true,
|
|
679
719
|
minHeight: true,
|
|
680
720
|
maxHeight: true,
|
|
@@ -706,6 +746,7 @@ export const OPTION_FIXED_FLAG = {
|
|
|
706
746
|
v2Migration: 'fixed',
|
|
707
747
|
strictMode: 'fixed',
|
|
708
748
|
mode: 'fixed',
|
|
749
|
+
blockHandle: 'fixed',
|
|
709
750
|
type: 'fixed',
|
|
710
751
|
theme: true,
|
|
711
752
|
lang: 'fixed',
|
|
@@ -752,6 +793,7 @@ export const OPTION_FIXED_FLAG = {
|
|
|
752
793
|
formatClosureBlock: 'fixed',
|
|
753
794
|
allowedEmptyTags: true,
|
|
754
795
|
toolbar_width: true,
|
|
796
|
+
toolbar_innerWidth: 'fixed',
|
|
755
797
|
toolbar_container: 'fixed',
|
|
756
798
|
toolbar_sticky: true,
|
|
757
799
|
toolbar_hide: true,
|