slate-angular 20.2.0-next.9 → 20.2.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.
@@ -353,6 +353,18 @@ const CustomDOMEditor = {
353
353
  }
354
354
  };
355
355
 
356
+ /**
357
+ * Symbols.
358
+ */
359
+ const PLACEHOLDER_SYMBOL = Symbol('placeholder');
360
+ /**
361
+ * Weak map for associating the html element with the component.
362
+ */
363
+ const ELEMENT_TO_COMPONENT = new WeakMap();
364
+ const IS_ENABLED_VIRTUAL_SCROLL = new WeakMap();
365
+ const EDITOR_TO_VIRTUAL_SCROLL_SELECTION = new WeakMap();
366
+ const EDITOR_TO_AFTER_VIEW_INIT_QUEUE = new WeakMap();
367
+
356
368
  const AngularEditor = {
357
369
  ...CustomDOMEditor,
358
370
  /**
@@ -424,19 +436,12 @@ const AngularEditor = {
424
436
  // FocusedContext is updated to the correct value
425
437
  el.focus({ preventScroll: true });
426
438
  }
439
+ },
440
+ isEnabledVirtualScroll(editor) {
441
+ return IS_ENABLED_VIRTUAL_SCROLL.get(editor);
427
442
  }
428
443
  };
429
444
 
430
- /**
431
- * Symbols.
432
- */
433
- const PLACEHOLDER_SYMBOL = Symbol('placeholder');
434
- /**
435
- * Weak map for associating the html element with the component.
436
- */
437
- const ELEMENT_TO_COMPONENT = new WeakMap();
438
- const EDITOR_TO_AFTER_VIEW_INIT_QUEUE = new WeakMap();
439
-
440
445
  const IS_IOS = typeof navigator !== 'undefined' &&
441
446
  typeof window !== 'undefined' &&
442
447
  /iPad|iPhone|iPod/.test(navigator.userAgent) &&
@@ -470,9 +475,9 @@ const HAS_BEFORE_INPUT_SUPPORT = !IS_CHROME_LEGACY &&
470
475
  globalThis.InputEvent &&
471
476
  // @ts-ignore The `getTargetRanges` property isn't recognized.
472
477
  typeof globalThis.InputEvent.prototype.getTargetRanges === 'function';
473
- const VIRTUAL_SCROLL_DEFAULT_BUFFER_COUNT = 3;
474
- const VIRTUAL_SCROLL_DEFAULT_BLOCK_HEIGHT = 40;
478
+ const VIRTUAL_SCROLL_DEFAULT_BLOCK_HEIGHT = 30;
475
479
  const SLATE_DEBUG_KEY = '__SLATE_DEBUG__';
480
+ const SLATE_DEBUG_KEY_SCROLL_TOP = '__SLATE_DEBUG_SCROLL_TOP__';
476
481
 
477
482
  /**
478
483
  * Hotkey mappings for each platform.
@@ -950,815 +955,1406 @@ const fallbackCopyText = async (text) => {
950
955
  });
951
956
  };
952
957
 
953
- const withAngular = (editor, clipboardFormatKey = 'x-slate-fragment') => {
954
- let e = editor;
955
- let { apply } = e;
956
- e = withDOM(e, clipboardFormatKey);
957
- e.setFragmentData = (dataTransfer, originEvent) => {
958
- const { selection } = e;
959
- if (!selection) {
960
- return;
961
- }
962
- const [start, end] = Range.edges(selection);
963
- const startVoid = Editor.void(e, { at: start.path });
964
- const endVoid = Editor.void(e, { at: end.path });
965
- if (Range.isCollapsed(selection) && !startVoid) {
966
- return;
967
- }
968
- // Create a fake selection so that we can add a Base64-encoded copy of the
969
- // fragment to the HTML, to decode on future pastes.
970
- const domRange = AngularEditor.toDOMRange(e, selection);
971
- let contents = domRange.cloneContents();
972
- let attach = contents.childNodes[0];
973
- // Make sure attach is non-empty, since empty nodes will not get copied.
974
- const contentsArray = Array.from(contents.children);
975
- contentsArray.forEach(node => {
976
- if (node.textContent && node.textContent.trim() !== '') {
977
- attach = node;
958
+ class VirtualScrollDebugOverlay {
959
+ static { this.storageKey = 'slate_virtual_scroll_debug_overlay_state'; }
960
+ static { this.minWidth = 320; }
961
+ static { this.minHeight = 240; }
962
+ static { this.defaultWidth = 410; }
963
+ static { this.defaultHeight = 480; }
964
+ static getInstance(doc) {
965
+ if (!this.instance) {
966
+ this.instance = new VirtualScrollDebugOverlay(doc);
967
+ }
968
+ this.instance.init();
969
+ return this.instance;
970
+ }
971
+ static log(doc, type, ...args) {
972
+ this.getInstance(doc).log(type, ...args);
973
+ }
974
+ // will trigger selectionchange and clear editor's selection
975
+ static syncScrollTop(doc, value) {
976
+ const instance = this.getInstance(doc);
977
+ instance.setScrollTopValue(value);
978
+ }
979
+ constructor(doc) {
980
+ this.doc = doc;
981
+ this.state = {
982
+ left: 16,
983
+ top: 16,
984
+ collapsed: false,
985
+ width: VirtualScrollDebugOverlay.defaultWidth,
986
+ height: VirtualScrollDebugOverlay.defaultHeight
987
+ };
988
+ this.originalConsoleLog = console.log.bind(console);
989
+ this.originalConsoleWarn = console.warn.bind(console);
990
+ this.dragOffsetX = 0;
991
+ this.dragOffsetY = 0;
992
+ this.isDragging = false;
993
+ this.isResizing = false;
994
+ this.resizeStartX = 0;
995
+ this.resizeStartY = 0;
996
+ this.resizeStartWidth = 0;
997
+ this.resizeStartHeight = 0;
998
+ this.dragMoved = false;
999
+ this.wasDragged = false;
1000
+ this.onDragging = (event) => {
1001
+ if (!this.isDragging || !this.container) {
1002
+ return;
978
1003
  }
979
- });
980
- // COMPAT: If the end node is a void node, we need to move the end of the
981
- // range from the void node's spacer span, to the end of the void node's
982
- // content, since the spacer is before void's content in the DOM.
983
- if (endVoid) {
984
- const [voidNode] = endVoid;
985
- const r = domRange.cloneRange();
986
- const domNode = AngularEditor.toDOMNode(e, voidNode);
987
- r.setEndAfter(domNode);
988
- contents = r.cloneContents();
989
- }
990
- // COMPAT: If the start node is a void node, we need to attach the encoded
991
- // fragment to the void node's content node instead of the spacer, because
992
- // attaching it to empty `<div>/<span>` nodes will end up having it erased by
993
- // most browsers. (2018/04/27)
994
- if (startVoid) {
995
- attach = contents.querySelector('[data-slate-spacer]');
996
- }
997
- // Remove any zero-width space spans from the cloned DOM so that they don't
998
- // show up elsewhere when pasted.
999
- Array.from(contents.querySelectorAll('[data-slate-zero-width]')).forEach(zw => {
1000
- const isNewline = zw.getAttribute('data-slate-zero-width') === 'n';
1001
- zw.textContent = isNewline ? '\n' : '';
1002
- });
1003
- // Set a `data-slate-fragment` attribute on a non-empty node, so it shows up
1004
- // in the HTML, and can be used for intra-Slate pasting. If it's a text
1005
- // node, wrap it in a `<span>` so we have something to set an attribute on.
1006
- if (isDOMText(attach)) {
1007
- const span = attach.ownerDocument.createElement('span');
1008
- // COMPAT: In Chrome and Safari, if we don't add the `white-space` style
1009
- // then leading and trailing spaces will be ignored. (2017/09/21)
1010
- span.style.whiteSpace = 'pre';
1011
- span.appendChild(attach);
1012
- contents.appendChild(span);
1013
- attach = span;
1014
- }
1015
- const fragment = e.getFragment();
1016
- // Add the content to a <div> so that we can get its inner HTML.
1017
- const div = contents.ownerDocument.createElement('div');
1018
- const attachWrapper = document.createElement('div');
1019
- const elements = Array.from(contents.children);
1020
- if (isInvalidTable(elements)) {
1021
- contents = completeTable(contents.cloneNode(true));
1022
- }
1023
- attachWrapper.appendChild(contents);
1024
- div.appendChild(attachWrapper);
1025
- div.setAttribute('hidden', 'true');
1026
- contents.ownerDocument.body.appendChild(div);
1027
- setClipboardData({ text: getPlainText(div), elements: fragment }, div, attachWrapper, dataTransfer);
1028
- contents.ownerDocument.body.removeChild(div);
1029
- };
1030
- e.deleteCutData = () => {
1031
- const { selection } = editor;
1032
- if (selection) {
1033
- if (Range.isExpanded(selection)) {
1034
- Editor.deleteFragment(editor);
1004
+ this.dragMoved = true;
1005
+ const nextLeft = event.clientX - this.dragOffsetX;
1006
+ const nextTop = event.clientY - this.dragOffsetY;
1007
+ this.container.style.left = `${nextLeft}px`;
1008
+ this.container.style.top = `${nextTop}px`;
1009
+ this.container.style.right = 'auto';
1010
+ this.container.style.bottom = 'auto';
1011
+ };
1012
+ this.onDragEnd = () => {
1013
+ if (!this.isDragging) {
1014
+ return;
1035
1015
  }
1036
- else {
1037
- const node = Node.parent(editor, selection.anchor.path);
1038
- if (Element.isElement(node) && Editor.isVoid(editor, node)) {
1039
- Transforms.delete(editor);
1040
- }
1016
+ this.isDragging = false;
1017
+ this.wasDragged = this.dragMoved;
1018
+ this.dragMoved = false;
1019
+ this.doc.removeEventListener('mousemove', this.onDragging);
1020
+ this.doc.removeEventListener('mouseup', this.onDragEnd);
1021
+ if (this.container) {
1022
+ const rect = this.container.getBoundingClientRect();
1023
+ this.state.left = rect.left;
1024
+ this.state.top = rect.top;
1025
+ this.persistState();
1026
+ this.container.style.transition = '';
1041
1027
  }
1042
- }
1043
- };
1044
- e.insertData = async (data) => {
1045
- if (!(await e.customInsertFragmentData(data, null))) {
1046
- e.insertTextData(data);
1047
- }
1048
- };
1049
- e.customInsertFragmentData = async (data, contextClipboardData) => {
1050
- /**
1051
- * Checking copied fragment from application/x-slate-fragment or data-slate-fragment
1052
- */
1053
- const clipboardData = contextClipboardData || (await getClipboardData(data));
1054
- if (clipboardData && clipboardData.elements) {
1055
- e.insertFragment(clipboardData.elements);
1056
- return true;
1057
- }
1058
- return false;
1059
- };
1060
- e.customInsertTextData = async (data) => {
1061
- const clipboardData = await getClipboardData(data);
1062
- if (clipboardData && clipboardData.text) {
1063
- const lines = clipboardData.text.split(/\r\n|\r|\n/);
1064
- let split = false;
1065
- for (const line of lines) {
1066
- if (split) {
1067
- Transforms.splitNodes(e, { always: true });
1068
- }
1069
- e.insertText(line);
1070
- split = true;
1028
+ };
1029
+ this.onResizing = (event) => {
1030
+ if (!this.isResizing || !this.container) {
1031
+ return;
1071
1032
  }
1072
- return true;
1033
+ const deltaX = event.clientX - this.resizeStartX;
1034
+ const deltaY = event.clientY - this.resizeStartY;
1035
+ const nextWidth = Math.max(VirtualScrollDebugOverlay.minWidth, this.resizeStartWidth + deltaX);
1036
+ const nextHeight = Math.max(VirtualScrollDebugOverlay.minHeight, this.resizeStartHeight + deltaY);
1037
+ this.state.width = nextWidth;
1038
+ this.state.height = nextHeight;
1039
+ this.applySize();
1040
+ };
1041
+ this.onResizeEnd = () => {
1042
+ if (!this.isResizing) {
1043
+ return;
1044
+ }
1045
+ this.isResizing = false;
1046
+ this.doc.removeEventListener('mousemove', this.onResizing);
1047
+ this.doc.removeEventListener('mouseup', this.onResizeEnd);
1048
+ this.persistState();
1049
+ };
1050
+ }
1051
+ init() {
1052
+ if (!this.container) {
1053
+ this.createContainer();
1073
1054
  }
1074
- return false;
1075
- };
1076
- e.onKeydown = () => { };
1077
- e.onClick = () => { };
1078
- e.isBlockCard = element => false;
1079
- e.isExpanded = element => true;
1080
- e.onError = (errorData) => {
1081
- if (errorData.nativeError) {
1082
- console.error(errorData.nativeError);
1055
+ }
1056
+ log(type, ...args) {
1057
+ this.init();
1058
+ if (type === 'warn') {
1059
+ this.originalConsoleWarn(...args);
1083
1060
  }
1084
1061
  else {
1085
- console.error(errorData);
1062
+ this.originalConsoleLog(...args);
1086
1063
  }
1087
- };
1088
- // exist issue for move operation in withDOM
1089
- e.apply = (op) => {
1090
- const matches = [];
1091
- switch (op.type) {
1092
- case 'insert_text':
1093
- case 'remove_text':
1094
- case 'set_node': {
1095
- for (const [node, path] of Editor.levels(e, { at: op.path })) {
1096
- const key = AngularEditor.findKey(e, node);
1097
- matches.push([path, key]);
1098
- }
1099
- break;
1100
- }
1101
- case 'insert_node':
1102
- case 'remove_node':
1103
- case 'merge_node':
1104
- case 'split_node': {
1105
- for (const [node, path] of Editor.levels(e, {
1106
- at: Path.parent(op.path)
1107
- })) {
1108
- const key = AngularEditor.findKey(e, node);
1109
- matches.push([path, key]);
1110
- }
1111
- break;
1064
+ this.appendLog(type, ...args);
1065
+ }
1066
+ dispose() {
1067
+ this.container?.remove();
1068
+ this.container = undefined;
1069
+ this.contentWrapper = undefined;
1070
+ this.bubble = undefined;
1071
+ this.logList = undefined;
1072
+ this.selectorInput = undefined;
1073
+ this.distanceInput = undefined;
1074
+ this.collapseToggle = undefined;
1075
+ this.resizeHandle = undefined;
1076
+ this.doc.removeEventListener('mousemove', this.onDragging);
1077
+ this.doc.removeEventListener('mouseup', this.onDragEnd);
1078
+ this.doc.removeEventListener('mousemove', this.onResizing);
1079
+ this.doc.removeEventListener('mouseup', this.onResizeEnd);
1080
+ this.isDragging = false;
1081
+ this.isResizing = false;
1082
+ }
1083
+ createContainer() {
1084
+ this.loadState();
1085
+ const doc = this.doc;
1086
+ const container = doc.createElement('div');
1087
+ container.style.position = 'fixed';
1088
+ container.style.right = 'auto';
1089
+ container.style.bottom = 'auto';
1090
+ container.style.boxSizing = 'border-box';
1091
+ container.style.background = 'rgba(17, 24, 39, 0.95)';
1092
+ container.style.color = '#e5e7eb';
1093
+ container.style.fontSize = '12px';
1094
+ container.style.border = '1px solid #1f2937';
1095
+ container.style.borderRadius = '10px';
1096
+ container.style.fontFamily = 'Menlo, Consolas, monospace';
1097
+ container.style.boxShadow = '0 10px 30px rgba(0, 0, 0, 0.35)';
1098
+ container.style.zIndex = '9999';
1099
+ container.style.display = 'flex';
1100
+ container.style.flexDirection = 'column';
1101
+ container.style.gap = '10px';
1102
+ container.style.minWidth = `${VirtualScrollDebugOverlay.minWidth}px`;
1103
+ container.style.minHeight = `${VirtualScrollDebugOverlay.minHeight}px`;
1104
+ container.style.maxHeight = '80vh';
1105
+ container.style.cursor = 'default';
1106
+ container.addEventListener('mousedown', event => {
1107
+ if (this.state.collapsed) {
1108
+ this.startDrag(event);
1112
1109
  }
1113
- case 'move_node': {
1114
- const commonPath = Path.common(Path.parent(op.path), Path.parent(op.newPath));
1115
- for (const [node, path] of Editor.levels(e, {
1116
- at: Path.parent(op.path)
1117
- })) {
1118
- const key = AngularEditor.findKey(e, node);
1119
- matches.push([Editor.pathRef(editor, path), key]);
1120
- }
1121
- for (const [node, path] of Editor.levels(e, {
1122
- at: Path.parent(op.newPath)
1123
- })) {
1124
- if (path.length > commonPath.length) {
1125
- const key = AngularEditor.findKey(e, node);
1126
- matches.push([Editor.pathRef(editor, path), key]);
1127
- }
1110
+ });
1111
+ const header = doc.createElement('div');
1112
+ header.style.display = 'flex';
1113
+ header.style.alignItems = 'center';
1114
+ header.style.justifyContent = 'space-between';
1115
+ header.style.cursor = 'move';
1116
+ header.addEventListener('mousedown', event => {
1117
+ this.startDrag(event);
1118
+ });
1119
+ const title = doc.createElement('div');
1120
+ title.textContent = 'Virtual Scroll Debug';
1121
+ title.style.fontWeight = '600';
1122
+ title.style.letterSpacing = '0.3px';
1123
+ const actions = doc.createElement('div');
1124
+ actions.style.display = 'flex';
1125
+ actions.style.gap = '6px';
1126
+ const collapseButton = doc.createElement('button');
1127
+ collapseButton.type = 'button';
1128
+ collapseButton.textContent = '折叠';
1129
+ collapseButton.style.background = '#1f2937';
1130
+ collapseButton.style.color = '#e5e7eb';
1131
+ collapseButton.style.border = '1px solid #374151';
1132
+ collapseButton.style.borderRadius = '6px';
1133
+ collapseButton.style.padding = '4px 8px';
1134
+ collapseButton.style.cursor = 'pointer';
1135
+ collapseButton.addEventListener('click', () => {
1136
+ this.setCollapsed(!this.state.collapsed);
1137
+ });
1138
+ const clearButton = doc.createElement('button');
1139
+ clearButton.type = 'button';
1140
+ clearButton.textContent = '清除日志';
1141
+ clearButton.style.background = '#374151';
1142
+ clearButton.style.color = '#e5e7eb';
1143
+ clearButton.style.border = '1px solid #4b5563';
1144
+ clearButton.style.borderRadius = '6px';
1145
+ clearButton.style.padding = '4px 10px';
1146
+ clearButton.style.cursor = 'pointer';
1147
+ clearButton.addEventListener('click', () => {
1148
+ if (this.logList) {
1149
+ this.logList.innerHTML = '';
1150
+ }
1151
+ });
1152
+ actions.appendChild(collapseButton);
1153
+ actions.appendChild(clearButton);
1154
+ header.appendChild(title);
1155
+ header.appendChild(actions);
1156
+ const scrollForm = doc.createElement('div');
1157
+ scrollForm.style.display = 'grid';
1158
+ scrollForm.style.gridTemplateColumns = '1fr 110px 50px';
1159
+ scrollForm.style.gap = '6px';
1160
+ scrollForm.style.alignItems = 'center';
1161
+ const selectorInput = doc.createElement('input');
1162
+ selectorInput.placeholder = '滚动容器 selector';
1163
+ selectorInput.style.padding = '6px 8px';
1164
+ selectorInput.style.borderRadius = '6px';
1165
+ selectorInput.style.border = '1px solid #4b5563';
1166
+ selectorInput.style.background = '#111827';
1167
+ selectorInput.style.color = '#e5e7eb';
1168
+ selectorInput.autocomplete = 'off';
1169
+ const distanceInput = doc.createElement('input');
1170
+ distanceInput.placeholder = '滚动距离(px)';
1171
+ distanceInput.type = 'number';
1172
+ distanceInput.style.padding = '6px 8px';
1173
+ distanceInput.style.borderRadius = '6px';
1174
+ distanceInput.style.border = '1px solid #4b5563';
1175
+ distanceInput.style.background = '#111827';
1176
+ distanceInput.style.color = '#e5e7eb';
1177
+ const scrollButton = doc.createElement('button');
1178
+ scrollButton.type = 'button';
1179
+ scrollButton.textContent = '滚动';
1180
+ scrollButton.style.background = '#10b981';
1181
+ scrollButton.style.color = '#0b1c15';
1182
+ scrollButton.style.border = 'none';
1183
+ scrollButton.style.borderRadius = '6px';
1184
+ scrollButton.style.padding = '6px 10px';
1185
+ scrollButton.style.cursor = 'pointer';
1186
+ scrollButton.addEventListener('click', () => {
1187
+ const selector = selectorInput.value.trim();
1188
+ const distanceValue = Number(distanceInput.value ?? 0);
1189
+ const distance = Number.isFinite(distanceValue) ? distanceValue : 0;
1190
+ if (!selector) {
1191
+ this.log('warn', '请先填写滚动容器 selector');
1192
+ return;
1193
+ }
1194
+ const target = doc.querySelector(selector);
1195
+ if (!target) {
1196
+ this.log('warn', `未找到滚动容器: ${selector}`);
1197
+ return;
1198
+ }
1199
+ if (typeof target.scrollTo === 'function') {
1200
+ target.scrollTo({ top: distance, behavior: 'auto' });
1201
+ }
1202
+ else if (Object.prototype.hasOwnProperty.call(target, 'scrollTop')) {
1203
+ target.scrollTop = distance;
1204
+ }
1205
+ else {
1206
+ this.log('warn', '目标元素不支持滚动:', selector);
1207
+ return;
1208
+ }
1209
+ this.log('log', `已将 ${selector} 滚动到`, distance);
1210
+ });
1211
+ scrollForm.appendChild(selectorInput);
1212
+ scrollForm.appendChild(distanceInput);
1213
+ scrollForm.appendChild(scrollButton);
1214
+ const logList = doc.createElement('div');
1215
+ logList.style.height = '260px';
1216
+ logList.style.overflowY = 'auto';
1217
+ logList.style.background = '#0b1220';
1218
+ logList.style.border = '1px solid #1f2937';
1219
+ logList.style.borderRadius = '8px';
1220
+ logList.style.padding = '8px';
1221
+ logList.style.display = 'flex';
1222
+ logList.style.flexDirection = 'column';
1223
+ logList.style.gap = '6px';
1224
+ logList.style.flex = '1';
1225
+ logList.style.minHeight = '160px';
1226
+ const bubble = doc.createElement('div');
1227
+ bubble.textContent = 'VS';
1228
+ bubble.style.display = 'none';
1229
+ bubble.style.alignItems = 'center';
1230
+ bubble.style.justifyContent = 'center';
1231
+ bubble.style.fontWeight = '700';
1232
+ bubble.style.fontSize = '14px';
1233
+ bubble.style.letterSpacing = '0.5px';
1234
+ bubble.style.width = '100%';
1235
+ bubble.style.height = '100%';
1236
+ bubble.style.userSelect = 'none';
1237
+ bubble.addEventListener('click', () => {
1238
+ if (this.wasDragged) {
1239
+ this.wasDragged = false;
1240
+ return;
1241
+ }
1242
+ this.setCollapsed(false);
1243
+ });
1244
+ const contentWrapper = doc.createElement('div');
1245
+ contentWrapper.style.display = 'flex';
1246
+ contentWrapper.style.flexDirection = 'column';
1247
+ contentWrapper.style.gap = '10px';
1248
+ contentWrapper.style.width = '100%';
1249
+ contentWrapper.style.height = '100%';
1250
+ contentWrapper.appendChild(header);
1251
+ contentWrapper.appendChild(scrollForm);
1252
+ contentWrapper.appendChild(logList);
1253
+ container.appendChild(contentWrapper);
1254
+ container.appendChild(bubble);
1255
+ const resizeHandle = doc.createElement('div');
1256
+ resizeHandle.style.position = 'absolute';
1257
+ resizeHandle.style.right = '6px';
1258
+ resizeHandle.style.bottom = '6px';
1259
+ resizeHandle.style.width = '14px';
1260
+ resizeHandle.style.height = '14px';
1261
+ resizeHandle.style.cursor = 'nwse-resize';
1262
+ resizeHandle.style.borderRight = '2px solid #4b5563';
1263
+ resizeHandle.style.borderBottom = '2px solid #4b5563';
1264
+ resizeHandle.addEventListener('mousedown', event => {
1265
+ this.startResize(event);
1266
+ });
1267
+ container.appendChild(resizeHandle);
1268
+ doc.body.appendChild(container);
1269
+ this.container = container;
1270
+ this.contentWrapper = contentWrapper;
1271
+ this.bubble = bubble;
1272
+ this.logList = logList;
1273
+ this.selectorInput = selectorInput;
1274
+ this.distanceInput = distanceInput;
1275
+ this.collapseToggle = collapseButton;
1276
+ this.resizeHandle = resizeHandle;
1277
+ this.applyState();
1278
+ }
1279
+ startDrag(event) {
1280
+ if (event.button !== 0) {
1281
+ return;
1282
+ }
1283
+ if (!this.container) {
1284
+ return;
1285
+ }
1286
+ const rect = this.container.getBoundingClientRect();
1287
+ this.isDragging = true;
1288
+ this.wasDragged = false;
1289
+ this.dragMoved = false;
1290
+ this.dragOffsetX = event.clientX - rect.left;
1291
+ this.dragOffsetY = event.clientY - rect.top;
1292
+ this.container.style.transition = 'none';
1293
+ this.doc.addEventListener('mousemove', this.onDragging);
1294
+ this.doc.addEventListener('mouseup', this.onDragEnd);
1295
+ if (!this.state.collapsed) {
1296
+ event.preventDefault();
1297
+ }
1298
+ }
1299
+ startResize(event) {
1300
+ if (event.button !== 0 || this.state.collapsed) {
1301
+ return;
1302
+ }
1303
+ if (!this.container) {
1304
+ return;
1305
+ }
1306
+ const rect = this.container.getBoundingClientRect();
1307
+ this.isResizing = true;
1308
+ this.resizeStartX = event.clientX;
1309
+ this.resizeStartY = event.clientY;
1310
+ this.resizeStartWidth = rect.width;
1311
+ this.resizeStartHeight = rect.height;
1312
+ this.doc.addEventListener('mousemove', this.onResizing);
1313
+ this.doc.addEventListener('mouseup', this.onResizeEnd);
1314
+ event.preventDefault();
1315
+ event.stopPropagation();
1316
+ }
1317
+ applyPosition() {
1318
+ if (!this.container) {
1319
+ return;
1320
+ }
1321
+ this.container.style.left = `${this.state.left}px`;
1322
+ this.container.style.top = `${this.state.top}px`;
1323
+ }
1324
+ applySize() {
1325
+ if (!this.container) {
1326
+ return;
1327
+ }
1328
+ const width = Math.max(VirtualScrollDebugOverlay.minWidth, this.state.width || VirtualScrollDebugOverlay.defaultWidth);
1329
+ const height = Math.max(VirtualScrollDebugOverlay.minHeight, this.state.height || VirtualScrollDebugOverlay.defaultHeight);
1330
+ this.state.width = width;
1331
+ this.state.height = height;
1332
+ this.container.style.width = `${width}px`;
1333
+ this.container.style.height = `${height}px`;
1334
+ }
1335
+ applyCollapsedState() {
1336
+ if (!this.container || !this.contentWrapper || !this.bubble || !this.collapseToggle) {
1337
+ return;
1338
+ }
1339
+ if (this.state.collapsed) {
1340
+ this.container.style.width = '36px';
1341
+ this.container.style.height = '36px';
1342
+ this.container.style.minWidth = '';
1343
+ this.container.style.minHeight = '';
1344
+ this.container.style.padding = '0';
1345
+ this.container.style.borderRadius = '50%';
1346
+ this.container.style.display = 'flex';
1347
+ this.container.style.flexDirection = 'row';
1348
+ this.container.style.alignItems = 'center';
1349
+ this.container.style.justifyContent = 'center';
1350
+ this.container.style.cursor = 'move';
1351
+ this.contentWrapper.style.display = 'none';
1352
+ this.bubble.style.display = 'flex';
1353
+ this.collapseToggle.textContent = '展开';
1354
+ this.resizeHandle.style.display = 'none';
1355
+ }
1356
+ else {
1357
+ this.applySize();
1358
+ this.container.style.padding = '12px';
1359
+ this.container.style.borderRadius = '10px';
1360
+ this.container.style.display = 'flex';
1361
+ this.container.style.flexDirection = 'column';
1362
+ this.container.style.gap = '10px';
1363
+ this.container.style.cursor = 'default';
1364
+ this.contentWrapper.style.display = 'flex';
1365
+ this.bubble.style.display = 'none';
1366
+ this.collapseToggle.textContent = '折叠';
1367
+ this.resizeHandle.style.display = 'block';
1368
+ }
1369
+ }
1370
+ setCollapsed(collapsed) {
1371
+ this.state.collapsed = collapsed;
1372
+ this.applyCollapsedState();
1373
+ this.persistState();
1374
+ }
1375
+ applyState() {
1376
+ this.applyPosition();
1377
+ this.applyCollapsedState();
1378
+ }
1379
+ loadState() {
1380
+ try {
1381
+ const raw = this.doc.defaultView?.localStorage?.getItem(VirtualScrollDebugOverlay.storageKey);
1382
+ if (raw) {
1383
+ const parsed = JSON.parse(raw);
1384
+ if (typeof parsed.left === 'number') {
1385
+ this.state.left = parsed.left;
1386
+ }
1387
+ if (typeof parsed.top === 'number') {
1388
+ this.state.top = parsed.top;
1389
+ }
1390
+ if (typeof parsed.collapsed === 'boolean') {
1391
+ this.state.collapsed = parsed.collapsed;
1392
+ }
1393
+ if (typeof parsed.width === 'number') {
1394
+ this.state.width = parsed.width;
1395
+ }
1396
+ if (typeof parsed.height === 'number') {
1397
+ this.state.height = parsed.height;
1128
1398
  }
1129
- break;
1130
1399
  }
1131
1400
  }
1132
- apply(op);
1133
- for (const [source, key] of matches) {
1134
- const [node] = Editor.node(e, Path.isPath(source) ? source : source.current);
1135
- NODE_TO_KEY.set(node, key);
1401
+ catch {
1402
+ // ignore storage errors
1136
1403
  }
1137
- };
1138
- e.selectAll = () => {
1139
- Transforms.select(e, []);
1140
- };
1141
- return e;
1142
- };
1143
-
1144
- const TOP_BLUR = 'blur';
1145
- const TOP_COMPOSITION_END = 'compositionend';
1146
- const TOP_COMPOSITION_START = 'compositionstart';
1147
- const TOP_COMPOSITION_UPDATE = 'compositionupdate';
1148
- const TOP_KEY_DOWN = 'keydown';
1149
- const TOP_KEY_PRESS = 'keypress';
1150
- const TOP_KEY_UP = 'keyup';
1151
- const TOP_MOUSE_DOWN = 'mousedown';
1152
- const TOP_MOUSE_MOVE = 'mousemove';
1153
- const TOP_MOUSE_OUT = 'mouseout';
1154
- const TOP_TEXT_INPUT = 'textInput';
1155
- const TOP_PASTE = 'paste';
1156
-
1157
- /**
1158
- * Copyright (c) Facebook, Inc. and its affiliates.
1159
- *
1160
- * This source code is licensed under the MIT license found in the
1161
- * LICENSE file in the root directory of this source tree.
1162
- */
1163
- /**
1164
- * These variables store information about text content of a target node,
1165
- * allowing comparison of content before and after a given event.
1166
- *
1167
- * Identify the node where selection currently begins, then observe
1168
- * both its text content and its current position in the DOM. Since the
1169
- * browser may natively replace the target node during composition, we can
1170
- * use its position to find its replacement.
1171
- *
1172
- *
1173
- */
1174
- let root = null;
1175
- let startText = null;
1176
- let fallbackText = null;
1177
- function initialize(nativeEventTarget) {
1178
- root = nativeEventTarget;
1179
- startText = getText();
1180
- return true;
1181
- }
1182
- function reset() {
1183
- root = null;
1184
- startText = null;
1185
- fallbackText = null;
1186
- }
1187
- function getData() {
1188
- if (fallbackText) {
1189
- return fallbackText;
1190
1404
  }
1191
- let start;
1192
- const startValue = startText;
1193
- const startLength = startValue.length;
1194
- let end;
1195
- const endValue = getText();
1196
- const endLength = endValue.length;
1197
- for (start = 0; start < startLength; start++) {
1198
- if (startValue[start] !== endValue[start]) {
1199
- break;
1405
+ persistState() {
1406
+ try {
1407
+ this.doc.defaultView?.localStorage?.setItem(VirtualScrollDebugOverlay.storageKey, JSON.stringify(this.state));
1408
+ }
1409
+ catch {
1410
+ // ignore storage errors
1200
1411
  }
1201
1412
  }
1202
- const minEnd = startLength - start;
1203
- for (end = 1; end <= minEnd; end++) {
1204
- if (startValue[startLength - end] !== endValue[endLength - end]) {
1205
- break;
1413
+ appendLog(type, ...args) {
1414
+ if (!this.logList) {
1415
+ return;
1416
+ }
1417
+ const item = this.doc.createElement('div');
1418
+ item.style.display = 'flex';
1419
+ item.style.gap = '6px';
1420
+ item.style.alignItems = 'flex-start';
1421
+ item.style.wordBreak = 'break-all';
1422
+ item.style.color = type === 'warn' ? '#fbbf24' : '#9ca3af';
1423
+ const time = this.doc.createElement('span');
1424
+ time.textContent = new Date().toLocaleTimeString();
1425
+ time.style.color = '#6b7280';
1426
+ time.style.flexShrink = '0';
1427
+ time.style.width = '72px';
1428
+ const text = this.doc.createElement('span');
1429
+ text.textContent = `[${type}] ${args.map(arg => this.formatValue(arg)).join(' ')}`;
1430
+ item.appendChild(time);
1431
+ item.appendChild(text);
1432
+ this.logList.appendChild(item);
1433
+ }
1434
+ formatValue(value) {
1435
+ if (typeof value === 'string') {
1436
+ return value;
1437
+ }
1438
+ try {
1439
+ return JSON.stringify(value);
1440
+ }
1441
+ catch (error) {
1442
+ return String(value);
1206
1443
  }
1207
1444
  }
1208
- const sliceTail = end > 1 ? 1 - end : undefined;
1209
- fallbackText = endValue.slice(start, sliceTail);
1210
- return fallbackText;
1211
- }
1212
- function getText() {
1213
- if ('value' in root) {
1214
- return root.value;
1445
+ setScrollTopValue(value) {
1446
+ if (this.distanceInput) {
1447
+ this.distanceInput.value = String(value ?? 0);
1448
+ }
1215
1449
  }
1216
- return root.textContent;
1217
1450
  }
1218
1451
 
1219
- /**
1220
- * Copyright (c) Facebook, Inc. and its affiliates.
1221
- *
1222
- * This source code is licensed under the MIT license found in the
1223
- * LICENSE file in the root directory of this source tree.
1224
- */
1225
- const canUseDOM = !!(typeof window !== 'undefined' &&
1226
- typeof window.document !== 'undefined' &&
1227
- typeof window.document.createElement !== 'undefined');
1228
- const END_KEYCODES = [9, 13, 27, 32]; // Tab, Return, Esc, Space
1229
- const START_KEYCODE = 229;
1230
- const canUseCompositionEvent = canUseDOM && 'CompositionEvent' in window;
1231
- let documentMode = null;
1232
- if (canUseDOM && 'documentMode' in document) {
1233
- documentMode = document.documentMode;
1234
- }
1235
- // Webkit offers a very useful `textInput` event that can be used to
1236
- // directly represent `beforeInput`. The IE `textinput` event is not as
1237
- // useful, so we don't use it.
1238
- const canUseTextInputEvent = canUseDOM && 'TextEvent' in window && !documentMode;
1239
- // In IE9+, we have access to composition events, but the data supplied
1240
- // by the native compositionend event may be incorrect. Japanese ideographic
1241
- // spaces, for instance (\u3000) are not recorded correctly.
1242
- const useFallbackCompositionData = canUseDOM && (!canUseCompositionEvent || (documentMode && documentMode > 8 && documentMode <= 11));
1243
- const SPACEBAR_CODE = 32;
1244
- const SPACEBAR_CHAR = String.fromCharCode(SPACEBAR_CODE);
1245
- // Events and their corresponding property names.
1246
- const eventTypes = {
1247
- beforeInput: {
1248
- phasedRegistrationNames: {
1249
- bubbled: 'onBeforeInput',
1250
- captured: 'onBeforeInputCapture'
1251
- },
1252
- dependencies: [TOP_COMPOSITION_END, TOP_KEY_PRESS, TOP_TEXT_INPUT, TOP_PASTE]
1253
- }
1452
+ const isDebug = localStorage.getItem(SLATE_DEBUG_KEY) === 'true';
1453
+ const isDebugScrollTop = localStorage.getItem(SLATE_DEBUG_KEY_SCROLL_TOP) === 'true';
1454
+ const ELEMENT_KEY_TO_HEIGHTS = new WeakMap();
1455
+ const EDITOR_TO_BUSINESS_TOP = new WeakMap();
1456
+ const EDITOR_TO_ROOT_NODE_WIDTH = new WeakMap();
1457
+ const debugLog = (type, ...args) => {
1458
+ const doc = document;
1459
+ VirtualScrollDebugOverlay.log(doc, type, ...args);
1254
1460
  };
1255
- // Track whether we've ever handled a keypress on the space key.
1256
- let hasSpaceKeypress = false;
1257
- /**
1258
- * Return whether a native keypress event is assumed to be a command.
1259
- * This is required because Firefox fires `keypress` events for key commands
1260
- * (cut, copy, select-all, etc.) even though no character is inserted.
1261
- */
1262
- function isKeypressCommand(nativeEvent) {
1263
- return ((nativeEvent.ctrlKey || nativeEvent.altKey || nativeEvent.metaKey) &&
1264
- // ctrlKey && altKey is equivalent to AltGr, and is not a command.
1265
- !(nativeEvent.ctrlKey && nativeEvent.altKey));
1266
- }
1267
- /**
1268
- * Does our fallback mode think that this event is the end of composition?
1269
- *
1270
- */
1271
- function isFallbackCompositionEnd(topLevelType, nativeEvent) {
1272
- switch (topLevelType) {
1273
- case TOP_KEY_UP:
1274
- // Command keys insert or clear IME input.
1275
- return END_KEYCODES.indexOf(nativeEvent.keyCode) !== -1;
1276
- case TOP_KEY_DOWN:
1277
- // Expect IME keyCode on each keydown. If we get any other
1278
- // code we must have exited earlier.
1279
- return nativeEvent.keyCode !== START_KEYCODE;
1280
- case TOP_KEY_PRESS:
1281
- case TOP_MOUSE_DOWN:
1282
- case TOP_BLUR:
1283
- // Events are not possible without cancelling IME.
1284
- return true;
1285
- default:
1286
- return false;
1287
- }
1288
- }
1289
- /**
1290
- * Google Input Tools provides composition data via a CustomEvent,
1291
- * with the `data` property populated in the `detail` object. If this
1292
- * is available on the event object, use it. If not, this is a plain
1293
- * composition event and we have nothing special to extract.
1294
- *
1295
- */
1296
- function getDataFromCustomEvent(nativeEvent) {
1297
- const detail = nativeEvent.detail;
1298
- if (typeof detail === 'object' && 'data' in detail) {
1299
- return detail.data;
1300
- }
1301
- return null;
1302
- }
1303
- /**
1304
- * Check if a composition event was triggered by Korean IME.
1305
- * Our fallback mode does not work well with IE's Korean IME,
1306
- * so just use native composition events when Korean IME is used.
1307
- * Although CompositionEvent.locale property is deprecated,
1308
- * it is available in IE, where our fallback mode is enabled.
1309
- *
1310
- */
1311
- function isUsingKoreanIME(nativeEvent) {
1312
- return nativeEvent.locale === 'ko';
1313
- }
1314
- // Track the current IME composition status, if any.
1315
- let isComposing = false;
1316
- function getNativeBeforeInputChars(topLevelType, nativeEvent) {
1317
- switch (topLevelType) {
1318
- case TOP_COMPOSITION_END:
1319
- return getDataFromCustomEvent(nativeEvent);
1320
- case TOP_KEY_PRESS:
1321
- /**
1322
- * If native `textInput` events are available, our goal is to make
1323
- * use of them. However, there is a special case: the spacebar key.
1324
- * In Webkit, preventing default on a spacebar `textInput` event
1325
- * cancels character insertion, but it *also* causes the browser
1326
- * to fall back to its default spacebar behavior of scrolling the
1327
- * page.
1328
- *
1329
- * Tracking at:
1330
- * https://code.google.com/p/chromium/issues/detail?id=355103
1331
- *
1332
- * To avoid this issue, use the keypress event as if no `textInput`
1333
- * event is available.
1334
- */
1335
- const which = nativeEvent.which;
1336
- if (which !== SPACEBAR_CODE) {
1337
- return null;
1338
- }
1339
- hasSpaceKeypress = true;
1340
- return SPACEBAR_CHAR;
1341
- case TOP_TEXT_INPUT:
1342
- // Record the characters to be added to the DOM.
1343
- const chars = nativeEvent.data;
1344
- // If it's a spacebar character, assume that we have already handled
1345
- // it at the keypress level and bail immediately. Android Chrome
1346
- // doesn't give us keycodes, so we need to ignore it.
1347
- if (chars === SPACEBAR_CHAR && hasSpaceKeypress) {
1348
- return null;
1349
- }
1350
- return chars;
1351
- default:
1352
- // For other native event types, do nothing.
1353
- return null;
1354
- }
1355
- }
1356
- /**
1357
- * For browsers that do not provide the `textInput` event, extract the
1358
- * appropriate string to use for SyntheticInputEvent.
1359
- *
1360
- */
1361
- function getFallbackBeforeInputChars(topLevelType, nativeEvent) {
1362
- // If we are currently composing (IME) and using a fallback to do so,
1363
- // try to extract the composed characters from the fallback object.
1364
- // If composition event is available, we extract a string only at
1365
- // compositionevent, otherwise extract it at fallback events.
1366
- if (isComposing) {
1367
- if (topLevelType === TOP_COMPOSITION_END || (!canUseCompositionEvent && isFallbackCompositionEnd(topLevelType, nativeEvent))) {
1368
- const chars = getData();
1369
- reset();
1370
- isComposing = false;
1371
- return chars;
1372
- }
1373
- return null;
1461
+ const measureHeightByElement = (editor, element) => {
1462
+ const key = AngularEditor.findKey(editor, element);
1463
+ const view = ELEMENT_TO_COMPONENT.get(element);
1464
+ if (!view) {
1465
+ return;
1374
1466
  }
1375
- switch (topLevelType) {
1376
- case TOP_PASTE:
1377
- // If a paste event occurs after a keypress, throw out the input
1378
- // chars. Paste events should not lead to BeforeInput events.
1379
- return null;
1380
- case TOP_KEY_PRESS:
1381
- /**
1382
- * As of v27, Firefox may fire keypress events even when no character
1383
- * will be inserted. A few possibilities:
1384
- *
1385
- * - `which` is `0`. Arrow keys, Esc key, etc.
1386
- *
1387
- * - `which` is the pressed key code, but no char is available.
1388
- * Ex: 'AltGr + d` in Polish. There is no modified character for
1389
- * this key combination and no character is inserted into the
1390
- * document, but FF fires the keypress for char code `100` anyway.
1391
- * No `input` event will occur.
1392
- *
1393
- * - `which` is the pressed key code, but a command combination is
1394
- * being used. Ex: `Cmd+C`. No character is inserted, and no
1395
- * `input` event will occur.
1396
- */
1397
- if (!isKeypressCommand(nativeEvent)) {
1398
- // IE fires the `keypress` event when a user types an emoji via
1399
- // Touch keyboard of Windows. In such a case, the `char` property
1400
- // holds an emoji character like `\uD83D\uDE0A`. Because its length
1401
- // is 2, the property `which` does not represent an emoji correctly.
1402
- // In such a case, we directly return the `char` property instead of
1403
- // using `which`.
1404
- if (nativeEvent.char && nativeEvent.char.length > 1) {
1405
- return nativeEvent.char;
1406
- }
1407
- else if (nativeEvent.which) {
1408
- return String.fromCharCode(nativeEvent.which);
1467
+ const ret = view.getRealHeight();
1468
+ const heights = ELEMENT_KEY_TO_HEIGHTS.get(editor);
1469
+ heights.set(key.id, ret);
1470
+ return ret;
1471
+ };
1472
+ const measureHeightByIndics = (editor, indics, force = false) => {
1473
+ let hasChanged = false;
1474
+ indics.forEach((index, i) => {
1475
+ const element = editor.children[index];
1476
+ const preHeight = getRealHeightByElement(editor, element, 0);
1477
+ if (preHeight && !force) {
1478
+ if (isDebug) {
1479
+ const height = measureHeightByElement(editor, element);
1480
+ if (height !== preHeight) {
1481
+ debugLog('warn', 'measureHeightByElement: height not equal, index: ', index, 'preHeight: ', preHeight, 'height: ', height);
1409
1482
  }
1410
1483
  }
1411
- return null;
1412
- case TOP_COMPOSITION_END:
1413
- return useFallbackCompositionData && !isUsingKoreanIME(nativeEvent) ? null : nativeEvent.data;
1414
- default:
1415
- return null;
1484
+ return;
1485
+ }
1486
+ hasChanged = true;
1487
+ measureHeightByElement(editor, element);
1488
+ });
1489
+ return hasChanged;
1490
+ };
1491
+ const getBusinessTop = (editor) => {
1492
+ return EDITOR_TO_BUSINESS_TOP.get(editor) ?? 0;
1493
+ };
1494
+ const getRealHeightByElement = (editor, element, defaultHeight = VIRTUAL_SCROLL_DEFAULT_BLOCK_HEIGHT) => {
1495
+ const isVisible = editor.isVisible(element);
1496
+ if (!isVisible) {
1497
+ return 0;
1416
1498
  }
1417
- }
1418
- /**
1419
- * Extract a SyntheticInputEvent for `beforeInput`, based on either native
1420
- * `textInput` or fallback behavior.
1421
- *
1422
- */
1423
- function extractBeforeInputEvent(topLevelType, targetInst, nativeEvent, nativeEventTarget) {
1424
- let chars;
1425
- if (canUseTextInputEvent) {
1426
- chars = getNativeBeforeInputChars(topLevelType, nativeEvent);
1499
+ const heights = ELEMENT_KEY_TO_HEIGHTS.get(editor);
1500
+ const key = AngularEditor.findKey(editor, element);
1501
+ const height = heights?.get(key.id);
1502
+ if (typeof height === 'number') {
1503
+ return height;
1427
1504
  }
1428
- else {
1429
- chars = getFallbackBeforeInputChars(topLevelType, nativeEvent);
1505
+ if (heights?.has(key.id)) {
1506
+ console.error('getBlockHeight: invalid height value', key.id, height);
1430
1507
  }
1431
- // If no characters are being inserted, no BeforeInput event should
1432
- // be fired.
1433
- if (!chars) {
1434
- return null;
1508
+ return defaultHeight;
1509
+ };
1510
+ const buildHeightsAndAccumulatedHeights = (editor) => {
1511
+ const children = (editor.children || []);
1512
+ const heights = new Array(children.length);
1513
+ const accumulatedHeights = new Array(children.length + 1);
1514
+ accumulatedHeights[0] = 0;
1515
+ for (let i = 0; i < children.length; i++) {
1516
+ const height = getRealHeightByElement(editor, children[i]);
1517
+ heights[i] = height;
1518
+ accumulatedHeights[i + 1] = accumulatedHeights[i] + height;
1519
+ }
1520
+ return { heights, accumulatedHeights };
1521
+ };
1522
+ const calculateVirtualTopHeight = (editor, startIndex) => {
1523
+ const { accumulatedHeights } = buildHeightsAndAccumulatedHeights(editor);
1524
+ return accumulatedHeights[startIndex] ?? 0;
1525
+ };
1526
+ const scrollToElement = (editor, element, scrollTo) => {
1527
+ const children = editor.children;
1528
+ if (!children.length) {
1529
+ return;
1435
1530
  }
1436
- const beforeInputEvent = new BeforeInputEvent();
1437
- beforeInputEvent.data = chars;
1438
- beforeInputEvent.nativeEvent = nativeEvent;
1439
- return beforeInputEvent;
1440
- }
1441
- /**
1442
- * Create an `onBeforeInput` event to match
1443
- * http://www.w3.org/TR/2013/WD-DOM-Level-3-Events-20131105/#events-inputevents.
1444
- *
1445
- * This event plugin is based on the native `textInput` event
1446
- * available in Chrome, Safari, Opera, and IE. This event fires after
1447
- * `onKeyPress` and `onCompositionEnd`, but before `onInput`.
1448
- *
1449
- * `beforeInput` is spec'd but not implemented in any browsers, and
1450
- * the `input` event does not provide any useful information about what has
1451
- * actually been added, contrary to the spec. Thus, `textInput` is the best
1452
- * available event to identify the characters that have actually been inserted
1453
- * into the target node.
1454
- *
1455
- * This plugin is also responsible for emitting `composition` events, thus
1456
- * allowing us to share composition fallback code for both `beforeInput` and
1457
- * `composition` event types.
1458
- */
1459
- const BeforeInputEventPlugin = {
1460
- extractEvents: (topLevelType, targetInst, nativeEvent, nativeEventTarget) => {
1461
- return extractBeforeInputEvent(topLevelType, targetInst, nativeEvent, nativeEventTarget);
1531
+ const anchorIndex = children.findIndex(item => item === element);
1532
+ if (anchorIndex < 0) {
1533
+ return;
1462
1534
  }
1535
+ const { accumulatedHeights } = buildHeightsAndAccumulatedHeights(editor);
1536
+ scrollTo((accumulatedHeights[anchorIndex] ?? 0) + getBusinessTop(editor));
1463
1537
  };
1464
- class BeforeInputEvent {
1465
- }
1466
1538
 
1467
- const BEFORE_INPUT_EVENTS = [
1468
- // { name: 'blur', handler: 'onBlur', isTriggerBeforeInput: true },
1469
- // { name: 'compositionstart', handler: 'onCompositionStart', isTriggerBeforeInput: true },
1470
- { name: 'compositionupdate', handler: null, isTriggerBeforeInput: true },
1471
- // { name: 'compositionend', handler: 'onCompositionEnd', isTriggerBeforeInput: false },
1472
- // { name: 'keydown', handler: 'onKeyDown', isTriggerBeforeInput: true },
1473
- { name: 'keypress', handler: null, isTriggerBeforeInput: true },
1474
- { name: 'keyup', handler: 'onKeyUp', isTriggerBeforeInput: true },
1475
- { name: 'mousedown', handler: 'onMouseDown', isTriggerBeforeInput: true },
1476
- { name: 'textInput', handler: null, isTriggerBeforeInput: true }
1477
- // { name: 'paste', handler: 'onPaste', isTriggerBeforeInput: true }
1478
- ];
1479
-
1480
- var SlateErrorCode;
1481
- (function (SlateErrorCode) {
1482
- SlateErrorCode[SlateErrorCode["ToNativeSelectionError"] = 2100] = "ToNativeSelectionError";
1483
- SlateErrorCode[SlateErrorCode["ToSlateSelectionError"] = 2101] = "ToSlateSelectionError";
1484
- SlateErrorCode[SlateErrorCode["OnDOMBeforeInputError"] = 2102] = "OnDOMBeforeInputError";
1485
- SlateErrorCode[SlateErrorCode["OnSyntheticBeforeInputError"] = 2103] = "OnSyntheticBeforeInputError";
1486
- SlateErrorCode[SlateErrorCode["OnDOMKeydownError"] = 2104] = "OnDOMKeydownError";
1487
- SlateErrorCode[SlateErrorCode["GetStartPointError"] = 2105] = "GetStartPointError";
1488
- SlateErrorCode[SlateErrorCode["NotFoundPreviousRootNodeError"] = 3100] = "NotFoundPreviousRootNodeError";
1489
- SlateErrorCode[SlateErrorCode["InvalidValueError"] = 4100] = "InvalidValueError";
1490
- })(SlateErrorCode || (SlateErrorCode = {}));
1491
-
1492
- function restoreDom(editor, execute) {
1493
- const editable = EDITOR_TO_ELEMENT.get(editor);
1494
- let observer = new MutationObserver(mutations => {
1495
- mutations.reverse().forEach(mutation => {
1496
- if (mutation.type === 'characterData') {
1497
- // We don't want to restore the DOM for characterData mutations
1498
- // because this interrupts the composition.
1499
- return;
1539
+ const withAngular = (editor, clipboardFormatKey = 'x-slate-fragment') => {
1540
+ let e = editor;
1541
+ let { apply } = e;
1542
+ e = withDOM(e, clipboardFormatKey);
1543
+ e.setFragmentData = (dataTransfer, originEvent) => {
1544
+ const { selection } = e;
1545
+ if (!selection) {
1546
+ return;
1547
+ }
1548
+ const [start, end] = Range.edges(selection);
1549
+ const startVoid = Editor.void(e, { at: start.path });
1550
+ const endVoid = Editor.void(e, { at: end.path });
1551
+ if (Range.isCollapsed(selection) && !startVoid) {
1552
+ return;
1553
+ }
1554
+ // Create a fake selection so that we can add a Base64-encoded copy of the
1555
+ // fragment to the HTML, to decode on future pastes.
1556
+ let domRange;
1557
+ if (AngularEditor.isEnabledVirtualScroll(e)) {
1558
+ const virtualScrollSelection = EDITOR_TO_VIRTUAL_SCROLL_SELECTION.get(e);
1559
+ if (virtualScrollSelection) {
1560
+ domRange = AngularEditor.toDOMRange(e, virtualScrollSelection);
1500
1561
  }
1501
- mutation.removedNodes.forEach(node => {
1502
- mutation.target.insertBefore(node, mutation.nextSibling);
1503
- });
1504
- mutation.addedNodes.forEach(node => {
1505
- mutation.target.removeChild(node);
1506
- });
1507
- });
1508
- disconnect();
1509
- execute();
1510
- });
1511
- const disconnect = () => {
1512
- observer.disconnect();
1513
- observer = null;
1514
- };
1515
- observer.observe(editable, { subtree: true, childList: true, characterData: true, characterDataOldValue: true });
1516
- setTimeout(() => {
1517
- if (observer) {
1518
- disconnect();
1519
- execute();
1520
1562
  }
1521
- }, 0);
1522
- }
1523
-
1524
- class FlavourRef {
1525
- destroy() {
1526
- this.instance.onDestroy();
1527
- }
1528
- }
1529
- class BlockCardRef {
1530
- destroy() {
1531
- this.instance.onDestroy();
1532
- }
1533
- }
1534
-
1535
- function createEmbeddedViewOrComponentOrFlavour(viewType, context, viewContext, viewContainerRef) {
1536
- if (isFlavourType(viewType)) {
1537
- const flavourRef = new FlavourRef();
1538
- flavourRef.instance = new viewType();
1539
- flavourRef.instance.context = context;
1540
- flavourRef.instance.viewContext = viewContext;
1541
- flavourRef.instance.viewContainerRef = viewContainerRef;
1542
- flavourRef.instance.onInit();
1543
- return flavourRef;
1544
- }
1545
- if (isTemplateRef(viewType)) {
1546
- const embeddedViewContext = {
1547
- context,
1548
- viewContext
1549
- };
1550
- const embeddedViewRef = viewContainerRef.createEmbeddedView(viewType, embeddedViewContext);
1551
- embeddedViewRef.detectChanges();
1552
- return embeddedViewRef;
1553
- }
1554
- if (isComponentType(viewType)) {
1555
- const componentRef = viewContainerRef.createComponent(viewType, {
1556
- injector: viewContainerRef.injector
1557
- });
1558
- componentRef.instance.viewContext = viewContext;
1559
- componentRef.instance.context = context;
1560
- componentRef.changeDetectorRef.detectChanges();
1561
- return componentRef;
1562
- }
1563
- }
1564
- function updateContext(view, newContext, viewContext) {
1565
- if (view instanceof FlavourRef) {
1566
- view.instance.context = newContext;
1567
- return;
1568
- }
1569
- if (view instanceof ComponentRef) {
1570
- view.instance.context = newContext;
1571
- }
1572
- else {
1573
- view.context.context = newContext;
1574
- view.context.viewContext = viewContext;
1575
- view.detectChanges();
1576
- }
1577
- }
1578
- function mount(views, blockCards, outletParent, outletElement) {
1579
- if (views.length > 0) {
1580
- const fragment = document.createDocumentFragment();
1581
- views.forEach((view, index) => {
1582
- const blockCard = blockCards ? blockCards[index] : undefined;
1583
- fragment.append(...getRootNodes(view, blockCard));
1563
+ domRange = domRange ?? AngularEditor.toDOMRange(e, selection);
1564
+ let contents = domRange.cloneContents();
1565
+ let attach = contents.childNodes[0];
1566
+ // Make sure attach is non-empty, since empty nodes will not get copied.
1567
+ const contentsArray = Array.from(contents.children);
1568
+ contentsArray.forEach(node => {
1569
+ if (node.textContent && node.textContent.trim() !== '') {
1570
+ attach = node;
1571
+ }
1584
1572
  });
1585
- if (outletElement) {
1586
- outletElement.parentElement.insertBefore(fragment, outletElement);
1587
- outletElement.remove();
1573
+ // COMPAT: If the end node is a void node, we need to move the end of the
1574
+ // range from the void node's spacer span, to the end of the void node's
1575
+ // content, since the spacer is before void's content in the DOM.
1576
+ if (endVoid) {
1577
+ const [voidNode] = endVoid;
1578
+ const r = domRange.cloneRange();
1579
+ const domNode = AngularEditor.toDOMNode(e, voidNode);
1580
+ r.setEndAfter(domNode);
1581
+ contents = r.cloneContents();
1588
1582
  }
1589
- else {
1590
- outletParent.prepend(fragment);
1583
+ // COMPAT: If the start node is a void node, we need to attach the encoded
1584
+ // fragment to the void node's content node instead of the spacer, because
1585
+ // attaching it to empty `<div>/<span>` nodes will end up having it erased by
1586
+ // most browsers. (2018/04/27)
1587
+ if (startVoid) {
1588
+ attach = contents.querySelector('[data-slate-spacer]');
1591
1589
  }
1592
- }
1593
- }
1594
- function getRootNodes(ref, blockCard) {
1595
- if (blockCard) {
1596
- return [blockCard.instance.nativeElement];
1597
- }
1598
- if (ref instanceof FlavourRef) {
1599
- return [ref.instance.nativeElement];
1600
- }
1601
- if (ref instanceof ComponentRef) {
1602
- ref.hostView.rootNodes.forEach(ele => {
1603
- if (!(ele instanceof HTMLElement)) {
1604
- ele.remove();
1605
- }
1590
+ // Remove any zero-width space spans from the cloned DOM so that they don't
1591
+ // show up elsewhere when pasted.
1592
+ Array.from(contents.querySelectorAll('[data-slate-zero-width]')).forEach(zw => {
1593
+ const isNewline = zw.getAttribute('data-slate-zero-width') === 'n';
1594
+ zw.textContent = isNewline ? '\n' : '';
1606
1595
  });
1607
- return [ref.instance.nativeElement];
1608
- }
1609
- else {
1610
- const result = [];
1611
- ref.rootNodes.forEach(rootNode => {
1612
- const isHTMLElement = rootNode instanceof HTMLElement;
1613
- const isSlateNodeOfLeaf = isHTMLElement && (rootNode.hasAttribute('data-slate-node') || rootNode.hasAttribute('data-slate-leaf'));
1614
- if (isSlateNodeOfLeaf && result.every(item => !item.contains(rootNode))) {
1615
- result.push(rootNode);
1596
+ // Set a `data-slate-fragment` attribute on a non-empty node, so it shows up
1597
+ // in the HTML, and can be used for intra-Slate pasting. If it's a text
1598
+ // node, wrap it in a `<span>` so we have something to set an attribute on.
1599
+ if (isDOMText(attach)) {
1600
+ const span = attach.ownerDocument.createElement('span');
1601
+ // COMPAT: In Chrome and Safari, if we don't add the `white-space` style
1602
+ // then leading and trailing spaces will be ignored. (2017/09/21)
1603
+ span.style.whiteSpace = 'pre';
1604
+ span.appendChild(attach);
1605
+ contents.appendChild(span);
1606
+ attach = span;
1607
+ }
1608
+ const fragment = e.getFragment();
1609
+ // Add the content to a <div> so that we can get its inner HTML.
1610
+ const div = contents.ownerDocument.createElement('div');
1611
+ const attachWrapper = document.createElement('div');
1612
+ const elements = Array.from(contents.children);
1613
+ if (isInvalidTable(elements)) {
1614
+ contents = completeTable(contents.cloneNode(true));
1615
+ }
1616
+ attachWrapper.appendChild(contents);
1617
+ div.appendChild(attachWrapper);
1618
+ div.setAttribute('hidden', 'true');
1619
+ contents.ownerDocument.body.appendChild(div);
1620
+ setClipboardData({ text: getPlainText(div), elements: fragment }, div, attachWrapper, dataTransfer);
1621
+ contents.ownerDocument.body.removeChild(div);
1622
+ };
1623
+ e.deleteCutData = () => {
1624
+ const { selection } = editor;
1625
+ if (selection) {
1626
+ if (Range.isExpanded(selection)) {
1627
+ Editor.deleteFragment(editor);
1616
1628
  }
1617
- if (!isHTMLElement) {
1618
- rootNode.remove();
1629
+ else {
1630
+ const node = Node.parent(editor, selection.anchor.path);
1631
+ if (Element.isElement(node) && Editor.isVoid(editor, node)) {
1632
+ Transforms.delete(editor);
1633
+ }
1619
1634
  }
1620
- });
1621
- return result;
1622
- }
1623
- }
1624
- function mountOnItemChange(index, item, views, blockCards, outletParent, firstRootNode, viewContext) {
1625
- const view = views[index];
1626
- let rootNodes = getRootNodes(view);
1627
- if (blockCards) {
1628
- const isBlockCard = viewContext.editor.isBlockCard(item);
1629
- if (isBlockCard) {
1630
- const blockCard = blockCards[index];
1631
- rootNodes = [blockCard.instance.nativeElement];
1632
1635
  }
1633
- }
1634
- if (index === 0) {
1635
- if (firstRootNode) {
1636
- rootNodes.forEach(rootNode => {
1637
- firstRootNode.insertAdjacentElement('beforebegin', rootNode);
1638
- });
1636
+ };
1637
+ e.insertData = async (data) => {
1638
+ if (!(await e.customInsertFragmentData(data, null))) {
1639
+ e.insertTextData(data);
1639
1640
  }
1640
- else {
1641
- outletParent.prepend(...rootNodes);
1641
+ };
1642
+ e.customInsertFragmentData = async (data, contextClipboardData) => {
1643
+ /**
1644
+ * Checking copied fragment from application/x-slate-fragment or data-slate-fragment
1645
+ */
1646
+ const clipboardData = contextClipboardData || (await getClipboardData(data));
1647
+ if (clipboardData && clipboardData.elements) {
1648
+ e.insertFragment(clipboardData.elements);
1649
+ return true;
1642
1650
  }
1643
- }
1644
- else {
1645
- const previousView = views[index - 1];
1646
- const blockCard = blockCards ? blockCards[index - 1] : null;
1647
- const previousRootNodes = getRootNodes(previousView, blockCard);
1648
- let previousRootNode = previousRootNodes[previousRootNodes.length - 1];
1649
- rootNodes.forEach(rootNode => {
1650
- previousRootNode.insertAdjacentElement('afterend', rootNode);
1651
- previousRootNode = rootNode;
1652
- });
1653
- }
1654
- }
1655
-
1656
- function hasBeforeContextChange(value) {
1657
- if (value.beforeContextChange) {
1651
+ return false;
1652
+ };
1653
+ e.customInsertTextData = async (data) => {
1654
+ const clipboardData = await getClipboardData(data);
1655
+ if (clipboardData && clipboardData.text) {
1656
+ const lines = clipboardData.text.split(/\r\n|\r|\n/);
1657
+ let split = false;
1658
+ for (const line of lines) {
1659
+ if (split) {
1660
+ Transforms.splitNodes(e, { always: true });
1661
+ }
1662
+ e.insertText(line);
1663
+ split = true;
1664
+ }
1665
+ return true;
1666
+ }
1667
+ return false;
1668
+ };
1669
+ e.onKeydown = () => { };
1670
+ e.onClick = () => { };
1671
+ e.isBlockCard = element => false;
1672
+ e.isExpanded = element => true;
1673
+ e.onError = (errorData) => {
1674
+ if (errorData.nativeError) {
1675
+ console.error(errorData.nativeError);
1676
+ }
1677
+ else {
1678
+ console.error(errorData);
1679
+ }
1680
+ };
1681
+ // exist issue for move operation in withDOM
1682
+ e.apply = (op) => {
1683
+ const matches = [];
1684
+ switch (op.type) {
1685
+ case 'insert_text':
1686
+ case 'remove_text':
1687
+ case 'set_node': {
1688
+ for (const [node, path] of Editor.levels(e, { at: op.path })) {
1689
+ const key = AngularEditor.findKey(e, node);
1690
+ matches.push([path, key]);
1691
+ }
1692
+ break;
1693
+ }
1694
+ case 'insert_node':
1695
+ case 'remove_node':
1696
+ case 'merge_node':
1697
+ case 'split_node': {
1698
+ for (const [node, path] of Editor.levels(e, {
1699
+ at: Path.parent(op.path)
1700
+ })) {
1701
+ const key = AngularEditor.findKey(e, node);
1702
+ matches.push([path, key]);
1703
+ }
1704
+ break;
1705
+ }
1706
+ case 'move_node': {
1707
+ const commonPath = Path.common(Path.parent(op.path), Path.parent(op.newPath));
1708
+ for (const [node, path] of Editor.levels(e, {
1709
+ at: Path.parent(op.path)
1710
+ })) {
1711
+ const key = AngularEditor.findKey(e, node);
1712
+ matches.push([Editor.pathRef(editor, path), key]);
1713
+ }
1714
+ for (const [node, path] of Editor.levels(e, {
1715
+ at: Path.parent(op.newPath)
1716
+ })) {
1717
+ if (path.length > commonPath.length) {
1718
+ const key = AngularEditor.findKey(e, node);
1719
+ matches.push([Editor.pathRef(editor, path), key]);
1720
+ }
1721
+ }
1722
+ break;
1723
+ }
1724
+ }
1725
+ apply(op);
1726
+ for (const [source, key] of matches) {
1727
+ const [node] = Editor.node(e, Path.isPath(source) ? source : source.current);
1728
+ NODE_TO_KEY.set(node, key);
1729
+ }
1730
+ };
1731
+ e.selectAll = () => {
1732
+ Transforms.select(e, []);
1733
+ };
1734
+ e.isVisible = element => {
1658
1735
  return true;
1659
- }
1660
- return false;
1736
+ };
1737
+ return e;
1738
+ };
1739
+
1740
+ const TOP_BLUR = 'blur';
1741
+ const TOP_COMPOSITION_END = 'compositionend';
1742
+ const TOP_COMPOSITION_START = 'compositionstart';
1743
+ const TOP_COMPOSITION_UPDATE = 'compositionupdate';
1744
+ const TOP_KEY_DOWN = 'keydown';
1745
+ const TOP_KEY_PRESS = 'keypress';
1746
+ const TOP_KEY_UP = 'keyup';
1747
+ const TOP_MOUSE_DOWN = 'mousedown';
1748
+ const TOP_MOUSE_MOVE = 'mousemove';
1749
+ const TOP_MOUSE_OUT = 'mouseout';
1750
+ const TOP_TEXT_INPUT = 'textInput';
1751
+ const TOP_PASTE = 'paste';
1752
+
1753
+ /**
1754
+ * Copyright (c) Facebook, Inc. and its affiliates.
1755
+ *
1756
+ * This source code is licensed under the MIT license found in the
1757
+ * LICENSE file in the root directory of this source tree.
1758
+ */
1759
+ /**
1760
+ * These variables store information about text content of a target node,
1761
+ * allowing comparison of content before and after a given event.
1762
+ *
1763
+ * Identify the node where selection currently begins, then observe
1764
+ * both its text content and its current position in the DOM. Since the
1765
+ * browser may natively replace the target node during composition, we can
1766
+ * use its position to find its replacement.
1767
+ *
1768
+ *
1769
+ */
1770
+ let root = null;
1771
+ let startText = null;
1772
+ let fallbackText = null;
1773
+ function initialize(nativeEventTarget) {
1774
+ root = nativeEventTarget;
1775
+ startText = getText();
1776
+ return true;
1661
1777
  }
1662
- function hasAfterContextChange(value) {
1663
- if (value.afterContextChange) {
1664
- return true;
1665
- }
1666
- return false;
1778
+ function reset() {
1779
+ root = null;
1780
+ startText = null;
1781
+ fallbackText = null;
1667
1782
  }
1668
-
1669
- class BaseFlavour {
1670
- constructor() {
1671
- this.initialized = false;
1783
+ function getData() {
1784
+ if (fallbackText) {
1785
+ return fallbackText;
1672
1786
  }
1673
- static { this.isFlavour = true; }
1674
- set context(value) {
1675
- if (hasBeforeContextChange(this)) {
1676
- this.beforeContextChange(value);
1677
- }
1678
- this._context = value;
1679
- this.onContextChange();
1680
- if (hasAfterContextChange(this)) {
1681
- this.afterContextChange();
1787
+ let start;
1788
+ const startValue = startText;
1789
+ const startLength = startValue.length;
1790
+ let end;
1791
+ const endValue = getText();
1792
+ const endLength = endValue.length;
1793
+ for (start = 0; start < startLength; start++) {
1794
+ if (startValue[start] !== endValue[start]) {
1795
+ break;
1682
1796
  }
1683
1797
  }
1684
- get context() {
1685
- return this._context;
1686
- }
1687
- get editor() {
1688
- return this.viewContext && this.viewContext.editor;
1798
+ const minEnd = startLength - start;
1799
+ for (end = 1; end <= minEnd; end++) {
1800
+ if (startValue[startLength - end] !== endValue[endLength - end]) {
1801
+ break;
1802
+ }
1689
1803
  }
1804
+ const sliceTail = end > 1 ? 1 - end : undefined;
1805
+ fallbackText = endValue.slice(start, sliceTail);
1806
+ return fallbackText;
1690
1807
  }
1691
-
1692
- const SLATE_BLOCK_CARD_CLASS_NAME = 'slate-block-card';
1693
- class SlateBlockCard {
1694
- onInit() {
1695
- const nativeElement = document.createElement('div');
1696
- nativeElement.classList.add(SLATE_BLOCK_CARD_CLASS_NAME);
1697
- this.nativeElement = nativeElement;
1698
- this.createContent();
1699
- }
1700
- createContent() {
1701
- const leftCaret = document.createElement('span');
1702
- leftCaret.setAttribute(`card-target`, 'card-left');
1703
- leftCaret.classList.add('card-left');
1704
- leftCaret.appendChild(getZeroTextNode());
1705
- const rightCaret = document.createElement('span');
1706
- rightCaret.setAttribute(`card-target`, 'card-right');
1707
- rightCaret.classList.add('card-right');
1708
- rightCaret.appendChild(getZeroTextNode());
1709
- const center = document.createElement('div');
1710
- center.setAttribute(`card-target`, 'card-center');
1711
- this.nativeElement.appendChild(leftCaret);
1712
- this.nativeElement.appendChild(center);
1713
- this.nativeElement.appendChild(rightCaret);
1714
- this.centerContainer = center;
1715
- }
1716
- append() {
1717
- this.centerRootNodes.forEach(rootNode => !this.centerContainer.contains(rootNode) && this.centerContainer.appendChild(rootNode));
1718
- }
1719
- initializeCenter(rootNodes) {
1720
- this.centerRootNodes = rootNodes;
1721
- this.append();
1722
- }
1723
- onDestroy() {
1724
- this.nativeElement.remove();
1808
+ function getText() {
1809
+ if ('value' in root) {
1810
+ return root.value;
1725
1811
  }
1812
+ return root.textContent;
1726
1813
  }
1727
- const getBlockCardByNativeElement = (nativeElement) => {
1728
- const blockCardElement = nativeElement?.parentElement?.parentElement;
1729
- if (blockCardElement && blockCardElement.classList.contains(SLATE_BLOCK_CARD_CLASS_NAME)) {
1730
- return blockCardElement;
1731
- }
1732
- return null;
1733
- };
1734
1814
 
1735
- const DEFAULT_ELEMENT_HEIGHT = 24;
1736
- class BaseElementFlavour extends BaseFlavour {
1737
- constructor() {
1738
- super(...arguments);
1739
- this.getOutletParent = () => {
1740
- return this.nativeElement;
1741
- };
1742
- this.getOutletElement = () => {
1743
- return this.nativeElement.querySelector('.children-outlet');
1744
- };
1745
- this.stableHeight = null;
1746
- }
1747
- get element() {
1748
- return this._context && this._context.element;
1749
- }
1750
- get selection() {
1751
- return this._context && this._context.selection;
1752
- }
1753
- get decorations() {
1754
- return this._context && this._context.decorations;
1755
- }
1756
- get children() {
1757
- return this._context && this._context.element.children;
1758
- }
1759
- get isCollapsed() {
1760
- return this.selection && Range.isCollapsed(this.selection);
1761
- }
1815
+ /**
1816
+ * Copyright (c) Facebook, Inc. and its affiliates.
1817
+ *
1818
+ * This source code is licensed under the MIT license found in the
1819
+ * LICENSE file in the root directory of this source tree.
1820
+ */
1821
+ const canUseDOM = !!(typeof window !== 'undefined' &&
1822
+ typeof window.document !== 'undefined' &&
1823
+ typeof window.document.createElement !== 'undefined');
1824
+ const END_KEYCODES = [9, 13, 27, 32]; // Tab, Return, Esc, Space
1825
+ const START_KEYCODE = 229;
1826
+ const canUseCompositionEvent = canUseDOM && 'CompositionEvent' in window;
1827
+ let documentMode = null;
1828
+ if (canUseDOM && 'documentMode' in document) {
1829
+ documentMode = document.documentMode;
1830
+ }
1831
+ // Webkit offers a very useful `textInput` event that can be used to
1832
+ // directly represent `beforeInput`. The IE `textinput` event is not as
1833
+ // useful, so we don't use it.
1834
+ const canUseTextInputEvent = canUseDOM && 'TextEvent' in window && !documentMode;
1835
+ // In IE9+, we have access to composition events, but the data supplied
1836
+ // by the native compositionend event may be incorrect. Japanese ideographic
1837
+ // spaces, for instance (\u3000) are not recorded correctly.
1838
+ const useFallbackCompositionData = canUseDOM && (!canUseCompositionEvent || (documentMode && documentMode > 8 && documentMode <= 11));
1839
+ const SPACEBAR_CODE = 32;
1840
+ const SPACEBAR_CHAR = String.fromCharCode(SPACEBAR_CODE);
1841
+ // Events and their corresponding property names.
1842
+ const eventTypes = {
1843
+ beforeInput: {
1844
+ phasedRegistrationNames: {
1845
+ bubbled: 'onBeforeInput',
1846
+ captured: 'onBeforeInputCapture'
1847
+ },
1848
+ dependencies: [TOP_COMPOSITION_END, TOP_KEY_PRESS, TOP_TEXT_INPUT, TOP_PASTE]
1849
+ }
1850
+ };
1851
+ // Track whether we've ever handled a keypress on the space key.
1852
+ let hasSpaceKeypress = false;
1853
+ /**
1854
+ * Return whether a native keypress event is assumed to be a command.
1855
+ * This is required because Firefox fires `keypress` events for key commands
1856
+ * (cut, copy, select-all, etc.) even though no character is inserted.
1857
+ */
1858
+ function isKeypressCommand(nativeEvent) {
1859
+ return ((nativeEvent.ctrlKey || nativeEvent.altKey || nativeEvent.metaKey) &&
1860
+ // ctrlKey && altKey is equivalent to AltGr, and is not a command.
1861
+ !(nativeEvent.ctrlKey && nativeEvent.altKey));
1862
+ }
1863
+ /**
1864
+ * Does our fallback mode think that this event is the end of composition?
1865
+ *
1866
+ */
1867
+ function isFallbackCompositionEnd(topLevelType, nativeEvent) {
1868
+ switch (topLevelType) {
1869
+ case TOP_KEY_UP:
1870
+ // Command keys insert or clear IME input.
1871
+ return END_KEYCODES.indexOf(nativeEvent.keyCode) !== -1;
1872
+ case TOP_KEY_DOWN:
1873
+ // Expect IME keyCode on each keydown. If we get any other
1874
+ // code we must have exited earlier.
1875
+ return nativeEvent.keyCode !== START_KEYCODE;
1876
+ case TOP_KEY_PRESS:
1877
+ case TOP_MOUSE_DOWN:
1878
+ case TOP_BLUR:
1879
+ // Events are not possible without cancelling IME.
1880
+ return true;
1881
+ default:
1882
+ return false;
1883
+ }
1884
+ }
1885
+ /**
1886
+ * Google Input Tools provides composition data via a CustomEvent,
1887
+ * with the `data` property populated in the `detail` object. If this
1888
+ * is available on the event object, use it. If not, this is a plain
1889
+ * composition event and we have nothing special to extract.
1890
+ *
1891
+ */
1892
+ function getDataFromCustomEvent(nativeEvent) {
1893
+ const detail = nativeEvent.detail;
1894
+ if (typeof detail === 'object' && 'data' in detail) {
1895
+ return detail.data;
1896
+ }
1897
+ return null;
1898
+ }
1899
+ /**
1900
+ * Check if a composition event was triggered by Korean IME.
1901
+ * Our fallback mode does not work well with IE's Korean IME,
1902
+ * so just use native composition events when Korean IME is used.
1903
+ * Although CompositionEvent.locale property is deprecated,
1904
+ * it is available in IE, where our fallback mode is enabled.
1905
+ *
1906
+ */
1907
+ function isUsingKoreanIME(nativeEvent) {
1908
+ return nativeEvent.locale === 'ko';
1909
+ }
1910
+ // Track the current IME composition status, if any.
1911
+ let isComposing = false;
1912
+ function getNativeBeforeInputChars(topLevelType, nativeEvent) {
1913
+ switch (topLevelType) {
1914
+ case TOP_COMPOSITION_END:
1915
+ return getDataFromCustomEvent(nativeEvent);
1916
+ case TOP_KEY_PRESS:
1917
+ /**
1918
+ * If native `textInput` events are available, our goal is to make
1919
+ * use of them. However, there is a special case: the spacebar key.
1920
+ * In Webkit, preventing default on a spacebar `textInput` event
1921
+ * cancels character insertion, but it *also* causes the browser
1922
+ * to fall back to its default spacebar behavior of scrolling the
1923
+ * page.
1924
+ *
1925
+ * Tracking at:
1926
+ * https://code.google.com/p/chromium/issues/detail?id=355103
1927
+ *
1928
+ * To avoid this issue, use the keypress event as if no `textInput`
1929
+ * event is available.
1930
+ */
1931
+ const which = nativeEvent.which;
1932
+ if (which !== SPACEBAR_CODE) {
1933
+ return null;
1934
+ }
1935
+ hasSpaceKeypress = true;
1936
+ return SPACEBAR_CHAR;
1937
+ case TOP_TEXT_INPUT:
1938
+ // Record the characters to be added to the DOM.
1939
+ const chars = nativeEvent.data;
1940
+ // If it's a spacebar character, assume that we have already handled
1941
+ // it at the keypress level and bail immediately. Android Chrome
1942
+ // doesn't give us keycodes, so we need to ignore it.
1943
+ if (chars === SPACEBAR_CHAR && hasSpaceKeypress) {
1944
+ return null;
1945
+ }
1946
+ return chars;
1947
+ default:
1948
+ // For other native event types, do nothing.
1949
+ return null;
1950
+ }
1951
+ }
1952
+ /**
1953
+ * For browsers that do not provide the `textInput` event, extract the
1954
+ * appropriate string to use for SyntheticInputEvent.
1955
+ *
1956
+ */
1957
+ function getFallbackBeforeInputChars(topLevelType, nativeEvent) {
1958
+ // If we are currently composing (IME) and using a fallback to do so,
1959
+ // try to extract the composed characters from the fallback object.
1960
+ // If composition event is available, we extract a string only at
1961
+ // compositionevent, otherwise extract it at fallback events.
1962
+ if (isComposing) {
1963
+ if (topLevelType === TOP_COMPOSITION_END || (!canUseCompositionEvent && isFallbackCompositionEnd(topLevelType, nativeEvent))) {
1964
+ const chars = getData();
1965
+ reset();
1966
+ isComposing = false;
1967
+ return chars;
1968
+ }
1969
+ return null;
1970
+ }
1971
+ switch (topLevelType) {
1972
+ case TOP_PASTE:
1973
+ // If a paste event occurs after a keypress, throw out the input
1974
+ // chars. Paste events should not lead to BeforeInput events.
1975
+ return null;
1976
+ case TOP_KEY_PRESS:
1977
+ /**
1978
+ * As of v27, Firefox may fire keypress events even when no character
1979
+ * will be inserted. A few possibilities:
1980
+ *
1981
+ * - `which` is `0`. Arrow keys, Esc key, etc.
1982
+ *
1983
+ * - `which` is the pressed key code, but no char is available.
1984
+ * Ex: 'AltGr + d` in Polish. There is no modified character for
1985
+ * this key combination and no character is inserted into the
1986
+ * document, but FF fires the keypress for char code `100` anyway.
1987
+ * No `input` event will occur.
1988
+ *
1989
+ * - `which` is the pressed key code, but a command combination is
1990
+ * being used. Ex: `Cmd+C`. No character is inserted, and no
1991
+ * `input` event will occur.
1992
+ */
1993
+ if (!isKeypressCommand(nativeEvent)) {
1994
+ // IE fires the `keypress` event when a user types an emoji via
1995
+ // Touch keyboard of Windows. In such a case, the `char` property
1996
+ // holds an emoji character like `\uD83D\uDE0A`. Because its length
1997
+ // is 2, the property `which` does not represent an emoji correctly.
1998
+ // In such a case, we directly return the `char` property instead of
1999
+ // using `which`.
2000
+ if (nativeEvent.char && nativeEvent.char.length > 1) {
2001
+ return nativeEvent.char;
2002
+ }
2003
+ else if (nativeEvent.which) {
2004
+ return String.fromCharCode(nativeEvent.which);
2005
+ }
2006
+ }
2007
+ return null;
2008
+ case TOP_COMPOSITION_END:
2009
+ return useFallbackCompositionData && !isUsingKoreanIME(nativeEvent) ? null : nativeEvent.data;
2010
+ default:
2011
+ return null;
2012
+ }
2013
+ }
2014
+ /**
2015
+ * Extract a SyntheticInputEvent for `beforeInput`, based on either native
2016
+ * `textInput` or fallback behavior.
2017
+ *
2018
+ */
2019
+ function extractBeforeInputEvent(topLevelType, targetInst, nativeEvent, nativeEventTarget) {
2020
+ let chars;
2021
+ if (canUseTextInputEvent) {
2022
+ chars = getNativeBeforeInputChars(topLevelType, nativeEvent);
2023
+ }
2024
+ else {
2025
+ chars = getFallbackBeforeInputChars(topLevelType, nativeEvent);
2026
+ }
2027
+ // If no characters are being inserted, no BeforeInput event should
2028
+ // be fired.
2029
+ if (!chars) {
2030
+ return null;
2031
+ }
2032
+ const beforeInputEvent = new BeforeInputEvent();
2033
+ beforeInputEvent.data = chars;
2034
+ beforeInputEvent.nativeEvent = nativeEvent;
2035
+ return beforeInputEvent;
2036
+ }
2037
+ /**
2038
+ * Create an `onBeforeInput` event to match
2039
+ * http://www.w3.org/TR/2013/WD-DOM-Level-3-Events-20131105/#events-inputevents.
2040
+ *
2041
+ * This event plugin is based on the native `textInput` event
2042
+ * available in Chrome, Safari, Opera, and IE. This event fires after
2043
+ * `onKeyPress` and `onCompositionEnd`, but before `onInput`.
2044
+ *
2045
+ * `beforeInput` is spec'd but not implemented in any browsers, and
2046
+ * the `input` event does not provide any useful information about what has
2047
+ * actually been added, contrary to the spec. Thus, `textInput` is the best
2048
+ * available event to identify the characters that have actually been inserted
2049
+ * into the target node.
2050
+ *
2051
+ * This plugin is also responsible for emitting `composition` events, thus
2052
+ * allowing us to share composition fallback code for both `beforeInput` and
2053
+ * `composition` event types.
2054
+ */
2055
+ const BeforeInputEventPlugin = {
2056
+ extractEvents: (topLevelType, targetInst, nativeEvent, nativeEventTarget) => {
2057
+ return extractBeforeInputEvent(topLevelType, targetInst, nativeEvent, nativeEventTarget);
2058
+ }
2059
+ };
2060
+ class BeforeInputEvent {
2061
+ }
2062
+
2063
+ const BEFORE_INPUT_EVENTS = [
2064
+ // { name: 'blur', handler: 'onBlur', isTriggerBeforeInput: true },
2065
+ // { name: 'compositionstart', handler: 'onCompositionStart', isTriggerBeforeInput: true },
2066
+ { name: 'compositionupdate', handler: null, isTriggerBeforeInput: true },
2067
+ // { name: 'compositionend', handler: 'onCompositionEnd', isTriggerBeforeInput: false },
2068
+ // { name: 'keydown', handler: 'onKeyDown', isTriggerBeforeInput: true },
2069
+ { name: 'keypress', handler: null, isTriggerBeforeInput: true },
2070
+ { name: 'keyup', handler: 'onKeyUp', isTriggerBeforeInput: true },
2071
+ { name: 'mousedown', handler: 'onMouseDown', isTriggerBeforeInput: true },
2072
+ { name: 'textInput', handler: null, isTriggerBeforeInput: true }
2073
+ // { name: 'paste', handler: 'onPaste', isTriggerBeforeInput: true }
2074
+ ];
2075
+
2076
+ var SlateErrorCode;
2077
+ (function (SlateErrorCode) {
2078
+ SlateErrorCode[SlateErrorCode["ToNativeSelectionError"] = 2100] = "ToNativeSelectionError";
2079
+ SlateErrorCode[SlateErrorCode["ToSlateSelectionError"] = 2101] = "ToSlateSelectionError";
2080
+ SlateErrorCode[SlateErrorCode["OnDOMBeforeInputError"] = 2102] = "OnDOMBeforeInputError";
2081
+ SlateErrorCode[SlateErrorCode["OnSyntheticBeforeInputError"] = 2103] = "OnSyntheticBeforeInputError";
2082
+ SlateErrorCode[SlateErrorCode["OnDOMKeydownError"] = 2104] = "OnDOMKeydownError";
2083
+ SlateErrorCode[SlateErrorCode["GetStartPointError"] = 2105] = "GetStartPointError";
2084
+ SlateErrorCode[SlateErrorCode["NotFoundPreviousRootNodeError"] = 3100] = "NotFoundPreviousRootNodeError";
2085
+ SlateErrorCode[SlateErrorCode["InvalidValueError"] = 4100] = "InvalidValueError";
2086
+ })(SlateErrorCode || (SlateErrorCode = {}));
2087
+
2088
+ function restoreDom(editor, execute) {
2089
+ const editable = EDITOR_TO_ELEMENT.get(editor);
2090
+ let observer = new MutationObserver(mutations => {
2091
+ mutations.reverse().forEach(mutation => {
2092
+ if (mutation.type === 'characterData') {
2093
+ // We don't want to restore the DOM for characterData mutations
2094
+ // because this interrupts the composition.
2095
+ return;
2096
+ }
2097
+ mutation.removedNodes.forEach(node => {
2098
+ mutation.target.insertBefore(node, mutation.nextSibling);
2099
+ });
2100
+ mutation.addedNodes.forEach(node => {
2101
+ mutation.target.removeChild(node);
2102
+ });
2103
+ });
2104
+ disconnect();
2105
+ execute();
2106
+ });
2107
+ const disconnect = () => {
2108
+ observer.disconnect();
2109
+ observer = null;
2110
+ };
2111
+ observer.observe(editable, { subtree: true, childList: true, characterData: true, characterDataOldValue: true });
2112
+ setTimeout(() => {
2113
+ if (observer) {
2114
+ disconnect();
2115
+ execute();
2116
+ }
2117
+ }, 0);
2118
+ }
2119
+
2120
+ class FlavourRef {
2121
+ destroy() {
2122
+ this.instance.onDestroy();
2123
+ }
2124
+ }
2125
+ class BlockCardRef {
2126
+ destroy() {
2127
+ this.instance.onDestroy();
2128
+ }
2129
+ }
2130
+
2131
+ function createEmbeddedViewOrComponentOrFlavour(viewType, context, viewContext, viewContainerRef) {
2132
+ if (isFlavourType(viewType)) {
2133
+ const flavourRef = new FlavourRef();
2134
+ flavourRef.instance = new viewType();
2135
+ flavourRef.instance.context = context;
2136
+ flavourRef.instance.viewContext = viewContext;
2137
+ flavourRef.instance.viewContainerRef = viewContainerRef;
2138
+ flavourRef.instance.onInit();
2139
+ return flavourRef;
2140
+ }
2141
+ if (isTemplateRef(viewType)) {
2142
+ const embeddedViewContext = {
2143
+ context,
2144
+ viewContext
2145
+ };
2146
+ const embeddedViewRef = viewContainerRef.createEmbeddedView(viewType, embeddedViewContext);
2147
+ embeddedViewRef.detectChanges();
2148
+ return embeddedViewRef;
2149
+ }
2150
+ if (isComponentType(viewType)) {
2151
+ const componentRef = viewContainerRef.createComponent(viewType, {
2152
+ injector: viewContainerRef.injector
2153
+ });
2154
+ componentRef.instance.viewContext = viewContext;
2155
+ componentRef.instance.context = context;
2156
+ componentRef.changeDetectorRef.detectChanges();
2157
+ return componentRef;
2158
+ }
2159
+ }
2160
+ function updateContext(view, newContext, viewContext) {
2161
+ if (view instanceof FlavourRef) {
2162
+ view.instance.context = newContext;
2163
+ return;
2164
+ }
2165
+ if (view instanceof ComponentRef) {
2166
+ view.instance.context = newContext;
2167
+ }
2168
+ else {
2169
+ view.context.context = newContext;
2170
+ view.context.viewContext = viewContext;
2171
+ view.detectChanges();
2172
+ }
2173
+ }
2174
+ function mount(views, blockCards, outletParent, outletElement) {
2175
+ if (views.length > 0) {
2176
+ const fragment = document.createDocumentFragment();
2177
+ views.forEach((view, index) => {
2178
+ const blockCard = blockCards ? blockCards[index] : undefined;
2179
+ fragment.append(...getRootNodes(view, blockCard));
2180
+ });
2181
+ if (outletElement) {
2182
+ outletElement.parentElement.insertBefore(fragment, outletElement);
2183
+ outletElement.remove();
2184
+ }
2185
+ else {
2186
+ outletParent.prepend(fragment);
2187
+ }
2188
+ }
2189
+ }
2190
+ function getRootNodes(ref, blockCard) {
2191
+ if (blockCard) {
2192
+ return [blockCard.instance.nativeElement];
2193
+ }
2194
+ if (ref instanceof FlavourRef) {
2195
+ return [ref.instance.nativeElement];
2196
+ }
2197
+ if (ref instanceof ComponentRef) {
2198
+ ref.hostView.rootNodes.forEach(ele => {
2199
+ if (!(ele instanceof HTMLElement)) {
2200
+ ele.remove();
2201
+ }
2202
+ });
2203
+ return [ref.instance.nativeElement];
2204
+ }
2205
+ else {
2206
+ const result = [];
2207
+ ref.rootNodes.forEach(rootNode => {
2208
+ const isHTMLElement = rootNode instanceof HTMLElement;
2209
+ const isSlateNodeOfLeaf = isHTMLElement && (rootNode.hasAttribute('data-slate-node') || rootNode.hasAttribute('data-slate-leaf'));
2210
+ if (isSlateNodeOfLeaf && result.every(item => !item.contains(rootNode))) {
2211
+ result.push(rootNode);
2212
+ }
2213
+ if (!isHTMLElement) {
2214
+ rootNode.remove();
2215
+ }
2216
+ });
2217
+ return result;
2218
+ }
2219
+ }
2220
+ function mountOnItemChange(index, item, views, blockCards, outletParent, firstRootNode, viewContext) {
2221
+ const view = views[index];
2222
+ let rootNodes = getRootNodes(view);
2223
+ if (blockCards) {
2224
+ const isBlockCard = viewContext.editor.isBlockCard(item);
2225
+ if (isBlockCard) {
2226
+ const blockCard = blockCards[index];
2227
+ rootNodes = [blockCard.instance.nativeElement];
2228
+ }
2229
+ }
2230
+ if (index === 0) {
2231
+ if (firstRootNode) {
2232
+ rootNodes.forEach(rootNode => {
2233
+ firstRootNode.insertAdjacentElement('beforebegin', rootNode);
2234
+ });
2235
+ }
2236
+ else {
2237
+ outletParent.prepend(...rootNodes);
2238
+ }
2239
+ }
2240
+ else {
2241
+ const previousView = views[index - 1];
2242
+ const blockCard = blockCards ? blockCards[index - 1] : null;
2243
+ const previousRootNodes = getRootNodes(previousView, blockCard);
2244
+ let previousRootNode = previousRootNodes[previousRootNodes.length - 1];
2245
+ rootNodes.forEach(rootNode => {
2246
+ previousRootNode.insertAdjacentElement('afterend', rootNode);
2247
+ previousRootNode = rootNode;
2248
+ });
2249
+ }
2250
+ }
2251
+
2252
+ function hasBeforeContextChange(value) {
2253
+ if (value.beforeContextChange) {
2254
+ return true;
2255
+ }
2256
+ return false;
2257
+ }
2258
+ function hasAfterContextChange(value) {
2259
+ if (value.afterContextChange) {
2260
+ return true;
2261
+ }
2262
+ return false;
2263
+ }
2264
+
2265
+ class BaseFlavour {
2266
+ constructor() {
2267
+ this.initialized = false;
2268
+ }
2269
+ static { this.isFlavour = true; }
2270
+ set context(value) {
2271
+ if (hasBeforeContextChange(this)) {
2272
+ this.beforeContextChange(value);
2273
+ }
2274
+ this._context = value;
2275
+ this.onContextChange();
2276
+ if (hasAfterContextChange(this)) {
2277
+ this.afterContextChange();
2278
+ }
2279
+ }
2280
+ get context() {
2281
+ return this._context;
2282
+ }
2283
+ get editor() {
2284
+ return this.viewContext && this.viewContext.editor;
2285
+ }
2286
+ }
2287
+
2288
+ const SLATE_BLOCK_CARD_CLASS_NAME = 'slate-block-card';
2289
+ class SlateBlockCard {
2290
+ onInit() {
2291
+ const nativeElement = document.createElement('div');
2292
+ nativeElement.classList.add(SLATE_BLOCK_CARD_CLASS_NAME);
2293
+ this.nativeElement = nativeElement;
2294
+ this.createContent();
2295
+ }
2296
+ createContent() {
2297
+ const leftCaret = document.createElement('span');
2298
+ leftCaret.setAttribute(`card-target`, 'card-left');
2299
+ leftCaret.classList.add('card-left');
2300
+ leftCaret.appendChild(getZeroTextNode());
2301
+ const rightCaret = document.createElement('span');
2302
+ rightCaret.setAttribute(`card-target`, 'card-right');
2303
+ rightCaret.classList.add('card-right');
2304
+ rightCaret.appendChild(getZeroTextNode());
2305
+ const center = document.createElement('div');
2306
+ center.setAttribute(`card-target`, 'card-center');
2307
+ this.nativeElement.appendChild(leftCaret);
2308
+ this.nativeElement.appendChild(center);
2309
+ this.nativeElement.appendChild(rightCaret);
2310
+ this.centerContainer = center;
2311
+ }
2312
+ append() {
2313
+ this.centerRootNodes.forEach(rootNode => !this.centerContainer.contains(rootNode) && this.centerContainer.appendChild(rootNode));
2314
+ }
2315
+ initializeCenter(rootNodes) {
2316
+ this.centerRootNodes = rootNodes;
2317
+ this.append();
2318
+ }
2319
+ onDestroy() {
2320
+ this.nativeElement.remove();
2321
+ }
2322
+ }
2323
+ const getBlockCardByNativeElement = (nativeElement) => {
2324
+ const blockCardElement = nativeElement?.parentElement?.parentElement;
2325
+ if (blockCardElement && blockCardElement.classList.contains(SLATE_BLOCK_CARD_CLASS_NAME)) {
2326
+ return blockCardElement;
2327
+ }
2328
+ return null;
2329
+ };
2330
+
2331
+ const DEFAULT_ELEMENT_HEIGHT = 24;
2332
+ class BaseElementFlavour extends BaseFlavour {
2333
+ constructor() {
2334
+ super(...arguments);
2335
+ this.getOutletParent = () => {
2336
+ return this.nativeElement;
2337
+ };
2338
+ this.getOutletElement = () => {
2339
+ return this.nativeElement.querySelector('.children-outlet');
2340
+ };
2341
+ this.stableHeight = null;
2342
+ }
2343
+ get element() {
2344
+ return this._context && this._context.element;
2345
+ }
2346
+ get selection() {
2347
+ return this._context && this._context.selection;
2348
+ }
2349
+ get decorations() {
2350
+ return this._context && this._context.decorations;
2351
+ }
2352
+ get children() {
2353
+ return this._context && this._context.element.children;
2354
+ }
2355
+ get isCollapsed() {
2356
+ return this.selection && Range.isCollapsed(this.selection);
2357
+ }
1762
2358
  get isCollapsedAndNonReadonly() {
1763
2359
  return this.selection && Range.isCollapsed(this.selection) && !this.readonly;
1764
2360
  }
@@ -1798,6 +2394,7 @@ class BaseElementFlavour extends BaseFlavour {
1798
2394
  if (ELEMENT_TO_COMPONENT.get(this.element) === this) {
1799
2395
  ELEMENT_TO_COMPONENT.delete(this.element);
1800
2396
  }
2397
+ this.listRender.destroy();
1801
2398
  this.nativeElement?.remove();
1802
2399
  }
1803
2400
  onContextChange() {
@@ -2052,758 +2649,600 @@ const createCompatibleStringNode = (text) => {
2052
2649
  const zeroWidthSpan = document.createElement('span');
2053
2650
  const zeroWidthSpace = getZeroTextNode();
2054
2651
  zeroWidthSpan.setAttribute('data-slate-zero-width', '');
2055
- zeroWidthSpan.appendChild(zeroWidthSpace);
2056
- stringNode.appendChild(zeroWidthSpan);
2057
- return stringNode;
2058
- };
2059
- const createLineBreakEmptyStringDOM = (elementStringLength) => {
2060
- const stringNode = document.createElement('span');
2061
- stringNode.setAttribute('data-slate-zero-width', 'n');
2062
- stringNode.setAttribute('data-slate-length', `${elementStringLength}`);
2063
- const zeroWidthSpace = getZeroTextNode();
2064
- stringNode.appendChild(zeroWidthSpace);
2065
- const brNode = document.createElement('br');
2066
- stringNode.appendChild(brNode);
2067
- stringNode.setAttribute('editable-text', '');
2068
- return stringNode;
2069
- };
2070
- /**
2071
- * TODO: remove when bump slate
2072
- * copy from slate
2073
- * @param editor
2074
- * @param element
2075
- * @returns
2076
- */
2077
- const isEmpty = (editor, element) => {
2078
- const { children } = element;
2079
- const [first] = children;
2080
- return children.length === 0 || (children.length === 1 && Text$1.isText(first) && first.text === '' && !editor.isVoid(element));
2081
- };
2082
-
2083
- class DefaultLeafFlavour extends BaseLeafFlavour {
2084
- constructor() {
2085
- super(...arguments);
2086
- this.stringRender = null;
2087
- }
2088
- render() {
2089
- const leafNode = createDefaultLeafNode();
2090
- this.stringRender = new SlateStringRender(this.context, this.viewContext);
2091
- const stringNode = this.stringRender.render();
2092
- leafNode.appendChild(stringNode);
2093
- this.nativeElement = leafNode;
2094
- }
2095
- rerender() {
2096
- this.stringRender?.update(this.context, this.viewContext);
2097
- }
2098
- }
2099
- const createDefaultLeafNode = () => {
2100
- const span = document.createElement('span');
2101
- span.setAttribute('data-slate-leaf', 'true');
2102
- return span;
2103
- };
2104
-
2105
- class LeavesRender {
2106
- constructor(viewContext, viewContainerRef, getOutletParent, getOutletElement) {
2107
- this.viewContext = viewContext;
2108
- this.viewContainerRef = viewContainerRef;
2109
- this.getOutletParent = getOutletParent;
2110
- this.getOutletElement = getOutletElement;
2111
- this.views = [];
2112
- this.contexts = [];
2113
- this.viewTypes = [];
2114
- }
2115
- initialize(context) {
2116
- const { decoratedLeaves, contexts } = this.getLeaves(context);
2117
- this.decoratedLeaves = decoratedLeaves;
2118
- this.contexts = contexts;
2119
- this.decoratedLeaves.forEach((leaf, index) => {
2120
- const context = getContext$1(index, this.contexts);
2121
- const viewType = getViewType$1(context, this.viewContext);
2122
- const view = createEmbeddedViewOrComponentOrFlavour(viewType, context, this.viewContext, this.viewContainerRef);
2123
- this.views.push(view);
2124
- this.contexts.push(context);
2125
- this.viewTypes.push(viewType);
2126
- });
2127
- mount(this.views, null, this.getOutletParent(), this.getOutletElement());
2128
- const newDiffers = this.viewContext.editor.injector.get(IterableDiffers);
2129
- this.differ = newDiffers.find(this.decoratedLeaves).create(trackBy$1(this.viewContext));
2130
- this.differ.diff(this.decoratedLeaves);
2131
- }
2132
- update(context) {
2133
- const { decoratedLeaves, contexts } = this.getLeaves(context);
2134
- const outletParent = this.getOutletParent();
2135
- const diffResult = this.differ.diff(decoratedLeaves);
2136
- if (diffResult) {
2137
- let firstRootNode = getRootNodes(this.views[0])[0];
2138
- const newContexts = [];
2139
- const newViewTypes = [];
2140
- const newViews = [];
2141
- diffResult.forEachItem(record => {
2142
- let context = getContext$1(record.currentIndex, contexts);
2143
- const viewType = getViewType$1(context, this.viewContext);
2144
- newViewTypes.push(viewType);
2145
- let view;
2146
- if (record.previousIndex === null) {
2147
- view = createEmbeddedViewOrComponentOrFlavour(viewType, context, this.viewContext, this.viewContainerRef);
2148
- newContexts.push(context);
2149
- newViews.push(view);
2150
- mountOnItemChange(record.currentIndex, record.item, newViews, null, outletParent, firstRootNode, this.viewContext);
2151
- }
2152
- else {
2153
- const previousView = this.views[record.previousIndex];
2154
- const previousViewType = this.viewTypes[record.previousIndex];
2155
- if (previousViewType !== viewType) {
2156
- view = createEmbeddedViewOrComponentOrFlavour(viewType, context, this.viewContext, this.viewContainerRef);
2157
- const firstRootNode = getRootNodes(previousView, null)[0];
2158
- const newRootNodes = getRootNodes(view, null);
2159
- firstRootNode.replaceWith(...newRootNodes);
2160
- previousView.destroy();
2161
- }
2162
- else {
2163
- view = previousView;
2164
- updateContext(previousView, context, this.viewContext);
2165
- }
2166
- newContexts.push(context);
2167
- newViews.push(view);
2168
- }
2169
- });
2170
- diffResult.forEachRemovedItem(record => {
2171
- const view = this.views[record.previousIndex];
2172
- view.destroy();
2173
- });
2174
- diffResult.forEachMovedItem(record => {
2175
- mountOnItemChange(record.currentIndex, record.item, newViews, null, outletParent, firstRootNode, this.viewContext);
2176
- });
2177
- this.viewTypes = newViewTypes;
2178
- this.views = newViews;
2179
- this.contexts = newContexts;
2180
- this.decoratedLeaves = decoratedLeaves;
2181
- }
2182
- }
2183
- getLeaves(context) {
2184
- const decoratedLeaves = Text$1.decorations(context.text, context.decorations);
2185
- const contexts = decoratedLeaves.map((decoratedLeaf, index) => {
2186
- return {
2187
- leaf: decoratedLeaf.leaf,
2188
- leafPosition: decoratedLeaf.position,
2189
- text: context.text,
2190
- parent: context.parent,
2191
- index,
2192
- isLast: context.isLast && index === decoratedLeaves.length - 1
2193
- };
2194
- });
2195
- return { decoratedLeaves, contexts };
2196
- }
2197
- }
2198
- function getContext$1(index, leafContexts) {
2199
- return leafContexts[index];
2200
- }
2201
- function getViewType$1(leafContext, viewContext) {
2202
- return (viewContext.renderLeaf && viewContext.renderLeaf(leafContext.leaf)) || DefaultLeafFlavour;
2203
- }
2204
- function trackBy$1(viewContext) {
2205
- return (index, node) => {
2206
- return index;
2207
- };
2208
- }
2209
-
2210
- class BaseTextFlavour extends BaseFlavour {
2211
- constructor() {
2212
- super(...arguments);
2213
- this.getOutletParent = () => {
2214
- return this.nativeElement;
2215
- };
2216
- this.getOutletElement = () => {
2217
- return this.nativeElement.querySelector('.children-outlet');
2218
- };
2219
- }
2220
- get text() {
2221
- return this._context && this._context.text;
2222
- }
2223
- onInit() {
2224
- this.render();
2225
- this.updateWeakMap();
2226
- this.initialized = true;
2227
- this.leavesRender = new LeavesRender(this.viewContext, this.viewContainerRef, this.getOutletParent, this.getOutletElement);
2228
- this.leavesRender.initialize(this.context);
2229
- }
2230
- updateWeakMap() {
2231
- ELEMENT_TO_NODE.set(this.nativeElement, this.text);
2232
- NODE_TO_ELEMENT.set(this.text, this.nativeElement);
2233
- }
2234
- onDestroy() {
2235
- if (NODE_TO_ELEMENT.get(this.text) === this.nativeElement) {
2236
- NODE_TO_ELEMENT.delete(this.text);
2237
- }
2238
- ELEMENT_TO_NODE.delete(this.nativeElement);
2239
- this.nativeElement?.remove();
2240
- }
2241
- onContextChange() {
2242
- if (!this.initialized) {
2243
- return;
2244
- }
2245
- this.rerender();
2246
- this.updateWeakMap();
2247
- this.leavesRender.update(this.context);
2248
- }
2249
- }
2652
+ zeroWidthSpan.appendChild(zeroWidthSpace);
2653
+ stringNode.appendChild(zeroWidthSpan);
2654
+ return stringNode;
2655
+ };
2656
+ const createLineBreakEmptyStringDOM = (elementStringLength) => {
2657
+ const stringNode = document.createElement('span');
2658
+ stringNode.setAttribute('data-slate-zero-width', 'n');
2659
+ stringNode.setAttribute('data-slate-length', `${elementStringLength}`);
2660
+ const zeroWidthSpace = getZeroTextNode();
2661
+ stringNode.appendChild(zeroWidthSpace);
2662
+ const brNode = document.createElement('br');
2663
+ stringNode.appendChild(brNode);
2664
+ stringNode.setAttribute('editable-text', '');
2665
+ return stringNode;
2666
+ };
2667
+ /**
2668
+ * TODO: remove when bump slate
2669
+ * copy from slate
2670
+ * @param editor
2671
+ * @param element
2672
+ * @returns
2673
+ */
2674
+ const isEmpty = (editor, element) => {
2675
+ const { children } = element;
2676
+ const [first] = children;
2677
+ return children.length === 0 || (children.length === 1 && Text$1.isText(first) && first.text === '' && !editor.isVoid(element));
2678
+ };
2250
2679
 
2251
- class DefaultTextFlavour extends BaseTextFlavour {
2252
- render() {
2253
- const { nativeElement } = createText(this.text.text);
2254
- this.nativeElement = nativeElement;
2680
+ class DefaultLeafFlavour extends BaseLeafFlavour {
2681
+ constructor() {
2682
+ super(...arguments);
2683
+ this.stringRender = null;
2255
2684
  }
2256
- rerender() { }
2257
- }
2258
- class VoidTextFlavour extends BaseTextFlavour {
2259
2685
  render() {
2260
- const { nativeElement } = createText(this.text.text);
2261
- this.nativeElement = nativeElement;
2262
- this.nativeElement.setAttribute('data-slate-spacer', 'true');
2263
- this.nativeElement.classList.add('slate-spacer');
2686
+ const leafNode = createDefaultLeafNode();
2687
+ this.stringRender = new SlateStringRender(this.context, this.viewContext);
2688
+ const stringNode = this.stringRender.render();
2689
+ leafNode.appendChild(stringNode);
2690
+ this.nativeElement = leafNode;
2691
+ }
2692
+ rerender() {
2693
+ this.stringRender?.update(this.context, this.viewContext);
2264
2694
  }
2265
- rerender() { }
2266
2695
  }
2267
- const createText = (text) => {
2268
- const nativeElement = document.createElement('span');
2269
- nativeElement.setAttribute('data-slate-node', 'text');
2270
- return { nativeElement };
2696
+ const createDefaultLeafNode = () => {
2697
+ const span = document.createElement('span');
2698
+ span.setAttribute('data-slate-leaf', 'true');
2699
+ return span;
2271
2700
  };
2272
2701
 
2273
- class ListRender {
2702
+ class LeavesRender {
2274
2703
  constructor(viewContext, viewContainerRef, getOutletParent, getOutletElement) {
2275
2704
  this.viewContext = viewContext;
2276
2705
  this.viewContainerRef = viewContainerRef;
2277
2706
  this.getOutletParent = getOutletParent;
2278
2707
  this.getOutletElement = getOutletElement;
2279
2708
  this.views = [];
2280
- this.blockCards = [];
2281
2709
  this.contexts = [];
2282
2710
  this.viewTypes = [];
2283
- this.differ = null;
2284
- this.initialized = false;
2285
2711
  }
2286
- initialize(children, parent, childrenContext) {
2287
- this.initialized = true;
2288
- this.children = children;
2289
- const isRoot = parent === this.viewContext.editor;
2290
- const firstIndex = isRoot ? this.viewContext.editor.children.indexOf(children[0]) : 0;
2291
- const parentPath = AngularEditor.findPath(this.viewContext.editor, parent);
2292
- children.forEach((descendant, _index) => {
2293
- NODE_TO_INDEX.set(descendant, firstIndex + _index);
2294
- NODE_TO_PARENT.set(descendant, parent);
2295
- const context = getContext(firstIndex + _index, descendant, parentPath, childrenContext, this.viewContext);
2296
- const viewType = getViewType(descendant, parent, this.viewContext);
2712
+ initialize(context) {
2713
+ const { decoratedLeaves, contexts } = this.getLeaves(context);
2714
+ this.decoratedLeaves = decoratedLeaves;
2715
+ this.contexts = contexts;
2716
+ this.decoratedLeaves.forEach((leaf, index) => {
2717
+ const context = getContext$1(index, this.contexts);
2718
+ const viewType = getViewType$1(context, this.viewContext);
2297
2719
  const view = createEmbeddedViewOrComponentOrFlavour(viewType, context, this.viewContext, this.viewContainerRef);
2298
- const blockCard = createBlockCard(descendant, view, this.viewContext);
2299
2720
  this.views.push(view);
2300
2721
  this.contexts.push(context);
2301
2722
  this.viewTypes.push(viewType);
2302
- this.blockCards.push(blockCard);
2303
2723
  });
2304
- mount(this.views, this.blockCards, this.getOutletParent(), this.getOutletElement());
2724
+ mount(this.views, null, this.getOutletParent(), this.getOutletElement());
2305
2725
  const newDiffers = this.viewContext.editor.injector.get(IterableDiffers);
2306
- this.differ = newDiffers.find(children).create(trackBy(this.viewContext));
2307
- this.differ.diff(children);
2308
- if (parent === this.viewContext.editor) {
2309
- executeAfterViewInit(this.viewContext.editor);
2310
- }
2726
+ this.differ = newDiffers.find(this.decoratedLeaves).create(trackBy$1(this.viewContext));
2727
+ this.differ.diff(this.decoratedLeaves);
2311
2728
  }
2312
- update(children, parent, childrenContext) {
2313
- if (!this.initialized || this.children.length === 0) {
2314
- this.initialize(children, parent, childrenContext);
2315
- return;
2316
- }
2317
- if (!this.differ) {
2318
- throw new Error('Exception: Can not find differ ');
2319
- }
2729
+ update(context) {
2730
+ const { decoratedLeaves, contexts } = this.getLeaves(context);
2320
2731
  const outletParent = this.getOutletParent();
2321
- const diffResult = this.differ.diff(children);
2322
- const parentPath = AngularEditor.findPath(this.viewContext.editor, parent);
2323
- const isRoot = parent === this.viewContext.editor;
2324
- const firstIndex = isRoot ? this.viewContext.editor.children.indexOf(children[0]) : 0;
2732
+ const diffResult = this.differ.diff(decoratedLeaves);
2325
2733
  if (diffResult) {
2326
- let firstRootNode = getRootNodes(this.views[0], this.blockCards[0])[0];
2734
+ let firstRootNode = getRootNodes(this.views[0])[0];
2327
2735
  const newContexts = [];
2328
2736
  const newViewTypes = [];
2329
2737
  const newViews = [];
2330
- const newBlockCards = [];
2331
2738
  diffResult.forEachItem(record => {
2332
- const currentIndex = firstIndex + record.currentIndex;
2333
- NODE_TO_INDEX.set(record.item, currentIndex);
2334
- NODE_TO_PARENT.set(record.item, parent);
2335
- let context = getContext(currentIndex, record.item, parentPath, childrenContext, this.viewContext);
2336
- const viewType = getViewType(record.item, parent, this.viewContext);
2739
+ let context = getContext$1(record.currentIndex, contexts);
2740
+ const viewType = getViewType$1(context, this.viewContext);
2337
2741
  newViewTypes.push(viewType);
2338
2742
  let view;
2339
- let blockCard;
2340
2743
  if (record.previousIndex === null) {
2341
2744
  view = createEmbeddedViewOrComponentOrFlavour(viewType, context, this.viewContext, this.viewContainerRef);
2342
- blockCard = createBlockCard(record.item, view, this.viewContext);
2343
2745
  newContexts.push(context);
2344
2746
  newViews.push(view);
2345
- newBlockCards.push(blockCard);
2346
- mountOnItemChange(record.currentIndex, record.item, newViews, newBlockCards, outletParent, firstRootNode, this.viewContext);
2747
+ mountOnItemChange(record.currentIndex, record.item, newViews, null, outletParent, firstRootNode, this.viewContext);
2347
2748
  }
2348
2749
  else {
2349
2750
  const previousView = this.views[record.previousIndex];
2350
2751
  const previousViewType = this.viewTypes[record.previousIndex];
2351
- const previousContext = this.contexts[record.previousIndex];
2352
- const previousBlockCard = this.blockCards[record.previousIndex];
2353
2752
  if (previousViewType !== viewType) {
2354
2753
  view = createEmbeddedViewOrComponentOrFlavour(viewType, context, this.viewContext, this.viewContainerRef);
2355
- blockCard = createBlockCard(record.item, view, this.viewContext);
2356
- const firstRootNode = getRootNodes(previousView, previousBlockCard)[0];
2357
- const newRootNodes = getRootNodes(view, blockCard);
2754
+ const firstRootNode = getRootNodes(previousView, null)[0];
2755
+ const newRootNodes = getRootNodes(view, null);
2358
2756
  firstRootNode.replaceWith(...newRootNodes);
2359
2757
  previousView.destroy();
2360
- previousBlockCard?.destroy();
2361
2758
  }
2362
2759
  else {
2363
2760
  view = previousView;
2364
- blockCard = previousBlockCard;
2365
- if (memoizedContext(this.viewContext, record.item, previousContext, context)) {
2366
- context = previousContext;
2367
- }
2368
- else {
2369
- updateContext(previousView, context, this.viewContext);
2370
- }
2761
+ updateContext(previousView, context, this.viewContext);
2371
2762
  }
2372
2763
  newContexts.push(context);
2373
2764
  newViews.push(view);
2374
- newBlockCards.push(blockCard);
2375
- }
2376
- });
2377
- diffResult.forEachOperation(record => {
2378
- // removed
2379
- if (record.currentIndex === null) {
2380
- const view = this.views[record.previousIndex];
2381
- const blockCard = this.blockCards[record.previousIndex];
2382
- view.destroy();
2383
- blockCard?.destroy();
2384
- }
2385
- // moved
2386
- if (record.previousIndex !== null && record.currentIndex !== null) {
2387
- mountOnItemChange(record.currentIndex, record.item, newViews, newBlockCards, outletParent, firstRootNode, this.viewContext);
2388
- // Solve the block-card DOMElement loss when moving nodes
2389
- newBlockCards[record.currentIndex]?.instance.append();
2390
- }
2391
- });
2392
- this.viewTypes = newViewTypes;
2393
- this.views = newViews;
2394
- this.contexts = newContexts;
2395
- this.children = children;
2396
- this.blockCards = newBlockCards;
2397
- if (parent === this.viewContext.editor) {
2398
- executeAfterViewInit(this.viewContext.editor);
2399
- }
2400
- }
2401
- else {
2402
- const newContexts = [];
2403
- this.children.forEach((child, _index) => {
2404
- NODE_TO_INDEX.set(child, firstIndex + _index);
2405
- NODE_TO_PARENT.set(child, parent);
2406
- let context = getContext(firstIndex + _index, child, parentPath, childrenContext, this.viewContext);
2407
- const previousContext = this.contexts[_index];
2408
- if (memoizedContext(this.viewContext, child, previousContext, context)) {
2409
- context = previousContext;
2410
2765
  }
2411
- else {
2412
- updateContext(this.views[_index], context, this.viewContext);
2413
- }
2414
- newContexts.push(context);
2415
2766
  });
2416
- this.contexts = newContexts;
2417
- }
2418
- }
2419
- destroy() {
2420
- this.children.forEach((element, index) => {
2421
- if (this.views[index]) {
2422
- this.views[index].destroy();
2423
- }
2424
- if (this.blockCards[index]) {
2425
- this.blockCards[index].destroy();
2426
- }
2427
- });
2428
- this.views = [];
2429
- this.blockCards = [];
2430
- this.contexts = [];
2431
- this.viewTypes = [];
2432
- this.initialized = false;
2433
- this.differ = null;
2434
- }
2435
- }
2436
- function getContext(index, item, parentPath, childrenContext, viewContext) {
2437
- if (Element.isElement(item)) {
2438
- const computedContext = getCommonContext(index, item, parentPath, viewContext, childrenContext);
2439
- const key = AngularEditor.findKey(viewContext.editor, item);
2440
- const isInline = viewContext.editor.isInline(item);
2441
- const isVoid = viewContext.editor.isVoid(item);
2442
- const elementContext = {
2443
- element: item,
2444
- ...computedContext,
2445
- attributes: {
2446
- 'data-slate-node': 'element',
2447
- 'data-slate-key': key.id
2448
- },
2449
- decorate: childrenContext.decorate,
2450
- readonly: childrenContext.readonly
2451
- };
2452
- if (isInline) {
2453
- elementContext.attributes['data-slate-inline'] = true;
2454
- }
2455
- if (isVoid) {
2456
- elementContext.attributes['data-slate-void'] = true;
2457
- }
2458
- // add contentEditable for block element only to avoid chinese input be broken
2459
- if (isVoid && !isInline) {
2460
- elementContext.contentEditable = false;
2461
- }
2462
- return elementContext;
2463
- }
2464
- else {
2465
- const computedContext = getCommonContext(index, item, parentPath, viewContext, childrenContext);
2466
- const isLeafBlock = AngularEditor.isLeafBlock(viewContext.editor, childrenContext.parent);
2467
- const textContext = {
2468
- decorations: computedContext.decorations,
2469
- isLast: isLeafBlock && index === childrenContext.parent.children.length - 1,
2470
- parent: childrenContext.parent,
2471
- text: item
2472
- };
2473
- return textContext;
2474
- }
2475
- }
2476
- function getCommonContext(index, item, parentPath, viewContext, childrenContext) {
2477
- const p = parentPath.concat(index);
2478
- try {
2479
- const ds = childrenContext.decorate([item, p]);
2480
- // [list-render] performance optimization: reduce the number of calls to the `Editor.range(viewContext.editor, p)` method
2481
- if (childrenContext.selection || childrenContext.decorations.length > 0) {
2482
- const range = Editor.range(viewContext.editor, p);
2483
- const sel = childrenContext.selection && Range.intersection(range, childrenContext.selection);
2484
- for (const dec of childrenContext.decorations) {
2485
- const d = Range.intersection(dec, range);
2486
- if (d) {
2487
- ds.push(d);
2488
- }
2489
- }
2490
- return { selection: sel, decorations: ds };
2491
- }
2492
- else {
2493
- return { selection: null, decorations: ds };
2767
+ diffResult.forEachRemovedItem(record => {
2768
+ const view = this.views[record.previousIndex];
2769
+ view.destroy();
2770
+ });
2771
+ diffResult.forEachMovedItem(record => {
2772
+ mountOnItemChange(record.currentIndex, record.item, newViews, null, outletParent, firstRootNode, this.viewContext);
2773
+ });
2774
+ this.viewTypes = newViewTypes;
2775
+ this.views = newViews;
2776
+ this.contexts = newContexts;
2777
+ this.decoratedLeaves = decoratedLeaves;
2494
2778
  }
2495
2779
  }
2496
- catch (error) {
2497
- viewContext.editor.onError({
2498
- code: SlateErrorCode.GetStartPointError,
2499
- nativeError: error
2780
+ getLeaves(context) {
2781
+ const decoratedLeaves = Text$1.decorations(context.text, context.decorations);
2782
+ const contexts = decoratedLeaves.map((decoratedLeaf, index) => {
2783
+ return {
2784
+ leaf: decoratedLeaf.leaf,
2785
+ leafPosition: decoratedLeaf.position,
2786
+ text: context.text,
2787
+ parent: context.parent,
2788
+ index,
2789
+ isLast: context.isLast && index === decoratedLeaves.length - 1
2790
+ };
2500
2791
  });
2501
- return { selection: null, decorations: [] };
2502
- }
2503
- }
2504
- function getViewType(item, parent, viewContext) {
2505
- if (Element.isElement(item)) {
2506
- return (viewContext.renderElement && viewContext.renderElement(item)) || DefaultElementFlavour;
2792
+ return { decoratedLeaves, contexts };
2507
2793
  }
2508
- else {
2509
- const isVoid = viewContext.editor.isVoid(parent);
2510
- return isVoid ? VoidTextFlavour : (viewContext.renderText && viewContext.renderText(item)) || DefaultTextFlavour;
2794
+ destroy() {
2795
+ this.views.forEach(view => view.destroy());
2511
2796
  }
2512
2797
  }
2513
- function createBlockCard(item, view, viewContext) {
2514
- const isBlockCard = viewContext.editor.isBlockCard(item);
2515
- if (isBlockCard) {
2516
- const rootNodes = getRootNodes(view);
2517
- const blockCardRef = new BlockCardRef();
2518
- blockCardRef.instance = new SlateBlockCard();
2519
- blockCardRef.instance.onInit();
2520
- blockCardRef.instance.initializeCenter(rootNodes);
2521
- return blockCardRef;
2522
- }
2523
- else {
2524
- return null;
2525
- }
2798
+ function getContext$1(index, leafContexts) {
2799
+ return leafContexts[index];
2526
2800
  }
2527
- function trackBy(viewContext) {
2801
+ function getViewType$1(leafContext, viewContext) {
2802
+ return (viewContext.renderLeaf && viewContext.renderLeaf(leafContext.leaf)) || DefaultLeafFlavour;
2803
+ }
2804
+ function trackBy$1(viewContext) {
2528
2805
  return (index, node) => {
2529
- return viewContext.trackBy(node) || AngularEditor.findKey(viewContext.editor, node);
2806
+ return index;
2530
2807
  };
2531
2808
  }
2532
- function memoizedContext(viewContext, descendant, prev, next) {
2533
- if (Element.isElement(descendant)) {
2534
- return memoizedElementContext(viewContext, prev, next);
2809
+
2810
+ class BaseTextFlavour extends BaseFlavour {
2811
+ constructor() {
2812
+ super(...arguments);
2813
+ this.getOutletParent = () => {
2814
+ return this.nativeElement;
2815
+ };
2816
+ this.getOutletElement = () => {
2817
+ return this.nativeElement.querySelector('.children-outlet');
2818
+ };
2535
2819
  }
2536
- else {
2537
- return memoizedTextContext(prev, next);
2820
+ get text() {
2821
+ return this._context && this._context.text;
2822
+ }
2823
+ onInit() {
2824
+ this.render();
2825
+ this.updateWeakMap();
2826
+ this.initialized = true;
2827
+ this.leavesRender = new LeavesRender(this.viewContext, this.viewContainerRef, this.getOutletParent, this.getOutletElement);
2828
+ this.leavesRender.initialize(this.context);
2829
+ }
2830
+ updateWeakMap() {
2831
+ ELEMENT_TO_NODE.set(this.nativeElement, this.text);
2832
+ NODE_TO_ELEMENT.set(this.text, this.nativeElement);
2833
+ }
2834
+ onDestroy() {
2835
+ if (NODE_TO_ELEMENT.get(this.text) === this.nativeElement) {
2836
+ NODE_TO_ELEMENT.delete(this.text);
2837
+ }
2838
+ ELEMENT_TO_NODE.delete(this.nativeElement);
2839
+ this.leavesRender.destroy();
2840
+ this.nativeElement?.remove();
2841
+ }
2842
+ onContextChange() {
2843
+ if (!this.initialized) {
2844
+ return;
2845
+ }
2846
+ this.rerender();
2847
+ this.updateWeakMap();
2848
+ this.leavesRender.update(this.context);
2538
2849
  }
2539
2850
  }
2540
- function memoizedElementContext(viewContext, prev, next) {
2541
- return (prev.element === next.element &&
2542
- (!viewContext.isStrictDecorate || prev.decorate === next.decorate) &&
2543
- prev.readonly === next.readonly &&
2544
- isDecoratorRangeListEqual(prev.decorations, next.decorations) &&
2545
- (prev.selection === next.selection || (!!prev.selection && !!next.selection && Range.equals(prev.selection, next.selection))));
2546
- }
2547
- function memoizedTextContext(prev, next) {
2548
- return (next.parent === prev.parent &&
2549
- next.isLast === prev.isLast &&
2550
- next.text === prev.text &&
2551
- isDecoratorRangeListEqual(next.decorations, prev.decorations));
2552
- }
2553
- function addAfterViewInitQueue(editor, afterViewInitCallback) {
2554
- const queue = getAfterViewInitQueue(editor);
2555
- queue.push(afterViewInitCallback);
2556
- EDITOR_TO_AFTER_VIEW_INIT_QUEUE.set(editor, queue);
2557
- }
2558
- function getAfterViewInitQueue(editor) {
2559
- return EDITOR_TO_AFTER_VIEW_INIT_QUEUE.get(editor) || [];
2560
- }
2561
- function clearAfterViewInitQueue(editor) {
2562
- EDITOR_TO_AFTER_VIEW_INIT_QUEUE.set(editor, []);
2851
+
2852
+ class DefaultTextFlavour extends BaseTextFlavour {
2853
+ render() {
2854
+ const { nativeElement } = createText(this.text.text);
2855
+ this.nativeElement = nativeElement;
2856
+ }
2857
+ rerender() { }
2563
2858
  }
2564
- function executeAfterViewInit(editor) {
2565
- const queue = getAfterViewInitQueue(editor);
2566
- queue.forEach(callback => callback());
2567
- clearAfterViewInitQueue(editor);
2859
+ class VoidTextFlavour extends BaseTextFlavour {
2860
+ render() {
2861
+ const { nativeElement } = createText(this.text.text);
2862
+ this.nativeElement = nativeElement;
2863
+ this.nativeElement.setAttribute('data-slate-spacer', 'true');
2864
+ this.nativeElement.classList.add('slate-spacer');
2865
+ }
2866
+ rerender() { }
2568
2867
  }
2868
+ const createText = (text) => {
2869
+ const nativeElement = document.createElement('span');
2870
+ nativeElement.setAttribute('data-slate-node', 'text');
2871
+ return { nativeElement };
2872
+ };
2569
2873
 
2570
- class VirtualScrollDebugOverlay {
2571
- constructor(doc) {
2572
- this.doc = doc;
2573
- this.originalConsoleLog = console.log.bind(console);
2574
- this.originalConsoleWarn = console.warn.bind(console);
2575
- this.dragOffsetX = 0;
2576
- this.dragOffsetY = 0;
2577
- this.isDragging = false;
2578
- this.onDragging = (event) => {
2579
- if (!this.isDragging || !this.container) {
2580
- return;
2581
- }
2582
- const nextLeft = event.clientX - this.dragOffsetX;
2583
- const nextTop = event.clientY - this.dragOffsetY;
2584
- this.container.style.left = `${nextLeft}px`;
2585
- this.container.style.top = `${nextTop}px`;
2586
- this.container.style.right = 'auto';
2587
- this.container.style.bottom = 'auto';
2588
- };
2589
- this.onDragEnd = () => {
2590
- if (!this.isDragging) {
2591
- return;
2592
- }
2593
- this.isDragging = false;
2594
- this.doc.removeEventListener('mousemove', this.onDragging);
2595
- this.doc.removeEventListener('mouseup', this.onDragEnd);
2596
- if (this.container) {
2597
- this.container.style.transition = '';
2874
+ const setPreRenderingElementStyle = (editor, rootNode, isClear = false) => {
2875
+ if (isClear) {
2876
+ rootNode.style.top = '';
2877
+ rootNode.style.width = '';
2878
+ rootNode.style.position = '';
2879
+ return;
2880
+ }
2881
+ const preRenderingWidth = EDITOR_TO_ROOT_NODE_WIDTH.get(editor) ?? 0;
2882
+ rootNode.style.top = '-100%';
2883
+ if (preRenderingWidth) {
2884
+ rootNode.style.width = `${preRenderingWidth}px`;
2885
+ }
2886
+ else {
2887
+ rootNode.style.width = '100%';
2888
+ }
2889
+ rootNode.style.position = 'absolute';
2890
+ };
2891
+ class ListRender {
2892
+ constructor(viewContext, viewContainerRef, getOutletParent, getOutletElement) {
2893
+ this.viewContext = viewContext;
2894
+ this.viewContainerRef = viewContainerRef;
2895
+ this.getOutletParent = getOutletParent;
2896
+ this.getOutletElement = getOutletElement;
2897
+ this.children = [];
2898
+ this.views = [];
2899
+ this.blockCards = [];
2900
+ this.contexts = [];
2901
+ this.viewTypes = [];
2902
+ this.differ = null;
2903
+ this.initialized = false;
2904
+ this.preRenderingHTMLElement = [];
2905
+ }
2906
+ initialize(children, parent, childrenContext, preRenderingCount = 0) {
2907
+ this.initialized = true;
2908
+ this.children = children;
2909
+ const isRoot = parent === this.viewContext.editor;
2910
+ const firstIndex = isRoot ? this.viewContext.editor.children.indexOf(children[0]) : 0;
2911
+ const parentPath = AngularEditor.findPath(this.viewContext.editor, parent);
2912
+ children.forEach((descendant, _index) => {
2913
+ NODE_TO_INDEX.set(descendant, firstIndex + _index);
2914
+ NODE_TO_PARENT.set(descendant, parent);
2915
+ const context = getContext(firstIndex + _index, descendant, parentPath, childrenContext, this.viewContext);
2916
+ const viewType = getViewType(descendant, parent, this.viewContext);
2917
+ const view = createEmbeddedViewOrComponentOrFlavour(viewType, context, this.viewContext, this.viewContainerRef);
2918
+ const blockCard = createBlockCard(descendant, view, this.viewContext);
2919
+ this.views.push(view);
2920
+ this.contexts.push(context);
2921
+ this.viewTypes.push(viewType);
2922
+ this.blockCards.push(blockCard);
2923
+ });
2924
+ mount(this.views, this.blockCards, this.getOutletParent(), this.getOutletElement());
2925
+ const newDiffers = this.viewContext.editor.injector.get(IterableDiffers);
2926
+ this.differ = newDiffers.find(children).create(trackBy(this.viewContext));
2927
+ this.differ.diff(children);
2928
+ if (parent === this.viewContext.editor) {
2929
+ executeAfterViewInit(this.viewContext.editor);
2930
+ }
2931
+ }
2932
+ update(children, parent, childrenContext, preRenderingCount = 0) {
2933
+ if (!this.initialized || this.children.length === 0) {
2934
+ this.initialize(children, parent, childrenContext, preRenderingCount);
2935
+ return;
2936
+ }
2937
+ if (!this.differ) {
2938
+ throw new Error('Exception: Can not find differ ');
2939
+ }
2940
+ const outletParent = this.getOutletParent();
2941
+ if (this.preRenderingHTMLElement.length > 0) {
2942
+ const preRenderingElement = [...this.preRenderingHTMLElement];
2943
+ preRenderingElement.forEach((rootNodes, index) => {
2944
+ const slateElement = this.children[index];
2945
+ if (slateElement && children.indexOf(slateElement) >= 0) {
2946
+ rootNodes.forEach(rootNode => {
2947
+ setPreRenderingElementStyle(this.viewContext.editor, rootNode, true);
2948
+ });
2949
+ if (isDebug) {
2950
+ debugLog('log', 'preRenderingHTMLElement index: ', this.viewContext.editor.children.indexOf(this.children[index]), 'is clear true');
2951
+ }
2952
+ }
2953
+ else {
2954
+ if (isDebug) {
2955
+ debugLog('log', 'preRenderingHTMLElement index: ', this.viewContext.editor.children.indexOf(this.children[index]), 'do not clear since it would be removed soon');
2956
+ }
2957
+ }
2958
+ });
2959
+ this.preRenderingHTMLElement = [];
2960
+ }
2961
+ const diffResult = this.differ.diff(children);
2962
+ const parentPath = AngularEditor.findPath(this.viewContext.editor, parent);
2963
+ const isRoot = parent === this.viewContext.editor;
2964
+ const firstIndex = isRoot ? this.viewContext.editor.children.indexOf(children[0]) : 0;
2965
+ if (diffResult) {
2966
+ let firstRootNode = getRootNodes(this.views[0], this.blockCards[0])[0];
2967
+ const newContexts = [];
2968
+ const newViewTypes = [];
2969
+ const newViews = [];
2970
+ const newBlockCards = [];
2971
+ diffResult.forEachItem(record => {
2972
+ const currentIndex = firstIndex + record.currentIndex;
2973
+ NODE_TO_INDEX.set(record.item, currentIndex);
2974
+ NODE_TO_PARENT.set(record.item, parent);
2975
+ let context = getContext(currentIndex, record.item, parentPath, childrenContext, this.viewContext);
2976
+ const viewType = getViewType(record.item, parent, this.viewContext);
2977
+ newViewTypes.push(viewType);
2978
+ let view;
2979
+ let blockCard;
2980
+ if (record.previousIndex === null) {
2981
+ view = createEmbeddedViewOrComponentOrFlavour(viewType, context, this.viewContext, this.viewContainerRef);
2982
+ blockCard = createBlockCard(record.item, view, this.viewContext);
2983
+ newContexts.push(context);
2984
+ newViews.push(view);
2985
+ newBlockCards.push(blockCard);
2986
+ mountOnItemChange(record.currentIndex, record.item, newViews, newBlockCards, outletParent, firstRootNode, this.viewContext);
2987
+ }
2988
+ else {
2989
+ const previousView = this.views[record.previousIndex];
2990
+ const previousViewType = this.viewTypes[record.previousIndex];
2991
+ const previousContext = this.contexts[record.previousIndex];
2992
+ const previousBlockCard = this.blockCards[record.previousIndex];
2993
+ if (previousViewType !== viewType) {
2994
+ view = createEmbeddedViewOrComponentOrFlavour(viewType, context, this.viewContext, this.viewContainerRef);
2995
+ blockCard = createBlockCard(record.item, view, this.viewContext);
2996
+ const firstRootNode = getRootNodes(previousView, previousBlockCard)[0];
2997
+ const newRootNodes = getRootNodes(view, blockCard);
2998
+ firstRootNode.replaceWith(...newRootNodes);
2999
+ previousView.destroy();
3000
+ previousBlockCard?.destroy();
3001
+ }
3002
+ else {
3003
+ view = previousView;
3004
+ blockCard = previousBlockCard;
3005
+ if (memoizedContext(this.viewContext, record.item, previousContext, context)) {
3006
+ context = previousContext;
3007
+ }
3008
+ else {
3009
+ updateContext(previousView, context, this.viewContext);
3010
+ }
3011
+ }
3012
+ newContexts.push(context);
3013
+ newViews.push(view);
3014
+ newBlockCards.push(blockCard);
3015
+ }
3016
+ });
3017
+ diffResult.forEachOperation(record => {
3018
+ // removed
3019
+ if (record.currentIndex === null) {
3020
+ const view = this.views[record.previousIndex];
3021
+ const blockCard = this.blockCards[record.previousIndex];
3022
+ view.destroy();
3023
+ blockCard?.destroy();
3024
+ }
3025
+ // moved
3026
+ if (record.previousIndex !== null && record.currentIndex !== null) {
3027
+ mountOnItemChange(record.currentIndex, record.item, newViews, newBlockCards, outletParent, firstRootNode, this.viewContext);
3028
+ // Solve the block-card DOMElement loss when moving nodes
3029
+ newBlockCards[record.currentIndex]?.instance.append();
3030
+ }
3031
+ });
3032
+ this.viewTypes = newViewTypes;
3033
+ this.views = newViews;
3034
+ this.contexts = newContexts;
3035
+ this.children = children;
3036
+ this.blockCards = newBlockCards;
3037
+ if (parent === this.viewContext.editor) {
3038
+ executeAfterViewInit(this.viewContext.editor);
2598
3039
  }
2599
- };
2600
- }
2601
- init() {
2602
- if (!this.container) {
2603
- this.createContainer();
2604
- }
2605
- }
2606
- log(type, ...args) {
2607
- this.init();
2608
- if (type === 'warn') {
2609
- this.originalConsoleWarn(...args);
2610
3040
  }
2611
3041
  else {
2612
- this.originalConsoleLog(...args);
3042
+ const newContexts = [];
3043
+ this.children.forEach((child, _index) => {
3044
+ NODE_TO_INDEX.set(child, firstIndex + _index);
3045
+ NODE_TO_PARENT.set(child, parent);
3046
+ let context = getContext(firstIndex + _index, child, parentPath, childrenContext, this.viewContext);
3047
+ const previousContext = this.contexts[_index];
3048
+ if (memoizedContext(this.viewContext, child, previousContext, context)) {
3049
+ context = previousContext;
3050
+ }
3051
+ else {
3052
+ updateContext(this.views[_index], context, this.viewContext);
3053
+ }
3054
+ newContexts.push(context);
3055
+ });
3056
+ this.contexts = newContexts;
2613
3057
  }
2614
- this.appendLog(type, ...args);
2615
- }
2616
- dispose() {
2617
- this.container?.remove();
2618
- this.container = undefined;
2619
- this.logList = undefined;
2620
- this.selectorInput = undefined;
2621
- this.distanceInput = undefined;
2622
- this.doc.removeEventListener('mousemove', this.onDragging);
2623
- this.doc.removeEventListener('mouseup', this.onDragEnd);
2624
- this.isDragging = false;
2625
- }
2626
- createContainer() {
2627
- const doc = this.doc;
2628
- const container = doc.createElement('div');
2629
- container.style.position = 'fixed';
2630
- container.style.left = '16px';
2631
- container.style.top = '16px';
2632
- container.style.right = 'auto';
2633
- container.style.bottom = 'auto';
2634
- container.style.width = '360px';
2635
- container.style.maxHeight = '70vh';
2636
- container.style.padding = '12px';
2637
- container.style.boxSizing = 'border-box';
2638
- container.style.background = 'rgba(17, 24, 39, 0.95)';
2639
- container.style.color = '#e5e7eb';
2640
- container.style.fontSize = '12px';
2641
- container.style.fontFamily = 'Menlo, Consolas, monospace';
2642
- container.style.border = '1px solid #1f2937';
2643
- container.style.borderRadius = '10px';
2644
- container.style.boxShadow = '0 10px 30px rgba(0, 0, 0, 0.35)';
2645
- container.style.zIndex = '9999';
2646
- container.style.display = 'flex';
2647
- container.style.flexDirection = 'column';
2648
- container.style.gap = '10px';
2649
- const header = doc.createElement('div');
2650
- header.style.display = 'flex';
2651
- header.style.alignItems = 'center';
2652
- header.style.justifyContent = 'space-between';
2653
- header.style.cursor = 'move';
2654
- header.addEventListener('mousedown', event => {
2655
- if (!this.container) {
2656
- return;
2657
- }
2658
- const rect = this.container.getBoundingClientRect();
2659
- this.isDragging = true;
2660
- this.dragOffsetX = event.clientX - rect.left;
2661
- this.dragOffsetY = event.clientY - rect.top;
2662
- this.container.style.transition = 'none';
2663
- this.doc.addEventListener('mousemove', this.onDragging);
2664
- this.doc.addEventListener('mouseup', this.onDragEnd);
2665
- event.preventDefault();
2666
- });
2667
- const title = doc.createElement('div');
2668
- title.textContent = 'Virtual Scroll Debug';
2669
- title.style.fontWeight = '600';
2670
- title.style.letterSpacing = '0.3px';
2671
- const clearButton = doc.createElement('button');
2672
- clearButton.type = 'button';
2673
- clearButton.textContent = '清除日志';
2674
- clearButton.style.background = '#374151';
2675
- clearButton.style.color = '#e5e7eb';
2676
- clearButton.style.border = '1px solid #4b5563';
2677
- clearButton.style.borderRadius = '6px';
2678
- clearButton.style.padding = '4px 10px';
2679
- clearButton.style.cursor = 'pointer';
2680
- clearButton.addEventListener('click', () => {
2681
- if (this.logList) {
2682
- this.logList.innerHTML = '';
2683
- }
2684
- });
2685
- header.appendChild(title);
2686
- header.appendChild(clearButton);
2687
- const scrollForm = doc.createElement('div');
2688
- scrollForm.style.display = 'grid';
2689
- scrollForm.style.gridTemplateColumns = '1fr 90px 70px';
2690
- scrollForm.style.gap = '6px';
2691
- scrollForm.style.alignItems = 'center';
2692
- const selectorInput = doc.createElement('input');
2693
- selectorInput.placeholder = '滚动容器 selector';
2694
- selectorInput.style.padding = '6px 8px';
2695
- selectorInput.style.borderRadius = '6px';
2696
- selectorInput.style.border = '1px solid #4b5563';
2697
- selectorInput.style.background = '#111827';
2698
- selectorInput.style.color = '#e5e7eb';
2699
- selectorInput.autocomplete = 'off';
2700
- const distanceInput = doc.createElement('input');
2701
- distanceInput.placeholder = '滚动距离(px)';
2702
- distanceInput.type = 'number';
2703
- distanceInput.style.padding = '6px 8px';
2704
- distanceInput.style.borderRadius = '6px';
2705
- distanceInput.style.border = '1px solid #4b5563';
2706
- distanceInput.style.background = '#111827';
2707
- distanceInput.style.color = '#e5e7eb';
2708
- const scrollButton = doc.createElement('button');
2709
- scrollButton.type = 'button';
2710
- scrollButton.textContent = '滚动';
2711
- scrollButton.style.background = '#10b981';
2712
- scrollButton.style.color = '#0b1c15';
2713
- scrollButton.style.border = 'none';
2714
- scrollButton.style.borderRadius = '6px';
2715
- scrollButton.style.padding = '6px 10px';
2716
- scrollButton.style.cursor = 'pointer';
2717
- scrollButton.addEventListener('click', () => {
2718
- const selector = selectorInput.value.trim();
2719
- const distanceValue = Number(distanceInput.value ?? 0);
2720
- const distance = Number.isFinite(distanceValue) ? distanceValue : 0;
2721
- if (!selector) {
2722
- this.log('warn', '请先填写滚动容器 selector');
2723
- return;
2724
- }
2725
- const target = doc.querySelector(selector);
2726
- if (!target) {
2727
- this.log('warn', `未找到滚动容器: ${selector}`);
2728
- return;
3058
+ if (preRenderingCount > 0) {
3059
+ for (let i = 0; i < preRenderingCount; i++) {
3060
+ const rootNodes = [...getRootNodes(this.views[i], this.blockCards[i])];
3061
+ rootNodes.forEach(rootNode => {
3062
+ setPreRenderingElementStyle(this.viewContext.editor, rootNode);
3063
+ });
3064
+ this.preRenderingHTMLElement.push(rootNodes);
3065
+ if (isDebug) {
3066
+ debugLog('log', 'preRenderingHTMLElement index: ', this.viewContext.editor.children.indexOf(children[i]));
3067
+ }
2729
3068
  }
2730
- if (typeof target.scrollTo === 'function') {
2731
- target.scrollTo({ top: distance, behavior: 'auto' });
3069
+ }
3070
+ if (isDebug) {
3071
+ for (let i = 0; i < children.length; i++) {
3072
+ const rootNodes = [...getRootNodes(this.views[i], this.blockCards[i])];
3073
+ const index = this.viewContext.editor.children.indexOf(children[i]);
3074
+ const height = getRealHeightByElement(this.viewContext.editor, children[i]);
3075
+ rootNodes.forEach(rootNode => {
3076
+ rootNode.setAttribute('debug-index', index.toString());
3077
+ rootNode.setAttribute('debug-height', height.toString());
3078
+ });
2732
3079
  }
2733
- else if (Object.prototype.hasOwnProperty.call(target, 'scrollTop')) {
2734
- target.scrollTop = distance;
3080
+ }
3081
+ }
3082
+ destroy() {
3083
+ this.children.forEach((element, index) => {
3084
+ if (this.views[index]) {
3085
+ this.views[index].destroy();
2735
3086
  }
2736
- else {
2737
- this.log('warn', '目标元素不支持滚动:', selector);
2738
- return;
3087
+ if (this.blockCards[index]) {
3088
+ this.blockCards[index].destroy();
2739
3089
  }
2740
- this.log('log', `已将 ${selector} 滚动到`, distance);
2741
3090
  });
2742
- scrollForm.appendChild(selectorInput);
2743
- scrollForm.appendChild(distanceInput);
2744
- scrollForm.appendChild(scrollButton);
2745
- const logList = doc.createElement('div');
2746
- logList.style.height = '260px';
2747
- logList.style.overflowY = 'auto';
2748
- logList.style.background = '#0b1220';
2749
- logList.style.border = '1px solid #1f2937';
2750
- logList.style.borderRadius = '8px';
2751
- logList.style.padding = '8px';
2752
- logList.style.display = 'flex';
2753
- logList.style.flexDirection = 'column';
2754
- logList.style.gap = '6px';
2755
- container.appendChild(header);
2756
- container.appendChild(scrollForm);
2757
- container.appendChild(logList);
2758
- doc.body.appendChild(container);
2759
- this.container = container;
2760
- this.logList = logList;
2761
- this.selectorInput = selectorInput;
2762
- this.distanceInput = distanceInput;
3091
+ this.children = [];
3092
+ this.views = [];
3093
+ this.blockCards = [];
3094
+ this.contexts = [];
3095
+ this.viewTypes = [];
3096
+ this.initialized = false;
3097
+ this.differ = null;
2763
3098
  }
2764
- appendLog(type, ...args) {
2765
- if (!this.logList) {
2766
- return;
3099
+ }
3100
+ function getContext(index, item, parentPath, childrenContext, viewContext) {
3101
+ if (Element.isElement(item)) {
3102
+ const computedContext = getCommonContext(index, item, parentPath, viewContext, childrenContext);
3103
+ const key = AngularEditor.findKey(viewContext.editor, item);
3104
+ const isInline = viewContext.editor.isInline(item);
3105
+ const isVoid = viewContext.editor.isVoid(item);
3106
+ const elementContext = {
3107
+ element: item,
3108
+ ...computedContext,
3109
+ attributes: {
3110
+ 'data-slate-node': 'element',
3111
+ 'data-slate-key': key.id
3112
+ },
3113
+ decorate: childrenContext.decorate,
3114
+ readonly: childrenContext.readonly
3115
+ };
3116
+ if (isInline) {
3117
+ elementContext.attributes['data-slate-inline'] = true;
3118
+ }
3119
+ if (isVoid) {
3120
+ elementContext.attributes['data-slate-void'] = true;
3121
+ }
3122
+ // add contentEditable for block element only to avoid chinese input be broken
3123
+ if (isVoid && !isInline) {
3124
+ elementContext.contentEditable = false;
2767
3125
  }
2768
- const item = this.doc.createElement('div');
2769
- item.style.display = 'flex';
2770
- item.style.gap = '6px';
2771
- item.style.alignItems = 'flex-start';
2772
- item.style.wordBreak = 'break-all';
2773
- item.style.color = type === 'warn' ? '#fbbf24' : '#9ca3af';
2774
- const time = this.doc.createElement('span');
2775
- time.textContent = new Date().toLocaleTimeString();
2776
- time.style.color = '#6b7280';
2777
- time.style.flexShrink = '0';
2778
- time.style.width = '72px';
2779
- const text = this.doc.createElement('span');
2780
- text.textContent = `[${type}] ${args.map(arg => this.formatValue(arg)).join(' ')}`;
2781
- item.appendChild(time);
2782
- item.appendChild(text);
2783
- this.logList.appendChild(item);
2784
- this.logList.scrollTop = this.logList.scrollHeight;
3126
+ return elementContext;
2785
3127
  }
2786
- formatValue(value) {
2787
- if (typeof value === 'string') {
2788
- return value;
2789
- }
2790
- try {
2791
- return JSON.stringify(value);
3128
+ else {
3129
+ const computedContext = getCommonContext(index, item, parentPath, viewContext, childrenContext);
3130
+ const isLeafBlock = AngularEditor.isLeafBlock(viewContext.editor, childrenContext.parent);
3131
+ const textContext = {
3132
+ decorations: computedContext.decorations,
3133
+ isLast: isLeafBlock && index === childrenContext.parent.children.length - 1,
3134
+ parent: childrenContext.parent,
3135
+ text: item
3136
+ };
3137
+ return textContext;
3138
+ }
3139
+ }
3140
+ function getCommonContext(index, item, parentPath, viewContext, childrenContext) {
3141
+ const p = parentPath.concat(index);
3142
+ try {
3143
+ const ds = childrenContext.decorate([item, p]);
3144
+ // [list-render] performance optimization: reduce the number of calls to the `Editor.range(viewContext.editor, p)` method
3145
+ if (childrenContext.selection || childrenContext.decorations.length > 0) {
3146
+ const range = Editor.range(viewContext.editor, p);
3147
+ const sel = childrenContext.selection && Range.intersection(range, childrenContext.selection);
3148
+ for (const dec of childrenContext.decorations) {
3149
+ const d = Range.intersection(dec, range);
3150
+ if (d) {
3151
+ ds.push(d);
3152
+ }
3153
+ }
3154
+ return { selection: sel, decorations: ds };
2792
3155
  }
2793
- catch (error) {
2794
- return String(value);
3156
+ else {
3157
+ return { selection: null, decorations: ds };
2795
3158
  }
2796
3159
  }
3160
+ catch (error) {
3161
+ viewContext.editor.onError({
3162
+ code: SlateErrorCode.GetStartPointError,
3163
+ nativeError: error
3164
+ });
3165
+ return { selection: null, decorations: [] };
3166
+ }
3167
+ }
3168
+ function getViewType(item, parent, viewContext) {
3169
+ if (Element.isElement(item)) {
3170
+ return (viewContext.renderElement && viewContext.renderElement(item)) || DefaultElementFlavour;
3171
+ }
3172
+ else {
3173
+ const isVoid = viewContext.editor.isVoid(parent);
3174
+ return isVoid ? VoidTextFlavour : (viewContext.renderText && viewContext.renderText(item)) || DefaultTextFlavour;
3175
+ }
3176
+ }
3177
+ function createBlockCard(item, view, viewContext) {
3178
+ const isBlockCard = viewContext.editor.isBlockCard(item);
3179
+ if (isBlockCard) {
3180
+ const rootNodes = getRootNodes(view);
3181
+ const blockCardRef = new BlockCardRef();
3182
+ blockCardRef.instance = new SlateBlockCard();
3183
+ blockCardRef.instance.onInit();
3184
+ blockCardRef.instance.initializeCenter(rootNodes);
3185
+ return blockCardRef;
3186
+ }
3187
+ else {
3188
+ return null;
3189
+ }
3190
+ }
3191
+ function trackBy(viewContext) {
3192
+ return (index, node) => {
3193
+ return viewContext.trackBy(node) || AngularEditor.findKey(viewContext.editor, node);
3194
+ };
3195
+ }
3196
+ function memoizedContext(viewContext, descendant, prev, next) {
3197
+ if (Element.isElement(descendant)) {
3198
+ return memoizedElementContext(viewContext, prev, next);
3199
+ }
3200
+ else {
3201
+ return memoizedTextContext(prev, next);
3202
+ }
3203
+ }
3204
+ function memoizedElementContext(viewContext, prev, next) {
3205
+ return (prev.element === next.element &&
3206
+ (!viewContext.isStrictDecorate || prev.decorate === next.decorate) &&
3207
+ prev.readonly === next.readonly &&
3208
+ isDecoratorRangeListEqual(prev.decorations, next.decorations) &&
3209
+ (prev.selection === next.selection || (!!prev.selection && !!next.selection && Range.equals(prev.selection, next.selection))));
3210
+ }
3211
+ function memoizedTextContext(prev, next) {
3212
+ return (next.parent === prev.parent &&
3213
+ next.isLast === prev.isLast &&
3214
+ next.text === prev.text &&
3215
+ isDecoratorRangeListEqual(next.decorations, prev.decorations));
3216
+ }
3217
+ function addAfterViewInitQueue(editor, afterViewInitCallback) {
3218
+ const queue = getAfterViewInitQueue(editor);
3219
+ queue.push(afterViewInitCallback);
3220
+ EDITOR_TO_AFTER_VIEW_INIT_QUEUE.set(editor, queue);
3221
+ }
3222
+ function getAfterViewInitQueue(editor) {
3223
+ return EDITOR_TO_AFTER_VIEW_INIT_QUEUE.get(editor) || [];
3224
+ }
3225
+ function clearAfterViewInitQueue(editor) {
3226
+ EDITOR_TO_AFTER_VIEW_INIT_QUEUE.set(editor, []);
3227
+ }
3228
+ function executeAfterViewInit(editor) {
3229
+ const queue = getAfterViewInitQueue(editor);
3230
+ queue.forEach(callback => callback());
3231
+ clearAfterViewInitQueue(editor);
2797
3232
  }
2798
3233
 
2799
- const JUST_NOW_UPDATED_VIRTUAL_VIEW = new WeakMap();
2800
3234
  // not correctly clipboardData on beforeinput
2801
3235
  const forceOnDOMPaste = IS_SAFARI;
2802
- const isDebug = localStorage.getItem(SLATE_DEBUG_KEY) === 'true';
2803
3236
  class SlateEditable {
2804
3237
  set virtualScroll(config) {
2805
- this.virtualConfig = config;
2806
- this.doVirtualScroll();
3238
+ this.virtualScrollConfig = config;
3239
+ if (isDebugScrollTop) {
3240
+ debugLog('log', 'virtualScrollConfig scrollTop:', config.scrollTop);
3241
+ }
3242
+ IS_ENABLED_VIRTUAL_SCROLL.set(this.editor, config.enabled);
3243
+ if (this.isEnabledVirtualScroll()) {
3244
+ this.tryUpdateVirtualViewport();
3245
+ }
2807
3246
  }
2808
3247
  get hasBeforeInputSupport() {
2809
3248
  return HAS_BEFORE_INPUT_SUPPORT;
@@ -2848,16 +3287,16 @@ class SlateEditable {
2848
3287
  return null;
2849
3288
  }
2850
3289
  };
2851
- this.virtualConfig = {
3290
+ this.virtualScrollConfig = {
2852
3291
  enabled: false,
2853
3292
  scrollTop: 0,
2854
- viewportHeight: 0
3293
+ viewportHeight: 0,
3294
+ viewportBoundingTop: 0,
3295
+ scrollContainer: null
2855
3296
  };
2856
- this.renderedChildren = [];
2857
- this.virtualVisibleIndexes = new Set();
2858
- this.measuredHeights = new Map();
2859
- // the height from scroll container top to editor top height element
2860
- this.businessHeight = 0;
3297
+ this.inViewportChildren = [];
3298
+ this.inViewportIndics = [];
3299
+ this.keyHeightMap = new Map();
2861
3300
  this.virtualScrollInitialized = false;
2862
3301
  }
2863
3302
  ngOnInit() {
@@ -2869,6 +3308,7 @@ class SlateEditable {
2869
3308
  NODE_TO_ELEMENT.set(this.editor, this.elementRef.nativeElement);
2870
3309
  ELEMENT_TO_NODE.set(this.elementRef.nativeElement, this.editor);
2871
3310
  IS_READ_ONLY.set(this.editor, this.readonly);
3311
+ ELEMENT_KEY_TO_HEIGHTS.set(this.editor, this.keyHeightMap);
2872
3312
  EDITOR_TO_ON_CHANGE.set(this.editor, () => {
2873
3313
  this.ngZone.run(() => {
2874
3314
  this.onChange();
@@ -2882,7 +3322,7 @@ class SlateEditable {
2882
3322
  // add browser class
2883
3323
  let browserClass = IS_FIREFOX ? 'firefox' : IS_SAFARI ? 'safari' : '';
2884
3324
  browserClass && this.elementRef.nativeElement.classList.add(browserClass);
2885
- this.initializeVirtualScrolling();
3325
+ this.initializeVirtualScroll();
2886
3326
  this.listRender = new ListRender(this.viewContext, this.viewContainerRef, this.getOutletParent, this.getOutletElement);
2887
3327
  }
2888
3328
  ngOnChanges(simpleChanges) {
@@ -2914,16 +3354,29 @@ class SlateEditable {
2914
3354
  if (value && value.length) {
2915
3355
  this.editor.children = value;
2916
3356
  this.initializeContext();
2917
- const virtualView = this.refreshVirtualView();
2918
- this.applyVirtualView(virtualView);
2919
- const childrenForRender = virtualView.renderedChildren;
2920
- if (!this.listRender.initialized) {
2921
- this.listRender.initialize(childrenForRender, this.editor, this.context);
3357
+ if (this.isEnabledVirtualScroll()) {
3358
+ const virtualView = this.calculateVirtualViewport();
3359
+ this.applyVirtualView(virtualView);
3360
+ const childrenForRender = virtualView.inViewportChildren;
3361
+ if (isDebug) {
3362
+ debugLog('log', 'writeValue calculate: ', virtualView.inViewportIndics, 'initialized: ', this.listRender.initialized);
3363
+ }
3364
+ if (!this.listRender.initialized) {
3365
+ this.listRender.initialize(childrenForRender, this.editor, this.context);
3366
+ }
3367
+ else {
3368
+ const { preRenderingCount, childrenWithPreRendering } = this.handlePreRendering();
3369
+ this.listRender.update(childrenWithPreRendering, this.editor, this.context, preRenderingCount);
3370
+ }
2922
3371
  }
2923
3372
  else {
2924
- this.listRender.update(childrenForRender, this.editor, this.context);
3373
+ if (!this.listRender.initialized) {
3374
+ this.listRender.initialize(this.editor.children, this.editor, this.context);
3375
+ }
3376
+ else {
3377
+ this.listRender.update(this.editor.children, this.editor, this.context);
3378
+ }
2925
3379
  }
2926
- this.scheduleMeasureVisibleHeights();
2927
3380
  this.cdr.markForCheck();
2928
3381
  }
2929
3382
  }
@@ -2954,26 +3407,45 @@ class SlateEditable {
2954
3407
  this.addEventListener(event.name, () => { });
2955
3408
  });
2956
3409
  }
2957
- toNativeSelection() {
2958
- try {
2959
- let { selection } = this.editor;
2960
- if (this.virtualConfig?.enabled && selection) {
2961
- const indics = Array.from(this.virtualVisibleIndexes.values());
2962
- if (indics.length > 0) {
2963
- const currentVisibleRange = {
2964
- anchor: Editor.start(this.editor, [indics[0]]),
2965
- focus: Editor.end(this.editor, [indics[indics.length - 1]])
2966
- };
2967
- const [start, end] = Range.edges(selection);
2968
- const forwardSelection = { anchor: start, focus: end };
2969
- const intersectedSelection = Range.intersection(forwardSelection, currentVisibleRange);
2970
- if (!intersectedSelection || !Range.equals(intersectedSelection, forwardSelection)) {
2971
- selection = intersectedSelection;
2972
- if (isDebug) {
2973
- this.debugLog('log', `selection is not in visible range, selection: ${JSON.stringify(selection)}, intersectedSelection: ${JSON.stringify(intersectedSelection)}`);
2974
- }
3410
+ calculateVirtualScrollSelection(selection) {
3411
+ if (selection) {
3412
+ const isBlockCardCursor = AngularEditor.isBlockCardLeftCursor(this.editor) || AngularEditor.isBlockCardRightCursor(this.editor);
3413
+ const indics = this.inViewportIndics;
3414
+ if (indics.length > 0) {
3415
+ const currentVisibleRange = {
3416
+ anchor: Editor.start(this.editor, [indics[0]]),
3417
+ focus: Editor.end(this.editor, [indics[indics.length - 1]])
3418
+ };
3419
+ const [start, end] = Range.edges(selection);
3420
+ let forwardSelection = { anchor: start, focus: end };
3421
+ if (!isBlockCardCursor) {
3422
+ forwardSelection = { anchor: start, focus: end };
3423
+ }
3424
+ else {
3425
+ forwardSelection = { anchor: { path: start.path, offset: 0 }, focus: { path: end.path, offset: 0 } };
3426
+ }
3427
+ const intersectedSelection = Range.intersection(forwardSelection, currentVisibleRange);
3428
+ if (intersectedSelection && isBlockCardCursor) {
3429
+ return selection;
3430
+ }
3431
+ EDITOR_TO_VIRTUAL_SCROLL_SELECTION.set(this.editor, intersectedSelection);
3432
+ if (!intersectedSelection || !Range.equals(intersectedSelection, forwardSelection)) {
3433
+ if (isDebug) {
3434
+ debugLog('log', `selection is not in visible range, selection: ${JSON.stringify(selection)}, currentVisibleRange: ${JSON.stringify(currentVisibleRange)}, intersectedSelection: ${JSON.stringify(intersectedSelection)}`);
2975
3435
  }
3436
+ return intersectedSelection;
2976
3437
  }
3438
+ return selection;
3439
+ }
3440
+ }
3441
+ EDITOR_TO_VIRTUAL_SCROLL_SELECTION.set(this.editor, null);
3442
+ return selection;
3443
+ }
3444
+ toNativeSelection(autoScroll = true) {
3445
+ try {
3446
+ let { selection } = this.editor;
3447
+ if (this.isEnabledVirtualScroll()) {
3448
+ selection = this.calculateVirtualScrollSelection(selection);
2977
3449
  }
2978
3450
  const root = AngularEditor.findDocumentOrShadowRoot(this.editor);
2979
3451
  const { activeElement } = root;
@@ -3035,13 +3507,24 @@ class SlateEditable {
3035
3507
  domSelection.removeAllRanges();
3036
3508
  }
3037
3509
  setTimeout(() => {
3038
- // handle scrolling in setTimeout because of
3039
- // dom should not have updated immediately after listRender's updating
3040
- newDomRange && this.scrollSelectionIntoView(this.editor, newDomRange);
3041
- // COMPAT: In Firefox, it's not enough to create a range, you also need
3042
- // to focus the contenteditable element too. (2016/11/16)
3043
- if (newDomRange && IS_FIREFOX) {
3044
- el.focus();
3510
+ if (this.isEnabledVirtualScroll() &&
3511
+ !selection &&
3512
+ this.editor.selection &&
3513
+ autoScroll &&
3514
+ this.virtualScrollConfig.scrollContainer) {
3515
+ this.virtualScrollConfig.scrollContainer.scrollTop = this.virtualScrollConfig.scrollContainer.scrollTop + 100;
3516
+ this.isUpdatingSelection = false;
3517
+ return;
3518
+ }
3519
+ else {
3520
+ // handle scrolling in setTimeout because of
3521
+ // dom should not have updated immediately after listRender's updating
3522
+ newDomRange && autoScroll && this.scrollSelectionIntoView(this.editor, newDomRange);
3523
+ // COMPAT: In Firefox, it's not enough to create a range, you also need
3524
+ // to focus the contenteditable element too. (2016/11/16)
3525
+ if (newDomRange && IS_FIREFOX) {
3526
+ el.focus();
3527
+ }
3045
3528
  }
3046
3529
  this.isUpdatingSelection = false;
3047
3530
  });
@@ -3062,10 +3545,12 @@ class SlateEditable {
3062
3545
  ngDoCheck() { }
3063
3546
  forceRender() {
3064
3547
  this.updateContext();
3065
- const virtualView = this.refreshVirtualView();
3066
- this.applyVirtualView(virtualView);
3067
- this.listRender.update(virtualView.renderedChildren, this.editor, this.context);
3068
- this.scheduleMeasureVisibleHeights();
3548
+ if (this.isEnabledVirtualScroll()) {
3549
+ this.updateListRenderAndRemeasureHeights();
3550
+ }
3551
+ else {
3552
+ this.listRender.update(this.editor.children, this.editor, this.context);
3553
+ }
3069
3554
  // repair collaborative editing when Chinese input is interrupted by other users' cursors
3070
3555
  // when the DOMElement where the selection is located is removed
3071
3556
  // the compositionupdate and compositionend events will no longer be fired
@@ -3104,10 +3589,29 @@ class SlateEditable {
3104
3589
  render() {
3105
3590
  const changed = this.updateContext();
3106
3591
  if (changed) {
3107
- const virtualView = this.refreshVirtualView();
3108
- this.applyVirtualView(virtualView);
3109
- this.listRender.update(virtualView.renderedChildren, this.editor, this.context);
3110
- this.scheduleMeasureVisibleHeights();
3592
+ if (this.isEnabledVirtualScroll()) {
3593
+ this.updateListRenderAndRemeasureHeights();
3594
+ }
3595
+ else {
3596
+ this.listRender.update(this.editor.children, this.editor, this.context);
3597
+ }
3598
+ }
3599
+ }
3600
+ updateListRenderAndRemeasureHeights() {
3601
+ const virtualView = this.calculateVirtualViewport();
3602
+ const oldInViewportChildren = this.inViewportChildren;
3603
+ this.applyVirtualView(virtualView);
3604
+ const { preRenderingCount, childrenWithPreRendering } = this.handlePreRendering();
3605
+ this.listRender.update(childrenWithPreRendering, this.editor, this.context, preRenderingCount);
3606
+ // 新增或者修改的才需要重算,计算出这个结果
3607
+ const remeasureIndics = [];
3608
+ this.inViewportChildren.forEach((child, index) => {
3609
+ if (oldInViewportChildren.indexOf(child) === -1) {
3610
+ remeasureIndics.push(this.inViewportIndics[index]);
3611
+ }
3612
+ });
3613
+ if (isDebug && remeasureIndics.length > 0) {
3614
+ console.log('remeasure height by indics: ', remeasureIndics);
3111
3615
  }
3112
3616
  }
3113
3617
  updateContext() {
@@ -3170,14 +3674,14 @@ class SlateEditable {
3170
3674
  decorations.push(...placeholderDecorations);
3171
3675
  return decorations;
3172
3676
  }
3173
- shouldUseVirtual() {
3174
- return !!(this.virtualConfig && this.virtualConfig.enabled);
3677
+ isEnabledVirtualScroll() {
3678
+ return !!(this.virtualScrollConfig && this.virtualScrollConfig.enabled);
3175
3679
  }
3176
- initializeVirtualScrolling() {
3680
+ initializeVirtualScroll() {
3177
3681
  if (this.virtualScrollInitialized) {
3178
3682
  return;
3179
3683
  }
3180
- if (this.virtualConfig && this.virtualConfig.enabled) {
3684
+ if (this.isEnabledVirtualScroll()) {
3181
3685
  this.virtualScrollInitialized = true;
3182
3686
  this.virtualTopHeightElement = document.createElement('div');
3183
3687
  this.virtualTopHeightElement.classList.add('virtual-top-height');
@@ -3190,188 +3694,256 @@ class SlateEditable {
3190
3694
  this.elementRef.nativeElement.appendChild(this.virtualTopHeightElement);
3191
3695
  this.elementRef.nativeElement.appendChild(this.virtualCenterOutlet);
3192
3696
  this.elementRef.nativeElement.appendChild(this.virtualBottomHeightElement);
3193
- this.businessHeight = this.virtualTopHeightElement.getBoundingClientRect()?.top ?? 0;
3194
- let editorResizeObserverRectWidth = this.elementRef.nativeElement.getBoundingClientRect()?.width ?? 0;
3697
+ let editorResizeObserverRectWidth = this.elementRef.nativeElement.getBoundingClientRect().width;
3698
+ EDITOR_TO_ROOT_NODE_WIDTH.set(this.editor, this.virtualTopHeightElement.getBoundingClientRect().width);
3195
3699
  this.editorResizeObserver = new ResizeObserver(entries => {
3196
3700
  if (entries.length > 0 && entries[0].contentRect.width !== editorResizeObserverRectWidth) {
3197
3701
  editorResizeObserverRectWidth = entries[0].contentRect.width;
3198
- this.remeasureHeightByIndics(Array.from(this.virtualVisibleIndexes));
3702
+ this.keyHeightMap.clear();
3703
+ const remeasureIndics = this.inViewportIndics;
3704
+ measureHeightByIndics(this.editor, remeasureIndics, true);
3705
+ EDITOR_TO_ROOT_NODE_WIDTH.set(this.editor, this.virtualTopHeightElement.getBoundingClientRect().width);
3706
+ if (isDebug) {
3707
+ debugLog('log', 'editorResizeObserverRectWidth: ', editorResizeObserverRectWidth, 'EDITOR_TO_ROOT_NODE_WIDTH: ', EDITOR_TO_ROOT_NODE_WIDTH.get(this.editor));
3708
+ }
3199
3709
  }
3200
3710
  });
3201
3711
  this.editorResizeObserver.observe(this.elementRef.nativeElement);
3202
- if (isDebug) {
3203
- const doc = this.elementRef?.nativeElement?.ownerDocument ?? document;
3204
- this.debugOverlay = new VirtualScrollDebugOverlay(doc);
3205
- this.debugOverlay.init();
3206
- }
3207
3712
  }
3208
3713
  }
3209
- changeVirtualHeight(topHeight, bottomHeight) {
3714
+ setVirtualSpaceHeight(topHeight, bottomHeight) {
3210
3715
  if (!this.virtualScrollInitialized) {
3211
3716
  return;
3212
3717
  }
3213
3718
  this.virtualTopHeightElement.style.height = `${topHeight}px`;
3214
- this.virtualBottomHeightElement.style.height = `${bottomHeight}px`;
3719
+ if (bottomHeight !== undefined) {
3720
+ this.virtualBottomHeightElement.style.height = `${bottomHeight}px`;
3721
+ }
3722
+ }
3723
+ getActualVirtualTopHeight() {
3724
+ if (!this.virtualScrollInitialized) {
3725
+ return 0;
3726
+ }
3727
+ return parseFloat(this.virtualTopHeightElement.style.height.replace('px', ''));
3215
3728
  }
3216
- debugLog(type, ...args) {
3217
- if (!this.debugOverlay) {
3218
- const doc = this.elementRef?.nativeElement?.ownerDocument ?? document;
3219
- this.debugOverlay = new VirtualScrollDebugOverlay(doc);
3729
+ handlePreRendering() {
3730
+ let preRenderingCount = 1;
3731
+ const childrenWithPreRendering = [...this.inViewportChildren];
3732
+ if (this.inViewportIndics[0] !== 0) {
3733
+ childrenWithPreRendering.unshift(this.editor.children[this.inViewportIndics[0] - 1]);
3734
+ }
3735
+ else {
3736
+ preRenderingCount = 0;
3220
3737
  }
3221
- this.debugOverlay.log(type, ...args);
3738
+ const lastIndex = this.inViewportIndics[this.inViewportIndics.length - 1];
3739
+ if (lastIndex !== this.editor.children.length - 1) {
3740
+ childrenWithPreRendering.push(this.editor.children[lastIndex + 1]);
3741
+ }
3742
+ return { preRenderingCount, childrenWithPreRendering };
3222
3743
  }
3223
- doVirtualScroll() {
3224
- this.refreshVirtualViewAnimId && cancelAnimationFrame(this.refreshVirtualViewAnimId);
3225
- this.refreshVirtualViewAnimId = requestAnimationFrame(() => {
3226
- let virtualView = this.refreshVirtualView();
3227
- let diff = this.diffVirtualView(virtualView);
3228
- if (!diff.isDiff) {
3744
+ tryUpdateVirtualViewport() {
3745
+ if (isDebug) {
3746
+ debugLog('log', 'tryUpdateVirtualViewport');
3747
+ }
3748
+ if (this.inViewportIndics.length > 0) {
3749
+ const topHeight = this.getActualVirtualTopHeight();
3750
+ const refreshVirtualTopHeight = calculateVirtualTopHeight(this.editor, this.inViewportIndics[0]);
3751
+ if (topHeight !== refreshVirtualTopHeight) {
3752
+ if (isDebug) {
3753
+ debugLog('log', 'update top height since dirty state(正数减去高度,负数代表增加高度): ', topHeight - refreshVirtualTopHeight);
3754
+ }
3755
+ this.setVirtualSpaceHeight(refreshVirtualTopHeight);
3229
3756
  return;
3230
3757
  }
3231
- if (diff.isMissingTop) {
3232
- const result = this.remeasureHeightByIndics(diff.diffTopRenderedIndexes);
3233
- if (result) {
3234
- virtualView = this.refreshVirtualView();
3235
- diff = this.diffVirtualView(virtualView, 'second');
3236
- if (!diff.isDiff) {
3237
- return;
3238
- }
3758
+ }
3759
+ this.tryUpdateVirtualViewportAnimId && cancelAnimationFrame(this.tryUpdateVirtualViewportAnimId);
3760
+ this.tryUpdateVirtualViewportAnimId = requestAnimationFrame(() => {
3761
+ if (isDebug) {
3762
+ debugLog('log', 'tryUpdateVirtualViewport Anim start');
3763
+ }
3764
+ let virtualView = this.calculateVirtualViewport();
3765
+ let diff = this.diffVirtualViewport(virtualView);
3766
+ if (diff.isDifferent && diff.needRemoveOnTop) {
3767
+ const remeasureIndics = diff.changedIndexesOfTop;
3768
+ const changed = measureHeightByIndics(this.editor, remeasureIndics);
3769
+ if (changed) {
3770
+ virtualView = this.calculateVirtualViewport();
3771
+ diff = this.diffVirtualViewport(virtualView, 'second');
3239
3772
  }
3240
3773
  }
3241
- this.applyVirtualView(virtualView);
3242
- if (this.listRender.initialized) {
3243
- this.listRender.update(virtualView.renderedChildren, this.editor, this.context);
3244
- if (!AngularEditor.isReadOnly(this.editor) && this.editor.selection) {
3245
- this.toNativeSelection();
3774
+ if (diff.isDifferent) {
3775
+ this.applyVirtualView(virtualView);
3776
+ if (this.listRender.initialized) {
3777
+ const { preRenderingCount, childrenWithPreRendering } = this.handlePreRendering();
3778
+ this.listRender.update(childrenWithPreRendering, this.editor, this.context, preRenderingCount);
3779
+ if (diff.needAddOnTop) {
3780
+ const remeasureAddedIndics = diff.changedIndexesOfTop;
3781
+ if (isDebug) {
3782
+ debugLog('log', 'needAddOnTop to remeasure heights: ', remeasureAddedIndics);
3783
+ }
3784
+ const startIndexBeforeAdd = diff.changedIndexesOfTop[diff.changedIndexesOfTop.length - 1] + 1;
3785
+ const topHeightBeforeAdd = virtualView.accumulatedHeights[startIndexBeforeAdd];
3786
+ const changed = measureHeightByIndics(this.editor, remeasureAddedIndics);
3787
+ if (changed) {
3788
+ const newHeights = buildHeightsAndAccumulatedHeights(this.editor);
3789
+ const actualTopHeightAfterAdd = newHeights.accumulatedHeights[startIndexBeforeAdd];
3790
+ const newTopHeight = virtualView.top - (actualTopHeightAfterAdd - topHeightBeforeAdd);
3791
+ this.setVirtualSpaceHeight(newTopHeight);
3792
+ if (isDebug) {
3793
+ debugLog('log', `update top height since will add element in top(正数减去高度,负数代表增加高度): ${actualTopHeightAfterAdd - topHeightBeforeAdd}`);
3794
+ }
3795
+ }
3796
+ }
3797
+ if (!AngularEditor.isReadOnly(this.editor) && this.editor.selection) {
3798
+ this.toNativeSelection(false);
3799
+ }
3246
3800
  }
3247
3801
  }
3248
- this.scheduleMeasureVisibleHeights();
3802
+ if (isDebug) {
3803
+ debugLog('log', 'tryUpdateVirtualViewport Anim end');
3804
+ }
3249
3805
  });
3250
3806
  }
3251
- refreshVirtualView() {
3807
+ calculateVirtualViewport() {
3252
3808
  const children = (this.editor.children || []);
3253
- if (!children.length || !this.shouldUseVirtual()) {
3809
+ if (!children.length || !this.isEnabledVirtualScroll()) {
3254
3810
  return {
3255
- renderedChildren: children,
3256
- visibleIndexes: new Set(),
3811
+ inViewportChildren: children,
3812
+ inViewportIndics: [],
3257
3813
  top: 0,
3258
3814
  bottom: 0,
3259
3815
  heights: []
3260
3816
  };
3261
3817
  }
3262
- const scrollTop = this.virtualConfig.scrollTop;
3263
- const viewportHeight = this.virtualConfig.viewportHeight ?? 0;
3818
+ const scrollTop = this.virtualScrollConfig.scrollTop;
3819
+ const viewportHeight = this.virtualScrollConfig.viewportHeight ?? 0;
3264
3820
  if (!viewportHeight) {
3265
3821
  return {
3266
- renderedChildren: [],
3267
- visibleIndexes: new Set(),
3822
+ inViewportChildren: [],
3823
+ inViewportIndics: [],
3268
3824
  top: 0,
3269
3825
  bottom: 0,
3270
3826
  heights: []
3271
3827
  };
3272
3828
  }
3273
3829
  const elementLength = children.length;
3274
- const adjustedScrollTop = Math.max(0, scrollTop - this.businessHeight);
3275
- const heights = children.map((_, idx) => this.getBlockHeight(idx));
3276
- const accumulatedHeights = this.buildAccumulatedHeight(heights);
3830
+ if (!EDITOR_TO_BUSINESS_TOP.has(this.editor)) {
3831
+ EDITOR_TO_BUSINESS_TOP.set(this.editor, 0);
3832
+ setTimeout(() => {
3833
+ const virtualTopBoundingTop = this.virtualTopHeightElement.getBoundingClientRect()?.top ?? 0;
3834
+ const businessTop = Math.ceil(virtualTopBoundingTop) +
3835
+ Math.ceil(this.virtualScrollConfig.scrollTop) -
3836
+ Math.floor(this.virtualScrollConfig.viewportBoundingTop);
3837
+ EDITOR_TO_BUSINESS_TOP.set(this.editor, businessTop);
3838
+ if (isDebug) {
3839
+ debugLog('log', 'businessTop', businessTop);
3840
+ }
3841
+ }, 100);
3842
+ }
3843
+ const adjustedScrollTop = Math.max(0, scrollTop - getBusinessTop(this.editor));
3844
+ const { heights, accumulatedHeights } = buildHeightsAndAccumulatedHeights(this.editor);
3277
3845
  const totalHeight = accumulatedHeights[elementLength];
3278
3846
  const maxScrollTop = Math.max(0, totalHeight - viewportHeight);
3279
3847
  const limitedScrollTop = Math.min(adjustedScrollTop, maxScrollTop);
3280
- const viewBottom = limitedScrollTop + viewportHeight + this.businessHeight;
3848
+ const viewBottom = limitedScrollTop + viewportHeight;
3281
3849
  let accumulatedOffset = 0;
3282
- let visibleStartIndex = -1;
3850
+ let inViewportStartIndex = -1;
3283
3851
  const visible = [];
3284
- const visibleIndexes = [];
3852
+ const inViewportIndics = [];
3285
3853
  for (let i = 0; i < elementLength && accumulatedOffset < viewBottom; i++) {
3286
3854
  const currentHeight = heights[i];
3287
3855
  const nextOffset = accumulatedOffset + currentHeight;
3288
3856
  // 可视区域有交集,加入渲染
3289
3857
  if (nextOffset > limitedScrollTop && accumulatedOffset < viewBottom) {
3290
- if (visibleStartIndex === -1)
3291
- visibleStartIndex = i; // 第一个相交起始位置
3858
+ if (inViewportStartIndex === -1)
3859
+ inViewportStartIndex = i; // 第一个相交起始位置
3292
3860
  visible.push(children[i]);
3293
- visibleIndexes.push(i);
3861
+ inViewportIndics.push(i);
3294
3862
  }
3295
3863
  accumulatedOffset = nextOffset;
3296
3864
  }
3297
- if (visibleStartIndex === -1 && elementLength) {
3298
- visibleStartIndex = elementLength - 1;
3299
- visible.push(children[visibleStartIndex]);
3300
- visibleIndexes.push(visibleStartIndex);
3865
+ if (inViewportStartIndex === -1 && elementLength) {
3866
+ inViewportStartIndex = elementLength - 1;
3867
+ visible.push(children[inViewportStartIndex]);
3868
+ inViewportIndics.push(inViewportStartIndex);
3301
3869
  }
3302
- const visibleEndIndex = visibleStartIndex === -1 ? elementLength - 1 : (visibleIndexes[visibleIndexes.length - 1] ?? visibleStartIndex);
3303
- const top = visibleStartIndex === -1 ? 0 : accumulatedHeights[visibleStartIndex];
3304
- const bottom = totalHeight - accumulatedHeights[visibleEndIndex + 1];
3870
+ const inViewportEndIndex = inViewportStartIndex === -1 ? elementLength - 1 : (inViewportIndics[inViewportIndics.length - 1] ?? inViewportStartIndex);
3871
+ const top = inViewportStartIndex === -1 ? 0 : accumulatedHeights[inViewportStartIndex];
3872
+ const bottom = totalHeight - accumulatedHeights[inViewportEndIndex + 1];
3305
3873
  return {
3306
- renderedChildren: visible.length ? visible : children,
3307
- visibleIndexes: new Set(visibleIndexes),
3874
+ inViewportChildren: visible.length ? visible : children,
3875
+ inViewportIndics,
3308
3876
  top,
3309
3877
  bottom,
3310
- heights
3878
+ heights,
3879
+ accumulatedHeights
3311
3880
  };
3312
3881
  }
3313
3882
  applyVirtualView(virtualView) {
3314
- this.renderedChildren = virtualView.renderedChildren;
3315
- this.changeVirtualHeight(virtualView.top, virtualView.bottom);
3316
- this.virtualVisibleIndexes = virtualView.visibleIndexes;
3883
+ this.inViewportChildren = virtualView.inViewportChildren;
3884
+ this.setVirtualSpaceHeight(virtualView.top, virtualView.bottom);
3885
+ this.inViewportIndics = virtualView.inViewportIndics;
3317
3886
  }
3318
- diffVirtualView(virtualView, stage = 'first') {
3319
- if (!this.renderedChildren.length) {
3887
+ diffVirtualViewport(virtualView, stage = 'first') {
3888
+ if (!this.inViewportChildren.length) {
3889
+ if (isDebug) {
3890
+ debugLog('log', 'diffVirtualViewport', stage, 'empty inViewportChildren', virtualView.inViewportIndics);
3891
+ }
3320
3892
  return {
3321
- isDiff: true,
3322
- diffTopRenderedIndexes: [],
3323
- diffBottomRenderedIndexes: []
3893
+ isDifferent: true,
3894
+ changedIndexesOfTop: [],
3895
+ changedIndexesOfBottom: []
3324
3896
  };
3325
3897
  }
3326
- const oldVisibleIndexes = [...this.virtualVisibleIndexes];
3327
- const newVisibleIndexes = [...virtualView.visibleIndexes];
3328
- const firstNewIndex = newVisibleIndexes[0];
3329
- const lastNewIndex = newVisibleIndexes[newVisibleIndexes.length - 1];
3330
- const firstOldIndex = oldVisibleIndexes[0];
3331
- const lastOldIndex = oldVisibleIndexes[oldVisibleIndexes.length - 1];
3898
+ const oldIndexesInViewport = [...this.inViewportIndics];
3899
+ const newIndexesInViewport = [...virtualView.inViewportIndics];
3900
+ const firstNewIndex = newIndexesInViewport[0];
3901
+ const lastNewIndex = newIndexesInViewport[newIndexesInViewport.length - 1];
3902
+ const firstOldIndex = oldIndexesInViewport[0];
3903
+ const lastOldIndex = oldIndexesInViewport[oldIndexesInViewport.length - 1];
3332
3904
  if (firstNewIndex !== firstOldIndex || lastNewIndex !== lastOldIndex) {
3333
- const diffTopRenderedIndexes = [];
3334
- const diffBottomRenderedIndexes = [];
3335
- const isMissingTop = firstNewIndex !== firstOldIndex && firstNewIndex > firstOldIndex;
3336
- const isAddedTop = firstNewIndex !== firstOldIndex && firstNewIndex < firstOldIndex;
3337
- const isMissingBottom = lastNewIndex !== lastOldIndex && lastOldIndex > lastNewIndex;
3338
- const isAddedBottom = lastNewIndex !== lastOldIndex && lastOldIndex < lastNewIndex;
3339
- if (isMissingTop || isAddedBottom) {
3905
+ const changedIndexesOfTop = [];
3906
+ const changedIndexesOfBottom = [];
3907
+ const needRemoveOnTop = firstNewIndex !== firstOldIndex && firstNewIndex > firstOldIndex;
3908
+ const needAddOnTop = firstNewIndex !== firstOldIndex && firstNewIndex < firstOldIndex;
3909
+ const needRemoveOnBottom = lastNewIndex !== lastOldIndex && lastOldIndex > lastNewIndex;
3910
+ const needAddOnBottom = lastNewIndex !== lastOldIndex && lastOldIndex < lastNewIndex;
3911
+ if (needRemoveOnTop || needAddOnBottom) {
3340
3912
  // 向下
3341
- for (let index = 0; index < oldVisibleIndexes.length; index++) {
3342
- const element = oldVisibleIndexes[index];
3343
- if (!newVisibleIndexes.includes(element)) {
3344
- diffTopRenderedIndexes.push(element);
3913
+ for (let index = 0; index < oldIndexesInViewport.length; index++) {
3914
+ const element = oldIndexesInViewport[index];
3915
+ if (!newIndexesInViewport.includes(element)) {
3916
+ changedIndexesOfTop.push(element);
3345
3917
  }
3346
3918
  else {
3347
3919
  break;
3348
3920
  }
3349
3921
  }
3350
- for (let index = newVisibleIndexes.length - 1; index >= 0; index--) {
3351
- const element = newVisibleIndexes[index];
3352
- if (!oldVisibleIndexes.includes(element)) {
3353
- diffBottomRenderedIndexes.push(element);
3922
+ for (let index = newIndexesInViewport.length - 1; index >= 0; index--) {
3923
+ const element = newIndexesInViewport[index];
3924
+ if (!oldIndexesInViewport.includes(element)) {
3925
+ changedIndexesOfBottom.push(element);
3354
3926
  }
3355
3927
  else {
3356
3928
  break;
3357
3929
  }
3358
3930
  }
3359
3931
  }
3360
- else if (isAddedTop || isMissingBottom) {
3932
+ else if (needAddOnTop || needRemoveOnBottom) {
3361
3933
  // 向上
3362
- for (let index = 0; index < newVisibleIndexes.length; index++) {
3363
- const element = newVisibleIndexes[index];
3364
- if (!oldVisibleIndexes.includes(element)) {
3365
- diffTopRenderedIndexes.push(element);
3934
+ for (let index = 0; index < newIndexesInViewport.length; index++) {
3935
+ const element = newIndexesInViewport[index];
3936
+ if (!oldIndexesInViewport.includes(element)) {
3937
+ changedIndexesOfTop.push(element);
3366
3938
  }
3367
3939
  else {
3368
3940
  break;
3369
3941
  }
3370
3942
  }
3371
- for (let index = oldVisibleIndexes.length - 1; index >= 0; index--) {
3372
- const element = oldVisibleIndexes[index];
3373
- if (!newVisibleIndexes.includes(element)) {
3374
- diffBottomRenderedIndexes.push(element);
3943
+ for (let index = oldIndexesInViewport.length - 1; index >= 0; index--) {
3944
+ const element = oldIndexesInViewport[index];
3945
+ if (!newIndexesInViewport.includes(element)) {
3946
+ changedIndexesOfBottom.push(element);
3375
3947
  }
3376
3948
  else {
3377
3949
  break;
@@ -3379,125 +3951,35 @@ class SlateEditable {
3379
3951
  }
3380
3952
  }
3381
3953
  if (isDebug) {
3382
- this.debugLog('log', `====== diffVirtualView stage: ${stage} ======`);
3383
- this.debugLog('log', 'oldVisibleIndexes:', oldVisibleIndexes);
3384
- this.debugLog('log', 'newVisibleIndexes:', newVisibleIndexes);
3385
- this.debugLog('log', 'diffTopRenderedIndexes:', isMissingTop ? '-' : isAddedTop ? '+' : '-', diffTopRenderedIndexes, diffTopRenderedIndexes.map(index => this.getBlockHeight(index, 0)));
3386
- this.debugLog('log', 'diffBottomRenderedIndexes:', isAddedBottom ? '+' : isMissingBottom ? '-' : '+', diffBottomRenderedIndexes, diffBottomRenderedIndexes.map(index => this.getBlockHeight(index, 0)));
3387
- const needTop = virtualView.heights.slice(0, newVisibleIndexes[0]).reduce((acc, height) => acc + height, 0);
3954
+ debugLog('log', `====== diffVirtualViewport stage: ${stage} ======`);
3955
+ debugLog('log', 'oldIndexesInViewport:', oldIndexesInViewport);
3956
+ debugLog('log', 'newIndexesInViewport:', newIndexesInViewport);
3957
+ debugLog('log', 'changedIndexesOfTop:', needRemoveOnTop ? '-' : needAddOnTop ? '+' : '-', changedIndexesOfTop, changedIndexesOfTop.map(index => getRealHeightByElement(this.editor, this.editor.children[index], 0)));
3958
+ debugLog('log', 'changedIndexesOfBottom:', needAddOnBottom ? '+' : needRemoveOnBottom ? '-' : '+', changedIndexesOfBottom, changedIndexesOfBottom.map(index => getRealHeightByElement(this.editor, this.editor.children[index], 0)));
3959
+ const needTop = virtualView.heights.slice(0, newIndexesInViewport[0]).reduce((acc, height) => acc + height, 0);
3388
3960
  const needBottom = virtualView.heights
3389
- .slice(newVisibleIndexes[newVisibleIndexes.length - 1] + 1)
3961
+ .slice(newIndexesInViewport[newIndexesInViewport.length - 1] + 1)
3390
3962
  .reduce((acc, height) => acc + height, 0);
3391
- this.debugLog('log', 'newTopHeight:', needTop, 'prevTopHeight:', parseFloat(this.virtualTopHeightElement.style.height));
3392
- this.debugLog('log', 'newBottomHeight:', needBottom, 'prevBottomHeight:', parseFloat(this.virtualBottomHeightElement.style.height));
3393
- this.debugLog('warn', '=========== Dividing line ===========');
3963
+ debugLog('log', needTop - parseFloat(this.virtualTopHeightElement.style.height), 'newTopHeight:', needTop, 'prevTopHeight:', parseFloat(this.virtualTopHeightElement.style.height));
3964
+ debugLog('log', 'newBottomHeight:', needBottom, 'prevBottomHeight:', parseFloat(this.virtualBottomHeightElement.style.height));
3965
+ debugLog('warn', '=========== Dividing line ===========');
3394
3966
  }
3395
3967
  return {
3396
- isDiff: true,
3397
- isMissingTop,
3398
- isAddedTop,
3399
- isMissingBottom,
3400
- isAddedBottom,
3401
- diffTopRenderedIndexes,
3402
- diffBottomRenderedIndexes
3968
+ isDifferent: true,
3969
+ needRemoveOnTop,
3970
+ needAddOnTop,
3971
+ needRemoveOnBottom,
3972
+ needAddOnBottom,
3973
+ changedIndexesOfTop,
3974
+ changedIndexesOfBottom
3403
3975
  };
3404
3976
  }
3405
3977
  return {
3406
- isDiff: false,
3407
- diffTopRenderedIndexes: [],
3408
- diffBottomRenderedIndexes: []
3978
+ isDifferent: false,
3979
+ changedIndexesOfTop: [],
3980
+ changedIndexesOfBottom: []
3409
3981
  };
3410
3982
  }
3411
- getBlockHeight(index, defaultHeight = VIRTUAL_SCROLL_DEFAULT_BLOCK_HEIGHT) {
3412
- const node = this.editor.children[index];
3413
- if (!node) {
3414
- return defaultHeight;
3415
- }
3416
- const key = AngularEditor.findKey(this.editor, node);
3417
- return this.measuredHeights.get(key.id) ?? defaultHeight;
3418
- }
3419
- buildAccumulatedHeight(heights) {
3420
- const accumulatedHeights = new Array(heights.length + 1).fill(0);
3421
- for (let i = 0; i < heights.length; i++) {
3422
- // 存储前 i 个的累计高度
3423
- accumulatedHeights[i + 1] = accumulatedHeights[i] + heights[i];
3424
- }
3425
- return accumulatedHeights;
3426
- }
3427
- scheduleMeasureVisibleHeights() {
3428
- if (!this.shouldUseVirtual()) {
3429
- return;
3430
- }
3431
- this.measureVisibleHeightsAnimId && cancelAnimationFrame(this.measureVisibleHeightsAnimId);
3432
- this.measureVisibleHeightsAnimId = requestAnimationFrame(() => {
3433
- this.measureVisibleHeights();
3434
- });
3435
- }
3436
- measureVisibleHeights() {
3437
- const children = (this.editor.children || []);
3438
- this.virtualVisibleIndexes.forEach(index => {
3439
- const node = children[index];
3440
- if (!node) {
3441
- return;
3442
- }
3443
- const key = AngularEditor.findKey(this.editor, node);
3444
- // 跳过已测过的块
3445
- if (this.measuredHeights.has(key.id)) {
3446
- return;
3447
- }
3448
- const view = ELEMENT_TO_COMPONENT.get(node);
3449
- if (!view) {
3450
- return;
3451
- }
3452
- const ret = view.getRealHeight();
3453
- if (ret instanceof Promise) {
3454
- ret.then(height => {
3455
- this.measuredHeights.set(key.id, height);
3456
- });
3457
- }
3458
- else {
3459
- this.measuredHeights.set(key.id, ret);
3460
- }
3461
- });
3462
- }
3463
- remeasureHeightByIndics(indics) {
3464
- const children = (this.editor.children || []);
3465
- let isHeightChanged = false;
3466
- indics.forEach(index => {
3467
- const node = children[index];
3468
- if (!node) {
3469
- return;
3470
- }
3471
- const key = AngularEditor.findKey(this.editor, node);
3472
- const view = ELEMENT_TO_COMPONENT.get(node);
3473
- if (!view) {
3474
- return;
3475
- }
3476
- const prevHeight = this.measuredHeights.get(key.id);
3477
- const ret = view.getRealHeight();
3478
- if (ret instanceof Promise) {
3479
- ret.then(height => {
3480
- if (height !== prevHeight) {
3481
- this.measuredHeights.set(key.id, height);
3482
- isHeightChanged = true;
3483
- if (isDebug) {
3484
- this.debugLog('log', `remeasureHeightByIndics, index: ${index} prevHeight: ${prevHeight} newHeight: ${height}`);
3485
- }
3486
- }
3487
- });
3488
- }
3489
- else {
3490
- if (ret !== prevHeight) {
3491
- this.measuredHeights.set(key.id, ret);
3492
- isHeightChanged = true;
3493
- if (isDebug) {
3494
- this.debugLog('log', `remeasureHeightByIndics, index: ${index} prevHeight: ${prevHeight} newHeight: ${ret}`);
3495
- }
3496
- }
3497
- }
3498
- });
3499
- return isHeightChanged;
3500
- }
3501
3983
  //#region event proxy
3502
3984
  addEventListener(eventName, listener, target = this.elementRef.nativeElement) {
3503
3985
  this.manualListeners.push(this.renderer2.listen(target, eventName, (event) => {
@@ -3538,7 +4020,7 @@ class SlateEditable {
3538
4020
  if (this.editor.selection && Range.equals(range, this.editor.selection) && !hasStringTarget(domSelection)) {
3539
4021
  if (!isTargetInsideVoid(this.editor, activeElement)) {
3540
4022
  // force adjust DOMSelection
3541
- this.toNativeSelection();
4023
+ this.toNativeSelection(false);
3542
4024
  }
3543
4025
  }
3544
4026
  else {
@@ -4195,8 +4677,6 @@ class SlateEditable {
4195
4677
  //#endregion
4196
4678
  ngOnDestroy() {
4197
4679
  this.editorResizeObserver?.disconnect();
4198
- this.debugOverlay?.dispose();
4199
- this.debugOverlay = undefined;
4200
4680
  NODE_TO_ELEMENT.delete(this.editor);
4201
4681
  this.manualListeners.forEach(manualListener => {
4202
4682
  manualListener();
@@ -4385,16 +4865,35 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImpo
4385
4865
  }]
4386
4866
  }], ctorParameters: () => [{ type: i0.ElementRef }] });
4387
4867
 
4868
+ class SlateString {
4869
+ ngOnInit() { }
4870
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: SlateString, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
4871
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.12", type: SlateString, isStandalone: true, selector: "span[slateString]", inputs: { context: "context", viewContext: "viewContext" }, ngImport: i0, template: '', isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
4872
+ }
4873
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: SlateString, decorators: [{
4874
+ type: Component,
4875
+ args: [{
4876
+ selector: 'span[slateString]',
4877
+ template: '',
4878
+ changeDetection: ChangeDetectionStrategy.OnPush,
4879
+ standalone: true
4880
+ }]
4881
+ }], propDecorators: { context: [{
4882
+ type: Input
4883
+ }], viewContext: [{
4884
+ type: Input
4885
+ }] } });
4886
+
4388
4887
  class SlateModule {
4389
4888
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: SlateModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
4390
- static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.12", ngImport: i0, type: SlateModule, imports: [CommonModule, SlateEditable, SlateChildrenOutlet], exports: [SlateEditable, SlateChildrenOutlet] }); }
4889
+ static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.12", ngImport: i0, type: SlateModule, imports: [CommonModule, SlateEditable, SlateChildrenOutlet, SlateString], exports: [SlateEditable, SlateChildrenOutlet, SlateString] }); }
4391
4890
  static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: SlateModule, imports: [CommonModule] }); }
4392
4891
  }
4393
4892
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: SlateModule, decorators: [{
4394
4893
  type: NgModule,
4395
4894
  args: [{
4396
- imports: [CommonModule, SlateEditable, SlateChildrenOutlet],
4397
- exports: [SlateEditable, SlateChildrenOutlet],
4895
+ imports: [CommonModule, SlateEditable, SlateChildrenOutlet, SlateString],
4896
+ exports: [SlateEditable, SlateChildrenOutlet, SlateString],
4398
4897
  providers: []
4399
4898
  }]
4400
4899
  }] });
@@ -4510,6 +5009,7 @@ class BaseElementComponent extends BaseComponent {
4510
5009
  if (ELEMENT_TO_COMPONENT.get(this.element) === this) {
4511
5010
  ELEMENT_TO_COMPONENT.delete(this.element);
4512
5011
  }
5012
+ this.listRender.destroy();
4513
5013
  }
4514
5014
  onContextChange() {
4515
5015
  this.childrenContext = this.getChildrenContext();
@@ -4597,6 +5097,7 @@ class BaseTextComponent extends BaseComponent {
4597
5097
  NODE_TO_ELEMENT.delete(this.text);
4598
5098
  }
4599
5099
  ELEMENT_TO_NODE.delete(this.nativeElement);
5100
+ this.leavesRender.destroy();
4600
5101
  }
4601
5102
  onContextChange() {
4602
5103
  this.updateWeakMap();
@@ -4622,6 +5123,9 @@ class BaseLeafComponent extends BaseComponent {
4622
5123
  super(...arguments);
4623
5124
  this.stringRender = null;
4624
5125
  this.isSlateLeaf = true;
5126
+ this.getOutletParent = () => {
5127
+ return this.elementRef.nativeElement;
5128
+ };
4625
5129
  }
4626
5130
  get text() {
4627
5131
  return this.context && this.context.text;
@@ -4636,7 +5140,7 @@ class BaseLeafComponent extends BaseComponent {
4636
5140
  if (!this.initialized) {
4637
5141
  this.stringRender = new SlateStringRender(this.context, this.viewContext);
4638
5142
  const stringNode = this.stringRender.render();
4639
- this.nativeElement.appendChild(stringNode);
5143
+ this.getOutletParent().appendChild(stringNode);
4640
5144
  }
4641
5145
  else {
4642
5146
  this.stringRender?.update(this.context, this.viewContext);
@@ -4708,5 +5212,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImpo
4708
5212
  * Generated bundle index. Do not edit.
4709
5213
  */
4710
5214
 
4711
- export { AngularEditor, BaseComponent, BaseElementComponent, BaseElementFlavour, BaseFlavour, BaseLeafComponent, BaseLeafFlavour, BaseTextComponent, BaseTextFlavour, BlockCardRef, DEFAULT_ELEMENT_HEIGHT, DefaultTextFlavour, EDITOR_TO_AFTER_VIEW_INIT_QUEUE, ELEMENT_TO_COMPONENT, FAKE_LEFT_BLOCK_CARD_OFFSET, FAKE_RIGHT_BLOCK_CARD_OFFSET, FlavourRef, HAS_BEFORE_INPUT_SUPPORT, IS_ANDROID, IS_APPLE, IS_CHROME, IS_CHROME_LEGACY, IS_EDGE_LEGACY, IS_FIREFOX, IS_FIREFOX_LEGACY, IS_IOS, IS_QQBROWSER, IS_SAFARI, IS_UC_MOBILE, IS_WECHATBROWSER, JUST_NOW_UPDATED_VIRTUAL_VIEW, PLACEHOLDER_SYMBOL, SLATE_BLOCK_CARD_CLASS_NAME, SLATE_DEBUG_KEY, SlateBlockCard, SlateChildrenOutlet, SlateEditable, SlateErrorCode, SlateFragmentAttributeKey, SlateModule, VIRTUAL_SCROLL_DEFAULT_BLOCK_HEIGHT, VIRTUAL_SCROLL_DEFAULT_BUFFER_COUNT, VoidTextFlavour, blobAsString, buildHTMLText, check, completeTable, createClipboardData, createText, createThrottleRAF, defaultScrollSelectionIntoView, fallbackCopyText, getBlockCardByNativeElement, getCardTargetAttribute, getClipboardData, getClipboardFromHTMLText, getContentHeight, getDataTransferClipboard, getDataTransferClipboardText, getNavigatorClipboard, getPlainText, getSelection, getSlateFragmentAttribute, getZeroTextNode, hasAfterContextChange, hasBeforeContextChange, hasBlockCard, hasBlockCardWithNode, hotkeys, isCardCenterByTargetAttr, isCardLeft, isCardLeftByTargetAttr, isCardRightByTargetAttr, isClipboardFile, isClipboardReadSupported, isClipboardWriteSupported, isClipboardWriteTextSupported, isComponentType, isDOMText, isDecoratorRangeListEqual, isFlavourType, isInvalidTable, isTemplateRef, isValid, normalize, setClipboardData, setDataTransferClipboard, setDataTransferClipboardText, setNavigatorClipboard, shallowCompare, stripHtml, withAngular };
5215
+ export { AngularEditor, BaseComponent, BaseElementComponent, BaseElementFlavour, BaseFlavour, BaseLeafComponent, BaseLeafFlavour, BaseTextComponent, BaseTextFlavour, BlockCardRef, DEFAULT_ELEMENT_HEIGHT, DefaultTextFlavour, EDITOR_TO_AFTER_VIEW_INIT_QUEUE, EDITOR_TO_BUSINESS_TOP, EDITOR_TO_ROOT_NODE_WIDTH, EDITOR_TO_VIRTUAL_SCROLL_SELECTION, ELEMENT_KEY_TO_HEIGHTS, ELEMENT_TO_COMPONENT, FAKE_LEFT_BLOCK_CARD_OFFSET, FAKE_RIGHT_BLOCK_CARD_OFFSET, FlavourRef, HAS_BEFORE_INPUT_SUPPORT, IS_ANDROID, IS_APPLE, IS_CHROME, IS_CHROME_LEGACY, IS_EDGE_LEGACY, IS_ENABLED_VIRTUAL_SCROLL, IS_FIREFOX, IS_FIREFOX_LEGACY, IS_IOS, IS_QQBROWSER, IS_SAFARI, IS_UC_MOBILE, IS_WECHATBROWSER, PLACEHOLDER_SYMBOL, SLATE_BLOCK_CARD_CLASS_NAME, SLATE_DEBUG_KEY, SLATE_DEBUG_KEY_SCROLL_TOP, SlateBlockCard, SlateChildrenOutlet, SlateEditable, SlateErrorCode, SlateFragmentAttributeKey, SlateModule, SlateString, VIRTUAL_SCROLL_DEFAULT_BLOCK_HEIGHT, VoidTextFlavour, blobAsString, buildHTMLText, buildHeightsAndAccumulatedHeights, calculateVirtualTopHeight, check, completeTable, createClipboardData, createText, createThrottleRAF, debugLog, defaultScrollSelectionIntoView, fallbackCopyText, getBlockCardByNativeElement, getBusinessTop, getCardTargetAttribute, getClipboardData, getClipboardFromHTMLText, getContentHeight, getDataTransferClipboard, getDataTransferClipboardText, getNavigatorClipboard, getPlainText, getRealHeightByElement, getSelection, getSlateFragmentAttribute, getZeroTextNode, hasAfterContextChange, hasBeforeContextChange, hasBlockCard, hasBlockCardWithNode, hotkeys, isCardCenterByTargetAttr, isCardLeft, isCardLeftByTargetAttr, isCardRightByTargetAttr, isClipboardFile, isClipboardReadSupported, isClipboardWriteSupported, isClipboardWriteTextSupported, isComponentType, isDOMText, isDebug, isDebugScrollTop, isDecoratorRangeListEqual, isFlavourType, isInvalidTable, isTemplateRef, isValid, measureHeightByElement, measureHeightByIndics, normalize, scrollToElement, setClipboardData, setDataTransferClipboard, setDataTransferClipboardText, setNavigatorClipboard, shallowCompare, stripHtml, withAngular };
4712
5216
  //# sourceMappingURL=slate-angular.mjs.map