suneditor 3.0.0-beta.2 → 3.0.0-beta.20

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 (177) hide show
  1. package/CONTRIBUTING.md +186 -184
  2. package/LICENSE +21 -21
  3. package/README.md +157 -180
  4. package/dist/suneditor.min.css +1 -1
  5. package/dist/suneditor.min.js +1 -1
  6. package/package.json +126 -123
  7. package/src/assets/design/color.css +131 -121
  8. package/src/assets/design/index.css +3 -3
  9. package/src/assets/design/size.css +37 -35
  10. package/src/assets/design/typography.css +37 -37
  11. package/src/assets/icons/defaultIcons.js +247 -232
  12. package/src/assets/suneditor-contents.css +779 -778
  13. package/src/assets/suneditor.css +43 -35
  14. package/src/core/base/eventHandlers/handler_toolbar.js +135 -135
  15. package/src/core/base/eventHandlers/handler_ww_clipboard.js +56 -56
  16. package/src/core/base/eventHandlers/handler_ww_dragDrop.js +115 -113
  17. package/src/core/base/eventHandlers/handler_ww_key_input.js +1200 -1200
  18. package/src/core/base/eventHandlers/handler_ww_mouse.js +194 -194
  19. package/src/core/base/eventManager.js +1550 -1484
  20. package/src/core/base/history.js +355 -355
  21. package/src/core/class/char.js +163 -162
  22. package/src/core/class/component.js +856 -842
  23. package/src/core/class/format.js +3433 -3422
  24. package/src/core/class/html.js +1927 -1890
  25. package/src/core/class/menu.js +357 -346
  26. package/src/core/class/nodeTransform.js +424 -424
  27. package/src/core/class/offset.js +858 -891
  28. package/src/core/class/selection.js +710 -620
  29. package/src/core/class/shortcuts.js +98 -98
  30. package/src/core/class/toolbar.js +438 -430
  31. package/src/core/class/ui.js +424 -422
  32. package/src/core/class/viewer.js +750 -750
  33. package/src/core/editor.js +1810 -1708
  34. package/src/core/section/actives.js +268 -241
  35. package/src/core/section/constructor.js +1348 -1661
  36. package/src/core/section/context.js +102 -102
  37. package/src/core/section/documentType.js +582 -561
  38. package/src/core/section/options.js +367 -0
  39. package/src/core/util/instanceCheck.js +59 -0
  40. package/src/editorInjector/_classes.js +36 -36
  41. package/src/editorInjector/_core.js +92 -92
  42. package/src/editorInjector/index.js +75 -75
  43. package/src/events.js +634 -622
  44. package/src/helper/clipboard.js +59 -59
  45. package/src/helper/converter.js +586 -564
  46. package/src/helper/dom/domCheck.js +304 -304
  47. package/src/helper/dom/domQuery.js +677 -669
  48. package/src/helper/dom/domUtils.js +618 -557
  49. package/src/helper/dom/index.js +12 -12
  50. package/src/helper/env.js +249 -240
  51. package/src/helper/index.js +25 -25
  52. package/src/helper/keyCodeMap.js +183 -183
  53. package/src/helper/numbers.js +72 -72
  54. package/src/helper/unicode.js +47 -47
  55. package/src/langs/ckb.js +231 -231
  56. package/src/langs/cs.js +231 -231
  57. package/src/langs/da.js +231 -231
  58. package/src/langs/de.js +231 -231
  59. package/src/langs/en.js +230 -230
  60. package/src/langs/es.js +231 -231
  61. package/src/langs/fa.js +231 -231
  62. package/src/langs/fr.js +231 -231
  63. package/src/langs/he.js +231 -231
  64. package/src/langs/hu.js +230 -230
  65. package/src/langs/index.js +28 -28
  66. package/src/langs/it.js +231 -231
  67. package/src/langs/ja.js +230 -230
  68. package/src/langs/km.js +230 -230
  69. package/src/langs/ko.js +230 -230
  70. package/src/langs/lv.js +231 -231
  71. package/src/langs/nl.js +231 -231
  72. package/src/langs/pl.js +231 -231
  73. package/src/langs/pt_br.js +231 -231
  74. package/src/langs/ro.js +231 -231
  75. package/src/langs/ru.js +231 -231
  76. package/src/langs/se.js +231 -231
  77. package/src/langs/tr.js +231 -231
  78. package/src/langs/uk.js +231 -231
  79. package/src/langs/ur.js +231 -231
  80. package/src/langs/zh_cn.js +231 -231
  81. package/src/modules/ApiManager.js +191 -191
  82. package/src/modules/Browser.js +669 -667
  83. package/src/modules/ColorPicker.js +364 -362
  84. package/src/modules/Controller.js +474 -454
  85. package/src/modules/Figure.js +1620 -1617
  86. package/src/modules/FileManager.js +359 -359
  87. package/src/modules/HueSlider.js +577 -565
  88. package/src/modules/Modal.js +346 -346
  89. package/src/modules/ModalAnchorEditor.js +643 -643
  90. package/src/modules/SelectMenu.js +549 -549
  91. package/src/modules/_DragHandle.js +17 -17
  92. package/src/modules/index.js +14 -14
  93. package/src/plugins/browser/audioGallery.js +83 -83
  94. package/src/plugins/browser/fileBrowser.js +103 -103
  95. package/src/plugins/browser/fileGallery.js +83 -83
  96. package/src/plugins/browser/imageGallery.js +81 -81
  97. package/src/plugins/browser/videoGallery.js +103 -103
  98. package/src/plugins/command/blockquote.js +61 -60
  99. package/src/plugins/command/exportPDF.js +134 -134
  100. package/src/plugins/command/fileUpload.js +456 -456
  101. package/src/plugins/command/list_bulleted.js +149 -148
  102. package/src/plugins/command/list_numbered.js +152 -151
  103. package/src/plugins/dropdown/align.js +157 -155
  104. package/src/plugins/dropdown/backgroundColor.js +108 -104
  105. package/src/plugins/dropdown/font.js +141 -137
  106. package/src/plugins/dropdown/fontColor.js +109 -105
  107. package/src/plugins/dropdown/formatBlock.js +170 -178
  108. package/src/plugins/dropdown/hr.js +152 -152
  109. package/src/plugins/dropdown/layout.js +83 -83
  110. package/src/plugins/dropdown/lineHeight.js +131 -130
  111. package/src/plugins/dropdown/list.js +123 -122
  112. package/src/plugins/dropdown/paragraphStyle.js +138 -138
  113. package/src/plugins/dropdown/table.js +4110 -4000
  114. package/src/plugins/dropdown/template.js +83 -83
  115. package/src/plugins/dropdown/textStyle.js +149 -149
  116. package/src/plugins/field/mention.js +242 -242
  117. package/src/plugins/index.js +120 -120
  118. package/src/plugins/input/fontSize.js +414 -410
  119. package/src/plugins/input/pageNavigator.js +71 -70
  120. package/src/plugins/modal/audio.js +677 -677
  121. package/src/plugins/modal/drawing.js +537 -531
  122. package/src/plugins/modal/embed.js +886 -886
  123. package/src/plugins/modal/image.js +1377 -1376
  124. package/src/plugins/modal/link.js +248 -240
  125. package/src/plugins/modal/math.js +563 -563
  126. package/src/plugins/modal/video.js +1226 -1226
  127. package/src/plugins/popup/anchor.js +224 -222
  128. package/src/suneditor.js +114 -107
  129. package/src/themes/dark.css +132 -122
  130. package/src/typedef.js +132 -130
  131. package/types/assets/icons/defaultIcons.d.ts +8 -0
  132. package/types/core/base/eventManager.d.ts +29 -4
  133. package/types/core/class/char.d.ts +2 -1
  134. package/types/core/class/component.d.ts +1 -2
  135. package/types/core/class/format.d.ts +8 -1
  136. package/types/core/class/html.d.ts +8 -0
  137. package/types/core/class/menu.d.ts +8 -0
  138. package/types/core/class/offset.d.ts +24 -26
  139. package/types/core/class/selection.d.ts +2 -0
  140. package/types/core/class/toolbar.d.ts +6 -0
  141. package/types/core/class/ui.d.ts +1 -1
  142. package/types/core/editor.d.ts +34 -12
  143. package/types/core/section/constructor.d.ts +5 -638
  144. package/types/core/section/documentType.d.ts +12 -2
  145. package/types/core/section/options.d.ts +740 -0
  146. package/types/core/util/instanceCheck.d.ts +50 -0
  147. package/types/editorInjector/_core.d.ts +5 -5
  148. package/types/editorInjector/index.d.ts +2 -2
  149. package/types/events.d.ts +2 -0
  150. package/types/helper/converter.d.ts +9 -0
  151. package/types/helper/dom/domQuery.d.ts +5 -5
  152. package/types/helper/dom/domUtils.d.ts +8 -0
  153. package/types/helper/env.d.ts +6 -1
  154. package/types/helper/index.d.ts +4 -1
  155. package/types/index.d.ts +122 -120
  156. package/types/langs/_Lang.d.ts +194 -194
  157. package/types/modules/ColorPicker.d.ts +5 -1
  158. package/types/modules/Controller.d.ts +8 -4
  159. package/types/modules/Figure.d.ts +2 -1
  160. package/types/modules/HueSlider.d.ts +4 -1
  161. package/types/modules/SelectMenu.d.ts +1 -1
  162. package/types/plugins/command/blockquote.d.ts +1 -0
  163. package/types/plugins/command/list_bulleted.d.ts +1 -0
  164. package/types/plugins/command/list_numbered.d.ts +1 -0
  165. package/types/plugins/dropdown/align.d.ts +1 -0
  166. package/types/plugins/dropdown/backgroundColor.d.ts +1 -0
  167. package/types/plugins/dropdown/font.d.ts +1 -0
  168. package/types/plugins/dropdown/fontColor.d.ts +1 -0
  169. package/types/plugins/dropdown/formatBlock.d.ts +3 -2
  170. package/types/plugins/dropdown/lineHeight.d.ts +1 -0
  171. package/types/plugins/dropdown/list.d.ts +1 -0
  172. package/types/plugins/dropdown/table.d.ts +6 -0
  173. package/types/plugins/input/fontSize.d.ts +1 -0
  174. package/types/plugins/modal/drawing.d.ts +4 -0
  175. package/types/plugins/modal/link.d.ts +32 -15
  176. package/types/suneditor.d.ts +13 -9
  177. package/types/typedef.d.ts +8 -0
@@ -1,422 +1,424 @@
1
- /**
2
- * @fileoverview UI class
3
- */
4
-
5
- import CoreInjector from '../../editorInjector/_core';
6
- import { dom, converter, keyCodeMap, env } from '../../helper';
7
- const { _w } = env;
8
-
9
- /**
10
- * @typedef {Omit<UI & Partial<__se__EditorInjector>, 'ui'>} UIThis
11
- */
12
-
13
- /**
14
- * @constructor
15
- * @this {UIThis}
16
- * @description The UI class is a class that handles operations related to the user interface of SunEditor.
17
- * - This class sets the editor's style, theme, editor mode, etc., and controls the state of various UI elements.
18
- * @param {__se__EditorCore} editor - The root editor instance
19
- */
20
- function UI(editor) {
21
- CoreInjector.call(this, editor);
22
-
23
- // members
24
- this._controllerOnBtnDisabled = false;
25
-
26
- // members - modal
27
- const alertModal = CreateAlertHTML(editor);
28
- this.alertModal = alertModal;
29
- this.alertMessage = alertModal.querySelector('span');
30
- this._alertArea = /** @type {HTMLElement} */ (this.carrierWrapper.querySelector('.se-alert'));
31
- this._alertInner = /** @type {HTMLElement} */ (this.carrierWrapper.querySelector('.se-alert .se-modal-inner'));
32
- this._alertInner.appendChild(alertModal);
33
-
34
- this._closeListener = [CloseListener.bind(this), OnClick_alert.bind(this)];
35
- this._closeSignal = !this.eventManager.addEvent(alertModal.querySelector('[data-command="close"]'), 'click', this.alertClose.bind(this));
36
- this._bindClose = null;
37
- this._backWrapper = /** @type {HTMLElement} */ (this.carrierWrapper.querySelector('.se-back-wrapper'));
38
-
39
- // members - toast
40
- const toastPopup = CreateToastHTML();
41
- this.toastPopup = toastPopup;
42
- this.toastContainer = toastPopup.querySelector('.se-toast-container');
43
- this.toastMessage = toastPopup.querySelector('span');
44
- this.carrierWrapper.appendChild(toastPopup);
45
- this._toastToggle = null;
46
- }
47
-
48
- UI.prototype = {
49
- /**
50
- * @this {UIThis}
51
- * @description Set "options.get('editorStyle')" style.
52
- * - Define the style of the edit area
53
- * - It can also be defined with the "setOptions" method, but the "setEditorStyle" method does not render the editor again.
54
- * @param {string} style Style string
55
- * @param {__se__FrameContext|null} fc Frame context
56
- */
57
- setEditorStyle(style, fc) {
58
- fc = fc || this.editor.frameContext;
59
- const fo = fc.get('options');
60
-
61
- const newStyles = converter._setDefaultOptionStyle(fo, style);
62
- fo.set('_defaultStyles', newStyles);
63
-
64
- // top area
65
- fc.get('topArea').style.cssText = newStyles.top;
66
-
67
- // code view
68
- const code = fc.get('code');
69
- code.style.cssText = fo.get('_defaultStyles').frame;
70
- code.style.display = 'none';
71
-
72
- // wysiwyg frame
73
- if (!fo.get('iframe')) {
74
- fc.get('wysiwygFrame').style.cssText = newStyles.frame + newStyles.editor;
75
- } else {
76
- fc.get('wysiwygFrame').style.cssText = newStyles.frame;
77
- fc.get('wysiwyg').style.cssText = newStyles.editor;
78
- }
79
- },
80
-
81
- /**
82
- * @this {UIThis}
83
- * @description Set the theme to the editor
84
- * @param {string} theme Theme name
85
- */
86
- setTheme(theme) {
87
- if (typeof theme !== 'string') return;
88
- const o = this.options;
89
- const prevTheme = o.get('_themeClass').trim();
90
- o.set('theme', theme || '');
91
- o.set('_themeClass', theme ? ` se-theme-${theme}` : '');
92
- theme = o.get('_themeClass').trim();
93
-
94
- const applyTheme = (target) => {
95
- if (!target) return;
96
- if (prevTheme) dom.utils.removeClass(target, prevTheme);
97
- if (theme) dom.utils.addClass(target, theme);
98
- };
99
-
100
- applyTheme(this.carrierWrapper);
101
- this.editor.applyFrameRoots((e) => {
102
- applyTheme(e.get('topArea'));
103
- applyTheme(e.get('wysiwyg'));
104
- });
105
-
106
- applyTheme(this.context.get('statusbar._wrapper'));
107
- applyTheme(this.context.get('toolbar._wrapper'));
108
- },
109
-
110
- /**
111
- * @this {UIThis}
112
- * @description Switch to or off "ReadOnly" mode.
113
- * @param {boolean} value "readOnly" boolean value.
114
- * @param {string|undefined} rootKey Root key
115
- */
116
- readOnly(value, rootKey) {
117
- const fc = rootKey ? this.frameRoots.get(rootKey) : this.editor.frameContext;
118
-
119
- fc.set('isReadOnly', !!value);
120
- dom.utils.setDisabled(this.editor._controllerOnDisabledButtons, !!value);
121
-
122
- if (value) {
123
- this._offCurrentController();
124
- this._offCurrentModal();
125
-
126
- if (this.toolbar?.currentMoreLayerActiveButton?.disabled) this.toolbar._moreLayerOff();
127
- if (this.subToolbar?.currentMoreLayerActiveButton?.disabled) this.subToolbar._moreLayerOff();
128
- if (this.menu?.currentDropdownActiveButton?.disabled) this.menu.dropdownOff();
129
- if (this.menu?.currentContainerActiveButton?.disabled) this.menu.containerOff();
130
-
131
- fc.get('code').setAttribute('readOnly', 'true');
132
- dom.utils.addClass(fc.get('wysiwyg'), 'se-read-only');
133
- } else {
134
- fc.get('code').removeAttribute('readOnly');
135
- dom.utils.removeClass(fc.get('wysiwyg'), 'se-read-only');
136
- }
137
-
138
- if (this.options.get('hasCodeMirror')) {
139
- this.viewer._codeMirrorEditor('readonly', !!value, rootKey);
140
- }
141
- },
142
-
143
- /**
144
- * @this {UIThis}
145
- * @description Disable the suneditor
146
- * @param {string|undefined} rootKey Root key
147
- */
148
- disable(rootKey) {
149
- const fc = rootKey ? this.frameRoots.get(rootKey) : this.editor.frameContext;
150
-
151
- this.toolbar.disable();
152
- this._offCurrentController();
153
- this._offCurrentModal();
154
-
155
- fc.get('wysiwyg').setAttribute('contenteditable', false);
156
- fc.set('isDisabled', true);
157
-
158
- if (this.options.get('hasCodeMirror')) {
159
- this.viewer._codeMirrorEditor('readonly', true, rootKey);
160
- } else {
161
- fc.get('code').disabled = true;
162
- }
163
- },
164
-
165
- /**
166
- * @this {UIThis}
167
- * @description Enable the suneditor
168
- * @param {string|undefined} rootKey Root key
169
- */
170
- enable(rootKey) {
171
- const fc = rootKey ? this.frameRoots.get(rootKey) : this.editor.frameContext;
172
-
173
- this.toolbar.enable();
174
- fc.get('wysiwyg').setAttribute('contenteditable', true);
175
- fc.set('isDisabled', false);
176
-
177
- if (this.options.get('hasCodeMirror')) {
178
- this.viewer._codeMirrorEditor('readonly', false, rootKey);
179
- } else {
180
- fc.get('code').disabled = false;
181
- }
182
- },
183
-
184
- /**
185
- * @this {UIThis}
186
- * @description Show the suneditor
187
- * @param {string|undefined} rootKey Root key
188
- */
189
- show(rootKey) {
190
- const fc = rootKey ? this.frameRoots.get(rootKey) : this.editor.frameContext;
191
- const topAreaStyle = fc.get('topArea').style;
192
- if (topAreaStyle.display === 'none') topAreaStyle.display = 'block';
193
- },
194
-
195
- /**
196
- * @this {UIThis}
197
- * @description Hide the suneditor
198
- * @param {string|undefined} rootKey Root key
199
- */
200
- hide(rootKey) {
201
- const fc = rootKey ? this.frameRoots.get(rootKey) : this.editor.frameContext;
202
- fc.get('topArea').style.display = 'none';
203
- },
204
-
205
- /**
206
- * @this {UIThis}
207
- * @description Show loading box
208
- * @param {string=} rootKey Root key
209
- */
210
- showLoading(rootKey) {
211
- (rootKey ? this.frameRoots.get(rootKey).get('container') : this.carrierWrapper).querySelector('.se-loading-box').style.display = 'block';
212
- },
213
-
214
- /**
215
- * @this {UIThis}
216
- * @description Hide loading box
217
- * @param {string=} rootKey Root key
218
- */
219
- hideLoading(rootKey) {
220
- (rootKey ? this.frameRoots.get(rootKey).get('container') : this.carrierWrapper).querySelector('.se-loading-box').style.display = 'none';
221
- },
222
-
223
- /**
224
- * @this {UIThis}
225
- * @description This method disables or enables the toolbar buttons when the controller is activated or deactivated.
226
- * - When the controller is activated, the toolbar buttons are disabled; when the controller is deactivated, the buttons are enabled.
227
- * @param {boolean} active If `true`, the toolbar buttons will be disabled. If `false`, the toolbar buttons will be enabled.
228
- */
229
- setControllerOnDisabledButtons(active) {
230
- if (active && !this._controllerOnBtnDisabled) {
231
- dom.utils.setDisabled(this.editor._controllerOnDisabledButtons, true);
232
- this._controllerOnBtnDisabled = true;
233
- } else if (!active && this._controllerOnBtnDisabled) {
234
- dom.utils.setDisabled(this.editor._controllerOnDisabledButtons, false);
235
- this._controllerOnBtnDisabled = false;
236
- }
237
- },
238
-
239
- /**
240
- * @this {UIThis}
241
- * @description Activate the transparent background "div" so that other elements are not affected during resizing.
242
- * @param {string} cursor cursor css property
243
- */
244
- enableBackWrapper(cursor) {
245
- this._backWrapper.style.cursor = cursor;
246
- this._backWrapper.style.display = 'block';
247
- },
248
-
249
- /**
250
- * @this {UIThis}
251
- * @description Disabled background "div"
252
- */
253
- disableBackWrapper() {
254
- this._backWrapper.style.display = 'none';
255
- this._backWrapper.style.cursor = 'default';
256
- },
257
-
258
- /**
259
- * @this {UIThis}
260
- * @description Open the alert panel
261
- * @param {string} text alert message
262
- * @param {""|"error"|"success"} type alert type
263
- */
264
- alertOpen(text, type) {
265
- this.alertMessage.textContent = text;
266
-
267
- dom.utils.removeClass(this.alertModal, 'se-alert-error|se-alert-success');
268
- if (type) dom.utils.addClass(this.alertModal, `se-alert-${type}`);
269
-
270
- if (this._closeSignal) this._alertInner.addEventListener('click', this._closeListener[1]);
271
- if (this._bindClose) this._bindClose = this.eventManager.removeGlobalEvent(this._bindClose);
272
- this._bindClose = this.eventManager.addGlobalEvent('keydown', this._closeListener[0]);
273
-
274
- this._alertArea.style.display = 'block';
275
- dom.utils.addClass(this.alertModal, 'se-modal-show');
276
- },
277
-
278
- /**
279
- * @this {UIThis}
280
- * @description Close the alert panel
281
- */
282
- alertClose() {
283
- dom.utils.removeClass(this.alertModal, 'se-modal-show');
284
- dom.utils.removeClass(this.alertModal, 'se-alert-*');
285
- this._alertArea.style.display = 'none';
286
- if (this._closeSignal) this._alertInner.removeEventListener('click', this._closeListener[1]);
287
- if (this._bindClose) this._bindClose = this.eventManager.removeGlobalEvent(this._bindClose);
288
- },
289
-
290
- /**
291
- * @description Show toast
292
- * @param {string} message toast message
293
- * @param {number} [duration=1000] duration time(ms)
294
- * @param {""|"error"|"success"} [type=""] duration time(ms)
295
- */
296
- showToast(message, duration = 1000, type) {
297
- if (dom.utils.hasClass(this.toastContainer, 'se-toast-show')) {
298
- this.closeToast();
299
- }
300
-
301
- dom.utils.removeClass(this.toastPopup, 'se-toast-error|se-toast-success');
302
- if (type) dom.utils.addClass(this.toastPopup, `se-toast-${type}`);
303
-
304
- this.toastPopup.style.display = 'block';
305
- this.toastMessage.textContent = message;
306
- dom.utils.addClass(this.toastContainer, 'se-toast-show');
307
-
308
- // remove after animation
309
- this._toastToggle = _w.setTimeout(() => {
310
- this.closeToast();
311
- }, duration);
312
- },
313
-
314
- /**
315
- * @description Close toast
316
- */
317
- closeToast() {
318
- if (this._toastToggle) _w.clearTimeout(this._toastToggle);
319
- this._toastToggle = null;
320
- dom.utils.removeClass(this.toastContainer, 'se-toast-show');
321
- this.toastPopup.style.display = 'none';
322
- },
323
-
324
- /**
325
- * @private
326
- * @this {UIThis}
327
- * @description visible controllers
328
- * @param {boolean} value hidden/show
329
- * @param {?boolean=} lineBreakShow Line break hidden/show (default: Follows the value "value".)
330
- */
331
- _visibleControllers(value, lineBreakShow) {
332
- const visible = value ? '' : 'hidden';
333
- const breakerVisible = lineBreakShow ?? visible ? '' : 'hidden';
334
-
335
- const cont = this.editor.opendControllers;
336
- for (let i = 0, c; i < cont.length; i++) {
337
- c = cont[i];
338
- if (c.form) c.form.style.visibility = visible;
339
- }
340
-
341
- this.editor._lineBreaker_t.style.visibility = breakerVisible;
342
- this.editor._lineBreaker_b.style.visibility = breakerVisible;
343
- },
344
-
345
- /**
346
- * @private
347
- * @this {UIThis}
348
- * @description Off current controllers
349
- */
350
- _offCurrentController() {
351
- this.component.__deselect();
352
- },
353
-
354
- /**
355
- * @private
356
- * @this {UIThis}
357
- * @description Off controllers
358
- */
359
- __offControllers() {
360
- const cont = this.editor.opendControllers;
361
- const fixedCont = [];
362
- for (let i = 0, c; i < cont.length; i++) {
363
- c = cont[i];
364
- if (c.fixed) {
365
- fixedCont.push(c);
366
- continue;
367
- }
368
- if (typeof c.inst.close === 'function') c.inst.close();
369
- if (c.form) c.form.style.display = 'none';
370
- }
371
- this.editor.opendControllers = fixedCont;
372
- this.editor.currentControllerName = '';
373
- this.editor._preventBlur = false;
374
- },
375
-
376
- /**
377
- * @private
378
- * @this {UIThis}
379
- * @description Off current modal
380
- */
381
- _offCurrentModal() {
382
- if (this.editor.opendModal) {
383
- this.editor.opendModal.close();
384
- }
385
- },
386
-
387
- constructor: UI
388
- };
389
-
390
- /**
391
- * @private
392
- * @this {UIThis}
393
- * @param {MouseEvent} e - Event object
394
- */
395
- function OnClick_alert(e) {
396
- const eventTarget = dom.query.getEventTarget(e);
397
- if (/close/.test(eventTarget.getAttribute('data-command')) || eventTarget === this._alertInner) {
398
- this.alertClose();
399
- }
400
- }
401
-
402
- /**
403
- * @private
404
- * @this {UIThis}
405
- * @param {KeyboardEvent} e - Event object
406
- */
407
- function CloseListener(e) {
408
- if (!keyCodeMap.isEsc(e.code)) return;
409
- this.alertClose();
410
- }
411
-
412
- function CreateAlertHTML({ lang, icons }) {
413
- const html = '<div><button class="close" data-command="close" title="' + lang.close + '">' + icons.cancel + '</button></div><div><span></span></div>';
414
- return dom.utils.createElement('DIV', { class: 'se-alert-content' }, html);
415
- }
416
-
417
- function CreateToastHTML() {
418
- const html = '<div class="se-toast-container"><span></span></div>';
419
- return dom.utils.createElement('DIV', { class: 'se-toast' }, html);
420
- }
421
-
422
- export default UI;
1
+ /**
2
+ * @fileoverview UI class
3
+ */
4
+
5
+ import CoreInjector from '../../editorInjector/_core';
6
+ import { dom, converter, keyCodeMap, env } from '../../helper';
7
+ const { _w } = env;
8
+
9
+ /**
10
+ * @typedef {Omit<UI & Partial<__se__EditorInjector>, 'ui'>} UIThis
11
+ */
12
+
13
+ /**
14
+ * @constructor
15
+ * @this {UIThis}
16
+ * @description The UI class is a class that handles operations related to the user interface of SunEditor.
17
+ * - This class sets the editor's style, theme, editor mode, etc., and controls the state of various UI elements.
18
+ * @param {__se__EditorCore} editor - The root editor instance
19
+ */
20
+ function UI(editor) {
21
+ CoreInjector.call(this, editor);
22
+
23
+ // members
24
+ this._controllerOnBtnDisabled = false;
25
+
26
+ // members - modal
27
+ const alertModal = CreateAlertHTML(editor);
28
+ this.alertModal = alertModal;
29
+ this.alertMessage = alertModal.querySelector('span');
30
+ this._alertArea = /** @type {HTMLElement} */ (this.carrierWrapper.querySelector('.se-alert'));
31
+ this._alertInner = /** @type {HTMLElement} */ (this.carrierWrapper.querySelector('.se-alert .se-modal-inner'));
32
+ this._alertInner.appendChild(alertModal);
33
+
34
+ this._closeListener = [CloseListener.bind(this), OnClick_alert.bind(this)];
35
+ this._closeSignal = !this.eventManager.addEvent(alertModal.querySelector('[data-command="close"]'), 'click', this.alertClose.bind(this));
36
+ this._bindClose = null;
37
+ this._backWrapper = /** @type {HTMLElement} */ (this.carrierWrapper.querySelector('.se-back-wrapper'));
38
+
39
+ // members - toast
40
+ const toastPopup = CreateToastHTML();
41
+ this.toastPopup = toastPopup;
42
+ this.toastContainer = toastPopup.querySelector('.se-toast-container');
43
+ this.toastMessage = toastPopup.querySelector('span');
44
+ this.carrierWrapper.appendChild(toastPopup);
45
+ this._toastToggle = null;
46
+ }
47
+
48
+ UI.prototype = {
49
+ /**
50
+ * @this {UIThis}
51
+ * @description set editor frame styles.
52
+ * - Define the style of the edit area
53
+ * - It can also be defined with the "setOptions" method, but the "setEditorStyle" method does not render the editor again.
54
+ * @param {string} style Style string
55
+ * @param {__se__FrameContext|null} fc Frame context
56
+ */
57
+ setEditorStyle(style, fc) {
58
+ fc = fc || this.editor.frameContext;
59
+
60
+ const fo = fc.get('options');
61
+ fo.set('editorStyle', style);
62
+
63
+ const newStyles = converter._setDefaultOptionStyle(fo, style);
64
+ fo.set('_defaultStyles', newStyles);
65
+
66
+ // top area
67
+ fc.get('topArea').style.cssText = newStyles.top;
68
+
69
+ // code view
70
+ const code = fc.get('code');
71
+ code.style.cssText = fo.get('_defaultStyles').frame;
72
+ code.style.display = 'none';
73
+
74
+ // wysiwyg frame
75
+ if (!fo.get('iframe')) {
76
+ fc.get('wysiwygFrame').style.cssText = newStyles.frame + newStyles.editor;
77
+ } else {
78
+ fc.get('wysiwygFrame').style.cssText = newStyles.frame;
79
+ fc.get('wysiwyg').style.cssText = newStyles.editor;
80
+ }
81
+ },
82
+
83
+ /**
84
+ * @this {UIThis}
85
+ * @description Set the theme to the editor
86
+ * @param {string} theme Theme name
87
+ */
88
+ setTheme(theme) {
89
+ if (typeof theme !== 'string') return;
90
+ const o = this.options;
91
+ const prevTheme = o.get('_themeClass').trim();
92
+ o.set('theme', theme || '');
93
+ o.set('_themeClass', theme ? ` se-theme-${theme}` : '');
94
+ theme = o.get('_themeClass').trim();
95
+
96
+ const applyTheme = (target) => {
97
+ if (!target) return;
98
+ if (prevTheme) dom.utils.removeClass(target, prevTheme);
99
+ if (theme) dom.utils.addClass(target, theme);
100
+ };
101
+
102
+ applyTheme(this.carrierWrapper);
103
+ this.editor.applyFrameRoots((e) => {
104
+ applyTheme(e.get('topArea'));
105
+ applyTheme(e.get('wysiwyg'));
106
+ });
107
+
108
+ applyTheme(this.context.get('statusbar._wrapper'));
109
+ applyTheme(this.context.get('toolbar._wrapper'));
110
+ },
111
+
112
+ /**
113
+ * @this {UIThis}
114
+ * @description Switch to or off "ReadOnly" mode.
115
+ * @param {boolean} value "readOnly" boolean value.
116
+ * @param {string|undefined} rootKey Root key
117
+ */
118
+ readOnly(value, rootKey) {
119
+ const fc = rootKey ? this.frameRoots.get(rootKey) : this.editor.frameContext;
120
+
121
+ fc.set('isReadOnly', !!value);
122
+ dom.utils.setDisabled(this.editor._controllerOnDisabledButtons, !!value);
123
+
124
+ if (value) {
125
+ this._offCurrentController();
126
+ this._offCurrentModal();
127
+
128
+ if (this.toolbar?.currentMoreLayerActiveButton?.disabled) this.toolbar._moreLayerOff();
129
+ if (this.subToolbar?.currentMoreLayerActiveButton?.disabled) this.subToolbar._moreLayerOff();
130
+ if (this.menu?.currentDropdownActiveButton?.disabled) this.menu.dropdownOff();
131
+ if (this.menu?.currentContainerActiveButton?.disabled) this.menu.containerOff();
132
+
133
+ fc.get('code').setAttribute('readOnly', 'true');
134
+ dom.utils.addClass(fc.get('wysiwyg'), 'se-read-only');
135
+ } else {
136
+ fc.get('code').removeAttribute('readOnly');
137
+ dom.utils.removeClass(fc.get('wysiwyg'), 'se-read-only');
138
+ }
139
+
140
+ if (this.options.get('hasCodeMirror')) {
141
+ this.viewer._codeMirrorEditor('readonly', !!value, rootKey);
142
+ }
143
+ },
144
+
145
+ /**
146
+ * @this {UIThis}
147
+ * @description Disable the suneditor
148
+ * @param {string|undefined} rootKey Root key
149
+ */
150
+ disable(rootKey) {
151
+ const fc = rootKey ? this.frameRoots.get(rootKey) : this.editor.frameContext;
152
+
153
+ this.toolbar.disable();
154
+ this._offCurrentController();
155
+ this._offCurrentModal();
156
+
157
+ fc.get('wysiwyg').setAttribute('contenteditable', false);
158
+ fc.set('isDisabled', true);
159
+
160
+ if (this.options.get('hasCodeMirror')) {
161
+ this.viewer._codeMirrorEditor('readonly', true, rootKey);
162
+ } else {
163
+ fc.get('code').disabled = true;
164
+ }
165
+ },
166
+
167
+ /**
168
+ * @this {UIThis}
169
+ * @description Enable the suneditor
170
+ * @param {string|undefined} rootKey Root key
171
+ */
172
+ enable(rootKey) {
173
+ const fc = rootKey ? this.frameRoots.get(rootKey) : this.editor.frameContext;
174
+
175
+ this.toolbar.enable();
176
+ fc.get('wysiwyg').setAttribute('contenteditable', true);
177
+ fc.set('isDisabled', false);
178
+
179
+ if (this.options.get('hasCodeMirror')) {
180
+ this.viewer._codeMirrorEditor('readonly', false, rootKey);
181
+ } else {
182
+ fc.get('code').disabled = false;
183
+ }
184
+ },
185
+
186
+ /**
187
+ * @this {UIThis}
188
+ * @description Show the suneditor
189
+ * @param {string|undefined} rootKey Root key
190
+ */
191
+ show(rootKey) {
192
+ const fc = rootKey ? this.frameRoots.get(rootKey) : this.editor.frameContext;
193
+ const topAreaStyle = fc.get('topArea').style;
194
+ if (topAreaStyle.display === 'none') topAreaStyle.display = 'block';
195
+ },
196
+
197
+ /**
198
+ * @this {UIThis}
199
+ * @description Hide the suneditor
200
+ * @param {string|undefined} rootKey Root key
201
+ */
202
+ hide(rootKey) {
203
+ const fc = rootKey ? this.frameRoots.get(rootKey) : this.editor.frameContext;
204
+ fc.get('topArea').style.display = 'none';
205
+ },
206
+
207
+ /**
208
+ * @this {UIThis}
209
+ * @description Show loading box
210
+ * @param {string=} rootKey Root key
211
+ */
212
+ showLoading(rootKey) {
213
+ (rootKey ? this.frameRoots.get(rootKey).get('container') : this.carrierWrapper).querySelector('.se-loading-box').style.display = 'block';
214
+ },
215
+
216
+ /**
217
+ * @this {UIThis}
218
+ * @description Hide loading box
219
+ * @param {string=} rootKey Root key
220
+ */
221
+ hideLoading(rootKey) {
222
+ (rootKey ? this.frameRoots.get(rootKey).get('container') : this.carrierWrapper).querySelector('.se-loading-box').style.display = 'none';
223
+ },
224
+
225
+ /**
226
+ * @this {UIThis}
227
+ * @description This method disables or enables the toolbar buttons when the controller is activated or deactivated.
228
+ * - When the controller is activated, the toolbar buttons are disabled; when the controller is deactivated, the buttons are enabled.
229
+ * @param {boolean} active If `true`, the toolbar buttons will be disabled. If `false`, the toolbar buttons will be enabled.
230
+ */
231
+ setControllerOnDisabledButtons(active) {
232
+ if (active && !this._controllerOnBtnDisabled) {
233
+ dom.utils.setDisabled(this.editor._controllerOnDisabledButtons, true);
234
+ this._controllerOnBtnDisabled = true;
235
+ } else if (!active && this._controllerOnBtnDisabled) {
236
+ dom.utils.setDisabled(this.editor._controllerOnDisabledButtons, false);
237
+ this._controllerOnBtnDisabled = false;
238
+ }
239
+ },
240
+
241
+ /**
242
+ * @this {UIThis}
243
+ * @description Activate the transparent background "div" so that other elements are not affected during resizing.
244
+ * @param {string} cursor cursor css property
245
+ */
246
+ enableBackWrapper(cursor) {
247
+ this._backWrapper.style.cursor = cursor;
248
+ this._backWrapper.style.display = 'block';
249
+ },
250
+
251
+ /**
252
+ * @this {UIThis}
253
+ * @description Disabled background "div"
254
+ */
255
+ disableBackWrapper() {
256
+ this._backWrapper.style.display = 'none';
257
+ this._backWrapper.style.cursor = 'default';
258
+ },
259
+
260
+ /**
261
+ * @this {UIThis}
262
+ * @description Open the alert panel
263
+ * @param {string} text alert message
264
+ * @param {""|"error"|"success"} type alert type
265
+ */
266
+ alertOpen(text, type) {
267
+ this.alertMessage.textContent = text;
268
+
269
+ dom.utils.removeClass(this.alertModal, 'se-alert-error|se-alert-success');
270
+ if (type) dom.utils.addClass(this.alertModal, `se-alert-${type}`);
271
+
272
+ if (this._closeSignal) this._alertInner.addEventListener('click', this._closeListener[1]);
273
+ if (this._bindClose) this._bindClose = this.eventManager.removeGlobalEvent(this._bindClose);
274
+ this._bindClose = this.eventManager.addGlobalEvent('keydown', this._closeListener[0]);
275
+
276
+ this._alertArea.style.display = 'block';
277
+ dom.utils.addClass(this.alertModal, 'se-modal-show');
278
+ },
279
+
280
+ /**
281
+ * @this {UIThis}
282
+ * @description Close the alert panel
283
+ */
284
+ alertClose() {
285
+ dom.utils.removeClass(this.alertModal, 'se-modal-show');
286
+ dom.utils.removeClass(this.alertModal, 'se-alert-*');
287
+ this._alertArea.style.display = 'none';
288
+ if (this._closeSignal) this._alertInner.removeEventListener('click', this._closeListener[1]);
289
+ if (this._bindClose) this._bindClose = this.eventManager.removeGlobalEvent(this._bindClose);
290
+ },
291
+
292
+ /**
293
+ * @description Show toast
294
+ * @param {string} message toast message
295
+ * @param {number} [duration=1000] duration time(ms)
296
+ * @param {""|"error"|"success"} [type=""] duration time(ms)
297
+ */
298
+ showToast(message, duration = 1000, type) {
299
+ if (dom.utils.hasClass(this.toastContainer, 'se-toast-show')) {
300
+ this.closeToast();
301
+ }
302
+
303
+ dom.utils.removeClass(this.toastPopup, 'se-toast-error|se-toast-success');
304
+ if (type) dom.utils.addClass(this.toastPopup, `se-toast-${type}`);
305
+
306
+ this.toastPopup.style.display = 'block';
307
+ this.toastMessage.textContent = message;
308
+ dom.utils.addClass(this.toastContainer, 'se-toast-show');
309
+
310
+ // remove after animation
311
+ this._toastToggle = _w.setTimeout(() => {
312
+ this.closeToast();
313
+ }, duration);
314
+ },
315
+
316
+ /**
317
+ * @description Close toast
318
+ */
319
+ closeToast() {
320
+ if (this._toastToggle) _w.clearTimeout(this._toastToggle);
321
+ this._toastToggle = null;
322
+ dom.utils.removeClass(this.toastContainer, 'se-toast-show');
323
+ this.toastPopup.style.display = 'none';
324
+ },
325
+
326
+ /**
327
+ * @private
328
+ * @this {UIThis}
329
+ * @description visible controllers
330
+ * @param {boolean} value hidden/show
331
+ * @param {?boolean=} lineBreakShow Line break hidden/show (default: Follows the value "value".)
332
+ */
333
+ _visibleControllers(value, lineBreakShow) {
334
+ const visible = value ? '' : 'hidden';
335
+ const breakerVisible = lineBreakShow ?? visible ? '' : 'hidden';
336
+
337
+ const cont = this.editor.opendControllers;
338
+ for (let i = 0, c; i < cont.length; i++) {
339
+ c = cont[i];
340
+ if (c.form) c.form.style.visibility = visible;
341
+ }
342
+
343
+ this.editor._lineBreaker_t.style.visibility = breakerVisible;
344
+ this.editor._lineBreaker_b.style.visibility = breakerVisible;
345
+ },
346
+
347
+ /**
348
+ * @private
349
+ * @this {UIThis}
350
+ * @description Off current controllers
351
+ */
352
+ _offCurrentController() {
353
+ this.component.__deselect();
354
+ },
355
+
356
+ /**
357
+ * @private
358
+ * @this {UIThis}
359
+ * @description Off controllers
360
+ */
361
+ __offControllers() {
362
+ const cont = this.editor.opendControllers;
363
+ const fixedCont = [];
364
+ for (let i = 0, c; i < cont.length; i++) {
365
+ c = cont[i];
366
+ if (c.fixed) {
367
+ fixedCont.push(c);
368
+ continue;
369
+ }
370
+ if (typeof c.inst.close === 'function') c.inst.close();
371
+ if (c.form) c.form.style.display = 'none';
372
+ }
373
+ this.editor.opendControllers = fixedCont;
374
+ this.editor.currentControllerName = '';
375
+ this.editor._preventBlur = false;
376
+ },
377
+
378
+ /**
379
+ * @private
380
+ * @this {UIThis}
381
+ * @description Off current modal
382
+ */
383
+ _offCurrentModal() {
384
+ if (this.editor.opendModal) {
385
+ this.editor.opendModal.close();
386
+ }
387
+ },
388
+
389
+ constructor: UI
390
+ };
391
+
392
+ /**
393
+ * @private
394
+ * @this {UIThis}
395
+ * @param {MouseEvent} e - Event object
396
+ */
397
+ function OnClick_alert(e) {
398
+ const eventTarget = dom.query.getEventTarget(e);
399
+ if (/close/.test(eventTarget.getAttribute('data-command')) || eventTarget === this._alertInner) {
400
+ this.alertClose();
401
+ }
402
+ }
403
+
404
+ /**
405
+ * @private
406
+ * @this {UIThis}
407
+ * @param {KeyboardEvent} e - Event object
408
+ */
409
+ function CloseListener(e) {
410
+ if (!keyCodeMap.isEsc(e.code)) return;
411
+ this.alertClose();
412
+ }
413
+
414
+ function CreateAlertHTML({ lang, icons }) {
415
+ const html = '<div><button class="close" data-command="close" title="' + lang.close + '">' + icons.cancel + '</button></div><div><span></span></div>';
416
+ return dom.utils.createElement('DIV', { class: 'se-alert-content' }, html);
417
+ }
418
+
419
+ function CreateToastHTML() {
420
+ const html = '<div class="se-toast-container"><span></span></div>';
421
+ return dom.utils.createElement('DIV', { class: 'se-toast' }, html);
422
+ }
423
+
424
+ export default UI;