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

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.
@@ -1,7 +1,8 @@
1
1
  import 'reflect-metadata';
2
- import { Slot, Textbus, ViewAdapter, createBidirectionalMapping, ComponentInstance, VElement, VTextNode, Controller, Selection, RootComponentRef, ContentType, Event, invokeListener, Keyboard, Commander, Scheduler, NativeSelectionBridge } from '@textbus/core';
3
- import { InjectionToken, Injectable, Inject, Optional } from '@viewfly/core';
2
+ import { Slot, Textbus, Adapter, Controller, Selection, RootComponentRef, ContentType, Event, invokeListener, Keyboard, Commander, Scheduler, makeError, NativeSelectionBridge, FocusManager, Component, Registry } from '@textbus/core';
4
3
  import { Subject, filter, fromEvent, Subscription, distinctUntilChanged, merge, map, Observable } from '@tanbo/stream';
4
+ import { InjectionToken, Injectable, Inject, Optional } from '@viewfly/core';
5
+ import { UserActivity } from '@textbus/collaborate';
5
6
 
6
7
  function createElement(tagName, options = {}) {
7
8
  const el = document.createElement(tagName);
@@ -167,7 +168,6 @@ let Parser = Parser_1 = class Parser {
167
168
  return new DOMParser().parseFromString(html, 'text/html').body;
168
169
  }
169
170
  constructor(options, textbus) {
170
- this.options = options;
171
171
  this.textbus = textbus;
172
172
  const componentLoaders = [
173
173
  ...(options.componentLoaders || [])
@@ -212,8 +212,9 @@ let Parser = Parser_1 = class Parser {
212
212
  slot.insert('\n');
213
213
  return;
214
214
  }
215
+ const schema = [...slot.schema];
215
216
  for (const t of this.componentLoaders) {
216
- if (t.match(el)) {
217
+ if (t.match(el, schema)) {
217
218
  const result = t.read(el, this.textbus, (childSlot, slotRootElement, slotContentHostElement = slotRootElement) => {
218
219
  return this.readSlot(childSlot, slotRootElement, slotContentHostElement);
219
220
  });
@@ -300,13 +301,10 @@ Parser = Parser_1 = __decorate([
300
301
  class Input {
301
302
  }
302
303
 
303
- /**
304
- * Textbus PC 端浏览器渲染能力桥接器抽象类,提供了 DOM 元素查询能力,具体渲染能力由各前端框架实现相应桥接
305
- */
306
- class DomAdapter extends ViewAdapter {
307
- constructor(mount) {
308
- super();
309
- this.mount = mount;
304
+ class DomAdapter extends Adapter {
305
+ constructor() {
306
+ super(...arguments);
307
+ this.onViewUpdated = new Subject();
310
308
  this.host = createElement('div', {
311
309
  styles: {
312
310
  cursor: 'text',
@@ -322,126 +320,6 @@ class DomAdapter extends ViewAdapter {
322
320
  id: 'textbus-' + Number((Math.random() + '').substring(2)).toString(16)
323
321
  }
324
322
  });
325
- this.componentRootElementCaches = createBidirectionalMapping(a => {
326
- return a instanceof ComponentInstance;
327
- });
328
- this.slotRootNativeElementCaches = createBidirectionalMapping(a => {
329
- return a instanceof Slot;
330
- });
331
- this.slotRootVElementCaches = new WeakMap();
332
- }
333
- render(rootComponent) {
334
- const view = this.componentRender(rootComponent);
335
- return this.mount(this.host, view);
336
- }
337
- copy() {
338
- document.execCommand('copy');
339
- }
340
- /**
341
- * 根据组件获取组件的根 DOM 节点
342
- * @param component
343
- */
344
- getNativeNodeByComponent(component) {
345
- return this.componentRootElementCaches.get(component) || null;
346
- }
347
- /**
348
- * 根据 DOM 节点,获对对应的组件根节点,如传入的 DOM 节点不为组件的根节点,则返回 null
349
- * @param node
350
- */
351
- getComponentByNativeNode(node) {
352
- return this.componentRootElementCaches.get(node) || null;
353
- }
354
- /**
355
- * 根据插槽获取插槽的根 DOM 节点
356
- * @param slot
357
- */
358
- getNativeNodeBySlot(slot) {
359
- return this.slotRootNativeElementCaches.get(slot) || null;
360
- }
361
- /**
362
- * 根据 DOM 节点,获对对应的插槽根节点,如传入的 DOM 节点不为插槽的根节点,则返回 null
363
- * @param node
364
- */
365
- getSlotByNativeNode(node) {
366
- return this.slotRootNativeElementCaches.get(node) || null;
367
- }
368
- /**
369
- * 获取插槽内容节点集合
370
- * @param slot
371
- */
372
- getNodesBySlot(slot) {
373
- const rootNativeNode = this.getNativeNodeBySlot(slot);
374
- if (!rootNativeNode) {
375
- return [];
376
- }
377
- const rootVNode = this.slotRootVElementCaches.get(slot);
378
- const getNodes = (vElement, nativeNode, result) => {
379
- if (vElement.location) {
380
- result.push(nativeNode);
381
- }
382
- for (let i = 0; i < vElement.children.length; i++) {
383
- const vChild = vElement.children[i];
384
- const nativeChild = nativeNode.childNodes[i];
385
- if (vChild instanceof VElement) {
386
- getNodes(vChild, nativeChild, result);
387
- }
388
- else if (vChild instanceof VTextNode) {
389
- result.push(nativeChild);
390
- }
391
- else {
392
- result.push(this.getNativeNodeByComponent(vChild));
393
- }
394
- }
395
- return result;
396
- };
397
- return getNodes(rootVNode, rootNativeNode, []);
398
- }
399
- /**
400
- * 获取原生节点的原始数据在文档中的位置
401
- * @param node
402
- */
403
- getLocationByNativeNode(node) {
404
- let slotRootNode = node;
405
- while (!this.slotRootNativeElementCaches.get(slotRootNode)) {
406
- slotRootNode = slotRootNode.parentNode;
407
- if (!slotRootNode) {
408
- return null;
409
- }
410
- }
411
- const slot = this.slotRootNativeElementCaches.get(slotRootNode);
412
- const rootVNode = this.slotRootVElementCaches.get(slot);
413
- const getLocation = (target, tree, vNodeTree) => {
414
- if (target === tree) {
415
- return Object.assign({}, vNodeTree.location);
416
- }
417
- const childNodes = tree.childNodes;
418
- for (let i = 0; i < childNodes.length; i++) {
419
- const child = vNodeTree.children[i];
420
- const nativeChild = tree.childNodes[i];
421
- if (nativeChild === target) {
422
- if (child instanceof ComponentInstance) {
423
- const index = child.parent.indexOf(child);
424
- return {
425
- slot: child.parent,
426
- startIndex: index,
427
- endIndex: index + 1
428
- };
429
- }
430
- return child.location;
431
- }
432
- else if (child instanceof VElement) {
433
- let r = null;
434
- if (nativeChild.nodeType === Node.ELEMENT_NODE) {
435
- r = getLocation(target, nativeChild, child);
436
- }
437
- if (r) {
438
- return r;
439
- }
440
- }
441
- }
442
- return null;
443
- };
444
- return getLocation(node, slotRootNode, rootVNode);
445
323
  }
446
324
  }
447
325
 
@@ -540,7 +418,33 @@ let SelectionBridge = class SelectionBridge {
540
418
  this.listen(this.connector);
541
419
  return;
542
420
  }
543
- this.nativeSelection.setBaseAndExtent(anchor.node, anchor.offset, focus.node, focus.offset);
421
+ function tryOffset(position) {
422
+ if (!position.node) {
423
+ return;
424
+ }
425
+ if (position.node.nodeType === Node.TEXT_NODE) {
426
+ const len = position.node.textContent.length;
427
+ if (position.offset > len) {
428
+ position.offset = len;
429
+ }
430
+ }
431
+ else if (position.node.nodeType === Node.ELEMENT_NODE) {
432
+ const len = position.node.childNodes.length;
433
+ if (position.offset > len) {
434
+ position.offset = len;
435
+ }
436
+ }
437
+ }
438
+ try {
439
+ tryOffset(focus);
440
+ tryOffset(anchor);
441
+ this.nativeSelection.setBaseAndExtent(anchor.node, anchor.offset, focus.node, focus.offset);
442
+ }
443
+ catch (e) {
444
+ setTimeout(() => {
445
+ throw e;
446
+ });
447
+ }
544
448
  if (this.nativeSelection.rangeCount) {
545
449
  const nativeRange = this.nativeSelection.getRangeAt(0);
546
450
  this.selectionChangeEvent.next(nativeRange);
@@ -563,6 +467,7 @@ let SelectionBridge = class SelectionBridge {
563
467
  // hack end
564
468
  }
565
469
  destroy() {
470
+ this.subs.forEach(i => i.unsubscribe());
566
471
  this.sub.unsubscribe();
567
472
  }
568
473
  getPositionByRange(abstractSelection) {
@@ -645,9 +550,10 @@ let SelectionBridge = class SelectionBridge {
645
550
  }
646
551
  minLeft = rect2.left;
647
552
  minTop = rect2.top;
553
+ // oldPosition = position
648
554
  }
649
555
  if (isToPrevLine) {
650
- if (rect2.left < startLeft) {
556
+ if (rect2.left <= startLeft) {
651
557
  return position;
652
558
  }
653
559
  if (oldPosition) {
@@ -751,7 +657,7 @@ let SelectionBridge = class SelectionBridge {
751
657
  this.input.composition ||
752
658
  selection.rangeCount === 0 ||
753
659
  !this.docContainer.contains(selection.anchorNode) ||
754
- this.rootComponentRef.component.slots.length === 0) {
660
+ this.rootComponentRef.component.__slots__.length === 0) {
755
661
  return;
756
662
  }
757
663
  const rawRange = selection.getRangeAt(0);
@@ -760,14 +666,14 @@ let SelectionBridge = class SelectionBridge {
760
666
  const isFocusStart = selection.focusNode === nativeRange.startContainer && selection.focusOffset === nativeRange.startOffset;
761
667
  if (!this.docContainer.contains(selection.focusNode)) {
762
668
  if (isFocusEnd) {
763
- const nativeNode = this.domAdapter.getNativeNodeBySlot(this.rootComponentRef.component.slots.first);
669
+ const nativeNode = this.domAdapter.getNativeNodeBySlot(this.rootComponentRef.component.__slots__.first);
764
670
  if (!nativeNode) {
765
671
  return;
766
672
  }
767
673
  nativeRange.setEndAfter(nativeNode.lastChild);
768
674
  }
769
675
  else {
770
- const nativeNode = this.domAdapter.getNativeNodeBySlot(this.rootComponentRef.component.slots.last);
676
+ const nativeNode = this.domAdapter.getNativeNodeBySlot(this.rootComponentRef.component.__slots__.last);
771
677
  if (!nativeNode) {
772
678
  return;
773
679
  }
@@ -1028,25 +934,24 @@ class ExperimentalCaret {
1028
934
  get display() {
1029
935
  return this._display;
1030
936
  }
1031
- constructor(scheduler, editorMask) {
937
+ constructor(domRenderer, scheduler, editorMask) {
938
+ this.domRenderer = domRenderer;
1032
939
  this.scheduler = scheduler;
1033
940
  this.editorMask = editorMask;
1034
- this.compositionState = null;
1035
- this.compositionElement = createElement('span', {
1036
- styles: {
1037
- textDecoration: 'underline'
1038
- }
1039
- });
941
+ this.changeFromSelf = false;
942
+ this.getLimit = function () {
943
+ return {
944
+ top: 0,
945
+ bottom: document.documentElement.clientHeight
946
+ };
947
+ };
1040
948
  this.timer = null;
1041
- this.oldPosition = null;
1042
949
  this._display = true;
1043
950
  this.flashing = true;
1044
- this.subs = [];
1045
951
  this.subscription = new Subscription();
1046
952
  this.positionChangeEvent = new Subject();
1047
953
  this.styleChangeEvent = new Subject();
1048
954
  this.oldRange = null;
1049
- this.isFixed = false;
1050
955
  this.onPositionChange = this.positionChangeEvent.pipe(distinctUntilChanged());
1051
956
  this.onStyleChange = this.styleChangeEvent.asObservable();
1052
957
  this.elementRef = createElement('div', {
@@ -1074,20 +979,12 @@ class ExperimentalCaret {
1074
979
  }));
1075
980
  this.editorMask.appendChild(this.elementRef);
1076
981
  }
1077
- refresh(isFixedCaret = false) {
1078
- this.isFixed = isFixedCaret;
982
+ refresh() {
1079
983
  if (this.oldRange) {
1080
984
  this.show(this.oldRange, false);
1081
985
  }
1082
- this.isFixed = false;
1083
986
  }
1084
987
  show(range, restart) {
1085
- const oldRect = this.elementRef.getBoundingClientRect();
1086
- this.oldPosition = {
1087
- top: oldRect.top,
1088
- left: oldRect.left,
1089
- height: oldRect.height
1090
- };
1091
988
  this.oldRange = range;
1092
989
  if (restart || this.scheduler.lastChangesHasLocalUpdate) {
1093
990
  clearTimeout(this.timer);
@@ -1116,62 +1013,8 @@ class ExperimentalCaret {
1116
1013
  }
1117
1014
  destroy() {
1118
1015
  clearTimeout(this.timer);
1016
+ // this.caret.
1119
1017
  this.subscription.unsubscribe();
1120
- this.subs.forEach(i => i.unsubscribe());
1121
- }
1122
- correctScrollTop(scroller) {
1123
- this.subs.forEach(i => i.unsubscribe());
1124
- this.subs = [];
1125
- const scheduler = this.scheduler;
1126
- let docIsChanged = true;
1127
- function limitPosition(position) {
1128
- const { top, bottom } = scroller.getLimit();
1129
- const caretTop = position.top;
1130
- if (caretTop + position.height > bottom) {
1131
- const offset = caretTop - bottom + position.height;
1132
- scroller.setOffset(offset);
1133
- }
1134
- else if (position.top < top) {
1135
- scroller.setOffset(-(top - position.top));
1136
- }
1137
- }
1138
- let isPressed = false;
1139
- this.subs.push(scroller.onScroll.subscribe(() => {
1140
- if (this.oldPosition) {
1141
- const rect = this.rect;
1142
- this.oldPosition.top = rect.top;
1143
- this.oldPosition.left = rect.left;
1144
- this.oldPosition.height = rect.height;
1145
- }
1146
- }), fromEvent(document, 'mousedown', true).subscribe(() => {
1147
- isPressed = true;
1148
- }), fromEvent(document, 'mouseup', true).subscribe(() => {
1149
- isPressed = false;
1150
- }), scheduler.onDocChange.subscribe(() => {
1151
- docIsChanged = true;
1152
- }), this.onPositionChange.subscribe(position => {
1153
- if (position) {
1154
- if (docIsChanged) {
1155
- if (scheduler.lastChangesHasLocalUpdate) {
1156
- limitPosition(position);
1157
- }
1158
- else if (this.oldPosition) {
1159
- const offset = Math.floor(position.top - this.oldPosition.top);
1160
- scroller.setOffset(offset);
1161
- }
1162
- }
1163
- else if (!isPressed) {
1164
- if (this.isFixed && this.oldPosition) {
1165
- const offset = Math.floor(position.top - this.oldPosition.top);
1166
- scroller.setOffset(offset);
1167
- }
1168
- else {
1169
- limitPosition(position);
1170
- }
1171
- }
1172
- }
1173
- docIsChanged = false;
1174
- }));
1175
1018
  }
1176
1019
  updateCursorPosition(nativeRange) {
1177
1020
  const startContainer = nativeRange.startContainer;
@@ -1180,12 +1023,10 @@ class ExperimentalCaret {
1180
1023
  this.positionChangeEvent.next(null);
1181
1024
  return;
1182
1025
  }
1183
- if (this.compositionState) {
1184
- const compositionElement = this.compositionElement;
1185
- compositionElement.innerText = this.compositionState.data;
1026
+ const compositionNode = this.domRenderer.compositionNode;
1027
+ if (compositionNode) {
1186
1028
  nativeRange = nativeRange.cloneRange();
1187
- nativeRange.insertNode(compositionElement);
1188
- nativeRange.selectNodeContents(compositionElement);
1029
+ nativeRange.selectNodeContents(compositionNode);
1189
1030
  nativeRange.collapse();
1190
1031
  }
1191
1032
  const rect = getLayoutRectByRange(nativeRange);
@@ -1231,6 +1072,21 @@ class ExperimentalCaret {
1231
1072
  top: rectTop,
1232
1073
  height: boxHeight
1233
1074
  });
1075
+ if (this.changeFromSelf) {
1076
+ this.changeFromSelf = false;
1077
+ const selfRect = this.elementRef.getBoundingClientRect();
1078
+ const limit = this.getLimit();
1079
+ if (selfRect.top < limit.top) {
1080
+ this.elementRef.scrollIntoView({
1081
+ block: 'start'
1082
+ });
1083
+ }
1084
+ else if (selfRect.bottom > limit.bottom) {
1085
+ this.elementRef.scrollIntoView({
1086
+ block: 'end'
1087
+ });
1088
+ }
1089
+ }
1234
1090
  }
1235
1091
  }
1236
1092
  /**
@@ -1246,8 +1102,9 @@ let MagicInput = class MagicInput extends Input {
1246
1102
  get disabled() {
1247
1103
  return this._disabled;
1248
1104
  }
1249
- constructor(parser, keyboard, commander, selection, controller, scheduler, textbus) {
1105
+ constructor(domAdapter, parser, keyboard, commander, selection, controller, scheduler, textbus) {
1250
1106
  super();
1107
+ this.domAdapter = domAdapter;
1251
1108
  this.parser = parser;
1252
1109
  this.keyboard = keyboard;
1253
1110
  this.commander = commander;
@@ -1256,8 +1113,7 @@ let MagicInput = class MagicInput extends Input {
1256
1113
  this.scheduler = scheduler;
1257
1114
  this.textbus = textbus;
1258
1115
  this.composition = false;
1259
- this.compositionState = null;
1260
- this.caret = new ExperimentalCaret(this.scheduler, this.textbus.get(VIEW_MASK));
1116
+ this.caret = new ExperimentalCaret(this.domAdapter, this.scheduler, this.textbus.get(VIEW_MASK));
1261
1117
  this.isSafari = isSafari();
1262
1118
  this.isFirefox = isFirefox();
1263
1119
  this.isMac = isMac();
@@ -1297,13 +1153,8 @@ let MagicInput = class MagicInput extends Input {
1297
1153
  if (!this.isFocus) {
1298
1154
  (_a = this.textarea) === null || _a === void 0 ? void 0 : _a.focus();
1299
1155
  setTimeout(() => {
1300
- var _a, _b, _c;
1301
1156
  if (!this.nativeFocus && this.isFocus) {
1302
- this.subscription.unsubscribe();
1303
- (_b = (_a = this.textarea) === null || _a === void 0 ? void 0 : _a.parentNode) === null || _b === void 0 ? void 0 : _b.removeChild(this.textarea);
1304
- this.subscription = new Subscription();
1305
- this.init();
1306
- (_c = this.textarea) === null || _c === void 0 ? void 0 : _c.focus();
1157
+ this.reInit();
1307
1158
  }
1308
1159
  });
1309
1160
  }
@@ -1319,6 +1170,22 @@ let MagicInput = class MagicInput extends Input {
1319
1170
  this.caret.destroy();
1320
1171
  this.subscription.unsubscribe();
1321
1172
  }
1173
+ reInit(delay = false) {
1174
+ var _a, _b, _c;
1175
+ this.subscription.unsubscribe();
1176
+ (_b = (_a = this.textarea) === null || _a === void 0 ? void 0 : _a.parentNode) === null || _b === void 0 ? void 0 : _b.removeChild(this.textarea);
1177
+ this.subscription = new Subscription();
1178
+ this.init();
1179
+ if (delay) {
1180
+ setTimeout(() => {
1181
+ var _a;
1182
+ (_a = this.textarea) === null || _a === void 0 ? void 0 : _a.focus();
1183
+ });
1184
+ }
1185
+ else {
1186
+ (_c = this.textarea) === null || _c === void 0 ? void 0 : _c.focus();
1187
+ }
1188
+ }
1322
1189
  init() {
1323
1190
  const doc = this.doc;
1324
1191
  const contentBody = doc.body;
@@ -1327,9 +1194,19 @@ let MagicInput = class MagicInput extends Input {
1327
1194
  contentBody.appendChild(textarea);
1328
1195
  this.textarea = textarea;
1329
1196
  this.subscription.add(fromEvent(textarea, 'blur').subscribe(() => {
1197
+ // if (this.isFocus) {
1198
+ // this.isFocus = false
1199
+ // this.reInit(true)
1200
+ // }
1330
1201
  this.isFocus = false;
1331
1202
  this.nativeFocus = false;
1332
1203
  this.caret.hide();
1204
+ if (this.domAdapter.composition) {
1205
+ const slot = this.domAdapter.composition.slot;
1206
+ this.domAdapter.composition = null;
1207
+ this.domAdapter.compositionNode = null;
1208
+ slot.__changeMarker__.forceMarkDirtied();
1209
+ }
1333
1210
  }), fromEvent(textarea, 'focus').subscribe(() => {
1334
1211
  this.nativeFocus = true;
1335
1212
  }), this.caret.onStyleChange.subscribe(style => {
@@ -1340,7 +1217,7 @@ let MagicInput = class MagicInput extends Input {
1340
1217
  this.handleDefaultActions(textarea);
1341
1218
  }
1342
1219
  handleDefaultActions(textarea) {
1343
- this.subscription.add(fromEvent(document, 'copy').subscribe(ev => {
1220
+ this.subscription.add(fromEvent(isFirefox() ? textarea : document, 'copy').subscribe(ev => {
1344
1221
  const selection = this.selection;
1345
1222
  if (!selection.isSelected) {
1346
1223
  return;
@@ -1410,6 +1287,7 @@ let MagicInput = class MagicInput extends Input {
1410
1287
  }), fromEvent(textarea, 'compositionend').subscribe(() => {
1411
1288
  isWriting = false;
1412
1289
  }), fromEvent(textarea, 'beforeinput').subscribe(ev => {
1290
+ this.ignoreComposition = false;
1413
1291
  if (this.isSafari) {
1414
1292
  if (ev.inputType === 'insertFromComposition') {
1415
1293
  isIgnore = true;
@@ -1431,6 +1309,7 @@ let MagicInput = class MagicInput extends Input {
1431
1309
  key = keys.charAt(+ev.code.substring(5));
1432
1310
  ev.preventDefault();
1433
1311
  }
1312
+ this.caret.changeFromSelf = true;
1434
1313
  const is = this.keyboard.execShortcut({
1435
1314
  key: key,
1436
1315
  altKey: ev.altKey,
@@ -1441,6 +1320,9 @@ let MagicInput = class MagicInput extends Input {
1441
1320
  this.ignoreComposition = true;
1442
1321
  ev.preventDefault();
1443
1322
  }
1323
+ else {
1324
+ this.caret.changeFromSelf = false;
1325
+ }
1444
1326
  }));
1445
1327
  }
1446
1328
  handleInput(textarea) {
@@ -1449,10 +1331,10 @@ let MagicInput = class MagicInput extends Input {
1449
1331
  return !this.ignoreComposition;
1450
1332
  })).subscribe(() => {
1451
1333
  if (!this.selection.isCollapsed) {
1334
+ this.caret.changeFromSelf = true;
1452
1335
  this.commander.delete();
1453
1336
  }
1454
1337
  this.composition = true;
1455
- this.caret.compositionState = this.compositionState = null;
1456
1338
  startIndex = this.selection.startOffset;
1457
1339
  const startSlot = this.selection.startSlot;
1458
1340
  const event = new Event(startSlot, {
@@ -1469,17 +1351,20 @@ let MagicInput = class MagicInput extends Input {
1469
1351
  return;
1470
1352
  }
1471
1353
  const startSlot = this.selection.startSlot;
1472
- this.caret.compositionState = this.compositionState = {
1354
+ this.domAdapter.composition = {
1473
1355
  slot: startSlot,
1474
- index: startIndex,
1475
- data: ev.data
1356
+ text: ev.data,
1357
+ offset: ev.data.length,
1358
+ index: startIndex
1476
1359
  };
1477
- this.caret.refresh(true);
1360
+ this.caret.changeFromSelf = true;
1361
+ this.caret.refresh();
1478
1362
  const event = new Event(startSlot, {
1479
1363
  index: startIndex,
1480
1364
  data: ev.data
1481
1365
  });
1482
1366
  invokeListener(startSlot.parent, 'onCompositionUpdate', event);
1367
+ startSlot.__changeMarker__.forceMarkDirtied();
1483
1368
  }));
1484
1369
  let isCompositionEnd = false;
1485
1370
  this.subscription.add(merge(fromEvent(textarea, 'beforeinput').pipe(filter(ev => {
@@ -1503,24 +1388,16 @@ let MagicInput = class MagicInput extends Input {
1503
1388
  textarea.value = '';
1504
1389
  return ev.data;
1505
1390
  }))).subscribe(text => {
1391
+ var _a;
1506
1392
  this.composition = false;
1507
- this.caret.compositionState = this.compositionState = null;
1508
- const compositionElement = this.caret.compositionElement;
1509
- let nextSibling = compositionElement.nextSibling;
1510
- while (nextSibling) {
1511
- if (!nextSibling.textContent) {
1512
- const next = nextSibling.nextSibling;
1513
- nextSibling.remove();
1514
- nextSibling = next;
1515
- continue;
1516
- }
1517
- nextSibling.remove();
1518
- break;
1519
- }
1520
- compositionElement.remove();
1393
+ this.domAdapter.composition = null;
1521
1394
  if (text) {
1395
+ this.caret.changeFromSelf = true;
1522
1396
  this.commander.write(text);
1523
1397
  }
1398
+ else {
1399
+ (_a = this.selection.startSlot) === null || _a === void 0 ? void 0 : _a.__changeMarker__.forceMarkDirtied();
1400
+ }
1524
1401
  if (isCompositionEnd) {
1525
1402
  const startSlot = this.selection.startSlot;
1526
1403
  if (startSlot) {
@@ -1549,7 +1426,8 @@ let MagicInput = class MagicInput extends Input {
1549
1426
  };
1550
1427
  MagicInput = __decorate([
1551
1428
  Injectable(),
1552
- __metadata("design:paramtypes", [Parser,
1429
+ __metadata("design:paramtypes", [DomAdapter,
1430
+ Parser,
1553
1431
  Keyboard,
1554
1432
  Commander,
1555
1433
  Selection,
@@ -1567,10 +1445,11 @@ class CollaborateSelectionAwarenessDelegate {
1567
1445
  * 协作光标绘制类
1568
1446
  */
1569
1447
  let CollaborateCursor = class CollaborateCursor {
1570
- constructor(textbus, nativeSelection, scheduler, selection, awarenessDelegate) {
1448
+ constructor(textbus, nativeSelection, scheduler, selection, userActivity, awarenessDelegate) {
1571
1449
  this.nativeSelection = nativeSelection;
1572
1450
  this.scheduler = scheduler;
1573
1451
  this.selection = selection;
1452
+ this.userActivity = userActivity;
1574
1453
  this.awarenessDelegate = awarenessDelegate;
1575
1454
  this.host = createElement('div', {
1576
1455
  styles: {
@@ -1620,6 +1499,7 @@ let CollaborateCursor = class CollaborateCursor {
1620
1499
  this.onRectsChange = new Subject();
1621
1500
  this.subscription = new Subscription();
1622
1501
  this.currentSelection = [];
1502
+ this.ratio = window.devicePixelRatio || 1;
1623
1503
  this.container = textbus.get(VIEW_CONTAINER);
1624
1504
  this.canvasContainer.append(this.canvas);
1625
1505
  this.host.append(this.canvasContainer, this.tooltips);
@@ -1639,6 +1519,13 @@ let CollaborateCursor = class CollaborateCursor {
1639
1519
  this.refresh();
1640
1520
  }));
1641
1521
  }
1522
+ init() {
1523
+ if (this.userActivity) {
1524
+ this.subscription.add(this.userActivity.onStateChange.subscribe(v => {
1525
+ this.draw(v);
1526
+ }));
1527
+ }
1528
+ }
1642
1529
  /**
1643
1530
  * 刷新协作光标,由于 Textbus 只会绘制可视区域的光标,当可视区域发生变化时,需要重新绘制
1644
1531
  */
@@ -1656,15 +1543,16 @@ let CollaborateCursor = class CollaborateCursor {
1656
1543
  this.currentSelection = paths;
1657
1544
  const containerRect = this.container.getBoundingClientRect();
1658
1545
  this.canvas.style.top = containerRect.top * -1 + 'px';
1659
- this.canvas.width = this.canvas.offsetWidth;
1660
- this.canvas.height = this.canvas.offsetHeight;
1546
+ this.canvas.width = this.canvas.offsetWidth * this.ratio;
1547
+ this.canvas.height = this.canvas.offsetHeight * this.ratio;
1548
+ this.context.scale(this.ratio, this.ratio);
1661
1549
  this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
1662
1550
  const users = [];
1663
1551
  paths.filter(i => {
1664
- return i.paths.anchor.length && i.paths.focus.length;
1552
+ return i.selection.anchor.length && i.selection.focus.length;
1665
1553
  }).forEach(item => {
1666
- const anchorPaths = [...item.paths.anchor];
1667
- const focusPaths = [...item.paths.focus];
1554
+ const anchorPaths = [...item.selection.anchor];
1555
+ const focusPaths = [...item.selection.focus];
1668
1556
  const anchorOffset = anchorPaths.pop();
1669
1557
  const anchorSlot = this.selection.findSlotByPaths(anchorPaths);
1670
1558
  const focusOffset = focusPaths.pop();
@@ -1682,8 +1570,13 @@ let CollaborateCursor = class CollaborateCursor {
1682
1570
  return;
1683
1571
  }
1684
1572
  const nativeRange = document.createRange();
1685
- nativeRange.setStart(anchor.node, anchor.offset);
1686
- nativeRange.setEnd(focus.node, focus.offset);
1573
+ try {
1574
+ nativeRange.setStart(anchor.node, anchor.offset);
1575
+ nativeRange.setEnd(focus.node, focus.offset);
1576
+ }
1577
+ catch (e) {
1578
+ return;
1579
+ }
1687
1580
  if ((anchor.node !== focus.node || anchor.offset !== focus.offset) && nativeRange.collapsed) {
1688
1581
  nativeRange.setStart(focus.node, focus.offset);
1689
1582
  nativeRange.setEnd(anchor.node, anchor.offset);
@@ -1812,10 +1705,12 @@ let CollaborateCursor = class CollaborateCursor {
1812
1705
  CollaborateCursor = __decorate([
1813
1706
  Injectable(),
1814
1707
  __param(4, Optional()),
1708
+ __param(5, Optional()),
1815
1709
  __metadata("design:paramtypes", [Textbus,
1816
1710
  SelectionBridge,
1817
1711
  Scheduler,
1818
1712
  Selection,
1713
+ UserActivity,
1819
1714
  CollaborateSelectionAwarenessDelegate])
1820
1715
  ], CollaborateCursor);
1821
1716
 
@@ -1852,9 +1747,7 @@ class NativeCaret {
1852
1747
  height: 0
1853
1748
  };
1854
1749
  }
1855
- constructor(scheduler) {
1856
- this.scheduler = scheduler;
1857
- this.oldPosition = null;
1750
+ constructor() {
1858
1751
  this._nativeRange = null;
1859
1752
  this.subs = [];
1860
1753
  this.positionChangeEvent = new Subject();
@@ -1863,59 +1756,6 @@ class NativeCaret {
1863
1756
  refresh() {
1864
1757
  //
1865
1758
  }
1866
- correctScrollTop(scroller) {
1867
- this.destroy();
1868
- const scheduler = this.scheduler;
1869
- let docIsChanged = true;
1870
- function limitPosition(position) {
1871
- const { top, bottom } = scroller.getLimit();
1872
- const caretTop = position.top;
1873
- if (caretTop + position.height > bottom) {
1874
- const offset = caretTop - bottom + position.height;
1875
- scroller.setOffset(offset);
1876
- }
1877
- else if (position.top < top) {
1878
- scroller.setOffset(-(top - position.top));
1879
- }
1880
- }
1881
- let isPressed = false;
1882
- this.subs.push(scroller.onScroll.subscribe(() => {
1883
- if (this.oldPosition) {
1884
- const rect = this.rect;
1885
- this.oldPosition.top = rect.top;
1886
- this.oldPosition.left = rect.left;
1887
- this.oldPosition.height = rect.height;
1888
- }
1889
- }), fromEvent(document, 'mousedown', true).subscribe(() => {
1890
- isPressed = true;
1891
- }), fromEvent(document, 'mouseup', true).subscribe(() => {
1892
- isPressed = false;
1893
- }), scheduler.onDocChange.subscribe(() => {
1894
- docIsChanged = true;
1895
- }), this.onPositionChange.subscribe(position => {
1896
- if (position) {
1897
- if (docIsChanged) {
1898
- if (scheduler.lastChangesHasLocalUpdate) {
1899
- limitPosition(position);
1900
- }
1901
- else if (this.oldPosition) {
1902
- const offset = Math.floor(position.top - this.oldPosition.top);
1903
- scroller.setOffset(offset);
1904
- }
1905
- }
1906
- else if (!isPressed) {
1907
- if (this.oldPosition) {
1908
- const offset = Math.floor(position.top - this.oldPosition.top);
1909
- scroller.setOffset(offset);
1910
- }
1911
- else {
1912
- limitPosition(position);
1913
- }
1914
- }
1915
- }
1916
- docIsChanged = false;
1917
- }));
1918
- }
1919
1759
  destroy() {
1920
1760
  this.subs.forEach(i => i.unsubscribe());
1921
1761
  this.subs = [];
@@ -1933,18 +1773,17 @@ let NativeInput = class NativeInput extends Input {
1933
1773
  get disabled() {
1934
1774
  return this._disabled;
1935
1775
  }
1936
- constructor(textbus, parser, scheduler, selection, keyboard, domAdapter, commander, controller) {
1776
+ constructor(textbus, parser, selection, keyboard, domAdapter, commander, controller) {
1937
1777
  super();
1938
1778
  this.parser = parser;
1939
- this.scheduler = scheduler;
1940
1779
  this.selection = selection;
1941
1780
  this.keyboard = keyboard;
1942
1781
  this.domAdapter = domAdapter;
1943
1782
  this.commander = commander;
1944
1783
  this.controller = controller;
1945
- this.caret = new NativeCaret(this.scheduler);
1784
+ this.caret = new NativeCaret();
1946
1785
  this.composition = false;
1947
- this.compositionState = null;
1786
+ // compositionState: CompositionState | null = null
1948
1787
  this.onReady = Promise.resolve();
1949
1788
  this._disabled = false;
1950
1789
  this.nativeSelection = document.getSelection();
@@ -1987,7 +1826,7 @@ let NativeInput = class NativeInput extends Input {
1987
1826
  this.subscription.unsubscribe();
1988
1827
  }
1989
1828
  handleDefaultActions(textarea) {
1990
- this.subscription.add(fromEvent(document, 'copy').subscribe(ev => {
1829
+ this.subscription.add(fromEvent(isFirefox() ? textarea : document, 'copy').subscribe(ev => {
1991
1830
  const selection = this.selection;
1992
1831
  if (!selection.isSelected) {
1993
1832
  return;
@@ -2041,8 +1880,8 @@ let NativeInput = class NativeInput extends Input {
2041
1880
  });
2042
1881
  }));
2043
1882
  }
2044
- handlePaste(html, text) {
2045
- const slot = this.parser.parse(html, new Slot([
1883
+ handlePaste(dom, text) {
1884
+ const slot = this.parser.parse(dom, new Slot([
2046
1885
  ContentType.BlockComponent,
2047
1886
  ContentType.InlineComponent,
2048
1887
  ContentType.Text
@@ -2102,7 +1941,6 @@ let NativeInput = class NativeInput extends Input {
2102
1941
  let startIndex;
2103
1942
  const compositionStart = () => {
2104
1943
  this.composition = true;
2105
- this.compositionState = null;
2106
1944
  startIndex = this.selection.startOffset;
2107
1945
  const startSlot = this.selection.startSlot;
2108
1946
  const event = new Event(startSlot, {
@@ -2112,11 +1950,6 @@ let NativeInput = class NativeInput extends Input {
2112
1950
  };
2113
1951
  const compositionUpdate = (data) => {
2114
1952
  const startSlot = this.selection.startSlot;
2115
- this.compositionState = {
2116
- slot: startSlot,
2117
- index: startIndex,
2118
- data
2119
- };
2120
1953
  const event = new Event(startSlot, {
2121
1954
  index: startIndex,
2122
1955
  data
@@ -2203,7 +2036,6 @@ let NativeInput = class NativeInput extends Input {
2203
2036
  return !this.ignoreComposition;
2204
2037
  })).subscribe(() => {
2205
2038
  this.composition = true;
2206
- this.compositionState = null;
2207
2039
  startIndex = this.selection.startOffset;
2208
2040
  const startSlot = this.selection.startSlot;
2209
2041
  const event = new Event(startSlot, {
@@ -2214,11 +2046,6 @@ let NativeInput = class NativeInput extends Input {
2214
2046
  return !this.ignoreComposition;
2215
2047
  })).subscribe(ev => {
2216
2048
  const startSlot = this.selection.startSlot;
2217
- this.compositionState = {
2218
- slot: startSlot,
2219
- index: startIndex,
2220
- data: ev.data
2221
- };
2222
2049
  const event = new Event(startSlot, {
2223
2050
  index: startIndex,
2224
2051
  data: ev.data
@@ -2268,8 +2095,11 @@ let NativeInput = class NativeInput extends Input {
2268
2095
  return !b;
2269
2096
  }))).subscribe(text => {
2270
2097
  this.composition = false;
2271
- this.compositionState = null;
2272
2098
  if (text) {
2099
+ const startContainer = this.nativeSelection.focusNode;
2100
+ if (startContainer instanceof Text && startContainer.textContent === text) {
2101
+ startContainer.remove();
2102
+ }
2273
2103
  this.commander.write(text);
2274
2104
  }
2275
2105
  if (isCompositionEnd) {
@@ -2287,7 +2117,6 @@ NativeInput = __decorate([
2287
2117
  Injectable(),
2288
2118
  __metadata("design:paramtypes", [Textbus,
2289
2119
  Parser,
2290
- Scheduler,
2291
2120
  Selection,
2292
2121
  Keyboard,
2293
2122
  DomAdapter,
@@ -2295,9 +2124,9 @@ NativeInput = __decorate([
2295
2124
  Controller])
2296
2125
  ], NativeInput);
2297
2126
 
2127
+ const browserErrorFn = makeError('BrowserModule');
2298
2128
  class BrowserModule {
2299
- constructor(host, config) {
2300
- this.host = host;
2129
+ constructor(config) {
2301
2130
  this.config = config;
2302
2131
  const { mask, wrapper } = BrowserModule.createLayout();
2303
2132
  wrapper.prepend(config.adapter.host);
@@ -2323,24 +2152,83 @@ class BrowserModule {
2323
2152
  provide: Input,
2324
2153
  useClass: config.useContentEditable ? NativeInput : MagicInput
2325
2154
  }, {
2326
- provide: ViewAdapter,
2327
- useFactory(v) {
2328
- return v;
2329
- },
2330
- deps: [DomAdapter]
2155
+ provide: Adapter,
2156
+ useValue: config.adapter
2331
2157
  }, {
2332
2158
  provide: DomAdapter,
2333
2159
  useValue: config.adapter
2160
+ }, {
2161
+ provide: FocusManager,
2162
+ useFactory: (input) => {
2163
+ const focusEvent = new Subject();
2164
+ const blurEvent = new Subject();
2165
+ input.caret.onPositionChange.pipe(map(p => !!p), distinctUntilChanged()).subscribe(b => {
2166
+ if (b) {
2167
+ focusEvent.next();
2168
+ }
2169
+ else {
2170
+ blurEvent.next();
2171
+ }
2172
+ });
2173
+ return {
2174
+ onFocus: focusEvent,
2175
+ onBlur: blurEvent
2176
+ };
2177
+ },
2178
+ deps: [Input]
2334
2179
  },
2335
2180
  Parser,
2336
2181
  SelectionBridge,
2337
- CollaborateCursor
2338
- ];
2182
+ CollaborateCursor];
2339
2183
  this.workbench = wrapper;
2340
- this.host.append(wrapper);
2341
2184
  }
2342
- onDestroy() {
2343
- this.workbench.remove();
2185
+ /**
2186
+ * 解析 HTML 并返回一个组件实例
2187
+ * @param html 要解析的 HTML
2188
+ * @param rootComponentLoader 文档根组件加载器
2189
+ * @param textbus
2190
+ */
2191
+ readDocumentByHTML(html, rootComponentLoader, textbus) {
2192
+ const parser = textbus.get(Parser);
2193
+ const doc = parser.parseDoc(html, rootComponentLoader);
2194
+ if (doc instanceof Component) {
2195
+ return doc;
2196
+ }
2197
+ throw browserErrorFn('rootComponentLoader must return a component instance.');
2198
+ }
2199
+ /**
2200
+ * 将组件数据解析到组件实例中
2201
+ * @param data 要解析的 JSON 数据
2202
+ * @param rootComponent 根组件
2203
+ * @param textbus
2204
+ */
2205
+ readDocumentByComponentLiteral(data, rootComponent, textbus) {
2206
+ const registry = textbus.get(Registry);
2207
+ return registry.createComponentByFactory(data, rootComponent);
2208
+ }
2209
+ setup(textbus) {
2210
+ this.textbus = textbus;
2211
+ const host = this.config.renderTo();
2212
+ if (!(host instanceof HTMLElement)) {
2213
+ throw browserErrorFn('view container is not a HTMLElement');
2214
+ }
2215
+ const cursor = textbus.get(CollaborateCursor);
2216
+ cursor.init();
2217
+ host.append(this.workbench);
2218
+ return () => {
2219
+ cursor.destroy();
2220
+ this.workbench.remove();
2221
+ };
2222
+ }
2223
+ onAfterStartup(textbus) {
2224
+ if (this.config.autoFocus) {
2225
+ textbus.focus();
2226
+ }
2227
+ }
2228
+ onDestroy(textbus) {
2229
+ textbus.get(Input).destroy();
2230
+ textbus.get(SelectionBridge).destroy();
2231
+ textbus.get(CollaborateCursor).destroy();
2344
2232
  }
2345
2233
  static createLayout() {
2346
2234
  const mask = createElement('div', {
@@ -2353,10 +2241,23 @@ class BrowserModule {
2353
2241
  right: 0,
2354
2242
  top: 0,
2355
2243
  bottom: 0,
2244
+ pointerEvents: 'none',
2245
+ // overflow: 'hidden'
2246
+ }
2247
+ });
2248
+ const maskWrapper = createElement('div', {
2249
+ styles: {
2250
+ position: 'absolute',
2251
+ left: 0,
2252
+ right: 0,
2253
+ top: 0,
2254
+ bottom: 0,
2255
+ margin: '0 -2px',
2356
2256
  zIndex: 1,
2357
2257
  pointerEvents: 'none',
2358
2258
  overflow: 'hidden'
2359
- }
2259
+ },
2260
+ children: [mask]
2360
2261
  });
2361
2262
  const wrapper = createElement('div', {
2362
2263
  attrs: {
@@ -2368,7 +2269,7 @@ class BrowserModule {
2368
2269
  position: 'relative',
2369
2270
  flexDirection: 'column'
2370
2271
  },
2371
- children: [mask]
2272
+ children: [maskWrapper]
2372
2273
  });
2373
2274
  return {
2374
2275
  wrapper,
@@ -2377,4 +2278,4 @@ class BrowserModule {
2377
2278
  }
2378
2279
  }
2379
2280
 
2380
- export { BrowserModule, CollaborateCursor, CollaborateSelectionAwarenessDelegate, DomAdapter, EDITOR_OPTIONS, Input, MagicInput, Parser, SelectionBridge, VIEW_CONTAINER, VIEW_DOCUMENT, VIEW_MASK, createElement, createTextNode, getLayoutRectByRange, isFirefox, isMac, isMobileBrowser, isSafari, isWindows };
2281
+ export { BrowserModule, CollaborateCursor, CollaborateSelectionAwarenessDelegate, DomAdapter, EDITOR_OPTIONS, Input, MagicInput, NativeInput, Parser, SelectionBridge, VIEW_CONTAINER, VIEW_DOCUMENT, VIEW_MASK, createElement, createTextNode, getLayoutRectByRange, isFirefox, isMac, isMobileBrowser, isSafari, isWindows };