slate-angular 20.2.0-next.26 → 20.2.0-next.28

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.
@@ -955,2237 +955,2247 @@ const fallbackCopyText = async (text) => {
955
955
  });
956
956
  };
957
957
 
958
- const ELEMENT_KEY_TO_HEIGHTS = new WeakMap();
959
- const EDITOR_TO_BUSINESS_TOP = new WeakMap();
960
- const measureHeightByElement = (editor, element) => {
961
- const key = AngularEditor.findKey(editor, element);
962
- const view = ELEMENT_TO_COMPONENT.get(element);
963
- if (!view) {
964
- return;
965
- }
966
- const ret = view.getRealHeight();
967
- const heights = ELEMENT_KEY_TO_HEIGHTS.get(editor);
968
- heights.set(key.id, ret);
969
- return ret;
970
- };
971
- const measureHeightByIndics = (editor, indics, force = false) => {
972
- let hasChanged = false;
973
- indics.forEach((index, i) => {
974
- const element = editor.children[index];
975
- const preHeight = getRealHeightByElement(editor, element, 0);
976
- if (preHeight && !force) {
977
- return;
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);
978
967
  }
979
- hasChanged = true;
980
- measureHeightByElement(editor, element);
981
- });
982
- return hasChanged;
983
- };
984
- const getBusinessTop = (editor) => {
985
- return EDITOR_TO_BUSINESS_TOP.get(editor) ?? 0;
986
- };
987
- const getRealHeightByElement = (editor, element, defaultHeight = VIRTUAL_SCROLL_DEFAULT_BLOCK_HEIGHT) => {
988
- const isVisible = editor.isVisible(element);
989
- if (!isVisible) {
990
- return 0;
991
- }
992
- const heights = ELEMENT_KEY_TO_HEIGHTS.get(editor);
993
- const key = AngularEditor.findKey(editor, element);
994
- const height = heights?.get(key.id);
995
- if (typeof height === 'number') {
996
- return height;
997
- }
998
- if (heights?.has(key.id)) {
999
- console.error('getBlockHeight: invalid height value', key.id, height);
1000
- }
1001
- return defaultHeight;
1002
- };
1003
- const buildHeightsAndAccumulatedHeights = (editor) => {
1004
- const children = (editor.children || []);
1005
- const heights = new Array(children.length);
1006
- const accumulatedHeights = new Array(children.length + 1);
1007
- accumulatedHeights[0] = 0;
1008
- for (let i = 0; i < children.length; i++) {
1009
- const height = getRealHeightByElement(editor, children[i]);
1010
- heights[i] = height;
1011
- accumulatedHeights[i + 1] = accumulatedHeights[i] + height;
968
+ this.instance.init();
969
+ return this.instance;
1012
970
  }
1013
- return { heights, accumulatedHeights };
1014
- };
1015
- const scrollToElement = (editor, element, scrollTo) => {
1016
- const children = editor.children;
1017
- if (!children.length) {
1018
- return;
971
+ static log(doc, type, ...args) {
972
+ this.getInstance(doc).log(type, ...args);
1019
973
  }
1020
- const anchorIndex = children.findIndex(item => item === element);
1021
- if (anchorIndex < 0) {
1022
- return;
974
+ static syncScrollTop(doc, value) {
975
+ const instance = this.getInstance(doc);
976
+ instance.setScrollTopValue(value);
1023
977
  }
1024
- const { accumulatedHeights } = buildHeightsAndAccumulatedHeights(editor);
1025
- scrollTo((accumulatedHeights[anchorIndex] ?? 0) + getBusinessTop(editor));
1026
- };
1027
-
1028
- const withAngular = (editor, clipboardFormatKey = 'x-slate-fragment') => {
1029
- let e = editor;
1030
- let { apply } = e;
1031
- e = withDOM(e, clipboardFormatKey);
1032
- e.setFragmentData = (dataTransfer, originEvent) => {
1033
- const { selection } = e;
1034
- if (!selection) {
1035
- return;
1036
- }
1037
- const [start, end] = Range.edges(selection);
1038
- const startVoid = Editor.void(e, { at: start.path });
1039
- const endVoid = Editor.void(e, { at: end.path });
1040
- if (Range.isCollapsed(selection) && !startVoid) {
1041
- return;
1042
- }
1043
- // Create a fake selection so that we can add a Base64-encoded copy of the
1044
- // fragment to the HTML, to decode on future pastes.
1045
- let domRange;
1046
- if (AngularEditor.isEnabledVirtualScroll(e)) {
1047
- const virtualScrollSelection = EDITOR_TO_VIRTUAL_SCROLL_SELECTION.get(e);
1048
- if (virtualScrollSelection) {
1049
- domRange = AngularEditor.toDOMRange(e, virtualScrollSelection);
978
+ constructor(doc) {
979
+ this.doc = doc;
980
+ this.state = {
981
+ left: 16,
982
+ top: 16,
983
+ collapsed: false,
984
+ width: VirtualScrollDebugOverlay.defaultWidth,
985
+ height: VirtualScrollDebugOverlay.defaultHeight
986
+ };
987
+ this.originalConsoleLog = console.log.bind(console);
988
+ this.originalConsoleWarn = console.warn.bind(console);
989
+ this.dragOffsetX = 0;
990
+ this.dragOffsetY = 0;
991
+ this.isDragging = false;
992
+ this.isResizing = false;
993
+ this.resizeStartX = 0;
994
+ this.resizeStartY = 0;
995
+ this.resizeStartWidth = 0;
996
+ this.resizeStartHeight = 0;
997
+ this.dragMoved = false;
998
+ this.wasDragged = false;
999
+ this.onDragging = (event) => {
1000
+ if (!this.isDragging || !this.container) {
1001
+ return;
1050
1002
  }
1051
- }
1052
- domRange = domRange ?? AngularEditor.toDOMRange(e, selection);
1053
- let contents = domRange.cloneContents();
1054
- let attach = contents.childNodes[0];
1055
- // Make sure attach is non-empty, since empty nodes will not get copied.
1056
- const contentsArray = Array.from(contents.children);
1057
- contentsArray.forEach(node => {
1058
- if (node.textContent && node.textContent.trim() !== '') {
1059
- attach = node;
1003
+ this.dragMoved = true;
1004
+ const nextLeft = event.clientX - this.dragOffsetX;
1005
+ const nextTop = event.clientY - this.dragOffsetY;
1006
+ this.container.style.left = `${nextLeft}px`;
1007
+ this.container.style.top = `${nextTop}px`;
1008
+ this.container.style.right = 'auto';
1009
+ this.container.style.bottom = 'auto';
1010
+ };
1011
+ this.onDragEnd = () => {
1012
+ if (!this.isDragging) {
1013
+ return;
1060
1014
  }
1061
- });
1062
- // COMPAT: If the end node is a void node, we need to move the end of the
1063
- // range from the void node's spacer span, to the end of the void node's
1064
- // content, since the spacer is before void's content in the DOM.
1065
- if (endVoid) {
1066
- const [voidNode] = endVoid;
1067
- const r = domRange.cloneRange();
1068
- const domNode = AngularEditor.toDOMNode(e, voidNode);
1069
- r.setEndAfter(domNode);
1070
- contents = r.cloneContents();
1071
- }
1072
- // COMPAT: If the start node is a void node, we need to attach the encoded
1073
- // fragment to the void node's content node instead of the spacer, because
1074
- // attaching it to empty `<div>/<span>` nodes will end up having it erased by
1075
- // most browsers. (2018/04/27)
1076
- if (startVoid) {
1077
- attach = contents.querySelector('[data-slate-spacer]');
1078
- }
1079
- // Remove any zero-width space spans from the cloned DOM so that they don't
1080
- // show up elsewhere when pasted.
1081
- Array.from(contents.querySelectorAll('[data-slate-zero-width]')).forEach(zw => {
1082
- const isNewline = zw.getAttribute('data-slate-zero-width') === 'n';
1083
- zw.textContent = isNewline ? '\n' : '';
1084
- });
1085
- // Set a `data-slate-fragment` attribute on a non-empty node, so it shows up
1086
- // in the HTML, and can be used for intra-Slate pasting. If it's a text
1087
- // node, wrap it in a `<span>` so we have something to set an attribute on.
1088
- if (isDOMText(attach)) {
1089
- const span = attach.ownerDocument.createElement('span');
1090
- // COMPAT: In Chrome and Safari, if we don't add the `white-space` style
1091
- // then leading and trailing spaces will be ignored. (2017/09/21)
1092
- span.style.whiteSpace = 'pre';
1093
- span.appendChild(attach);
1094
- contents.appendChild(span);
1095
- attach = span;
1096
- }
1097
- const fragment = e.getFragment();
1098
- // Add the content to a <div> so that we can get its inner HTML.
1099
- const div = contents.ownerDocument.createElement('div');
1100
- const attachWrapper = document.createElement('div');
1101
- const elements = Array.from(contents.children);
1102
- if (isInvalidTable(elements)) {
1103
- contents = completeTable(contents.cloneNode(true));
1104
- }
1105
- attachWrapper.appendChild(contents);
1106
- div.appendChild(attachWrapper);
1107
- div.setAttribute('hidden', 'true');
1108
- contents.ownerDocument.body.appendChild(div);
1109
- setClipboardData({ text: getPlainText(div), elements: fragment }, div, attachWrapper, dataTransfer);
1110
- contents.ownerDocument.body.removeChild(div);
1111
- };
1112
- e.deleteCutData = () => {
1113
- const { selection } = editor;
1114
- if (selection) {
1115
- if (Range.isExpanded(selection)) {
1116
- Editor.deleteFragment(editor);
1015
+ this.isDragging = false;
1016
+ this.wasDragged = this.dragMoved;
1017
+ this.dragMoved = false;
1018
+ this.doc.removeEventListener('mousemove', this.onDragging);
1019
+ this.doc.removeEventListener('mouseup', this.onDragEnd);
1020
+ if (this.container) {
1021
+ const rect = this.container.getBoundingClientRect();
1022
+ this.state.left = rect.left;
1023
+ this.state.top = rect.top;
1024
+ this.persistState();
1025
+ this.container.style.transition = '';
1117
1026
  }
1118
- else {
1119
- const node = Node.parent(editor, selection.anchor.path);
1120
- if (Element.isElement(node) && Editor.isVoid(editor, node)) {
1121
- Transforms.delete(editor);
1122
- }
1027
+ };
1028
+ this.onResizing = (event) => {
1029
+ if (!this.isResizing || !this.container) {
1030
+ return;
1123
1031
  }
1032
+ const deltaX = event.clientX - this.resizeStartX;
1033
+ const deltaY = event.clientY - this.resizeStartY;
1034
+ const nextWidth = Math.max(VirtualScrollDebugOverlay.minWidth, this.resizeStartWidth + deltaX);
1035
+ const nextHeight = Math.max(VirtualScrollDebugOverlay.minHeight, this.resizeStartHeight + deltaY);
1036
+ this.state.width = nextWidth;
1037
+ this.state.height = nextHeight;
1038
+ this.applySize();
1039
+ };
1040
+ this.onResizeEnd = () => {
1041
+ if (!this.isResizing) {
1042
+ return;
1043
+ }
1044
+ this.isResizing = false;
1045
+ this.doc.removeEventListener('mousemove', this.onResizing);
1046
+ this.doc.removeEventListener('mouseup', this.onResizeEnd);
1047
+ this.persistState();
1048
+ };
1049
+ }
1050
+ init() {
1051
+ if (!this.container) {
1052
+ this.createContainer();
1124
1053
  }
1125
- };
1126
- e.insertData = async (data) => {
1127
- if (!(await e.customInsertFragmentData(data, null))) {
1128
- e.insertTextData(data);
1054
+ else {
1055
+ this.applyState();
1129
1056
  }
1130
- };
1131
- e.customInsertFragmentData = async (data, contextClipboardData) => {
1132
- /**
1133
- * Checking copied fragment from application/x-slate-fragment or data-slate-fragment
1134
- */
1135
- const clipboardData = contextClipboardData || (await getClipboardData(data));
1136
- if (clipboardData && clipboardData.elements) {
1137
- e.insertFragment(clipboardData.elements);
1138
- return true;
1139
- }
1140
- return false;
1141
- };
1142
- e.customInsertTextData = async (data) => {
1143
- const clipboardData = await getClipboardData(data);
1144
- if (clipboardData && clipboardData.text) {
1145
- const lines = clipboardData.text.split(/\r\n|\r|\n/);
1146
- let split = false;
1147
- for (const line of lines) {
1148
- if (split) {
1149
- Transforms.splitNodes(e, { always: true });
1150
- }
1151
- e.insertText(line);
1152
- split = true;
1153
- }
1154
- return true;
1155
- }
1156
- return false;
1157
- };
1158
- e.onKeydown = () => { };
1159
- e.onClick = () => { };
1160
- e.isBlockCard = element => false;
1161
- e.isExpanded = element => true;
1162
- e.onError = (errorData) => {
1163
- if (errorData.nativeError) {
1164
- console.error(errorData.nativeError);
1057
+ }
1058
+ log(type, ...args) {
1059
+ this.init();
1060
+ if (type === 'warn') {
1061
+ this.originalConsoleWarn(...args);
1165
1062
  }
1166
1063
  else {
1167
- console.error(errorData);
1064
+ this.originalConsoleLog(...args);
1168
1065
  }
1169
- };
1170
- // exist issue for move operation in withDOM
1171
- e.apply = (op) => {
1172
- const matches = [];
1173
- switch (op.type) {
1174
- case 'insert_text':
1175
- case 'remove_text':
1176
- case 'set_node': {
1177
- for (const [node, path] of Editor.levels(e, { at: op.path })) {
1178
- const key = AngularEditor.findKey(e, node);
1179
- matches.push([path, key]);
1180
- }
1181
- break;
1066
+ this.appendLog(type, ...args);
1067
+ }
1068
+ dispose() {
1069
+ this.container?.remove();
1070
+ this.container = undefined;
1071
+ this.contentWrapper = undefined;
1072
+ this.bubble = undefined;
1073
+ this.logList = undefined;
1074
+ this.selectorInput = undefined;
1075
+ this.distanceInput = undefined;
1076
+ this.collapseToggle = undefined;
1077
+ this.resizeHandle = undefined;
1078
+ this.doc.removeEventListener('mousemove', this.onDragging);
1079
+ this.doc.removeEventListener('mouseup', this.onDragEnd);
1080
+ this.doc.removeEventListener('mousemove', this.onResizing);
1081
+ this.doc.removeEventListener('mouseup', this.onResizeEnd);
1082
+ this.isDragging = false;
1083
+ this.isResizing = false;
1084
+ }
1085
+ createContainer() {
1086
+ this.loadState();
1087
+ const doc = this.doc;
1088
+ const container = doc.createElement('div');
1089
+ container.style.position = 'fixed';
1090
+ container.style.right = 'auto';
1091
+ container.style.bottom = 'auto';
1092
+ container.style.boxSizing = 'border-box';
1093
+ container.style.background = 'rgba(17, 24, 39, 0.95)';
1094
+ container.style.color = '#e5e7eb';
1095
+ container.style.fontSize = '12px';
1096
+ container.style.border = '1px solid #1f2937';
1097
+ container.style.borderRadius = '10px';
1098
+ container.style.fontFamily = 'Menlo, Consolas, monospace';
1099
+ container.style.boxShadow = '0 10px 30px rgba(0, 0, 0, 0.35)';
1100
+ container.style.zIndex = '9999';
1101
+ container.style.display = 'flex';
1102
+ container.style.flexDirection = 'column';
1103
+ container.style.gap = '10px';
1104
+ container.style.minWidth = `${VirtualScrollDebugOverlay.minWidth}px`;
1105
+ container.style.minHeight = `${VirtualScrollDebugOverlay.minHeight}px`;
1106
+ container.style.maxHeight = '80vh';
1107
+ container.style.cursor = 'default';
1108
+ container.addEventListener('mousedown', event => {
1109
+ if (this.state.collapsed) {
1110
+ this.startDrag(event);
1182
1111
  }
1183
- case 'insert_node':
1184
- case 'remove_node':
1185
- case 'merge_node':
1186
- case 'split_node': {
1187
- for (const [node, path] of Editor.levels(e, {
1188
- at: Path.parent(op.path)
1189
- })) {
1190
- const key = AngularEditor.findKey(e, node);
1191
- matches.push([path, key]);
1192
- }
1193
- break;
1112
+ });
1113
+ const header = doc.createElement('div');
1114
+ header.style.display = 'flex';
1115
+ header.style.alignItems = 'center';
1116
+ header.style.justifyContent = 'space-between';
1117
+ header.style.cursor = 'move';
1118
+ header.addEventListener('mousedown', event => {
1119
+ this.startDrag(event);
1120
+ });
1121
+ const title = doc.createElement('div');
1122
+ title.textContent = 'Virtual Scroll Debug';
1123
+ title.style.fontWeight = '600';
1124
+ title.style.letterSpacing = '0.3px';
1125
+ const actions = doc.createElement('div');
1126
+ actions.style.display = 'flex';
1127
+ actions.style.gap = '6px';
1128
+ const collapseButton = doc.createElement('button');
1129
+ collapseButton.type = 'button';
1130
+ collapseButton.textContent = '折叠';
1131
+ collapseButton.style.background = '#1f2937';
1132
+ collapseButton.style.color = '#e5e7eb';
1133
+ collapseButton.style.border = '1px solid #374151';
1134
+ collapseButton.style.borderRadius = '6px';
1135
+ collapseButton.style.padding = '4px 8px';
1136
+ collapseButton.style.cursor = 'pointer';
1137
+ collapseButton.addEventListener('click', () => {
1138
+ this.setCollapsed(!this.state.collapsed);
1139
+ });
1140
+ const clearButton = doc.createElement('button');
1141
+ clearButton.type = 'button';
1142
+ clearButton.textContent = '清除日志';
1143
+ clearButton.style.background = '#374151';
1144
+ clearButton.style.color = '#e5e7eb';
1145
+ clearButton.style.border = '1px solid #4b5563';
1146
+ clearButton.style.borderRadius = '6px';
1147
+ clearButton.style.padding = '4px 10px';
1148
+ clearButton.style.cursor = 'pointer';
1149
+ clearButton.addEventListener('click', () => {
1150
+ if (this.logList) {
1151
+ this.logList.innerHTML = '';
1194
1152
  }
1195
- case 'move_node': {
1196
- const commonPath = Path.common(Path.parent(op.path), Path.parent(op.newPath));
1197
- for (const [node, path] of Editor.levels(e, {
1198
- at: Path.parent(op.path)
1199
- })) {
1200
- const key = AngularEditor.findKey(e, node);
1201
- matches.push([Editor.pathRef(editor, path), key]);
1202
- }
1203
- for (const [node, path] of Editor.levels(e, {
1204
- at: Path.parent(op.newPath)
1205
- })) {
1206
- if (path.length > commonPath.length) {
1207
- const key = AngularEditor.findKey(e, node);
1208
- matches.push([Editor.pathRef(editor, path), key]);
1209
- }
1210
- }
1211
- break;
1153
+ });
1154
+ actions.appendChild(collapseButton);
1155
+ actions.appendChild(clearButton);
1156
+ header.appendChild(title);
1157
+ header.appendChild(actions);
1158
+ const scrollForm = doc.createElement('div');
1159
+ scrollForm.style.display = 'grid';
1160
+ scrollForm.style.gridTemplateColumns = '1fr 110px 50px';
1161
+ scrollForm.style.gap = '6px';
1162
+ scrollForm.style.alignItems = 'center';
1163
+ const selectorInput = doc.createElement('input');
1164
+ selectorInput.placeholder = '滚动容器 selector';
1165
+ selectorInput.style.padding = '6px 8px';
1166
+ selectorInput.style.borderRadius = '6px';
1167
+ selectorInput.style.border = '1px solid #4b5563';
1168
+ selectorInput.style.background = '#111827';
1169
+ selectorInput.style.color = '#e5e7eb';
1170
+ selectorInput.autocomplete = 'off';
1171
+ const distanceInput = doc.createElement('input');
1172
+ distanceInput.placeholder = '滚动距离(px)';
1173
+ distanceInput.type = 'number';
1174
+ distanceInput.style.padding = '6px 8px';
1175
+ distanceInput.style.borderRadius = '6px';
1176
+ distanceInput.style.border = '1px solid #4b5563';
1177
+ distanceInput.style.background = '#111827';
1178
+ distanceInput.style.color = '#e5e7eb';
1179
+ const scrollButton = doc.createElement('button');
1180
+ scrollButton.type = 'button';
1181
+ scrollButton.textContent = '滚动';
1182
+ scrollButton.style.background = '#10b981';
1183
+ scrollButton.style.color = '#0b1c15';
1184
+ scrollButton.style.border = 'none';
1185
+ scrollButton.style.borderRadius = '6px';
1186
+ scrollButton.style.padding = '6px 10px';
1187
+ scrollButton.style.cursor = 'pointer';
1188
+ scrollButton.addEventListener('click', () => {
1189
+ const selector = selectorInput.value.trim();
1190
+ const distanceValue = Number(distanceInput.value ?? 0);
1191
+ const distance = Number.isFinite(distanceValue) ? distanceValue : 0;
1192
+ if (!selector) {
1193
+ this.log('warn', '请先填写滚动容器 selector');
1194
+ return;
1212
1195
  }
1213
- }
1214
- apply(op);
1215
- for (const [source, key] of matches) {
1216
- const [node] = Editor.node(e, Path.isPath(source) ? source : source.current);
1217
- NODE_TO_KEY.set(node, key);
1218
- }
1219
- };
1220
- e.selectAll = () => {
1221
- Transforms.select(e, []);
1222
- };
1223
- e.isVisible = element => {
1224
- return true;
1225
- };
1226
- return e;
1227
- };
1228
-
1229
- const TOP_BLUR = 'blur';
1230
- const TOP_COMPOSITION_END = 'compositionend';
1231
- const TOP_COMPOSITION_START = 'compositionstart';
1232
- const TOP_COMPOSITION_UPDATE = 'compositionupdate';
1233
- const TOP_KEY_DOWN = 'keydown';
1234
- const TOP_KEY_PRESS = 'keypress';
1235
- const TOP_KEY_UP = 'keyup';
1236
- const TOP_MOUSE_DOWN = 'mousedown';
1237
- const TOP_MOUSE_MOVE = 'mousemove';
1238
- const TOP_MOUSE_OUT = 'mouseout';
1239
- const TOP_TEXT_INPUT = 'textInput';
1240
- const TOP_PASTE = 'paste';
1241
-
1242
- /**
1243
- * Copyright (c) Facebook, Inc. and its affiliates.
1244
- *
1245
- * This source code is licensed under the MIT license found in the
1246
- * LICENSE file in the root directory of this source tree.
1247
- */
1248
- /**
1249
- * These variables store information about text content of a target node,
1250
- * allowing comparison of content before and after a given event.
1251
- *
1252
- * Identify the node where selection currently begins, then observe
1253
- * both its text content and its current position in the DOM. Since the
1254
- * browser may natively replace the target node during composition, we can
1255
- * use its position to find its replacement.
1256
- *
1257
- *
1258
- */
1259
- let root = null;
1260
- let startText = null;
1261
- let fallbackText = null;
1262
- function initialize(nativeEventTarget) {
1263
- root = nativeEventTarget;
1264
- startText = getText();
1265
- return true;
1266
- }
1267
- function reset() {
1268
- root = null;
1269
- startText = null;
1270
- fallbackText = null;
1271
- }
1272
- function getData() {
1273
- if (fallbackText) {
1274
- return fallbackText;
1196
+ const target = doc.querySelector(selector);
1197
+ if (!target) {
1198
+ this.log('warn', `未找到滚动容器: ${selector}`);
1199
+ return;
1200
+ }
1201
+ if (typeof target.scrollTo === 'function') {
1202
+ target.scrollTo({ top: distance, behavior: 'auto' });
1203
+ }
1204
+ else if (Object.prototype.hasOwnProperty.call(target, 'scrollTop')) {
1205
+ target.scrollTop = distance;
1206
+ }
1207
+ else {
1208
+ this.log('warn', '目标元素不支持滚动:', selector);
1209
+ return;
1210
+ }
1211
+ this.log('log', `已将 ${selector} 滚动到`, distance);
1212
+ });
1213
+ scrollForm.appendChild(selectorInput);
1214
+ scrollForm.appendChild(distanceInput);
1215
+ scrollForm.appendChild(scrollButton);
1216
+ const logList = doc.createElement('div');
1217
+ logList.style.height = '260px';
1218
+ logList.style.overflowY = 'auto';
1219
+ logList.style.background = '#0b1220';
1220
+ logList.style.border = '1px solid #1f2937';
1221
+ logList.style.borderRadius = '8px';
1222
+ logList.style.padding = '8px';
1223
+ logList.style.display = 'flex';
1224
+ logList.style.flexDirection = 'column';
1225
+ logList.style.gap = '6px';
1226
+ logList.style.flex = '1';
1227
+ logList.style.minHeight = '160px';
1228
+ const bubble = doc.createElement('div');
1229
+ bubble.textContent = 'VS';
1230
+ bubble.style.display = 'none';
1231
+ bubble.style.alignItems = 'center';
1232
+ bubble.style.justifyContent = 'center';
1233
+ bubble.style.fontWeight = '700';
1234
+ bubble.style.fontSize = '14px';
1235
+ bubble.style.letterSpacing = '0.5px';
1236
+ bubble.style.width = '100%';
1237
+ bubble.style.height = '100%';
1238
+ bubble.style.userSelect = 'none';
1239
+ bubble.addEventListener('click', () => {
1240
+ if (this.wasDragged) {
1241
+ this.wasDragged = false;
1242
+ return;
1243
+ }
1244
+ this.setCollapsed(false);
1245
+ });
1246
+ const contentWrapper = doc.createElement('div');
1247
+ contentWrapper.style.display = 'flex';
1248
+ contentWrapper.style.flexDirection = 'column';
1249
+ contentWrapper.style.gap = '10px';
1250
+ contentWrapper.style.width = '100%';
1251
+ contentWrapper.style.height = '100%';
1252
+ contentWrapper.appendChild(header);
1253
+ contentWrapper.appendChild(scrollForm);
1254
+ contentWrapper.appendChild(logList);
1255
+ container.appendChild(contentWrapper);
1256
+ container.appendChild(bubble);
1257
+ const resizeHandle = doc.createElement('div');
1258
+ resizeHandle.style.position = 'absolute';
1259
+ resizeHandle.style.right = '6px';
1260
+ resizeHandle.style.bottom = '6px';
1261
+ resizeHandle.style.width = '14px';
1262
+ resizeHandle.style.height = '14px';
1263
+ resizeHandle.style.cursor = 'nwse-resize';
1264
+ resizeHandle.style.borderRight = '2px solid #4b5563';
1265
+ resizeHandle.style.borderBottom = '2px solid #4b5563';
1266
+ resizeHandle.addEventListener('mousedown', event => {
1267
+ this.startResize(event);
1268
+ });
1269
+ container.appendChild(resizeHandle);
1270
+ doc.body.appendChild(container);
1271
+ this.container = container;
1272
+ this.contentWrapper = contentWrapper;
1273
+ this.bubble = bubble;
1274
+ this.logList = logList;
1275
+ this.selectorInput = selectorInput;
1276
+ this.distanceInput = distanceInput;
1277
+ this.collapseToggle = collapseButton;
1278
+ this.resizeHandle = resizeHandle;
1279
+ this.applyState();
1275
1280
  }
1276
- let start;
1277
- const startValue = startText;
1278
- const startLength = startValue.length;
1279
- let end;
1280
- const endValue = getText();
1281
- const endLength = endValue.length;
1282
- for (start = 0; start < startLength; start++) {
1283
- if (startValue[start] !== endValue[start]) {
1284
- break;
1281
+ startDrag(event) {
1282
+ if (event.button !== 0) {
1283
+ return;
1285
1284
  }
1286
- }
1287
- const minEnd = startLength - start;
1288
- for (end = 1; end <= minEnd; end++) {
1289
- if (startValue[startLength - end] !== endValue[endLength - end]) {
1290
- break;
1285
+ if (!this.container) {
1286
+ return;
1287
+ }
1288
+ const rect = this.container.getBoundingClientRect();
1289
+ this.isDragging = true;
1290
+ this.wasDragged = false;
1291
+ this.dragMoved = false;
1292
+ this.dragOffsetX = event.clientX - rect.left;
1293
+ this.dragOffsetY = event.clientY - rect.top;
1294
+ this.container.style.transition = 'none';
1295
+ this.doc.addEventListener('mousemove', this.onDragging);
1296
+ this.doc.addEventListener('mouseup', this.onDragEnd);
1297
+ if (!this.state.collapsed) {
1298
+ event.preventDefault();
1291
1299
  }
1292
1300
  }
1293
- const sliceTail = end > 1 ? 1 - end : undefined;
1294
- fallbackText = endValue.slice(start, sliceTail);
1295
- return fallbackText;
1296
- }
1297
- function getText() {
1298
- if ('value' in root) {
1299
- return root.value;
1300
- }
1301
- return root.textContent;
1302
- }
1303
-
1304
- /**
1305
- * Copyright (c) Facebook, Inc. and its affiliates.
1306
- *
1307
- * This source code is licensed under the MIT license found in the
1308
- * LICENSE file in the root directory of this source tree.
1309
- */
1310
- const canUseDOM = !!(typeof window !== 'undefined' &&
1311
- typeof window.document !== 'undefined' &&
1312
- typeof window.document.createElement !== 'undefined');
1313
- const END_KEYCODES = [9, 13, 27, 32]; // Tab, Return, Esc, Space
1314
- const START_KEYCODE = 229;
1315
- const canUseCompositionEvent = canUseDOM && 'CompositionEvent' in window;
1316
- let documentMode = null;
1317
- if (canUseDOM && 'documentMode' in document) {
1318
- documentMode = document.documentMode;
1319
- }
1320
- // Webkit offers a very useful `textInput` event that can be used to
1321
- // directly represent `beforeInput`. The IE `textinput` event is not as
1322
- // useful, so we don't use it.
1323
- const canUseTextInputEvent = canUseDOM && 'TextEvent' in window && !documentMode;
1324
- // In IE9+, we have access to composition events, but the data supplied
1325
- // by the native compositionend event may be incorrect. Japanese ideographic
1326
- // spaces, for instance (\u3000) are not recorded correctly.
1327
- const useFallbackCompositionData = canUseDOM && (!canUseCompositionEvent || (documentMode && documentMode > 8 && documentMode <= 11));
1328
- const SPACEBAR_CODE = 32;
1329
- const SPACEBAR_CHAR = String.fromCharCode(SPACEBAR_CODE);
1330
- // Events and their corresponding property names.
1331
- const eventTypes = {
1332
- beforeInput: {
1333
- phasedRegistrationNames: {
1334
- bubbled: 'onBeforeInput',
1335
- captured: 'onBeforeInputCapture'
1336
- },
1337
- dependencies: [TOP_COMPOSITION_END, TOP_KEY_PRESS, TOP_TEXT_INPUT, TOP_PASTE]
1301
+ startResize(event) {
1302
+ if (event.button !== 0 || this.state.collapsed) {
1303
+ return;
1304
+ }
1305
+ if (!this.container) {
1306
+ return;
1307
+ }
1308
+ const rect = this.container.getBoundingClientRect();
1309
+ this.isResizing = true;
1310
+ this.resizeStartX = event.clientX;
1311
+ this.resizeStartY = event.clientY;
1312
+ this.resizeStartWidth = rect.width;
1313
+ this.resizeStartHeight = rect.height;
1314
+ this.doc.addEventListener('mousemove', this.onResizing);
1315
+ this.doc.addEventListener('mouseup', this.onResizeEnd);
1316
+ event.preventDefault();
1317
+ event.stopPropagation();
1338
1318
  }
1339
- };
1340
- // Track whether we've ever handled a keypress on the space key.
1341
- let hasSpaceKeypress = false;
1342
- /**
1343
- * Return whether a native keypress event is assumed to be a command.
1344
- * This is required because Firefox fires `keypress` events for key commands
1345
- * (cut, copy, select-all, etc.) even though no character is inserted.
1346
- */
1347
- function isKeypressCommand(nativeEvent) {
1348
- return ((nativeEvent.ctrlKey || nativeEvent.altKey || nativeEvent.metaKey) &&
1349
- // ctrlKey && altKey is equivalent to AltGr, and is not a command.
1350
- !(nativeEvent.ctrlKey && nativeEvent.altKey));
1351
- }
1352
- /**
1353
- * Does our fallback mode think that this event is the end of composition?
1354
- *
1355
- */
1356
- function isFallbackCompositionEnd(topLevelType, nativeEvent) {
1357
- switch (topLevelType) {
1358
- case TOP_KEY_UP:
1359
- // Command keys insert or clear IME input.
1360
- return END_KEYCODES.indexOf(nativeEvent.keyCode) !== -1;
1361
- case TOP_KEY_DOWN:
1362
- // Expect IME keyCode on each keydown. If we get any other
1363
- // code we must have exited earlier.
1364
- return nativeEvent.keyCode !== START_KEYCODE;
1365
- case TOP_KEY_PRESS:
1366
- case TOP_MOUSE_DOWN:
1367
- case TOP_BLUR:
1368
- // Events are not possible without cancelling IME.
1369
- return true;
1370
- default:
1371
- return false;
1319
+ applyPosition() {
1320
+ if (!this.container) {
1321
+ return;
1322
+ }
1323
+ this.container.style.left = `${this.state.left}px`;
1324
+ this.container.style.top = `${this.state.top}px`;
1372
1325
  }
1373
- }
1374
- /**
1375
- * Google Input Tools provides composition data via a CustomEvent,
1376
- * with the `data` property populated in the `detail` object. If this
1377
- * is available on the event object, use it. If not, this is a plain
1378
- * composition event and we have nothing special to extract.
1379
- *
1380
- */
1381
- function getDataFromCustomEvent(nativeEvent) {
1382
- const detail = nativeEvent.detail;
1383
- if (typeof detail === 'object' && 'data' in detail) {
1384
- return detail.data;
1326
+ applySize() {
1327
+ if (!this.container) {
1328
+ return;
1329
+ }
1330
+ const width = Math.max(VirtualScrollDebugOverlay.minWidth, this.state.width || VirtualScrollDebugOverlay.defaultWidth);
1331
+ const height = Math.max(VirtualScrollDebugOverlay.minHeight, this.state.height || VirtualScrollDebugOverlay.defaultHeight);
1332
+ this.state.width = width;
1333
+ this.state.height = height;
1334
+ this.container.style.width = `${width}px`;
1335
+ this.container.style.height = `${height}px`;
1385
1336
  }
1386
- return null;
1387
- }
1388
- /**
1389
- * Check if a composition event was triggered by Korean IME.
1390
- * Our fallback mode does not work well with IE's Korean IME,
1391
- * so just use native composition events when Korean IME is used.
1392
- * Although CompositionEvent.locale property is deprecated,
1393
- * it is available in IE, where our fallback mode is enabled.
1394
- *
1395
- */
1396
- function isUsingKoreanIME(nativeEvent) {
1397
- return nativeEvent.locale === 'ko';
1398
- }
1399
- // Track the current IME composition status, if any.
1400
- let isComposing = false;
1401
- function getNativeBeforeInputChars(topLevelType, nativeEvent) {
1402
- switch (topLevelType) {
1403
- case TOP_COMPOSITION_END:
1404
- return getDataFromCustomEvent(nativeEvent);
1405
- case TOP_KEY_PRESS:
1406
- /**
1407
- * If native `textInput` events are available, our goal is to make
1408
- * use of them. However, there is a special case: the spacebar key.
1409
- * In Webkit, preventing default on a spacebar `textInput` event
1410
- * cancels character insertion, but it *also* causes the browser
1411
- * to fall back to its default spacebar behavior of scrolling the
1412
- * page.
1413
- *
1414
- * Tracking at:
1415
- * https://code.google.com/p/chromium/issues/detail?id=355103
1416
- *
1417
- * To avoid this issue, use the keypress event as if no `textInput`
1418
- * event is available.
1419
- */
1420
- const which = nativeEvent.which;
1421
- if (which !== SPACEBAR_CODE) {
1422
- return null;
1423
- }
1424
- hasSpaceKeypress = true;
1425
- return SPACEBAR_CHAR;
1426
- case TOP_TEXT_INPUT:
1427
- // Record the characters to be added to the DOM.
1428
- const chars = nativeEvent.data;
1429
- // If it's a spacebar character, assume that we have already handled
1430
- // it at the keypress level and bail immediately. Android Chrome
1431
- // doesn't give us keycodes, so we need to ignore it.
1432
- if (chars === SPACEBAR_CHAR && hasSpaceKeypress) {
1433
- return null;
1434
- }
1435
- return chars;
1436
- default:
1437
- // For other native event types, do nothing.
1438
- return null;
1439
- }
1440
- }
1441
- /**
1442
- * For browsers that do not provide the `textInput` event, extract the
1443
- * appropriate string to use for SyntheticInputEvent.
1444
- *
1445
- */
1446
- function getFallbackBeforeInputChars(topLevelType, nativeEvent) {
1447
- // If we are currently composing (IME) and using a fallback to do so,
1448
- // try to extract the composed characters from the fallback object.
1449
- // If composition event is available, we extract a string only at
1450
- // compositionevent, otherwise extract it at fallback events.
1451
- if (isComposing) {
1452
- if (topLevelType === TOP_COMPOSITION_END || (!canUseCompositionEvent && isFallbackCompositionEnd(topLevelType, nativeEvent))) {
1453
- const chars = getData();
1454
- reset();
1455
- isComposing = false;
1456
- return chars;
1337
+ applyCollapsedState() {
1338
+ if (!this.container || !this.contentWrapper || !this.bubble || !this.collapseToggle) {
1339
+ return;
1340
+ }
1341
+ if (this.state.collapsed) {
1342
+ this.container.style.width = '36px';
1343
+ this.container.style.height = '36px';
1344
+ this.container.style.minWidth = '';
1345
+ this.container.style.minHeight = '';
1346
+ this.container.style.padding = '0';
1347
+ this.container.style.borderRadius = '50%';
1348
+ this.container.style.display = 'flex';
1349
+ this.container.style.flexDirection = 'row';
1350
+ this.container.style.alignItems = 'center';
1351
+ this.container.style.justifyContent = 'center';
1352
+ this.container.style.cursor = 'move';
1353
+ this.contentWrapper.style.display = 'none';
1354
+ this.bubble.style.display = 'flex';
1355
+ this.collapseToggle.textContent = '展开';
1356
+ this.resizeHandle.style.display = 'none';
1357
+ }
1358
+ else {
1359
+ this.applySize();
1360
+ this.container.style.padding = '12px';
1361
+ this.container.style.borderRadius = '10px';
1362
+ this.container.style.display = 'flex';
1363
+ this.container.style.flexDirection = 'column';
1364
+ this.container.style.gap = '10px';
1365
+ this.container.style.cursor = 'default';
1366
+ this.contentWrapper.style.display = 'flex';
1367
+ this.bubble.style.display = 'none';
1368
+ this.collapseToggle.textContent = '折叠';
1369
+ this.resizeHandle.style.display = 'block';
1457
1370
  }
1458
- return null;
1459
1371
  }
1460
- switch (topLevelType) {
1461
- case TOP_PASTE:
1462
- // If a paste event occurs after a keypress, throw out the input
1463
- // chars. Paste events should not lead to BeforeInput events.
1464
- return null;
1465
- case TOP_KEY_PRESS:
1466
- /**
1467
- * As of v27, Firefox may fire keypress events even when no character
1468
- * will be inserted. A few possibilities:
1469
- *
1470
- * - `which` is `0`. Arrow keys, Esc key, etc.
1471
- *
1472
- * - `which` is the pressed key code, but no char is available.
1473
- * Ex: 'AltGr + d` in Polish. There is no modified character for
1474
- * this key combination and no character is inserted into the
1475
- * document, but FF fires the keypress for char code `100` anyway.
1476
- * No `input` event will occur.
1477
- *
1478
- * - `which` is the pressed key code, but a command combination is
1479
- * being used. Ex: `Cmd+C`. No character is inserted, and no
1480
- * `input` event will occur.
1481
- */
1482
- if (!isKeypressCommand(nativeEvent)) {
1483
- // IE fires the `keypress` event when a user types an emoji via
1484
- // Touch keyboard of Windows. In such a case, the `char` property
1485
- // holds an emoji character like `\uD83D\uDE0A`. Because its length
1486
- // is 2, the property `which` does not represent an emoji correctly.
1487
- // In such a case, we directly return the `char` property instead of
1488
- // using `which`.
1489
- if (nativeEvent.char && nativeEvent.char.length > 1) {
1490
- return nativeEvent.char;
1372
+ setCollapsed(collapsed) {
1373
+ this.state.collapsed = collapsed;
1374
+ this.applyCollapsedState();
1375
+ this.persistState();
1376
+ }
1377
+ applyState() {
1378
+ this.applyPosition();
1379
+ this.applyCollapsedState();
1380
+ }
1381
+ loadState() {
1382
+ try {
1383
+ const raw = this.doc.defaultView?.localStorage?.getItem(VirtualScrollDebugOverlay.storageKey);
1384
+ if (raw) {
1385
+ const parsed = JSON.parse(raw);
1386
+ if (typeof parsed.left === 'number') {
1387
+ this.state.left = parsed.left;
1491
1388
  }
1492
- else if (nativeEvent.which) {
1493
- return String.fromCharCode(nativeEvent.which);
1389
+ if (typeof parsed.top === 'number') {
1390
+ this.state.top = parsed.top;
1391
+ }
1392
+ if (typeof parsed.collapsed === 'boolean') {
1393
+ this.state.collapsed = parsed.collapsed;
1394
+ }
1395
+ if (typeof parsed.width === 'number') {
1396
+ this.state.width = parsed.width;
1397
+ }
1398
+ if (typeof parsed.height === 'number') {
1399
+ this.state.height = parsed.height;
1494
1400
  }
1495
1401
  }
1496
- return null;
1497
- case TOP_COMPOSITION_END:
1498
- return useFallbackCompositionData && !isUsingKoreanIME(nativeEvent) ? null : nativeEvent.data;
1499
- default:
1500
- return null;
1402
+ }
1403
+ catch {
1404
+ // ignore storage errors
1405
+ }
1501
1406
  }
1502
- }
1503
- /**
1504
- * Extract a SyntheticInputEvent for `beforeInput`, based on either native
1505
- * `textInput` or fallback behavior.
1506
- *
1507
- */
1508
- function extractBeforeInputEvent(topLevelType, targetInst, nativeEvent, nativeEventTarget) {
1509
- let chars;
1510
- if (canUseTextInputEvent) {
1511
- chars = getNativeBeforeInputChars(topLevelType, nativeEvent);
1407
+ persistState() {
1408
+ try {
1409
+ this.doc.defaultView?.localStorage?.setItem(VirtualScrollDebugOverlay.storageKey, JSON.stringify(this.state));
1410
+ }
1411
+ catch {
1412
+ // ignore storage errors
1413
+ }
1512
1414
  }
1513
- else {
1514
- chars = getFallbackBeforeInputChars(topLevelType, nativeEvent);
1415
+ appendLog(type, ...args) {
1416
+ if (!this.logList) {
1417
+ return;
1418
+ }
1419
+ const item = this.doc.createElement('div');
1420
+ item.style.display = 'flex';
1421
+ item.style.gap = '6px';
1422
+ item.style.alignItems = 'flex-start';
1423
+ item.style.wordBreak = 'break-all';
1424
+ item.style.color = type === 'warn' ? '#fbbf24' : '#9ca3af';
1425
+ const time = this.doc.createElement('span');
1426
+ time.textContent = new Date().toLocaleTimeString();
1427
+ time.style.color = '#6b7280';
1428
+ time.style.flexShrink = '0';
1429
+ time.style.width = '72px';
1430
+ const text = this.doc.createElement('span');
1431
+ text.textContent = `[${type}] ${args.map(arg => this.formatValue(arg)).join(' ')}`;
1432
+ item.appendChild(time);
1433
+ item.appendChild(text);
1434
+ this.logList.appendChild(item);
1435
+ this.logList.scrollTop = this.logList.scrollHeight;
1515
1436
  }
1516
- // If no characters are being inserted, no BeforeInput event should
1517
- // be fired.
1518
- if (!chars) {
1519
- return null;
1437
+ formatValue(value) {
1438
+ if (typeof value === 'string') {
1439
+ return value;
1440
+ }
1441
+ try {
1442
+ return JSON.stringify(value);
1443
+ }
1444
+ catch (error) {
1445
+ return String(value);
1446
+ }
1520
1447
  }
1521
- const beforeInputEvent = new BeforeInputEvent();
1522
- beforeInputEvent.data = chars;
1523
- beforeInputEvent.nativeEvent = nativeEvent;
1524
- return beforeInputEvent;
1525
- }
1526
- /**
1527
- * Create an `onBeforeInput` event to match
1528
- * http://www.w3.org/TR/2013/WD-DOM-Level-3-Events-20131105/#events-inputevents.
1529
- *
1530
- * This event plugin is based on the native `textInput` event
1531
- * available in Chrome, Safari, Opera, and IE. This event fires after
1532
- * `onKeyPress` and `onCompositionEnd`, but before `onInput`.
1533
- *
1534
- * `beforeInput` is spec'd but not implemented in any browsers, and
1535
- * the `input` event does not provide any useful information about what has
1536
- * actually been added, contrary to the spec. Thus, `textInput` is the best
1537
- * available event to identify the characters that have actually been inserted
1538
- * into the target node.
1539
- *
1540
- * This plugin is also responsible for emitting `composition` events, thus
1541
- * allowing us to share composition fallback code for both `beforeInput` and
1542
- * `composition` event types.
1543
- */
1544
- const BeforeInputEventPlugin = {
1545
- extractEvents: (topLevelType, targetInst, nativeEvent, nativeEventTarget) => {
1546
- return extractBeforeInputEvent(topLevelType, targetInst, nativeEvent, nativeEventTarget);
1448
+ setScrollTopValue(value) {
1449
+ if (this.distanceInput) {
1450
+ this.distanceInput.value = String(value ?? 0);
1451
+ }
1547
1452
  }
1548
- };
1549
- class BeforeInputEvent {
1550
1453
  }
1551
1454
 
1552
- const BEFORE_INPUT_EVENTS = [
1553
- // { name: 'blur', handler: 'onBlur', isTriggerBeforeInput: true },
1554
- // { name: 'compositionstart', handler: 'onCompositionStart', isTriggerBeforeInput: true },
1555
- { name: 'compositionupdate', handler: null, isTriggerBeforeInput: true },
1556
- // { name: 'compositionend', handler: 'onCompositionEnd', isTriggerBeforeInput: false },
1557
- // { name: 'keydown', handler: 'onKeyDown', isTriggerBeforeInput: true },
1558
- { name: 'keypress', handler: null, isTriggerBeforeInput: true },
1559
- { name: 'keyup', handler: 'onKeyUp', isTriggerBeforeInput: true },
1560
- { name: 'mousedown', handler: 'onMouseDown', isTriggerBeforeInput: true },
1561
- { name: 'textInput', handler: null, isTriggerBeforeInput: true }
1562
- // { name: 'paste', handler: 'onPaste', isTriggerBeforeInput: true }
1563
- ];
1564
-
1565
- var SlateErrorCode;
1566
- (function (SlateErrorCode) {
1567
- SlateErrorCode[SlateErrorCode["ToNativeSelectionError"] = 2100] = "ToNativeSelectionError";
1568
- SlateErrorCode[SlateErrorCode["ToSlateSelectionError"] = 2101] = "ToSlateSelectionError";
1569
- SlateErrorCode[SlateErrorCode["OnDOMBeforeInputError"] = 2102] = "OnDOMBeforeInputError";
1570
- SlateErrorCode[SlateErrorCode["OnSyntheticBeforeInputError"] = 2103] = "OnSyntheticBeforeInputError";
1571
- SlateErrorCode[SlateErrorCode["OnDOMKeydownError"] = 2104] = "OnDOMKeydownError";
1572
- SlateErrorCode[SlateErrorCode["GetStartPointError"] = 2105] = "GetStartPointError";
1573
- SlateErrorCode[SlateErrorCode["NotFoundPreviousRootNodeError"] = 3100] = "NotFoundPreviousRootNodeError";
1574
- SlateErrorCode[SlateErrorCode["InvalidValueError"] = 4100] = "InvalidValueError";
1575
- })(SlateErrorCode || (SlateErrorCode = {}));
1576
-
1577
- function restoreDom(editor, execute) {
1578
- const editable = EDITOR_TO_ELEMENT.get(editor);
1579
- let observer = new MutationObserver(mutations => {
1580
- mutations.reverse().forEach(mutation => {
1581
- if (mutation.type === 'characterData') {
1582
- // We don't want to restore the DOM for characterData mutations
1583
- // because this interrupts the composition.
1584
- return;
1455
+ const isDebug = localStorage.getItem(SLATE_DEBUG_KEY) === 'true';
1456
+ const isDebugScrollTop = localStorage.getItem(SLATE_DEBUG_KEY_SCROLL_TOP) === 'true';
1457
+ const ELEMENT_KEY_TO_HEIGHTS = new WeakMap();
1458
+ const EDITOR_TO_BUSINESS_TOP = new WeakMap();
1459
+ const debugLog = (type, ...args) => {
1460
+ const doc = document;
1461
+ VirtualScrollDebugOverlay.log(doc, type, ...args);
1462
+ };
1463
+ const measureHeightByElement = (editor, element) => {
1464
+ const key = AngularEditor.findKey(editor, element);
1465
+ const view = ELEMENT_TO_COMPONENT.get(element);
1466
+ if (!view) {
1467
+ return;
1468
+ }
1469
+ const ret = view.getRealHeight();
1470
+ const heights = ELEMENT_KEY_TO_HEIGHTS.get(editor);
1471
+ heights.set(key.id, ret);
1472
+ return ret;
1473
+ };
1474
+ const measureHeightByIndics = (editor, indics, force = false) => {
1475
+ let hasChanged = false;
1476
+ indics.forEach((index, i) => {
1477
+ const element = editor.children[index];
1478
+ const preHeight = getRealHeightByElement(editor, element, 0);
1479
+ if (preHeight && !force) {
1480
+ if (isDebug) {
1481
+ const height = measureHeightByElement(editor, element);
1482
+ if (height !== preHeight) {
1483
+ debugLog('warn', 'measureHeightByElement: height not equal, index: ', index, 'preHeight: ', preHeight, 'height: ', height);
1484
+ }
1585
1485
  }
1586
- mutation.removedNodes.forEach(node => {
1587
- mutation.target.insertBefore(node, mutation.nextSibling);
1588
- });
1589
- mutation.addedNodes.forEach(node => {
1590
- mutation.target.removeChild(node);
1591
- });
1592
- });
1593
- disconnect();
1594
- execute();
1595
- });
1596
- const disconnect = () => {
1597
- observer.disconnect();
1598
- observer = null;
1599
- };
1600
- observer.observe(editable, { subtree: true, childList: true, characterData: true, characterDataOldValue: true });
1601
- setTimeout(() => {
1602
- if (observer) {
1603
- disconnect();
1604
- execute();
1486
+ return;
1605
1487
  }
1606
- }, 0);
1607
- }
1608
-
1609
- class FlavourRef {
1610
- destroy() {
1611
- this.instance.onDestroy();
1612
- }
1613
- }
1614
- class BlockCardRef {
1615
- destroy() {
1616
- this.instance.onDestroy();
1488
+ hasChanged = true;
1489
+ measureHeightByElement(editor, element);
1490
+ });
1491
+ return hasChanged;
1492
+ };
1493
+ const getBusinessTop = (editor) => {
1494
+ return EDITOR_TO_BUSINESS_TOP.get(editor) ?? 0;
1495
+ };
1496
+ const getRealHeightByElement = (editor, element, defaultHeight = VIRTUAL_SCROLL_DEFAULT_BLOCK_HEIGHT) => {
1497
+ const isVisible = editor.isVisible(element);
1498
+ if (!isVisible) {
1499
+ return 0;
1617
1500
  }
1618
- }
1619
-
1620
- function createEmbeddedViewOrComponentOrFlavour(viewType, context, viewContext, viewContainerRef) {
1621
- if (isFlavourType(viewType)) {
1622
- const flavourRef = new FlavourRef();
1623
- flavourRef.instance = new viewType();
1624
- flavourRef.instance.context = context;
1625
- flavourRef.instance.viewContext = viewContext;
1626
- flavourRef.instance.viewContainerRef = viewContainerRef;
1627
- flavourRef.instance.onInit();
1628
- return flavourRef;
1501
+ const heights = ELEMENT_KEY_TO_HEIGHTS.get(editor);
1502
+ const key = AngularEditor.findKey(editor, element);
1503
+ const height = heights?.get(key.id);
1504
+ if (typeof height === 'number') {
1505
+ return height;
1629
1506
  }
1630
- if (isTemplateRef(viewType)) {
1631
- const embeddedViewContext = {
1632
- context,
1633
- viewContext
1634
- };
1635
- const embeddedViewRef = viewContainerRef.createEmbeddedView(viewType, embeddedViewContext);
1636
- embeddedViewRef.detectChanges();
1637
- return embeddedViewRef;
1507
+ if (heights?.has(key.id)) {
1508
+ console.error('getBlockHeight: invalid height value', key.id, height);
1638
1509
  }
1639
- if (isComponentType(viewType)) {
1640
- const componentRef = viewContainerRef.createComponent(viewType, {
1641
- injector: viewContainerRef.injector
1642
- });
1643
- componentRef.instance.viewContext = viewContext;
1644
- componentRef.instance.context = context;
1645
- componentRef.changeDetectorRef.detectChanges();
1646
- return componentRef;
1510
+ return defaultHeight;
1511
+ };
1512
+ const buildHeightsAndAccumulatedHeights = (editor) => {
1513
+ const children = (editor.children || []);
1514
+ const heights = new Array(children.length);
1515
+ const accumulatedHeights = new Array(children.length + 1);
1516
+ accumulatedHeights[0] = 0;
1517
+ for (let i = 0; i < children.length; i++) {
1518
+ const height = getRealHeightByElement(editor, children[i]);
1519
+ heights[i] = height;
1520
+ accumulatedHeights[i + 1] = accumulatedHeights[i] + height;
1647
1521
  }
1648
- }
1649
- function updateContext(view, newContext, viewContext) {
1650
- if (view instanceof FlavourRef) {
1651
- view.instance.context = newContext;
1522
+ return { heights, accumulatedHeights };
1523
+ };
1524
+ const scrollToElement = (editor, element, scrollTo) => {
1525
+ const children = editor.children;
1526
+ if (!children.length) {
1652
1527
  return;
1653
1528
  }
1654
- if (view instanceof ComponentRef) {
1655
- view.instance.context = newContext;
1656
- }
1657
- else {
1658
- view.context.context = newContext;
1659
- view.context.viewContext = viewContext;
1660
- view.detectChanges();
1529
+ const anchorIndex = children.findIndex(item => item === element);
1530
+ if (anchorIndex < 0) {
1531
+ return;
1661
1532
  }
1662
- }
1663
- function mount(views, blockCards, outletParent, outletElement) {
1664
- if (views.length > 0) {
1665
- const fragment = document.createDocumentFragment();
1666
- views.forEach((view, index) => {
1667
- const blockCard = blockCards ? blockCards[index] : undefined;
1668
- fragment.append(...getRootNodes(view, blockCard));
1669
- });
1670
- if (outletElement) {
1671
- outletElement.parentElement.insertBefore(fragment, outletElement);
1672
- outletElement.remove();
1533
+ const { accumulatedHeights } = buildHeightsAndAccumulatedHeights(editor);
1534
+ scrollTo((accumulatedHeights[anchorIndex] ?? 0) + getBusinessTop(editor));
1535
+ };
1536
+
1537
+ const withAngular = (editor, clipboardFormatKey = 'x-slate-fragment') => {
1538
+ let e = editor;
1539
+ let { apply } = e;
1540
+ e = withDOM(e, clipboardFormatKey);
1541
+ e.setFragmentData = (dataTransfer, originEvent) => {
1542
+ const { selection } = e;
1543
+ if (!selection) {
1544
+ return;
1673
1545
  }
1674
- else {
1675
- outletParent.prepend(fragment);
1546
+ const [start, end] = Range.edges(selection);
1547
+ const startVoid = Editor.void(e, { at: start.path });
1548
+ const endVoid = Editor.void(e, { at: end.path });
1549
+ if (Range.isCollapsed(selection) && !startVoid) {
1550
+ return;
1676
1551
  }
1677
- }
1678
- }
1679
- function getRootNodes(ref, blockCard) {
1680
- if (blockCard) {
1681
- return [blockCard.instance.nativeElement];
1682
- }
1683
- if (ref instanceof FlavourRef) {
1684
- return [ref.instance.nativeElement];
1685
- }
1686
- if (ref instanceof ComponentRef) {
1687
- ref.hostView.rootNodes.forEach(ele => {
1688
- if (!(ele instanceof HTMLElement)) {
1689
- ele.remove();
1690
- }
1691
- });
1692
- return [ref.instance.nativeElement];
1693
- }
1694
- else {
1695
- const result = [];
1696
- ref.rootNodes.forEach(rootNode => {
1697
- const isHTMLElement = rootNode instanceof HTMLElement;
1698
- const isSlateNodeOfLeaf = isHTMLElement && (rootNode.hasAttribute('data-slate-node') || rootNode.hasAttribute('data-slate-leaf'));
1699
- if (isSlateNodeOfLeaf && result.every(item => !item.contains(rootNode))) {
1700
- result.push(rootNode);
1552
+ // Create a fake selection so that we can add a Base64-encoded copy of the
1553
+ // fragment to the HTML, to decode on future pastes.
1554
+ let domRange;
1555
+ if (AngularEditor.isEnabledVirtualScroll(e)) {
1556
+ const virtualScrollSelection = EDITOR_TO_VIRTUAL_SCROLL_SELECTION.get(e);
1557
+ if (virtualScrollSelection) {
1558
+ domRange = AngularEditor.toDOMRange(e, virtualScrollSelection);
1701
1559
  }
1702
- if (!isHTMLElement) {
1703
- rootNode.remove();
1560
+ }
1561
+ domRange = domRange ?? AngularEditor.toDOMRange(e, selection);
1562
+ let contents = domRange.cloneContents();
1563
+ let attach = contents.childNodes[0];
1564
+ // Make sure attach is non-empty, since empty nodes will not get copied.
1565
+ const contentsArray = Array.from(contents.children);
1566
+ contentsArray.forEach(node => {
1567
+ if (node.textContent && node.textContent.trim() !== '') {
1568
+ attach = node;
1704
1569
  }
1705
1570
  });
1706
- return result;
1707
- }
1708
- }
1709
- function mountOnItemChange(index, item, views, blockCards, outletParent, firstRootNode, viewContext) {
1710
- const view = views[index];
1711
- let rootNodes = getRootNodes(view);
1712
- if (blockCards) {
1713
- const isBlockCard = viewContext.editor.isBlockCard(item);
1714
- if (isBlockCard) {
1715
- const blockCard = blockCards[index];
1716
- rootNodes = [blockCard.instance.nativeElement];
1571
+ // COMPAT: If the end node is a void node, we need to move the end of the
1572
+ // range from the void node's spacer span, to the end of the void node's
1573
+ // content, since the spacer is before void's content in the DOM.
1574
+ if (endVoid) {
1575
+ const [voidNode] = endVoid;
1576
+ const r = domRange.cloneRange();
1577
+ const domNode = AngularEditor.toDOMNode(e, voidNode);
1578
+ r.setEndAfter(domNode);
1579
+ contents = r.cloneContents();
1717
1580
  }
1718
- }
1719
- if (index === 0) {
1720
- if (firstRootNode) {
1721
- rootNodes.forEach(rootNode => {
1722
- firstRootNode.insertAdjacentElement('beforebegin', rootNode);
1723
- });
1724
- }
1725
- else {
1726
- outletParent.prepend(...rootNodes);
1581
+ // COMPAT: If the start node is a void node, we need to attach the encoded
1582
+ // fragment to the void node's content node instead of the spacer, because
1583
+ // attaching it to empty `<div>/<span>` nodes will end up having it erased by
1584
+ // most browsers. (2018/04/27)
1585
+ if (startVoid) {
1586
+ attach = contents.querySelector('[data-slate-spacer]');
1727
1587
  }
1728
- }
1729
- else {
1730
- const previousView = views[index - 1];
1731
- const blockCard = blockCards ? blockCards[index - 1] : null;
1732
- const previousRootNodes = getRootNodes(previousView, blockCard);
1733
- let previousRootNode = previousRootNodes[previousRootNodes.length - 1];
1734
- rootNodes.forEach(rootNode => {
1735
- previousRootNode.insertAdjacentElement('afterend', rootNode);
1736
- previousRootNode = rootNode;
1588
+ // Remove any zero-width space spans from the cloned DOM so that they don't
1589
+ // show up elsewhere when pasted.
1590
+ Array.from(contents.querySelectorAll('[data-slate-zero-width]')).forEach(zw => {
1591
+ const isNewline = zw.getAttribute('data-slate-zero-width') === 'n';
1592
+ zw.textContent = isNewline ? '\n' : '';
1737
1593
  });
1738
- }
1739
- }
1740
-
1741
- function hasBeforeContextChange(value) {
1742
- if (value.beforeContextChange) {
1743
- return true;
1744
- }
1745
- return false;
1746
- }
1747
- function hasAfterContextChange(value) {
1748
- if (value.afterContextChange) {
1749
- return true;
1750
- }
1751
- return false;
1752
- }
1753
-
1754
- class BaseFlavour {
1755
- constructor() {
1756
- this.initialized = false;
1757
- }
1758
- static { this.isFlavour = true; }
1759
- set context(value) {
1760
- if (hasBeforeContextChange(this)) {
1761
- this.beforeContextChange(value);
1594
+ // Set a `data-slate-fragment` attribute on a non-empty node, so it shows up
1595
+ // in the HTML, and can be used for intra-Slate pasting. If it's a text
1596
+ // node, wrap it in a `<span>` so we have something to set an attribute on.
1597
+ if (isDOMText(attach)) {
1598
+ const span = attach.ownerDocument.createElement('span');
1599
+ // COMPAT: In Chrome and Safari, if we don't add the `white-space` style
1600
+ // then leading and trailing spaces will be ignored. (2017/09/21)
1601
+ span.style.whiteSpace = 'pre';
1602
+ span.appendChild(attach);
1603
+ contents.appendChild(span);
1604
+ attach = span;
1762
1605
  }
1763
- this._context = value;
1764
- this.onContextChange();
1765
- if (hasAfterContextChange(this)) {
1766
- this.afterContextChange();
1606
+ const fragment = e.getFragment();
1607
+ // Add the content to a <div> so that we can get its inner HTML.
1608
+ const div = contents.ownerDocument.createElement('div');
1609
+ const attachWrapper = document.createElement('div');
1610
+ const elements = Array.from(contents.children);
1611
+ if (isInvalidTable(elements)) {
1612
+ contents = completeTable(contents.cloneNode(true));
1767
1613
  }
1768
- }
1769
- get context() {
1770
- return this._context;
1771
- }
1772
- get editor() {
1773
- return this.viewContext && this.viewContext.editor;
1774
- }
1775
- }
1776
-
1777
- const SLATE_BLOCK_CARD_CLASS_NAME = 'slate-block-card';
1778
- class SlateBlockCard {
1779
- onInit() {
1780
- const nativeElement = document.createElement('div');
1781
- nativeElement.classList.add(SLATE_BLOCK_CARD_CLASS_NAME);
1782
- this.nativeElement = nativeElement;
1783
- this.createContent();
1784
- }
1785
- createContent() {
1786
- const leftCaret = document.createElement('span');
1787
- leftCaret.setAttribute(`card-target`, 'card-left');
1788
- leftCaret.classList.add('card-left');
1789
- leftCaret.appendChild(getZeroTextNode());
1790
- const rightCaret = document.createElement('span');
1791
- rightCaret.setAttribute(`card-target`, 'card-right');
1792
- rightCaret.classList.add('card-right');
1793
- rightCaret.appendChild(getZeroTextNode());
1794
- const center = document.createElement('div');
1795
- center.setAttribute(`card-target`, 'card-center');
1796
- this.nativeElement.appendChild(leftCaret);
1797
- this.nativeElement.appendChild(center);
1798
- this.nativeElement.appendChild(rightCaret);
1799
- this.centerContainer = center;
1800
- }
1801
- append() {
1802
- this.centerRootNodes.forEach(rootNode => !this.centerContainer.contains(rootNode) && this.centerContainer.appendChild(rootNode));
1803
- }
1804
- initializeCenter(rootNodes) {
1805
- this.centerRootNodes = rootNodes;
1806
- this.append();
1807
- }
1808
- onDestroy() {
1809
- this.nativeElement.remove();
1810
- }
1811
- }
1812
- const getBlockCardByNativeElement = (nativeElement) => {
1813
- const blockCardElement = nativeElement?.parentElement?.parentElement;
1814
- if (blockCardElement && blockCardElement.classList.contains(SLATE_BLOCK_CARD_CLASS_NAME)) {
1815
- return blockCardElement;
1816
- }
1817
- return null;
1818
- };
1819
-
1820
- const DEFAULT_ELEMENT_HEIGHT = 24;
1821
- class BaseElementFlavour extends BaseFlavour {
1822
- constructor() {
1823
- super(...arguments);
1824
- this.getOutletParent = () => {
1825
- return this.nativeElement;
1826
- };
1827
- this.getOutletElement = () => {
1828
- return this.nativeElement.querySelector('.children-outlet');
1829
- };
1830
- this.stableHeight = null;
1831
- }
1832
- get element() {
1833
- return this._context && this._context.element;
1834
- }
1835
- get selection() {
1836
- return this._context && this._context.selection;
1837
- }
1838
- get decorations() {
1839
- return this._context && this._context.decorations;
1840
- }
1841
- get children() {
1842
- return this._context && this._context.element.children;
1843
- }
1844
- get isCollapsed() {
1845
- return this.selection && Range.isCollapsed(this.selection);
1846
- }
1847
- get isCollapsedAndNonReadonly() {
1848
- return this.selection && Range.isCollapsed(this.selection) && !this.readonly;
1849
- }
1850
- get readonly() {
1851
- return this._context && this._context.readonly;
1852
- }
1853
- onInit() {
1854
- this.initialized = true;
1855
- this.render();
1856
- for (const key in this._context.attributes) {
1857
- this.nativeElement.setAttribute(key, this._context.attributes[key]);
1614
+ attachWrapper.appendChild(contents);
1615
+ div.appendChild(attachWrapper);
1616
+ div.setAttribute('hidden', 'true');
1617
+ contents.ownerDocument.body.appendChild(div);
1618
+ setClipboardData({ text: getPlainText(div), elements: fragment }, div, attachWrapper, dataTransfer);
1619
+ contents.ownerDocument.body.removeChild(div);
1620
+ };
1621
+ e.deleteCutData = () => {
1622
+ const { selection } = editor;
1623
+ if (selection) {
1624
+ if (Range.isExpanded(selection)) {
1625
+ Editor.deleteFragment(editor);
1626
+ }
1627
+ else {
1628
+ const node = Node.parent(editor, selection.anchor.path);
1629
+ if (Element.isElement(node) && Editor.isVoid(editor, node)) {
1630
+ Transforms.delete(editor);
1631
+ }
1632
+ }
1858
1633
  }
1859
- this.updateWeakMap();
1860
- this.listRender = new ListRender(this.viewContext, this.viewContainerRef, this.getOutletParent, this.getOutletElement);
1861
- if (this.editor.isExpanded(this.element)) {
1862
- this.listRender.initialize(this.children, this.element, this.childrenContext);
1634
+ };
1635
+ e.insertData = async (data) => {
1636
+ if (!(await e.customInsertFragmentData(data, null))) {
1637
+ e.insertTextData(data);
1863
1638
  }
1864
- addAfterViewInitQueue(this.editor, () => {
1865
- this.afterViewInit();
1866
- });
1867
- }
1868
- afterViewInit() {
1869
- if (this._context.contentEditable !== undefined) {
1870
- this.nativeElement.setAttribute('contenteditable', this._context.contentEditable + '');
1639
+ };
1640
+ e.customInsertFragmentData = async (data, contextClipboardData) => {
1641
+ /**
1642
+ * Checking copied fragment from application/x-slate-fragment or data-slate-fragment
1643
+ */
1644
+ const clipboardData = contextClipboardData || (await getClipboardData(data));
1645
+ if (clipboardData && clipboardData.elements) {
1646
+ e.insertFragment(clipboardData.elements);
1647
+ return true;
1871
1648
  }
1872
- }
1873
- updateWeakMap() {
1874
- NODE_TO_ELEMENT.set(this.element, this.nativeElement);
1875
- ELEMENT_TO_NODE.set(this.nativeElement, this.element);
1876
- ELEMENT_TO_COMPONENT.set(this.element, this);
1877
- }
1878
- onDestroy() {
1879
- if (NODE_TO_ELEMENT.get(this.element) === this.nativeElement) {
1880
- NODE_TO_ELEMENT.delete(this.element);
1649
+ return false;
1650
+ };
1651
+ e.customInsertTextData = async (data) => {
1652
+ const clipboardData = await getClipboardData(data);
1653
+ if (clipboardData && clipboardData.text) {
1654
+ const lines = clipboardData.text.split(/\r\n|\r|\n/);
1655
+ let split = false;
1656
+ for (const line of lines) {
1657
+ if (split) {
1658
+ Transforms.splitNodes(e, { always: true });
1659
+ }
1660
+ e.insertText(line);
1661
+ split = true;
1662
+ }
1663
+ return true;
1881
1664
  }
1882
- ELEMENT_TO_NODE.delete(this.nativeElement);
1883
- if (ELEMENT_TO_COMPONENT.get(this.element) === this) {
1884
- ELEMENT_TO_COMPONENT.delete(this.element);
1665
+ return false;
1666
+ };
1667
+ e.onKeydown = () => { };
1668
+ e.onClick = () => { };
1669
+ e.isBlockCard = element => false;
1670
+ e.isExpanded = element => true;
1671
+ e.onError = (errorData) => {
1672
+ if (errorData.nativeError) {
1673
+ console.error(errorData.nativeError);
1885
1674
  }
1886
- this.listRender.destroy();
1887
- this.nativeElement?.remove();
1888
- }
1889
- onContextChange() {
1890
- this.childrenContext = this.getChildrenContext();
1891
- if (!this.initialized) {
1892
- return;
1675
+ else {
1676
+ console.error(errorData);
1893
1677
  }
1894
- this.rerender();
1895
- this.updateWeakMap();
1896
- this.updateChildrenView();
1897
- }
1898
- updateChildrenView() {
1899
- if (this.editor.isExpanded(this.element)) {
1900
- this.listRender.update(this.children, this.element, this.childrenContext);
1901
- }
1902
- else {
1903
- if (this.listRender.initialized) {
1904
- this.listRender.destroy();
1678
+ };
1679
+ // exist issue for move operation in withDOM
1680
+ e.apply = (op) => {
1681
+ const matches = [];
1682
+ switch (op.type) {
1683
+ case 'insert_text':
1684
+ case 'remove_text':
1685
+ case 'set_node': {
1686
+ for (const [node, path] of Editor.levels(e, { at: op.path })) {
1687
+ const key = AngularEditor.findKey(e, node);
1688
+ matches.push([path, key]);
1689
+ }
1690
+ break;
1691
+ }
1692
+ case 'insert_node':
1693
+ case 'remove_node':
1694
+ case 'merge_node':
1695
+ case 'split_node': {
1696
+ for (const [node, path] of Editor.levels(e, {
1697
+ at: Path.parent(op.path)
1698
+ })) {
1699
+ const key = AngularEditor.findKey(e, node);
1700
+ matches.push([path, key]);
1701
+ }
1702
+ break;
1703
+ }
1704
+ case 'move_node': {
1705
+ const commonPath = Path.common(Path.parent(op.path), Path.parent(op.newPath));
1706
+ for (const [node, path] of Editor.levels(e, {
1707
+ at: Path.parent(op.path)
1708
+ })) {
1709
+ const key = AngularEditor.findKey(e, node);
1710
+ matches.push([Editor.pathRef(editor, path), key]);
1711
+ }
1712
+ for (const [node, path] of Editor.levels(e, {
1713
+ at: Path.parent(op.newPath)
1714
+ })) {
1715
+ if (path.length > commonPath.length) {
1716
+ const key = AngularEditor.findKey(e, node);
1717
+ matches.push([Editor.pathRef(editor, path), key]);
1718
+ }
1719
+ }
1720
+ break;
1905
1721
  }
1906
1722
  }
1907
- }
1908
- getChildrenContext() {
1909
- return {
1910
- parent: this._context.element,
1911
- selection: this._context.selection,
1912
- decorations: this._context.decorations,
1913
- decorate: this._context.decorate,
1914
- readonly: this._context.readonly
1915
- };
1916
- }
1917
- isStableHeight() {
1918
- return false;
1919
- }
1920
- getRealHeight() {
1921
- if (this.isStableHeight() && this.stableHeight !== null) {
1922
- return this.stableHeight;
1923
- }
1924
- const blockCard = getBlockCardByNativeElement(this.nativeElement);
1925
- const target = blockCard || this.nativeElement;
1926
- const computedStyle = getComputedStyle(target);
1927
- const height = Math.ceil(target.getBoundingClientRect().height) + parseFloat(computedStyle.marginTop) + parseFloat(computedStyle.marginBottom);
1928
- if (this.isStableHeight()) {
1929
- this.stableHeight = height;
1723
+ apply(op);
1724
+ for (const [source, key] of matches) {
1725
+ const [node] = Editor.node(e, Path.isPath(source) ? source : source.current);
1726
+ NODE_TO_KEY.set(node, key);
1930
1727
  }
1931
- return height;
1932
- }
1933
- }
1728
+ };
1729
+ e.selectAll = () => {
1730
+ Transforms.select(e, []);
1731
+ };
1732
+ e.isVisible = element => {
1733
+ return true;
1734
+ };
1735
+ return e;
1736
+ };
1934
1737
 
1935
- class DefaultElementFlavour extends BaseElementFlavour {
1936
- render() {
1937
- const nativeElement = document.createElement('div');
1938
- this.nativeElement = nativeElement;
1939
- }
1940
- rerender() {
1941
- // No-op
1942
- }
1943
- }
1738
+ const TOP_BLUR = 'blur';
1739
+ const TOP_COMPOSITION_END = 'compositionend';
1740
+ const TOP_COMPOSITION_START = 'compositionstart';
1741
+ const TOP_COMPOSITION_UPDATE = 'compositionupdate';
1742
+ const TOP_KEY_DOWN = 'keydown';
1743
+ const TOP_KEY_PRESS = 'keypress';
1744
+ const TOP_KEY_UP = 'keyup';
1745
+ const TOP_MOUSE_DOWN = 'mousedown';
1746
+ const TOP_MOUSE_MOVE = 'mousemove';
1747
+ const TOP_MOUSE_OUT = 'mouseout';
1748
+ const TOP_TEXT_INPUT = 'textInput';
1749
+ const TOP_PASTE = 'paste';
1944
1750
 
1945
- class BaseLeafFlavour extends BaseFlavour {
1946
- get text() {
1947
- return this.context && this.context.text;
1948
- }
1949
- get leaf() {
1950
- return this.context && this.context.leaf;
1951
- }
1952
- onInit() {
1953
- this.render();
1954
- this.renderPlaceholder();
1955
- this.initialized = true;
1956
- }
1957
- onContextChange() {
1958
- if (!this.initialized) {
1959
- return;
1960
- }
1961
- this.rerender();
1962
- this.renderPlaceholder();
1963
- }
1964
- renderPlaceholder() {
1965
- // issue-1: IME input was interrupted
1966
- // issue-2: IME input focus jumping
1967
- // Issue occurs when the span node of the placeholder is before the slateString span node
1968
- if (this.context.leaf['placeholder']) {
1969
- if (!this.placeholderElement) {
1970
- this.createPlaceholder();
1971
- }
1972
- this.updatePlaceholder();
1973
- }
1974
- else {
1975
- this.destroyPlaceholder();
1976
- }
1977
- }
1978
- createPlaceholder() {
1979
- const placeholderElement = document.createElement('span');
1980
- placeholderElement.innerText = this.context.leaf['placeholder'];
1981
- placeholderElement.contentEditable = 'false';
1982
- placeholderElement.setAttribute('data-slate-placeholder', 'true');
1983
- this.placeholderElement = placeholderElement;
1984
- this.nativeElement.classList.add('leaf-with-placeholder');
1985
- this.nativeElement.appendChild(placeholderElement);
1986
- setTimeout(() => {
1987
- const editorElement = this.nativeElement.closest('.the-editor-typo');
1988
- const editorContentHeight = getContentHeight(editorElement);
1989
- if (editorContentHeight > 0) {
1990
- // Not supported webkitLineClamp exceeds height hiding
1991
- placeholderElement.style.maxHeight = `${editorContentHeight}px`;
1992
- }
1993
- const lineClamp = Math.floor(editorContentHeight / this.nativeElement.offsetHeight) || 0;
1994
- placeholderElement.style.webkitLineClamp = `${Math.max(lineClamp, 1)}`;
1995
- });
1751
+ /**
1752
+ * Copyright (c) Facebook, Inc. and its affiliates.
1753
+ *
1754
+ * This source code is licensed under the MIT license found in the
1755
+ * LICENSE file in the root directory of this source tree.
1756
+ */
1757
+ /**
1758
+ * These variables store information about text content of a target node,
1759
+ * allowing comparison of content before and after a given event.
1760
+ *
1761
+ * Identify the node where selection currently begins, then observe
1762
+ * both its text content and its current position in the DOM. Since the
1763
+ * browser may natively replace the target node during composition, we can
1764
+ * use its position to find its replacement.
1765
+ *
1766
+ *
1767
+ */
1768
+ let root = null;
1769
+ let startText = null;
1770
+ let fallbackText = null;
1771
+ function initialize(nativeEventTarget) {
1772
+ root = nativeEventTarget;
1773
+ startText = getText();
1774
+ return true;
1775
+ }
1776
+ function reset() {
1777
+ root = null;
1778
+ startText = null;
1779
+ fallbackText = null;
1780
+ }
1781
+ function getData() {
1782
+ if (fallbackText) {
1783
+ return fallbackText;
1996
1784
  }
1997
- updatePlaceholder() {
1998
- if (this.placeholderElement.innerText !== this.context.leaf['placeholder']) {
1999
- this.placeholderElement.innerText = this.context.leaf['placeholder'];
1785
+ let start;
1786
+ const startValue = startText;
1787
+ const startLength = startValue.length;
1788
+ let end;
1789
+ const endValue = getText();
1790
+ const endLength = endValue.length;
1791
+ for (start = 0; start < startLength; start++) {
1792
+ if (startValue[start] !== endValue[start]) {
1793
+ break;
2000
1794
  }
2001
1795
  }
2002
- destroyPlaceholder() {
2003
- if (this.placeholderElement) {
2004
- this.placeholderElement.remove();
2005
- this.placeholderElement = null;
2006
- this.nativeElement.classList.remove('leaf-with-placeholder');
1796
+ const minEnd = startLength - start;
1797
+ for (end = 1; end <= minEnd; end++) {
1798
+ if (startValue[startLength - end] !== endValue[endLength - end]) {
1799
+ break;
2007
1800
  }
2008
1801
  }
2009
- onDestroy() {
2010
- this.nativeElement?.remove();
1802
+ const sliceTail = end > 1 ? 1 - end : undefined;
1803
+ fallbackText = endValue.slice(start, sliceTail);
1804
+ return fallbackText;
1805
+ }
1806
+ function getText() {
1807
+ if ('value' in root) {
1808
+ return root.value;
2011
1809
  }
1810
+ return root.textContent;
2012
1811
  }
2013
1812
 
2014
- var StringType;
2015
- (function (StringType) {
2016
- StringType["normalString"] = "normalString";
2017
- StringType["lineBreakEmptyString"] = "lineBreakEmptyString";
2018
- StringType["normalEmptyText"] = "normalEmptyText";
2019
- StringType["compatibleString"] = "compatibleString";
2020
- StringType["voidString"] = "voidString";
2021
- })(StringType || (StringType = {}));
2022
- class SlateStringRender {
2023
- constructor(context, viewContext) {
2024
- this.context = context;
2025
- this.viewContext = viewContext;
2026
- }
2027
- // COMPAT: If the text is empty, it's because it's on the edge of an inline
2028
- // node, so we render a zero-width space so that the selection can be
2029
- // inserted next to it still.
2030
- isEmptyText() {
2031
- return this.context.leaf.text === '';
2032
- }
2033
- // COMPAT: Browsers will collapse trailing new lines at the end of blocks,
2034
- // so we need to add an extra trailing new lines to prevent that.
2035
- isCompatibleString() {
2036
- return this.context.isLast && this.context.leaf.text.slice(-1) === '\n';
2037
- }
2038
- // COMPAT: Render text inside void nodes with a zero-width space.
2039
- // So the node can contain selection but the text is not visible.
2040
- isVoid() {
2041
- return this.viewContext.editor.isVoid(this.context.parent);
2042
- }
2043
- get leaf() {
2044
- return this.context && this.context.leaf;
2045
- }
2046
- // COMPAT: If this is the last text node in an empty block, render a zero-
2047
- // width space that will convert into a line break when copying and pasting
2048
- // to support expected plain text.
2049
- isLineBreakEmptyString() {
2050
- return (this.context.leaf.text === '' &&
2051
- this.context.parent.children[this.context.parent.children.length - 1] === this.context.text &&
2052
- !this.viewContext.editor.isInline(this.context.parent) &&
2053
- // [list-render] performance optimization: reduce the number of calls to the `Editor.string(editor, path)` method
2054
- isEmpty(this.viewContext.editor, this.context.parent));
1813
+ /**
1814
+ * Copyright (c) Facebook, Inc. and its affiliates.
1815
+ *
1816
+ * This source code is licensed under the MIT license found in the
1817
+ * LICENSE file in the root directory of this source tree.
1818
+ */
1819
+ const canUseDOM = !!(typeof window !== 'undefined' &&
1820
+ typeof window.document !== 'undefined' &&
1821
+ typeof window.document.createElement !== 'undefined');
1822
+ const END_KEYCODES = [9, 13, 27, 32]; // Tab, Return, Esc, Space
1823
+ const START_KEYCODE = 229;
1824
+ const canUseCompositionEvent = canUseDOM && 'CompositionEvent' in window;
1825
+ let documentMode = null;
1826
+ if (canUseDOM && 'documentMode' in document) {
1827
+ documentMode = document.documentMode;
1828
+ }
1829
+ // Webkit offers a very useful `textInput` event that can be used to
1830
+ // directly represent `beforeInput`. The IE `textinput` event is not as
1831
+ // useful, so we don't use it.
1832
+ const canUseTextInputEvent = canUseDOM && 'TextEvent' in window && !documentMode;
1833
+ // In IE9+, we have access to composition events, but the data supplied
1834
+ // by the native compositionend event may be incorrect. Japanese ideographic
1835
+ // spaces, for instance (\u3000) are not recorded correctly.
1836
+ const useFallbackCompositionData = canUseDOM && (!canUseCompositionEvent || (documentMode && documentMode > 8 && documentMode <= 11));
1837
+ const SPACEBAR_CODE = 32;
1838
+ const SPACEBAR_CHAR = String.fromCharCode(SPACEBAR_CODE);
1839
+ // Events and their corresponding property names.
1840
+ const eventTypes = {
1841
+ beforeInput: {
1842
+ phasedRegistrationNames: {
1843
+ bubbled: 'onBeforeInput',
1844
+ captured: 'onBeforeInputCapture'
1845
+ },
1846
+ dependencies: [TOP_COMPOSITION_END, TOP_KEY_PRESS, TOP_TEXT_INPUT, TOP_PASTE]
2055
1847
  }
2056
- createStringNode(type) {
2057
- let newNativeElement;
2058
- switch (type) {
2059
- case StringType.lineBreakEmptyString:
2060
- newNativeElement = createLineBreakEmptyStringDOM(this.getElementStringLength());
2061
- break;
2062
- case StringType.voidString:
2063
- case StringType.normalEmptyText:
2064
- newNativeElement = createEmptyOrVoidStringNode();
2065
- break;
2066
- case StringType.compatibleString:
2067
- newNativeElement = createCompatibleStringNode(this.leaf.text);
2068
- break;
2069
- case StringType.normalString:
2070
- newNativeElement = createDefaultStringNode(this.leaf.text);
2071
- break;
2072
- default:
2073
- newNativeElement = createDefaultStringNode(this.leaf.text);
2074
- }
2075
- return newNativeElement;
1848
+ };
1849
+ // Track whether we've ever handled a keypress on the space key.
1850
+ let hasSpaceKeypress = false;
1851
+ /**
1852
+ * Return whether a native keypress event is assumed to be a command.
1853
+ * This is required because Firefox fires `keypress` events for key commands
1854
+ * (cut, copy, select-all, etc.) even though no character is inserted.
1855
+ */
1856
+ function isKeypressCommand(nativeEvent) {
1857
+ return ((nativeEvent.ctrlKey || nativeEvent.altKey || nativeEvent.metaKey) &&
1858
+ // ctrlKey && altKey is equivalent to AltGr, and is not a command.
1859
+ !(nativeEvent.ctrlKey && nativeEvent.altKey));
1860
+ }
1861
+ /**
1862
+ * Does our fallback mode think that this event is the end of composition?
1863
+ *
1864
+ */
1865
+ function isFallbackCompositionEnd(topLevelType, nativeEvent) {
1866
+ switch (topLevelType) {
1867
+ case TOP_KEY_UP:
1868
+ // Command keys insert or clear IME input.
1869
+ return END_KEYCODES.indexOf(nativeEvent.keyCode) !== -1;
1870
+ case TOP_KEY_DOWN:
1871
+ // Expect IME keyCode on each keydown. If we get any other
1872
+ // code we must have exited earlier.
1873
+ return nativeEvent.keyCode !== START_KEYCODE;
1874
+ case TOP_KEY_PRESS:
1875
+ case TOP_MOUSE_DOWN:
1876
+ case TOP_BLUR:
1877
+ // Events are not possible without cancelling IME.
1878
+ return true;
1879
+ default:
1880
+ return false;
2076
1881
  }
2077
- render() {
2078
- this.type = this.getType();
2079
- this.nativeElement = this.createStringNode(this.type);
2080
- return this.nativeElement;
1882
+ }
1883
+ /**
1884
+ * Google Input Tools provides composition data via a CustomEvent,
1885
+ * with the `data` property populated in the `detail` object. If this
1886
+ * is available on the event object, use it. If not, this is a plain
1887
+ * composition event and we have nothing special to extract.
1888
+ *
1889
+ */
1890
+ function getDataFromCustomEvent(nativeEvent) {
1891
+ const detail = nativeEvent.detail;
1892
+ if (typeof detail === 'object' && 'data' in detail) {
1893
+ return detail.data;
2081
1894
  }
2082
- getType() {
2083
- if (this.isLineBreakEmptyString()) {
2084
- return StringType.lineBreakEmptyString;
2085
- }
2086
- if (this.isVoid()) {
2087
- return StringType.voidString;
2088
- }
2089
- if (this.isEmptyText()) {
2090
- return StringType.normalEmptyText;
2091
- }
2092
- if (this.isCompatibleString()) {
2093
- return StringType.compatibleString;
2094
- }
2095
- return StringType.normalString;
1895
+ return null;
1896
+ }
1897
+ /**
1898
+ * Check if a composition event was triggered by Korean IME.
1899
+ * Our fallback mode does not work well with IE's Korean IME,
1900
+ * so just use native composition events when Korean IME is used.
1901
+ * Although CompositionEvent.locale property is deprecated,
1902
+ * it is available in IE, where our fallback mode is enabled.
1903
+ *
1904
+ */
1905
+ function isUsingKoreanIME(nativeEvent) {
1906
+ return nativeEvent.locale === 'ko';
1907
+ }
1908
+ // Track the current IME composition status, if any.
1909
+ let isComposing = false;
1910
+ function getNativeBeforeInputChars(topLevelType, nativeEvent) {
1911
+ switch (topLevelType) {
1912
+ case TOP_COMPOSITION_END:
1913
+ return getDataFromCustomEvent(nativeEvent);
1914
+ case TOP_KEY_PRESS:
1915
+ /**
1916
+ * If native `textInput` events are available, our goal is to make
1917
+ * use of them. However, there is a special case: the spacebar key.
1918
+ * In Webkit, preventing default on a spacebar `textInput` event
1919
+ * cancels character insertion, but it *also* causes the browser
1920
+ * to fall back to its default spacebar behavior of scrolling the
1921
+ * page.
1922
+ *
1923
+ * Tracking at:
1924
+ * https://code.google.com/p/chromium/issues/detail?id=355103
1925
+ *
1926
+ * To avoid this issue, use the keypress event as if no `textInput`
1927
+ * event is available.
1928
+ */
1929
+ const which = nativeEvent.which;
1930
+ if (which !== SPACEBAR_CODE) {
1931
+ return null;
1932
+ }
1933
+ hasSpaceKeypress = true;
1934
+ return SPACEBAR_CHAR;
1935
+ case TOP_TEXT_INPUT:
1936
+ // Record the characters to be added to the DOM.
1937
+ const chars = nativeEvent.data;
1938
+ // If it's a spacebar character, assume that we have already handled
1939
+ // it at the keypress level and bail immediately. Android Chrome
1940
+ // doesn't give us keycodes, so we need to ignore it.
1941
+ if (chars === SPACEBAR_CHAR && hasSpaceKeypress) {
1942
+ return null;
1943
+ }
1944
+ return chars;
1945
+ default:
1946
+ // For other native event types, do nothing.
1947
+ return null;
2096
1948
  }
2097
- update(context, viewContext) {
2098
- this.context = context;
2099
- this.viewContext = viewContext;
2100
- const type = this.getType();
2101
- if (type !== this.type) {
2102
- const newNativeElement = this.createStringNode(type);
2103
- this.nativeElement.replaceWith(newNativeElement);
2104
- this.nativeElement = newNativeElement;
2105
- this.type = type;
2106
- return;
2107
- }
2108
- if (this.type === StringType.normalString) {
2109
- this.nativeElement.textContent = this.leaf.text;
1949
+ }
1950
+ /**
1951
+ * For browsers that do not provide the `textInput` event, extract the
1952
+ * appropriate string to use for SyntheticInputEvent.
1953
+ *
1954
+ */
1955
+ function getFallbackBeforeInputChars(topLevelType, nativeEvent) {
1956
+ // If we are currently composing (IME) and using a fallback to do so,
1957
+ // try to extract the composed characters from the fallback object.
1958
+ // If composition event is available, we extract a string only at
1959
+ // compositionevent, otherwise extract it at fallback events.
1960
+ if (isComposing) {
1961
+ if (topLevelType === TOP_COMPOSITION_END || (!canUseCompositionEvent && isFallbackCompositionEnd(topLevelType, nativeEvent))) {
1962
+ const chars = getData();
1963
+ reset();
1964
+ isComposing = false;
1965
+ return chars;
2110
1966
  }
1967
+ return null;
2111
1968
  }
2112
- getElementStringLength() {
2113
- return Node.string(this.context.parent).length;
1969
+ switch (topLevelType) {
1970
+ case TOP_PASTE:
1971
+ // If a paste event occurs after a keypress, throw out the input
1972
+ // chars. Paste events should not lead to BeforeInput events.
1973
+ return null;
1974
+ case TOP_KEY_PRESS:
1975
+ /**
1976
+ * As of v27, Firefox may fire keypress events even when no character
1977
+ * will be inserted. A few possibilities:
1978
+ *
1979
+ * - `which` is `0`. Arrow keys, Esc key, etc.
1980
+ *
1981
+ * - `which` is the pressed key code, but no char is available.
1982
+ * Ex: 'AltGr + d` in Polish. There is no modified character for
1983
+ * this key combination and no character is inserted into the
1984
+ * document, but FF fires the keypress for char code `100` anyway.
1985
+ * No `input` event will occur.
1986
+ *
1987
+ * - `which` is the pressed key code, but a command combination is
1988
+ * being used. Ex: `Cmd+C`. No character is inserted, and no
1989
+ * `input` event will occur.
1990
+ */
1991
+ if (!isKeypressCommand(nativeEvent)) {
1992
+ // IE fires the `keypress` event when a user types an emoji via
1993
+ // Touch keyboard of Windows. In such a case, the `char` property
1994
+ // holds an emoji character like `\uD83D\uDE0A`. Because its length
1995
+ // is 2, the property `which` does not represent an emoji correctly.
1996
+ // In such a case, we directly return the `char` property instead of
1997
+ // using `which`.
1998
+ if (nativeEvent.char && nativeEvent.char.length > 1) {
1999
+ return nativeEvent.char;
2000
+ }
2001
+ else if (nativeEvent.which) {
2002
+ return String.fromCharCode(nativeEvent.which);
2003
+ }
2004
+ }
2005
+ return null;
2006
+ case TOP_COMPOSITION_END:
2007
+ return useFallbackCompositionData && !isUsingKoreanIME(nativeEvent) ? null : nativeEvent.data;
2008
+ default:
2009
+ return null;
2114
2010
  }
2115
2011
  }
2116
- const createDefaultStringNode = (text) => {
2117
- const stringNode = document.createElement('span');
2118
- stringNode.textContent = text;
2119
- stringNode.setAttribute('data-slate-string', 'true');
2120
- stringNode.setAttribute('editable-text', '');
2121
- return stringNode;
2122
- };
2123
- const createEmptyOrVoidStringNode = () => {
2124
- const stringNode = document.createElement('span');
2125
- stringNode.setAttribute('data-slate-string', 'true');
2126
- stringNode.setAttribute('data-slate-zero-width', 'z');
2127
- stringNode.setAttribute('data-slate-length', '0');
2128
- const zeroWidthSpace = getZeroTextNode();
2129
- stringNode.appendChild(zeroWidthSpace);
2130
- stringNode.setAttribute('editable-text', '');
2131
- return stringNode;
2132
- };
2133
- const createCompatibleStringNode = (text) => {
2134
- const stringNode = document.createElement('span');
2135
- stringNode.setAttribute('data-slate-string', 'true');
2136
- stringNode.textContent = text;
2137
- stringNode.setAttribute('editable-text', '');
2138
- const zeroWidthSpan = document.createElement('span');
2139
- const zeroWidthSpace = getZeroTextNode();
2140
- zeroWidthSpan.setAttribute('data-slate-zero-width', '');
2141
- zeroWidthSpan.appendChild(zeroWidthSpace);
2142
- stringNode.appendChild(zeroWidthSpan);
2143
- return stringNode;
2144
- };
2145
- const createLineBreakEmptyStringDOM = (elementStringLength) => {
2146
- const stringNode = document.createElement('span');
2147
- stringNode.setAttribute('data-slate-zero-width', 'n');
2148
- stringNode.setAttribute('data-slate-length', `${elementStringLength}`);
2149
- const zeroWidthSpace = getZeroTextNode();
2150
- stringNode.appendChild(zeroWidthSpace);
2151
- const brNode = document.createElement('br');
2152
- stringNode.appendChild(brNode);
2153
- stringNode.setAttribute('editable-text', '');
2154
- return stringNode;
2155
- };
2156
2012
  /**
2157
- * TODO: remove when bump slate
2158
- * copy from slate
2159
- * @param editor
2160
- * @param element
2161
- * @returns
2013
+ * Extract a SyntheticInputEvent for `beforeInput`, based on either native
2014
+ * `textInput` or fallback behavior.
2015
+ *
2162
2016
  */
2163
- const isEmpty = (editor, element) => {
2164
- const { children } = element;
2165
- const [first] = children;
2166
- return children.length === 0 || (children.length === 1 && Text$1.isText(first) && first.text === '' && !editor.isVoid(element));
2167
- };
2168
-
2169
- class DefaultLeafFlavour extends BaseLeafFlavour {
2170
- constructor() {
2171
- super(...arguments);
2172
- this.stringRender = null;
2017
+ function extractBeforeInputEvent(topLevelType, targetInst, nativeEvent, nativeEventTarget) {
2018
+ let chars;
2019
+ if (canUseTextInputEvent) {
2020
+ chars = getNativeBeforeInputChars(topLevelType, nativeEvent);
2173
2021
  }
2174
- render() {
2175
- const leafNode = createDefaultLeafNode();
2176
- this.stringRender = new SlateStringRender(this.context, this.viewContext);
2177
- const stringNode = this.stringRender.render();
2178
- leafNode.appendChild(stringNode);
2179
- this.nativeElement = leafNode;
2022
+ else {
2023
+ chars = getFallbackBeforeInputChars(topLevelType, nativeEvent);
2180
2024
  }
2181
- rerender() {
2182
- this.stringRender?.update(this.context, this.viewContext);
2025
+ // If no characters are being inserted, no BeforeInput event should
2026
+ // be fired.
2027
+ if (!chars) {
2028
+ return null;
2183
2029
  }
2030
+ const beforeInputEvent = new BeforeInputEvent();
2031
+ beforeInputEvent.data = chars;
2032
+ beforeInputEvent.nativeEvent = nativeEvent;
2033
+ return beforeInputEvent;
2184
2034
  }
2185
- const createDefaultLeafNode = () => {
2186
- const span = document.createElement('span');
2187
- span.setAttribute('data-slate-leaf', 'true');
2188
- return span;
2035
+ /**
2036
+ * Create an `onBeforeInput` event to match
2037
+ * http://www.w3.org/TR/2013/WD-DOM-Level-3-Events-20131105/#events-inputevents.
2038
+ *
2039
+ * This event plugin is based on the native `textInput` event
2040
+ * available in Chrome, Safari, Opera, and IE. This event fires after
2041
+ * `onKeyPress` and `onCompositionEnd`, but before `onInput`.
2042
+ *
2043
+ * `beforeInput` is spec'd but not implemented in any browsers, and
2044
+ * the `input` event does not provide any useful information about what has
2045
+ * actually been added, contrary to the spec. Thus, `textInput` is the best
2046
+ * available event to identify the characters that have actually been inserted
2047
+ * into the target node.
2048
+ *
2049
+ * This plugin is also responsible for emitting `composition` events, thus
2050
+ * allowing us to share composition fallback code for both `beforeInput` and
2051
+ * `composition` event types.
2052
+ */
2053
+ const BeforeInputEventPlugin = {
2054
+ extractEvents: (topLevelType, targetInst, nativeEvent, nativeEventTarget) => {
2055
+ return extractBeforeInputEvent(topLevelType, targetInst, nativeEvent, nativeEventTarget);
2056
+ }
2189
2057
  };
2058
+ class BeforeInputEvent {
2059
+ }
2190
2060
 
2191
- class LeavesRender {
2192
- constructor(viewContext, viewContainerRef, getOutletParent, getOutletElement) {
2193
- this.viewContext = viewContext;
2194
- this.viewContainerRef = viewContainerRef;
2195
- this.getOutletParent = getOutletParent;
2196
- this.getOutletElement = getOutletElement;
2197
- this.views = [];
2198
- this.contexts = [];
2199
- this.viewTypes = [];
2200
- }
2201
- initialize(context) {
2202
- const { decoratedLeaves, contexts } = this.getLeaves(context);
2203
- this.decoratedLeaves = decoratedLeaves;
2204
- this.contexts = contexts;
2205
- this.decoratedLeaves.forEach((leaf, index) => {
2206
- const context = getContext$1(index, this.contexts);
2207
- const viewType = getViewType$1(context, this.viewContext);
2208
- const view = createEmbeddedViewOrComponentOrFlavour(viewType, context, this.viewContext, this.viewContainerRef);
2209
- this.views.push(view);
2210
- this.contexts.push(context);
2211
- this.viewTypes.push(viewType);
2212
- });
2213
- mount(this.views, null, this.getOutletParent(), this.getOutletElement());
2214
- const newDiffers = this.viewContext.editor.injector.get(IterableDiffers);
2215
- this.differ = newDiffers.find(this.decoratedLeaves).create(trackBy$1(this.viewContext));
2216
- this.differ.diff(this.decoratedLeaves);
2217
- }
2218
- update(context) {
2219
- const { decoratedLeaves, contexts } = this.getLeaves(context);
2220
- const outletParent = this.getOutletParent();
2221
- const diffResult = this.differ.diff(decoratedLeaves);
2222
- if (diffResult) {
2223
- let firstRootNode = getRootNodes(this.views[0])[0];
2224
- const newContexts = [];
2225
- const newViewTypes = [];
2226
- const newViews = [];
2227
- diffResult.forEachItem(record => {
2228
- let context = getContext$1(record.currentIndex, contexts);
2229
- const viewType = getViewType$1(context, this.viewContext);
2230
- newViewTypes.push(viewType);
2231
- let view;
2232
- if (record.previousIndex === null) {
2233
- view = createEmbeddedViewOrComponentOrFlavour(viewType, context, this.viewContext, this.viewContainerRef);
2234
- newContexts.push(context);
2235
- newViews.push(view);
2236
- mountOnItemChange(record.currentIndex, record.item, newViews, null, outletParent, firstRootNode, this.viewContext);
2237
- }
2238
- else {
2239
- const previousView = this.views[record.previousIndex];
2240
- const previousViewType = this.viewTypes[record.previousIndex];
2241
- if (previousViewType !== viewType) {
2242
- view = createEmbeddedViewOrComponentOrFlavour(viewType, context, this.viewContext, this.viewContainerRef);
2243
- const firstRootNode = getRootNodes(previousView, null)[0];
2244
- const newRootNodes = getRootNodes(view, null);
2245
- firstRootNode.replaceWith(...newRootNodes);
2246
- previousView.destroy();
2247
- }
2248
- else {
2249
- view = previousView;
2250
- updateContext(previousView, context, this.viewContext);
2251
- }
2252
- newContexts.push(context);
2253
- newViews.push(view);
2254
- }
2255
- });
2256
- diffResult.forEachRemovedItem(record => {
2257
- const view = this.views[record.previousIndex];
2258
- view.destroy();
2061
+ const BEFORE_INPUT_EVENTS = [
2062
+ // { name: 'blur', handler: 'onBlur', isTriggerBeforeInput: true },
2063
+ // { name: 'compositionstart', handler: 'onCompositionStart', isTriggerBeforeInput: true },
2064
+ { name: 'compositionupdate', handler: null, isTriggerBeforeInput: true },
2065
+ // { name: 'compositionend', handler: 'onCompositionEnd', isTriggerBeforeInput: false },
2066
+ // { name: 'keydown', handler: 'onKeyDown', isTriggerBeforeInput: true },
2067
+ { name: 'keypress', handler: null, isTriggerBeforeInput: true },
2068
+ { name: 'keyup', handler: 'onKeyUp', isTriggerBeforeInput: true },
2069
+ { name: 'mousedown', handler: 'onMouseDown', isTriggerBeforeInput: true },
2070
+ { name: 'textInput', handler: null, isTriggerBeforeInput: true }
2071
+ // { name: 'paste', handler: 'onPaste', isTriggerBeforeInput: true }
2072
+ ];
2073
+
2074
+ var SlateErrorCode;
2075
+ (function (SlateErrorCode) {
2076
+ SlateErrorCode[SlateErrorCode["ToNativeSelectionError"] = 2100] = "ToNativeSelectionError";
2077
+ SlateErrorCode[SlateErrorCode["ToSlateSelectionError"] = 2101] = "ToSlateSelectionError";
2078
+ SlateErrorCode[SlateErrorCode["OnDOMBeforeInputError"] = 2102] = "OnDOMBeforeInputError";
2079
+ SlateErrorCode[SlateErrorCode["OnSyntheticBeforeInputError"] = 2103] = "OnSyntheticBeforeInputError";
2080
+ SlateErrorCode[SlateErrorCode["OnDOMKeydownError"] = 2104] = "OnDOMKeydownError";
2081
+ SlateErrorCode[SlateErrorCode["GetStartPointError"] = 2105] = "GetStartPointError";
2082
+ SlateErrorCode[SlateErrorCode["NotFoundPreviousRootNodeError"] = 3100] = "NotFoundPreviousRootNodeError";
2083
+ SlateErrorCode[SlateErrorCode["InvalidValueError"] = 4100] = "InvalidValueError";
2084
+ })(SlateErrorCode || (SlateErrorCode = {}));
2085
+
2086
+ function restoreDom(editor, execute) {
2087
+ const editable = EDITOR_TO_ELEMENT.get(editor);
2088
+ let observer = new MutationObserver(mutations => {
2089
+ mutations.reverse().forEach(mutation => {
2090
+ if (mutation.type === 'characterData') {
2091
+ // We don't want to restore the DOM for characterData mutations
2092
+ // because this interrupts the composition.
2093
+ return;
2094
+ }
2095
+ mutation.removedNodes.forEach(node => {
2096
+ mutation.target.insertBefore(node, mutation.nextSibling);
2259
2097
  });
2260
- diffResult.forEachMovedItem(record => {
2261
- mountOnItemChange(record.currentIndex, record.item, newViews, null, outletParent, firstRootNode, this.viewContext);
2098
+ mutation.addedNodes.forEach(node => {
2099
+ mutation.target.removeChild(node);
2262
2100
  });
2263
- this.viewTypes = newViewTypes;
2264
- this.views = newViews;
2265
- this.contexts = newContexts;
2266
- this.decoratedLeaves = decoratedLeaves;
2267
- }
2268
- }
2269
- getLeaves(context) {
2270
- const decoratedLeaves = Text$1.decorations(context.text, context.decorations);
2271
- const contexts = decoratedLeaves.map((decoratedLeaf, index) => {
2272
- return {
2273
- leaf: decoratedLeaf.leaf,
2274
- leafPosition: decoratedLeaf.position,
2275
- text: context.text,
2276
- parent: context.parent,
2277
- index,
2278
- isLast: context.isLast && index === decoratedLeaves.length - 1
2279
- };
2280
2101
  });
2281
- return { decoratedLeaves, contexts };
2282
- }
2102
+ disconnect();
2103
+ execute();
2104
+ });
2105
+ const disconnect = () => {
2106
+ observer.disconnect();
2107
+ observer = null;
2108
+ };
2109
+ observer.observe(editable, { subtree: true, childList: true, characterData: true, characterDataOldValue: true });
2110
+ setTimeout(() => {
2111
+ if (observer) {
2112
+ disconnect();
2113
+ execute();
2114
+ }
2115
+ }, 0);
2116
+ }
2117
+
2118
+ class FlavourRef {
2283
2119
  destroy() {
2284
- this.views.forEach(view => view.destroy());
2120
+ this.instance.onDestroy();
2285
2121
  }
2286
2122
  }
2287
- function getContext$1(index, leafContexts) {
2288
- return leafContexts[index];
2289
- }
2290
- function getViewType$1(leafContext, viewContext) {
2291
- return (viewContext.renderLeaf && viewContext.renderLeaf(leafContext.leaf)) || DefaultLeafFlavour;
2292
- }
2293
- function trackBy$1(viewContext) {
2294
- return (index, node) => {
2295
- return index;
2296
- };
2123
+ class BlockCardRef {
2124
+ destroy() {
2125
+ this.instance.onDestroy();
2126
+ }
2297
2127
  }
2298
2128
 
2299
- class BaseTextFlavour extends BaseFlavour {
2300
- constructor() {
2301
- super(...arguments);
2302
- this.getOutletParent = () => {
2303
- return this.nativeElement;
2304
- };
2305
- this.getOutletElement = () => {
2306
- return this.nativeElement.querySelector('.children-outlet');
2129
+ function createEmbeddedViewOrComponentOrFlavour(viewType, context, viewContext, viewContainerRef) {
2130
+ if (isFlavourType(viewType)) {
2131
+ const flavourRef = new FlavourRef();
2132
+ flavourRef.instance = new viewType();
2133
+ flavourRef.instance.context = context;
2134
+ flavourRef.instance.viewContext = viewContext;
2135
+ flavourRef.instance.viewContainerRef = viewContainerRef;
2136
+ flavourRef.instance.onInit();
2137
+ return flavourRef;
2138
+ }
2139
+ if (isTemplateRef(viewType)) {
2140
+ const embeddedViewContext = {
2141
+ context,
2142
+ viewContext
2307
2143
  };
2144
+ const embeddedViewRef = viewContainerRef.createEmbeddedView(viewType, embeddedViewContext);
2145
+ embeddedViewRef.detectChanges();
2146
+ return embeddedViewRef;
2308
2147
  }
2309
- get text() {
2310
- return this._context && this._context.text;
2148
+ if (isComponentType(viewType)) {
2149
+ const componentRef = viewContainerRef.createComponent(viewType, {
2150
+ injector: viewContainerRef.injector
2151
+ });
2152
+ componentRef.instance.viewContext = viewContext;
2153
+ componentRef.instance.context = context;
2154
+ componentRef.changeDetectorRef.detectChanges();
2155
+ return componentRef;
2311
2156
  }
2312
- onInit() {
2313
- this.render();
2314
- this.updateWeakMap();
2315
- this.initialized = true;
2316
- this.leavesRender = new LeavesRender(this.viewContext, this.viewContainerRef, this.getOutletParent, this.getOutletElement);
2317
- this.leavesRender.initialize(this.context);
2157
+ }
2158
+ function updateContext(view, newContext, viewContext) {
2159
+ if (view instanceof FlavourRef) {
2160
+ view.instance.context = newContext;
2161
+ return;
2318
2162
  }
2319
- updateWeakMap() {
2320
- ELEMENT_TO_NODE.set(this.nativeElement, this.text);
2321
- NODE_TO_ELEMENT.set(this.text, this.nativeElement);
2163
+ if (view instanceof ComponentRef) {
2164
+ view.instance.context = newContext;
2322
2165
  }
2323
- onDestroy() {
2324
- if (NODE_TO_ELEMENT.get(this.text) === this.nativeElement) {
2325
- NODE_TO_ELEMENT.delete(this.text);
2166
+ else {
2167
+ view.context.context = newContext;
2168
+ view.context.viewContext = viewContext;
2169
+ view.detectChanges();
2170
+ }
2171
+ }
2172
+ function mount(views, blockCards, outletParent, outletElement) {
2173
+ if (views.length > 0) {
2174
+ const fragment = document.createDocumentFragment();
2175
+ views.forEach((view, index) => {
2176
+ const blockCard = blockCards ? blockCards[index] : undefined;
2177
+ fragment.append(...getRootNodes(view, blockCard));
2178
+ });
2179
+ if (outletElement) {
2180
+ outletElement.parentElement.insertBefore(fragment, outletElement);
2181
+ outletElement.remove();
2182
+ }
2183
+ else {
2184
+ outletParent.prepend(fragment);
2326
2185
  }
2327
- ELEMENT_TO_NODE.delete(this.nativeElement);
2328
- this.leavesRender.destroy();
2329
- this.nativeElement?.remove();
2330
2186
  }
2331
- onContextChange() {
2332
- if (!this.initialized) {
2333
- return;
2187
+ }
2188
+ function getRootNodes(ref, blockCard) {
2189
+ if (blockCard) {
2190
+ return [blockCard.instance.nativeElement];
2191
+ }
2192
+ if (ref instanceof FlavourRef) {
2193
+ return [ref.instance.nativeElement];
2194
+ }
2195
+ if (ref instanceof ComponentRef) {
2196
+ ref.hostView.rootNodes.forEach(ele => {
2197
+ if (!(ele instanceof HTMLElement)) {
2198
+ ele.remove();
2199
+ }
2200
+ });
2201
+ return [ref.instance.nativeElement];
2202
+ }
2203
+ else {
2204
+ const result = [];
2205
+ ref.rootNodes.forEach(rootNode => {
2206
+ const isHTMLElement = rootNode instanceof HTMLElement;
2207
+ const isSlateNodeOfLeaf = isHTMLElement && (rootNode.hasAttribute('data-slate-node') || rootNode.hasAttribute('data-slate-leaf'));
2208
+ if (isSlateNodeOfLeaf && result.every(item => !item.contains(rootNode))) {
2209
+ result.push(rootNode);
2210
+ }
2211
+ if (!isHTMLElement) {
2212
+ rootNode.remove();
2213
+ }
2214
+ });
2215
+ return result;
2216
+ }
2217
+ }
2218
+ function mountOnItemChange(index, item, views, blockCards, outletParent, firstRootNode, viewContext) {
2219
+ const view = views[index];
2220
+ let rootNodes = getRootNodes(view);
2221
+ if (blockCards) {
2222
+ const isBlockCard = viewContext.editor.isBlockCard(item);
2223
+ if (isBlockCard) {
2224
+ const blockCard = blockCards[index];
2225
+ rootNodes = [blockCard.instance.nativeElement];
2334
2226
  }
2335
- this.rerender();
2336
- this.updateWeakMap();
2337
- this.leavesRender.update(this.context);
2227
+ }
2228
+ if (index === 0) {
2229
+ if (firstRootNode) {
2230
+ rootNodes.forEach(rootNode => {
2231
+ firstRootNode.insertAdjacentElement('beforebegin', rootNode);
2232
+ });
2233
+ }
2234
+ else {
2235
+ outletParent.prepend(...rootNodes);
2236
+ }
2237
+ }
2238
+ else {
2239
+ const previousView = views[index - 1];
2240
+ const blockCard = blockCards ? blockCards[index - 1] : null;
2241
+ const previousRootNodes = getRootNodes(previousView, blockCard);
2242
+ let previousRootNode = previousRootNodes[previousRootNodes.length - 1];
2243
+ rootNodes.forEach(rootNode => {
2244
+ previousRootNode.insertAdjacentElement('afterend', rootNode);
2245
+ previousRootNode = rootNode;
2246
+ });
2338
2247
  }
2339
2248
  }
2340
2249
 
2341
- class DefaultTextFlavour extends BaseTextFlavour {
2342
- render() {
2343
- const { nativeElement } = createText(this.text.text);
2344
- this.nativeElement = nativeElement;
2250
+ function hasBeforeContextChange(value) {
2251
+ if (value.beforeContextChange) {
2252
+ return true;
2345
2253
  }
2346
- rerender() { }
2254
+ return false;
2347
2255
  }
2348
- class VoidTextFlavour extends BaseTextFlavour {
2349
- render() {
2350
- const { nativeElement } = createText(this.text.text);
2351
- this.nativeElement = nativeElement;
2352
- this.nativeElement.setAttribute('data-slate-spacer', 'true');
2353
- this.nativeElement.classList.add('slate-spacer');
2256
+ function hasAfterContextChange(value) {
2257
+ if (value.afterContextChange) {
2258
+ return true;
2354
2259
  }
2355
- rerender() { }
2260
+ return false;
2356
2261
  }
2357
- const createText = (text) => {
2358
- const nativeElement = document.createElement('span');
2359
- nativeElement.setAttribute('data-slate-node', 'text');
2360
- return { nativeElement };
2361
- };
2362
2262
 
2363
- class ListRender {
2364
- constructor(viewContext, viewContainerRef, getOutletParent, getOutletElement) {
2365
- this.viewContext = viewContext;
2366
- this.viewContainerRef = viewContainerRef;
2367
- this.getOutletParent = getOutletParent;
2368
- this.getOutletElement = getOutletElement;
2369
- this.children = [];
2370
- this.views = [];
2371
- this.blockCards = [];
2372
- this.contexts = [];
2373
- this.viewTypes = [];
2374
- this.differ = null;
2263
+ class BaseFlavour {
2264
+ constructor() {
2375
2265
  this.initialized = false;
2376
- this.preRenderingHTMLElement = [];
2377
- }
2378
- initialize(children, parent, childrenContext, preRenderingCount = 0) {
2379
- this.initialized = true;
2380
- this.children = children;
2381
- const isRoot = parent === this.viewContext.editor;
2382
- const firstIndex = isRoot ? this.viewContext.editor.children.indexOf(children[0]) : 0;
2383
- const parentPath = AngularEditor.findPath(this.viewContext.editor, parent);
2384
- children.forEach((descendant, _index) => {
2385
- NODE_TO_INDEX.set(descendant, firstIndex + _index);
2386
- NODE_TO_PARENT.set(descendant, parent);
2387
- const context = getContext(firstIndex + _index, descendant, parentPath, childrenContext, this.viewContext);
2388
- const viewType = getViewType(descendant, parent, this.viewContext);
2389
- const view = createEmbeddedViewOrComponentOrFlavour(viewType, context, this.viewContext, this.viewContainerRef);
2390
- const blockCard = createBlockCard(descendant, view, this.viewContext);
2391
- this.views.push(view);
2392
- this.contexts.push(context);
2393
- this.viewTypes.push(viewType);
2394
- this.blockCards.push(blockCard);
2395
- });
2396
- mount(this.views, this.blockCards, this.getOutletParent(), this.getOutletElement());
2397
- const newDiffers = this.viewContext.editor.injector.get(IterableDiffers);
2398
- this.differ = newDiffers.find(children).create(trackBy(this.viewContext));
2399
- this.differ.diff(children);
2400
- if (parent === this.viewContext.editor) {
2401
- executeAfterViewInit(this.viewContext.editor);
2402
- }
2403
2266
  }
2404
- update(children, parent, childrenContext, preRenderingCount = 0) {
2405
- if (!this.initialized || this.children.length === 0) {
2406
- this.initialize(children, parent, childrenContext, preRenderingCount);
2407
- return;
2408
- }
2409
- if (!this.differ) {
2410
- throw new Error('Exception: Can not find differ ');
2267
+ static { this.isFlavour = true; }
2268
+ set context(value) {
2269
+ if (hasBeforeContextChange(this)) {
2270
+ this.beforeContextChange(value);
2411
2271
  }
2412
- const outletParent = this.getOutletParent();
2413
- if (this.preRenderingHTMLElement.length > 0) {
2414
- const preRenderingElement = [...this.preRenderingHTMLElement];
2415
- preRenderingElement.forEach((rootNodes, index) => {
2416
- rootNodes.forEach(rootNode => {
2417
- rootNode.style.position = '';
2418
- rootNode.style.top = '';
2419
- rootNode.style.width = '';
2420
- });
2421
- });
2422
- this.preRenderingHTMLElement = [];
2272
+ this._context = value;
2273
+ this.onContextChange();
2274
+ if (hasAfterContextChange(this)) {
2275
+ this.afterContextChange();
2423
2276
  }
2424
- const diffResult = this.differ.diff(children);
2425
- const parentPath = AngularEditor.findPath(this.viewContext.editor, parent);
2426
- const isRoot = parent === this.viewContext.editor;
2427
- const firstIndex = isRoot ? this.viewContext.editor.children.indexOf(children[0]) : 0;
2428
- if (diffResult) {
2429
- let firstRootNode = getRootNodes(this.views[0], this.blockCards[0])[0];
2430
- const newContexts = [];
2431
- const newViewTypes = [];
2432
- const newViews = [];
2433
- const newBlockCards = [];
2434
- diffResult.forEachItem(record => {
2435
- const currentIndex = firstIndex + record.currentIndex;
2436
- NODE_TO_INDEX.set(record.item, currentIndex);
2437
- NODE_TO_PARENT.set(record.item, parent);
2438
- let context = getContext(currentIndex, record.item, parentPath, childrenContext, this.viewContext);
2439
- const viewType = getViewType(record.item, parent, this.viewContext);
2440
- newViewTypes.push(viewType);
2441
- let view;
2442
- let blockCard;
2443
- if (record.previousIndex === null) {
2444
- view = createEmbeddedViewOrComponentOrFlavour(viewType, context, this.viewContext, this.viewContainerRef);
2445
- blockCard = createBlockCard(record.item, view, this.viewContext);
2446
- newContexts.push(context);
2447
- newViews.push(view);
2448
- newBlockCards.push(blockCard);
2449
- mountOnItemChange(record.currentIndex, record.item, newViews, newBlockCards, outletParent, firstRootNode, this.viewContext);
2450
- }
2451
- else {
2452
- const previousView = this.views[record.previousIndex];
2453
- const previousViewType = this.viewTypes[record.previousIndex];
2454
- const previousContext = this.contexts[record.previousIndex];
2455
- const previousBlockCard = this.blockCards[record.previousIndex];
2456
- if (previousViewType !== viewType) {
2457
- view = createEmbeddedViewOrComponentOrFlavour(viewType, context, this.viewContext, this.viewContainerRef);
2458
- blockCard = createBlockCard(record.item, view, this.viewContext);
2459
- const firstRootNode = getRootNodes(previousView, previousBlockCard)[0];
2460
- const newRootNodes = getRootNodes(view, blockCard);
2461
- firstRootNode.replaceWith(...newRootNodes);
2462
- previousView.destroy();
2463
- previousBlockCard?.destroy();
2464
- }
2465
- else {
2466
- view = previousView;
2467
- blockCard = previousBlockCard;
2468
- if (memoizedContext(this.viewContext, record.item, previousContext, context)) {
2469
- context = previousContext;
2470
- }
2471
- else {
2472
- updateContext(previousView, context, this.viewContext);
2473
- }
2474
- }
2475
- newContexts.push(context);
2476
- newViews.push(view);
2477
- newBlockCards.push(blockCard);
2478
- }
2479
- });
2480
- diffResult.forEachOperation(record => {
2481
- // removed
2482
- if (record.currentIndex === null) {
2483
- const view = this.views[record.previousIndex];
2484
- const blockCard = this.blockCards[record.previousIndex];
2485
- view.destroy();
2486
- blockCard?.destroy();
2487
- }
2488
- // moved
2489
- if (record.previousIndex !== null && record.currentIndex !== null) {
2490
- mountOnItemChange(record.currentIndex, record.item, newViews, newBlockCards, outletParent, firstRootNode, this.viewContext);
2491
- // Solve the block-card DOMElement loss when moving nodes
2492
- newBlockCards[record.currentIndex]?.instance.append();
2493
- }
2494
- });
2495
- this.viewTypes = newViewTypes;
2496
- this.views = newViews;
2497
- this.contexts = newContexts;
2498
- this.children = children;
2499
- this.blockCards = newBlockCards;
2500
- if (parent === this.viewContext.editor) {
2501
- executeAfterViewInit(this.viewContext.editor);
2502
- }
2503
- }
2504
- else {
2505
- const newContexts = [];
2506
- this.children.forEach((child, _index) => {
2507
- NODE_TO_INDEX.set(child, firstIndex + _index);
2508
- NODE_TO_PARENT.set(child, parent);
2509
- let context = getContext(firstIndex + _index, child, parentPath, childrenContext, this.viewContext);
2510
- const previousContext = this.contexts[_index];
2511
- if (memoizedContext(this.viewContext, child, previousContext, context)) {
2512
- context = previousContext;
2513
- }
2514
- else {
2515
- updateContext(this.views[_index], context, this.viewContext);
2516
- }
2517
- newContexts.push(context);
2518
- });
2519
- this.contexts = newContexts;
2520
- }
2521
- if (preRenderingCount > 0) {
2522
- for (let i = 0; i < preRenderingCount; i++) {
2523
- const rootNodes = [...getRootNodes(this.views[i], this.blockCards[i])];
2524
- rootNodes.forEach(rootNode => {
2525
- rootNode.style = `position: absolute;top: -100%;`;
2526
- });
2527
- this.preRenderingHTMLElement.push(rootNodes);
2528
- }
2529
- }
2530
- }
2531
- destroy() {
2532
- this.children.forEach((element, index) => {
2533
- if (this.views[index]) {
2534
- this.views[index].destroy();
2535
- }
2536
- if (this.blockCards[index]) {
2537
- this.blockCards[index].destroy();
2538
- }
2539
- });
2540
- this.children = [];
2541
- this.views = [];
2542
- this.blockCards = [];
2543
- this.contexts = [];
2544
- this.viewTypes = [];
2545
- this.initialized = false;
2546
- this.differ = null;
2547
2277
  }
2548
- }
2549
- function getContext(index, item, parentPath, childrenContext, viewContext) {
2550
- if (Element.isElement(item)) {
2551
- const computedContext = getCommonContext(index, item, parentPath, viewContext, childrenContext);
2552
- const key = AngularEditor.findKey(viewContext.editor, item);
2553
- const isInline = viewContext.editor.isInline(item);
2554
- const isVoid = viewContext.editor.isVoid(item);
2555
- const elementContext = {
2556
- element: item,
2557
- ...computedContext,
2558
- attributes: {
2559
- 'data-slate-node': 'element',
2560
- 'data-slate-key': key.id
2561
- },
2562
- decorate: childrenContext.decorate,
2563
- readonly: childrenContext.readonly
2564
- };
2565
- if (isInline) {
2566
- elementContext.attributes['data-slate-inline'] = true;
2567
- }
2568
- if (isVoid) {
2569
- elementContext.attributes['data-slate-void'] = true;
2570
- }
2571
- // add contentEditable for block element only to avoid chinese input be broken
2572
- if (isVoid && !isInline) {
2573
- elementContext.contentEditable = false;
2574
- }
2575
- return elementContext;
2278
+ get context() {
2279
+ return this._context;
2576
2280
  }
2577
- else {
2578
- const computedContext = getCommonContext(index, item, parentPath, viewContext, childrenContext);
2579
- const isLeafBlock = AngularEditor.isLeafBlock(viewContext.editor, childrenContext.parent);
2580
- const textContext = {
2581
- decorations: computedContext.decorations,
2582
- isLast: isLeafBlock && index === childrenContext.parent.children.length - 1,
2583
- parent: childrenContext.parent,
2584
- text: item
2585
- };
2586
- return textContext;
2281
+ get editor() {
2282
+ return this.viewContext && this.viewContext.editor;
2587
2283
  }
2588
2284
  }
2589
- function getCommonContext(index, item, parentPath, viewContext, childrenContext) {
2590
- const p = parentPath.concat(index);
2591
- try {
2592
- const ds = childrenContext.decorate([item, p]);
2593
- // [list-render] performance optimization: reduce the number of calls to the `Editor.range(viewContext.editor, p)` method
2594
- if (childrenContext.selection || childrenContext.decorations.length > 0) {
2595
- const range = Editor.range(viewContext.editor, p);
2596
- const sel = childrenContext.selection && Range.intersection(range, childrenContext.selection);
2597
- for (const dec of childrenContext.decorations) {
2598
- const d = Range.intersection(dec, range);
2599
- if (d) {
2600
- ds.push(d);
2601
- }
2602
- }
2603
- return { selection: sel, decorations: ds };
2604
- }
2605
- else {
2606
- return { selection: null, decorations: ds };
2607
- }
2285
+
2286
+ const SLATE_BLOCK_CARD_CLASS_NAME = 'slate-block-card';
2287
+ class SlateBlockCard {
2288
+ onInit() {
2289
+ const nativeElement = document.createElement('div');
2290
+ nativeElement.classList.add(SLATE_BLOCK_CARD_CLASS_NAME);
2291
+ this.nativeElement = nativeElement;
2292
+ this.createContent();
2608
2293
  }
2609
- catch (error) {
2610
- viewContext.editor.onError({
2611
- code: SlateErrorCode.GetStartPointError,
2612
- nativeError: error
2613
- });
2614
- return { selection: null, decorations: [] };
2294
+ createContent() {
2295
+ const leftCaret = document.createElement('span');
2296
+ leftCaret.setAttribute(`card-target`, 'card-left');
2297
+ leftCaret.classList.add('card-left');
2298
+ leftCaret.appendChild(getZeroTextNode());
2299
+ const rightCaret = document.createElement('span');
2300
+ rightCaret.setAttribute(`card-target`, 'card-right');
2301
+ rightCaret.classList.add('card-right');
2302
+ rightCaret.appendChild(getZeroTextNode());
2303
+ const center = document.createElement('div');
2304
+ center.setAttribute(`card-target`, 'card-center');
2305
+ this.nativeElement.appendChild(leftCaret);
2306
+ this.nativeElement.appendChild(center);
2307
+ this.nativeElement.appendChild(rightCaret);
2308
+ this.centerContainer = center;
2615
2309
  }
2616
- }
2617
- function getViewType(item, parent, viewContext) {
2618
- if (Element.isElement(item)) {
2619
- return (viewContext.renderElement && viewContext.renderElement(item)) || DefaultElementFlavour;
2310
+ append() {
2311
+ this.centerRootNodes.forEach(rootNode => !this.centerContainer.contains(rootNode) && this.centerContainer.appendChild(rootNode));
2620
2312
  }
2621
- else {
2622
- const isVoid = viewContext.editor.isVoid(parent);
2623
- return isVoid ? VoidTextFlavour : (viewContext.renderText && viewContext.renderText(item)) || DefaultTextFlavour;
2313
+ initializeCenter(rootNodes) {
2314
+ this.centerRootNodes = rootNodes;
2315
+ this.append();
2316
+ }
2317
+ onDestroy() {
2318
+ this.nativeElement.remove();
2624
2319
  }
2625
2320
  }
2626
- function createBlockCard(item, view, viewContext) {
2627
- const isBlockCard = viewContext.editor.isBlockCard(item);
2628
- if (isBlockCard) {
2629
- const rootNodes = getRootNodes(view);
2630
- const blockCardRef = new BlockCardRef();
2631
- blockCardRef.instance = new SlateBlockCard();
2632
- blockCardRef.instance.onInit();
2633
- blockCardRef.instance.initializeCenter(rootNodes);
2634
- return blockCardRef;
2321
+ const getBlockCardByNativeElement = (nativeElement) => {
2322
+ const blockCardElement = nativeElement?.parentElement?.parentElement;
2323
+ if (blockCardElement && blockCardElement.classList.contains(SLATE_BLOCK_CARD_CLASS_NAME)) {
2324
+ return blockCardElement;
2635
2325
  }
2636
- else {
2637
- return null;
2326
+ return null;
2327
+ };
2328
+
2329
+ const DEFAULT_ELEMENT_HEIGHT = 24;
2330
+ class BaseElementFlavour extends BaseFlavour {
2331
+ constructor() {
2332
+ super(...arguments);
2333
+ this.getOutletParent = () => {
2334
+ return this.nativeElement;
2335
+ };
2336
+ this.getOutletElement = () => {
2337
+ return this.nativeElement.querySelector('.children-outlet');
2338
+ };
2339
+ this.stableHeight = null;
2638
2340
  }
2639
- }
2640
- function trackBy(viewContext) {
2641
- return (index, node) => {
2642
- return viewContext.trackBy(node) || AngularEditor.findKey(viewContext.editor, node);
2643
- };
2644
- }
2645
- function memoizedContext(viewContext, descendant, prev, next) {
2646
- if (Element.isElement(descendant)) {
2647
- return memoizedElementContext(viewContext, prev, next);
2341
+ get element() {
2342
+ return this._context && this._context.element;
2648
2343
  }
2649
- else {
2650
- return memoizedTextContext(prev, next);
2344
+ get selection() {
2345
+ return this._context && this._context.selection;
2651
2346
  }
2652
- }
2653
- function memoizedElementContext(viewContext, prev, next) {
2654
- return (prev.element === next.element &&
2655
- (!viewContext.isStrictDecorate || prev.decorate === next.decorate) &&
2656
- prev.readonly === next.readonly &&
2657
- isDecoratorRangeListEqual(prev.decorations, next.decorations) &&
2658
- (prev.selection === next.selection || (!!prev.selection && !!next.selection && Range.equals(prev.selection, next.selection))));
2659
- }
2660
- function memoizedTextContext(prev, next) {
2661
- return (next.parent === prev.parent &&
2662
- next.isLast === prev.isLast &&
2663
- next.text === prev.text &&
2664
- isDecoratorRangeListEqual(next.decorations, prev.decorations));
2665
- }
2666
- function addAfterViewInitQueue(editor, afterViewInitCallback) {
2667
- const queue = getAfterViewInitQueue(editor);
2668
- queue.push(afterViewInitCallback);
2669
- EDITOR_TO_AFTER_VIEW_INIT_QUEUE.set(editor, queue);
2670
- }
2671
- function getAfterViewInitQueue(editor) {
2672
- return EDITOR_TO_AFTER_VIEW_INIT_QUEUE.get(editor) || [];
2673
- }
2674
- function clearAfterViewInitQueue(editor) {
2675
- EDITOR_TO_AFTER_VIEW_INIT_QUEUE.set(editor, []);
2676
- }
2677
- function executeAfterViewInit(editor) {
2678
- const queue = getAfterViewInitQueue(editor);
2679
- queue.forEach(callback => callback());
2680
- clearAfterViewInitQueue(editor);
2681
- }
2682
-
2683
- class VirtualScrollDebugOverlay {
2684
- static { this.storageKey = 'slate_virtual_scroll_debug_overlay_state'; }
2685
- static { this.minWidth = 320; }
2686
- static { this.minHeight = 240; }
2687
- static { this.defaultWidth = 410; }
2688
- static { this.defaultHeight = 480; }
2689
- static getInstance(doc) {
2690
- if (!this.instance) {
2691
- this.instance = new VirtualScrollDebugOverlay(doc);
2347
+ get decorations() {
2348
+ return this._context && this._context.decorations;
2349
+ }
2350
+ get children() {
2351
+ return this._context && this._context.element.children;
2352
+ }
2353
+ get isCollapsed() {
2354
+ return this.selection && Range.isCollapsed(this.selection);
2355
+ }
2356
+ get isCollapsedAndNonReadonly() {
2357
+ return this.selection && Range.isCollapsed(this.selection) && !this.readonly;
2358
+ }
2359
+ get readonly() {
2360
+ return this._context && this._context.readonly;
2361
+ }
2362
+ onInit() {
2363
+ this.initialized = true;
2364
+ this.render();
2365
+ for (const key in this._context.attributes) {
2366
+ this.nativeElement.setAttribute(key, this._context.attributes[key]);
2692
2367
  }
2693
- this.instance.init();
2694
- return this.instance;
2368
+ this.updateWeakMap();
2369
+ this.listRender = new ListRender(this.viewContext, this.viewContainerRef, this.getOutletParent, this.getOutletElement);
2370
+ if (this.editor.isExpanded(this.element)) {
2371
+ this.listRender.initialize(this.children, this.element, this.childrenContext);
2372
+ }
2373
+ addAfterViewInitQueue(this.editor, () => {
2374
+ this.afterViewInit();
2375
+ });
2695
2376
  }
2696
- static log(doc, type, ...args) {
2697
- this.getInstance(doc).log(type, ...args);
2377
+ afterViewInit() {
2378
+ if (this._context.contentEditable !== undefined) {
2379
+ this.nativeElement.setAttribute('contenteditable', this._context.contentEditable + '');
2380
+ }
2698
2381
  }
2699
- static syncScrollTop(doc, value) {
2700
- const instance = this.getInstance(doc);
2701
- instance.setScrollTopValue(value);
2382
+ updateWeakMap() {
2383
+ NODE_TO_ELEMENT.set(this.element, this.nativeElement);
2384
+ ELEMENT_TO_NODE.set(this.nativeElement, this.element);
2385
+ ELEMENT_TO_COMPONENT.set(this.element, this);
2702
2386
  }
2703
- constructor(doc) {
2704
- this.doc = doc;
2705
- this.state = {
2706
- left: 16,
2707
- top: 16,
2708
- collapsed: false,
2709
- width: VirtualScrollDebugOverlay.defaultWidth,
2710
- height: VirtualScrollDebugOverlay.defaultHeight
2711
- };
2712
- this.originalConsoleLog = console.log.bind(console);
2713
- this.originalConsoleWarn = console.warn.bind(console);
2714
- this.dragOffsetX = 0;
2715
- this.dragOffsetY = 0;
2716
- this.isDragging = false;
2717
- this.isResizing = false;
2718
- this.resizeStartX = 0;
2719
- this.resizeStartY = 0;
2720
- this.resizeStartWidth = 0;
2721
- this.resizeStartHeight = 0;
2722
- this.dragMoved = false;
2723
- this.wasDragged = false;
2724
- this.onDragging = (event) => {
2725
- if (!this.isDragging || !this.container) {
2726
- return;
2727
- }
2728
- this.dragMoved = true;
2729
- const nextLeft = event.clientX - this.dragOffsetX;
2730
- const nextTop = event.clientY - this.dragOffsetY;
2731
- this.container.style.left = `${nextLeft}px`;
2732
- this.container.style.top = `${nextTop}px`;
2733
- this.container.style.right = 'auto';
2734
- this.container.style.bottom = 'auto';
2735
- };
2736
- this.onDragEnd = () => {
2737
- if (!this.isDragging) {
2738
- return;
2739
- }
2740
- this.isDragging = false;
2741
- this.wasDragged = this.dragMoved;
2742
- this.dragMoved = false;
2743
- this.doc.removeEventListener('mousemove', this.onDragging);
2744
- this.doc.removeEventListener('mouseup', this.onDragEnd);
2745
- if (this.container) {
2746
- const rect = this.container.getBoundingClientRect();
2747
- this.state.left = rect.left;
2748
- this.state.top = rect.top;
2749
- this.persistState();
2750
- this.container.style.transition = '';
2751
- }
2752
- };
2753
- this.onResizing = (event) => {
2754
- if (!this.isResizing || !this.container) {
2755
- return;
2756
- }
2757
- const deltaX = event.clientX - this.resizeStartX;
2758
- const deltaY = event.clientY - this.resizeStartY;
2759
- const nextWidth = Math.max(VirtualScrollDebugOverlay.minWidth, this.resizeStartWidth + deltaX);
2760
- const nextHeight = Math.max(VirtualScrollDebugOverlay.minHeight, this.resizeStartHeight + deltaY);
2761
- this.state.width = nextWidth;
2762
- this.state.height = nextHeight;
2763
- this.applySize();
2764
- };
2765
- this.onResizeEnd = () => {
2766
- if (!this.isResizing) {
2767
- return;
2387
+ onDestroy() {
2388
+ if (NODE_TO_ELEMENT.get(this.element) === this.nativeElement) {
2389
+ NODE_TO_ELEMENT.delete(this.element);
2390
+ }
2391
+ ELEMENT_TO_NODE.delete(this.nativeElement);
2392
+ if (ELEMENT_TO_COMPONENT.get(this.element) === this) {
2393
+ ELEMENT_TO_COMPONENT.delete(this.element);
2394
+ }
2395
+ this.listRender.destroy();
2396
+ this.nativeElement?.remove();
2397
+ }
2398
+ onContextChange() {
2399
+ this.childrenContext = this.getChildrenContext();
2400
+ if (!this.initialized) {
2401
+ return;
2402
+ }
2403
+ this.rerender();
2404
+ this.updateWeakMap();
2405
+ this.updateChildrenView();
2406
+ }
2407
+ updateChildrenView() {
2408
+ if (this.editor.isExpanded(this.element)) {
2409
+ this.listRender.update(this.children, this.element, this.childrenContext);
2410
+ }
2411
+ else {
2412
+ if (this.listRender.initialized) {
2413
+ this.listRender.destroy();
2768
2414
  }
2769
- this.isResizing = false;
2770
- this.doc.removeEventListener('mousemove', this.onResizing);
2771
- this.doc.removeEventListener('mouseup', this.onResizeEnd);
2772
- this.persistState();
2415
+ }
2416
+ }
2417
+ getChildrenContext() {
2418
+ return {
2419
+ parent: this._context.element,
2420
+ selection: this._context.selection,
2421
+ decorations: this._context.decorations,
2422
+ decorate: this._context.decorate,
2423
+ readonly: this._context.readonly
2773
2424
  };
2774
2425
  }
2775
- init() {
2776
- if (!this.container) {
2777
- this.createContainer();
2426
+ isStableHeight() {
2427
+ return false;
2428
+ }
2429
+ getRealHeight() {
2430
+ if (this.isStableHeight() && this.stableHeight !== null) {
2431
+ return this.stableHeight;
2778
2432
  }
2779
- else {
2780
- this.applyState();
2433
+ const blockCard = getBlockCardByNativeElement(this.nativeElement);
2434
+ const target = blockCard || this.nativeElement;
2435
+ const computedStyle = getComputedStyle(target);
2436
+ const height = Math.ceil(target.getBoundingClientRect().height) + parseFloat(computedStyle.marginTop) + parseFloat(computedStyle.marginBottom);
2437
+ if (this.isStableHeight()) {
2438
+ this.stableHeight = height;
2781
2439
  }
2440
+ return height;
2782
2441
  }
2783
- log(type, ...args) {
2784
- this.init();
2785
- if (type === 'warn') {
2786
- this.originalConsoleWarn(...args);
2442
+ }
2443
+
2444
+ class DefaultElementFlavour extends BaseElementFlavour {
2445
+ render() {
2446
+ const nativeElement = document.createElement('div');
2447
+ this.nativeElement = nativeElement;
2448
+ }
2449
+ rerender() {
2450
+ // No-op
2451
+ }
2452
+ }
2453
+
2454
+ class BaseLeafFlavour extends BaseFlavour {
2455
+ get text() {
2456
+ return this.context && this.context.text;
2457
+ }
2458
+ get leaf() {
2459
+ return this.context && this.context.leaf;
2460
+ }
2461
+ onInit() {
2462
+ this.render();
2463
+ this.renderPlaceholder();
2464
+ this.initialized = true;
2465
+ }
2466
+ onContextChange() {
2467
+ if (!this.initialized) {
2468
+ return;
2469
+ }
2470
+ this.rerender();
2471
+ this.renderPlaceholder();
2472
+ }
2473
+ renderPlaceholder() {
2474
+ // issue-1: IME input was interrupted
2475
+ // issue-2: IME input focus jumping
2476
+ // Issue occurs when the span node of the placeholder is before the slateString span node
2477
+ if (this.context.leaf['placeholder']) {
2478
+ if (!this.placeholderElement) {
2479
+ this.createPlaceholder();
2480
+ }
2481
+ this.updatePlaceholder();
2787
2482
  }
2788
2483
  else {
2789
- this.originalConsoleLog(...args);
2484
+ this.destroyPlaceholder();
2790
2485
  }
2791
- this.appendLog(type, ...args);
2792
2486
  }
2793
- dispose() {
2794
- this.container?.remove();
2795
- this.container = undefined;
2796
- this.contentWrapper = undefined;
2797
- this.bubble = undefined;
2798
- this.logList = undefined;
2799
- this.selectorInput = undefined;
2800
- this.distanceInput = undefined;
2801
- this.collapseToggle = undefined;
2802
- this.resizeHandle = undefined;
2803
- this.doc.removeEventListener('mousemove', this.onDragging);
2804
- this.doc.removeEventListener('mouseup', this.onDragEnd);
2805
- this.doc.removeEventListener('mousemove', this.onResizing);
2806
- this.doc.removeEventListener('mouseup', this.onResizeEnd);
2807
- this.isDragging = false;
2808
- this.isResizing = false;
2809
- }
2810
- createContainer() {
2811
- this.loadState();
2812
- const doc = this.doc;
2813
- const container = doc.createElement('div');
2814
- container.style.position = 'fixed';
2815
- container.style.right = 'auto';
2816
- container.style.bottom = 'auto';
2817
- container.style.boxSizing = 'border-box';
2818
- container.style.background = 'rgba(17, 24, 39, 0.95)';
2819
- container.style.color = '#e5e7eb';
2820
- container.style.fontSize = '12px';
2821
- container.style.border = '1px solid #1f2937';
2822
- container.style.borderRadius = '10px';
2823
- container.style.fontFamily = 'Menlo, Consolas, monospace';
2824
- container.style.boxShadow = '0 10px 30px rgba(0, 0, 0, 0.35)';
2825
- container.style.zIndex = '9999';
2826
- container.style.display = 'flex';
2827
- container.style.flexDirection = 'column';
2828
- container.style.gap = '10px';
2829
- container.style.minWidth = `${VirtualScrollDebugOverlay.minWidth}px`;
2830
- container.style.minHeight = `${VirtualScrollDebugOverlay.minHeight}px`;
2831
- container.style.maxHeight = '80vh';
2832
- container.style.cursor = 'default';
2833
- container.addEventListener('mousedown', event => {
2834
- if (this.state.collapsed) {
2835
- this.startDrag(event);
2836
- }
2837
- });
2838
- const header = doc.createElement('div');
2839
- header.style.display = 'flex';
2840
- header.style.alignItems = 'center';
2841
- header.style.justifyContent = 'space-between';
2842
- header.style.cursor = 'move';
2843
- header.addEventListener('mousedown', event => {
2844
- this.startDrag(event);
2845
- });
2846
- const title = doc.createElement('div');
2847
- title.textContent = 'Virtual Scroll Debug';
2848
- title.style.fontWeight = '600';
2849
- title.style.letterSpacing = '0.3px';
2850
- const actions = doc.createElement('div');
2851
- actions.style.display = 'flex';
2852
- actions.style.gap = '6px';
2853
- const collapseButton = doc.createElement('button');
2854
- collapseButton.type = 'button';
2855
- collapseButton.textContent = '折叠';
2856
- collapseButton.style.background = '#1f2937';
2857
- collapseButton.style.color = '#e5e7eb';
2858
- collapseButton.style.border = '1px solid #374151';
2859
- collapseButton.style.borderRadius = '6px';
2860
- collapseButton.style.padding = '4px 8px';
2861
- collapseButton.style.cursor = 'pointer';
2862
- collapseButton.addEventListener('click', () => {
2863
- this.setCollapsed(!this.state.collapsed);
2864
- });
2865
- const clearButton = doc.createElement('button');
2866
- clearButton.type = 'button';
2867
- clearButton.textContent = '清除日志';
2868
- clearButton.style.background = '#374151';
2869
- clearButton.style.color = '#e5e7eb';
2870
- clearButton.style.border = '1px solid #4b5563';
2871
- clearButton.style.borderRadius = '6px';
2872
- clearButton.style.padding = '4px 10px';
2873
- clearButton.style.cursor = 'pointer';
2874
- clearButton.addEventListener('click', () => {
2875
- if (this.logList) {
2876
- this.logList.innerHTML = '';
2877
- }
2878
- });
2879
- actions.appendChild(collapseButton);
2880
- actions.appendChild(clearButton);
2881
- header.appendChild(title);
2882
- header.appendChild(actions);
2883
- const scrollForm = doc.createElement('div');
2884
- scrollForm.style.display = 'grid';
2885
- scrollForm.style.gridTemplateColumns = '1fr 110px 50px';
2886
- scrollForm.style.gap = '6px';
2887
- scrollForm.style.alignItems = 'center';
2888
- const selectorInput = doc.createElement('input');
2889
- selectorInput.placeholder = '滚动容器 selector';
2890
- selectorInput.style.padding = '6px 8px';
2891
- selectorInput.style.borderRadius = '6px';
2892
- selectorInput.style.border = '1px solid #4b5563';
2893
- selectorInput.style.background = '#111827';
2894
- selectorInput.style.color = '#e5e7eb';
2895
- selectorInput.autocomplete = 'off';
2896
- const distanceInput = doc.createElement('input');
2897
- distanceInput.placeholder = '滚动距离(px)';
2898
- distanceInput.type = 'number';
2899
- distanceInput.style.padding = '6px 8px';
2900
- distanceInput.style.borderRadius = '6px';
2901
- distanceInput.style.border = '1px solid #4b5563';
2902
- distanceInput.style.background = '#111827';
2903
- distanceInput.style.color = '#e5e7eb';
2904
- const scrollButton = doc.createElement('button');
2905
- scrollButton.type = 'button';
2906
- scrollButton.textContent = '滚动';
2907
- scrollButton.style.background = '#10b981';
2908
- scrollButton.style.color = '#0b1c15';
2909
- scrollButton.style.border = 'none';
2910
- scrollButton.style.borderRadius = '6px';
2911
- scrollButton.style.padding = '6px 10px';
2912
- scrollButton.style.cursor = 'pointer';
2913
- scrollButton.addEventListener('click', () => {
2914
- const selector = selectorInput.value.trim();
2915
- const distanceValue = Number(distanceInput.value ?? 0);
2916
- const distance = Number.isFinite(distanceValue) ? distanceValue : 0;
2917
- if (!selector) {
2918
- this.log('warn', '请先填写滚动容器 selector');
2919
- return;
2920
- }
2921
- const target = doc.querySelector(selector);
2922
- if (!target) {
2923
- this.log('warn', `未找到滚动容器: ${selector}`);
2924
- return;
2925
- }
2926
- if (typeof target.scrollTo === 'function') {
2927
- target.scrollTo({ top: distance, behavior: 'auto' });
2928
- }
2929
- else if (Object.prototype.hasOwnProperty.call(target, 'scrollTop')) {
2930
- target.scrollTop = distance;
2931
- }
2932
- else {
2933
- this.log('warn', '目标元素不支持滚动:', selector);
2934
- return;
2487
+ createPlaceholder() {
2488
+ const placeholderElement = document.createElement('span');
2489
+ placeholderElement.innerText = this.context.leaf['placeholder'];
2490
+ placeholderElement.contentEditable = 'false';
2491
+ placeholderElement.setAttribute('data-slate-placeholder', 'true');
2492
+ this.placeholderElement = placeholderElement;
2493
+ this.nativeElement.classList.add('leaf-with-placeholder');
2494
+ this.nativeElement.appendChild(placeholderElement);
2495
+ setTimeout(() => {
2496
+ const editorElement = this.nativeElement.closest('.the-editor-typo');
2497
+ const editorContentHeight = getContentHeight(editorElement);
2498
+ if (editorContentHeight > 0) {
2499
+ // Not supported webkitLineClamp exceeds height hiding
2500
+ placeholderElement.style.maxHeight = `${editorContentHeight}px`;
2935
2501
  }
2936
- this.log('log', `已将 ${selector} 滚动到`, distance);
2502
+ const lineClamp = Math.floor(editorContentHeight / this.nativeElement.offsetHeight) || 0;
2503
+ placeholderElement.style.webkitLineClamp = `${Math.max(lineClamp, 1)}`;
2937
2504
  });
2938
- scrollForm.appendChild(selectorInput);
2939
- scrollForm.appendChild(distanceInput);
2940
- scrollForm.appendChild(scrollButton);
2941
- const logList = doc.createElement('div');
2942
- logList.style.height = '260px';
2943
- logList.style.overflowY = 'auto';
2944
- logList.style.background = '#0b1220';
2945
- logList.style.border = '1px solid #1f2937';
2946
- logList.style.borderRadius = '8px';
2947
- logList.style.padding = '8px';
2948
- logList.style.display = 'flex';
2949
- logList.style.flexDirection = 'column';
2950
- logList.style.gap = '6px';
2951
- logList.style.flex = '1';
2952
- logList.style.minHeight = '160px';
2953
- const bubble = doc.createElement('div');
2954
- bubble.textContent = 'VS';
2955
- bubble.style.display = 'none';
2956
- bubble.style.alignItems = 'center';
2957
- bubble.style.justifyContent = 'center';
2958
- bubble.style.fontWeight = '700';
2959
- bubble.style.fontSize = '14px';
2960
- bubble.style.letterSpacing = '0.5px';
2961
- bubble.style.width = '100%';
2962
- bubble.style.height = '100%';
2963
- bubble.style.userSelect = 'none';
2964
- bubble.addEventListener('click', () => {
2965
- if (this.wasDragged) {
2966
- this.wasDragged = false;
2967
- return;
2968
- }
2969
- this.setCollapsed(false);
2505
+ }
2506
+ updatePlaceholder() {
2507
+ if (this.placeholderElement.innerText !== this.context.leaf['placeholder']) {
2508
+ this.placeholderElement.innerText = this.context.leaf['placeholder'];
2509
+ }
2510
+ }
2511
+ destroyPlaceholder() {
2512
+ if (this.placeholderElement) {
2513
+ this.placeholderElement.remove();
2514
+ this.placeholderElement = null;
2515
+ this.nativeElement.classList.remove('leaf-with-placeholder');
2516
+ }
2517
+ }
2518
+ onDestroy() {
2519
+ this.nativeElement?.remove();
2520
+ }
2521
+ }
2522
+
2523
+ var StringType;
2524
+ (function (StringType) {
2525
+ StringType["normalString"] = "normalString";
2526
+ StringType["lineBreakEmptyString"] = "lineBreakEmptyString";
2527
+ StringType["normalEmptyText"] = "normalEmptyText";
2528
+ StringType["compatibleString"] = "compatibleString";
2529
+ StringType["voidString"] = "voidString";
2530
+ })(StringType || (StringType = {}));
2531
+ class SlateStringRender {
2532
+ constructor(context, viewContext) {
2533
+ this.context = context;
2534
+ this.viewContext = viewContext;
2535
+ }
2536
+ // COMPAT: If the text is empty, it's because it's on the edge of an inline
2537
+ // node, so we render a zero-width space so that the selection can be
2538
+ // inserted next to it still.
2539
+ isEmptyText() {
2540
+ return this.context.leaf.text === '';
2541
+ }
2542
+ // COMPAT: Browsers will collapse trailing new lines at the end of blocks,
2543
+ // so we need to add an extra trailing new lines to prevent that.
2544
+ isCompatibleString() {
2545
+ return this.context.isLast && this.context.leaf.text.slice(-1) === '\n';
2546
+ }
2547
+ // COMPAT: Render text inside void nodes with a zero-width space.
2548
+ // So the node can contain selection but the text is not visible.
2549
+ isVoid() {
2550
+ return this.viewContext.editor.isVoid(this.context.parent);
2551
+ }
2552
+ get leaf() {
2553
+ return this.context && this.context.leaf;
2554
+ }
2555
+ // COMPAT: If this is the last text node in an empty block, render a zero-
2556
+ // width space that will convert into a line break when copying and pasting
2557
+ // to support expected plain text.
2558
+ isLineBreakEmptyString() {
2559
+ return (this.context.leaf.text === '' &&
2560
+ this.context.parent.children[this.context.parent.children.length - 1] === this.context.text &&
2561
+ !this.viewContext.editor.isInline(this.context.parent) &&
2562
+ // [list-render] performance optimization: reduce the number of calls to the `Editor.string(editor, path)` method
2563
+ isEmpty(this.viewContext.editor, this.context.parent));
2564
+ }
2565
+ createStringNode(type) {
2566
+ let newNativeElement;
2567
+ switch (type) {
2568
+ case StringType.lineBreakEmptyString:
2569
+ newNativeElement = createLineBreakEmptyStringDOM(this.getElementStringLength());
2570
+ break;
2571
+ case StringType.voidString:
2572
+ case StringType.normalEmptyText:
2573
+ newNativeElement = createEmptyOrVoidStringNode();
2574
+ break;
2575
+ case StringType.compatibleString:
2576
+ newNativeElement = createCompatibleStringNode(this.leaf.text);
2577
+ break;
2578
+ case StringType.normalString:
2579
+ newNativeElement = createDefaultStringNode(this.leaf.text);
2580
+ break;
2581
+ default:
2582
+ newNativeElement = createDefaultStringNode(this.leaf.text);
2583
+ }
2584
+ return newNativeElement;
2585
+ }
2586
+ render() {
2587
+ this.type = this.getType();
2588
+ this.nativeElement = this.createStringNode(this.type);
2589
+ return this.nativeElement;
2590
+ }
2591
+ getType() {
2592
+ if (this.isLineBreakEmptyString()) {
2593
+ return StringType.lineBreakEmptyString;
2594
+ }
2595
+ if (this.isVoid()) {
2596
+ return StringType.voidString;
2597
+ }
2598
+ if (this.isEmptyText()) {
2599
+ return StringType.normalEmptyText;
2600
+ }
2601
+ if (this.isCompatibleString()) {
2602
+ return StringType.compatibleString;
2603
+ }
2604
+ return StringType.normalString;
2605
+ }
2606
+ update(context, viewContext) {
2607
+ this.context = context;
2608
+ this.viewContext = viewContext;
2609
+ const type = this.getType();
2610
+ if (type !== this.type) {
2611
+ const newNativeElement = this.createStringNode(type);
2612
+ this.nativeElement.replaceWith(newNativeElement);
2613
+ this.nativeElement = newNativeElement;
2614
+ this.type = type;
2615
+ return;
2616
+ }
2617
+ if (this.type === StringType.normalString) {
2618
+ this.nativeElement.textContent = this.leaf.text;
2619
+ }
2620
+ }
2621
+ getElementStringLength() {
2622
+ return Node.string(this.context.parent).length;
2623
+ }
2624
+ }
2625
+ const createDefaultStringNode = (text) => {
2626
+ const stringNode = document.createElement('span');
2627
+ stringNode.textContent = text;
2628
+ stringNode.setAttribute('data-slate-string', 'true');
2629
+ stringNode.setAttribute('editable-text', '');
2630
+ return stringNode;
2631
+ };
2632
+ const createEmptyOrVoidStringNode = () => {
2633
+ const stringNode = document.createElement('span');
2634
+ stringNode.setAttribute('data-slate-string', 'true');
2635
+ stringNode.setAttribute('data-slate-zero-width', 'z');
2636
+ stringNode.setAttribute('data-slate-length', '0');
2637
+ const zeroWidthSpace = getZeroTextNode();
2638
+ stringNode.appendChild(zeroWidthSpace);
2639
+ stringNode.setAttribute('editable-text', '');
2640
+ return stringNode;
2641
+ };
2642
+ const createCompatibleStringNode = (text) => {
2643
+ const stringNode = document.createElement('span');
2644
+ stringNode.setAttribute('data-slate-string', 'true');
2645
+ stringNode.textContent = text;
2646
+ stringNode.setAttribute('editable-text', '');
2647
+ const zeroWidthSpan = document.createElement('span');
2648
+ const zeroWidthSpace = getZeroTextNode();
2649
+ zeroWidthSpan.setAttribute('data-slate-zero-width', '');
2650
+ zeroWidthSpan.appendChild(zeroWidthSpace);
2651
+ stringNode.appendChild(zeroWidthSpan);
2652
+ return stringNode;
2653
+ };
2654
+ const createLineBreakEmptyStringDOM = (elementStringLength) => {
2655
+ const stringNode = document.createElement('span');
2656
+ stringNode.setAttribute('data-slate-zero-width', 'n');
2657
+ stringNode.setAttribute('data-slate-length', `${elementStringLength}`);
2658
+ const zeroWidthSpace = getZeroTextNode();
2659
+ stringNode.appendChild(zeroWidthSpace);
2660
+ const brNode = document.createElement('br');
2661
+ stringNode.appendChild(brNode);
2662
+ stringNode.setAttribute('editable-text', '');
2663
+ return stringNode;
2664
+ };
2665
+ /**
2666
+ * TODO: remove when bump slate
2667
+ * copy from slate
2668
+ * @param editor
2669
+ * @param element
2670
+ * @returns
2671
+ */
2672
+ const isEmpty = (editor, element) => {
2673
+ const { children } = element;
2674
+ const [first] = children;
2675
+ return children.length === 0 || (children.length === 1 && Text$1.isText(first) && first.text === '' && !editor.isVoid(element));
2676
+ };
2677
+
2678
+ class DefaultLeafFlavour extends BaseLeafFlavour {
2679
+ constructor() {
2680
+ super(...arguments);
2681
+ this.stringRender = null;
2682
+ }
2683
+ render() {
2684
+ const leafNode = createDefaultLeafNode();
2685
+ this.stringRender = new SlateStringRender(this.context, this.viewContext);
2686
+ const stringNode = this.stringRender.render();
2687
+ leafNode.appendChild(stringNode);
2688
+ this.nativeElement = leafNode;
2689
+ }
2690
+ rerender() {
2691
+ this.stringRender?.update(this.context, this.viewContext);
2692
+ }
2693
+ }
2694
+ const createDefaultLeafNode = () => {
2695
+ const span = document.createElement('span');
2696
+ span.setAttribute('data-slate-leaf', 'true');
2697
+ return span;
2698
+ };
2699
+
2700
+ class LeavesRender {
2701
+ constructor(viewContext, viewContainerRef, getOutletParent, getOutletElement) {
2702
+ this.viewContext = viewContext;
2703
+ this.viewContainerRef = viewContainerRef;
2704
+ this.getOutletParent = getOutletParent;
2705
+ this.getOutletElement = getOutletElement;
2706
+ this.views = [];
2707
+ this.contexts = [];
2708
+ this.viewTypes = [];
2709
+ }
2710
+ initialize(context) {
2711
+ const { decoratedLeaves, contexts } = this.getLeaves(context);
2712
+ this.decoratedLeaves = decoratedLeaves;
2713
+ this.contexts = contexts;
2714
+ this.decoratedLeaves.forEach((leaf, index) => {
2715
+ const context = getContext$1(index, this.contexts);
2716
+ const viewType = getViewType$1(context, this.viewContext);
2717
+ const view = createEmbeddedViewOrComponentOrFlavour(viewType, context, this.viewContext, this.viewContainerRef);
2718
+ this.views.push(view);
2719
+ this.contexts.push(context);
2720
+ this.viewTypes.push(viewType);
2970
2721
  });
2971
- const contentWrapper = doc.createElement('div');
2972
- contentWrapper.style.display = 'flex';
2973
- contentWrapper.style.flexDirection = 'column';
2974
- contentWrapper.style.gap = '10px';
2975
- contentWrapper.style.width = '100%';
2976
- contentWrapper.style.height = '100%';
2977
- contentWrapper.appendChild(header);
2978
- contentWrapper.appendChild(scrollForm);
2979
- contentWrapper.appendChild(logList);
2980
- container.appendChild(contentWrapper);
2981
- container.appendChild(bubble);
2982
- const resizeHandle = doc.createElement('div');
2983
- resizeHandle.style.position = 'absolute';
2984
- resizeHandle.style.right = '6px';
2985
- resizeHandle.style.bottom = '6px';
2986
- resizeHandle.style.width = '14px';
2987
- resizeHandle.style.height = '14px';
2988
- resizeHandle.style.cursor = 'nwse-resize';
2989
- resizeHandle.style.borderRight = '2px solid #4b5563';
2990
- resizeHandle.style.borderBottom = '2px solid #4b5563';
2991
- resizeHandle.addEventListener('mousedown', event => {
2992
- this.startResize(event);
2722
+ mount(this.views, null, this.getOutletParent(), this.getOutletElement());
2723
+ const newDiffers = this.viewContext.editor.injector.get(IterableDiffers);
2724
+ this.differ = newDiffers.find(this.decoratedLeaves).create(trackBy$1(this.viewContext));
2725
+ this.differ.diff(this.decoratedLeaves);
2726
+ }
2727
+ update(context) {
2728
+ const { decoratedLeaves, contexts } = this.getLeaves(context);
2729
+ const outletParent = this.getOutletParent();
2730
+ const diffResult = this.differ.diff(decoratedLeaves);
2731
+ if (diffResult) {
2732
+ let firstRootNode = getRootNodes(this.views[0])[0];
2733
+ const newContexts = [];
2734
+ const newViewTypes = [];
2735
+ const newViews = [];
2736
+ diffResult.forEachItem(record => {
2737
+ let context = getContext$1(record.currentIndex, contexts);
2738
+ const viewType = getViewType$1(context, this.viewContext);
2739
+ newViewTypes.push(viewType);
2740
+ let view;
2741
+ if (record.previousIndex === null) {
2742
+ view = createEmbeddedViewOrComponentOrFlavour(viewType, context, this.viewContext, this.viewContainerRef);
2743
+ newContexts.push(context);
2744
+ newViews.push(view);
2745
+ mountOnItemChange(record.currentIndex, record.item, newViews, null, outletParent, firstRootNode, this.viewContext);
2746
+ }
2747
+ else {
2748
+ const previousView = this.views[record.previousIndex];
2749
+ const previousViewType = this.viewTypes[record.previousIndex];
2750
+ if (previousViewType !== viewType) {
2751
+ view = createEmbeddedViewOrComponentOrFlavour(viewType, context, this.viewContext, this.viewContainerRef);
2752
+ const firstRootNode = getRootNodes(previousView, null)[0];
2753
+ const newRootNodes = getRootNodes(view, null);
2754
+ firstRootNode.replaceWith(...newRootNodes);
2755
+ previousView.destroy();
2756
+ }
2757
+ else {
2758
+ view = previousView;
2759
+ updateContext(previousView, context, this.viewContext);
2760
+ }
2761
+ newContexts.push(context);
2762
+ newViews.push(view);
2763
+ }
2764
+ });
2765
+ diffResult.forEachRemovedItem(record => {
2766
+ const view = this.views[record.previousIndex];
2767
+ view.destroy();
2768
+ });
2769
+ diffResult.forEachMovedItem(record => {
2770
+ mountOnItemChange(record.currentIndex, record.item, newViews, null, outletParent, firstRootNode, this.viewContext);
2771
+ });
2772
+ this.viewTypes = newViewTypes;
2773
+ this.views = newViews;
2774
+ this.contexts = newContexts;
2775
+ this.decoratedLeaves = decoratedLeaves;
2776
+ }
2777
+ }
2778
+ getLeaves(context) {
2779
+ const decoratedLeaves = Text$1.decorations(context.text, context.decorations);
2780
+ const contexts = decoratedLeaves.map((decoratedLeaf, index) => {
2781
+ return {
2782
+ leaf: decoratedLeaf.leaf,
2783
+ leafPosition: decoratedLeaf.position,
2784
+ text: context.text,
2785
+ parent: context.parent,
2786
+ index,
2787
+ isLast: context.isLast && index === decoratedLeaves.length - 1
2788
+ };
2993
2789
  });
2994
- container.appendChild(resizeHandle);
2995
- doc.body.appendChild(container);
2996
- this.container = container;
2997
- this.contentWrapper = contentWrapper;
2998
- this.bubble = bubble;
2999
- this.logList = logList;
3000
- this.selectorInput = selectorInput;
3001
- this.distanceInput = distanceInput;
3002
- this.collapseToggle = collapseButton;
3003
- this.resizeHandle = resizeHandle;
3004
- this.applyState();
2790
+ return { decoratedLeaves, contexts };
2791
+ }
2792
+ destroy() {
2793
+ this.views.forEach(view => view.destroy());
2794
+ }
2795
+ }
2796
+ function getContext$1(index, leafContexts) {
2797
+ return leafContexts[index];
2798
+ }
2799
+ function getViewType$1(leafContext, viewContext) {
2800
+ return (viewContext.renderLeaf && viewContext.renderLeaf(leafContext.leaf)) || DefaultLeafFlavour;
2801
+ }
2802
+ function trackBy$1(viewContext) {
2803
+ return (index, node) => {
2804
+ return index;
2805
+ };
2806
+ }
2807
+
2808
+ class BaseTextFlavour extends BaseFlavour {
2809
+ constructor() {
2810
+ super(...arguments);
2811
+ this.getOutletParent = () => {
2812
+ return this.nativeElement;
2813
+ };
2814
+ this.getOutletElement = () => {
2815
+ return this.nativeElement.querySelector('.children-outlet');
2816
+ };
3005
2817
  }
3006
- startDrag(event) {
3007
- if (event.button !== 0) {
3008
- return;
3009
- }
3010
- if (!this.container) {
3011
- return;
3012
- }
3013
- const rect = this.container.getBoundingClientRect();
3014
- this.isDragging = true;
3015
- this.wasDragged = false;
3016
- this.dragMoved = false;
3017
- this.dragOffsetX = event.clientX - rect.left;
3018
- this.dragOffsetY = event.clientY - rect.top;
3019
- this.container.style.transition = 'none';
3020
- this.doc.addEventListener('mousemove', this.onDragging);
3021
- this.doc.addEventListener('mouseup', this.onDragEnd);
3022
- if (!this.state.collapsed) {
3023
- event.preventDefault();
3024
- }
2818
+ get text() {
2819
+ return this._context && this._context.text;
3025
2820
  }
3026
- startResize(event) {
3027
- if (event.button !== 0 || this.state.collapsed) {
3028
- return;
3029
- }
3030
- if (!this.container) {
3031
- return;
2821
+ onInit() {
2822
+ this.render();
2823
+ this.updateWeakMap();
2824
+ this.initialized = true;
2825
+ this.leavesRender = new LeavesRender(this.viewContext, this.viewContainerRef, this.getOutletParent, this.getOutletElement);
2826
+ this.leavesRender.initialize(this.context);
2827
+ }
2828
+ updateWeakMap() {
2829
+ ELEMENT_TO_NODE.set(this.nativeElement, this.text);
2830
+ NODE_TO_ELEMENT.set(this.text, this.nativeElement);
2831
+ }
2832
+ onDestroy() {
2833
+ if (NODE_TO_ELEMENT.get(this.text) === this.nativeElement) {
2834
+ NODE_TO_ELEMENT.delete(this.text);
3032
2835
  }
3033
- const rect = this.container.getBoundingClientRect();
3034
- this.isResizing = true;
3035
- this.resizeStartX = event.clientX;
3036
- this.resizeStartY = event.clientY;
3037
- this.resizeStartWidth = rect.width;
3038
- this.resizeStartHeight = rect.height;
3039
- this.doc.addEventListener('mousemove', this.onResizing);
3040
- this.doc.addEventListener('mouseup', this.onResizeEnd);
3041
- event.preventDefault();
3042
- event.stopPropagation();
2836
+ ELEMENT_TO_NODE.delete(this.nativeElement);
2837
+ this.leavesRender.destroy();
2838
+ this.nativeElement?.remove();
3043
2839
  }
3044
- applyPosition() {
3045
- if (!this.container) {
2840
+ onContextChange() {
2841
+ if (!this.initialized) {
3046
2842
  return;
3047
2843
  }
3048
- this.container.style.left = `${this.state.left}px`;
3049
- this.container.style.top = `${this.state.top}px`;
2844
+ this.rerender();
2845
+ this.updateWeakMap();
2846
+ this.leavesRender.update(this.context);
3050
2847
  }
3051
- applySize() {
3052
- if (!this.container) {
3053
- return;
2848
+ }
2849
+
2850
+ class DefaultTextFlavour extends BaseTextFlavour {
2851
+ render() {
2852
+ const { nativeElement } = createText(this.text.text);
2853
+ this.nativeElement = nativeElement;
2854
+ }
2855
+ rerender() { }
2856
+ }
2857
+ class VoidTextFlavour extends BaseTextFlavour {
2858
+ render() {
2859
+ const { nativeElement } = createText(this.text.text);
2860
+ this.nativeElement = nativeElement;
2861
+ this.nativeElement.setAttribute('data-slate-spacer', 'true');
2862
+ this.nativeElement.classList.add('slate-spacer');
2863
+ }
2864
+ rerender() { }
2865
+ }
2866
+ const createText = (text) => {
2867
+ const nativeElement = document.createElement('span');
2868
+ nativeElement.setAttribute('data-slate-node', 'text');
2869
+ return { nativeElement };
2870
+ };
2871
+
2872
+ class ListRender {
2873
+ constructor(viewContext, viewContainerRef, getOutletParent, getOutletElement) {
2874
+ this.viewContext = viewContext;
2875
+ this.viewContainerRef = viewContainerRef;
2876
+ this.getOutletParent = getOutletParent;
2877
+ this.getOutletElement = getOutletElement;
2878
+ this.children = [];
2879
+ this.views = [];
2880
+ this.blockCards = [];
2881
+ this.contexts = [];
2882
+ this.viewTypes = [];
2883
+ this.differ = null;
2884
+ this.initialized = false;
2885
+ this.preRenderingHTMLElement = [];
2886
+ }
2887
+ initialize(children, parent, childrenContext, preRenderingCount = 0) {
2888
+ this.initialized = true;
2889
+ this.children = children;
2890
+ const isRoot = parent === this.viewContext.editor;
2891
+ const firstIndex = isRoot ? this.viewContext.editor.children.indexOf(children[0]) : 0;
2892
+ const parentPath = AngularEditor.findPath(this.viewContext.editor, parent);
2893
+ children.forEach((descendant, _index) => {
2894
+ NODE_TO_INDEX.set(descendant, firstIndex + _index);
2895
+ NODE_TO_PARENT.set(descendant, parent);
2896
+ const context = getContext(firstIndex + _index, descendant, parentPath, childrenContext, this.viewContext);
2897
+ const viewType = getViewType(descendant, parent, this.viewContext);
2898
+ const view = createEmbeddedViewOrComponentOrFlavour(viewType, context, this.viewContext, this.viewContainerRef);
2899
+ const blockCard = createBlockCard(descendant, view, this.viewContext);
2900
+ this.views.push(view);
2901
+ this.contexts.push(context);
2902
+ this.viewTypes.push(viewType);
2903
+ this.blockCards.push(blockCard);
2904
+ });
2905
+ mount(this.views, this.blockCards, this.getOutletParent(), this.getOutletElement());
2906
+ const newDiffers = this.viewContext.editor.injector.get(IterableDiffers);
2907
+ this.differ = newDiffers.find(children).create(trackBy(this.viewContext));
2908
+ this.differ.diff(children);
2909
+ if (parent === this.viewContext.editor) {
2910
+ executeAfterViewInit(this.viewContext.editor);
3054
2911
  }
3055
- const width = Math.max(VirtualScrollDebugOverlay.minWidth, this.state.width || VirtualScrollDebugOverlay.defaultWidth);
3056
- const height = Math.max(VirtualScrollDebugOverlay.minHeight, this.state.height || VirtualScrollDebugOverlay.defaultHeight);
3057
- this.state.width = width;
3058
- this.state.height = height;
3059
- this.container.style.width = `${width}px`;
3060
- this.container.style.height = `${height}px`;
3061
2912
  }
3062
- applyCollapsedState() {
3063
- if (!this.container || !this.contentWrapper || !this.bubble || !this.collapseToggle) {
2913
+ update(children, parent, childrenContext, preRenderingCount = 0) {
2914
+ if (!this.initialized || this.children.length === 0) {
2915
+ this.initialize(children, parent, childrenContext, preRenderingCount);
3064
2916
  return;
3065
2917
  }
3066
- if (this.state.collapsed) {
3067
- this.container.style.width = '36px';
3068
- this.container.style.height = '36px';
3069
- this.container.style.minWidth = '';
3070
- this.container.style.minHeight = '';
3071
- this.container.style.padding = '0';
3072
- this.container.style.borderRadius = '50%';
3073
- this.container.style.display = 'flex';
3074
- this.container.style.flexDirection = 'row';
3075
- this.container.style.alignItems = 'center';
3076
- this.container.style.justifyContent = 'center';
3077
- this.container.style.cursor = 'move';
3078
- this.contentWrapper.style.display = 'none';
3079
- this.bubble.style.display = 'flex';
3080
- this.collapseToggle.textContent = '展开';
3081
- this.resizeHandle.style.display = 'none';
2918
+ if (!this.differ) {
2919
+ throw new Error('Exception: Can not find differ ');
3082
2920
  }
3083
- else {
3084
- this.applySize();
3085
- this.container.style.padding = '12px';
3086
- this.container.style.borderRadius = '10px';
3087
- this.container.style.display = 'flex';
3088
- this.container.style.flexDirection = 'column';
3089
- this.container.style.gap = '10px';
3090
- this.container.style.cursor = 'default';
3091
- this.contentWrapper.style.display = 'flex';
3092
- this.bubble.style.display = 'none';
3093
- this.collapseToggle.textContent = '折叠';
3094
- this.resizeHandle.style.display = 'block';
2921
+ const outletParent = this.getOutletParent();
2922
+ if (this.preRenderingHTMLElement.length > 0) {
2923
+ const preRenderingElement = [...this.preRenderingHTMLElement];
2924
+ preRenderingElement.forEach((rootNodes, index) => {
2925
+ rootNodes.forEach(rootNode => {
2926
+ rootNode.style.position = '';
2927
+ rootNode.style.top = '';
2928
+ });
2929
+ });
2930
+ this.preRenderingHTMLElement = [];
3095
2931
  }
3096
- }
3097
- setCollapsed(collapsed) {
3098
- this.state.collapsed = collapsed;
3099
- this.applyCollapsedState();
3100
- this.persistState();
3101
- }
3102
- applyState() {
3103
- this.applyPosition();
3104
- this.applyCollapsedState();
3105
- }
3106
- loadState() {
3107
- try {
3108
- const raw = this.doc.defaultView?.localStorage?.getItem(VirtualScrollDebugOverlay.storageKey);
3109
- if (raw) {
3110
- const parsed = JSON.parse(raw);
3111
- if (typeof parsed.left === 'number') {
3112
- this.state.left = parsed.left;
2932
+ const diffResult = this.differ.diff(children);
2933
+ const parentPath = AngularEditor.findPath(this.viewContext.editor, parent);
2934
+ const isRoot = parent === this.viewContext.editor;
2935
+ const firstIndex = isRoot ? this.viewContext.editor.children.indexOf(children[0]) : 0;
2936
+ if (diffResult) {
2937
+ let firstRootNode = getRootNodes(this.views[0], this.blockCards[0])[0];
2938
+ const newContexts = [];
2939
+ const newViewTypes = [];
2940
+ const newViews = [];
2941
+ const newBlockCards = [];
2942
+ diffResult.forEachItem(record => {
2943
+ const currentIndex = firstIndex + record.currentIndex;
2944
+ NODE_TO_INDEX.set(record.item, currentIndex);
2945
+ NODE_TO_PARENT.set(record.item, parent);
2946
+ let context = getContext(currentIndex, record.item, parentPath, childrenContext, this.viewContext);
2947
+ const viewType = getViewType(record.item, parent, this.viewContext);
2948
+ newViewTypes.push(viewType);
2949
+ let view;
2950
+ let blockCard;
2951
+ if (record.previousIndex === null) {
2952
+ view = createEmbeddedViewOrComponentOrFlavour(viewType, context, this.viewContext, this.viewContainerRef);
2953
+ blockCard = createBlockCard(record.item, view, this.viewContext);
2954
+ newContexts.push(context);
2955
+ newViews.push(view);
2956
+ newBlockCards.push(blockCard);
2957
+ mountOnItemChange(record.currentIndex, record.item, newViews, newBlockCards, outletParent, firstRootNode, this.viewContext);
2958
+ }
2959
+ else {
2960
+ const previousView = this.views[record.previousIndex];
2961
+ const previousViewType = this.viewTypes[record.previousIndex];
2962
+ const previousContext = this.contexts[record.previousIndex];
2963
+ const previousBlockCard = this.blockCards[record.previousIndex];
2964
+ if (previousViewType !== viewType) {
2965
+ view = createEmbeddedViewOrComponentOrFlavour(viewType, context, this.viewContext, this.viewContainerRef);
2966
+ blockCard = createBlockCard(record.item, view, this.viewContext);
2967
+ const firstRootNode = getRootNodes(previousView, previousBlockCard)[0];
2968
+ const newRootNodes = getRootNodes(view, blockCard);
2969
+ firstRootNode.replaceWith(...newRootNodes);
2970
+ previousView.destroy();
2971
+ previousBlockCard?.destroy();
2972
+ }
2973
+ else {
2974
+ view = previousView;
2975
+ blockCard = previousBlockCard;
2976
+ if (memoizedContext(this.viewContext, record.item, previousContext, context)) {
2977
+ context = previousContext;
2978
+ }
2979
+ else {
2980
+ updateContext(previousView, context, this.viewContext);
2981
+ }
2982
+ }
2983
+ newContexts.push(context);
2984
+ newViews.push(view);
2985
+ newBlockCards.push(blockCard);
3113
2986
  }
3114
- if (typeof parsed.top === 'number') {
3115
- this.state.top = parsed.top;
2987
+ });
2988
+ diffResult.forEachOperation(record => {
2989
+ // removed
2990
+ if (record.currentIndex === null) {
2991
+ const view = this.views[record.previousIndex];
2992
+ const blockCard = this.blockCards[record.previousIndex];
2993
+ view.destroy();
2994
+ blockCard?.destroy();
3116
2995
  }
3117
- if (typeof parsed.collapsed === 'boolean') {
3118
- this.state.collapsed = parsed.collapsed;
2996
+ // moved
2997
+ if (record.previousIndex !== null && record.currentIndex !== null) {
2998
+ mountOnItemChange(record.currentIndex, record.item, newViews, newBlockCards, outletParent, firstRootNode, this.viewContext);
2999
+ // Solve the block-card DOMElement loss when moving nodes
3000
+ newBlockCards[record.currentIndex]?.instance.append();
3119
3001
  }
3120
- if (typeof parsed.width === 'number') {
3121
- this.state.width = parsed.width;
3002
+ });
3003
+ this.viewTypes = newViewTypes;
3004
+ this.views = newViews;
3005
+ this.contexts = newContexts;
3006
+ this.children = children;
3007
+ this.blockCards = newBlockCards;
3008
+ if (parent === this.viewContext.editor) {
3009
+ executeAfterViewInit(this.viewContext.editor);
3010
+ }
3011
+ }
3012
+ else {
3013
+ const newContexts = [];
3014
+ this.children.forEach((child, _index) => {
3015
+ NODE_TO_INDEX.set(child, firstIndex + _index);
3016
+ NODE_TO_PARENT.set(child, parent);
3017
+ let context = getContext(firstIndex + _index, child, parentPath, childrenContext, this.viewContext);
3018
+ const previousContext = this.contexts[_index];
3019
+ if (memoizedContext(this.viewContext, child, previousContext, context)) {
3020
+ context = previousContext;
3122
3021
  }
3123
- if (typeof parsed.height === 'number') {
3124
- this.state.height = parsed.height;
3022
+ else {
3023
+ updateContext(this.views[_index], context, this.viewContext);
3125
3024
  }
3126
- }
3025
+ newContexts.push(context);
3026
+ });
3027
+ this.contexts = newContexts;
3127
3028
  }
3128
- catch {
3129
- // ignore storage errors
3029
+ if (preRenderingCount > 0) {
3030
+ for (let i = 0; i < preRenderingCount; i++) {
3031
+ const rootNodes = [...getRootNodes(this.views[i], this.blockCards[i])];
3032
+ rootNodes.forEach(rootNode => {
3033
+ rootNode.style.top = '-100%';
3034
+ rootNode.style.position = 'absolute';
3035
+ });
3036
+ this.preRenderingHTMLElement.push(rootNodes);
3037
+ }
3130
3038
  }
3131
3039
  }
3132
- persistState() {
3133
- try {
3134
- this.doc.defaultView?.localStorage?.setItem(VirtualScrollDebugOverlay.storageKey, JSON.stringify(this.state));
3040
+ destroy() {
3041
+ this.children.forEach((element, index) => {
3042
+ if (this.views[index]) {
3043
+ this.views[index].destroy();
3044
+ }
3045
+ if (this.blockCards[index]) {
3046
+ this.blockCards[index].destroy();
3047
+ }
3048
+ });
3049
+ this.children = [];
3050
+ this.views = [];
3051
+ this.blockCards = [];
3052
+ this.contexts = [];
3053
+ this.viewTypes = [];
3054
+ this.initialized = false;
3055
+ this.differ = null;
3056
+ }
3057
+ }
3058
+ function getContext(index, item, parentPath, childrenContext, viewContext) {
3059
+ if (Element.isElement(item)) {
3060
+ const computedContext = getCommonContext(index, item, parentPath, viewContext, childrenContext);
3061
+ const key = AngularEditor.findKey(viewContext.editor, item);
3062
+ const isInline = viewContext.editor.isInline(item);
3063
+ const isVoid = viewContext.editor.isVoid(item);
3064
+ const elementContext = {
3065
+ element: item,
3066
+ ...computedContext,
3067
+ attributes: {
3068
+ 'data-slate-node': 'element',
3069
+ 'data-slate-key': key.id
3070
+ },
3071
+ decorate: childrenContext.decorate,
3072
+ readonly: childrenContext.readonly
3073
+ };
3074
+ if (isInline) {
3075
+ elementContext.attributes['data-slate-inline'] = true;
3135
3076
  }
3136
- catch {
3137
- // ignore storage errors
3077
+ if (isVoid) {
3078
+ elementContext.attributes['data-slate-void'] = true;
3138
3079
  }
3139
- }
3140
- appendLog(type, ...args) {
3141
- if (!this.logList) {
3142
- return;
3080
+ // add contentEditable for block element only to avoid chinese input be broken
3081
+ if (isVoid && !isInline) {
3082
+ elementContext.contentEditable = false;
3143
3083
  }
3144
- const item = this.doc.createElement('div');
3145
- item.style.display = 'flex';
3146
- item.style.gap = '6px';
3147
- item.style.alignItems = 'flex-start';
3148
- item.style.wordBreak = 'break-all';
3149
- item.style.color = type === 'warn' ? '#fbbf24' : '#9ca3af';
3150
- const time = this.doc.createElement('span');
3151
- time.textContent = new Date().toLocaleTimeString();
3152
- time.style.color = '#6b7280';
3153
- time.style.flexShrink = '0';
3154
- time.style.width = '72px';
3155
- const text = this.doc.createElement('span');
3156
- text.textContent = `[${type}] ${args.map(arg => this.formatValue(arg)).join(' ')}`;
3157
- item.appendChild(time);
3158
- item.appendChild(text);
3159
- this.logList.appendChild(item);
3160
- this.logList.scrollTop = this.logList.scrollHeight;
3084
+ return elementContext;
3161
3085
  }
3162
- formatValue(value) {
3163
- if (typeof value === 'string') {
3164
- return value;
3165
- }
3166
- try {
3167
- return JSON.stringify(value);
3086
+ else {
3087
+ const computedContext = getCommonContext(index, item, parentPath, viewContext, childrenContext);
3088
+ const isLeafBlock = AngularEditor.isLeafBlock(viewContext.editor, childrenContext.parent);
3089
+ const textContext = {
3090
+ decorations: computedContext.decorations,
3091
+ isLast: isLeafBlock && index === childrenContext.parent.children.length - 1,
3092
+ parent: childrenContext.parent,
3093
+ text: item
3094
+ };
3095
+ return textContext;
3096
+ }
3097
+ }
3098
+ function getCommonContext(index, item, parentPath, viewContext, childrenContext) {
3099
+ const p = parentPath.concat(index);
3100
+ try {
3101
+ const ds = childrenContext.decorate([item, p]);
3102
+ // [list-render] performance optimization: reduce the number of calls to the `Editor.range(viewContext.editor, p)` method
3103
+ if (childrenContext.selection || childrenContext.decorations.length > 0) {
3104
+ const range = Editor.range(viewContext.editor, p);
3105
+ const sel = childrenContext.selection && Range.intersection(range, childrenContext.selection);
3106
+ for (const dec of childrenContext.decorations) {
3107
+ const d = Range.intersection(dec, range);
3108
+ if (d) {
3109
+ ds.push(d);
3110
+ }
3111
+ }
3112
+ return { selection: sel, decorations: ds };
3168
3113
  }
3169
- catch (error) {
3170
- return String(value);
3114
+ else {
3115
+ return { selection: null, decorations: ds };
3171
3116
  }
3172
3117
  }
3173
- setScrollTopValue(value) {
3174
- if (this.distanceInput) {
3175
- this.distanceInput.value = String(value ?? 0);
3176
- }
3118
+ catch (error) {
3119
+ viewContext.editor.onError({
3120
+ code: SlateErrorCode.GetStartPointError,
3121
+ nativeError: error
3122
+ });
3123
+ return { selection: null, decorations: [] };
3124
+ }
3125
+ }
3126
+ function getViewType(item, parent, viewContext) {
3127
+ if (Element.isElement(item)) {
3128
+ return (viewContext.renderElement && viewContext.renderElement(item)) || DefaultElementFlavour;
3129
+ }
3130
+ else {
3131
+ const isVoid = viewContext.editor.isVoid(parent);
3132
+ return isVoid ? VoidTextFlavour : (viewContext.renderText && viewContext.renderText(item)) || DefaultTextFlavour;
3133
+ }
3134
+ }
3135
+ function createBlockCard(item, view, viewContext) {
3136
+ const isBlockCard = viewContext.editor.isBlockCard(item);
3137
+ if (isBlockCard) {
3138
+ const rootNodes = getRootNodes(view);
3139
+ const blockCardRef = new BlockCardRef();
3140
+ blockCardRef.instance = new SlateBlockCard();
3141
+ blockCardRef.instance.onInit();
3142
+ blockCardRef.instance.initializeCenter(rootNodes);
3143
+ return blockCardRef;
3144
+ }
3145
+ else {
3146
+ return null;
3147
+ }
3148
+ }
3149
+ function trackBy(viewContext) {
3150
+ return (index, node) => {
3151
+ return viewContext.trackBy(node) || AngularEditor.findKey(viewContext.editor, node);
3152
+ };
3153
+ }
3154
+ function memoizedContext(viewContext, descendant, prev, next) {
3155
+ if (Element.isElement(descendant)) {
3156
+ return memoizedElementContext(viewContext, prev, next);
3157
+ }
3158
+ else {
3159
+ return memoizedTextContext(prev, next);
3177
3160
  }
3178
3161
  }
3162
+ function memoizedElementContext(viewContext, prev, next) {
3163
+ return (prev.element === next.element &&
3164
+ (!viewContext.isStrictDecorate || prev.decorate === next.decorate) &&
3165
+ prev.readonly === next.readonly &&
3166
+ isDecoratorRangeListEqual(prev.decorations, next.decorations) &&
3167
+ (prev.selection === next.selection || (!!prev.selection && !!next.selection && Range.equals(prev.selection, next.selection))));
3168
+ }
3169
+ function memoizedTextContext(prev, next) {
3170
+ return (next.parent === prev.parent &&
3171
+ next.isLast === prev.isLast &&
3172
+ next.text === prev.text &&
3173
+ isDecoratorRangeListEqual(next.decorations, prev.decorations));
3174
+ }
3175
+ function addAfterViewInitQueue(editor, afterViewInitCallback) {
3176
+ const queue = getAfterViewInitQueue(editor);
3177
+ queue.push(afterViewInitCallback);
3178
+ EDITOR_TO_AFTER_VIEW_INIT_QUEUE.set(editor, queue);
3179
+ }
3180
+ function getAfterViewInitQueue(editor) {
3181
+ return EDITOR_TO_AFTER_VIEW_INIT_QUEUE.get(editor) || [];
3182
+ }
3183
+ function clearAfterViewInitQueue(editor) {
3184
+ EDITOR_TO_AFTER_VIEW_INIT_QUEUE.set(editor, []);
3185
+ }
3186
+ function executeAfterViewInit(editor) {
3187
+ const queue = getAfterViewInitQueue(editor);
3188
+ queue.forEach(callback => callback());
3189
+ clearAfterViewInitQueue(editor);
3190
+ }
3179
3191
 
3180
3192
  // not correctly clipboardData on beforeinput
3181
3193
  const forceOnDOMPaste = IS_SAFARI;
3182
- const isDebug = localStorage.getItem(SLATE_DEBUG_KEY) === 'true';
3183
- const isDebugScrollTop = localStorage.getItem(SLATE_DEBUG_KEY_SCROLL_TOP) === 'true';
3184
3194
  class SlateEditable {
3185
3195
  set virtualScroll(config) {
3186
3196
  this.virtualScrollConfig = config;
3187
3197
  if (isDebugScrollTop) {
3188
- this.debugLog('log', 'virtualScrollConfig scrollTop:', config.scrollTop);
3198
+ debugLog('log', 'virtualScrollConfig scrollTop:', config.scrollTop);
3189
3199
  }
3190
3200
  IS_ENABLED_VIRTUAL_SCROLL.set(this.editor, config.enabled);
3191
3201
  if (this.isEnabledVirtualScroll()) {
@@ -3239,7 +3249,8 @@ class SlateEditable {
3239
3249
  enabled: false,
3240
3250
  scrollTop: 0,
3241
3251
  viewportHeight: 0,
3242
- viewportBoundingTop: 0
3252
+ viewportBoundingTop: 0,
3253
+ scrollContainer: null
3243
3254
  };
3244
3255
  this.inViewportChildren = [];
3245
3256
  this.inViewportIndics = [];
@@ -3353,6 +3364,7 @@ class SlateEditable {
3353
3364
  }
3354
3365
  calculateVirtualScrollSelection(selection) {
3355
3366
  if (selection) {
3367
+ const isBlockCardCursor = AngularEditor.isBlockCardLeftCursor(this.editor) || AngularEditor.isBlockCardRightCursor(this.editor);
3356
3368
  const indics = this.inViewportIndics;
3357
3369
  if (indics.length > 0) {
3358
3370
  const currentVisibleRange = {
@@ -3360,12 +3372,21 @@ class SlateEditable {
3360
3372
  focus: Editor.end(this.editor, [indics[indics.length - 1]])
3361
3373
  };
3362
3374
  const [start, end] = Range.edges(selection);
3363
- const forwardSelection = { anchor: start, focus: end };
3375
+ let forwardSelection = { anchor: start, focus: end };
3376
+ if (!isBlockCardCursor) {
3377
+ forwardSelection = { anchor: start, focus: end };
3378
+ }
3379
+ else {
3380
+ forwardSelection = { anchor: { path: start.path, offset: 0 }, focus: { path: end.path, offset: 0 } };
3381
+ }
3364
3382
  const intersectedSelection = Range.intersection(forwardSelection, currentVisibleRange);
3383
+ if (intersectedSelection && isBlockCardCursor) {
3384
+ return selection;
3385
+ }
3365
3386
  EDITOR_TO_VIRTUAL_SCROLL_SELECTION.set(this.editor, intersectedSelection);
3366
3387
  if (!intersectedSelection || !Range.equals(intersectedSelection, forwardSelection)) {
3367
3388
  if (isDebug) {
3368
- this.debugLog('log', `selection is not in visible range, selection: ${JSON.stringify(selection)}, intersectedSelection: ${JSON.stringify(intersectedSelection)}`);
3389
+ debugLog('log', `selection is not in visible range, selection: ${JSON.stringify(selection)}, currentVisibleRange: ${JSON.stringify(currentVisibleRange)}, intersectedSelection: ${JSON.stringify(intersectedSelection)}`);
3369
3390
  }
3370
3391
  return intersectedSelection;
3371
3392
  }
@@ -3375,7 +3396,7 @@ class SlateEditable {
3375
3396
  EDITOR_TO_VIRTUAL_SCROLL_SELECTION.set(this.editor, null);
3376
3397
  return selection;
3377
3398
  }
3378
- toNativeSelection() {
3399
+ toNativeSelection(autoScroll = true) {
3379
3400
  try {
3380
3401
  let { selection } = this.editor;
3381
3402
  if (this.isEnabledVirtualScroll()) {
@@ -3441,13 +3462,23 @@ class SlateEditable {
3441
3462
  domSelection.removeAllRanges();
3442
3463
  }
3443
3464
  setTimeout(() => {
3444
- // handle scrolling in setTimeout because of
3445
- // dom should not have updated immediately after listRender's updating
3446
- newDomRange && this.scrollSelectionIntoView(this.editor, newDomRange);
3447
- // COMPAT: In Firefox, it's not enough to create a range, you also need
3448
- // to focus the contenteditable element too. (2016/11/16)
3449
- if (newDomRange && IS_FIREFOX) {
3450
- el.focus();
3465
+ if (this.isEnabledVirtualScroll() &&
3466
+ !selection &&
3467
+ this.editor.selection &&
3468
+ autoScroll &&
3469
+ this.virtualScrollConfig.scrollContainer) {
3470
+ this.virtualScrollConfig.scrollContainer.scrollTop = this.virtualScrollConfig.scrollContainer.scrollTop + 100;
3471
+ return;
3472
+ }
3473
+ else {
3474
+ // handle scrolling in setTimeout because of
3475
+ // dom should not have updated immediately after listRender's updating
3476
+ newDomRange && autoScroll && this.scrollSelectionIntoView(this.editor, newDomRange);
3477
+ // COMPAT: In Firefox, it's not enough to create a range, you also need
3478
+ // to focus the contenteditable element too. (2016/11/16)
3479
+ if (newDomRange && IS_FIREFOX) {
3480
+ el.focus();
3481
+ }
3451
3482
  }
3452
3483
  this.isUpdatingSelection = false;
3453
3484
  });
@@ -3624,14 +3655,10 @@ class SlateEditable {
3624
3655
  editorResizeObserverRectWidth = entries[0].contentRect.width;
3625
3656
  this.keyHeightMap.clear();
3626
3657
  const remeasureIndics = this.inViewportIndics;
3627
- measureHeightByIndics(this.editor, remeasureIndics);
3658
+ measureHeightByIndics(this.editor, remeasureIndics, true);
3628
3659
  }
3629
3660
  });
3630
3661
  this.editorResizeObserver.observe(this.elementRef.nativeElement);
3631
- if (isDebug) {
3632
- const doc = this.elementRef?.nativeElement?.ownerDocument ?? document;
3633
- VirtualScrollDebugOverlay.getInstance(doc);
3634
- }
3635
3662
  }
3636
3663
  }
3637
3664
  setVirtualSpaceHeight(topHeight, bottomHeight) {
@@ -3649,10 +3676,6 @@ class SlateEditable {
3649
3676
  }
3650
3677
  return parseFloat(this.virtualTopHeightElement.style.height.replace('px', ''));
3651
3678
  }
3652
- debugLog(type, ...args) {
3653
- const doc = this.elementRef?.nativeElement?.ownerDocument ?? document;
3654
- VirtualScrollDebugOverlay.log(doc, type, ...args);
3655
- }
3656
3679
  handlePreRendering() {
3657
3680
  let preRenderingCount = 1;
3658
3681
  const childrenWithPreRendering = [...this.inViewportChildren];
@@ -3670,12 +3693,12 @@ class SlateEditable {
3670
3693
  }
3671
3694
  tryUpdateVirtualViewport() {
3672
3695
  if (isDebug) {
3673
- this.debugLog('log', 'tryUpdateVirtualViewport');
3696
+ debugLog('log', 'tryUpdateVirtualViewport');
3674
3697
  }
3675
3698
  this.tryUpdateVirtualViewportAnimId && cancelAnimationFrame(this.tryUpdateVirtualViewportAnimId);
3676
3699
  this.tryUpdateVirtualViewportAnimId = requestAnimationFrame(() => {
3677
3700
  if (isDebug) {
3678
- this.debugLog('log', 'tryUpdateVirtualViewport Anim start');
3701
+ debugLog('log', 'tryUpdateVirtualViewport Anim start');
3679
3702
  }
3680
3703
  let virtualView = this.calculateVirtualViewport();
3681
3704
  let diff = this.diffVirtualViewport(virtualView);
@@ -3695,35 +3718,37 @@ class SlateEditable {
3695
3718
  if (diff.needAddOnTop) {
3696
3719
  const remeasureAddedIndics = diff.changedIndexesOfTop;
3697
3720
  if (isDebug) {
3698
- this.debugLog('log', 'needAddOnTop to remeasure heights: ', remeasureAddedIndics);
3721
+ debugLog('log', 'needAddOnTop to remeasure heights: ', remeasureAddedIndics);
3699
3722
  }
3700
3723
  const startIndexBeforeAdd = diff.changedIndexesOfTop[diff.changedIndexesOfTop.length - 1] + 1;
3701
3724
  const topHeightBeforeAdd = virtualView.accumulatedHeights[startIndexBeforeAdd];
3702
- const result = measureHeightByIndics(this.editor, remeasureAddedIndics);
3703
- if (result) {
3725
+ const changed = measureHeightByIndics(this.editor, remeasureAddedIndics);
3726
+ if (changed) {
3704
3727
  const newHeights = buildHeightsAndAccumulatedHeights(this.editor);
3705
3728
  const actualTopHeightAfterAdd = newHeights.accumulatedHeights[startIndexBeforeAdd];
3706
3729
  const newTopHeight = virtualView.top - (actualTopHeightAfterAdd - topHeightBeforeAdd);
3707
3730
  this.setVirtualSpaceHeight(newTopHeight);
3708
3731
  if (isDebug) {
3709
- this.debugLog('log', `update top height since will add element in top(正数减去高度,负数代表增加高度): ${actualTopHeightAfterAdd - topHeightBeforeAdd}`);
3732
+ debugLog('log', `update top height since will add element in top(正数减去高度,负数代表增加高度): ${actualTopHeightAfterAdd - topHeightBeforeAdd}`);
3710
3733
  }
3711
3734
  }
3712
3735
  }
3713
3736
  if (!AngularEditor.isReadOnly(this.editor) && this.editor.selection) {
3714
- this.toNativeSelection();
3737
+ this.toNativeSelection(false);
3715
3738
  }
3716
3739
  }
3717
3740
  }
3718
3741
  else {
3719
3742
  const topHeight = this.getVirtualTopHeight();
3720
3743
  if (virtualView.top !== topHeight) {
3721
- this.debugLog('log', 'update top height since invalid status(正数减去高度,负数代表增加高度): ', topHeight - virtualView.top);
3744
+ if (isDebug) {
3745
+ debugLog('log', 'update top height since invalid status(正数减去高度,负数代表增加高度): ', topHeight - virtualView.top);
3746
+ }
3722
3747
  this.setVirtualSpaceHeight(virtualView.top);
3723
3748
  }
3724
3749
  }
3725
3750
  if (isDebug) {
3726
- this.debugLog('log', 'tryUpdateVirtualViewport Anim end');
3751
+ debugLog('log', 'tryUpdateVirtualViewport Anim end');
3727
3752
  }
3728
3753
  });
3729
3754
  }
@@ -3763,7 +3788,7 @@ class SlateEditable {
3763
3788
  Math.floor(this.virtualScrollConfig.viewportBoundingTop);
3764
3789
  EDITOR_TO_BUSINESS_TOP.set(this.editor, businessTop);
3765
3790
  if (isDebug) {
3766
- this.debugLog('log', 'businessTop', businessTop);
3791
+ debugLog('log', 'businessTop', businessTop);
3767
3792
  }
3768
3793
  }, 100);
3769
3794
  }
@@ -3875,18 +3900,18 @@ class SlateEditable {
3875
3900
  }
3876
3901
  }
3877
3902
  if (isDebug) {
3878
- this.debugLog('log', `====== diffVirtualViewport stage: ${stage} ======`);
3879
- this.debugLog('log', 'oldIndexesInViewport:', oldIndexesInViewport);
3880
- this.debugLog('log', 'newIndexesInViewport:', newIndexesInViewport);
3881
- this.debugLog('log', 'changedIndexesOfTop:', needRemoveOnTop ? '-' : needAddOnTop ? '+' : '-', changedIndexesOfTop, changedIndexesOfTop.map(index => getRealHeightByElement(this.editor, this.editor.children[index], 0)));
3882
- this.debugLog('log', 'changedIndexesOfBottom:', needAddOnBottom ? '+' : needRemoveOnBottom ? '-' : '+', changedIndexesOfBottom, changedIndexesOfBottom.map(index => getRealHeightByElement(this.editor, this.editor.children[index], 0)));
3903
+ debugLog('log', `====== diffVirtualViewport stage: ${stage} ======`);
3904
+ debugLog('log', 'oldIndexesInViewport:', oldIndexesInViewport);
3905
+ debugLog('log', 'newIndexesInViewport:', newIndexesInViewport);
3906
+ debugLog('log', 'changedIndexesOfTop:', needRemoveOnTop ? '-' : needAddOnTop ? '+' : '-', changedIndexesOfTop, changedIndexesOfTop.map(index => getRealHeightByElement(this.editor, this.editor.children[index], 0)));
3907
+ debugLog('log', 'changedIndexesOfBottom:', needAddOnBottom ? '+' : needRemoveOnBottom ? '-' : '+', changedIndexesOfBottom, changedIndexesOfBottom.map(index => getRealHeightByElement(this.editor, this.editor.children[index], 0)));
3883
3908
  const needTop = virtualView.heights.slice(0, newIndexesInViewport[0]).reduce((acc, height) => acc + height, 0);
3884
3909
  const needBottom = virtualView.heights
3885
3910
  .slice(newIndexesInViewport[newIndexesInViewport.length - 1] + 1)
3886
3911
  .reduce((acc, height) => acc + height, 0);
3887
- this.debugLog('log', needTop - parseFloat(this.virtualTopHeightElement.style.height), 'newTopHeight:', needTop, 'prevTopHeight:', parseFloat(this.virtualTopHeightElement.style.height));
3888
- this.debugLog('log', 'newBottomHeight:', needBottom, 'prevBottomHeight:', parseFloat(this.virtualBottomHeightElement.style.height));
3889
- this.debugLog('warn', '=========== Dividing line ===========');
3912
+ debugLog('log', needTop - parseFloat(this.virtualTopHeightElement.style.height), 'newTopHeight:', needTop, 'prevTopHeight:', parseFloat(this.virtualTopHeightElement.style.height));
3913
+ debugLog('log', 'newBottomHeight:', needBottom, 'prevBottomHeight:', parseFloat(this.virtualBottomHeightElement.style.height));
3914
+ debugLog('warn', '=========== Dividing line ===========');
3890
3915
  }
3891
3916
  return {
3892
3917
  isDifferent: true,
@@ -3944,7 +3969,7 @@ class SlateEditable {
3944
3969
  if (this.editor.selection && Range.equals(range, this.editor.selection) && !hasStringTarget(domSelection)) {
3945
3970
  if (!isTargetInsideVoid(this.editor, activeElement)) {
3946
3971
  // force adjust DOMSelection
3947
- this.toNativeSelection();
3972
+ this.toNativeSelection(false);
3948
3973
  }
3949
3974
  }
3950
3975
  else {
@@ -5136,5 +5161,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImpo
5136
5161
  * Generated bundle index. Do not edit.
5137
5162
  */
5138
5163
 
5139
- 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_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, check, completeTable, createClipboardData, createText, createThrottleRAF, 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, isDecoratorRangeListEqual, isFlavourType, isInvalidTable, isTemplateRef, isValid, measureHeightByElement, measureHeightByIndics, normalize, scrollToElement, setClipboardData, setDataTransferClipboard, setDataTransferClipboardText, setNavigatorClipboard, shallowCompare, stripHtml, withAngular };
5164
+ 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_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, 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 };
5140
5165
  //# sourceMappingURL=slate-angular.mjs.map