suneditor 3.0.5 → 3.0.6
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/dist/suneditor.min.css +1 -1
- package/dist/suneditor.min.js +1 -1
- package/package.json +1 -1
- package/src/assets/suneditor.css +51 -3
- package/src/core/logic/shell/_commandExecutor.js +38 -1
- package/src/core/section/constructor.js +1 -1
- package/src/modules/contract/Browser.js +98 -10
- package/src/modules/ui/ModalAnchorEditor.js +16 -1
- package/src/plugins/browser/audioGallery.js +13 -0
- package/src/plugins/browser/fileBrowser.js +22 -0
- package/src/plugins/browser/fileGallery.js +14 -0
- package/src/plugins/browser/imageGallery.js +14 -0
- package/src/plugins/browser/videoGallery.js +14 -0
- package/src/plugins/command/fileUpload.js +12 -0
- package/src/plugins/field/mention.js +2 -2
- package/src/plugins/modal/audio.js +12 -0
- package/src/plugins/modal/embed.js +12 -0
- package/src/plugins/modal/image/index.js +12 -0
- package/src/plugins/modal/link.js +12 -0
- package/src/plugins/modal/video/index.js +12 -0
- package/types/langs/_Lang.d.ts +1 -0
- package/types/modules/contract/Browser.d.ts +32 -0
- package/types/modules/ui/ModalAnchorEditor.d.ts +32 -2
- package/types/plugins/browser/audioGallery.d.ts +26 -0
- package/types/plugins/browser/fileBrowser.d.ts +45 -0
- package/types/plugins/browser/fileGallery.d.ts +28 -0
- package/types/plugins/browser/imageGallery.d.ts +28 -0
- package/types/plugins/browser/videoGallery.d.ts +28 -0
- package/types/plugins/command/fileUpload.d.ts +24 -0
- package/types/plugins/field/mention.d.ts +1 -1
- package/types/plugins/modal/audio.d.ts +24 -0
- package/types/plugins/modal/embed.d.ts +24 -0
- package/types/plugins/modal/image/index.d.ts +24 -0
- package/types/plugins/modal/link.d.ts +28 -1
- package/types/plugins/modal/video/index.d.ts +24 -0
package/package.json
CHANGED
package/src/assets/suneditor.css
CHANGED
|
@@ -368,6 +368,26 @@
|
|
|
368
368
|
z-index: 1;
|
|
369
369
|
}
|
|
370
370
|
|
|
371
|
+
/** -- plain text button */
|
|
372
|
+
.sun-editor .se-btn.se-btn-plain {
|
|
373
|
+
background-color: transparent;
|
|
374
|
+
border-color: transparent;
|
|
375
|
+
box-shadow: none;
|
|
376
|
+
}
|
|
377
|
+
.sun-editor .se-btn.se-btn-plain:not(.on):not(.active):enabled:hover,
|
|
378
|
+
.sun-editor .se-btn.se-btn-plain:not(.on):not(.active):enabled:focus {
|
|
379
|
+
background-color: transparent;
|
|
380
|
+
border-color: transparent;
|
|
381
|
+
box-shadow: none;
|
|
382
|
+
}
|
|
383
|
+
.sun-editor .se-btn.se-btn-plain:not(.on):not(.active):enabled:active,
|
|
384
|
+
.sun-editor .se-btn.se-btn-plain:not(.on):not(.active):enabled.__se__active {
|
|
385
|
+
background-color: transparent;
|
|
386
|
+
border-color: transparent !important;
|
|
387
|
+
box-shadow: none;
|
|
388
|
+
outline: none !important;
|
|
389
|
+
}
|
|
390
|
+
|
|
371
391
|
/** --- primary button */
|
|
372
392
|
.sun-editor .se-btn-primary {
|
|
373
393
|
background-color: var(--se-active-light2-color);
|
|
@@ -3232,11 +3252,14 @@
|
|
|
3232
3252
|
margin-left: 0;
|
|
3233
3253
|
margin-right: 0;
|
|
3234
3254
|
}
|
|
3235
|
-
|
|
3236
|
-
|
|
3255
|
+
.sun-editor .se-browser .se-browser-search-input-wrap {
|
|
3256
|
+
position: relative;
|
|
3237
3257
|
flex: auto;
|
|
3258
|
+
}
|
|
3259
|
+
.sun-editor .se-browser .se-browser-search-input-wrap input {
|
|
3260
|
+
width: 100%;
|
|
3261
|
+
padding: 3px 24px 3px 6px;
|
|
3238
3262
|
background-color: transparent;
|
|
3239
|
-
padding: 3px 6px;
|
|
3240
3263
|
color: var(--se-main-font-color);
|
|
3241
3264
|
text-decoration: none;
|
|
3242
3265
|
border-radius: var(--se-border-radius);
|
|
@@ -3245,6 +3268,18 @@
|
|
|
3245
3268
|
-moz-background-clip: padding;
|
|
3246
3269
|
-webkit-background-clip: padding-box;
|
|
3247
3270
|
background-clip: padding-box;
|
|
3271
|
+
box-sizing: border-box;
|
|
3272
|
+
}
|
|
3273
|
+
.sun-editor .se-browser .se-browser-search-clear {
|
|
3274
|
+
position: absolute;
|
|
3275
|
+
right: 2px;
|
|
3276
|
+
top: 50%;
|
|
3277
|
+
transform: translateY(-50%);
|
|
3278
|
+
padding: 0 2px;
|
|
3279
|
+
opacity: 0.35;
|
|
3280
|
+
}
|
|
3281
|
+
.sun-editor .se-browser .se-browser-search-clear:hover {
|
|
3282
|
+
opacity: 0.8;
|
|
3248
3283
|
}
|
|
3249
3284
|
|
|
3250
3285
|
.sun-editor .se-browser .se-browser-search button {
|
|
@@ -3396,6 +3431,12 @@
|
|
|
3396
3431
|
opacity: 0.6;
|
|
3397
3432
|
pointer-events: none;
|
|
3398
3433
|
}
|
|
3434
|
+
.sun-editor .se-browser .se-file-name-image mark {
|
|
3435
|
+
background-color: var(--se-active-color);
|
|
3436
|
+
color: var(--se-active-font-color);
|
|
3437
|
+
padding: 0 1px;
|
|
3438
|
+
border-radius: 2px;
|
|
3439
|
+
}
|
|
3399
3440
|
|
|
3400
3441
|
.sun-editor .se-browser .se-browser-list.se-preview-list .se-file-item-img > * {
|
|
3401
3442
|
display: flex;
|
|
@@ -4148,6 +4189,13 @@
|
|
|
4148
4189
|
margin: 2px 8px;
|
|
4149
4190
|
flex-direction: row-reverse;
|
|
4150
4191
|
}
|
|
4192
|
+
.sun-editor.se-rtl .se-browser .se-browser-search-input-wrap input {
|
|
4193
|
+
padding: 3px 6px 3px 24px;
|
|
4194
|
+
}
|
|
4195
|
+
.sun-editor.se-rtl .se-browser .se-browser-search-clear {
|
|
4196
|
+
right: auto;
|
|
4197
|
+
left: 2px;
|
|
4198
|
+
}
|
|
4151
4199
|
.sun-editor.se-rtl .se-browser .se-browser-side .se-menu-icon {
|
|
4152
4200
|
margin: 0 0 0 10px;
|
|
4153
4201
|
}
|
|
@@ -59,12 +59,15 @@ export default class CommandExecutor {
|
|
|
59
59
|
* @description Execute default command of command button
|
|
60
60
|
*/
|
|
61
61
|
async execute(command, button) {
|
|
62
|
-
if (this.#frameContext.get('isReadOnly') && !/copy|cut|selectAll|codeView|markdownView|fullScreen|print|preview|showBlocks|finder/.test(command)) return;
|
|
62
|
+
if (this.#frameContext.get('isReadOnly') && !/copy|cut|selectAll|selectAll_full|codeView|markdownView|fullScreen|print|preview|showBlocks|finder/.test(command)) return;
|
|
63
63
|
|
|
64
64
|
switch (command) {
|
|
65
65
|
case 'selectAll':
|
|
66
66
|
this.#SELECT_ALL();
|
|
67
67
|
break;
|
|
68
|
+
case 'selectAll_full':
|
|
69
|
+
this.#SELECT_ALL_FULL();
|
|
70
|
+
break;
|
|
68
71
|
case 'copy': {
|
|
69
72
|
const range = this.#$.selection.getRange();
|
|
70
73
|
if (range.collapsed) break;
|
|
@@ -254,6 +257,40 @@ export default class CommandExecutor {
|
|
|
254
257
|
this.#$.toolbar._showBalloon(this.#$.selection.setRange(first, 0, last, last.textContent.length));
|
|
255
258
|
}
|
|
256
259
|
|
|
260
|
+
/**
|
|
261
|
+
* @description Selects all content in the entire editor without scope stepping.
|
|
262
|
+
*/
|
|
263
|
+
#SELECT_ALL_FULL() {
|
|
264
|
+
this.#$.ui.offCurrentController();
|
|
265
|
+
this.#$.menu.containerOff();
|
|
266
|
+
|
|
267
|
+
const ww = this.#frameContext.get('wysiwyg');
|
|
268
|
+
let { first, last } = __findFirstAndLast(ww);
|
|
269
|
+
|
|
270
|
+
if (!first || !last) return;
|
|
271
|
+
|
|
272
|
+
let info = null;
|
|
273
|
+
if (dom.check.isMedia(first) || (info = this.#$.component.get(first)) || dom.check.isTableElements(first)) {
|
|
274
|
+
info ||= this.#$.component.get(first);
|
|
275
|
+
const br = dom.utils.createElement('BR');
|
|
276
|
+
const format = dom.utils.createElement(this.#options.get('defaultLine'), null, br);
|
|
277
|
+
first = info ? info.container || info.cover : first;
|
|
278
|
+
first.parentElement.insertBefore(format, first);
|
|
279
|
+
first = br;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
if (dom.check.isMedia(last) || (info = this.#$.component.get(last)) || dom.check.isTableElements(last)) {
|
|
283
|
+
info ||= this.#$.component.get(last);
|
|
284
|
+
const br = dom.utils.createElement('BR');
|
|
285
|
+
const format = dom.utils.createElement(this.#options.get('defaultLine'), null, br);
|
|
286
|
+
last = info ? info.container || info.cover : last;
|
|
287
|
+
last.parentElement.appendChild(format);
|
|
288
|
+
last = br;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
this.#$.toolbar._showBalloon(this.#$.selection.setRange(first, 0, last, last.textContent.length));
|
|
292
|
+
}
|
|
293
|
+
|
|
257
294
|
/**
|
|
258
295
|
* @description Saves the editor content.
|
|
259
296
|
* @returns {Promise<void>}
|
|
@@ -1132,7 +1132,7 @@ function _defaultButtons(isRTL, icons, lang) {
|
|
|
1132
1132
|
finder: ['se-component-enabled', lang.find, 'finder', '', icons.finder],
|
|
1133
1133
|
save: ['se-component-enabled', lang.save, 'save', '', icons.save],
|
|
1134
1134
|
newDocument: ['se-component-enabled', lang.newDocument, 'newDocument', '', icons.new_document],
|
|
1135
|
-
selectAll: ['se-component-enabled', lang.selectAll, '
|
|
1135
|
+
selectAll: ['se-component-enabled', lang.selectAll, 'selectAll_full', '', icons.select_all],
|
|
1136
1136
|
pageBreak: ['se-component-enabled', lang.pageBreak, 'pageBreak', '', icons.page_break],
|
|
1137
1137
|
// document type buttons
|
|
1138
1138
|
pageUp: ['se-component-enabled', lang.pageUp, 'pageUp', '', icons.page_up],
|
|
@@ -27,6 +27,19 @@ import ApiManager from '../manager/ApiManager';
|
|
|
27
27
|
* @property {(target: Node) => void} selectorHandler - Function that actions when an item is clicked. Required. Can be overridden in browser.
|
|
28
28
|
* @property {boolean} [useSearch] - Whether to use the search function. Optional. Default: `true`.
|
|
29
29
|
* @property {string} [searchUrl] - File server search url. Optional. Can be overridden in browser.
|
|
30
|
+
* - Requested as `searchUrl + '?keyword=' + keyword`. The server must return:
|
|
31
|
+
* ```js
|
|
32
|
+
* {
|
|
33
|
+
* "result": [
|
|
34
|
+
* {
|
|
35
|
+
* "src": "https://example.com/file.jpg",
|
|
36
|
+
* "name": "file.jpg",
|
|
37
|
+
* "thumbnail": "https://example.com/file_thumb.jpg",
|
|
38
|
+
* "tag": ["photo"]
|
|
39
|
+
* }
|
|
40
|
+
* ]
|
|
41
|
+
* }
|
|
42
|
+
* ```
|
|
30
43
|
* @property {Object<string, string>} [searchUrlHeader] - File server search http header. Optional. Can be overridden in browser.
|
|
31
44
|
* @property {string} [listClass] - Class name of list div. Required. Can be overridden in browser.
|
|
32
45
|
* @property {(item: BrowserFile) => string} [drawItemHandler] - Function that returns HTML string for rendering each file item. Required. Can be overridden in browser.
|
|
@@ -37,6 +50,7 @@ import ApiManager from '../manager/ApiManager';
|
|
|
37
50
|
* @property {Array<*>} [props] - `props` argument to `drawItemHandler` function. Optional. Can be overridden in browser.
|
|
38
51
|
* @property {number} [columnSize] - Number of `div.se-file-item-column` to be created.
|
|
39
52
|
* - Optional. Can be overridden in browser. Default: 4.
|
|
53
|
+
* @property {number} [expand=1] - Initial folder expand depth. `1` expands the first level, `Infinity` expands all. Default: `1`.
|
|
40
54
|
* @property {((item: BrowserFile) => string)} [thumbnail] - Default thumbnail
|
|
41
55
|
*/
|
|
42
56
|
|
|
@@ -50,6 +64,10 @@ class Browser {
|
|
|
50
64
|
#loading;
|
|
51
65
|
#globalEventHandler;
|
|
52
66
|
|
|
67
|
+
/** @type {Array<BrowserFile>} */
|
|
68
|
+
#allItems = [];
|
|
69
|
+
#searchInput;
|
|
70
|
+
#searchClearBtn;
|
|
53
71
|
#closeSignal = false;
|
|
54
72
|
#bindClose = null;
|
|
55
73
|
|
|
@@ -102,6 +120,7 @@ class Browser {
|
|
|
102
120
|
this.drawItemHandler = (params.drawItemHandler || DrawItems).bind({ thumbnail: params.thumbnail, props: params.props || [] });
|
|
103
121
|
this.selectorHandler = params.selectorHandler;
|
|
104
122
|
this.columnSize = params.columnSize || 4;
|
|
123
|
+
this.expand = params.expand ?? 1;
|
|
105
124
|
this.folderDefaultPath = '';
|
|
106
125
|
this.closeArrow = this.#$.icons.menu_arrow_right;
|
|
107
126
|
this.openArrow = this.#$.icons.menu_arrow_down;
|
|
@@ -139,9 +158,15 @@ class Browser {
|
|
|
139
158
|
this.#$.eventManager.addEvent(this.side, 'click', this.#OnClickSide.bind(this));
|
|
140
159
|
this.#$.eventManager.addEvent(content, 'mousedown', this.#OnMouseDown_browser.bind(this));
|
|
141
160
|
this.#$.eventManager.addEvent(content, 'click', this.#OnClick_browser.bind(this));
|
|
142
|
-
this.#$.eventManager.addEvent(browserFrame.querySelector('form.se-browser-search-form'), 'submit', this.#Search.bind(this));
|
|
143
161
|
this.#$.eventManager.addEvent((this.sideOpenBtn = /** @type {HTMLButtonElement} */ (browserFrame.querySelector('.se-side-open-btn'))), 'click', this.#SideOpen.bind(this));
|
|
144
162
|
this.#$.eventManager.addEvent([this.header, browserFrame.querySelector('.se-browser-main')], 'mousedown', this.#SideClose.bind(this));
|
|
163
|
+
|
|
164
|
+
// search
|
|
165
|
+
const searchForm = browserFrame.querySelector('form.se-browser-search-form');
|
|
166
|
+
this.#searchInput = /** @type {HTMLInputElement} */ (searchForm?.querySelector('input[type="text"]'));
|
|
167
|
+
this.#searchClearBtn = /** @type {HTMLButtonElement} */ (browserFrame.querySelector('.se-browser-search-clear'));
|
|
168
|
+
this.#$.eventManager.addEvent(searchForm, 'submit', this.#Search.bind(this));
|
|
169
|
+
this.#$.eventManager.addEvent(this.#searchClearBtn, 'click', this.#ClearSearch.bind(this));
|
|
145
170
|
}
|
|
146
171
|
|
|
147
172
|
/**
|
|
@@ -195,11 +220,16 @@ class Browser {
|
|
|
195
220
|
this.area.style.display = 'none';
|
|
196
221
|
this.selectedTags = [];
|
|
197
222
|
this.items = [];
|
|
223
|
+
this.#allItems = [];
|
|
198
224
|
this.folders = {};
|
|
199
225
|
this.tree = {};
|
|
200
226
|
this.data = {};
|
|
201
227
|
this.keyword = '';
|
|
202
228
|
this.list.innerHTML = this.tagArea.innerHTML = this.titleArea.textContent = '';
|
|
229
|
+
|
|
230
|
+
if (this.#searchInput) this.#searchInput.value = '';
|
|
231
|
+
if (this.#searchClearBtn) this.#searchClearBtn.style.display = 'none';
|
|
232
|
+
|
|
203
233
|
this.#$.ui.opendBrowser = null;
|
|
204
234
|
this.sideInner = null;
|
|
205
235
|
|
|
@@ -216,8 +246,25 @@ class Browser {
|
|
|
216
246
|
this.#drawFileList(this.searchUrl + '?keyword=' + keyword, this.searchUrlHeader, false);
|
|
217
247
|
} else {
|
|
218
248
|
this.keyword = keyword.toLowerCase();
|
|
219
|
-
this.#drawListItem(this.items, false);
|
|
249
|
+
this.#drawListItem(this.#allItems.length > 0 ? this.#allItems : this.items, false);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* @description Collects all file items from every folder in `this.data`.
|
|
255
|
+
* @returns {Array<BrowserFile>}
|
|
256
|
+
*/
|
|
257
|
+
#collectAllItems() {
|
|
258
|
+
const all = [];
|
|
259
|
+
for (const key in this.data) {
|
|
260
|
+
const items = this.data[key];
|
|
261
|
+
if (Array.isArray(items)) {
|
|
262
|
+
for (let i = 0; i < items.length; i++) {
|
|
263
|
+
all.push(items[i]);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
220
266
|
}
|
|
267
|
+
return all;
|
|
221
268
|
}
|
|
222
269
|
|
|
223
270
|
/**
|
|
@@ -306,6 +353,10 @@ class Browser {
|
|
|
306
353
|
|
|
307
354
|
this.list.innerHTML = listHTML;
|
|
308
355
|
|
|
356
|
+
if (keyword) {
|
|
357
|
+
this.#highlightKeyword(keyword);
|
|
358
|
+
}
|
|
359
|
+
|
|
309
360
|
if (update) {
|
|
310
361
|
this.items = items;
|
|
311
362
|
this.tagArea.innerHTML = tagsHTML;
|
|
@@ -341,6 +392,7 @@ class Browser {
|
|
|
341
392
|
} else if (typeof data === 'object') {
|
|
342
393
|
this.sideOpenBtn.style.display = '';
|
|
343
394
|
this.#parseFolderData(data);
|
|
395
|
+
this.#allItems = this.#collectAllItems();
|
|
344
396
|
|
|
345
397
|
this.side.innerHTML = '';
|
|
346
398
|
const sideInner = (this.sideInner = dom.utils.createElement('div', null));
|
|
@@ -412,8 +464,11 @@ class Browser {
|
|
|
412
464
|
* @description Creates a nested folder list from parsed data.
|
|
413
465
|
* @param {BrowserFile[]|BrowserFile} folderData - The structured folder data.
|
|
414
466
|
* @param {HTMLElement} parentElement - The parent element to append folder structure to.
|
|
467
|
+
* @param {number} [depth=0] - Current depth level.
|
|
415
468
|
*/
|
|
416
|
-
#createFolderList(folderData, parentElement) {
|
|
469
|
+
#createFolderList(folderData, parentElement, depth = 0) {
|
|
470
|
+
const expanded = depth < this.expand;
|
|
471
|
+
|
|
417
472
|
for (const key in folderData) {
|
|
418
473
|
const item = folderData[key];
|
|
419
474
|
if (!item) continue;
|
|
@@ -426,10 +481,10 @@ class Browser {
|
|
|
426
481
|
);
|
|
427
482
|
const folderDiv = dom.utils.createElement('div', { class: 'se-menu-folder' }, folderLabel);
|
|
428
483
|
|
|
429
|
-
folderLabel.insertBefore(dom.utils.createElement('button', null, this.closeArrow), folderLabel.firstElementChild);
|
|
484
|
+
folderLabel.insertBefore(dom.utils.createElement('button', null, expanded ? this.openArrow : this.closeArrow), folderLabel.firstElementChild);
|
|
430
485
|
const childContainer = document.createElement('div');
|
|
431
|
-
dom.utils.addClass(childContainer, 'se-menu-child|se-menu-hidden');
|
|
432
|
-
this.#createFolderList(item.children, childContainer);
|
|
486
|
+
dom.utils.addClass(childContainer, expanded ? 'se-menu-child' : 'se-menu-child|se-menu-hidden');
|
|
487
|
+
this.#createFolderList(item.children, childContainer, depth + 1);
|
|
433
488
|
folderDiv.appendChild(childContainer);
|
|
434
489
|
|
|
435
490
|
parentElement.appendChild(folderDiv);
|
|
@@ -544,7 +599,7 @@ class Browser {
|
|
|
544
599
|
if (typeof data === 'string') {
|
|
545
600
|
this.#drawFileList(data, this.urlHeader, true);
|
|
546
601
|
} else {
|
|
547
|
-
this.#drawListItem(data,
|
|
602
|
+
this.#drawListItem(data, true);
|
|
548
603
|
}
|
|
549
604
|
}
|
|
550
605
|
|
|
@@ -576,9 +631,39 @@ class Browser {
|
|
|
576
631
|
* @param {SubmitEvent} e - Event object
|
|
577
632
|
*/
|
|
578
633
|
#Search(e) {
|
|
579
|
-
const eventTarget = /** @type {HTMLElement} */ (e.currentTarget);
|
|
580
634
|
e.preventDefault();
|
|
581
|
-
|
|
635
|
+
|
|
636
|
+
const keyword = this.#searchInput.value;
|
|
637
|
+
this.search(keyword);
|
|
638
|
+
|
|
639
|
+
if (this.#searchClearBtn) this.#searchClearBtn.style.display = keyword ? '' : 'none';
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
/**
|
|
643
|
+
* @description Clears the search keyword and restores the current folder's item list.
|
|
644
|
+
*/
|
|
645
|
+
#ClearSearch() {
|
|
646
|
+
if (this.#searchInput) this.#searchInput.value = '';
|
|
647
|
+
if (this.#searchClearBtn) this.#searchClearBtn.style.display = 'none';
|
|
648
|
+
|
|
649
|
+
this.keyword = '';
|
|
650
|
+
this.#drawListItem(this.items, false);
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
/**
|
|
654
|
+
* @description Highlights the search keyword in file name elements.
|
|
655
|
+
* @param {string} keyword - Lowercase keyword to highlight.
|
|
656
|
+
*/
|
|
657
|
+
#highlightKeyword(keyword) {
|
|
658
|
+
const nameEls = this.list.querySelectorAll('.se-file-name-image:not(.se-file-name-back)');
|
|
659
|
+
for (let i = 0; i < nameEls.length; i++) {
|
|
660
|
+
const el = nameEls[i];
|
|
661
|
+
const text = el.textContent;
|
|
662
|
+
const idx = text.toLowerCase().indexOf(keyword);
|
|
663
|
+
if (idx > -1) {
|
|
664
|
+
el.innerHTML = text.substring(0, idx) + '<mark>' + text.substring(idx, idx + keyword.length) + '</mark>' + text.substring(idx + keyword.length);
|
|
665
|
+
}
|
|
666
|
+
}
|
|
582
667
|
}
|
|
583
668
|
|
|
584
669
|
/**
|
|
@@ -633,7 +718,10 @@ function CreateHTMLInfos($, useSearch) {
|
|
|
633
718
|
useSearch
|
|
634
719
|
? /*html*/ `
|
|
635
720
|
<form class="se-browser-search-form">
|
|
636
|
-
<
|
|
721
|
+
<div class="se-browser-search-input-wrap">
|
|
722
|
+
<input type="text" class="se-input-form" placeholder="${lang.search}" aria-label="${lang.search}">
|
|
723
|
+
<button type="button" class="se-btn se-btn-plain se-browser-search-clear" title="${lang.cancel}" aria-label="${lang.cancel}" style="display:none">${icons.cancel}</button>
|
|
724
|
+
</div>
|
|
637
725
|
<button type="submit" class="se-btn" title="${lang.search}" aria-label="${lang.search}">${icons.search}</button>
|
|
638
726
|
</form>`
|
|
639
727
|
: ''
|
|
@@ -13,9 +13,24 @@ const { _w, NO_EVENT } = env;
|
|
|
13
13
|
* @property {{default?: string, check_new_window?: string, check_bookmark?: string}} [defaultRel={}] - Default `rel` values auto-applied by condition.
|
|
14
14
|
* `default` is always applied, `check_new_window` when "Open in new window" is checked, `check_bookmark` for bookmark links.
|
|
15
15
|
* ```js
|
|
16
|
-
* {
|
|
16
|
+
* {
|
|
17
|
+
* relList: ['nofollow', 'noreferrer', 'noopener'],
|
|
18
|
+
* defaultRel: { default: 'noopener', check_new_window: 'noreferrer' }
|
|
19
|
+
* }
|
|
17
20
|
* ```
|
|
18
21
|
* @property {string} [uploadUrl] - File upload URL.
|
|
22
|
+
* - The server must return:
|
|
23
|
+
* ```js
|
|
24
|
+
* {
|
|
25
|
+
* "result": [
|
|
26
|
+
* {
|
|
27
|
+
* "url": "https://example.com/file.pdf",
|
|
28
|
+
* "name": "file.pdf",
|
|
29
|
+
* "size": 1048576
|
|
30
|
+
* }
|
|
31
|
+
* ]
|
|
32
|
+
* }
|
|
33
|
+
* ```
|
|
19
34
|
* @property {Object<string, string>} [uploadHeaders] - File upload headers.
|
|
20
35
|
* @property {number} [uploadSizeLimit] - File upload size limit.
|
|
21
36
|
* @property {number} [uploadSingleSizeLimit] - File upload single size limit.
|
|
@@ -5,6 +5,19 @@ import { Browser } from '../../modules/contract';
|
|
|
5
5
|
* @typedef {Object} AudioGalleryPluginOptions
|
|
6
6
|
* @property {Array<SunEditor.Module.Browser.File>} [data] - Direct data without server calls
|
|
7
7
|
* @property {string} [url] - Server request URL
|
|
8
|
+
* - The server must return:
|
|
9
|
+
* ```js
|
|
10
|
+
* {
|
|
11
|
+
* "result": [
|
|
12
|
+
* {
|
|
13
|
+
* "src": "https://example.com/audio.mp3",
|
|
14
|
+
* "name": "audio.mp3",
|
|
15
|
+
* "thumbnail": "https://example.com/audio_icon.png",
|
|
16
|
+
* "tag": ["music"]
|
|
17
|
+
* }
|
|
18
|
+
* ]
|
|
19
|
+
* }
|
|
20
|
+
* ```
|
|
8
21
|
* @property {Object<string, string>} [headers] - Server request headers
|
|
9
22
|
* @property {string|((item: SunEditor.Module.Browser.File) => string)} [thumbnail] - Default thumbnail
|
|
10
23
|
*/
|
|
@@ -5,8 +5,29 @@ import { Browser } from '../../modules/contract';
|
|
|
5
5
|
* @typedef {Object} FileBrowserPluginOptions
|
|
6
6
|
* @property {Object<string, *>|Array<*>} [data] - Direct data without server calls (bypasses URL fetch).
|
|
7
7
|
* @property {string} [url] - Server request URL
|
|
8
|
+
* - The server must return a nested folder structure.
|
|
9
|
+
* - `_data`: array (inline) or string URL (lazy-loaded on folder click).
|
|
10
|
+
* - `"default": true` sets the initially selected folder.
|
|
11
|
+
* ```js
|
|
12
|
+
* {
|
|
13
|
+
* "result": {
|
|
14
|
+
* "root": {
|
|
15
|
+
* "name": "Root",
|
|
16
|
+
* "default": true,
|
|
17
|
+
* "_data": [
|
|
18
|
+
* { "src": "https://example.com/file1.pdf", "name": "file1.pdf" }
|
|
19
|
+
* ],
|
|
20
|
+
* "documents": {
|
|
21
|
+
* "name": "Documents",
|
|
22
|
+
* "_data": "https://api.example.com/files/documents"
|
|
23
|
+
* }
|
|
24
|
+
* }
|
|
25
|
+
* }
|
|
26
|
+
* }
|
|
27
|
+
* ```
|
|
8
28
|
* @property {Object<string, string>} [headers] - Server request headers
|
|
9
29
|
* @property {string|((item: SunEditor.Module.Browser.File) => string)} [thumbnail] - Default thumbnail URL or a function that returns a thumbnail URL per item.
|
|
30
|
+
* @property {number} [expand=1] - Initial folder expand depth. `1` expands the first level, `Infinity` expands all. Default: `1`.
|
|
10
31
|
* @property {Array<string>} [props] - Additional tag names
|
|
11
32
|
* ```js
|
|
12
33
|
* { url: '/api/files', headers: { Authorization: 'Bearer token' }, thumbnail: (item) => item.thumbUrl }
|
|
@@ -48,6 +69,7 @@ class FileBrowser extends PluginBrowser {
|
|
|
48
69
|
className: 'se-file-browser',
|
|
49
70
|
thumbnail: typeof pluginOptions.thumbnail === 'function' ? pluginOptions.thumbnail : (item) => thumbnail[item.type] || defaultThumbnail,
|
|
50
71
|
props: [...new Set((pluginOptions.props ?? []).concat(['frame']))],
|
|
72
|
+
expand: pluginOptions.expand,
|
|
51
73
|
});
|
|
52
74
|
}
|
|
53
75
|
|
|
@@ -5,6 +5,20 @@ import { Browser } from '../../modules/contract';
|
|
|
5
5
|
* @typedef {Object} FileGalleryPluginOptions
|
|
6
6
|
* @property {Array<SunEditor.Module.Browser.File>} [data] - Direct data without server calls
|
|
7
7
|
* @property {string} [url] - Server request URL
|
|
8
|
+
* - The server must return:
|
|
9
|
+
* ```js
|
|
10
|
+
* {
|
|
11
|
+
* "result": [
|
|
12
|
+
* {
|
|
13
|
+
* "src": "https://example.com/doc.pdf",
|
|
14
|
+
* "name": "doc.pdf",
|
|
15
|
+
* "thumbnail": "https://example.com/pdf_icon.png",
|
|
16
|
+
* "type": "file", // video, image ..[plugin name]
|
|
17
|
+
* "tag": ["document"]
|
|
18
|
+
* }
|
|
19
|
+
* ]
|
|
20
|
+
* }
|
|
21
|
+
* ```
|
|
8
22
|
* @property {Object<string, string>} [headers] - Server request headers
|
|
9
23
|
* @property {string|((item: SunEditor.Module.Browser.File) => string)} [thumbnail] - Default thumbnail
|
|
10
24
|
*/
|
|
@@ -5,6 +5,20 @@ import { Browser } from '../../modules/contract';
|
|
|
5
5
|
* @typedef ImageGalleryPluginOptions
|
|
6
6
|
* @property {Array<*>} [data] - Direct data without server calls
|
|
7
7
|
* @property {string} [url] - Server request URL
|
|
8
|
+
* - The server must return:
|
|
9
|
+
* ```js
|
|
10
|
+
* {
|
|
11
|
+
* "result": [
|
|
12
|
+
* {
|
|
13
|
+
* "src": "https://example.com/img.jpg",
|
|
14
|
+
* "name": "img.jpg",
|
|
15
|
+
* "thumbnail": "https://example.com/img_thumb.jpg",
|
|
16
|
+
* "alt": "description",
|
|
17
|
+
* "tag": ["nature"]
|
|
18
|
+
* }
|
|
19
|
+
* ]
|
|
20
|
+
* }
|
|
21
|
+
* ```
|
|
8
22
|
* @property {Object<string, string>} [headers] - Server request headers
|
|
9
23
|
*/
|
|
10
24
|
|
|
@@ -5,6 +5,20 @@ import { Browser } from '../../modules/contract';
|
|
|
5
5
|
* @typedef {Object} VideoGalleryPluginOptions
|
|
6
6
|
* @property {Array<SunEditor.Module.Browser.File>} [data] - Direct data without server calls
|
|
7
7
|
* @property {string} [url] - Server request URL
|
|
8
|
+
* - The server must return:
|
|
9
|
+
* ```js
|
|
10
|
+
* {
|
|
11
|
+
* "result": [
|
|
12
|
+
* {
|
|
13
|
+
* "src": "https://example.com/video.mp4",
|
|
14
|
+
* "name": "video.mp4",
|
|
15
|
+
* "thumbnail": "https://example.com/video_thumb.jpg",
|
|
16
|
+
* "frame": "video",
|
|
17
|
+
* "tag": ["tutorial"]
|
|
18
|
+
* }
|
|
19
|
+
* ]
|
|
20
|
+
* }
|
|
21
|
+
* ```
|
|
8
22
|
* @property {Object<string, string>} [headers] - Server request headers
|
|
9
23
|
* @property {string|((item: SunEditor.Module.Browser.File) => string)} [thumbnail] - Default thumbnail
|
|
10
24
|
*/
|
|
@@ -8,6 +8,18 @@ const { NO_EVENT } = env;
|
|
|
8
8
|
/**
|
|
9
9
|
* @typedef FileUploadPluginOptions
|
|
10
10
|
* @property {string} uploadUrl - Server request URL for file upload
|
|
11
|
+
* - The server must return:
|
|
12
|
+
* ```js
|
|
13
|
+
* {
|
|
14
|
+
* "result": [
|
|
15
|
+
* {
|
|
16
|
+
* "url": "https://example.com/file.pdf",
|
|
17
|
+
* "name": "file.pdf",
|
|
18
|
+
* "size": 1048576
|
|
19
|
+
* }
|
|
20
|
+
* ]
|
|
21
|
+
* }
|
|
22
|
+
* ```
|
|
11
23
|
* @property {Object<string, string>} [uploadHeaders] - Server request headers
|
|
12
24
|
* @property {number} [uploadSizeLimit] - Total upload size limit in bytes
|
|
13
25
|
* @property {number} [uploadSingleSizeLimit] - Single file size limit in bytes
|
|
@@ -11,7 +11,7 @@ const { debounce } = converter;
|
|
|
11
11
|
* @property {string} [triggerText="@"] - The character that triggers the mention list.
|
|
12
12
|
* @property {number} [limitSize=5] - The number of items to display in the mention list
|
|
13
13
|
* @property {number} [searchStartLength=0] - The number of characters to start searching for the mention list
|
|
14
|
-
* @property {number} [delayTime=
|
|
14
|
+
* @property {number} [delayTime=120] - The time to wait before displaying the mention list
|
|
15
15
|
* @property {Array<{key: string, name: string, url: string}>} [data] - Static mention data (used instead of API).
|
|
16
16
|
* ```js
|
|
17
17
|
* // data
|
|
@@ -54,7 +54,7 @@ class Mention extends PluginField {
|
|
|
54
54
|
this.triggerText = pluginOptions.triggerText || '@';
|
|
55
55
|
this.limitSize = pluginOptions.limitSize || 5;
|
|
56
56
|
this.searchStartLength = pluginOptions.searchStartLength || 0;
|
|
57
|
-
this.delayTime = typeof pluginOptions.delayTime === 'number' ? pluginOptions.delayTime :
|
|
57
|
+
this.delayTime = typeof pluginOptions.delayTime === 'number' ? pluginOptions.delayTime : 120;
|
|
58
58
|
this.directData = pluginOptions.data;
|
|
59
59
|
this.apiUrl = pluginOptions.apiUrl?.replace(/\s/g, '').replace(/\{limitSize\}/i, String(this.limitSize)) || '';
|
|
60
60
|
// members - api, caching
|
|
@@ -13,6 +13,18 @@ const { NO_EVENT, ON_OVER_COMPONENT } = env;
|
|
|
13
13
|
* @property {boolean} [createUrlInput] - Whether to create a URL input element.
|
|
14
14
|
* - Defaults to `true`. Always `true` when `createFileInput` is `false`.
|
|
15
15
|
* @property {string} [uploadUrl] - The URL to which files will be uploaded.
|
|
16
|
+
* - The server must return:
|
|
17
|
+
* ```js
|
|
18
|
+
* {
|
|
19
|
+
* "result": [
|
|
20
|
+
* {
|
|
21
|
+
* "url": "https://example.com/audio.mp3",
|
|
22
|
+
* "name": "audio.mp3",
|
|
23
|
+
* "size": 3145728
|
|
24
|
+
* }
|
|
25
|
+
* ]
|
|
26
|
+
* }
|
|
27
|
+
* ```
|
|
16
28
|
* @property {Object<string, string>} [uploadHeaders] - Headers to include in the file upload request.
|
|
17
29
|
* @property {number} [uploadSizeLimit] - The total upload size limit in bytes.
|
|
18
30
|
* @property {number} [uploadSingleSizeLimit] - The single file size limit in bytes.
|
|
@@ -11,6 +11,18 @@ const { _w, NO_EVENT } = env;
|
|
|
11
11
|
* @property {string} [defaultHeight] - The default height of the embed element (numeric value or with unit).
|
|
12
12
|
* @property {boolean} [percentageOnlySize=false] - Whether to allow only percentage-based sizing.
|
|
13
13
|
* @property {string} [uploadUrl] - The URL for file uploads.
|
|
14
|
+
* - The server must return:
|
|
15
|
+
* ```js
|
|
16
|
+
* {
|
|
17
|
+
* "result": [
|
|
18
|
+
* {
|
|
19
|
+
* "url": "https://example.com/embed.html",
|
|
20
|
+
* "name": "embed.html",
|
|
21
|
+
* "size": 2048
|
|
22
|
+
* }
|
|
23
|
+
* ]
|
|
24
|
+
* }
|
|
25
|
+
* ```
|
|
14
26
|
* @property {Object<string, string>} [uploadHeaders] - Headers to include in file upload requests.
|
|
15
27
|
* @property {number} [uploadSizeLimit] - The total file upload size limit in bytes.
|
|
16
28
|
* @property {number} [uploadSingleSizeLimit] - The single file upload size limit in bytes.
|
|
@@ -22,6 +22,18 @@ const { NO_EVENT } = env;
|
|
|
22
22
|
* @property {boolean} [createUrlInput] - Whether to create a URL input element for image insertion.
|
|
23
23
|
* - Defaults to `true`. Always `true` when `createFileInput` is `false`.
|
|
24
24
|
* @property {string} [uploadUrl] - The URL endpoint for image file uploads.
|
|
25
|
+
* - The server must return:
|
|
26
|
+
* ```js
|
|
27
|
+
* {
|
|
28
|
+
* "result": [
|
|
29
|
+
* {
|
|
30
|
+
* "url": "https://example.com/image.jpg",
|
|
31
|
+
* "name": "image.jpg",
|
|
32
|
+
* "size": 123456
|
|
33
|
+
* }
|
|
34
|
+
* ]
|
|
35
|
+
* }
|
|
36
|
+
* ```
|
|
25
37
|
* @property {Object<string, string>} [uploadHeaders] - Additional headers to include in the file upload request.
|
|
26
38
|
* ```js
|
|
27
39
|
* { uploadUrl: '/api/upload/image', uploadHeaders: { Authorization: 'Bearer token' } }
|
|
@@ -6,6 +6,18 @@ import { dom, numbers } from '../../helper';
|
|
|
6
6
|
/**
|
|
7
7
|
* @typedef {Object} LinkOptions
|
|
8
8
|
* @property {string} [uploadUrl] - The URL endpoint for file uploads.
|
|
9
|
+
* - The server must return:
|
|
10
|
+
* ```js
|
|
11
|
+
* {
|
|
12
|
+
* "result": [
|
|
13
|
+
* {
|
|
14
|
+
* "url": "https://example.com/file.pdf",
|
|
15
|
+
* "name": "file.pdf",
|
|
16
|
+
* "size": 1048576
|
|
17
|
+
* }
|
|
18
|
+
* ]
|
|
19
|
+
* }
|
|
20
|
+
* ```
|
|
9
21
|
* @property {Object<string, string>} [uploadHeaders] - Additional headers for file upload requests.
|
|
10
22
|
* @property {number} [uploadSizeLimit] - The total file upload size limit in bytes.
|
|
11
23
|
* @property {number} [uploadSingleSizeLimit] - The single file upload size limit in bytes.
|
|
@@ -19,6 +19,18 @@ import { CreateHTML_modal } from './render/video.html';
|
|
|
19
19
|
* @property {boolean} [createUrlInput] - Whether to create a URL input element for video embedding.
|
|
20
20
|
* - Defaults to `true`. Always `true` when `createFileInput` is `false`.
|
|
21
21
|
* @property {string} [uploadUrl] - The URL endpoint for video file uploads.
|
|
22
|
+
* - The server must return:
|
|
23
|
+
* ```js
|
|
24
|
+
* {
|
|
25
|
+
* "result": [
|
|
26
|
+
* {
|
|
27
|
+
* "url": "https://example.com/video.mp4",
|
|
28
|
+
* "name": "video.mp4",
|
|
29
|
+
* "size": 5242880
|
|
30
|
+
* }
|
|
31
|
+
* ]
|
|
32
|
+
* }
|
|
33
|
+
* ```
|
|
22
34
|
* @property {Object<string, string>} [uploadHeaders] - Additional headers to include in the video upload request.
|
|
23
35
|
* @property {number} [uploadSizeLimit] - The total upload size limit for videos in bytes.
|
|
24
36
|
* @property {number} [uploadSingleSizeLimit] - The single file upload size limit for videos in bytes.
|