slate-angular 20.2.0-next.10 → 20.2.0-next.12

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();
@@ -3064,16 +3335,9 @@ class SlateEditable {
3064
3335
  ngDoCheck() { }
3065
3336
  forceRender() {
3066
3337
  this.updateContext();
3067
- let visibleIndexes = Array.from(this.virtualVisibleIndexes);
3068
- const isFirstRender = visibleIndexes.length === 0;
3069
- if (isFirstRender) {
3070
- const virtualView = this.refreshVirtualView();
3071
- this.applyVirtualView(virtualView);
3072
- visibleIndexes = Array.from(this.virtualVisibleIndexes);
3073
- }
3074
- else {
3075
- this.renderedChildren = visibleIndexes.map(index => this.editor.children[index]);
3076
- }
3338
+ const virtualView = this.refreshVirtualView();
3339
+ this.applyVirtualView(virtualView);
3340
+ const visibleIndexes = Array.from(this.virtualVisibleIndexes);
3077
3341
  this.listRender.update(this.renderedChildren, this.editor, this.context);
3078
3342
  this.remeasureHeightByIndics(visibleIndexes);
3079
3343
  // repair collaborative editing when Chinese input is interrupted by other users' cursors
@@ -3211,8 +3475,7 @@ class SlateEditable {
3211
3475
  this.editorResizeObserver.observe(this.elementRef.nativeElement);
3212
3476
  if (isDebug) {
3213
3477
  const doc = this.elementRef?.nativeElement?.ownerDocument ?? document;
3214
- this.debugOverlay = new VirtualScrollDebugOverlay(doc);
3215
- this.debugOverlay.init();
3478
+ VirtualScrollDebugOverlay.getInstance(doc);
3216
3479
  }
3217
3480
  }
3218
3481
  }
@@ -3224,11 +3487,8 @@ class SlateEditable {
3224
3487
  this.virtualBottomHeightElement.style.height = `${bottomHeight}px`;
3225
3488
  }
3226
3489
  debugLog(type, ...args) {
3227
- if (!this.debugOverlay) {
3228
- const doc = this.elementRef?.nativeElement?.ownerDocument ?? document;
3229
- this.debugOverlay = new VirtualScrollDebugOverlay(doc);
3230
- }
3231
- this.debugOverlay.log(type, ...args);
3490
+ const doc = this.elementRef?.nativeElement?.ownerDocument ?? document;
3491
+ VirtualScrollDebugOverlay.log(doc, type, ...args);
3232
3492
  }
3233
3493
  doVirtualScroll() {
3234
3494
  this.refreshVirtualViewAnimId && cancelAnimationFrame(this.refreshVirtualViewAnimId);
@@ -3270,6 +3530,10 @@ class SlateEditable {
3270
3530
  };
3271
3531
  }
3272
3532
  const scrollTop = this.virtualConfig.scrollTop;
3533
+ if (isDebug) {
3534
+ const doc = this.elementRef?.nativeElement?.ownerDocument ?? document;
3535
+ VirtualScrollDebugOverlay.syncScrollTop(doc, Number.isFinite(scrollTop) ? scrollTop : 0);
3536
+ }
3273
3537
  const viewportHeight = this.virtualConfig.viewportHeight ?? 0;
3274
3538
  if (!viewportHeight) {
3275
3539
  return {
@@ -3420,11 +3684,22 @@ class SlateEditable {
3420
3684
  }
3421
3685
  getBlockHeight(index, defaultHeight = VIRTUAL_SCROLL_DEFAULT_BLOCK_HEIGHT) {
3422
3686
  const node = this.editor.children[index];
3687
+ const isVisible = this.editor.isVisible(node);
3688
+ if (!isVisible) {
3689
+ return 0;
3690
+ }
3423
3691
  if (!node) {
3424
3692
  return defaultHeight;
3425
3693
  }
3426
3694
  const key = AngularEditor.findKey(this.editor, node);
3427
- return this.measuredHeights.get(key.id) ?? defaultHeight;
3695
+ const height = this.measuredHeights.get(key.id);
3696
+ if (typeof height === 'number') {
3697
+ return height;
3698
+ }
3699
+ if (this.measuredHeights.has(key.id)) {
3700
+ console.error('getBlockHeight: invalid height value', key.id, height);
3701
+ }
3702
+ return defaultHeight;
3428
3703
  }
3429
3704
  buildAccumulatedHeight(heights) {
3430
3705
  const accumulatedHeights = new Array(heights.length + 1).fill(0);
@@ -4205,8 +4480,6 @@ class SlateEditable {
4205
4480
  //#endregion
4206
4481
  ngOnDestroy() {
4207
4482
  this.editorResizeObserver?.disconnect();
4208
- this.debugOverlay?.dispose();
4209
- this.debugOverlay = undefined;
4210
4483
  NODE_TO_ELEMENT.delete(this.editor);
4211
4484
  this.manualListeners.forEach(manualListener => {
4212
4485
  manualListener();