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,549 +1,549 @@
1
- import CoreInjector from '../editorInjector/_core';
2
- import { dom, env, keyCodeMap } from '../helper';
3
-
4
- const MENU_MIN_HEIGHT = 38;
5
-
6
- /**
7
- * @typedef {Object} SelectMenuParams
8
- * @property {string} position Position of the select menu, specified as "[left|right]-[middle|top|bottom]" or "[top|bottom]-[center|left|right]"
9
- * @property {boolean} [checkList=false] Flag to determine if the checklist is enabled (true or false)
10
- * @property {"rtl" | "ltr"} [dir="ltr"] Optional text direction: "rtl" for right-to-left, "ltr" for left-to-right
11
- * @property {number} [splitNum=0] Optional split number for horizontal positioning; defines how many items per row
12
- * @property {() => void=} openMethod Optional method to call when the menu is opened
13
- * @property {() => void=} closeMethod Optional method to call when the menu is closed
14
- */
15
-
16
- /**
17
- * @class
18
- * @description Creates a select menu
19
- */
20
- class SelectMenu extends CoreInjector {
21
- /**
22
- * @constructor
23
- * @param {*} inst The instance object that called the constructor.
24
- * @param {SelectMenuParams} params Select menu options
25
- */
26
- constructor(inst, params) {
27
- // plugin bisic properties
28
- super(inst.editor);
29
-
30
- // members
31
- this.kink = inst.constructor.key || inst.constructor.name;
32
- this.inst = inst;
33
- const positionItems = params.position.split('-');
34
- this.form = null;
35
- this.items = [];
36
- /** @type {HTMLLIElement[]} */
37
- this.menus = null;
38
- this.menuLen = 0;
39
- this.index = -1;
40
- this.item = null;
41
- this.isOpen = false;
42
- this.checkList = !!params.checkList;
43
- this.position = positionItems[0];
44
- this.subPosition = positionItems[1];
45
- this._dirPosition = /^(left|right)$/.test(this.position) ? (this.position === 'left' ? 'right' : 'left') : this.position;
46
- this._dirSubPosition = /^(left|right)$/.test(this.subPosition) ? (this.subPosition === 'left' ? 'right' : 'left') : this.subPosition;
47
- this._textDirDiff = params.dir === 'ltr' ? false : params.dir === 'rtl' ? true : null;
48
- this.splitNum = params.splitNum || 0;
49
- this.horizontal = !!this.splitNum;
50
- this.openMethod = params.openMethod;
51
- this.closeMethod = params.closeMethod;
52
- this._refer = null;
53
- this._keydownTarget = null;
54
- this._selectMethod = null;
55
- this._bindClose_key = null;
56
- this._bindClose_mousedown = null;
57
- this._bindClose_click = null;
58
- this._closeSignal = false;
59
- this.__events = null;
60
- this.__eventHandlers = {
61
- mousedown: this.#OnMousedown_list.bind(this),
62
- mousemove: this.#OnMouseMove_list.bind(this),
63
- click: this.#OnClick_list.bind(this),
64
- keydown: this.#OnKeyDown_refer.bind(this)
65
- };
66
- this.__globalEventHandlers = { keydown: this.#CloseListener_key.bind(this), mousedown: this.#CloseListener_mousedown.bind(this), click: this.#CloseListener_click.bind(this) };
67
- }
68
-
69
- /**
70
- * @description Creates the select menu items.
71
- * @param {Array<string>|__se__NodeCollection} items - Command list of selectable items.
72
- * @param {Array<string>|__se__NodeCollection} [menus] - Optional list of menu display elements; defaults to `items`.
73
- */
74
- create(items, menus) {
75
- this.form.firstElementChild.innerHTML = '';
76
- menus = menus || items;
77
- let html = '';
78
- for (let i = 0, len = menus.length; i < len; i++) {
79
- if (i > 0 && i % this.splitNum === 0) {
80
- this._createFormat(html);
81
- html = '';
82
- }
83
- html += `<li class="se-select-item" data-index="${i}">${typeof menus[i] === 'string' ? menus[i] : /** @type {HTMLElement} */ (menus[i]).outerHTML}</li>`;
84
- }
85
- this._createFormat(html);
86
-
87
- this.items = /** @type {Array<string|Node>} */ (items);
88
- this.menus = Array.from(this.form.querySelectorAll('li'));
89
- this.menuLen = this.menus.length;
90
- }
91
-
92
- /**
93
- * @description Initializes the select menu and attaches it to a reference element.
94
- * @param {Node} referElement - The element that triggers the select menu.
95
- * @param {(command: string) => void} selectMethod - The function to execute when an item is selected.
96
- * @param {{class?: string, style?: string}} [attr={}] - Additional attributes for the select menu container.
97
- */
98
- on(referElement, selectMethod, attr) {
99
- if (!attr) attr = {};
100
- this._refer = /** @type {HTMLElement} */ (referElement);
101
- this._keydownTarget = dom.check.isInputElement(referElement) ? referElement : this._w;
102
- this._selectMethod = selectMethod;
103
- this.form = dom.utils.createElement(
104
- 'DIV',
105
- {
106
- class: 'se-select-menu' + (attr.class ? ' ' + attr.class : ''),
107
- style: attr.style || ''
108
- },
109
- '<div class="se-list-inner"></div>'
110
- );
111
- referElement.parentNode.insertBefore(this.form, referElement);
112
- }
113
-
114
- /**
115
- * @description Select menu open
116
- * @param {?string=} position "[left|right]-[middle|top|bottom] | [top|bottom]-[center|left|right]"
117
- * @param {?string=} onItemQuerySelector The querySelector string of the menu to be activated
118
- */
119
- open(position, onItemQuerySelector) {
120
- this.editor.selectMenuOn = true;
121
- if (typeof this.openMethod === 'function') this.openMethod();
122
- this.__addEvents();
123
- this.__addGlobalEvent();
124
- const positionItems = position ? position.split('-') : [];
125
- const mainPosition = positionItems[0] || (this._textDirDiff !== null && this._textDirDiff !== this.options.get('_rtl') ? this._dirPosition : this.position);
126
- const subPosition = positionItems[1] || (this._textDirDiff !== null && this._textDirDiff !== this.options.get('_rtl') ? this._dirSubPosition : this.subPosition);
127
- this._setPosition(mainPosition, subPosition, onItemQuerySelector);
128
- this.isOpen = true;
129
- }
130
-
131
- /**
132
- * @description Select menu close
133
- */
134
- close() {
135
- this.editor.selectMenuOn = false;
136
- dom.utils.removeClass(this._refer, 'on');
137
- this._init();
138
- if (this.form) this.form.style.cssText = '';
139
- this.isOpen = false;
140
- if (typeof this.closeMethod === 'function') this.closeMethod();
141
- }
142
-
143
- /**
144
- * @description Get the index of the selected item
145
- * @param {number} index Item index
146
- * @returns
147
- */
148
- getItem(index) {
149
- return this.items[index];
150
- }
151
-
152
- /**
153
- * @description Set the index of the selected item
154
- * @param {number} index Item index
155
- */
156
- setItem(index) {
157
- this._selectItem(index);
158
- }
159
-
160
- /**
161
- * @private
162
- * @description Appends a formatted list of items to the menu.
163
- * @param {string} html - The HTML string representing the menu items.
164
- */
165
- _createFormat(html) {
166
- this.form.firstElementChild.innerHTML += `<ul class="se-list-basic se-list-checked${this.horizontal ? ' se-list-horizontal' : ''}">${html}</ul>`;
167
- }
168
-
169
- /**
170
- * @private
171
- * @description Resets the menu state and removes event listeners.
172
- */
173
- _init() {
174
- this.__removeEvents();
175
- this.__removeGlobalEvent();
176
- this.index = -1;
177
- this.item = null;
178
- if (this._onItem) {
179
- dom.utils.removeClass(this._onItem, 'se-select-on');
180
- this._onItem = null;
181
- }
182
- }
183
-
184
- /**
185
- * @private
186
- * @description Moves the selection up or down by a specified number of items.
187
- * @param {number} num - The number of items to move (negative for up, positive for down).
188
- */
189
- _moveItem(num) {
190
- num = this.index + num;
191
- const len = this.menuLen;
192
- const selectIndex = (this.index = num >= len ? 0 : num < 0 ? len - 1 : num);
193
-
194
- this._selectItem(selectIndex);
195
- }
196
-
197
- /**
198
- * @private
199
- * @description Highlights and selects an item by index.
200
- * @param {number} selectIndex - The index of the item to select.
201
- */
202
- _selectItem(selectIndex) {
203
- dom.utils.removeClass(this.form, 'se-select-menu-mouse-move');
204
-
205
- const len = this.menuLen;
206
- for (let i = 0; i < len; i++) {
207
- if (i === selectIndex) {
208
- dom.utils.addClass(this.menus[i], 'active');
209
- } else {
210
- dom.utils.removeClass(this.menus[i], 'active');
211
- }
212
- }
213
-
214
- this.index = selectIndex;
215
- this.item = this.items[selectIndex];
216
- }
217
-
218
- /**
219
- * @private
220
- * @description Sets the position of the select menu relative to the reference element.
221
- * @param {string} position Menu position ("left"|"right") | ("top"|"bottom")
222
- * @param {string} subPosition Sub position ("middle"|"top"|"bottom") | ("center"|"left"|"right")
223
- * @param {string} [onItemQuerySelector] - A query selector string to highlight a specific item.
224
- * @param {boolean} [_re=false] - Whether this is a retry after adjusting the position.
225
- */
226
- _setPosition(position, subPosition, onItemQuerySelector, _re) {
227
- const originP = position;
228
- const form = this.form;
229
- const target = this._refer;
230
- form.style.visibility = 'hidden';
231
- form.style.display = 'block';
232
- dom.utils.removeClass(form, 'se-select-menu-scroll');
233
- dom.utils.addClass(target, 'on');
234
-
235
- const formW = form.offsetWidth;
236
- const targetW = target.offsetWidth;
237
- const targetL = target.offsetLeft;
238
- let side = false;
239
- let l = 0,
240
- t = 0;
241
-
242
- if (position === 'left') {
243
- l = targetL - formW - 1;
244
- position = subPosition;
245
- side = true;
246
- } else if (position === 'right') {
247
- l = targetL + targetW + 1;
248
- position = subPosition;
249
- side = true;
250
- }
251
-
252
- // set top position
253
- const globalTarget = this.editor.offset.get(target);
254
- const targetOffsetTop = target.offsetTop;
255
- const targetGlobalTop = globalTarget.top;
256
- const targetHeight = target.offsetHeight;
257
- const wbottom = dom.utils.getClientSize().h - (targetGlobalTop - this._w.scrollY + targetHeight);
258
- const sideAddH = side ? targetHeight : 0;
259
- let overH = 10000;
260
- switch (position) {
261
- case 'middle': {
262
- let h = form.offsetHeight;
263
- const th = targetHeight / 2;
264
- t = targetOffsetTop - h / 2 + th;
265
- // over top
266
- if (targetGlobalTop < h / 2) {
267
- t += h / 2 - targetGlobalTop - th + 4;
268
- form.style.top = t + 'px';
269
- }
270
- // over bottom
271
- let formT = this.editor.offset.getGlobal(form).top;
272
- const modH = h - (targetGlobalTop - formT) - wbottom - targetHeight;
273
- if (modH > 0) {
274
- t -= modH + 4;
275
- form.style.top = t + 'px';
276
- }
277
- // over height
278
- formT = this.editor.offset.getGlobal(form).top;
279
- if (formT < 0) {
280
- h += formT - 4;
281
- t -= formT - 4;
282
- }
283
- form.style.height = h + 'px';
284
- break;
285
- }
286
- case 'top':
287
- if (targetGlobalTop < form.offsetHeight - sideAddH) {
288
- if (!_re) {
289
- overH = 0;
290
- break;
291
- }
292
- overH = targetGlobalTop - 4 + sideAddH;
293
- if (overH >= MENU_MIN_HEIGHT) form.style.height = overH + 'px';
294
- }
295
- t = targetOffsetTop - form.offsetHeight + sideAddH;
296
- break;
297
- case 'bottom':
298
- if (wbottom < form.offsetHeight + sideAddH) {
299
- if (!_re) {
300
- overH = 0;
301
- break;
302
- }
303
- overH = wbottom - 4 + sideAddH;
304
- if (overH >= MENU_MIN_HEIGHT) form.style.height = overH + 'px';
305
- }
306
- t = targetOffsetTop + (side ? 0 : targetHeight);
307
- break;
308
- }
309
-
310
- if (overH < MENU_MIN_HEIGHT && !_re && position !== 'middle') {
311
- this._setPosition(position === 'top' ? 'bottpm' : 'top', subPosition, onItemQuerySelector, true);
312
- return;
313
- }
314
-
315
- if (!side) {
316
- switch (subPosition) {
317
- case 'center':
318
- l = targetL + targetW / 2 - formW / 2;
319
- break;
320
- case 'left':
321
- l = targetL;
322
- break;
323
- case 'right':
324
- l = targetL - (formW - targetW);
325
- break;
326
- }
327
- }
328
-
329
- form.style.left = l + 'px';
330
- const fl = this.editor.offset.getGlobal(form).left;
331
- let overW = 0;
332
- switch (side + '-' + (side ? originP : subPosition)) {
333
- case 'true-left':
334
- overW = globalTarget.left - this._w.scrollX + fl;
335
- if (overW < 0) l = l = targetL + targetW + 1;
336
- break;
337
- case 'true-right':
338
- overW = this._w.innerWidth - (fl + formW);
339
- if (overW < 0) l = targetL - formW - 1;
340
- break;
341
- case 'false-center': {
342
- overW = this._w.innerWidth - (fl + formW);
343
- if (overW < 0) l += overW - 4;
344
- form.style.left = l + 'px';
345
- const centerfl = this.editor.offset.getGlobal(form).left;
346
- if (centerfl < 0) l -= centerfl - 4;
347
- break;
348
- }
349
- case 'false-left':
350
- overW = this._w.innerWidth - (globalTarget.left - this._w.scrollX + formW);
351
- if (overW < 0) l += overW - 4;
352
- break;
353
- case 'false-right':
354
- if (fl < 0) l -= fl - 4;
355
- break;
356
- }
357
-
358
- if (onItemQuerySelector) {
359
- const item = form.firstElementChild.querySelector(onItemQuerySelector);
360
- if (item) {
361
- this._onItem = item;
362
- dom.utils.addClass(item, 'se-select-on');
363
- }
364
- }
365
-
366
- form.style.left = l + 'px';
367
- form.style.top = t + 'px';
368
- form.style.visibility = '';
369
- }
370
-
371
- /**
372
- * @private
373
- * @description Selects an item and triggers the callback function.
374
- * @param {number} index - The index of the item to select.
375
- */
376
- _select(index) {
377
- if (this.checkList) dom.utils.toggleClass(this.menus[index], 'se-checked');
378
- this._selectMethod(this.getItem(index));
379
- }
380
-
381
- /**
382
- * @private
383
- * @description Adds event listeners for menu interactions.
384
- */
385
- __addEvents() {
386
- this.__removeEvents();
387
- this.__events = this.__eventHandlers;
388
- this.form.addEventListener('mousedown', this.__events.mousedown);
389
- this.form.addEventListener('mousemove', this.__events.mousemove);
390
- this.form.addEventListener('click', this.__events.click);
391
- this._keydownTarget.addEventListener('keydown', this.__events.keydown);
392
- }
393
-
394
- /**
395
- * @private
396
- * @description Removes event listeners for menu interactions.
397
- */
398
- __removeEvents() {
399
- if (!this.__events) return;
400
- this.form.removeEventListener('mousedown', this.__events.mousedown);
401
- this.form.removeEventListener('mousemove', this.__events.mousemove);
402
- this.form.removeEventListener('click', this.__events.click);
403
- this._keydownTarget.removeEventListener('keydown', this.__events.keydown);
404
- this.__events = null;
405
- }
406
-
407
- /**
408
- * @private
409
- * @description Adds global event listeners for closing the menu.
410
- */
411
- __addGlobalEvent() {
412
- this.__removeGlobalEvent();
413
- this._bindClose_key = this.eventManager.addGlobalEvent('keydown', this.__globalEventHandlers.keydown, true);
414
- this._bindClose_mousedown = this.eventManager.addGlobalEvent('mousedown', this.__globalEventHandlers.mousedown, true);
415
- }
416
-
417
- /**
418
- * @private
419
- * @description Removes global event listeners for closing the menu.
420
- */
421
- __removeGlobalEvent() {
422
- if (this._bindClose_key) this._bindClose_key = this.eventManager.removeGlobalEvent(this._bindClose_key);
423
- if (this._bindClose_mousedown) this._bindClose_mousedown = this.eventManager.removeGlobalEvent(this._bindClose_mousedown);
424
- if (this._bindClose_click) this._bindClose_click = this.eventManager.removeGlobalEvent(this._bindClose_click);
425
- }
426
-
427
- /**
428
- * @param {KeyboardEvent} e - Event object
429
- */
430
- #OnKeyDown_refer(e) {
431
- let moveIndex;
432
- switch (e.code) {
433
- case 'ArrowUp': // up
434
- e.preventDefault();
435
- e.stopPropagation();
436
- if (this.horizontal && this.index > -1) {
437
- const num = this.splitNum;
438
- moveIndex = this.index - num < 0 ? num : -num;
439
- } else {
440
- moveIndex = -1;
441
- }
442
- break;
443
- case 'ArrowDown': // down
444
- e.preventDefault();
445
- e.stopPropagation();
446
- if (this.horizontal && this.index > -1) {
447
- const num = this.splitNum;
448
- moveIndex = this.index + num > this.menuLen ? -num : num;
449
- } else {
450
- moveIndex = 1;
451
- }
452
- break;
453
- case 'ArrowLeft': // left
454
- e.preventDefault();
455
- e.stopPropagation();
456
- moveIndex = -1;
457
- break;
458
- case 'ArrowRight': //right
459
- e.preventDefault();
460
- e.stopPropagation();
461
- moveIndex = 1;
462
- break;
463
- case 'Enter':
464
- case 'Space': // enter, space
465
- if (this.index > -1) {
466
- e.preventDefault();
467
- e.stopPropagation();
468
- this._select(this.index);
469
- } else {
470
- this.close();
471
- }
472
- break;
473
- }
474
-
475
- if (moveIndex) this._moveItem(moveIndex);
476
- }
477
-
478
- /**
479
- * @param {MouseEvent} e - Event object
480
- */
481
- #OnMousedown_list(e) {
482
- if (env.isGecko) {
483
- const eventTarget = dom.query.getEventTarget(e);
484
- const target = dom.query.getParentElement(eventTarget, '.se-select-item');
485
- if (target) this.eventManager._injectActiveEvent(target);
486
- }
487
- }
488
-
489
- /**
490
- * @param {MouseEvent} e - Event object
491
- */
492
- #OnMouseMove_list(e) {
493
- const eventTarget = dom.query.getEventTarget(e);
494
- dom.utils.addClass(this.form, 'se-select-menu-mouse-move');
495
- const index = eventTarget.getAttribute('data-index');
496
- if (!index) return;
497
- this.index = Number(index);
498
- }
499
-
500
- /**
501
- * @param {MouseEvent} e - Event object
502
- */
503
- #OnClick_list(e) {
504
- let target = dom.query.getEventTarget(e);
505
- let index = null;
506
-
507
- while (!index && !/UL/i.test(target.tagName) && !dom.utils.hasClass(target, 'se-select-menu')) {
508
- index = target.getAttribute('data-index');
509
- target = target.parentElement;
510
- }
511
-
512
- if (!index) return;
513
- this._select(Number(index));
514
- }
515
-
516
- /**
517
- * @param {KeyboardEvent} e - Event object
518
- */
519
- #CloseListener_key(e) {
520
- if (!keyCodeMap.isEsc(e.code)) return;
521
- this.close();
522
- }
523
-
524
- /**
525
- * @param {MouseEvent} e - Event object
526
- */
527
- #CloseListener_mousedown(e) {
528
- const eventTarget = dom.query.getEventTarget(e);
529
- if (this.form.contains(eventTarget)) return;
530
- if (e.target !== this._refer) {
531
- this.close();
532
- } else if (!dom.check.isInputElement(eventTarget)) {
533
- this._bindClose_click = this.eventManager.addGlobalEvent('click', this.__globalEventHandlers.click, true);
534
- }
535
- }
536
-
537
- /**
538
- * @param {MouseEvent} e - Event object
539
- */
540
- #CloseListener_click(e) {
541
- this._bindClose_click = this.eventManager.removeGlobalEvent(this._bindClose_click);
542
- if (e.target === this._refer) {
543
- e.stopPropagation();
544
- this.close();
545
- }
546
- }
547
- }
548
-
549
- export default SelectMenu;
1
+ import CoreInjector from '../editorInjector/_core';
2
+ import { dom, env, keyCodeMap } from '../helper';
3
+
4
+ const MENU_MIN_HEIGHT = 38;
5
+
6
+ /**
7
+ * @typedef {Object} SelectMenuParams
8
+ * @property {string} position Position of the select menu, specified as "[left|right]-[middle|top|bottom]" or "[top|bottom]-[center|left|right]"
9
+ * @property {boolean} [checkList=false] Flag to determine if the checklist is enabled (true or false)
10
+ * @property {"rtl" | "ltr"} [dir="ltr"] Optional text direction: "rtl" for right-to-left, "ltr" for left-to-right
11
+ * @property {number} [splitNum=0] Optional split number for horizontal positioning; defines how many items per row
12
+ * @property {() => void=} openMethod Optional method to call when the menu is opened
13
+ * @property {() => void=} closeMethod Optional method to call when the menu is closed
14
+ */
15
+
16
+ /**
17
+ * @class
18
+ * @description Creates a select menu
19
+ */
20
+ class SelectMenu extends CoreInjector {
21
+ /**
22
+ * @constructor
23
+ * @param {*} inst The instance object that called the constructor.
24
+ * @param {SelectMenuParams} params Select menu options
25
+ */
26
+ constructor(inst, params) {
27
+ // plugin bisic properties
28
+ super(inst.editor);
29
+
30
+ // members
31
+ this.kink = inst.constructor.key || inst.constructor.name;
32
+ this.inst = inst;
33
+ const positionItems = params.position.split('-');
34
+ this.form = null;
35
+ this.items = [];
36
+ /** @type {HTMLLIElement[]} */
37
+ this.menus = null;
38
+ this.menuLen = 0;
39
+ this.index = -1;
40
+ this.item = null;
41
+ this.isOpen = false;
42
+ this.checkList = !!params.checkList;
43
+ this.position = positionItems[0];
44
+ this.subPosition = positionItems[1];
45
+ this._dirPosition = /^(left|right)$/.test(this.position) ? (this.position === 'left' ? 'right' : 'left') : this.position;
46
+ this._dirSubPosition = /^(left|right)$/.test(this.subPosition) ? (this.subPosition === 'left' ? 'right' : 'left') : this.subPosition;
47
+ this._textDirDiff = params.dir === 'ltr' ? false : params.dir === 'rtl' ? true : null;
48
+ this.splitNum = params.splitNum || 0;
49
+ this.horizontal = !!this.splitNum;
50
+ this.openMethod = params.openMethod;
51
+ this.closeMethod = params.closeMethod;
52
+ this._refer = null;
53
+ this._keydownTarget = null;
54
+ this._selectMethod = null;
55
+ this._bindClose_key = null;
56
+ this._bindClose_mousedown = null;
57
+ this._bindClose_click = null;
58
+ this._closeSignal = false;
59
+ this.__events = null;
60
+ this.__eventHandlers = {
61
+ mousedown: this.#OnMousedown_list.bind(this),
62
+ mousemove: this.#OnMouseMove_list.bind(this),
63
+ click: this.#OnClick_list.bind(this),
64
+ keydown: this.#OnKeyDown_refer.bind(this)
65
+ };
66
+ this.__globalEventHandlers = { keydown: this.#CloseListener_key.bind(this), mousedown: this.#CloseListener_mousedown.bind(this), click: this.#CloseListener_click.bind(this) };
67
+ }
68
+
69
+ /**
70
+ * @description Creates the select menu items.
71
+ * @param {Array<string>|__se__NodeCollection} items - Command list of selectable items.
72
+ * @param {Array<string>|__se__NodeCollection} [menus] - Optional list of menu display elements; defaults to `items`.
73
+ */
74
+ create(items, menus) {
75
+ this.form.firstElementChild.innerHTML = '';
76
+ menus = menus || items;
77
+ let html = '';
78
+ for (let i = 0, len = menus.length; i < len; i++) {
79
+ if (i > 0 && i % this.splitNum === 0) {
80
+ this._createFormat(html);
81
+ html = '';
82
+ }
83
+ html += `<li class="se-select-item" data-index="${i}">${typeof menus[i] === 'string' ? menus[i] : /** @type {HTMLElement} */ (menus[i]).outerHTML}</li>`;
84
+ }
85
+ this._createFormat(html);
86
+
87
+ this.items = /** @type {Array<string|Node>} */ (items);
88
+ this.menus = Array.from(this.form.querySelectorAll('li'));
89
+ this.menuLen = this.menus.length;
90
+ }
91
+
92
+ /**
93
+ * @description Initializes the select menu and attaches it to a reference element.
94
+ * @param {Node} referElement - The element that triggers the select menu.
95
+ * @param {(command: string) => void} selectMethod - The function to execute when an item is selected.
96
+ * @param {{class?: string, style?: string}} [attr={}] - Additional attributes for the select menu container.
97
+ */
98
+ on(referElement, selectMethod, attr) {
99
+ if (!attr) attr = {};
100
+ this._refer = /** @type {HTMLElement} */ (referElement);
101
+ this._keydownTarget = dom.check.isInputElement(referElement) ? referElement : this.editor.frameContext.get('_ww');
102
+ this._selectMethod = selectMethod;
103
+ this.form = dom.utils.createElement(
104
+ 'DIV',
105
+ {
106
+ class: 'se-select-menu' + (attr.class ? ' ' + attr.class : ''),
107
+ style: attr.style || ''
108
+ },
109
+ '<div class="se-list-inner"></div>'
110
+ );
111
+ referElement.parentNode.insertBefore(this.form, referElement);
112
+ }
113
+
114
+ /**
115
+ * @description Select menu open
116
+ * @param {?string=} position "[left|right]-[middle|top|bottom] | [top|bottom]-[center|left|right]"
117
+ * @param {?string=} onItemQuerySelector The querySelector string of the menu to be activated
118
+ */
119
+ open(position, onItemQuerySelector) {
120
+ this.editor.selectMenuOn = true;
121
+ if (typeof this.openMethod === 'function') this.openMethod();
122
+ this.__addEvents();
123
+ this.__addGlobalEvent();
124
+ const positionItems = position ? position.split('-') : [];
125
+ const mainPosition = positionItems[0] || (this._textDirDiff !== null && this._textDirDiff !== this.options.get('_rtl') ? this._dirPosition : this.position);
126
+ const subPosition = positionItems[1] || (this._textDirDiff !== null && this._textDirDiff !== this.options.get('_rtl') ? this._dirSubPosition : this.subPosition);
127
+ this._setPosition(mainPosition, subPosition, onItemQuerySelector);
128
+ this.isOpen = true;
129
+ }
130
+
131
+ /**
132
+ * @description Select menu close
133
+ */
134
+ close() {
135
+ this.editor.selectMenuOn = false;
136
+ dom.utils.removeClass(this._refer, 'on');
137
+ this._init();
138
+ if (this.form) this.form.style.cssText = '';
139
+ this.isOpen = false;
140
+ if (typeof this.closeMethod === 'function') this.closeMethod();
141
+ }
142
+
143
+ /**
144
+ * @description Get the index of the selected item
145
+ * @param {number} index Item index
146
+ * @returns
147
+ */
148
+ getItem(index) {
149
+ return this.items[index];
150
+ }
151
+
152
+ /**
153
+ * @description Set the index of the selected item
154
+ * @param {number} index Item index
155
+ */
156
+ setItem(index) {
157
+ this._selectItem(index);
158
+ }
159
+
160
+ /**
161
+ * @private
162
+ * @description Appends a formatted list of items to the menu.
163
+ * @param {string} html - The HTML string representing the menu items.
164
+ */
165
+ _createFormat(html) {
166
+ this.form.firstElementChild.innerHTML += `<ul class="se-list-basic se-list-checked${this.horizontal ? ' se-list-horizontal' : ''}">${html}</ul>`;
167
+ }
168
+
169
+ /**
170
+ * @private
171
+ * @description Resets the menu state and removes event listeners.
172
+ */
173
+ _init() {
174
+ this.__removeEvents();
175
+ this.__removeGlobalEvent();
176
+ this.index = -1;
177
+ this.item = null;
178
+ if (this._onItem) {
179
+ dom.utils.removeClass(this._onItem, 'se-select-on');
180
+ this._onItem = null;
181
+ }
182
+ }
183
+
184
+ /**
185
+ * @private
186
+ * @description Moves the selection up or down by a specified number of items.
187
+ * @param {number} num - The number of items to move (negative for up, positive for down).
188
+ */
189
+ _moveItem(num) {
190
+ num = this.index + num;
191
+ const len = this.menuLen;
192
+ const selectIndex = (this.index = num >= len ? 0 : num < 0 ? len - 1 : num);
193
+
194
+ this._selectItem(selectIndex);
195
+ }
196
+
197
+ /**
198
+ * @private
199
+ * @description Highlights and selects an item by index.
200
+ * @param {number} selectIndex - The index of the item to select.
201
+ */
202
+ _selectItem(selectIndex) {
203
+ dom.utils.removeClass(this.form, 'se-select-menu-mouse-move');
204
+
205
+ const len = this.menuLen;
206
+ for (let i = 0; i < len; i++) {
207
+ if (i === selectIndex) {
208
+ dom.utils.addClass(this.menus[i], 'active');
209
+ } else {
210
+ dom.utils.removeClass(this.menus[i], 'active');
211
+ }
212
+ }
213
+
214
+ this.index = selectIndex;
215
+ this.item = this.items[selectIndex];
216
+ }
217
+
218
+ /**
219
+ * @private
220
+ * @description Sets the position of the select menu relative to the reference element.
221
+ * @param {string} position Menu position ("left"|"right") | ("top"|"bottom")
222
+ * @param {string} subPosition Sub position ("middle"|"top"|"bottom") | ("center"|"left"|"right")
223
+ * @param {string} [onItemQuerySelector] - A query selector string to highlight a specific item.
224
+ * @param {boolean} [_re=false] - Whether this is a retry after adjusting the position.
225
+ */
226
+ _setPosition(position, subPosition, onItemQuerySelector, _re) {
227
+ const originP = position;
228
+ const form = this.form;
229
+ const target = this._refer;
230
+ form.style.visibility = 'hidden';
231
+ form.style.display = 'block';
232
+ dom.utils.removeClass(form, 'se-select-menu-scroll');
233
+ dom.utils.addClass(target, 'on');
234
+
235
+ const formW = form.offsetWidth;
236
+ const targetW = target.offsetWidth;
237
+ const targetL = target.offsetLeft;
238
+ let side = false;
239
+ let l = 0,
240
+ t = 0;
241
+
242
+ if (position === 'left') {
243
+ l = targetL - formW - 1;
244
+ position = subPosition;
245
+ side = true;
246
+ } else if (position === 'right') {
247
+ l = targetL + targetW + 1;
248
+ position = subPosition;
249
+ side = true;
250
+ }
251
+
252
+ // set top position
253
+ const globalTarget = this.editor.offset.get(target);
254
+ const targetOffsetTop = target.offsetTop;
255
+ const targetGlobalTop = globalTarget.top;
256
+ const targetHeight = target.offsetHeight;
257
+ const wbottom = dom.utils.getClientSize().h - (targetGlobalTop - this._w.scrollY + targetHeight);
258
+ const sideAddH = side ? targetHeight : 0;
259
+ let overH = 10000;
260
+ switch (position) {
261
+ case 'middle': {
262
+ let h = form.offsetHeight;
263
+ const th = targetHeight / 2;
264
+ t = targetOffsetTop - h / 2 + th;
265
+ // over top
266
+ if (targetGlobalTop < h / 2) {
267
+ t += h / 2 - targetGlobalTop - th + 4;
268
+ form.style.top = t + 'px';
269
+ }
270
+ // over bottom
271
+ let formT = this.editor.offset.getGlobal(form).top;
272
+ const modH = h - (targetGlobalTop - formT) - wbottom - targetHeight;
273
+ if (modH > 0) {
274
+ t -= modH + 4;
275
+ form.style.top = t + 'px';
276
+ }
277
+ // over height
278
+ formT = this.editor.offset.getGlobal(form).top;
279
+ if (formT < 0) {
280
+ h += formT - 4;
281
+ t -= formT - 4;
282
+ }
283
+ form.style.height = h + 'px';
284
+ break;
285
+ }
286
+ case 'top':
287
+ if (targetGlobalTop < form.offsetHeight - sideAddH) {
288
+ if (!_re) {
289
+ overH = 0;
290
+ break;
291
+ }
292
+ overH = targetGlobalTop - 4 + sideAddH;
293
+ if (overH >= MENU_MIN_HEIGHT) form.style.height = overH + 'px';
294
+ }
295
+ t = targetOffsetTop - form.offsetHeight + sideAddH;
296
+ break;
297
+ case 'bottom':
298
+ if (wbottom < form.offsetHeight + sideAddH) {
299
+ if (!_re) {
300
+ overH = 0;
301
+ break;
302
+ }
303
+ overH = wbottom - 4 + sideAddH;
304
+ if (overH >= MENU_MIN_HEIGHT) form.style.height = overH + 'px';
305
+ }
306
+ t = targetOffsetTop + (side ? 0 : targetHeight);
307
+ break;
308
+ }
309
+
310
+ if (overH < MENU_MIN_HEIGHT && !_re && position !== 'middle') {
311
+ this._setPosition(position === 'top' ? 'bottpm' : 'top', subPosition, onItemQuerySelector, true);
312
+ return;
313
+ }
314
+
315
+ if (!side) {
316
+ switch (subPosition) {
317
+ case 'center':
318
+ l = targetL + targetW / 2 - formW / 2;
319
+ break;
320
+ case 'left':
321
+ l = targetL;
322
+ break;
323
+ case 'right':
324
+ l = targetL - (formW - targetW);
325
+ break;
326
+ }
327
+ }
328
+
329
+ form.style.left = l + 'px';
330
+ const fl = this.editor.offset.getGlobal(form).left;
331
+ let overW = 0;
332
+ switch (side + '-' + (side ? originP : subPosition)) {
333
+ case 'true-left':
334
+ overW = globalTarget.left - this._w.scrollX + fl;
335
+ if (overW < 0) l = l = targetL + targetW + 1;
336
+ break;
337
+ case 'true-right':
338
+ overW = this._w.innerWidth - (fl + formW);
339
+ if (overW < 0) l = targetL - formW - 1;
340
+ break;
341
+ case 'false-center': {
342
+ overW = this._w.innerWidth - (fl + formW);
343
+ if (overW < 0) l += overW - 4;
344
+ form.style.left = l + 'px';
345
+ const centerfl = this.editor.offset.getGlobal(form).left;
346
+ if (centerfl < 0) l -= centerfl - 4;
347
+ break;
348
+ }
349
+ case 'false-left':
350
+ overW = this._w.innerWidth - (globalTarget.left - this._w.scrollX + formW);
351
+ if (overW < 0) l += overW - 4;
352
+ break;
353
+ case 'false-right':
354
+ if (fl < 0) l -= fl - 4;
355
+ break;
356
+ }
357
+
358
+ if (onItemQuerySelector) {
359
+ const item = form.firstElementChild.querySelector(onItemQuerySelector);
360
+ if (item) {
361
+ this._onItem = item;
362
+ dom.utils.addClass(item, 'se-select-on');
363
+ }
364
+ }
365
+
366
+ form.style.left = l + 'px';
367
+ form.style.top = t + 'px';
368
+ form.style.visibility = '';
369
+ }
370
+
371
+ /**
372
+ * @private
373
+ * @description Selects an item and triggers the callback function.
374
+ * @param {number} index - The index of the item to select.
375
+ */
376
+ _select(index) {
377
+ if (this.checkList) dom.utils.toggleClass(this.menus[index], 'se-checked');
378
+ this._selectMethod(this.getItem(index));
379
+ }
380
+
381
+ /**
382
+ * @private
383
+ * @description Adds event listeners for menu interactions.
384
+ */
385
+ __addEvents() {
386
+ this.__removeEvents();
387
+ this.__events = this.__eventHandlers;
388
+ this.form.addEventListener('mousedown', this.__events.mousedown);
389
+ this.form.addEventListener('mousemove', this.__events.mousemove);
390
+ this.form.addEventListener('click', this.__events.click);
391
+ this._keydownTarget.addEventListener('keydown', this.__events.keydown);
392
+ }
393
+
394
+ /**
395
+ * @private
396
+ * @description Removes event listeners for menu interactions.
397
+ */
398
+ __removeEvents() {
399
+ if (!this.__events) return;
400
+ this.form.removeEventListener('mousedown', this.__events.mousedown);
401
+ this.form.removeEventListener('mousemove', this.__events.mousemove);
402
+ this.form.removeEventListener('click', this.__events.click);
403
+ this._keydownTarget.removeEventListener('keydown', this.__events.keydown);
404
+ this.__events = null;
405
+ }
406
+
407
+ /**
408
+ * @private
409
+ * @description Adds global event listeners for closing the menu.
410
+ */
411
+ __addGlobalEvent() {
412
+ this.__removeGlobalEvent();
413
+ this._bindClose_key = this.eventManager.addGlobalEvent('keydown', this.__globalEventHandlers.keydown, true);
414
+ this._bindClose_mousedown = this.eventManager.addGlobalEvent('mousedown', this.__globalEventHandlers.mousedown, true);
415
+ }
416
+
417
+ /**
418
+ * @private
419
+ * @description Removes global event listeners for closing the menu.
420
+ */
421
+ __removeGlobalEvent() {
422
+ if (this._bindClose_key) this._bindClose_key = this.eventManager.removeGlobalEvent(this._bindClose_key);
423
+ if (this._bindClose_mousedown) this._bindClose_mousedown = this.eventManager.removeGlobalEvent(this._bindClose_mousedown);
424
+ if (this._bindClose_click) this._bindClose_click = this.eventManager.removeGlobalEvent(this._bindClose_click);
425
+ }
426
+
427
+ /**
428
+ * @param {KeyboardEvent} e - Event object
429
+ */
430
+ #OnKeyDown_refer(e) {
431
+ let moveIndex;
432
+ switch (e.code) {
433
+ case 'ArrowUp': // up
434
+ e.preventDefault();
435
+ e.stopPropagation();
436
+ if (this.horizontal && this.index > -1) {
437
+ const num = this.splitNum;
438
+ moveIndex = this.index - num < 0 ? num : -num;
439
+ } else {
440
+ moveIndex = -1;
441
+ }
442
+ break;
443
+ case 'ArrowDown': // down
444
+ e.preventDefault();
445
+ e.stopPropagation();
446
+ if (this.horizontal && this.index > -1) {
447
+ const num = this.splitNum;
448
+ moveIndex = this.index + num > this.menuLen ? -num : num;
449
+ } else {
450
+ moveIndex = 1;
451
+ }
452
+ break;
453
+ case 'ArrowLeft': // left
454
+ e.preventDefault();
455
+ e.stopPropagation();
456
+ moveIndex = -1;
457
+ break;
458
+ case 'ArrowRight': //right
459
+ e.preventDefault();
460
+ e.stopPropagation();
461
+ moveIndex = 1;
462
+ break;
463
+ case 'Enter':
464
+ case 'Space': // enter, space
465
+ if (this.index > -1) {
466
+ e.preventDefault();
467
+ e.stopPropagation();
468
+ this._select(this.index);
469
+ } else {
470
+ this.close();
471
+ }
472
+ break;
473
+ }
474
+
475
+ if (moveIndex) this._moveItem(moveIndex);
476
+ }
477
+
478
+ /**
479
+ * @param {MouseEvent} e - Event object
480
+ */
481
+ #OnMousedown_list(e) {
482
+ if (env.isGecko) {
483
+ const eventTarget = dom.query.getEventTarget(e);
484
+ const target = dom.query.getParentElement(eventTarget, '.se-select-item');
485
+ if (target) this.eventManager._injectActiveEvent(target);
486
+ }
487
+ }
488
+
489
+ /**
490
+ * @param {MouseEvent} e - Event object
491
+ */
492
+ #OnMouseMove_list(e) {
493
+ const eventTarget = dom.query.getEventTarget(e);
494
+ dom.utils.addClass(this.form, 'se-select-menu-mouse-move');
495
+ const index = eventTarget.getAttribute('data-index');
496
+ if (!index) return;
497
+ this.index = Number(index);
498
+ }
499
+
500
+ /**
501
+ * @param {MouseEvent} e - Event object
502
+ */
503
+ #OnClick_list(e) {
504
+ let target = dom.query.getEventTarget(e);
505
+ let index = null;
506
+
507
+ while (!index && !/UL/i.test(target.tagName) && !dom.utils.hasClass(target, 'se-select-menu')) {
508
+ index = target.getAttribute('data-index');
509
+ target = target.parentElement;
510
+ }
511
+
512
+ if (!index) return;
513
+ this._select(Number(index));
514
+ }
515
+
516
+ /**
517
+ * @param {KeyboardEvent} e - Event object
518
+ */
519
+ #CloseListener_key(e) {
520
+ if (!keyCodeMap.isEsc(e.code)) return;
521
+ this.close();
522
+ }
523
+
524
+ /**
525
+ * @param {MouseEvent} e - Event object
526
+ */
527
+ #CloseListener_mousedown(e) {
528
+ const eventTarget = dom.query.getEventTarget(e);
529
+ if (this.form.contains(eventTarget)) return;
530
+ if (e.target !== this._refer) {
531
+ this.close();
532
+ } else if (!dom.check.isInputElement(eventTarget)) {
533
+ this._bindClose_click = this.eventManager.addGlobalEvent('click', this.__globalEventHandlers.click, true);
534
+ }
535
+ }
536
+
537
+ /**
538
+ * @param {MouseEvent} e - Event object
539
+ */
540
+ #CloseListener_click(e) {
541
+ this._bindClose_click = this.eventManager.removeGlobalEvent(this._bindClose_click);
542
+ if (e.target === this._refer) {
543
+ e.stopPropagation();
544
+ this.close();
545
+ }
546
+ }
547
+ }
548
+
549
+ export default SelectMenu;