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,346 +1,357 @@
1
- /**
2
- * @fileoverview Toolbar Menu class
3
- */
4
-
5
- import CoreInjector from '../../editorInjector/_core';
6
- import { dom, converter } from '../../helper';
7
-
8
- /**
9
- * @typedef {Omit<Menu & Partial<__se__EditorInjector>, 'menu'>} MenuThis
10
- */
11
-
12
- /**
13
- * @constructor
14
- * @this {MenuThis}
15
- * @description Dropdown and container menu management class
16
- * @param {__se__EditorCore} editor - The root editor instance
17
- */
18
- function Menu(editor) {
19
- CoreInjector.call(this, editor);
20
-
21
- // members
22
- /** @type {Object<string, HTMLElement>} */
23
- this.targetMap = {};
24
- this.index = -1;
25
- this.menus = [];
26
- // dropdown
27
- this.currentDropdown = null;
28
- this.currentDropdownActiveButton = null;
29
- this.currentDropdownName = '';
30
- this.currentDropdownType = '';
31
- // container
32
- this.currentContainer = null;
33
- this.currentContainerActiveButton = null;
34
- this.currentContainerName = '';
35
- // event
36
- this._dropdownCommands = [];
37
- this.__globalEventHandler = {
38
- mousedown: OnMouseDown_dropdown.bind(this),
39
- containerDown: this.containerOff.bind(this),
40
- keydown: OnKeyDown_dropdown.bind(this),
41
- mousemove: OnMousemove_dropdown.bind(this),
42
- mouseout: OnMouseout_dropdown.bind(this)
43
- };
44
- this._bindClose_dropdown_mouse = null;
45
- this._bindClose_dropdown_key = null;
46
- this._bindClose_cons_mouse = null;
47
- this.currentDropdownPlugin = null;
48
-
49
- // eventManager member (viewport)
50
- this.__menuBtn = null;
51
- this.__menuContainer = null;
52
- }
53
-
54
- Menu.prototype = {
55
- /**
56
- * @this {MenuThis}
57
- * @description Method for managing dropdown element.
58
- * - You must add the "dropdown" element using the this method at custom plugin.
59
- * @param {{key: string, type: string}} classObj Class object
60
- * @param {Node} menu Dropdown element
61
- */
62
- initDropdownTarget({ key, type }, menu) {
63
- if (key) {
64
- if (!/free$/.test(type)) {
65
- /** @type {HTMLElement} */ (menu).setAttribute('data-key', key);
66
- this._dropdownCommands.push(key);
67
- }
68
- this.context.get('menuTray').appendChild(menu);
69
- this.targetMap[key] = /** @type {HTMLElement} */ (menu);
70
- } else {
71
- throw Error("[SUNEDITOR.init.fail] The plugin's key is not added.");
72
- }
73
- },
74
-
75
- /**
76
- * @this {MenuThis}
77
- * @description On dropdown
78
- * @param {Node} button Dropdown's button element to call
79
- */
80
- dropdownOn(button) {
81
- this.__removeGlobalEvent();
82
- const moreBtn = this._checkMoreLayer(button);
83
- if (moreBtn) {
84
- const target = dom.query.getParentElement(moreBtn, '.se-btn-tray').querySelector('[data-command="' + moreBtn.getAttribute('data-ref') + '"]');
85
- if (target) {
86
- this.editor.runFromTarget(target);
87
- this.dropdownOn(button);
88
- return;
89
- }
90
- }
91
-
92
- const btnEl = /** @type {HTMLButtonElement} */ (button);
93
- const dropdownName = (this.currentDropdownName = btnEl.getAttribute('data-command'));
94
- this.currentDropdownType = btnEl.getAttribute('data-type');
95
- const menu = (this.currentDropdown = this.targetMap[dropdownName]);
96
- this.currentDropdownActiveButton = btnEl;
97
- this._setMenuPosition(btnEl, menu);
98
-
99
- this._bindClose_dropdown_mouse = this.eventManager.addGlobalEvent('mousedown', this.__globalEventHandler.mousedown, false);
100
- if (this._dropdownCommands.includes(dropdownName)) {
101
- this.menus = converter.nodeListToArray(menu.querySelectorAll('[data-command]'));
102
- if (this.menus.length > 0) {
103
- this._bindClose_dropdown_key = this.eventManager.addGlobalEvent('keydown', this.__globalEventHandler.keydown, false);
104
- menu.addEventListener('mousemove', this.__globalEventHandler.mousemove, false);
105
- menu.addEventListener('mouseout', this.__globalEventHandler.mouseout, false);
106
- }
107
- }
108
-
109
- this.currentDropdownPlugin = this.plugins[dropdownName];
110
- if (typeof this.currentDropdownPlugin?.on === 'function') this.currentDropdownPlugin.on(btnEl);
111
- this.editor._preventBlur = true;
112
- },
113
-
114
- /**
115
- * @this {MenuThis}
116
- * @description Off dropdown
117
- */
118
- dropdownOff() {
119
- this.__removeGlobalEvent();
120
- this.index = -1;
121
- this.menus = [];
122
- this.__menuBtn = null;
123
- this.__menuContainer = null;
124
-
125
- if (this.currentDropdown) {
126
- this.currentDropdownName = '';
127
- this.currentDropdownType = '';
128
- this.currentDropdown.style.display = 'none';
129
- this.currentDropdown = null;
130
- if (this.currentDropdownActiveButton) {
131
- dom.utils.removeClass(this.currentDropdownActiveButton.parentElement.children, 'on');
132
- }
133
- this.currentDropdownActiveButton = null;
134
- this.editor._notHideToolbar = false;
135
- }
136
-
137
- this.editor._preventBlur = false;
138
-
139
- if (typeof this.currentDropdownPlugin?.off === 'function') this.currentDropdownPlugin.off();
140
- this.currentDropdownPlugin = null;
141
- },
142
-
143
- /**
144
- * @this {MenuThis}
145
- * @description On menu container
146
- * @param {Node} button Container's button element to call
147
- */
148
- containerOn(button) {
149
- this.__removeGlobalEvent();
150
-
151
- this.currentContainerActiveButton = /** @type {HTMLButtonElement} */ (button);
152
- const containerName = (this.currentContainerName = this.currentContainerActiveButton.getAttribute('data-command'));
153
- this._setMenuPosition(button, (this.currentContainer = this.targetMap[containerName]));
154
-
155
- this._bindClose_cons_mouse = this.eventManager.addGlobalEvent('mousedown', this.__globalEventHandler.containerDown, false);
156
-
157
- if (this.plugins[containerName].on) this.plugins[containerName].on(button);
158
- this.editor._preventBlur = true;
159
- },
160
-
161
- /**
162
- * @this {MenuThis}
163
- * @description Off menu container
164
- */
165
- containerOff() {
166
- this.__removeGlobalEvent();
167
-
168
- if (this.currentContainer) {
169
- this.currentContainerName = '';
170
- this.currentContainer.style.display = 'none';
171
- this.currentContainer = null;
172
- dom.utils.removeClass(this.currentContainerActiveButton, 'on');
173
- this.currentContainerActiveButton = null;
174
- this.editor._notHideToolbar = false;
175
- }
176
-
177
- this.editor._preventBlur = false;
178
- },
179
-
180
- /**
181
- * @private
182
- * @this {MenuThis}
183
- * @description Set the menu position.
184
- * @param {Node} element Button element
185
- * @param {HTMLElement} menu Menu element
186
- */
187
- _setMenuPosition(element, menu) {
188
- menu.style.visibility = 'hidden';
189
- menu.style.display = 'block';
190
- menu.style.height = '';
191
- dom.utils.addClass(element.parentElement.children, 'on');
192
-
193
- this.offset.setRelPosition(menu, this.carrierWrapper, element.parentElement, dom.query.getParentElement(element, '.se-toolbar'), false);
194
-
195
- menu.style.visibility = '';
196
-
197
- this.__menuBtn = element;
198
- this.__menuContainer = menu;
199
- },
200
-
201
- /**
202
- * @private
203
- * @this {MenuThis}
204
- * @description Restore the last menu position using previously stored button and menu elements.
205
- */
206
- _restoreMenuPosition() {
207
- if (!this.__menuBtn || !this.__menuContainer) return;
208
- this._setMenuPosition(this.__menuBtn, this.__menuContainer);
209
- },
210
-
211
- /**
212
- * @private
213
- * @this {MenuThis}
214
- * @description Check if the element is part of a more layer
215
- * @param {Node} element The element to check
216
- * @returns {HTMLElement|null} The more layer element or null
217
- */
218
- _checkMoreLayer(element) {
219
- const more = dom.query.getParentElement(element, '.se-more-layer');
220
- if (more && more.style.display !== 'block') {
221
- return more.getAttribute('data-ref') ? more : null;
222
- } else {
223
- return null;
224
- }
225
- },
226
-
227
- /**
228
- * @private
229
- * @this {MenuThis}
230
- * @description Move the selected item in the dropdown menu
231
- * @param {number} num Direction and amount to move (-1 for up, 1 for down)
232
- */
233
- _moveItem(num) {
234
- dom.utils.removeClass(this.currentDropdown, 'se-select-menu-mouse-move');
235
- dom.utils.addClass(this.currentDropdown, 'se-select-menu-key-action');
236
- num = this.index + num;
237
- const len = this.menus.length;
238
- const selectIndex = (this.index = num >= len ? 0 : num < 0 ? len - 1 : num);
239
-
240
- for (let i = 0; i < len; i++) {
241
- if (i === selectIndex) {
242
- dom.utils.addClass(this.menus[i], 'on');
243
- } else {
244
- dom.utils.removeClass(this.menus[i], 'on');
245
- }
246
- }
247
- },
248
-
249
- /**
250
- * @private
251
- * @this {MenuThis}
252
- * @description Remove global event listeners
253
- */
254
- __removeGlobalEvent() {
255
- if (this._bindClose_dropdown_mouse) this._bindClose_dropdown_mouse = this.eventManager.removeGlobalEvent(this._bindClose_dropdown_mouse);
256
- if (this._bindClose_cons_mouse) this._bindClose_cons_mouse = this.eventManager.removeGlobalEvent(this._bindClose_cons_mouse);
257
- if (this._bindClose_dropdown_key) {
258
- this._bindClose_dropdown_key = this.eventManager.removeGlobalEvent(this._bindClose_dropdown_key);
259
- dom.utils.removeClass(this.menus, 'on');
260
- dom.utils.removeClass(this.currentDropdown, 'se-select-menu-key-action|se-select-menu-mouse-move');
261
- this.currentDropdown.removeEventListener('mousemove', this.__globalEventHandler.mousemove, false);
262
- this.currentDropdown.removeEventListener('mouseout', this.__globalEventHandler.mouseout, false);
263
- }
264
- },
265
-
266
- constructor: Menu
267
- };
268
-
269
- /**
270
- * @private
271
- * @this {MenuThis}
272
- * @param {MouseEvent} e - Event object
273
- */
274
- function OnMouseDown_dropdown(e) {
275
- const eventTarget = dom.query.getEventTarget(e);
276
- if (dom.query.getParentElement(eventTarget, '.se-dropdown')) return;
277
- this.dropdownOff();
278
- }
279
-
280
- /**
281
- * @private
282
- * @this {MenuThis}
283
- */
284
- function OnMouseout_dropdown() {
285
- this.index = -1;
286
- }
287
-
288
- /**
289
- * @private
290
- * @this {MenuThis}
291
- * @param {KeyboardEvent} e - Event object
292
- */
293
- function OnKeyDown_dropdown(e) {
294
- const keyCode = e.code;
295
- switch (keyCode) {
296
- case 'ArrowUp': // up
297
- e.preventDefault();
298
- e.stopPropagation();
299
- this._moveItem(-1);
300
- break;
301
- case 'ArrowDown': // down
302
- e.preventDefault();
303
- e.stopPropagation();
304
- this._moveItem(1);
305
- break;
306
- case 'ArrowLeft': // left
307
- e.preventDefault();
308
- e.stopPropagation();
309
- this._moveItem(-1);
310
- break;
311
- case 'ArrowRight': //right
312
- e.preventDefault();
313
- e.stopPropagation();
314
- this._moveItem(1);
315
- break;
316
- case 'Enter':
317
- case 'Space': /* enter, space */ {
318
- if (this.index < 0) break;
319
-
320
- const target = this.menus[this.index];
321
- if (!target || typeof this.plugins[this.currentDropdownName].action !== 'function') return;
322
-
323
- e.preventDefault();
324
- e.stopPropagation();
325
- this.plugins[this.currentDropdownName].action(target);
326
- this.dropdownOff();
327
- break;
328
- }
329
- }
330
- }
331
-
332
- /**
333
- * @private
334
- * @this {MenuThis}
335
- * @param {MouseEvent} e - Event object
336
- */
337
- function OnMousemove_dropdown(e) {
338
- dom.utils.addClass(this.currentDropdown, 'se-select-menu-mouse-move');
339
- dom.utils.removeClass(this.currentDropdown, 'se-select-menu-key-action');
340
-
341
- const index = this.menus.indexOf(e.target);
342
- if (index === -1) return;
343
- this.index = index * 1;
344
- }
345
-
346
- export default Menu;
1
+ /**
2
+ * @fileoverview Toolbar Menu class
3
+ */
4
+
5
+ import CoreInjector from '../../editorInjector/_core';
6
+ import { dom, converter } from '../../helper';
7
+
8
+ /**
9
+ * @typedef {Omit<Menu & Partial<__se__EditorInjector>, 'menu'>} MenuThis
10
+ */
11
+
12
+ /**
13
+ * @constructor
14
+ * @this {MenuThis}
15
+ * @description Dropdown and container menu management class
16
+ * @param {__se__EditorCore} editor - The root editor instance
17
+ */
18
+ function Menu(editor) {
19
+ CoreInjector.call(this, editor);
20
+
21
+ // members
22
+ /** @type {Object<string, HTMLElement>} */
23
+ this.targetMap = {};
24
+ this.index = -1;
25
+ this.menus = [];
26
+ // dropdown
27
+ this.currentDropdown = null;
28
+ this.currentDropdownActiveButton = null;
29
+ this.currentDropdownName = '';
30
+ this.currentDropdownType = '';
31
+ // container
32
+ this.currentContainer = null;
33
+ this.currentContainerActiveButton = null;
34
+ this.currentContainerName = '';
35
+ // event
36
+ this._dropdownCommands = [];
37
+ this.__globalEventHandler = {
38
+ mousedown: OnMouseDown_dropdown.bind(this),
39
+ containerDown: this.containerOff.bind(this),
40
+ keydown: OnKeyDown_dropdown.bind(this),
41
+ mousemove: OnMousemove_dropdown.bind(this),
42
+ mouseout: OnMouseout_dropdown.bind(this)
43
+ };
44
+ this._bindClose_dropdown_mouse = null;
45
+ this._bindClose_dropdown_key = null;
46
+ this._bindClose_cons_mouse = null;
47
+ this.currentDropdownPlugin = null;
48
+
49
+ // eventManager member (viewport)
50
+ this.__menuBtn = null;
51
+ this.__menuContainer = null;
52
+ }
53
+
54
+ Menu.prototype = {
55
+ /**
56
+ * @this {MenuThis}
57
+ * @description Method for managing dropdown element.
58
+ * - You must add the "dropdown" element using the this method at custom plugin.
59
+ * @param {{key: string, type: string}} classObj Class object
60
+ * @param {Node} menu Dropdown element
61
+ */
62
+ initDropdownTarget({ key, type }, menu) {
63
+ if (key) {
64
+ if (!/free$/.test(type)) {
65
+ /** @type {HTMLElement} */ (menu).setAttribute('data-key', key);
66
+ this._dropdownCommands.push(key);
67
+ }
68
+ this.context.get('menuTray').appendChild(menu);
69
+ this.targetMap[key] = /** @type {HTMLElement} */ (menu);
70
+ } else {
71
+ throw Error("[SUNEDITOR.init.fail] The plugin's key is not added.");
72
+ }
73
+ },
74
+
75
+ /**
76
+ * @this {MenuThis}
77
+ * @description On dropdown
78
+ * @param {Node} button Dropdown's button element to call
79
+ */
80
+ dropdownOn(button) {
81
+ this.__removeGlobalEvent();
82
+ const moreBtn = this._checkMoreLayer(button);
83
+ if (moreBtn) {
84
+ const target = dom.query.getParentElement(moreBtn, '.se-btn-tray').querySelector('[data-command="' + moreBtn.getAttribute('data-ref') + '"]');
85
+ if (target) {
86
+ this.editor.runFromTarget(target);
87
+ this.dropdownOn(button);
88
+ return;
89
+ }
90
+ }
91
+
92
+ const btnEl = /** @type {HTMLButtonElement} */ (button);
93
+ const dropdownName = (this.currentDropdownName = btnEl.getAttribute('data-command'));
94
+ this.currentDropdownType = btnEl.getAttribute('data-type');
95
+ const menu = (this.currentDropdown = this.targetMap[dropdownName]);
96
+ this.currentDropdownActiveButton = btnEl;
97
+ this._setMenuPosition(btnEl, menu);
98
+
99
+ this._bindClose_dropdown_mouse = this.eventManager.addGlobalEvent('mousedown', this.__globalEventHandler.mousedown, false);
100
+ if (this._dropdownCommands.includes(dropdownName)) {
101
+ this.menus = converter.nodeListToArray(menu.querySelectorAll('[data-command]'));
102
+ if (this.menus.length > 0) {
103
+ this._bindClose_dropdown_key = this.eventManager.addGlobalEvent('keydown', this.__globalEventHandler.keydown, false);
104
+ menu.addEventListener('mousemove', this.__globalEventHandler.mousemove, false);
105
+ menu.addEventListener('mouseout', this.__globalEventHandler.mouseout, false);
106
+ }
107
+ }
108
+
109
+ this.currentDropdownPlugin = this.plugins[dropdownName];
110
+ if (typeof this.currentDropdownPlugin?.on === 'function') this.currentDropdownPlugin.on(btnEl);
111
+ this.editor._preventBlur = true;
112
+ },
113
+
114
+ /**
115
+ * @this {MenuThis}
116
+ * @description Off dropdown
117
+ */
118
+ dropdownOff() {
119
+ this.__removeGlobalEvent();
120
+ this.index = -1;
121
+ this.menus = [];
122
+ this.__menuBtn = null;
123
+ this.__menuContainer = null;
124
+
125
+ if (this.currentDropdown) {
126
+ this.currentDropdownName = '';
127
+ this.currentDropdownType = '';
128
+ this.currentDropdown.style.display = 'none';
129
+ this.currentDropdown = null;
130
+ if (this.currentDropdownActiveButton) {
131
+ dom.utils.removeClass(this.currentDropdownActiveButton.parentElement.children, 'on');
132
+ }
133
+ this.currentDropdownActiveButton = null;
134
+ this.editor._notHideToolbar = false;
135
+ }
136
+
137
+ this.editor._preventBlur = false;
138
+
139
+ if (typeof this.currentDropdownPlugin?.off === 'function') this.currentDropdownPlugin.off();
140
+ this.currentDropdownPlugin = null;
141
+ },
142
+
143
+ /**
144
+ * @this {MenuThis}
145
+ * @description On menu container
146
+ * @param {Node} button Container's button element to call
147
+ */
148
+ containerOn(button) {
149
+ this.__removeGlobalEvent();
150
+
151
+ this.currentContainerActiveButton = /** @type {HTMLButtonElement} */ (button);
152
+ const containerName = (this.currentContainerName = this.currentContainerActiveButton.getAttribute('data-command'));
153
+ this._setMenuPosition(button, (this.currentContainer = this.targetMap[containerName]));
154
+
155
+ this._bindClose_cons_mouse = this.eventManager.addGlobalEvent('mousedown', this.__globalEventHandler.containerDown, false);
156
+
157
+ if (this.plugins[containerName].on) this.plugins[containerName].on(button);
158
+ this.editor._preventBlur = true;
159
+ },
160
+
161
+ /**
162
+ * @this {MenuThis}
163
+ * @description Off menu container
164
+ */
165
+ containerOff() {
166
+ this.__removeGlobalEvent();
167
+
168
+ if (this.currentContainer) {
169
+ this.currentContainerName = '';
170
+ this.currentContainer.style.display = 'none';
171
+ this.currentContainer = null;
172
+ dom.utils.removeClass(this.currentContainerActiveButton, 'on');
173
+ this.currentContainerActiveButton = null;
174
+ this.editor._notHideToolbar = false;
175
+ }
176
+
177
+ this.editor._preventBlur = false;
178
+ },
179
+
180
+ /**
181
+ * @private
182
+ * @this {MenuThis}
183
+ * @description Set the menu position.
184
+ * @param {Node} element Button element
185
+ * @param {HTMLElement} menu Menu element
186
+ */
187
+ _setMenuPosition(element, menu) {
188
+ menu.style.visibility = 'hidden';
189
+ menu.style.display = 'block';
190
+ menu.style.height = '';
191
+ dom.utils.addClass(element.parentElement.children, 'on');
192
+
193
+ this.offset.setRelPosition(menu, this.carrierWrapper, element.parentElement, dom.query.getParentElement(element, '.se-toolbar'));
194
+
195
+ menu.style.visibility = '';
196
+
197
+ this.__menuBtn = element;
198
+ this.__menuContainer = menu;
199
+ },
200
+
201
+ /**
202
+ * @private
203
+ * @this {MenuThis}
204
+ * @description Reset the menu position.
205
+ * @param {Node} element Button element
206
+ * @param {HTMLElement} menu Menu element
207
+ */
208
+ _resetMenuPosition(element, menu) {
209
+ this.offset.setRelPosition(menu, this.carrierWrapper, element.parentElement, dom.query.getParentElement(element, '.se-toolbar'));
210
+ },
211
+
212
+ /**
213
+ * @private
214
+ * @this {MenuThis}
215
+ * @description Restore the last menu position using previously stored button and menu elements.
216
+ */
217
+ _restoreMenuPosition() {
218
+ if (!this.__menuBtn || !this.__menuContainer) return;
219
+ this._setMenuPosition(this.__menuBtn, this.__menuContainer);
220
+ },
221
+
222
+ /**
223
+ * @private
224
+ * @this {MenuThis}
225
+ * @description Check if the element is part of a more layer
226
+ * @param {Node} element The element to check
227
+ * @returns {HTMLElement|null} The more layer element or null
228
+ */
229
+ _checkMoreLayer(element) {
230
+ const more = dom.query.getParentElement(element, '.se-more-layer');
231
+ if (more && more.style.display !== 'block') {
232
+ return more.getAttribute('data-ref') ? more : null;
233
+ } else {
234
+ return null;
235
+ }
236
+ },
237
+
238
+ /**
239
+ * @private
240
+ * @this {MenuThis}
241
+ * @description Move the selected item in the dropdown menu
242
+ * @param {number} num Direction and amount to move (-1 for up, 1 for down)
243
+ */
244
+ _moveItem(num) {
245
+ dom.utils.removeClass(this.currentDropdown, 'se-select-menu-mouse-move');
246
+ dom.utils.addClass(this.currentDropdown, 'se-select-menu-key-action');
247
+ num = this.index + num;
248
+ const len = this.menus.length;
249
+ const selectIndex = (this.index = num >= len ? 0 : num < 0 ? len - 1 : num);
250
+
251
+ for (let i = 0; i < len; i++) {
252
+ if (i === selectIndex) {
253
+ dom.utils.addClass(this.menus[i], 'on');
254
+ } else {
255
+ dom.utils.removeClass(this.menus[i], 'on');
256
+ }
257
+ }
258
+ },
259
+
260
+ /**
261
+ * @private
262
+ * @this {MenuThis}
263
+ * @description Remove global event listeners
264
+ */
265
+ __removeGlobalEvent() {
266
+ if (this._bindClose_dropdown_mouse) this._bindClose_dropdown_mouse = this.eventManager.removeGlobalEvent(this._bindClose_dropdown_mouse);
267
+ if (this._bindClose_cons_mouse) this._bindClose_cons_mouse = this.eventManager.removeGlobalEvent(this._bindClose_cons_mouse);
268
+ if (this._bindClose_dropdown_key) {
269
+ this._bindClose_dropdown_key = this.eventManager.removeGlobalEvent(this._bindClose_dropdown_key);
270
+ dom.utils.removeClass(this.menus, 'on');
271
+ dom.utils.removeClass(this.currentDropdown, 'se-select-menu-key-action|se-select-menu-mouse-move');
272
+ this.currentDropdown.removeEventListener('mousemove', this.__globalEventHandler.mousemove, false);
273
+ this.currentDropdown.removeEventListener('mouseout', this.__globalEventHandler.mouseout, false);
274
+ }
275
+ },
276
+
277
+ constructor: Menu
278
+ };
279
+
280
+ /**
281
+ * @private
282
+ * @this {MenuThis}
283
+ * @param {MouseEvent} e - Event object
284
+ */
285
+ function OnMouseDown_dropdown(e) {
286
+ const eventTarget = dom.query.getEventTarget(e);
287
+ if (dom.query.getParentElement(eventTarget, '.se-dropdown')) return;
288
+ this.dropdownOff();
289
+ }
290
+
291
+ /**
292
+ * @private
293
+ * @this {MenuThis}
294
+ */
295
+ function OnMouseout_dropdown() {
296
+ this.index = -1;
297
+ }
298
+
299
+ /**
300
+ * @private
301
+ * @this {MenuThis}
302
+ * @param {KeyboardEvent} e - Event object
303
+ */
304
+ function OnKeyDown_dropdown(e) {
305
+ const keyCode = e.code;
306
+ switch (keyCode) {
307
+ case 'ArrowUp': // up
308
+ e.preventDefault();
309
+ e.stopPropagation();
310
+ this._moveItem(-1);
311
+ break;
312
+ case 'ArrowDown': // down
313
+ e.preventDefault();
314
+ e.stopPropagation();
315
+ this._moveItem(1);
316
+ break;
317
+ case 'ArrowLeft': // left
318
+ e.preventDefault();
319
+ e.stopPropagation();
320
+ this._moveItem(-1);
321
+ break;
322
+ case 'ArrowRight': //right
323
+ e.preventDefault();
324
+ e.stopPropagation();
325
+ this._moveItem(1);
326
+ break;
327
+ case 'Enter':
328
+ case 'Space': /* enter, space */ {
329
+ if (this.index < 0) break;
330
+
331
+ const target = this.menus[this.index];
332
+ if (!target || typeof this.plugins[this.currentDropdownName].action !== 'function') return;
333
+
334
+ e.preventDefault();
335
+ e.stopPropagation();
336
+ this.plugins[this.currentDropdownName].action(target);
337
+ this.dropdownOff();
338
+ break;
339
+ }
340
+ }
341
+ }
342
+
343
+ /**
344
+ * @private
345
+ * @this {MenuThis}
346
+ * @param {MouseEvent} e - Event object
347
+ */
348
+ function OnMousemove_dropdown(e) {
349
+ dom.utils.addClass(this.currentDropdown, 'se-select-menu-mouse-move');
350
+ dom.utils.removeClass(this.currentDropdown, 'se-select-menu-key-action');
351
+
352
+ const index = this.menus.indexOf(e.target);
353
+ if (index === -1) return;
354
+ this.index = index * 1;
355
+ }
356
+
357
+ export default Menu;