suneditor 3.0.0-beta.3 → 3.0.0-beta.30

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 (241) hide show
  1. package/CONTRIBUTING.md +8 -8
  2. package/README.md +44 -49
  3. package/dist/suneditor.min.css +1 -1
  4. package/dist/suneditor.min.js +1 -1
  5. package/package.json +95 -53
  6. package/src/assets/design/color.css +2 -2
  7. package/src/assets/design/size.css +2 -0
  8. package/src/assets/icons/defaultIcons.js +16 -1
  9. package/src/assets/suneditor-contents.css +9 -8
  10. package/src/assets/suneditor.css +29 -26
  11. package/src/core/{section → base}/actives.js +20 -12
  12. package/src/core/base/history.js +4 -4
  13. package/src/core/class/char.js +10 -10
  14. package/src/core/class/component.js +146 -57
  15. package/src/core/class/format.js +94 -2458
  16. package/src/core/class/html.js +187 -129
  17. package/src/core/class/inline.js +1853 -0
  18. package/src/core/class/listFormat.js +582 -0
  19. package/src/core/class/menu.js +14 -3
  20. package/src/core/class/nodeTransform.js +9 -14
  21. package/src/core/class/offset.js +162 -197
  22. package/src/core/class/selection.js +137 -34
  23. package/src/core/class/toolbar.js +73 -52
  24. package/src/core/class/ui.js +11 -11
  25. package/src/core/class/viewer.js +56 -55
  26. package/src/core/config/context.js +122 -0
  27. package/src/core/config/frameContext.js +204 -0
  28. package/src/core/config/options.js +639 -0
  29. package/src/core/editor.js +181 -108
  30. package/src/core/event/actions/index.js +229 -0
  31. package/src/core/event/effects/common.registry.js +60 -0
  32. package/src/core/event/effects/keydown.registry.js +551 -0
  33. package/src/core/event/effects/ruleHelpers.js +145 -0
  34. package/src/core/{base → event}/eventManager.js +119 -201
  35. package/src/core/event/executor.js +21 -0
  36. package/src/core/{base/eventHandlers → event/handlers}/handler_toolbar.js +4 -4
  37. package/src/core/{base/eventHandlers → event/handlers}/handler_ww_dragDrop.js +2 -2
  38. package/src/core/event/handlers/handler_ww_input.js +77 -0
  39. package/src/core/event/handlers/handler_ww_key.js +228 -0
  40. package/src/core/{base/eventHandlers → event/handlers}/handler_ww_mouse.js +3 -3
  41. package/src/core/event/ports.js +211 -0
  42. package/src/core/event/reducers/keydown.reducer.js +89 -0
  43. package/src/core/event/rules/keydown.rule.arrow.js +54 -0
  44. package/src/core/event/rules/keydown.rule.backspace.js +202 -0
  45. package/src/core/event/rules/keydown.rule.delete.js +126 -0
  46. package/src/core/event/rules/keydown.rule.enter.js +144 -0
  47. package/src/core/event/rules/keydown.rule.tab.js +29 -0
  48. package/src/core/section/constructor.js +79 -388
  49. package/src/core/section/documentType.js +47 -26
  50. package/src/core/util/instanceCheck.js +59 -0
  51. package/src/editorInjector/_classes.js +4 -0
  52. package/src/editorInjector/_core.js +17 -7
  53. package/src/editorInjector/index.js +10 -2
  54. package/src/events.js +6 -0
  55. package/src/helper/clipboard.js +24 -10
  56. package/src/helper/converter.js +17 -12
  57. package/src/helper/dom/domCheck.js +22 -3
  58. package/src/helper/dom/domQuery.js +91 -45
  59. package/src/helper/dom/domUtils.js +93 -19
  60. package/src/helper/dom/index.js +4 -0
  61. package/src/helper/env.js +11 -7
  62. package/src/helper/keyCodeMap.js +4 -3
  63. package/src/langs/ckb.js +1 -1
  64. package/src/langs/cs.js +1 -1
  65. package/src/langs/da.js +1 -1
  66. package/src/langs/de.js +1 -1
  67. package/src/langs/en.js +1 -1
  68. package/src/langs/es.js +1 -1
  69. package/src/langs/fa.js +1 -1
  70. package/src/langs/fr.js +1 -1
  71. package/src/langs/he.js +1 -1
  72. package/src/langs/hu.js +1 -1
  73. package/src/langs/it.js +1 -1
  74. package/src/langs/ja.js +1 -1
  75. package/src/langs/km.js +1 -1
  76. package/src/langs/ko.js +1 -1
  77. package/src/langs/lv.js +1 -1
  78. package/src/langs/nl.js +1 -1
  79. package/src/langs/pl.js +1 -1
  80. package/src/langs/pt_br.js +10 -10
  81. package/src/langs/ro.js +1 -1
  82. package/src/langs/ru.js +1 -1
  83. package/src/langs/se.js +1 -1
  84. package/src/langs/tr.js +1 -1
  85. package/src/langs/uk.js +1 -1
  86. package/src/langs/ur.js +1 -1
  87. package/src/langs/zh_cn.js +1 -1
  88. package/src/modules/ApiManager.js +25 -18
  89. package/src/modules/Browser.js +52 -61
  90. package/src/modules/ColorPicker.js +37 -38
  91. package/src/modules/Controller.js +85 -79
  92. package/src/modules/Figure.js +275 -187
  93. package/src/modules/FileManager.js +86 -92
  94. package/src/modules/HueSlider.js +67 -35
  95. package/src/modules/Modal.js +84 -77
  96. package/src/modules/ModalAnchorEditor.js +62 -79
  97. package/src/modules/SelectMenu.js +89 -86
  98. package/src/plugins/browser/audioGallery.js +9 -5
  99. package/src/plugins/browser/fileBrowser.js +10 -6
  100. package/src/plugins/browser/fileGallery.js +9 -5
  101. package/src/plugins/browser/imageGallery.js +9 -5
  102. package/src/plugins/browser/videoGallery.js +11 -6
  103. package/src/plugins/command/blockquote.js +1 -0
  104. package/src/plugins/command/exportPDF.js +11 -8
  105. package/src/plugins/command/fileUpload.js +41 -29
  106. package/src/plugins/command/list_bulleted.js +2 -1
  107. package/src/plugins/command/list_numbered.js +2 -1
  108. package/src/plugins/dropdown/align.js +8 -2
  109. package/src/plugins/dropdown/backgroundColor.js +19 -11
  110. package/src/plugins/dropdown/font.js +15 -9
  111. package/src/plugins/dropdown/fontColor.js +19 -11
  112. package/src/plugins/dropdown/formatBlock.js +7 -2
  113. package/src/plugins/dropdown/hr.js +7 -3
  114. package/src/plugins/dropdown/layout.js +6 -2
  115. package/src/plugins/dropdown/lineHeight.js +8 -3
  116. package/src/plugins/dropdown/list.js +2 -1
  117. package/src/plugins/dropdown/paragraphStyle.js +15 -11
  118. package/src/plugins/dropdown/{table.js → table/index.js} +514 -362
  119. package/src/plugins/dropdown/template.js +6 -2
  120. package/src/plugins/dropdown/textStyle.js +7 -3
  121. package/src/plugins/field/mention.js +33 -27
  122. package/src/plugins/input/fontSize.js +44 -37
  123. package/src/plugins/input/pageNavigator.js +3 -2
  124. package/src/plugins/modal/audio.js +90 -85
  125. package/src/plugins/modal/drawing.js +58 -66
  126. package/src/plugins/modal/embed.js +193 -180
  127. package/src/plugins/modal/image.js +441 -439
  128. package/src/plugins/modal/link.js +31 -8
  129. package/src/plugins/modal/math.js +23 -22
  130. package/src/plugins/modal/video.js +233 -230
  131. package/src/plugins/popup/anchor.js +24 -18
  132. package/src/suneditor.js +69 -24
  133. package/src/typedef.js +42 -19
  134. package/types/assets/icons/defaultIcons.d.ts +8 -0
  135. package/types/core/class/char.d.ts +1 -1
  136. package/types/core/class/component.d.ts +29 -7
  137. package/types/core/class/format.d.ts +4 -354
  138. package/types/core/class/html.d.ts +13 -4
  139. package/types/core/class/inline.d.ts +263 -0
  140. package/types/core/class/listFormat.d.ts +135 -0
  141. package/types/core/class/menu.d.ts +10 -2
  142. package/types/core/class/offset.d.ts +24 -26
  143. package/types/core/class/selection.d.ts +2 -0
  144. package/types/core/class/toolbar.d.ts +24 -11
  145. package/types/core/class/ui.d.ts +1 -1
  146. package/types/core/class/viewer.d.ts +1 -1
  147. package/types/core/config/context.d.ts +157 -0
  148. package/types/core/config/frameContext.d.ts +367 -0
  149. package/types/core/config/options.d.ts +1119 -0
  150. package/types/core/editor.d.ts +101 -66
  151. package/types/core/event/actions/index.d.ts +47 -0
  152. package/types/core/event/effects/common.registry.d.ts +50 -0
  153. package/types/core/event/effects/keydown.registry.d.ts +73 -0
  154. package/types/core/event/effects/ruleHelpers.d.ts +31 -0
  155. package/types/core/{base → event}/eventManager.d.ts +15 -46
  156. package/types/core/event/executor.d.ts +6 -0
  157. package/types/core/event/handlers/handler_ww_input.d.ts +41 -0
  158. package/types/core/{base/eventHandlers/handler_ww_key_input.d.ts → event/handlers/handler_ww_key.d.ts} +4 -6
  159. package/types/core/event/ports.d.ts +255 -0
  160. package/types/core/event/reducers/keydown.reducer.d.ts +75 -0
  161. package/types/core/event/rules/keydown.rule.arrow.d.ts +8 -0
  162. package/types/core/event/rules/keydown.rule.backspace.d.ts +9 -0
  163. package/types/core/event/rules/keydown.rule.delete.d.ts +9 -0
  164. package/types/core/event/rules/keydown.rule.enter.d.ts +9 -0
  165. package/types/core/event/rules/keydown.rule.tab.d.ts +9 -0
  166. package/types/core/section/constructor.d.ts +101 -631
  167. package/types/core/section/documentType.d.ts +14 -4
  168. package/types/core/util/instanceCheck.d.ts +50 -0
  169. package/types/editorInjector/_classes.d.ts +4 -0
  170. package/types/editorInjector/_core.d.ts +17 -7
  171. package/types/editorInjector/index.d.ts +10 -2
  172. package/types/events.d.ts +1 -0
  173. package/types/helper/clipboard.d.ts +2 -2
  174. package/types/helper/converter.d.ts +6 -9
  175. package/types/helper/dom/domCheck.d.ts +7 -0
  176. package/types/helper/dom/domQuery.d.ts +19 -8
  177. package/types/helper/dom/domUtils.d.ts +24 -2
  178. package/types/helper/dom/index.d.ts +86 -1
  179. package/types/helper/env.d.ts +6 -1
  180. package/types/helper/index.d.ts +7 -1
  181. package/types/helper/keyCodeMap.d.ts +3 -3
  182. package/types/index.d.ts +23 -117
  183. package/types/langs/index.d.ts +2 -2
  184. package/types/modules/ApiManager.d.ts +1 -8
  185. package/types/modules/Browser.d.ts +4 -62
  186. package/types/modules/ColorPicker.d.ts +4 -21
  187. package/types/modules/Controller.d.ts +8 -64
  188. package/types/modules/Figure.d.ts +54 -50
  189. package/types/modules/FileManager.d.ts +1 -13
  190. package/types/modules/HueSlider.d.ts +13 -3
  191. package/types/modules/Modal.d.ts +0 -43
  192. package/types/modules/ModalAnchorEditor.d.ts +0 -73
  193. package/types/modules/SelectMenu.d.ts +0 -85
  194. package/types/modules/index.d.ts +3 -3
  195. package/types/plugins/browser/audioGallery.d.ts +29 -18
  196. package/types/plugins/browser/fileBrowser.d.ts +38 -27
  197. package/types/plugins/browser/fileGallery.d.ts +29 -18
  198. package/types/plugins/browser/imageGallery.d.ts +24 -16
  199. package/types/plugins/browser/videoGallery.d.ts +29 -18
  200. package/types/plugins/command/blockquote.d.ts +1 -0
  201. package/types/plugins/command/exportPDF.d.ts +18 -18
  202. package/types/plugins/command/fileUpload.d.ts +65 -45
  203. package/types/plugins/command/list_bulleted.d.ts +1 -0
  204. package/types/plugins/command/list_numbered.d.ts +1 -0
  205. package/types/plugins/dropdown/align.d.ts +13 -8
  206. package/types/plugins/dropdown/backgroundColor.d.ts +30 -19
  207. package/types/plugins/dropdown/font.d.ts +13 -12
  208. package/types/plugins/dropdown/fontColor.d.ts +30 -19
  209. package/types/plugins/dropdown/formatBlock.d.ts +13 -8
  210. package/types/plugins/dropdown/hr.d.ts +15 -11
  211. package/types/plugins/dropdown/layout.d.ts +15 -11
  212. package/types/plugins/dropdown/lineHeight.d.ts +16 -11
  213. package/types/plugins/dropdown/list.d.ts +1 -0
  214. package/types/plugins/dropdown/paragraphStyle.d.ts +31 -27
  215. package/types/plugins/dropdown/table/index.d.ts +582 -0
  216. package/types/plugins/dropdown/table.d.ts +41 -86
  217. package/types/plugins/dropdown/template.d.ts +15 -11
  218. package/types/plugins/dropdown/textStyle.d.ts +19 -11
  219. package/types/plugins/field/mention.d.ts +58 -56
  220. package/types/plugins/index.d.ts +38 -38
  221. package/types/plugins/input/fontSize.d.ts +46 -50
  222. package/types/plugins/modal/audio.d.ts +26 -56
  223. package/types/plugins/modal/drawing.d.ts +0 -85
  224. package/types/plugins/modal/embed.d.ts +15 -79
  225. package/types/plugins/modal/image.d.ts +24 -136
  226. package/types/plugins/modal/link.d.ts +34 -15
  227. package/types/plugins/modal/math.d.ts +0 -16
  228. package/types/plugins/modal/video.d.ts +17 -86
  229. package/types/plugins/popup/anchor.d.ts +1 -8
  230. package/types/suneditor.d.ts +70 -19
  231. package/types/typedef.d.ts +60 -46
  232. package/src/core/base/eventHandlers/handler_ww_key_input.js +0 -1200
  233. package/src/core/section/context.js +0 -102
  234. package/types/core/section/context.d.ts +0 -45
  235. package/types/langs/_Lang.d.ts +0 -194
  236. /package/src/core/{base/eventHandlers → event/handlers}/handler_ww_clipboard.js +0 -0
  237. /package/types/core/{section → base}/actives.d.ts +0 -0
  238. /package/types/core/{base/eventHandlers → event/handlers}/handler_toolbar.d.ts +0 -0
  239. /package/types/core/{base/eventHandlers → event/handlers}/handler_ww_clipboard.d.ts +0 -0
  240. /package/types/core/{base/eventHandlers → event/handlers}/handler_ww_dragDrop.d.ts +0 -0
  241. /package/types/core/{base/eventHandlers → event/handlers}/handler_ww_mouse.d.ts +0 -0
@@ -104,7 +104,7 @@ class ColorPicker extends CoreInjector {
104
104
  this.inputElement = /** @type {HTMLInputElement} */ (this.target.querySelector('.se-color-input'));
105
105
  this.styleProperties = styles;
106
106
  this.splitNum = params.splitNum || 0;
107
- this.defaultColor = params.defaultColor;
107
+ this.defaultColor = params.defaultColor || '';
108
108
  this.hueSliderOptions = params.hueSliderOptions;
109
109
  this.parentDisplay = '';
110
110
  this.currentColor = '';
@@ -123,7 +123,7 @@ class ColorPicker extends CoreInjector {
123
123
  this.parentFormDisplay = [];
124
124
  this.parentForm = params.hueSliderOptions?.controllerOptions?.parents?.length > 0 && !params.hueSliderOptions?.controllerOptions?.isInsideForm ? params.hueSliderOptions.controllerOptions.parents : null;
125
125
  // hue open
126
- this.eventManager.addEvent(this.target.querySelector('.se-btn-info'), 'click', this.#OnColorPalette.bind(this));
126
+ this.eventManager.addEvent(this.target.querySelector('.__se_hue'), 'click', this.#OnColorPalette.bind(this));
127
127
  this.eventManager.addEvent(this.inputElement, 'input', this.#OnChangeInput.bind(this));
128
128
  this.eventManager.addEvent(this.target.querySelector('form'), 'submit', this.#Submit.bind(this));
129
129
  }
@@ -140,14 +140,15 @@ class ColorPicker extends CoreInjector {
140
140
  * @description Displays or resets the currently selected color at color list.
141
141
  * @param {Node|string} nodeOrColor Current Selected node
142
142
  * @param {Node} target target
143
+ * @param {?((current: Node) => boolean)=} stopCondition - A function used to stop traversing parent nodes while finding the color.
144
+ * - When this function returns true, the traversal ends at that node.
145
+ * - e.g., `(node) => this.format.isLine(node)` stops at line-level elements like <p>, <div>.
143
146
  */
144
- init(nodeOrColor, target) {
147
+ init(nodeOrColor, target, stopCondition) {
145
148
  this.targetButton = target;
149
+ if (typeof stopCondition !== 'function') stopCondition = () => false;
146
150
 
147
- const computedColor = this.editor.frameContext.get('wwComputedStyle')[this.styleProperties];
148
- const defaultColor = this.defaultColor || converter.isHexColor(computedColor) ? computedColor : converter.rgb2hex(computedColor);
149
-
150
- let fillColor = (typeof nodeOrColor === 'string' ? nodeOrColor : this._getColorInNode(nodeOrColor)) || defaultColor;
151
+ let fillColor = (typeof nodeOrColor === 'string' ? nodeOrColor : this.#getColorInNode(nodeOrColor, stopCondition)) || this.defaultColor;
151
152
  fillColor = converter.isHexColor(fillColor) ? fillColor : converter.rgb2hex(fillColor) || fillColor || '';
152
153
 
153
154
  const colorList = this.colorList;
@@ -162,7 +163,7 @@ class ColorPicker extends CoreInjector {
162
163
  }
163
164
  }
164
165
 
165
- this._setInputText(this._colorName2hex(fillColor));
166
+ this.#setInputText(this.#colorName2hex(fillColor));
166
167
  }
167
168
 
168
169
  /**
@@ -182,27 +183,45 @@ class ColorPicker extends CoreInjector {
182
183
  }
183
184
 
184
185
  /**
185
- * @private
186
+ * @editorMethod Modules.HueSlider
187
+ * @description This method is called when the color is selected in the hue slider.
188
+ * @param {HueSliderColor_colorPicker} color - Color object
189
+ */
190
+ hueSliderAction(color) {
191
+ this.#setInputText(color.hex);
192
+ }
193
+
194
+ /**
195
+ * @editorMethod Modules.HueSlider
196
+ * @description This method is called when the hue slider is closed.
197
+ */
198
+ hueSliderCancelAction() {
199
+ if (this.parentForm?.length > 0) {
200
+ this.parentFormDisplay.forEach((e) => (e[0].style.display = e[1]));
201
+ }
202
+ }
203
+
204
+ /**
186
205
  * @description Set color at input element
187
206
  * @param {string} hexColorStr Hax color value
188
207
  */
189
- _setInputText(hexColorStr) {
190
- hexColorStr = /^#/.test(hexColorStr) ? hexColorStr : '#' + hexColorStr;
208
+ #setInputText(hexColorStr) {
209
+ hexColorStr = !hexColorStr || /^#/.test(hexColorStr) ? hexColorStr : '#' + hexColorStr;
191
210
  this.inputElement.value = hexColorStr;
192
211
  this.setHexColor.call(this, hexColorStr);
193
212
  }
194
213
 
195
214
  /**
196
- * @private
197
215
  * @description Gets color value at color property of node
198
216
  * @param {Node} node Selected node
217
+ * @param {(current: Node) => boolean} stopCondition - A function used to stop traversing parent nodes while finding the color.
199
218
  * @returns {string}
200
219
  */
201
- _getColorInNode(node) {
220
+ #getColorInNode(node, stopCondition) {
202
221
  let findColor = '';
203
222
  const sp = this.styleProperties;
204
223
 
205
- while (node && !dom.check.isWysiwygFrame(node) && findColor.length === 0) {
224
+ while (node && !stopCondition(node) && !dom.check.isWysiwygFrame(node) && findColor.length === 0) {
206
225
  if (isElement(node) && node.style[sp]) findColor = node.style[sp];
207
226
  node = node.parentNode;
208
227
  }
@@ -211,13 +230,12 @@ class ColorPicker extends CoreInjector {
211
230
  }
212
231
 
213
232
  /**
214
- * @private
215
233
  * @description Converts color values of other formats to hex color values and returns.
216
234
  * @param {string} colorName Color value
217
235
  * @returns {string}
218
236
  */
219
- _colorName2hex(colorName) {
220
- if (/^#/.test(colorName)) return colorName;
237
+ #colorName2hex(colorName) {
238
+ if (!colorName || /^#/.test(colorName)) return colorName;
221
239
  const temp = dom.utils.createElement('div', { style: 'display: none; color: ' + colorName });
222
240
  const colors = this._w
223
241
  .getComputedStyle(this._d.body.appendChild(temp))
@@ -229,25 +247,6 @@ class ColorPicker extends CoreInjector {
229
247
  return colors.length >= 3 ? '#' + ((1 << 24) + (colors[0] << 16) + (colors[1] << 8) + colors[2]).toString(16).substring(1) : '';
230
248
  }
231
249
 
232
- /**
233
- * @editorMethod Modules.HueSlider
234
- * @description This method is called when the color is selected in the hue slider.
235
- * @param {HueSliderColor_colorPicker} color - Color object
236
- */
237
- hueSliderAction(color) {
238
- this._setInputText(color.hex);
239
- }
240
-
241
- /**
242
- * @editorMethod Modules.HueSlider
243
- * @description This method is called when the hue slider is closed.
244
- */
245
- hueSliderCancelAction() {
246
- if (this.parentForm?.length > 0) {
247
- this.parentFormDisplay.forEach((e) => (e[0].style.display = e[1]));
248
- }
249
- }
250
-
251
250
  #OnColorPalette() {
252
251
  if (this.parentForm?.length > 0) {
253
252
  this.parentForm.forEach((e) => {
@@ -303,7 +302,7 @@ class ColorPicker extends CoreInjector {
303
302
  * @returns
304
303
  */
305
304
  function CreateHTML({ lang, icons }, { colorList, disableHEXInput, disableRemove, splitNum }) {
306
- colorList = colorList || DEFAULT_COLOR_LIST;
305
+ colorList ||= DEFAULT_COLOR_LIST;
307
306
  splitNum = colorList === DEFAULT_COLOR_LIST ? DEFAULLT_COLOR_SPLITNUM : splitNum;
308
307
 
309
308
  let list = '';
@@ -325,7 +324,7 @@ function CreateHTML({ lang, icons }, { colorList, disableHEXInput, disableRemove
325
324
  }
326
325
  list += /*html*/ `
327
326
  <form class="se-form-group se-form-w0">
328
- ${disableHEXInput ? '' : `<button type="button" class="se-btn se-btn-info" title="${lang.colorPicker}" aria-label="${lang.colorPicker}">${icons.color_palette}</button>`}
327
+ ${disableHEXInput ? '' : `<button type="button" class="se-btn __se_hue" title="${lang.colorPicker}" aria-label="${lang.colorPicker}">${icons.color_palette}</button>`}
329
328
  <input type="text" class="se-color-input" ${disableHEXInput ? 'readonly' : ''} placeholder="${lang.color}" />
330
329
  ${disableHEXInput ? '' : `<button type="submit" class="se-btn se-btn-success" title="${lang.submitButton}" aria-label="${lang.submitButton}">${icons.checked}</button>`}
331
330
  ${disableRemove ? '' : `<button type="button" class="se-btn __se_remove" title="${lang.remove}" aria-label="${lang.remove}">${icons.remove_color}</button>`}
@@ -2,7 +2,7 @@ import EditorInjector from '../editorInjector';
2
2
  import { dom, env, keyCodeMap } from '../helper';
3
3
  import { _DragHandle } from '../modules';
4
4
 
5
- const { _w, ON_OVER_COMPONENT } = env;
5
+ const { _w, isMobile, ON_OVER_COMPONENT } = env;
6
6
  const INDEX_00 = '2147483646';
7
7
  const INDEX_0 = '2147483645';
8
8
  const INDEX_S_1 = '2147483641';
@@ -28,7 +28,9 @@ const INDEX_1 = '2147483640';
28
28
  * @property {Array<HTMLElement>=} [parents=[]] The parent "controller" array when "controller" is opened nested.
29
29
  * @property {boolean=} [parentsHide=false] If true, the parent element is hidden when the controller is opened.
30
30
  * @property {HTMLElement=} [sibling=null] The related sibling controller element that this controller is positioned relative to.
31
- * @property {"top"|"side"} [siblingPosition="top"] The relative position of this controller to the sibling element (e.g., display above or beside the sibling).
31
+ * - e.g.) table plugin :: 118
32
+ * @property {boolean=} [siblingMain=false] If true, This sibling controller is the main controller.
33
+ * - You must specify this option, if use "sibling"
32
34
  * @property {boolean=} [isInsideForm=false] If the controller is inside a form, set it to true.
33
35
  * @property {boolean=} [isOutsideForm=false] If the controller is outside a form, set it to true.
34
36
  */
@@ -38,6 +40,16 @@ const INDEX_1 = '2147483640';
38
40
  * @description Controller module class that handles the UI and interaction logic for a specific editor controller element.
39
41
  */
40
42
  class Controller extends EditorInjector {
43
+ #reserveIndex;
44
+ #initMethod;
45
+ #globalEventHandlers;
46
+ #bindClose_key;
47
+ #bindClose_mouse;
48
+ #addOffset;
49
+ #shadowRootEventForm;
50
+ #shadowRootEventListener;
51
+ #preventClose;
52
+
41
53
  /**
42
54
  * @constructor
43
55
  * @param {*} inst The instance object that called the constructor.
@@ -61,20 +73,22 @@ class Controller extends EditorInjector {
61
73
  this.parents = /** @type {Array<HTMLElement>} */ (params.parents || []);
62
74
  this.parentsHide = !!params.parentsHide;
63
75
  this.sibling = /** @type {HTMLElement} */ (params.sibling || null);
64
- this.siblingPosition = ['top', 'side'].includes(params.siblingPosition) ? params.siblingPosition : 'top';
76
+ this.siblingMain = !!params.siblingMain;
65
77
  this.isInsideForm = !!params.isInsideForm;
66
78
  this.isOutsideForm = !!params.isOutsideForm;
67
79
  this.toTop = false;
68
- this._reserveIndex = false;
69
- this._initMethod = typeof params.initMethod === 'function' ? params.initMethod : null;
70
- this.__globalEventHandlers = { keydown: this.#CloseListener_keydown.bind(this), mousedown: this.#CloseListener_mousedown.bind(this) };
71
- this._bindClose_key = null;
72
- this._bindClose_mouse = null;
73
80
  /** @type {{left?: number, top?: number, addOfffset?: {left?: number, top?: number}}} */
74
81
  this.__offset = {};
75
- this.__addOffset = { left: 0, top: 0 };
76
- this.__shadowRootEventForm = null;
77
- this.__shadowRootEventListener = null;
82
+
83
+ this.#reserveIndex = false;
84
+ this.#initMethod = typeof params.initMethod === 'function' ? params.initMethod : null;
85
+ this.#globalEventHandlers = { keydown: this.#CloseListener_keydown.bind(this), mousedown: this.#CloseListener_mousedown.bind(this) };
86
+ this.#bindClose_key = null;
87
+ this.#bindClose_mouse = null;
88
+ this.#addOffset = { left: 0, top: 0 };
89
+ this.#shadowRootEventForm = null;
90
+ this.#shadowRootEventListener = null;
91
+ this.#preventClose = false;
78
92
 
79
93
  // add element
80
94
  this.carrierWrapper.appendChild(element);
@@ -118,11 +132,11 @@ class Controller extends EditorInjector {
118
132
 
119
133
  this.currentPositionTarget = positionTarget || target;
120
134
  this.isWWTarget = isWWTarget ?? this.isWWTarget;
121
- if (typeof initMethod === 'function') this._initMethod = initMethod;
135
+ if (typeof initMethod === 'function') this.#initMethod = initMethod;
122
136
  this.editor.currentControllerName = this.kind;
123
137
 
124
- this.__addOffset = { left: 0, top: 0 };
125
- if (addOffset) this.__addOffset = { ...this.__addOffset, ...addOffset };
138
+ this.#addOffset = { left: 0, top: 0 };
139
+ if (addOffset) this.#addOffset = { ...this.#addOffset, ...addOffset };
126
140
 
127
141
  const parents = this.isOutsideForm ? this.parents : [];
128
142
  this.editor.opendControllers?.forEach((e) => {
@@ -135,23 +149,15 @@ class Controller extends EditorInjector {
135
149
  });
136
150
  }
137
151
 
138
- this.__addGlobalEvent();
152
+ this.#addGlobalEvent();
139
153
 
140
- // add sibling offset
141
- if (this.sibling) {
142
- if (this.siblingPosition === 'top') {
143
- this.__addOffset.top += -this.sibling.offsetHeight + 1;
144
- } else {
145
- this.__addOffset.left += this.form.offsetWidth + this.sibling.offsetWidth - 1;
146
- }
147
- }
148
154
  // display controller
149
- this._setControllerPosition(this.form, this.currentPositionTarget, false);
155
+ this.#setControllerPosition(this.form, this.currentPositionTarget, false);
150
156
 
151
- const isRangeTarget = target instanceof Range;
157
+ const isRangeTarget = this.instanceCheck.isRange(target);
152
158
  this.currentTarget = isRangeTarget ? null : target;
153
- this._controllerOn(this.form, target, isRangeTarget);
154
- this._w.setTimeout(() => _DragHandle.set('__overInfo', false), 0);
159
+ this.#controllerOn(this.form, target, isRangeTarget);
160
+ _w.setTimeout(() => _DragHandle.set('__overInfo', false), 0);
155
161
  }
156
162
 
157
163
  /**
@@ -160,17 +166,18 @@ class Controller extends EditorInjector {
160
166
  * @param {boolean=} force If true, parent controllers are forcibly closed.
161
167
  */
162
168
  close(force) {
163
- if (!this.isOpen) return;
169
+ if (!this.isOpen || this.#preventClose) return;
164
170
 
165
171
  this.toTop = false;
166
172
  this.isOpen = false;
173
+ this.#preventClose = false;
167
174
  this.__offset = {};
168
- this.__addOffset = { left: 0, top: 0 };
175
+ this.#addOffset = { left: 0, top: 0 };
169
176
 
170
- this.__removeGlobalEvent();
177
+ this.#removeGlobalEvent();
171
178
 
172
- if (typeof this._initMethod === 'function') this._initMethod();
173
- this._controllerOff();
179
+ if (typeof this.#initMethod === 'function') this.#initMethod();
180
+ this.#controllerOff();
174
181
 
175
182
  if (this.parentsHide && !force) {
176
183
  this.parents.forEach((e) => {
@@ -194,7 +201,7 @@ class Controller extends EditorInjector {
194
201
  * @description Show controller
195
202
  */
196
203
  show() {
197
- this._setControllerPosition(this.form, this.currentPositionTarget, false);
204
+ this.#setControllerPosition(this.form, this.currentPositionTarget, false);
198
205
  }
199
206
 
200
207
  /**
@@ -211,17 +218,16 @@ class Controller extends EditorInjector {
211
218
  * @param {Node=} target
212
219
  */
213
220
  resetPosition(target) {
214
- this._setControllerPosition(this.form, target || this.currentPositionTarget, true);
221
+ this.#setControllerPosition(this.form, target || this.currentPositionTarget, true);
215
222
  }
216
223
 
217
224
  /**
218
- * @private
219
225
  * @description Show controller at editor area (controller elements, function, "controller target element(@Required)", "controller name(@Required)", etc..)
220
226
  * @param {HTMLFormElement} form Controller element
221
227
  * @param {Node|Range} target Controller target element
222
228
  * @param {boolean} isRangeTarget If the target is a Range, set it to true.
223
229
  */
224
- async _controllerOn(form, target, isRangeTarget) {
230
+ async #controllerOn(form, target, isRangeTarget) {
225
231
  /** @type {ControllerInfo} */
226
232
  const info = {
227
233
  position: this.position,
@@ -232,16 +238,16 @@ class Controller extends EditorInjector {
232
238
  notInCarrier: !this.carrierWrapper.contains(form)
233
239
  };
234
240
 
235
- if ((await this.triggerEvent('onBeforeShowController', { caller: this.kind, frameContext: this.editor.frameContext, info })) === false) return;
241
+ if ((await this.triggerEvent('onBeforeShowController', { caller: this.kind, frameContext: this.frameContext, info })) === false) return;
236
242
 
237
243
  form.style.display = 'block';
238
- if (this._shadowRoot) {
239
- this.__shadowRootEventForm = form;
240
- this.__shadowRootEventListener = (e) => e.stopPropagation();
241
- form.addEventListener('mousedown', this.__shadowRootEventListener);
244
+ if (this.editor._shadowRoot) {
245
+ this.#shadowRootEventForm = form;
246
+ this.#shadowRootEventListener = (e) => e.stopPropagation();
247
+ form.addEventListener('mousedown', this.#shadowRootEventListener);
242
248
  }
243
249
 
244
- this.editor._controllerTargetContext = this.editor.frameContext.get('topArea');
250
+ this.editor._controllerTargetContext = this.frameContext.get('topArea');
245
251
 
246
252
  if (!this.isOpen) {
247
253
  this.editor.opendControllers.push(info);
@@ -250,21 +256,20 @@ class Controller extends EditorInjector {
250
256
  this.isOpen = true;
251
257
  this.editor._preventBlur = true;
252
258
  this.editor.status.onSelected = true;
253
- this.triggerEvent('onShowController', { caller: this.kind, frameContext: this.editor.frameContext, info });
259
+ this.triggerEvent('onShowController', { caller: this.kind, frameContext: this.frameContext, info });
254
260
  }
255
261
 
256
262
  /**
257
- * @private
258
263
  * @description Hide controller at editor area (link button, image resize button..)
259
264
  */
260
- _controllerOff() {
265
+ #controllerOff() {
261
266
  this.form.style.display = 'none';
262
267
  this.editor.opendControllers = this.editor.opendControllers.filter((v) => v.form !== this.form);
263
268
  if (this.editor.currentControllerName !== this.kind && this.editor.opendControllers.length > 0) return;
264
269
 
265
270
  this.ui.setControllerOnDisabledButtons(false);
266
271
 
267
- this.editor.frameContext.get('lineBreaker_t').style.display = this.editor.frameContext.get('lineBreaker_b').style.display = 'none';
272
+ this.frameContext.get('lineBreaker_t').style.display = this.frameContext.get('lineBreaker_b').style.display = 'none';
268
273
  this.editor.effectNode = null;
269
274
  this.editor.currentControllerName = '';
270
275
  this.editor._preventBlur = false;
@@ -272,24 +277,28 @@ class Controller extends EditorInjector {
272
277
  _w.setTimeout(() => {
273
278
  this.editor.status.onSelected = false;
274
279
  }, 0);
275
- if (this.__shadowRootEventForm) {
276
- this.__shadowRootEventForm.removeEventListener('mousedown', this.__shadowRootEventListener);
277
- this.__shadowRootEventForm = this.__shadowRootEventListener = null;
280
+ if (this.#shadowRootEventForm) {
281
+ this.#shadowRootEventForm.removeEventListener('mousedown', this.#shadowRootEventListener);
282
+ this.#shadowRootEventForm = this.#shadowRootEventListener = null;
278
283
  }
279
284
  if (typeof this.inst.reset === 'function') this.inst.reset();
280
285
  }
281
286
 
282
287
  /**
283
- * @private
284
288
  * @description Specify the position of the controller.
285
289
  * @param {HTMLElement} controller Controller element.
286
290
  * @param {Node|Range} refer Element or Range that is the basis of the controller's position.
287
291
  * @param {boolean} [skipAutoReposition=false] If true, skips scroll/resize-based automatic positioning logic.
288
292
  */
289
- _setControllerPosition(controller, refer, skipAutoReposition) {
293
+ #setControllerPosition(controller, refer, skipAutoReposition) {
290
294
  controller.style.visibility = 'hidden';
291
295
  controller.style.display = 'block';
292
296
 
297
+ if (this.sibling && this.sibling.style.display !== 'block') {
298
+ this.sibling.style.visibility = 'hidden';
299
+ this.sibling.style.display = 'block';
300
+ }
301
+
293
302
  if (this.selection.isRange(refer)) {
294
303
  if (!this.offset.setRangePosition(this.form, /** @type {Range} */ (refer), { position: 'bottom' })) {
295
304
  this.hide();
@@ -297,58 +306,56 @@ class Controller extends EditorInjector {
297
306
  }
298
307
  } else {
299
308
  if (refer) {
300
- const positionResult = this.offset.setAbsPosition(controller, /** @type {HTMLElement} */ (refer), { addOffset: this.__addOffset, position: this.position, isWWTarget: this.isWWTarget, inst: this });
309
+ const positionResult = this.offset.setAbsPosition(controller, /** @type {HTMLElement} */ (refer), { addOffset: this.#addOffset, position: this.position, isWWTarget: this.isWWTarget, inst: this, sibling: this.sibling });
301
310
  if (!positionResult) {
302
311
  this.hide();
303
312
  return;
304
313
  }
305
314
 
306
- if (!skipAutoReposition && this.sibling && this.siblingPosition === 'top' && positionResult.position !== this.position) {
307
- const resetPosition = controller.offsetTop - this.__addOffset.top;
315
+ if (!skipAutoReposition && this.sibling && !this.siblingMain) {
316
+ const resetPosition = controller.offsetTop - this.#addOffset.top;
308
317
  if (positionResult.position === 'bottom') {
309
- this._reserveIndex = true;
310
- controller.style.top = resetPosition + this.sibling.offsetHeight - 2 + 'px';
318
+ this.#reserveIndex = true;
319
+ controller.style.top = resetPosition + this.sibling.offsetHeight - 1 + 'px';
311
320
  } else {
321
+ this.#reserveIndex = false;
312
322
  controller.style.top = resetPosition - this.sibling.offsetHeight + 2 + 'px';
313
323
  }
314
324
  } else {
315
- this._reserveIndex = false;
325
+ this.#reserveIndex = false;
316
326
  }
317
327
  }
318
328
  }
319
329
 
320
- controller.style.zIndex = this.toTop ? INDEX_0 : this._reserveIndex ? INDEX_S_1 : INDEX_1;
330
+ controller.style.zIndex = this.toTop ? INDEX_0 : this.#reserveIndex ? INDEX_S_1 : INDEX_1;
321
331
  controller.style.visibility = '';
322
332
  }
323
333
 
324
334
  /**
325
- * @private
326
335
  * @description Adds global event listeners.
327
336
  * - When the controller is opened
328
337
  */
329
- __addGlobalEvent() {
330
- this.__removeGlobalEvent();
331
- this._bindClose_key = this.eventManager.addGlobalEvent('keydown', this.__globalEventHandlers.keydown, true);
332
- this._bindClose_mouse = this.eventManager.addGlobalEvent('mousedown', this.__globalEventHandlers.mousedown, true);
338
+ #addGlobalEvent() {
339
+ this.#removeGlobalEvent();
340
+ this.#bindClose_key = this.eventManager.addGlobalEvent('keydown', this.#globalEventHandlers.keydown, true);
341
+ this.#bindClose_mouse = this.eventManager.addGlobalEvent(isMobile ? 'click' : 'mousedown', this.#globalEventHandlers.mousedown, true);
333
342
  }
334
343
 
335
344
  /**
336
- * @private
337
345
  * @description Removes global event listeners.
338
346
  * - When the ESC key is pressed, the controller is closed.
339
347
  */
340
- __removeGlobalEvent() {
348
+ #removeGlobalEvent() {
341
349
  this.component.__removeGlobalEvent();
342
- if (this._bindClose_key) this._bindClose_key = this.eventManager.removeGlobalEvent(this._bindClose_key);
343
- if (this._bindClose_mouse) this._bindClose_mouse = this.eventManager.removeGlobalEvent(this._bindClose_mouse);
350
+ this.#bindClose_key &&= this.eventManager.removeGlobalEvent(this.#bindClose_key);
351
+ this.#bindClose_mouse &&= this.eventManager.removeGlobalEvent(this.#bindClose_mouse);
344
352
  }
345
353
 
346
354
  /**
347
- * @private
348
355
  * @description Checks if the controller is fixed and should not be closed.
349
356
  * @returns {boolean} True if the controller is fixed.
350
357
  */
351
- _checkFixed() {
358
+ #checkFixed() {
352
359
  if (this.editor.selectMenuOn) return true;
353
360
 
354
361
  const cont = this.editor.opendControllers;
@@ -361,12 +368,11 @@ class Controller extends EditorInjector {
361
368
  }
362
369
 
363
370
  /**
364
- * @private
365
371
  * @description Checks if the given target is within a form or controller.
366
372
  * @param {Node} target The target element.
367
373
  * @returns {boolean} True if the target is inside a form or controller.
368
374
  */
369
- _checkForm(target) {
375
+ #checkForm(target) {
370
376
  if (dom.check.isWysiwygFrame(target)) return false;
371
377
  if (dom.utils.hasClass(target, 'se-drag-handle')) return true;
372
378
 
@@ -415,20 +421,20 @@ class Controller extends EditorInjector {
415
421
  if (this.parents.length > 0 && this.isInsideForm) return;
416
422
 
417
423
  const eventTarget = dom.query.getEventTarget(e);
418
- eventTarget.style.zIndex = this.toTop ? INDEX_0 : this._reserveIndex ? INDEX_S_1 : INDEX_1;
424
+ eventTarget.style.zIndex = this.toTop ? INDEX_0 : this.#reserveIndex ? INDEX_S_1 : INDEX_1;
419
425
  }
420
426
 
421
427
  /**
422
428
  * @param {KeyboardEvent} e - Event object
423
429
  */
424
430
  #CloseListener_keydown(e) {
425
- if (this._checkFixed()) return;
431
+ if (this.#checkFixed()) return;
426
432
  const keyCode = e.code;
427
433
  const ctrl = keyCodeMap.isCtrl(e);
428
434
  if (ctrl || !keyCodeMap.isNonResponseKey(keyCode)) return;
429
435
 
430
436
  const eventTarget = dom.query.getEventTarget(e);
431
- if (this.form.contains(eventTarget) || this._checkForm(eventTarget)) return;
437
+ if (this.form.contains(eventTarget) || this.#checkForm(eventTarget)) return;
432
438
  if (this.editor._fileManager.pluginRegExp.test(this.kind) && !keyCodeMap.isEsc(keyCode)) return;
433
439
 
434
440
  this.#PostCloseEvent(eventTarget);
@@ -441,17 +447,17 @@ class Controller extends EditorInjector {
441
447
  #CloseListener_mousedown(e) {
442
448
  const eventTarget = dom.query.getEventTarget(e);
443
449
  if (this.inst?._element?.contains(eventTarget)) {
444
- this.isOpen = false;
450
+ this.#preventClose = true;
445
451
  return;
446
452
  }
447
453
 
448
- this.isOpen = true;
454
+ this.#preventClose = false;
449
455
  if (
450
456
  eventTarget === this.inst._element ||
451
457
  eventTarget === this.currentTarget ||
452
- this._checkFixed() ||
458
+ this.#checkFixed() ||
453
459
  this.form.contains(eventTarget) ||
454
- this._checkForm(eventTarget) ||
460
+ this.#checkForm(eventTarget) ||
455
461
  dom.query.getParentElement(eventTarget, '.se-line-breaker-component')
456
462
  ) {
457
463
  return;
@@ -465,7 +471,7 @@ class Controller extends EditorInjector {
465
471
  * @param {HTMLElement} eventTarget - The target element that triggered the event.
466
472
  */
467
473
  #PostCloseEvent(eventTarget) {
468
- if (!this.editor.frameContext.get('wysiwyg').contains(eventTarget)) {
474
+ if (!this.frameContext.get('wysiwyg').contains(eventTarget)) {
469
475
  this.component.__prevent = false;
470
476
  }
471
477
  }