@brandup/ui-richeditor 1.0.39 → 1.0.40
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/package.json +2 -2
- package/source/richeditor.ts +27 -1
- package/source/toolbar.ts +35 -1
package/package.json
CHANGED
|
@@ -27,12 +27,12 @@
|
|
|
27
27
|
"email": "it@brandup.online"
|
|
28
28
|
},
|
|
29
29
|
"license": "Apache-2.0",
|
|
30
|
-
"version": "1.0.
|
|
30
|
+
"version": "1.0.40",
|
|
31
31
|
"main": "source/index.ts",
|
|
32
32
|
"types": "source/index.ts",
|
|
33
33
|
"dependencies": {
|
|
34
34
|
"@brandup/ui": "^2.0.7",
|
|
35
|
-
"@brandup/ui-kit": "^1.0.
|
|
35
|
+
"@brandup/ui-kit": "^1.0.40"
|
|
36
36
|
},
|
|
37
37
|
"files": [
|
|
38
38
|
"source",
|
package/source/richeditor.ts
CHANGED
|
@@ -274,7 +274,21 @@ export default class RichEditor extends UIElementBound<RichEditorEvents> {
|
|
|
274
274
|
if (this.readonly) return;
|
|
275
275
|
|
|
276
276
|
const target = container ?? initiator.parentElement;
|
|
277
|
-
if (target)
|
|
277
|
+
if (!target) return;
|
|
278
|
+
|
|
279
|
+
formatToolbar.openEmoji(this, initiator, target);
|
|
280
|
+
|
|
281
|
+
// По кнопке могли нажать, ни разу не заходя в поле, — тогда каретки нет и вставлять символ
|
|
282
|
+
// некуда. Фокусировать вслепую нельзя: focus() сбросил бы уже стоящую каретку, и символ
|
|
283
|
+
// уехал бы не туда. Конец содержимого назначаем сами: фокус ставит каретку в начало,
|
|
284
|
+
// и дописанный к сообщению символ оказался бы перед текстом.
|
|
285
|
+
//
|
|
286
|
+
// Строго после открытия панели: фокус показывает тулбар, а придержать его панель успевает
|
|
287
|
+
// только когда открыта сама.
|
|
288
|
+
if (!this.selection) {
|
|
289
|
+
this.focus();
|
|
290
|
+
caretToEnd(this.editable, this.multiline);
|
|
291
|
+
}
|
|
278
292
|
}
|
|
279
293
|
|
|
280
294
|
getLength(): number {
|
|
@@ -908,6 +922,18 @@ export default class RichEditor extends UIElementBound<RichEditorEvents> {
|
|
|
908
922
|
|
|
909
923
|
insertFormattedText(this.editable, data, Array.from(this.__pendingFormats), selection);
|
|
910
924
|
|
|
925
|
+
// В пустом поле абзацев ещё нет, и каретке некуда встать внутрь — вставка из кода
|
|
926
|
+
// (смайлик, переменная) ложится прямо в редактор. Приводим к модели абзацев тем же
|
|
927
|
+
// способом, что и обработчик input при печати: он на программную вставку не приходит,
|
|
928
|
+
// а без <p> следующий Enter правит текст мимо абзаца.
|
|
929
|
+
if (this.multiline)
|
|
930
|
+
preserveCaret(this.editable, () => {
|
|
931
|
+
const before = this.editable.innerHTML;
|
|
932
|
+
ensureParagraphs(this.editable);
|
|
933
|
+
|
|
934
|
+
return this.editable.innerHTML !== before;
|
|
935
|
+
});
|
|
936
|
+
|
|
911
937
|
this.__emitChange();
|
|
912
938
|
formatToolbar.refresh();
|
|
913
939
|
}
|
package/source/toolbar.ts
CHANGED
|
@@ -85,6 +85,7 @@ class FormatToolbar {
|
|
|
85
85
|
private __actionButtons: Array<[EditorAction, HTMLButtonElement]> = [];
|
|
86
86
|
private __hostButtons: Array<[ToolbarButton, HTMLButtonElement]> = [];
|
|
87
87
|
private __active: ToolbarHost | null = null;
|
|
88
|
+
private __suspended: ToolbarHost | null = null; // показ придержан на время панели смайликов
|
|
88
89
|
private __toolsKey = "";
|
|
89
90
|
private __inContainer = false;
|
|
90
91
|
private readonly __reposition = () => this.__schedule("position");
|
|
@@ -140,6 +141,9 @@ class FormatToolbar {
|
|
|
140
141
|
|
|
141
142
|
/** Показать тулбар для редактора (на фокусе): перестроить кнопки, спозиционировать, показать. */
|
|
142
143
|
attach(host: ToolbarHost) {
|
|
144
|
+
// открыта панель смайликов у собственной кнопки хоста — показ отложен до её закрытия
|
|
145
|
+
if (this.__suspended === host) return;
|
|
146
|
+
|
|
143
147
|
const actions = host.editorActions ?? [];
|
|
144
148
|
const buttons = host.toolbarButtons ?? [];
|
|
145
149
|
if (!host.formatTools.length && !actions.length && !buttons.length) return;
|
|
@@ -177,6 +181,10 @@ class FormatToolbar {
|
|
|
177
181
|
|
|
178
182
|
/** Скрыть тулбар, если он обслуживает этот редактор (на blur/destroy). */
|
|
179
183
|
detach(host: ToolbarHost) {
|
|
184
|
+
// Придержанный показ снимаем первым делом, до закрытия панели: иначе её onClose поднял бы
|
|
185
|
+
// тулбар над редактором, который как раз уходит (в том числе разрушается).
|
|
186
|
+
if (this.__suspended === host) this.__suspended = null;
|
|
187
|
+
|
|
180
188
|
// панель смайликов могла быть открыта не для активного редактора (у своей кнопки хоста) —
|
|
181
189
|
// ссылку на него всё равно отпускаем, иначе уничтоженный редактор держится синглтоном
|
|
182
190
|
const emojiHost = this.__emojiHost === host;
|
|
@@ -191,6 +199,11 @@ class FormatToolbar {
|
|
|
191
199
|
|
|
192
200
|
if (this.__active !== host) return;
|
|
193
201
|
|
|
202
|
+
this.__hide();
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/** Убрать панель с экрана и отпустить активный редактор (позиционирование, подсветка). */
|
|
206
|
+
private __hide() {
|
|
194
207
|
this.__active = null;
|
|
195
208
|
this.__cancelScheduled();
|
|
196
209
|
if (this.__elem) this.__elem.classList.remove("visible");
|
|
@@ -335,7 +348,28 @@ class FormatToolbar {
|
|
|
335
348
|
this.__emojiHost = host;
|
|
336
349
|
this.__emojiInitiator = initiator;
|
|
337
350
|
|
|
338
|
-
|
|
351
|
+
// Панель у собственной кнопки хоста — самостоятельный слой, и показывать её вместе с тулбаром
|
|
352
|
+
// нельзя: это два всплывающих окна над одним полем. Тулбар придерживаем на всё время работы
|
|
353
|
+
// панели, а показанный убираем с экрана; вернётся он сам при её закрытии, если поле осталось
|
|
354
|
+
// в фокусе. Панель самого тулбара (container === __elem) — его собственный выпадающий слой,
|
|
355
|
+
// прятать её носителя незачем и нечем.
|
|
356
|
+
if (container !== this.__elem) {
|
|
357
|
+
this.__suspended = host;
|
|
358
|
+
if (this.__active === host) this.__hide();
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
PopupManager.open(this.__ensureEmojiPicker(container), { initiator, onClose: () => this.__resume() });
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
/** Панель смайликов закрылась — показываем придержанный тулбар, если редактор ещё в фокусе. */
|
|
365
|
+
private __resume() {
|
|
366
|
+
const host = this.__suspended;
|
|
367
|
+
if (!host) return;
|
|
368
|
+
|
|
369
|
+
this.__suspended = null;
|
|
370
|
+
|
|
371
|
+
// панель могло закрыть и движение мимо поля — тогда показывать нечего
|
|
372
|
+
if (host.editable.ownerDocument.activeElement === host.editable) this.attach(host);
|
|
339
373
|
}
|
|
340
374
|
|
|
341
375
|
private __toggleEmoji(initiator: HTMLButtonElement, e: MouseEvent) {
|