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