slate-angular 20.2.0-next.11 → 20.2.0-next.13

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.
@@ -1138,6 +1138,9 @@ const withAngular = (editor, clipboardFormatKey = 'x-slate-fragment') => {
1138
1138
  e.selectAll = () => {
1139
1139
  Transforms.select(e, []);
1140
1140
  };
1141
+ e.isVisible = element => {
1142
+ return true;
1143
+ };
1141
1144
  return e;
1142
1145
  };
1143
1146
 
@@ -2568,17 +2571,51 @@ function executeAfterViewInit(editor) {
2568
2571
  }
2569
2572
 
2570
2573
  class VirtualScrollDebugOverlay {
2574
+ static { this.storageKey = 'slate_virtual_scroll_debug_overlay_state'; }
2575
+ static { this.minWidth = 320; }
2576
+ static { this.minHeight = 240; }
2577
+ static { this.defaultWidth = 410; }
2578
+ static { this.defaultHeight = 480; }
2579
+ static getInstance(doc) {
2580
+ if (!this.instance) {
2581
+ this.instance = new VirtualScrollDebugOverlay(doc);
2582
+ }
2583
+ this.instance.init();
2584
+ return this.instance;
2585
+ }
2586
+ static log(doc, type, ...args) {
2587
+ this.getInstance(doc).log(type, ...args);
2588
+ }
2589
+ static syncScrollTop(doc, value) {
2590
+ const instance = this.getInstance(doc);
2591
+ instance.setScrollTopValue(value);
2592
+ }
2571
2593
  constructor(doc) {
2572
2594
  this.doc = doc;
2595
+ this.state = {
2596
+ left: 16,
2597
+ top: 16,
2598
+ collapsed: false,
2599
+ width: VirtualScrollDebugOverlay.defaultWidth,
2600
+ height: VirtualScrollDebugOverlay.defaultHeight
2601
+ };
2573
2602
  this.originalConsoleLog = console.log.bind(console);
2574
2603
  this.originalConsoleWarn = console.warn.bind(console);
2575
2604
  this.dragOffsetX = 0;
2576
2605
  this.dragOffsetY = 0;
2577
2606
  this.isDragging = false;
2607
+ this.isResizing = false;
2608
+ this.resizeStartX = 0;
2609
+ this.resizeStartY = 0;
2610
+ this.resizeStartWidth = 0;
2611
+ this.resizeStartHeight = 0;
2612
+ this.dragMoved = false;
2613
+ this.wasDragged = false;
2578
2614
  this.onDragging = (event) => {
2579
2615
  if (!this.isDragging || !this.container) {
2580
2616
  return;
2581
2617
  }
2618
+ this.dragMoved = true;
2582
2619
  const nextLeft = event.clientX - this.dragOffsetX;
2583
2620
  const nextTop = event.clientY - this.dragOffsetY;
2584
2621
  this.container.style.left = `${nextLeft}px`;
@@ -2591,17 +2628,47 @@ class VirtualScrollDebugOverlay {
2591
2628
  return;
2592
2629
  }
2593
2630
  this.isDragging = false;
2631
+ this.wasDragged = this.dragMoved;
2632
+ this.dragMoved = false;
2594
2633
  this.doc.removeEventListener('mousemove', this.onDragging);
2595
2634
  this.doc.removeEventListener('mouseup', this.onDragEnd);
2596
2635
  if (this.container) {
2636
+ const rect = this.container.getBoundingClientRect();
2637
+ this.state.left = rect.left;
2638
+ this.state.top = rect.top;
2639
+ this.persistState();
2597
2640
  this.container.style.transition = '';
2598
2641
  }
2599
2642
  };
2643
+ this.onResizing = (event) => {
2644
+ if (!this.isResizing || !this.container) {
2645
+ return;
2646
+ }
2647
+ const deltaX = event.clientX - this.resizeStartX;
2648
+ const deltaY = event.clientY - this.resizeStartY;
2649
+ const nextWidth = Math.max(VirtualScrollDebugOverlay.minWidth, this.resizeStartWidth + deltaX);
2650
+ const nextHeight = Math.max(VirtualScrollDebugOverlay.minHeight, this.resizeStartHeight + deltaY);
2651
+ this.state.width = nextWidth;
2652
+ this.state.height = nextHeight;
2653
+ this.applySize();
2654
+ };
2655
+ this.onResizeEnd = () => {
2656
+ if (!this.isResizing) {
2657
+ return;
2658
+ }
2659
+ this.isResizing = false;
2660
+ this.doc.removeEventListener('mousemove', this.onResizing);
2661
+ this.doc.removeEventListener('mouseup', this.onResizeEnd);
2662
+ this.persistState();
2663
+ };
2600
2664
  }
2601
2665
  init() {
2602
2666
  if (!this.container) {
2603
2667
  this.createContainer();
2604
2668
  }
2669
+ else {
2670
+ this.applyState();
2671
+ }
2605
2672
  }
2606
2673
  log(type, ...args) {
2607
2674
  this.init();
@@ -2616,58 +2683,75 @@ class VirtualScrollDebugOverlay {
2616
2683
  dispose() {
2617
2684
  this.container?.remove();
2618
2685
  this.container = undefined;
2686
+ this.contentWrapper = undefined;
2687
+ this.bubble = undefined;
2619
2688
  this.logList = undefined;
2620
2689
  this.selectorInput = undefined;
2621
2690
  this.distanceInput = undefined;
2691
+ this.collapseToggle = undefined;
2692
+ this.resizeHandle = undefined;
2622
2693
  this.doc.removeEventListener('mousemove', this.onDragging);
2623
2694
  this.doc.removeEventListener('mouseup', this.onDragEnd);
2695
+ this.doc.removeEventListener('mousemove', this.onResizing);
2696
+ this.doc.removeEventListener('mouseup', this.onResizeEnd);
2624
2697
  this.isDragging = false;
2698
+ this.isResizing = false;
2625
2699
  }
2626
2700
  createContainer() {
2701
+ this.loadState();
2627
2702
  const doc = this.doc;
2628
2703
  const container = doc.createElement('div');
2629
2704
  container.style.position = 'fixed';
2630
- container.style.left = '16px';
2631
- container.style.top = '16px';
2632
2705
  container.style.right = 'auto';
2633
2706
  container.style.bottom = 'auto';
2634
- container.style.width = '360px';
2635
- container.style.maxHeight = '70vh';
2636
- container.style.padding = '12px';
2637
2707
  container.style.boxSizing = 'border-box';
2638
2708
  container.style.background = 'rgba(17, 24, 39, 0.95)';
2639
2709
  container.style.color = '#e5e7eb';
2640
2710
  container.style.fontSize = '12px';
2641
- container.style.fontFamily = 'Menlo, Consolas, monospace';
2642
2711
  container.style.border = '1px solid #1f2937';
2643
2712
  container.style.borderRadius = '10px';
2713
+ container.style.fontFamily = 'Menlo, Consolas, monospace';
2644
2714
  container.style.boxShadow = '0 10px 30px rgba(0, 0, 0, 0.35)';
2645
2715
  container.style.zIndex = '9999';
2646
2716
  container.style.display = 'flex';
2647
2717
  container.style.flexDirection = 'column';
2648
2718
  container.style.gap = '10px';
2719
+ container.style.minWidth = `${VirtualScrollDebugOverlay.minWidth}px`;
2720
+ container.style.minHeight = `${VirtualScrollDebugOverlay.minHeight}px`;
2721
+ container.style.maxHeight = '80vh';
2722
+ container.style.cursor = 'default';
2723
+ container.addEventListener('mousedown', event => {
2724
+ if (this.state.collapsed) {
2725
+ this.startDrag(event);
2726
+ }
2727
+ });
2649
2728
  const header = doc.createElement('div');
2650
2729
  header.style.display = 'flex';
2651
2730
  header.style.alignItems = 'center';
2652
2731
  header.style.justifyContent = 'space-between';
2653
2732
  header.style.cursor = 'move';
2654
2733
  header.addEventListener('mousedown', event => {
2655
- if (!this.container) {
2656
- return;
2657
- }
2658
- const rect = this.container.getBoundingClientRect();
2659
- this.isDragging = true;
2660
- this.dragOffsetX = event.clientX - rect.left;
2661
- this.dragOffsetY = event.clientY - rect.top;
2662
- this.container.style.transition = 'none';
2663
- this.doc.addEventListener('mousemove', this.onDragging);
2664
- this.doc.addEventListener('mouseup', this.onDragEnd);
2665
- event.preventDefault();
2734
+ this.startDrag(event);
2666
2735
  });
2667
2736
  const title = doc.createElement('div');
2668
2737
  title.textContent = 'Virtual Scroll Debug';
2669
2738
  title.style.fontWeight = '600';
2670
2739
  title.style.letterSpacing = '0.3px';
2740
+ const actions = doc.createElement('div');
2741
+ actions.style.display = 'flex';
2742
+ actions.style.gap = '6px';
2743
+ const collapseButton = doc.createElement('button');
2744
+ collapseButton.type = 'button';
2745
+ collapseButton.textContent = '折叠';
2746
+ collapseButton.style.background = '#1f2937';
2747
+ collapseButton.style.color = '#e5e7eb';
2748
+ collapseButton.style.border = '1px solid #374151';
2749
+ collapseButton.style.borderRadius = '6px';
2750
+ collapseButton.style.padding = '4px 8px';
2751
+ collapseButton.style.cursor = 'pointer';
2752
+ collapseButton.addEventListener('click', () => {
2753
+ this.setCollapsed(!this.state.collapsed);
2754
+ });
2671
2755
  const clearButton = doc.createElement('button');
2672
2756
  clearButton.type = 'button';
2673
2757
  clearButton.textContent = '清除日志';
@@ -2682,11 +2766,13 @@ class VirtualScrollDebugOverlay {
2682
2766
  this.logList.innerHTML = '';
2683
2767
  }
2684
2768
  });
2769
+ actions.appendChild(collapseButton);
2770
+ actions.appendChild(clearButton);
2685
2771
  header.appendChild(title);
2686
- header.appendChild(clearButton);
2772
+ header.appendChild(actions);
2687
2773
  const scrollForm = doc.createElement('div');
2688
2774
  scrollForm.style.display = 'grid';
2689
- scrollForm.style.gridTemplateColumns = '1fr 90px 70px';
2775
+ scrollForm.style.gridTemplateColumns = '1fr 110px 50px';
2690
2776
  scrollForm.style.gap = '6px';
2691
2777
  scrollForm.style.alignItems = 'center';
2692
2778
  const selectorInput = doc.createElement('input');
@@ -2752,14 +2838,194 @@ class VirtualScrollDebugOverlay {
2752
2838
  logList.style.display = 'flex';
2753
2839
  logList.style.flexDirection = 'column';
2754
2840
  logList.style.gap = '6px';
2755
- container.appendChild(header);
2756
- container.appendChild(scrollForm);
2757
- container.appendChild(logList);
2841
+ logList.style.flex = '1';
2842
+ logList.style.minHeight = '160px';
2843
+ const bubble = doc.createElement('div');
2844
+ bubble.textContent = 'VS';
2845
+ bubble.style.display = 'none';
2846
+ bubble.style.alignItems = 'center';
2847
+ bubble.style.justifyContent = 'center';
2848
+ bubble.style.fontWeight = '700';
2849
+ bubble.style.fontSize = '14px';
2850
+ bubble.style.letterSpacing = '0.5px';
2851
+ bubble.style.width = '100%';
2852
+ bubble.style.height = '100%';
2853
+ bubble.style.userSelect = 'none';
2854
+ bubble.addEventListener('click', () => {
2855
+ if (this.wasDragged) {
2856
+ this.wasDragged = false;
2857
+ return;
2858
+ }
2859
+ this.setCollapsed(false);
2860
+ });
2861
+ const contentWrapper = doc.createElement('div');
2862
+ contentWrapper.style.display = 'flex';
2863
+ contentWrapper.style.flexDirection = 'column';
2864
+ contentWrapper.style.gap = '10px';
2865
+ contentWrapper.style.width = '100%';
2866
+ contentWrapper.style.height = '100%';
2867
+ contentWrapper.appendChild(header);
2868
+ contentWrapper.appendChild(scrollForm);
2869
+ contentWrapper.appendChild(logList);
2870
+ container.appendChild(contentWrapper);
2871
+ container.appendChild(bubble);
2872
+ const resizeHandle = doc.createElement('div');
2873
+ resizeHandle.style.position = 'absolute';
2874
+ resizeHandle.style.right = '6px';
2875
+ resizeHandle.style.bottom = '6px';
2876
+ resizeHandle.style.width = '14px';
2877
+ resizeHandle.style.height = '14px';
2878
+ resizeHandle.style.cursor = 'nwse-resize';
2879
+ resizeHandle.style.borderRight = '2px solid #4b5563';
2880
+ resizeHandle.style.borderBottom = '2px solid #4b5563';
2881
+ resizeHandle.addEventListener('mousedown', event => {
2882
+ this.startResize(event);
2883
+ });
2884
+ container.appendChild(resizeHandle);
2758
2885
  doc.body.appendChild(container);
2759
2886
  this.container = container;
2887
+ this.contentWrapper = contentWrapper;
2888
+ this.bubble = bubble;
2760
2889
  this.logList = logList;
2761
2890
  this.selectorInput = selectorInput;
2762
2891
  this.distanceInput = distanceInput;
2892
+ this.collapseToggle = collapseButton;
2893
+ this.resizeHandle = resizeHandle;
2894
+ this.applyState();
2895
+ }
2896
+ startDrag(event) {
2897
+ if (event.button !== 0) {
2898
+ return;
2899
+ }
2900
+ if (!this.container) {
2901
+ return;
2902
+ }
2903
+ const rect = this.container.getBoundingClientRect();
2904
+ this.isDragging = true;
2905
+ this.wasDragged = false;
2906
+ this.dragMoved = false;
2907
+ this.dragOffsetX = event.clientX - rect.left;
2908
+ this.dragOffsetY = event.clientY - rect.top;
2909
+ this.container.style.transition = 'none';
2910
+ this.doc.addEventListener('mousemove', this.onDragging);
2911
+ this.doc.addEventListener('mouseup', this.onDragEnd);
2912
+ if (!this.state.collapsed) {
2913
+ event.preventDefault();
2914
+ }
2915
+ }
2916
+ startResize(event) {
2917
+ if (event.button !== 0 || this.state.collapsed) {
2918
+ return;
2919
+ }
2920
+ if (!this.container) {
2921
+ return;
2922
+ }
2923
+ const rect = this.container.getBoundingClientRect();
2924
+ this.isResizing = true;
2925
+ this.resizeStartX = event.clientX;
2926
+ this.resizeStartY = event.clientY;
2927
+ this.resizeStartWidth = rect.width;
2928
+ this.resizeStartHeight = rect.height;
2929
+ this.doc.addEventListener('mousemove', this.onResizing);
2930
+ this.doc.addEventListener('mouseup', this.onResizeEnd);
2931
+ event.preventDefault();
2932
+ event.stopPropagation();
2933
+ }
2934
+ applyPosition() {
2935
+ if (!this.container) {
2936
+ return;
2937
+ }
2938
+ this.container.style.left = `${this.state.left}px`;
2939
+ this.container.style.top = `${this.state.top}px`;
2940
+ }
2941
+ applySize() {
2942
+ if (!this.container) {
2943
+ return;
2944
+ }
2945
+ const width = Math.max(VirtualScrollDebugOverlay.minWidth, this.state.width || VirtualScrollDebugOverlay.defaultWidth);
2946
+ const height = Math.max(VirtualScrollDebugOverlay.minHeight, this.state.height || VirtualScrollDebugOverlay.defaultHeight);
2947
+ this.state.width = width;
2948
+ this.state.height = height;
2949
+ this.container.style.width = `${width}px`;
2950
+ this.container.style.height = `${height}px`;
2951
+ }
2952
+ applyCollapsedState() {
2953
+ if (!this.container || !this.contentWrapper || !this.bubble || !this.collapseToggle) {
2954
+ return;
2955
+ }
2956
+ if (this.state.collapsed) {
2957
+ this.container.style.width = '36px';
2958
+ this.container.style.height = '36px';
2959
+ this.container.style.minWidth = '';
2960
+ this.container.style.minHeight = '';
2961
+ this.container.style.padding = '0';
2962
+ this.container.style.borderRadius = '50%';
2963
+ this.container.style.display = 'flex';
2964
+ this.container.style.flexDirection = 'row';
2965
+ this.container.style.alignItems = 'center';
2966
+ this.container.style.justifyContent = 'center';
2967
+ this.container.style.cursor = 'move';
2968
+ this.contentWrapper.style.display = 'none';
2969
+ this.bubble.style.display = 'flex';
2970
+ this.collapseToggle.textContent = '展开';
2971
+ this.resizeHandle.style.display = 'none';
2972
+ }
2973
+ else {
2974
+ this.applySize();
2975
+ this.container.style.padding = '12px';
2976
+ this.container.style.borderRadius = '10px';
2977
+ this.container.style.display = 'flex';
2978
+ this.container.style.flexDirection = 'column';
2979
+ this.container.style.gap = '10px';
2980
+ this.container.style.cursor = 'default';
2981
+ this.contentWrapper.style.display = 'flex';
2982
+ this.bubble.style.display = 'none';
2983
+ this.collapseToggle.textContent = '折叠';
2984
+ this.resizeHandle.style.display = 'block';
2985
+ }
2986
+ }
2987
+ setCollapsed(collapsed) {
2988
+ this.state.collapsed = collapsed;
2989
+ this.applyCollapsedState();
2990
+ this.persistState();
2991
+ }
2992
+ applyState() {
2993
+ this.applyPosition();
2994
+ this.applyCollapsedState();
2995
+ }
2996
+ loadState() {
2997
+ try {
2998
+ const raw = this.doc.defaultView?.localStorage?.getItem(VirtualScrollDebugOverlay.storageKey);
2999
+ if (raw) {
3000
+ const parsed = JSON.parse(raw);
3001
+ if (typeof parsed.left === 'number') {
3002
+ this.state.left = parsed.left;
3003
+ }
3004
+ if (typeof parsed.top === 'number') {
3005
+ this.state.top = parsed.top;
3006
+ }
3007
+ if (typeof parsed.collapsed === 'boolean') {
3008
+ this.state.collapsed = parsed.collapsed;
3009
+ }
3010
+ if (typeof parsed.width === 'number') {
3011
+ this.state.width = parsed.width;
3012
+ }
3013
+ if (typeof parsed.height === 'number') {
3014
+ this.state.height = parsed.height;
3015
+ }
3016
+ }
3017
+ }
3018
+ catch {
3019
+ // ignore storage errors
3020
+ }
3021
+ }
3022
+ persistState() {
3023
+ try {
3024
+ this.doc.defaultView?.localStorage?.setItem(VirtualScrollDebugOverlay.storageKey, JSON.stringify(this.state));
3025
+ }
3026
+ catch {
3027
+ // ignore storage errors
3028
+ }
2763
3029
  }
2764
3030
  appendLog(type, ...args) {
2765
3031
  if (!this.logList) {
@@ -2794,6 +3060,11 @@ class VirtualScrollDebugOverlay {
2794
3060
  return String(value);
2795
3061
  }
2796
3062
  }
3063
+ setScrollTopValue(value) {
3064
+ if (this.distanceInput) {
3065
+ this.distanceInput.value = String(value ?? 0);
3066
+ }
3067
+ }
2797
3068
  }
2798
3069
 
2799
3070
  const JUST_NOW_UPDATED_VIRTUAL_VIEW = new WeakMap();
@@ -2803,8 +3074,8 @@ const forceOnDOMPaste = IS_SAFARI;
2803
3074
  const isDebug = localStorage.getItem(SLATE_DEBUG_KEY) === 'true';
2804
3075
  class SlateEditable {
2805
3076
  set virtualScroll(config) {
2806
- this.virtualConfig = config;
2807
- this.doVirtualScroll();
3077
+ this.virtualScrollConfig = config;
3078
+ this.tryUpdateVirtualViewport();
2808
3079
  }
2809
3080
  get hasBeforeInputSupport() {
2810
3081
  return HAS_BEFORE_INPUT_SUPPORT;
@@ -2849,14 +3120,14 @@ class SlateEditable {
2849
3120
  return null;
2850
3121
  }
2851
3122
  };
2852
- this.virtualConfig = {
3123
+ this.virtualScrollConfig = {
2853
3124
  enabled: false,
2854
3125
  scrollTop: 0,
2855
3126
  viewportHeight: 0
2856
3127
  };
2857
- this.renderedChildren = [];
2858
- this.virtualVisibleIndexes = new Set();
2859
- this.measuredHeights = new Map();
3128
+ this.inViewportChildren = [];
3129
+ this.inViewportIndics = new Set();
3130
+ this.keyHeightMap = new Map();
2860
3131
  // the height from scroll container top to editor top height element
2861
3132
  this.businessHeight = 0;
2862
3133
  this.virtualScrollInitialized = false;
@@ -2870,7 +3141,7 @@ class SlateEditable {
2870
3141
  NODE_TO_ELEMENT.set(this.editor, this.elementRef.nativeElement);
2871
3142
  ELEMENT_TO_NODE.set(this.elementRef.nativeElement, this.editor);
2872
3143
  IS_READ_ONLY.set(this.editor, this.readonly);
2873
- ELEMENT_KEY_TO_HEIGHTS.set(this.editor, this.measuredHeights);
3144
+ ELEMENT_KEY_TO_HEIGHTS.set(this.editor, this.keyHeightMap);
2874
3145
  EDITOR_TO_ON_CHANGE.set(this.editor, () => {
2875
3146
  this.ngZone.run(() => {
2876
3147
  this.onChange();
@@ -2884,7 +3155,7 @@ class SlateEditable {
2884
3155
  // add browser class
2885
3156
  let browserClass = IS_FIREFOX ? 'firefox' : IS_SAFARI ? 'safari' : '';
2886
3157
  browserClass && this.elementRef.nativeElement.classList.add(browserClass);
2887
- this.initializeVirtualScrolling();
3158
+ this.initializeVirtualScroll();
2888
3159
  this.listRender = new ListRender(this.viewContext, this.viewContainerRef, this.getOutletParent, this.getOutletElement);
2889
3160
  }
2890
3161
  ngOnChanges(simpleChanges) {
@@ -2916,9 +3187,9 @@ class SlateEditable {
2916
3187
  if (value && value.length) {
2917
3188
  this.editor.children = value;
2918
3189
  this.initializeContext();
2919
- const virtualView = this.refreshVirtualView();
3190
+ const virtualView = this.calculateVirtualViewport();
2920
3191
  this.applyVirtualView(virtualView);
2921
- const childrenForRender = virtualView.renderedChildren;
3192
+ const childrenForRender = virtualView.inViewportChildren;
2922
3193
  if (!this.listRender.initialized) {
2923
3194
  this.listRender.initialize(childrenForRender, this.editor, this.context);
2924
3195
  }
@@ -2959,8 +3230,8 @@ class SlateEditable {
2959
3230
  toNativeSelection() {
2960
3231
  try {
2961
3232
  let { selection } = this.editor;
2962
- if (this.virtualConfig?.enabled && selection) {
2963
- const indics = Array.from(this.virtualVisibleIndexes.values());
3233
+ if (this.virtualScrollConfig?.enabled && selection) {
3234
+ const indics = Array.from(this.inViewportIndics.values());
2964
3235
  if (indics.length > 0) {
2965
3236
  const currentVisibleRange = {
2966
3237
  anchor: Editor.start(this.editor, [indics[0]]),
@@ -3064,10 +3335,10 @@ class SlateEditable {
3064
3335
  ngDoCheck() { }
3065
3336
  forceRender() {
3066
3337
  this.updateContext();
3067
- const virtualView = this.refreshVirtualView();
3338
+ const virtualView = this.calculateVirtualViewport();
3068
3339
  this.applyVirtualView(virtualView);
3069
- const visibleIndexes = Array.from(this.virtualVisibleIndexes);
3070
- this.listRender.update(this.renderedChildren, this.editor, this.context);
3340
+ const visibleIndexes = Array.from(this.inViewportIndics);
3341
+ this.listRender.update(this.inViewportChildren, this.editor, this.context);
3071
3342
  this.remeasureHeightByIndics(visibleIndexes);
3072
3343
  // repair collaborative editing when Chinese input is interrupted by other users' cursors
3073
3344
  // when the DOMElement where the selection is located is removed
@@ -3107,9 +3378,9 @@ class SlateEditable {
3107
3378
  render() {
3108
3379
  const changed = this.updateContext();
3109
3380
  if (changed) {
3110
- const virtualView = this.refreshVirtualView();
3381
+ const virtualView = this.calculateVirtualViewport();
3111
3382
  this.applyVirtualView(virtualView);
3112
- this.listRender.update(virtualView.renderedChildren, this.editor, this.context);
3383
+ this.listRender.update(virtualView.inViewportChildren, this.editor, this.context);
3113
3384
  this.scheduleMeasureVisibleHeights();
3114
3385
  }
3115
3386
  }
@@ -3173,14 +3444,14 @@ class SlateEditable {
3173
3444
  decorations.push(...placeholderDecorations);
3174
3445
  return decorations;
3175
3446
  }
3176
- shouldUseVirtual() {
3177
- return !!(this.virtualConfig && this.virtualConfig.enabled);
3447
+ isEnabledVirtualScroll() {
3448
+ return !!(this.virtualScrollConfig && this.virtualScrollConfig.enabled);
3178
3449
  }
3179
- initializeVirtualScrolling() {
3450
+ initializeVirtualScroll() {
3180
3451
  if (this.virtualScrollInitialized) {
3181
3452
  return;
3182
3453
  }
3183
- if (this.virtualConfig && this.virtualConfig.enabled) {
3454
+ if (this.virtualScrollConfig && this.virtualScrollConfig.enabled) {
3184
3455
  this.virtualScrollInitialized = true;
3185
3456
  this.virtualTopHeightElement = document.createElement('div');
3186
3457
  this.virtualTopHeightElement.classList.add('virtual-top-height');
@@ -3198,18 +3469,17 @@ class SlateEditable {
3198
3469
  this.editorResizeObserver = new ResizeObserver(entries => {
3199
3470
  if (entries.length > 0 && entries[0].contentRect.width !== editorResizeObserverRectWidth) {
3200
3471
  editorResizeObserverRectWidth = entries[0].contentRect.width;
3201
- this.remeasureHeightByIndics(Array.from(this.virtualVisibleIndexes));
3472
+ this.remeasureHeightByIndics(Array.from(this.inViewportIndics));
3202
3473
  }
3203
3474
  });
3204
3475
  this.editorResizeObserver.observe(this.elementRef.nativeElement);
3205
3476
  if (isDebug) {
3206
3477
  const doc = this.elementRef?.nativeElement?.ownerDocument ?? document;
3207
- this.debugOverlay = new VirtualScrollDebugOverlay(doc);
3208
- this.debugOverlay.init();
3478
+ VirtualScrollDebugOverlay.getInstance(doc);
3209
3479
  }
3210
3480
  }
3211
3481
  }
3212
- changeVirtualHeight(topHeight, bottomHeight) {
3482
+ setVirtualSpaceHeight(topHeight, bottomHeight) {
3213
3483
  if (!this.virtualScrollInitialized) {
3214
3484
  return;
3215
3485
  }
@@ -3217,25 +3487,22 @@ class SlateEditable {
3217
3487
  this.virtualBottomHeightElement.style.height = `${bottomHeight}px`;
3218
3488
  }
3219
3489
  debugLog(type, ...args) {
3220
- if (!this.debugOverlay) {
3221
- const doc = this.elementRef?.nativeElement?.ownerDocument ?? document;
3222
- this.debugOverlay = new VirtualScrollDebugOverlay(doc);
3223
- }
3224
- this.debugOverlay.log(type, ...args);
3490
+ const doc = this.elementRef?.nativeElement?.ownerDocument ?? document;
3491
+ VirtualScrollDebugOverlay.log(doc, type, ...args);
3225
3492
  }
3226
- doVirtualScroll() {
3493
+ tryUpdateVirtualViewport() {
3227
3494
  this.refreshVirtualViewAnimId && cancelAnimationFrame(this.refreshVirtualViewAnimId);
3228
3495
  this.refreshVirtualViewAnimId = requestAnimationFrame(() => {
3229
- let virtualView = this.refreshVirtualView();
3230
- let diff = this.diffVirtualView(virtualView);
3496
+ let virtualView = this.calculateVirtualViewport();
3497
+ let diff = this.diffVirtualViewport(virtualView);
3231
3498
  if (!diff.isDiff) {
3232
3499
  return;
3233
3500
  }
3234
3501
  if (diff.isMissingTop) {
3235
3502
  const result = this.remeasureHeightByIndics(diff.diffTopRenderedIndexes);
3236
3503
  if (result) {
3237
- virtualView = this.refreshVirtualView();
3238
- diff = this.diffVirtualView(virtualView, 'second');
3504
+ virtualView = this.calculateVirtualViewport();
3505
+ diff = this.diffVirtualViewport(virtualView, 'second');
3239
3506
  if (!diff.isDiff) {
3240
3507
  return;
3241
3508
  }
@@ -3243,7 +3510,7 @@ class SlateEditable {
3243
3510
  }
3244
3511
  this.applyVirtualView(virtualView);
3245
3512
  if (this.listRender.initialized) {
3246
- this.listRender.update(virtualView.renderedChildren, this.editor, this.context);
3513
+ this.listRender.update(virtualView.inViewportChildren, this.editor, this.context);
3247
3514
  if (!AngularEditor.isReadOnly(this.editor) && this.editor.selection) {
3248
3515
  this.toNativeSelection();
3249
3516
  }
@@ -3251,22 +3518,26 @@ class SlateEditable {
3251
3518
  this.scheduleMeasureVisibleHeights();
3252
3519
  });
3253
3520
  }
3254
- refreshVirtualView() {
3521
+ calculateVirtualViewport() {
3255
3522
  const children = (this.editor.children || []);
3256
- if (!children.length || !this.shouldUseVirtual()) {
3523
+ if (!children.length || !this.isEnabledVirtualScroll()) {
3257
3524
  return {
3258
- renderedChildren: children,
3525
+ inViewportChildren: children,
3259
3526
  visibleIndexes: new Set(),
3260
3527
  top: 0,
3261
3528
  bottom: 0,
3262
3529
  heights: []
3263
3530
  };
3264
3531
  }
3265
- const scrollTop = this.virtualConfig.scrollTop;
3266
- const viewportHeight = this.virtualConfig.viewportHeight ?? 0;
3532
+ const scrollTop = this.virtualScrollConfig.scrollTop;
3533
+ if (isDebug) {
3534
+ const doc = this.elementRef?.nativeElement?.ownerDocument ?? document;
3535
+ VirtualScrollDebugOverlay.syncScrollTop(doc, Number.isFinite(scrollTop) ? scrollTop : 0);
3536
+ }
3537
+ const viewportHeight = this.virtualScrollConfig.viewportHeight ?? 0;
3267
3538
  if (!viewportHeight) {
3268
3539
  return {
3269
- renderedChildren: [],
3540
+ inViewportChildren: [],
3270
3541
  visibleIndexes: new Set(),
3271
3542
  top: 0,
3272
3543
  bottom: 0,
@@ -3306,7 +3577,7 @@ class SlateEditable {
3306
3577
  const top = visibleStartIndex === -1 ? 0 : accumulatedHeights[visibleStartIndex];
3307
3578
  const bottom = totalHeight - accumulatedHeights[visibleEndIndex + 1];
3308
3579
  return {
3309
- renderedChildren: visible.length ? visible : children,
3580
+ inViewportChildren: visible.length ? visible : children,
3310
3581
  visibleIndexes: new Set(visibleIndexes),
3311
3582
  top,
3312
3583
  bottom,
@@ -3314,19 +3585,19 @@ class SlateEditable {
3314
3585
  };
3315
3586
  }
3316
3587
  applyVirtualView(virtualView) {
3317
- this.renderedChildren = virtualView.renderedChildren;
3318
- this.changeVirtualHeight(virtualView.top, virtualView.bottom);
3319
- this.virtualVisibleIndexes = virtualView.visibleIndexes;
3588
+ this.inViewportChildren = virtualView.inViewportChildren;
3589
+ this.setVirtualSpaceHeight(virtualView.top, virtualView.bottom);
3590
+ this.inViewportIndics = virtualView.visibleIndexes;
3320
3591
  }
3321
- diffVirtualView(virtualView, stage = 'first') {
3322
- if (!this.renderedChildren.length) {
3592
+ diffVirtualViewport(virtualView, stage = 'first') {
3593
+ if (!this.inViewportChildren.length) {
3323
3594
  return {
3324
3595
  isDiff: true,
3325
3596
  diffTopRenderedIndexes: [],
3326
3597
  diffBottomRenderedIndexes: []
3327
3598
  };
3328
3599
  }
3329
- const oldVisibleIndexes = [...this.virtualVisibleIndexes];
3600
+ const oldVisibleIndexes = [...this.inViewportIndics];
3330
3601
  const newVisibleIndexes = [...virtualView.visibleIndexes];
3331
3602
  const firstNewIndex = newVisibleIndexes[0];
3332
3603
  const lastNewIndex = newVisibleIndexes[newVisibleIndexes.length - 1];
@@ -3382,7 +3653,7 @@ class SlateEditable {
3382
3653
  }
3383
3654
  }
3384
3655
  if (isDebug) {
3385
- this.debugLog('log', `====== diffVirtualView stage: ${stage} ======`);
3656
+ this.debugLog('log', `====== diffVirtualViewport stage: ${stage} ======`);
3386
3657
  this.debugLog('log', 'oldVisibleIndexes:', oldVisibleIndexes);
3387
3658
  this.debugLog('log', 'newVisibleIndexes:', newVisibleIndexes);
3388
3659
  this.debugLog('log', 'diffTopRenderedIndexes:', isMissingTop ? '-' : isAddedTop ? '+' : '-', diffTopRenderedIndexes, diffTopRenderedIndexes.map(index => this.getBlockHeight(index, 0)));
@@ -3413,11 +3684,22 @@ class SlateEditable {
3413
3684
  }
3414
3685
  getBlockHeight(index, defaultHeight = VIRTUAL_SCROLL_DEFAULT_BLOCK_HEIGHT) {
3415
3686
  const node = this.editor.children[index];
3687
+ const isVisible = this.editor.isVisible(node);
3688
+ if (!isVisible) {
3689
+ return 0;
3690
+ }
3416
3691
  if (!node) {
3417
3692
  return defaultHeight;
3418
3693
  }
3419
3694
  const key = AngularEditor.findKey(this.editor, node);
3420
- return this.measuredHeights.get(key.id) ?? defaultHeight;
3695
+ const height = this.keyHeightMap.get(key.id);
3696
+ if (typeof height === 'number') {
3697
+ return height;
3698
+ }
3699
+ if (this.keyHeightMap.has(key.id)) {
3700
+ console.error('getBlockHeight: invalid height value', key.id, height);
3701
+ }
3702
+ return defaultHeight;
3421
3703
  }
3422
3704
  buildAccumulatedHeight(heights) {
3423
3705
  const accumulatedHeights = new Array(heights.length + 1).fill(0);
@@ -3428,7 +3710,7 @@ class SlateEditable {
3428
3710
  return accumulatedHeights;
3429
3711
  }
3430
3712
  scheduleMeasureVisibleHeights() {
3431
- if (!this.shouldUseVirtual()) {
3713
+ if (!this.isEnabledVirtualScroll()) {
3432
3714
  return;
3433
3715
  }
3434
3716
  this.measureVisibleHeightsAnimId && cancelAnimationFrame(this.measureVisibleHeightsAnimId);
@@ -3438,14 +3720,14 @@ class SlateEditable {
3438
3720
  }
3439
3721
  measureVisibleHeights() {
3440
3722
  const children = (this.editor.children || []);
3441
- this.virtualVisibleIndexes.forEach(index => {
3723
+ this.inViewportIndics.forEach(index => {
3442
3724
  const node = children[index];
3443
3725
  if (!node) {
3444
3726
  return;
3445
3727
  }
3446
3728
  const key = AngularEditor.findKey(this.editor, node);
3447
3729
  // 跳过已测过的块,除非强制测量
3448
- if (this.measuredHeights.has(key.id)) {
3730
+ if (this.keyHeightMap.has(key.id)) {
3449
3731
  return;
3450
3732
  }
3451
3733
  const view = ELEMENT_TO_COMPONENT.get(node);
@@ -3455,11 +3737,11 @@ class SlateEditable {
3455
3737
  const ret = view.getRealHeight();
3456
3738
  if (ret instanceof Promise) {
3457
3739
  ret.then(height => {
3458
- this.measuredHeights.set(key.id, height);
3740
+ this.keyHeightMap.set(key.id, height);
3459
3741
  });
3460
3742
  }
3461
3743
  else {
3462
- this.measuredHeights.set(key.id, ret);
3744
+ this.keyHeightMap.set(key.id, ret);
3463
3745
  }
3464
3746
  });
3465
3747
  }
@@ -3476,12 +3758,12 @@ class SlateEditable {
3476
3758
  if (!view) {
3477
3759
  return;
3478
3760
  }
3479
- const prevHeight = this.measuredHeights.get(key.id);
3761
+ const prevHeight = this.keyHeightMap.get(key.id);
3480
3762
  const ret = view.getRealHeight();
3481
3763
  if (ret instanceof Promise) {
3482
3764
  ret.then(height => {
3483
3765
  if (height !== prevHeight) {
3484
- this.measuredHeights.set(key.id, height);
3766
+ this.keyHeightMap.set(key.id, height);
3485
3767
  isHeightChanged = true;
3486
3768
  if (isDebug) {
3487
3769
  this.debugLog('log', `remeasureHeightByIndics, index: ${index} prevHeight: ${prevHeight} newHeight: ${height}`);
@@ -3491,7 +3773,7 @@ class SlateEditable {
3491
3773
  }
3492
3774
  else {
3493
3775
  if (ret !== prevHeight) {
3494
- this.measuredHeights.set(key.id, ret);
3776
+ this.keyHeightMap.set(key.id, ret);
3495
3777
  isHeightChanged = true;
3496
3778
  if (isDebug) {
3497
3779
  this.debugLog('log', `remeasureHeightByIndics, index: ${index} prevHeight: ${prevHeight} newHeight: ${ret}`);
@@ -4198,8 +4480,6 @@ class SlateEditable {
4198
4480
  //#endregion
4199
4481
  ngOnDestroy() {
4200
4482
  this.editorResizeObserver?.disconnect();
4201
- this.debugOverlay?.dispose();
4202
- this.debugOverlay = undefined;
4203
4483
  NODE_TO_ELEMENT.delete(this.editor);
4204
4484
  this.manualListeners.forEach(manualListener => {
4205
4485
  manualListener();