suneditor 3.0.0-rc.5 → 3.0.0
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.
- package/README.md +3 -2
- package/dist/suneditor-contents.min.css +1 -1
- package/dist/suneditor.min.css +1 -1
- package/dist/suneditor.min.js +1 -1
- package/package.json +2 -3
- package/src/assets/design/color.css +14 -2
- package/src/assets/design/typography.css +5 -0
- package/src/assets/icons/defaultIcons.js +22 -4
- package/src/assets/suneditor-contents.css +1 -1
- package/src/assets/suneditor.css +312 -18
- package/src/core/config/eventManager.js +6 -9
- package/src/core/editor.js +1 -1
- package/src/core/event/actions/index.js +5 -0
- package/src/core/event/effects/keydown.registry.js +25 -0
- package/src/core/event/eventOrchestrator.js +69 -2
- package/src/core/event/handlers/handler_ww_mouse.js +1 -0
- package/src/core/event/rules/keydown.rule.backspace.js +9 -1
- package/src/core/kernel/coreKernel.js +4 -0
- package/src/core/kernel/store.js +2 -0
- package/src/core/logic/dom/html.js +110 -11
- package/src/core/logic/dom/offset.js +89 -35
- package/src/core/logic/dom/selection.js +46 -19
- package/src/core/logic/panel/finder.js +982 -0
- package/src/core/logic/panel/menu.js +8 -6
- package/src/core/logic/panel/toolbar.js +112 -19
- package/src/core/logic/panel/viewer.js +214 -43
- package/src/core/logic/shell/_commandExecutor.js +7 -1
- package/src/core/logic/shell/commandDispatcher.js +1 -1
- package/src/core/logic/shell/component.js +5 -7
- package/src/core/logic/shell/history.js +24 -0
- package/src/core/logic/shell/shortcuts.js +3 -3
- package/src/core/logic/shell/ui.js +25 -26
- package/src/core/schema/frameContext.js +15 -1
- package/src/core/schema/options.js +75 -16
- package/src/core/section/constructor.js +61 -20
- package/src/core/section/documentType.js +1 -1
- package/src/events.js +12 -0
- package/src/helper/clipboard.js +1 -1
- package/src/helper/dom/domUtils.js +5 -14
- package/src/helper/index.js +3 -0
- package/src/helper/markdown.js +876 -0
- package/src/langs/ckb.js +9 -0
- package/src/langs/cs.js +9 -0
- package/src/langs/da.js +9 -0
- package/src/langs/de.js +9 -0
- package/src/langs/en.js +9 -0
- package/src/langs/es.js +9 -0
- package/src/langs/fa.js +9 -0
- package/src/langs/fr.js +9 -0
- package/src/langs/he.js +9 -0
- package/src/langs/hu.js +9 -0
- package/src/langs/it.js +9 -0
- package/src/langs/ja.js +9 -0
- package/src/langs/km.js +9 -0
- package/src/langs/ko.js +9 -0
- package/src/langs/lv.js +9 -0
- package/src/langs/nl.js +9 -0
- package/src/langs/pl.js +9 -0
- package/src/langs/pt_br.js +9 -0
- package/src/langs/ro.js +9 -0
- package/src/langs/ru.js +9 -0
- package/src/langs/se.js +9 -0
- package/src/langs/tr.js +9 -0
- package/src/langs/uk.js +9 -0
- package/src/langs/ur.js +9 -0
- package/src/langs/zh_cn.js +9 -0
- package/src/modules/contract/Controller.js +50 -39
- package/src/modules/manager/ApiManager.js +27 -4
- package/src/modules/manager/FileManager.js +1 -1
- package/src/modules/ui/SelectMenu.js +22 -11
- package/src/plugins/command/codeBlock.js +324 -0
- package/src/plugins/command/exportPDF.js +15 -3
- package/src/plugins/dropdown/blockStyle.js +1 -1
- package/src/plugins/dropdown/paragraphStyle.js +1 -2
- package/src/plugins/dropdown/table/render/table.html.js +1 -1
- package/src/plugins/dropdown/table/services/table.grid.js +16 -8
- package/src/plugins/dropdown/table/services/table.style.js +5 -9
- package/src/plugins/index.js +3 -0
- package/src/plugins/input/fontSize.js +4 -2
- package/src/plugins/modal/audio.js +2 -1
- package/src/plugins/modal/image/index.js +2 -1
- package/src/plugins/modal/math.js +2 -1
- package/src/plugins/modal/video/index.js +2 -1
- package/src/themes/cobalt.css +13 -4
- package/src/themes/cream.css +11 -2
- package/src/themes/dark.css +13 -4
- package/src/themes/midnight.css +13 -4
- package/src/typedef.js +4 -4
- package/types/assets/icons/defaultIcons.d.ts +12 -1
- package/types/core/config/eventManager.d.ts +6 -8
- package/types/core/event/actions/index.d.ts +1 -0
- package/types/core/event/effects/keydown.registry.d.ts +2 -0
- package/types/core/event/eventOrchestrator.d.ts +2 -1
- package/types/core/kernel/coreKernel.d.ts +5 -0
- package/types/core/kernel/store.d.ts +5 -0
- package/types/core/logic/dom/offset.d.ts +16 -3
- package/types/core/logic/dom/selection.d.ts +3 -3
- package/types/core/logic/panel/finder.d.ts +83 -0
- package/types/core/logic/panel/toolbar.d.ts +14 -1
- package/types/core/logic/panel/viewer.d.ts +22 -2
- package/types/core/logic/shell/shortcuts.d.ts +1 -1
- package/types/core/schema/frameContext.d.ts +22 -0
- package/types/core/schema/options.d.ts +153 -31
- package/types/events.d.ts +11 -0
- package/types/helper/dom/domUtils.d.ts +2 -2
- package/types/helper/index.d.ts +5 -0
- package/types/helper/markdown.d.ts +27 -0
- package/types/langs/_Lang.d.ts +9 -0
- package/types/modules/contract/Controller.d.ts +8 -1
- package/types/modules/ui/SelectMenu.d.ts +12 -0
- package/types/plugins/command/codeBlock.d.ts +53 -0
- package/types/plugins/index.d.ts +3 -0
- package/types/plugins/input/fontSize.d.ts +6 -2
- package/types/plugins/modal/audio.d.ts +4 -2
- package/types/plugins/modal/image/index.d.ts +3 -1
- package/types/plugins/modal/math.d.ts +3 -1
- package/types/plugins/modal/video/index.d.ts +3 -1
- package/types/typedef.d.ts +5 -2
package/src/langs/uk.js
CHANGED
|
@@ -126,6 +126,7 @@
|
|
|
126
126
|
link_modal_url: 'Посилання',
|
|
127
127
|
link_modal_relAttribute: 'Атрибут rel',
|
|
128
128
|
list: 'Список',
|
|
129
|
+
markdownView: 'Перегляд Markdown',
|
|
129
130
|
math: 'Формула',
|
|
130
131
|
math_modal_fontSizeLabel: 'Розмір шрифту',
|
|
131
132
|
math_modal_inputLabel: 'Математична запис',
|
|
@@ -187,6 +188,7 @@
|
|
|
187
188
|
tableProperties: 'Властивості таблиці',
|
|
188
189
|
tags: 'Теги',
|
|
189
190
|
tag_blockquote: 'Цитата',
|
|
191
|
+
codeBlock: 'Блок коду',
|
|
190
192
|
tag_div: 'Базовий',
|
|
191
193
|
tag_h: 'Заголовок',
|
|
192
194
|
tag_p: 'Абзац',
|
|
@@ -205,6 +207,13 @@
|
|
|
205
207
|
video_modal_title: 'Вставити відео',
|
|
206
208
|
video_modal_url: 'Посилання на відео, Youtube, Vimeo',
|
|
207
209
|
width: 'Ширина',
|
|
210
|
+
codeLanguage: 'Мова',
|
|
211
|
+
codeLanguage_none: 'Жоден',
|
|
212
|
+
finder_matchCase: 'Зіставте регістр',
|
|
213
|
+
finder_wholeWord: 'Ціле слово',
|
|
214
|
+
finder_regex: 'Регулярний вираз',
|
|
215
|
+
finder_prev: 'Попередній матч',
|
|
216
|
+
finder_next: 'Наступний матч',
|
|
208
217
|
message_copy_success: 'Скопійовано в буфер обміну',
|
|
209
218
|
message_copy_fail: 'Не вдалося скопіювати. Будь ласка, скопіюйте вручну.',
|
|
210
219
|
};
|
package/src/langs/ur.js
CHANGED
|
@@ -126,6 +126,7 @@
|
|
|
126
126
|
link_modal_url: 'لنک کرنے کے لیے URL',
|
|
127
127
|
link_modal_relAttribute: 'Rel وصف',
|
|
128
128
|
list: 'فہرست',
|
|
129
|
+
markdownView: 'مارک ڈاؤن منظر',
|
|
129
130
|
math: 'ریاضی',
|
|
130
131
|
math_modal_fontSizeLabel: 'حرف کا سائز',
|
|
131
132
|
math_modal_inputLabel: 'ریاضیاتی اشارے',
|
|
@@ -187,6 +188,7 @@
|
|
|
187
188
|
tableProperties: 'ٹیبل کی خصوصیات',
|
|
188
189
|
tags: 'ٹیگز',
|
|
189
190
|
tag_blockquote: 'اقتباس',
|
|
191
|
+
codeBlock: 'کوڈ بلاک',
|
|
190
192
|
tag_div: 'عام (div)',
|
|
191
193
|
tag_h: 'ہیڈر',
|
|
192
194
|
tag_p: 'پیراگراف',
|
|
@@ -205,6 +207,13 @@
|
|
|
205
207
|
video_modal_title: 'ویڈیو داخل کریں',
|
|
206
208
|
video_modal_url: 'ذرائع ابلاغ کا یو آر ایل، یوٹیوب/ویمیو',
|
|
207
209
|
width: 'چوڑائی',
|
|
210
|
+
codeLanguage: 'زبان',
|
|
211
|
+
codeLanguage_none: 'کوئی نہیں۔',
|
|
212
|
+
finder_matchCase: 'میچ کیس',
|
|
213
|
+
finder_wholeWord: 'پورا کلام',
|
|
214
|
+
finder_regex: 'باقاعدہ اظہار',
|
|
215
|
+
finder_prev: 'پچھلا میچ',
|
|
216
|
+
finder_next: 'اگلا میچ',
|
|
208
217
|
message_copy_success: 'کلپ بورڈ میں کاپی ہو گیا',
|
|
209
218
|
message_copy_fail: 'کاپی ناکام۔ براہ کرم دستی طور پر کاپی کریں۔',
|
|
210
219
|
};
|
package/src/langs/zh_cn.js
CHANGED
|
@@ -126,6 +126,7 @@
|
|
|
126
126
|
link_modal_url: '网址',
|
|
127
127
|
link_modal_relAttribute: 'Rel 属性',
|
|
128
128
|
list: '列表',
|
|
129
|
+
markdownView: 'Markdown视图',
|
|
129
130
|
math: '数学',
|
|
130
131
|
math_modal_fontSizeLabel: '字号',
|
|
131
132
|
math_modal_inputLabel: '数学符号',
|
|
@@ -187,6 +188,7 @@
|
|
|
187
188
|
tableProperties: '表格属性',
|
|
188
189
|
tags: '标签',
|
|
189
190
|
tag_blockquote: '引用',
|
|
191
|
+
codeBlock: '代码块',
|
|
190
192
|
tag_div: '正文 (DIV)',
|
|
191
193
|
tag_h: '标题',
|
|
192
194
|
tag_p: '段落',
|
|
@@ -205,6 +207,13 @@
|
|
|
205
207
|
video_modal_title: '插入视频',
|
|
206
208
|
video_modal_url: '嵌入网址, Youtube,Vimeo',
|
|
207
209
|
width: '宽度',
|
|
210
|
+
codeLanguage: '语言',
|
|
211
|
+
codeLanguage_none: '没有任何',
|
|
212
|
+
finder_matchCase: '火柴盒',
|
|
213
|
+
finder_wholeWord: '整个单词',
|
|
214
|
+
finder_regex: '正则表达式',
|
|
215
|
+
finder_prev: '上一场比赛',
|
|
216
|
+
finder_next: '下一场比赛',
|
|
208
217
|
message_copy_success: '已复制到剪贴板',
|
|
209
218
|
message_copy_fail: '复制失败,请手动复制。',
|
|
210
219
|
};
|
|
@@ -6,6 +6,7 @@ const INDEX_00 = '2147483646';
|
|
|
6
6
|
const INDEX_0 = '2147483645';
|
|
7
7
|
const INDEX_S_1 = '2147483642';
|
|
8
8
|
const INDEX_1 = '2147483641';
|
|
9
|
+
const ADD_OFFSET_VALUE = { left: 0, right: 0, top: 0 };
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
12
|
* Controller information object
|
|
@@ -46,11 +47,10 @@ class Controller {
|
|
|
46
47
|
#initMethod;
|
|
47
48
|
#globalEventHandlers;
|
|
48
49
|
|
|
49
|
-
#addOffset =
|
|
50
|
+
#addOffset = ADD_OFFSET_VALUE;
|
|
50
51
|
#reserveIndex = false;
|
|
51
52
|
#preventClose = false;
|
|
52
|
-
#
|
|
53
|
-
#shadowRootEventListener = null;
|
|
53
|
+
#bindShadowRootEvent = null;
|
|
54
54
|
#bindClose_key = null;
|
|
55
55
|
#bindClose_mouse = null;
|
|
56
56
|
|
|
@@ -124,9 +124,13 @@ class Controller {
|
|
|
124
124
|
* @param {Node} [positionTarget] Position target element
|
|
125
125
|
* @param {Object} [params={}] params
|
|
126
126
|
* @param {boolean} [params.isWWTarget] If the controller is in the WYSIWYG area, set it to `true`.
|
|
127
|
+
* @param {boolean} [params.passive] If `true`, opens the controller visually without affecting editor state
|
|
128
|
+
* - (`_preventBlur`, `controlActive`, `onControllerContext`, `opendControllers`).
|
|
129
|
+
* - Used for lightweight, non-intrusive display such as hover-triggered UI (e.g., codeLang selector on `<pre>` hover).
|
|
130
|
+
* - Automatically set to `true` when opened during component hover selection (`ON_OVER_COMPONENT`).
|
|
127
131
|
* @param {() => void} [params.initMethod] Method to be called when the controller is closed.
|
|
128
132
|
* @param {boolean} [params.disabled] If `true`, When the `controller` is opened, buttons without the `se-component-enabled` class are disabled. (default: `this.disabled`)
|
|
129
|
-
* @param {{left?: number, top?: number}} [params.addOffset] Additional offset values
|
|
133
|
+
* @param {{left?: number, right?:number, top?: number}} [params.addOffset] Additional offset values
|
|
130
134
|
* @example
|
|
131
135
|
* // Open controller on a target element with default options
|
|
132
136
|
* this.controller.open(target);
|
|
@@ -137,9 +141,9 @@ class Controller {
|
|
|
137
141
|
* // Open on a Range target (e.g., text selection)
|
|
138
142
|
* this.controller.open(this.$.selection.getRange());
|
|
139
143
|
*/
|
|
140
|
-
open(target, positionTarget, { isWWTarget, initMethod, disabled, addOffset } = {}) {
|
|
144
|
+
open(target, positionTarget, { isWWTarget, passive, initMethod, disabled, addOffset } = {}) {
|
|
141
145
|
if (_DragHandle.get('__overInfo') === ON_OVER_COMPONENT) {
|
|
142
|
-
|
|
146
|
+
passive = true;
|
|
143
147
|
}
|
|
144
148
|
|
|
145
149
|
if (!target) {
|
|
@@ -151,35 +155,38 @@ class Controller {
|
|
|
151
155
|
this.form.removeAttribute('data-se-hidden-by-children');
|
|
152
156
|
this.#__hiddenByParents__.clear();
|
|
153
157
|
|
|
154
|
-
if (
|
|
155
|
-
|
|
158
|
+
if (!passive) {
|
|
159
|
+
if (this.#$.store.mode.isBalloon) this.#$.toolbar.hide();
|
|
160
|
+
else if (this.#$.store.mode.isSubBalloon) this.#$.subToolbar.hide();
|
|
156
161
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
+
if (!this.#$.store.get('hasFocus')) {
|
|
163
|
+
if (disabled ?? this.disabled) {
|
|
164
|
+
this.#$.ui.setControllerOnDisabledButtons(true);
|
|
165
|
+
} else {
|
|
166
|
+
this.#$.ui.setControllerOnDisabledButtons(false);
|
|
167
|
+
}
|
|
162
168
|
}
|
|
163
169
|
}
|
|
164
170
|
|
|
165
171
|
this.currentPositionTarget = positionTarget || target;
|
|
166
172
|
this.isWWTarget = isWWTarget ?? this.isWWTarget;
|
|
167
173
|
if (typeof initMethod === 'function') this.#initMethod = initMethod;
|
|
168
|
-
this.#$.ui.currentControllerName = this.kind;
|
|
174
|
+
if (!passive) this.#$.ui.currentControllerName = this.kind;
|
|
169
175
|
|
|
170
|
-
this.#addOffset = { left: 0, top: 0 };
|
|
171
|
-
if (addOffset) this.#addOffset = { ...this.#addOffset, ...addOffset };
|
|
176
|
+
this.#addOffset = { left: 0, right: 0, top: 0, ...addOffset };
|
|
172
177
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
if (this.parentsHide) {
|
|
179
|
-
this.parentsForm.forEach((e) => {
|
|
180
|
-
e.style.display = 'none';
|
|
181
|
-
e.setAttribute('data-se-hidden-by-children', '1');
|
|
178
|
+
if (!passive) {
|
|
179
|
+
const parents = this.isOutsideForm ? this.parentsForm : [];
|
|
180
|
+
this.#$.ui.opendControllers?.forEach((e) => {
|
|
181
|
+
if (!parents.includes(e.form)) e.form.style.zIndex = INDEX_1;
|
|
182
182
|
});
|
|
183
|
+
|
|
184
|
+
if (this.parentsHide) {
|
|
185
|
+
this.parentsForm.forEach((e) => {
|
|
186
|
+
e.style.display = 'none';
|
|
187
|
+
e.setAttribute('data-se-hidden-by-children', '1');
|
|
188
|
+
});
|
|
189
|
+
}
|
|
183
190
|
}
|
|
184
191
|
|
|
185
192
|
this.#addGlobalEvent();
|
|
@@ -189,7 +196,7 @@ class Controller {
|
|
|
189
196
|
|
|
190
197
|
const isRangeTarget = this.#$.instanceCheck.isRange(target);
|
|
191
198
|
this.currentTarget = /** @type {HTMLElement} */ (isRangeTarget ? null : target);
|
|
192
|
-
this.#controllerOn(this.form, target, isRangeTarget);
|
|
199
|
+
this.#controllerOn(this.form, target, isRangeTarget, passive);
|
|
193
200
|
_w.setTimeout(() => _DragHandle.set('__overInfo', false), 0);
|
|
194
201
|
}
|
|
195
202
|
|
|
@@ -216,7 +223,7 @@ class Controller {
|
|
|
216
223
|
this.isOpen = false;
|
|
217
224
|
this.#preventClose = false;
|
|
218
225
|
this.__offset = {};
|
|
219
|
-
this.#addOffset =
|
|
226
|
+
this.#addOffset = ADD_OFFSET_VALUE;
|
|
220
227
|
|
|
221
228
|
this.#removeGlobalEvent();
|
|
222
229
|
|
|
@@ -347,8 +354,9 @@ class Controller {
|
|
|
347
354
|
* @param {HTMLFormElement} form Controller element
|
|
348
355
|
* @param {Node|Range} target Controller target element
|
|
349
356
|
* @param {boolean} isRangeTarget If the target is a `Range`, set it to `true`.
|
|
357
|
+
* @param {boolean} [passive=false] If `true`, opens without affecting editor state (_preventBlur, controlActive, etc.)
|
|
350
358
|
*/
|
|
351
|
-
async #controllerOn(form, target, isRangeTarget) {
|
|
359
|
+
async #controllerOn(form, target, isRangeTarget, passive) {
|
|
352
360
|
/** @type {ControllerInfo} */
|
|
353
361
|
const info = {
|
|
354
362
|
position: this.position,
|
|
@@ -363,20 +371,21 @@ class Controller {
|
|
|
363
371
|
|
|
364
372
|
form.style.display = 'block';
|
|
365
373
|
if (this.#$.contextProvider.shadowRoot) {
|
|
366
|
-
this.#
|
|
367
|
-
this.#shadowRootEventListener = (e) => e.stopPropagation();
|
|
368
|
-
form.addEventListener('mousedown', this.#shadowRootEventListener);
|
|
374
|
+
this.#bindShadowRootEvent = this.#$.eventManager.addEvent(form, 'mousedown', (e) => e.stopPropagation());
|
|
369
375
|
}
|
|
370
376
|
|
|
371
|
-
|
|
377
|
+
if (!passive) {
|
|
378
|
+
this.#$.ui.onControllerContext();
|
|
379
|
+
this.#$.store.set('controlActive', true);
|
|
380
|
+
}
|
|
372
381
|
|
|
373
382
|
if (!this.isOpen) {
|
|
374
383
|
this.#$.ui.opendControllers.push(info);
|
|
375
384
|
}
|
|
376
385
|
|
|
377
|
-
this.isOpen = true;
|
|
378
386
|
this.#$.store.set('_preventBlur', true);
|
|
379
|
-
|
|
387
|
+
|
|
388
|
+
this.isOpen = true;
|
|
380
389
|
|
|
381
390
|
this.host.controllerOn?.(form, target);
|
|
382
391
|
this.#$.eventManager.triggerEvent('onShowController', { caller: this.kind, frameContext: this.#$.frameContext, info });
|
|
@@ -401,10 +410,7 @@ class Controller {
|
|
|
401
410
|
_w.setTimeout(() => {
|
|
402
411
|
this.#$.store.set('controlActive', false);
|
|
403
412
|
}, 0);
|
|
404
|
-
|
|
405
|
-
this.#shadowRootEventForm.removeEventListener('mousedown', this.#shadowRootEventListener);
|
|
406
|
-
this.#shadowRootEventForm = this.#shadowRootEventListener = null;
|
|
407
|
-
}
|
|
413
|
+
this.#bindShadowRootEvent &&= this.#$.eventManager.removeEvent(this.#bindShadowRootEvent);
|
|
408
414
|
}
|
|
409
415
|
|
|
410
416
|
/**
|
|
@@ -500,7 +506,7 @@ class Controller {
|
|
|
500
506
|
|
|
501
507
|
/**
|
|
502
508
|
* @description Checks if the given target is within a form or controller.
|
|
503
|
-
* @param {
|
|
509
|
+
* @param {Element} target The target element.
|
|
504
510
|
* @returns {boolean} `true` if the target is inside a form or controller.
|
|
505
511
|
*/
|
|
506
512
|
#checkForm(target) {
|
|
@@ -532,6 +538,11 @@ class Controller {
|
|
|
532
538
|
e.stopPropagation();
|
|
533
539
|
e.preventDefault();
|
|
534
540
|
|
|
541
|
+
if (target.getAttribute('data-command') === 'close') {
|
|
542
|
+
this.close();
|
|
543
|
+
return;
|
|
544
|
+
}
|
|
545
|
+
|
|
535
546
|
this.host.controllerAction(target);
|
|
536
547
|
}
|
|
537
548
|
|
|
@@ -144,9 +144,9 @@ class ApiManager {
|
|
|
144
144
|
this.#$.ui.hideLoading();
|
|
145
145
|
}
|
|
146
146
|
} else {
|
|
147
|
+
console.error(`[SUNEDITOR.ApiManager[${this.kind}].upload.serverException]`, xhr);
|
|
147
148
|
try {
|
|
148
|
-
|
|
149
|
-
reject(res);
|
|
149
|
+
reject(_parseErrorResponse(xhr));
|
|
150
150
|
} finally {
|
|
151
151
|
this.#$.ui.hideLoading();
|
|
152
152
|
}
|
|
@@ -203,12 +203,12 @@ class ApiManager {
|
|
|
203
203
|
// exception
|
|
204
204
|
console.error(`[SUNEDITOR.ApiManager[${this.kind}].upload.serverException]`, xmlHttp);
|
|
205
205
|
try {
|
|
206
|
-
const res =
|
|
206
|
+
const res = _parseErrorResponse(xmlHttp);
|
|
207
207
|
let message = '';
|
|
208
208
|
if (typeof errorCallBack === 'function') {
|
|
209
209
|
message = await errorCallBack(res, xmlHttp);
|
|
210
210
|
}
|
|
211
|
-
const err = `[SUNEDITOR.ApiManager[${this.kind}].upload.serverException] status: ${xmlHttp.status}, response: ${message || res.errorMessage ||
|
|
211
|
+
const err = `[SUNEDITOR.ApiManager[${this.kind}].upload.serverException] status: ${xmlHttp.status}, response: ${message || res.errorMessage || (typeof res === 'string' ? res : JSON.stringify(res))}`;
|
|
212
212
|
this.#$.ui.alertOpen(err, 'error');
|
|
213
213
|
} catch (error) {
|
|
214
214
|
throw Error(`[SUNEDITOR.ApiManager[${this.kind}].upload.errorCallBack.fail] ${error.message}`);
|
|
@@ -220,4 +220,27 @@ class ApiManager {
|
|
|
220
220
|
}
|
|
221
221
|
}
|
|
222
222
|
|
|
223
|
+
/**
|
|
224
|
+
* @description Parses error response from XMLHttpRequest.
|
|
225
|
+
* Safely handles non-text responseTypes (blob, arraybuffer, etc.) where accessing responseText throws.
|
|
226
|
+
* @param {XMLHttpRequest} xhr
|
|
227
|
+
* @returns {Object|string} Parsed JSON object, raw text, or status string as fallback
|
|
228
|
+
*/
|
|
229
|
+
function _parseErrorResponse(xhr) {
|
|
230
|
+
let text;
|
|
231
|
+
try {
|
|
232
|
+
text = xhr.responseText;
|
|
233
|
+
} catch {
|
|
234
|
+
return `status ${xhr.status}`;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
if (!text) return `status ${xhr.status}`;
|
|
238
|
+
|
|
239
|
+
try {
|
|
240
|
+
return JSON.parse(text);
|
|
241
|
+
} catch {
|
|
242
|
+
return text;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
223
246
|
export default ApiManager;
|
|
@@ -16,6 +16,8 @@ const MENU_MIN_HEIGHT = 38;
|
|
|
16
16
|
* @property {number} [splitNum=0] Optional split number for horizontal positioning; defines how many items per row
|
|
17
17
|
* @property {() => void} [openMethod] Optional method to call when the menu is opened
|
|
18
18
|
* @property {() => void} [closeMethod] Optional method to call when the menu is closed
|
|
19
|
+
* @property {string} [maxHeight] Optional max-height CSS value (e.g. `"200px"`). Enables scrolling when items exceed this height.
|
|
20
|
+
* @property {string} [minWidth] Optional min-width CSS value (e.g. `"130px"`).
|
|
19
21
|
*/
|
|
20
22
|
|
|
21
23
|
/**
|
|
@@ -64,6 +66,8 @@ class SelectMenu {
|
|
|
64
66
|
this.horizontal = !!this.splitNum;
|
|
65
67
|
this.openMethod = params.openMethod;
|
|
66
68
|
this.closeMethod = params.closeMethod;
|
|
69
|
+
this.maxHeight = params.maxHeight || '';
|
|
70
|
+
this.minWidth = params.minWidth || '';
|
|
67
71
|
|
|
68
72
|
this.#dirPosition = /^(left|right)$/.test(this.position) ? (this.position === 'left' ? 'right' : 'left') : this.position;
|
|
69
73
|
this.#dirSubPosition = /^(left|right)$/.test(this.subPosition) ? (this.subPosition === 'left' ? 'right' : 'left') : this.subPosition;
|
|
@@ -117,14 +121,20 @@ class SelectMenu {
|
|
|
117
121
|
this.#refer = /** @type {HTMLElement} */ (referElement);
|
|
118
122
|
this.#keydownTarget = dom.check.isInputElement(referElement) ? referElement : this.#$.frameContext.get('_ww');
|
|
119
123
|
this.#selectMethod = selectMethod;
|
|
124
|
+
|
|
125
|
+
let innerStyle = '';
|
|
126
|
+
if (this.maxHeight) innerStyle += 'max-height:' + this.maxHeight + ';overflow-y:auto;';
|
|
127
|
+
if (this.minWidth) innerStyle += 'min-width:' + this.minWidth + ';';
|
|
128
|
+
|
|
120
129
|
this.form = dom.utils.createElement(
|
|
121
130
|
'DIV',
|
|
122
131
|
{
|
|
123
132
|
class: 'se-select-menu' + (attr.class ? ' ' + attr.class : ''),
|
|
124
133
|
style: attr.style || '',
|
|
125
134
|
},
|
|
126
|
-
'<div class="se-list-inner"></div>',
|
|
135
|
+
'<div class="se-list-inner"' + (innerStyle ? ' style="' + innerStyle + '"' : '') + '></div>',
|
|
127
136
|
);
|
|
137
|
+
|
|
128
138
|
referElement.parentNode.insertBefore(this.form, referElement);
|
|
129
139
|
}
|
|
130
140
|
|
|
@@ -407,11 +417,12 @@ class SelectMenu {
|
|
|
407
417
|
*/
|
|
408
418
|
#addEvents() {
|
|
409
419
|
this.#removeEvents();
|
|
410
|
-
this.#events =
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
420
|
+
this.#events = {
|
|
421
|
+
mousedown: this.#$.eventManager.addEvent(this.form, 'mousedown', this.#eventHandlers.mousedown),
|
|
422
|
+
mousemove: this.#$.eventManager.addEvent(this.form, 'mousemove', this.#eventHandlers.mousemove),
|
|
423
|
+
click: this.#$.eventManager.addEvent(this.form, 'click', this.#eventHandlers.click),
|
|
424
|
+
keydown: this.#$.eventManager.addEvent(this.#keydownTarget, 'keydown', this.#eventHandlers.keydown),
|
|
425
|
+
};
|
|
415
426
|
}
|
|
416
427
|
|
|
417
428
|
/**
|
|
@@ -419,10 +430,10 @@ class SelectMenu {
|
|
|
419
430
|
*/
|
|
420
431
|
#removeEvents() {
|
|
421
432
|
if (!this.#events) return;
|
|
422
|
-
this.
|
|
423
|
-
this.
|
|
424
|
-
this.
|
|
425
|
-
this
|
|
433
|
+
this.#$.eventManager.removeEvent(this.#events.mousedown);
|
|
434
|
+
this.#$.eventManager.removeEvent(this.#events.mousemove);
|
|
435
|
+
this.#$.eventManager.removeEvent(this.#events.click);
|
|
436
|
+
this.#$.eventManager.removeEvent(this.#events.keydown);
|
|
426
437
|
this.#events = null;
|
|
427
438
|
}
|
|
428
439
|
|
|
@@ -547,7 +558,7 @@ class SelectMenu {
|
|
|
547
558
|
#CloseListener_mousedown(e) {
|
|
548
559
|
const eventTarget = dom.query.getEventTarget(e);
|
|
549
560
|
if (this.form.contains(eventTarget)) return;
|
|
550
|
-
if (
|
|
561
|
+
if (!this.#refer.contains(eventTarget)) {
|
|
551
562
|
this.close();
|
|
552
563
|
} else if (!dom.check.isInputElement(eventTarget)) {
|
|
553
564
|
this.#bindClose_click = this.#$.eventManager.addGlobalEvent('click', this.#globalEventHandlers.click, true);
|