suneditor 3.0.0-beta.2 → 3.0.0-beta.3

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 (36) hide show
  1. package/dist/suneditor.min.css +1 -1
  2. package/dist/suneditor.min.js +1 -1
  3. package/package.json +1 -1
  4. package/src/assets/design/color.css +11 -1
  5. package/src/assets/suneditor.css +17 -12
  6. package/src/core/base/eventHandlers/handler_ww_dragDrop.js +4 -2
  7. package/src/core/base/eventHandlers/handler_ww_key_input.js +1 -1
  8. package/src/core/base/eventManager.js +61 -30
  9. package/src/core/class/char.js +3 -2
  10. package/src/core/class/component.js +17 -3
  11. package/src/core/class/format.js +13 -2
  12. package/src/core/class/html.js +58 -26
  13. package/src/core/class/selection.js +1 -8
  14. package/src/core/class/toolbar.js +2 -1
  15. package/src/core/class/ui.js +3 -1
  16. package/src/core/editor.js +124 -58
  17. package/src/core/section/actives.js +48 -23
  18. package/src/core/section/constructor.js +92 -78
  19. package/src/events.js +12 -0
  20. package/src/helper/converter.js +23 -1
  21. package/src/helper/dom/domCheck.js +2 -2
  22. package/src/modules/Controller.js +25 -5
  23. package/src/plugins/dropdown/formatBlock.js +4 -13
  24. package/src/themes/dark.css +11 -1
  25. package/types/core/base/eventManager.d.ts +16 -0
  26. package/types/core/class/char.d.ts +2 -1
  27. package/types/core/class/component.d.ts +1 -0
  28. package/types/core/class/format.d.ts +8 -1
  29. package/types/core/class/html.d.ts +8 -0
  30. package/types/core/class/ui.d.ts +1 -1
  31. package/types/core/editor.d.ts +7 -2
  32. package/types/core/section/constructor.d.ts +7 -0
  33. package/types/events.d.ts +2 -0
  34. package/types/helper/converter.d.ts +9 -0
  35. package/types/helper/index.d.ts +1 -0
  36. package/types/plugins/dropdown/formatBlock.d.ts +2 -2
package/src/events.js CHANGED
@@ -161,12 +161,24 @@ export default {
161
161
  */
162
162
  onFocus: null,
163
163
 
164
+ /**
165
+ * @description Event call back function
166
+ * @param {BaseEvent} params
167
+ */
168
+ onNativeFocus: null,
169
+
164
170
  /**
165
171
  * @description Event call back function
166
172
  * @param {BaseEvent} params
167
173
  */
168
174
  onBlur: null,
169
175
 
176
+ /**
177
+ * @description Event call back function
178
+ * @param {BaseEvent} params
179
+ */
180
+ onNativeBlur: null,
181
+
170
182
  /**
171
183
  * @description Event function on copy
172
184
  * @param {Object} params
@@ -170,6 +170,26 @@ export function syncMaps(targetMap, referenceMap) {
170
170
  });
171
171
  }
172
172
 
173
+ /**
174
+ * @description Merges multiple Map objects into a new Map using spread syntax.
175
+ * - Entries from later maps in the arguments list will overwrite entries from earlier maps if keys conflict.
176
+ * - The original maps are not modified.
177
+ * @param {...Map<*, *>} mapsToMerge - An arbitrary number of Map objects to merge.
178
+ * @returns {Map<*, *>} A new Map containing all entries from the input maps.
179
+ */
180
+ export function mergeMaps(...mapsToMerge) {
181
+ const validMaps = mapsToMerge.filter((m) => {
182
+ if (!(m instanceof Map)) {
183
+ return false;
184
+ }
185
+ return true;
186
+ });
187
+
188
+ const allEntries = validMaps.flatMap((map) => [...map]);
189
+
190
+ return new Map(allEntries);
191
+ }
192
+
173
193
  /**
174
194
  * @description Object.values
175
195
  * @param {Object<*, *>} obj Object parameter.
@@ -269,8 +289,9 @@ export function nodeListToArray(nodeList) {
269
289
  export function swapKeyValue(obj) {
270
290
  const swappedObj = {};
271
291
 
292
+ const hasOwn = Object.prototype.hasOwnProperty;
272
293
  for (const key in obj) {
273
- if (Object.prototype.hasOwnProperty.call(obj, key)) {
294
+ if (hasOwn.call(obj, key)) {
274
295
  swappedObj[obj[key]] = key;
275
296
  }
276
297
  }
@@ -542,6 +563,7 @@ const converter = {
542
563
  entityToHTML,
543
564
  debounce,
544
565
  syncMaps,
566
+ mergeMaps,
545
567
  getValues,
546
568
  camelToKebabCase,
547
569
  kebabToCamelCase,
@@ -179,7 +179,7 @@ export function isContentLess(node) {
179
179
  export function isEmptyLine(node) {
180
180
  if (!node?.parentNode) return true;
181
181
  const el = /** @type {HTMLElement} */ (node);
182
- return !el.querySelector('IMG, IFRAME, AUDIO, VIDEO, CANVAS, TABLE') && el.children.length === 0 && check.isZeroWidth(el.textContent);
182
+ return !el.querySelector('IMG, IFRAME, AUDIO, VIDEO, CANVAS, TABLE') && (el.children.length <= 1 || isBreak(el.firstElementChild)) && isZeroWidth(el.textContent);
183
183
  }
184
184
 
185
185
  /**
@@ -188,7 +188,7 @@ export function isEmptyLine(node) {
188
188
  * @returns {node is HTMLElement}
189
189
  */
190
190
  export function isWysiwygFrame(node) {
191
- return node?.nodeType === 1 && (domUtils.hasClass(node, 'se-wrapper-wysiwyg|sun-editor-carrier-wrapper') || /^BODY$/i.test(node.nodeName));
191
+ return node?.nodeType === 1 && (domUtils.hasClass(node, 'se-wrapper-wysiwyg|sun-editor-carrier-wrapper|se-wrapper') || /^BODY$/i.test(node.nodeName));
192
192
  }
193
193
 
194
194
  /**
@@ -108,10 +108,12 @@ class Controller extends EditorInjector {
108
108
  if (this.editor.isBalloon) this.toolbar.hide();
109
109
  else if (this.editor.isSubBalloon) this.subToolbar.hide();
110
110
 
111
- if (disabled ?? this.disabled) {
112
- this.ui.setControllerOnDisabledButtons(true);
113
- } else {
114
- this.ui.setControllerOnDisabledButtons(false);
111
+ if (!this.status.hasFocus) {
112
+ if (disabled ?? this.disabled) {
113
+ this.ui.setControllerOnDisabledButtons(true);
114
+ } else {
115
+ this.ui.setControllerOnDisabledButtons(false);
116
+ }
115
117
  }
116
118
 
117
119
  this.currentPositionTarget = positionTarget || target;
@@ -429,6 +431,7 @@ class Controller extends EditorInjector {
429
431
  if (this.form.contains(eventTarget) || this._checkForm(eventTarget)) return;
430
432
  if (this.editor._fileManager.pluginRegExp.test(this.kind) && !keyCodeMap.isEsc(keyCode)) return;
431
433
 
434
+ this.#PostCloseEvent(eventTarget);
432
435
  this.close();
433
436
  }
434
437
 
@@ -443,12 +446,29 @@ class Controller extends EditorInjector {
443
446
  }
444
447
 
445
448
  this.isOpen = true;
446
- if (eventTarget === this.inst._element || eventTarget === this.currentTarget || this._checkFixed() || this.form.contains(eventTarget) || this._checkForm(eventTarget)) {
449
+ if (
450
+ eventTarget === this.inst._element ||
451
+ eventTarget === this.currentTarget ||
452
+ this._checkFixed() ||
453
+ this.form.contains(eventTarget) ||
454
+ this._checkForm(eventTarget) ||
455
+ dom.query.getParentElement(eventTarget, '.se-line-breaker-component')
456
+ ) {
447
457
  return;
448
458
  }
449
459
 
460
+ this.#PostCloseEvent(eventTarget);
450
461
  this.close(true);
451
462
  }
463
+
464
+ /**
465
+ * @param {HTMLElement} eventTarget - The target element that triggered the event.
466
+ */
467
+ #PostCloseEvent(eventTarget) {
468
+ if (!this.editor.frameContext.get('wysiwyg').contains(eventTarget)) {
469
+ this.component.__prevent = false;
470
+ }
471
+ }
452
472
  }
453
473
 
454
474
  export default Controller;
@@ -1,15 +1,6 @@
1
1
  import EditorInjector from '../../editorInjector';
2
2
  import { dom } from '../../helper';
3
3
 
4
- const HEADER_KEYCODE = new Map([
5
- ['Digit1', 'h1'],
6
- ['Digit2', 'h2'],
7
- ['Digit3', 'h3'],
8
- ['Digit4', 'h4'],
9
- ['Digit5', 'h5'],
10
- ['Digit6', 'h6']
11
- ]);
12
-
13
4
  /**
14
5
  * @class
15
6
  * @description FormatBlock Plugin (P, BLOCKQUOTE, PRE, H1, H2...)
@@ -125,12 +116,12 @@ class FormatBlock extends EditorInjector {
125
116
 
126
117
  /**
127
118
  * @description Create a header tag, call by "shortcut" class
128
- * - (e.g. shortcuts._h1: ['c+s+49+p~formatBlock.createHeader', ''])
119
+ * - (e.g. shortcuts._h1: ['c+s+49+$~formatBlock.applyHeaderByShortcut', ''])
129
120
  * @param {__se__PluginShortcutInfo} params - Information of the "shortcut" plugin
130
121
  */
131
- createHeader({ keyCode }) {
132
- const headerName = HEADER_KEYCODE.get(keyCode);
133
- const tag = dom.utils.createElement(headerName);
122
+ applyHeaderByShortcut({ keyCode }) {
123
+ const headerNum = keyCode.match(/\d+$/)?.[0];
124
+ const tag = dom.utils.createElement(`H${headerNum}`);
134
125
  this.format.setLine(tag);
135
126
  }
136
127
  }
@@ -38,8 +38,18 @@
38
38
  --se-statusbar-font-color: #aaa;
39
39
  --se-overlay-background-color: #111;
40
40
 
41
- /* hover, active */
41
+ /* hover */
42
+ --se-hover-color: #0d200c;
43
+ --se-hover-dark-color: #8192a3;
44
+ --se-hover-dark2-color: #738495;
45
+ --se-hover-dark3-color: #667788;
46
+ --se-hover-light-color: #9ab0c5;
47
+ --se-hover-light2-color: #adc2d7;
48
+ --se-hover-light3-color: #c0d3e5;
49
+
50
+ /* active */
42
51
  --se-active-color: #5cd2e6;
52
+ --se-active-hover-color: #6de9f5;
43
53
  --se-active-dark-color: #3bb9cd;
44
54
  --se-active-dark2-color: #2a9fb1;
45
55
  --se-active-dark3-color: #5cd2e6;
@@ -382,4 +382,20 @@ declare class EventManager {
382
382
  * @param {*} range Range object
383
383
  */
384
384
  __enterScrollTo(this: Omit<EventManager & Partial<import('../../editorInjector').default>, 'eventManager'>, range: any): void;
385
+ /**
386
+ * @private
387
+ * @description Focus Event Postprocessing
388
+ * @this {EventManagerThis}
389
+ * @param {__se__FrameContext} frameContext - frame context object
390
+ * @param {Event} event - Event object
391
+ */
392
+ __postFocusEvent(this: Omit<EventManager & Partial<import('../../editorInjector').default>, 'eventManager'>, frameContext: __se__FrameContext, event: Event): void;
393
+ /**
394
+ * @private
395
+ * @description Blur Event Postprocessing
396
+ * @this {EventManagerThis}
397
+ * @param {__se__FrameContext} frameContext - frame context object
398
+ * @param {Event} event - Event object
399
+ */
400
+ __postBlurEvent(this: Omit<EventManager & Partial<import('../../editorInjector').default>, 'eventManager'>, frameContext: __se__FrameContext, event: Event): void;
385
401
  }
@@ -46,8 +46,9 @@ declare class Char {
46
46
  /**
47
47
  * @this {CharThis}
48
48
  * @description Set the char count to charCounter element textContent.
49
+ * @param {?__se__FrameContext=} fc Frame context
49
50
  */
50
- display(this: Omit<Char & Partial<import('../../editorInjector').default>, 'char'>): void;
51
+ display(this: Omit<Char & Partial<import('../../editorInjector').default>, 'char'>, fc?: (__se__FrameContext | null) | undefined): void;
51
52
  /**
52
53
  * @this {CharThis}
53
54
  * @description Returns false if char count is greater than "frameOptions.get('charCounter_max')" when "inputText" is added to the current editor.
@@ -67,6 +67,7 @@ declare class Component {
67
67
  _bindClose_touchstart: __se__GlobalEventInfo | void;
68
68
  /** @type {boolean} */
69
69
  __selectionSelected: boolean;
70
+ __prevent: boolean;
70
71
  /**
71
72
  * @this {ComponentThis}
72
73
  * @description Inserts an element and returns it. (Used for elements: table, hr, image, video)
@@ -49,7 +49,7 @@ declare class Format {
49
49
  _formatClosureBlockCheck: any;
50
50
  _formatClosureBrLineCheck: any;
51
51
  _textStyleTagsCheck: RegExp;
52
- _brLineBreak: any;
52
+ _brLineBreak: boolean;
53
53
  /**
54
54
  * @this {FormatThis}
55
55
  * @description Replace the line tag of the current selection.
@@ -613,4 +613,11 @@ declare class Format {
613
613
  * @param {Array|null} styleArray Refer style array
614
614
  */
615
615
  _sn_resetCommonListCell(this: Omit<Format & Partial<import('../../editorInjector').default>, 'format'>, el: Node, styleArray: any[] | null): boolean;
616
+ /**
617
+ * @private
618
+ * @this {FormatThis}
619
+ * @description Reset the line break format.
620
+ * @param {"line"|"br"} breakFormat options.get('defaultLineBreakFormat')
621
+ */
622
+ __resetBrLineBreak(this: Omit<Format & Partial<import('../../editorInjector').default>, 'format'>, breakFormat: 'line' | 'br'): void;
616
623
  }
@@ -419,4 +419,12 @@ declare class HTML {
419
419
  * @returns {Node} The cleaned node with redundant styles removed.
420
420
  */
421
421
  _dupleCheck(this: Omit<HTML & Partial<import('../../editorInjector').default>, 'html'>, oNode: Node, parentNode: Node): Node;
422
+ /**
423
+ * @private
424
+ * @this {HTMLThis}
425
+ * @description Reset autoStyleify options.
426
+ * @param {Array.<string>} autoStyleify Styles applied automatically on text input.
427
+ * - ex ["bold", "underline", "italic", "strike"]
428
+ */
429
+ __resetAutoStyleify(this: Omit<HTML & Partial<import('../../editorInjector').default>, 'html'>, autoStyleify: Array<string>): void;
422
430
  }
@@ -38,7 +38,7 @@ declare class UI {
38
38
  _toastToggle: number;
39
39
  /**
40
40
  * @this {UIThis}
41
- * @description Set "options.get('editorStyle')" style.
41
+ * @description set editor frame styles.
42
42
  * - Define the style of the edit area
43
43
  * - It can also be defined with the "setOptions" method, but the "setEditorStyle" method does not render the editor again.
44
44
  * @param {string} style Style string
@@ -333,10 +333,15 @@ declare class Editor {
333
333
  */
334
334
  _notHideToolbar: boolean;
335
335
  /**
336
- * @description Variables for controlling focus and blur events
336
+ * @description Variables for controlling blur events
337
337
  * @type {boolean}
338
338
  */
339
339
  _preventBlur: boolean;
340
+ /**
341
+ * @description Variables for controlling focus events
342
+ * @type {boolean}
343
+ */
344
+ _preventFocus: boolean;
340
345
  /**
341
346
  * @description Variables for controlling selection change events
342
347
  */
@@ -570,7 +575,7 @@ declare class Editor {
570
575
  __saveCommandButtons(cmdButtons: Map<string, Element>, tray: Element): void;
571
576
  /**
572
577
  * @private
573
- * @description Caches shortcut keys for commands.
578
+ * @description Caches custom(starts with "_") shortcut keys for commands.
574
579
  */
575
580
  __cachingShortcuts(): void;
576
581
  /**
@@ -266,6 +266,13 @@ export function CreateToolBar(
266
266
  /**
267
267
  * @typedef {EditorBaseOptions & EditorFrameOptions} EditorInitOptions
268
268
  */
269
+ /** ------------- [OPTIONS FRAG] ------------- */
270
+ /**
271
+ * @description For all EditorInitOptions keys, only boolean | null values are allowed.
272
+ * - 'fixed' → Immutable / null → Resettable.
273
+ * @type {Partial<Record<keyof EditorInitOptions, "fixed" | true>>}
274
+ */
275
+ export const OPTION_FRAME_FIXED_FLAG: Partial<Record<keyof EditorInitOptions, 'fixed' | true>>;
269
276
  /**
270
277
  * @description For all EditorInitOptions keys, only boolean | null values are allowed.
271
278
  * - 'fixed' → Immutable / null → Resettable.
package/types/events.d.ts CHANGED
@@ -8,7 +8,9 @@ declare namespace _default {
8
8
  let onKeyDown: any;
9
9
  let onKeyUp: any;
10
10
  let onFocus: any;
11
+ let onNativeFocus: any;
11
12
  let onBlur: any;
13
+ let onNativeBlur: any;
12
14
  let onCopy: any;
13
15
  let onCut: any;
14
16
  let onChange: any;
@@ -44,6 +44,14 @@ export function debounce(func: (...args: any) => void, wait: number): any;
44
44
  * @param {Map<*, *>} referenceMap The Map providing the reference values (source).
45
45
  */
46
46
  export function syncMaps(targetMap: Map<any, any>, referenceMap: Map<any, any>): void;
47
+ /**
48
+ * @description Merges multiple Map objects into a new Map using spread syntax.
49
+ * - Entries from later maps in the arguments list will overwrite entries from earlier maps if keys conflict.
50
+ * - The original maps are not modified.
51
+ * @param {...Map<*, *>} mapsToMerge - An arbitrary number of Map objects to merge.
52
+ * @returns {Map<*, *>} A new Map containing all entries from the input maps.
53
+ */
54
+ export function mergeMaps(...mapsToMerge: Map<any, any>[]): Map<any, any>;
47
55
  /**
48
56
  * @description Object.values
49
57
  * @param {Object<*, *>} obj Object parameter.
@@ -177,6 +185,7 @@ declare namespace converter {
177
185
  export { entityToHTML };
178
186
  export { debounce };
179
187
  export { syncMaps };
188
+ export { mergeMaps };
180
189
  export { getValues };
181
190
  export { camelToKebabCase };
182
191
  export { kebabToCamelCase };
@@ -35,6 +35,7 @@ export const converter: {
35
35
  entityToHTML: typeof import('./converter').entityToHTML;
36
36
  debounce: typeof import('./converter').debounce;
37
37
  syncMaps: typeof import('./converter').syncMaps;
38
+ mergeMaps: typeof import('./converter').mergeMaps;
38
39
  getValues: typeof import('./converter').getValues;
39
40
  camelToKebabCase: typeof import('./converter').camelToKebabCase;
40
41
  kebabToCamelCase: typeof import('./converter').kebabToCamelCase;
@@ -46,9 +46,9 @@ declare class FormatBlock extends EditorInjector {
46
46
  action(target: HTMLElement): void;
47
47
  /**
48
48
  * @description Create a header tag, call by "shortcut" class
49
- * - (e.g. shortcuts._h1: ['c+s+49+p~formatBlock.createHeader', ''])
49
+ * - (e.g. shortcuts._h1: ['c+s+49+$~formatBlock.applyHeaderByShortcut', ''])
50
50
  * @param {__se__PluginShortcutInfo} params - Information of the "shortcut" plugin
51
51
  */
52
- createHeader({ keyCode }: __se__PluginShortcutInfo): void;
52
+ applyHeaderByShortcut({ keyCode }: __se__PluginShortcutInfo): void;
53
53
  }
54
54
  import EditorInjector from '../../editorInjector';