@textbus/platform-browser 4.0.0-alpha.6 → 4.0.0-alpha.60

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
@@ -2,8 +2,9 @@
2
2
 
3
3
  require('reflect-metadata');
4
4
  var core$1 = require('@textbus/core');
5
- var core = require('@viewfly/core');
6
5
  var stream = require('@tanbo/stream');
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);
@@ -169,7 +170,6 @@ exports.Parser = Parser_1 = class Parser {
169
170
  return new DOMParser().parseFromString(html, 'text/html').body;
170
171
  }
171
172
  constructor(options, textbus) {
172
- this.options = options;
173
173
  this.textbus = textbus;
174
174
  const componentLoaders = [
175
175
  ...(options.componentLoaders || [])
@@ -214,8 +214,9 @@ exports.Parser = Parser_1 = class Parser {
214
214
  slot.insert('\n');
215
215
  return;
216
216
  }
217
+ const schema = [...slot.schema];
217
218
  for (const t of this.componentLoaders) {
218
- if (t.match(el)) {
219
+ if (t.match(el, schema)) {
219
220
  const result = t.read(el, this.textbus, (childSlot, slotRootElement, slotContentHostElement = slotRootElement) => {
220
221
  return this.readSlot(childSlot, slotRootElement, slotContentHostElement);
221
222
  });
@@ -302,13 +303,10 @@ exports.Parser = Parser_1 = __decorate([
302
303
  class Input {
303
304
  }
304
305
 
305
- /**
306
- * Textbus PC 端浏览器渲染能力桥接器抽象类,提供了 DOM 元素查询能力,具体渲染能力由各前端框架实现相应桥接
307
- */
308
- class DomAdapter extends core$1.ViewAdapter {
309
- constructor(mount) {
310
- super();
311
- this.mount = mount;
306
+ class DomAdapter extends core$1.Adapter {
307
+ constructor() {
308
+ super(...arguments);
309
+ this.onViewUpdated = new stream.Subject();
312
310
  this.host = createElement('div', {
313
311
  styles: {
314
312
  cursor: 'text',
@@ -324,126 +322,6 @@ class DomAdapter extends core$1.ViewAdapter {
324
322
  id: 'textbus-' + Number((Math.random() + '').substring(2)).toString(16)
325
323
  }
326
324
  });
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
325
  }
448
326
  }
449
327
 
@@ -542,7 +420,33 @@ exports.SelectionBridge = class SelectionBridge {
542
420
  this.listen(this.connector);
543
421
  return;
544
422
  }
545
- this.nativeSelection.setBaseAndExtent(anchor.node, anchor.offset, focus.node, focus.offset);
423
+ function tryOffset(position) {
424
+ if (!position.node) {
425
+ return;
426
+ }
427
+ if (position.node.nodeType === Node.TEXT_NODE) {
428
+ const len = position.node.textContent.length;
429
+ if (position.offset > len) {
430
+ position.offset = len;
431
+ }
432
+ }
433
+ else if (position.node.nodeType === Node.ELEMENT_NODE) {
434
+ const len = position.node.childNodes.length;
435
+ if (position.offset > len) {
436
+ position.offset = len;
437
+ }
438
+ }
439
+ }
440
+ try {
441
+ tryOffset(focus);
442
+ tryOffset(anchor);
443
+ this.nativeSelection.setBaseAndExtent(anchor.node, anchor.offset, focus.node, focus.offset);
444
+ }
445
+ catch (e) {
446
+ setTimeout(() => {
447
+ throw e;
448
+ });
449
+ }
546
450
  if (this.nativeSelection.rangeCount) {
547
451
  const nativeRange = this.nativeSelection.getRangeAt(0);
548
452
  this.selectionChangeEvent.next(nativeRange);
@@ -565,6 +469,7 @@ exports.SelectionBridge = class SelectionBridge {
565
469
  // hack end
566
470
  }
567
471
  destroy() {
472
+ this.subs.forEach(i => i.unsubscribe());
568
473
  this.sub.unsubscribe();
569
474
  }
570
475
  getPositionByRange(abstractSelection) {
@@ -647,9 +552,10 @@ exports.SelectionBridge = class SelectionBridge {
647
552
  }
648
553
  minLeft = rect2.left;
649
554
  minTop = rect2.top;
555
+ // oldPosition = position
650
556
  }
651
557
  if (isToPrevLine) {
652
- if (rect2.left < startLeft) {
558
+ if (rect2.left <= startLeft) {
653
559
  return position;
654
560
  }
655
561
  if (oldPosition) {
@@ -753,7 +659,7 @@ exports.SelectionBridge = class SelectionBridge {
753
659
  this.input.composition ||
754
660
  selection.rangeCount === 0 ||
755
661
  !this.docContainer.contains(selection.anchorNode) ||
756
- this.rootComponentRef.component.slots.length === 0) {
662
+ this.rootComponentRef.component.__slots__.length === 0) {
757
663
  return;
758
664
  }
759
665
  const rawRange = selection.getRangeAt(0);
@@ -762,14 +668,14 @@ exports.SelectionBridge = class SelectionBridge {
762
668
  const isFocusStart = selection.focusNode === nativeRange.startContainer && selection.focusOffset === nativeRange.startOffset;
763
669
  if (!this.docContainer.contains(selection.focusNode)) {
764
670
  if (isFocusEnd) {
765
- const nativeNode = this.domAdapter.getNativeNodeBySlot(this.rootComponentRef.component.slots.first);
671
+ const nativeNode = this.domAdapter.getNativeNodeBySlot(this.rootComponentRef.component.__slots__.first);
766
672
  if (!nativeNode) {
767
673
  return;
768
674
  }
769
675
  nativeRange.setEndAfter(nativeNode.lastChild);
770
676
  }
771
677
  else {
772
- const nativeNode = this.domAdapter.getNativeNodeBySlot(this.rootComponentRef.component.slots.last);
678
+ const nativeNode = this.domAdapter.getNativeNodeBySlot(this.rootComponentRef.component.__slots__.last);
773
679
  if (!nativeNode) {
774
680
  return;
775
681
  }
@@ -1030,25 +936,24 @@ class ExperimentalCaret {
1030
936
  get display() {
1031
937
  return this._display;
1032
938
  }
1033
- constructor(scheduler, editorMask) {
939
+ constructor(domRenderer, scheduler, editorMask) {
940
+ this.domRenderer = domRenderer;
1034
941
  this.scheduler = scheduler;
1035
942
  this.editorMask = editorMask;
1036
- this.compositionState = null;
1037
- this.compositionElement = createElement('span', {
1038
- styles: {
1039
- textDecoration: 'underline'
1040
- }
1041
- });
943
+ this.changeFromSelf = false;
944
+ this.getLimit = function () {
945
+ return {
946
+ top: 0,
947
+ bottom: document.documentElement.clientHeight
948
+ };
949
+ };
1042
950
  this.timer = null;
1043
- this.oldPosition = null;
1044
951
  this._display = true;
1045
952
  this.flashing = true;
1046
- this.subs = [];
1047
953
  this.subscription = new stream.Subscription();
1048
954
  this.positionChangeEvent = new stream.Subject();
1049
955
  this.styleChangeEvent = new stream.Subject();
1050
956
  this.oldRange = null;
1051
- this.isFixed = false;
1052
957
  this.onPositionChange = this.positionChangeEvent.pipe(stream.distinctUntilChanged());
1053
958
  this.onStyleChange = this.styleChangeEvent.asObservable();
1054
959
  this.elementRef = createElement('div', {
@@ -1076,20 +981,12 @@ class ExperimentalCaret {
1076
981
  }));
1077
982
  this.editorMask.appendChild(this.elementRef);
1078
983
  }
1079
- refresh(isFixedCaret = false) {
1080
- this.isFixed = isFixedCaret;
984
+ refresh() {
1081
985
  if (this.oldRange) {
1082
986
  this.show(this.oldRange, false);
1083
987
  }
1084
- this.isFixed = false;
1085
988
  }
1086
989
  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
990
  this.oldRange = range;
1094
991
  if (restart || this.scheduler.lastChangesHasLocalUpdate) {
1095
992
  clearTimeout(this.timer);
@@ -1118,62 +1015,8 @@ class ExperimentalCaret {
1118
1015
  }
1119
1016
  destroy() {
1120
1017
  clearTimeout(this.timer);
1018
+ // this.caret.
1121
1019
  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
1020
  }
1178
1021
  updateCursorPosition(nativeRange) {
1179
1022
  const startContainer = nativeRange.startContainer;
@@ -1182,12 +1025,10 @@ class ExperimentalCaret {
1182
1025
  this.positionChangeEvent.next(null);
1183
1026
  return;
1184
1027
  }
1185
- if (this.compositionState) {
1186
- const compositionElement = this.compositionElement;
1187
- compositionElement.innerText = this.compositionState.data;
1028
+ const compositionNode = this.domRenderer.compositionNode;
1029
+ if (compositionNode) {
1188
1030
  nativeRange = nativeRange.cloneRange();
1189
- nativeRange.insertNode(compositionElement);
1190
- nativeRange.selectNodeContents(compositionElement);
1031
+ nativeRange.selectNodeContents(compositionNode);
1191
1032
  nativeRange.collapse();
1192
1033
  }
1193
1034
  const rect = getLayoutRectByRange(nativeRange);
@@ -1233,6 +1074,21 @@ class ExperimentalCaret {
1233
1074
  top: rectTop,
1234
1075
  height: boxHeight
1235
1076
  });
1077
+ if (this.changeFromSelf) {
1078
+ this.changeFromSelf = false;
1079
+ const selfRect = this.elementRef.getBoundingClientRect();
1080
+ const limit = this.getLimit();
1081
+ if (selfRect.top < limit.top) {
1082
+ this.elementRef.scrollIntoView({
1083
+ block: 'start'
1084
+ });
1085
+ }
1086
+ else if (selfRect.bottom > limit.bottom) {
1087
+ this.elementRef.scrollIntoView({
1088
+ block: 'end'
1089
+ });
1090
+ }
1091
+ }
1236
1092
  }
1237
1093
  }
1238
1094
  /**
@@ -1248,8 +1104,9 @@ exports.MagicInput = class MagicInput extends Input {
1248
1104
  get disabled() {
1249
1105
  return this._disabled;
1250
1106
  }
1251
- constructor(parser, keyboard, commander, selection, controller, scheduler, textbus) {
1107
+ constructor(domAdapter, parser, keyboard, commander, selection, controller, scheduler, textbus) {
1252
1108
  super();
1109
+ this.domAdapter = domAdapter;
1253
1110
  this.parser = parser;
1254
1111
  this.keyboard = keyboard;
1255
1112
  this.commander = commander;
@@ -1258,8 +1115,7 @@ exports.MagicInput = class MagicInput extends Input {
1258
1115
  this.scheduler = scheduler;
1259
1116
  this.textbus = textbus;
1260
1117
  this.composition = false;
1261
- this.compositionState = null;
1262
- this.caret = new ExperimentalCaret(this.scheduler, this.textbus.get(VIEW_MASK));
1118
+ this.caret = new ExperimentalCaret(this.domAdapter, this.scheduler, this.textbus.get(VIEW_MASK));
1263
1119
  this.isSafari = isSafari();
1264
1120
  this.isFirefox = isFirefox();
1265
1121
  this.isMac = isMac();
@@ -1299,13 +1155,8 @@ exports.MagicInput = class MagicInput extends Input {
1299
1155
  if (!this.isFocus) {
1300
1156
  (_a = this.textarea) === null || _a === void 0 ? void 0 : _a.focus();
1301
1157
  setTimeout(() => {
1302
- var _a, _b, _c;
1303
1158
  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();
1159
+ this.reInit();
1309
1160
  }
1310
1161
  });
1311
1162
  }
@@ -1321,6 +1172,22 @@ exports.MagicInput = class MagicInput extends Input {
1321
1172
  this.caret.destroy();
1322
1173
  this.subscription.unsubscribe();
1323
1174
  }
1175
+ reInit(delay = false) {
1176
+ var _a, _b, _c;
1177
+ this.subscription.unsubscribe();
1178
+ (_b = (_a = this.textarea) === null || _a === void 0 ? void 0 : _a.parentNode) === null || _b === void 0 ? void 0 : _b.removeChild(this.textarea);
1179
+ this.subscription = new stream.Subscription();
1180
+ this.init();
1181
+ if (delay) {
1182
+ setTimeout(() => {
1183
+ var _a;
1184
+ (_a = this.textarea) === null || _a === void 0 ? void 0 : _a.focus();
1185
+ });
1186
+ }
1187
+ else {
1188
+ (_c = this.textarea) === null || _c === void 0 ? void 0 : _c.focus();
1189
+ }
1190
+ }
1324
1191
  init() {
1325
1192
  const doc = this.doc;
1326
1193
  const contentBody = doc.body;
@@ -1329,9 +1196,19 @@ exports.MagicInput = class MagicInput extends Input {
1329
1196
  contentBody.appendChild(textarea);
1330
1197
  this.textarea = textarea;
1331
1198
  this.subscription.add(stream.fromEvent(textarea, 'blur').subscribe(() => {
1199
+ // if (this.isFocus) {
1200
+ // this.isFocus = false
1201
+ // this.reInit(true)
1202
+ // }
1332
1203
  this.isFocus = false;
1333
1204
  this.nativeFocus = false;
1334
1205
  this.caret.hide();
1206
+ if (this.domAdapter.composition) {
1207
+ const slot = this.domAdapter.composition.slot;
1208
+ this.domAdapter.composition = null;
1209
+ this.domAdapter.compositionNode = null;
1210
+ slot.__changeMarker__.forceMarkDirtied();
1211
+ }
1335
1212
  }), stream.fromEvent(textarea, 'focus').subscribe(() => {
1336
1213
  this.nativeFocus = true;
1337
1214
  }), this.caret.onStyleChange.subscribe(style => {
@@ -1342,7 +1219,7 @@ exports.MagicInput = class MagicInput extends Input {
1342
1219
  this.handleDefaultActions(textarea);
1343
1220
  }
1344
1221
  handleDefaultActions(textarea) {
1345
- this.subscription.add(stream.fromEvent(document, 'copy').subscribe(ev => {
1222
+ this.subscription.add(stream.fromEvent(isFirefox() ? textarea : document, 'copy').subscribe(ev => {
1346
1223
  const selection = this.selection;
1347
1224
  if (!selection.isSelected) {
1348
1225
  return;
@@ -1412,6 +1289,7 @@ exports.MagicInput = class MagicInput extends Input {
1412
1289
  }), stream.fromEvent(textarea, 'compositionend').subscribe(() => {
1413
1290
  isWriting = false;
1414
1291
  }), stream.fromEvent(textarea, 'beforeinput').subscribe(ev => {
1292
+ this.ignoreComposition = false;
1415
1293
  if (this.isSafari) {
1416
1294
  if (ev.inputType === 'insertFromComposition') {
1417
1295
  isIgnore = true;
@@ -1433,6 +1311,7 @@ exports.MagicInput = class MagicInput extends Input {
1433
1311
  key = keys.charAt(+ev.code.substring(5));
1434
1312
  ev.preventDefault();
1435
1313
  }
1314
+ this.caret.changeFromSelf = true;
1436
1315
  const is = this.keyboard.execShortcut({
1437
1316
  key: key,
1438
1317
  altKey: ev.altKey,
@@ -1443,6 +1322,9 @@ exports.MagicInput = class MagicInput extends Input {
1443
1322
  this.ignoreComposition = true;
1444
1323
  ev.preventDefault();
1445
1324
  }
1325
+ else {
1326
+ this.caret.changeFromSelf = false;
1327
+ }
1446
1328
  }));
1447
1329
  }
1448
1330
  handleInput(textarea) {
@@ -1451,10 +1333,10 @@ exports.MagicInput = class MagicInput extends Input {
1451
1333
  return !this.ignoreComposition;
1452
1334
  })).subscribe(() => {
1453
1335
  if (!this.selection.isCollapsed) {
1336
+ this.caret.changeFromSelf = true;
1454
1337
  this.commander.delete();
1455
1338
  }
1456
1339
  this.composition = true;
1457
- this.caret.compositionState = this.compositionState = null;
1458
1340
  startIndex = this.selection.startOffset;
1459
1341
  const startSlot = this.selection.startSlot;
1460
1342
  const event = new core$1.Event(startSlot, {
@@ -1471,17 +1353,20 @@ exports.MagicInput = class MagicInput extends Input {
1471
1353
  return;
1472
1354
  }
1473
1355
  const startSlot = this.selection.startSlot;
1474
- this.caret.compositionState = this.compositionState = {
1356
+ this.domAdapter.composition = {
1475
1357
  slot: startSlot,
1476
- index: startIndex,
1477
- data: ev.data
1358
+ text: ev.data,
1359
+ offset: ev.data.length,
1360
+ index: startIndex
1478
1361
  };
1479
- this.caret.refresh(true);
1362
+ this.caret.changeFromSelf = true;
1363
+ this.caret.refresh();
1480
1364
  const event = new core$1.Event(startSlot, {
1481
1365
  index: startIndex,
1482
1366
  data: ev.data
1483
1367
  });
1484
1368
  core$1.invokeListener(startSlot.parent, 'onCompositionUpdate', event);
1369
+ startSlot.__changeMarker__.forceMarkDirtied();
1485
1370
  }));
1486
1371
  let isCompositionEnd = false;
1487
1372
  this.subscription.add(stream.merge(stream.fromEvent(textarea, 'beforeinput').pipe(stream.filter(ev => {
@@ -1505,24 +1390,16 @@ exports.MagicInput = class MagicInput extends Input {
1505
1390
  textarea.value = '';
1506
1391
  return ev.data;
1507
1392
  }))).subscribe(text => {
1393
+ var _a;
1508
1394
  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();
1395
+ this.domAdapter.composition = null;
1523
1396
  if (text) {
1397
+ this.caret.changeFromSelf = true;
1524
1398
  this.commander.write(text);
1525
1399
  }
1400
+ else {
1401
+ (_a = this.selection.startSlot) === null || _a === void 0 ? void 0 : _a.__changeMarker__.forceMarkDirtied();
1402
+ }
1526
1403
  if (isCompositionEnd) {
1527
1404
  const startSlot = this.selection.startSlot;
1528
1405
  if (startSlot) {
@@ -1551,7 +1428,8 @@ exports.MagicInput = class MagicInput extends Input {
1551
1428
  };
1552
1429
  exports.MagicInput = __decorate([
1553
1430
  core.Injectable(),
1554
- __metadata("design:paramtypes", [exports.Parser,
1431
+ __metadata("design:paramtypes", [DomAdapter,
1432
+ exports.Parser,
1555
1433
  core$1.Keyboard,
1556
1434
  core$1.Commander,
1557
1435
  core$1.Selection,
@@ -1569,10 +1447,11 @@ class CollaborateSelectionAwarenessDelegate {
1569
1447
  * 协作光标绘制类
1570
1448
  */
1571
1449
  exports.CollaborateCursor = class CollaborateCursor {
1572
- constructor(textbus, nativeSelection, scheduler, selection, awarenessDelegate) {
1450
+ constructor(textbus, nativeSelection, scheduler, selection, userActivity, awarenessDelegate) {
1573
1451
  this.nativeSelection = nativeSelection;
1574
1452
  this.scheduler = scheduler;
1575
1453
  this.selection = selection;
1454
+ this.userActivity = userActivity;
1576
1455
  this.awarenessDelegate = awarenessDelegate;
1577
1456
  this.host = createElement('div', {
1578
1457
  styles: {
@@ -1622,6 +1501,7 @@ exports.CollaborateCursor = class CollaborateCursor {
1622
1501
  this.onRectsChange = new stream.Subject();
1623
1502
  this.subscription = new stream.Subscription();
1624
1503
  this.currentSelection = [];
1504
+ this.ratio = window.devicePixelRatio || 1;
1625
1505
  this.container = textbus.get(VIEW_CONTAINER);
1626
1506
  this.canvasContainer.append(this.canvas);
1627
1507
  this.host.append(this.canvasContainer, this.tooltips);
@@ -1641,6 +1521,13 @@ exports.CollaborateCursor = class CollaborateCursor {
1641
1521
  this.refresh();
1642
1522
  }));
1643
1523
  }
1524
+ init() {
1525
+ if (this.userActivity) {
1526
+ this.subscription.add(this.userActivity.onStateChange.subscribe(v => {
1527
+ this.draw(v);
1528
+ }));
1529
+ }
1530
+ }
1644
1531
  /**
1645
1532
  * 刷新协作光标,由于 Textbus 只会绘制可视区域的光标,当可视区域发生变化时,需要重新绘制
1646
1533
  */
@@ -1658,15 +1545,16 @@ exports.CollaborateCursor = class CollaborateCursor {
1658
1545
  this.currentSelection = paths;
1659
1546
  const containerRect = this.container.getBoundingClientRect();
1660
1547
  this.canvas.style.top = containerRect.top * -1 + 'px';
1661
- this.canvas.width = this.canvas.offsetWidth;
1662
- this.canvas.height = this.canvas.offsetHeight;
1548
+ this.canvas.width = this.canvas.offsetWidth * this.ratio;
1549
+ this.canvas.height = this.canvas.offsetHeight * this.ratio;
1550
+ this.context.scale(this.ratio, this.ratio);
1663
1551
  this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
1664
1552
  const users = [];
1665
1553
  paths.filter(i => {
1666
- return i.paths.anchor.length && i.paths.focus.length;
1554
+ return i.selection.anchor.length && i.selection.focus.length;
1667
1555
  }).forEach(item => {
1668
- const anchorPaths = [...item.paths.anchor];
1669
- const focusPaths = [...item.paths.focus];
1556
+ const anchorPaths = [...item.selection.anchor];
1557
+ const focusPaths = [...item.selection.focus];
1670
1558
  const anchorOffset = anchorPaths.pop();
1671
1559
  const anchorSlot = this.selection.findSlotByPaths(anchorPaths);
1672
1560
  const focusOffset = focusPaths.pop();
@@ -1684,8 +1572,13 @@ exports.CollaborateCursor = class CollaborateCursor {
1684
1572
  return;
1685
1573
  }
1686
1574
  const nativeRange = document.createRange();
1687
- nativeRange.setStart(anchor.node, anchor.offset);
1688
- nativeRange.setEnd(focus.node, focus.offset);
1575
+ try {
1576
+ nativeRange.setStart(anchor.node, anchor.offset);
1577
+ nativeRange.setEnd(focus.node, focus.offset);
1578
+ }
1579
+ catch (e) {
1580
+ return;
1581
+ }
1689
1582
  if ((anchor.node !== focus.node || anchor.offset !== focus.offset) && nativeRange.collapsed) {
1690
1583
  nativeRange.setStart(focus.node, focus.offset);
1691
1584
  nativeRange.setEnd(anchor.node, anchor.offset);
@@ -1814,10 +1707,12 @@ exports.CollaborateCursor = class CollaborateCursor {
1814
1707
  exports.CollaborateCursor = __decorate([
1815
1708
  core.Injectable(),
1816
1709
  __param(4, core.Optional()),
1710
+ __param(5, core.Optional()),
1817
1711
  __metadata("design:paramtypes", [core$1.Textbus,
1818
1712
  exports.SelectionBridge,
1819
1713
  core$1.Scheduler,
1820
1714
  core$1.Selection,
1715
+ collaborate.UserActivity,
1821
1716
  CollaborateSelectionAwarenessDelegate])
1822
1717
  ], exports.CollaborateCursor);
1823
1718
 
@@ -1854,9 +1749,7 @@ class NativeCaret {
1854
1749
  height: 0
1855
1750
  };
1856
1751
  }
1857
- constructor(scheduler) {
1858
- this.scheduler = scheduler;
1859
- this.oldPosition = null;
1752
+ constructor() {
1860
1753
  this._nativeRange = null;
1861
1754
  this.subs = [];
1862
1755
  this.positionChangeEvent = new stream.Subject();
@@ -1865,65 +1758,12 @@ class NativeCaret {
1865
1758
  refresh() {
1866
1759
  //
1867
1760
  }
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
1761
  destroy() {
1922
1762
  this.subs.forEach(i => i.unsubscribe());
1923
1763
  this.subs = [];
1924
1764
  }
1925
1765
  }
1926
- let NativeInput = class NativeInput extends Input {
1766
+ exports.NativeInput = class NativeInput extends Input {
1927
1767
  set disabled(b) {
1928
1768
  this._disabled = b;
1929
1769
  if (this.controller.readonly) {
@@ -1935,18 +1775,17 @@ let NativeInput = class NativeInput extends Input {
1935
1775
  get disabled() {
1936
1776
  return this._disabled;
1937
1777
  }
1938
- constructor(textbus, parser, scheduler, selection, keyboard, domAdapter, commander, controller) {
1778
+ constructor(textbus, parser, selection, keyboard, domAdapter, commander, controller) {
1939
1779
  super();
1940
1780
  this.parser = parser;
1941
- this.scheduler = scheduler;
1942
1781
  this.selection = selection;
1943
1782
  this.keyboard = keyboard;
1944
1783
  this.domAdapter = domAdapter;
1945
1784
  this.commander = commander;
1946
1785
  this.controller = controller;
1947
- this.caret = new NativeCaret(this.scheduler);
1786
+ this.caret = new NativeCaret();
1948
1787
  this.composition = false;
1949
- this.compositionState = null;
1788
+ // compositionState: CompositionState | null = null
1950
1789
  this.onReady = Promise.resolve();
1951
1790
  this._disabled = false;
1952
1791
  this.nativeSelection = document.getSelection();
@@ -1989,7 +1828,7 @@ let NativeInput = class NativeInput extends Input {
1989
1828
  this.subscription.unsubscribe();
1990
1829
  }
1991
1830
  handleDefaultActions(textarea) {
1992
- this.subscription.add(stream.fromEvent(document, 'copy').subscribe(ev => {
1831
+ this.subscription.add(stream.fromEvent(isFirefox() ? textarea : document, 'copy').subscribe(ev => {
1993
1832
  const selection = this.selection;
1994
1833
  if (!selection.isSelected) {
1995
1834
  return;
@@ -2043,8 +1882,8 @@ let NativeInput = class NativeInput extends Input {
2043
1882
  });
2044
1883
  }));
2045
1884
  }
2046
- handlePaste(html, text) {
2047
- const slot = this.parser.parse(html, new core$1.Slot([
1885
+ handlePaste(dom, text) {
1886
+ const slot = this.parser.parse(dom, new core$1.Slot([
2048
1887
  core$1.ContentType.BlockComponent,
2049
1888
  core$1.ContentType.InlineComponent,
2050
1889
  core$1.ContentType.Text
@@ -2104,7 +1943,6 @@ let NativeInput = class NativeInput extends Input {
2104
1943
  let startIndex;
2105
1944
  const compositionStart = () => {
2106
1945
  this.composition = true;
2107
- this.compositionState = null;
2108
1946
  startIndex = this.selection.startOffset;
2109
1947
  const startSlot = this.selection.startSlot;
2110
1948
  const event = new core$1.Event(startSlot, {
@@ -2114,11 +1952,6 @@ let NativeInput = class NativeInput extends Input {
2114
1952
  };
2115
1953
  const compositionUpdate = (data) => {
2116
1954
  const startSlot = this.selection.startSlot;
2117
- this.compositionState = {
2118
- slot: startSlot,
2119
- index: startIndex,
2120
- data
2121
- };
2122
1955
  const event = new core$1.Event(startSlot, {
2123
1956
  index: startIndex,
2124
1957
  data
@@ -2205,7 +2038,6 @@ let NativeInput = class NativeInput extends Input {
2205
2038
  return !this.ignoreComposition;
2206
2039
  })).subscribe(() => {
2207
2040
  this.composition = true;
2208
- this.compositionState = null;
2209
2041
  startIndex = this.selection.startOffset;
2210
2042
  const startSlot = this.selection.startSlot;
2211
2043
  const event = new core$1.Event(startSlot, {
@@ -2216,11 +2048,6 @@ let NativeInput = class NativeInput extends Input {
2216
2048
  return !this.ignoreComposition;
2217
2049
  })).subscribe(ev => {
2218
2050
  const startSlot = this.selection.startSlot;
2219
- this.compositionState = {
2220
- slot: startSlot,
2221
- index: startIndex,
2222
- data: ev.data
2223
- };
2224
2051
  const event = new core$1.Event(startSlot, {
2225
2052
  index: startIndex,
2226
2053
  data: ev.data
@@ -2270,7 +2097,6 @@ let NativeInput = class NativeInput extends Input {
2270
2097
  return !b;
2271
2098
  }))).subscribe(text => {
2272
2099
  this.composition = false;
2273
- this.compositionState = null;
2274
2100
  if (text) {
2275
2101
  this.commander.write(text);
2276
2102
  }
@@ -2285,21 +2111,20 @@ let NativeInput = class NativeInput extends Input {
2285
2111
  }));
2286
2112
  }
2287
2113
  };
2288
- NativeInput = __decorate([
2114
+ exports.NativeInput = __decorate([
2289
2115
  core.Injectable(),
2290
2116
  __metadata("design:paramtypes", [core$1.Textbus,
2291
2117
  exports.Parser,
2292
- core$1.Scheduler,
2293
2118
  core$1.Selection,
2294
2119
  core$1.Keyboard,
2295
2120
  DomAdapter,
2296
2121
  core$1.Commander,
2297
2122
  core$1.Controller])
2298
- ], NativeInput);
2123
+ ], exports.NativeInput);
2299
2124
 
2125
+ const browserErrorFn = core$1.makeError('BrowserModule');
2300
2126
  class BrowserModule {
2301
- constructor(host, config) {
2302
- this.host = host;
2127
+ constructor(config) {
2303
2128
  this.config = config;
2304
2129
  const { mask, wrapper } = BrowserModule.createLayout();
2305
2130
  wrapper.prepend(config.adapter.host);
@@ -2323,26 +2148,85 @@ class BrowserModule {
2323
2148
  useExisting: exports.SelectionBridge
2324
2149
  }, {
2325
2150
  provide: Input,
2326
- useClass: config.useContentEditable ? NativeInput : exports.MagicInput
2151
+ useClass: config.useContentEditable ? exports.NativeInput : exports.MagicInput
2327
2152
  }, {
2328
- provide: core$1.ViewAdapter,
2329
- useFactory(v) {
2330
- return v;
2331
- },
2332
- deps: [DomAdapter]
2153
+ provide: core$1.Adapter,
2154
+ useValue: config.adapter
2333
2155
  }, {
2334
2156
  provide: DomAdapter,
2335
2157
  useValue: config.adapter
2158
+ }, {
2159
+ provide: core$1.FocusManager,
2160
+ useFactory: (input) => {
2161
+ const focusEvent = new stream.Subject();
2162
+ const blurEvent = new stream.Subject();
2163
+ input.caret.onPositionChange.pipe(stream.map(p => !!p), stream.distinctUntilChanged()).subscribe(b => {
2164
+ if (b) {
2165
+ focusEvent.next();
2166
+ }
2167
+ else {
2168
+ blurEvent.next();
2169
+ }
2170
+ });
2171
+ return {
2172
+ onFocus: focusEvent,
2173
+ onBlur: blurEvent
2174
+ };
2175
+ },
2176
+ deps: [Input]
2336
2177
  },
2337
2178
  exports.Parser,
2338
2179
  exports.SelectionBridge,
2339
- exports.CollaborateCursor
2340
- ];
2180
+ exports.CollaborateCursor];
2341
2181
  this.workbench = wrapper;
2342
- this.host.append(wrapper);
2343
2182
  }
2344
- onDestroy() {
2345
- this.workbench.remove();
2183
+ /**
2184
+ * 解析 HTML 并返回一个组件实例
2185
+ * @param html 要解析的 HTML
2186
+ * @param rootComponentLoader 文档根组件加载器
2187
+ * @param textbus
2188
+ */
2189
+ readDocumentByHTML(html, rootComponentLoader, textbus) {
2190
+ const parser = textbus.get(exports.Parser);
2191
+ const doc = parser.parseDoc(html, rootComponentLoader);
2192
+ if (doc instanceof core$1.Component) {
2193
+ return doc;
2194
+ }
2195
+ throw browserErrorFn('rootComponentLoader must return a component instance.');
2196
+ }
2197
+ /**
2198
+ * 将组件数据解析到组件实例中
2199
+ * @param data 要解析的 JSON 数据
2200
+ * @param rootComponent 根组件
2201
+ * @param textbus
2202
+ */
2203
+ readDocumentByComponentLiteral(data, rootComponent, textbus) {
2204
+ const registry = textbus.get(core$1.Registry);
2205
+ return registry.createComponentByFactory(data, rootComponent);
2206
+ }
2207
+ setup(textbus) {
2208
+ this.textbus = textbus;
2209
+ const host = this.config.renderTo();
2210
+ if (!(host instanceof HTMLElement)) {
2211
+ throw browserErrorFn('view container is not a HTMLElement');
2212
+ }
2213
+ const cursor = textbus.get(exports.CollaborateCursor);
2214
+ cursor.init();
2215
+ host.append(this.workbench);
2216
+ return () => {
2217
+ cursor.destroy();
2218
+ this.workbench.remove();
2219
+ };
2220
+ }
2221
+ onAfterStartup(textbus) {
2222
+ if (this.config.autoFocus) {
2223
+ textbus.focus();
2224
+ }
2225
+ }
2226
+ onDestroy(textbus) {
2227
+ textbus.get(Input).destroy();
2228
+ textbus.get(exports.SelectionBridge).destroy();
2229
+ textbus.get(exports.CollaborateCursor).destroy();
2346
2230
  }
2347
2231
  static createLayout() {
2348
2232
  const mask = createElement('div', {
@@ -2355,10 +2239,23 @@ class BrowserModule {
2355
2239
  right: 0,
2356
2240
  top: 0,
2357
2241
  bottom: 0,
2242
+ pointerEvents: 'none',
2243
+ // overflow: 'hidden'
2244
+ }
2245
+ });
2246
+ const maskWrapper = createElement('div', {
2247
+ styles: {
2248
+ position: 'absolute',
2249
+ left: 0,
2250
+ right: 0,
2251
+ top: 0,
2252
+ bottom: 0,
2253
+ margin: '0 -2px',
2358
2254
  zIndex: 1,
2359
2255
  pointerEvents: 'none',
2360
2256
  overflow: 'hidden'
2361
- }
2257
+ },
2258
+ children: [mask]
2362
2259
  });
2363
2260
  const wrapper = createElement('div', {
2364
2261
  attrs: {
@@ -2370,7 +2267,7 @@ class BrowserModule {
2370
2267
  position: 'relative',
2371
2268
  flexDirection: 'column'
2372
2269
  },
2373
- children: [mask]
2270
+ children: [maskWrapper]
2374
2271
  });
2375
2272
  return {
2376
2273
  wrapper,