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