slate-angular 20.2.0-next.2 → 20.2.0-next.21
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 +893 -185
- package/fesm2022/slate-angular.mjs.map +1 -1
- package/index.d.ts +46 -25
- package/package.json +1 -1
|
@@ -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,6 +955,55 @@ const fallbackCopyText = async (text) => {
|
|
|
950
955
|
});
|
|
951
956
|
};
|
|
952
957
|
|
|
958
|
+
const ELEMENT_KEY_TO_HEIGHTS = new WeakMap();
|
|
959
|
+
const EDITOR_TO_BUSINESS_TOP = new WeakMap();
|
|
960
|
+
const getBusinessTop = (editor) => {
|
|
961
|
+
return EDITOR_TO_BUSINESS_TOP.get(editor) ?? 0;
|
|
962
|
+
};
|
|
963
|
+
const getRealHeightByElement = (editor, element, defaultHeight = VIRTUAL_SCROLL_DEFAULT_BLOCK_HEIGHT) => {
|
|
964
|
+
const isVisible = editor.isVisible(element);
|
|
965
|
+
if (!isVisible) {
|
|
966
|
+
return 0;
|
|
967
|
+
}
|
|
968
|
+
if (!element) {
|
|
969
|
+
return defaultHeight;
|
|
970
|
+
}
|
|
971
|
+
const heights = ELEMENT_KEY_TO_HEIGHTS.get(editor);
|
|
972
|
+
const key = AngularEditor.findKey(editor, element);
|
|
973
|
+
const height = heights?.get(key.id);
|
|
974
|
+
if (typeof height === 'number') {
|
|
975
|
+
return height;
|
|
976
|
+
}
|
|
977
|
+
if (heights?.has(key.id)) {
|
|
978
|
+
console.error('getBlockHeight: invalid height value', key.id, height);
|
|
979
|
+
}
|
|
980
|
+
return defaultHeight;
|
|
981
|
+
};
|
|
982
|
+
const buildHeightsAndAccumulatedHeights = (editor) => {
|
|
983
|
+
const children = (editor.children || []);
|
|
984
|
+
const heights = new Array(children.length);
|
|
985
|
+
const accumulatedHeights = new Array(children.length + 1);
|
|
986
|
+
accumulatedHeights[0] = 0;
|
|
987
|
+
for (let i = 0; i < children.length; i++) {
|
|
988
|
+
const height = getRealHeightByElement(editor, children[i]);
|
|
989
|
+
heights[i] = height;
|
|
990
|
+
accumulatedHeights[i + 1] = accumulatedHeights[i] + height;
|
|
991
|
+
}
|
|
992
|
+
return { heights, accumulatedHeights };
|
|
993
|
+
};
|
|
994
|
+
const scrollToElement = (editor, element, scrollTo) => {
|
|
995
|
+
const children = editor.children;
|
|
996
|
+
if (!children.length) {
|
|
997
|
+
return;
|
|
998
|
+
}
|
|
999
|
+
const anchorIndex = children.findIndex(item => item === element);
|
|
1000
|
+
if (anchorIndex < 0) {
|
|
1001
|
+
return;
|
|
1002
|
+
}
|
|
1003
|
+
const { accumulatedHeights } = buildHeightsAndAccumulatedHeights(editor);
|
|
1004
|
+
scrollTo((accumulatedHeights[anchorIndex] ?? 0) + getBusinessTop(editor));
|
|
1005
|
+
};
|
|
1006
|
+
|
|
953
1007
|
const withAngular = (editor, clipboardFormatKey = 'x-slate-fragment') => {
|
|
954
1008
|
let e = editor;
|
|
955
1009
|
let { apply } = e;
|
|
@@ -967,7 +1021,14 @@ const withAngular = (editor, clipboardFormatKey = 'x-slate-fragment') => {
|
|
|
967
1021
|
}
|
|
968
1022
|
// Create a fake selection so that we can add a Base64-encoded copy of the
|
|
969
1023
|
// fragment to the HTML, to decode on future pastes.
|
|
970
|
-
|
|
1024
|
+
let domRange;
|
|
1025
|
+
if (AngularEditor.isEnabledVirtualScroll(e)) {
|
|
1026
|
+
const virtualScrollSelection = EDITOR_TO_VIRTUAL_SCROLL_SELECTION.get(e);
|
|
1027
|
+
if (virtualScrollSelection) {
|
|
1028
|
+
domRange = AngularEditor.toDOMRange(e, virtualScrollSelection);
|
|
1029
|
+
}
|
|
1030
|
+
}
|
|
1031
|
+
domRange = domRange ?? AngularEditor.toDOMRange(e, selection);
|
|
971
1032
|
let contents = domRange.cloneContents();
|
|
972
1033
|
let attach = contents.childNodes[0];
|
|
973
1034
|
// Make sure attach is non-empty, since empty nodes will not get copied.
|
|
@@ -1135,6 +1196,12 @@ const withAngular = (editor, clipboardFormatKey = 'x-slate-fragment') => {
|
|
|
1135
1196
|
NODE_TO_KEY.set(node, key);
|
|
1136
1197
|
}
|
|
1137
1198
|
};
|
|
1199
|
+
e.selectAll = () => {
|
|
1200
|
+
Transforms.select(e, []);
|
|
1201
|
+
};
|
|
1202
|
+
e.isVisible = element => {
|
|
1203
|
+
return true;
|
|
1204
|
+
};
|
|
1138
1205
|
return e;
|
|
1139
1206
|
};
|
|
1140
1207
|
|
|
@@ -1739,6 +1806,7 @@ class BaseElementFlavour extends BaseFlavour {
|
|
|
1739
1806
|
this.getOutletElement = () => {
|
|
1740
1807
|
return this.nativeElement.querySelector('.children-outlet');
|
|
1741
1808
|
};
|
|
1809
|
+
this.stableHeight = null;
|
|
1742
1810
|
}
|
|
1743
1811
|
get element() {
|
|
1744
1812
|
return this._context && this._context.element;
|
|
@@ -1794,6 +1862,7 @@ class BaseElementFlavour extends BaseFlavour {
|
|
|
1794
1862
|
if (ELEMENT_TO_COMPONENT.get(this.element) === this) {
|
|
1795
1863
|
ELEMENT_TO_COMPONENT.delete(this.element);
|
|
1796
1864
|
}
|
|
1865
|
+
this.listRender.destroy();
|
|
1797
1866
|
this.nativeElement?.remove();
|
|
1798
1867
|
}
|
|
1799
1868
|
onContextChange() {
|
|
@@ -1824,11 +1893,21 @@ class BaseElementFlavour extends BaseFlavour {
|
|
|
1824
1893
|
readonly: this._context.readonly
|
|
1825
1894
|
};
|
|
1826
1895
|
}
|
|
1896
|
+
isStableHeight() {
|
|
1897
|
+
return false;
|
|
1898
|
+
}
|
|
1827
1899
|
getRealHeight() {
|
|
1900
|
+
if (this.isStableHeight() && this.stableHeight !== null) {
|
|
1901
|
+
return this.stableHeight;
|
|
1902
|
+
}
|
|
1828
1903
|
const blockCard = getBlockCardByNativeElement(this.nativeElement);
|
|
1829
1904
|
const target = blockCard || this.nativeElement;
|
|
1830
1905
|
const computedStyle = getComputedStyle(target);
|
|
1831
|
-
|
|
1906
|
+
const height = Math.ceil(target.getBoundingClientRect().height) + parseFloat(computedStyle.marginTop) + parseFloat(computedStyle.marginBottom);
|
|
1907
|
+
if (this.isStableHeight()) {
|
|
1908
|
+
this.stableHeight = height;
|
|
1909
|
+
}
|
|
1910
|
+
return height;
|
|
1832
1911
|
}
|
|
1833
1912
|
}
|
|
1834
1913
|
|
|
@@ -2180,6 +2259,9 @@ class LeavesRender {
|
|
|
2180
2259
|
});
|
|
2181
2260
|
return { decoratedLeaves, contexts };
|
|
2182
2261
|
}
|
|
2262
|
+
destroy() {
|
|
2263
|
+
this.views.forEach(view => view.destroy());
|
|
2264
|
+
}
|
|
2183
2265
|
}
|
|
2184
2266
|
function getContext$1(index, leafContexts) {
|
|
2185
2267
|
return leafContexts[index];
|
|
@@ -2222,6 +2304,7 @@ class BaseTextFlavour extends BaseFlavour {
|
|
|
2222
2304
|
NODE_TO_ELEMENT.delete(this.text);
|
|
2223
2305
|
}
|
|
2224
2306
|
ELEMENT_TO_NODE.delete(this.nativeElement);
|
|
2307
|
+
this.leavesRender.destroy();
|
|
2225
2308
|
this.nativeElement?.remove();
|
|
2226
2309
|
}
|
|
2227
2310
|
onContextChange() {
|
|
@@ -2262,6 +2345,7 @@ class ListRender {
|
|
|
2262
2345
|
this.viewContainerRef = viewContainerRef;
|
|
2263
2346
|
this.getOutletParent = getOutletParent;
|
|
2264
2347
|
this.getOutletElement = getOutletElement;
|
|
2348
|
+
this.children = [];
|
|
2265
2349
|
this.views = [];
|
|
2266
2350
|
this.blockCards = [];
|
|
2267
2351
|
this.contexts = [];
|
|
@@ -2411,6 +2495,7 @@ class ListRender {
|
|
|
2411
2495
|
this.blockCards[index].destroy();
|
|
2412
2496
|
}
|
|
2413
2497
|
});
|
|
2498
|
+
this.children = [];
|
|
2414
2499
|
this.views = [];
|
|
2415
2500
|
this.blockCards = [];
|
|
2416
2501
|
this.contexts = [];
|
|
@@ -2553,39 +2638,517 @@ function executeAfterViewInit(editor) {
|
|
|
2553
2638
|
clearAfterViewInitQueue(editor);
|
|
2554
2639
|
}
|
|
2555
2640
|
|
|
2556
|
-
|
|
2641
|
+
class VirtualScrollDebugOverlay {
|
|
2642
|
+
static { this.storageKey = 'slate_virtual_scroll_debug_overlay_state'; }
|
|
2643
|
+
static { this.minWidth = 320; }
|
|
2644
|
+
static { this.minHeight = 240; }
|
|
2645
|
+
static { this.defaultWidth = 410; }
|
|
2646
|
+
static { this.defaultHeight = 480; }
|
|
2647
|
+
static getInstance(doc) {
|
|
2648
|
+
if (!this.instance) {
|
|
2649
|
+
this.instance = new VirtualScrollDebugOverlay(doc);
|
|
2650
|
+
}
|
|
2651
|
+
this.instance.init();
|
|
2652
|
+
return this.instance;
|
|
2653
|
+
}
|
|
2654
|
+
static log(doc, type, ...args) {
|
|
2655
|
+
this.getInstance(doc).log(type, ...args);
|
|
2656
|
+
}
|
|
2657
|
+
static syncScrollTop(doc, value) {
|
|
2658
|
+
const instance = this.getInstance(doc);
|
|
2659
|
+
instance.setScrollTopValue(value);
|
|
2660
|
+
}
|
|
2661
|
+
constructor(doc) {
|
|
2662
|
+
this.doc = doc;
|
|
2663
|
+
this.state = {
|
|
2664
|
+
left: 16,
|
|
2665
|
+
top: 16,
|
|
2666
|
+
collapsed: false,
|
|
2667
|
+
width: VirtualScrollDebugOverlay.defaultWidth,
|
|
2668
|
+
height: VirtualScrollDebugOverlay.defaultHeight
|
|
2669
|
+
};
|
|
2670
|
+
this.originalConsoleLog = console.log.bind(console);
|
|
2671
|
+
this.originalConsoleWarn = console.warn.bind(console);
|
|
2672
|
+
this.dragOffsetX = 0;
|
|
2673
|
+
this.dragOffsetY = 0;
|
|
2674
|
+
this.isDragging = false;
|
|
2675
|
+
this.isResizing = false;
|
|
2676
|
+
this.resizeStartX = 0;
|
|
2677
|
+
this.resizeStartY = 0;
|
|
2678
|
+
this.resizeStartWidth = 0;
|
|
2679
|
+
this.resizeStartHeight = 0;
|
|
2680
|
+
this.dragMoved = false;
|
|
2681
|
+
this.wasDragged = false;
|
|
2682
|
+
this.onDragging = (event) => {
|
|
2683
|
+
if (!this.isDragging || !this.container) {
|
|
2684
|
+
return;
|
|
2685
|
+
}
|
|
2686
|
+
this.dragMoved = true;
|
|
2687
|
+
const nextLeft = event.clientX - this.dragOffsetX;
|
|
2688
|
+
const nextTop = event.clientY - this.dragOffsetY;
|
|
2689
|
+
this.container.style.left = `${nextLeft}px`;
|
|
2690
|
+
this.container.style.top = `${nextTop}px`;
|
|
2691
|
+
this.container.style.right = 'auto';
|
|
2692
|
+
this.container.style.bottom = 'auto';
|
|
2693
|
+
};
|
|
2694
|
+
this.onDragEnd = () => {
|
|
2695
|
+
if (!this.isDragging) {
|
|
2696
|
+
return;
|
|
2697
|
+
}
|
|
2698
|
+
this.isDragging = false;
|
|
2699
|
+
this.wasDragged = this.dragMoved;
|
|
2700
|
+
this.dragMoved = false;
|
|
2701
|
+
this.doc.removeEventListener('mousemove', this.onDragging);
|
|
2702
|
+
this.doc.removeEventListener('mouseup', this.onDragEnd);
|
|
2703
|
+
if (this.container) {
|
|
2704
|
+
const rect = this.container.getBoundingClientRect();
|
|
2705
|
+
this.state.left = rect.left;
|
|
2706
|
+
this.state.top = rect.top;
|
|
2707
|
+
this.persistState();
|
|
2708
|
+
this.container.style.transition = '';
|
|
2709
|
+
}
|
|
2710
|
+
};
|
|
2711
|
+
this.onResizing = (event) => {
|
|
2712
|
+
if (!this.isResizing || !this.container) {
|
|
2713
|
+
return;
|
|
2714
|
+
}
|
|
2715
|
+
const deltaX = event.clientX - this.resizeStartX;
|
|
2716
|
+
const deltaY = event.clientY - this.resizeStartY;
|
|
2717
|
+
const nextWidth = Math.max(VirtualScrollDebugOverlay.minWidth, this.resizeStartWidth + deltaX);
|
|
2718
|
+
const nextHeight = Math.max(VirtualScrollDebugOverlay.minHeight, this.resizeStartHeight + deltaY);
|
|
2719
|
+
this.state.width = nextWidth;
|
|
2720
|
+
this.state.height = nextHeight;
|
|
2721
|
+
this.applySize();
|
|
2722
|
+
};
|
|
2723
|
+
this.onResizeEnd = () => {
|
|
2724
|
+
if (!this.isResizing) {
|
|
2725
|
+
return;
|
|
2726
|
+
}
|
|
2727
|
+
this.isResizing = false;
|
|
2728
|
+
this.doc.removeEventListener('mousemove', this.onResizing);
|
|
2729
|
+
this.doc.removeEventListener('mouseup', this.onResizeEnd);
|
|
2730
|
+
this.persistState();
|
|
2731
|
+
};
|
|
2732
|
+
}
|
|
2733
|
+
init() {
|
|
2734
|
+
if (!this.container) {
|
|
2735
|
+
this.createContainer();
|
|
2736
|
+
}
|
|
2737
|
+
else {
|
|
2738
|
+
this.applyState();
|
|
2739
|
+
}
|
|
2740
|
+
}
|
|
2741
|
+
log(type, ...args) {
|
|
2742
|
+
this.init();
|
|
2743
|
+
if (type === 'warn') {
|
|
2744
|
+
this.originalConsoleWarn(...args);
|
|
2745
|
+
}
|
|
2746
|
+
else {
|
|
2747
|
+
this.originalConsoleLog(...args);
|
|
2748
|
+
}
|
|
2749
|
+
this.appendLog(type, ...args);
|
|
2750
|
+
}
|
|
2751
|
+
dispose() {
|
|
2752
|
+
this.container?.remove();
|
|
2753
|
+
this.container = undefined;
|
|
2754
|
+
this.contentWrapper = undefined;
|
|
2755
|
+
this.bubble = undefined;
|
|
2756
|
+
this.logList = undefined;
|
|
2757
|
+
this.selectorInput = undefined;
|
|
2758
|
+
this.distanceInput = undefined;
|
|
2759
|
+
this.collapseToggle = undefined;
|
|
2760
|
+
this.resizeHandle = undefined;
|
|
2761
|
+
this.doc.removeEventListener('mousemove', this.onDragging);
|
|
2762
|
+
this.doc.removeEventListener('mouseup', this.onDragEnd);
|
|
2763
|
+
this.doc.removeEventListener('mousemove', this.onResizing);
|
|
2764
|
+
this.doc.removeEventListener('mouseup', this.onResizeEnd);
|
|
2765
|
+
this.isDragging = false;
|
|
2766
|
+
this.isResizing = false;
|
|
2767
|
+
}
|
|
2768
|
+
createContainer() {
|
|
2769
|
+
this.loadState();
|
|
2770
|
+
const doc = this.doc;
|
|
2771
|
+
const container = doc.createElement('div');
|
|
2772
|
+
container.style.position = 'fixed';
|
|
2773
|
+
container.style.right = 'auto';
|
|
2774
|
+
container.style.bottom = 'auto';
|
|
2775
|
+
container.style.boxSizing = 'border-box';
|
|
2776
|
+
container.style.background = 'rgba(17, 24, 39, 0.95)';
|
|
2777
|
+
container.style.color = '#e5e7eb';
|
|
2778
|
+
container.style.fontSize = '12px';
|
|
2779
|
+
container.style.border = '1px solid #1f2937';
|
|
2780
|
+
container.style.borderRadius = '10px';
|
|
2781
|
+
container.style.fontFamily = 'Menlo, Consolas, monospace';
|
|
2782
|
+
container.style.boxShadow = '0 10px 30px rgba(0, 0, 0, 0.35)';
|
|
2783
|
+
container.style.zIndex = '9999';
|
|
2784
|
+
container.style.display = 'flex';
|
|
2785
|
+
container.style.flexDirection = 'column';
|
|
2786
|
+
container.style.gap = '10px';
|
|
2787
|
+
container.style.minWidth = `${VirtualScrollDebugOverlay.minWidth}px`;
|
|
2788
|
+
container.style.minHeight = `${VirtualScrollDebugOverlay.minHeight}px`;
|
|
2789
|
+
container.style.maxHeight = '80vh';
|
|
2790
|
+
container.style.cursor = 'default';
|
|
2791
|
+
container.addEventListener('mousedown', event => {
|
|
2792
|
+
if (this.state.collapsed) {
|
|
2793
|
+
this.startDrag(event);
|
|
2794
|
+
}
|
|
2795
|
+
});
|
|
2796
|
+
const header = doc.createElement('div');
|
|
2797
|
+
header.style.display = 'flex';
|
|
2798
|
+
header.style.alignItems = 'center';
|
|
2799
|
+
header.style.justifyContent = 'space-between';
|
|
2800
|
+
header.style.cursor = 'move';
|
|
2801
|
+
header.addEventListener('mousedown', event => {
|
|
2802
|
+
this.startDrag(event);
|
|
2803
|
+
});
|
|
2804
|
+
const title = doc.createElement('div');
|
|
2805
|
+
title.textContent = 'Virtual Scroll Debug';
|
|
2806
|
+
title.style.fontWeight = '600';
|
|
2807
|
+
title.style.letterSpacing = '0.3px';
|
|
2808
|
+
const actions = doc.createElement('div');
|
|
2809
|
+
actions.style.display = 'flex';
|
|
2810
|
+
actions.style.gap = '6px';
|
|
2811
|
+
const collapseButton = doc.createElement('button');
|
|
2812
|
+
collapseButton.type = 'button';
|
|
2813
|
+
collapseButton.textContent = '折叠';
|
|
2814
|
+
collapseButton.style.background = '#1f2937';
|
|
2815
|
+
collapseButton.style.color = '#e5e7eb';
|
|
2816
|
+
collapseButton.style.border = '1px solid #374151';
|
|
2817
|
+
collapseButton.style.borderRadius = '6px';
|
|
2818
|
+
collapseButton.style.padding = '4px 8px';
|
|
2819
|
+
collapseButton.style.cursor = 'pointer';
|
|
2820
|
+
collapseButton.addEventListener('click', () => {
|
|
2821
|
+
this.setCollapsed(!this.state.collapsed);
|
|
2822
|
+
});
|
|
2823
|
+
const clearButton = doc.createElement('button');
|
|
2824
|
+
clearButton.type = 'button';
|
|
2825
|
+
clearButton.textContent = '清除日志';
|
|
2826
|
+
clearButton.style.background = '#374151';
|
|
2827
|
+
clearButton.style.color = '#e5e7eb';
|
|
2828
|
+
clearButton.style.border = '1px solid #4b5563';
|
|
2829
|
+
clearButton.style.borderRadius = '6px';
|
|
2830
|
+
clearButton.style.padding = '4px 10px';
|
|
2831
|
+
clearButton.style.cursor = 'pointer';
|
|
2832
|
+
clearButton.addEventListener('click', () => {
|
|
2833
|
+
if (this.logList) {
|
|
2834
|
+
this.logList.innerHTML = '';
|
|
2835
|
+
}
|
|
2836
|
+
});
|
|
2837
|
+
actions.appendChild(collapseButton);
|
|
2838
|
+
actions.appendChild(clearButton);
|
|
2839
|
+
header.appendChild(title);
|
|
2840
|
+
header.appendChild(actions);
|
|
2841
|
+
const scrollForm = doc.createElement('div');
|
|
2842
|
+
scrollForm.style.display = 'grid';
|
|
2843
|
+
scrollForm.style.gridTemplateColumns = '1fr 110px 50px';
|
|
2844
|
+
scrollForm.style.gap = '6px';
|
|
2845
|
+
scrollForm.style.alignItems = 'center';
|
|
2846
|
+
const selectorInput = doc.createElement('input');
|
|
2847
|
+
selectorInput.placeholder = '滚动容器 selector';
|
|
2848
|
+
selectorInput.style.padding = '6px 8px';
|
|
2849
|
+
selectorInput.style.borderRadius = '6px';
|
|
2850
|
+
selectorInput.style.border = '1px solid #4b5563';
|
|
2851
|
+
selectorInput.style.background = '#111827';
|
|
2852
|
+
selectorInput.style.color = '#e5e7eb';
|
|
2853
|
+
selectorInput.autocomplete = 'off';
|
|
2854
|
+
const distanceInput = doc.createElement('input');
|
|
2855
|
+
distanceInput.placeholder = '滚动距离(px)';
|
|
2856
|
+
distanceInput.type = 'number';
|
|
2857
|
+
distanceInput.style.padding = '6px 8px';
|
|
2858
|
+
distanceInput.style.borderRadius = '6px';
|
|
2859
|
+
distanceInput.style.border = '1px solid #4b5563';
|
|
2860
|
+
distanceInput.style.background = '#111827';
|
|
2861
|
+
distanceInput.style.color = '#e5e7eb';
|
|
2862
|
+
const scrollButton = doc.createElement('button');
|
|
2863
|
+
scrollButton.type = 'button';
|
|
2864
|
+
scrollButton.textContent = '滚动';
|
|
2865
|
+
scrollButton.style.background = '#10b981';
|
|
2866
|
+
scrollButton.style.color = '#0b1c15';
|
|
2867
|
+
scrollButton.style.border = 'none';
|
|
2868
|
+
scrollButton.style.borderRadius = '6px';
|
|
2869
|
+
scrollButton.style.padding = '6px 10px';
|
|
2870
|
+
scrollButton.style.cursor = 'pointer';
|
|
2871
|
+
scrollButton.addEventListener('click', () => {
|
|
2872
|
+
const selector = selectorInput.value.trim();
|
|
2873
|
+
const distanceValue = Number(distanceInput.value ?? 0);
|
|
2874
|
+
const distance = Number.isFinite(distanceValue) ? distanceValue : 0;
|
|
2875
|
+
if (!selector) {
|
|
2876
|
+
this.log('warn', '请先填写滚动容器 selector');
|
|
2877
|
+
return;
|
|
2878
|
+
}
|
|
2879
|
+
const target = doc.querySelector(selector);
|
|
2880
|
+
if (!target) {
|
|
2881
|
+
this.log('warn', `未找到滚动容器: ${selector}`);
|
|
2882
|
+
return;
|
|
2883
|
+
}
|
|
2884
|
+
if (typeof target.scrollTo === 'function') {
|
|
2885
|
+
target.scrollTo({ top: distance, behavior: 'auto' });
|
|
2886
|
+
}
|
|
2887
|
+
else if (Object.prototype.hasOwnProperty.call(target, 'scrollTop')) {
|
|
2888
|
+
target.scrollTop = distance;
|
|
2889
|
+
}
|
|
2890
|
+
else {
|
|
2891
|
+
this.log('warn', '目标元素不支持滚动:', selector);
|
|
2892
|
+
return;
|
|
2893
|
+
}
|
|
2894
|
+
this.log('log', `已将 ${selector} 滚动到`, distance);
|
|
2895
|
+
});
|
|
2896
|
+
scrollForm.appendChild(selectorInput);
|
|
2897
|
+
scrollForm.appendChild(distanceInput);
|
|
2898
|
+
scrollForm.appendChild(scrollButton);
|
|
2899
|
+
const logList = doc.createElement('div');
|
|
2900
|
+
logList.style.height = '260px';
|
|
2901
|
+
logList.style.overflowY = 'auto';
|
|
2902
|
+
logList.style.background = '#0b1220';
|
|
2903
|
+
logList.style.border = '1px solid #1f2937';
|
|
2904
|
+
logList.style.borderRadius = '8px';
|
|
2905
|
+
logList.style.padding = '8px';
|
|
2906
|
+
logList.style.display = 'flex';
|
|
2907
|
+
logList.style.flexDirection = 'column';
|
|
2908
|
+
logList.style.gap = '6px';
|
|
2909
|
+
logList.style.flex = '1';
|
|
2910
|
+
logList.style.minHeight = '160px';
|
|
2911
|
+
const bubble = doc.createElement('div');
|
|
2912
|
+
bubble.textContent = 'VS';
|
|
2913
|
+
bubble.style.display = 'none';
|
|
2914
|
+
bubble.style.alignItems = 'center';
|
|
2915
|
+
bubble.style.justifyContent = 'center';
|
|
2916
|
+
bubble.style.fontWeight = '700';
|
|
2917
|
+
bubble.style.fontSize = '14px';
|
|
2918
|
+
bubble.style.letterSpacing = '0.5px';
|
|
2919
|
+
bubble.style.width = '100%';
|
|
2920
|
+
bubble.style.height = '100%';
|
|
2921
|
+
bubble.style.userSelect = 'none';
|
|
2922
|
+
bubble.addEventListener('click', () => {
|
|
2923
|
+
if (this.wasDragged) {
|
|
2924
|
+
this.wasDragged = false;
|
|
2925
|
+
return;
|
|
2926
|
+
}
|
|
2927
|
+
this.setCollapsed(false);
|
|
2928
|
+
});
|
|
2929
|
+
const contentWrapper = doc.createElement('div');
|
|
2930
|
+
contentWrapper.style.display = 'flex';
|
|
2931
|
+
contentWrapper.style.flexDirection = 'column';
|
|
2932
|
+
contentWrapper.style.gap = '10px';
|
|
2933
|
+
contentWrapper.style.width = '100%';
|
|
2934
|
+
contentWrapper.style.height = '100%';
|
|
2935
|
+
contentWrapper.appendChild(header);
|
|
2936
|
+
contentWrapper.appendChild(scrollForm);
|
|
2937
|
+
contentWrapper.appendChild(logList);
|
|
2938
|
+
container.appendChild(contentWrapper);
|
|
2939
|
+
container.appendChild(bubble);
|
|
2940
|
+
const resizeHandle = doc.createElement('div');
|
|
2941
|
+
resizeHandle.style.position = 'absolute';
|
|
2942
|
+
resizeHandle.style.right = '6px';
|
|
2943
|
+
resizeHandle.style.bottom = '6px';
|
|
2944
|
+
resizeHandle.style.width = '14px';
|
|
2945
|
+
resizeHandle.style.height = '14px';
|
|
2946
|
+
resizeHandle.style.cursor = 'nwse-resize';
|
|
2947
|
+
resizeHandle.style.borderRight = '2px solid #4b5563';
|
|
2948
|
+
resizeHandle.style.borderBottom = '2px solid #4b5563';
|
|
2949
|
+
resizeHandle.addEventListener('mousedown', event => {
|
|
2950
|
+
this.startResize(event);
|
|
2951
|
+
});
|
|
2952
|
+
container.appendChild(resizeHandle);
|
|
2953
|
+
doc.body.appendChild(container);
|
|
2954
|
+
this.container = container;
|
|
2955
|
+
this.contentWrapper = contentWrapper;
|
|
2956
|
+
this.bubble = bubble;
|
|
2957
|
+
this.logList = logList;
|
|
2958
|
+
this.selectorInput = selectorInput;
|
|
2959
|
+
this.distanceInput = distanceInput;
|
|
2960
|
+
this.collapseToggle = collapseButton;
|
|
2961
|
+
this.resizeHandle = resizeHandle;
|
|
2962
|
+
this.applyState();
|
|
2963
|
+
}
|
|
2964
|
+
startDrag(event) {
|
|
2965
|
+
if (event.button !== 0) {
|
|
2966
|
+
return;
|
|
2967
|
+
}
|
|
2968
|
+
if (!this.container) {
|
|
2969
|
+
return;
|
|
2970
|
+
}
|
|
2971
|
+
const rect = this.container.getBoundingClientRect();
|
|
2972
|
+
this.isDragging = true;
|
|
2973
|
+
this.wasDragged = false;
|
|
2974
|
+
this.dragMoved = false;
|
|
2975
|
+
this.dragOffsetX = event.clientX - rect.left;
|
|
2976
|
+
this.dragOffsetY = event.clientY - rect.top;
|
|
2977
|
+
this.container.style.transition = 'none';
|
|
2978
|
+
this.doc.addEventListener('mousemove', this.onDragging);
|
|
2979
|
+
this.doc.addEventListener('mouseup', this.onDragEnd);
|
|
2980
|
+
if (!this.state.collapsed) {
|
|
2981
|
+
event.preventDefault();
|
|
2982
|
+
}
|
|
2983
|
+
}
|
|
2984
|
+
startResize(event) {
|
|
2985
|
+
if (event.button !== 0 || this.state.collapsed) {
|
|
2986
|
+
return;
|
|
2987
|
+
}
|
|
2988
|
+
if (!this.container) {
|
|
2989
|
+
return;
|
|
2990
|
+
}
|
|
2991
|
+
const rect = this.container.getBoundingClientRect();
|
|
2992
|
+
this.isResizing = true;
|
|
2993
|
+
this.resizeStartX = event.clientX;
|
|
2994
|
+
this.resizeStartY = event.clientY;
|
|
2995
|
+
this.resizeStartWidth = rect.width;
|
|
2996
|
+
this.resizeStartHeight = rect.height;
|
|
2997
|
+
this.doc.addEventListener('mousemove', this.onResizing);
|
|
2998
|
+
this.doc.addEventListener('mouseup', this.onResizeEnd);
|
|
2999
|
+
event.preventDefault();
|
|
3000
|
+
event.stopPropagation();
|
|
3001
|
+
}
|
|
3002
|
+
applyPosition() {
|
|
3003
|
+
if (!this.container) {
|
|
3004
|
+
return;
|
|
3005
|
+
}
|
|
3006
|
+
this.container.style.left = `${this.state.left}px`;
|
|
3007
|
+
this.container.style.top = `${this.state.top}px`;
|
|
3008
|
+
}
|
|
3009
|
+
applySize() {
|
|
3010
|
+
if (!this.container) {
|
|
3011
|
+
return;
|
|
3012
|
+
}
|
|
3013
|
+
const width = Math.max(VirtualScrollDebugOverlay.minWidth, this.state.width || VirtualScrollDebugOverlay.defaultWidth);
|
|
3014
|
+
const height = Math.max(VirtualScrollDebugOverlay.minHeight, this.state.height || VirtualScrollDebugOverlay.defaultHeight);
|
|
3015
|
+
this.state.width = width;
|
|
3016
|
+
this.state.height = height;
|
|
3017
|
+
this.container.style.width = `${width}px`;
|
|
3018
|
+
this.container.style.height = `${height}px`;
|
|
3019
|
+
}
|
|
3020
|
+
applyCollapsedState() {
|
|
3021
|
+
if (!this.container || !this.contentWrapper || !this.bubble || !this.collapseToggle) {
|
|
3022
|
+
return;
|
|
3023
|
+
}
|
|
3024
|
+
if (this.state.collapsed) {
|
|
3025
|
+
this.container.style.width = '36px';
|
|
3026
|
+
this.container.style.height = '36px';
|
|
3027
|
+
this.container.style.minWidth = '';
|
|
3028
|
+
this.container.style.minHeight = '';
|
|
3029
|
+
this.container.style.padding = '0';
|
|
3030
|
+
this.container.style.borderRadius = '50%';
|
|
3031
|
+
this.container.style.display = 'flex';
|
|
3032
|
+
this.container.style.flexDirection = 'row';
|
|
3033
|
+
this.container.style.alignItems = 'center';
|
|
3034
|
+
this.container.style.justifyContent = 'center';
|
|
3035
|
+
this.container.style.cursor = 'move';
|
|
3036
|
+
this.contentWrapper.style.display = 'none';
|
|
3037
|
+
this.bubble.style.display = 'flex';
|
|
3038
|
+
this.collapseToggle.textContent = '展开';
|
|
3039
|
+
this.resizeHandle.style.display = 'none';
|
|
3040
|
+
}
|
|
3041
|
+
else {
|
|
3042
|
+
this.applySize();
|
|
3043
|
+
this.container.style.padding = '12px';
|
|
3044
|
+
this.container.style.borderRadius = '10px';
|
|
3045
|
+
this.container.style.display = 'flex';
|
|
3046
|
+
this.container.style.flexDirection = 'column';
|
|
3047
|
+
this.container.style.gap = '10px';
|
|
3048
|
+
this.container.style.cursor = 'default';
|
|
3049
|
+
this.contentWrapper.style.display = 'flex';
|
|
3050
|
+
this.bubble.style.display = 'none';
|
|
3051
|
+
this.collapseToggle.textContent = '折叠';
|
|
3052
|
+
this.resizeHandle.style.display = 'block';
|
|
3053
|
+
}
|
|
3054
|
+
}
|
|
3055
|
+
setCollapsed(collapsed) {
|
|
3056
|
+
this.state.collapsed = collapsed;
|
|
3057
|
+
this.applyCollapsedState();
|
|
3058
|
+
this.persistState();
|
|
3059
|
+
}
|
|
3060
|
+
applyState() {
|
|
3061
|
+
this.applyPosition();
|
|
3062
|
+
this.applyCollapsedState();
|
|
3063
|
+
}
|
|
3064
|
+
loadState() {
|
|
3065
|
+
try {
|
|
3066
|
+
const raw = this.doc.defaultView?.localStorage?.getItem(VirtualScrollDebugOverlay.storageKey);
|
|
3067
|
+
if (raw) {
|
|
3068
|
+
const parsed = JSON.parse(raw);
|
|
3069
|
+
if (typeof parsed.left === 'number') {
|
|
3070
|
+
this.state.left = parsed.left;
|
|
3071
|
+
}
|
|
3072
|
+
if (typeof parsed.top === 'number') {
|
|
3073
|
+
this.state.top = parsed.top;
|
|
3074
|
+
}
|
|
3075
|
+
if (typeof parsed.collapsed === 'boolean') {
|
|
3076
|
+
this.state.collapsed = parsed.collapsed;
|
|
3077
|
+
}
|
|
3078
|
+
if (typeof parsed.width === 'number') {
|
|
3079
|
+
this.state.width = parsed.width;
|
|
3080
|
+
}
|
|
3081
|
+
if (typeof parsed.height === 'number') {
|
|
3082
|
+
this.state.height = parsed.height;
|
|
3083
|
+
}
|
|
3084
|
+
}
|
|
3085
|
+
}
|
|
3086
|
+
catch {
|
|
3087
|
+
// ignore storage errors
|
|
3088
|
+
}
|
|
3089
|
+
}
|
|
3090
|
+
persistState() {
|
|
3091
|
+
try {
|
|
3092
|
+
this.doc.defaultView?.localStorage?.setItem(VirtualScrollDebugOverlay.storageKey, JSON.stringify(this.state));
|
|
3093
|
+
}
|
|
3094
|
+
catch {
|
|
3095
|
+
// ignore storage errors
|
|
3096
|
+
}
|
|
3097
|
+
}
|
|
3098
|
+
appendLog(type, ...args) {
|
|
3099
|
+
if (!this.logList) {
|
|
3100
|
+
return;
|
|
3101
|
+
}
|
|
3102
|
+
const item = this.doc.createElement('div');
|
|
3103
|
+
item.style.display = 'flex';
|
|
3104
|
+
item.style.gap = '6px';
|
|
3105
|
+
item.style.alignItems = 'flex-start';
|
|
3106
|
+
item.style.wordBreak = 'break-all';
|
|
3107
|
+
item.style.color = type === 'warn' ? '#fbbf24' : '#9ca3af';
|
|
3108
|
+
const time = this.doc.createElement('span');
|
|
3109
|
+
time.textContent = new Date().toLocaleTimeString();
|
|
3110
|
+
time.style.color = '#6b7280';
|
|
3111
|
+
time.style.flexShrink = '0';
|
|
3112
|
+
time.style.width = '72px';
|
|
3113
|
+
const text = this.doc.createElement('span');
|
|
3114
|
+
text.textContent = `[${type}] ${args.map(arg => this.formatValue(arg)).join(' ')}`;
|
|
3115
|
+
item.appendChild(time);
|
|
3116
|
+
item.appendChild(text);
|
|
3117
|
+
this.logList.appendChild(item);
|
|
3118
|
+
this.logList.scrollTop = this.logList.scrollHeight;
|
|
3119
|
+
}
|
|
3120
|
+
formatValue(value) {
|
|
3121
|
+
if (typeof value === 'string') {
|
|
3122
|
+
return value;
|
|
3123
|
+
}
|
|
3124
|
+
try {
|
|
3125
|
+
return JSON.stringify(value);
|
|
3126
|
+
}
|
|
3127
|
+
catch (error) {
|
|
3128
|
+
return String(value);
|
|
3129
|
+
}
|
|
3130
|
+
}
|
|
3131
|
+
setScrollTopValue(value) {
|
|
3132
|
+
if (this.distanceInput) {
|
|
3133
|
+
this.distanceInput.value = String(value ?? 0);
|
|
3134
|
+
}
|
|
3135
|
+
}
|
|
3136
|
+
}
|
|
3137
|
+
|
|
2557
3138
|
// not correctly clipboardData on beforeinput
|
|
2558
3139
|
const forceOnDOMPaste = IS_SAFARI;
|
|
2559
3140
|
const isDebug = localStorage.getItem(SLATE_DEBUG_KEY) === 'true';
|
|
3141
|
+
const isDebugScrollTop = localStorage.getItem(SLATE_DEBUG_KEY_SCROLL_TOP) === 'true';
|
|
2560
3142
|
class SlateEditable {
|
|
2561
3143
|
set virtualScroll(config) {
|
|
2562
|
-
this.
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
if (isDebug) {
|
|
2571
|
-
console.log('async measureHeightByIndexes:', result);
|
|
2572
|
-
}
|
|
2573
|
-
this.applyVirtualView(result || virtualView);
|
|
2574
|
-
if (this.listRender.initialized) {
|
|
2575
|
-
this.listRender.update(this.renderedChildren, this.editor, this.context);
|
|
2576
|
-
}
|
|
2577
|
-
this.scheduleMeasureVisibleHeights();
|
|
2578
|
-
});
|
|
2579
|
-
}
|
|
2580
|
-
else {
|
|
2581
|
-
this.applyVirtualView(virtualView);
|
|
2582
|
-
if (this.listRender.initialized) {
|
|
2583
|
-
this.listRender.update(virtualView.renderedChildren, this.editor, this.context);
|
|
2584
|
-
}
|
|
2585
|
-
this.scheduleMeasureVisibleHeights();
|
|
2586
|
-
}
|
|
2587
|
-
}
|
|
2588
|
-
});
|
|
3144
|
+
this.virtualScrollConfig = config;
|
|
3145
|
+
if (isDebugScrollTop) {
|
|
3146
|
+
this.debugLog('log', 'virtualScrollConfig scrollTop:', config.scrollTop);
|
|
3147
|
+
}
|
|
3148
|
+
IS_ENABLED_VIRTUAL_SCROLL.set(this.editor, config.enabled);
|
|
3149
|
+
if (this.isEnabledVirtualScroll()) {
|
|
3150
|
+
this.tryUpdateVirtualViewport();
|
|
3151
|
+
}
|
|
2589
3152
|
}
|
|
2590
3153
|
get hasBeforeInputSupport() {
|
|
2591
3154
|
return HAS_BEFORE_INPUT_SUPPORT;
|
|
@@ -2630,15 +3193,15 @@ class SlateEditable {
|
|
|
2630
3193
|
return null;
|
|
2631
3194
|
}
|
|
2632
3195
|
};
|
|
2633
|
-
this.
|
|
3196
|
+
this.virtualScrollConfig = {
|
|
2634
3197
|
enabled: false,
|
|
2635
3198
|
scrollTop: 0,
|
|
2636
|
-
viewportHeight: 0
|
|
3199
|
+
viewportHeight: 0,
|
|
3200
|
+
viewportBoundingTop: 0
|
|
2637
3201
|
};
|
|
2638
|
-
this.
|
|
2639
|
-
this.
|
|
2640
|
-
this.
|
|
2641
|
-
this.measurePending = false;
|
|
3202
|
+
this.inViewportChildren = [];
|
|
3203
|
+
this.inViewportIndics = new Set();
|
|
3204
|
+
this.keyHeightMap = new Map();
|
|
2642
3205
|
this.virtualScrollInitialized = false;
|
|
2643
3206
|
}
|
|
2644
3207
|
ngOnInit() {
|
|
@@ -2650,6 +3213,7 @@ class SlateEditable {
|
|
|
2650
3213
|
NODE_TO_ELEMENT.set(this.editor, this.elementRef.nativeElement);
|
|
2651
3214
|
ELEMENT_TO_NODE.set(this.elementRef.nativeElement, this.editor);
|
|
2652
3215
|
IS_READ_ONLY.set(this.editor, this.readonly);
|
|
3216
|
+
ELEMENT_KEY_TO_HEIGHTS.set(this.editor, this.keyHeightMap);
|
|
2653
3217
|
EDITOR_TO_ON_CHANGE.set(this.editor, () => {
|
|
2654
3218
|
this.ngZone.run(() => {
|
|
2655
3219
|
this.onChange();
|
|
@@ -2663,7 +3227,7 @@ class SlateEditable {
|
|
|
2663
3227
|
// add browser class
|
|
2664
3228
|
let browserClass = IS_FIREFOX ? 'firefox' : IS_SAFARI ? 'safari' : '';
|
|
2665
3229
|
browserClass && this.elementRef.nativeElement.classList.add(browserClass);
|
|
2666
|
-
this.
|
|
3230
|
+
this.initializeVirtualScroll();
|
|
2667
3231
|
this.listRender = new ListRender(this.viewContext, this.viewContainerRef, this.getOutletParent, this.getOutletElement);
|
|
2668
3232
|
}
|
|
2669
3233
|
ngOnChanges(simpleChanges) {
|
|
@@ -2695,16 +3259,26 @@ class SlateEditable {
|
|
|
2695
3259
|
if (value && value.length) {
|
|
2696
3260
|
this.editor.children = value;
|
|
2697
3261
|
this.initializeContext();
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
this.listRender.
|
|
3262
|
+
if (this.isEnabledVirtualScroll()) {
|
|
3263
|
+
const virtualView = this.calculateVirtualViewport();
|
|
3264
|
+
this.applyVirtualView(virtualView);
|
|
3265
|
+
const childrenForRender = virtualView.inViewportChildren;
|
|
3266
|
+
if (!this.listRender.initialized) {
|
|
3267
|
+
this.listRender.initialize(childrenForRender, this.editor, this.context);
|
|
3268
|
+
}
|
|
3269
|
+
else {
|
|
3270
|
+
this.listRender.update(childrenForRender, this.editor, this.context);
|
|
3271
|
+
}
|
|
3272
|
+
this.tryMeasureInViewportChildrenHeights();
|
|
2703
3273
|
}
|
|
2704
3274
|
else {
|
|
2705
|
-
this.listRender.
|
|
3275
|
+
if (!this.listRender.initialized) {
|
|
3276
|
+
this.listRender.initialize(this.editor.children, this.editor, this.context);
|
|
3277
|
+
}
|
|
3278
|
+
else {
|
|
3279
|
+
this.listRender.update(this.editor.children, this.editor, this.context);
|
|
3280
|
+
}
|
|
2706
3281
|
}
|
|
2707
|
-
this.scheduleMeasureVisibleHeights();
|
|
2708
3282
|
this.cdr.markForCheck();
|
|
2709
3283
|
}
|
|
2710
3284
|
}
|
|
@@ -2735,9 +3309,36 @@ class SlateEditable {
|
|
|
2735
3309
|
this.addEventListener(event.name, () => { });
|
|
2736
3310
|
});
|
|
2737
3311
|
}
|
|
3312
|
+
calculateVirtualScrollSelection(selection) {
|
|
3313
|
+
if (selection) {
|
|
3314
|
+
const indics = Array.from(this.inViewportIndics.values());
|
|
3315
|
+
if (indics.length > 0) {
|
|
3316
|
+
const currentVisibleRange = {
|
|
3317
|
+
anchor: Editor.start(this.editor, [indics[0]]),
|
|
3318
|
+
focus: Editor.end(this.editor, [indics[indics.length - 1]])
|
|
3319
|
+
};
|
|
3320
|
+
const [start, end] = Range.edges(selection);
|
|
3321
|
+
const forwardSelection = { anchor: start, focus: end };
|
|
3322
|
+
const intersectedSelection = Range.intersection(forwardSelection, currentVisibleRange);
|
|
3323
|
+
EDITOR_TO_VIRTUAL_SCROLL_SELECTION.set(this.editor, intersectedSelection);
|
|
3324
|
+
if (!intersectedSelection || !Range.equals(intersectedSelection, forwardSelection)) {
|
|
3325
|
+
if (isDebug) {
|
|
3326
|
+
this.debugLog('log', `selection is not in visible range, selection: ${JSON.stringify(selection)}, intersectedSelection: ${JSON.stringify(intersectedSelection)}`);
|
|
3327
|
+
}
|
|
3328
|
+
return intersectedSelection;
|
|
3329
|
+
}
|
|
3330
|
+
return selection;
|
|
3331
|
+
}
|
|
3332
|
+
}
|
|
3333
|
+
EDITOR_TO_VIRTUAL_SCROLL_SELECTION.set(this.editor, null);
|
|
3334
|
+
return selection;
|
|
3335
|
+
}
|
|
2738
3336
|
toNativeSelection() {
|
|
2739
3337
|
try {
|
|
2740
|
-
|
|
3338
|
+
let { selection } = this.editor;
|
|
3339
|
+
if (this.isEnabledVirtualScroll()) {
|
|
3340
|
+
selection = this.calculateVirtualScrollSelection(selection);
|
|
3341
|
+
}
|
|
2741
3342
|
const root = AngularEditor.findDocumentOrShadowRoot(this.editor);
|
|
2742
3343
|
const { activeElement } = root;
|
|
2743
3344
|
const domSelection = root.getSelection();
|
|
@@ -2825,10 +3426,12 @@ class SlateEditable {
|
|
|
2825
3426
|
ngDoCheck() { }
|
|
2826
3427
|
forceRender() {
|
|
2827
3428
|
this.updateContext();
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
|
|
3429
|
+
if (this.isEnabledVirtualScroll()) {
|
|
3430
|
+
this.updateListRenderAndRemeasureHeights();
|
|
3431
|
+
}
|
|
3432
|
+
else {
|
|
3433
|
+
this.listRender.update(this.editor.children, this.editor, this.context);
|
|
3434
|
+
}
|
|
2832
3435
|
// repair collaborative editing when Chinese input is interrupted by other users' cursors
|
|
2833
3436
|
// when the DOMElement where the selection is located is removed
|
|
2834
3437
|
// the compositionupdate and compositionend events will no longer be fired
|
|
@@ -2867,12 +3470,32 @@ class SlateEditable {
|
|
|
2867
3470
|
render() {
|
|
2868
3471
|
const changed = this.updateContext();
|
|
2869
3472
|
if (changed) {
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
|
|
2873
|
-
|
|
3473
|
+
if (this.isEnabledVirtualScroll()) {
|
|
3474
|
+
this.updateListRenderAndRemeasureHeights();
|
|
3475
|
+
}
|
|
3476
|
+
else {
|
|
3477
|
+
this.listRender.update(this.editor.children, this.editor, this.context);
|
|
3478
|
+
}
|
|
2874
3479
|
}
|
|
2875
3480
|
}
|
|
3481
|
+
updateListRenderAndRemeasureHeights() {
|
|
3482
|
+
const virtualView = this.calculateVirtualViewport();
|
|
3483
|
+
const oldInViewportChildren = this.inViewportChildren;
|
|
3484
|
+
this.applyVirtualView(virtualView);
|
|
3485
|
+
this.listRender.update(this.inViewportChildren, this.editor, this.context);
|
|
3486
|
+
// 新增或者修改的才需要重算,计算出这个结果
|
|
3487
|
+
const remeasureIndics = [];
|
|
3488
|
+
const newInViewportIndics = Array.from(this.inViewportIndics);
|
|
3489
|
+
this.inViewportChildren.forEach((child, index) => {
|
|
3490
|
+
if (oldInViewportChildren.indexOf(child) === -1) {
|
|
3491
|
+
remeasureIndics.push(newInViewportIndics[index]);
|
|
3492
|
+
}
|
|
3493
|
+
});
|
|
3494
|
+
if (isDebug && remeasureIndics.length > 0) {
|
|
3495
|
+
console.log('remeasure height by indics: ', remeasureIndics);
|
|
3496
|
+
}
|
|
3497
|
+
this.remeasureHeightByIndics(remeasureIndics);
|
|
3498
|
+
}
|
|
2876
3499
|
updateContext() {
|
|
2877
3500
|
const decorations = this.generateDecorations();
|
|
2878
3501
|
if (this.context.selection !== this.editor.selection ||
|
|
@@ -2933,105 +3556,197 @@ class SlateEditable {
|
|
|
2933
3556
|
decorations.push(...placeholderDecorations);
|
|
2934
3557
|
return decorations;
|
|
2935
3558
|
}
|
|
2936
|
-
|
|
2937
|
-
return !!(this.
|
|
3559
|
+
isEnabledVirtualScroll() {
|
|
3560
|
+
return !!(this.virtualScrollConfig && this.virtualScrollConfig.enabled);
|
|
2938
3561
|
}
|
|
2939
|
-
|
|
3562
|
+
initializeVirtualScroll() {
|
|
2940
3563
|
if (this.virtualScrollInitialized) {
|
|
2941
3564
|
return;
|
|
2942
3565
|
}
|
|
2943
|
-
if (this.
|
|
3566
|
+
if (this.isEnabledVirtualScroll()) {
|
|
2944
3567
|
this.virtualScrollInitialized = true;
|
|
2945
3568
|
this.virtualTopHeightElement = document.createElement('div');
|
|
2946
3569
|
this.virtualTopHeightElement.classList.add('virtual-top-height');
|
|
3570
|
+
this.virtualTopHeightElement.contentEditable = 'false';
|
|
2947
3571
|
this.virtualBottomHeightElement = document.createElement('div');
|
|
2948
3572
|
this.virtualBottomHeightElement.classList.add('virtual-bottom-height');
|
|
3573
|
+
this.virtualBottomHeightElement.contentEditable = 'false';
|
|
2949
3574
|
this.virtualCenterOutlet = document.createElement('div');
|
|
2950
3575
|
this.virtualCenterOutlet.classList.add('virtual-center-outlet');
|
|
2951
3576
|
this.elementRef.nativeElement.appendChild(this.virtualTopHeightElement);
|
|
2952
3577
|
this.elementRef.nativeElement.appendChild(this.virtualCenterOutlet);
|
|
2953
3578
|
this.elementRef.nativeElement.appendChild(this.virtualBottomHeightElement);
|
|
3579
|
+
let editorResizeObserverRectWidth = this.elementRef.nativeElement.getBoundingClientRect()?.width ?? 0;
|
|
3580
|
+
this.editorResizeObserver = new ResizeObserver(entries => {
|
|
3581
|
+
if (entries.length > 0 && entries[0].contentRect.width !== editorResizeObserverRectWidth) {
|
|
3582
|
+
editorResizeObserverRectWidth = entries[0].contentRect.width;
|
|
3583
|
+
const remeasureIndics = Array.from(this.inViewportIndics);
|
|
3584
|
+
this.remeasureHeightByIndics(remeasureIndics);
|
|
3585
|
+
}
|
|
3586
|
+
});
|
|
3587
|
+
this.editorResizeObserver.observe(this.elementRef.nativeElement);
|
|
3588
|
+
if (isDebug) {
|
|
3589
|
+
const doc = this.elementRef?.nativeElement?.ownerDocument ?? document;
|
|
3590
|
+
VirtualScrollDebugOverlay.getInstance(doc);
|
|
3591
|
+
}
|
|
2954
3592
|
}
|
|
2955
3593
|
}
|
|
2956
|
-
|
|
3594
|
+
setVirtualSpaceHeight(topHeight, bottomHeight) {
|
|
2957
3595
|
if (!this.virtualScrollInitialized) {
|
|
2958
3596
|
return;
|
|
2959
3597
|
}
|
|
2960
3598
|
this.virtualTopHeightElement.style.height = `${topHeight}px`;
|
|
2961
3599
|
this.virtualBottomHeightElement.style.height = `${bottomHeight}px`;
|
|
2962
3600
|
}
|
|
2963
|
-
|
|
3601
|
+
debugLog(type, ...args) {
|
|
3602
|
+
const doc = this.elementRef?.nativeElement?.ownerDocument ?? document;
|
|
3603
|
+
VirtualScrollDebugOverlay.log(doc, type, ...args);
|
|
3604
|
+
}
|
|
3605
|
+
tryUpdateVirtualViewport() {
|
|
3606
|
+
this.tryUpdateVirtualViewportAnimId && cancelAnimationFrame(this.tryUpdateVirtualViewportAnimId);
|
|
3607
|
+
this.tryUpdateVirtualViewportAnimId = requestAnimationFrame(() => {
|
|
3608
|
+
let virtualView = this.calculateVirtualViewport();
|
|
3609
|
+
let diff = this.diffVirtualViewport(virtualView);
|
|
3610
|
+
if (!diff.isDiff) {
|
|
3611
|
+
return;
|
|
3612
|
+
}
|
|
3613
|
+
// diff.isAddedTop
|
|
3614
|
+
if (diff.isMissingTop) {
|
|
3615
|
+
const remeasureIndics = diff.diffTopRenderedIndexes;
|
|
3616
|
+
const result = this.remeasureHeightByIndics(remeasureIndics);
|
|
3617
|
+
if (result) {
|
|
3618
|
+
virtualView = this.calculateVirtualViewport();
|
|
3619
|
+
diff = this.diffVirtualViewport(virtualView, 'second');
|
|
3620
|
+
if (!diff.isDiff) {
|
|
3621
|
+
return;
|
|
3622
|
+
}
|
|
3623
|
+
}
|
|
3624
|
+
}
|
|
3625
|
+
this.applyVirtualView(virtualView);
|
|
3626
|
+
if (this.listRender.initialized) {
|
|
3627
|
+
this.listRender.update(virtualView.inViewportChildren, this.editor, this.context);
|
|
3628
|
+
if (!AngularEditor.isReadOnly(this.editor) && this.editor.selection) {
|
|
3629
|
+
this.toNativeSelection();
|
|
3630
|
+
}
|
|
3631
|
+
}
|
|
3632
|
+
if (diff.isAddedTop) {
|
|
3633
|
+
const remeasureAddedIndics = diff.diffTopRenderedIndexes;
|
|
3634
|
+
if (isDebug) {
|
|
3635
|
+
this.debugLog('log', 'isAddedTop to remeasure heights: ', remeasureAddedIndics);
|
|
3636
|
+
}
|
|
3637
|
+
const startIndexBeforeAdd = diff.diffTopRenderedIndexes[diff.diffTopRenderedIndexes.length - 1] + 1;
|
|
3638
|
+
const topHeightBeforeAdd = virtualView.accumulatedHeights[startIndexBeforeAdd];
|
|
3639
|
+
const result = this.remeasureHeightByIndics(remeasureAddedIndics);
|
|
3640
|
+
if (result) {
|
|
3641
|
+
const newHeights = buildHeightsAndAccumulatedHeights(this.editor);
|
|
3642
|
+
const visibleStartIndex = diff.diffTopRenderedIndexes[0];
|
|
3643
|
+
const actualTopHeightAfterAdd = newHeights.accumulatedHeights[startIndexBeforeAdd];
|
|
3644
|
+
const adjustedTopHeight = (visibleStartIndex === -1 ? 0 : newHeights.accumulatedHeights[visibleStartIndex]) -
|
|
3645
|
+
(actualTopHeightAfterAdd - topHeightBeforeAdd);
|
|
3646
|
+
if (adjustedTopHeight !== virtualView.top) {
|
|
3647
|
+
if (isDebug) {
|
|
3648
|
+
this.debugLog('log', `update top height cause added element in top: ${adjustedTopHeight}, old height: ${virtualView.top}`);
|
|
3649
|
+
}
|
|
3650
|
+
this.virtualTopHeightElement.style.height = `${adjustedTopHeight}px`;
|
|
3651
|
+
}
|
|
3652
|
+
}
|
|
3653
|
+
}
|
|
3654
|
+
this.tryMeasureInViewportChildrenHeights();
|
|
3655
|
+
});
|
|
3656
|
+
}
|
|
3657
|
+
calculateVirtualViewport() {
|
|
2964
3658
|
const children = (this.editor.children || []);
|
|
2965
|
-
if (!children.length || !this.
|
|
3659
|
+
if (!children.length || !this.isEnabledVirtualScroll()) {
|
|
2966
3660
|
return {
|
|
2967
|
-
|
|
3661
|
+
inViewportChildren: children,
|
|
2968
3662
|
visibleIndexes: new Set(),
|
|
2969
3663
|
top: 0,
|
|
2970
3664
|
bottom: 0,
|
|
2971
3665
|
heights: []
|
|
2972
3666
|
};
|
|
2973
3667
|
}
|
|
2974
|
-
const scrollTop = this.
|
|
2975
|
-
|
|
3668
|
+
const scrollTop = this.virtualScrollConfig.scrollTop;
|
|
3669
|
+
if (isDebug) {
|
|
3670
|
+
const doc = this.elementRef?.nativeElement?.ownerDocument ?? document;
|
|
3671
|
+
VirtualScrollDebugOverlay.syncScrollTop(doc, Number.isFinite(scrollTop) ? scrollTop : 0);
|
|
3672
|
+
}
|
|
3673
|
+
const viewportHeight = this.virtualScrollConfig.viewportHeight ?? 0;
|
|
2976
3674
|
if (!viewportHeight) {
|
|
2977
|
-
// 已经启用虚拟滚动,但可视区域高度还未获取到,先置空不渲染
|
|
2978
3675
|
return {
|
|
2979
|
-
|
|
3676
|
+
inViewportChildren: [],
|
|
2980
3677
|
visibleIndexes: new Set(),
|
|
2981
3678
|
top: 0,
|
|
2982
3679
|
bottom: 0,
|
|
2983
3680
|
heights: []
|
|
2984
3681
|
};
|
|
2985
3682
|
}
|
|
2986
|
-
const
|
|
2987
|
-
|
|
2988
|
-
|
|
2989
|
-
|
|
2990
|
-
|
|
2991
|
-
|
|
2992
|
-
|
|
2993
|
-
|
|
2994
|
-
|
|
2995
|
-
|
|
2996
|
-
|
|
2997
|
-
|
|
2998
|
-
|
|
3683
|
+
const elementLength = children.length;
|
|
3684
|
+
if (!EDITOR_TO_BUSINESS_TOP.has(this.editor)) {
|
|
3685
|
+
EDITOR_TO_BUSINESS_TOP.set(this.editor, 0);
|
|
3686
|
+
setTimeout(() => {
|
|
3687
|
+
const virtualTopBoundingTop = this.virtualTopHeightElement.getBoundingClientRect()?.top ?? 0;
|
|
3688
|
+
const businessTop = Math.ceil(virtualTopBoundingTop) +
|
|
3689
|
+
Math.ceil(this.virtualScrollConfig.scrollTop) -
|
|
3690
|
+
Math.floor(this.virtualScrollConfig.viewportBoundingTop);
|
|
3691
|
+
EDITOR_TO_BUSINESS_TOP.set(this.editor, businessTop);
|
|
3692
|
+
if (isDebug) {
|
|
3693
|
+
this.debugLog('log', 'businessTop', businessTop);
|
|
3694
|
+
}
|
|
3695
|
+
}, 100);
|
|
3696
|
+
}
|
|
3697
|
+
const adjustedScrollTop = Math.max(0, scrollTop - getBusinessTop(this.editor));
|
|
3698
|
+
const { heights, accumulatedHeights } = buildHeightsAndAccumulatedHeights(this.editor);
|
|
3699
|
+
const totalHeight = accumulatedHeights[elementLength];
|
|
3700
|
+
const maxScrollTop = Math.max(0, totalHeight - viewportHeight);
|
|
3701
|
+
const limitedScrollTop = Math.min(adjustedScrollTop, maxScrollTop);
|
|
3702
|
+
const viewBottom = limitedScrollTop + viewportHeight + getBusinessTop(this.editor);
|
|
3703
|
+
let accumulatedOffset = 0;
|
|
3704
|
+
let visibleStartIndex = -1;
|
|
2999
3705
|
const visible = [];
|
|
3000
3706
|
const visibleIndexes = [];
|
|
3001
|
-
let
|
|
3002
|
-
|
|
3003
|
-
|
|
3004
|
-
|
|
3005
|
-
|
|
3006
|
-
|
|
3007
|
-
|
|
3008
|
-
|
|
3009
|
-
|
|
3010
|
-
|
|
3011
|
-
|
|
3012
|
-
|
|
3707
|
+
for (let i = 0; i < elementLength && accumulatedOffset < viewBottom; i++) {
|
|
3708
|
+
const currentHeight = heights[i];
|
|
3709
|
+
const nextOffset = accumulatedOffset + currentHeight;
|
|
3710
|
+
// 可视区域有交集,加入渲染
|
|
3711
|
+
if (nextOffset > limitedScrollTop && accumulatedOffset < viewBottom) {
|
|
3712
|
+
if (visibleStartIndex === -1)
|
|
3713
|
+
visibleStartIndex = i; // 第一个相交起始位置
|
|
3714
|
+
visible.push(children[i]);
|
|
3715
|
+
visibleIndexes.push(i);
|
|
3716
|
+
}
|
|
3717
|
+
accumulatedOffset = nextOffset;
|
|
3718
|
+
}
|
|
3719
|
+
if (visibleStartIndex === -1 && elementLength) {
|
|
3720
|
+
visibleStartIndex = elementLength - 1;
|
|
3721
|
+
visible.push(children[visibleStartIndex]);
|
|
3722
|
+
visibleIndexes.push(visibleStartIndex);
|
|
3723
|
+
}
|
|
3724
|
+
const visibleEndIndex = visibleStartIndex === -1 ? elementLength - 1 : (visibleIndexes[visibleIndexes.length - 1] ?? visibleStartIndex);
|
|
3725
|
+
const top = visibleStartIndex === -1 ? 0 : accumulatedHeights[visibleStartIndex];
|
|
3726
|
+
const bottom = totalHeight - accumulatedHeights[visibleEndIndex + 1];
|
|
3013
3727
|
return {
|
|
3014
|
-
|
|
3015
|
-
visibleIndexes:
|
|
3728
|
+
inViewportChildren: visible.length ? visible : children,
|
|
3729
|
+
visibleIndexes: new Set(visibleIndexes),
|
|
3016
3730
|
top,
|
|
3017
3731
|
bottom,
|
|
3018
|
-
heights
|
|
3732
|
+
heights,
|
|
3733
|
+
accumulatedHeights
|
|
3019
3734
|
};
|
|
3020
3735
|
}
|
|
3021
3736
|
applyVirtualView(virtualView) {
|
|
3022
|
-
this.
|
|
3023
|
-
this.
|
|
3024
|
-
this.
|
|
3737
|
+
this.inViewportChildren = virtualView.inViewportChildren;
|
|
3738
|
+
this.setVirtualSpaceHeight(virtualView.top, virtualView.bottom);
|
|
3739
|
+
this.inViewportIndics = virtualView.visibleIndexes;
|
|
3025
3740
|
}
|
|
3026
|
-
|
|
3027
|
-
if (!this.
|
|
3741
|
+
diffVirtualViewport(virtualView, stage = 'first') {
|
|
3742
|
+
if (!this.inViewportChildren.length) {
|
|
3028
3743
|
return {
|
|
3029
3744
|
isDiff: true,
|
|
3030
3745
|
diffTopRenderedIndexes: [],
|
|
3031
3746
|
diffBottomRenderedIndexes: []
|
|
3032
3747
|
};
|
|
3033
3748
|
}
|
|
3034
|
-
const oldVisibleIndexes = [...this.
|
|
3749
|
+
const oldVisibleIndexes = [...this.inViewportIndics];
|
|
3035
3750
|
const newVisibleIndexes = [...virtualView.visibleIndexes];
|
|
3036
3751
|
const firstNewIndex = newVisibleIndexes[0];
|
|
3037
3752
|
const lastNewIndex = newVisibleIndexes[newVisibleIndexes.length - 1];
|
|
@@ -3087,17 +3802,18 @@ class SlateEditable {
|
|
|
3087
3802
|
}
|
|
3088
3803
|
}
|
|
3089
3804
|
if (isDebug) {
|
|
3090
|
-
|
|
3091
|
-
|
|
3092
|
-
|
|
3093
|
-
|
|
3805
|
+
this.debugLog('log', `====== diffVirtualViewport stage: ${stage} ======`);
|
|
3806
|
+
this.debugLog('log', 'oldVisibleIndexes:', oldVisibleIndexes);
|
|
3807
|
+
this.debugLog('log', 'newVisibleIndexes:', newVisibleIndexes);
|
|
3808
|
+
this.debugLog('log', 'diffTopRenderedIndexes:', isMissingTop ? '-' : isAddedTop ? '+' : '-', diffTopRenderedIndexes, diffTopRenderedIndexes.map(index => getRealHeightByElement(this.editor, this.editor.children[index], 0)));
|
|
3809
|
+
this.debugLog('log', 'diffBottomRenderedIndexes:', isAddedBottom ? '+' : isMissingBottom ? '-' : '+', diffBottomRenderedIndexes, diffBottomRenderedIndexes.map(index => getRealHeightByElement(this.editor, this.editor.children[index], 0)));
|
|
3094
3810
|
const needTop = virtualView.heights.slice(0, newVisibleIndexes[0]).reduce((acc, height) => acc + height, 0);
|
|
3095
3811
|
const needBottom = virtualView.heights
|
|
3096
3812
|
.slice(newVisibleIndexes[newVisibleIndexes.length - 1] + 1)
|
|
3097
3813
|
.reduce((acc, height) => acc + height, 0);
|
|
3098
|
-
|
|
3099
|
-
|
|
3100
|
-
|
|
3814
|
+
this.debugLog('log', 'newTopHeight:', needTop, 'prevTopHeight:', parseFloat(this.virtualTopHeightElement.style.height));
|
|
3815
|
+
this.debugLog('log', 'newBottomHeight:', needBottom, 'prevBottomHeight:', parseFloat(this.virtualBottomHeightElement.style.height));
|
|
3816
|
+
this.debugLog('warn', '=========== Dividing line ===========');
|
|
3101
3817
|
}
|
|
3102
3818
|
return {
|
|
3103
3819
|
isDiff: true,
|
|
@@ -3115,76 +3831,46 @@ class SlateEditable {
|
|
|
3115
3831
|
diffBottomRenderedIndexes: []
|
|
3116
3832
|
};
|
|
3117
3833
|
}
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
if (!node) {
|
|
3121
|
-
return defaultHeight;
|
|
3122
|
-
}
|
|
3123
|
-
const key = AngularEditor.findKey(this.editor, node);
|
|
3124
|
-
return this.measuredHeights.get(key.id) ?? defaultHeight;
|
|
3125
|
-
}
|
|
3126
|
-
buildAccumulatedHeight(heights) {
|
|
3127
|
-
const accumulatedHeights = new Array(heights.length + 1).fill(0);
|
|
3128
|
-
for (let i = 0; i < heights.length; i++) {
|
|
3129
|
-
// 存储前 i 个的累计高度
|
|
3130
|
-
accumulatedHeights[i + 1] = accumulatedHeights[i] + heights[i];
|
|
3131
|
-
}
|
|
3132
|
-
return accumulatedHeights;
|
|
3133
|
-
}
|
|
3134
|
-
getBufferBelowHeight(viewportHeight, visibleStart, bufferCount) {
|
|
3135
|
-
let blockHeight = 0;
|
|
3136
|
-
let start = visibleStart;
|
|
3137
|
-
// 循环累计高度超出视图高度代表找到向下缓冲区的起始位置
|
|
3138
|
-
while (blockHeight < viewportHeight) {
|
|
3139
|
-
blockHeight += this.getBlockHeight(start);
|
|
3140
|
-
start++;
|
|
3141
|
-
}
|
|
3142
|
-
let bufferHeight = 0;
|
|
3143
|
-
for (let i = start; i < start + bufferCount; i++) {
|
|
3144
|
-
bufferHeight += this.getBlockHeight(i);
|
|
3145
|
-
}
|
|
3146
|
-
return bufferHeight;
|
|
3147
|
-
}
|
|
3148
|
-
scheduleMeasureVisibleHeights() {
|
|
3149
|
-
if (!this.shouldUseVirtual()) {
|
|
3150
|
-
return;
|
|
3151
|
-
}
|
|
3152
|
-
if (this.measurePending) {
|
|
3834
|
+
tryMeasureInViewportChildrenHeights() {
|
|
3835
|
+
if (!this.isEnabledVirtualScroll()) {
|
|
3153
3836
|
return;
|
|
3154
3837
|
}
|
|
3155
|
-
this.
|
|
3156
|
-
this.
|
|
3157
|
-
this.measureVisibleHeightsAnimId = requestAnimationFrame(() => {
|
|
3838
|
+
this.tryMeasureInViewportChildrenHeightsAnimId && cancelAnimationFrame(this.tryMeasureInViewportChildrenHeightsAnimId);
|
|
3839
|
+
this.tryMeasureInViewportChildrenHeightsAnimId = requestAnimationFrame(() => {
|
|
3158
3840
|
this.measureVisibleHeights();
|
|
3159
|
-
this.measurePending = false;
|
|
3160
3841
|
});
|
|
3161
3842
|
}
|
|
3162
3843
|
measureVisibleHeights() {
|
|
3163
3844
|
const children = (this.editor.children || []);
|
|
3164
|
-
this.
|
|
3845
|
+
this.inViewportIndics.forEach(index => {
|
|
3165
3846
|
const node = children[index];
|
|
3166
3847
|
if (!node) {
|
|
3167
3848
|
return;
|
|
3168
3849
|
}
|
|
3169
3850
|
const key = AngularEditor.findKey(this.editor, node);
|
|
3170
|
-
//
|
|
3171
|
-
if (this.
|
|
3851
|
+
// 跳过已测过的块,除非强制测量
|
|
3852
|
+
if (this.keyHeightMap.has(key.id)) {
|
|
3172
3853
|
return;
|
|
3173
3854
|
}
|
|
3174
3855
|
const view = ELEMENT_TO_COMPONENT.get(node);
|
|
3175
3856
|
if (!view) {
|
|
3176
3857
|
return;
|
|
3177
3858
|
}
|
|
3178
|
-
view.getRealHeight()
|
|
3179
|
-
|
|
3180
|
-
|
|
3859
|
+
const ret = view.getRealHeight();
|
|
3860
|
+
if (ret instanceof Promise) {
|
|
3861
|
+
ret.then(height => {
|
|
3862
|
+
this.keyHeightMap.set(key.id, height);
|
|
3863
|
+
});
|
|
3864
|
+
}
|
|
3865
|
+
else {
|
|
3866
|
+
this.keyHeightMap.set(key.id, ret);
|
|
3867
|
+
}
|
|
3181
3868
|
});
|
|
3182
3869
|
}
|
|
3183
|
-
|
|
3870
|
+
remeasureHeightByIndics(indics) {
|
|
3184
3871
|
const children = (this.editor.children || []);
|
|
3185
3872
|
let isHeightChanged = false;
|
|
3186
|
-
|
|
3187
|
-
indexes.forEach(index => {
|
|
3873
|
+
indics.forEach((index, i) => {
|
|
3188
3874
|
const node = children[index];
|
|
3189
3875
|
if (!node) {
|
|
3190
3876
|
return;
|
|
@@ -3194,27 +3880,30 @@ class SlateEditable {
|
|
|
3194
3880
|
if (!view) {
|
|
3195
3881
|
return;
|
|
3196
3882
|
}
|
|
3197
|
-
const
|
|
3198
|
-
|
|
3199
|
-
|
|
3200
|
-
|
|
3201
|
-
|
|
3202
|
-
|
|
3203
|
-
|
|
3883
|
+
const prevHeight = this.keyHeightMap.get(key.id);
|
|
3884
|
+
const ret = view.getRealHeight();
|
|
3885
|
+
if (ret instanceof Promise) {
|
|
3886
|
+
ret.then(height => {
|
|
3887
|
+
this.keyHeightMap.set(key.id, height);
|
|
3888
|
+
if (height !== prevHeight) {
|
|
3889
|
+
isHeightChanged = true;
|
|
3890
|
+
if (isDebug) {
|
|
3891
|
+
this.debugLog('log', `remeasure element height, index: ${index} prevHeight: ${prevHeight} newHeight: ${height}`);
|
|
3892
|
+
}
|
|
3893
|
+
}
|
|
3894
|
+
});
|
|
3895
|
+
}
|
|
3896
|
+
else {
|
|
3897
|
+
this.keyHeightMap.set(key.id, ret);
|
|
3898
|
+
if (ret !== prevHeight) {
|
|
3204
3899
|
isHeightChanged = true;
|
|
3900
|
+
if (isDebug) {
|
|
3901
|
+
this.debugLog('log', `remeasure element height, index: ${index} prevHeight: ${prevHeight} newHeight: ${ret}`);
|
|
3902
|
+
}
|
|
3205
3903
|
}
|
|
3206
|
-
});
|
|
3207
|
-
if (promise) {
|
|
3208
|
-
promises.push(promise);
|
|
3209
3904
|
}
|
|
3210
3905
|
});
|
|
3211
|
-
|
|
3212
|
-
await Promise.all(promises);
|
|
3213
|
-
if (isHeightChanged && isRefresh) {
|
|
3214
|
-
return this.refreshVirtualView();
|
|
3215
|
-
}
|
|
3216
|
-
}
|
|
3217
|
-
return null;
|
|
3906
|
+
return isHeightChanged;
|
|
3218
3907
|
}
|
|
3219
3908
|
//#region event proxy
|
|
3220
3909
|
addEventListener(eventName, listener, target = this.elementRef.nativeElement) {
|
|
@@ -3740,6 +4429,11 @@ class SlateEditable {
|
|
|
3740
4429
|
Transforms.move(editor, { unit: 'word', reverse: isRTL });
|
|
3741
4430
|
return;
|
|
3742
4431
|
}
|
|
4432
|
+
if (isKeyHotkey('mod+a', event)) {
|
|
4433
|
+
this.editor.selectAll();
|
|
4434
|
+
event.preventDefault();
|
|
4435
|
+
return;
|
|
4436
|
+
}
|
|
3743
4437
|
// COMPAT: Certain browsers don't support the `beforeinput` event, so we
|
|
3744
4438
|
// fall back to guessing at the input intention for hotkeys.
|
|
3745
4439
|
// COMPAT: In iOS, some of these hotkeys are handled in the
|
|
@@ -3907,6 +4601,7 @@ class SlateEditable {
|
|
|
3907
4601
|
}
|
|
3908
4602
|
//#endregion
|
|
3909
4603
|
ngOnDestroy() {
|
|
4604
|
+
this.editorResizeObserver?.disconnect();
|
|
3910
4605
|
NODE_TO_ELEMENT.delete(this.editor);
|
|
3911
4606
|
this.manualListeners.forEach(manualListener => {
|
|
3912
4607
|
manualListener();
|
|
@@ -4166,6 +4861,7 @@ class BaseElementComponent extends BaseComponent {
|
|
|
4166
4861
|
}
|
|
4167
4862
|
return null;
|
|
4168
4863
|
};
|
|
4864
|
+
this.stableHeight = null;
|
|
4169
4865
|
}
|
|
4170
4866
|
get element() {
|
|
4171
4867
|
return this._context && this._context.element;
|
|
@@ -4219,6 +4915,7 @@ class BaseElementComponent extends BaseComponent {
|
|
|
4219
4915
|
if (ELEMENT_TO_COMPONENT.get(this.element) === this) {
|
|
4220
4916
|
ELEMENT_TO_COMPONENT.delete(this.element);
|
|
4221
4917
|
}
|
|
4918
|
+
this.listRender.destroy();
|
|
4222
4919
|
}
|
|
4223
4920
|
onContextChange() {
|
|
4224
4921
|
this.childrenContext = this.getChildrenContext();
|
|
@@ -4247,11 +4944,21 @@ class BaseElementComponent extends BaseComponent {
|
|
|
4247
4944
|
readonly: this._context.readonly
|
|
4248
4945
|
};
|
|
4249
4946
|
}
|
|
4947
|
+
isStableHeight() {
|
|
4948
|
+
return false;
|
|
4949
|
+
}
|
|
4250
4950
|
getRealHeight() {
|
|
4951
|
+
if (this.isStableHeight() && this.stableHeight !== null) {
|
|
4952
|
+
return this.stableHeight;
|
|
4953
|
+
}
|
|
4251
4954
|
const blockCard = getBlockCardByNativeElement(this.nativeElement);
|
|
4252
4955
|
const target = blockCard || this.nativeElement;
|
|
4253
4956
|
const computedStyle = getComputedStyle(target);
|
|
4254
|
-
|
|
4957
|
+
const height = Math.ceil(target.getBoundingClientRect().height) + parseFloat(computedStyle.marginTop) + parseFloat(computedStyle.marginBottom);
|
|
4958
|
+
if (this.isStableHeight()) {
|
|
4959
|
+
this.stableHeight = height;
|
|
4960
|
+
}
|
|
4961
|
+
return height;
|
|
4255
4962
|
}
|
|
4256
4963
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: BaseElementComponent, deps: null, target: i0.ɵɵFactoryTarget.Directive }); }
|
|
4257
4964
|
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.12", type: BaseElementComponent, isStandalone: true, viewQueries: [{ propertyName: "childrenOutletInstance", first: true, predicate: SlateChildrenOutlet, descendants: true, static: true }], usesInheritance: true, ngImport: i0 }); }
|
|
@@ -4296,6 +5003,7 @@ class BaseTextComponent extends BaseComponent {
|
|
|
4296
5003
|
NODE_TO_ELEMENT.delete(this.text);
|
|
4297
5004
|
}
|
|
4298
5005
|
ELEMENT_TO_NODE.delete(this.nativeElement);
|
|
5006
|
+
this.leavesRender.destroy();
|
|
4299
5007
|
}
|
|
4300
5008
|
onContextChange() {
|
|
4301
5009
|
this.updateWeakMap();
|
|
@@ -4407,5 +5115,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImpo
|
|
|
4407
5115
|
* Generated bundle index. Do not edit.
|
|
4408
5116
|
*/
|
|
4409
5117
|
|
|
4410
|
-
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,
|
|
5118
|
+
export { AngularEditor, BaseComponent, BaseElementComponent, BaseElementFlavour, BaseFlavour, BaseLeafComponent, BaseLeafFlavour, BaseTextComponent, BaseTextFlavour, BlockCardRef, DEFAULT_ELEMENT_HEIGHT, DefaultTextFlavour, EDITOR_TO_AFTER_VIEW_INIT_QUEUE, EDITOR_TO_BUSINESS_TOP, EDITOR_TO_VIRTUAL_SCROLL_SELECTION, ELEMENT_KEY_TO_HEIGHTS, ELEMENT_TO_COMPONENT, FAKE_LEFT_BLOCK_CARD_OFFSET, FAKE_RIGHT_BLOCK_CARD_OFFSET, FlavourRef, HAS_BEFORE_INPUT_SUPPORT, IS_ANDROID, IS_APPLE, IS_CHROME, IS_CHROME_LEGACY, IS_EDGE_LEGACY, IS_ENABLED_VIRTUAL_SCROLL, IS_FIREFOX, IS_FIREFOX_LEGACY, IS_IOS, IS_QQBROWSER, IS_SAFARI, IS_UC_MOBILE, IS_WECHATBROWSER, PLACEHOLDER_SYMBOL, SLATE_BLOCK_CARD_CLASS_NAME, SLATE_DEBUG_KEY, SLATE_DEBUG_KEY_SCROLL_TOP, SlateBlockCard, SlateChildrenOutlet, SlateEditable, SlateErrorCode, SlateFragmentAttributeKey, SlateModule, VIRTUAL_SCROLL_DEFAULT_BLOCK_HEIGHT, VoidTextFlavour, blobAsString, buildHTMLText, buildHeightsAndAccumulatedHeights, check, completeTable, createClipboardData, createText, createThrottleRAF, defaultScrollSelectionIntoView, fallbackCopyText, getBlockCardByNativeElement, getBusinessTop, getCardTargetAttribute, getClipboardData, getClipboardFromHTMLText, getContentHeight, getDataTransferClipboard, getDataTransferClipboardText, getNavigatorClipboard, getPlainText, getRealHeightByElement, getSelection, getSlateFragmentAttribute, getZeroTextNode, hasAfterContextChange, hasBeforeContextChange, hasBlockCard, hasBlockCardWithNode, hotkeys, isCardCenterByTargetAttr, isCardLeft, isCardLeftByTargetAttr, isCardRightByTargetAttr, isClipboardFile, isClipboardReadSupported, isClipboardWriteSupported, isClipboardWriteTextSupported, isComponentType, isDOMText, isDecoratorRangeListEqual, isFlavourType, isInvalidTable, isTemplateRef, isValid, normalize, scrollToElement, setClipboardData, setDataTransferClipboard, setDataTransferClipboardText, setNavigatorClipboard, shallowCompare, stripHtml, withAngular };
|
|
4411
5119
|
//# sourceMappingURL=slate-angular.mjs.map
|