modern-text 1.9.0 → 1.9.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/index.cjs +1 -1
- package/dist/index.js +1 -1
- package/dist/index.mjs +2 -2
- package/dist/web-components/index.cjs +271 -230
- package/dist/web-components/index.d.cts +38 -32
- package/dist/web-components/index.d.mts +38 -32
- package/dist/web-components/index.d.ts +38 -32
- package/dist/web-components/index.mjs +271 -230
- package/package.json +8 -8
- package/dist/shared/{modern-text.y0a7DPYV.mjs → modern-text.B2pcGP3X.mjs} +1 -1
- package/dist/shared/{modern-text.XBrC1nBJ.cjs → modern-text.BmTeQ51D.cjs} +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { diffChars } from 'diff';
|
|
2
2
|
import { isCRLF, textContentToString, normalizeCRLF, normalizeTextContent, property } from 'modern-idoc';
|
|
3
|
-
import { T as Text } from '../shared/modern-text.
|
|
3
|
+
import { T as Text } from '../shared/modern-text.B2pcGP3X.mjs';
|
|
4
4
|
import 'modern-path2d';
|
|
5
5
|
import 'modern-font';
|
|
6
6
|
|
|
@@ -49,51 +49,147 @@ function parseHTML(html) {
|
|
|
49
49
|
}
|
|
50
50
|
const SUPPORTS_POINTER_EVENTS = "PointerEvent" in globalThis;
|
|
51
51
|
class TextEditor extends HTMLElement {
|
|
52
|
-
|
|
53
|
-
|
|
52
|
+
_prevSelection = [0, 0];
|
|
53
|
+
_composition = false;
|
|
54
|
+
get composition() {
|
|
55
|
+
return this._composition;
|
|
56
|
+
}
|
|
57
|
+
static _defined = false;
|
|
54
58
|
static register() {
|
|
55
|
-
|
|
59
|
+
if (!this._defined) {
|
|
60
|
+
this._defined = true;
|
|
61
|
+
customElements.define("text-editor", this);
|
|
62
|
+
}
|
|
56
63
|
}
|
|
57
64
|
_text = new Text();
|
|
58
65
|
get text() {
|
|
59
66
|
return this._text;
|
|
60
67
|
}
|
|
61
|
-
set text(
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
68
|
+
set text(newText) {
|
|
69
|
+
if (newText) {
|
|
70
|
+
this._text?.off("update", this._update);
|
|
71
|
+
newText.on("update", this._update);
|
|
72
|
+
this._text = newText;
|
|
73
|
+
this._setTextInput(this.getPlaintext());
|
|
74
|
+
this.text.update();
|
|
75
|
+
this._update();
|
|
76
|
+
}
|
|
67
77
|
}
|
|
68
78
|
_oldText = "";
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
79
|
+
_container;
|
|
80
|
+
_selection;
|
|
81
|
+
_textarea;
|
|
82
|
+
_cursor;
|
|
83
|
+
constructor() {
|
|
84
|
+
super();
|
|
85
|
+
const shadowRoot = this.attachShadow({ mode: "open" });
|
|
86
|
+
shadowRoot.appendChild(
|
|
87
|
+
parseHTML(`
|
|
88
|
+
<style>
|
|
89
|
+
:host {
|
|
90
|
+
position: absolute;
|
|
91
|
+
width: 0;
|
|
92
|
+
height: 0;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
.container {
|
|
96
|
+
position: absolute;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
textarea {
|
|
100
|
+
position: absolute;
|
|
101
|
+
opacity: 0;
|
|
102
|
+
caret-color: transparent;
|
|
103
|
+
left: 0;
|
|
104
|
+
top: 0;
|
|
105
|
+
width: 100%;
|
|
106
|
+
height: 100%;
|
|
107
|
+
padding: 0;
|
|
108
|
+
border: 0;
|
|
109
|
+
white-space: pre;
|
|
110
|
+
resize: none;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
.selection {
|
|
114
|
+
position: absolute;
|
|
115
|
+
left: 0;
|
|
116
|
+
top: 0;
|
|
117
|
+
pointer-events: none;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
.selection > * {
|
|
121
|
+
position: absolute;
|
|
122
|
+
left: 0;
|
|
123
|
+
top: 0;
|
|
124
|
+
background: rgba(var(--color, 0, 0, 0), 0.4);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
.cursor {
|
|
128
|
+
position: absolute;
|
|
129
|
+
left: 0;
|
|
130
|
+
top: 0;
|
|
131
|
+
pointer-events: none;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
.cursor.blink {
|
|
135
|
+
animation: cursor-blink 1s steps(2, start) infinite;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
@keyframes cursor-blink {
|
|
139
|
+
100% {
|
|
140
|
+
visibility: hidden;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
</style>
|
|
144
|
+
|
|
145
|
+
<div class="container">
|
|
146
|
+
<textarea></textarea>
|
|
147
|
+
<div class="selection"></div>
|
|
148
|
+
<div class="cursor blink"></div>
|
|
149
|
+
</div>
|
|
150
|
+
`)
|
|
151
|
+
);
|
|
152
|
+
this._container = shadowRoot.querySelector(".container");
|
|
153
|
+
this._selection = shadowRoot.querySelector(".selection");
|
|
154
|
+
this._textarea = shadowRoot.querySelector("textarea");
|
|
155
|
+
this._cursor = shadowRoot.querySelector(".cursor");
|
|
156
|
+
this._bindEventListeners();
|
|
157
|
+
this._update = this._update.bind(this);
|
|
158
|
+
}
|
|
159
|
+
connectedCallback() {
|
|
160
|
+
this._emit("init");
|
|
161
|
+
}
|
|
162
|
+
set(text) {
|
|
163
|
+
this.text = text;
|
|
164
|
+
}
|
|
165
|
+
onUpdateProperty(key, _newValue, _oldValue) {
|
|
74
166
|
switch (key) {
|
|
75
167
|
case "selection":
|
|
76
|
-
this.
|
|
168
|
+
this._selectionMinMax = {
|
|
77
169
|
min: Math.min(...this.selection),
|
|
78
170
|
max: Math.max(...this.selection)
|
|
79
171
|
};
|
|
80
172
|
break;
|
|
81
|
-
case "
|
|
173
|
+
case "_selectionMinMax":
|
|
82
174
|
case "chars":
|
|
83
|
-
this.
|
|
84
|
-
this.
|
|
175
|
+
this._updateSelectedChars();
|
|
176
|
+
this._updateCursorPosition();
|
|
85
177
|
break;
|
|
86
|
-
case "
|
|
87
|
-
case "
|
|
88
|
-
this.
|
|
178
|
+
case "_showCursor":
|
|
179
|
+
case "_cursorPosition":
|
|
180
|
+
this._renderCursor();
|
|
89
181
|
break;
|
|
90
|
-
case "
|
|
91
|
-
this.
|
|
92
|
-
this.
|
|
182
|
+
case "_selectedChars":
|
|
183
|
+
this._renderCursor();
|
|
184
|
+
this._renderSelectRange();
|
|
185
|
+
break;
|
|
186
|
+
case "left":
|
|
187
|
+
case "top":
|
|
188
|
+
this._update();
|
|
93
189
|
break;
|
|
94
190
|
}
|
|
95
191
|
}
|
|
96
|
-
|
|
192
|
+
_updateChars() {
|
|
97
193
|
const paragraphs = [];
|
|
98
194
|
this.text.paragraphs.forEach((p, paragraphIndex) => {
|
|
99
195
|
p.fragments.forEach((f, fragmentIndex) => {
|
|
@@ -163,21 +259,21 @@ class TextEditor extends HTMLElement {
|
|
|
163
259
|
if (chars[chars.length - 1]) {
|
|
164
260
|
chars[chars.length - 1].isLast = true;
|
|
165
261
|
}
|
|
166
|
-
this.
|
|
262
|
+
this._chars = chars;
|
|
167
263
|
}
|
|
168
|
-
|
|
169
|
-
this.
|
|
170
|
-
return index >= this.
|
|
264
|
+
_updateSelectedChars() {
|
|
265
|
+
this._selectedChars = this._chars.filter((_char, index) => {
|
|
266
|
+
return index >= this._selectionMinMax.min && index < this._selectionMinMax.max;
|
|
171
267
|
});
|
|
172
|
-
this.
|
|
173
|
-
this.
|
|
174
|
-
this.
|
|
268
|
+
this._emit("selected", [
|
|
269
|
+
this._chars[this._selectionMinMax.min],
|
|
270
|
+
this._chars[this._selectionMinMax.max]
|
|
175
271
|
]);
|
|
176
272
|
}
|
|
177
|
-
|
|
273
|
+
_updateCursorPosition() {
|
|
178
274
|
let left = 0;
|
|
179
275
|
let top = 0;
|
|
180
|
-
const char = this.
|
|
276
|
+
const char = this._chars[this._selectionMinMax.min];
|
|
181
277
|
if (char?.isLastSelected) {
|
|
182
278
|
if (this.text.isVertical) {
|
|
183
279
|
top += char?.height ?? 0;
|
|
@@ -187,7 +283,7 @@ class TextEditor extends HTMLElement {
|
|
|
187
283
|
}
|
|
188
284
|
left += char?.left ?? 0;
|
|
189
285
|
top += char?.top ?? 0;
|
|
190
|
-
this.
|
|
286
|
+
this._cursorPosition = {
|
|
191
287
|
color: char?.color,
|
|
192
288
|
left,
|
|
193
289
|
top,
|
|
@@ -195,88 +291,14 @@ class TextEditor extends HTMLElement {
|
|
|
195
291
|
width: char?.width ?? 0
|
|
196
292
|
};
|
|
197
293
|
}
|
|
198
|
-
constructor() {
|
|
199
|
-
super();
|
|
200
|
-
const shadowRoot = this.attachShadow({ mode: "open" });
|
|
201
|
-
shadowRoot.appendChild(
|
|
202
|
-
parseHTML(`
|
|
203
|
-
<style>
|
|
204
|
-
:host {
|
|
205
|
-
position: absolute;
|
|
206
|
-
width: 0;
|
|
207
|
-
height: 0;
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
.container {
|
|
211
|
-
position: absolute;
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
textarea {
|
|
215
|
-
position: absolute;
|
|
216
|
-
opacity: 0;
|
|
217
|
-
caret-color: transparent;
|
|
218
|
-
left: 0;
|
|
219
|
-
top: 0;
|
|
220
|
-
width: 100%;
|
|
221
|
-
height: 100%;
|
|
222
|
-
padding: 0;
|
|
223
|
-
border: 0;
|
|
224
|
-
white-space: pre;
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
.selection {
|
|
228
|
-
position: absolute;
|
|
229
|
-
left: 0;
|
|
230
|
-
top: 0;
|
|
231
|
-
pointer-events: none;
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
.selection > * {
|
|
235
|
-
position: absolute;
|
|
236
|
-
left: 0;
|
|
237
|
-
top: 0;
|
|
238
|
-
background: rgba(var(--color, 0, 0, 0), 0.4);
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
.cursor {
|
|
242
|
-
position: absolute;
|
|
243
|
-
left: 0;
|
|
244
|
-
top: 0;
|
|
245
|
-
pointer-events: none;
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
.cursor.blink {
|
|
249
|
-
animation: cursor-blink 1s steps(2, start) infinite;
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
@keyframes cursor-blink {
|
|
253
|
-
100% {
|
|
254
|
-
display: none;
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
</style>
|
|
258
|
-
|
|
259
|
-
<div class="container">
|
|
260
|
-
<textarea autofocus></textarea>
|
|
261
|
-
<div class="selection"></div>
|
|
262
|
-
<div class="cursor blink"></div>
|
|
263
|
-
</div>
|
|
264
|
-
`)
|
|
265
|
-
);
|
|
266
|
-
this.$container = shadowRoot.querySelector(".container");
|
|
267
|
-
this.$selection = shadowRoot.querySelector(".selection");
|
|
268
|
-
this.$textarea = shadowRoot.querySelector("textarea");
|
|
269
|
-
this.$cursor = shadowRoot.querySelector(".cursor");
|
|
270
|
-
this.bindEventListeners();
|
|
271
|
-
}
|
|
272
294
|
getPlaintext() {
|
|
273
295
|
return textContentToString(
|
|
274
|
-
this.
|
|
296
|
+
this._getNewContent(
|
|
275
297
|
this.text.content
|
|
276
298
|
)
|
|
277
299
|
);
|
|
278
300
|
}
|
|
279
|
-
|
|
301
|
+
_getNewContent(content, newString = textContentToString(content), oldString = newString) {
|
|
280
302
|
newString = normalizeCRLF(newString);
|
|
281
303
|
newString = newString.replace(emojiRE, (emoji) => {
|
|
282
304
|
if (Array.from(emoji).length > 1) {
|
|
@@ -344,41 +366,41 @@ class TextEditor extends HTMLElement {
|
|
|
344
366
|
});
|
|
345
367
|
return newContents;
|
|
346
368
|
}
|
|
347
|
-
|
|
348
|
-
this
|
|
369
|
+
_setTextInput(newText) {
|
|
370
|
+
this._textarea.value = newText;
|
|
349
371
|
this._oldText = newText;
|
|
350
372
|
}
|
|
351
|
-
|
|
352
|
-
const newText = this
|
|
353
|
-
this.text.content = this.
|
|
373
|
+
_onInput() {
|
|
374
|
+
const newText = this._textarea.value;
|
|
375
|
+
this.text.content = this._getNewContent(
|
|
354
376
|
this.text.content,
|
|
355
377
|
newText,
|
|
356
378
|
this._oldText
|
|
357
379
|
);
|
|
358
380
|
this._oldText = newText;
|
|
359
381
|
this.text.update();
|
|
360
|
-
this.
|
|
382
|
+
this._emit("update");
|
|
361
383
|
}
|
|
362
384
|
_timer;
|
|
363
|
-
|
|
385
|
+
_onKeydown(e) {
|
|
364
386
|
e.stopPropagation();
|
|
365
387
|
switch (e.key) {
|
|
366
388
|
case "Escape":
|
|
367
|
-
return this
|
|
389
|
+
return this._textarea.blur();
|
|
368
390
|
}
|
|
369
|
-
this.
|
|
370
|
-
setTimeout(() => this.
|
|
371
|
-
setTimeout(() => this.
|
|
391
|
+
this._updateSelectionByDom();
|
|
392
|
+
setTimeout(() => this._updateSelectionByDom(), 0);
|
|
393
|
+
setTimeout(() => this._updateSelectionByDom(), 100);
|
|
372
394
|
}
|
|
373
|
-
|
|
374
|
-
this.
|
|
395
|
+
_onFocus() {
|
|
396
|
+
this._showCursor = true;
|
|
375
397
|
}
|
|
376
|
-
|
|
377
|
-
this.
|
|
398
|
+
_onBlur() {
|
|
399
|
+
this._showCursor = false;
|
|
378
400
|
}
|
|
379
|
-
|
|
401
|
+
_findNearest(options) {
|
|
380
402
|
const { x, y, xWeight = 1, yWeight = 1 } = options;
|
|
381
|
-
const char = this.
|
|
403
|
+
const char = this._chars.reduce(
|
|
382
404
|
(prev, current, index) => {
|
|
383
405
|
const diff = Math.abs(current.left + current.width / 2 - x) * xWeight + Math.abs(current.top + current.height / 2 - y) * yWeight;
|
|
384
406
|
if (diff < prev.diff) {
|
|
@@ -405,120 +427,133 @@ class TextEditor extends HTMLElement {
|
|
|
405
427
|
}
|
|
406
428
|
return -1;
|
|
407
429
|
}
|
|
408
|
-
|
|
409
|
-
if (this.
|
|
410
|
-
this.selection = this.
|
|
430
|
+
_updateSelectionByDom() {
|
|
431
|
+
if (this._composition) {
|
|
432
|
+
this.selection = this._prevSelection;
|
|
411
433
|
} else {
|
|
412
434
|
let count = 0;
|
|
413
435
|
const _selection = [];
|
|
414
|
-
this.
|
|
415
|
-
if (count <= this
|
|
436
|
+
this._chars.forEach((char, index) => {
|
|
437
|
+
if (count <= this._textarea.selectionStart) {
|
|
416
438
|
_selection[0] = index;
|
|
417
|
-
} else if (count <= this
|
|
439
|
+
} else if (count <= this._textarea.selectionEnd) {
|
|
418
440
|
_selection[1] = index;
|
|
419
441
|
}
|
|
420
442
|
count += char.content.length;
|
|
421
443
|
});
|
|
422
444
|
const oldSelection = this.selection;
|
|
423
445
|
this.selection = _selection;
|
|
424
|
-
this.
|
|
446
|
+
this._prevSelection = oldSelection;
|
|
425
447
|
}
|
|
426
448
|
}
|
|
427
|
-
|
|
449
|
+
_updateDomSelection() {
|
|
428
450
|
let start = 0;
|
|
429
451
|
let end = 0;
|
|
430
|
-
this.
|
|
431
|
-
if (index < this.
|
|
452
|
+
this._chars.forEach((char, index) => {
|
|
453
|
+
if (index < this._selectionMinMax.min) {
|
|
432
454
|
start += char.content.length;
|
|
433
455
|
end = start;
|
|
434
|
-
} else if (index < this.
|
|
456
|
+
} else if (index < this._selectionMinMax.max) {
|
|
435
457
|
end += char.content.length;
|
|
436
458
|
}
|
|
437
459
|
});
|
|
438
|
-
this
|
|
439
|
-
this
|
|
460
|
+
this._textarea.selectionStart = start;
|
|
461
|
+
this._textarea.selectionEnd = end;
|
|
440
462
|
}
|
|
441
463
|
_update() {
|
|
442
|
-
this.
|
|
464
|
+
this._updateChars();
|
|
443
465
|
const host = this.shadowRoot.host;
|
|
466
|
+
host.style.left = `${this.left}px`;
|
|
467
|
+
host.style.top = `${this.top}px`;
|
|
444
468
|
host.style.width = `${this.text.boundingBox.width}px`;
|
|
445
469
|
host.style.height = `${this.text.boundingBox.height}px`;
|
|
446
|
-
this
|
|
447
|
-
this
|
|
448
|
-
this
|
|
449
|
-
this
|
|
450
|
-
this
|
|
451
|
-
this
|
|
452
|
-
this.
|
|
453
|
-
this.
|
|
454
|
-
}
|
|
455
|
-
|
|
456
|
-
this
|
|
457
|
-
this
|
|
458
|
-
this
|
|
459
|
-
this
|
|
460
|
-
this
|
|
461
|
-
this
|
|
470
|
+
this._container.style.left = `${this.text.glyphBox.left}px`;
|
|
471
|
+
this._container.style.top = `${this.text.glyphBox.top}px`;
|
|
472
|
+
this._container.style.width = `${this.text.glyphBox.width}px`;
|
|
473
|
+
this._container.style.height = `${this.text.glyphBox.height}px`;
|
|
474
|
+
this._textarea.style.fontSize = `${this.text.computedStyle.fontSize}px`;
|
|
475
|
+
this._textarea.style.writingMode = this.text.computedStyle.writingMode;
|
|
476
|
+
this._renderSelectRange();
|
|
477
|
+
this._renderCursor();
|
|
478
|
+
}
|
|
479
|
+
_bindEventListeners() {
|
|
480
|
+
this._textarea.addEventListener("compositionstart", () => this._composition = true);
|
|
481
|
+
this._textarea.addEventListener("compositionend", () => this._composition = false);
|
|
482
|
+
this._textarea.addEventListener("keydown", this._onKeydown.bind(this));
|
|
483
|
+
this._textarea.addEventListener("input", this._onInput.bind(this));
|
|
484
|
+
this._textarea.addEventListener("focus", this._onFocus.bind(this));
|
|
485
|
+
this._textarea.addEventListener("blur", this._onBlur.bind(this));
|
|
462
486
|
if (SUPPORTS_POINTER_EVENTS) {
|
|
463
|
-
this
|
|
487
|
+
this._textarea.addEventListener("pointerdown", this.pointerdown.bind(this));
|
|
464
488
|
} else {
|
|
465
|
-
this
|
|
489
|
+
this._textarea.addEventListener("mousedown", this.pointerdown.bind(this));
|
|
466
490
|
}
|
|
467
491
|
["keyup", "mouseup", "input", "paste", "cut"].forEach((key) => {
|
|
468
|
-
this
|
|
469
|
-
this.
|
|
492
|
+
this._textarea.addEventListener(key, () => {
|
|
493
|
+
this._updateSelectionByDom();
|
|
470
494
|
});
|
|
471
495
|
});
|
|
472
496
|
}
|
|
473
|
-
|
|
497
|
+
pointerdown(e) {
|
|
498
|
+
if (e && e.button !== 0) {
|
|
499
|
+
e.preventDefault();
|
|
500
|
+
e.stopPropagation();
|
|
501
|
+
return false;
|
|
502
|
+
}
|
|
474
503
|
const host = this.shadowRoot.host;
|
|
475
504
|
const rect = host.getBoundingClientRect();
|
|
476
505
|
const originalWidth = host.offsetWidth;
|
|
477
506
|
const originalHeight = host.offsetHeight;
|
|
478
507
|
const scaleX = rect.width / originalWidth;
|
|
479
508
|
const scaleY = rect.height / originalHeight;
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
if (
|
|
483
|
-
|
|
509
|
+
let offsetX = 0;
|
|
510
|
+
let offsetY = 0;
|
|
511
|
+
if (e) {
|
|
512
|
+
offsetX = (e.clientX - rect.left) / scaleX;
|
|
513
|
+
offsetY = (e.clientY - rect.top) / scaleY;
|
|
514
|
+
if (!(offsetX > this.text.glyphBox.left && offsetX < this.text.glyphBox.left + this.text.glyphBox.width && offsetY > this.text.glyphBox.top && offsetY < this.text.glyphBox.top + this.text.glyphBox.height)) {
|
|
515
|
+
return false;
|
|
516
|
+
}
|
|
517
|
+
e.preventDefault();
|
|
518
|
+
e.stopPropagation();
|
|
484
519
|
}
|
|
485
|
-
|
|
486
|
-
e.stopPropagation();
|
|
487
|
-
const index = this.findNearest({ x: offsetX, y: offsetY });
|
|
520
|
+
const index = this._findNearest({ x: offsetX, y: offsetY });
|
|
488
521
|
this.selection = [index, index];
|
|
489
|
-
this.
|
|
490
|
-
|
|
491
|
-
const
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
this.selection[
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
522
|
+
this._updateDomSelection();
|
|
523
|
+
if (e && !["mouseup", "pointerup"].includes(e.type)) {
|
|
524
|
+
const onMove = (e2) => {
|
|
525
|
+
const offsetX2 = (e2.clientX - rect.left) / scaleX;
|
|
526
|
+
const offsetY2 = (e2.clientY - rect.top) / scaleY;
|
|
527
|
+
this.selection = [
|
|
528
|
+
this.selection[0],
|
|
529
|
+
this._findNearest({ x: offsetX2, y: offsetY2 })
|
|
530
|
+
];
|
|
531
|
+
this._updateDomSelection();
|
|
532
|
+
};
|
|
533
|
+
const onUp = () => {
|
|
534
|
+
if (SUPPORTS_POINTER_EVENTS) {
|
|
535
|
+
document.removeEventListener("pointermove", onMove);
|
|
536
|
+
document.removeEventListener("pointerup", onUp);
|
|
537
|
+
} else {
|
|
538
|
+
document.removeEventListener("mousemove", onMove);
|
|
539
|
+
document.removeEventListener("mouseup", onUp);
|
|
540
|
+
}
|
|
541
|
+
};
|
|
500
542
|
if (SUPPORTS_POINTER_EVENTS) {
|
|
501
|
-
document.
|
|
502
|
-
document.
|
|
543
|
+
document.addEventListener("pointermove", onMove);
|
|
544
|
+
document.addEventListener("pointerup", onUp);
|
|
503
545
|
} else {
|
|
504
|
-
document.
|
|
505
|
-
document.
|
|
546
|
+
document.addEventListener("mousemove", onMove);
|
|
547
|
+
document.addEventListener("mouseup", onUp);
|
|
506
548
|
}
|
|
507
|
-
};
|
|
508
|
-
if (SUPPORTS_POINTER_EVENTS) {
|
|
509
|
-
document.addEventListener("pointermove", onMove);
|
|
510
|
-
document.addEventListener("pointerup", onUp);
|
|
511
|
-
} else {
|
|
512
|
-
document.addEventListener("mousemove", onMove);
|
|
513
|
-
document.addEventListener("mouseup", onUp);
|
|
514
549
|
}
|
|
515
|
-
this
|
|
550
|
+
this._textarea.focus();
|
|
516
551
|
return true;
|
|
517
552
|
}
|
|
518
553
|
attributeChangedCallback(name, _oldValue, newValue) {
|
|
519
554
|
this[name] = newValue;
|
|
520
555
|
}
|
|
521
|
-
|
|
556
|
+
_emit(type, detail) {
|
|
522
557
|
return this.dispatchEvent(
|
|
523
558
|
new CustomEvent(type, {
|
|
524
559
|
bubbles: true,
|
|
@@ -528,11 +563,11 @@ class TextEditor extends HTMLElement {
|
|
|
528
563
|
})
|
|
529
564
|
);
|
|
530
565
|
}
|
|
531
|
-
|
|
532
|
-
if (this.selection[0] !== this.
|
|
566
|
+
_renderSelectRange() {
|
|
567
|
+
if (this.selection[0] !== this._prevSelection[0] || this.selection[1] !== this._prevSelection[1]) {
|
|
533
568
|
const isVertical = this.text.isVertical;
|
|
534
569
|
const boxesGroupsMap = {};
|
|
535
|
-
this.
|
|
570
|
+
this._selectedChars.forEach((char) => {
|
|
536
571
|
if (char.isLastSelected) {
|
|
537
572
|
return;
|
|
538
573
|
}
|
|
@@ -548,18 +583,19 @@ class TextEditor extends HTMLElement {
|
|
|
548
583
|
});
|
|
549
584
|
});
|
|
550
585
|
const boxesGroups = Object.values(boxesGroupsMap);
|
|
551
|
-
const sourceLen = this
|
|
586
|
+
const sourceLen = this._selection.children.length;
|
|
552
587
|
const targetLen = boxesGroups.length;
|
|
553
588
|
const len = Math.max(sourceLen, targetLen);
|
|
589
|
+
const deleted = [];
|
|
554
590
|
for (let i = 0; i < len; i++) {
|
|
555
|
-
let element = this
|
|
591
|
+
let element = this._selection.children.item(i);
|
|
556
592
|
const boxes = boxesGroups[i];
|
|
557
593
|
if (!boxes) {
|
|
558
|
-
element
|
|
594
|
+
deleted.push(element);
|
|
559
595
|
continue;
|
|
560
596
|
} else if (!element) {
|
|
561
597
|
element = document.createElement("div");
|
|
562
|
-
this
|
|
598
|
+
this._selection.append(element);
|
|
563
599
|
}
|
|
564
600
|
const min = {
|
|
565
601
|
x: Math.min(...boxes.map((v) => v.x)),
|
|
@@ -573,46 +609,51 @@ class TextEditor extends HTMLElement {
|
|
|
573
609
|
element.style.height = `${max.y - min.y}px`;
|
|
574
610
|
element.style.transform = `translate(${min.x}px, ${min.y}px)`;
|
|
575
611
|
}
|
|
612
|
+
deleted.forEach((el) => el?.remove());
|
|
576
613
|
}
|
|
577
614
|
}
|
|
578
|
-
|
|
579
|
-
if (this.
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
clearTimeout(this._timer);
|
|
594
|
-
}
|
|
595
|
-
this._timer = setTimeout(() => this.$cursor.classList.add("blink"), 500);
|
|
615
|
+
_renderCursor() {
|
|
616
|
+
if (this._showCursor && this._cursorPosition && this._selectedChars.length === 0) {
|
|
617
|
+
const _cursorPosition = this._cursorPosition;
|
|
618
|
+
this._cursor.style.display = "block";
|
|
619
|
+
this._cursor.style.backgroundColor = _cursorPosition.color ?? "rgba(var(--color))";
|
|
620
|
+
this._cursor.style.left = `${_cursorPosition.left - this.text.glyphBox.left}px`;
|
|
621
|
+
this._cursor.style.top = `${_cursorPosition.top - this.text.glyphBox.top}px`;
|
|
622
|
+
this._cursor.style.height = this.text.isVertical ? "1px" : `${_cursorPosition.height}px`;
|
|
623
|
+
this._cursor.style.width = this.text.isVertical ? `${_cursorPosition.width}px` : "1px";
|
|
624
|
+
} else {
|
|
625
|
+
this._cursor.style.display = "none";
|
|
626
|
+
}
|
|
627
|
+
this._cursor.classList.remove("blink");
|
|
628
|
+
if (this._timer) {
|
|
629
|
+
clearTimeout(this._timer);
|
|
596
630
|
}
|
|
631
|
+
this._timer = setTimeout(() => this._cursor.classList.add("blink"), 500);
|
|
597
632
|
}
|
|
598
633
|
}
|
|
634
|
+
__decorateClass([
|
|
635
|
+
property({ fallback: 0 })
|
|
636
|
+
], TextEditor.prototype, "left");
|
|
637
|
+
__decorateClass([
|
|
638
|
+
property({ fallback: 0 })
|
|
639
|
+
], TextEditor.prototype, "top");
|
|
599
640
|
__decorateClass([
|
|
600
641
|
property({ fallback: () => [0, 0] })
|
|
601
642
|
], TextEditor.prototype, "selection");
|
|
602
643
|
__decorateClass([
|
|
603
|
-
property({ fallback: () => ({ min: 0, max: 0 }) })
|
|
604
|
-
], TextEditor.prototype, "
|
|
644
|
+
property({ internal: true, fallback: () => ({ min: 0, max: 0 }) })
|
|
645
|
+
], TextEditor.prototype, "_selectionMinMax");
|
|
605
646
|
__decorateClass([
|
|
606
|
-
property({ fallback: () => [] })
|
|
607
|
-
], TextEditor.prototype, "
|
|
647
|
+
property({ internal: true, fallback: () => [] })
|
|
648
|
+
], TextEditor.prototype, "_chars");
|
|
608
649
|
__decorateClass([
|
|
609
|
-
property({ fallback: () => [] })
|
|
610
|
-
], TextEditor.prototype, "
|
|
650
|
+
property({ internal: true, fallback: () => [] })
|
|
651
|
+
], TextEditor.prototype, "_selectedChars");
|
|
611
652
|
__decorateClass([
|
|
612
|
-
property()
|
|
613
|
-
], TextEditor.prototype, "
|
|
653
|
+
property({ internal: true })
|
|
654
|
+
], TextEditor.prototype, "_cursorPosition");
|
|
614
655
|
__decorateClass([
|
|
615
|
-
property({ fallback: false })
|
|
616
|
-
], TextEditor.prototype, "
|
|
656
|
+
property({ internal: true, fallback: false })
|
|
657
|
+
], TextEditor.prototype, "_showCursor");
|
|
617
658
|
|
|
618
659
|
export { TextEditor };
|