@textbus/platform-browser 4.0.0-alpha.8 → 4.0.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/bundles/index.js CHANGED
@@ -4,6 +4,7 @@ require('reflect-metadata');
4
4
  var core$1 = require('@textbus/core');
5
5
  var stream = require('@tanbo/stream');
6
6
  var core = require('@viewfly/core');
7
+ var collaborate = require('@textbus/collaborate');
7
8
 
8
9
  function createElement(tagName, options = {}) {
9
10
  const el = document.createElement(tagName);
@@ -35,9 +36,6 @@ function createElement(tagName, options = {}) {
35
36
  }
36
37
  return el;
37
38
  }
38
- function createTextNode(content) {
39
- return document.createTextNode(content);
40
- }
41
39
  function getLayoutRectByRange(range) {
42
40
  let { startContainer, startOffset } = range;
43
41
  if (startContainer.nodeType === Node.TEXT_NODE) {
@@ -55,7 +53,7 @@ function getLayoutRectByRange(range) {
55
53
  return {
56
54
  left: rect.right,
57
55
  top: rect.top,
58
- width: rect.width,
56
+ width: range.collapsed ? 0 : rect.width,
59
57
  height: rect.height
60
58
  };
61
59
  }
@@ -63,7 +61,13 @@ function getLayoutRectByRange(range) {
63
61
  const range2 = document.createRange();
64
62
  range2.setStart(beforeNode, beforeNode.textContent.length);
65
63
  range2.setEnd(beforeNode, beforeNode.textContent.length);
66
- return range2.getBoundingClientRect();
64
+ const rect = range2.getBoundingClientRect();
65
+ return {
66
+ left: rect.right,
67
+ top: rect.top,
68
+ width: range.collapsed ? 0 : rect.width,
69
+ height: rect.height
70
+ };
67
71
  }
68
72
  }
69
73
  const offsetNode = startContainer.childNodes[startOffset];
@@ -75,14 +79,20 @@ function getLayoutRectByRange(range) {
75
79
  return {
76
80
  left: rect.right,
77
81
  top: rect.top,
78
- width: rect.width,
82
+ width: range.collapsed ? 0 : rect.width,
79
83
  height: rect.height
80
84
  };
81
85
  }
82
86
  }
83
87
  if (offsetNode) {
84
88
  if (offsetNode.nodeType === Node.ELEMENT_NODE && offsetNode.nodeName.toLowerCase() !== 'br') {
85
- return offsetNode.getBoundingClientRect();
89
+ const rect = offsetNode.getBoundingClientRect();
90
+ return {
91
+ left: rect.left,
92
+ top: rect.top,
93
+ width: range.collapsed ? 0 : rect.width,
94
+ height: rect.height
95
+ };
86
96
  }
87
97
  isInsertBefore = true;
88
98
  }
@@ -97,7 +107,12 @@ function getLayoutRectByRange(range) {
97
107
  }
98
108
  const rect = span.getBoundingClientRect();
99
109
  startContainer.removeChild(span);
100
- return rect;
110
+ return {
111
+ left: rect.left,
112
+ top: rect.top,
113
+ width: range.collapsed ? 0 : rect.width,
114
+ height: rect.height
115
+ };
101
116
  }
102
117
 
103
118
  const isWindows = () => /win(dows|32|64)/i.test(navigator.userAgent);
@@ -169,7 +184,6 @@ exports.Parser = Parser_1 = class Parser {
169
184
  return new DOMParser().parseFromString(html, 'text/html').body;
170
185
  }
171
186
  constructor(options, textbus) {
172
- this.options = options;
173
187
  this.textbus = textbus;
174
188
  const componentLoaders = [
175
189
  ...(options.componentLoaders || [])
@@ -214,8 +228,9 @@ exports.Parser = Parser_1 = class Parser {
214
228
  slot.insert('\n');
215
229
  return;
216
230
  }
231
+ const schema = [...slot.schema];
217
232
  for (const t of this.componentLoaders) {
218
- if (t.match(el)) {
233
+ if (t.match(el, schema)) {
219
234
  const result = t.read(el, this.textbus, (childSlot, slotRootElement, slotContentHostElement = slotRootElement) => {
220
235
  return this.readSlot(childSlot, slotRootElement, slotContentHostElement);
221
236
  });
@@ -302,13 +317,10 @@ exports.Parser = Parser_1 = __decorate([
302
317
  class Input {
303
318
  }
304
319
 
305
- /**
306
- * Textbus PC 端浏览器渲染能力桥接器抽象类,提供了 DOM 元素查询能力,具体渲染能力由各前端框架实现相应桥接
307
- */
308
- class DomAdapter extends core$1.ViewAdapter {
309
- constructor(mount) {
310
- super();
311
- this.mount = mount;
320
+ class DomAdapter extends core$1.Adapter {
321
+ constructor() {
322
+ super(...arguments);
323
+ this.onViewUpdated = new stream.Subject();
312
324
  this.host = createElement('div', {
313
325
  styles: {
314
326
  cursor: 'text',
@@ -324,126 +336,6 @@ class DomAdapter extends core$1.ViewAdapter {
324
336
  id: 'textbus-' + Number((Math.random() + '').substring(2)).toString(16)
325
337
  }
326
338
  });
327
- this.componentRootElementCaches = core$1.createBidirectionalMapping(a => {
328
- return a instanceof core$1.ComponentInstance;
329
- });
330
- this.slotRootNativeElementCaches = core$1.createBidirectionalMapping(a => {
331
- return a instanceof core$1.Slot;
332
- });
333
- this.slotRootVElementCaches = new WeakMap();
334
- }
335
- render(rootComponent) {
336
- const view = this.componentRender(rootComponent);
337
- return this.mount(this.host, view);
338
- }
339
- copy() {
340
- document.execCommand('copy');
341
- }
342
- /**
343
- * 根据组件获取组件的根 DOM 节点
344
- * @param component
345
- */
346
- getNativeNodeByComponent(component) {
347
- return this.componentRootElementCaches.get(component) || null;
348
- }
349
- /**
350
- * 根据 DOM 节点,获对对应的组件根节点,如传入的 DOM 节点不为组件的根节点,则返回 null
351
- * @param node
352
- */
353
- getComponentByNativeNode(node) {
354
- return this.componentRootElementCaches.get(node) || null;
355
- }
356
- /**
357
- * 根据插槽获取插槽的根 DOM 节点
358
- * @param slot
359
- */
360
- getNativeNodeBySlot(slot) {
361
- return this.slotRootNativeElementCaches.get(slot) || null;
362
- }
363
- /**
364
- * 根据 DOM 节点,获对对应的插槽根节点,如传入的 DOM 节点不为插槽的根节点,则返回 null
365
- * @param node
366
- */
367
- getSlotByNativeNode(node) {
368
- return this.slotRootNativeElementCaches.get(node) || null;
369
- }
370
- /**
371
- * 获取插槽内容节点集合
372
- * @param slot
373
- */
374
- getNodesBySlot(slot) {
375
- const rootNativeNode = this.getNativeNodeBySlot(slot);
376
- if (!rootNativeNode) {
377
- return [];
378
- }
379
- const rootVNode = this.slotRootVElementCaches.get(slot);
380
- const getNodes = (vElement, nativeNode, result) => {
381
- if (vElement.location) {
382
- result.push(nativeNode);
383
- }
384
- for (let i = 0; i < vElement.children.length; i++) {
385
- const vChild = vElement.children[i];
386
- const nativeChild = nativeNode.childNodes[i];
387
- if (vChild instanceof core$1.VElement) {
388
- getNodes(vChild, nativeChild, result);
389
- }
390
- else if (vChild instanceof core$1.VTextNode) {
391
- result.push(nativeChild);
392
- }
393
- else {
394
- result.push(this.getNativeNodeByComponent(vChild));
395
- }
396
- }
397
- return result;
398
- };
399
- return getNodes(rootVNode, rootNativeNode, []);
400
- }
401
- /**
402
- * 获取原生节点的原始数据在文档中的位置
403
- * @param node
404
- */
405
- getLocationByNativeNode(node) {
406
- let slotRootNode = node;
407
- while (!this.slotRootNativeElementCaches.get(slotRootNode)) {
408
- slotRootNode = slotRootNode.parentNode;
409
- if (!slotRootNode) {
410
- return null;
411
- }
412
- }
413
- const slot = this.slotRootNativeElementCaches.get(slotRootNode);
414
- const rootVNode = this.slotRootVElementCaches.get(slot);
415
- const getLocation = (target, tree, vNodeTree) => {
416
- if (target === tree) {
417
- return Object.assign({}, vNodeTree.location);
418
- }
419
- const childNodes = tree.childNodes;
420
- for (let i = 0; i < childNodes.length; i++) {
421
- const child = vNodeTree.children[i];
422
- const nativeChild = tree.childNodes[i];
423
- if (nativeChild === target) {
424
- if (child instanceof core$1.ComponentInstance) {
425
- const index = child.parent.indexOf(child);
426
- return {
427
- slot: child.parent,
428
- startIndex: index,
429
- endIndex: index + 1
430
- };
431
- }
432
- return child.location;
433
- }
434
- else if (child instanceof core$1.VElement) {
435
- let r = null;
436
- if (nativeChild.nodeType === Node.ELEMENT_NODE) {
437
- r = getLocation(target, nativeChild, child);
438
- }
439
- if (r) {
440
- return r;
441
- }
442
- }
443
- }
444
- return null;
445
- };
446
- return getLocation(node, slotRootNode, rootVNode);
447
339
  }
448
340
  }
449
341
 
@@ -542,7 +434,33 @@ exports.SelectionBridge = class SelectionBridge {
542
434
  this.listen(this.connector);
543
435
  return;
544
436
  }
545
- this.nativeSelection.setBaseAndExtent(anchor.node, anchor.offset, focus.node, focus.offset);
437
+ function tryOffset(position) {
438
+ if (!position.node) {
439
+ return;
440
+ }
441
+ if (position.node.nodeType === Node.TEXT_NODE) {
442
+ const len = position.node.textContent.length;
443
+ if (position.offset > len) {
444
+ position.offset = len;
445
+ }
446
+ }
447
+ else if (position.node.nodeType === Node.ELEMENT_NODE) {
448
+ const len = position.node.childNodes.length;
449
+ if (position.offset > len) {
450
+ position.offset = len;
451
+ }
452
+ }
453
+ }
454
+ try {
455
+ tryOffset(focus);
456
+ tryOffset(anchor);
457
+ this.nativeSelection.setBaseAndExtent(anchor.node, anchor.offset, focus.node, focus.offset);
458
+ }
459
+ catch (e) {
460
+ setTimeout(() => {
461
+ throw e;
462
+ });
463
+ }
546
464
  if (this.nativeSelection.rangeCount) {
547
465
  const nativeRange = this.nativeSelection.getRangeAt(0);
548
466
  this.selectionChangeEvent.next(nativeRange);
@@ -565,6 +483,7 @@ exports.SelectionBridge = class SelectionBridge {
565
483
  // hack end
566
484
  }
567
485
  destroy() {
486
+ this.subs.forEach(i => i.unsubscribe());
568
487
  this.sub.unsubscribe();
569
488
  }
570
489
  getPositionByRange(abstractSelection) {
@@ -647,9 +566,10 @@ exports.SelectionBridge = class SelectionBridge {
647
566
  }
648
567
  minLeft = rect2.left;
649
568
  minTop = rect2.top;
569
+ // oldPosition = position
650
570
  }
651
571
  if (isToPrevLine) {
652
- if (rect2.left < startLeft) {
572
+ if (rect2.left <= startLeft) {
653
573
  return position;
654
574
  }
655
575
  if (oldPosition) {
@@ -762,14 +682,14 @@ exports.SelectionBridge = class SelectionBridge {
762
682
  const isFocusStart = selection.focusNode === nativeRange.startContainer && selection.focusOffset === nativeRange.startOffset;
763
683
  if (!this.docContainer.contains(selection.focusNode)) {
764
684
  if (isFocusEnd) {
765
- const nativeNode = this.domAdapter.getNativeNodeBySlot(this.rootComponentRef.component.slots.first);
685
+ const nativeNode = this.domAdapter.getNativeNodeBySlot(this.rootComponentRef.component.slots.at(0));
766
686
  if (!nativeNode) {
767
687
  return;
768
688
  }
769
689
  nativeRange.setEndAfter(nativeNode.lastChild);
770
690
  }
771
691
  else {
772
- const nativeNode = this.domAdapter.getNativeNodeBySlot(this.rootComponentRef.component.slots.last);
692
+ const nativeNode = this.domAdapter.getNativeNodeBySlot(this.rootComponentRef.component.slots.at(-1));
773
693
  if (!nativeNode) {
774
694
  return;
775
695
  }
@@ -1030,25 +950,24 @@ class ExperimentalCaret {
1030
950
  get display() {
1031
951
  return this._display;
1032
952
  }
1033
- constructor(scheduler, editorMask) {
953
+ constructor(domRenderer, scheduler, editorMask) {
954
+ this.domRenderer = domRenderer;
1034
955
  this.scheduler = scheduler;
1035
956
  this.editorMask = editorMask;
1036
- this.compositionState = null;
1037
- this.compositionElement = createElement('span', {
1038
- styles: {
1039
- textDecoration: 'underline'
1040
- }
1041
- });
957
+ this.changeFromSelf = false;
958
+ this.getLimit = function () {
959
+ return {
960
+ top: 0,
961
+ bottom: document.documentElement.clientHeight
962
+ };
963
+ };
1042
964
  this.timer = null;
1043
- this.oldPosition = null;
1044
965
  this._display = true;
1045
966
  this.flashing = true;
1046
- this.subs = [];
1047
967
  this.subscription = new stream.Subscription();
1048
968
  this.positionChangeEvent = new stream.Subject();
1049
969
  this.styleChangeEvent = new stream.Subject();
1050
970
  this.oldRange = null;
1051
- this.isFixed = false;
1052
971
  this.onPositionChange = this.positionChangeEvent.pipe(stream.distinctUntilChanged());
1053
972
  this.onStyleChange = this.styleChangeEvent.asObservable();
1054
973
  this.elementRef = createElement('div', {
@@ -1076,20 +995,12 @@ class ExperimentalCaret {
1076
995
  }));
1077
996
  this.editorMask.appendChild(this.elementRef);
1078
997
  }
1079
- refresh(isFixedCaret = false) {
1080
- this.isFixed = isFixedCaret;
998
+ refresh() {
1081
999
  if (this.oldRange) {
1082
1000
  this.show(this.oldRange, false);
1083
1001
  }
1084
- this.isFixed = false;
1085
1002
  }
1086
1003
  show(range, restart) {
1087
- const oldRect = this.elementRef.getBoundingClientRect();
1088
- this.oldPosition = {
1089
- top: oldRect.top,
1090
- left: oldRect.left,
1091
- height: oldRect.height
1092
- };
1093
1004
  this.oldRange = range;
1094
1005
  if (restart || this.scheduler.lastChangesHasLocalUpdate) {
1095
1006
  clearTimeout(this.timer);
@@ -1118,62 +1029,8 @@ class ExperimentalCaret {
1118
1029
  }
1119
1030
  destroy() {
1120
1031
  clearTimeout(this.timer);
1032
+ // this.caret.
1121
1033
  this.subscription.unsubscribe();
1122
- this.subs.forEach(i => i.unsubscribe());
1123
- }
1124
- correctScrollTop(scroller) {
1125
- this.subs.forEach(i => i.unsubscribe());
1126
- this.subs = [];
1127
- const scheduler = this.scheduler;
1128
- let docIsChanged = true;
1129
- function limitPosition(position) {
1130
- const { top, bottom } = scroller.getLimit();
1131
- const caretTop = position.top;
1132
- if (caretTop + position.height > bottom) {
1133
- const offset = caretTop - bottom + position.height;
1134
- scroller.setOffset(offset);
1135
- }
1136
- else if (position.top < top) {
1137
- scroller.setOffset(-(top - position.top));
1138
- }
1139
- }
1140
- let isPressed = false;
1141
- this.subs.push(scroller.onScroll.subscribe(() => {
1142
- if (this.oldPosition) {
1143
- const rect = this.rect;
1144
- this.oldPosition.top = rect.top;
1145
- this.oldPosition.left = rect.left;
1146
- this.oldPosition.height = rect.height;
1147
- }
1148
- }), stream.fromEvent(document, 'mousedown', true).subscribe(() => {
1149
- isPressed = true;
1150
- }), stream.fromEvent(document, 'mouseup', true).subscribe(() => {
1151
- isPressed = false;
1152
- }), scheduler.onDocChange.subscribe(() => {
1153
- docIsChanged = true;
1154
- }), this.onPositionChange.subscribe(position => {
1155
- if (position) {
1156
- if (docIsChanged) {
1157
- if (scheduler.lastChangesHasLocalUpdate) {
1158
- limitPosition(position);
1159
- }
1160
- else if (this.oldPosition) {
1161
- const offset = Math.floor(position.top - this.oldPosition.top);
1162
- scroller.setOffset(offset);
1163
- }
1164
- }
1165
- else if (!isPressed) {
1166
- if (this.isFixed && this.oldPosition) {
1167
- const offset = Math.floor(position.top - this.oldPosition.top);
1168
- scroller.setOffset(offset);
1169
- }
1170
- else {
1171
- limitPosition(position);
1172
- }
1173
- }
1174
- }
1175
- docIsChanged = false;
1176
- }));
1177
1034
  }
1178
1035
  updateCursorPosition(nativeRange) {
1179
1036
  const startContainer = nativeRange.startContainer;
@@ -1182,12 +1039,10 @@ class ExperimentalCaret {
1182
1039
  this.positionChangeEvent.next(null);
1183
1040
  return;
1184
1041
  }
1185
- if (this.compositionState) {
1186
- const compositionElement = this.compositionElement;
1187
- compositionElement.innerText = this.compositionState.data;
1042
+ const compositionNode = this.domRenderer.compositionNode;
1043
+ if (compositionNode) {
1188
1044
  nativeRange = nativeRange.cloneRange();
1189
- nativeRange.insertNode(compositionElement);
1190
- nativeRange.selectNodeContents(compositionElement);
1045
+ nativeRange.selectNodeContents(compositionNode);
1191
1046
  nativeRange.collapse();
1192
1047
  }
1193
1048
  const rect = getLayoutRectByRange(nativeRange);
@@ -1214,13 +1069,34 @@ class ExperimentalCaret {
1214
1069
  rectTop = Math.floor(rectTop);
1215
1070
  const containerRect = this.editorMask.getBoundingClientRect();
1216
1071
  const top = Math.floor(rectTop - containerRect.top);
1217
- const left = Math.floor(rect.left - containerRect.left);
1072
+ const left = Math.floor(rect.left + rect.width / 2 - containerRect.left);
1073
+ let rotate = 0;
1074
+ if (nativeRange.collapsed) {
1075
+ rotate = Math.round(Math.atan2(rect.width, rect.height) * 180 / Math.PI);
1076
+ if (rotate !== 0) {
1077
+ const hackEle = document.createElement('span');
1078
+ hackEle.style.cssText = 'display: inline-block; width: 10px; height: 10px; position: relative; contain: layout style size;';
1079
+ const pointEle = document.createElement('span');
1080
+ pointEle.style.cssText = 'position: absolute; left: 0; top: 0; width:0;height:0';
1081
+ hackEle.append(pointEle);
1082
+ node.append(hackEle);
1083
+ const t1 = pointEle.getBoundingClientRect().top;
1084
+ pointEle.style.right = '0';
1085
+ pointEle.style.left = '';
1086
+ const t2 = pointEle.getBoundingClientRect().top;
1087
+ if (t2 < t1) {
1088
+ rotate = -rotate;
1089
+ }
1090
+ hackEle.remove();
1091
+ }
1092
+ }
1218
1093
  Object.assign(this.elementRef.style, {
1219
1094
  left: left + 'px',
1220
1095
  top: top + 'px',
1221
1096
  height: boxHeight + 'px',
1222
1097
  lineHeight: boxHeight + 'px',
1223
- fontSize
1098
+ fontSize,
1099
+ transform: `rotate(${rotate}deg)`,
1224
1100
  });
1225
1101
  this.caret.style.backgroundColor = color;
1226
1102
  this.styleChangeEvent.next({
@@ -1233,6 +1109,21 @@ class ExperimentalCaret {
1233
1109
  top: rectTop,
1234
1110
  height: boxHeight
1235
1111
  });
1112
+ if (this.changeFromSelf) {
1113
+ this.changeFromSelf = false;
1114
+ const selfRect = this.elementRef.getBoundingClientRect();
1115
+ const limit = this.getLimit();
1116
+ if (selfRect.top < limit.top) {
1117
+ this.elementRef.scrollIntoView({
1118
+ block: 'start'
1119
+ });
1120
+ }
1121
+ else if (selfRect.bottom > limit.bottom) {
1122
+ this.elementRef.scrollIntoView({
1123
+ block: 'end'
1124
+ });
1125
+ }
1126
+ }
1236
1127
  }
1237
1128
  }
1238
1129
  /**
@@ -1248,8 +1139,9 @@ exports.MagicInput = class MagicInput extends Input {
1248
1139
  get disabled() {
1249
1140
  return this._disabled;
1250
1141
  }
1251
- constructor(parser, keyboard, commander, selection, controller, scheduler, textbus) {
1142
+ constructor(domAdapter, parser, keyboard, commander, selection, controller, scheduler, textbus) {
1252
1143
  super();
1144
+ this.domAdapter = domAdapter;
1253
1145
  this.parser = parser;
1254
1146
  this.keyboard = keyboard;
1255
1147
  this.commander = commander;
@@ -1258,8 +1150,7 @@ exports.MagicInput = class MagicInput extends Input {
1258
1150
  this.scheduler = scheduler;
1259
1151
  this.textbus = textbus;
1260
1152
  this.composition = false;
1261
- this.compositionState = null;
1262
- this.caret = new ExperimentalCaret(this.scheduler, this.textbus.get(VIEW_MASK));
1153
+ this.caret = new ExperimentalCaret(this.domAdapter, this.scheduler, this.textbus.get(VIEW_MASK));
1263
1154
  this.isSafari = isSafari();
1264
1155
  this.isFirefox = isFirefox();
1265
1156
  this.isMac = isMac();
@@ -1299,13 +1190,8 @@ exports.MagicInput = class MagicInput extends Input {
1299
1190
  if (!this.isFocus) {
1300
1191
  (_a = this.textarea) === null || _a === void 0 ? void 0 : _a.focus();
1301
1192
  setTimeout(() => {
1302
- var _a, _b, _c;
1303
1193
  if (!this.nativeFocus && this.isFocus) {
1304
- this.subscription.unsubscribe();
1305
- (_b = (_a = this.textarea) === null || _a === void 0 ? void 0 : _a.parentNode) === null || _b === void 0 ? void 0 : _b.removeChild(this.textarea);
1306
- this.subscription = new stream.Subscription();
1307
- this.init();
1308
- (_c = this.textarea) === null || _c === void 0 ? void 0 : _c.focus();
1194
+ this.reInit();
1309
1195
  }
1310
1196
  });
1311
1197
  }
@@ -1321,6 +1207,22 @@ exports.MagicInput = class MagicInput extends Input {
1321
1207
  this.caret.destroy();
1322
1208
  this.subscription.unsubscribe();
1323
1209
  }
1210
+ reInit(delay = false) {
1211
+ var _a, _b, _c;
1212
+ this.subscription.unsubscribe();
1213
+ (_b = (_a = this.textarea) === null || _a === void 0 ? void 0 : _a.parentNode) === null || _b === void 0 ? void 0 : _b.removeChild(this.textarea);
1214
+ this.subscription = new stream.Subscription();
1215
+ this.init();
1216
+ if (delay) {
1217
+ setTimeout(() => {
1218
+ var _a;
1219
+ (_a = this.textarea) === null || _a === void 0 ? void 0 : _a.focus();
1220
+ });
1221
+ }
1222
+ else {
1223
+ (_c = this.textarea) === null || _c === void 0 ? void 0 : _c.focus();
1224
+ }
1225
+ }
1324
1226
  init() {
1325
1227
  const doc = this.doc;
1326
1228
  const contentBody = doc.body;
@@ -1329,9 +1231,19 @@ exports.MagicInput = class MagicInput extends Input {
1329
1231
  contentBody.appendChild(textarea);
1330
1232
  this.textarea = textarea;
1331
1233
  this.subscription.add(stream.fromEvent(textarea, 'blur').subscribe(() => {
1234
+ // if (this.isFocus) {
1235
+ // this.isFocus = false
1236
+ // this.reInit(true)
1237
+ // }
1332
1238
  this.isFocus = false;
1333
1239
  this.nativeFocus = false;
1334
1240
  this.caret.hide();
1241
+ if (this.domAdapter.composition) {
1242
+ const slot = this.domAdapter.composition.slot;
1243
+ this.domAdapter.composition = null;
1244
+ this.domAdapter.compositionNode = null;
1245
+ slot.__changeMarker__.forceMarkDirtied();
1246
+ }
1335
1247
  }), stream.fromEvent(textarea, 'focus').subscribe(() => {
1336
1248
  this.nativeFocus = true;
1337
1249
  }), this.caret.onStyleChange.subscribe(style => {
@@ -1342,7 +1254,7 @@ exports.MagicInput = class MagicInput extends Input {
1342
1254
  this.handleDefaultActions(textarea);
1343
1255
  }
1344
1256
  handleDefaultActions(textarea) {
1345
- this.subscription.add(stream.fromEvent(document, 'copy').subscribe(ev => {
1257
+ this.subscription.add(stream.fromEvent(isFirefox() ? textarea : document, 'copy').subscribe(ev => {
1346
1258
  const selection = this.selection;
1347
1259
  if (!selection.isSelected) {
1348
1260
  return;
@@ -1412,6 +1324,7 @@ exports.MagicInput = class MagicInput extends Input {
1412
1324
  }), stream.fromEvent(textarea, 'compositionend').subscribe(() => {
1413
1325
  isWriting = false;
1414
1326
  }), stream.fromEvent(textarea, 'beforeinput').subscribe(ev => {
1327
+ this.ignoreComposition = false;
1415
1328
  if (this.isSafari) {
1416
1329
  if (ev.inputType === 'insertFromComposition') {
1417
1330
  isIgnore = true;
@@ -1433,6 +1346,7 @@ exports.MagicInput = class MagicInput extends Input {
1433
1346
  key = keys.charAt(+ev.code.substring(5));
1434
1347
  ev.preventDefault();
1435
1348
  }
1349
+ this.caret.changeFromSelf = true;
1436
1350
  const is = this.keyboard.execShortcut({
1437
1351
  key: key,
1438
1352
  altKey: ev.altKey,
@@ -1443,6 +1357,9 @@ exports.MagicInput = class MagicInput extends Input {
1443
1357
  this.ignoreComposition = true;
1444
1358
  ev.preventDefault();
1445
1359
  }
1360
+ else {
1361
+ this.caret.changeFromSelf = false;
1362
+ }
1446
1363
  }));
1447
1364
  }
1448
1365
  handleInput(textarea) {
@@ -1451,10 +1368,10 @@ exports.MagicInput = class MagicInput extends Input {
1451
1368
  return !this.ignoreComposition;
1452
1369
  })).subscribe(() => {
1453
1370
  if (!this.selection.isCollapsed) {
1371
+ this.caret.changeFromSelf = true;
1454
1372
  this.commander.delete();
1455
1373
  }
1456
1374
  this.composition = true;
1457
- this.caret.compositionState = this.compositionState = null;
1458
1375
  startIndex = this.selection.startOffset;
1459
1376
  const startSlot = this.selection.startSlot;
1460
1377
  const event = new core$1.Event(startSlot, {
@@ -1471,17 +1388,20 @@ exports.MagicInput = class MagicInput extends Input {
1471
1388
  return;
1472
1389
  }
1473
1390
  const startSlot = this.selection.startSlot;
1474
- this.caret.compositionState = this.compositionState = {
1391
+ this.domAdapter.composition = {
1475
1392
  slot: startSlot,
1476
- index: startIndex,
1477
- data: ev.data
1393
+ text: ev.data,
1394
+ offset: ev.data.length,
1395
+ index: startIndex
1478
1396
  };
1479
- this.caret.refresh(true);
1397
+ this.caret.changeFromSelf = true;
1398
+ this.caret.refresh();
1480
1399
  const event = new core$1.Event(startSlot, {
1481
1400
  index: startIndex,
1482
1401
  data: ev.data
1483
1402
  });
1484
1403
  core$1.invokeListener(startSlot.parent, 'onCompositionUpdate', event);
1404
+ startSlot.__changeMarker__.forceMarkDirtied();
1485
1405
  }));
1486
1406
  let isCompositionEnd = false;
1487
1407
  this.subscription.add(stream.merge(stream.fromEvent(textarea, 'beforeinput').pipe(stream.filter(ev => {
@@ -1505,24 +1425,16 @@ exports.MagicInput = class MagicInput extends Input {
1505
1425
  textarea.value = '';
1506
1426
  return ev.data;
1507
1427
  }))).subscribe(text => {
1428
+ var _a;
1508
1429
  this.composition = false;
1509
- this.caret.compositionState = this.compositionState = null;
1510
- const compositionElement = this.caret.compositionElement;
1511
- let nextSibling = compositionElement.nextSibling;
1512
- while (nextSibling) {
1513
- if (!nextSibling.textContent) {
1514
- const next = nextSibling.nextSibling;
1515
- nextSibling.remove();
1516
- nextSibling = next;
1517
- continue;
1518
- }
1519
- nextSibling.remove();
1520
- break;
1521
- }
1522
- compositionElement.remove();
1430
+ this.domAdapter.composition = null;
1523
1431
  if (text) {
1432
+ this.caret.changeFromSelf = true;
1524
1433
  this.commander.write(text);
1525
1434
  }
1435
+ else {
1436
+ (_a = this.selection.startSlot) === null || _a === void 0 ? void 0 : _a.__changeMarker__.forceMarkDirtied();
1437
+ }
1526
1438
  if (isCompositionEnd) {
1527
1439
  const startSlot = this.selection.startSlot;
1528
1440
  if (startSlot) {
@@ -1551,7 +1463,8 @@ exports.MagicInput = class MagicInput extends Input {
1551
1463
  };
1552
1464
  exports.MagicInput = __decorate([
1553
1465
  core.Injectable(),
1554
- __metadata("design:paramtypes", [exports.Parser,
1466
+ __metadata("design:paramtypes", [DomAdapter,
1467
+ exports.Parser,
1555
1468
  core$1.Keyboard,
1556
1469
  core$1.Commander,
1557
1470
  core$1.Selection,
@@ -1569,10 +1482,11 @@ class CollaborateSelectionAwarenessDelegate {
1569
1482
  * 协作光标绘制类
1570
1483
  */
1571
1484
  exports.CollaborateCursor = class CollaborateCursor {
1572
- constructor(textbus, nativeSelection, scheduler, selection, awarenessDelegate) {
1485
+ constructor(textbus, nativeSelection, scheduler, selection, userActivity, awarenessDelegate) {
1573
1486
  this.nativeSelection = nativeSelection;
1574
1487
  this.scheduler = scheduler;
1575
1488
  this.selection = selection;
1489
+ this.userActivity = userActivity;
1576
1490
  this.awarenessDelegate = awarenessDelegate;
1577
1491
  this.host = createElement('div', {
1578
1492
  styles: {
@@ -1622,6 +1536,7 @@ exports.CollaborateCursor = class CollaborateCursor {
1622
1536
  this.onRectsChange = new stream.Subject();
1623
1537
  this.subscription = new stream.Subscription();
1624
1538
  this.currentSelection = [];
1539
+ this.ratio = window.devicePixelRatio || 1;
1625
1540
  this.container = textbus.get(VIEW_CONTAINER);
1626
1541
  this.canvasContainer.append(this.canvas);
1627
1542
  this.host.append(this.canvasContainer, this.tooltips);
@@ -1641,6 +1556,13 @@ exports.CollaborateCursor = class CollaborateCursor {
1641
1556
  this.refresh();
1642
1557
  }));
1643
1558
  }
1559
+ init() {
1560
+ if (this.userActivity) {
1561
+ this.subscription.add(this.userActivity.onStateChange.subscribe(v => {
1562
+ this.draw(v);
1563
+ }));
1564
+ }
1565
+ }
1644
1566
  /**
1645
1567
  * 刷新协作光标,由于 Textbus 只会绘制可视区域的光标,当可视区域发生变化时,需要重新绘制
1646
1568
  */
@@ -1658,15 +1580,16 @@ exports.CollaborateCursor = class CollaborateCursor {
1658
1580
  this.currentSelection = paths;
1659
1581
  const containerRect = this.container.getBoundingClientRect();
1660
1582
  this.canvas.style.top = containerRect.top * -1 + 'px';
1661
- this.canvas.width = this.canvas.offsetWidth;
1662
- this.canvas.height = this.canvas.offsetHeight;
1583
+ this.canvas.width = this.canvas.offsetWidth * this.ratio;
1584
+ this.canvas.height = this.canvas.offsetHeight * this.ratio;
1585
+ this.context.scale(this.ratio, this.ratio);
1663
1586
  this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
1664
1587
  const users = [];
1665
1588
  paths.filter(i => {
1666
- return i.paths.anchor.length && i.paths.focus.length;
1589
+ return i.selection.anchor.length && i.selection.focus.length;
1667
1590
  }).forEach(item => {
1668
- const anchorPaths = [...item.paths.anchor];
1669
- const focusPaths = [...item.paths.focus];
1591
+ const anchorPaths = [...item.selection.anchor];
1592
+ const focusPaths = [...item.selection.focus];
1670
1593
  const anchorOffset = anchorPaths.pop();
1671
1594
  const anchorSlot = this.selection.findSlotByPaths(anchorPaths);
1672
1595
  const focusOffset = focusPaths.pop();
@@ -1684,8 +1607,13 @@ exports.CollaborateCursor = class CollaborateCursor {
1684
1607
  return;
1685
1608
  }
1686
1609
  const nativeRange = document.createRange();
1687
- nativeRange.setStart(anchor.node, anchor.offset);
1688
- nativeRange.setEnd(focus.node, focus.offset);
1610
+ try {
1611
+ nativeRange.setStart(anchor.node, anchor.offset);
1612
+ nativeRange.setEnd(focus.node, focus.offset);
1613
+ }
1614
+ catch (e) {
1615
+ return;
1616
+ }
1689
1617
  if ((anchor.node !== focus.node || anchor.offset !== focus.offset) && nativeRange.collapsed) {
1690
1618
  nativeRange.setStart(focus.node, focus.offset);
1691
1619
  nativeRange.setEnd(anchor.node, anchor.offset);
@@ -1814,10 +1742,12 @@ exports.CollaborateCursor = class CollaborateCursor {
1814
1742
  exports.CollaborateCursor = __decorate([
1815
1743
  core.Injectable(),
1816
1744
  __param(4, core.Optional()),
1745
+ __param(5, core.Optional()),
1817
1746
  __metadata("design:paramtypes", [core$1.Textbus,
1818
1747
  exports.SelectionBridge,
1819
1748
  core$1.Scheduler,
1820
1749
  core$1.Selection,
1750
+ collaborate.UserActivity,
1821
1751
  CollaborateSelectionAwarenessDelegate])
1822
1752
  ], exports.CollaborateCursor);
1823
1753
 
@@ -1854,9 +1784,7 @@ class NativeCaret {
1854
1784
  height: 0
1855
1785
  };
1856
1786
  }
1857
- constructor(scheduler) {
1858
- this.scheduler = scheduler;
1859
- this.oldPosition = null;
1787
+ constructor() {
1860
1788
  this._nativeRange = null;
1861
1789
  this.subs = [];
1862
1790
  this.positionChangeEvent = new stream.Subject();
@@ -1865,65 +1793,12 @@ class NativeCaret {
1865
1793
  refresh() {
1866
1794
  //
1867
1795
  }
1868
- correctScrollTop(scroller) {
1869
- this.destroy();
1870
- const scheduler = this.scheduler;
1871
- let docIsChanged = true;
1872
- function limitPosition(position) {
1873
- const { top, bottom } = scroller.getLimit();
1874
- const caretTop = position.top;
1875
- if (caretTop + position.height > bottom) {
1876
- const offset = caretTop - bottom + position.height;
1877
- scroller.setOffset(offset);
1878
- }
1879
- else if (position.top < top) {
1880
- scroller.setOffset(-(top - position.top));
1881
- }
1882
- }
1883
- let isPressed = false;
1884
- this.subs.push(scroller.onScroll.subscribe(() => {
1885
- if (this.oldPosition) {
1886
- const rect = this.rect;
1887
- this.oldPosition.top = rect.top;
1888
- this.oldPosition.left = rect.left;
1889
- this.oldPosition.height = rect.height;
1890
- }
1891
- }), stream.fromEvent(document, 'mousedown', true).subscribe(() => {
1892
- isPressed = true;
1893
- }), stream.fromEvent(document, 'mouseup', true).subscribe(() => {
1894
- isPressed = false;
1895
- }), scheduler.onDocChange.subscribe(() => {
1896
- docIsChanged = true;
1897
- }), this.onPositionChange.subscribe(position => {
1898
- if (position) {
1899
- if (docIsChanged) {
1900
- if (scheduler.lastChangesHasLocalUpdate) {
1901
- limitPosition(position);
1902
- }
1903
- else if (this.oldPosition) {
1904
- const offset = Math.floor(position.top - this.oldPosition.top);
1905
- scroller.setOffset(offset);
1906
- }
1907
- }
1908
- else if (!isPressed) {
1909
- if (this.oldPosition) {
1910
- const offset = Math.floor(position.top - this.oldPosition.top);
1911
- scroller.setOffset(offset);
1912
- }
1913
- else {
1914
- limitPosition(position);
1915
- }
1916
- }
1917
- }
1918
- docIsChanged = false;
1919
- }));
1920
- }
1921
1796
  destroy() {
1922
1797
  this.subs.forEach(i => i.unsubscribe());
1923
1798
  this.subs = [];
1924
1799
  }
1925
1800
  }
1926
- let NativeInput = class NativeInput extends Input {
1801
+ exports.NativeInput = class NativeInput extends Input {
1927
1802
  set disabled(b) {
1928
1803
  this._disabled = b;
1929
1804
  if (this.controller.readonly) {
@@ -1935,18 +1810,17 @@ let NativeInput = class NativeInput extends Input {
1935
1810
  get disabled() {
1936
1811
  return this._disabled;
1937
1812
  }
1938
- constructor(textbus, parser, scheduler, selection, keyboard, domAdapter, commander, controller) {
1813
+ constructor(textbus, parser, selection, keyboard, domAdapter, commander, controller) {
1939
1814
  super();
1940
1815
  this.parser = parser;
1941
- this.scheduler = scheduler;
1942
1816
  this.selection = selection;
1943
1817
  this.keyboard = keyboard;
1944
1818
  this.domAdapter = domAdapter;
1945
1819
  this.commander = commander;
1946
1820
  this.controller = controller;
1947
- this.caret = new NativeCaret(this.scheduler);
1821
+ this.caret = new NativeCaret();
1948
1822
  this.composition = false;
1949
- this.compositionState = null;
1823
+ // compositionState: CompositionState | null = null
1950
1824
  this.onReady = Promise.resolve();
1951
1825
  this._disabled = false;
1952
1826
  this.nativeSelection = document.getSelection();
@@ -1989,7 +1863,7 @@ let NativeInput = class NativeInput extends Input {
1989
1863
  this.subscription.unsubscribe();
1990
1864
  }
1991
1865
  handleDefaultActions(textarea) {
1992
- this.subscription.add(stream.fromEvent(document, 'copy').subscribe(ev => {
1866
+ this.subscription.add(stream.fromEvent(isFirefox() ? textarea : document, 'copy').subscribe(ev => {
1993
1867
  const selection = this.selection;
1994
1868
  if (!selection.isSelected) {
1995
1869
  return;
@@ -2043,8 +1917,8 @@ let NativeInput = class NativeInput extends Input {
2043
1917
  });
2044
1918
  }));
2045
1919
  }
2046
- handlePaste(html, text) {
2047
- const slot = this.parser.parse(html, new core$1.Slot([
1920
+ handlePaste(dom, text) {
1921
+ const slot = this.parser.parse(dom, new core$1.Slot([
2048
1922
  core$1.ContentType.BlockComponent,
2049
1923
  core$1.ContentType.InlineComponent,
2050
1924
  core$1.ContentType.Text
@@ -2104,7 +1978,6 @@ let NativeInput = class NativeInput extends Input {
2104
1978
  let startIndex;
2105
1979
  const compositionStart = () => {
2106
1980
  this.composition = true;
2107
- this.compositionState = null;
2108
1981
  startIndex = this.selection.startOffset;
2109
1982
  const startSlot = this.selection.startSlot;
2110
1983
  const event = new core$1.Event(startSlot, {
@@ -2114,11 +1987,6 @@ let NativeInput = class NativeInput extends Input {
2114
1987
  };
2115
1988
  const compositionUpdate = (data) => {
2116
1989
  const startSlot = this.selection.startSlot;
2117
- this.compositionState = {
2118
- slot: startSlot,
2119
- index: startIndex,
2120
- data
2121
- };
2122
1990
  const event = new core$1.Event(startSlot, {
2123
1991
  index: startIndex,
2124
1992
  data
@@ -2142,6 +2010,10 @@ let NativeInput = class NativeInput extends Input {
2142
2010
  compositionUpdate(ev.data);
2143
2011
  }), stream.fromEvent(input, 'compositionend').subscribe(ev => {
2144
2012
  compositionEnd(ev.data);
2013
+ const startContainer = this.nativeSelection.focusNode;
2014
+ if (startContainer instanceof Text && startContainer.textContent === ev.data) {
2015
+ startContainer.remove();
2016
+ }
2145
2017
  }), stream.fromEvent(input, 'beforeinput').subscribe(ev => {
2146
2018
  var _a;
2147
2019
  switch (ev.inputType) {
@@ -2205,7 +2077,6 @@ let NativeInput = class NativeInput extends Input {
2205
2077
  return !this.ignoreComposition;
2206
2078
  })).subscribe(() => {
2207
2079
  this.composition = true;
2208
- this.compositionState = null;
2209
2080
  startIndex = this.selection.startOffset;
2210
2081
  const startSlot = this.selection.startSlot;
2211
2082
  const event = new core$1.Event(startSlot, {
@@ -2216,11 +2087,6 @@ let NativeInput = class NativeInput extends Input {
2216
2087
  return !this.ignoreComposition;
2217
2088
  })).subscribe(ev => {
2218
2089
  const startSlot = this.selection.startSlot;
2219
- this.compositionState = {
2220
- slot: startSlot,
2221
- index: startIndex,
2222
- data: ev.data
2223
- };
2224
2090
  const event = new core$1.Event(startSlot, {
2225
2091
  index: startIndex,
2226
2092
  data: ev.data
@@ -2270,8 +2136,11 @@ let NativeInput = class NativeInput extends Input {
2270
2136
  return !b;
2271
2137
  }))).subscribe(text => {
2272
2138
  this.composition = false;
2273
- this.compositionState = null;
2274
2139
  if (text) {
2140
+ const startContainer = this.nativeSelection.focusNode;
2141
+ if (startContainer instanceof Text && startContainer.textContent === text) {
2142
+ startContainer.remove();
2143
+ }
2275
2144
  this.commander.write(text);
2276
2145
  }
2277
2146
  if (isCompositionEnd) {
@@ -2285,21 +2154,20 @@ let NativeInput = class NativeInput extends Input {
2285
2154
  }));
2286
2155
  }
2287
2156
  };
2288
- NativeInput = __decorate([
2157
+ exports.NativeInput = __decorate([
2289
2158
  core.Injectable(),
2290
2159
  __metadata("design:paramtypes", [core$1.Textbus,
2291
2160
  exports.Parser,
2292
- core$1.Scheduler,
2293
2161
  core$1.Selection,
2294
2162
  core$1.Keyboard,
2295
2163
  DomAdapter,
2296
2164
  core$1.Commander,
2297
2165
  core$1.Controller])
2298
- ], NativeInput);
2166
+ ], exports.NativeInput);
2299
2167
 
2168
+ const browserErrorFn = core$1.makeError('BrowserModule');
2300
2169
  class BrowserModule {
2301
- constructor(host, config) {
2302
- this.host = host;
2170
+ constructor(config) {
2303
2171
  this.config = config;
2304
2172
  const { mask, wrapper } = BrowserModule.createLayout();
2305
2173
  wrapper.prepend(config.adapter.host);
@@ -2323,13 +2191,10 @@ class BrowserModule {
2323
2191
  useExisting: exports.SelectionBridge
2324
2192
  }, {
2325
2193
  provide: Input,
2326
- useClass: config.useContentEditable ? NativeInput : exports.MagicInput
2194
+ useClass: config.useContentEditable ? exports.NativeInput : exports.MagicInput
2327
2195
  }, {
2328
- provide: core$1.ViewAdapter,
2329
- useFactory(v) {
2330
- return v;
2331
- },
2332
- deps: [DomAdapter]
2196
+ provide: core$1.Adapter,
2197
+ useValue: config.adapter
2333
2198
  }, {
2334
2199
  provide: DomAdapter,
2335
2200
  useValue: config.adapter
@@ -2357,10 +2222,54 @@ class BrowserModule {
2357
2222
  exports.SelectionBridge,
2358
2223
  exports.CollaborateCursor];
2359
2224
  this.workbench = wrapper;
2360
- this.host.append(wrapper);
2361
2225
  }
2362
- onDestroy() {
2363
- this.workbench.remove();
2226
+ /**
2227
+ * 解析 HTML 并返回一个组件实例
2228
+ * @param html 要解析的 HTML
2229
+ * @param rootComponentLoader 文档根组件加载器
2230
+ * @param textbus
2231
+ */
2232
+ readDocumentByHTML(html, rootComponentLoader, textbus) {
2233
+ const parser = textbus.get(exports.Parser);
2234
+ const doc = parser.parseDoc(html, rootComponentLoader);
2235
+ if (doc instanceof core$1.Component) {
2236
+ return doc;
2237
+ }
2238
+ throw browserErrorFn('rootComponentLoader must return a component instance.');
2239
+ }
2240
+ /**
2241
+ * 将组件数据解析到组件实例中
2242
+ * @param data 要解析的 JSON 数据
2243
+ * @param rootComponent 根组件
2244
+ * @param textbus
2245
+ */
2246
+ readDocumentByComponentLiteral(data, rootComponent, textbus) {
2247
+ const registry = textbus.get(core$1.Registry);
2248
+ return registry.createComponentByFactory(data, rootComponent);
2249
+ }
2250
+ setup(textbus) {
2251
+ this.textbus = textbus;
2252
+ const host = this.config.renderTo();
2253
+ if (!(host instanceof HTMLElement)) {
2254
+ throw browserErrorFn('view container is not a HTMLElement');
2255
+ }
2256
+ const cursor = textbus.get(exports.CollaborateCursor);
2257
+ cursor.init();
2258
+ host.append(this.workbench);
2259
+ return () => {
2260
+ cursor.destroy();
2261
+ this.workbench.remove();
2262
+ };
2263
+ }
2264
+ onAfterStartup(textbus) {
2265
+ if (this.config.autoFocus) {
2266
+ textbus.focus();
2267
+ }
2268
+ }
2269
+ onDestroy(textbus) {
2270
+ textbus.get(Input).destroy();
2271
+ textbus.get(exports.SelectionBridge).destroy();
2272
+ textbus.get(exports.CollaborateCursor).destroy();
2364
2273
  }
2365
2274
  static createLayout() {
2366
2275
  const mask = createElement('div', {
@@ -2373,10 +2282,23 @@ class BrowserModule {
2373
2282
  right: 0,
2374
2283
  top: 0,
2375
2284
  bottom: 0,
2285
+ pointerEvents: 'none',
2286
+ // overflow: 'hidden'
2287
+ }
2288
+ });
2289
+ const maskWrapper = createElement('div', {
2290
+ styles: {
2291
+ position: 'absolute',
2292
+ left: 0,
2293
+ right: 0,
2294
+ top: 0,
2295
+ bottom: 0,
2296
+ margin: '0 -2px',
2376
2297
  zIndex: 1,
2377
2298
  pointerEvents: 'none',
2378
2299
  overflow: 'hidden'
2379
- }
2300
+ },
2301
+ children: [mask]
2380
2302
  });
2381
2303
  const wrapper = createElement('div', {
2382
2304
  attrs: {
@@ -2388,7 +2310,7 @@ class BrowserModule {
2388
2310
  position: 'relative',
2389
2311
  flexDirection: 'column'
2390
2312
  },
2391
- children: [mask]
2313
+ children: [maskWrapper]
2392
2314
  });
2393
2315
  return {
2394
2316
  wrapper,
@@ -2406,7 +2328,6 @@ exports.VIEW_CONTAINER = VIEW_CONTAINER;
2406
2328
  exports.VIEW_DOCUMENT = VIEW_DOCUMENT;
2407
2329
  exports.VIEW_MASK = VIEW_MASK;
2408
2330
  exports.createElement = createElement;
2409
- exports.createTextNode = createTextNode;
2410
2331
  exports.getLayoutRectByRange = getLayoutRectByRange;
2411
2332
  exports.isFirefox = isFirefox;
2412
2333
  exports.isMac = isMac;