suneditor 3.0.6 → 3.1.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/dist/suneditor.min.css +1 -1
- package/dist/suneditor.min.js +1 -1
- package/package.json +1 -1
- package/src/assets/suneditor.css +2 -2
- package/src/core/editor.js +20 -3
- package/src/core/event/eventOrchestrator.js +2 -1
- package/src/core/event/handlers/handler_ww_key.js +2 -2
- package/src/core/logic/dom/html.js +23 -1
- package/src/core/logic/dom/offset.js +24 -1
- package/src/core/logic/panel/viewer.js +6 -4
- package/src/core/logic/shell/shortcuts.js +1 -1
- package/src/core/schema/options.js +1 -1
- package/src/core/section/constructor.js +2 -2
- package/src/helper/index.js +3 -0
- package/src/helper/msOffice.js +849 -0
- package/src/interfaces/plugins.js +1 -1
- package/src/langs/ckb.js +1 -0
- package/src/langs/cs.js +1 -0
- package/src/langs/da.js +1 -0
- package/src/langs/de.js +1 -0
- package/src/langs/en.js +1 -1
- package/src/langs/es.js +1 -0
- package/src/langs/fa.js +1 -0
- package/src/langs/fr.js +1 -0
- package/src/langs/he.js +1 -0
- package/src/langs/hu.js +1 -0
- package/src/langs/it.js +1 -0
- package/src/langs/ja.js +1 -0
- package/src/langs/km.js +1 -0
- package/src/langs/ko.js +1 -0
- package/src/langs/lv.js +1 -0
- package/src/langs/nl.js +1 -0
- package/src/langs/pl.js +1 -0
- package/src/langs/pt_br.js +1 -0
- package/src/langs/ro.js +1 -0
- package/src/langs/ru.js +1 -0
- package/src/langs/se.js +1 -0
- package/src/langs/tr.js +1 -0
- package/src/langs/uk.js +1 -0
- package/src/langs/ur.js +1 -0
- package/src/langs/zh_cn.js +1 -0
- package/src/modules/contract/Browser.js +1 -0
- package/src/plugins/dropdown/layout.js +1 -1
- package/src/plugins/dropdown/template.js +2 -1
- package/src/plugins/field/autocomplete.js +346 -0
- package/src/plugins/index.js +3 -3
- package/src/typedef.js +1 -1
- package/types/core/logic/shell/shortcuts.d.ts +2 -2
- package/types/core/schema/options.d.ts +2 -2
- package/types/helper/index.d.ts +4 -0
- package/types/helper/msOffice.d.ts +11 -0
- package/types/interfaces/plugins.d.ts +1 -1
- package/types/langs/_Lang.d.ts +1 -2
- package/types/plugins/field/autocomplete.d.ts +177 -0
- package/types/plugins/index.d.ts +3 -3
- package/types/typedef.d.ts +1 -1
- package/src/plugins/field/mention.js +0 -251
- package/types/plugins/field/mention.d.ts +0 -104
|
@@ -171,7 +171,7 @@ export class PluginDropdownFree extends Base {
|
|
|
171
171
|
* These plugins typically respond to input events in the wysiwyg area
|
|
172
172
|
*
|
|
173
173
|
* **Commonly used hooks:**
|
|
174
|
-
* - `onInput()` - Responds to input events in the editor (See: `
|
|
174
|
+
* - `onInput()` - Responds to input events in the editor (See: `autocomplete` plugin)
|
|
175
175
|
* - Other event hooks can be used as needed (`onKeydown`, `onClick`, etc.)
|
|
176
176
|
*
|
|
177
177
|
* Child classes MAY optionally implement event hook methods
|
package/src/langs/ckb.js
CHANGED
package/src/langs/cs.js
CHANGED
package/src/langs/da.js
CHANGED
package/src/langs/de.js
CHANGED
package/src/langs/en.js
CHANGED
package/src/langs/es.js
CHANGED
package/src/langs/fa.js
CHANGED
package/src/langs/fr.js
CHANGED
package/src/langs/he.js
CHANGED
package/src/langs/hu.js
CHANGED
package/src/langs/it.js
CHANGED
package/src/langs/ja.js
CHANGED
package/src/langs/km.js
CHANGED
package/src/langs/ko.js
CHANGED
package/src/langs/lv.js
CHANGED
package/src/langs/nl.js
CHANGED
package/src/langs/pl.js
CHANGED
package/src/langs/pt_br.js
CHANGED
package/src/langs/ro.js
CHANGED
package/src/langs/ru.js
CHANGED
package/src/langs/se.js
CHANGED
package/src/langs/tr.js
CHANGED
package/src/langs/uk.js
CHANGED
package/src/langs/ur.js
CHANGED
package/src/langs/zh_cn.js
CHANGED
|
@@ -595,6 +595,7 @@ class Browser {
|
|
|
595
595
|
dom.utils.removeClass(this.side.querySelectorAll('.active'), 'active');
|
|
596
596
|
dom.utils.addClass([cmdTarget, dom.query.getParentElement(cmdTarget, '.se-menu-folder')], 'active');
|
|
597
597
|
this.tagArea.innerHTML = '';
|
|
598
|
+
this.selectedTags = [];
|
|
598
599
|
|
|
599
600
|
if (typeof data === 'string') {
|
|
600
601
|
this.#drawFileList(data, this.urlHeader, true);
|
|
@@ -75,7 +75,7 @@ function CreateHTML(layoutList) {
|
|
|
75
75
|
t = layoutList[i];
|
|
76
76
|
list += /*html*/ `
|
|
77
77
|
<li>
|
|
78
|
-
<button type="button" class="se-btn se-btn-list" data-value="${i}" title="${t.name}" aria-label="${t.name}">
|
|
78
|
+
<button type="button" class="se-btn se-btn-list" data-command="layout" data-value="${i}" title="${t.name}" aria-label="${t.name}">
|
|
79
79
|
${t.name}
|
|
80
80
|
</button>
|
|
81
81
|
</li>`;
|
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
import { PluginField } from '../../interfaces';
|
|
2
|
+
import { Controller } from '../../modules/contract';
|
|
3
|
+
import { ApiManager } from '../../modules/manager';
|
|
4
|
+
import { SelectMenu } from '../../modules/ui';
|
|
5
|
+
import { dom, converter } from '../../helper';
|
|
6
|
+
|
|
7
|
+
const { debounce } = converter;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @description Default render function for dropdown items.
|
|
11
|
+
* @param {{key: string, name?: string}} item - The data item.
|
|
12
|
+
* @returns {string} HTML string for the dropdown item.
|
|
13
|
+
*/
|
|
14
|
+
function defaultRenderItem(item) {
|
|
15
|
+
return `<div class="se-autocomplete-item"><span>${item.key}</span>${item.name ? `<span>${item.name}</span>` : ''}</div>`;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* @description Default select handler. Creates a span element with the trigger text + key.
|
|
20
|
+
* @param {{key: string}} item - The selected data item.
|
|
21
|
+
* @param {string} triggerText - The trigger character.
|
|
22
|
+
* @returns {{tag: string, attrs: Object, text: string}} Descriptor for element creation.
|
|
23
|
+
*/
|
|
24
|
+
function defaultOnSelect(item, triggerText) {
|
|
25
|
+
return {
|
|
26
|
+
tag: 'span',
|
|
27
|
+
attrs: { 'data-se-autocomplete': triggerText + item.key },
|
|
28
|
+
text: triggerText + item.key,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* @typedef {Object} AutocompleteTriggerConfig
|
|
34
|
+
* @property {Array<{key: string, [x: string]: any}>} [data] - Static data array. Each item must have a `key` field. Mutually exclusive with `apiUrl`.
|
|
35
|
+
* ```js
|
|
36
|
+
* // data
|
|
37
|
+
* [{ key: 'john', name: 'John Doe', url: '/users/john' }]
|
|
38
|
+
* ```
|
|
39
|
+
* @property {string} [apiUrl] - API endpoint URL. Supports `{key}` and `{limitSize}` placeholders. Mutually exclusive with `data`.
|
|
40
|
+
* @property {Object<string, string>} [apiHeaders] - HTTP headers for the API request.
|
|
41
|
+
* @property {function(Object, XMLHttpRequest): Array<{key: string}>} [transformResponse] - Transforms parsed JSON response into an array of data items.
|
|
42
|
+
* @property {number} [limitSize] - Override global `limitSize` for this trigger.
|
|
43
|
+
* @property {number} [searchStartLength] - Override global `searchStartLength` for this trigger.
|
|
44
|
+
* @property {boolean} [useCachingData] - Override global `useCachingData` for this trigger.
|
|
45
|
+
* @property {boolean} [useCachingFieldData] - Override global `useCachingFieldData` for this trigger.
|
|
46
|
+
* @property {function({key: string, [x: string]: any}, string): string} [renderItem] - Custom dropdown item renderer. Receives `(item, triggerText)`, returns HTML string.
|
|
47
|
+
* @property {function({key: string, [x: string]: any}, string): (string|Element|{tag: string, attrs?: Object, text?: string})} [onSelect] - Custom selection handler. Returns:
|
|
48
|
+
* - `string`: inserted as text node
|
|
49
|
+
* - `Element`: inserted as-is
|
|
50
|
+
* - `{tag, attrs, text}`: creates element via `dom.utils.createElement`
|
|
51
|
+
*/
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* @typedef {Object} AutocompletePluginOptions
|
|
55
|
+
* @property {number} [delayTime=120] - Debounce delay in ms before processing input.
|
|
56
|
+
* @property {number} [limitSize=5] - Maximum number of items to display in the dropdown.
|
|
57
|
+
* @property {number} [searchStartLength=0] - Minimum input length before triggering search.
|
|
58
|
+
* @property {boolean} [useCachingData=true] - Whether to cache query responses per trigger.
|
|
59
|
+
* @property {boolean} [useCachingFieldData=true] - Whether to cache selected items for priority display.
|
|
60
|
+
* @property {Object<string, AutocompleteTriggerConfig>} triggers - Per-trigger configurations keyed by trigger character.
|
|
61
|
+
* ```js
|
|
62
|
+
* // triggers
|
|
63
|
+
* {
|
|
64
|
+
* '@': { data: [...], renderItem: (item) => `...` },
|
|
65
|
+
* '#': { apiUrl: '/api/tags?q={key}' }
|
|
66
|
+
* }
|
|
67
|
+
* ```
|
|
68
|
+
*/
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* @class
|
|
72
|
+
* @description Autocomplete Plugin
|
|
73
|
+
* - A generic autocomplete plugin supporting multiple trigger characters.
|
|
74
|
+
* - Each trigger can have its own data source, rendering, and selection behavior.
|
|
75
|
+
* - Supports static data arrays and API-based data fetching.
|
|
76
|
+
* - Uses per-trigger caching for optimized performance.
|
|
77
|
+
*/
|
|
78
|
+
class Autocomplete extends PluginField {
|
|
79
|
+
static key = 'autocomplete';
|
|
80
|
+
static className = '';
|
|
81
|
+
|
|
82
|
+
#lastTriggerPos = 0;
|
|
83
|
+
#anchorOffset = 0;
|
|
84
|
+
#anchorNode = null;
|
|
85
|
+
#activeTrigger = null;
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* @constructor
|
|
89
|
+
* @param {SunEditor.Kernel} kernel - The Kernel instance
|
|
90
|
+
* @param {AutocompletePluginOptions} pluginOptions
|
|
91
|
+
*/
|
|
92
|
+
constructor(kernel, pluginOptions) {
|
|
93
|
+
super(kernel);
|
|
94
|
+
this.title = this.$.lang.autocomplete;
|
|
95
|
+
this.icon = 'autocomplete';
|
|
96
|
+
|
|
97
|
+
// global defaults
|
|
98
|
+
const limitSize = pluginOptions.limitSize || 5;
|
|
99
|
+
const searchStartLength = pluginOptions.searchStartLength || 0;
|
|
100
|
+
const delayTime = typeof pluginOptions.delayTime === 'number' ? pluginOptions.delayTime : 120;
|
|
101
|
+
const useCachingData = pluginOptions.useCachingData ?? true;
|
|
102
|
+
const useCachingFieldData = pluginOptions.useCachingFieldData ?? true;
|
|
103
|
+
|
|
104
|
+
// build trigger contexts
|
|
105
|
+
this.triggerContexts = new Map();
|
|
106
|
+
const triggers = pluginOptions.triggers || {};
|
|
107
|
+
for (const [triggerChar, config] of Object.entries(triggers)) {
|
|
108
|
+
const triggerLimit = config.limitSize ?? limitSize;
|
|
109
|
+
this.triggerContexts.set(triggerChar, {
|
|
110
|
+
trigger: triggerChar,
|
|
111
|
+
limitSize: triggerLimit,
|
|
112
|
+
searchStartLength: config.searchStartLength ?? searchStartLength,
|
|
113
|
+
directData: config.data || null,
|
|
114
|
+
apiUrl: config.apiUrl?.replace(/\s/g, '').replace(/\{limitSize\}/i, String(triggerLimit)) || '',
|
|
115
|
+
apiHeaders: config.apiHeaders || null,
|
|
116
|
+
transformResponse: config.transformResponse || null,
|
|
117
|
+
renderItem: config.renderItem || defaultRenderItem,
|
|
118
|
+
onSelect: config.onSelect || defaultOnSelect,
|
|
119
|
+
apiManager: config.apiUrl ? new ApiManager(this, this.$, { headers: config.apiHeaders }) : null,
|
|
120
|
+
cachingData: (config.useCachingData ?? useCachingData) ? new Map() : null,
|
|
121
|
+
cachingFieldData: (config.useCachingFieldData ?? useCachingFieldData) ? [] : null,
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// sort triggers by length descending (longest match first)
|
|
126
|
+
this.sortedTriggers = [...this.triggerContexts.keys()].sort((a, b) => b.length - a.length);
|
|
127
|
+
|
|
128
|
+
// controller
|
|
129
|
+
const controllerEl = CreateHTML_controller();
|
|
130
|
+
this.selectMenu = new SelectMenu(this.$, { position: 'right-bottom', dir: 'ltr', closeMethod: () => this.controller.close() });
|
|
131
|
+
this.controller = new Controller(
|
|
132
|
+
this,
|
|
133
|
+
this.$,
|
|
134
|
+
controllerEl,
|
|
135
|
+
{
|
|
136
|
+
position: 'bottom',
|
|
137
|
+
initMethod: () => {
|
|
138
|
+
this.#cancelActiveApi();
|
|
139
|
+
this.selectMenu.close();
|
|
140
|
+
},
|
|
141
|
+
},
|
|
142
|
+
null,
|
|
143
|
+
);
|
|
144
|
+
this.selectMenu.on(controllerEl.firstElementChild, this.#onSelectItem.bind(this));
|
|
145
|
+
|
|
146
|
+
// onInput debounce
|
|
147
|
+
this.onInput = debounce(this.onInput.bind(this), delayTime);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* @description Cancels the active trigger's in-flight API request.
|
|
152
|
+
*/
|
|
153
|
+
#cancelActiveApi() {
|
|
154
|
+
if (this.#activeTrigger?.apiManager) {
|
|
155
|
+
this.#activeTrigger.apiManager.cancel();
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* @hook Editor.EventManager
|
|
161
|
+
* @type {SunEditor.Hook.Event.OnInputAsync}
|
|
162
|
+
*/
|
|
163
|
+
async onInput() {
|
|
164
|
+
this.#cancelActiveApi();
|
|
165
|
+
|
|
166
|
+
const sel = this.$.selection.get();
|
|
167
|
+
if (!sel.rangeCount) {
|
|
168
|
+
this.selectMenu.close();
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const anchorNode = sel.anchorNode;
|
|
173
|
+
const anchorOffset = sel.anchorOffset;
|
|
174
|
+
const textBeforeCursor = anchorNode.textContent.substring(0, anchorOffset);
|
|
175
|
+
|
|
176
|
+
// find matching trigger (longest first)
|
|
177
|
+
for (const trigger of this.sortedTriggers) {
|
|
178
|
+
const lastPos = textBeforeCursor.lastIndexOf(trigger);
|
|
179
|
+
if (lastPos === -1) continue;
|
|
180
|
+
|
|
181
|
+
const query = textBeforeCursor.substring(lastPos + trigger.length, anchorOffset);
|
|
182
|
+
const beforeText = textBeforeCursor[lastPos - 1]?.trim();
|
|
183
|
+
|
|
184
|
+
if (!/\s/.test(query) && (!beforeText || dom.check.isZeroWidth(beforeText))) {
|
|
185
|
+
const ctx = this.triggerContexts.get(trigger);
|
|
186
|
+
if (query.length < ctx.searchStartLength) return;
|
|
187
|
+
|
|
188
|
+
const anchorParent = anchorNode.parentNode;
|
|
189
|
+
if (dom.check.isAnchor(anchorParent) && !anchorParent.getAttribute('data-se-autocomplete')) {
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
try {
|
|
194
|
+
this.#activeTrigger = ctx;
|
|
195
|
+
await this.#createList(ctx, query, anchorNode);
|
|
196
|
+
this.#lastTriggerPos = lastPos;
|
|
197
|
+
this.#anchorNode = anchorNode;
|
|
198
|
+
this.#anchorOffset = anchorOffset;
|
|
199
|
+
return;
|
|
200
|
+
} catch (error) {
|
|
201
|
+
console.warn('[SUNEDITOR.autocomplete.api] ', error);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
this.selectMenu.close();
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* @description Generates the autocomplete dropdown list.
|
|
213
|
+
* @param {Object} ctx - The trigger context.
|
|
214
|
+
* @param {string} value - The query text after the trigger.
|
|
215
|
+
* @param {Node} targetNode - The node where the trigger was detected.
|
|
216
|
+
* @returns {Promise<boolean>}
|
|
217
|
+
*/
|
|
218
|
+
async #createList(ctx, value, targetNode) {
|
|
219
|
+
const limit = ctx.limitSize;
|
|
220
|
+
const lowerValue = value.toLowerCase();
|
|
221
|
+
let response = null;
|
|
222
|
+
|
|
223
|
+
if (ctx.cachingData) {
|
|
224
|
+
response = ctx.cachingData.get(value);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
if (!response) {
|
|
228
|
+
if (ctx.directData) {
|
|
229
|
+
response = ctx.directData.filter((item) => item.key.toLowerCase().startsWith(lowerValue)).slice(0, limit);
|
|
230
|
+
} else {
|
|
231
|
+
const xmlHttp = await ctx.apiManager.asyncCall({ method: 'GET', url: this.#createUrl(ctx, value) });
|
|
232
|
+
const json = JSON.parse(xmlHttp.responseText);
|
|
233
|
+
response = ctx.transformResponse ? ctx.transformResponse(json, xmlHttp) : json;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
if (ctx.cachingFieldData) {
|
|
238
|
+
const uniqueKeys = new Set();
|
|
239
|
+
response = ctx.cachingFieldData
|
|
240
|
+
.concat(response)
|
|
241
|
+
.filter(({ key }) => {
|
|
242
|
+
if (uniqueKeys.has(key)) return false;
|
|
243
|
+
uniqueKeys.add(key);
|
|
244
|
+
return key.toLowerCase().startsWith(lowerValue);
|
|
245
|
+
})
|
|
246
|
+
.slice(0, limit);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
if (!response?.length) {
|
|
250
|
+
this.selectMenu.close();
|
|
251
|
+
return false;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
const list = [];
|
|
255
|
+
const menus = [];
|
|
256
|
+
for (let i = 0, len = response.length, v; i < len; i++) {
|
|
257
|
+
v = response[i];
|
|
258
|
+
list.push(v);
|
|
259
|
+
menus.push(ctx.renderItem(v, ctx.trigger));
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// controller open
|
|
263
|
+
this.controller.open(targetNode, null, { isWWTarget: true, initMethod: null, addOffset: null });
|
|
264
|
+
// select menu create
|
|
265
|
+
this.selectMenu.create(list, menus);
|
|
266
|
+
this.selectMenu.open();
|
|
267
|
+
this.selectMenu.setItem(0);
|
|
268
|
+
if (ctx.cachingData) ctx.cachingData.set(value, list);
|
|
269
|
+
return true;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* @description Constructs the API request URL with the query value.
|
|
274
|
+
* @param {Object} ctx - The trigger context.
|
|
275
|
+
* @param {string} key - The query text.
|
|
276
|
+
* @returns {string}
|
|
277
|
+
*/
|
|
278
|
+
#createUrl(ctx, key) {
|
|
279
|
+
return ctx.apiUrl.replace(/\{key\}/i, key);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* @description Handles item selection from the dropdown.
|
|
284
|
+
* @param {{key: string, [x: string]: any}} item - The selected data item.
|
|
285
|
+
* @returns {boolean}
|
|
286
|
+
*/
|
|
287
|
+
#onSelectItem(item) {
|
|
288
|
+
if (!item) return false;
|
|
289
|
+
|
|
290
|
+
const ctx = this.#activeTrigger;
|
|
291
|
+
if (!ctx) return false;
|
|
292
|
+
|
|
293
|
+
const result = ctx.onSelect(item, ctx.trigger);
|
|
294
|
+
let insertedNode = null;
|
|
295
|
+
|
|
296
|
+
const anchorParent = this.#anchorNode.parentNode;
|
|
297
|
+
|
|
298
|
+
if (typeof result === 'string') {
|
|
299
|
+
// plain text insertion
|
|
300
|
+
this.$.selection.setRange(this.#anchorNode, this.#lastTriggerPos, this.#anchorNode, this.#anchorOffset);
|
|
301
|
+
insertedNode = dom.utils.createTextNode(result);
|
|
302
|
+
if (!this.$.html.insertNode(insertedNode, { afterNode: null, skipCharCount: false })) return false;
|
|
303
|
+
} else {
|
|
304
|
+
// element insertion (descriptor or DOM element)
|
|
305
|
+
let element;
|
|
306
|
+
if (result?.nodeType) {
|
|
307
|
+
element = result;
|
|
308
|
+
} else if (result?.tag) {
|
|
309
|
+
element = dom.utils.createElement(result.tag.toUpperCase(), result.attrs || {}, result.text || '');
|
|
310
|
+
} else {
|
|
311
|
+
return false;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
if (anchorParent.getAttribute?.('data-se-autocomplete')) {
|
|
315
|
+
// update existing autocomplete element in-place
|
|
316
|
+
for (const attr of [...anchorParent.attributes]) anchorParent.removeAttribute(attr.name);
|
|
317
|
+
for (const attr of [...element.attributes]) anchorParent.setAttribute(attr.name, attr.value);
|
|
318
|
+
anchorParent.textContent = element.textContent;
|
|
319
|
+
insertedNode = anchorParent;
|
|
320
|
+
} else {
|
|
321
|
+
this.$.selection.setRange(this.#anchorNode, this.#lastTriggerPos, this.#anchorNode, this.#anchorOffset);
|
|
322
|
+
if (!this.$.html.insertNode(element, { afterNode: null, skipCharCount: false })) return false;
|
|
323
|
+
insertedNode = element;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
this.selectMenu.close();
|
|
328
|
+
|
|
329
|
+
const space = dom.utils.createTextNode('\u00A0');
|
|
330
|
+
insertedNode.parentNode.insertBefore(space, insertedNode.nextSibling);
|
|
331
|
+
this.$.selection.setRange(space, 1, space, 1);
|
|
332
|
+
|
|
333
|
+
if (ctx.cachingFieldData && !ctx.cachingFieldData.some((data) => data.key === item.key)) {
|
|
334
|
+
ctx.cachingFieldData.push(item);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* @returns {HTMLElement}
|
|
341
|
+
*/
|
|
342
|
+
function CreateHTML_controller() {
|
|
343
|
+
return dom.utils.createElement('DIV', { class: 'se-controller se-empty-controller' }, '<div></div>');
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
export default Autocomplete;
|
package/src/plugins/index.js
CHANGED
|
@@ -7,7 +7,7 @@ import list_bulleted from './command/list_bulleted';
|
|
|
7
7
|
import list_numbered from './command/list_numbered';
|
|
8
8
|
|
|
9
9
|
// field
|
|
10
|
-
import
|
|
10
|
+
import autocomplete from './field/autocomplete';
|
|
11
11
|
|
|
12
12
|
// dropdown
|
|
13
13
|
import align from './dropdown/align';
|
|
@@ -54,7 +54,7 @@ export {
|
|
|
54
54
|
fileUpload,
|
|
55
55
|
list_bulleted,
|
|
56
56
|
list_numbered,
|
|
57
|
-
|
|
57
|
+
autocomplete,
|
|
58
58
|
align,
|
|
59
59
|
font,
|
|
60
60
|
fontColor,
|
|
@@ -91,7 +91,7 @@ export default {
|
|
|
91
91
|
fileUpload,
|
|
92
92
|
list_bulleted,
|
|
93
93
|
list_numbered,
|
|
94
|
-
|
|
94
|
+
autocomplete,
|
|
95
95
|
align,
|
|
96
96
|
font,
|
|
97
97
|
fontColor,
|
package/src/typedef.js
CHANGED
|
@@ -268,7 +268,7 @@
|
|
|
268
268
|
* @typedef {"bold"|"underline"|"italic"|"strike"|"subscript"|"superscript"|"removeFormat"|"copyFormat"|"indent"|"outdent"|"fullScreen"|"showBlocks"|"codeView"|"markdownView"|"undo"|"redo"|"preview"|"print"|"copy"|"dir"|"dir_ltr"|"dir_rtl"|"finder"|"save"|"newDocument"|"selectAll"|"pageBreak"|"pageUp"|"pageDown"|"pageNavigator"} SunEditor.UI.ButtonCommand
|
|
269
269
|
*
|
|
270
270
|
* Plugin buttons available in the toolbar
|
|
271
|
-
* @typedef {"blockquote"|"codeBlock"|"exportPDF"|"fileUpload"|"list_bulleted"|"list_numbered"|"
|
|
271
|
+
* @typedef {"blockquote"|"codeBlock"|"exportPDF"|"fileUpload"|"list_bulleted"|"list_numbered"|"autocomplete"|"align"|"font"|"fontColor"|"backgroundColor"|"list"|"table"|"blockStyle"|"hr"|"layout"|"lineHeight"|"template"|"paragraphStyle"|"textStyle"|"link"|"image"|"video"|"audio"|"embed"|"math"|"drawing"|"imageGallery"|"videoGallery"|"audioGallery"|"fileGallery"|"fileBrowser"|"fontSize"|"pageNavigator"|"anchor"} SunEditor.UI.ButtonPlugin
|
|
272
272
|
*
|
|
273
273
|
* Single button item in the toolbar (includes special controls and custom strings)
|
|
274
274
|
* @typedef {SunEditor.UI.ButtonCommand|SunEditor.UI.ButtonPlugin|SunEditor.UI.ButtonSpecial|string} SunEditor.UI.ButtonItem
|