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