suneditor 3.0.6 → 3.1.1
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/event/rules/keydown.rule.enter.js +2 -2
- package/src/core/logic/dom/format.js +5 -1
- package/src/core/logic/dom/html.js +23 -1
- package/src/core/logic/dom/offset.js +24 -1
- package/src/core/logic/panel/menu.js +74 -3
- 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 +383 -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 +251 -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
|
@@ -1,251 +0,0 @@
|
|
|
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
|
-
* @typedef {Object} MentionPluginOptions
|
|
11
|
-
* @property {string} [triggerText="@"] - The character that triggers the mention list.
|
|
12
|
-
* @property {number} [limitSize=5] - The number of items to display in the mention list
|
|
13
|
-
* @property {number} [searchStartLength=0] - The number of characters to start searching for the mention list
|
|
14
|
-
* @property {number} [delayTime=120] - The time to wait before displaying the mention list
|
|
15
|
-
* @property {Array<{key: string, name: string, url: string}>} [data] - Static mention data (used instead of API).
|
|
16
|
-
* ```js
|
|
17
|
-
* // data
|
|
18
|
-
* [{ key: 'john', name: 'John Doe', url: '/users/john' }]
|
|
19
|
-
* ```
|
|
20
|
-
* @property {string} [apiUrl] - The URL to call the mention list
|
|
21
|
-
* @property {Object<string, string>} [apiHeaders] - The headers to send with the API call
|
|
22
|
-
* @property {boolean} [useCachingData=true] - Whether to cache the mention list data
|
|
23
|
-
* @property {boolean} [useCachingFieldData=true] - Whether to cache the mention list data in the field
|
|
24
|
-
*/
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* @class
|
|
28
|
-
* @description Mention Plugin
|
|
29
|
-
* - A plugin that provides a mention feature using `@` or a custom trigger character.
|
|
30
|
-
* - Displays a mention list when the trigger character is typed.
|
|
31
|
-
* - Supports fetching mention data from an API or a predefined data array.
|
|
32
|
-
* - Uses caching for optimized performance.
|
|
33
|
-
*/
|
|
34
|
-
class Mention extends PluginField {
|
|
35
|
-
static key = 'mention';
|
|
36
|
-
static className = '';
|
|
37
|
-
|
|
38
|
-
#lastAtPos = 0;
|
|
39
|
-
#anchorOffset = 0;
|
|
40
|
-
#anchorNode = null;
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* @constructor
|
|
44
|
-
* @param {SunEditor.Kernel} kernel - The Kernel instance
|
|
45
|
-
* @param {MentionPluginOptions} pluginOptions
|
|
46
|
-
*/
|
|
47
|
-
constructor(kernel, pluginOptions) {
|
|
48
|
-
super(kernel);
|
|
49
|
-
// plugin basic properties
|
|
50
|
-
this.title = this.$.lang.mention;
|
|
51
|
-
this.icon = 'mention';
|
|
52
|
-
|
|
53
|
-
// members
|
|
54
|
-
this.triggerText = pluginOptions.triggerText || '@';
|
|
55
|
-
this.limitSize = pluginOptions.limitSize || 5;
|
|
56
|
-
this.searchStartLength = pluginOptions.searchStartLength || 0;
|
|
57
|
-
this.delayTime = typeof pluginOptions.delayTime === 'number' ? pluginOptions.delayTime : 120;
|
|
58
|
-
this.directData = pluginOptions.data;
|
|
59
|
-
this.apiUrl = pluginOptions.apiUrl?.replace(/\s/g, '').replace(/\{limitSize\}/i, String(this.limitSize)) || '';
|
|
60
|
-
// members - api, caching
|
|
61
|
-
this.apiManager = new ApiManager(this, this.$, { headers: pluginOptions.apiHeaders });
|
|
62
|
-
this.cachingData = (pluginOptions.useCachingData ?? true) ? new Map() : null;
|
|
63
|
-
this.cachingFieldData = (pluginOptions.useCachingFieldData ?? true) ? [] : null;
|
|
64
|
-
|
|
65
|
-
// controller
|
|
66
|
-
const controllerEl = CreateHTML_controller();
|
|
67
|
-
this.selectMenu = new SelectMenu(this.$, { position: 'right-bottom', dir: 'ltr', closeMethod: () => this.controller.close() });
|
|
68
|
-
this.controller = new Controller(
|
|
69
|
-
this,
|
|
70
|
-
this.$,
|
|
71
|
-
controllerEl,
|
|
72
|
-
{
|
|
73
|
-
position: 'bottom',
|
|
74
|
-
initMethod: () => {
|
|
75
|
-
this.apiManager.cancel();
|
|
76
|
-
this.selectMenu.close();
|
|
77
|
-
},
|
|
78
|
-
},
|
|
79
|
-
null,
|
|
80
|
-
);
|
|
81
|
-
this.selectMenu.on(controllerEl.firstElementChild, this.#SelectMention.bind(this));
|
|
82
|
-
|
|
83
|
-
// onInput debounce
|
|
84
|
-
this.onInput = debounce(this.onInput.bind(this), this.delayTime);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* @hook Editor.EventManager
|
|
89
|
-
* @type {SunEditor.Hook.Event.OnInputAsync}
|
|
90
|
-
*/
|
|
91
|
-
async onInput() {
|
|
92
|
-
if (!this.directData) {
|
|
93
|
-
this.apiManager.cancel();
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
const sel = this.$.selection.get();
|
|
97
|
-
if (!sel.rangeCount) {
|
|
98
|
-
this.selectMenu.close();
|
|
99
|
-
return;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
const anchorNode = sel.anchorNode;
|
|
103
|
-
const anchorOffset = sel.anchorOffset;
|
|
104
|
-
const textBeforeCursor = anchorNode.textContent.substring(0, anchorOffset);
|
|
105
|
-
const lastAtPos = textBeforeCursor.lastIndexOf(this.triggerText);
|
|
106
|
-
|
|
107
|
-
if (lastAtPos > -1) {
|
|
108
|
-
const mentionQuery = textBeforeCursor.substring(lastAtPos + 1, anchorOffset);
|
|
109
|
-
const beforeText = textBeforeCursor[lastAtPos - 1]?.trim();
|
|
110
|
-
if (!/\s/.test(mentionQuery) && (!beforeText || dom.check.isZeroWidth(beforeText))) {
|
|
111
|
-
if (mentionQuery.length < this.searchStartLength) return;
|
|
112
|
-
|
|
113
|
-
const anchorParent = anchorNode.parentNode;
|
|
114
|
-
if (dom.check.isAnchor(anchorParent) && !anchorParent.getAttribute('data-se-mention')) {
|
|
115
|
-
return;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
try {
|
|
119
|
-
await this.#createMentionList(mentionQuery, anchorNode);
|
|
120
|
-
this.#lastAtPos = lastAtPos;
|
|
121
|
-
this.#anchorNode = anchorNode;
|
|
122
|
-
this.#anchorOffset = anchorOffset;
|
|
123
|
-
return;
|
|
124
|
-
} catch (error) {
|
|
125
|
-
console.warn('[SUNEDITOR.mention.api.file] ', error);
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
this.selectMenu.close();
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
/**
|
|
134
|
-
* @description Generates the mention list based on user input.
|
|
135
|
-
* - Fetches data from cache, direct data, or an API.
|
|
136
|
-
* - Creates and opens the mention dropdown.
|
|
137
|
-
* - Caches the fetched data for future use.
|
|
138
|
-
* @param {string} value - The mention query text.
|
|
139
|
-
* @param {Node} targetNode - The node where the mention is triggered.
|
|
140
|
-
* @returns {Promise<boolean>} - Returns `true` if the mention list is displayed, `false` otherwise.
|
|
141
|
-
*/
|
|
142
|
-
async #createMentionList(value, targetNode) {
|
|
143
|
-
const limit = this.limitSize;
|
|
144
|
-
const lowerValue = value.toLowerCase();
|
|
145
|
-
let response = null;
|
|
146
|
-
if (this.cachingData) {
|
|
147
|
-
response = this.cachingData.get(value);
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
if (!response) {
|
|
151
|
-
if (this.directData) {
|
|
152
|
-
response = this.directData.filter((item) => item.key.toLowerCase().startsWith(lowerValue)).slice(0, limit);
|
|
153
|
-
} else {
|
|
154
|
-
const xmlHttp = await this.apiManager.asyncCall({ method: 'GET', url: this.#createUrl(value) });
|
|
155
|
-
response = JSON.parse(xmlHttp.responseText);
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
if (this.cachingFieldData) {
|
|
160
|
-
const uniqueKeys = new Set();
|
|
161
|
-
response = this.cachingFieldData
|
|
162
|
-
.concat(response)
|
|
163
|
-
.filter(({ key }) => {
|
|
164
|
-
if (uniqueKeys.has(key)) return false;
|
|
165
|
-
uniqueKeys.add(key);
|
|
166
|
-
return key.toLowerCase().startsWith(lowerValue);
|
|
167
|
-
})
|
|
168
|
-
.slice(0, limit);
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
if (!response?.length) {
|
|
172
|
-
this.selectMenu.close();
|
|
173
|
-
return false;
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
const list = [];
|
|
177
|
-
const menus = [];
|
|
178
|
-
for (let i = 0, len = response.length, v; i < len; i++) {
|
|
179
|
-
v = response[i];
|
|
180
|
-
list.push(v);
|
|
181
|
-
menus.push(`<div class="se-mention-item"><span>${v.key}</span><span>${v.name}</span></div>`);
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
if (list.length === 0) {
|
|
185
|
-
this.selectMenu.close();
|
|
186
|
-
return false;
|
|
187
|
-
} else {
|
|
188
|
-
// controller open
|
|
189
|
-
this.controller.open(targetNode, null, { isWWTarget: true, initMethod: null, addOffset: null });
|
|
190
|
-
// select menu create
|
|
191
|
-
this.selectMenu.create(list, menus);
|
|
192
|
-
this.selectMenu.open();
|
|
193
|
-
this.selectMenu.setItem(0);
|
|
194
|
-
if (this.cachingData) this.cachingData.set(value, list);
|
|
195
|
-
return true;
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
/**
|
|
200
|
-
* @description Constructs the API request URL with the mention query.
|
|
201
|
-
* @param {string} key - The mention query text.
|
|
202
|
-
* @returns {string} - The formatted API request URL.
|
|
203
|
-
*/
|
|
204
|
-
#createUrl(key) {
|
|
205
|
-
return this.apiUrl.replace(/\{key\}/i, key);
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
/**
|
|
209
|
-
* @description Inserts a mention link into the editor when a user selects a mention from the list.
|
|
210
|
-
* @param {{ key: string, name: string, url: string }} item - The selected mention item.
|
|
211
|
-
* @returns {boolean} Returns `false` if insertion fails, otherwise completes execution.
|
|
212
|
-
*/
|
|
213
|
-
#SelectMention(item) {
|
|
214
|
-
if (!item) return false;
|
|
215
|
-
|
|
216
|
-
let oA = null;
|
|
217
|
-
const { key, name, url } = item;
|
|
218
|
-
const anchorParent = this.#anchorNode.parentNode;
|
|
219
|
-
|
|
220
|
-
if (dom.check.isAnchor(anchorParent)) {
|
|
221
|
-
oA = anchorParent;
|
|
222
|
-
oA.setAttribute('data-se-mention', key);
|
|
223
|
-
oA.setAttribute('href', url);
|
|
224
|
-
oA.setAttribute('title', name);
|
|
225
|
-
oA.textContent = this.triggerText + key;
|
|
226
|
-
} else {
|
|
227
|
-
this.$.selection.setRange(this.#anchorNode, this.#lastAtPos, this.#anchorNode, this.#anchorOffset);
|
|
228
|
-
oA = dom.utils.createElement('A', { 'data-se-mention': key, href: url, title: name, target: '_blank' }, this.triggerText + key);
|
|
229
|
-
if (!this.$.html.insertNode(oA, { afterNode: null, skipCharCount: false })) return false;
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
this.selectMenu.close();
|
|
233
|
-
|
|
234
|
-
const space = dom.utils.createTextNode('\u00A0');
|
|
235
|
-
oA.parentNode.insertBefore(space, oA.nextSibling);
|
|
236
|
-
this.$.selection.setRange(space, 1, space, 1);
|
|
237
|
-
|
|
238
|
-
if (this.cachingFieldData && !this.cachingFieldData.some((data) => data.key === item.key)) {
|
|
239
|
-
this.cachingFieldData.push(item);
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
/**
|
|
245
|
-
* @returns {HTMLElement}
|
|
246
|
-
*/
|
|
247
|
-
function CreateHTML_controller() {
|
|
248
|
-
return dom.utils.createElement('DIV', { class: 'se-controller se-empty-controller' }, '<div></div>');
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
export default Mention;
|
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
import type {} from '../../typedef';
|
|
2
|
-
export default Mention;
|
|
3
|
-
export type MentionPluginOptions = {
|
|
4
|
-
/**
|
|
5
|
-
* - The character that triggers the mention list.
|
|
6
|
-
*/
|
|
7
|
-
triggerText?: string;
|
|
8
|
-
/**
|
|
9
|
-
* - The number of items to display in the mention list
|
|
10
|
-
*/
|
|
11
|
-
limitSize?: number;
|
|
12
|
-
/**
|
|
13
|
-
* - The number of characters to start searching for the mention list
|
|
14
|
-
*/
|
|
15
|
-
searchStartLength?: number;
|
|
16
|
-
/**
|
|
17
|
-
* - The time to wait before displaying the mention list
|
|
18
|
-
*/
|
|
19
|
-
delayTime?: number;
|
|
20
|
-
/**
|
|
21
|
-
* - Static mention data (used instead of API).
|
|
22
|
-
* ```js
|
|
23
|
-
* // data
|
|
24
|
-
* [{ key: 'john', name: 'John Doe', url: '/users/john' }]
|
|
25
|
-
* ```
|
|
26
|
-
*/
|
|
27
|
-
data?: Array<{
|
|
28
|
-
key: string;
|
|
29
|
-
name: string;
|
|
30
|
-
url: string;
|
|
31
|
-
}>;
|
|
32
|
-
/**
|
|
33
|
-
* - The URL to call the mention list
|
|
34
|
-
*/
|
|
35
|
-
apiUrl?: string;
|
|
36
|
-
/**
|
|
37
|
-
* - The headers to send with the API call
|
|
38
|
-
*/
|
|
39
|
-
apiHeaders?: {
|
|
40
|
-
[x: string]: string;
|
|
41
|
-
};
|
|
42
|
-
/**
|
|
43
|
-
* - Whether to cache the mention list data
|
|
44
|
-
*/
|
|
45
|
-
useCachingData?: boolean;
|
|
46
|
-
/**
|
|
47
|
-
* - Whether to cache the mention list data in the field
|
|
48
|
-
*/
|
|
49
|
-
useCachingFieldData?: boolean;
|
|
50
|
-
};
|
|
51
|
-
/**
|
|
52
|
-
* @typedef {Object} MentionPluginOptions
|
|
53
|
-
* @property {string} [triggerText="@"] - The character that triggers the mention list.
|
|
54
|
-
* @property {number} [limitSize=5] - The number of items to display in the mention list
|
|
55
|
-
* @property {number} [searchStartLength=0] - The number of characters to start searching for the mention list
|
|
56
|
-
* @property {number} [delayTime=120] - The time to wait before displaying the mention list
|
|
57
|
-
* @property {Array<{key: string, name: string, url: string}>} [data] - Static mention data (used instead of API).
|
|
58
|
-
* ```js
|
|
59
|
-
* // data
|
|
60
|
-
* [{ key: 'john', name: 'John Doe', url: '/users/john' }]
|
|
61
|
-
* ```
|
|
62
|
-
* @property {string} [apiUrl] - The URL to call the mention list
|
|
63
|
-
* @property {Object<string, string>} [apiHeaders] - The headers to send with the API call
|
|
64
|
-
* @property {boolean} [useCachingData=true] - Whether to cache the mention list data
|
|
65
|
-
* @property {boolean} [useCachingFieldData=true] - Whether to cache the mention list data in the field
|
|
66
|
-
*/
|
|
67
|
-
/**
|
|
68
|
-
* @class
|
|
69
|
-
* @description Mention Plugin
|
|
70
|
-
* - A plugin that provides a mention feature using `@` or a custom trigger character.
|
|
71
|
-
* - Displays a mention list when the trigger character is typed.
|
|
72
|
-
* - Supports fetching mention data from an API or a predefined data array.
|
|
73
|
-
* - Uses caching for optimized performance.
|
|
74
|
-
*/
|
|
75
|
-
declare class Mention extends PluginField {
|
|
76
|
-
/**
|
|
77
|
-
* @constructor
|
|
78
|
-
* @param {SunEditor.Kernel} kernel - The Kernel instance
|
|
79
|
-
* @param {MentionPluginOptions} pluginOptions
|
|
80
|
-
*/
|
|
81
|
-
constructor(kernel: SunEditor.Kernel, pluginOptions: MentionPluginOptions);
|
|
82
|
-
title: any;
|
|
83
|
-
triggerText: string;
|
|
84
|
-
limitSize: number;
|
|
85
|
-
searchStartLength: number;
|
|
86
|
-
delayTime: number;
|
|
87
|
-
directData: {
|
|
88
|
-
key: string;
|
|
89
|
-
name: string;
|
|
90
|
-
url: string;
|
|
91
|
-
}[];
|
|
92
|
-
apiUrl: string;
|
|
93
|
-
apiManager: ApiManager;
|
|
94
|
-
cachingData: Map<any, any>;
|
|
95
|
-
cachingFieldData: any[];
|
|
96
|
-
selectMenu: SelectMenu;
|
|
97
|
-
controller: Controller;
|
|
98
|
-
onInput(params: SunEditor.HookParams.InputWithData): Promise<void>;
|
|
99
|
-
#private;
|
|
100
|
-
}
|
|
101
|
-
import { PluginField } from '../../interfaces';
|
|
102
|
-
import { Controller } from '../../modules/contract';
|
|
103
|
-
import { ApiManager } from '../../modules/manager';
|
|
104
|
-
import { SelectMenu } from '../../modules/ui';
|