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.
- package/fesm2022/slate-angular.mjs +2111 -2086
- package/fesm2022/slate-angular.mjs.map +1 -1
- package/index.d.ts +6 -3
- package/package.json +1 -1
|
@@ -955,2237 +955,2247 @@ const fallbackCopyText = async (text) => {
|
|
|
955
955
|
});
|
|
956
956
|
};
|
|
957
957
|
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
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
|
-
|
|
980
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
974
|
+
static syncScrollTop(doc, value) {
|
|
975
|
+
const instance = this.getInstance(doc);
|
|
976
|
+
instance.setScrollTopValue(value);
|
|
1023
977
|
}
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
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
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
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
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
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
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
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
|
-
|
|
1127
|
-
if (!(await e.customInsertFragmentData(data, null))) {
|
|
1128
|
-
e.insertTextData(data);
|
|
1054
|
+
else {
|
|
1055
|
+
this.applyState();
|
|
1129
1056
|
}
|
|
1130
|
-
}
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
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
|
-
|
|
1064
|
+
this.originalConsoleLog(...args);
|
|
1168
1065
|
}
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
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
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
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
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
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
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
}
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
const
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
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
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
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
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
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
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
}
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
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
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
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
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
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
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
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
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
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
|
-
|
|
1493
|
-
|
|
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
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
return null;
|
|
1402
|
+
}
|
|
1403
|
+
catch {
|
|
1404
|
+
// ignore storage errors
|
|
1405
|
+
}
|
|
1501
1406
|
}
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
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
|
-
|
|
1514
|
-
|
|
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
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
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
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
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
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
}
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
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
|
-
|
|
1621
|
-
if (
|
|
1622
|
-
|
|
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 (
|
|
1631
|
-
|
|
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
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
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
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
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
|
-
|
|
1655
|
-
|
|
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
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
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
|
-
|
|
1675
|
-
|
|
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
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
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
|
-
|
|
1703
|
-
|
|
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
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
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
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
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
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
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
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
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
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
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
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
}
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
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
|
-
|
|
1860
|
-
|
|
1861
|
-
if (
|
|
1862
|
-
|
|
1634
|
+
};
|
|
1635
|
+
e.insertData = async (data) => {
|
|
1636
|
+
if (!(await e.customInsertFragmentData(data, null))) {
|
|
1637
|
+
e.insertTextData(data);
|
|
1863
1638
|
}
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
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
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
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
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
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
|
-
|
|
1887
|
-
|
|
1888
|
-
}
|
|
1889
|
-
onContextChange() {
|
|
1890
|
-
this.childrenContext = this.getChildrenContext();
|
|
1891
|
-
if (!this.initialized) {
|
|
1892
|
-
return;
|
|
1675
|
+
else {
|
|
1676
|
+
console.error(errorData);
|
|
1893
1677
|
}
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
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
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
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
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
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
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
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
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
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
|
-
|
|
2010
|
-
|
|
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
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
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
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
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
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
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
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
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
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
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
|
-
|
|
2113
|
-
|
|
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
|
-
*
|
|
2158
|
-
*
|
|
2159
|
-
*
|
|
2160
|
-
* @param element
|
|
2161
|
-
* @returns
|
|
2013
|
+
* Extract a SyntheticInputEvent for `beforeInput`, based on either native
|
|
2014
|
+
* `textInput` or fallback behavior.
|
|
2015
|
+
*
|
|
2162
2016
|
*/
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
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
|
-
|
|
2175
|
-
|
|
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
|
-
|
|
2182
|
-
|
|
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
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
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
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
}
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
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
|
-
|
|
2261
|
-
|
|
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
|
-
|
|
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.
|
|
2120
|
+
this.instance.onDestroy();
|
|
2285
2121
|
}
|
|
2286
2122
|
}
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
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
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
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
|
-
|
|
2310
|
-
|
|
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
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
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
|
-
|
|
2320
|
-
|
|
2321
|
-
NODE_TO_ELEMENT.set(this.text, this.nativeElement);
|
|
2163
|
+
if (view instanceof ComponentRef) {
|
|
2164
|
+
view.instance.context = newContext;
|
|
2322
2165
|
}
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
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
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
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
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
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
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
this.nativeElement = nativeElement;
|
|
2250
|
+
function hasBeforeContextChange(value) {
|
|
2251
|
+
if (value.beforeContextChange) {
|
|
2252
|
+
return true;
|
|
2345
2253
|
}
|
|
2346
|
-
|
|
2254
|
+
return false;
|
|
2347
2255
|
}
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
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
|
-
|
|
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
|
|
2364
|
-
constructor(
|
|
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
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
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
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2578
|
-
|
|
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
|
-
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
|
|
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
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2622
|
-
|
|
2623
|
-
|
|
2313
|
+
initializeCenter(rootNodes) {
|
|
2314
|
+
this.centerRootNodes = rootNodes;
|
|
2315
|
+
this.append();
|
|
2316
|
+
}
|
|
2317
|
+
onDestroy() {
|
|
2318
|
+
this.nativeElement.remove();
|
|
2624
2319
|
}
|
|
2625
2320
|
}
|
|
2626
|
-
|
|
2627
|
-
const
|
|
2628
|
-
if (
|
|
2629
|
-
|
|
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
|
-
|
|
2637
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2650
|
-
return
|
|
2344
|
+
get selection() {
|
|
2345
|
+
return this._context && this._context.selection;
|
|
2651
2346
|
}
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
|
|
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.
|
|
2694
|
-
|
|
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
|
-
|
|
2697
|
-
this.
|
|
2377
|
+
afterViewInit() {
|
|
2378
|
+
if (this._context.contentEditable !== undefined) {
|
|
2379
|
+
this.nativeElement.setAttribute('contenteditable', this._context.contentEditable + '');
|
|
2380
|
+
}
|
|
2698
2381
|
}
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
|
|
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
|
-
|
|
2704
|
-
this.
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
|
|
2708
|
-
|
|
2709
|
-
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
this.
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
this.
|
|
2716
|
-
this.
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
this.
|
|
2720
|
-
this.
|
|
2721
|
-
this.
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
this.
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
this.
|
|
2729
|
-
|
|
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
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
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
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2426
|
+
isStableHeight() {
|
|
2427
|
+
return false;
|
|
2428
|
+
}
|
|
2429
|
+
getRealHeight() {
|
|
2430
|
+
if (this.isStableHeight() && this.stableHeight !== null) {
|
|
2431
|
+
return this.stableHeight;
|
|
2778
2432
|
}
|
|
2779
|
-
|
|
2780
|
-
|
|
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
|
-
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
|
|
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.
|
|
2484
|
+
this.destroyPlaceholder();
|
|
2790
2485
|
}
|
|
2791
|
-
this.appendLog(type, ...args);
|
|
2792
2486
|
}
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
|
|
2798
|
-
this.
|
|
2799
|
-
this.
|
|
2800
|
-
this.
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
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
|
-
|
|
2502
|
+
const lineClamp = Math.floor(editorContentHeight / this.nativeElement.offsetHeight) || 0;
|
|
2503
|
+
placeholderElement.style.webkitLineClamp = `${Math.max(lineClamp, 1)}`;
|
|
2937
2504
|
});
|
|
2938
|
-
|
|
2939
|
-
|
|
2940
|
-
|
|
2941
|
-
|
|
2942
|
-
|
|
2943
|
-
|
|
2944
|
-
|
|
2945
|
-
|
|
2946
|
-
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
|
|
2951
|
-
|
|
2952
|
-
|
|
2953
|
-
|
|
2954
|
-
|
|
2955
|
-
|
|
2956
|
-
|
|
2957
|
-
|
|
2958
|
-
|
|
2959
|
-
|
|
2960
|
-
|
|
2961
|
-
|
|
2962
|
-
|
|
2963
|
-
|
|
2964
|
-
|
|
2965
|
-
|
|
2966
|
-
|
|
2967
|
-
|
|
2968
|
-
|
|
2969
|
-
|
|
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
|
-
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
|
|
2980
|
-
|
|
2981
|
-
|
|
2982
|
-
|
|
2983
|
-
|
|
2984
|
-
|
|
2985
|
-
|
|
2986
|
-
|
|
2987
|
-
|
|
2988
|
-
|
|
2989
|
-
|
|
2990
|
-
|
|
2991
|
-
|
|
2992
|
-
|
|
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
|
-
|
|
2995
|
-
|
|
2996
|
-
|
|
2997
|
-
this.
|
|
2998
|
-
|
|
2999
|
-
|
|
3000
|
-
|
|
3001
|
-
|
|
3002
|
-
|
|
3003
|
-
|
|
3004
|
-
|
|
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
|
-
|
|
3007
|
-
|
|
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
|
-
|
|
3027
|
-
|
|
3028
|
-
|
|
3029
|
-
|
|
3030
|
-
|
|
3031
|
-
|
|
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
|
-
|
|
3034
|
-
this.
|
|
3035
|
-
this.
|
|
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
|
-
|
|
3045
|
-
if (!this.
|
|
2840
|
+
onContextChange() {
|
|
2841
|
+
if (!this.initialized) {
|
|
3046
2842
|
return;
|
|
3047
2843
|
}
|
|
3048
|
-
this.
|
|
3049
|
-
this.
|
|
2844
|
+
this.rerender();
|
|
2845
|
+
this.updateWeakMap();
|
|
2846
|
+
this.leavesRender.update(this.context);
|
|
3050
2847
|
}
|
|
3051
|
-
|
|
3052
|
-
|
|
3053
|
-
|
|
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
|
-
|
|
3063
|
-
if (!this.
|
|
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.
|
|
3067
|
-
|
|
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
|
-
|
|
3084
|
-
|
|
3085
|
-
|
|
3086
|
-
|
|
3087
|
-
|
|
3088
|
-
|
|
3089
|
-
|
|
3090
|
-
|
|
3091
|
-
|
|
3092
|
-
this.
|
|
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
|
-
|
|
3098
|
-
this.
|
|
3099
|
-
this.
|
|
3100
|
-
|
|
3101
|
-
|
|
3102
|
-
|
|
3103
|
-
|
|
3104
|
-
|
|
3105
|
-
|
|
3106
|
-
|
|
3107
|
-
|
|
3108
|
-
|
|
3109
|
-
|
|
3110
|
-
|
|
3111
|
-
|
|
3112
|
-
|
|
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
|
-
|
|
3115
|
-
|
|
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
|
-
|
|
3118
|
-
|
|
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
|
-
|
|
3121
|
-
|
|
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
|
-
|
|
3124
|
-
this.
|
|
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
|
-
|
|
3129
|
-
|
|
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
|
-
|
|
3133
|
-
|
|
3134
|
-
|
|
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
|
-
|
|
3137
|
-
|
|
3077
|
+
if (isVoid) {
|
|
3078
|
+
elementContext.attributes['data-slate-void'] = true;
|
|
3138
3079
|
}
|
|
3139
|
-
|
|
3140
|
-
|
|
3141
|
-
|
|
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
|
-
|
|
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
|
-
|
|
3163
|
-
|
|
3164
|
-
|
|
3165
|
-
|
|
3166
|
-
|
|
3167
|
-
|
|
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
|
-
|
|
3170
|
-
return
|
|
3114
|
+
else {
|
|
3115
|
+
return { selection: null, decorations: ds };
|
|
3171
3116
|
}
|
|
3172
3117
|
}
|
|
3173
|
-
|
|
3174
|
-
|
|
3175
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
3445
|
-
|
|
3446
|
-
|
|
3447
|
-
|
|
3448
|
-
|
|
3449
|
-
|
|
3450
|
-
|
|
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
|
-
|
|
3696
|
+
debugLog('log', 'tryUpdateVirtualViewport');
|
|
3674
3697
|
}
|
|
3675
3698
|
this.tryUpdateVirtualViewportAnimId && cancelAnimationFrame(this.tryUpdateVirtualViewportAnimId);
|
|
3676
3699
|
this.tryUpdateVirtualViewportAnimId = requestAnimationFrame(() => {
|
|
3677
3700
|
if (isDebug) {
|
|
3678
|
-
|
|
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
|
-
|
|
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
|
|
3703
|
-
if (
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
3879
|
-
|
|
3880
|
-
|
|
3881
|
-
|
|
3882
|
-
|
|
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
|
-
|
|
3888
|
-
|
|
3889
|
-
|
|
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
|