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,410 +1,414 @@
1
- import EditorInjector from '../../editorInjector';
2
- import { dom, numbers, keyCodeMap } from '../../helper';
3
-
4
- const DEFAULT_UNIT_MAP = {
5
- text: {
6
- default: '13px',
7
- list: [
8
- {
9
- title: 'XX-Small',
10
- size: '8px'
11
- },
12
- {
13
- title: 'X-Small',
14
- size: '10px'
15
- },
16
- {
17
- title: 'Small',
18
- size: '13px'
19
- },
20
- {
21
- title: 'Medium',
22
- size: '16px'
23
- },
24
- {
25
- title: 'Large',
26
- size: '18px'
27
- },
28
- {
29
- title: 'X-Large',
30
- size: '24px'
31
- },
32
- {
33
- title: 'XX-Large',
34
- size: '32px'
35
- }
36
- ]
37
- },
38
- px: {
39
- default: 13,
40
- inc: 1,
41
- min: 8,
42
- max: 72,
43
- list: [8, 10, 13, 15, 18, 20, 22, 26, 28, 36, 48, 72]
44
- },
45
- pt: {
46
- default: 10,
47
- inc: 1,
48
- min: 6,
49
- max: 72,
50
- list: [6, 8, 10, 12, 14, 18, 22, 26, 32]
51
- },
52
- em: {
53
- default: 1,
54
- inc: 0.1,
55
- min: 0.5,
56
- max: 5,
57
- list: [0.5, 0.75, 1, 1.25, 1.5, 1.75, 2, 2.5, 3]
58
- },
59
- rem: {
60
- default: 1,
61
- inc: 0.1,
62
- min: 0.5,
63
- max: 5,
64
- list: [0.5, 0.75, 1, 1.25, 1.5, 1.75, 2, 2.5, 3]
65
- },
66
- vw: {
67
- inc: 0.1,
68
- min: 0.5,
69
- max: 10,
70
- list: [2, 3.5, 4, 4.5, 6, 8]
71
- },
72
- vh: {
73
- default: 1.5,
74
- inc: 0.1,
75
- min: 0.5,
76
- max: 10,
77
- list: [1, 1.5, 2, 2.5, 3, 3.5, 4]
78
- },
79
- '%': {
80
- default: 100,
81
- inc: 1,
82
- min: 50,
83
- max: 200,
84
- list: [50, 70, 90, 100, 120, 140, 160, 180, 200]
85
- }
86
- };
87
-
88
- /**
89
- * @class
90
- * @description FontSize Plugin
91
- * - This plugin enables users to modify the font size of selected text within the editor.
92
- * - It supports various measurement units (e.g., 'px', 'pt', 'em', 'rem', 'vw', 'vh', '%') and
93
- * - provides multiple interfaces: dropdown menus, direct input, and optional increment/decrement buttons.
94
- */
95
- class FontSize extends EditorInjector {
96
- static key = 'fontSize';
97
- static type = 'input';
98
- static className = 'se-btn-select se-btn-input se-btn-tool-font-size';
99
-
100
- /**
101
- * @constructor
102
- * @param {__se__EditorCore} editor - The root editor instance
103
- * @param {Object} pluginOptions - Configuration options for the FontSize plugin.
104
- * @param {string=} [pluginOptions.sizeUnit='px'] - The unit for the font size.
105
- * - Accepted values include: 'px', 'pt', 'em', 'rem', 'vw', 'vh', '%' or 'text'.
106
- * - If 'text' is used, a text-based font size list is applied.
107
- * @param {boolean=} [pluginOptions.showDefaultSizeLabel=true] - Determines whether the default size label is displayed in the dropdown menu.
108
- * @param {boolean=} [pluginOptions.showIncDecControls=false] - When true, displays increase and decrease buttons for font size adjustments.
109
- * @param {boolean=} [pluginOptions.disableInput=true] - When true, disables the direct font size input box.
110
- * @param {Object<string, {default: number, inc: number, min: number, max: number, list: Array<number>}>} [pluginOptions.unitMap={}] - Optional object to override or extend the default unit mapping for font sizes.
111
- */
112
- constructor(editor, pluginOptions) {
113
- super(editor);
114
-
115
- // create HTML
116
- this.unitMap = { ...DEFAULT_UNIT_MAP, ...(pluginOptions.unitMap || {}) };
117
- this.sizeUnit = /text/.test(pluginOptions.sizeUnit) ? '' : pluginOptions.sizeUnit || this.options.get('fontSizeUnits')[0];
118
-
119
- const unitMap = this.unitMap[this.sizeUnit || 'text'];
120
- const menu = CreateHTML(editor, unitMap, this.sizeUnit, pluginOptions.showDefaultSizeLabel);
121
-
122
- // plugin basic properties
123
- const showIncDec = this.sizeUnit ? pluginOptions.showIncDecControls ?? false : false;
124
- const disableInput = this.sizeUnit ? pluginOptions.disableInput ?? false : true;
125
-
126
- this.title = this.lang.fontSize;
127
- this.inner =
128
- disableInput && !showIncDec
129
- ? false
130
- : disableInput
131
- ? `<span class="se-txt se-not-arrow-text __se__font_size">${this.lang.fontSize}</span>`
132
- : `<input type="text" class="__se__font_size se-not-arrow-text" placeholder="${this.lang.fontSize}" />`;
133
-
134
- // increase, decrease buttons
135
- if (showIncDec) {
136
- this.beforeItem = dom.utils.createElement(
137
- 'button',
138
- { class: 'se-btn se-tooltip se-sub-btn', 'data-command': FontSize.key, 'data-type': 'command', 'data-value': 'dec' },
139
- `${this.icons.minus}<span class="se-tooltip-inner"><span class="se-tooltip-text">${this.lang.decrease}</span></span>`
140
- );
141
- this.afterItem = dom.utils.createElement(
142
- 'button',
143
- { class: 'se-btn se-tooltip se-sub-btn', 'data-command': FontSize.key, 'data-type': 'command', 'data-value': 'inc' },
144
- `${this.icons.plus}<span class="se-tooltip-inner"><span class="se-tooltip-text">${this.lang.increase}</span></span>`
145
- );
146
- } else if (!disableInput) {
147
- this.afterItem = dom.utils.createElement(
148
- 'button',
149
- { class: 'se-btn se-tooltip se-sub-arrow-btn', 'data-command': FontSize.key, 'data-type': 'dropdown' },
150
- `${this.icons.arrow_down}<span class="se-tooltip-inner"><span class="se-tooltip-text">${this.lang.fontSize}</span></span>`
151
- );
152
- this.menu.initDropdownTarget({ key: FontSize.key, type: 'dropdown' }, menu);
153
- } else if (disableInput && !showIncDec) {
154
- this.replaceButton = dom.utils.createElement(
155
- 'button',
156
- { class: 'se-btn se-tooltip se-btn-select se-btn-tool-font-size', 'data-command': FontSize.key, 'data-type': 'dropdown' },
157
- `<span class="se-txt __se__font_size">${this.lang.fontSize}</span>${this.icons.arrow_down}<span class="se-tooltip-inner"><span class="se-tooltip-text">${this.lang.fontSize}</span></span>`
158
- );
159
- this.menu.initDropdownTarget({ key: FontSize.key, type: 'dropdown' }, menu);
160
- }
161
-
162
- // members
163
- this.currentSize = '';
164
- this.sizeList = menu.querySelectorAll('li button');
165
- this.hasInputFocus = false;
166
- this.isInputActive = false; // input target event
167
- this._disableInput = disableInput;
168
-
169
- // init
170
- this.menu.initDropdownTarget(FontSize, menu);
171
- }
172
-
173
- /**
174
- * @editorMethod Editor.EventManager
175
- * @description Executes the method that is called whenever the cursor position changes.
176
- * @param {?HTMLElement=} element - Node element where the cursor is currently located
177
- * @param {?HTMLElement=} target - The plugin's toolbar button element
178
- * @returns {boolean} - Whether the plugin is active
179
- */
180
- active(element, target) {
181
- if (!dom.utils.hasClass(target, '__se__font_size')) return false;
182
-
183
- if (!element) {
184
- this._setSize(target, this._getDefaultSize());
185
- } else if (element?.style.fontSize.length > 0) {
186
- this._setSize(target, element.style.fontSize);
187
- return true;
188
- }
189
-
190
- return false;
191
- }
192
-
193
- /**
194
- * @editorMethod Editor.Toolbar
195
- * @description Executes the event function of toolbar's input tag - "keydown".
196
- * @param {Object} params
197
- * @param {HTMLElement} params.target Input element
198
- * @param {KeyboardEvent} params.event Event object
199
- */
200
- onInputKeyDown({ target, event }) {
201
- const keyCode = event.code;
202
-
203
- if (this._disableInput || keyCodeMap.isSpace(keyCode)) {
204
- event.preventDefault();
205
- return;
206
- }
207
-
208
- if (!/^(38|40|13)$/.test(keyCode)) return;
209
-
210
- const { value, unit } = this._getSize(target);
211
- if (!value) return;
212
-
213
- const numValue = numbers.get(value);
214
- const unitMap = this.unitMap[unit];
215
- let changeValue = numValue;
216
- switch (keyCode) {
217
- case 'ArrowUp': //up
218
- changeValue += unitMap.inc;
219
- if (changeValue > unitMap.max) changeValue = numValue;
220
- break;
221
- case 'ArrowDown': //down
222
- changeValue -= unitMap.inc;
223
- if (changeValue < unitMap.min) changeValue = numValue;
224
- }
225
-
226
- event.preventDefault();
227
-
228
- try {
229
- this.isInputActive = true;
230
- const size = this._setSize(target, changeValue + unit);
231
- if (this._disableInput) return;
232
-
233
- const newNode = dom.utils.createElement('SPAN', { style: 'font-size: ' + size + ';' });
234
- this.format.applyInlineElement(newNode, { stylesToModify: ['font-size'], nodesToRemove: null, strictRemove: null });
235
-
236
- if (!keyCodeMap.isEnter(keyCode)) target.focus();
237
- } finally {
238
- this.isInputActive = false;
239
- }
240
- }
241
-
242
- /**
243
- * @editorMethod Editor.Toolbar
244
- * @description Executes the event function of toolbar's input tag - "change".
245
- * @param {__se__PluginToolbarInputChangeEventInfo} params
246
- */
247
- onInputChange({ target, value: changeValue, event }) {
248
- if (this._disableInput) return;
249
-
250
- try {
251
- this.isInputActive = true;
252
-
253
- // eslint-disable-next-line prefer-const
254
- let { value, unit } = this._getSize(changeValue);
255
- const { max, min } = this.unitMap[unit];
256
- value = value > max ? max : value < min ? min : value;
257
-
258
- const newNode = dom.utils.createElement('SPAN', { style: 'font-size: ' + this._setSize(target, value + unit) + ';' });
259
- this.format.applyInlineElement(newNode, { stylesToModify: ['font-size'], nodesToRemove: null, strictRemove: null });
260
- } finally {
261
- this.isInputActive = false;
262
- event.preventDefault();
263
- this.editor.focus();
264
- }
265
- }
266
-
267
- /**
268
- * @editorMethod Modules.Dropdown
269
- * @description Executes the method that is called when a plugin's dropdown menu is opened.
270
- * @param {HTMLElement} target Line element at the current cursor position
271
- */
272
- on(target) {
273
- const { value, unit } = this._getSize(target);
274
- const currentSize = value + unit;
275
-
276
- if (currentSize === this.currentSize) return;
277
-
278
- const sizeList = this.sizeList;
279
- for (let i = 0, len = sizeList.length; i < len; i++) {
280
- if (currentSize === sizeList[i].getAttribute('data-value')) {
281
- dom.utils.addClass(sizeList[i], 'active');
282
- } else {
283
- dom.utils.removeClass(sizeList[i], 'active');
284
- }
285
- }
286
-
287
- this.currentSize = currentSize;
288
- }
289
-
290
- /**
291
- * @editorMethod Editor.core
292
- * @description Executes the main execution method of the plugin.
293
- * - Called when an item in the "dropdown" menu is clicked.
294
- * @param {HTMLElement} target - The plugin's toolbar button element
295
- */
296
- action(target) {
297
- const commandValue = target.getAttribute('data-command');
298
-
299
- if (commandValue === FontSize.key) {
300
- const { value, unit } = this._getSize(target);
301
- let newSize = numbers.get(value) + (target.getAttribute('data-value') === 'inc' ? 1 : -1);
302
- const { min, max } = this.unitMap[unit];
303
- newSize = newSize < min ? min : newSize > max ? max : newSize;
304
-
305
- const newNode = dom.utils.createElement('SPAN', { style: 'font-size: ' + newSize + unit + ';' });
306
- this.format.applyInlineElement(newNode, { stylesToModify: ['font-size'], nodesToRemove: null, strictRemove: null });
307
- } else if (commandValue) {
308
- const newNode = dom.utils.createElement('SPAN', { style: 'font-size: ' + commandValue + ';' });
309
- this.format.applyInlineElement(newNode, { stylesToModify: ['font-size'], nodesToRemove: null, strictRemove: null });
310
- } else {
311
- this.format.applyInlineElement(null, { stylesToModify: ['font-size'], nodesToRemove: ['span'], strictRemove: true });
312
- }
313
-
314
- this.menu.dropdownOff();
315
- }
316
-
317
- /**
318
- * @private
319
- * @description Retrieves the default font size of the editor.
320
- * @returns {string} - The computed font size from the editor.
321
- */
322
- _getDefaultSize() {
323
- return this.editor.frameContext.get('wwComputedStyle').fontSize;
324
- }
325
-
326
- /**
327
- * @private
328
- * @description Extracts the font size and unit from the given element or input value.
329
- * @param {string|Element} target - The target input or element.
330
- * @returns {{ unit: string, value: number|string }} - An object containing:
331
- * - `unit` (string): The detected font size unit.
332
- * - `value` (number|string): The numeric font size value or text-based size.
333
- */
334
- _getSize(target) {
335
- target = typeof target === 'string' ? target : target.parentElement.querySelector('.__se__font_size');
336
- if (!target)
337
- return {
338
- unit: this.sizeUnit,
339
- value: this.sizeUnit ? 0 : ''
340
- };
341
-
342
- const size = typeof target === 'string' ? target : dom.check.isInputElement(target) ? target.value : target.textContent;
343
- const splitValue = this.sizeUnit ? size.split(/(\d+)/) : [size, ''];
344
-
345
- let unit = (splitValue.pop() || '').trim().toLowerCase();
346
- unit = this.options.get('fontSizeUnits').includes(unit) ? unit : this.sizeUnit;
347
-
348
- const tempValue = splitValue.pop();
349
- const value = unit ? Number(tempValue) : tempValue;
350
-
351
- return {
352
- unit,
353
- value
354
- };
355
- }
356
-
357
- /**
358
- * @private
359
- * @description Sets the font size in the toolbar input field or button label.
360
- * @param {HTMLElement} target - The target element in the toolbar.
361
- * @param {string|number} value - The font size value.
362
- * @returns {string|number} - The applied font size.
363
- */
364
- _setSize(target, value) {
365
- target = target.parentElement.querySelector('.__se__font_size');
366
- if (!target) return 0;
367
-
368
- if (dom.check.isInputElement(target)) {
369
- return (target.value = String(value));
370
- } else {
371
- return (target.textContent = String(this.sizeUnit ? value : this.unitMap.text.list.find((v) => v.size === value)?.title || value));
372
- }
373
- }
374
- }
375
-
376
- function CreateHTML({ lang }, unitMap, sizeUnit, showDefaultSizeLabel) {
377
- const sizeList = unitMap.list;
378
- const defaultSize = unitMap.default;
379
- const defaultLang = showDefaultSizeLabel ? lang.default : '';
380
-
381
- let list = /*html*/ `
382
- <div class="se-list-inner">
383
- <ul class="se-list-basic">`;
384
-
385
- for (let i = 0, len = sizeList.length, size, t, v, d, l; i < len; i++) {
386
- size = sizeList[i];
387
-
388
- if (typeof size === 'object') {
389
- t = size.title;
390
- v = size.size;
391
- } else {
392
- t = v = size + sizeUnit;
393
- }
394
-
395
- d = defaultSize === v ? ' default_value' : '';
396
- l = d ? defaultLang || t : t;
397
- list += /*html*/ `
398
- <li>
399
- <button type="button" class="se-btn se-btn-list${d}" data-command="${v}" data-value="${t}" title="${l}" aria-label="${l}" style="font-size:${v};">${l}</button>
400
- </li>`;
401
- }
402
-
403
- list += /*html*/ `
404
- </ul>
405
- </div>`;
406
-
407
- return dom.utils.createElement('DIV', { class: 'se-dropdown se-list-layer se-list-font-size' }, list);
408
- }
409
-
410
- export default FontSize;
1
+ import EditorInjector from '../../editorInjector';
2
+ import { dom, numbers, keyCodeMap } from '../../helper';
3
+
4
+ const DEFAULT_UNIT_MAP = {
5
+ text: {
6
+ default: '13px',
7
+ list: [
8
+ {
9
+ title: 'XX-Small',
10
+ size: '8px'
11
+ },
12
+ {
13
+ title: 'X-Small',
14
+ size: '10px'
15
+ },
16
+ {
17
+ title: 'Small',
18
+ size: '13px'
19
+ },
20
+ {
21
+ title: 'Medium',
22
+ size: '16px'
23
+ },
24
+ {
25
+ title: 'Large',
26
+ size: '18px'
27
+ },
28
+ {
29
+ title: 'X-Large',
30
+ size: '24px'
31
+ },
32
+ {
33
+ title: 'XX-Large',
34
+ size: '32px'
35
+ }
36
+ ]
37
+ },
38
+ px: {
39
+ default: 13,
40
+ inc: 1,
41
+ min: 8,
42
+ max: 72,
43
+ list: [8, 10, 13, 15, 18, 20, 22, 26, 28, 36, 48, 72]
44
+ },
45
+ pt: {
46
+ default: 10,
47
+ inc: 1,
48
+ min: 6,
49
+ max: 72,
50
+ list: [6, 8, 10, 12, 14, 18, 22, 26, 32]
51
+ },
52
+ em: {
53
+ default: 1,
54
+ inc: 0.1,
55
+ min: 0.5,
56
+ max: 5,
57
+ list: [0.5, 0.75, 1, 1.25, 1.5, 1.75, 2, 2.5, 3]
58
+ },
59
+ rem: {
60
+ default: 1,
61
+ inc: 0.1,
62
+ min: 0.5,
63
+ max: 5,
64
+ list: [0.5, 0.75, 1, 1.25, 1.5, 1.75, 2, 2.5, 3]
65
+ },
66
+ vw: {
67
+ inc: 0.1,
68
+ min: 0.5,
69
+ max: 10,
70
+ list: [2, 3.5, 4, 4.5, 6, 8]
71
+ },
72
+ vh: {
73
+ default: 1.5,
74
+ inc: 0.1,
75
+ min: 0.5,
76
+ max: 10,
77
+ list: [1, 1.5, 2, 2.5, 3, 3.5, 4]
78
+ },
79
+ '%': {
80
+ default: 100,
81
+ inc: 1,
82
+ min: 50,
83
+ max: 200,
84
+ list: [50, 70, 90, 100, 120, 140, 160, 180, 200]
85
+ }
86
+ };
87
+
88
+ /**
89
+ * @class
90
+ * @description FontSize Plugin
91
+ * - This plugin enables users to modify the font size of selected text within the editor.
92
+ * - It supports various measurement units (e.g., 'px', 'pt', 'em', 'rem', 'vw', 'vh', '%') and
93
+ * - provides multiple interfaces: dropdown menus, direct input, and optional increment/decrement buttons.
94
+ */
95
+ class FontSize extends EditorInjector {
96
+ static key = 'fontSize';
97
+ static type = 'input';
98
+ static className = 'se-btn-select se-btn-input se-btn-tool-font-size';
99
+
100
+ /**
101
+ * @constructor
102
+ * @param {__se__EditorCore} editor - The root editor instance
103
+ * @param {Object} pluginOptions - Configuration options for the FontSize plugin.
104
+ * @param {string=} [pluginOptions.sizeUnit='px'] - The unit for the font size.
105
+ * - Accepted values include: 'px', 'pt', 'em', 'rem', 'vw', 'vh', '%' or 'text'.
106
+ * - If 'text' is used, a text-based font size list is applied.
107
+ * @param {boolean=} [pluginOptions.showDefaultSizeLabel=true] - Determines whether the default size label is displayed in the dropdown menu.
108
+ * @param {boolean=} [pluginOptions.showIncDecControls=false] - When true, displays increase and decrease buttons for font size adjustments.
109
+ * @param {boolean=} [pluginOptions.disableInput=true] - When true, disables the direct font size input box.
110
+ * @param {Object<string, {default: number, inc: number, min: number, max: number, list: Array<number>}>} [pluginOptions.unitMap={}] - Optional object to override or extend the default unit mapping for font sizes.
111
+ */
112
+ constructor(editor, pluginOptions) {
113
+ super(editor);
114
+
115
+ // create HTML
116
+ this.unitMap = { ...DEFAULT_UNIT_MAP, ...(pluginOptions.unitMap || {}) };
117
+ this.sizeUnit = /text/.test(pluginOptions.sizeUnit) ? '' : pluginOptions.sizeUnit || this.options.get('fontSizeUnits')[0];
118
+
119
+ const unitMap = this.unitMap[this.sizeUnit || 'text'];
120
+ const menu = CreateHTML(editor, unitMap, this.sizeUnit, pluginOptions.showDefaultSizeLabel);
121
+
122
+ // plugin basic properties
123
+ const showIncDec = this.sizeUnit ? pluginOptions.showIncDecControls ?? false : false;
124
+ const disableInput = this.sizeUnit ? pluginOptions.disableInput ?? false : true;
125
+
126
+ this.title = this.lang.fontSize;
127
+ this.inner =
128
+ disableInput && !showIncDec
129
+ ? false
130
+ : disableInput
131
+ ? `<span class="se-txt se-not-arrow-text __se__font_size">${this.lang.fontSize}</span>`
132
+ : `<input type="text" class="__se__font_size se-not-arrow-text" placeholder="${this.lang.fontSize}" />`;
133
+
134
+ // increase, decrease buttons
135
+ if (showIncDec) {
136
+ this.beforeItem = dom.utils.createElement(
137
+ 'button',
138
+ { class: 'se-btn se-tooltip se-sub-btn', 'data-command': FontSize.key, 'data-type': 'command', 'data-value': 'dec' },
139
+ `${this.icons.minus}<span class="se-tooltip-inner"><span class="se-tooltip-text">${this.lang.decrease}</span></span>`
140
+ );
141
+ this.afterItem = dom.utils.createElement(
142
+ 'button',
143
+ { class: 'se-btn se-tooltip se-sub-btn', 'data-command': FontSize.key, 'data-type': 'command', 'data-value': 'inc' },
144
+ `${this.icons.plus}<span class="se-tooltip-inner"><span class="se-tooltip-text">${this.lang.increase}</span></span>`
145
+ );
146
+ } else if (!disableInput) {
147
+ this.afterItem = dom.utils.createElement(
148
+ 'button',
149
+ { class: 'se-btn se-tooltip se-sub-arrow-btn', 'data-command': FontSize.key, 'data-type': 'dropdown' },
150
+ `${this.icons.arrow_down}<span class="se-tooltip-inner"><span class="se-tooltip-text">${this.lang.fontSize}</span></span>`
151
+ );
152
+ this.menu.initDropdownTarget({ key: FontSize.key, type: 'dropdown' }, menu);
153
+ } else if (disableInput && !showIncDec) {
154
+ this.replaceButton = dom.utils.createElement(
155
+ 'button',
156
+ { class: 'se-btn se-tooltip se-btn-select se-btn-tool-font-size', 'data-command': FontSize.key, 'data-type': 'dropdown' },
157
+ `<span class="se-txt __se__font_size">${this.lang.fontSize}</span>${this.icons.arrow_down}<span class="se-tooltip-inner"><span class="se-tooltip-text">${this.lang.fontSize}</span></span>`
158
+ );
159
+ this.menu.initDropdownTarget({ key: FontSize.key, type: 'dropdown' }, menu);
160
+ }
161
+
162
+ // members
163
+ this.currentSize = '';
164
+ this.sizeList = menu.querySelectorAll('li button');
165
+ this.hasInputFocus = false;
166
+ this.isInputActive = false; // input target event
167
+ this._disableInput = disableInput;
168
+
169
+ // init
170
+ this.menu.initDropdownTarget(FontSize, menu);
171
+ }
172
+
173
+ /**
174
+ * @editorMethod Editor.EventManager
175
+ * @description Executes the method that is called whenever the cursor position changes.
176
+ * @param {?HTMLElement=} element - Node element where the cursor is currently located
177
+ * @param {?HTMLElement=} target - The plugin's toolbar button element
178
+ * @returns {boolean} - Whether the plugin is active
179
+ * - If it returns "undefined", it will no longer be called in this scope.
180
+ */
181
+ active(element, target) {
182
+ if (!dom.utils.hasClass(target, '__se__font_size')) return false;
183
+
184
+ let fontSize = '';
185
+ if (!element) {
186
+ this._setSize(target, this._getDefaultSize());
187
+ } else if (this.format.isLine(element)) {
188
+ return undefined;
189
+ } else if ((fontSize = dom.utils.getStyle(element, 'fontSize'))) {
190
+ this._setSize(target, fontSize);
191
+ return true;
192
+ }
193
+
194
+ return false;
195
+ }
196
+
197
+ /**
198
+ * @editorMethod Editor.Toolbar
199
+ * @description Executes the event function of toolbar's input tag - "keydown".
200
+ * @param {Object} params
201
+ * @param {HTMLElement} params.target Input element
202
+ * @param {KeyboardEvent} params.event Event object
203
+ */
204
+ onInputKeyDown({ target, event }) {
205
+ const keyCode = event.code;
206
+
207
+ if (this._disableInput || keyCodeMap.isSpace(keyCode)) {
208
+ event.preventDefault();
209
+ return;
210
+ }
211
+
212
+ if (!/^(38|40|13)$/.test(keyCode)) return;
213
+
214
+ const { value, unit } = this._getSize(target);
215
+ if (!value) return;
216
+
217
+ const numValue = numbers.get(value);
218
+ const unitMap = this.unitMap[unit];
219
+ let changeValue = numValue;
220
+ switch (keyCode) {
221
+ case 'ArrowUp': //up
222
+ changeValue += unitMap.inc;
223
+ if (changeValue > unitMap.max) changeValue = numValue;
224
+ break;
225
+ case 'ArrowDown': //down
226
+ changeValue -= unitMap.inc;
227
+ if (changeValue < unitMap.min) changeValue = numValue;
228
+ }
229
+
230
+ event.preventDefault();
231
+
232
+ try {
233
+ this.isInputActive = true;
234
+ const size = this._setSize(target, changeValue + unit);
235
+ if (this._disableInput) return;
236
+
237
+ const newNode = dom.utils.createElement('SPAN', { style: 'font-size: ' + size + ';' });
238
+ this.format.applyInlineElement(newNode, { stylesToModify: ['font-size'], nodesToRemove: null, strictRemove: null });
239
+
240
+ if (!keyCodeMap.isEnter(keyCode)) target.focus();
241
+ } finally {
242
+ this.isInputActive = false;
243
+ }
244
+ }
245
+
246
+ /**
247
+ * @editorMethod Editor.Toolbar
248
+ * @description Executes the event function of toolbar's input tag - "change".
249
+ * @param {__se__PluginToolbarInputChangeEventInfo} params
250
+ */
251
+ onInputChange({ target, value: changeValue, event }) {
252
+ if (this._disableInput) return;
253
+
254
+ try {
255
+ this.isInputActive = true;
256
+
257
+ // eslint-disable-next-line prefer-const
258
+ let { value, unit } = this._getSize(changeValue);
259
+ const { max, min } = this.unitMap[unit];
260
+ value = value > max ? max : value < min ? min : value;
261
+
262
+ const newNode = dom.utils.createElement('SPAN', { style: 'font-size: ' + this._setSize(target, value + unit) + ';' });
263
+ this.format.applyInlineElement(newNode, { stylesToModify: ['font-size'], nodesToRemove: null, strictRemove: null });
264
+ } finally {
265
+ this.isInputActive = false;
266
+ event.preventDefault();
267
+ this.editor.focus();
268
+ }
269
+ }
270
+
271
+ /**
272
+ * @editorMethod Modules.Dropdown
273
+ * @description Executes the method that is called when a plugin's dropdown menu is opened.
274
+ * @param {HTMLElement} target Line element at the current cursor position
275
+ */
276
+ on(target) {
277
+ const { value, unit } = this._getSize(target);
278
+ const currentSize = value + unit;
279
+
280
+ if (currentSize === this.currentSize) return;
281
+
282
+ const sizeList = this.sizeList;
283
+ for (let i = 0, len = sizeList.length; i < len; i++) {
284
+ if (currentSize === sizeList[i].getAttribute('data-value')) {
285
+ dom.utils.addClass(sizeList[i], 'active');
286
+ } else {
287
+ dom.utils.removeClass(sizeList[i], 'active');
288
+ }
289
+ }
290
+
291
+ this.currentSize = currentSize;
292
+ }
293
+
294
+ /**
295
+ * @editorMethod Editor.core
296
+ * @description Executes the main execution method of the plugin.
297
+ * - Called when an item in the "dropdown" menu is clicked.
298
+ * @param {HTMLElement} target - The plugin's toolbar button element
299
+ */
300
+ action(target) {
301
+ const commandValue = target.getAttribute('data-command');
302
+
303
+ if (commandValue === FontSize.key) {
304
+ const { value, unit } = this._getSize(target);
305
+ let newSize = numbers.get(value) + (target.getAttribute('data-value') === 'inc' ? 1 : -1);
306
+ const { min, max } = this.unitMap[unit];
307
+ newSize = newSize < min ? min : newSize > max ? max : newSize;
308
+
309
+ const newNode = dom.utils.createElement('SPAN', { style: 'font-size: ' + newSize + unit + ';' });
310
+ this.format.applyInlineElement(newNode, { stylesToModify: ['font-size'], nodesToRemove: null, strictRemove: null });
311
+ } else if (commandValue) {
312
+ const newNode = dom.utils.createElement('SPAN', { style: 'font-size: ' + commandValue + ';' });
313
+ this.format.applyInlineElement(newNode, { stylesToModify: ['font-size'], nodesToRemove: null, strictRemove: null });
314
+ } else {
315
+ this.format.applyInlineElement(null, { stylesToModify: ['font-size'], nodesToRemove: ['span'], strictRemove: true });
316
+ }
317
+
318
+ this.menu.dropdownOff();
319
+ }
320
+
321
+ /**
322
+ * @private
323
+ * @description Retrieves the default font size of the editor.
324
+ * @returns {string} - The computed font size from the editor.
325
+ */
326
+ _getDefaultSize() {
327
+ return this.editor.frameContext.get('wwComputedStyle').fontSize;
328
+ }
329
+
330
+ /**
331
+ * @private
332
+ * @description Extracts the font size and unit from the given element or input value.
333
+ * @param {string|Element} target - The target input or element.
334
+ * @returns {{ unit: string, value: number|string }} - An object containing:
335
+ * - `unit` (string): The detected font size unit.
336
+ * - `value` (number|string): The numeric font size value or text-based size.
337
+ */
338
+ _getSize(target) {
339
+ target = typeof target === 'string' ? target : target.parentElement.querySelector('.__se__font_size');
340
+ if (!target)
341
+ return {
342
+ unit: this.sizeUnit,
343
+ value: this.sizeUnit ? 0 : ''
344
+ };
345
+
346
+ const size = typeof target === 'string' ? target : dom.check.isInputElement(target) ? target.value : target.textContent;
347
+ const splitValue = this.sizeUnit ? size.split(/(\d+)/) : [size, ''];
348
+
349
+ let unit = (splitValue.pop() || '').trim().toLowerCase();
350
+ unit = this.options.get('fontSizeUnits').includes(unit) ? unit : this.sizeUnit;
351
+
352
+ const tempValue = splitValue.pop();
353
+ const value = unit ? Number(tempValue) : tempValue;
354
+
355
+ return {
356
+ unit,
357
+ value
358
+ };
359
+ }
360
+
361
+ /**
362
+ * @private
363
+ * @description Sets the font size in the toolbar input field or button label.
364
+ * @param {HTMLElement} target - The target element in the toolbar.
365
+ * @param {string|number} value - The font size value.
366
+ * @returns {string|number} - The applied font size.
367
+ */
368
+ _setSize(target, value) {
369
+ target = target.parentElement.querySelector('.__se__font_size');
370
+ if (!target) return 0;
371
+
372
+ if (dom.check.isInputElement(target)) {
373
+ return (target.value = String(value));
374
+ } else {
375
+ return (target.textContent = String(this.sizeUnit ? value : this.unitMap.text.list.find((v) => v.size === value)?.title || value));
376
+ }
377
+ }
378
+ }
379
+
380
+ function CreateHTML({ lang }, unitMap, sizeUnit, showDefaultSizeLabel) {
381
+ const sizeList = unitMap.list;
382
+ const defaultSize = unitMap.default;
383
+ const defaultLang = showDefaultSizeLabel ? lang.default : '';
384
+
385
+ let list = /*html*/ `
386
+ <div class="se-list-inner">
387
+ <ul class="se-list-basic">`;
388
+
389
+ for (let i = 0, len = sizeList.length, size, t, v, d, l; i < len; i++) {
390
+ size = sizeList[i];
391
+
392
+ if (typeof size === 'object') {
393
+ t = size.title;
394
+ v = size.size;
395
+ } else {
396
+ t = v = size + sizeUnit;
397
+ }
398
+
399
+ d = defaultSize === v ? ' default_value' : '';
400
+ l = d ? defaultLang || t : t;
401
+ list += /*html*/ `
402
+ <li>
403
+ <button type="button" class="se-btn se-btn-list${d}" data-command="${v}" data-value="${t}" title="${l}" aria-label="${l}" style="font-size:${v};">${l}</button>
404
+ </li>`;
405
+ }
406
+
407
+ list += /*html*/ `
408
+ </ul>
409
+ </div>`;
410
+
411
+ return dom.utils.createElement('DIV', { class: 'se-dropdown se-list-layer se-list-font-size' }, list);
412
+ }
413
+
414
+ export default FontSize;