slate-angular 20.2.0-next.7 → 20.2.0-next.9

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.
@@ -2567,6 +2567,235 @@ function executeAfterViewInit(editor) {
2567
2567
  clearAfterViewInitQueue(editor);
2568
2568
  }
2569
2569
 
2570
+ class VirtualScrollDebugOverlay {
2571
+ constructor(doc) {
2572
+ this.doc = doc;
2573
+ this.originalConsoleLog = console.log.bind(console);
2574
+ this.originalConsoleWarn = console.warn.bind(console);
2575
+ this.dragOffsetX = 0;
2576
+ this.dragOffsetY = 0;
2577
+ this.isDragging = false;
2578
+ this.onDragging = (event) => {
2579
+ if (!this.isDragging || !this.container) {
2580
+ return;
2581
+ }
2582
+ const nextLeft = event.clientX - this.dragOffsetX;
2583
+ const nextTop = event.clientY - this.dragOffsetY;
2584
+ this.container.style.left = `${nextLeft}px`;
2585
+ this.container.style.top = `${nextTop}px`;
2586
+ this.container.style.right = 'auto';
2587
+ this.container.style.bottom = 'auto';
2588
+ };
2589
+ this.onDragEnd = () => {
2590
+ if (!this.isDragging) {
2591
+ return;
2592
+ }
2593
+ this.isDragging = false;
2594
+ this.doc.removeEventListener('mousemove', this.onDragging);
2595
+ this.doc.removeEventListener('mouseup', this.onDragEnd);
2596
+ if (this.container) {
2597
+ this.container.style.transition = '';
2598
+ }
2599
+ };
2600
+ }
2601
+ init() {
2602
+ if (!this.container) {
2603
+ this.createContainer();
2604
+ }
2605
+ }
2606
+ log(type, ...args) {
2607
+ this.init();
2608
+ if (type === 'warn') {
2609
+ this.originalConsoleWarn(...args);
2610
+ }
2611
+ else {
2612
+ this.originalConsoleLog(...args);
2613
+ }
2614
+ this.appendLog(type, ...args);
2615
+ }
2616
+ dispose() {
2617
+ this.container?.remove();
2618
+ this.container = undefined;
2619
+ this.logList = undefined;
2620
+ this.selectorInput = undefined;
2621
+ this.distanceInput = undefined;
2622
+ this.doc.removeEventListener('mousemove', this.onDragging);
2623
+ this.doc.removeEventListener('mouseup', this.onDragEnd);
2624
+ this.isDragging = false;
2625
+ }
2626
+ createContainer() {
2627
+ const doc = this.doc;
2628
+ const container = doc.createElement('div');
2629
+ container.style.position = 'fixed';
2630
+ container.style.left = '16px';
2631
+ container.style.top = '16px';
2632
+ container.style.right = 'auto';
2633
+ container.style.bottom = 'auto';
2634
+ container.style.width = '360px';
2635
+ container.style.maxHeight = '70vh';
2636
+ container.style.padding = '12px';
2637
+ container.style.boxSizing = 'border-box';
2638
+ container.style.background = 'rgba(17, 24, 39, 0.95)';
2639
+ container.style.color = '#e5e7eb';
2640
+ container.style.fontSize = '12px';
2641
+ container.style.fontFamily = 'Menlo, Consolas, monospace';
2642
+ container.style.border = '1px solid #1f2937';
2643
+ container.style.borderRadius = '10px';
2644
+ container.style.boxShadow = '0 10px 30px rgba(0, 0, 0, 0.35)';
2645
+ container.style.zIndex = '9999';
2646
+ container.style.display = 'flex';
2647
+ container.style.flexDirection = 'column';
2648
+ container.style.gap = '10px';
2649
+ const header = doc.createElement('div');
2650
+ header.style.display = 'flex';
2651
+ header.style.alignItems = 'center';
2652
+ header.style.justifyContent = 'space-between';
2653
+ header.style.cursor = 'move';
2654
+ header.addEventListener('mousedown', event => {
2655
+ if (!this.container) {
2656
+ return;
2657
+ }
2658
+ const rect = this.container.getBoundingClientRect();
2659
+ this.isDragging = true;
2660
+ this.dragOffsetX = event.clientX - rect.left;
2661
+ this.dragOffsetY = event.clientY - rect.top;
2662
+ this.container.style.transition = 'none';
2663
+ this.doc.addEventListener('mousemove', this.onDragging);
2664
+ this.doc.addEventListener('mouseup', this.onDragEnd);
2665
+ event.preventDefault();
2666
+ });
2667
+ const title = doc.createElement('div');
2668
+ title.textContent = 'Virtual Scroll Debug';
2669
+ title.style.fontWeight = '600';
2670
+ title.style.letterSpacing = '0.3px';
2671
+ const clearButton = doc.createElement('button');
2672
+ clearButton.type = 'button';
2673
+ clearButton.textContent = '清除日志';
2674
+ clearButton.style.background = '#374151';
2675
+ clearButton.style.color = '#e5e7eb';
2676
+ clearButton.style.border = '1px solid #4b5563';
2677
+ clearButton.style.borderRadius = '6px';
2678
+ clearButton.style.padding = '4px 10px';
2679
+ clearButton.style.cursor = 'pointer';
2680
+ clearButton.addEventListener('click', () => {
2681
+ if (this.logList) {
2682
+ this.logList.innerHTML = '';
2683
+ }
2684
+ });
2685
+ header.appendChild(title);
2686
+ header.appendChild(clearButton);
2687
+ const scrollForm = doc.createElement('div');
2688
+ scrollForm.style.display = 'grid';
2689
+ scrollForm.style.gridTemplateColumns = '1fr 90px 70px';
2690
+ scrollForm.style.gap = '6px';
2691
+ scrollForm.style.alignItems = 'center';
2692
+ const selectorInput = doc.createElement('input');
2693
+ selectorInput.placeholder = '滚动容器 selector';
2694
+ selectorInput.style.padding = '6px 8px';
2695
+ selectorInput.style.borderRadius = '6px';
2696
+ selectorInput.style.border = '1px solid #4b5563';
2697
+ selectorInput.style.background = '#111827';
2698
+ selectorInput.style.color = '#e5e7eb';
2699
+ selectorInput.autocomplete = 'off';
2700
+ const distanceInput = doc.createElement('input');
2701
+ distanceInput.placeholder = '滚动距离(px)';
2702
+ distanceInput.type = 'number';
2703
+ distanceInput.style.padding = '6px 8px';
2704
+ distanceInput.style.borderRadius = '6px';
2705
+ distanceInput.style.border = '1px solid #4b5563';
2706
+ distanceInput.style.background = '#111827';
2707
+ distanceInput.style.color = '#e5e7eb';
2708
+ const scrollButton = doc.createElement('button');
2709
+ scrollButton.type = 'button';
2710
+ scrollButton.textContent = '滚动';
2711
+ scrollButton.style.background = '#10b981';
2712
+ scrollButton.style.color = '#0b1c15';
2713
+ scrollButton.style.border = 'none';
2714
+ scrollButton.style.borderRadius = '6px';
2715
+ scrollButton.style.padding = '6px 10px';
2716
+ scrollButton.style.cursor = 'pointer';
2717
+ scrollButton.addEventListener('click', () => {
2718
+ const selector = selectorInput.value.trim();
2719
+ const distanceValue = Number(distanceInput.value ?? 0);
2720
+ const distance = Number.isFinite(distanceValue) ? distanceValue : 0;
2721
+ if (!selector) {
2722
+ this.log('warn', '请先填写滚动容器 selector');
2723
+ return;
2724
+ }
2725
+ const target = doc.querySelector(selector);
2726
+ if (!target) {
2727
+ this.log('warn', `未找到滚动容器: ${selector}`);
2728
+ return;
2729
+ }
2730
+ if (typeof target.scrollTo === 'function') {
2731
+ target.scrollTo({ top: distance, behavior: 'auto' });
2732
+ }
2733
+ else if (Object.prototype.hasOwnProperty.call(target, 'scrollTop')) {
2734
+ target.scrollTop = distance;
2735
+ }
2736
+ else {
2737
+ this.log('warn', '目标元素不支持滚动:', selector);
2738
+ return;
2739
+ }
2740
+ this.log('log', `已将 ${selector} 滚动到`, distance);
2741
+ });
2742
+ scrollForm.appendChild(selectorInput);
2743
+ scrollForm.appendChild(distanceInput);
2744
+ scrollForm.appendChild(scrollButton);
2745
+ const logList = doc.createElement('div');
2746
+ logList.style.height = '260px';
2747
+ logList.style.overflowY = 'auto';
2748
+ logList.style.background = '#0b1220';
2749
+ logList.style.border = '1px solid #1f2937';
2750
+ logList.style.borderRadius = '8px';
2751
+ logList.style.padding = '8px';
2752
+ logList.style.display = 'flex';
2753
+ logList.style.flexDirection = 'column';
2754
+ logList.style.gap = '6px';
2755
+ container.appendChild(header);
2756
+ container.appendChild(scrollForm);
2757
+ container.appendChild(logList);
2758
+ doc.body.appendChild(container);
2759
+ this.container = container;
2760
+ this.logList = logList;
2761
+ this.selectorInput = selectorInput;
2762
+ this.distanceInput = distanceInput;
2763
+ }
2764
+ appendLog(type, ...args) {
2765
+ if (!this.logList) {
2766
+ return;
2767
+ }
2768
+ const item = this.doc.createElement('div');
2769
+ item.style.display = 'flex';
2770
+ item.style.gap = '6px';
2771
+ item.style.alignItems = 'flex-start';
2772
+ item.style.wordBreak = 'break-all';
2773
+ item.style.color = type === 'warn' ? '#fbbf24' : '#9ca3af';
2774
+ const time = this.doc.createElement('span');
2775
+ time.textContent = new Date().toLocaleTimeString();
2776
+ time.style.color = '#6b7280';
2777
+ time.style.flexShrink = '0';
2778
+ time.style.width = '72px';
2779
+ const text = this.doc.createElement('span');
2780
+ text.textContent = `[${type}] ${args.map(arg => this.formatValue(arg)).join(' ')}`;
2781
+ item.appendChild(time);
2782
+ item.appendChild(text);
2783
+ this.logList.appendChild(item);
2784
+ this.logList.scrollTop = this.logList.scrollHeight;
2785
+ }
2786
+ formatValue(value) {
2787
+ if (typeof value === 'string') {
2788
+ return value;
2789
+ }
2790
+ try {
2791
+ return JSON.stringify(value);
2792
+ }
2793
+ catch (error) {
2794
+ return String(value);
2795
+ }
2796
+ }
2797
+ }
2798
+
2570
2799
  const JUST_NOW_UPDATED_VIRTUAL_VIEW = new WeakMap();
2571
2800
  // not correctly clipboardData on beforeinput
2572
2801
  const forceOnDOMPaste = IS_SAFARI;
@@ -2574,32 +2803,7 @@ const isDebug = localStorage.getItem(SLATE_DEBUG_KEY) === 'true';
2574
2803
  class SlateEditable {
2575
2804
  set virtualScroll(config) {
2576
2805
  this.virtualConfig = config;
2577
- this.refreshVirtualViewAnimId && cancelAnimationFrame(this.refreshVirtualViewAnimId);
2578
- this.refreshVirtualViewAnimId = requestAnimationFrame(() => {
2579
- let virtualView = this.refreshVirtualView();
2580
- let diff = this.diffVirtualView(virtualView);
2581
- if (!diff.isDiff) {
2582
- return;
2583
- }
2584
- if (diff.isMissingTop) {
2585
- const result = this.remeasureHeightByIndics(diff.diffTopRenderedIndexes);
2586
- if (result) {
2587
- virtualView = this.refreshVirtualView();
2588
- diff = this.diffVirtualView(virtualView, 'second');
2589
- if (!diff.isDiff) {
2590
- return;
2591
- }
2592
- }
2593
- }
2594
- this.applyVirtualView(virtualView);
2595
- if (this.listRender.initialized) {
2596
- this.listRender.update(virtualView.renderedChildren, this.editor, this.context);
2597
- if (!AngularEditor.isReadOnly(this.editor) && this.editor.selection) {
2598
- this.toNativeSelection();
2599
- }
2600
- }
2601
- this.scheduleMeasureVisibleHeights();
2602
- });
2806
+ this.doVirtualScroll();
2603
2807
  }
2604
2808
  get hasBeforeInputSupport() {
2605
2809
  return HAS_BEFORE_INPUT_SUPPORT;
@@ -2766,7 +2970,7 @@ class SlateEditable {
2766
2970
  if (!intersectedSelection || !Range.equals(intersectedSelection, forwardSelection)) {
2767
2971
  selection = intersectedSelection;
2768
2972
  if (isDebug) {
2769
- console.log(`selection is not in visible range, selection: ${JSON.stringify(selection)}, intersectedSelection: ${JSON.stringify(intersectedSelection)}`);
2973
+ this.debugLog('log', `selection is not in visible range, selection: ${JSON.stringify(selection)}, intersectedSelection: ${JSON.stringify(intersectedSelection)}`);
2770
2974
  }
2771
2975
  }
2772
2976
  }
@@ -2987,6 +3191,19 @@ class SlateEditable {
2987
3191
  this.elementRef.nativeElement.appendChild(this.virtualCenterOutlet);
2988
3192
  this.elementRef.nativeElement.appendChild(this.virtualBottomHeightElement);
2989
3193
  this.businessHeight = this.virtualTopHeightElement.getBoundingClientRect()?.top ?? 0;
3194
+ let editorResizeObserverRectWidth = this.elementRef.nativeElement.getBoundingClientRect()?.width ?? 0;
3195
+ this.editorResizeObserver = new ResizeObserver(entries => {
3196
+ if (entries.length > 0 && entries[0].contentRect.width !== editorResizeObserverRectWidth) {
3197
+ editorResizeObserverRectWidth = entries[0].contentRect.width;
3198
+ this.remeasureHeightByIndics(Array.from(this.virtualVisibleIndexes));
3199
+ }
3200
+ });
3201
+ this.editorResizeObserver.observe(this.elementRef.nativeElement);
3202
+ if (isDebug) {
3203
+ const doc = this.elementRef?.nativeElement?.ownerDocument ?? document;
3204
+ this.debugOverlay = new VirtualScrollDebugOverlay(doc);
3205
+ this.debugOverlay.init();
3206
+ }
2990
3207
  }
2991
3208
  }
2992
3209
  changeVirtualHeight(topHeight, bottomHeight) {
@@ -2996,6 +3213,41 @@ class SlateEditable {
2996
3213
  this.virtualTopHeightElement.style.height = `${topHeight}px`;
2997
3214
  this.virtualBottomHeightElement.style.height = `${bottomHeight}px`;
2998
3215
  }
3216
+ debugLog(type, ...args) {
3217
+ if (!this.debugOverlay) {
3218
+ const doc = this.elementRef?.nativeElement?.ownerDocument ?? document;
3219
+ this.debugOverlay = new VirtualScrollDebugOverlay(doc);
3220
+ }
3221
+ this.debugOverlay.log(type, ...args);
3222
+ }
3223
+ doVirtualScroll() {
3224
+ this.refreshVirtualViewAnimId && cancelAnimationFrame(this.refreshVirtualViewAnimId);
3225
+ this.refreshVirtualViewAnimId = requestAnimationFrame(() => {
3226
+ let virtualView = this.refreshVirtualView();
3227
+ let diff = this.diffVirtualView(virtualView);
3228
+ if (!diff.isDiff) {
3229
+ return;
3230
+ }
3231
+ if (diff.isMissingTop) {
3232
+ const result = this.remeasureHeightByIndics(diff.diffTopRenderedIndexes);
3233
+ if (result) {
3234
+ virtualView = this.refreshVirtualView();
3235
+ diff = this.diffVirtualView(virtualView, 'second');
3236
+ if (!diff.isDiff) {
3237
+ return;
3238
+ }
3239
+ }
3240
+ }
3241
+ this.applyVirtualView(virtualView);
3242
+ if (this.listRender.initialized) {
3243
+ this.listRender.update(virtualView.renderedChildren, this.editor, this.context);
3244
+ if (!AngularEditor.isReadOnly(this.editor) && this.editor.selection) {
3245
+ this.toNativeSelection();
3246
+ }
3247
+ }
3248
+ this.scheduleMeasureVisibleHeights();
3249
+ });
3250
+ }
2999
3251
  refreshVirtualView() {
3000
3252
  const children = (this.editor.children || []);
3001
3253
  if (!children.length || !this.shouldUseVirtual()) {
@@ -3127,18 +3379,18 @@ class SlateEditable {
3127
3379
  }
3128
3380
  }
3129
3381
  if (isDebug) {
3130
- console.log(`====== diffVirtualView stage: ${stage} ======`);
3131
- console.log('oldVisibleIndexes:', oldVisibleIndexes);
3132
- console.log('newVisibleIndexes:', newVisibleIndexes);
3133
- console.log('diffTopRenderedIndexes:', isMissingTop ? '-' : isAddedTop ? '+' : '-', diffTopRenderedIndexes, diffTopRenderedIndexes.map(index => this.getBlockHeight(index, 0)));
3134
- console.log('diffBottomRenderedIndexes:', isAddedBottom ? '+' : isMissingBottom ? '-' : '+', diffBottomRenderedIndexes, diffBottomRenderedIndexes.map(index => this.getBlockHeight(index, 0)));
3382
+ this.debugLog('log', `====== diffVirtualView stage: ${stage} ======`);
3383
+ this.debugLog('log', 'oldVisibleIndexes:', oldVisibleIndexes);
3384
+ this.debugLog('log', 'newVisibleIndexes:', newVisibleIndexes);
3385
+ this.debugLog('log', 'diffTopRenderedIndexes:', isMissingTop ? '-' : isAddedTop ? '+' : '-', diffTopRenderedIndexes, diffTopRenderedIndexes.map(index => this.getBlockHeight(index, 0)));
3386
+ this.debugLog('log', 'diffBottomRenderedIndexes:', isAddedBottom ? '+' : isMissingBottom ? '-' : '+', diffBottomRenderedIndexes, diffBottomRenderedIndexes.map(index => this.getBlockHeight(index, 0)));
3135
3387
  const needTop = virtualView.heights.slice(0, newVisibleIndexes[0]).reduce((acc, height) => acc + height, 0);
3136
3388
  const needBottom = virtualView.heights
3137
3389
  .slice(newVisibleIndexes[newVisibleIndexes.length - 1] + 1)
3138
3390
  .reduce((acc, height) => acc + height, 0);
3139
- console.log('newTopHeight:', needTop, 'prevTopHeight:', parseFloat(this.virtualTopHeightElement.style.height));
3140
- console.log('newBottomHeight:', needBottom, 'prevBottomHeight:', parseFloat(this.virtualBottomHeightElement.style.height));
3141
- console.warn('=========== Dividing line ===========');
3391
+ this.debugLog('log', 'newTopHeight:', needTop, 'prevTopHeight:', parseFloat(this.virtualTopHeightElement.style.height));
3392
+ this.debugLog('log', 'newBottomHeight:', needBottom, 'prevBottomHeight:', parseFloat(this.virtualBottomHeightElement.style.height));
3393
+ this.debugLog('warn', '=========== Dividing line ===========');
3142
3394
  }
3143
3395
  return {
3144
3396
  isDiff: true,
@@ -3229,7 +3481,7 @@ class SlateEditable {
3229
3481
  this.measuredHeights.set(key.id, height);
3230
3482
  isHeightChanged = true;
3231
3483
  if (isDebug) {
3232
- console.log(`remeasureHeightByIndics, index: ${index} prevHeight: ${prevHeight} newHeight: ${height}`);
3484
+ this.debugLog('log', `remeasureHeightByIndics, index: ${index} prevHeight: ${prevHeight} newHeight: ${height}`);
3233
3485
  }
3234
3486
  }
3235
3487
  });
@@ -3239,7 +3491,7 @@ class SlateEditable {
3239
3491
  this.measuredHeights.set(key.id, ret);
3240
3492
  isHeightChanged = true;
3241
3493
  if (isDebug) {
3242
- console.log(`remeasureHeightByIndics, index: ${index} prevHeight: ${prevHeight} newHeight: ${ret}`);
3494
+ this.debugLog('log', `remeasureHeightByIndics, index: ${index} prevHeight: ${prevHeight} newHeight: ${ret}`);
3243
3495
  }
3244
3496
  }
3245
3497
  }
@@ -3942,6 +4194,9 @@ class SlateEditable {
3942
4194
  }
3943
4195
  //#endregion
3944
4196
  ngOnDestroy() {
4197
+ this.editorResizeObserver?.disconnect();
4198
+ this.debugOverlay?.dispose();
4199
+ this.debugOverlay = undefined;
3945
4200
  NODE_TO_ELEMENT.delete(this.editor);
3946
4201
  this.manualListeners.forEach(manualListener => {
3947
4202
  manualListener();